引言:社群电商的技术架构与业务设计

在移动互联网快速发展的今天,社群拼团已成为电商领域中最具活力的商业模式之一。以拼多多为代表的社群拼团平台,通过社交裂变的方式将商品采购成本压至最低,同时为消费者带来极致性价比的购物体验。本文将深入剖析一个基于HarmonyOS ArkTS声明式UI框架构建的社群拼团管理平台,该平台涵盖了拼团大厅、社群管理、营销活动、订单追踪、数据看板、排行榜及个人中心等七大核心业务模块,是一个功能完整的电商管理端应用。

从技术架构角度来看,该平台采用了HarmonyOS API 24提供的声明式UI开发范式,通过@Entry@Component@State@Builder等核心装饰器构建出高度可组合的组件体系。声明式UI的核心思想在于"状态驱动视图"——开发者只需描述界面在任何给定状态下应该呈现的样子,框架会自动处理状态变化时的UI更新。这种范式相比传统的命令式UI开发,大幅减少了模板代码量,提升了开发效率。在本应用中,@State装饰器管理着当前选中的Tab索引、搜索关键词、分类筛选条件以及各种弹窗的显示状态,这些状态的任何变化都会触发框架精确的差分渲染。

从业务设计角度来看,该平台围绕"拼团"这一核心交易模式构建了完整的运营闭环。拼团大厅采用双列瀑布流网格布局展示商品,每个商品卡片包含状态角标、折扣信息、进度条等丰富视觉元素;社群管理模块通过KPI卡片直观展示运营数据;数据看板集成了柱状图和分类销量分布的可视化图表;排行榜模块则通过前三名展示台和列表相结合的方式呈现排名信息。整个应用的设计风格以橙红色(#FF4242)为主色调,搭配金橙色(#FF7733)作为强调色,浅粉色(#FFF5F5)作为背景色,营造出与拼多多品牌高度一致的视觉体验。

一、类型定义与数据模型设计

在ArkTS中,接口(interface)是定义数据模型的核心手段。本平台定义了五个核心数据接口,分别对应拼团商品、社群、订单、活动和排行榜五个业务实体。这些接口的设计遵循了单一职责原则,每个接口只负责描述一个实体的数据结构。

interface GroupItem {
  id: number; title: string; category: string; originPrice: number
  groupPrice: number; needCount: number; joinedCount: number
  endTime: string; status: string; image: string; description: string
  tags: string[]; shopName: string; rating: number; salesCount: number
}
interface CommunityItem {
  id: number; name: string; memberCount: number; todayOrders: number
  todayRevenue: number; level: string; leaderName: string; createTime: string
  tag: string; status: string
}
interface OrderItem {
  id: number; groupTitle: string; buyerName: string; phone: string
  quantity: number; totalPrice: number; status: string; createTime: string
  address: string; remark: string
}
interface ActivityItem {
  id: number; title: string; type: string; discount: string
  startTime: string; endTime: string; budget: number; used: number
  status: string; joinCount: number; description: string
}
interface RankItem {
  rank: number; name: string; avatar: string; groupCount: number
  revenue: number; members: number; growth: string
}

GroupItem接口是整个平台最核心的数据模型,包含了商品的标题、分类、原价、拼团价、所需人数、已参与人数、截止时间、状态、图片、描述、标签、店铺名称、评分和销量等14个字段。值得注意的是价格字段使用了number类型并以"分"为单位存储,这是一种电商领域常见的做法,可以有效避免浮点数精度问题。CommunityItem接口则描述了社群的基本信息,包括成员数、今日订单数、今日营收、等级、团长姓名等运营关键指标。OrderItemActivityItem分别定义了订单和营销活动的数据结构,RankItem则用于排行榜的数据展示。

二、静态配置与视觉映射体系

静态配置是本平台架构设计中的一个重要模式。通过将业务配置数据集中定义为常量对象,实现了配置与逻辑的分离,便于后期维护和扩展。

const CAT_CONFIG: Record<string, string> = {
  '生鲜': '🥬', '零食': '🍪', '日用': '🧴', '美妆': '💄',
  '数码': '📱', '服饰': '👕', '家居': '🛋️', '母婴': '🍼',
}
const STATUS_CONFIG: Record<string, string> = {
  '拼团中': '#FF4242', '已成团': '#4CAF50', '未成团': '#9E9E9E',
  '已结束': '#BDBDBD', '即将开团': '#FF9800',
}
const ORDER_STATUS_CONFIG: Record<string, string> = {
  '待付款': '#FF9800', '待发货': '#2196F3', '已发货': '#4CAF50',
  '已完成': '#9E9E9E', '退款中': '#F44336', '已退款': '#9C27B0',
}
const ACTIVITY_TYPE_CONFIG: Record<string, string> = {
  '满减': '💰', '折扣': '🏷️', '秒杀': '⚡', '新人': '🎁', '红包': '🧧',
}
interface LevelMeta {
  color: string;
  bg: string;
}
const LEVEL_CONFIG: Record<string, LevelMeta> = {
  '钻石': { color: '#9C27B0', bg: '#F3E5F5' },
  '金牌': { color: '#FF9800', bg: '#FFF3E0' },
  '银牌': { color: '#757575', bg: '#F5F5F5' },
  '铜牌': { color: '#8D6E63', bg: '#EFEBE9' },
}

在这里插入图片描述

上述配置代码展示了平台的三层视觉映射体系。第一层是分类配置CAT_CONFIG,将商品分类映射为emoji图标,使界面更加直观生动。第二层是状态颜色配置,包括STATUS_CONFIGORDER_STATUS_CONFIG,它们将业务状态映射为对应的颜色值——例如"已成团"使用绿色表示成功,"退款中"使用红色表示警示。第三层是等级配置LEVEL_CONFIG,它使用了LevelMeta接口来同时存储前景色和背景色,实现了更复杂的视觉样式映射。这种"配置即数据"的设计模式使得新增分类、状态或等级时只需修改配置对象,无需改动任何业务逻辑代码。

三、工具函数与业务逻辑封装

工具函数层是连接数据模型和UI视图的桥梁。本平台定义了一系列纯函数来处理数据格式化和状态查询,这些函数具有无副作用、可复用的特点。

function fmtPrice(p: number): string { return '¥' + (p / 100).toFixed(2) }
function fmtNum(n: number): string { return n >= 10000 ? (n / 10000).toFixed(1) + 'w' : n.toString() }
function fmtDate(d: string): string { return d.substring(5) }
function getStatusColor(s: string): string { return STATUS_CONFIG[s] ?? '#9E9E9E' }
function getOrderStatusColor(s: string): string { return ORDER_STATUS_CONFIG[s] ?? '#9E9E9E' }
function getCatIcon(c: string): string { return CAT_CONFIG[c] ?? '📦' }
function getActTypeIcon(t: string): string { return ACTIVITY_TYPE_CONFIG[t] ?? '📌' }
function getLevelMeta(l: string): LevelMeta {
  return LEVEL_CONFIG[l] ?? { color: '#757575', bg: '#F5F5F5' }
}
function getProgressPct(item: GroupItem): number {
  return Math.min(100, Math.round(item.joinedCount / item.needCount * 100))
}
function getProgressColor(pct: number): string {
  if (pct >= 100) return '#4CAF50'
  if (pct >= 60) return '#FF9800'
  if (pct >= 30) return '#FF4242'
  return '#FF7733'
}
function getDiscountRate(origin: number, group: number): number {
  return Math.round(group / origin * 100) / 10
}

fmtPrice函数将以分为单位的数值转换为带人民币符号的字符串,fmtNum函数在数字超过一万时自动转换为"w"单位的简写形式,这些格式化函数确保了数据在UI上的展示既规范又简洁。getProgressPctgetProgressColor两个函数配合实现了拼团进度的动态计算和颜色分级——进度100%显示绿色表示已完成,60%以上显示橙色表示接近完成,30%以上显示红色表示需要加速,低于30%则显示金橙色表示刚刚开始。getDiscountRate函数通过原价和拼团价的比例计算折扣率,Math.round确保结果为整数后除以10得到一位小数的折扣值。所有查询函数都使用了空值合并运算符??提供默认值,确保在配置缺失时不会导致界面异常。

四、主组件架构与状态管理

主组件GroupBuyHubApp是整个应用的入口,通过@Entry@Component装饰器声明。它管理着应用级别的所有状态,并通过@Builder方法将界面拆分为可复用的构建块。

@Entry
@Component
struct GroupBuyHubApp {
  @State activeTab: number = 0
  @State searchKw: string = ''
  @State catFilter: string = '全部'
  @State orderFilter: string = '全部'
  @State selectedGroup: GroupItem | null = null
  @State selectedOrder: OrderItem | null = null
  @State selectedActivity: ActivityItem | null = null
  @State showAddGroup: boolean = false
  @State showEditGroup: boolean = false
  @State showDeleteConfirm: boolean = false
  @State showGroupDetail: boolean = false
  @State showJoinGroup: boolean = false
  @State showOrderDetail: boolean = false
  @State showActDetail: boolean = false
  @State deleteTargetId: number = -1
  @State joinQuantity: number = 1

该组件的状态管理设计体现了ArkTS状态驱动UI的核心理念。activeTab控制当前显示的Tab页面,searchKwcatFilter用于拼团商品的搜索和分类筛选,orderFilter用于订单状态筛选。七个show*布尔类型状态分别控制七种弹窗的显示与隐藏。三个selected*状态采用联合类型(| null),在未选中任何项时为null,选中后变为对应的数据对象。这种设计使得弹窗内容能够根据选中的数据动态渲染。joinQuantity状态专门用于加入拼团弹窗中的数量选择器,支持用户增减购买数量。

五、头部区域与分类快捷入口

头部区域是用户进入应用后首先看到的区域,它承载了品牌标识、搜索功能和分类导航三大核心功能。

@Builder headerSection() {
  Column() {
    Row() {
      Column() {
        Text('🔥 拼团')
          .fontSize(20).fontWeight(FontWeight.Bold).fontColor('#FFFFFF')
        Text('社群拼团管理平台')
          .fontSize(10).fontColor('#FFD0CC').margin({ top: 2 })
      }.alignItems(HorizontalAlign.Start)

      Row() {
        Text('🔍').fontSize(16)
        TextInput({ placeholder: '搜索拼团商品…' })
          .placeholderColor('#FFCCC0').fontSize(12).layoutWeight(1)
          .backgroundColor('transparent').borderWidth(0)
          .onChange((v: string) => { this.searchKw = v })
      }
      .width('55%').backgroundColor('#FFFFFF').borderRadius(20)
      .padding({ left: 12, right: 12, top: 6, bottom: 6 })
      .margin({ left: 12 })

      Text('🔔').fontSize(18).fontColor('#FFFFFF').margin({ left: 8 })
    }
    .width('100%').padding({ left: 16, right: 16, top: 14, bottom: 12 })
    .backgroundColor('#FF4242')

    Scroll() {
      Row() {
        ForEach(Object.keys(CAT_CONFIG), (cat: string) => {
          Column() {
            Text(getCatIcon(cat)).fontSize(22)
            Text(cat).fontSize(9).fontColor('#666666').margin({ top: 4 })
          }
          .width(58).alignItems(HorizontalAlign.Center)
          .onClick(() => { this.catFilter = cat; this.activeTab = 0 })
        })
      }
      .padding({ left: 12, right: 12, top: 10, bottom: 10 })
    }
    .width('100%').scrollable(ScrollDirection.Horizontal).scrollBar(BarState.Off)
    .backgroundColor('#FFFFFF')
  }
}

在这里插入图片描述

头部区域由两部分组成。上部分是一个Row容器,左侧是品牌标题和副标题,中间是搜索输入框,右侧是通知图标。搜索框通过onChange回调实时更新searchKw状态,触发拼团列表的重新筛选。整个头部使用橙红色背景搭配白色文字,营造出强烈的电商品牌氛围。下部分是分类快捷入口,使用横向滚动的Scroll容器承载ForEach遍历的8个分类项。每个分类项由emoji图标和文字标签组成,点击后会设置catFilter并切换到拼团大厅Tab,实现了快捷导航的功能。横向滚动通过scrollable(ScrollDirection.Horizontal)设置,并隐藏了滚动条以保持界面整洁。

六、底部Tab栏与页面切换机制

底部Tab栏是移动端应用的核心导航组件,本平台采用了7Tab设计,涵盖了应用的所有核心功能入口。

@Builder bottomTabBar() {
  Column() {
    Divider().width('100%').height(0.5).backgroundColor('#FFE0E0')
    Row() {
      this.tabBtn('拼团', '🛒', 0)
      this.tabBtn('社群', '👥', 1)
      this.tabBtn('活动', '🎉', 2)
      this.tabBtn('订单', '📦', 3)
      this.tabBtn('看板', '📊', 4)
      this.tabBtn('排行', '🏆', 5)
      this.tabBtn('我的', '👤', 6)
    }
    .width('100%').padding({ top: 6, bottom: 4 })
    .backgroundColor('#FFFFFF')
  }
}

@Builder tabBtn(label: string, icon: string, idx: number) {
  Column() {
    Text(icon).fontSize(this.activeTab === idx ? 22 : 18)
    Text(label).fontSize(9).margin({ top: 2 })
      .fontColor(this.activeTab === idx ? '#FF4242' : '#999999')
      .fontWeight(this.activeTab === idx ? FontWeight.Bold : FontWeight.Normal)
  }
  .layoutWeight(1).alignItems(HorizontalAlign.Center)
  .padding({ top: 4, bottom: 4 })
  .onClick(() => { this.activeTab = idx })
}

@Builder tabContent() {
  if (this.activeTab === 0) { this.groupsTab() }
  else if (this.activeTab === 1) { this.communitiesTab() }
  else if (this.activeTab === 2) { this.activitiesTab() }
  else if (this.activeTab === 3) { this.ordersTab() }
  else if (this.activeTab === 4) { this.dashboardTab() }
  else if (this.activeTab === 5) { this.rankingTab() }
  else if (this.activeTab === 6) { this.profileTab() }
}

tabBtn构建器是Tab按钮的通用模板,通过参数化设计实现了高度复用。每个Tab按钮的图标大小、文字颜色和字重都根据activeTab与当前idx的比较结果动态变化——选中时图标放大至22号字体、文字变为橙红色加粗,未选中时图标为18号字体、文字为灰色常规字重。这种视觉反馈让用户能够清晰地感知当前所处的页面。tabContent构建器通过if-else if条件链将activeTab的值映射到对应的页面构建器方法,实现了页面切换的核心逻辑。顶部Divider提供了一条细分隔线,增强了底部栏与内容区的视觉分离感。

七、拼团大厅双列网格布局

拼团大厅是应用的核心页面,采用了双列瀑布流网格布局来展示拼团商品卡片,这种布局方式在电商应用中非常普遍,能够高效利用屏幕空间。

@Builder groupsTab() {
  Scroll() {
    Column() {
      Scroll() {
        Row() {
          ForEach(['全部', ...Object.keys(CAT_CONFIG)], (cat: string) => {
            Text(cat)
              .fontSize(11)
              .fontColor(this.catFilter === cat ? '#FFFFFF' : '#FF4242')
              .backgroundColor(this.catFilter === cat ? '#FF4242' : '#FFF0EE')
              .borderRadius(14)
              .padding({ left: 10, right: 10, top: 5, bottom: 5 })
              .margin({ right: 6 })
              .onClick(() => { this.catFilter = cat })
          })
        }.padding({ left: 16, right: 16, top: 8, bottom: 8 })
      }.width('100%').scrollable(ScrollDirection.Horizontal).scrollBar(BarState.Off)

      Row() {
        Text('共' + this.getFilteredGroups().length + '个拼团进行中')
          .fontSize(11).fontColor('#999999')
        Column() {
          Text('+ 发起拼团').fontSize(12).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
        }
        .backgroundColor('#FF4242').borderRadius(20)
        .padding({ left: 16, right: 16, top: 8, bottom: 8 })
        .onClick(() => { this.showAddGroup = true })
      }
      .width('92%').justifyContent(FlexAlign.SpaceBetween)
      .padding({ left: '4%', right: '4%', top: 4, bottom: 8 })

      Row() {
        Column() {
          ForEach(this.getFilteredGroups().filter((_: GroupItem, i: number) => i % 2 === 0), (g: GroupItem) => {
            this.groupGridCard(g)
          })
        }.layoutWeight(1)

        Column() {
          ForEach(this.getFilteredGroups().filter((_: GroupItem, i: number) => i % 2 === 1), (g: GroupItem) => {
            this.groupGridCard(g)
          })
        }.layoutWeight(1)
      }.width('100%').padding({ left: 8, right: 8, bottom: 16 })
    }
  }.width('100%').layoutWeight(1).backgroundColor('#FFF5F5')
}

在这里插入图片描述

拼团大厅页面由三个部分组成。顶部是分类筛选条,通过横向滚动的Scroll容器承载"全部"以及8个分类的筛选标签,每个标签根据是否为当前选中分类显示不同的配色方案。中间是操作栏,左侧显示当前筛选后的拼团数量,右侧是"发起拼团"按钮,点击后触发showAddGroup状态为true,弹出新建拼团表单。底部是双列网格区域,通过将筛选后的数据按索引奇偶性拆分到两个Column中,实现了双列瀑布流的效果。每个商品卡片由groupGridCard构建器渲染,layoutWeight(1)确保两列等宽分布。

八、拼团商品卡片与进度条组件

商品卡片是拼团大厅中最核心的UI组件,它需要在有限的空间内展示商品图片、状态、折扣、价格、进度等丰富信息。

@Builder groupGridCard(g: GroupItem) {
  Column() {
    Column() {
      Text(g.image).fontSize(40)
      Row() {
        Text(g.status).fontSize(8).fontColor('#FFFFFF')
          .backgroundColor(getStatusColor(g.status))
          .borderRadius(8).padding({ left: 4, right: 4, top: 1, bottom: 1 })
      }
      .position({ x: 6, y: 6 })
      Column() {
        Text(getDiscountRate(g.originPrice, g.groupPrice) + '折').fontSize(9).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
      }
      .backgroundColor('#FF4242').borderRadius(8)
      .padding({ left: 6, right: 6, top: 2, bottom: 2 })
      .position({ x: 0, y: 0 })
    }
    .width('100%').height(100)
    .backgroundColor('#FFF0EE')
    .borderRadius({ topLeft: 12, topRight: 12 })
    .justifyContent(FlexAlign.Center)

    Column() {
      Text(g.title).fontSize(11).fontColor('#333333').maxLines(2)
        .textOverflow({ overflow: TextOverflow.Ellipsis })
        .margin({ bottom: 4 })

      Row() {
        Text(fmtPrice(g.groupPrice)).fontSize(13).fontColor('#FF4242').fontWeight(FontWeight.Bold)
        Text(fmtPrice(g.originPrice)).fontSize(9).fontColor('#999999')
          .decoration({ type: TextDecorationType.LineThrough }).margin({ left: 4 })
      }.margin({ bottom: 4 })

      Column() {
        Column()
          .width(getProgressPct(g) + '%').height(4)
          .backgroundColor(getProgressColor(getProgressPct(g)))
          .borderRadius(2)
      }
      .width('100%').height(4).backgroundColor('#FFE0E0').borderRadius(2)
      .margin({ bottom: 4 })

      Row() {
        Text(g.joinedCount + '/' + g.needCount + '人').fontSize(9).fontColor('#FF7733')
        Column().layoutWeight(1)
        Text('去拼团').fontSize(9).fontColor('#FFFFFF')
          .backgroundColor('#FF4242').borderRadius(8)
          .padding({ left: 8, right: 8, top: 2, bottom: 2 })
          .onClick(() => {
            this.selectedGroup = g
            this.showJoinGroup = true
          })
      }.width('100%')
    }
    .width('100%').padding({ left: 8, right: 8, top: 6, bottom: 8 })
  }
  .width('100%').backgroundColor('#FFFFFF').borderRadius(12)
  .margin({ left: 4, right: 4, bottom: 8 })
  .shadow({ radius: 4, color: '#00000010', offsetX: 0, offsetY: 2 })
  .onClick(() => {
    this.selectedGroup = g
    this.showGroupDetail = true
  })
}

在这里插入图片描述

商品卡片的视觉设计层次分明。图片区域使用position定位在左上角放置折扣角标、右上角放置状态角标,中间居中显示商品emoji图标。信息区域展示商品标题(最多两行,超出显示省略号)、拼团价和原价(带删除线装饰),以及一个动态进度条。进度条通过getProgressPct计算宽度百分比,getProgressColor根据百分比返回对应颜色,实现了进度的可视化反馈。"去拼团"按钮和卡片整体的onClick分别触发加入拼团和查看详情弹窗。shadow属性为卡片添加了微妙的阴影效果,增强了卡片的立体感和层次感。textOverflow配置确保长文本不会破坏卡片布局。

流程图:拼团交易核心流程

进度未满

已满员

用户进入拼团大厅

选择分类筛选

浏览双列商品网格

点击商品卡片

弹出拼团详情弹窗

查看拼团进度

点击立即参团

显示已成团状态

弹出加入拼团弹窗

选择购买数量

选择商品规格

确认参团下单

生成订单记录

订单追踪页面更新

数据看板刷新统计

点击发起拼团

弹出新建拼团表单

填写商品信息

确认发起

新拼团上架展示

九、社群管理与KPI数据卡片

社群管理页面采用了KPI数据卡片加列表的布局模式,通过顶部三个关键指标卡片快速传达运营概览,下方列表展示各社群的详细信息。

@Builder communitiesTab() {
  Scroll() {
    Column() {
      Row() {
        this.kpiCard('总社群', mockCommunities.length + '', '#FF4242', '#FFF0EE')
        this.kpiCard('今日下单', mockCommunities.reduce((s: number, c: CommunityItem) => s + c.todayOrders, 0) + '', '#FF7733', '#FFF3E0')
        this.kpiCard('今日营收', '¥' + fmtNum(mockCommunities.reduce((s: number, c: CommunityItem) => s + c.todayRevenue, 0)), '#4CAF50', '#E8F5E9')
      }.width('92%').padding({ left: '4%', right: '4%', top: 10, bottom: 10 })

      Column() {
        ForEach(mockCommunities, (c: CommunityItem) => {
          this.communityCard(c)
        })
      }.width('92%').padding({ left: '4%', right: '4%', bottom: 16 })
    }
  }.width('100%').layoutWeight(1).backgroundColor('#FFF5F5')
}

@Builder kpiCard(label: string, val: string, fc: string, bg: string) {
  Column() {
    Text(val).fontSize(18).fontWeight(FontWeight.Bold).fontColor(fc)
    Text(label).fontSize(9).fontColor('#999999').margin({ top: 3 })
  }
  .layoutWeight(1).backgroundColor(bg).borderRadius(12)
  .padding({ top: 12, bottom: 12 })
  .alignItems(HorizontalAlign.Center).margin({ left: 3, right: 3 })
}

在这里插入图片描述

KPI卡片区域通过三个等宽的kpiCard构建器实例展示了总社群数、今日下单数和今日营收三个核心指标。每个卡片的数值通过reduce方法对mockCommunities数组进行聚合计算得出。kpiCard构建器接收标签、值、前景色和背景色四个参数,实现了高度参数化的卡片样式。下方社群列表通过ForEach遍历mockCommunities数组,为每个社群渲染一个communityCard。社群卡片展示了社群名称、团长信息、成员数、标签、等级、状态、今日营收和订单数等丰富的运营数据,帮助管理者全面掌握各社群的运营状况。

十、数据看板与柱状图可视化

数据看板模块是整个平台数据可视化的核心,它集成了柱状图、分类销量分布和状态统计三大可视化组件。

@Builder dashboardTab() {
  Scroll() {
    Column() {
      Row() {
        this.kpiCard('拼团总数', mockGroups.length + '', '#FF4242', '#FFF0EE')
        this.kpiCard('社群数', mockCommunities.length + '', '#FF7733', '#FFF3E0')
        this.kpiCard('订单数', mockOrders.length + '', '#4CAF50', '#E8F5E9')
      }.width('92%').padding({ left: '4%', right: '4%', top: 10, bottom: 10 })

      Column() {
        Row() {
          Text('📈 本周销售趋势').fontSize(13).fontWeight(FontWeight.Bold).fontColor('#333333')
          Column().layoutWeight(1)
          Text('单位: 万元').fontSize(9).fontColor('#999999')
        }.width('100%').margin({ bottom: 12 })

        Row() {
          ForEach(['周一', '周二', '周三', '周四', '周五', '周六', '周日'], (day: string, idx: number) => {
            Column() {
              Column()
                .width(20).height(40 + idx * 12 + (idx === 5 ? 30 : 0))
                .backgroundColor(idx === 5 || idx === 6 ? '#FF4242' : '#FFB199')
                .borderRadius({ topLeft: 4, topRight: 4 })
              Text(day).fontSize(8).fontColor('#999999').margin({ top: 4 })
            }.layoutWeight(1).alignItems(HorizontalAlign.Center)
          })
        }.width('100%').height(120).justifyContent(FlexAlign.SpaceEvenly)
      }
      .width('92%').backgroundColor('#FFFFFF').borderRadius(14)
      .padding(14).margin({ left: '4%', right: '4%', bottom: 10 })
      .shadow({ radius: 6, color: '#00000008', offsetX: 0, offsetY: 2 })

      Column() {
        Text('🏷️ 分类销量分布').fontSize(13).fontWeight(FontWeight.Bold).fontColor('#333333')
          .margin({ bottom: 10 })
        ForEach(Object.keys(CAT_CONFIG), (cat: string) => {
          Row() {
            Text(getCatIcon(cat) + ' ' + cat).fontSize(10).fontColor('#666666').width(70)
            Column() {
              Column()
                .width(this.getCatCount(cat) / this.getMaxCatCount() * 100 + '%')
                .height(8).backgroundColor('#FF4242').borderRadius(4)
            }
            .layoutWeight(1).height(8).backgroundColor('#FFF0EE').borderRadius(4).margin({ left: 8, right: 8 })
            Text(this.getCatCount(cat) + '单').fontSize(9).fontColor('#999999').width(36)
          }.width('100%').margin({ bottom: 6 })
        })
      }
      .width('92%').backgroundColor('#FFFFFF').borderRadius(14)
      .padding(14).margin({ left: '4%', right: '4%', bottom: 10 })
      .shadow({ radius: 6, color: '#00000008', offsetX: 0, offsetY: 2 })
    }
  }.width('100%').layoutWeight(1).backgroundColor('#FFF5F5')
}

getCatCount(cat: string): number {
  return mockGroups.filter((g: GroupItem) => g.category === cat).length
}
getMaxCatCount(): number {
  return Math.max(...Object.keys(CAT_CONFIG).map((c: string) => this.getCatCount(c)))
}

柱状图通过ForEach遍历一周七天,为每天生成一个Column柱子,柱子高度通过公式40 + idx * 12 + (idx === 5 ? 30 : 0)计算,使得柱子随天数递增且周六有额外加成。周末柱子使用主色橙红色,工作日使用浅橙色,形成视觉对比。分类销量分布图通过横向进度条展示各分类的商品数量占比,每个进度条的宽度通过getCatCount(cat) / getMaxCatCount() * 100计算,确保最大值的分类占据100%宽度。getMaxCatCount使用Math.max和展开运算符获取所有分类中的最大商品数,作为进度条的缩放基准。整个看板区域通过白色卡片背景、圆角和阴影效果打造出数据仪表盘的专业感。

十一、排行榜与前三名展示台

排行榜页面是平台中视觉设计最丰富的页面之一,它通过前三名展示台和列表两种形式呈现排名信息。

@Builder rankingTab() {
  Scroll() {
    Column() {
      Row() {
        Column() {
          Text('🥈').fontSize(36)
          Text(mockRanks[1].name).fontSize(8).fontColor('#666666').maxLines(1)
            .textOverflow({ overflow: TextOverflow.Ellipsis }).margin({ top: 4 })
          Text('¥' + mockRanks[1].revenue + 'w').fontSize(11).fontColor('#FF9800').fontWeight(FontWeight.Bold)
        }
        .layoutWeight(1).alignItems(HorizontalAlign.Center)
        .backgroundColor('#FFF8E1').borderRadius(12)
        .padding({ top: 16, bottom: 16 })

        Column() {
          Text('🥇').fontSize(48)
          Text(mockRanks[0].name).fontSize(8).fontColor('#333333').maxLines(1)
            .textOverflow({ overflow: TextOverflow.Ellipsis }).margin({ top: 4 })
          Text('¥' + mockRanks[0].revenue + 'w').fontSize(13).fontColor('#FF4242').fontWeight(FontWeight.Bold)
        }
        .layoutWeight(1.2).alignItems(HorizontalAlign.Center)
        .backgroundColor('#FFF3E0').borderRadius(12)
        .padding({ top: 24, bottom: 16 })

        Column() {
          Text('🥉').fontSize(36)
          Text(mockRanks[2].name).fontSize(8).fontColor('#666666').maxLines(1)
            .textOverflow({ overflow: TextOverflow.Ellipsis }).margin({ top: 4 })
          Text('¥' + mockRanks[2].revenue + 'w').fontSize(11).fontColor('#8D6E63').fontWeight(FontWeight.Bold)
        }
        .layoutWeight(1).alignItems(HorizontalAlign.Center)
        .backgroundColor('#EFEBE9').borderRadius(12)
        .padding({ top: 16, bottom: 16 })
      }
      .width('92%').padding({ left: '4%', right: '4%', top: 16, bottom: 16 })

      Column() {
        ForEach(mockRanks.slice(3), (r: RankItem) => {
          this.rankRow(r)
        })
      }.width('92%').padding({ left: '4%', right: '4%', bottom: 16 })
    }
  }.width('100%').layoutWeight(1).backgroundColor('#FFF5F5')
}

@Builder rankRow(r: RankItem) {
  Row() {
    Text(r.rank.toString()).fontSize(14).fontWeight(FontWeight.Bold).fontColor('#CCCCCC').width(30)
    Column() {
      Text(r.name).fontSize(12).fontColor('#333333')
      Text(r.members + '人 · ' + r.groupCount + '团').fontSize(9).fontColor('#999999').margin({ top: 2 })
    }
    .layoutWeight(1).margin({ left: 8 }).alignItems(HorizontalAlign.Start)
    Column() {
      Text('¥' + r.revenue + 'w').fontSize(12).fontColor('#FF4242').fontWeight(FontWeight.Bold)
      Text(r.growth).fontSize(9)
        .fontColor(r.growth.startsWith('+') ? '#4CAF50' : '#F44336').margin({ top: 2 })
    }.alignItems(HorizontalAlign.End)
  }
  .width('100%').backgroundColor('#FFFFFF').borderRadius(10)
  .padding({ left: 12, right: 12, top: 10, bottom: 10 }).margin({ bottom: 6 })
  .shadow({ radius: 2, color: '#00000008', offsetX: 0, offsetY: 1 })
}

在这里插入图片描述

前三名展示台采用了经典的领奖台布局——第二名居左、第一名居中且更大更高、第三名居右。第一名使用layoutWeight(1.2)使其比两侧更宽,padding的顶部间距更大,图标字号达到48,营造出"冠军"的视觉突出感。三者的背景色分别对应银牌的浅黄色、金牌的浅橙色和铜牌的浅棕色。第4至12名通过rankRow构建器渲染为列表行,每行包含排名序号、名称、成员数与团数、营收和增长率。增长率通过startsWith('+')判断正负,正增长显示绿色,负增长显示红色,为运营者提供了直观的趋势信号。

十二、弹窗系统与表单交互

本平台实现了7种弹窗,涵盖了新建拼团、拼团详情、编辑拼团、删除确认、加入拼团、订单详情和活动详情等完整的交互场景。以下展示加入拼团弹窗的实现。

@Builder joinGroupModal() {
  if (this.showJoinGroup && this.selectedGroup) {
    Column() {
      Column().width('100%').height('100%').backgroundColor('rgba(0,0,0,0.5)')
        .onClick(() => { this.showJoinGroup = false })

      Column() {
        Row() {
          Column() {
            Text(this.selectedGroup.image).fontSize(32)
          }
          .width(56).height(56).backgroundColor('#FFF0EE').borderRadius(12)
          .justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)

          Column() {
            Text(fmtPrice(this.selectedGroup.groupPrice)).fontSize(16).fontWeight(FontWeight.Bold).fontColor('#FF4242')
            Text('已选: ' + this.joinQuantity + '件').fontSize(10).fontColor('#999999').margin({ top: 4 })
          }
          .layoutWeight(1).margin({ left: 10 }).alignItems(HorizontalAlign.Start)

          Text('✕').fontSize(18).fontColor('#999999')
            .onClick(() => { this.showJoinGroup = false })
        }.width('90%').padding({ top: 16, bottom: 12 })

        Divider().width('90%').height(0.5).backgroundColor('#FFF0EE')

        Row() {
          Text('购买数量').fontSize(13).fontColor('#333333')
          Column().layoutWeight(1)
          Row() {
            Text('−').fontSize(16).fontColor('#FF4242')
              .width(32).height(32).backgroundColor('#FFF5F5')
              .borderRadius({ topLeft: 8, bottomLeft: 8 })
              .textAlign(TextAlign.Center)
              .onClick(() => { if (this.joinQuantity > 1) this.joinQuantity-- })
            Text(this.joinQuantity.toString()).fontSize(14)
              .width(40).height(32).backgroundColor('#FFFFFF')
              .textAlign(TextAlign.Center)
            Text('+').fontSize(16).fontColor('#FF4242')
              .width(32).height(32).backgroundColor('#FFF5F5')
              .borderRadius({ topRight: 8, bottomRight: 8 })
              .textAlign(TextAlign.Center)
              .onClick(() => { this.joinQuantity++ })
          }
        }.width('90%').margin({ top: 16, bottom: 16 })

        Row() {
          Column() {
            Text('合计').fontSize(10).fontColor('#999999')
            Text(fmtPrice(this.selectedGroup.groupPrice * this.joinQuantity))
              .fontSize(16).fontWeight(FontWeight.Bold).fontColor('#FF4242')
          }.alignItems(HorizontalAlign.Start)
          Column().layoutWeight(1)
          Text('确认参团').fontSize(14).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
            .backgroundColor('#FF4242').borderRadius(20)
            .padding({ left: 28, right: 28, top: 12, bottom: 12 })
            .onClick(() => { this.showJoinGroup = false })
        }.width('90%').margin({ bottom: 20 })
      }
      .width('88%').backgroundColor('#FFFFFF').borderRadius(20)
      .position({ x: '6%', y: '30%' })
    }
    .width('100%').height('100%').position({ x: 0, y: 0 }).zIndex(999)
  }
}

在这里插入图片描述

弹窗系统采用统一的架构模式:外层Column作为全屏遮罩容器,通过半透明黑色背景和点击关闭实现遮罩交互。内层Column作为弹窗主体,通过positionzIndex(999)定位在屏幕上层。加入拼团弹窗包含商品头部、数量选择器、规格选择和底部价格汇总四个区域。数量选择器通过"−"和"+"按钮实现数量的增减,减号按钮包含if (this.joinQuantity > 1)的保护逻辑防止数量减至0以下。合计价格通过selectedGroup.groupPrice * joinQuantity实时计算,随着数量变化自动更新,充分体现了ArkTS状态驱动UI的特性。所有弹窗都遵循"条件渲染 + 遮罩层 + 主体层"的三段式结构,保证了交互体验的一致性。

十三、应用入口与组件组装

build() {
  Column() {
    this.headerSection()
    this.tabContent()
    this.bottomTabBar()
    this.addGroupModal()
    this.groupDetailModal()
    this.editGroupModal()
    this.deleteConfirmModal()
    this.joinGroupModal()
    this.orderDetailModal()
    this.actDetailModal()
  }
  .width('100%').height('100%')
  .backgroundColor('#FFF5F5')
}

在这里插入图片描述

build方法是组件的入口,它将所有构建器方法按顺序组装。头部、内容区和底部Tab栏构成应用的主体结构,7个弹窗构建器紧随其后。虽然弹窗在视觉上是浮动的,但在结构上它们与主体平级,通过自身的positionzIndex属性实现层叠效果。整个应用以浅粉色作为基础背景色,通过width('100%')height('100%')确保填满整个屏幕空间。这种将复杂界面拆分为独立构建器的方法,使得每个部分可以独立开发和维护,极大提升了代码的可读性和可维护性。

核心技术点对比总结

技术维度 实现方式 关键API/装饰器 设计特点 适用场景
状态管理 @State装饰器管理组件级状态 @State、@Entry、@Component 响应式数据绑定,状态变化自动触发UI更新 Tab切换、弹窗显隐、筛选条件
UI构建 @Builder方法拆分界面 @Builder、ForEach 声明式UI组装,参数化复用,代码结构清晰 商品卡片、KPI卡片、Tab按钮
布局系统 Column/Row/Scroll组合 layoutWeight、FlexAlign 弹性布局+滚动容器,支持瀑布流双列网格 双列商品网格、横向滚动筛选
数据过滤 纯函数+数组方法 filter、reduce、map 无副作用函数封装,支持链式调用 商品搜索分类、订单状态筛选
视觉映射 配置对象+查询函数 Record、空值合并运算符?? 配置与逻辑分离,默认值兜底防崩溃 状态颜色、分类图标、等级样式
弹窗系统 条件渲染+遮罩层 if、position、zIndex 三段式弹窗架构,点击遮罩关闭 新建拼团、详情查看、表单编辑
数据可视化 Column高度模拟图表 height、backgroundColor、borderRadius 纯ArkTS组件实现柱状图和进度条 销售趋势、分类销量、拼团进度
文本溢出 maxLines+textOverflow maxLines、TextOverflow.Ellipsis 防止长文本破坏布局,显示省略号 商品标题、地址、社群名称
阴影效果 shadow属性 radius、color、offsetX、offsetY 轻量级阴影提升卡片立体感 商品卡片、数据面板、列表项
进度条 动态宽度+颜色分级 width百分比、条件颜色 根据进度自动变色,视觉反馈直观 拼团进度、预算使用率、活动参与度

安装DevEco Studio程序

在这里插入图片描述
选择目标安装目录:

在这里插入图片描述
设置环境变量,但是需要重启一下:

在这里插入图片描述
新建一个空白模板:

在这里插入图片描述
设置API为24的模板项目:
在这里插入图片描述
初始化项目,自动下载相关依赖:

在这里插入图片描述


完整代码:

// 场景:拼多多风格 / 社群拼团 / 优惠活动 / 订单追踪 / 数据看板
// 视觉:橙红 #FF4242(主色)+ 金橙 #FF7733(强调)+ 浅粉 #FFF5F5(背景)
// 布局:底部7Tab + 各Tab差异化布局 + 5类弹框 + 进度条/柱状图特效
// 编译合规:无Blank / Scroll单子组件 / 无.maxHeight / border简写 / Button无文字

// ============ 类型定义 ============
interface GroupItem {
  id: number; title: string; category: string; originPrice: number
  groupPrice: number; needCount: number; joinedCount: number
  endTime: string; status: string; image: string; description: string
  tags: string[]; shopName: string; rating: number; salesCount: number
}
interface CommunityItem {
  id: number; name: string; memberCount: number; todayOrders: number
  todayRevenue: number; level: string; leaderName: string; createTime: string
  tag: string; status: string
}
interface OrderItem {
  id: number; groupTitle: string; buyerName: string; phone: string
  quantity: number; totalPrice: number; status: string; createTime: string
  address: string; remark: string
}
interface ActivityItem {
  id: number; title: string; type: string; discount: string
  startTime: string; endTime: string; budget: number; used: number
  status: string; joinCount: number; description: string
}
interface RankItem {
  rank: number; name: string; avatar: string; groupCount: number
  revenue: number; members: number; growth: string
}

// ============ 静态配置 ============
const CAT_CONFIG: Record<string, string> = {
  '生鲜': '🥬', '零食': '🍪', '日用': '🧴', '美妆': '💄',
  '数码': '📱', '服饰': '👕', '家居': '🛋️', '母婴': '🍼',
}
const STATUS_CONFIG: Record<string, string> = {
  '拼团中': '#FF4242', '已成团': '#4CAF50', '未成团': '#9E9E9E',
  '已结束': '#BDBDBD', '即将开团': '#FF9800',
}
const ORDER_STATUS_CONFIG: Record<string, string> = {
  '待付款': '#FF9800', '待发货': '#2196F3', '已发货': '#4CAF50',
  '已完成': '#9E9E9E', '退款中': '#F44336', '已退款': '#9C27B0',
}
const ACTIVITY_TYPE_CONFIG: Record<string, string> = {
  '满减': '💰', '折扣': '🏷️', '秒杀': '⚡', '新人': '🎁', '红包': '🧧',
}
interface LevelMeta {
  color: string;
  bg: string;
}
const LEVEL_CONFIG: Record<string, LevelMeta> = {
  '钻石': { color: '#9C27B0', bg: '#F3E5F5' },
  '金牌': { color: '#FF9800', bg: '#FFF3E0' },
  '银牌': { color: '#757575', bg: '#F5F5F5' },
  '铜牌': { color: '#8D6E63', bg: '#EFEBE9' },
}

// ============ 工具函数 ============
function fmtPrice(p: number): string { return '¥' + (p / 100).toFixed(2) }
function fmtNum(n: number): string { return n >= 10000 ? (n / 10000).toFixed(1) + 'w' : n.toString() }
function fmtDate(d: string): string { return d.substring(5) }
function getStatusColor(s: string): string { return STATUS_CONFIG[s] ?? '#9E9E9E' }
function getOrderStatusColor(s: string): string { return ORDER_STATUS_CONFIG[s] ?? '#9E9E9E' }
function getCatIcon(c: string): string { return CAT_CONFIG[c] ?? '📦' }
function getActTypeIcon(t: string): string { return ACTIVITY_TYPE_CONFIG[t] ?? '📌' }
function getLevelMeta(l: string): LevelMeta {
  return LEVEL_CONFIG[l] ?? { color: '#757575', bg: '#F5F5F5' }
}
function getProgressPct(item: GroupItem): number {
  return Math.min(100, Math.round(item.joinedCount / item.needCount * 100))
}
function getProgressColor(pct: number): string {
  if (pct >= 100) return '#4CAF50'
  if (pct >= 60) return '#FF9800'
  if (pct >= 30) return '#FF4242'
  return '#FF7733'
}
function getDiscountRate(origin: number, group: number): number {
  return Math.round(group / origin * 100) / 10
}

// ============ 模拟数据 ============
const mockGroups: GroupItem[] = [
  { id: 1, title: '新疆阿克苏苹果 5斤装 顺丰冷链', category: '生鲜', originPrice: 5900, groupPrice: 2990, needCount: 50, joinedCount: 38, endTime: '2026-08-25', status: '拼团中', image: '🍎', description: '阿克苏冰糖心苹果,光照充足,甜度爆表,坏果包赔。', tags: ['顺丰冷链', '坏果包赔', '产地直发'], shopName: '果农直供旗舰店', rating: 4.8, salesCount: 12850 },
  { id: 2, title: '三只松鼠每日坚果750g 混合装', category: '零食', originPrice: 9900, groupPrice: 4990, needCount: 100, joinedCount: 92, endTime: '2026-08-24', status: '即将开团', image: '🥜', description: '每日坚果750g,30小包独立包装,科学配比。', tags: ['独立包装', '科学配比', '送礼佳品'], shopName: '三只松鼠官方', rating: 4.9, salesCount: 35600 },
  { id: 3, title: '维达纸巾抽纸 24包整箱家用', category: '日用', originPrice: 8900, groupPrice: 3990, needCount: 200, joinedCount: 200, endTime: '2026-08-22', status: '已成团', image: '🧻', description: '24包抽纸整箱发货,3层加厚,湿水不破。', tags: ['3层加厚', '整箱发货', '湿水不破'], shopName: '维达自营店', rating: 4.7, salesCount: 89200 },
  { id: 4, title: '完美日记小细管唇釉 限定色号', category: '美妆', originPrice: 12900, groupPrice: 6900, needCount: 30, joinedCount: 15, endTime: '2026-08-26', status: '拼团中', image: '💄', description: '小细管唇釉,丝绒哑光质地,持久不脱色。', tags: ['丝绒哑光', '持久不脱色', '限定色号'], shopName: '完美日记旗舰店', rating: 4.6, salesCount: 23400 },
  { id: 5, title: '小米Redmi Note 13 Pro 8+256G', category: '数码', originPrice: 159900, groupPrice: 129900, needCount: 20, joinedCount: 8, endTime: '2026-08-28', status: '拼团中', image: '📱', description: 'Redmi Note 13 Pro,2亿像素主摄,骁龙7s Gen2。', tags: ['2亿像素', '骁龙7s', '官方质保'], shopName: '小米官方旗舰店', rating: 4.9, salesCount: 15800 },
  { id: 6, title: '优衣库同款纯棉T恤 多色可选', category: '服饰', originPrice: 9900, groupPrice: 3990, needCount: 50, joinedCount: 45, endTime: '2026-08-25', status: '拼团中', image: '👕', description: '纯棉T恤,20种颜色可选,宽松版型,百搭款。', tags: ['纯棉面料', '20色可选', '宽松版型'], shopName: '潮牌工厂店', rating: 4.5, salesCount: 45600 },
  { id: 7, title: '北欧风ins北欧简约四件套', category: '家居', originPrice: 29900, groupPrice: 12900, needCount: 40, joinedCount: 22, endTime: '2026-08-27', status: '拼团中', image: '🛏️', description: '北欧风四件套,60支全棉贡缎,亲肤透气。', tags: ['60支全棉', '贡缎面料', '亲肤透气'], shopName: '家纺源头工厂', rating: 4.7, salesCount: 12300 },
  { id: 8, title: '帮宝适纸尿裤 NB号 96片装', category: '母婴', originPrice: 15900, groupPrice: 8990, needCount: 60, joinedCount: 55, endTime: '2026-08-25', status: '拼团中', image: '🍼', description: '帮宝适一级帮,超薄透气,日本进口吸水珠珠。', tags: ['日本进口', '超薄透气', '96片装'], shopName: '帮宝适官方店', rating: 4.8, salesCount: 67800 },
  { id: 9, title: '赣南脐橙 10斤精品果 产地直发', category: '生鲜', originPrice: 7900, groupPrice: 3990, needCount: 80, joinedCount: 80, endTime: '2026-08-20', status: '已成团', image: '🍊', description: '赣南脐橙,皮薄多汁,甜度14°以上,产地直发。', tags: ['产地直发', '甜度14°', '坏果包赔'], shopName: '赣南果业', rating: 4.9, salesCount: 98700 },
  { id: 10, title: '良品铺子肉松饼 1kg 整箱装', category: '零食', originPrice: 6900, groupPrice: 2990, needCount: 100, joinedCount: 67, endTime: '2026-08-26', status: '拼团中', image: '🍪', description: '肉松饼1kg整箱,酥脆饼皮,满满肉松馅。', tags: ['整箱装', '酥脆饼皮', '满满肉松'], shopName: '良品铺子官方', rating: 4.7, salesCount: 54300 },
  { id: 11, title: '蓝月亮洗手液 抑菌型 3瓶装', category: '日用', originPrice: 5900, groupPrice: 2590, needCount: 150, joinedCount: 98, endTime: '2026-08-27', status: '拼团中', image: '🧴', description: '蓝月亮抑菌洗手液,3瓶装,温和不伤手。', tags: ['抑菌型', '3瓶装', '温和不伤手'], shopName: '蓝月亮自营', rating: 4.6, salesCount: 76500 },
  { id: 12, title: '花西子散粉蜜粉 持久控油', category: '美妆', originPrice: 18900, groupPrice: 9900, needCount: 30, joinedCount: 12, endTime: '2026-08-29', status: '拼团中', image: '🌸', description: '花西子散粉,持久控油12小时,细腻不卡粉。', tags: ['持久控油', '12小时', '细腻不卡粉'], shopName: '花西子旗舰店', rating: 4.8, salesCount: 32100 },
  { id: 13, title: '华为FreeBuds Pro 3 降噪耳机', category: '数码', originPrice: 149900, groupPrice: 109900, needCount: 15, joinedCount: 6, endTime: '2026-08-30', status: '拼团中', image: '🎧', description: '华为FreeBuds Pro 3,智慧降噪,双设备连接。', tags: ['智慧降噪', '双设备连接', '官方质保'], shopName: '华为官方店', rating: 4.9, salesCount: 23400 },
  { id: 14, title: '南极人男士冰丝内裤 5条装', category: '服饰', originPrice: 7900, groupPrice: 2990, needCount: 100, joinedCount: 88, endTime: '2026-08-26', status: '拼团中', image: '🩲', description: '冰丝内裤5条装,透气凉爽,抗菌裆设计。', tags: ['冰丝面料', '5条装', '抗菌裆'], shopName: '南极人工厂店', rating: 4.4, salesCount: 88900 },
  { id: 15, title: '懒人沙发 北欧风单人豆袋沙发', category: '家居', originPrice: 59900, groupPrice: 25900, needCount: 20, joinedCount: 10, endTime: '2026-08-28', status: '拼团中', image: '🛋️', description: '懒人豆袋沙发,EPP填充,可坐可躺,多色可选。', tags: ['EPP填充', '可坐可躺', '多色可选'], shopName: '家具工厂直供', rating: 4.5, salesCount: 8900 },
  { id: 16, title: '飞鹤星飞帆奶粉 3段 800g', category: '母婴', originPrice: 29900, groupPrice: 19900, needCount: 40, joinedCount: 35, endTime: '2026-08-25', status: '拼团中', image: '🍼', description: '飞鹤星飞帆3段,新鲜生牛乳,适合12-36月龄。', tags: ['生牛乳', '3段', '12-36月龄'], shopName: '飞鹤官方店', rating: 4.9, salesCount: 45600 },
  { id: 17, title: '百草味芒果干 250g 大袋装', category: '零食', originPrice: 3900, groupPrice: 1590, needCount: 200, joinedCount: 156, endTime: '2026-08-24', status: '拼团中', image: '🥭', description: '百草味芒果干,泰国大青芒,果肉厚实,酸甜可口。', tags: ['泰国大青芒', '果肉厚实', '酸甜可口'], shopName: '百草味旗舰店', rating: 4.6, salesCount: 67800 },
  { id: 18, title: '舒肤佳香皂 3块装 抑菌除菌', category: '日用', originPrice: 2900, groupPrice: 1290, needCount: 300, joinedCount: 210, endTime: '2026-08-25', status: '拼团中', image: '🧼', description: '舒肤佳香皂3块装,抑菌除菌,温和洁净。', tags: ['抑菌除菌', '3块装', '温和洁净'], shopName: '舒肤佳官方', rating: 4.7, salesCount: 156000 },
  { id: 19, title: 'OPPO Watch 4 Pro 智能手表', category: '数码', originPrice: 229900, groupPrice: 179900, needCount: 10, joinedCount: 4, endTime: '2026-09-01', status: '拼团中', image: '⌚', description: 'OPPO Watch 4 Pro,1.91英寸AMOLED屏,独立eSIM。', tags: ['AMOLED', '独立eSIM', '14天续航'], shopName: 'OPPO官方店', rating: 4.8, salesCount: 5600 },
  { id: 20, title: '罗莱家纺乳胶枕 颈椎枕', category: '家居', originPrice: 19900, groupPrice: 8990, needCount: 50, joinedCount: 28, endTime: '2026-08-27', status: '拼团中', image: '💤', description: '泰国乳胶枕,人体工学设计,保护颈椎,透气防螨。', tags: ['泰国乳胶', '人体工学', '防螨透气'], shopName: '罗莱家纺', rating: 4.6, salesCount: 23400 },
]

const mockCommunities: CommunityItem[] = [
  { id: 1, name: '阳光小区拼团群', memberCount: 486, todayOrders: 23, todayRevenue: 89200, level: '钻石', leaderName: '王芳', createTime: '2026-03-15', tag: '生鲜专区', status: '活跃' },
  { id: 2, name: '锦绣花园团购群', memberCount: 352, todayOrders: 18, todayRevenue: 65400, level: '钻石', leaderName: '李强', createTime: '2026-04-01', tag: '日用专区', status: '活跃' },
  { id: 3, name: '万科城拼团福利群', memberCount: 620, todayOrders: 35, todayRevenue: 125800, level: '钻石', leaderName: '张敏', createTime: '2026-02-20', tag: '综合', status: '活跃' },
  { id: 4, name: '保利春天团购群', memberCount: 285, todayOrders: 12, todayRevenue: 32600, level: '金牌', leaderName: '陈丽', createTime: '2026-05-10', tag: '美妆专区', status: '活跃' },
  { id: 5, name: '碧桂园邻里拼团', memberCount: 198, todayOrders: 8, todayRevenue: 18900, level: '金牌', leaderName: '刘伟', createTime: '2026-05-22', tag: '零食专区', status: '一般' },
  { id: 6, name: '融创玖玺台拼团群', memberCount: 412, todayOrders: 20, todayRevenue: 76500, level: '钻石', leaderName: '赵琳', createTime: '2026-01-15', tag: '数码专区', status: '活跃' },
  { id: 7, name: '中海国际社区拼团', memberCount: 534, todayOrders: 28, todayRevenue: 98300, level: '钻石', leaderName: '孙浩', createTime: '2026-03-08', tag: '综合', status: '活跃' },
  { id: 8, name: '龙湖天街团购群', memberCount: 167, todayOrders: 5, todayRevenue: 12300, level: '银牌', leaderName: '周婷', createTime: '2026-06-01', tag: '母婴专区', status: '一般' },
  { id: 9, name: '绿地世纪拼团福利', memberCount: 378, todayOrders: 15, todayRevenue: 45600, level: '金牌', leaderName: '吴峰', createTime: '2026-04-18', tag: '服饰专区', status: '活跃' },
  { id: 10, name: '华润置地拼团群', memberCount: 445, todayOrders: 22, todayRevenue: 82100, level: '钻石', leaderName: '郑华', createTime: '2026-02-28', tag: '综合', status: '活跃' },
  { id: 11, name: '金地名峰邻里团', memberCount: 132, todayOrders: 3, todayRevenue: 8900, level: '银牌', leaderName: '冯静', createTime: '2026-06-15', tag: '生鲜专区', status: '一般' },
  { id: 12, name: '招商雍景湾拼团', memberCount: 298, todayOrders: 11, todayRevenue: 34500, level: '金牌', leaderName: '何明', createTime: '2026-05-05', tag: '日用专区', status: '活跃' },
]

const mockOrders: OrderItem[] = [
  { id: 1, groupTitle: '新疆阿克苏苹果 5斤装', buyerName: '张三', phone: '138****6688', quantity: 2, totalPrice: 5980, status: '已发货', createTime: '2026-08-22', address: '上海市浦东新区张江路100号', remark: '请放快递柜' },
  { id: 2, groupTitle: '三只松鼠每日坚果750g', buyerName: '李四', phone: '139****2233', quantity: 1, totalPrice: 4990, status: '待发货', createTime: '2026-08-23', address: '北京市海淀区中关村大街5号', remark: '' },
  { id: 3, groupTitle: '维达纸巾抽纸 24包', buyerName: '王五', phone: '137****4455', quantity: 3, totalPrice: 11970, status: '已完成', createTime: '2026-08-20', address: '广州市天河区珠江新城A座', remark: '工作日送达' },
  { id: 4, groupTitle: '完美日记小细管唇釉', buyerName: '赵六', phone: '136****7788', quantity: 1, totalPrice: 6900, status: '待付款', createTime: '2026-08-24', address: '深圳市南山区科技园B栋', remark: '' },
  { id: 5, groupTitle: '小米Redmi Note 13 Pro', buyerName: '钱七', phone: '135****9900', quantity: 1, totalPrice: 129900, status: '已发货', createTime: '2026-08-21', address: '杭州市西湖区文三路88号', remark: '贵重物品请当面签收' },
  { id: 6, groupTitle: '优衣库同款纯棉T恤', buyerName: '孙八', phone: '134****1122', quantity: 2, totalPrice: 7980, status: '已完成', createTime: '2026-08-19', address: '成都市武侯区天府大道200号', remark: '' },
  { id: 7, groupTitle: '北欧风ins简约四件套', buyerName: '周九', phone: '133****3344', quantity: 1, totalPrice: 12900, status: '退款中', createTime: '2026-08-22', address: '武汉市江汉区解放大道500号', remark: '颜色发错要求退款' },
  { id: 8, groupTitle: '帮宝适纸尿裤 NB号', buyerName: '吴十', phone: '132****5566', quantity: 2, totalPrice: 17980, status: '待发货', createTime: '2026-08-24', address: '南京市鼓楼区中山路100号', remark: '急需请尽快发货' },
  { id: 9, groupTitle: '赣南脐橙 10斤精品果', buyerName: '郑十一', phone: '131****7788', quantity: 2, totalPrice: 7980, status: '已发货', createTime: '2026-08-21', address: '重庆市渝北区新南路66号', remark: '' },
  { id: 10, groupTitle: '良品铺子肉松饼 1kg', buyerName: '王芳', phone: '130****9900', quantity: 3, totalPrice: 8970, status: '已完成', createTime: '2026-08-18', address: '西安市雁塔区高新路88号', remark: '' },
  { id: 11, groupTitle: '蓝月亮洗手液 3瓶装', buyerName: '李强', phone: '138****1234', quantity: 2, totalPrice: 5180, status: '待付款', createTime: '2026-08-24', address: '苏州市工业园区现代大道188号', remark: '' },
  { id: 12, groupTitle: '花西子散粉蜜粉', buyerName: '张敏', phone: '139****5678', quantity: 1, totalPrice: 9900, status: '已退款', createTime: '2026-08-20', address: '青岛市市南区香港中路20号', remark: '过敏退款' },
  { id: 13, groupTitle: '华为FreeBuds Pro 3', buyerName: '陈丽', phone: '137****9012', quantity: 1, totalPrice: 109900, status: '已发货', createTime: '2026-08-22', address: '天津市河西区友谊路30号', remark: '请包装加固' },
  { id: 14, groupTitle: '南极人男士冰丝内裤 5条', buyerName: '刘伟', phone: '136****3456', quantity: 2, totalPrice: 5980, status: '已完成', createTime: '2026-08-17', address: '长沙市岳麓区麓山南路9号', remark: '' },
  { id: 15, groupTitle: '懒人沙发 北欧风豆袋', buyerName: '赵琳', phone: '135****7890', quantity: 1, totalPrice: 25900, status: '待发货', createTime: '2026-08-24', address: '郑州市金水区花园路100号', remark: '周末送达' },
  { id: 16, groupTitle: '飞鹤星飞帆奶粉 3段', buyerName: '孙浩', phone: '134****2345', quantity: 2, totalPrice: 39800, status: '已发货', createTime: '2026-08-22', address: '沈阳市和平区中华路65号', remark: '保质期请确认' },
  { id: 17, groupTitle: '百草味芒果干 250g', buyerName: '周婷', phone: '133****6789', quantity: 5, totalPrice: 7950, status: '已完成', createTime: '2026-08-18', address: '哈尔滨市南岗区中山路180号', remark: '' },
  { id: 18, groupTitle: '舒肤佳香皂 3块装', buyerName: '吴峰', phone: '132****1234', quantity: 4, totalPrice: 5160, status: '待付款', createTime: '2026-08-24', address: '长春市朝阳区西安大路8号', remark: '' },
  { id: 19, groupTitle: 'OPPO Watch 4 Pro', buyerName: '郑华', phone: '131****5678', quantity: 1, totalPrice: 179900, status: '待发货', createTime: '2026-08-24', address: '大连市中山区人民路15号', remark: '需要发票' },
  { id: 20, groupTitle: '罗莱家纺乳胶枕', buyerName: '冯静', phone: '130****9012', quantity: 2, totalPrice: 17980, status: '已发货', createTime: '2026-08-21', address: '昆明市盘龙区北京路428号', remark: '' },
]

const mockActivities: ActivityItem[] = [
  { id: 1, title: '新人专享满50减20', type: '新人', discount: '满50减20', startTime: '2026-08-01', endTime: '2026-08-31', budget: 50000, used: 38600, status: '进行中', joinCount: 3250, description: '新用户首单专享,满50元立减20元,每人限用1次。' },
  { id: 2, title: '生鲜专区5折秒杀', type: '秒杀', discount: '5折', startTime: '2026-08-24', endTime: '2026-08-24', budget: 30000, used: 18500, status: '进行中', joinCount: 890, description: '生鲜专区限时5折,每日10点/14点/20点开抢。' },
  { id: 3, title: '全场满100减30红包雨', type: '红包', discount: '满100减30', startTime: '2026-08-20', endTime: '2026-08-26', budget: 80000, used: 52300, status: '进行中', joinCount: 5600, description: '每日整点抢红包,满100减30,可与拼团叠加使用。' },
  { id: 4, title: '美妆专区8.8折优惠', type: '折扣', discount: '8.8折', startTime: '2026-08-15', endTime: '2026-08-30', budget: 20000, used: 8900, status: '进行中', joinCount: 1200, description: '美妆专区全场8.8折,不与其他优惠叠加。' },
  { id: 5, title: '满200减50大促', type: '满减', discount: '满200减50', startTime: '2026-08-10', endTime: '2026-09-10', budget: 100000, used: 76800, status: '进行中', joinCount: 8900, description: '全场满200减50,无上限叠加,多买多减。' },
  { id: 6, title: '数码专区限时满减', type: '满减', discount: '满1000减100', startTime: '2026-08-22', endTime: '2026-08-28', budget: 50000, used: 34500, status: '进行中', joinCount: 680, description: '数码专区满1000减100,每日限量500张。' },
  { id: 7, title: '母婴专区新人立减', type: '新人', discount: '立减15', startTime: '2026-08-01', endTime: '2026-08-31', budget: 15000, used: 9800, status: '进行中', joinCount: 760, description: '母婴专区新用户立减15元,无门槛使用。' },
  { id: 8, title: '夏季清仓3折起秒杀', type: '秒杀', discount: '3折起', startTime: '2026-08-20', endTime: '2026-08-25', budget: 40000, used: 31200, status: '进行中', joinCount: 2300, description: '夏季清仓3折起,每日20点限时秒杀。' },
  { id: 9, title: '家居满300减80', type: '满减', discount: '满300减80', startTime: '2026-08-15', endTime: '2026-09-15', budget: 60000, used: 42300, status: '进行中', joinCount: 3400, description: '家居专区满300减80,叠加拼团更优惠。' },
  { id: 10, title: '服饰专区买二送一', type: '折扣', discount: '买二送一', startTime: '2026-08-18', endTime: '2026-09-18', budget: 35000, used: 15600, status: '进行中', joinCount: 1800, description: '服饰专区买二送一,同类商品可凑单。' },
]

const mockRanks: RankItem[] = [
  { rank: 1, name: '万科城拼团福利群', avatar: '🏆', groupCount: 356, revenue: 2856, members: 620, growth: '+12.5%' },
  { rank: 2, name: '中海国际社区拼团', avatar: '🥈', groupCount: 298, revenue: 2456, members: 534, growth: '+8.3%' },
  { rank: 3, name: '华润置地拼团群', avatar: '🥉', groupCount: 265, revenue: 2186, members: 445, growth: '+6.7%' },
  { rank: 4, name: '阳光小区拼团群', avatar: '4', groupCount: 234, revenue: 1892, members: 486, growth: '+5.2%' },
  { rank: 5, name: '融创玖玺台拼团群', avatar: '5', groupCount: 198, revenue: 1654, members: 412, growth: '+4.1%' },
  { rank: 6, name: '锦绣花园团购群', avatar: '6', groupCount: 176, revenue: 1432, members: 352, growth: '+3.5%' },
  { rank: 7, name: '绿地世纪拼团福利', avatar: '7', groupCount: 156, revenue: 1256, members: 378, growth: '+2.8%' },
  { rank: 8, name: '保利春天团购群', avatar: '8', groupCount: 128, revenue: 1056, members: 285, growth: '+1.9%' },
  { rank: 9, name: '招商雍景湾拼团', avatar: '9', groupCount: 98, revenue: 856, members: 298, growth: '+0.8%' },
  { rank: 10, name: '碧桂园邻里拼团', avatar: '10', groupCount: 76, revenue: 654, members: 198, growth: '-1.2%' },
  { rank: 11, name: '龙湖天街团购群', avatar: '11', groupCount: 52, revenue: 432, members: 167, growth: '-2.5%' },
  { rank: 12, name: '金地名峰邻里团', avatar: '12', groupCount: 38, revenue: 312, members: 132, growth: '-3.8%' },
]

// ============ 主组件 ============
@Entry
@Component
struct GroupBuyHubApp {
  @State activeTab: number = 0
  @State searchKw: string = ''
  @State catFilter: string = '全部'
  @State orderFilter: string = '全部'
  @State selectedGroup: GroupItem | null = null
  @State selectedOrder: OrderItem | null = null
  @State selectedActivity: ActivityItem | null = null
  @State showAddGroup: boolean = false
  @State showEditGroup: boolean = false
  @State showDeleteConfirm: boolean = false
  @State showGroupDetail: boolean = false
  @State showJoinGroup: boolean = false
  @State showOrderDetail: boolean = false
  @State showActDetail: boolean = false
  @State deleteTargetId: number = -1
  @State joinQuantity: number = 1

  // ============ @Builder 顶部电商风格头部 ============
  @Builder headerSection() {
    Column() {
      Row() {
        Column() {
          Text('🔥 拼团')
            .fontSize(20).fontWeight(FontWeight.Bold).fontColor('#FFFFFF')
          Text('社群拼团管理平台')
            .fontSize(10).fontColor('#FFD0CC').margin({ top: 2 })
        }.alignItems(HorizontalAlign.Start)

        Row() {
          Text('🔍').fontSize(16)
          TextInput({ placeholder: '搜索拼团商品…' })
            .placeholderColor('#FFCCC0').fontSize(12).layoutWeight(1)
            .backgroundColor('transparent').borderWidth(0)
            .onChange((v: string) => { this.searchKw = v })
        }
        .width('55%').backgroundColor('#FFFFFF').borderRadius(20)
        .padding({ left: 12, right: 12, top: 6, bottom: 6 })
        .margin({ left: 12 })

        Text('🔔').fontSize(18).fontColor('#FFFFFF').margin({ left: 8 })
      }
      .width('100%').padding({ left: 16, right: 16, top: 14, bottom: 12 })
      .backgroundColor('#FF4242')

      // 分类快捷入口
      Scroll() {
        Row() {
          ForEach(Object.keys(CAT_CONFIG), (cat: string) => {
            Column() {
              Text(getCatIcon(cat)).fontSize(22)
              Text(cat).fontSize(9).fontColor('#666666').margin({ top: 4 })
            }
            .width(58).alignItems(HorizontalAlign.Center)
            .onClick(() => { this.catFilter = cat; this.activeTab = 0 })
          })
        }
        .padding({ left: 12, right: 12, top: 10, bottom: 10 })
      }
      .width('100%').scrollable(ScrollDirection.Horizontal).scrollBar(BarState.Off)
      .backgroundColor('#FFFFFF')
    }
  }

  // ============ @Builder Tab切换内容区 ============
  @Builder tabContent() {
    if (this.activeTab === 0) { this.groupsTab() }
    else if (this.activeTab === 1) { this.communitiesTab() }
    else if (this.activeTab === 2) { this.activitiesTab() }
    else if (this.activeTab === 3) { this.ordersTab() }
    else if (this.activeTab === 4) { this.dashboardTab() }
    else if (this.activeTab === 5) { this.rankingTab() }
    else if (this.activeTab === 6) { this.profileTab() }
  }

  // ============ @Builder 底部Tab栏 ============
  @Builder bottomTabBar() {
    Column() {
      Divider().width('100%').height(0.5).backgroundColor('#FFE0E0')
      Row() {
        this.tabBtn('拼团', '🛒', 0)
        this.tabBtn('社群', '👥', 1)
        this.tabBtn('活动', '🎉', 2)
        this.tabBtn('订单', '📦', 3)
        this.tabBtn('看板', '📊', 4)
        this.tabBtn('排行', '🏆', 5)
        this.tabBtn('我的', '👤', 6)
      }
      .width('100%').padding({ top: 6, bottom: 4 })
      .backgroundColor('#FFFFFF')
    }
  }

  @Builder tabBtn(label: string, icon: string, idx: number) {
    Column() {
      Text(icon).fontSize(this.activeTab === idx ? 22 : 18)
      Text(label).fontSize(9).margin({ top: 2 })
        .fontColor(this.activeTab === idx ? '#FF4242' : '#999999')
        .fontWeight(this.activeTab === idx ? FontWeight.Bold : FontWeight.Normal)
    }
    .layoutWeight(1).alignItems(HorizontalAlign.Center)
    .padding({ top: 4, bottom: 4 })
    .onClick(() => { this.activeTab = idx })
  }

  // ============ Tab0: 拼团大厅(双列网格布局) ============
  @Builder groupsTab() {
    Scroll() {
      Column() {
        // 筛选条
        Scroll() {
          Row() {
            ForEach(['全部', ...Object.keys(CAT_CONFIG)], (cat: string) => {
              Text(cat)
                .fontSize(11)
                .fontColor(this.catFilter === cat ? '#FFFFFF' : '#FF4242')
                .backgroundColor(this.catFilter === cat ? '#FF4242' : '#FFF0EE')
                .borderRadius(14)
                .padding({ left: 10, right: 10, top: 5, bottom: 5 })
                .margin({ right: 6 })
                .onClick(() => { this.catFilter = cat })
            })
          }.padding({ left: 16, right: 16, top: 8, bottom: 8 })
        }.width('100%').scrollable(ScrollDirection.Horizontal).scrollBar(BarState.Off)

        // 新建拼团按钮
        Row() {
          Text('共' + this.getFilteredGroups().length + '个拼团进行中')
            .fontSize(11).fontColor('#999999')
          Column() {
            Text('+ 发起拼团').fontSize(12).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
          }
          .backgroundColor('#FF4242').borderRadius(20)
          .padding({ left: 16, right: 16, top: 8, bottom: 8 })
          .onClick(() => { this.showAddGroup = true })
        }
        .width('92%').justifyContent(FlexAlign.SpaceBetween)
        .padding({ left: '4%', right: '4%', top: 4, bottom: 8 })

        // 双列网格
        Row() {
          Column() {
            ForEach(this.getFilteredGroups().filter((_: GroupItem, i: number) => i % 2 === 0), (g: GroupItem) => {
              this.groupGridCard(g)
            })
          }.layoutWeight(1)

          Column() {
            ForEach(this.getFilteredGroups().filter((_: GroupItem, i: number) => i % 2 === 1), (g: GroupItem) => {
              this.groupGridCard(g)
            })
          }.layoutWeight(1)
        }.width('100%').padding({ left: 8, right: 8, bottom: 16 })
      }
    }.width('100%').layoutWeight(1).backgroundColor('#FFF5F5')
  }

  @Builder groupGridCard(g: GroupItem) {
    Column() {
      // 商品图片区
      Column() {
        Text(g.image).fontSize(40)
        // 状态角标
        Row() {
          Text(g.status).fontSize(8).fontColor('#FFFFFF')
            .backgroundColor(getStatusColor(g.status))
            .borderRadius(8).padding({ left: 4, right: 4, top: 1, bottom: 1 })
        }
        .position({ x: 6, y: 6 })
        // 折扣角标
        Column() {
          Text(getDiscountRate(g.originPrice, g.groupPrice) + '折').fontSize(9).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
        }
        .backgroundColor('#FF4242').borderRadius(8)
        .padding({ left: 6, right: 6, top: 2, bottom: 2 })
        .position({ x: 0, y: 0 })
      }
      .width('100%').height(100)
      .backgroundColor('#FFF0EE')
      .borderRadius({ topLeft: 12, topRight: 12 })
      .justifyContent(FlexAlign.Center)

      // 商品信息
      Column() {
        Text(g.title).fontSize(11).fontColor('#333333').maxLines(2)
          .textOverflow({ overflow: TextOverflow.Ellipsis })
          .margin({ bottom: 4 })

        Row() {
          Text(fmtPrice(g.groupPrice)).fontSize(13).fontColor('#FF4242').fontWeight(FontWeight.Bold)
          Text(fmtPrice(g.originPrice)).fontSize(9).fontColor('#999999')
            .decoration({ type: TextDecorationType.LineThrough }).margin({ left: 4 })
        }.margin({ bottom: 4 })

        // 拼团进度条
        Column() {
          Column()
            .width(getProgressPct(g) + '%').height(4)
            .backgroundColor(getProgressColor(getProgressPct(g)))
            .borderRadius(2)
        }
        .width('100%').height(4).backgroundColor('#FFE0E0').borderRadius(2)
        .margin({ bottom: 4 })

        Row() {
          Text(g.joinedCount + '/' + g.needCount + '人').fontSize(9).fontColor('#FF7733')
          Column().layoutWeight(1)
          Text('去拼团').fontSize(9).fontColor('#FFFFFF')
            .backgroundColor('#FF4242').borderRadius(8)
            .padding({ left: 8, right: 8, top: 2, bottom: 2 })
            .onClick(() => {
              this.selectedGroup = g
              this.showJoinGroup = true
            })
        }.width('100%')
      }
      .width('100%').padding({ left: 8, right: 8, top: 6, bottom: 8 })
    }
    .width('100%').backgroundColor('#FFFFFF').borderRadius(12)
    .margin({ left: 4, right: 4, bottom: 8 })
    .shadow({ radius: 4, color: '#00000010', offsetX: 0, offsetY: 2 })
    .onClick(() => {
      this.selectedGroup = g
      this.showGroupDetail = true
    })
  }

  getFilteredGroups(): GroupItem[] {
    let list = mockGroups
    if (this.catFilter !== '全部') {
      list = list.filter((g: GroupItem) => g.category === this.catFilter)
    }
    if (this.searchKw.length > 0) {
      const kw = this.searchKw.toLowerCase()
      list = list.filter((g: GroupItem) =>
      g.title.toLowerCase().includes(kw) || g.category.toLowerCase().includes(kw))
    }
    return list
  }

  // ============ Tab1: 社群管理(列表+KPI布局) ============
  @Builder communitiesTab() {
    Scroll() {
      Column() {
        // KPI卡片
        Row() {
          this.kpiCard('总社群', mockCommunities.length + '', '#FF4242', '#FFF0EE')
          this.kpiCard('今日下单', mockCommunities.reduce((s: number, c: CommunityItem) => s + c.todayOrders, 0) + '', '#FF7733', '#FFF3E0')
          this.kpiCard('今日营收', '¥' + fmtNum(mockCommunities.reduce((s: number, c: CommunityItem) => s + c.todayRevenue, 0)), '#4CAF50', '#E8F5E9')
        }.width('92%').padding({ left: '4%', right: '4%', top: 10, bottom: 10 })

        // 社群列表
        Column() {
          ForEach(mockCommunities, (c: CommunityItem) => {
            this.communityCard(c)
          })
        }.width('92%').padding({ left: '4%', right: '4%', bottom: 16 })
      }
    }.width('100%').layoutWeight(1).backgroundColor('#FFF5F5')
  }

  @Builder communityCard(c: CommunityItem) {
    Column() {
      Row() {
        Column() {
          Text(getLevelMeta(c.level).color === '#9C27B0' ? '💎' : getLevelMeta(c.level).color === '#FF9800' ? '🥇' : '🏅')
            .fontSize(24)
        }
        .width(48).height(48)
        .backgroundColor(getLevelMeta(c.level).bg).borderRadius(24)
        .justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)

        Column() {
          Text(c.name).fontSize(13).fontWeight(FontWeight.Medium).fontColor('#333333')
          Row() {
            Text('团长: ' + c.leaderName).fontSize(9).fontColor('#999999')
            Text(' | ').fontSize(9).fontColor('#E0E0E0')
            Text(c.memberCount + '人').fontSize(9).fontColor('#999999')
            Text(' | ').fontSize(9).fontColor('#E0E0E0')
            Text(c.tag).fontSize(9).fontColor('#FF7733')
          }.margin({ top: 3 })
        }
        .layoutWeight(1).margin({ left: 10 }).alignItems(HorizontalAlign.Start)

        Column() {
          Text('¥' + fmtNum(c.todayRevenue)).fontSize(12).fontWeight(FontWeight.Bold).fontColor('#FF4242')
          Text(c.todayOrders + '单').fontSize(9).fontColor('#999999').margin({ top: 2 })
        }.alignItems(HorizontalAlign.End)
      }.width('100%')

      Divider().width('100%').height(0.5).backgroundColor('#FFF0EE').margin({ top: 10 })

      Row() {
        Text(c.level).fontSize(9)
          .fontColor(getLevelMeta(c.level).color)
          .backgroundColor(getLevelMeta(c.level).bg)
          .borderRadius(8).padding({ left: 6, right: 6, top: 2, bottom: 2 })
        Text(c.status).fontSize(9).fontColor(c.status === '活跃' ? '#4CAF50' : '#999999')
          .margin({ left: 6 })
        Column().layoutWeight(1)
        Text('创建于 ' + c.createTime).fontSize(9).fontColor('#CCCCCC')
      }.width('100%').margin({ top: 8 })
    }
    .width('100%').backgroundColor('#FFFFFF').borderRadius(12)
    .padding(12).margin({ bottom: 8 })
    .shadow({ radius: 4, color: '#00000008', offsetX: 0, offsetY: 2 })
  }

  @Builder kpiCard(label: string, val: string, fc: string, bg: string) {
    Column() {
      Text(val).fontSize(18).fontWeight(FontWeight.Bold).fontColor(fc)
      Text(label).fontSize(9).fontColor('#999999').margin({ top: 3 })
    }
    .layoutWeight(1).backgroundColor(bg).borderRadius(12)
    .padding({ top: 12, bottom: 12 })
    .alignItems(HorizontalAlign.Center).margin({ left: 3, right: 3 })
  }

  // ============ Tab2: 优惠活动(卡片瀑布流布局) ============
  @Builder activitiesTab() {
    Scroll() {
      Column() {
        Row() {
          Text('🎉 营销活动中心').fontSize(15).fontWeight(FontWeight.Bold).fontColor('#333333')
          Column().layoutWeight(1)
          Text('+ 新建活动').fontSize(11).fontColor('#FF4242').fontWeight(FontWeight.Bold)
        }
        .width('92%').padding({ left: '4%', right: '4%', top: 12, bottom: 8 })

        ForEach(mockActivities, (a: ActivityItem) => {
          this.activityCard(a)
        })
      }
    }.width('100%').layoutWeight(1).backgroundColor('#FFF5F5')
  }

  @Builder activityCard(a: ActivityItem) {
    Column() {
      // 头部渐变
      Row() {
        Column() {
          Text(getActTypeIcon(a.type)).fontSize(28)
        }
        .width(48).height(48).backgroundColor('#FFFFFF').borderRadius(24)
        .justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)

        Column() {
          Text(a.title).fontSize(13).fontWeight(FontWeight.Bold).fontColor('#FFFFFF')
          Text(a.discount).fontSize(11).fontColor('#FFD0CC').margin({ top: 2 })
        }
        .layoutWeight(1).margin({ left: 10 }).alignItems(HorizontalAlign.Start)

        Text(a.status).fontSize(9).fontColor('#FFFFFF')
          .backgroundColor('rgba(255,255,255,0.3)')
          .borderRadius(8).padding({ left: 6, right: 6, top: 2, bottom: 2 })
      }
      .width('100%')
      .backgroundImage('/data/gradient_bg.png')
      .backgroundColor(a.type === '秒杀' ? '#E91E63' : a.type === '红包' ? '#F44336' : a.type === '新人' ? '#9C27B0' : '#FF4242')
      .borderRadius({ topLeft: 12, topRight: 12 })
      .padding(12)

      // 内容区
      Column() {
        Text(a.description).fontSize(10).fontColor('#666666').margin({ bottom: 8 })
          .maxLines(2).textOverflow({ overflow: TextOverflow.Ellipsis })

        Row() {
          Text('活动时间:').fontSize(9).fontColor('#999999')
          Text(fmtDate(a.startTime) + ' ~ ' + fmtDate(a.endTime)).fontSize(9).fontColor('#666666').margin({ left: 4 })
        }.margin({ bottom: 6 })

        // 预算使用进度
        Row() {
          Text('预算使用').fontSize(9).fontColor('#999999')
          Column().layoutWeight(1)
          Text(fmtPrice(a.used) + '/' + fmtPrice(a.budget)).fontSize(9).fontColor('#FF4242')
        }.width('100%').margin({ bottom: 4 })

        Column() {
          Column()
            .width(Math.round(a.used / a.budget * 100) + '%').height(5)
            .backgroundColor(a.used / a.budget > 0.8 ? '#F44336' : '#FF9800')
            .borderRadius(3)
        }
        .width('100%').height(5).backgroundColor('#FFF0EE').borderRadius(3).margin({ bottom: 8 })

        Row() {
          Text('参与' + fmtNum(a.joinCount) + '人').fontSize(9).fontColor('#999999')
          Column().layoutWeight(1)
          Text('查看详情').fontSize(9).fontColor('#FF4242')
            .borderWidth(1).borderColor('#FF4242').borderRadius(12)
            .padding({ left: 10, right: 10, top: 4, bottom: 4 })
            .onClick(() => {
              this.selectedActivity = a
              this.showActDetail = true
            })
        }.width('100%')
      }
      .width('100%').padding(12)
    }
    .width('92%').backgroundColor('#FFFFFF').borderRadius(12)
    .margin({ left: '4%', right: '4%', bottom: 10 })
    .clip(true)
    .shadow({ radius: 6, color: '#00000010', offsetX: 0, offsetY: 3 })
  }

  // ============ Tab3: 订单追踪(分段筛选+列表布局) ============
  @Builder ordersTab() {
    Scroll() {
      Column() {
        // 订单状态筛选
        Row() {
          ForEach(['全部', '待付款', '待发货', '已发货', '已完成', '退款中'], (s: string) => {
            Text(s)
              .fontSize(10)
              .fontColor(this.orderFilter === s ? '#FFFFFF' : '#666666')
              .backgroundColor(this.orderFilter === s ? '#FF4242' : '#FFFFFF')
              .borderRadius(14)
              .padding({ left: 8, right: 8, top: 5, bottom: 5 })
              .margin({ right: 6 })
              .onClick(() => { this.orderFilter = s })
          })
        }
        .width('92%').padding({ left: '4%', right: '4%', top: 10, bottom: 10 })

        // 订单列表
        Column() {
          ForEach(this.getFilteredOrders(), (o: OrderItem) => {
            this.orderCard(o)
          })
        }.width('92%').padding({ left: '4%', right: '4%', bottom: 16 })
      }
    }.width('100%').layoutWeight(1).backgroundColor('#FFF5F5')
  }

  getFilteredOrders(): OrderItem[] {
    if (this.orderFilter === '全部') return mockOrders
    if (this.orderFilter === '退款中') {
      return mockOrders.filter((o: OrderItem) => o.status === '退款中' || o.status === '已退款')
    }
    return mockOrders.filter((o: OrderItem) => o.status === this.orderFilter)
  }

  @Builder orderCard(o: OrderItem) {
    Column() {
      // 订单头部
      Row() {
        Text('订单号: PDD' + (10000000 + o.id).toString())
          .fontSize(10).fontColor('#999999')
        Column().layoutWeight(1)
        Text(o.status).fontSize(10).fontColor(getOrderStatusColor(o.status)).fontWeight(FontWeight.Bold)
      }.width('100%').margin({ bottom: 8 })

      Divider().width('100%').height(0.5).backgroundColor('#FFF0EE')

      // 商品信息
      Row() {
        Column() {
          Text('📦').fontSize(28)
        }
        .width(50).height(50).backgroundColor('#FFF0EE').borderRadius(10)
        .justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)

        Column() {
          Text(o.groupTitle).fontSize(12).fontColor('#333333').maxLines(2)
            .textOverflow({ overflow: TextOverflow.Ellipsis })
          Text('x' + o.quantity).fontSize(10).fontColor('#999999').margin({ top: 2 })
        }
        .layoutWeight(1).margin({ left: 10 }).alignItems(HorizontalAlign.Start)

        Column() {
          Text(fmtPrice(o.totalPrice)).fontSize(13).fontWeight(FontWeight.Bold).fontColor('#FF4242')
        }.alignItems(HorizontalAlign.End)
      }.width('100%').margin({ top: 8 })

      // 收件信息
      Row() {
        Text('📞').fontSize(10)
        Text(o.buyerName + ' ' + o.phone).fontSize(10).fontColor('#666666').margin({ left: 4 })
      }.margin({ top: 8 })

      Row() {
        Text('📍').fontSize(10)
        Text(o.address).fontSize(9).fontColor('#999999').margin({ left: 4 })
          .maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis }).layoutWeight(1)
      }.margin({ top: 4 })

      Divider().width('100%').height(0.5).backgroundColor('#FFF0EE').margin({ top: 8 })

      // 操作按钮
      Row() {
        Text(fmtDate(o.createTime)).fontSize(9).fontColor('#CCCCCC')
        Column().layoutWeight(1)
        Text('查看详情').fontSize(10).fontColor('#FF4242')
          .borderWidth(1).borderColor('#FF4242').borderRadius(16)
          .padding({ left: 12, right: 12, top: 5, bottom: 5 })
          .onClick(() => {
            this.selectedOrder = o
            this.showOrderDetail = true
          })
      }.width('100%').margin({ top: 8 })
    }
    .width('100%').backgroundColor('#FFFFFF').borderRadius(12)
    .padding(12).margin({ bottom: 8 })
    .shadow({ radius: 4, color: '#00000008', offsetX: 0, offsetY: 2 })
  }

  // ============ Tab4: 数据看板(图表布局) ============
  @Builder dashboardTab() {
    Scroll() {
      Column() {
        // 概览数据
        Row() {
          this.kpiCard('拼团总数', mockGroups.length + '', '#FF4242', '#FFF0EE')
          this.kpiCard('社群数', mockCommunities.length + '', '#FF7733', '#FFF3E0')
          this.kpiCard('订单数', mockOrders.length + '', '#4CAF50', '#E8F5E9')
        }.width('92%').padding({ left: '4%', right: '4%', top: 10, bottom: 10 })

        // 周销售柱状图
        Column() {
          Row() {
            Text('📈 本周销售趋势').fontSize(13).fontWeight(FontWeight.Bold).fontColor('#333333')
            Column().layoutWeight(1)
            Text('单位: 万元').fontSize(9).fontColor('#999999')
          }.width('100%').margin({ bottom: 12 })

          Row() {
            ForEach(['周一', '周二', '周三', '周四', '周五', '周六', '周日'], (day: string, idx: number) => {
              Column() {
                // 柱子
                Column()
                  .width(20).height(40 + idx * 12 + (idx === 5 ? 30 : 0))
                  .backgroundColor(idx === 5 || idx === 6 ? '#FF4242' : '#FFB199')
                  .borderRadius({ topLeft: 4, topRight: 4 })
                Text(day).fontSize(8).fontColor('#999999').margin({ top: 4 })
              }.layoutWeight(1).alignItems(HorizontalAlign.Center)
            })
          }.width('100%').height(120).justifyContent(FlexAlign.SpaceEvenly)
        }
        .width('92%').backgroundColor('#FFFFFF').borderRadius(14)
        .padding(14).margin({ left: '4%', right: '4%', bottom: 10 })
        .shadow({ radius: 6, color: '#00000008', offsetX: 0, offsetY: 2 })

        // 分类销量分布
        Column() {
          Text('🏷️ 分类销量分布').fontSize(13).fontWeight(FontWeight.Bold).fontColor('#333333')
            .margin({ bottom: 10 })
          ForEach(Object.keys(CAT_CONFIG), (cat: string) => {
            Row() {
              Text(getCatIcon(cat) + ' ' + cat).fontSize(10).fontColor('#666666').width(70)
              Column() {
                Column()
                  .width(this.getCatCount(cat) / this.getMaxCatCount() * 100 + '%')
                  .height(8).backgroundColor('#FF4242').borderRadius(4)
              }
              .layoutWeight(1).height(8).backgroundColor('#FFF0EE').borderRadius(4).margin({ left: 8, right: 8 })
              Text(this.getCatCount(cat) + '单').fontSize(9).fontColor('#999999').width(36)
            }.width('100%').margin({ bottom: 6 })
          })
        }
        .width('92%').backgroundColor('#FFFFFF').borderRadius(14)
        .padding(14).margin({ left: '4%', right: '4%', bottom: 10 })
        .shadow({ radius: 6, color: '#00000008', offsetX: 0, offsetY: 2 })

        // 拼团状态分布
        Column() {
          Text('📊 拼团状态统计').fontSize(13).fontWeight(FontWeight.Bold).fontColor('#333333')
            .margin({ bottom: 10 })
          Row() {
            ForEach(['拼团中', '已成团', '即将开团', '已结束'], (s: string) => {
              Column() {
                Text(mockGroups.filter((g: GroupItem) => g.status === s).length.toString())
                  .fontSize(18).fontWeight(FontWeight.Bold).fontColor(getStatusColor(s))
                Text(s).fontSize(9).fontColor('#999999').margin({ top: 3 })
              }.layoutWeight(1).alignItems(HorizontalAlign.Center)
            })
          }.width('100%')
        }
        .width('92%').backgroundColor('#FFFFFF').borderRadius(14)
        .padding(14).margin({ left: '4%', right: '4%', bottom: 10 })
        .shadow({ radius: 6, color: '#00000008', offsetX: 0, offsetY: 2 })
      }
    }.width('100%').layoutWeight(1).backgroundColor('#FFF5F5')
  }

  getCatCount(cat: string): number {
    return mockGroups.filter((g: GroupItem) => g.category === cat).length
  }
  getMaxCatCount(): number {
    return Math.max(...Object.keys(CAT_CONFIG).map((c: string) => this.getCatCount(c)))
  }

  // ============ Tab5: 拼团排行(排行榜样式) ============
  @Builder rankingTab() {
    Scroll() {
      Column() {
        // 前三名展示台
        Row() {
          // 第二名
          Column() {
            Text('🥈').fontSize(36)
            Text(mockRanks[1].name).fontSize(8).fontColor('#666666').maxLines(1)
              .textOverflow({ overflow: TextOverflow.Ellipsis }).margin({ top: 4 })
            Text('¥' + mockRanks[1].revenue + 'w').fontSize(11).fontColor('#FF9800').fontWeight(FontWeight.Bold)
          }
          .layoutWeight(1).alignItems(HorizontalAlign.Center)
          .backgroundColor('#FFF8E1').borderRadius(12)
          .padding({ top: 16, bottom: 16 })

          // 第一名
          Column() {
            Text('🥇').fontSize(48)
            Text(mockRanks[0].name).fontSize(8).fontColor('#333333').maxLines(1)
              .textOverflow({ overflow: TextOverflow.Ellipsis }).margin({ top: 4 })
            Text('¥' + mockRanks[0].revenue + 'w').fontSize(13).fontColor('#FF4242').fontWeight(FontWeight.Bold)
          }
          .layoutWeight(1.2).alignItems(HorizontalAlign.Center)
          .backgroundColor('#FFF3E0').borderRadius(12)
          .padding({ top: 24, bottom: 16 })

          // 第三名
          Column() {
            Text('🥉').fontSize(36)
            Text(mockRanks[2].name).fontSize(8).fontColor('#666666').maxLines(1)
              .textOverflow({ overflow: TextOverflow.Ellipsis }).margin({ top: 4 })
            Text('¥' + mockRanks[2].revenue + 'w').fontSize(11).fontColor('#8D6E63').fontWeight(FontWeight.Bold)
          }
          .layoutWeight(1).alignItems(HorizontalAlign.Center)
          .backgroundColor('#EFEBE9').borderRadius(12)
          .padding({ top: 16, bottom: 16 })
        }
        .width('92%').padding({ left: '4%', right: '4%', top: 16, bottom: 16 })

        // 第4~12名列表
        Column() {
          ForEach(mockRanks.slice(3), (r: RankItem) => {
            this.rankRow(r)
          })
        }.width('92%').padding({ left: '4%', right: '4%', bottom: 16 })
      }
    }.width('100%').layoutWeight(1).backgroundColor('#FFF5F5')
  }

  @Builder rankRow(r: RankItem) {
    Row() {
      Text(r.rank.toString()).fontSize(14).fontWeight(FontWeight.Bold).fontColor('#CCCCCC').width(30)
      Column() {
        Text(r.name).fontSize(12).fontColor('#333333')
        Text(r.members + '人 · ' + r.groupCount + '团').fontSize(9).fontColor('#999999').margin({ top: 2 })
      }
      .layoutWeight(1).margin({ left: 8 }).alignItems(HorizontalAlign.Start)
      Column() {
        Text('¥' + r.revenue + 'w').fontSize(12).fontColor('#FF4242').fontWeight(FontWeight.Bold)
        Text(r.growth).fontSize(9)
          .fontColor(r.growth.startsWith('+') ? '#4CAF50' : '#F44336').margin({ top: 2 })
      }.alignItems(HorizontalAlign.End)
    }
    .width('100%').backgroundColor('#FFFFFF').borderRadius(10)
    .padding({ left: 12, right: 12, top: 10, bottom: 10 }).margin({ bottom: 6 })
    .shadow({ radius: 2, color: '#00000008', offsetX: 0, offsetY: 1 })
  }

  // ============ Tab6: 我的(个人中心布局) ============
  @Builder profileTab() {
    Scroll() {
      Column() {
        // 用户信息卡
        Column() {
          Row() {
            Column() {
              Text('👤').fontSize(40)
            }
            .width(64).height(64).backgroundColor('#FFFFFF').borderRadius(32)
            .justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)

            Column() {
              Text('拼团团长').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#FFFFFF')
              Text('钻石团长 · ID: TG20260824').fontSize(10).fontColor('#FFD0CC').margin({ top: 4 })
            }
            .layoutWeight(1).margin({ left: 12 }).alignItems(HorizontalAlign.Start)

            Text('编辑').fontSize(10).fontColor('#FFFFFF')
              .borderWidth(1).borderColor('#FFFFFF').borderRadius(12)
              .padding({ left: 10, right: 10, top: 4, bottom: 4 })
          }.width('100%')
        }
        .width('92%')
        .backgroundColor('#FF4242')
        .borderRadius(16).padding(16)
        .margin({ left: '4%', right: '4%', top: 16, bottom: 16 })

        // 资产概览
        Row() {
          this.profileStat('账户余额', '¥2,856.50', '#FF4242')
          this.profileStat('拼团佣金', '¥1,230.00', '#FF7733')
          this.profileStat('优惠券', '12张', '#4CAF50')
        }.width('92%').padding({ left: '4%', right: '4%', bottom: 12 })

        // 功能菜单
        Column() {
          this.menuRow('📦', '我的拼团', '23个进行中')
          this.menuRow('📋', '我的订单', '5个待处理')
          this.menuRow('👥', '我的社群', '管理3个社群')
          this.menuRow('🎫', '优惠券中心', '3张即将过期')
          this.menuRow('💰', '提现账户', '已绑定')
          this.menuRow('📞', '客服中心', '')
          this.menuRow('⚙️', '设置', '')
        }.width('92%').padding({ left: '4%', right: '4%', bottom: 16 })
      }
    }.width('100%').layoutWeight(1).backgroundColor('#FFF5F5')
  }

  @Builder profileStat(label: string, val: string, fc: string) {
    Column() {
      Text(val).fontSize(14).fontWeight(FontWeight.Bold).fontColor(fc)
      Text(label).fontSize(9).fontColor('#999999').margin({ top: 3 })
    }
    .layoutWeight(1).backgroundColor('#FFFFFF').borderRadius(12)
    .padding({ top: 14, bottom: 14 }).alignItems(HorizontalAlign.Center)
    .margin({ left: 3, right: 3 })
    .shadow({ radius: 2, color: '#00000008', offsetX: 0, offsetY: 1 })
  }

  @Builder menuRow(icon: string, title: string, desc: string) {
    Row() {
      Text(icon).fontSize(18).width(30)
      Text(title).fontSize(13).fontColor('#333333')
      Column().layoutWeight(1)
      Text(desc).fontSize(10).fontColor('#999999')
      Text('›').fontSize(16).fontColor('#CCCCCC').margin({ left: 8 })
    }
    .width('100%').backgroundColor('#FFFFFF').borderRadius(10)
    .padding({ left: 14, right: 14, top: 14, bottom: 14 }).margin({ bottom: 6 })
    .shadow({ radius: 2, color: '#00000008', offsetX: 0, offsetY: 1 })
  }

  // ============ 弹框: 发起新拼团 ============
  @Builder addGroupModal() {
    if (this.showAddGroup) {
      Column() {
        // 遮罩
        Column().width('100%').height('100%').backgroundColor('rgba(0,0,0,0.5)')
          .onClick(() => { this.showAddGroup = false })

        // 弹框主体
        Column() {
          Row() {
            Text('🛒 发起新拼团').fontSize(17).fontWeight(FontWeight.Bold).fontColor('#333333')
            Column().layoutWeight(1)
            Text('✕').fontSize(18).fontColor('#999999')
              .onClick(() => { this.showAddGroup = false })
          }.width('90%').padding({ top: 18, bottom: 12 })

          Divider().width('90%').height(0.5).backgroundColor('#FFF0EE')

          Scroll() {
            Column() {
              Text('商品名称').fontSize(11).fontColor('#999999')
              TextInput({ placeholder: '输入拼团商品名称' }).fontSize(13).width('100%')
                .backgroundColor('#FFF5F5').borderRadius(10).padding(10).margin({ top: 4, bottom: 12 })

              Text('商品分类').fontSize(11).fontColor('#999999')
              Row() {
                ForEach(Object.keys(CAT_CONFIG), (cat: string) => {
                  Text(getCatIcon(cat) + ' ' + cat)
                    .fontSize(10).fontColor('#FF4242')
                    .backgroundColor('#FFF0EE').borderRadius(12)
                    .padding({ left: 8, right: 8, top: 5, bottom: 5 }).margin({ right: 6, bottom: 4 })
                })
              }.width('100%').margin({ top: 4, bottom: 12 })

              Row() {
                Column() {
                  Text('原价(分)').fontSize(11).fontColor('#999999')
                  TextInput({ placeholder: '5900' }).fontSize(13).width('100%')
                    .backgroundColor('#FFF5F5').borderRadius(10).padding(10).margin({ top: 4 })
                    .type(InputType.Number)
                }.layoutWeight(1).margin({ right: 8 })

                Column() {
                  Text('拼团价(分)').fontSize(11).fontColor('#999999')
                  TextInput({ placeholder: '2990' }).fontSize(13).width('100%')
                    .backgroundColor('#FFF5F5').borderRadius(10).padding(10).margin({ top: 4 })
                    .type(InputType.Number)
                }.layoutWeight(1)
              }.width('100%').margin({ bottom: 12 })

              Row() {
                Column() {
                  Text('开团人数').fontSize(11).fontColor('#999999')
                  TextInput({ placeholder: '50' }).fontSize(13).width('100%')
                    .backgroundColor('#FFF5F5').borderRadius(10).padding(10).margin({ top: 4 })
                    .type(InputType.Number)
                }.layoutWeight(1).margin({ right: 8 })

                Column() {
                  Text('截止日期').fontSize(11).fontColor('#999999')
                  TextInput({ placeholder: '2026-08-30' }).fontSize(13).width('100%')
                    .backgroundColor('#FFF5F5').borderRadius(10).padding(10).margin({ top: 4 })
                }.layoutWeight(1)
              }.width('100%').margin({ bottom: 12 })

              Text('商品描述').fontSize(11).fontColor('#999999')
              TextArea({ placeholder: '输入商品描述...' }).fontSize(13).width('100%')
                .backgroundColor('#FFF5F5').borderRadius(10).padding(10).margin({ top: 4, bottom: 16 })
                .height(70)

              Row() {
                Text('取消').fontSize(13).fontColor('#999999')
                  .borderWidth(1).borderColor('#E0E0E0').borderRadius(20)
                  .padding({ left: 20, right: 20, top: 10, bottom: 10 })
                  .onClick(() => { this.showAddGroup = false })
                Column().layoutWeight(1)
                Text('确认发起').fontSize(13).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
                  .backgroundColor('#FF4242').borderRadius(20)
                  .padding({ left: 20, right: 20, top: 10, bottom: 10 })
                  .onClick(() => { this.showAddGroup = false })
              }.width('100%')
            }.width('90%').padding({ top: 16, bottom: 20 })
          }
          .width('100%').layoutWeight(1)
        }
        .width('88%')
        .constraintSize({ maxHeight: '80%' })
        .backgroundColor('#FFFFFF').borderRadius(20)
        .position({ x: '6%', y: '10%' })
      }
      .width('100%').height('100%').position({ x: 0, y: 0 }).zIndex(999)
    }
  }

  // ============ 弹框: 拼团详情 ============
  @Builder groupDetailModal() {
    if (this.showGroupDetail && this.selectedGroup) {
      Column() {
        Column().width('100%').height('100%').backgroundColor('rgba(0,0,0,0.5)')
          .onClick(() => { this.showGroupDetail = false })

        Column() {
          // 商品图片区
          Column() {
            Text(this.selectedGroup.image).fontSize(60)
          }
          .width('100%').height(120).backgroundColor('#FFF0EE')
          .borderRadius({ topLeft: 20, topRight: 20 })
          .justifyContent(FlexAlign.Center)

          Scroll() {
            Column() {
              Row() {
                Text(fmtPrice(this.selectedGroup.groupPrice)).fontSize(22).fontWeight(FontWeight.Bold).fontColor('#FF4242')
                Text(fmtPrice(this.selectedGroup.originPrice)).fontSize(12).fontColor('#999999')
                  .decoration({ type: TextDecorationType.LineThrough }).margin({ left: 8 })
                Column().layoutWeight(1)
                Text(getDiscountRate(this.selectedGroup.originPrice, this.selectedGroup.groupPrice) + '折')
                  .fontSize(10).fontColor('#FFFFFF').backgroundColor('#FF4242')
                  .borderRadius(8).padding({ left: 6, right: 6, top: 2, bottom: 2 })
              }.width('90%').margin({ top: 16 })

              Text(this.selectedGroup.title).fontSize(14).fontWeight(FontWeight.Medium).fontColor('#333333')
                .width('90%').margin({ top: 8 })

              Row() {
                Text('⭐' + this.selectedGroup.rating.toFixed(1)).fontSize(10).fontColor('#FF9800')
                Text(' | ').fontSize(10).fontColor('#E0E0E0')
                Text('已售' + fmtNum(this.selectedGroup.salesCount)).fontSize(10).fontColor('#999999')
                Text(' | ').fontSize(10).fontColor('#E0E0E0')
                Text(this.selectedGroup.shopName).fontSize(10).fontColor('#FF4242')
              }.width('90%').margin({ top: 6 })

              // 拼团进度
              Column() {
                Row() {
                  Text('拼团进度').fontSize(11).fontColor('#999999')
                  Column().layoutWeight(1)
                  Text(this.selectedGroup.joinedCount + '/' + this.selectedGroup.needCount + '人')
                    .fontSize(11).fontColor('#FF4242').fontWeight(FontWeight.Bold)
                }.width('100%').margin({ bottom: 6 })

                Column() {
                  Column()
                    .width(getProgressPct(this.selectedGroup) + '%').height(8)
                    .backgroundColor(getProgressColor(getProgressPct(this.selectedGroup)))
                    .borderRadius(4)
                }
                .width('100%').height(8).backgroundColor('#FFF0EE').borderRadius(4)

                Row() {
                  Text('剩余名额').fontSize(10).fontColor('#999999')
                  Column().layoutWeight(1)
                  Text((this.selectedGroup.needCount - this.selectedGroup.joinedCount) + '人')
                    .fontSize(10).fontColor('#FF4242')
                }.width('100%').margin({ top: 6 })
              }
              .width('90%').backgroundColor('#FFF5F5').borderRadius(12)
              .padding(12).margin({ top: 12 })

              // 商品描述
              Text('商品详情').fontSize(12).fontWeight(FontWeight.Bold).fontColor('#333333')
                .width('90%').margin({ top: 16, bottom: 6 })
              Text(this.selectedGroup.description).fontSize(11).fontColor('#666666').width('90%')

              // 标签
              Row() {
                ForEach(this.selectedGroup.tags, (tag: string) => {
                  Text(tag).fontSize(9).fontColor('#FF4242').backgroundColor('#FFF0EE')
                    .borderRadius(10).padding({ left: 8, right: 8, top: 3, bottom: 3 })
                    .margin({ right: 6 })
                })
              }.width('90%').margin({ top: 10, bottom: 16 })

              // 操作按钮
              Row() {
                Text('编辑').fontSize(13).fontColor('#FF4242')
                  .borderWidth(1).borderColor('#FF4242').borderRadius(20)
                  .padding({ left: 20, right: 20, top: 10, bottom: 10 })
                  .onClick(() => {
                    this.showGroupDetail = false
                    this.showEditGroup = true
                  })
                Column().layoutWeight(1)
                Text('立即参团').fontSize(13).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
                  .backgroundColor('#FF4242').borderRadius(20)
                  .padding({ left: 24, right: 24, top: 10, bottom: 10 })
                  .onClick(() => {
                    this.showGroupDetail = false
                    this.showJoinGroup = true
                  })
              }.width('90%').margin({ bottom: 20 })
            }
          }
          .width('100%').layoutWeight(1)
        }
        .width('88%')
        .constraintSize({ maxHeight: '75%' })
        .backgroundColor('#FFFFFF').borderRadius(20)
        .position({ x: '6%', y: '12%' })
      }
      .width('100%').height('100%').position({ x: 0, y: 0 }).zIndex(999)
    }
  }

  // ============ 弹框: 编辑拼团 ============
  @Builder editGroupModal() {
    if (this.showEditGroup && this.selectedGroup) {
      Column() {
        Column().width('100%').height('100%').backgroundColor('rgba(0,0,0,0.5)')
          .onClick(() => { this.showEditGroup = false })

        Column() {
          Row() {
            Text('✏️ 编辑拼团信息').fontSize(17).fontWeight(FontWeight.Bold).fontColor('#333333')
            Column().layoutWeight(1)
            Text('✕').fontSize(18).fontColor('#999999')
              .onClick(() => { this.showEditGroup = false })
          }.width('90%').padding({ top: 18, bottom: 12 })

          Divider().width('90%').height(0.5).backgroundColor('#FFF0EE')

          Column() {
            Text('商品名称').fontSize(11).fontColor('#999999')
            TextInput({ placeholder: this.selectedGroup.title, text: this.selectedGroup.title })
              .fontSize(13).width('100%')
              .backgroundColor('#FFF5F5').borderRadius(10).padding(10).margin({ top: 4, bottom: 12 })

            Row() {
              Column() {
                Text('拼团价(分)').fontSize(11).fontColor('#999999')
                TextInput({ placeholder: this.selectedGroup.groupPrice.toString(), text: this.selectedGroup.groupPrice.toString() })
                  .fontSize(13).width('100%')
                  .backgroundColor('#FFF5F5').borderRadius(10).padding(10).margin({ top: 4 })
                  .type(InputType.Number)
              }.layoutWeight(1).margin({ right: 8 })

              Column() {
                Text('开团人数').fontSize(11).fontColor('#999999')
                TextInput({ placeholder: this.selectedGroup.needCount.toString(), text: this.selectedGroup.needCount.toString() })
                  .fontSize(13).width('100%')
                  .backgroundColor('#FFF5F5').borderRadius(10).padding(10).margin({ top: 4 })
                  .type(InputType.Number)
              }.layoutWeight(1)
            }.width('100%').margin({ bottom: 12 })

            Text('商品描述').fontSize(11).fontColor('#999999')
            TextArea({ placeholder: this.selectedGroup.description, text: this.selectedGroup.description })
              .fontSize(13).width('100%')
              .backgroundColor('#FFF5F5').borderRadius(10).padding(10).margin({ top: 4, bottom: 16 })
              .height(70)

            Row() {
              Text('取消').fontSize(13).fontColor('#999999')
                .borderWidth(1).borderColor('#E0E0E0').borderRadius(20)
                .padding({ left: 20, right: 20, top: 10, bottom: 10 })
                .onClick(() => { this.showEditGroup = false })
              Column().layoutWeight(1)
              Text('保存修改').fontSize(13).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
                .backgroundColor('#FF4242').borderRadius(20)
                .padding({ left: 20, right: 20, top: 10, bottom: 10 })
                .onClick(() => { this.showEditGroup = false })
            }.width('100%')
          }.width('90%').padding({ top: 16, bottom: 20 })
        }
        .width('88%')
        .constraintSize({ maxHeight: '70%' })
        .backgroundColor('#FFFFFF').borderRadius(20)
        .position({ x: '6%', y: '15%' })
      }
      .width('100%').height('100%').position({ x: 0, y: 0 }).zIndex(999)
    }
  }

  // ============ 弹框: 删除确认 ============
  @Builder deleteConfirmModal() {
    if (this.showDeleteConfirm) {
      Column() {
        Column().width('100%').height('100%').backgroundColor('rgba(0,0,0,0.5)')
          .onClick(() => { this.showDeleteConfirm = false })

        Column() {
          Column() {
            Text('⚠️').fontSize(40)
            Text('确认删除?').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#333333').margin({ top: 12 })
            Text('删除后不可恢复,确定要删除该拼团吗?').fontSize(11).fontColor('#999999').margin({ top: 8 })
          }.width('100%').padding({ top: 24, bottom: 20 }).alignItems(HorizontalAlign.Center)

          Divider().width('100%').height(0.5).backgroundColor('#FFF0EE')

          Row() {
            Text('取消').fontSize(14).fontColor('#999999')
              .layoutWeight(1).textAlign(TextAlign.Center)
              .padding({ top: 14, bottom: 14 })
              .onClick(() => { this.showDeleteConfirm = false })
            Column().width(0.5).height(40).backgroundColor('#FFF0EE')
            Text('确认删除').fontSize(14).fontColor('#FF4242').fontWeight(FontWeight.Bold)
              .layoutWeight(1).textAlign(TextAlign.Center)
              .padding({ top: 14, bottom: 14 })
              .onClick(() => { this.showDeleteConfirm = false })
          }.width('100%')
        }
        .width('72%').backgroundColor('#FFFFFF').borderRadius(16)
        .position({ x: '14%', y: '35%' })
      }
      .width('100%').height('100%').position({ x: 0, y: 0 }).zIndex(999)
    }
  }

  // ============ 弹框: 加入拼团 ============
  @Builder joinGroupModal() {
    if (this.showJoinGroup && this.selectedGroup) {
      Column() {
        Column().width('100%').height('100%').backgroundColor('rgba(0,0,0,0.5)')
          .onClick(() => { this.showJoinGroup = false })

        Column() {
          // 商品头部
          Row() {
            Column() {
              Text(this.selectedGroup.image).fontSize(32)
            }
            .width(56).height(56).backgroundColor('#FFF0EE').borderRadius(12)
            .justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)

            Column() {
              Text(fmtPrice(this.selectedGroup.groupPrice)).fontSize(16).fontWeight(FontWeight.Bold).fontColor('#FF4242')
              Text('已选: ' + this.joinQuantity + '件').fontSize(10).fontColor('#999999').margin({ top: 4 })
            }
            .layoutWeight(1).margin({ left: 10 }).alignItems(HorizontalAlign.Start)

            Text('✕').fontSize(18).fontColor('#999999')
              .onClick(() => { this.showJoinGroup = false })
          }.width('90%').padding({ top: 16, bottom: 12 })

          Divider().width('90%').height(0.5).backgroundColor('#FFF0EE')

          // 数量选择
          Row() {
            Text('购买数量').fontSize(13).fontColor('#333333')
            Column().layoutWeight(1)
            Row() {
              Text('−').fontSize(16).fontColor('#FF4242')
                .width(32).height(32).backgroundColor('#FFF5F5')
                .borderRadius({ topLeft: 8, bottomLeft: 8 })
                .textAlign(TextAlign.Center)
                .onClick(() => { if (this.joinQuantity > 1) this.joinQuantity-- })
              Text(this.joinQuantity.toString()).fontSize(14)
                .width(40).height(32).backgroundColor('#FFFFFF')
                .textAlign(TextAlign.Center)
              Text('+').fontSize(16).fontColor('#FF4242')
                .width(32).height(32).backgroundColor('#FFF5F5')
                .borderRadius({ topRight: 8, bottomRight: 8 })
                .textAlign(TextAlign.Center)
                .onClick(() => { this.joinQuantity++ })
            }
          }.width('90%').margin({ top: 16, bottom: 16 })

          // 规格选择
          Text('商品规格').fontSize(11).fontColor('#999999').width('90%')
          Row() {
            Text('默认规格').fontSize(11).fontColor('#FFFFFF').backgroundColor('#FF4242')
              .borderRadius(10).padding({ left: 10, right: 10, top: 5, bottom: 5 }).margin({ right: 6 })
            Text('大份装').fontSize(11).fontColor('#FF4242').backgroundColor('#FFF0EE')
              .borderRadius(10).padding({ left: 10, right: 10, top: 5, bottom: 5 }).margin({ right: 6 })
            Text('家庭装').fontSize(11).fontColor('#FF4242').backgroundColor('#FFF0EE')
              .borderRadius(10).padding({ left: 10, right: 10, top: 5, bottom: 5 })
          }.width('90%').margin({ top: 6, bottom: 16 })

          // 底部价格
          Row() {
            Column() {
              Text('合计').fontSize(10).fontColor('#999999')
              Text(fmtPrice(this.selectedGroup.groupPrice * this.joinQuantity))
                .fontSize(16).fontWeight(FontWeight.Bold).fontColor('#FF4242')
            }.alignItems(HorizontalAlign.Start)
            Column().layoutWeight(1)
            Text('确认参团').fontSize(14).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
              .backgroundColor('#FF4242').borderRadius(20)
              .padding({ left: 28, right: 28, top: 12, bottom: 12 })
              .onClick(() => { this.showJoinGroup = false })
          }.width('90%').margin({ bottom: 20 })
        }
        .width('88%').backgroundColor('#FFFFFF').borderRadius(20)
        .position({ x: '6%', y: '30%' })
      }
      .width('100%').height('100%').position({ x: 0, y: 0 }).zIndex(999)
    }
  }

  // ============ 弹框: 订单详情 ============
  @Builder orderDetailModal() {
    if (this.showOrderDetail && this.selectedOrder) {
      Column() {
        Column().width('100%').height('100%').backgroundColor('rgba(0,0,0,0.5)')
          .onClick(() => { this.showOrderDetail = false })

        Column() {
          Row() {
            Text('📦 订单详情').fontSize(17).fontWeight(FontWeight.Bold).fontColor('#333333')
            Column().layoutWeight(1)
            Text('✕').fontSize(18).fontColor('#999999')
              .onClick(() => { this.showOrderDetail = false })
          }.width('90%').padding({ top: 18, bottom: 12 })

          Divider().width('90%').height(0.5).backgroundColor('#FFF0EE')

          Scroll() {
            Column() {
              // 状态
              Row() {
                Text(this.selectedOrder.status).fontSize(14).fontWeight(FontWeight.Bold)
                  .fontColor(getOrderStatusColor(this.selectedOrder.status))
                Column().layoutWeight(1)
                Text(fmtDate(this.selectedOrder.createTime)).fontSize(10).fontColor('#999999')
              }.width('90%').margin({ top: 16, bottom: 12 })

              // 商品
              Row() {
                Column() {
                  Text('📦').fontSize(24)
                }.width(40).height(40).backgroundColor('#FFF0EE').borderRadius(10)
                .justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)
                Column() {
                  Text(this.selectedOrder.groupTitle).fontSize(12).fontColor('#333333')
                  Text('x' + this.selectedOrder.quantity).fontSize(10).fontColor('#999999').margin({ top: 2 })
                }.layoutWeight(1).margin({ left: 8 }).alignItems(HorizontalAlign.Start)
                Text(fmtPrice(this.selectedOrder.totalPrice)).fontSize(13).fontWeight(FontWeight.Bold).fontColor('#FF4242')
              }.width('90%').margin({ bottom: 12 })

              Divider().width('90%').height(0.5).backgroundColor('#FFF0EE')

              // 收件信息
              Column() {
                Text('收件信息').fontSize(11).fontColor('#999999').margin({ bottom: 6 })
                Row() {
                  Text('收件人').fontSize(11).fontColor('#999999').width(60)
                  Text(this.selectedOrder.buyerName).fontSize(11).fontColor('#333333')
                }.margin({ bottom: 4 })
                Row() {
                  Text('电话').fontSize(11).fontColor('#999999').width(60)
                  Text(this.selectedOrder.phone).fontSize(11).fontColor('#333333')
                }.margin({ bottom: 4 })
                Row() {
                  Text('地址').fontSize(11).fontColor('#999999').width(60)
                  Text(this.selectedOrder.address).fontSize(11).fontColor('#333333').layoutWeight(1)
                }.margin({ bottom: 4 })
                if (this.selectedOrder.remark.length > 0) {
                  Row() {
                    Text('备注').fontSize(11).fontColor('#999999').width(60)
                    Text(this.selectedOrder.remark).fontSize(11).fontColor('#FF4242')
                  }
                }
              }.width('90%').margin({ top: 12, bottom: 16 })
            }
          }
          .width('100%').layoutWeight(1)
        }
        .width('88%')
        .constraintSize({ maxHeight: '70%' })
        .backgroundColor('#FFFFFF').borderRadius(20)
        .position({ x: '6%', y: '15%' })
      }
      .width('100%').height('100%').position({ x: 0, y: 0 }).zIndex(999)
    }
  }

  // ============ 弹框: 活动详情 ============
  @Builder actDetailModal() {
    if (this.showActDetail && this.selectedActivity) {
      Column() {
        Column().width('100%').height('100%').backgroundColor('rgba(0,0,0,0.5)')
          .onClick(() => { this.showActDetail = false })

        Column() {
          Row() {
            Text(getActTypeIcon(this.selectedActivity.type) + ' ' + this.selectedActivity.title)
              .fontSize(15).fontWeight(FontWeight.Bold).fontColor('#333333')
              .layoutWeight(1)
            Text('✕').fontSize(18).fontColor('#999999')
              .onClick(() => { this.showActDetail = false })
          }.width('90%').padding({ top: 18, bottom: 12 })

          Divider().width('90%').height(0.5).backgroundColor('#FFF0EE')

          Column() {
            Text(this.selectedActivity.description).fontSize(12).fontColor('#666666').width('90%').margin({ top: 16 })

            Row() {
              Text('优惠力度').fontSize(11).fontColor('#999999').width(70)
              Text(this.selectedActivity.discount).fontSize(12).fontColor('#FF4242').fontWeight(FontWeight.Bold)
            }.width('90%').margin({ top: 12 })
            Row() {
              Text('活动时间').fontSize(11).fontColor('#999999').width(70)
              Text(this.selectedActivity.startTime + ' ~ ' + this.selectedActivity.endTime).fontSize(11).fontColor('#333333')
            }.width('90%').margin({ top: 8 })
            Row() {
              Text('参与人数').fontSize(11).fontColor('#999999').width(70)
              Text(fmtNum(this.selectedActivity.joinCount) + '人').fontSize(11).fontColor('#333333')
            }.width('90%').margin({ top: 8 })
            Row() {
              Text('预算使用').fontSize(11).fontColor('#999999').width(70)
              Text(fmtPrice(this.selectedActivity.used) + ' / ' + fmtPrice(this.selectedActivity.budget))
                .fontSize(11).fontColor('#333333')
            }.width('90%').margin({ top: 8, bottom: 16 })

            Row() {
              Text('关闭').fontSize(13).fontColor('#999999')
                .borderWidth(1).borderColor('#E0E0E0').borderRadius(20)
                .padding({ left: 20, right: 20, top: 10, bottom: 10 })
                .onClick(() => { this.showActDetail = false })
              Column().layoutWeight(1)
              Text('参与活动').fontSize(13).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
                .backgroundColor('#FF4242').borderRadius(20)
                .padding({ left: 20, right: 20, top: 10, bottom: 10 })
                .onClick(() => { this.showActDetail = false })
            }.width('90%').margin({ bottom: 20 })
          }
        }
        .width('88%')
        .constraintSize({ maxHeight: '65%' })
        .backgroundColor('#FFFFFF').borderRadius(20)
        .position({ x: '6%', y: '18%' })
      }
      .width('100%').height('100%').position({ x: 0, y: 0 }).zIndex(999)
    }
  }

  // ============ build ============
  build() {
    Column() {
      this.headerSection()
      this.tabContent()
      this.bottomTabBar()
      this.addGroupModal()
      this.groupDetailModal()
      this.editGroupModal()
      this.deleteConfirmModal()
      this.joinGroupModal()
      this.orderDetailModal()
      this.actDetailModal()
    }
    .width('100%').height('100%')
    .backgroundColor('#FFF5F5')
  }
}


