鸿蒙原生应用 ArkUI 声明式:积分商城首页的原价划线与缺货置灰
鸿蒙原生应用 ArkUI 声明式:积分商城首页的原价划线与缺货置灰
App 16「校园积分商城」首页(HomeTab),主题色
#F43F5E玫瑰红,4 个 Tab 分别为首页(🛍️)、商品(🎁)、兑换(🔄)、我的(👤)。首页采用固定 Header + 玫瑰红渐变 HeroCard(限时抽奖 + 3 栏积分统计 + 搜索框)+ 快捷功能入口(实物/虚拟/秒杀/抽奖)+ 本周积分获取条形图 + 5 个热门商品卡片(原价划线 + 库存进度条 + 缺货置灰)+ 兑换排行榜。本篇基于16-points-mall/entry/src/main/ets/pages/HomeTab.ets(约 333 行)逐段拆解,附 4 张实机截图。
一、整体结构:固定 Header + 内容流
HomeTab 与 App 14/15 首页骨架一致——固定 Header + Scroll 内容流,7 个 @Builder 块:
build() {
Column() {
this.Header()
Scroll() {
Column({ space: 14 }) {
this.HeroCard()
this.QuickActionRow()
this.WeeklyChart()
this.SectionTitle('热门商品', '查看全部')
this.ProductList()
this.SectionTitle('兑换排行', '更多')
this.RankList()
}
.width('100%')
.padding({ left: D.pad, right: D.pad, top: 14, bottom: D.pad + this.safeBottom + 20 })
}
.layoutWeight(1).scrollBar(BarState.Off).align(Alignment.Top)
}
.width('100%').height('100%').backgroundColor(C.bg)
}
7 个块按"活动 → 入口 → 数据 → 商品 → 排行"五段叙事:HeroCard(限时抽奖 + 3 栏统计 + 搜索)→ QuickActionRow(4 个兑换入口)→ WeeklyChart(本周积分获取)→ SectionTitle('热门商品') + ProductList(5 个商品卡)→ SectionTitle('兑换排行') + RankList(3 条排行)。
与 App 15 首页对比:App 15 主内容是"分类指南"(知识卡),App 16 主内容是"热门商品"(电商卡)——首页主内容反映产品形态(App 15 是知识库、App 16 是商城)。


