从辣妈GS3双锅炉意式机到EK43磨豆机,从专业手冲壶到耶加雪菲精品豆,从聪明杯到摩卡壶——本文以一套完整的ArkTS咖啡研究所应用为例,深度解析HarmonyOS ArkTS API 24在复古咖啡馆二手交易场景下的双Tab嵌套架构、14粒子漂浮动效、稀有度四级分级、棕金渐变详情头、以物换豆交换帖等核心技术实现。

一、引言

咖啡机、磨豆机、手冲壶、咖啡豆、配件——这些承载着咖啡爱好者日常冲煮与品鉴的器具,在精品咖啡浪潮下反而焕发了新的收藏热潮。从辣妈GS3双锅炉意式机到EK43磨豆机,从专业手冲壶到耶加雪菲精品豆,从聪明杯到摩卡壶,咖啡器具收藏已经成为一个庞大的文化市场。然而咖啡器具交易长期依赖闲鱼、贴吧等泛平台,缺乏针对咖啡器具的专业化功能——稀有度评级、规格区分(双锅炉/定量版/800ml/227g/500g)、以物换豆、收藏图鉴等核心需求无法得到满足。

本应用(咖啡研究所)以"深棕+奶油白"复古咖啡馆风为视觉基调,打造集器具交易、换豆交换、收藏图鉴、个人中心四大模块于一体的复古咖啡馆二手交易与收藏社区。18款咖啡器具覆盖咖啡机/磨豆机/手冲/咖啡豆/配件全品类,8条换豆交换帖覆盖以物换物场景,5套教程覆盖手冲入门/意式萃取/拉花进阶/咖啡豆烘焙/冷萃做法,9款配件周边覆盖收藏周边,6套图鉴收藏记录收藏进度,3条圈友点评提供真实评价。

本文将从架构设计、色彩系统、数据模型、四大Tab页面、弹框体系、核心技术特性等维度,完整解析这套应用的ArkTS实现。

二、整体架构

本应用采用单组件双Tab嵌套架构——@Entry @Component struct Index作为唯一入口组件,通过@State currentBottomTab控制四大底部Tab(器具/换豆/图鉴/我的),器具Tab内部再通过@State currentTopTab控制六大顶部分类Tab(全部/咖啡机/磨豆机/手冲/咖啡豆/配件),形成双层Tab嵌套。

架构层次:

  • 入口层:Index,负责双层Tab切换、头部、底部导航、粒子动效、弹框管理
  • 页面层:4个底部Tab对应的Builder方法(bazaarContent/swapContent/collectionContent/mineContent),器具Tab内含6个顶部Tab对应的Builder方法(cityContent/retiredContent/mocContent/figContent/techContent/streetContent)
  • 数据层:8个全局const数组(18器具+8换豆帖+5教程+9配件周边+6图鉴收藏+3点评+2图表数据+2弹框选项)+1个配色常量PALETTE
  • 工具函数层:10+全局纯函数(catColor/catLabel/rarityLabel/rarityColor/tradeBarHeight/rateColor/6个分类筛选函数/swapBadge)
  • 弹框层:4种弹框(发布出售/发起交换/下架删除/器具详情),均通过@State布尔值控制显隐

单组件架构的优势在于状态集中管理、数据共享方便、弹框统一调度,适合中等复杂度的应用。双层Tab嵌套是本应用的架构特色——器具作为主品类,内部再按咖啡机/磨豆机/手冲/咖啡豆/配件细分,用户可以快速切换品类浏览。

三、色彩系统:深棕+奶油白复古咖啡馆风

复古咖啡馆的视觉语言应该是"温暖、醇厚、有咖啡馆的木质质感"。本应用采用深棕+奶油白配色:

主色体系:

  • #6F4E37 深棕:主高亮色、Tab选中态、按钮、标签、渐变——代表咖啡的醇厚与温暖
  • #F5F0EB 奶油白:应用根背景、卡片背景、教程步骤背景——代表牛奶与奶泡的温暖质感
  • #2B2B2B 深黑:主文字色、深色卡片背景——代表沉稳
  • #A83A2E 砖红:价格、标签、统计数字——代表热情与复古
  • #C9A24B 复古金:Tab指示条、Banner渐变、标签、统计数字——代表经典与珍贵
  • #8A7F73 烟灰棕:辅助文字、未选中Tab、说明文字——代表素雅
  • #E5DDD5 浅米灰:分割线、进度条背景——代表层次

浅色背景层次:

  • #F5F0EB 奶油白:应用根背景、教程步骤背景
  • #FFFFFF 纯白:卡片背景、弹框背景
  • #F0E9E2 浅棕:标签背景、选中态背景、高亮卡片背景、我提供背景
  • #F5EFE0 浅金:想换物品背景、提示背景、热度标签背景
  • #E2CE94 浅金:Banner渐变终点
  • #E8F7EF 浅绿:成就标签背景

渐变设计:

  • 头部:linearGradient angle 135 从#6F4E37到#2B2B2B——深棕到深黑的复古渐变
  • 咖啡节Banner:linearGradient angle 120 从#C9A24B到#E2CE94——复古金到浅金的经典渐变
  • 顶级意式机大卡:linearGradient angle 160 从#6F4E37到#2B2B2B——与头部一致的深棕渐变
  • 器具详情头部:linearGradient angle 135 从#C9A24B到#F0E9E2——复古金到浅棕的明快渐变
  • 发布出售弹框头部:linearGradient angle 120 从#6F4E37到#2B2B2B——与头部一致的深棕渐变
  • 我的页会员卡:linearGradient angle 120 从#6F4E37到#2B2B2B——与头部一致的深棕渐变

深棕+奶油白的配色通过奶油白根背景+纯白卡片+浅棕/浅金高亮背景营造咖啡馆的温暖空间感,深棕#6F4E37作为主高亮和按钮色,复古金#C9A24B作为Tab指示条和Banner色,砖红#A83A2E作为价格和标签色,确保视觉一致性和复古咖啡馆韵味。

四、数据模型

应用定义了7个接口,覆盖咖啡器具交易全链路:

  • CoffeeItem器具(id/name/eng/cat/price/rarity/year/rating/starStr/seller/tag/desc/want/sales/emoji/size)——18款,含中英文名称、分类、稀有度0-100、发行年份、规格(双锅炉/定量版/800ml/227g/500g/350ml等)、想换物品等咖啡特色字段
  • SwapItem换豆帖(id/user/avatar/offer/want/time/likes/content/place)——8条交换帖,含我提供/我想换/地点/点赞数
  • GuideItem教程指南(id/name/type/pages/author/likes/downloads/emoji/steps)——5套教程,含步骤数组
  • AccessoryItem配件周边(id/name/series/price/condition/seller/emoji/desc)——9款配件
  • CollectionItem图鉴收藏(id/name/series/owned/total/rate/emoji)——6套图鉴,含已收/总数/进度百分比
  • CoffeeReview点评(id/user/setName/starStr/content)——3条点评
  • ColorPalette配色(bg/card/brown/cream/deep/red/gold/sub/line)——9色配色常量

rarity稀有度字段是咖啡器具的特色设计——0-100数值,通过rarityLabel函数映射为四级(传世级≥95/稀有级≥80/紧俏级≥60/流通级<60),通过rarityColor函数映射为四色(砖红/橙/蓝/深棕),在稀有度柱状图和器具详情中直接渲染。size规格字段区分双锅炉/定量版/800ml/227g/500g等不同规格,want想换物品字段支持以物换豆场景。

五、Tab0 器具交易(双Tab嵌套)

器具页是应用的核心,采用底部Tab+顶部Tab双层嵌套架构。顶部6个分类Tab(全部/咖啡机/磨豆机/手冲/咖啡豆/配件),每个Tab有独立的布局风格:

全部Tab:Banner+双列瀑布

  • 复古金渐变Banner:咖啡节春季专场+顶级器具3天限时+顶级意式机/精品咖啡豆标签
  • 全城新上架:双列瀑布布局,左列2件(温度计探针式/咖啡勺),右列1件(挂耳咖啡10包,浅棕高亮背景),每件含emoji/中文名/英文名/描述/价格/标签

咖啡机Tab:横滑大卡+稀有度柱状图+捡漏榜

  • 顶级意式机专区:3件横滑大卡(辣妈GS3意式机/EK43磨豆机/手冲壶不锈钢),深棕渐变背景160宽,含emoji/名称/英文+年份/价格+稀有度标签
  • 稀有度排行:3根柱状图(95/88/55),第三根手冲壶用复古金高亮,底部说明"辣妈GS3以95分稀有度登顶,市面流通不足十台"
  • 今日捡漏榜:3件排行列表,第1名砖红数字,含排名/emoji/名称+稀有度+想换/价格

磨豆机Tab:教程时间轴

  • 5套教程时间轴布局(手冲咖啡入门/意式浓缩萃取指南/拉花进阶技巧/咖啡豆烘焙指南/冷萃咖啡做法),左侧emoji+复古金竖线连接,右侧卡片含名称/下载数/作者+类型+页数/步骤列表(带序号圆点)/点赞+教程可下载

手冲Tab:三列格子+交换建议

  • 配件周边三列网格(9款配件,31%宽度),每件含emoji/名称/系列/价格
  • 配件交换行情:偶数ID配件的交换列表,含emoji/名称+品相/描述/价格(深棕)

咖啡豆Tab:对决卡+侧栏统计

  • 豆子对决:辣妈GS3 VS 耶加雪菲,两张深棕渐变对决卡中间VS标识
  • 左侧3件器具列表(聪明杯滤杯/法压壶800ml/摩卡壶6杯份),右侧3格统计(12本周器具成交深棕/1.2万器具总交易额复古金/96%好评率深棕)

配件Tab:标签云+紧凑列表

  • 收藏标签云:6套图鉴(意式咖啡机/手冲器具/磨豆机/咖啡豆/咖啡杯/咖啡配件),浅棕背景chip
  • 3件紧凑列表(咖啡分享壶/意式咖啡杯套装/黄油曲奇),第3件浅棕高亮背景,含稀有度彩色标签
  • 底部提示:集齐三大咖啡器具可召唤隐藏福利:辣妈GS3+EK43+手冲壶

双层Tab嵌套让用户可以在器具主Tab内快速切换6大品类,每个品类有独立的视觉布局和数据展示方式,避免单一布局的单调感。

六、Tab1 换豆交换

换豆页是咖啡器具交易的特色功能——以物换豆。由两个层次构成:

1. 统计条+发交换按钮:四格统计(156在交换中深棕/42今日成交复古金/98%面交好评深棕)+右侧发交换按钮(深棕底,触发发起交换弹框)。

2. 换豆帖列表:8条交换帖卡片,每帖含:

  • 头部:头像emoji+用户名+热度标签(🔥热门≥50/👍热度≥30/🌱新帖<30,通过swapBadge函数动态生成)+时间+地点+右侧⋯菜单(触发下架删除弹框)
  • 交换区:我提供(浅棕背景)⇄ 我想换(浅金背景),左右对称布局
  • 内容:帖文描述
  • 底部:📍地点标签+❤️点赞数

换豆页解决了咖啡器具交易中"以物换豆"的核心需求——咖友可以用自己的闲置咖啡机/磨豆机/手冲壶交换心仪的宝贝,通过地点标签支持线下面交,通过热度标签筛选热门交换帖。

七、Tab2 收藏图鉴

图鉴页是收藏进度管理功能。由三个层次构成:

1. 标题+登记按钮:我的咖啡图鉴标题+记录每一件心头好的收藏进度+右侧✚登记按钮(深棕底,触发发布出售弹框复用)。

2. 图鉴进度列表:6套图鉴(意式咖啡机/手冲器具/磨豆机/咖啡豆/咖啡杯/咖啡配件),每套含:

  • 头部:emoji+名称+英文系列名+已收X/总数+右侧进度百分比(通过rateColor函数动态着色:≥55深棕/≥40复古金/<40砖红)
  • 进度条:双色进度条(已收部分rateColor色+未收部分浅米灰),宽度通过百分比动态计算

3. 图鉴成就墙:4个成就标签(🎖️意式咖啡大师/🏆手冲达人/🫘豆子收藏家/🥄咖啡生活家),不同颜色背景。

图鉴页让咖友可以系统化管理自己的收藏进度,通过进度条和百分比直观看到每套图鉴的完成度,通过成就墙增加收藏的趣味性和成就感。

八、Tab3 个人中心

我的页是咖友个人信息管理。由三个层次构成:

1. 会员信息卡:深棕渐变背景(与头部一致),含☕头像+咖啡收藏家阿乐+LV.10资深咖啡控·信用分990+右侧>箭头+三格统计(38在售器具复古金/12求购中复古金/¥3.8万累计成交额复古金)。

