news 2026/9/9 13:34:42

鸿蒙原生应用 ArkTS 严格模式:预约页的 @State 真单选与动态回显

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
鸿蒙原生应用 ArkTS 严格模式:预约页的 @State 真单选与动态回显

鸿蒙原生应用 ArkTS 严格模式:预约页的 @State 真单选与动态回显

App 23「实验室设备预约」预约 Tab(Func2Tab),是预约时段选择页——@State slot真单选(4 时段 Flex 换行胶囊:09:00-11:00 默认选中)+ 确认预约按钮("预约成功: 明天 09:00-11:00"动态回显)+ 预约记录列表(4 条:待使用橙/已完成绿)。本篇基于23-lab-booking/entry/src/main/ets/pages/Func2Tab.ets(约 126 行)逐段拆解,附 4 张实机截图。

一、整体结构:选时段 → 提交 → 看记录

Func2Tab 是"选时段 → 确认 → 看历史"的预约流程:

build() { Column() { this.Header() Scroll() { Column({ space: 16 }) { this.SectionTitle('选择时段') this.SlotCard() this.SubmitBtn() this.SectionTitle('预约记录') this.BookingList() } .width('100%') .padding({ left: D.pad, right: D.pad, top: 16, bottom: D.pad + this.safeBottom + 20 }) } .layoutWeight(1).scrollBar(BarState.Off).align(Alignment.Top) } .width('100%').height('100%').backgroundColor(C.bg) }

5 个块按"选 → 提交 → 看"漏斗展开

  1. SectionTitle('选择时段')— 区块标题
  2. SlotCard— 4 时段 Flex 单选(本页核心交互
  3. SubmitBtn— 确认预约(动态回显时段)
  4. SectionTitle('预约记录')— 区块标题
  5. BookingList— 4 条预约记录(待使用橙/已完成绿)

与 App 14 场地预约的对比:App 14 是"场地选择 + 场次 Grid 三态",App 23 是"4 时段 Flex 单选"——App 23 更简单(4 个时段、单选、无三态)——"实验室预约 = 更简单的时段选择"

项目源码开源:https://gitee.com/codenestFlow/HarmonyOSHub

二、Header + SectionTitle

Header 是"设备预约"单行(系列标准)。SectionTitle 是参数化标题("选择时段"/"预约记录"两个区块):

@Builder SectionTitle(title: string) { Row() { Text(title).fontSize(15).fontWeight(FontWeight.Bold).fontColor(C.text) } .width('100%') }

两个 SectionTitle 分隔"操作区"(选时段+提交)和"记录区"(预约记录)——"区块标题 = 操作/历史的分界"

三、SlotCard:4 时段 Flex 真单选(本页核心)

SlotCard 是 4 个时段胶囊的Flex 换行 + 真单选

@Builder SlotCard() { Column({ space: 12 }) { Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.Start }) { ForEach(this.slots, (s: string) => { Text(s) .fontSize(13) .fontColor(this.slot === s ? C.primary : C.textSub) .padding({ left: 14, right: 14, top: 8, bottom: 8 }) .backgroundColor(this.slot === s ? C.primarySoft : C.cardSoft) .borderRadius(16) .border({ width: 1, color: this.slot === s ? C.primary : C.stroke }) .margin({ right: 10, bottom: 10 }) .onClick(() => { this.slot = s; }) }, (s: string) => s) } .width('100%') } .width('100%') .padding(14) .backgroundColor(C.card) .borderRadius(D.rLg) .border({ width: 1, color: C.stroke }) }

3.1 4 时段数据

@State slot: string = '09:00-11:00'; private slots: string[] = ['09:00-11:00', '11:00-13:00', '14:00-16:00', '16:00-18:00'];

4 个时段(连续覆盖一天):

  • 09:00-11:00(上午)
  • 11:00-13:00(午间)
  • 14:00-16:00(下午)
  • 16:00-18:00(傍晚)

