鸿蒙原生应用 HarmonyOS ArkTS 实战:阅读管理我的页等级卡与成就徽章墙组合技
App 20「阅读管理」我的 Tab(ProfileTab),是个人中心页。整页用渐变用户卡片(小明同学 + Lv.4 + 距 Lv.5 还需 120 经验 + 74% 经验条)+ 4 栏彩色统计(历史订单 28/进行中 3/满意度 98%/连续天数 15)+ 本周使用条形图(20 次/周六高亮)+ 6 徽章成就墙(4 解锁 2 未解锁)+ 5 项菜单(我的收藏带 NEW 角标)+ About。与 App 13 我的页几乎完全同源(连"历史订单/省钱能手"都保留)——是模板复刻的最完整示范。本篇基于
20-reading-manager/entry/src/main/ets/pages/ProfileTab.ets(约 226 行)逐段拆解,附 4 张实机截图。
一、整体结构:6 个 @Builder 的紫色个人中心
ProfileTab 的结构与 App 13/17 我的页完全同源(0 个 @State):
build() { Column() { Scroll() { Column({ space: 0 }) { this.UserCard() this.StatRow() this.WeekChartCard() this.AchievementWall() this.MenuList() this.About() } .width('100%').padding({ bottom: D.pad + this.safeBottom + 20 }) } .layoutWeight(1).scrollBar(BarState.Off).align(Alignment.Top) } .width('100%').height('100%').backgroundColor(C.bg) }6 个块顺序:UserCard(身份)→ StatRow(数据)→ WeekChartCard(行为图)→ AchievementWall(成就)→ MenuList(资产)→ About(关于)。
space: 0让 UserCard 和 StatRow 紧贴——StatRowmargin({ top: -28 })上浮形成"卡片叠加"悬浮效果。
与 App 13 完全同源——6 块顺序、布局、负边距悬浮完全一致。唯一差异:主题色(紫 #8B5CF6 → 紫 #A855F7)。
项目源码开源:https://gitee.com/codenestFlow/HarmonyOSHub
二、UserCard:渐变 + Lv.4 + 经验条 + 签到
UserCard 与 App 13 我的页同构(Stack 头像/Lv.4 徽章 + 姓名/认证/Slogan + 经验条 + 签到/数据报告双按钮):
@Builder UserCard() { Column({ space: 12 }) { Row({ space: 14 }) { Stack({ alignContent: Alignment.BottomEnd }) { Row() { Text('👤').fontSize(34) } .width(60).height(60).backgroundColor('rgba(255,255,255,0.35)').borderRadius(D.rLg).justifyContent(FlexAlign.Center) .border({ width: 2, color: 'rgba(255,255,255,0.7)' }) Row() { Text('Lv.4').fontSize(9).fontColor('#FFFFFF').fontWeight(FontWeight.Bold) } .padding({ left: 5, right: 5, top: 1, bottom: 1 }) .backgroundColor(C.warn).borderRadius(4).border({ width: 1.5, color: '#FFFFFF' }) } Column({ space: 5 }) { Row({ space: 8 }) { Text('小明同学').fontSize(18).fontWeight(FontWeight.Bold).fontColor('#FFFFFF') Text('认证').fontSize(9).fontColor('#FFFFFF') .backgroundColor('rgba(255,255,255,0.3)').padding({ left: 5, right: 5, top: 1, bottom: 1 }).borderRadius(3) } Text('活跃用户 · 已使用 28 天').fontSize(11).fontColor('rgba(255,255,255,0.85)') Row({ space: 4 }) { Text('距 Lv.5 还需').fontSize(9).fontColor('rgba(255,255,255,0.7)') Text('120').fontSize(11).fontColor('#FFFFFF').fontWeight(FontWeight.Bold) Text('经验').fontSize(9).fontColor('rgba(255,255,255,0.7)') } Stack({ alignContent: Alignment.Start }) { Column().width('100%').height(4).backgroundColor('rgba(255,255,255,0.25)').borderRadius(2) Column().width('74%').height(4).backgroundColor('#FFFFFF').borderRadius(2) }.width(140) }.alignItems(HorizontalAlign.Start).layoutWeight(1) }.width('100%').padding({ left: D.pad, right: D.pad, top: this.safeTop + 30, bottom: 16 }) Row() { Row({ space: 5 }) { Text('🎁').fontSize(11) Text('每日签到').fontSize(11).fontColor('#FFFFFF') } .padding({ left: 12, right: 12, top: 6, bottom: 6 }) .backgroundColor('rgba(255,255,255,0.2)').borderRadius(D.rSm) .onClick(() => { promptAction.showToast({ message: '签到成功 +5 经验' }); }) Blank() Row({ space: 5 }) { Text('📊').fontSize(11) Text('数据报告').fontSize(11).fontColor('#FFFFFF') } .padding({ left: 12, right: 12, top: 6, bottom: 6 }) .backgroundColor('rgba(255,255,255,0.2)').borderRadius(D.rSm) .onClick(() => { promptAction.showToast({ message: '数据报告' }); }) }.width('100%').padding({ left: D.pad, right: D.pad, bottom: 14 }) } .width('100%').linearGradient({ angle: 135, colors: [[C.primary, 0.0], [C.accent, 1.0]] }) }2.1 身份设定
- 头像 + Lv.4 徽章(Stack BottomEnd):60×60 半透明白头像 + 橙底白边 Lv.4 徽章(
C.warn) - 姓名:"小明同学" + "认证"标签
- Slogan:"活跃用户 · 已使用 28 天"——时长叙事(对比 App 18 的"匹配率"、App 19 的"社团地位",App 20 是"使用时长")
- 经验:"距 Lv.5 还需 120 经验" + 74% 白色进度条
Lv.4 徽章用橙色(C.warn)——等级徽章用警示色形成"显著标记"(App 13/17/20 同款)。
2.2 伪签到(不改变状态)
Row() { Row({ space: 5 }) { Text('🎁').fontSize(11) Text('每日签到').fontSize(11).fontColor('#FFFFFF') } .padding({ left: 12, right: 12, top: 6, bottom: 6 }) .backgroundColor('rgba(255,255,255,0.2)').borderRadius(D.rSm) .onClick(() => { promptAction.showToast({ message: '签到成功 +5 经验' }); }) ...签到只弹 Toast("签到成功 +5 经验")不改变状态——"伪签到"(App 13/17 同款,App 18/19 是"真签到 exp+=10")——**"0 @State 个人中心的签到是伪交互"**是模板复刻的简化(读者想看真签到参考 App 18/19)。
"+5 经验"的数值(App 13/17/20 都是 +5)——"经验值参数"跟随模板(App 18/19 是 +10)。
2.3 紫色 135° 渐变
.width('100%').linearGradient({ angle: 135, colors: [[C.primary, 0.0], [C.accent, 1.0]] })#A855F7 → #C084FC135° 渐变——左上浓紫、右下浅紫——"紫色个人中心"(阅读 App 的"知识深度"调性)。
三、StatRow:4 栏统计(模板遗留)
StatRow 是 4 栏统计(历史订单 28/进行中 3/满意度 98%/连续天数 15),与 App 13 完全同源:
private stats: StatItem[] = [ { value: '28', label: '历史订单', color: C.primary }, { value: '3', label: '进行中', color: C.accent }, { value: '98%', label: '满意度', color: C.ok }, { value: '15', label: '连续天数', color: C.warn } ];关键问题:"历史订单/满意度"是电商统计,不是阅读统计——"个人中心统计没换业务"(App 13 音乐订单模板原样保留)——这是 App 20 模板复刻最明显的遗留:
| App 13(音乐) | App 20(阅读)应改为 |
|---|---|
| 历史订单 28 | 已读书籍 36 |
| 进行中 3 | 在读 3 |
| 满意度 98% | 阅读时长 128h |
| 连续天数 15 | 读书笔记 246 |
"统计项没换"让 App 20 的我的页与首页(已读书籍 36)数据矛盾(首页 36、我的页 28)——**"跨页数据不一致"**是本页最大的瑕疵(首页已读书籍 36 vs 我的页历史订单 28)——读者必须引以为戒:换业务必换所有数据。
4 栏彩色(紫/浅紫/绿/橙)——"颜色留给状态"。
四、WeekChartCard:本周使用条形图
WeekChartCard 与 App 13 同构(7 天条形图 + 20 次/周六高亮):
@Builder WeekChartCard() { Column({ space: 12 }) { Row() { Text('📈 本周使用').fontSize(15).fontWeight(FontWeight.Bold).fontColor(C.text) Blank() Text('20 次').fontSize(13).fontWeight(FontWeight.Bold).fontColor(C.primary) }.width('100%') Row({ space: 4 }) { ForEach(this.weekBars, (b: WeekBar, idx: number) => { Column({ space: 6 }) { Column() .width(18) .height(Math.max(6, b.value / 5 * 80)) .borderRadius(4) .linearGradient({ angle: 180, colors: idx === 5 ? [[C.primary, 0.0], [C.accent, 1.0]] : [[C.primarySoft, 0.0], [C.primary, 1.0]] }) Text(b.day).fontSize(10).fontColor(idx === 5 ? C.primary : C.textDim) .fontWeight(idx === 5 ? FontWeight.Bold : FontWeight.Normal) }.layoutWeight(1).alignItems(HorizontalAlign.Center) }, (b: WeekBar) => b.day) }.width('100%') } .width('100%').padding(14).backgroundColor(C.card).borderRadius(D.rMd) .border({ width: 1, color: C.stroke }) .margin({ top: 14, left: D.pad, right: D.pad }) }数据:2/3/1/4/3/5/2——周六(idx=5)value=5 最高、周三(idx=1? 不对,idx=2 周三 value=1)最低——周六高亮(idx === 5写死)。
b.value / 5 * 80归一化(最大值 5 → 80vp)。汇总"20 次":2+3+1+4+3+5+2 = 20 ✓——严格自洽。
"本周使用 20 次"在阅读 App 里是"打开 20 次"(次数 vs 首页的"时长"——"次数"和"时长"两个度量混用又是口径问题,但"20 次 = 各柱之和"至少数学自洽)。
五、AchievementWall:6 徽章成就墙(模板遗留)
AchievementWall 是 6 个徽章(4 解锁 2 未解锁),与 App 13 完全同源:
private achievements: Achievement[] = [ { icon: '🎯', title: '初次使用', unlocked: true, desc: '第一次下单' }, { icon: '🔥', title: '活跃用户', unlocked: true, desc: '使用7天' }, { icon: '⭐', title: '好评达人', unlocked: true, desc: '5星好评10次' }, { icon: '💰', title: '省钱能手', unlocked: true, desc: '节省100元' }, { icon: '👑', title: '资深用户', unlocked: false, desc: '使用100天' }, { icon: '💎', title: '完美用户', unlocked: false, desc: '满意度100%' } ];关键问题(同 StatRow):"第一次下单/好评达人/省钱能手/节省100元"是电商徽章,不是阅读徽章——**"成就徽章没换业务"**是 App 20 模板复刻的最大遗留:
| App 13 徽章 | App 20(阅读)应改为 |
|---|---|
| 初次使用(第一次下单) | 初读体验(读完第一本书) |
| 好评达人(5星好评10次) | 阅读达人(累计阅读100小时) |
| 省钱能手(节省100元) | 藏书阁(收藏100本书) |
| 完美用户(满意度100%) | 大师读者(读完50本书) |
"省钱能手"出现在阅读 App 的违和感(和 App 17 志愿版一样的问题)——**"徽章 = 业务成就"**是必须遵守的原则。
解锁/未解锁三件套(背景/透明度/文字)+4/6动态计算(.filter(a => a.unlocked).length)——App 13/20 用动态计算(App 14/15/16 硬编码)。
六、MenuList + About
MenuList 是 5 项菜单(历史订单/消费统计/提醒设置/我的收藏/设置),我的收藏带 NEW 角标:
private menus: MenuItem[] = [ { id: 1, emoji: '📦', title: '历史订单', desc: '查看所有历史记录', badge: '' }, { id: 2, emoji: '📊', title: '消费统计', desc: '月度数据分析', badge: '' }, { id: 3, emoji: '🔔', title: '提醒设置', desc: '订单状态通知', badge: '' }, { id: 4, emoji: '⭐', title: '我的收藏', desc: '常用列表', badge: 'NEW' }, { id: 5, emoji: '⚙️', title: '设置', desc: '账号与隐私', badge: '' } ];同 StatRow 的问题:"历史订单/消费统计/订单状态通知"是电商菜单——"菜单没换业务"(阅读 App 应改为"阅读记录/读书笔记/书签管理/阅读偏好"等)。
角标:NEW →C.ok绿(App 13/17/20 同款:NEW 用绿、数字用红)——m.badge.length > 0条件渲染 +Divider().margin({ left: 58 })分隔线。
About:Version 1.0.0+HarmonyOS 6.0+ · ArkTS · API 23两行(App 13 同款)。
七、@State 数量 = 0(模板遗留)
ProfileTab0 个 @State——所有数据private不可变,签到是"伪签到"。
4 个 Tab 的 @State 分布(App 20):
| 页面 | @State 数量 | 类型 |
|---|---|---|
| 首页 | 1(searchText) | 展示+搜索 |
| 书库 | 5(表单) | 创建表单 |
| 记录 | 1(activeTab) | 筛选 |
| 我的 | 0 | 纯展示 |
**"0 @State 个人中心"是 App 13/17/20 这条"模板复刻线"的标准(App 18/19 是 3 个 @State 的真签到)——"模板复刻 vs 功能进化的两条线"**在系列里并行。
八、"模板复刻"的正反面教材
App 20 我的页是系列"模板复刻"的最完整示范(App 13 紫 #8B5CF6 → App 20 紫 #A855F7),也是最典型的反面教材:
复刻对了的:
- 主题色(紫 #A855F7 + 浅紫 + 渐变)
- 布局(6 块结构 + 负边距悬浮)
- 组件(Stack 头像/Lv 徽章/进度条/Toggle/角标)
- 交互(@Builder 拆分 + @State 管理)
没换的(反面教材):
- 统计(历史订单/满意度 → 应是已读书籍/时长)
- 徽章(省钱能手 → 应是阅读成就)
- 菜单(历史订单/消费统计 → 应是阅读记录/书签)
- 文案("下单""订单" → 应是"阅读""笔记")
"换主题不换业务"是模板复刻的最大陷阱——用户打开"阅读管理"却看到"省钱能手"徽章,违和感极强。真实项目套用模板必须"数据 + 文案全换"。
正面示范(App 18/19):App 18 社交、App 19 摄影的个人中心完全定制了业务数据(社交徽章/摄影徽章/业务统计)——"换主题 + 换业务 = 成功复刻"。
九、阅读 App 个人中心的"业务化改造清单"
对比 App 13(音乐)与 App 20(阅读)的个人中心,整理一份**"业务化改造清单"**——读者套用任何个人中心模板时按此清单逐项替换:
| 模板项 | App 13(音乐) | App 20(阅读)应改为 |
|---|---|---|
| 用户名 | 小明同学 | 读者小明(或"书友") |
| Slogan | 活跃用户·已使用28天 | 阅读达人·累计阅读128小时 |
| 统计1 | 历史订单 28 | 已读书籍 36 |
| 统计2 | 进行中 3 | 在读 3 |
| 统计3 | 满意度 98% | 阅读时长 128h |
| 统计4 | 连续天数 15 | 读书笔记 246 |
| 徽章 | 初次使用/好评达人/省钱能手 | 初读体验/阅读达人/藏书阁 |
| 菜单 | 历史订单/消费统计 | 阅读记录/读书笔记 |
| 周图 | 本周使用 20 次 | 本周阅读 20 次 |
| 签到 | +5 经验 | +10 阅读经验 |
"个人中心业务化"的 5 大块:用户/统计/徽章/菜单/周图——每块都要随业务换。
判断标准:把 App 20 的每个字读一遍,"出现'订单/消费/省钱'就是没换干净"——"业务词检测"是最快的复刻验收法(搜索"订单/消费/省钱/下单"关键词,有残留就换)。
十、个人中心模板的"系列演变线"
回顾系列 6 个"个人中心"(App 13/14/15/16/17/18/19/20 中选代表性的),可以看清两条演变线:
线 1:模板复刻线(骨架复用、业务未换):
| App | 用户 | 等级 | 业务数据 |
|---|---|---|---|
| 13 音乐 | 小明同学 | Lv.4 | 订单/省钱(音乐场景违和) |
| 17 志愿 | 小明同学 | Lv.4 | 订单/省钱(志愿场景违和) |
| 20 阅读 | 小明同学 | Lv.4 | 订单/省钱(阅读场景违和) |
线 2:业务定制线(骨架 + 业务全换):
| App | 用户 | 等级 | 业务数据 |
|---|---|---|---|
| 14 运动 | 张运动 | Lv.5 | 预约/时长/卡路里 |
| 15 环保 | 李环保 | Lv.6 | 分类/准确率/减碳 |
| 16 商城 | 王积分 | Lv.7 | 积分/兑换/收藏 |
| 18 社交 | 张同学 | Lv.8 | 好友/互动/匹配率 |
| 19 摄影 | 摄影爱好者 | Lv.6 | 作品/点赞/获奖 |
对比结论:"业务定制线"的 5 个 App 是成功的模板复用(数据/徽章/菜单全换);"模板复刻线"的 3 个 App 是偷懒的模板复用(只换主题色)。读者应学习线 2、警惕线 1——"复用模板"和"复制模板"只有一字之差,质量天壤之别。
"等级递进"的彩蛋:业务定制线等级 5→6→7→8→6 递增(反映"每个 App 用户更资深"的叙事),模板复刻线固定 Lv.4——"等级 = 业务定制的深度信号"。
十一、总结
App 20「阅读管理」我的页解析完毕。Lv.4 紫色个人中心是 App 13 模板的最完整复刻,同时也是"换主题不换业务"的反面教材——读者应对比 App 18/19 的"业务定制版"学习正确的模板复用。