2. 我的求购清单:3件求购器具(咖啡分享壶/挂耳咖啡10包/咖啡勺),每件含emoji/名称+预算+想换/右侧出价中标签(复古金底浅金背景)。

3. 常去面交点:5个地点列表(光谷广场站/江汉路站/洪山广场站/楚河汉街站/线上互换),每个含不同emoji(🚇/🏬/🚌/🏪/📦)+地点名+右侧站点说明。

我的页通过求购清单和常去面交点两个特色功能,将咖啡器具交易的线下场景融入个人中心,让咖友可以管理自己的求购需求和常去的线下交易地点。

九、弹框体系

本应用有4种弹框,均通过@State布尔值控制显隐,zIndex通过Stack层叠实现:

1. 发布出售弹框(新增):88%宽76%高,深棕渐变头部(☕发布出售+闲置回血神机不愁卖),滚动内容含:要出售的器具(8款器具chip选择,选中深棕底白字)+成色说明(5档chip:全新未拆/仅拆封/9成新/8成新/有瑕疵,选中复古金底白字)+出售价格(−/¥价格/+步进器,步进10元,价格>2000显示"高价好物建议包邮"否则"实惠价更容易出手")+宝贝描述(TextInput输入)+取消/上架出售双按钮。

2. 发起交换弹框(新增):88%宽72%高,复古金纯色头部(🤝发起交换+用闲置换心头好咖友互助),滚动内容含:我提供(5款器具chip选择,选中深黑底白字)+我想换可多选(5款器具chip,选中砖红底白字带✓)+面交地点(5个地点chip,选中深棕底白字)+安全提示(💡交换双方可自行协商补差价,面交请选择人流密集的公共场所,注意安全)+再想想/发布交换帖双按钮。

3. 下架删除弹框:72%宽居中,🗑️图标+下架这件器具?+说明文字(下架后买家将无法搜索到这条信息,收藏和留言会一并清空,可重新上架)+留着(深棕底浅棕背景)/确认下架(砖红底白字)双按钮。

4. 器具详情弹框:88%宽78%高,复古金到浅棕渐变头部(emoji+中文名+英文名+标签+稀有度标签),滚动内容含:四格数据(售价砖红/规格深棕/稀有度动态色/年份复古金)+宝贝描述(描述文字+卖家+星级评分)+卖家想换(🤝+想换物品,浅金背景)+圈友点评(3条点评卡片)+留言问问(深棕底浅棕背景)/立即购买(砖红底白字)双按钮。

所有弹框均通过Stack层叠+半透明黑色背景(99000000)+居中布局实现,点击背景区域关闭弹框。

十、核心技术特性

1. 双Tab嵌套架构:底部4Tab+器具Tab内部顶部6Tab,通过两个@State(currentBottomTab/currentTopTab)独立控制,if-else链分发到对应的Builder方法,实现双层分类浏览。

2. 14粒子漂浮动效:aboutToAppear生命周期中buildParticles生成14个粒子(☕/🫘/🫖/⚙️/🥄五种emoji随机,大小12-22px,速度0.6-2.0,透明度0.2-0.6),setInterval每80ms调用driftParticles更新x坐标(超出360重置为-20),通过position绝对定位漂浮在页面上,营造咖啡馆氛围。aboutToDisappear中clearInterval清理定时器避免内存泄漏。

3. 稀有度四级分级:rarity字段0-100数值,通过rarityLabel映射为四级标签(传世级≥95/稀有级≥80/紧俏级≥60/流通级<60),通过rarityColor映射为四色(砖红/橙/蓝/深棕),在柱状图、器具详情、紧凑列表中统一渲染,数据驱动UI。

4. 深棕渐变头部设计:头部、顶级意式机大卡、发布出售弹框头部、我的页会员卡均使用深棕#6F4E37到深黑#2B2B2B的linearGradient渐变,复古金#C9A24B文字和标签在深色背景上形成温暖对比,营造复古咖啡馆感。

5. 以物换豆交换帖:换豆页每条帖子采用我提供(浅棕背景)⇄我想换(浅金背景)的对称布局,通过swapBadge函数根据点赞数动态生成热度标签(🔥热门/👍热度/🌱新帖),支持线下面交,解决咖啡器具交易中的以物换豆需求。

6. 全局纯函数工具集:10+全局纯函数(catColor/catLabel/rarityLabel/rarityColor/tradeBarHeight/rateColor/6个分类筛选函数/swapBadge),将数据到UI的映射逻辑抽离为纯函数,便于复用和维护,符合函数式编程思想。

7. 分类筛选函数:6个函数(retiredSets/mocSets/figSets/techSets/streetSets/classicSets)通过数组切片返回对应分类的3件器具,cityLeft/cityRight返回全部Tab的双列数据,hotSets返回热门器具,数据筛选逻辑清晰。

8. 详情数据取值方法:14个detailXxx方法(detailName/detailEng/detailEmoji/detailPrice/detailSize/detailRarity/detailYear/detailSeller/detailTag/detailStar/detailRating/detailDesc/detailWant)通过遍历COFFEES数组查找selectedCoffeeId对应的器具字段,在详情弹框中调用,避免在Builder中写复杂的查找逻辑。

9. 步进器价格输入:发布出售弹框的价格采用−/¥价格/+步进器设计,步进10元,通过@State sellPrice控制,价格>2000时动态显示"高价好物建议包邮"提示,交互友好。

10. Stack层叠弹框管理:4种弹框通过Stack层叠+@State布尔值控制,半透明黑色背景99000000,点击背景关闭,弹框内容居中,zIndex自动由Stack层叠顺序决定,无需手动管理zIndex。

十一、同类对比

维度 通用二手咖啡App 本应用(咖啡研究所)
主题 浅色/红色为主 深棕+奶油白复古咖啡馆风
架构 单Tab或简单分类 双Tab嵌套(底部4+顶部6)
动效 无或简单 14个粒子漂浮动效
稀有度 无或简单星级 0-100数值+四级分级+四色映射
以物换豆 无或简单留言 交换帖+我提供⇄我想换对称布局
教程指南 无或外部链接 5套时间轴布局教程+步骤列表
收藏图鉴 无或简单收藏 6套图鉴+进度条+成就墙
求购清单 3件求购+预算+想换+出价中
常去面交点 5个地点+站点说明
弹框 2-3种 4种(发布出售/发起交换/下架/详情)

十二、完整代码结构总览

1306.ets
├── 数据结构(7个interface)
├── 静态配置(PALETTE配色 + 2个Tab配置 + 2个弹框选项 + 2个图表数据)
├── 静态数据(8个数组:18器具+8换豆帖+5教程+9配件周边+6图鉴+3点评)
├── 全局纯函数(10+个:catColor/catLabel/rarityLabel/rarityColor/rateColor/6分类筛选/swapBadge)
├── Index入口组件(@Entry @Component)
│   ├── 状态(currentBottomTab/currentTopTab/selectedCoffeeId/4个弹框显隐/出售表单/交换表单/particles粒子)
│   ├── 生命周期(aboutToAppear生成粒子+启动定时器/aboutToDisappear清理定时器)
│   ├── 粒子方法(buildParticles/driftParticles)
│   ├── 详情取值方法(14个detailXxx)
│   ├── 头部Builder
│   ├── 顶部Tab Builder
│   ├── 6个顶部Tab内容Builder(city/retired/moc/fig/tech/street)
│   ├── 器具首页Builder(bazaarContent)
│   ├── 换豆页Builder(swapContent)
│   ├── 图鉴页Builder(collectionContent)
│   ├── 我的页Builder(mineContent)
│   ├── 底部Tab Builder
│   ├── 4种弹框Builder(sellModal/swapModal/deleteModal/detailModal)
│   ├── 交换多选辅助方法(pickedWant/toggleWant)
│   └── build主布局(Stack层叠+Column内容+粒子+4弹框)

十三、开发环境搭建

13.1 环境要求

DevEco Studio 5.0+(支持HarmonyOS 6.1.1)、HarmonyOS SDK API 24、Node.js 18+、至少8GB内存。

13.2 安装DevEco Studio

从华为开发者官网下载最新版本,运行安装包,选择安装路径,勾选创建桌面快捷方式,点击安装。

13.3 配置SDK

首次启动后进入File > Settings > HarmonyOS SDK,勾选API 24及以上版本,点击Apply等待下载完成。

13.4 创建项目

选择Create Project > Empty Ability,配置项目名称、包名、保存路径,选择Compile SDK为API 24,点击Finish。

13.5 运行应用

将1306.ets代码替换到entry/src/main/ets/pages/Index.ets,连接HarmonyOS设备或启动模拟器,点击运行按钮预览。

13.6 预览调试

DevEco Studio提供实时预览功能,编辑器右侧打开Previewer面板,支持多设备尺寸预览、深色模式切换、组件树inspect等调试能力。

十四、总结展望

本文完整解析了基于HarmonyOS ArkTS API 24的咖啡研究所应用,从深棕+奶油白复古咖啡馆配色系统、双Tab嵌套架构、咖啡器具数据模型(含稀有度0-100/规格/想换物品等特色字段)、器具交易六大品类独立布局(全部双列瀑布/咖啡机横滑大卡+稀有度柱状图+捡漏榜/磨豆机教程时间轴/手冲三列网格/咖啡豆对决卡+侧栏统计/配件标签云+紧凑列表)、换豆以物换物交换帖、收藏图鉴进度条、个人中心求购清单+常去面交点,到4种弹框体系、14粒子漂浮动效、稀有度四级分级、深棕渐变头部、全局纯函数工具集、详情数据取值方法,展现了ArkTS在复古咖啡馆二手交易场景下的完整工程实践。

核心价值:

  1. 深棕+奶油白复古配色:6处linearGradient渐变营造咖啡馆的温暖木质质感
  2. 双Tab嵌套架构:底部4Tab+顶部6Tab,双层分类浏览体验
  3. 14粒子漂浮动效:setInterval驱动emoji粒子漂浮,aboutToDisappear清理定时器
  4. 稀有度四级分级:0-100数值→四级标签→四色映射,数据驱动UI
  5. 以物换豆交换帖:我提供⇄我想换对称布局+热度标签动态生成+线下面交
  6. 收藏图鉴进度管理:6套图鉴+双色进度条+成就墙,收藏系统化
  7. 全局纯函数工具集:10+纯函数抽离数据到UI映射逻辑,代码可维护

未来可扩展:

  • 接入真实咖啡器具数据库,自动识别咖啡机型号和稀有度,替代手动评级
  • 粒子动效接入设备陀螺仪,随手机倾斜方向漂移,增强沉浸感
  • 稀有度接入真实拍卖数据和市场行情,动态更新稀有度评分
  • 以物换豆接入智能匹配算法,根据器具品类和价值推荐交换对象
  • 图鉴接入NFC扫码登记,扫描器具NFC标签自动添加到图鉴
  • 接入咖啡冲煮直播功能,咖啡达人可以直播展示冲煮技巧请圈友在线学习
  • 求购清单接入推送通知,有匹配器具时自动提醒咖友

咖啡研究所,让每一台咖啡机都找到懂它的咖友,让深棕流转、奶油生香。


附录:完整源码

// ============================================================
// 1306.ets - 咖啡研究所
// 场景:咖啡机/磨豆机/手冲壶/咖啡豆/配件的二手交易与收藏社区(咖啡馆风)
// 风格:深棕 #6F4E37 + 奶油白 #F5F0EB 复古咖啡馆风
// 底部Tab:器具 / 换豆 / 图鉴 / 我的
// 顶部Tab:全部 / 咖啡机 / 磨豆机 / 手冲 / 咖啡豆 / 配件
// 弹框:发布出售(新增) / 发起交换(新增) / 下架(删除) / 器具详情(详情)
// ============================================================

// ---------------- 配色 ----------------
interface ColorPalette {
  bg: string;
  card: string;
  brown: string;
  cream: string;
  deep: string;
  red: string;
  gold: string;
  sub: string;
  line: string;
}

const PALETTE: ColorPalette = {
  bg: '#F5F0EB',
  card: '#FFFFFF',
  brown: '#6F4E37',
  cream: '#F5F0EB',
  deep: '#2B2B2B',
  red: '#A83A2E',
  gold: '#C9A24B',
  sub: '#8A7F73',
  line: '#E5DDD5'
};

// ---------------- 通用结构 ----------------
interface TabItem { label: string; icon: string; }
interface ParticleItem { x: number; emoji: string; size: number; speed: number; opacity: number; }

// ---------------- 器具条目 ----------------
interface CoffeeItem {
  id: number; name: string; eng: string; cat: string; price: number;
  rarity: number; year: number; rating: number; starStr: string; seller: string;
  tag: string; desc: string; want: string; sales: number; emoji: string; size: string;
}

// ---------------- 交换帖 ----------------
interface SwapItem {
  id: number; user: string; avatar: string; offer: string; want: string;
  time: string; likes: number; content: string; place: string;
}

