鸿蒙原生应用实战:校园投票我的页 —— 四块最简结构的个人中心设计
鸿蒙原生应用实战:校园投票我的页 —— 四块最简结构的个人中心设计
App 22「校园投票管理」我的 Tab(ProfileTab),是个人中心页——结构最简洁(4 块):渐变用户卡(🗳️ 投票达人 + 参与 28 次投票)+ 4 栏统计(创建投票 6/参与投票 28/历史投票 12/进行中 3)+ 5 菜单(参与记录/创建投票/历史投票/投票统计/设置)+ About(校园投票管理 v1.0.0)。与 App 21 问卷我的页同构(4 块最简 + 92% 宽 + 负边距悬浮)——是"任务型应用"的典型个人中心。本篇基于
22-campus-vote/entry/src/main/ets/pages/ProfileTab.ets(约 128 行)逐段拆解,附 4 张实机截图。
一、整体结构:4 个 @Builder 的最简个人中心
ProfileTab 是系列最简洁的个人中心——4 个 @Builder(UserCard / StatRow / MenuList / About):
build() {
Column() {
Scroll() {
Column() {
this.UserCard()
this.StatRow()
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)
}
4 块顺序:UserCard(身份)→ StatRow(数据)→ MenuList(资产)→ About(关于)。
与 App 21 问卷我的页 100% 同构(结构/StatRow 92% 宽 + 负边距/MenuList emoji+标题+>/About 双行版本)——"任务型个人中心模板"再次复用。
二、UserCard:紫色渐变 + 称号型身份
UserCard 是简洁的横向用户卡(与 App 21 同款"最简 UserCard"):
@Builder UserCard() {
Row({ space: 14 }) {
Row() {
Text('🗳️').fontSize(32)
}
.width(64).height(64)
.backgroundColor('#33FFFFFF').borderRadius(32)
.justifyContent(FlexAlign.Center)
Column({ space: 5 }) {
Text('投票达人').fontSize(18).fontWeight(FontWeight.Bold).fontColor('#FFFFFF')
Text('参与 28 次投票').fontSize(12).fontColor('#FFFFFF').opacity(0.85)
}
.alignItems(HorizontalAlign.Start).layoutWeight(1)
}
.width('100%')
.padding({ top: this.safeTop + 30, left: D.pad, right: D.pad, bottom: 44 })
.linearGradient({ angle: 135, colors: [[C.primary, 0.0], [C.accent, 1.0]] })
}
关键设计:
- 64×64 正圆头像(
borderRadius(32))—— 系列标准(App 18/19/20/21/22 同款 64×64) - 🗳️ 投票箱 emoji —— 投票 App 的视觉符号(与 App 18"张同学"/App 19"📷"/App 21"📊"/App 20"📚"同款"业务 emoji 头像")
- "投票达人"称号 —— 称号型身份(App 19"摄影爱好者"/App 21"问卷达人"同款)——"业务 + 身份"组合
- "参与 28 次投票" Slogan —— 业务型 Slogan(不用"已使用 X 天")——"我的投票身份"叙事
- 紫色 135° 渐变 ——
linearGradient主题色
bottom: 44 给 StatRow margin: -24 留 20vp 实际重叠——**"卡片叠加悬浮"**效果——比 App 14/15/16 的 bottom: 16 + margin: -28 略多。

三、StatRow:4 栏统计(投票双重视角)
StatRow 是 4 栏统计(与 App 21 我的页统计项结构一致):
private stats: StatItem[] = [
{ value: '6', label: '创建投票' },
{ value: '28', label: '参与投票' },
{ value: '12', label: '历史投票' },
{ value: '3', label: '进行中' }
];
4 栏的"投票双重视角":
| 数字 | 标签 | 视角 |
|---|---|---|
| 6 | 创建投票 | 创作者(我创建了 6 份投票) |
| 28 | 参与投票 | 填写者(我参与了 28 次投票) |
| 12 | 历史投票 | 已结束/已投票(12 条历史) |
| 3 | 进行中 | 待处理/未结束(3 条进行中) |
"创作者 + 填写者"双重视角(与 App 21 问卷的"我的问卷 + 填写记录"一致)——投票平台也是双向 UGC:
- 创作者:我发起的(6 份创建 + 3 进行中 + 部分历史)
- 填写者:我参与的(28 次参与 + 12 条历史)
"28 次参与"是 UserCard Slogan 的数据来源 —— "参与 28 次投票" = StatRow 第 2 栏——"用户卡 Slogan = 统计指标" 跨组件数据呼应。
4 栏同色 C.text 深色——"颜色留给状态"——投票统计没有"好/坏"语义(不像满意度 98% 那种),统一深色更稳重。
width('92%') 92% 宽 + margin({ top: -24 }) —— 与 App 21 同款——"卡片叠加"(与 UserCard 底部 20vp 重叠)。
StatRow @Builder 完整源码
StatRow 的 @Builder 实现(76-93 行)是系列"4 栏统计"的标准写法:
@Builder
StatRow() {
Row() {
ForEach(this.stats, (s: StatItem) => {
Column({ space: 4 }) {
Text(s.value).fontSize(18).fontWeight(FontWeight.Bold).fontColor(C.text)
Text(s.label).fontSize(11).fontColor(C.textDim)
}
.layoutWeight(1)
}, (s: StatItem) => s.label)
}
.width('92%')
.padding({ top: 14, bottom: 14 })
.backgroundColor(C.card)
.borderRadius(D.rMd)
.border({ width: 1, color: C.stroke })
.margin({ top: -24 })
}
StatRow @Builder 关键设计:
ForEach(this.stats, ...)遍历 4 栏——每栏一个Column({ space: 4 }),数字在上(18sp 加粗)、标签在下(11sp 灰色)——"数字 + 标签"垂直组合。.layoutWeight(1)等分 4 栏——4 个 Column 各占 1/4 宽——**"4 栏等宽"**是统计行的标准布局。width('92%')+margin({ top: -24 })——92% 宽 + 负 24vp 上边距——"悬浮在 UserCard 底部"——与 UserCard 的bottom: 44配合(44 - 24 = 20vp 实际重叠)。.border({ width: 1, color: C.stroke })——1vp 描边——"卡片边框"——让 StatRow 在视觉上与 UserCard 区分开(不使用阴影,用边框更轻量)。padding({ top: 14, bottom: 14 })——上下 14vp——"上下宽松"——因为 4 栏已经等分,左右无需额外 padding。
StatRow 与 UserCard 的"悬浮叠加"是本系列个人中心的标志性设计——负边距让两张卡片"咬合"——视觉上"用户卡 + 统计卡 = 一个整体"——比"两张独立卡片"更有层次感。
四、MenuList:5 项菜单(emoji + 标题 + >)
MenuList 是 5 项菜单(与 App 21 问卷的 5 项菜单同款"最简结构"):
private menus: MenuItem[] = [
{ id: 1, emoji: '📋', title: '参与记录' },
{ id: 2, emoji: '➕', title: '创建投票' },
{ id: 3, emoji: '📜', title: '历史投票' },
{ id: 4, emoji: '📊', title: '投票统计' },
{ id: 5, emoji: '⚙️', title: '设置' }
];
5 项菜单的"投票业务"覆盖:
| 菜单 | 业务 |
|---|---|
| 📋 参与记录 | 我投过哪些(填写者历史) |
| ➕ 创建投票 | 发起新投票(创作者入口) |
| 📜 历史投票 | 已结束的投票 |
| 📊 投票统计 | 个人数据看板 |
| ⚙️ 设置 | 通用 |
5 项按"使用频次"排序:
- 高频:参与记录、创建投票(日常操作)
- 中频:历史投票、投票统计(偶尔查)
- 低频:设置(少用)
"➕ 创建投票"和"📊 投票统计"是投票平台特色菜单——前者是创作者入口,后者是数据看板——"特色菜单 = 业务核心"。
"📜 历史投票"是 App 22 与 App 21 的差异——投票需要"历史记录"(投过什么可追溯),问卷是"匿名参与"(不需要历史)——"业务差异决定菜单"。
> 箭头 + emoji + 标题 三件套——"最简菜单结构"(无副标题/无角标/无图标块)——与 App 21 问卷完全一致。
五、MenuList 的"5 项"结构
@Builder MenuList() {
Column() {
ForEach(this.menus, (m: MenuItem, idx: number) => {
Row({ space: 12 }) {
Text(m.emoji).fontSize(18)
Text(m.title).fontSize(14).fontColor(C.text).layoutWeight(1)
Text('>').fontSize(14).fontColor(C.textDim)
}
.width('100%')
.padding({ left: 14, right: 14, top: 14, bottom: 14 })
.onClick(() => { promptAction.showToast({ message: m.title }); })
if (idx < this.menus.length - 1) {
Divider().color(C.stroke).strokeWidth(0.5)
}
}, (m: MenuItem) => m.title)
}
.width('92%')
.margin({ top: 16 })
.backgroundColor(C.card)
.borderRadius(D.rMd)
.border({ width: 1, color: C.stroke })
}
width('92%') 92% 宽 + margin({ top: 16 }) —— 与 StatRow 的 92% 宽形成"双 92%""两条 92% 卡片叠加" —— 视觉上"StatRow + MenuList 都是 92% 宽"——"等宽 = 一组"。
if (idx < this.menus.length - 1) 最后一条不加分隔线 —— 用 idx 判断(更可靠)—— Divider().strokeWidth(0.5) 0.5vp 超细分隔线。
六、About:双行版本信息
About 是简洁的双行版本信息(与 App 21 问卷同款):
@Builder About() {
Column({ space: 4 }) {
Text('校园投票管理').fontSize(12).fontColor(C.textDim)
Text('Version 1.0.0').fontSize(11).fontColor(C.textDim).opacity(0.7)
}
.width('100%').margin({ top: 24 })
}
"校园投票管理 / Version 1.0.0" —— App 名 + 版本号 —— 系列标准 About(App 13-21 同款)——opacity(0.7) 让版本号行更淡。
App 22 没有"© 2024 校园"版权行(App 14/15/16 有)—— "最简 About"(App 21/22 同款)。
七、@State 数量 = 0(系列第 6 个"0 @State 个人中心")
ProfileTab 0 个 @State——所有数据 private 不可变。这是系列第六个"0 @State"个人中心(App 13/17/20/21/22 + Func2Tab)——"展示型页面 = 0 @State"规律继续成立。
4 个 Tab 的 @State 分布(App 22):
| 页面 | @State | 类型 |
|---|---|---|
| 首页 | 1(tab) | Tab 视觉切换 |
| 投票 | 2(picked/voted) | 真交互 |
| 结果 | 0 | 纯展示 |
| 我的 | 0 | 纯展示 |
App 22 是"任务型 2 状态控制"的代表——picked/voted 2 个状态精确控制整个投票交互。
八、数据一致性
UserCard "参与 28 次投票" = StatRow 第 2 栏 "参与投票 28" ✓——Slogan 和统计的精确对应——读者一看就知道"这 28 次是从 StatRow 来的"。
没有"3 进行中"vs 首页/结果页的直接数据——"进行中"是用户维度(我投的还在进行中的 3 个)——不是平台维度(首页 4 张投票卡是"平台所有进行中")——两个不同维度的"进行中"。
九、与 App 21 问卷我的页的 100% 同构
App 22 我的页与 App 21 问卷我的页几乎 100% 同构——模板复刻第 4 次:
| 组件 | App 21(问卷) | App 22(投票) |
|---|---|---|
| UserCard | 📊 问卷达人 + 8 份问卷 | 🗳️ 投票达人 + 28 次投票 |
| StatRow 4 栏 | 我的问卷/填写记录/收集份数/进行中 | 创建投票/参与投票/历史投票/进行中 |
| MenuList 5 项 | 我的问卷/填写记录/数据导出/模板收藏/设置 | 参与记录/创建投票/历史投票/投票统计/设置 |
| About | 在线问卷调查 v1.0.0 | 校园投票管理 v1.0.0 |
"4 个组件 + 同结构"是任务型个人中心模板的完整复用——读者做自己的"任务型"产品(记账/打卡/工具)可直接套用此模板。
十、任务型个人中心的"4 块"模板
**App 21 问卷 + App 22 投票 的"4 块个人中心"**可抽成通用模板:
UserCard(业务 emoji + 称号 + 业务 Slogan)
+ StatRow(4 栏业务统计,92% 宽 + 负边距悬浮)
+ MenuList(5 项业务菜单,92% 宽)
+ About(App 名 + 版本号)
"4 块 = 4 业务信息层":
- UserCard —— 身份("我是谁、我做多少")
- StatRow —— 数据("4 个业务指标")
- MenuList —— 资产/操作("我能做什么")
- About —— 版本("当前版本")
这个模板适合"轻量级任务型 App"——任何"用户来 App 是做任务"的产品可套用——对比"内容型/社交型"的复杂个人中心(Lv/经验/签到/成就墙),任务型只需要"我做了多少"。

十一、"参与 28 次投票"的 Slogan 设计
App 22 UserCard Slogan "参与 28 次投票" 值得从文案角度分析:
Slogan 4 要素:
- 数字(28)—— 精确、可信
- 动词(参与)—— 行为描述
- 对象(投票)—— 业务核心
- 隐含时间(隐含"历史累计")—— 不写"已使用 28 天"避免"老气"
**"数字 + 动词 + 对象"**是业务型 Slogan 的标准结构:
- App 18:"匹配 X 位同好"
- App 19:"累计 X 作品"
- App 20:"已读书籍 X 本"
- App 21:"已创建 X 份问卷"
- App 22:"参与 X 次投票"
"X 次投票"是"活跃度指标"——比"已使用 X 天"(时长)更有激励性——**"X 次 = 我做了多少"**比"X 天 = 我注册了多久"更"有成就感"。
真实项目的 Slogan 设计原则:
- 数字 + 动词 + 对象——三件套不能少
- 避免"已使用 X 天"——"天数"暗示"用户老了"
- 聚焦业务而非时间——"我做了什么"比"我注册了多久"更正面
App 22 Slogan 是系列"业务型 Slogan"的代表(与 App 13"已使用 28 天"对比鲜明)。

十二、"4 块个人中心"与"系列其他个人中心"的数据维度对比
对比系列 5 个"个人中心"的数据维度:
| App | 4 栏统计 | 数据维度 |
|---|---|---|
| 13 音乐 | 订单 28/进行中 3/满意度 98%/连续 15 | 消费 + 满意度(电商) |
| 17 志愿 | 同上(订单没换) | 同上(遗留) |
| 20 阅读 | 同上 | 同上(遗留) |
| 21 问卷 | 我的问卷 8/填写记录 36/收集份数 2,860/进行中 3 | 创作者 + 填写者(双向) |
| 22 投票 | 创建投票 6/参与投票 28/历史投票 12/进行中 3 | 创作者 + 参与者(双向) |
App 21 和 App 22 都是"双向 UGC"——用户既是创作者(创建问卷/投票)又是参与者(填写/投票)——4 栏统计覆盖"双向":
- 创作者维度:创建 X(创建数)+ 进行中 X(待处理)
- 参与者维度:参与 X(参与数)+ 历史 X(已完成)
"双向 UGC"4 栏公式:创建 + 参与 + 进行中 + 历史——4 个角度完整刻画"创作者 + 参与者"双身份——读者做自己"双向 UGC"产品时可参考此公式。
十三、"任务型 App" 与 "内容型 App" 个人中心 5 大差异
总结系列 2 大类 App 的个人中心差异:
| 维度 | 任务型(App 21/22) | 内容型(App 13/17/18/19/20) |
|---|---|---|
| @State 数量 | 0-1 | 1-3 |
| @Builder 数量 | 4 | 6 |
| 个人卡 | 简洁(emoji+称号+Slogan) | 复杂(Stack+等级+进度条+签到) |
| 统计 | 4 栏业务数据 | 4 栏+周图+成就墙 |
| 菜单 | 5 项业务菜单 | 6 项 + 数字/NEW 角标 |
5 大差异都指向同一个核心:
"任务型 App = 用户来做事"——用户来"创建投票/填写问卷"——做完就走——"简洁个人中心"足够。 "内容型 App = 用户来消费"——用户来"看作品/看书/刷社交"——需要长期留存——"游戏化个人中心"留住用户。
真实项目决定个人中心复杂度:
- 任务型(记账/打卡/工具)—— 4 块简洁
- 内容型(小红书/抖音/B站)—— 6 块 + 游戏化
- 混合型(淘宝/美团)—— 5 块(任务 + 留存兼顾)
"用户来做什么"决定个人中心骨架——本系列 2 大类对比就是设计参考。
十四、App 22 "任务闭环" vs App 18 "关系闭环"
对比 App 22 投票的"任务闭环"和 App 18 社交的"关系闭环":
App 22 投票的"任务闭环":
- 首页(找投票)→ 投票(完成任务)→ 结果(看结果)→ 我的(看历史)
- "任务 = 投票"——完成就结束
- 用户不需要"回来"(除非有新投票)
App 18 社交的"关系闭环":
- 首页(找同好)→ 匹配(建立关系)→ 消息(维护关系)→ 我的(身份维护)
- "关系 = 朋友"——持续维护
- 用户需要"持续回来"(维护关系)
"任务闭环" vs "关系闭环":
- 任务 = 一次性,闭环短——完成就走——"简洁个人中心"足够
- 关系 = 持续性,闭环长——要维护——"游戏化个人中心"留住
"用户来 App 完成什么"决定"用户会不会回来"——任务型 App 解决"我做完了"、关系型 App 解决"我要常来"——两类 App 的留存策略不同。
十五、MenuList @Builder 源码与交互细节
MenuList 的 @Builder 实现(95-117 行)是系列"菜单列表"的标准写法:
@Builder MenuList() {
Column() {
ForEach(this.menus, (m: MenuItem, idx: number) => {
Row({ space: 12 }) {
Text(m.emoji).fontSize(18)
Text(m.title).fontSize(14).fontColor(C.text).layoutWeight(1)
Text('>').fontSize(14).fontColor(C.textDim)
}
.width('100%')
.padding({ left: 14, right: 14, top: 14, bottom: 14 })
.onClick(() => { promptAction.showToast({ message: m.title }); })
if (idx < this.menus.length - 1) {
Divider().color(C.stroke).strokeWidth(0.5)
}
}, (m: MenuItem) => m.title)
}
.width('92%')
.margin({ top: 16 })
.backgroundColor(C.card)
.borderRadius(D.rMd)
.border({ width: 1, color: C.stroke })
}
MenuList @Builder 关键设计:
Column()—— 菜单项之间无间距——因为整个 Column 是一张卡片(白底 + 圆角 + 边框),菜单项之间用分隔线区分——if (idx < this.menus.length - 1) { Divider()... }最后一条不加分隔线。Row({ space: 12 })三列 —— emoji 图标(18sp)+ 标题(14splayoutWeight(1))+ 箭头>(14sp 灰色)——**"图标 + 标题 + 箭头"**是系列菜单行的标准三列。padding({ left: 14, right: 14, top: 14, bottom: 14 })—— 四向 14vp 等距 padding——**"等距 padding"**控制行高。.onClick(() => { promptAction.showToast(...) })—— 点击弹出 toast——系列标准"占位交互"——真实项目中应跳转到对应子页面。整张卡片样式 ——
width('92%')+backgroundColor(C.card)+borderRadius(D.rMd)+border({ width: 1, color: C.stroke })——与 StatRow 同款卡片样式——"个人中心 = 统一卡片风格"。
十六、总结
App 22 我的页解析完毕。UserCard 紫色渐变 + StatRow 4 栏统计 + MenuList 5 项菜单 + About 双行版本是 4 大核心组件。"0 @State + 4 @Builder + 静态数据"是最简个人中心的标志——适合"任务型 App"(投票/问卷/打卡)。StatRow 的"悬浮叠加"设计(负边距 + 边框)是系列个人中心的视觉亮点——让"用户卡 + 统计卡"在视觉上合为一体。"4 块模板"(用户卡/统计行/菜单列表/关于)是任务型个人中心的通用骨架——读者做自己的"任务型 App"时可以直接复用。

更多推荐





所有评论(0)