`@State slot: string = '09:00-11:00' 默认选中第一时段——"默认选第一个"是时段选择的标准(减少用户操作)。

3.2 真单选交互

onClick(() => { this.slot = s; })—— 点击时段 →slot更新 ——4 个胶囊的选中态实时变化

Text(s) .fontSize(13) .fontColor(this.slot === s ? C.primary : C.textSub) .backgroundColor(this.slot === s ? C.primarySoft : C.cardSoft) .borderRadius(16) .border({ width: 1, color: this.slot === s ? C.primary : C.stroke })

选中态三件套

  • 文字:深青 vs 灰
  • 背景:浅青(C.primarySoft)vs 浅灰(C.cardSoft
  • 边框:深青 1vp vs 浅灰 1vp

"真单选" = 点击即切换 + 提交按钮回显(App 22 投票是真单选 + 锁定;App 23 是真单选 + 回显但无锁定)——"真单选"是"@State 状态驱动选中"的实现

3.3 Flex 换行

Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.Start })—— 4 个时段自动换行(4 个在窄屏可能 1 行放不下)——margin({ right: 10, bottom: 10 })胶囊间距。

justifyContent(FlexAlign.Start)—— 从左上角排列(不居中)——"换行内容左对齐"是 Flex 布局的标准

四、SubmitBtn:动态回显时段

SubmitBtn 是"确认预约"按钮,成功提示回显当前选中时段

@Builder SubmitBtn() { Button('确认预约') .width('100%').height(48) .fontSize(16).fontColor('#FFFFFF').backgroundColor(C.primary) .borderRadius(D.rMd) .onClick(() => { promptAction.showToast({ message: '预约成功: 明天 ' + this.slot }); }) }

'预约成功: 明天 ' + this.slot—— 成功提示拼接当前时段("预约成功: 明天 14:00-16:00")——"动态回显"让用户确认"我约的是这个时段"——与 App 14 场地预约"篮球场 19:00-20:00 预约成功"同款"回显"。

this.slot从 @State 读取—— 用户选了哪个时段,提交就回显哪个——"@State 状态驱动"

与 App 14 的差异:App 14 有"场次+姓名"双校验,App 23无校验(时段默认已选,无需校验)——**"默认选中 = 免校验"**是"默认值设计"的好处。

五、BookingList:预约记录列表

BookingList 是 4 条预约记录(待使用橙/已完成绿):

@Builder BookingList() { Column({ space: 12 }) { ForEach(this.bookings, (b: Booking) => { Row({ space: 12 }) { Text(b.device).fontSize(28) Column({ space: 4 }) { Text(b.date + ' ' + b.slot).fontSize(14).fontColor(C.text) Text(b.status).fontSize(12).fontColor(b.statusColor) } .alignItems(HorizontalAlign.Start).layoutWeight(1) } .width('100%') .padding(14) .backgroundColor(C.card).borderRadius(D.rMd) .border({ width: 1, color: C.stroke }) .onClick(() => { promptAction.showToast({ message: b.device }); }) }, (b: Booking) => b.id.toString()) } .width('100%') }

5.1 4 条记录的数据

private bookings: Booking[] = [ { id: 1, device: '🔬 高倍显微镜', date: '今天', slot: '14:00-16:00', status: '待使用', statusColor: C.warn }, { id: 2, device: '⚡ 示波器', date: '明天', slot: '09:00-11:00', status: '待使用', statusColor: C.warn }, { id: 3, device: '🧪 液相色谱仪', date: '昨天', slot: '14:00-16:00', status: '已完成', statusColor: C.ok }, { id: 4, device: '🔭 光谱分析仪', date: '8月10日', slot: '11:00-13:00', status: '已完成', statusColor: C.ok } ];

4 条记录的"设备 + 日期 + 时段 + 状态"

设备日期时段状态
🔬 高倍显微镜今天14:00-16:00待使用(橙)
⚡ 示波器明天09:00-11:00待使用(橙)
🧪 液相色谱仪昨天14:00-16:00已完成(绿)
🔭 光谱分析仪8月10日11:00-13:00已完成(绿)

2 待使用 + 2 已完成——"待使用 = 未来预约、已完成 = 历史使用"——"预约记录的生命周期"。

2 状态 2 色:待使用(橙)/已完成(绿)——"待办用橙、完成用绿"(系列标准)。

记录卡 2 列:左 emoji + 右"日期+时段"和"状态"——"设备 + 时间 + 状态"3 信息

5.2 与 Func1Tab 近期预约的"跨页对应"

预约页记录(今天 14:00-16:00 待使用)= 设备详情页近期预约(今天 14:00-16:00 已被预约)——"设备详情页的近期预约 = 预约页的记录"——跨页数据一致(同一时段同一状态)。

六、@State 与"预约流程"闭环

Func2Tab 只有1 个 @Stateslot(选中时段)——"1 个 @State 驱动时段选中 + 提交回显"

预约流程闭环

首页(找设备)→ 设备详情(看参数)→ 预约页(选时段 + 确认)→ 记录(看历史)

"找 → 看 → 选 → 记"4 步预约闭环——App 23 的 4 个 Tab 承担 4 步:首页(找)、设备(看)、预约(选)、我的(记)——"Tab 分工 = 业务闭环"

七、时段选择的"业务规则"设计

App 23 的 4 个时段(09:00-11:00 / 11:00-13:00 / 14:00-16:00 / 16:00-18:00)是"连续时段"——真实预约系统的"时段规则"有 3 种:

1. 固定时段(App 23)——系统预设 4 个时段,用户选一个——简单、可控、防冲突(适合实验室统一管理)。

2. 自由时间——用户输入起止时间(如 10:30-12:00)——灵活、但冲突检测复杂(需实时校验重叠)。

3. 时长预约——用户选"开始时间 + 使用时长"(14:00 开始用 2 小时)——介于两者之间(起点自由、时长固定)。

"固定时段 vs 自由时间"的取舍

  • 固定时段:实现简单、管理规范、用户选择成本低(4 选 1)
  • 自由时间:用户灵活、但需要时间选择器 + 实时冲突检测

实验室场景适合"固定时段"——实验室设备统一管理,固定时段方便调度和统计(每个时段 2 小时是标准的"实验课"时长)。

真实项目的时段配置

// 时段从配置读取,可按设备配置不同时段 private getSlots(deviceId: number): string[] { return slotConfig[deviceId] ?? ['09:00-11:00', '11:00-13:00', '14:00-16:00', '16:00-18:00']; }

"时段可配置"——不同设备不同时段(精密仪器晚间可用、普通设备白天可用)——"业务规则可配置"是预约系统的灵活性

八、"预约记录"的状态机与操作

App 23 预约记录有 2 状态(待使用/已完成)——真实预约记录有完整状态机

待使用(已预约,还没用) → 使用中(开始使用) → 已完成(使用结束) ↘ 已取消(用户取消) ↘ 已过期(没来也没取消)

5 状态的 UI 差异

状态颜色操作
待使用取消预约
使用中蓝/主色结束使用
已完成绿查看评价
已取消
已过期重新预约

"状态机 + 操作集"映射(系列记录页反复强调)——App 23 只有"待使用/已完成"2 状态 + 无操作按钮(只有点击 Toast)——"最简记录页"

真实预约记录的操作

  • 待使用 →取消(发短信/推送给管理员)
  • 使用中 →签到(确认到场)
  • 已完成 →评价(设备状态反馈)

"记录 = 状态机 + 操作"——读者做预约类 App 时,"记录页"要按状态机设计操作。

九、App 23 与 App 14 场地预约的对比

App 23 实验室预约与 App 14 运动场地预约是同类"预约应用"——对比:

维度App 14 场地预约App 23 实验室预约
主题色绿 #16A34A深青 #0F766E
预约对象场地(篮球场/足球场)设备(显微镜/示波器)
首页5 块(Hero/入口/周图/场地/排行)3 块(Chip/Grid)
时段选择2 列 Grid 三态(可选/已选/已满)Flex 换行单选(4 时段)
详情页无独立详情4 卡详情(身份/参数/说明/预约)
状态空闲/已约满空闲/使用中/维护中
个人中心6 块(含签到)4 块最简

两类预约 App 的差异根源

  • App 14 运动:内容型+行动型混合——首页要营销(推荐场地)、个人中心要留存(签到)
  • App 23 实验室:纯任务型——首页只要列表、个人中心只要统计

"预约对象决定 App 形态"——场地预约偏"消费"(休闲)、设备预约偏"工作"(科研)——"消费型预约丰富、工作型预约极简"

十、"预约提交"的完整校验链

App 23 的 SubmitBtn 无校验(默认选中时段)——真实预约提交应有完整校验链

1. 设备状态校验——"这台设备现在可预约吗"(空闲才可约、维护中拦截):

if (device.status === '维护中') { toast('设备维护中,暂不可预约'); return; }

2. 时段冲突校验——"这个时段有人约了吗"(前端预检 + 后端最终校验):

if (isSlotBooked(deviceId, date, slot)) { toast('该时段已被预约,请选择其他时段'); return; }

3. 用户资格校验——"用户有权限预约这台设备吗"(特殊设备需导师授权)。

4. 预约数量校验——"用户同时段预约上限"(防刷)。

"设备状态 + 时段冲突 + 用户资格 + 数量上限"4 层校验——真实预约系统的完整防线——App 23 默认选中免校验是"默认值设计"的简化,真实项目"后端校验"不可少。

十一、"预约记录"的降序与分页

App 23 的预约记录是 4 条硬编码——真实预约记录需要降序 + 分页

1. 按日期降序

// 真实项目:最新预约在前 const sorted = bookings.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt) );

2. 分页加载

// 上滑加载更多 Scroll() { List() { ... } .onReachEnd(() => { this.page++; this.loadMoreBookings(); }) }

3. 状态分组(进阶):按"待使用/已完成/已取消"分组显示,或 Tab 筛选。

"降序 + 分页 + 分组"是预约记录页的 3 个真实需求——App 23 只有 4 条硬编码(无排序/分页)——"记录列表的数据管理"是真实项目的必修课

十二、"预约成功"的后续流程

App 23 点击"确认预约"只弹 Toast——真实预约成功后有完整后续流程

1. 生成预约凭证——"预约单号 + 二维码"(到实验室扫码核销)。

2. 通知相关方——预约人(提醒)、实验室管理员(审批/准备)。

3. 状态流转——预约 → 待使用(记录列表出现)→ 使用 → 完成。

4. 积分/信用(可选)——按时履约 +信用、放鸽子 -信用(科研预约的信用体系)。

"预约成功 ≠ 结束"——后续的"凭证 + 通知 + 流转 + 信用"才是完整预约系统——App 23 的"预约成功"是入口,真实系统是"预约闭环"

十三、"Flex 换行" vs "Grid" 的时段选择器

App 23 的 SlotCard 用Flex({ wrap: FlexWrap.Wrap })换行布局——对比系列其他"选择器"的布局:

App选择器布局数量
App 18 兴趣10 标签多选Flex 换行10 个
App 19 分类8 分类多选Flex 换行8 个
App 21 题型4 单选Row 一行4 个
App 22 时段4 时段单选Flex 换行4 个
App 23 时段4 时段单选Flex 换行4 个
App 16 商品6 商品单选Grid 3 列6 个

"Flex 换行 vs Grid"的选择

  • Flex 换行(App 23):标签长短不一、数量 4-10 → 自动换行,宽度自适应内容
  • Grid(App 16):卡片等宽、需要整齐网格 → 固定列数,宽度均分

"时段选择器用 Flex 换行"是合理选择——时段标签长度固定("09:00-11:00"9 字符),Flex 换行排成 2-3 行整齐胶囊——"标签类选择器用 Flex、卡片类选择器用 Grid"

App 22 和 App 23 的"时段选择"都用 Flex 换行——"时段胶囊 = Flex 换行"是时段选择的稳定模式(对比 App 14 场次 Grid 三态是"复杂时段")。

十四、"预约记录"的"设备 + 时间"双信息

App 23 预约记录卡是"设备 + 时间 + 状态"3 信息——"预约记录必含设备和时间"

记录卡的信息架构

  1. 设备 emoji(28sp)——"约的什么"
  2. 日期 + 时段(14sp)——"什么时候用"
  3. 状态(12sp 状态色)——"现在什么状态"

"设备 + 时间 + 状态"3 要素是预约记录的标配——用户扫一眼知道"昨天约了显微镜 14:00-16:00,已完成"。

对比系列其他记录卡

  • App 14 场地记录:场地 + 时间 + 状态
  • App 15 分类记录:物品 + 分类 + 状态
  • App 16 兑换记录:商品 + 积分 + 状态
  • App 23 预约记录:设备 + 时间 + 状态

"记录卡 = 业务对象 + 时间 + 状态"——3 要素是所有"记录列表"的通用信息架构——读者做任何"记录"(预约/订单/日志)都按这 3 要素设计。

十五、总结

App 23 预约页解析完毕。4 时段 Flex 真单选(@State slot)+ 提交动态回显 + 预约记录 2 状态是核心组件。"默认选中第一时段 = 免校验"是默认值设计的好处。"预约 4 层校验"、"记录降序分页"、"预约成功后续流程"、"Flex vs Grid 选择器"、"记录 3 要素"是真实预约系统的进阶能力。"找 → 看 → 选 → 记"4 步预约闭环是预约类 App 的标准流程。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 13:34:00

Java Object类11个方法详解:从源码原理到实战应用

做Java开发这些年,我面试过不少候选人,也被人问过很多次“Object类有哪些方法”。这个问题看似基础,但它就像一面镜子,能照出一个人对Java语言底层设计到底理解到什么程度。毕竟Object是所有类的父类,Java里一切对象行…

作者头像 李华
网站建设 2026/9/9 13:32:50

深入解析MyBatis分页插件原理:PageHelper与MyBatis Plus实战

1. 从手写分页到插件接管,先聊清楚分页这件事 做Java后端的人,只要接触过数据库,基本都逃不过分页查询。早期用JDBC的时候,分页是纯手工活,MySQL写 LIMIT offset, size ,Oracle玩 ROWNUM ,S…

作者头像 李华
网站建设 2026/9/9 13:32:16

级联H桥SVG/STATCOM三相不平衡补偿的三层控制策略与仿真实践

1. 项目概述与整体设计思路 搞电力电子的朋友应该都有体会,SVG(静止无功发生器)和STATCOM(静止同步补偿器)在行业内基本被当成同一个东西用,只是叫法不同,一个侧重低压配电,一个侧重…

作者头像 李华
网站建设 2026/9/9 13:32:12

Seelen-UI 插件系统实战:4 类内置模块,从 0 到能用的桌面定制

Seelen-UI 插件系统实战:4 类内置模块,从 0 到能用的桌面定制 【免费下载链接】Seelen-UI The Fully Customizable Desktop Environment for Windows 10/11. 项目地址: https://gitcode.com/GitHub_Trending/se/Seelen-UI 一句话讲清楚它是什么 …

作者头像 李华
网站建设 2026/9/9 13:31:36

从被AI气晕到理性共处:大模型能力边界、偏见与落地实践

1. 从"被AI气晕"到"重新认识AI":我的认知转变1.1 早期我对AI的理解:死板的规则引擎大概十年前,我对人工智能的看法还停留在一个非常朴素的层面:某个程序能不能"听懂人话",本质上就是一堆…

作者头像 李华