// ---------------- 教程/指南 ----------------
interface GuideItem {
  id: number; name: string; type: string; pages: number; author: string;
  likes: number; downloads: number; emoji: string; steps: string[];
}

// ---------------- 配件/周边 ----------------
interface AccessoryItem {
  id: number; name: string; series: string; price: number; condition: string;
  seller: string; emoji: string; desc: string;
}

// ---------------- 图鉴收藏 ----------------
interface CollectionItem {
  id: number; name: string; series: string; owned: number; total: number; rate: number; emoji: string;
}

// ---------------- 点评 ----------------
interface CoffeeReview {
  id: number; user: string; setName: string; starStr: string; content: string;
}

// ---------------- Tab 配置 ----------------
const BOTTOM_TABS: TabItem[] = [
  { label: '器具', icon: '🏪' }, { label: '换豆', icon: '🤝' }, { label: '图鉴', icon: '📖' }, { label: '我的', icon: '☕' }
];

const TOP_TABS: TabItem[] = [
  { label: '全部', icon: '🧿' }, { label: '咖啡机', icon: '☕' }, { label: '磨豆机', icon: '⚙️' },
  { label: '手冲', icon: '🫖' }, { label: '咖啡豆', icon: '🫘' }, { label: '配件', icon: '🥄' }
];

// ---------------- 咖啡器具数据(18款写死) ----------------
const COFFEES: CoffeeItem[] = [
  { id: 1, name: '辣妈 GS3 意式机', eng: 'La Marzocco GS3', cat: 'retired', price: 38000, rarity: 95, year: 2015, rating: 5.0, starStr: '⭐⭐⭐⭐⭐', seller: '意式咖啡控', tag: '商用级', desc: '辣妈GS3双锅炉意式机,家用顶级,蒸汽强劲,萃取完美,意大利原装进口。', want: '换 辣妈Micra', sales: 1, emoji: '☕', size: '双锅炉' },
  { id: 2, name: 'EK43 磨豆机', eng: 'Mahlkonig EK43', cat: 'retired', price: 12000, rarity: 88, year: 2018, rating: 5.0, starStr: '⭐⭐⭐⭐⭐', seller: '研磨专家', tag: '单品磨王', desc: 'EK43磨豆机,研磨均匀度极高,适合单品手冲,配件齐全,成色极佳。', want: '换 意式磨豆机', sales: 2, emoji: '⚙️', size: '定量版' },
  { id: 3, name: '手冲壶 不锈钢', eng: 'Gooseneck Kettle', cat: 'retired', price: 380, rarity: 55, year: 2021, rating: 4.8, starStr: '⭐⭐⭐⭐✨', seller: '手冲爱好者', tag: '控水神器', desc: '专业手冲壶,细口鹅颈,控水精准,温度计一体,适合进阶玩家。', want: '换 智能手冲壶', sales: 12, emoji: '🫖', size: '800ml' },
  { id: 4, name: '手冲咖啡入门教程', eng: 'Pour Over Guide', cat: 'moc', price: 39, rarity: 30, year: 2025, rating: 4.9, starStr: '⭐⭐⭐⭐⭐', seller: '手冲老师', tag: '新手必学', desc: '从水温、研磨度到注水手法,图文并茂,教你手冲一杯完美的咖啡。', want: '可换咖啡豆', sales: 156, emoji: '📖', size: '电子版' },
  { id: 5, name: '拉花教学视频', eng: 'Latte Art Video', cat: 'moc', price: 59, rarity: 40, year: 2024, rating: 4.8, starStr: '⭐⭐⭐⭐✨', seller: '拉花大师', tag: '进阶技法', desc: '心形、树叶、郁金香全套拉花视频教程,讲解细腻,在家也能练。', want: '换 拉花缸', sales: 98, emoji: '🥛', size: '视频版' },
  { id: 6, name: '耶加雪菲 咖啡豆', eng: 'Yirgacheffe Beans', cat: 'moc', price: 88, rarity: 45, year: 2025, rating: 4.9, starStr: '⭐⭐⭐⭐⭐', seller: '生豆烘焙师', tag: '浅度烘焙', desc: '埃塞俄比亚耶加雪菲,柑橘、茉莉花香明显,浅度烘焙,手冲极佳。', want: '换 曼特宁', sales: 210, emoji: '🫘', size: '227g' },
  { id: 7, name: '意式咖啡豆 拼配', eng: 'Espresso Blend', cat: 'fig', price: 98, rarity: 35, year: 2025, rating: 4.8, starStr: '⭐⭐⭐⭐✨', seller: '意式咖啡师', tag: '中深烘焙', desc: '精品意式拼配豆,坚果、可可风味,油脂丰厚,适合制作拿铁。', want: '换 单品豆', sales: 180, emoji: '☕', size: '500g' },
  { id: 8, name: '滤纸 白色', eng: 'Filter Paper', cat: 'fig', price: 25, rarity: 20, year: 2024, rating: 4.7, starStr: '⭐⭐⭐⭐', seller: '手冲耗材', tag: '原木浆', desc: '原木浆滤纸,无异味,透水均匀,适合V60滤杯,一包100张。', want: '换 滤杯', sales: 50, emoji: '📄', size: '100张' },
  { id: 9, name: '拉花缸 300ml', eng: 'Milk Pitcher', cat: 'fig', price: 120, rarity: 30, year: 2023, rating: 4.8, starStr: '⭐⭐⭐⭐✨', seller: '拉花爱好者', tag: '尖嘴设计', desc: '不锈钢拉花缸,尖嘴设计,打奶泡细腻,拉花更容易,手感极佳。', want: '换 温度计', sales: 30, emoji: '🥛', size: '300ml' },
  { id: 10, name: '聪明杯 滤杯', eng: 'Clever Dripper', cat: 'tech', price: 65, rarity: 35, year: 2022, rating: 4.9, starStr: '⭐⭐⭐⭐⭐', seller: '懒人咖啡', tag: '浸泡式', desc: '聪明杯,浸泡式萃取,操作简单,适合新手,出来的咖啡很稳定。', want: '换 V60', sales: 45, emoji: '🫖', size: '350ml' },
  { id: 11, name: '法压壶 800ml', eng: 'French Press', cat: 'tech', price: 95, rarity: 25, year: 2023, rating: 4.6, starStr: '⭐⭐⭐⭐', seller: '咖啡器具店', tag: '经典耐造', desc: '法压壶,玻璃壶身,金属滤网,简单易用,适合做冷萃和热咖啡。', want: '换 分享壶', sales: 35, emoji: '🫙', size: '800ml' },
  { id: 12, name: '摩卡壶 6杯份', eng: 'Moka Pot', cat: 'tech', price: 180, rarity: 30, year: 2021, rating: 4.7, starStr: '⭐⭐⭐⭐', seller: '意式情怀党', tag: '铝制经典', desc: '意大利经典铝制摩卡壶,能煮出浓郁的意式咖啡,适合家用。', want: '换 手冲壶', sales: 25, emoji: '♨️', size: '6杯份' },
  { id: 13, name: '咖啡分享壶', eng: 'Server', cat: 'street', price: 75, rarity: 20, year: 2024, rating: 4.7, starStr: '⭐⭐⭐⭐', seller: '咖啡器具店', tag: '耐热玻璃', desc: '耐热玻璃分享壶,刻度清晰,保温效果不错,适合手冲咖啡。', want: '换 滤纸', sales: 40, emoji: '🧪', size: '600ml' },
  { id: 14, name: '意式咖啡杯 套装', eng: 'Espresso Cup Set', cat: 'street', price: 150, rarity: 30, year: 2023, rating: 4.8, starStr: '⭐⭐⭐⭐✨', seller: '咖啡杯收藏', tag: '陶瓷厚壁', desc: '经典意式咖啡杯,陶瓷厚壁,保温性好,手柄舒适,一套6只。', want: '换 拿铁杯', sales: 20, emoji: '🍵', size: '60ml' },
  { id: 15, name: '黄油曲奇', eng: 'Butter Cookies', cat: 'street', price: 35, rarity: 15, year: 2025, rating: 4.6, starStr: '⭐⭐⭐⭐', seller: '烘焙师', tag: '下午茶', desc: '手工黄油曲奇,奶香浓郁,酥脆可口,配咖啡完美。', want: '换 挂耳包', sales: 60, emoji: '🍪', size: '8块' },
  { id: 16, name: '挂耳咖啡 10包', eng: 'Drip Bag Coffee', cat: 'classic', price: 58, rarity: 20, year: 2025, rating: 4.7, starStr: '⭐⭐⭐⭐', seller: '挂耳专家', tag: '便携装', desc: '精品挂耳咖啡,10包混合装,方便携带,开水一冲即饮。', want: '换 咖啡豆', sales: 80, emoji: '☕', size: '10包' },
  { id: 17, name: '温度计 探针式', eng: 'Thermometer', cat: 'classic', price: 45, rarity: 15, year: 2024, rating: 4.5, starStr: '⭐⭐⭐⭐', seller: '手冲工具', tag: '精准测温', desc: '探针式温度计,测量精准,反应灵敏,适合手冲控温。', want: '换 计时器', sales: 30, emoji: '🌡️', size: '标准' },
  { id: 18, name: '咖啡勺', eng: 'Coffee Scoop', cat: 'classic', price: 20, rarity: 10, year: 2024, rating: 4.5, starStr: '⭐⭐⭐⭐', seller: '咖啡周边', tag: '不锈钢', desc: '不锈钢咖啡勺,一杯一勺,精准计量,咖啡玩家必备。', want: '换 滤纸', sales: 50, emoji: '🥄', size: '10g' }
];

// ---------------- 交换帖数据(8条写死) ----------------
const SWAPS: SwapItem[] = [
  { id: 1, user: '意式咖啡控', avatar: '☕', offer: '辣妈GS3', want: '辣妈Micra', time: '今天 13:20', likes: 46, content: 'GS3换Micra,都是顶级意式机,成色好,可当面验货,补差价。', place: '光谷广场站' },
  { id: 2, user: '研磨专家', avatar: '⚙️', offer: 'EK43磨豆机', want: '意式磨豆机', time: '今天 11:05', likes: 38, content: 'EK43换意式磨豆机,可补差价,都是顶级设备,诚心换。', place: '江汉路站' },
  { id: 3, user: '手冲爱好者', avatar: '🫖', offer: '手冲壶', want: '智能手冲壶', time: '昨天 18:42', likes: 61, content: '不锈钢手冲壶换智能控温壶,补差价换,诚心换装备。', place: '洪山广场站' },
  { id: 4, user: '手冲老师', avatar: '📖', offer: '手冲教程', want: '任意咖啡豆', time: '昨天 15:30', likes: 29, content: '电子版教程免费换,换取任意有缘的咖啡豆,学生党优先。', place: '线上交换' },
  { id: 5, user: '拉花大师', avatar: '🥛', offer: '拉花缸', want: '温度计', time: '前天 20:15', likes: 22, content: '拉花缸换温度计,都是手冲必备,可补差价,诚心换。', place: '楚河汉街站' },
  { id: 6, user: '咖啡器具店', avatar: '🫙', offer: '法压壶', want: '聪明杯', time: '前天 09:50', likes: 41, content: '法压壶换聪明杯,都是好器具,补差价换,可聊聊看。', place: '线上互换' },
  { id: 7, user: '咖啡杯收藏', avatar: '🍵', offer: '意式咖啡杯', want: '拿铁杯', time: '3天前 22:08', likes: 55, content: '咖啡杯套装换拿铁杯,都是好杯,愿补差价,面交优先。', place: '武昌火车站' },
  { id: 8, user: '挂耳专家', avatar: '🫘', offer: '挂耳包', want: '咖啡豆', time: '4天前 14:26', likes: 17, content: '挂耳咖啡换咖啡豆,都是好豆子,学生党不容易,大家帮帮忙。', place: '线上交换' }
];

// ---------------- 教程/指南数据(5条写死) ----------------
const GUIDES: GuideItem[] = [
  { id: 1, name: '手冲咖啡入门', type: '手冲', pages: 20, author: '手冲老师', likes: 342, downloads: 2100, emoji: '📖', steps: ['水温控制 1-5页', '研磨度调整 6-12页', '注水手法教学 13-20页'] },
  { id: 2, name: '意式浓缩萃取指南', type: '意式', pages: 25, author: '意式咖啡师', likes: 276, downloads: 1580, emoji: '☕', steps: ['粉量选择 1-8页', '压粉技巧 9-16页', '萃取时间调整 17-25页'] },
  { id: 3, name: '拉花进阶技巧', type: '拉花', pages: 30, author: '拉花大师', likes: 415, downloads: 3300, emoji: '🥛', steps: ['奶泡打发要点 1-10页', '心形拉花 11-20页', '郁金香拉花 21-30页'] },
  { id: 4, name: '咖啡豆烘焙指南', type: '烘焙', pages: 45, author: '生豆烘焙师', likes: 198, downloads: 1260, emoji: '🫘', steps: ['生豆选择 1-15页', '烘焙曲线控制 16-30页', '养豆与保存 31-45页'] },
  { id: 5, name: '冷萃咖啡做法', type: '冷萃', pages: 12, author: '懒人咖啡', likes: 233, downloads: 1420, emoji: '🧊', steps: ['粉水比例 1-4页', '冷萃时间控制 5-8页', '过滤与保存 9-12页'] }
];

