鸿蒙原生应用 ArkUI 实战:实验室预约我的页 —— 任务型个人中心的四块写法
App 23「实验室设备预约」我的 Tab(ProfileTab),是个人中心页——最简 4 块结构:渐变用户卡(🔬 科研达人 + 累计使用 36 小时)+ 4 栏统计(预约记录 18/使用时长 36h/收藏设备 4/待使用 2)+ 5 菜单(预约记录/使用记录/设备收藏/联系管理员/设置)+ About(实验室设备预约 v1.0.0)。与 App 21/22 我的页同构(4 块最简 + 92% 宽 + 负边距悬浮)——是"任务型应用"的典型个人中心。本篇基于
23-lab-booking/entry/src/main/ets/pages/ProfileTab.ets(约 101 行)逐段拆解,附 4 张实机截图。
一、整体结构:4 个 @Builder 的最简个人中心
ProfileTab 是系列最简洁的个人中心——4 个 @Builder:
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 问卷/App 22 投票我的页 100% 同构——"任务型个人中心模板"第 3 次复用(21/22/23 连续 3 个 App 都是"4 块最简")——"任务型 App 个人中心 = 4 块"成为系列的稳定模式。
项目源码开源:https://gitee.com/codenestFlow/HarmonyOSHub
二、UserCard:深青渐变 + 科研达人
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('累计使用 36 小时').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))—— 系列标准 - 🔬 显微镜 emoji—— 科研设备视觉符号(呼应 App 主题"实验室")
- "科研达人"称号—— 称号型身份(App 19"摄影爱好者"/App 21"问卷达人"/App 22"投票达人"同款)——"业务 + 达人"
- "累计使用 36 小时" Slogan——业务型 Slogan("累计使用" = 科研时长)——与 StatRow"使用时长 36h"一致 ✓
- 深青 135° 渐变——
linearGradient主题色
"累计使用 36 小时"—— 科研人员的核心指标(实验时长)——"科研达人"的称号靠"使用时长"支撑(36 小时 = 做过 36 小时实验)。
bottom: 44给 StatRowmargin: -24留 20vp 实际重叠——"卡片叠加悬浮"。
渐变背景的"135° 对角线"解析:
linearGradient({ angle: 135, colors: [[C.primary, 0.0], [C.accent, 1.0]] })—— 135° 意味着渐变从左上角到右下角。为什么选 135° 而非 90°(垂直)或 0°(水平)?
- 90° 垂直渐变:上深下浅,视觉重心偏上,适合"标题栏"但用户卡需要整体均匀
- 0° 水平渐变:左深右浅,横向动感强但与"头像在左、文字在右"的布局冲突
- 135° 对角渐变:左上深 → 右下浅,与"头像左上 + 文字右侧"的视觉流一致——深色区域恰好衬托头像,浅色区域衬托文字——"渐变方向 = 内容流向"
#33FFFFFF半透明头像底色——#33= 20% 不透明度白色,在深青渐变上形成**"磨砂玻璃"效果**——头像区域不抢渐变主色,但提供视觉边界——"半透明叠加"是渐变背景上的标准头像处理。
opacity(0.85)Slogan 淡化—— 称号fontColor('#FFFFFF')全不透明(主信息),Sloganopacity(0.85)(次信息)——"同色不同透明度"实现层级区分,比用不同颜色更和谐。
三、StatRow:4 栏统计
StatRow 是 4 栏统计(与 App 21/22 同款):
private stats: StatItem[] = [ { value: '18', label: '预约记录' }, { value: '36', label: '使用时长(h)' }, { value: '4', label: '收藏设备' }, { value: '2', label: '待使用' } ];4 栏的"科研预约视角":
| 数字 | 标签 | 含义 |
|---|---|---|
| 18 | 预约记录 | 累计预约 18 次 |
| 36 | 使用时长(h) | 累计使用 36 小时 |
| 4 | 收藏设备 | 收藏 4 台常用设备 |
| 2 | 待使用 | 2 条待使用的预约 |
"预约记录 + 使用时长 + 收藏设备 + 待使用"—— 科研人员的 4 个核心指标:
- 预约记录 18—— 活跃度(约过多少次)
- 使用时长 36h—— 投入度(用了多久)
- 收藏设备 4—— 偏好(常用设备)
- 待使用 2—— 待办(待处理的预约)
"使用时长 36h"= UserCard Slogan 的数据来源 ✓ ——"用户卡 Slogan = 统计指标"跨组件呼应(App 22 同款设计)。
4 栏同色C.text深色—— "颜色留给状态"。
width('92%')+margin({ top: -24 })—— 与 App 21/22 同款"卡片叠加"。
"浮动卡片"的负边距设计解析:
StatRow 的margin({ top: -24 })是负边距悬浮的核心——统计卡向上"侵入"UserCard 的bottom: 44padding 区域,形成 20vp 的视觉重叠:
- UserCard
bottom: 44—— 底部留白 44vp - StatRow
margin: -24—— 向上偏移 24vp - 实际重叠= 44 - 24 = 20vp —— 统计卡"骑"在用户卡底部
为什么用负边距而非 Stack 叠层?
- Stack 方案:
Stack() { UserCard(); StatRow().offset({ y: -24 }) }—— 需要额外容器,且 Scroll 内 Stack 布局复杂 - 负边距方案:
Column() { UserCard(); StatRow().margin({ top: -24 }) }——Column 流式布局 + 负边距微调,代码更简洁
"负边距悬浮"是系列个人中心的标志性技法——App 21/22/23 连续 3 个 App 使用同一方案——"Column + 负 margin"比"Stack + offset"更适合 Scroll 内的卡片叠加。
4 栏等宽布局:Row()+layoutWeight(1)每栏——4 栏自动等分 92% 宽度——"Row + layoutWeight"是等宽栏的标准写法(比 Grid 更灵活,不需要计算列数)。
四、MenuList:5 项菜单
MenuList 是 5 项菜单(与 App 21/22 同款结构):
private menus: MenuItem[] = [ { id: 1, emoji: '📋', title: '预约记录' }, { id: 2, emoji: '📝', title: '使用记录' }, { id: 3, emoji: '⭐', title: '设备收藏' }, { id: 4, emoji: '📞', title: '联系管理员' }, { id: 5, emoji: '⚙️', title: '设置' } ];5 项菜单的"实验室业务"覆盖:
| 菜单 | 业务 |
|---|---|
| 📋 预约记录 | 历史预约 |
| 📝 使用记录 | 设备使用日志 |
| ⭐ 设备收藏 | 常用设备 |
| 📞 联系管理员 | 实验室特色(设备故障/权限申请) |
| ⚙️ 设置 | 通用 |
"联系管理员"是实验室预约的特色菜单——设备出问题/需要权限时联系实验室管理员——"特色菜单 = 业务核心"(对比 App 21"数据导出"、App 22"投票统计")。
5 项按"使用频次"排序:预约记录(高频)→ 使用记录(中)→ 设备收藏(中)→ 联系管理员(低频)→ 设置(低频)。
>箭头 + emoji + 标题三件套 —— "最简菜单结构"。
菜单项的"分组逻辑"缺失分析:
App 23 的 5 项菜单是扁平列表——没有分组。真实实验室预约 App 的菜单通常需要分组:
- 第一组(我的记录):预约记录 / 使用记录 / 设备收藏
- 第二组(帮助):联系管理员
- 第三组(系统):设置
分组的好处:用户快速定位——"记录类"在上、"帮助"在中、"系统"在下——"功能分组 = 认知减负"。
真实项目的分组实现:
// 菜单分组(用 Section 分隔) Column() { // 第一组:记录 this.MenuSection([menus[0], menus[1], menus[2]]) // 间距 12vp Blank().height(12) // 第二组:帮助 this.MenuSection([menus[3]]) Blank().height(12) // 第三组:系统 this.MenuSection([menus[4]]) }App 23 的简化:5 项菜单放一个卡片——"扁平 < 分组"——当菜单项 ≤ 5 时扁平可接受,超过 5 项建议分组。
五、MenuList 结构与 About
MenuList 与 App 21/22 同款结构(92% 宽 + Divider 0.5vp +>箭头):
@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 }) }About是"实验室设备预约 / Version 1.0.0"双行(与 App 21/22 同款)——opacity(0.7)版本号淡化。
Divider 分割线的"最后一项跳过"逻辑:
if (idx < this.menus.length - 1) { Divider() }——最后一项不画分割线。这是列表分割线的标准写法:
- 为什么不用
border-bottom?—— 最后一项会有多余的底线,视觉上"断开" - 为什么不用
Divider()无条件渲染?—— 同上,最后一项底部多一条线 idx < length - 1条件渲染——只在项与项之间画线,首尾无线 ——"分割线 = 项间分隔"而非"项底装饰"
strokeWidth(0.5)的"细线"选择—— 0.5vp 比 1vp 更精致——"细分割线 = 高级感"(iOS 设置页同款 0.5vp 细线)——粗线(1vp+)显得"廉价",细线(0.5vp)显得"精致"。
color(C.stroke)用主题色—— 不硬编码#E0E0E0——"分割线跟随主题"(暗色模式自动适配)。
六、@State 数量 = 0(系列第 7 个"0 @State 个人中心")
ProfileTab0 个 @State—— 所有数据private不可变。这是系列第七个"0 @State"个人中心(App 13/17/20/21/22/23 + 各数据页)——"展示型页面 = 0 @State"规律持续成立。
4 个 Tab 的 @State 分布(App 23):
| 页面 | @State | 类型 |
|---|---|---|
| 首页 | 1(activeCat) | 分类 Chip |
| 设备 | 0 | 纯展示详情 |
| 预约 | 1(slot) | 时段真单选 |
| 我的 | 0 | 纯展示 |
App 23 是"任务型 2 状态"的代表——首页/预约各 1 个状态(分类/时段),设备/我的纯展示。
七、数据一致性
"累计使用 36 小时"= StatRow"使用时长 36h" ✓——Slogan 与统计精确对应。
"待使用 2"vs 预约页记录(今天/明天 2 条待使用)✓——"我的页待使用 2 = 预约页待使用 2 条"——跨页数据一致。
"预约记录 18"vs 预约页 4 条记录——18 是累计(含已结束)、4 条是近期列表——**"统计累计 vs 列表近期"**口径合理。
八、与 App 21/22 我的页的"三连同构"
App 21 问卷、App 22 投票、App 23 实验室——3 个"任务型 App"的个人中心 100% 同构:
| 组件 | App 21(问卷) | App 22(投票) | App 23(实验室) |
|---|---|---|---|
| UserCard | 📊 问卷达人 | 🗳️ 投票达人 | 🔬 科研达人 |
| Slogan | 已创建 8 份 | 参与 28 次 | 累计使用 36 小时 |
| StatRow | 我的问卷/填写/收集/进行 | 创建/参与/历史/进行 | 预约/时长/收藏/待使用 |
| MenuList | 我的问卷/填写/导出/模板/设置 | 参与/创建/历史/统计/设置 | 预约/使用/收藏/管理员/设置 |
| About | 在线问卷调查 | 校园投票管理 | 实验室设备预约 |
"3 连任务型 App"证明"4 块个人中心"是任务型应用的稳定模板——读者做任何"任务型"产品(工具/预约/打卡)可直接套用。
九、"累计使用 36 小时"的 Slogan 设计
App 23 UserCard Slogan"累计使用 36 小时"值得从文案角度分析:
"累计使用 X 小时"是科研/工具类 App 的"投入型 Slogan":
- App 22 投票:"参与 28 次投票"(次数型)
- App 23 实验室:"累计使用 36 小时"(时长型)
"次数 vs 时长"的选择:
- 次数(参与 28 次)——适合"任务型"(一次一个动作)
- 时长(使用 36 小时)——适合"投入型"(科研要长时间投入)
"科研达人"的身份靠"时长"支撑——36 小时实验 = 做了 36 小时科研——"时长的积累感"比"次数的完成感"更适合科研。
Slogan 的"3 要素"(系列标准):
- 数字(36)——精确可信
- 动词(累计使用)——投入描述
- 单位(小时)——量纲明确
**"数字 + 动词 + 单位"**是投入型 Slogan 的标准——读者做"工具/科研/学习"类 App 时,用"累计 X 小时"比"已使用 X 天"更有"投入感"。
十、"4 栏统计"的科研视角
App 23 的 4 栏统计(预约记录/使用时长/收藏设备/待使用)——"科研工作者"的数据画像:
| 数字 | 维度 | 科研含义 |
|---|---|---|
| 预约记录 18 | 行为 | 预约了多少次设备 |
| 使用时长 36h | 投入 | 在实验室花了多少时间 |
| 收藏设备 4 | 偏好 | 常用哪几台设备 |
| 待使用 2 | 待办 | 还有哪些预约没去 |
"行为 + 投入 + 偏好 + 待办"4 维度——完整刻画"科研工作者的实验室使用画像"——对比 App 21 问卷的"创建+参与+收集+进行"(双向 UGC 视角)——"统计维度跟随业务角色"。
真实项目的"统计聚合":
// 统计从预约记录聚合 const stats = { '预约记录': bookings.length, '使用时长': bookings.reduce((sum, b) => sum + b.duration, 0), '收藏设备': favorites.length, '待使用': bookings.filter(b => b.status === '待使用').length };"统计 = 业务数据的聚合"——不是硬编码,而是从预约记录/收藏列表实时计算——**"数据驱动统计"**是真实项目的正确做法。
十一、"联系管理员"菜单的"人工兜底"
App 23 的"📞 联系管理员"菜单是**"人工兜底"入口**——工具类 App 的必备设计:
"人工兜底"的价值:
- 自动流程解决 80%(预约/查询/统计)
- 人工兜底解决 20%(设备故障/权限问题/特殊需求)
"联系管理员"的 3 种形态:
- 电话(📞 图标暗示)——最直接
- 在线客服(IM 聊天)
- 工单系统(提交问题,管理员处理)
真实实验预约 App 的"联系管理员"场景:
- 设备故障(显微镜坏了)
- 权限申请(特殊设备需要导师批准)
- 时段调整(临时改预约)
- 异常处理(设备损坏报告)
"人工兜底 = 用户安全网"——工具类 App 必须留"人工入口"——"机器做不到的、人来做"。
十二、App 23 与"科研数字化"的完整闭环
App 23 实验室预约是"科研数字化"的一个环节——完整"科研数字化"生态:
设备预约(App 23)→ 实验数据 → 论文写作 → 成果管理 ↑ ↓ └──────── 成果反哺(新设备采购) ←────┘"科研数字化"的 3 层:
- 基础设施(App 23)——设备/场地/耗材的预约管理
- 数据采集(进阶)——实验数据自动记录(传感器)
- 知识管理(进阶)——文献/笔记/论文(App 24 研究管理?)
App 23 是"科研基础设施"的最简版本——读者做"科研数字化"产品时,从"设备预约"(最刚需)切入是最佳路径——"先解决预约、再扩展数据"。
真实"科研数字化"产品(如"超级计算平台"的预约模块)——设备预约 + 数据 + 计费 + 审批的完整系统——App 23 是它的"预约"子模块。
十三、总结
App 23 我的页解析完毕。4 块最简结构(UserCard + StatRow + MenuList + About)+ 深青渐变 + 4 栏统计 + 5 菜单是核心组件。"科研达人"的业务身份 + "累计使用 36 小时"的投入型 Slogan + "预约/时长/收藏/待使用"4 维画像 + "联系管理员"人工兜底——是实验室预约类 App 个人中心的典型设计。与 App 21/22 我的页 100% 同构——"任务型个人中心模板"第 3 次复用,证明"4 块最简"是任务型应用的稳定模式。0 @State 个人中心——纯展示页,所有数据硬编码在private数组中,真实项目应从数据库聚合。