在这里插入图片描述

总结

本文详细剖析了一个基于HarmonyOS API 24的ArkTS社群拼团管理平台的完整实现。该平台通过@Entry@Component@State@Builder等核心装饰器构建了声明式UI架构,将复杂的电商管理界面拆分为类型定义、静态配置、工具函数、主组件和多个构建器方法,实现了高度的模块化和可维护性。七大功能模块涵盖了拼团大厅、社群管理、营销活动、订单追踪、数据看板、排行榜和个人中心,每个模块都有差异化的布局设计和丰富的交互逻辑。

从技术实现角度来看,该平台的亮点在于多个方面。首先是配置驱动的视觉映射体系,通过Record类型的配置对象将业务状态映射为颜色和图标,实现了配置与逻辑的彻底分离。其次是纯ArkTS组件实现的数据可视化,柱状图、进度条和分类分布图全部通过Column的高度和宽度属性模拟,无需引入第三方图表库。再次是统一架构的弹窗系统,七种弹窗都遵循"条件渲染+遮罩层+主体层"的三段式模式,保证了交互体验的一致性和代码的可读性。

从架构设计角度来看,该平台展示了ArkTS声明式UI的最佳实践。@State驱动的响应式数据流使得界面更新变得自动化和精确化,@Builder方法的参数化设计实现了UI组件的高度复用,ForEach与数组方法的配合使得列表渲染简洁高效。状态联合类型(| null)的设计巧妙地表达了"选中"与"未选中"两种状态,避免了额外的布尔标记。整体代码结构清晰、层次分明,为HarmonyOS应用开发提供了一个具有参考价值的工程实践范例。

Logo

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

更多推荐