// ---------------- 配件/周边数据(9条写死) ----------------
const ACCESSORIES: AccessoryItem[] = [
  { id: 1, name: '咖啡杯', series: '陶瓷', price: 45, condition: '全新', seller: '咖啡器具店', emoji: '🍵', desc: '简约白瓷,保温好' },
  { id: 2, name: '奶泡壶', series: '手动', price: 60, condition: '9成新', seller: '拉花控', emoji: '🥛', desc: '手动打奶泡,细腻好用' },
  { id: 3, name: '咖啡豆密封罐', series: '玻璃', price: 50, condition: '全新', seller: '豆子专家', emoji: '🫙', desc: '避光防潮,保持新鲜' },
  { id: 4, name: 'V60滤杯', series: '树脂', price: 38, condition: '全新', seller: '手冲控', emoji: '🫖', desc: '经典圆锥形,快冲萃取' },
  { id: 5, name: '计时器', series: '电子', price: 25, condition: '9成新', seller: '咖啡工具', emoji: '⏱️', desc: '精准计时,适合手冲' },
  { id: 6, name: '咖啡勺', series: '不锈钢', price: 20, condition: '全新', seller: '咖啡周边', emoji: '🥄', desc: '精准计量,耐用' },
  { id: 7, name: '擦布', series: '超细纤维', price: 15, condition: '全新', seller: '咖啡吧台', emoji: '🧽', desc: '吸水性强,不伤器具' },
  { id: 8, name: '咖啡桌垫', series: '毛毡', price: 30, condition: '全新', seller: '咖啡周边', emoji: '🟫', desc: '防滑隔热,装饰吧台' },
  { id: 9, name: '便携咖啡磨豆机', series: '手摇', price: 150, condition: '9成新', seller: '便携党', emoji: '⚙️', desc: '轻巧便携,外出可用' }
];

// ---------------- 图鉴收藏数据(6条写死) ----------------
const COLLECTIONS: CollectionItem[] = [
  { id: 1, name: '意式咖啡机', series: 'Espresso', owned: 3, total: 10, rate: 30, emoji: '☕' },
  { id: 2, name: '手冲器具', series: 'Pour Over', owned: 8, total: 20, rate: 40, emoji: '🫖' },
  { id: 3, name: '磨豆机', series: 'Grinder', owned: 4, total: 12, rate: 33, emoji: '⚙️' },
  { id: 4, name: '咖啡豆', series: 'Beans', owned: 15, total: 30, rate: 50, emoji: '🫘' },
  { id: 5, name: '咖啡杯', series: 'Cups', owned: 6, total: 15, rate: 40, emoji: '🍵' },
  { id: 6, name: '咖啡配件', series: 'Accessories', owned: 8, total: 10, rate: 80, emoji: '🥄' }
];

// ---------------- 点评数据(3条写死) ----------------
const COFFEE_REVIEWS: CoffeeReview[] = [
  { id: 1, user: '咖啡发烧友', setName: '辣妈GS3', starStr: '⭐⭐⭐⭐⭐', content: '商用级性能,蒸汽强劲,油脂萃取完美,意式咖啡的终极目标。' },
  { id: 2, user: '手冲爱好者', setName: '手冲教程', starStr: '⭐⭐⭐⭐✨', content: '讲解非常细腻,对新手极其友好,跟着做就能冲出一杯好咖啡。' },
  { id: 3, user: '豆子控', setName: '耶加雪菲', starStr: '⭐⭐⭐⭐⭐', content: '香气扑鼻,柑橘和茉莉花香太迷人了,手冲出来的口感很干净。' }
];

// ---------------- 全城周交易量图表数据 ----------------
const TRADE_LABELS: string[] = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
const TRADE_BARS: number[] = [76, 64, 72, 58, 112, 156, 148];

// ---------------- 弹框选项 ----------------
const CONDITION_OPTIONS: string[] = ['全新未拆', '仅拆封', '9成新', '8成新', '有瑕疵'];
const PLACES: string[] = ['光谷广场站', '江汉路站', '洪山广场站', '楚河汉街站', '线上互换'];

// ---------------- 全局纯函数 ----------------
function catColor(cat: string): string {
  if (cat === 'retired') return '#A83A2E';
  if (cat === 'moc') return '#7C4DFF';
  if (cat === 'fig') return '#6F4E37';
  if (cat === 'tech') return '#C9A24B';
  if (cat === 'street') return '#1E88E5';
  return '#2B2B2B';
}

function catLabel(cat: string): string {
  if (cat === 'retired') return '顶级收藏';
  if (cat === 'moc') return '咖啡教程';
  if (cat === 'fig') return '精选豆子';
  if (cat === 'tech') return '实用器具';
  if (cat === 'street') return '风味好物';
  return '咖啡周边';
}

function rarityLabel(r: number): string {
  if (r >= 95) return '传世级';
  if (r >= 80) return '稀有级';
  if (r >= 60) return '紧俏级';
  return '流通级';
}

function rarityColor(r: number): string {
  if (r >= 95) return '#A83A2E';
  if (r >= 80) return '#FF8A00';
  if (r >= 60) return '#1E88E5';
  return '#6F4E37';
}

function tradeBarHeight(v: number): string {
  return Math.round(v / 156 * 96).toString() + 'vp';
}

function rateColor(r: number): string {
  if (r >= 55) return '#6F4E37';
  if (r >= 40) return '#C9A24B';
  return '#A83A2E';
}

function retiredSets(): CoffeeItem[] { return [COFFEES[0], COFFEES[1], COFFEES[2]]; }
function mocSets(): CoffeeItem[] { return [COFFEES[3], COFFEES[4], COFFEES[5]]; }
function figSets(): CoffeeItem[] { return [COFFEES[6], COFFEES[7], COFFEES[8]]; }
function techSets(): CoffeeItem[] { return [COFFEES[9], COFFEES[10], COFFEES[11]]; }
function streetSets(): CoffeeItem[] { return [COFFEES[12], COFFEES[13], COFFEES[14]]; }
function classicSets(): CoffeeItem[] { return [COFFEES[15], COFFEES[16], COFFEES[17]]; }
function cityLeft(): CoffeeItem[] { return [COFFEES[16], COFFEES[17]]; }
function cityRight(): CoffeeItem[] { return [COFFEES[15]]; }
function hotSets(): CoffeeItem[] { return [COFFEES[3], COFFEES[5], COFFEES[16], COFFEES[17], COFFEES[9]]; }

function swapBadge(likes: number): string {
  if (likes >= 50) return '🔥热门';
  if (likes >= 30) return '👍热度';
  return '🌱新帖';
}

// ============================================================
// 主页面
// ============================================================
@Entry
@Component
struct Index {
  @State currentBottomTab: number = 0;
  @State currentTopTab: number = 0;
  @State selectedCoffeeId: number = 1;
  @State showSellModal: boolean = false;
  @State showSwapModal: boolean = false;
  @State showDeleteModal: boolean = false;
  @State showDetailModal: boolean = false;
  @State sellCoffeeIdx: number = 0;
  @State sellCondIdx: number = 2;
  @State sellPrice: number = 100;
  @State sellWords: string = '';
  @State swapOfferIdx: number = 0;
  @State swapWantIdx: number = 1;
  @State swapPlaceIdx: number = 0;
  @State particles: ParticleItem[] = [];
  private timerId: number = -1;

  aboutToAppear(): void {
    this.buildParticles();
    this.timerId = setInterval(() => { this.driftParticles(); }, 80);
  }

  aboutToDisappear(): void {
    if (this.timerId !== -1) { clearInterval(this.timerId); this.timerId = -1; }
  }

  buildParticles(): void {
    const emojis: string[] = ['☕', '🫘', '🫖', '⚙️', '🥄'];
    const list: ParticleItem[] = [];
    for (let i = 0; i < 14; i++) {
      list.push({ x: Math.random() * 340, emoji: emojis[i % emojis.length], size: 12 + Math.random() * 10, speed: 0.6 + Math.random() * 1.4, opacity: 0.2 + Math.random() * 0.4 });
    }
    this.particles = list;
  }

  driftParticles(): void {
    for (let i = 0; i < this.particles.length; i++) {
      this.particles[i].x += this.particles[i].speed;
      if (this.particles[i].x > 360) { this.particles[i].x = -20; }
    }
    this.particles = this.particles.slice();
  }

  // 详情弹框数据取值
  detailName(): string { for (let i = 0; i < COFFEES.length; i++) if (COFFEES[i].id === this.selectedCoffeeId) return COFFEES[i].name; return ''; }
  detailEng(): string { for (let i = 0; i < COFFEES.length; i++) if (COFFEES[i].id === this.selectedCoffeeId) return COFFEES[i].eng; return ''; }
  detailEmoji(): string { for (let i = 0; i < COFFEES.length; i++) if (COFFEES[i].id === this.selectedCoffeeId) return COFFEES[i].emoji; return '☕'; }
  detailPrice(): string { for (let i = 0; i < COFFEES.length; i++) if (COFFEES[i].id === this.selectedCoffeeId) return '¥' + COFFEES[i].price.toString(); return ''; }
  detailSize(): string { for (let i = 0; i < COFFEES.length; i++) if (COFFEES[i].id === this.selectedCoffeeId) return COFFEES[i].size; return ''; }
  detailRarity(): string { for (let i = 0; i < COFFEES.length; i++) if (COFFEES[i].id === this.selectedCoffeeId) return COFFEES[i].rarity.toString(); return ''; }
  detailYear(): string { for (let i = 0; i < COFFEES.length; i++) if (COFFEES[i].id === this.selectedCoffeeId) return COFFEES[i].year.toString(); return ''; }
  detailSeller(): string { for (let i = 0; i < COFFEES.length; i++) if (COFFEES[i].id === this.selectedCoffeeId) return COFFEES[i].seller; return ''; }
  detailTag(): string { for (let i = 0; i < COFFEES.length; i++) if (COFFEES[i].id === this.selectedCoffeeId) return COFFEES[i].tag; return ''; }
  detailStar(): string { for (let i = 0; i < COFFEES.length; i++) if (COFFEES[i].id === this.selectedCoffeeId) return COFFEES[i].starStr; return ''; }
  detailRating(): string { for (let i = 0; i < COFFEES.length; i++) if (COFFEES[i].id === this.selectedCoffeeId) return COFFEES[i].rating.toFixed(1) + '分'; return ''; }
  detailDesc(): string { for (let i = 0; i < COFFEES.length; i++) if (COFFEES[i].id === this.selectedCoffeeId) return COFFEES[i].desc; return ''; }
  detailWant(): string { for (let i = 0; i < COFFEES.length; i++) if (COFFEES[i].id === this.selectedCoffeeId) return COFFEES[i].want; return ''; }

  // ---------------- 头部 ----------------
  @Builder
  headerBuilder() {
    Column() {
      Row() {
        Column() {
          Text('☕ 咖啡研究所').fontSize(22).fontWeight(FontWeight.Bold).fontColor('#FFFFFF')
          Text('淘器具 · 品好豆 · 换心头好').fontSize(11).fontColor('#E3D5C8').margin({ top: 3 })
        }.alignItems(HorizontalAlign.Start).layoutWeight(1)
        Row({ space: 14 }) {
          Text('🔎').fontSize(20)
          Text('📦').fontSize(20)
          Column() {
            Text('🎒').fontSize(20)
            Text('3').fontSize(9).fontColor('#6F4E37').backgroundColor('#FFFFFF').borderRadius(6).padding({ left: 4, right: 4 }).margin({ top: -6 })
          }.alignItems(HorizontalAlign.End)
        }
      }.width('100%').padding({ left: 16, right: 16, top: 14, bottom: 10 })

      Row({ space: 8 }) {
        Text('🔍 搜索:咖啡机 / 豆子 / 手冲').fontSize(12).fontColor('#D0BFB0').layoutWeight(1)
        Text('🧰 我的求购').fontSize(11).fontColor('#6F4E37').backgroundColor('#FFFFFF').borderRadius(10).padding({ left: 10, right: 10, top: 4, bottom: 4 })
      }.width('100%').padding({ left: 16, right: 16, bottom: 12 })
    }
    .linearGradient({ angle: 135, colors: [['#6F4E37', 0], ['#2B2B2B', 1]] })
    .borderRadius({ bottomLeft: 18, bottomRight: 18 })
  }