二、Header + HeroCard:抽奖 Banner + 积分统计
Header 与 App 15 同构("积分商城" + Slogan"积分换好礼,学习更有动力")。HeroCard 的活动横幅是限时抽奖:
@Builder
HeroCard() {
Column({ space: 14 }) {
Row({ space: 12 }) {
Text('🎰').fontSize(36)
Column({ space: 4 }) {
Text('限时抽奖活动').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#FFFFFF')
Text('100 积分抽一次,赢大奖').fontSize(12).fontColor('#FFFFFF').opacity(0.85)
}
.alignItems(HorizontalAlign.Start).layoutWeight(1)
Text('参与 >').fontSize(12).fontColor('#FFFFFF')
}
.width('100%')
.onClick(() => { promptAction.showToast({ message: '积分抽奖' }); })
Row() {
ForEach(this.stats, (s: StatItem) => {
Column({ space: 4 }) {
Text(s.value).fontSize(22).fontWeight(FontWeight.Bold).fontColor('#FFFFFF')
Text(s.label).fontSize(11).fontColor('#FFFFFF').opacity(0.8)
}
.layoutWeight(1)
}, (s: StatItem) => s.label)
}
.width('100%')
Row({ space: 8 }) {
TextInput({ placeholder: '搜索商品...', text: this.keyword })
.onChange((val: string) => { this.keyword = val; })
.backgroundColor('#33FFFFFF').borderRadius(D.rSm).height(40)
.layoutWeight(1).placeholderColor('#FFFFFF').placeholderFont({ size: 13 })
.fontColor('#FFFFFF')
Text('🔍')
.fontSize(16).width(40).height(40).textAlign(TextAlign.Center)
.backgroundColor('#FFFFFF').borderRadius(D.rSm)
.onClick(() => {
promptAction.showToast({ message: this.keyword.length > 0 ? '搜索: ' + this.keyword : '请输入关键词' });
})
}
.width('100%')
}
.width('100%')
.padding(16)
.borderRadius(D.rLg)
.linearGradient({ angle: 135, colors: [[C.primary, 0.0], [C.accent, 1.0]] })
}
"限时抽奖:100 积分抽一次,赢大奖"——电商 App 的"抽奖引流"文案,用"小投入博大奖"的心理驱动用户消耗积分(把积分花出去,形成消耗闭环)。与 App 15 的"减碳种树"(公益激励)不同,App 16 是"抽奖"(利益驱动)——活动 Banner 的激励类型跟随产品定位。
3 栏统计:可用积分 2,860 / 累计积分 5,120 / 已兑换 12——"可用"与"累计"分开(可用=还能花的,累计=总共赚过的),这是积分体系的专业展示(微信支付余额/总消费同理)。"已兑换 12"与快捷入口/我的页统计一致。
搜索框与 App 15 完全同构(半透明白 + 白色占位 + 🔍)。
三、QuickActionRow:4 个兑换入口
QuickActionRow 是 4 个积分功能入口:
private quickActions: QuickAction[] = [
{ id: 1, emoji: '🎁', label: '实物兑换' },
{ id: 2, emoji: '🎫', label: '虚拟兑换' },
{ id: 3, emoji: '⚡', label: '限时秒杀' },
{ id: 4, emoji: '🎰', label: '积分抽奖' }
];
4 个入口对应商城的 4 种兑换形态:实物兑换(🎁)、虚拟兑换(🎫 会员/优惠券)、限时秒杀(⚡ 限时抢购)、积分抽奖(🎰 博彩)。"入口覆盖完整业务"——用户想换啥都能找到入口。
入口卡片:48×48 正圆(C.primarySoft 浅玫瑰红)+ emoji + 文字,与 App 14/15 同构(App 15 是四色,App 16 回归单色浅底——"单色 vs 多色"取决于业务是否需要颜色编码,商城没有"分类色"需求,单色更简洁)。
四、WeeklyChart:本周积分获取
WeeklyChart 与 App 14/15 完全同构(7 天条形图),标题和汇总换为积分语义:
@Builder
WeeklyChart() {
Column({ space: 12 }) {
Row() {
Text('本周积分获取').fontSize(15).fontWeight(FontWeight.Bold).fontColor(C.text)
Blank()
Text('+385 积分').fontSize(12).fontColor(C.primary)
}
.width('100%')
Row({ space: 4 }) {
ForEach(this.barData, (b: BarItem) => {
Column({ space: 6 }) {
Stack({ alignContent: Alignment.Bottom }) {
Column()
.width(18).height(b.value)
.linearGradient({ angle: 180, colors: [[C.primary, 0.0], [C.accent, 1.0]] })
.borderRadius(4)
}
.width(24).height(90)
Text(b.day).fontSize(10).fontColor(C.textDim)
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Center)
}, (b: BarItem) => b.day)
}
.width('100%')
}
.width('100%')
.padding(14)
.backgroundColor(C.card).borderRadius(D.rMd)
.border({ width: 1, color: C.stroke })
}
数据:一 30 / 二 55 / 三 40 / 四 70 / 五 85 / 六 60 / 日 45——周五 85 最高(周五"刷任务赚积分"高峰)。"+385 积分"的数学:30+55+40+70+85+60+45 = 385——总计 = 各天之和,数据自洽(这是本系列第一次"汇总 = 各柱之和"完全成立,App 15 的"平均 68%"也是,但 App 15 我的页"47 次"与趋势图口径不一致的坑在这里避免了——App 16 的首页与我的页都用同一份 barData + "+385 积分",跨页口径统一)。
五、ProductList:5 个商品卡(本页核心)
ProductList 是首页的"电商主内容"——5 个商品卡,每个含原价划线、库存进度条、缺货置灰、动态按钮:
@Builder
ProductList() {
Column({ space: 12 }) {
ForEach(this.products, (p: Product) => {
Column({ space: 10 }) {
Row({ space: 12 }) {
Row() {
Text(p.emoji).fontSize(28)
}
.width(56).height(56)
.backgroundColor(C.primarySoft).borderRadius(D.rMd)
.justifyContent(FlexAlign.Center)
Column({ space: 4 }) {
Row({ space: 6 }) {
Text(p.name).fontSize(15).fontWeight(FontWeight.Medium).fontColor(C.text)
Text(p.tag)
.fontSize(9).fontColor('#FFFFFF')
.padding({ left: 5, right: 5, top: 2, bottom: 2 })
.backgroundColor(p.tagColor).borderRadius(3)
}
.width('100%')
Row({ space: 6 }) {
Row({ space: 1 }) {
ForEach([0, 1, 2, 3, 4], (i: number) => {
Text(i < p.rating ? '★' : '☆')
.fontSize(11).fontColor(i < p.rating ? C.warn : C.textDim)
}, (i: number) => i.toString())
}
Text('原价 ' + p.original).fontSize(11).fontColor(C.textDim).decoration({ type: TextDecorationType.LineThrough })
}
}
.alignItems(HorizontalAlign.Start).layoutWeight(1)
Button(p.stock > 0 ? '兑换' : '缺货')
.fontSize(12).fontColor('#FFFFFF')
.backgroundColor(p.stock > 0 ? C.primary : C.textDim)
.borderRadius(D.rSm).height(32)
.onClick(() => {
if (p.stock > 0) {
promptAction.showToast({ message: '兑换 ' + p.name + ' 消耗 ' + p.points + ' 积分' });
} else {
promptAction.showToast({ message: '暂无库存' });
}
})
}
.width('100%')
Row({ space: 8 }) {
Text('库存').fontSize(11).fontColor(C.textDim)
Progress({ value: (p.total - p.stock) * 100 / p.total, total: 100 })
.color(C.primary).backgroundColor(C.cardSoft)
.layoutWeight(1).height(6)
Text(p.stock + '/' + p.total).fontSize(11).fontColor(C.textSub)
}
.width('100%')
Row() {
Text(p.points + ' 积分').fontSize(14).fontColor(C.primary).fontWeight(FontWeight.Bold)
Blank()
Text('立即兑换 >').fontSize(11).fontColor(C.primary)
}
.width('100%')
}
.width('100%')
.padding(14)
.backgroundColor(C.card).borderRadius(D.rMd)
.border({ width: 1, color: C.stroke })
.onClick(() => { promptAction.showToast({ message: p.name }); })
}, (p: Product) => p.id.toString())
}
.width('100%')
}
5.1 商品数据与标签
private products: Product[] = [
{ id: 1, emoji: '🎧', name: '蓝牙耳机', tag: '热门', tagColor: C.danger, points: 800, original: 1200, rating: 5, stock: 3, total: 10 },
{ id: 2, emoji: '📚', name: '学习笔记本', tag: '新品', tagColor: C.primary, points: 200, original: 350, rating: 4, stock: 15, total: 20 },
{ id: 3, emoji: '☕', name: '保温杯', tag: '推荐', tagColor: C.warn, points: 500, original: 800, rating: 5, stock: 8, total: 15 },
{ id: 4, emoji: '🎒', name: '校园背包', tag: '限量', tagColor: C.danger, points: 1200, original: 2000, rating: 5, stock: 2, total: 5 },
{ id: 5, emoji: '🎮', name: '游戏手柄', tag: '热门', tagColor: C.danger, points: 600, original: 900, rating: 4, stock: 0, total: 10 }
];
5 个商品的"电商四件套":
- 名称 + 标签:热门(红)/新品(主色)/推荐(橙)/限量(红)——标签颜色三态(danger/primary/warn),"新品"用主色、其他用警示色
- 星级:5 星/4 星——评分
- 原价划线:
TextDecorationType.LineThrough删除线(原价 1200)——"划线价"是电商的经典促销手法(显示原价被划掉,暗示"现在便宜了") - 积分价:底部
800 积分加粗主色 + "立即兑换 >"
decoration({ type: TextDecorationType.LineThrough }) 是 ArkUI 的文本装饰——删除线/下划线/上划线。"划线价"用官方 API 实现,比手动加"──"字符专业得多。
5.2 库存进度条 + 缺货置灰
库存进度条与 App 14/15 同构(Progress + 已兑换占比):
Row({ space: 8 }) {
Text('库存').fontSize(11).fontColor(C.textDim)
Progress({ value: (p.total - p.stock) * 100 / p.total, total: 100 })
.color(C.primary).backgroundColor(C.cardSoft)
.layoutWeight(1).height(6)
Text(p.stock + '/' + p.total).fontSize(11).fontColor(C.textSub)
}
(p.total - p.stock) * 100 / p.total:已兑换占比(蓝牙耳机 7/10=70%、游戏手柄 10/10=100%)。库存数字 "3/10" 双通道。
缺货置灰是电商页的关键细节:
Button(p.stock > 0 ? '兑换' : '缺货')
.fontSize(12).fontColor('#FFFFFF')
.backgroundColor(p.stock > 0 ? C.primary : C.textDim)
.borderRadius(D.rSm).height(32)
.onClick(() => {
if (p.stock > 0) {
promptAction.showToast({ message: '兑换 ' + p.name + ' 消耗 ' + p.points + ' 积分' });
} else {
promptAction.showToast({ message: '暂无库存' });
}
})
游戏手柄 stock=0 → 按钮文字变"缺货"、背景变灰色(C.textDim)、点击弹"暂无库存"——三处联动(文案/颜色/点击逻辑)。"缺货状态"是电商的必备状态(淘宝"已售罄"、京东"无货"),"置灰 + 文案 + 拦截"是标准实现。
库存 2/5 的校园背包(限量)进度条 60%、0/10 的游戏手柄(热门)进度条 100%——"缺货商品反而是最热门的"(卖光了),符合电商逻辑。
5.3 底部积分价 + 立即兑换
Row() {
Text(p.points + ' 积分').fontSize(14).fontColor(C.primary).fontWeight(FontWeight.Bold)
Blank()
Text('立即兑换 >').fontSize(11).fontColor(C.primary)
}
积分价大字加粗主色(800 积分)+ 右侧"立即兑换 >"——价格是卡片最重的信息(电商原则:价格要醒目)。
六、RankList:兑换排行
RankList 与 App 14/15 同构(3 条排行 + 奖牌 + 次数 + 增长率):
private rankings: RankItem[] = [
{ id: 1, emoji: '🥇', name: '蓝牙耳机', count: '128 次兑换', trend: '+22%' },
{ id: 2, emoji: '🥈', name: '保温杯', count: '96 次兑换', trend: '+15%' },
{ id: 3, emoji: '🥉', name: '学习笔记本', count: '85 次兑换', trend: '+10%' }
];
排行与商品列表呼应:蓝牙耳机(128 次第 1)正好是商品列表第一条、5 星、库存仅 3/10——"最热商品"三重印证(排行第 1 + 列表首位 + 库存快光)。"数据互相印证"让首页自洽可信。
增长趋势(+22%/+15%/+10%)递减——第 1 名涨得最快、第 3 名涨得最慢,"强者恒强"的电商马太效应。

