鸿蒙原生应用 ArkUI 声明式:实验室首页的 2 列 Grid 与三状态色
App 23「实验室设备预约」首页(HomeTab),主题色
#0F766E深青色(teal),4 个 Tab 分别为首页(🔬)、设备(🧪)、预约(📅)、我的(👤)。首页采用紧凑布局——Header(实验室设备)+ 5 分类 Chip(全部/光学/电子/机械/化学)+ SectionTitle(设备列表)+2 列 Grid 6 台设备卡(60×100% emoji 图标块 + 名称 + 型号规格 + 3 状态色:空闲绿/使用中橙/维护中红)。本篇基于23-lab-booking/entry/src/main/ets/pages/HomeTab.ets(约 121 行)逐段拆解,附 4 张实机截图。
一、整体结构:3 块紧凑内容
App 23 首页是系列最紧凑的"设备列表"首页——3 个 @Builder 块:
build() { Column() { this.Header() Scroll() { Column({ space: 14 }) { this.ChipRow() this.SectionTitle('设备列表') this.DeviceGrid() } .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) }3 块结构:
- Header— "实验室设备"标题
- ChipRow— 5 分类胶囊(全部/光学/电子/机械/化学)
- SectionTitle('设备列表') + DeviceGrid— 2 列 Grid 6 台设备
与 App 21 问卷首页同构(3 块紧凑结构)——"设备列表 = 任务型列表"(用户来"找设备→预约"),不需要复杂首页。
主题色#0F766E深青色——实验室/科研/专业的调性(深青色接近"实验服蓝绿"、显微镜/试剂瓶的视觉联想)——与 App 19 青绿 #0D9488 同系但更深——**"深青 = 科研严肃、浅青 = 摄影清新"**同系不同调。
项目源码开源:https://gitee.com/codenestFlow/HarmonyOSHub
二、Header + ChipRow
Header 是"实验室设备"单行标题(无 Slogan——"工具型首页不配 Slogan")。
ChipRow 是 5 个分类(全部/光学/电子/机械/化学)的横滑胶囊:
@Builder ChipRow() { Scroll() { Row({ space: 10 }) { ForEach(this.cats, (cat: string, idx: number) => { Text(cat) .fontSize(13) .fontColor(this.activeCat === idx ? '#FFFFFF' : C.textSub) .padding({ left: 14, right: 14, top: 7, bottom: 7 }) .backgroundColor(this.activeCat === idx ? C.primary : C.card) .borderRadius(16) .onClick(() => { this.activeCat = idx; }) }, (cat: string) => cat) } } .scrollable(ScrollDirection.Horizontal).scrollBar(BarState.Off).width('100%') }5 个分类:['全部', '光学', '电子', '机械', '化学']——实验室设备的 4 大分类(光学/电子/机械/化学是高校实验室的标准分类)。
@State activeCat: number = 0默认选中"全部"——onClick切选中态(activeCat === idx ? 深青底白字 : 白底灰字)。"假筛选"(同系列其他 Chip)——demo 未联动过滤,真实项目应加filteredDevices()。
borderRadius(16)胶囊形——系列标准 Chip。
三、SectionTitle + DeviceGrid:2 列 Grid(本页核心)
SectionTitle 是"设备列表"标题(无右侧链接——简化版)。DeviceGrid 是 2 列 Grid 6 台设备:
@Builder DeviceGrid() { Grid() { ForEach(this.devices, (d: Device) => { GridItem() { Column({ space: 8 }) { Row() { Text(d.emoji).fontSize(30) } .width('100%').height(60) .backgroundColor(C.cardSoft).borderRadius(D.rSm) .justifyContent(FlexAlign.Center) Text(d.name).fontSize(13).fontColor(C.text).maxLines(1) .textOverflow({ overflow: TextOverflow.Ellipsis }).width('100%') Text(d.spec).fontSize(11).fontColor(C.textDim).width('100%') Row() { Blank() Text(d.status).fontSize(11).fontColor(d.statusColor).fontWeight(FontWeight.Medium) } .width('100%') } .width('100%') .padding(10) .backgroundColor(C.card).borderRadius(D.rMd) .border({ width: 1, color: C.stroke }) .onClick(() => { promptAction.showToast({ message: d.name }); }) } }, (d: Device) => d.id.toString()) } .columnsTemplate('1fr 1fr') .columnsGap(12) .rowsGap(12) .width('100%') }3.1 6 台设备的数据
private devices: Device[] = [ { id: 1, emoji: '🔬', name: '高倍显微镜', spec: '1000x · 偏光', status: '空闲', statusColor: C.ok }, { id: 2, emoji: '⚡', name: '示波器', spec: '200MHz · 4通道', status: '使用中', statusColor: C.warn }, { id: 3, emoji: '🧪', name: '高效液相色谱仪', spec: 'UV检测器', status: '空闲', statusColor: C.ok }, { id: 4, emoji: '🔩', name: '万能试验机', spec: '100kN', status: '维护中', statusColor: C.danger }, { id: 5, emoji: '🔭', name: '光谱分析仪', spec: '200-800nm', status: '空闲', statusColor: C.ok }, { id: 6, emoji: '📡', name: '网络分析仪', spec: '9kHz-6GHz', status: '使用中', statusColor: C.warn } ];6 台设备覆盖 4 大分类:
| 设备 | 分类 | 型号规格 | 状态 |
|---|---|---|---|
| 🔬 高倍显微镜 | 光学 | 1000x · 偏光 | 空闲(绿) |
| ⚡ 示波器 | 电子 | 200MHz · 4通道 | 使用中(橙) |
| 🧪 高效液相色谱仪 | 化学 | UV检测器 | 空闲(绿) |
| 🔩 万能试验机 | 机械 | 100kN | 维护中(红) |
| 🔭 光谱分析仪 | 光学 | 200-800nm | 空闲(绿) |
| 📡 网络分析仪 | 电子 | 9kHz-6GHz | 使用中(橙) |
6 台设备的"真实实验室设备":显微镜/示波器/色谱仪/试验机/光谱仪/网络分析仪——都是真实的科研仪器(高校实验室常见设备)——"设备列表 = 真实科研环境"。
3 状态分布:空闲 3 台(绿)/使用中 2 台(橙)/维护中 1 台(红)——"多空闲、少维护"是实验室常态。
3.2 3 状态色映射
- 空闲→
C.ok绿(可预约) - 使用中→
C.warn橙(被占用) - 维护中→
C.danger红(不可用)
3 状态 3 色——**"空闲绿/使用橙/维护红"**是设备管理/会议室预约的标准色映射——与 App 14 场地(空闲/已约满)、App 22 投票(进行中/已结束)同款"状态色编码"。
3.3 设备卡的 4 层结构
每张设备卡 4 行(从上到下):
- 60 高 emoji 图标块(
C.cardSoft浅灰底 + 🔬/⚡ 等) - 设备名称(13sp 深色 +
maxLines(1)截断) - 型号规格(11sp 灰)——"1000x · 偏光"等
- 状态(11sp 状态色 + 加粗 + 右对齐
Blank())
Blank()把状态推到右下角——"名称左上、状态右下"的视觉节奏。
maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })设备名截断——"高效液相色谱仪" 7 个字在 1 列内放不下——Ellipsis省略(实际显示"高效液相色…")——"Grid 卡片文字必须截断"(列宽有限)。
3.4 2 列 Grid 布局
columnsTemplate('1fr 1fr')+columnsGap(12)+rowsGap(12)——2 列等宽 + 12vp 间隙——6 台设备排成 3 行 2 列。
"2 列 Grid vs 1 列列表":
- 2 列(本页)——信息密度高(一屏看 4 台)、紧凑
- 1 列(App 21/22)——信息完整(每行有按钮)、宽松
"设备列表用 2 列 Grid"是合理选择——设备卡信息少(名称+规格+状态),2 列紧凑展示——"信息少的列表用 Grid、信息多的列表用 List"。
四、@State 与"紧凑首页"设计
HomeTab 只有1 个 @State:activeCat(分类 0-4)。其余数据private不可变。
App 23 首页是系列最紧凑的设备列表(3 块)——**"设备预约 = 找设备 → 看详情 → 预约"**的任务流——首页只负责"找设备"(列表展示)。
对比 App 14 运动场地预约首页(5 块:Hero/入口/周图/场地/排行)——App 14 首页"丰富"因为"运动"是内容型(场地推荐需要营销)——App 23 首页"紧凑"因为"实验室设备"是任务型(用户知道要什么设备)——**"业务类型决定首页复杂度"**再次验证。
五、设备状态与"预约意愿"的联动
App 23 首页的 3 状态直接决定"用户要不要点进详情预约":
状态 → 用户行为:
- 空闲(绿)→ "可以预约" → 点进详情 → 选时段 → 预约
- 使用中(橙)→ "被占用了" → 可能等/换设备
- 维护中(红)→ "坏了" → 换设备/联系管理员
"状态 = 预约意愿的引导"——用户扫一眼 Grid 就知道"哪台能约"——"状态色可视化 = 高效筛选"。
真实项目的状态扩展:
- 已锁定(被特定用户预约,但还没用)
- 预约中(未来时段已被预约)
- 待维护(快需要维护)
"状态机"从 3 态扩展为 6 态——UI 只需改"状态名 + 状态色"(数据结构不变)——"状态可扩展性"是设备管理的基础。
六、"设备状态色"与系列状态色的统一体系
App 23 首页的 3 状态色(空闲绿/使用中橙/维护中红)与系列其他 App 的状态色形成统一体系:
| 状态语义 | 颜色 | 出现位置 |
|---|---|---|
| 空闲/可用/进行中 | 绿C.ok | App 23 设备、App 14 场地、App 22 投票 |
| 使用中/待处理/即将结束 | 橙C.warn | App 23 设备、App 22 投票、App 15 环保 |
| 维护/已满/已结束 | 红C.danger | App 23 设备、App 14 场地、App 16 商城 |
"绿=可用、橙=警示、红=不可用"是系列统一的状态色语义——读者在多个 App 间切换不会混淆(同一颜色永远代表同一类语义)——"跨 App 状态色一致性"是设计系统的一致性原则。
"状态色映射"的工程实现(App 23 设备卡):
// 数据携带颜色(当前实现) { status: '空闲', statusColor: C.ok } { status: '使用中', statusColor: C.warn } { status: '维护中', statusColor: C.danger }真实项目应抽成"状态 → 颜色"映射表:
const STATUS_COLOR = { '空闲': C.ok, '使用中': C.warn, '维护中': C.danger, '已锁定': C.textDim, '待维护': C.warn }; // 数据只存 status 字符串,颜色查表"映射表代替数据携带颜色"——新增状态只改映射表一处,数据与视觉解耦。
七、"2 列 Grid" vs "1 列 List" 的选择标准
App 23 设备卡用 2 列 Grid(信息紧凑),对比系列其他列表:
| App | 主列表 | 布局 | 卡片信息量 |
|---|---|---|---|
| 21 问卷 | 问卷卡 | 1 列 List | 中(emoji+标题+状态) |
| 22 投票 | 投票卡 | 1 列 List | 中(emoji+标题+状态+剩余) |
| 23 实验室 | 设备卡 | 2 列 Grid | 低(emoji+名称+规格+状态) |
| 13 音乐 | 项目卡 | 1 列 List | 高(emoji+标题+标签+进度条) |
"Grid vs List"选择标准:
- 信息少 + 数量多(设备 6 台、规格短)→2 列 Grid(一屏看更多)
- 信息多 + 操作多(项目有进度条+按钮)→1 列 List(每行完整展示)
"卡片信息量决定布局"——信息少的卡片适合 Grid(高密度)、信息多的适合 List(完整性)。
"2 列 Grid"的注意点:
- 卡片宽度减半 →文字必须截断(
maxLines(1)+ Ellipsis) - 状态/操作按钮空间小 →只用"状态文字"不用按钮
- 点击区域小 →整卡可点(GridItem onClick)
App 23 设备卡的"Grid 三原则"(截断/无按钮/整卡可点)是 2 列 Grid 的标准实践。
八、设备列表的"搜索与筛选"扩展
App 23 首页有分类 Chip(光学/电子/机械/化学)但无搜索框——真实设备管理应有**"分类 + 搜索"双筛选**:
// 真实项目的双筛选 private filteredDevices(): Device[] { let result = this.devices; if (this.activeCat > 0) { const catName = this.cats[this.activeCat]; result = result.filter(d => this.catOf(d.id) === catName); } if (this.keyword.trim() !== '') { result = result.filter(d => d.name.includes(this.keyword.trim()) || d.spec.includes(this.keyword.trim())); } return result; }"分类 + 搜索"双条件过滤——activeCat(分类)+keyword(搜索词)两个 @State 组合过滤——"多条件筛选"是设备/商品列表的标准。
真实设备的"分类映射":
// 设备 → 分类映射 const DEVICE_CATEGORY: Record<number, string> = { 1: '光学', 2: '电子', 3: '化学', 4: '机械', 5: '光学', 6: '电子' };"分类映射表"让catOf()一行查表——"设备与分类的关系数据化"(当前 demo 的 devices 数组没有 category 字段,是"假分类")——"分类字段缺失是'假筛选'的根源"——真实项目每个设备应有category字段。
九、设备卡的"信息密度"与"科研设备命名"
App 23 的 6 台设备卡的"信息密度"设计:
每张卡 4 行信息的层级:
- emoji 图标(30sp)——视觉锚点(最高信息密度)
- 设备名(13sp 加粗)——核心识别
- 规格(11sp 灰)——补充参数
- 状态(11sp 状态色)——行动信号
"图标 + 名称 + 规格 + 状态"4 层——从"最直观"到"最行动"的视觉递减——"科研设备卡"的信息架构。
"科研设备命名"的专业性:
- 高倍显微镜(1000x · 偏光)
- 高效液相色谱仪(UV 检测器)
- 万能试验机(100kN)
- 光谱分析仪(200-800nm)
- 网络分析仪(9kHz-6GHz)
"型号规格"都是真实科研参数(倍率/通道/检测器/量程/频段)——"专业参数 = 科研可信度"——用户(科研人员)一眼看懂"这设备能干什么"——"设备卡是给专家看的"(对比 App 16 商品卡给消费者看价格,设备卡给科研人员看参数)。
十、"任务型首页"的 3 原则
App 23 首页是"任务型首页"的范本——3 个设计原则:
原则 1:分类优先(Chip 在最上方)——用户第一件事是"找分类"(光学/电子/机械/化学)——"分类 = 任务的第一步"。
原则 2:列表高效(2 列 Grid 一屏 4 台)——用户快速扫设备——"一屏看到尽可能多"。
原则 3:状态信号(状态色右对齐)——用户判断"哪台能约"——"状态 = 决策依据"。
**"分类 → 扫视 → 决策"**是任务型首页的用户路径——与内容型首页(App 19 摄影"欣赏→收藏")的差异——任务型用户"目的明确"、内容型用户"浏览探索"。
读者做"任务型首页"时 3 原则自查:
- 分类在最上面了吗?
- 列表一屏够多吗?
- 关键状态一眼可见吗?
十一、总结
App 23 首页解析完毕。3 块紧凑结构 + 5 分类 Chip + 2 列 Grid 6 设备卡(3 状态色)是核心组件。"设备状态 → 预约意愿"的联动是设备预约场景的特色。**"2 列 Grid 紧凑设备列表"是任务型首页的范本。"Grid vs List 选择标准"、"分类映射表"、"任务型首页 3 原则"**是真实项目的关键改进。