  // ---------------- 顶部Tab ----------------
  @Builder
  topTabsBuilder() {
    Row() {
      ForEach(TOP_TABS, (t: TabItem, idx: number) => {
        Column() {
          Text(t.icon).fontSize(17)
          Text(t.label).fontSize(12).fontColor(this.currentTopTab === idx ? '#6F4E37' : '#8A7F73').fontWeight(this.currentTopTab === idx ? FontWeight.Bold : FontWeight.Normal).margin({ top: 2 })
          if (this.currentTopTab === idx) {
            Divider().width(16).strokeWidth(3).color('#C9A24B').borderRadius(2).margin({ top: 4 })
          } else {
            Divider().width(16).strokeWidth(3).color('#F5F0EB').borderRadius(2).margin({ top: 4 })
          }
        }.layoutWeight(1).alignItems(HorizontalAlign.Center).padding({ top: 10, bottom: 6 }).onClick(() => { this.currentTopTab = idx; })
      }, (t: TabItem, idx: number) => 'top' + idx.toString() + t.label)
    }.width('100%').backgroundColor('#FFFFFF').borderRadius(14).margin({ top: 10 })
  }

  // ---------------- 全部Tab:banner + 双列瀑布 ----------------
  @Builder
  cityContent() {
    Column({ space: 12 }) {
      Column() {
        Text('☕ 咖啡节 · 春季专场').fontSize(18).fontWeight(FontWeight.Bold).fontColor('#2B2B2B')
        Text('顶级器具 3天限时 · 卖家免服务费').fontSize(12).fontColor('#5B6B7F').margin({ top: 6 })
        Row({ space: 10 }) {
          Text('🏆 顶级意式机').fontSize(11).fontColor('#2B2B2B').backgroundColor('#FFFFFF').borderRadius(12).padding({ left: 10, right: 10, top: 4, bottom: 4 })
          Text('🫘 精品咖啡豆').fontSize(11).fontColor('#2B2B2B').backgroundColor('#FFFFFF').borderRadius(12).padding({ left: 10, right: 10, top: 4, bottom: 4 })
        }.margin({ top: 10 })
      }.width('100%').padding(18).alignItems(HorizontalAlign.Start).linearGradient({ angle: 120, colors: [['#C9A24B', 0], ['#E2CE94', 1]] }).borderRadius(16)

      Text('✨ 全城新上架').fontSize(15).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').width('100%')

      Row({ space: 10 }) {
        Column({ space: 10 }) {
          ForEach(cityLeft(), (t: CoffeeItem) => {
            Column() {
              Text(t.emoji).fontSize(34).margin({ top: 16 })
              Text(t.name).fontSize(14).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').margin({ top: 8 })
              Text(t.eng).fontSize(9).fontColor('#8A7F73').margin({ top: 2 })
              Text(t.desc).fontSize(11).fontColor('#8A7F73').margin({ top: 8 }).maxLines(3).textOverflow({ overflow: TextOverflow.Ellipsis })
              Row({ space: 6 }) {
                Text('¥' + t.price.toString()).fontSize(15).fontWeight(FontWeight.Bold).fontColor('#A83A2E')
                Text(t.tag).fontSize(9).fontColor('#6F4E37').backgroundColor('#F0E9E2').borderRadius(8).padding({ left: 6, right: 6, top: 2, bottom: 2 })
              }.margin({ top: 10, bottom: 14 })
            }.width('100%').backgroundColor('#FFFFFF').borderRadius(14).alignItems(HorizontalAlign.Start).padding({ left: 14, right: 14 })
            .onClick(() => { this.selectedCoffeeId = t.id; this.showDetailModal = true; })
          }, (t: CoffeeItem) => 'cl' + t.id.toString())
        }.layoutWeight(1)

        Column({ space: 10 }) {
          ForEach(cityRight(), (t: CoffeeItem) => {
            Column() {
              Text(t.emoji).fontSize(34).margin({ top: 24 })
              Text(t.name).fontSize(14).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').margin({ top: 8 })
              Text(t.eng).fontSize(9).fontColor('#8A7F73').margin({ top: 2 })
              Text(t.desc).fontSize(11).fontColor('#8A7F73').margin({ top: 10 }).maxLines(4).textOverflow({ overflow: TextOverflow.Ellipsis })
              Row({ space: 6 }) {
                Text('¥' + t.price.toString()).fontSize(15).fontWeight(FontWeight.Bold).fontColor('#A83A2E')
                Text(t.starStr).fontSize(10)
              }.margin({ top: 12, bottom: 20 })
            }.width('100%').backgroundColor('#F0E9E2').borderRadius(14).alignItems(HorizontalAlign.Start).padding({ left: 14, right: 14 })
            .onClick(() => { this.selectedCoffeeId = t.id; this.showDetailModal = true; })
          }, (t: CoffeeItem) => 'cr' + t.id.toString())
        }.layoutWeight(1)
      }.width('100%').alignItems(VerticalAlign.Top)
    }.width('100%').padding({ left: 14, right: 14, top: 10, bottom: 16 })
  }

  // ---------------- 咖啡机Tab:横滑大卡 + 稀有度柱状图 + 捡漏榜 ----------------
  @Builder
  retiredContent() {
    Column({ space: 14 }) {
      Text('🏆 顶级意式机专区').fontSize(15).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').width('100%')
      Scroll() {
        Row({ space: 12 }) {
          ForEach(retiredSets(), (t: CoffeeItem) => {
            Column() {
              Text(t.emoji).fontSize(40).margin({ top: 18 })
              Text(t.name).fontSize(15).fontWeight(FontWeight.Bold).fontColor('#FFFFFF').margin({ top: 8 })
              Text(t.eng + ' · ' + t.year.toString() + '年').fontSize(9).fontColor('#E3D5C8').margin({ top: 4 })
              Row({ space: 6 }) {
                Text('¥' + t.price.toString()).fontSize(16).fontWeight(FontWeight.Bold).fontColor('#E2CE94')
                Text(rarityLabel(t.rarity)).fontSize(10).fontColor('#2B2B2B').backgroundColor('#C9A24B').borderRadius(8).padding({ left: 6, right: 6, top: 2, bottom: 2 })
              }.margin({ top: 10, bottom: 18 })
            }.width(160).linearGradient({ angle: 160, colors: [['#6F4E37', 0], ['#2B2B2B', 1]] }).borderRadius(16).alignItems(HorizontalAlign.Center)
            .onClick(() => { this.selectedCoffeeId = t.id; this.showDetailModal = true; })
          }, (t: CoffeeItem) => 'rt' + t.id.toString())
        }.padding({ left: 14, right: 14 })
      }.scrollable(ScrollDirection.Horizontal).scrollBar(BarState.Off).width('100%')

      Column() {
        Text('📈 稀有度排行').fontSize(13).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').width('100%')
        Row({ space: 6 }) {
          ForEach(retiredSets(), (t: CoffeeItem, i: number) => {
            Column({ space: 5 }) {
              Text(t.rarity.toString()).fontSize(9).fontColor('#6F4E37')
              Column().width(34).height((t.rarity).toString() + 'vp').backgroundColor(i === 2 ? '#C9A24B' : '#6F4E37').borderRadius({ topLeft: 5, topRight: 5 })
              Text(t.year.toString() + '年').fontSize(9).fontColor('#8A7F73')
            }.alignItems(HorizontalAlign.Center).layoutWeight(1)
          }, (t: CoffeeItem, i: number) => 'rb' + i.toString() + t.id.toString())
        }.width('100%').justifyContent(FlexAlign.Center).alignItems(VerticalAlign.Bottom).padding({ top: 12 })
        Text('辣妈GS3以95分稀有度登顶,市面流通不足十台').fontSize(10).fontColor('#8A7F73').margin({ top: 8 })
      }.width('100%').backgroundColor('#FFFFFF').borderRadius(14).padding(14).margin({ left: 14, right: 14 })

      Column() {
        Text('🕵️ 今日捡漏榜').fontSize(14).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').width('100%').margin({ bottom: 8 })
        ForEach(retiredSets(), (t: CoffeeItem, i: number) => {
          Row({ space: 10 }) {
            Text((i + 1).toString()).fontSize(14).fontWeight(FontWeight.Bold).fontColor(i === 0 ? '#A83A2E' : '#8A7F73').width(20)
            Text(t.emoji).fontSize(18)
            Column() {
              Text(t.name).fontSize(13).fontWeight(FontWeight.Bold).fontColor('#2B2B2B')
              Text(rarityLabel(t.rarity) + ' · ' + t.want).fontSize(10).fontColor('#8A7F73').margin({ top: 2 })
            }.alignItems(HorizontalAlign.Start).layoutWeight(1)
            Text('¥' + t.price.toString()).fontSize(13).fontWeight(FontWeight.Bold).fontColor('#A83A2E')
          }.width('100%').padding({ top: 8, bottom: 8 }).onClick(() => { this.selectedCoffeeId = t.id; this.showDetailModal = true; })
        }, (t: CoffeeItem, i: number) => 'pck' + i.toString() + t.id.toString())
      }.width('100%').backgroundColor('#FFFFFF').borderRadius(14).padding(14).margin({ left: 14, right: 14, bottom: 16 })
    }.width('100%').alignItems(HorizontalAlign.Start).padding({ top: 10 })
  }

  // ---------------- 磨豆机Tab:教程时间轴 ----------------
  @Builder
  mocContent() {
    Column({ space: 0 }) {
      Text('📖 咖啡教程 · 冲煮指南').fontSize(15).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').width('100%').margin({ top: 10, bottom: 12 })
      ForEach(GUIDES, (m: GuideItem, i: number) => {
        Row({ space: 12 }) {
          Column({ space: 4 }) {
            Text(m.emoji).fontSize(20)
            if (i < GUIDES.length - 1) {
              Column().width(2).layoutWeight(1).backgroundColor('#C9A24B').margin({ top: 4 })
            }
          }.width(40).alignItems(HorizontalAlign.Center)
          Column({ space: 6 }) {
            Row({ space: 8 }) {
              Text(m.name).fontSize(14).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').layoutWeight(1)
              Text('⬇ ' + m.downloads.toString()).fontSize(10).fontColor('#6F4E37')
            }.width('100%')
            Text('by ' + m.author + ' · ' + m.type + ' · ' + m.pages.toString() + '页').fontSize(10).fontColor('#8A7F73')
            Column({ space: 5 }) {
              ForEach(m.steps, (s: string, si: number) => {
                Row({ space: 8 }) {
                  Text((si + 1).toString()).fontSize(9).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').backgroundColor('#F5F0EB').borderRadius(9).width(18).height(18).textAlign(TextAlign.Center)
                  Text(s).fontSize(10).fontColor('#4A5A6E').layoutWeight(1)
                }.width('100%')
              }, (s: string, si: number) => 'ms' + si.toString() + m.id.toString())
            }.width('100%').backgroundColor('#F5F0EB').borderRadius(10).padding(10)
            Row({ space: 8 }) {
              Text('❤️ ' + m.likes.toString()).fontSize(10).fontColor('#A83A2E')
              Text(m.emoji + ' 教程可下载').fontSize(10).fontColor('#6F4E37').layoutWeight(1).textAlign(TextAlign.End)
            }.width('100%')
          }.layoutWeight(1).backgroundColor(i === 1 ? '#F0E9E2' : '#FFFFFF').borderRadius(14).padding(12).margin({ bottom: 14 })
        }.width('100%').alignItems(VerticalAlign.Top)
      }, (m: GuideItem, i: number) => 'moc' + i.toString() + m.id.toString())
    }.width('100%').padding({ left: 14, right: 14, bottom: 16 })
  }

  // ---------------- 手冲Tab:三列格子 + 交换建议 ----------------
  @Builder
  figContent() {
    Column({ space: 14 }) {
      Text('🫖 手冲器具 · 完美萃取').fontSize(15).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').width('100%')
      Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.SpaceBetween }) {
        ForEach(ACCESSORIES, (f: AccessoryItem) => {
          Column() {
            Text(f.emoji).fontSize(26).margin({ top: 12 })
            Text(f.name).fontSize(11).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').margin({ top: 6 }).maxLines(1)
            Text(f.series).fontSize(8).fontColor('#8A7F73').margin({ top: 2 })
            Text('¥' + f.price.toString()).fontSize(13).fontWeight(FontWeight.Bold).fontColor('#A83A2E').margin({ top: 4, bottom: 12 })
          }.width('31%').backgroundColor('#FFFFFF').borderRadius(12).alignItems(HorizontalAlign.Center).margin({ bottom: 8 })
          .onClick(() => { this.selectedCoffeeId = 7 + f.id % 3; this.showDetailModal = true; })
        }, (f: AccessoryItem) => 'fg' + f.id.toString())
      }.width('100%')