七、电商首页的 5 个设计要点
App 16 首页是系列第一个"电商型首页",5 个电商专属设计值得沉淀:
- 原价划线(
LineThrough)——"原价 vs 现价"的促销感 - 库存进度条 + 库存数字——"稀缺感"驱动尽快兑换(3/10 剩 7 件,用户会想"快没了")
- 缺货置灰——
stock > 0三元:按钮文字/颜色/点击逻辑三联动 - 标签体系——热门/新品/推荐/限量,颜色区分(红/主色/橙)
- 积分价醒目——底部大字加粗主色,"价格 = 页面最重信息"
**"稀缺感(库存进度条)+ 促销感(划线价)+ 紧迫感(限时秒杀)"**是电商转化的三驾马车,App 16 首页集齐了两个半(秒杀在快捷入口)。

八、与 App 14/15 首页的模板对比
| 维度 | App 14 运动 | App 15 环保 | App 16 商城 |
|---|---|---|---|
| 主题色 | 绿 #16A34A | 绿 #22C55E | 玫红 #F43F5E |
| Hero 活动 | 运动会报名 | 减碳种树 | 限时抽奖 |
| 快捷入口 | 4 运动 | 4 分类(四色) | 4 兑换形态 |
| 主列表 | 场地卡 | 指南卡 | 商品卡(划线价+库存+缺货) |
| 排行 | 场地预约 | 垃圾识别 | 商品兑换 |
| 特色 | 预约进度 | 掌握度+学测 | 划线价+缺货态 |
"内容流首页"模板第 4 次复用(App 13/14/15/16),每次换主题 + 换数据 + 换模块——"模板复用 + 主题演化"方法论稳定输出。