      Column() {
        Text('🤝 配件交换行情').fontSize(14).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').width('100%').margin({ bottom: 6 })
        ForEach(ACCESSORIES, (f: AccessoryItem) => {
          if (f.id % 2 === 0) {
            Row({ space: 10 }) {
              Text(f.emoji).fontSize(20)
              Column({ space: 2 }) {
                Text(f.name + ' · ' + f.condition).fontSize(12).fontWeight(FontWeight.Bold).fontColor('#2B2B2B')
                Text(f.desc).fontSize(10).fontColor('#8A7F73')
              }.alignItems(HorizontalAlign.Start).layoutWeight(1)
              Text('¥' + f.price.toString()).fontSize(12).fontWeight(FontWeight.Bold).fontColor('#6F4E37')
            }.width('100%').padding({ top: 8, bottom: 8 })
          }
        }, (f: AccessoryItem) => 'fgx' + f.id.toString())
      }.width('100%').backgroundColor('#FFFFFF').borderRadius(14).padding(14)
    }.width('100%').padding({ left: 14, right: 14, top: 10, bottom: 16 })
  }

  // ---------------- 咖啡豆Tab:对决卡 + 侧栏统计 ----------------
  @Builder
  techContent() {
    Column({ space: 14 }) {
      Text('🫘 豆子对决 · 风味之争').fontSize(15).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').width('100%')
      Row({ space: 10 }) {
        Column() {
          Text('☕').fontSize(34).margin({ top: 14 })
          Text('辣妈GS3').fontSize(13).fontWeight(FontWeight.Bold).fontColor('#FFFFFF').margin({ top: 6 })
          Text('2015年 · 现价¥38000').fontSize(10).fontColor('#E3D5C8').margin({ top: 4, bottom: 14 })
        }.layoutWeight(1).linearGradient({ angle: 150, colors: [['#6F4E37', 0], ['#2B2B2B', 1]] }).borderRadius(14).alignItems(HorizontalAlign.Center)
        Column() {
          Text('VS').fontSize(18).fontWeight(FontWeight.Bold).fontColor('#6F4E37')
          Text('本周').fontSize(9).fontColor('#8A7F73')
        }.alignItems(HorizontalAlign.Center)
        Column() {
          Text('🫘').fontSize(34).margin({ top: 14 })
          Text('耶加雪菲').fontSize(13).fontWeight(FontWeight.Bold).fontColor('#FFFFFF').margin({ top: 6 })
          Text('2025年 · 现价¥88').fontSize(10).fontColor('#E3D5C8').margin({ top: 4, bottom: 14 })
        }.layoutWeight(1).linearGradient({ angle: 150, colors: [['#8A7F73', 0], ['#2B2B2B', 1]] }).borderRadius(14).alignItems(HorizontalAlign.Center)
      }.width('100%')

      Row({ space: 10 }) {
        Column({ space: 10 }) {
          ForEach(techSets(), (t: CoffeeItem) => {
            Row({ space: 10 }) {
              Text(t.emoji).fontSize(22)
              Column({ space: 2 }) {
                Text(t.name).fontSize(12).fontWeight(FontWeight.Bold).fontColor('#2B2B2B')
                Text(t.tag + ' · ' + t.size).fontSize(10).fontColor('#8A7F73')
              }.alignItems(HorizontalAlign.Start).layoutWeight(1)
              Text('¥' + t.price.toString()).fontSize(12).fontWeight(FontWeight.Bold).fontColor('#A83A2E')
            }.width('100%').backgroundColor('#FFFFFF').borderRadius(12).padding(12).onClick(() => { this.selectedCoffeeId = t.id; this.showDetailModal = true; })
          }, (t: CoffeeItem) => 'tc' + t.id.toString())
        }.layoutWeight(1)
        Column({ space: 10 }) {
          Column({ space: 4 }) {
            Text('12').fontSize(18).fontWeight(FontWeight.Bold).fontColor('#6F4E37')
            Text('本周器具成交').fontSize(9).fontColor('#8A7F73')
          }.width('100%').backgroundColor('#F0E9E2').borderRadius(12).padding({ top: 10, bottom: 10 }).alignItems(HorizontalAlign.Center)
          Column({ space: 4 }) {
            Text('1.2万').fontSize(18).fontWeight(FontWeight.Bold).fontColor('#C9A24B')
            Text('器具总交易额').fontSize(9).fontColor('#8A7F73')
          }.width('100%').backgroundColor('#F5EFE0').borderRadius(12).padding({ top: 10, bottom: 10 }).alignItems(HorizontalAlign.Center)
          Column({ space: 4 }) {
            Text('96%').fontSize(18).fontWeight(FontWeight.Bold).fontColor('#6F4E37')
            Text('好评率').fontSize(9).fontColor('#8A7F73')
          }.width('100%').backgroundColor('#FFFFFF').borderRadius(12).padding({ top: 10, bottom: 10 }).alignItems(HorizontalAlign.Center)
        }.width(86)
      }.width('100%').alignItems(VerticalAlign.Top)
    }.width('100%').padding({ left: 14, right: 14, top: 10, bottom: 16 })
  }

  // ---------------- 配件Tab:标签云 + 紧凑列表 ----------------
  @Builder
  streetContent() {
    Column({ space: 14 }) {
      Text('🥄 配件周边 · 生活美学').fontSize(15).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').width('100%')
      Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.Start }) {
        ForEach(COLLECTIONS, (c: CollectionItem) => {
          Text(c.emoji + ' ' + c.name + ' ' + c.owned.toString() + '/' + c.total.toString()).fontSize(11).fontColor('#2B2B2B').backgroundColor('#F0E9E2').borderRadius(12).padding({ left: 10, right: 10, top: 6, bottom: 6 }).margin(4)
        }, (c: CollectionItem) => 'ccoll' + c.id.toString())
      }.width('100%')

      ForEach(streetSets(), (t: CoffeeItem) => {
        Row({ space: 12 }) {
          Text(t.emoji).fontSize(24)
          Column({ space: 2 }) {
            Row({ space: 6 }) {
              Text(t.name).fontSize(13).fontWeight(FontWeight.Bold).fontColor('#2B2B2B')
              Text(rarityLabel(t.rarity)).fontSize(8).fontColor('#FFFFFF').backgroundColor(rarityColor(t.rarity)).borderRadius(6).padding({ left: 5, right: 5, top: 1, bottom: 1 })
            }
            Text(t.desc).fontSize(10).fontColor('#8A7F73').maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
          }.alignItems(HorizontalAlign.Start).layoutWeight(1)
          Text('¥' + t.price.toString()).fontSize(13).fontWeight(FontWeight.Bold).fontColor('#A83A2E')
        }.width('100%').backgroundColor(t.id === 15 ? '#F0E9E2' : '#FFFFFF').borderRadius(12).padding(12).onClick(() => { this.selectedCoffeeId = t.id; this.showDetailModal = true; })
      }, (t: CoffeeItem) => 'st' + t.id.toString())

      Text('💬 集齐三大咖啡器具可召唤隐藏福利:辣妈GS3+EK43+手冲壶').fontSize(10).fontColor('#8A7F73').width('100%')
    }.width('100%').padding({ left: 14, right: 14, top: 10, bottom: 16 })
  }

  // ---------------- 器具首页 ----------------
  @Builder
  bazaarContent() {
    Column() {
      this.topTabsBuilder()
      if (this.currentTopTab === 0) { this.cityContent() }
      else if (this.currentTopTab === 1) { this.retiredContent() }
      else if (this.currentTopTab === 2) { this.mocContent() }
      else if (this.currentTopTab === 3) { this.figContent() }
      else if (this.currentTopTab === 4) { this.techContent() }
      else { this.streetContent() }
    }.width('100%')
  }

  // ---------------- 换豆页 ----------------
  @Builder
  swapContent() {
    Column({ space: 12 }) {
      Row({ space: 0 }) {
        Column({ space: 4 }) { Text('156').fontSize(20).fontWeight(FontWeight.Bold).fontColor('#6F4E37'); Text('在交换中').fontSize(10).fontColor('#8A7F73') }.layoutWeight(1).alignItems(HorizontalAlign.Center)
        Column({ space: 4 }) { Text('42').fontSize(20).fontWeight(FontWeight.Bold).fontColor('#C9A24B'); Text('今日成交').fontSize(10).fontColor('#8A7F73') }.layoutWeight(1).alignItems(HorizontalAlign.Center)
        Column({ space: 4 }) { Text('98%').fontSize(20).fontWeight(FontWeight.Bold).fontColor('#6F4E37'); Text('面交好评').fontSize(10).fontColor('#8A7F73') }.layoutWeight(1).alignItems(HorizontalAlign.Center)
        Column({ space: 6 }) { Text('🤝 发交换').fontSize(12).fontColor('#FFFFFF').backgroundColor('#6F4E37').borderRadius(14).padding({ left: 12, right: 12, top: 6, bottom: 6 }).onClick(() => { this.showSwapModal = true; }) }.alignItems(HorizontalAlign.Center)
      }.width('100%').backgroundColor('#FFFFFF').borderRadius(14).padding({ top: 14, bottom: 14 })

      ForEach(SWAPS, (s: SwapItem) => {
        Column({ space: 8 }) {
          Row({ space: 10 }) {
            Text(s.avatar).fontSize(26)
            Column({ space: 2 }) {
              Row({ space: 6 }) {
                Text(s.user).fontSize(13).fontWeight(FontWeight.Bold).fontColor('#2B2B2B')
                Text(swapBadge(s.likes)).fontSize(8).fontColor('#C9A24B').backgroundColor('#F5EFE0').borderRadius(6).padding({ left: 5, right: 5, top: 1, bottom: 1 })
              }
              Text(s.time + ' · ' + s.place).fontSize(9).fontColor('#8A7F73')
            }.alignItems(HorizontalAlign.Start).layoutWeight(1)
            Text('⋯').fontSize(16).fontColor('#8A7F73').onClick(() => { this.showDeleteModal = true; })
          }.width('100%')

          Row({ space: 10 }) {
            Column({ space: 4 }) { Text('我提供').fontSize(9).fontColor('#6F4E37'); Text(s.offer).fontSize(11).fontWeight(FontWeight.Bold).fontColor('#2B2B2B') }.layoutWeight(1).backgroundColor('#F0E9E2').borderRadius(10).padding(8).alignItems(HorizontalAlign.Center)
            Text('⇄').fontSize(14).fontColor('#C9A24B')
            Column({ space: 4 }) { Text('我想换').fontSize(9).fontColor('#A83A2E'); Text(s.want).fontSize(11).fontWeight(FontWeight.Bold).fontColor('#2B2B2B') }.layoutWeight(1).backgroundColor('#F5EFE0').borderRadius(10).padding(8).alignItems(HorizontalAlign.Center)
          }.width('100%')

          Text(s.content).fontSize(11).fontColor('#4A5A6E').lineHeight(16).width('100%')
          Row({ space: 8 }) {
            Text('📍 ' + s.place).fontSize(10).fontColor('#6F4E37').backgroundColor('#F5F0EB').borderRadius(10).padding({ left: 8, right: 8, top: 4, bottom: 4 })
            Text('❤️ ' + s.likes.toString()).fontSize(10).fontColor('#A83A2E').layoutWeight(1).textAlign(TextAlign.End)
          }.width('100%')
        }.width('100%').backgroundColor('#FFFFFF').borderRadius(14).padding(14)
      }, (s: SwapItem) => 'sw' + s.id.toString())
    }.width('100%').padding({ left: 14, right: 14, top: 10, bottom: 16 })
  }

  // ---------------- 图鉴页 ----------------
  @Builder
  collectionContent() {
    Column({ space: 12 }) {
      Row({ space: 10 }) {
        Column({ space: 4 }) {
          Text('📖 我的咖啡图鉴').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#2B2B2B')
          Text('记录每一件心头好的收藏进度').fontSize(10).fontColor('#8A7F73')
        }.alignItems(HorizontalAlign.Start).layoutWeight(1)
        Text('✚ 登记').fontSize(12).fontColor('#FFFFFF').backgroundColor('#6F4E37').borderRadius(14).padding({ left: 14, right: 14, top: 7, bottom: 7 }).onClick(() => { this.showSellModal = true; })
      }.width('100%').margin({ top: 10 })

      ForEach(COLLECTIONS, (c: CollectionItem) => {
        Column({ space: 8 }) {
          Row({ space: 10 }) {
            Text(c.emoji).fontSize(28)
            Column({ space: 3 }) {
              Text(c.name).fontSize(14).fontWeight(FontWeight.Bold).fontColor('#2B2B2B')
              Text(c.series + ' · 已收 ' + c.owned.toString() + '/' + c.total.toString()).fontSize(10).fontColor('#8A7F73')
            }.alignItems(HorizontalAlign.Start).layoutWeight(1)
            Text(c.rate.toString() + '%').fontSize(15).fontWeight(FontWeight.Bold).fontColor(rateColor(c.rate))
          }.width('100%')
          Row({ space: 0 }) {
            Column().width(c.rate.toString() + '%').height(6).backgroundColor(rateColor(c.rate)).borderRadius(3)
            Column().width((100 - c.rate).toString() + '%').height(6).backgroundColor('#E5DDD5').borderRadius(3)
          }.width('100%')
        }.width('100%').backgroundColor(c.id === 5 ? '#F5EFE0' : '#FFFFFF').borderRadius(14).padding(14)
      }, (c: CollectionItem) => 'coll' + c.id.toString())

      Column() {
        Text('🏅 图鉴成就墙').fontSize(14).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').width('100%').margin({ bottom: 8 })
        Flex({ wrap: FlexWrap.Wrap }) {
          Text('🎖️ 意式咖啡大师').fontSize(10).fontColor('#6F4E37').backgroundColor('#F0E9E2').borderRadius(10).padding({ left: 8, right: 8, top: 5, bottom: 5 }).margin(4)
          Text('🏆 手冲达人').fontSize(10).fontColor('#A83A2E').backgroundColor('#F5EFE0').borderRadius(10).padding({ left: 8, right: 8, top: 5, bottom: 5 }).margin(4)
          Text('🫘 豆子收藏家').fontSize(10).fontColor('#C9A24B').backgroundColor('#F5EFE0').borderRadius(10).padding({ left: 8, right: 8, top: 5, bottom: 5 }).margin(4)
          Text('🥄 咖啡生活家').fontSize(10).fontColor('#4A7C59').backgroundColor('#E8F7EF').borderRadius(10).padding({ left: 8, right: 8, top: 5, bottom: 5 }).margin(4)
        }.width('100%')
      }.width('100%').backgroundColor('#FFFFFF').borderRadius(14).padding(14)
    }.width('100%').padding({ left: 14, right: 14, bottom: 16 })
  }

  // ---------------- 我的页 ----------------
  @Builder
  mineContent() {
    Column({ space: 14 }) {
      Column() {
        Row({ space: 12 }) {
          Text('☕').fontSize(40)
          Column({ space: 4 }) {
            Text('咖啡收藏家阿乐').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#FFFFFF')
            Text('LV.10 资深咖啡控 · 信用分 990').fontSize(10).fontColor('#E3D5C8')
          }.alignItems(HorizontalAlign.Start).layoutWeight(1)
          Text('>').fontSize(18).fontColor('#E3D5C8')
        }.width('100%')
        Row({ space: 0 }) {
          Column({ space: 3 }) { Text('38').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#E2CE94'); Text('在售器具').fontSize(9).fontColor('#E3D5C8') }.layoutWeight(1).alignItems(HorizontalAlign.Center)
          Column({ space: 3 }) { Text('12').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#E2CE94'); Text('求购中').fontSize(9).fontColor('#E3D5C8') }.layoutWeight(1).alignItems(HorizontalAlign.Center)
          Column({ space: 3 }) { Text('¥3.8万').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#E2CE94'); Text('累计成交额').fontSize(9).fontColor('#E3D5C8') }.layoutWeight(1).alignItems(HorizontalAlign.Center)
        }.width('100%').margin({ top: 16 })
      }.width('100%').padding(18).linearGradient({ angle: 120, colors: [['#6F4E37', 0], ['#2B2B2B', 1]] }).borderRadius(16).margin({ top: 10 })

      Column() {
        Text('🔨 我的求购清单').fontSize(14).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').width('100%').margin({ bottom: 6 })
        ForEach(COFFEES, (t: CoffeeItem) => {
          if (t.id === 13 || t.id === 16 || t.id === 18) {
            Row({ space: 10 }) {
              Text(t.emoji).fontSize(20)
              Column({ space: 2 }) {
                Text(t.name).fontSize(12).fontWeight(FontWeight.Bold).fontColor('#2B2B2B')
                Text('预算 ¥' + t.price.toString() + ' 内 · ' + t.want).fontSize(9).fontColor('#8A7F73')
              }.alignItems(HorizontalAlign.Start).layoutWeight(1)
              Text('出价中').fontSize(10).fontColor('#C9A24B').backgroundColor('#F5EFE0').borderRadius(10).padding({ left: 8, right: 8, top: 3, bottom: 3 })
            }.width('100%').padding({ top: 9, bottom: 9 })
          }
        }, (t: CoffeeItem) => 'wish' + t.id.toString())
      }.width('100%').backgroundColor('#FFFFFF').borderRadius(14).padding(14)

      Column() {
        Text('📍 常去面交点').fontSize(14).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').width('100%').margin({ bottom: 6 })
        ForEach(PLACES, (p: string, i: number) => {
          Row({ space: 10 }) {
            Text(['🚇', '🏬', '🚌', '🏪', '📦'][i]).fontSize(16)
            Text(p).fontSize(12).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').layoutWeight(1)
            Text(['光谷站台', '江汉路天桥', '洪山转盘', '楚河入口', '顺丰网点'][i]).fontSize(9).fontColor('#8A7F73')
          }.width('100%').padding({ top: 9, bottom: 9 })
        }, (p: string, i: number) => 'pl' + i.toString() + p)
      }.width('100%').backgroundColor('#FFFFFF').borderRadius(14).padding(14)
    }.width('100%').padding({ left: 14, right: 14, bottom: 16 })
  }

  // ---------------- 底部Tab ----------------
  @Builder
  bottomTabsBuilder() {
    Row() {
      ForEach(BOTTOM_TABS, (t: TabItem, idx: number) => {
        Column({ space: 3 }) {
          Text(t.icon).fontSize(20)
          Text(t.label).fontSize(10).fontColor(this.currentBottomTab === idx ? '#6F4E37' : '#8A7F73').fontWeight(this.currentBottomTab === idx ? FontWeight.Bold : FontWeight.Normal)
        }.layoutWeight(1).alignItems(HorizontalAlign.Center).padding({ top: 8, bottom: 8 }).onClick(() => { this.currentBottomTab = idx; })
      }, (t: TabItem, idx: number) => 'bt' + idx.toString() + t.label)
    }.width('100%').backgroundColor('#FFFFFF').borderRadius({ topLeft: 18, topRight: 18 })
  }

  // ---------------- 弹框1:发布出售(新增·棕渐变头) ----------------
  @Builder
  sellModalBuilder() {
    Column() {
      Column() {
        Text('☕ 发布出售').fontSize(17).fontWeight(FontWeight.Bold).fontColor('#FFFFFF').margin({ top: 16 })
        Text('闲置回血,神机不愁卖').fontSize(11).fontColor('#E3D5C8').margin({ top: 4, bottom: 16 })
      }.width('100%').linearGradient({ angle: 120, colors: [['#6F4E37', 0], ['#2B2B2B', 1]] }).borderRadius({ topLeft: 18, topRight: 18 })

      Scroll() {
        Column({ space: 14 }) {
          Column({ space: 8 }) {
            Text('要出售的器具').fontSize(12).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').width('100%')
            Flex({ wrap: FlexWrap.Wrap }) {
              ForEach(COFFEES, (t: CoffeeItem, i: number) => {
                if (i < 8) {
                  Text(t.emoji + ' ' + t.name).fontSize(10).fontColor(this.sellCoffeeIdx === i ? '#FFFFFF' : '#2B2B2B').backgroundColor(this.sellCoffeeIdx === i ? '#6F4E37' : '#F0E9E2').borderRadius(12).padding({ left: 9, right: 9, top: 5, bottom: 5 }).margin(4).onClick(() => { this.sellCoffeeIdx = i; })
                }
              }, (t: CoffeeItem, i: number) => 'st' + i.toString() + t.id.toString())
            }.width('100%')
          }.width('100%').alignItems(HorizontalAlign.Start)

          Column({ space: 8 }) {
            Text('成色说明').fontSize(12).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').width('100%')
            Flex({ wrap: FlexWrap.Wrap }) {
              ForEach(CONDITION_OPTIONS, (c: string, i: number) => {
                Text(c).fontSize(10).fontColor(this.sellCondIdx === i ? '#FFFFFF' : '#C9A24B').backgroundColor(this.sellCondIdx === i ? '#C9A24B' : '#F5EFE0').borderRadius(12).padding({ left: 10, right: 10, top: 5, bottom: 5 }).margin(4).onClick(() => { this.sellCondIdx = i; })
              }, (c: string, i: number) => 'sd' + i.toString() + c)
            }.width('100%')
          }.width('100%').alignItems(HorizontalAlign.Start)

          Row({ space: 12 }) {
            Text('出售价格').fontSize(12).fontWeight(FontWeight.Bold).fontColor('#2B2B2B')
            Row({ space: 0 }) {
              Text('−').fontSize(16).fontColor('#6F4E37').backgroundColor('#F0E9E2').borderRadius({ topLeft: 10, bottomLeft: 10 }).width(34).height(30).textAlign(TextAlign.Center).onClick(() => { if (this.sellPrice > 10) { this.sellPrice -= 10; } })
              Text('¥' + this.sellPrice.toString()).fontSize(13).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').backgroundColor('#FFFFFF').height(30).textAlign(TextAlign.Center).padding({ left: 14, right: 14 })
              Text('+').fontSize(16).fontColor('#FFFFFF').backgroundColor('#6F4E37').borderRadius({ topRight: 10, bottomRight: 10 }).width(34).height(30).textAlign(TextAlign.Center).onClick(() => { if (this.sellPrice < 9999) { this.sellPrice += 10; } })
            }.borderRadius(10)
            Text(this.sellPrice > 2000 ? '💰 高价好物建议包邮' : '实惠价更容易出手').fontSize(9).fontColor(this.sellPrice > 2000 ? '#A83A2E' : '#8A7F73').layoutWeight(1).textAlign(TextAlign.End)
          }.width('100%')

          Column({ space: 8 }) {
            Text('宝贝描述').fontSize(12).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').width('100%')
            TextInput({ placeholder: '成色、瑕疵、配件情况如实描述…', text: this.sellWords }).fontSize(12).fontColor('#2B2B2B').backgroundColor('#F5F0EB').borderRadius(10).height(40).padding({ left: 12, right: 12 }).onChange((v: string) => { this.sellWords = v; })
          }.width('100%').alignItems(HorizontalAlign.Start)

          Row({ space: 12 }) {
            Text('取消').fontSize(13).fontColor('#8A7F73').backgroundColor('#F5F0EB').borderRadius(20).layoutWeight(1).height(42).textAlign(TextAlign.Center).onClick(() => { this.showSellModal = false; })
            Text('上架出售').fontSize(13).fontWeight(FontWeight.Bold).fontColor('#FFFFFF').backgroundColor('#6F4E37').borderRadius(20).layoutWeight(1).height(42).textAlign(TextAlign.Center).onClick(() => { this.showSellModal = false; })
          }.width('100%').margin({ top: 4, bottom: 18 })
        }.width('100%').padding({ left: 16, right: 16, top: 16 })
      }.scrollBar(BarState.Off).layoutWeight(1).align(Alignment.Top)
    }.width('88%').height('76%').backgroundColor('#FFFFFF').borderRadius(18).clip(true)
  }

  // ---------------- 弹框2:发起交换(新增·金头) ----------------
  @Builder
  swapModalBuilder() {
    Column() {
      Column() {
        Row({ space: 8 }) { Text('🤝').fontSize(22); Text('发起交换').fontSize(17).fontWeight(FontWeight.Bold).fontColor('#2B2B2B') }.margin({ top: 16 })
        Text('用闲置换心头好,咖友互助').fontSize(11).fontColor('#5B6B7F').margin({ top: 4, bottom: 14 })
      }.width('100%').backgroundColor('#C9A24B').borderRadius({ topLeft: 18, topRight: 18 })

      Scroll() {
        Column({ space: 14 }) {
          Column({ space: 8 }) {
            Text('我提供').fontSize(12).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').width('100%')
            Flex({ wrap: FlexWrap.Wrap }) {
              ForEach(COFFEES, (t: CoffeeItem, i: number) => {
                if (i % 3 === 0 && i < 15) {
                  Text(t.emoji + ' ' + t.name).fontSize(10).fontColor(this.swapOfferIdx === i ? '#FFFFFF' : '#2B2B2B').backgroundColor(this.swapOfferIdx === i ? '#2B2B2B' : '#F5F0EB').borderRadius(12).padding({ left: 9, right: 9, top: 5, bottom: 5 }).margin(4).onClick(() => { this.swapOfferIdx = i; })
                }
              }, (t: CoffeeItem, i: number) => 'wo' + i.toString() + t.id.toString())
            }.width('100%')
          }.width('100%').alignItems(HorizontalAlign.Start)

          Column({ space: 8 }) {
            Text('我想换(可多选)').fontSize(12).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').width('100%')
            Flex({ wrap: FlexWrap.Wrap }) {
              ForEach(COFFEES, (t: CoffeeItem, i: number) => {
                if (i % 3 === 2 && i < 15) {
                  Text((this.pickedWant(i) ? '✓ ' : '') + t.emoji + ' ' + t.name).fontSize(10).fontColor(this.pickedWant(i) ? '#FFFFFF' : '#A83A2E').backgroundColor(this.pickedWant(i) ? '#A83A2E' : '#F5EFE0').borderRadius(12).padding({ left: 9, right: 9, top: 5, bottom: 5 }).margin(4).onClick(() => { this.toggleWant(i); })
                }
              }, (t: CoffeeItem, i: number) => 'ww' + i.toString() + t.id.toString())
            }.width('100%')
          }.width('100%').alignItems(HorizontalAlign.Start)

          Column({ space: 8 }) {
            Text('面交地点').fontSize(12).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').width('100%')
            Flex({ wrap: FlexWrap.Wrap }) {
              ForEach(PLACES, (p: string, i: number) => {
                Text(p).fontSize(10).fontColor(this.swapPlaceIdx === i ? '#FFFFFF' : '#6F4E37').backgroundColor(this.swapPlaceIdx === i ? '#6F4E37' : '#F0E9E2').borderRadius(12).padding({ left: 10, right: 10, top: 5, bottom: 5 }).margin(4).onClick(() => { this.swapPlaceIdx = i; })
              }, (p: string, i: number) => 'wp' + i.toString() + p)
            }.width('100%')
          }.width('100%').alignItems(HorizontalAlign.Start)

          Row({ space: 8 }) {
            Text('💡').fontSize(14)
            Text('交换双方可自行协商补差价,面交请选择人流密集的公共场所,注意安全。').fontSize(10).fontColor('#8A7F73').lineHeight(15).layoutWeight(1)
          }.width('100%').backgroundColor('#F5EFE0').borderRadius(10).padding(10)

          Row({ space: 12 }) {
            Text('再想想').fontSize(13).fontColor('#8A7F73').backgroundColor('#F5F0EB').borderRadius(20).layoutWeight(1).height(42).textAlign(TextAlign.Center).onClick(() => { this.showSwapModal = false; })
            Text('发布交换帖').fontSize(13).fontWeight(FontWeight.Bold).fontColor('#FFFFFF').backgroundColor('#6F4E37').borderRadius(20).layoutWeight(1).height(42).textAlign(TextAlign.Center).onClick(() => { this.showSwapModal = false; })
          }.width('100%').margin({ top: 4, bottom: 18 })
        }.width('100%').padding({ left: 16, right: 16, top: 16 })
      }.scrollBar(BarState.Off).layoutWeight(1).align(Alignment.Top)
    }.width('88%').height('72%').backgroundColor('#FFFFFF').borderRadius(18).clip(true)
  }

  // ---------------- 弹框3:下架/删除 ----------------
  @Builder
  deleteModalBuilder() {
    Column({ space: 14 }) {
      Text('🗑️').fontSize(36)
      Text('下架这件器具?').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#2B2B2B')
      Text('下架后买家将无法搜索到这条信息,\n收藏和留言会一并清空,可重新上架。').fontSize(11).fontColor('#8A7F73').textAlign(TextAlign.Center).lineHeight(17)
      Row({ space: 12 }) {
        Text('留着').fontSize(13).fontColor('#6F4E37').backgroundColor('#F0E9E2').borderRadius(20).layoutWeight(1).height(40).textAlign(TextAlign.Center).onClick(() => { this.showDeleteModal = false; })
        Text('确认下架').fontSize(13).fontWeight(FontWeight.Bold).fontColor('#FFFFFF').backgroundColor('#A83A2E').borderRadius(20).layoutWeight(1).height(40).textAlign(TextAlign.Center).onClick(() => { this.showDeleteModal = false; })
      }.width('100%').margin({ top: 6 })
    }.width('72%').backgroundColor('#FFFFFF').borderRadius(18).padding({ left: 20, right: 20, top: 24, bottom: 22 }).alignItems(HorizontalAlign.Center)
  }

  // ---------------- 弹框4:器具详情(棕金渐变头) ----------------
  @Builder
  detailModalBuilder() {
    Column() {
      Column() {
        Text(this.detailEmoji()).fontSize(40).margin({ top: 18 })
        Text(this.detailName()).fontSize(18).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').margin({ top: 8 })
        Text(this.detailEng()).fontSize(10).fontColor('#5B6B7F').margin({ top: 2 })
        Row({ space: 8 }) {
          Text(this.detailTag()).fontSize(9).fontColor('#FFFFFF').backgroundColor('#6F4E37').borderRadius(8).padding({ left: 7, right: 7, top: 2, bottom: 2 })
          Text(rarityLabel(Number(this.detailRarity()))).fontSize(9).fontColor('#2B2B2B').backgroundColor('#C9A24B').borderRadius(8).padding({ left: 7, right: 7, top: 2, bottom: 2 })
        }.margin({ top: 10, bottom: 18 })
      }.width('100%').linearGradient({ angle: 135, colors: [['#C9A24B', 0], ['#F0E9E2', 1]] }).borderRadius({ topLeft: 18, topRight: 18 }).alignItems(HorizontalAlign.Center)

      Scroll() {
        Column({ space: 14 }) {
          Row({ space: 0 }) {
            Column({ space: 4 }) { Text(this.detailPrice()).fontSize(17).fontWeight(FontWeight.Bold).fontColor('#A83A2E'); Text('售价').fontSize(9).fontColor('#8A7F73') }.layoutWeight(1).alignItems(HorizontalAlign.Center)
            Column({ space: 4 }) { Text(this.detailSize()).fontSize(17).fontWeight(FontWeight.Bold).fontColor('#6F4E37'); Text('规格').fontSize(9).fontColor('#8A7F73') }.layoutWeight(1).alignItems(HorizontalAlign.Center)
            Column({ space: 4 }) { Text(this.detailRarity()).fontSize(17).fontWeight(FontWeight.Bold).fontColor(rarityColor(Number(this.detailRarity()))); Text('稀有度').fontSize(9).fontColor('#8A7F73') }.layoutWeight(1).alignItems(HorizontalAlign.Center)
            Column({ space: 4 }) { Text(this.detailYear()).fontSize(17).fontWeight(FontWeight.Bold).fontColor('#C9A24B'); Text('年份').fontSize(9).fontColor('#8A7F73') }.layoutWeight(1).alignItems(HorizontalAlign.Center)
          }.width('100%').backgroundColor('#F5F0EB').borderRadius(12).padding({ top: 12, bottom: 12 })

          Column({ space: 8 }) {
            Text('🧾 宝贝描述').fontSize(12).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').width('100%')
            Text(this.detailDesc()).fontSize(11).fontColor('#4A5A6E').lineHeight(18)
            Row({ space: 8 }) {
              Text('👤 ' + this.detailSeller()).fontSize(10).fontColor('#6F4E37')
              Text(this.detailStar() + ' ' + this.detailRating()).fontSize(10).fontColor('#C9A24B').layoutWeight(1).textAlign(TextAlign.End)
            }.width('100%')
          }.width('100%').alignItems(HorizontalAlign.Start)

          Row({ space: 8 }) {
            Text('🤝').fontSize(14)
            Text('卖家想换:' + this.detailWant()).fontSize(11).fontColor('#A83A2E').layoutWeight(1)
          }.width('100%').backgroundColor('#F5EFE0').borderRadius(10).padding(10)

          Column({ space: 10 }) {
            Text('💬 圈友点评').fontSize(12).fontWeight(FontWeight.Bold).fontColor('#2B2B2B').width('100%')
            ForEach(COFFEE_REVIEWS, (r: CoffeeReview) => {
              Column({ space: 5 }) {
                Row({ space: 8 }) { Text('👤').fontSize(14); Text(r.user).fontSize(11).fontWeight(FontWeight.Bold).fontColor('#2B2B2B'); Text(r.starStr).fontSize(9).layoutWeight(1).textAlign(TextAlign.End) }.width('100%')
                Text(r.content).fontSize(10).fontColor('#8A7F73').lineHeight(15)
              }.width('100%').backgroundColor('#F5F0EB').borderRadius(10).padding(10)
            }, (r: CoffeeReview) => 'rv' + r.id.toString())
          }.width('100%').alignItems(HorizontalAlign.Start)

          Row({ space: 12 }) {
            Text('💬 留言问问').fontSize(13).fontColor('#6F4E37').backgroundColor('#F0E9E2').borderRadius(20).layoutWeight(1).height(42).textAlign(TextAlign.Center).onClick(() => { this.showDetailModal = false; })
            Text('🛒 立即购买').fontSize(13).fontWeight(FontWeight.Bold).fontColor('#FFFFFF').backgroundColor('#A83A2E').borderRadius(20).layoutWeight(1).height(42).textAlign(TextAlign.Center).onClick(() => { this.showDetailModal = false; })
          }.width('100%').margin({ top: 2, bottom: 18 })
        }.width('100%').padding({ left: 16, right: 16, top: 14 })
      }.scrollBar(BarState.Off).layoutWeight(1).align(Alignment.Top)
    }.width('88%').height('78%').backgroundColor('#FFFFFF').borderRadius(18).clip(true)
  }

  // 交换多选辅助
  pickedWant(i: number): boolean { return (i % 2) === 0; }
  toggleWant(i: number): void { this.swapWantIdx = i; }

  // ---------------- 主布局 ----------------
  build() {
    Stack() {
      Column() {
        this.headerBuilder()
        Scroll() {
          Column() {
            if (this.currentBottomTab === 0) { this.bazaarContent() }
            else if (this.currentBottomTab === 1) { this.swapContent() }
            else if (this.currentBottomTab === 2) { this.collectionContent() }
            else { this.mineContent() }
          }.width('100%')
        }.layoutWeight(1).scrollBar(BarState.Off).align(Alignment.Top)
        this.bottomTabsBuilder()
      }.width('100%').height('100%')

      ForEach(this.particles, (p: ParticleItem, i: number) => {
        Text(p.emoji).fontSize(p.size).opacity(p.opacity).position({ x: p.x, y: 80 + (i % 5) * 118 })
      }, (p: ParticleItem, i: number) => 'pt' + i.toString())

      if (this.showDetailModal) {
        Column() { this.detailModalBuilder() }.width('100%').height('100%').backgroundColor('99000000').justifyContent(FlexAlign.Center).onClick(() => { this.showDetailModal = false; })
      }
      if (this.showSellModal) {
        Column() { this.sellModalBuilder() }.width('100%').height('100%').backgroundColor('99000000').justifyContent(FlexAlign.Center).onClick(() => { this.showSellModal = false; })
      }
      if (this.showSwapModal) {
        Column() { this.swapModalBuilder() }.width('100%').height('100%').backgroundColor('99000000').justifyContent(FlexAlign.Center).onClick(() => { this.showSwapModal = false; })
      }
      if (this.showDeleteModal) {
        Column() { this.deleteModalBuilder() }.width('100%').height('100%').backgroundColor('99000000').justifyContent(FlexAlign.Center).onClick(() => { this.showDeleteModal = false; })
      }
    }.width('100%').height('100%').backgroundColor(PALETTE.bg)
  }
}
absBuilder()
      }.width('100%').height('100%')

      ForEach(this.particles, (p: ParticleItem, i: number) => {
        Text(p.emoji).fontSize(p.size).opacity(p.opacity).position({ x: p.x, y: 80 + (i % 5) * 118 })
      }, (p: ParticleItem, i: number) => 'pt' + i.toString())

      if (this.showDetailModal) {
        Column() { this.detailModalBuilder() }.width('100%').height('100%').backgroundColor('99000000').justifyContent(FlexAlign.Center).onClick(() => { this.showDetailModal = false; })
      }
      if (this.showSellModal) {
        Column() { this.sellModalBuilder() }.width('100%').height('100%').backgroundColor('99000000').justifyContent(FlexAlign.Center).onClick(() => { this.showSellModal = false; })
      }
      if (this.showSwapModal) {
        Column() { this.swapModalBuilder() }.width('100%').height('100%').backgroundColor('99000000').justifyContent(FlexAlign.Center).onClick(() => { this.showSwapModal = false; })
      }
      if (this.showDeleteModal) {
        Column() { this.deleteModalBuilder() }.width('100%').height('100%').backgroundColor('99000000').justifyContent(FlexAlign.Center).onClick(() => { this.showDeleteModal = false; })
      }
    }.width('100%').height('100%').backgroundColor(PALETTE.bg)
  }
}
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