九、数据一致性与积分体系
App 16 首页的积分数据跨页一致:
- 可用积分 2,860 = 我的页"积分 2,860" ✓
- 累计积分 5,120 = 我的页 Slogan"累计 5,120 积分" ✓
- 已兑换 12 = 兑换页"总兑换 12" ✓
- +385 积分 = 我的页"+385 积分" ✓(本系列跨页一致性最完整的 App)
积分体系的双向流动(App 16 的"积分经济"完整闭环):
赚积分(本周 +385:签到/任务/抽奖…)→ 积分池(可用 2,860)→ 花积分(兑换商品 800/500/200…)→ 记录(兑换页时间线)
首页展示"池"(可用/累计)→ 商品页花"池"(兑换)→ 兑换页记"账"(记录)→ 我的页管"户"(签到/任务)——4 个 Tab 围绕"积分"形成完整闭环,这是 App 16 数据架构最漂亮的地方。
十、电商首页的信息架构与转化设计
App 16 首页的信息架构遵循电商"浏览 → 心动 → 行动"的转化漏斗:
1. 浏览层(视觉吸引):HeroCard 抽奖活动(🎰 参与 >)+ 趋势图——让用户"先感兴趣"。
2. 认知层(建立需求):4 个快捷入口(实物/虚拟/秒杀/抽奖)——让用户知道"这里能换什么"。
3. 心动层(商品展示):5 个热门商品卡——核心转化区。每个卡的"电商心理武器":
- 划线原价(
原价 1200删除线)→ "省了 400 积分"的赚感 - 库存进度条(3/10)→ "快没了"的稀缺感
- 标签(热门/限量)→ "大家都在换"的从众感
- 积分价大字(800 积分)→ 价格的明确呈现
4. 行动层(点击转化):"兑换"按钮(缺货变"缺货"灰按钮拦截)+"立即兑换 >"——每个卡片都有明确的行动入口。
"浏览 → 认知 → 心动 → 行动"四层漏斗是电商首页的标准结构(淘宝/京东首页都如此)。每一层都有"让用户往下走"的设计:抽奖(好奇心)→ 入口(导航)→ 划线价(赚感)→ 按钮(行动)。
对比 App 14/15 首页:App 14/15 的漏斗是"浏览 → 认知 → 行动"(无"心动"层——场地/指南卡没有促销心理),App 16 多了"心动"层(划线价+稀缺感)——"电商专属的转化设计"。
十一、缺货态的完整处理
游戏手柄(stock=0)是本页唯一的缺货商品,它的处理是本页值得细读的"缺货样板":
1. 按钮态:文字"缺货" + 灰色背景(C.textDim)——从"行动按钮"变"状态提示"。
2. 点击拦截:if (p.stock > 0) { 兑换 } else { Toast "暂无库存" }——缺货点击不执行兑换,只提示。
3. 库存条:(10-0)*100/10 = 100% 满进度条 + "0/10"——"卖光了"的事实呈现。
4. 排名呼应:游戏手柄在排行里没出现(排行是蓝牙耳机/保温杯/笔记本)——缺货商品不入排行(没货还排第一会误导)。
"缺货 ≠ 隐藏":游戏手柄仍展示(用户能看到"这个商品很抢手卖光了"),只是不能买——"缺货展示"本身是营销(制造"下次要抢"的期待感)。淘宝的"已售罄"同理——"缺货即广告"。
真实项目的缺货处理进阶:① 缺货商品支持"到货提醒"(订阅按钮);② 缺货原因展示("限量 5 件已售罄");③ 相似商品推荐("看看保温杯")。"缺货态的转化设计"是电商的进阶能力。App 16 首页把这套"电商漏斗 + 缺货样板"完整呈现,读者可直接照搬到自己项目的商品列表页。记住:电商首页的每一寸设计都在为"转化"服务。
十二、小结
App 16 首页解析完毕。原价划线 + 库存进度条 + 缺货置灰 + 标签体系 + 积分闭环是五大亮点,配合玫瑰红商城主题(抽奖/秒杀/兑换),让首页成为"电商型首页"的标准范本。
更多推荐




所有评论(0)