ArkTS 表单工程:停车记录页的快捷胶囊回填与四字段表单
App 56「停车缴费记录」记录页(Func1Tab),主题色
#2D3436深灰黑。本页是停车记录的"录入页"——白色单行 Header("添加记录"20 号加粗)+常用地点卡("常用地点"标题 + 4 个 📍 快捷胶囊:公司车库/万达广场/市医院/超市停车场,点击回填地点)+四字段停车表单(停车地点 TextInput 双向绑定 / 入场时间·出场时间双列 Row / 停车费用(元)Number 键盘)+深灰全宽"保存记录"按钮(地点非空弹"记录已保存",空则弹"请填写停车地点")。本篇基于56-parking-record/entry/src/main/ets/pages/Func1Tab.ets(共 92 行)逐段拆解,附 4 张实机截图。
一、整体结构:三区"Header + 快捷地点 + 表单"布局
记录页是"快捷入口 + 录入表单"的组合布局——3 个 @Builder 块:
build() { Column() { this.Header() Scroll() { Column({ space: 16 }) { this.QuickPlaces() this.FormCard() Button('保存记录').fontSize(15).fontColor('#FFFFFF').backgroundColor(C.primary) .width('100%').height(48).borderRadius(D.rMd) .onClick(() => { promptAction.showToast({ message: this.place.length > 0 ? '记录已保存' : '请填写停车地点' }); }) } .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) }3 块结构:
- QuickPlaces— 常用地点快捷胶囊(点击回填地点)
- FormCard— 四字段停车表单(地点/入场/出场/费用)
- 保存记录— 深灰全宽 CTA 按钮(地点校验)
记录页是"录入页"——先点常用地点快捷回填(公司车库、万达广场等一键填入),再补录时间与费用(入场/出场/金额),最后保存。快捷 + 表单的组合:常用地点解决"高频地点重复输入"的痛点(用户常去的地方就那几个),表单完成完整录入。
项目源码开源:https://gitee.com/codenestFlow/HarmonyOSHub
二、Header:单行品牌头
Header() { Row() { Text('添加记录').fontSize(20).fontWeight(FontWeight.Bold).fontColor(C.text) } .width('100%').height(this.safeTop + 56).padding({ top: this.safeTop, left: D.pad, right: D.pad }) .backgroundColor(C.card).alignItems(VerticalAlign.Bottom) }功能页 Header 标准范式(20 号、safeTop + 56固定高、文字贴底),与费用页、我的页同款。"添加记录"是动作型标题(动词短语),暗示"待完成的录入操作",与系列录入页(App 52 上传票据、App 53 添加商品、App 55 添加提醒)一致。
三、QuickPlaces:常用地点快捷胶囊
QuickPlaces() { Column({ space: 10 }) { Text('常用地点').fontSize(14).fontWeight(FontWeight.Bold).fontColor(C.text).width('100%') Flex({ wrap: FlexWrap.Wrap }) { ForEach(this.commonPlaces, (p: string) => { Text('📍 ' + p).fontSize(12).fontColor(C.textSub) .padding({ left: 12, right: 12, top: 7, bottom: 7 }) .backgroundColor(C.card).borderRadius(14).border({ width: 1, color: C.stroke }) .margin({ right: 10, bottom: 10 }) .onClick(() => { this.place = p; promptAction.showToast({ message: p }); }) }, (p: string) => p) }.width('100%') } }技术拆解:
- 常用地点胶囊:4 个带 📍 前缀的地点胶囊(公司车库/万达广场/市医院/超市停车场)——
Flex({ wrap: FlexWrap.Wrap })换行排布(3 个一行 + 1 个换行),白底 + 1 灰描边(borderRadius(14)),与选中态胶囊(主色底)不同——快捷胶囊是"白底描边"(非单选,点击即填入),视觉上可点击但非"选中"语义。 - 点击回填:
onClick(() => { this.place = p; })——点击把地点填入表单(this.place = p),同时 Toast 提示"公司车库"——"点一下、填好了"的快捷录入,省去手输常用地点。 - 与表单联动:填入的地点会显示在下方"停车地点"输入框(
text: this.place双向绑定)——快捷胶囊 → 表单回显的联动闭环。 - 常用地点数据:公司车库/万达广场/市医院/超市停车场——与首页记录的地点(万达/公司/医院/超市)、费用页地点(公司/万达/医院)高度重合(常用地点是高频停车场的集合),录入时点胶囊即用。
四、FormCard:四字段停车表单
FormCard() { Column({ space: 14 }) { Column({ space: 6 }) { Text('停车地点').fontSize(13).fontColor(C.textSub).width('100%') TextInput({ placeholder: '输入停车地点', text: this.place }) .backgroundColor(C.cardSoft).borderRadius(D.rSm).height(44) .onChange((v: string) => { this.place = v; }) } Row({ space: 12 }) { Column({ space: 6 }) { Text('入场时间').fontSize(13).fontColor(C.textSub).width('100%') TextInput({ placeholder: '14:30' }) .backgroundColor(C.cardSoft).borderRadius(D.rSm).height(44) .onChange((v: string) => { this.inTime = v; }) }.layoutWeight(1) Column({ space: 6 }) { Text('出场时间').fontSize(13).fontColor(C.textSub).width('100%') TextInput({ placeholder: '16:45' }) .backgroundColor(C.cardSoft).borderRadius(D.rSm).height(44) .onChange((v: string) => { this.outTime = v; }) }.layoutWeight(1) }.width('100%') } .width('100%').padding(16).backgroundColor(C.card).borderRadius(D.rLg).border({ width: 1, color: C.stroke }) }四字段表单:
- 停车地点:13 号灰标签 +
TextInput({ placeholder: '输入停车地点', text: this.place })——text: this.place是"受控回显"(快捷胶囊填入的值显示在输入框,用户也可手动修改),onChange 双向同步this.place——这是系列中少见的"受控输入"写法(多数页面只 onChange 单向绑定),回显 + 手输两种录入方式统一。 - 入场时间/出场时间双列:
Row({ space: 12 })+ 两个layoutWeight(1)Column——入场(14:30)与出场(16:45)左右并排(时间输入两栏一行),占位符示范 24 小时制格式(14:30/16:45),时长由两时间差计算(产品化后 16:45 - 14:30 = 2小时15分)。 - 停车费用(元):
TextInput({ placeholder: '0.00' }).type(InputType.Number)——Number 键盘(只输数字),费用是停车记录的核心字段(首页/费用页金额都来自这里),占位符 0.00 暗示两位小数(免费停车可填 0)。 - 表单布局:
Column({ space: 14 })垂直排列 +padding(16)白卡圆角 20 描边——与系列录入表单同款。
五、保存按钮:状态校验的交互闭环
Button('保存记录').fontSize(15).fontColor('#FFFFFF').backgroundColor(C.primary) .width('100%').height(48).borderRadius(D.rMd) .onClick(() => { promptAction.showToast({ message: this.place.length > 0 ? '记录已保存' : '请填写停车地点' }); })技术拆解:
- 全宽深灰 CTA:
width('100%').height(48)+backgroundColor(C.primary)——主色全宽按钮(录入页标准 CTA),深灰与主题一致。 - 三元校验:
this.place.length > 0 ? '记录已保存' : '请填写停车地点'——以地点为校验锚点(停车地点是记录的核心标识,没有地点的停车记录没有意义),地点非空 → 保存成功;为空 → 提示"请填写停车地点"(快捷胶囊点击已填入地点,正常流程不会触发空校验)。
六、跨页数据自洽:停车录入的全局闭环
记录页与首页、费用、我的三页形成闭环:
- 记录"常用地点 4 胶囊"↔ 首页"万达/公司/医院/超市"——同一批停车场(常用地点是高频场所),录入时点胶囊填入的地点会出现在首页"最近停车"列表。
- 记录"停车费用"↔ 首页"¥12 万达广场"——录入的停车费用(12 元)出现在首页列表(万达 ¥12),并汇入首页"本月停车费 286 元"与费用页月度统计,录入 → 展示 → 汇总全链路。
- 记录"入场/出场时间"↔ 首页"2小时15分"——录入的入场出场时间算出时长(2小时15分),首页显示"时长 · 日期",时间录入 → 时长展示。
- 记录"保存"↔ 我的页"156 次"累计——每保存一条记录,我的页停车次数 +1(产品化后),录入量与总量同步。
- 记录"常用地点"↔ 费用页"地点消费"——常用地点中收费的(公司/万达/医院)在费用页按地点统计(20 次 400 元等),快捷录入的地点就是费用统计的维度。
七、系列横向对比:录入页的快捷胶囊应用
系列中"录入型"页面的快捷设计对比:
- App 56 记录页:常用地点快捷胶囊(点击回填)——"快捷回填"(点一下填好地点)。
- App 43 明信片:最近联系人回填——"联系人回填"(选历史联系人)。
- App 49 投票创建:选项动态增删——"动态行"(随交互增长)。
- App 52 上传票据:双虚线卡 + 表单——"来源选择"(拍照/相册)。
56 的差异化在于**"地点胶囊的受控回填"**——常用地点胶囊点击后通过text: this.place受控绑定回显到输入框(用户可改可确认),这是系列中唯一"快捷胶囊 + 受控输入"的组合(其他录入页的快捷多为直接跳转)。快捷地点解决"高频重复输入"痛点(停车地点就那几个),受控回显让"快捷填入"与"手动修改"无缝衔接。
八、开发者避坑:录入页的四个细节
从 Func1Tab 的实现提炼四个易踩坑点:
- 受控 TextInput 的循环风险:
text: this.place+onChange((v) => { this.place = v; })——受控组件注意回写循环(程序赋值触发 onChange 再赋值可能死循环),ArkUI 中此写法安全(onChange 由用户输入触发),但产品化时程序化赋值需注意避免"赋值 → 触发 onChange → 再赋值"的连锁。 - 时间格式与时长计算:入场/出场是自由文本——产品化必须校验格式(时:分)并计算时长(出场 - 入场),且校验"出场 > 入场"(否则时长负数);更优方案用 TimePicker 时间选择器。
- 费用为 0 与免费的区分:费用 Number 输入可填 0——0 表示免费停车(首页显示"免费"绿字),需区分"填了 0(免费)"与"没填(费用未知)",产品化用空值校验区分。
- 快捷胶囊与表单的同步:点击胶囊填入
this.place后,输入框回显依赖text: this.place——若忘记绑text参数,胶囊点击后表单不显示已填地点(用户以为没填上),受控绑定是快捷录入可见性的关键。
九、常见问题 FAQ
问:常用地点怎么变成"常用"的?答:演示版本写死了 4 个高频停车场(公司车库/万达广场/市医院/超市停车场),产品化后"常用地点"由使用频率自动生成(停车次数最多的地点进入常用列表),或用户在设置中手动添加常用地点。快捷胶囊解决的是"高频地点重复输入"的痛点。
问:入场时间和出场时间都要填吗?答:当前演示允许不填(只校验地点),产品化后建议至少填一个(或都用时间选择器)——因为首页展示的"2小时15分"时长由入场/出场计算,不填时间则时长缺失。免费停车(如超市)可以不填费用(默认 0),但时间建议保留(记录停了多久)。
问:保存记录时为什么只校验地点?答:因为停车地点是记录的核心标识——没有地点,首页列表无法显示、费用统计无法归因(不知道在哪花的钱)。时间与费用可以保存后补全(或默认值兜底),校验只锁"不可缺失"的字段,降低录入门槛。
十、验收与总结
本篇对 56-parking-record 记录页(Func1Tab,92 行)做了逐块拆解:
- 三区结构:Header(添加记录)+ QuickPlaces(常用地点快捷胶囊)+ FormCard(四字段表单)+ 保存按钮
- 核心交互:常用地点点击回填(
text: this.place受控回显)、时间双列并排、保存按钮地点校验 - 主题色:
#2D3436深灰黑——保存按钮、胶囊描边 - 数据闭环:快捷地点 → 地点回填 → 保存记录 → 首页列表 → 费用统计,录入驱动全局
记录页是停车缴费记录的"录入口"——点常用地点快速填入、补录时间费用、校验保存,一页完成停车记录的创建。
十、扩展思考:从手工录入到智能记录
记录页的演进方向是"智能记录":第一是停车短信解析——很多停车场离场后发扣费短信("万达广场停车 2小时15分 收费 12 元"),演进方向是解析短信自动生成记录(地点/时长/费用自动填入),从"手工录入"变为"自动捕获";第二是定位自动识别——进入常用停车场时自动定位并开始计时(到停车场自动记录入场时间),离场时按出场时间自动算费用,记录页的常用地点是定位识别的基础;第三是车牌快捷管理——多车用户记录时要选"哪辆车停的",演进方向是车牌切换(沪A·88888 停的 vs 另一辆车),记录页可增加车牌字段;第四是票据拍照归档——停车缴费后拍下收费小票/付款截图作为凭证,演进方向是记录附带照片(类似 App 53 商品页的照片上传),记录页可增加"上传凭证"入口。这四个方向都从记录页已有的四字段长出来,让"记一笔"变成"自动记"。
十一、从设计稿到代码的映射
把记录页设计稿翻译成代码,有几组典型映射:第一,"常用地点胶囊"映射为"流式换行容器加描边文本"——设计稿顶部的地点胶囊组,代码里用流式换行容器包裹带位置图标前缀的描边文本,点击回填地点状态;第二,"停车地点回填"映射为"受控输入组件"——设计稿里点击胶囊后地点出现在输入框,代码里用带文本参数的输入组件实现回显,双向绑定地点状态;第三,"时间双列"映射为"等权重的行内两列"——设计稿的入场出场并排,代码里用一行内两个等权重列各放标签与输入组件;第四,"保存按钮"映射为"全宽主色按钮加三元校验"——设计稿底部深灰通栏按钮,代码里用全宽按钮加地点非空的条件判断弹不同提示。这四组映射展示了"录入页"的完整翻译,其中"受控回显"是本页区别于其他录入页的要点。
十二、常见问题 FAQ
问:常用地点为什么用"📍 地点"的形式展示?答:📍 是定位图标,前缀在地点前强化"这是地理位置"的语义——用户看到 📍 自然联想到"地点/定位",比纯文字胶囊更直观。四个常用地点(公司车库/万达广场/市医院/超市停车场)覆盖了"上班、购物、就医、采购"四大停车场景,是高频停车地点的典型集合。
问:入场时间和出场时间用 24 小时制还是 12 小时制?答:占位符展示的是 24 小时制(14:30、16:45)——24 小时制无歧义(不会混淆上午下午)、计算时长更直接(16:45 - 14:30),是国内停车场景的标准格式。产品化建议用时间选择器(TimePicker)代替手输,从源头保证格式正确。
问:保存记录时金额填 0 会怎样?答:金额填 0 表示免费停车——保存后首页显示"免费"(绿色标识),不进入费用统计(费用页只统计收费记录)。如果用户"没填金额"(空着),当前演示不校验(只校验地点),产品化应区分"填 0(免费)"与"没填(费用未知)",费用未知的记录可提示补录。
十三、补充要点:记录页三个细节再思考
第一,关于受控回显的交互价值。TextInput({ text: this.place })让快捷胶囊点击的地点"看得见"——用户点"公司车库"后输入框立即显示"公司车库",确认无误再补时间费用,这种"点击即见"的反馈避免了"点了没反应"的困惑,是快捷录入可用性的关键。第二,关于常用地点与手输的互补。常用胶囊覆盖高频地点(一键填入),输入框覆盖"临时/新地点"(手动输入)——两种录入方式互补,既有速度又有灵活性,产品化后新地点使用次数多了会自动加入常用列表。第三,关于时间与费用的录入顺序。入场/出场并排在前、费用在后——按"停车的自然顺序"排列(先记时间、再记费用),用户填写时思维连贯(什么时间停的、停了多久、花了多少),比乱序字段更易填。
十四、最后总结
记录页是停车缴费记录的"录入口"。它用常用地点胶囊解决"高频地点重复输入"的痛点(点击回填),用四字段表单完成完整录入(地点/入场/出场/费用),用带校验的保存按钮保证记录有效(地点必填)。整页交互围绕"录入效率"展开:快捷胶囊一键填入、受控回显所见即所得、双列时间并排填写、单一校验降低门槛。从演示到产品,记录页的进化路线清晰:短信解析自动生成、定位自动计时、多车车牌切换、票据拍照归档。
十五、补充要点:受控回填与常用地点的细节再思考
本页的"快捷胶囊 + 受控回填"有几个细节值得再思考:第一,回填后的可编辑性——点击"公司车库"后地点填入输入框(text: this.place),用户仍可手动修改("公司车库地下二层"),受控回显的"可改"特性让快捷与手输无缝衔接——如果只填不可改(只读),用户想补充细节就受限;如果只手输无快捷,高频地点就重复劳动。第二,常用地点胶囊的选中反馈——点击胶囊后地点填入表单、Toast 提示"公司车库",双重反馈让用户明确"已填入",但胶囊本身没有"选中态"(保持白底描边)——因为常用地点是"点击即用"而非"单选状态",保持非选中视觉是正确的(选中态语义会误导用户以为要"选定")。第三,常用地点与费用统计的联动——常用地点中收费的(公司/万达/医院)会进入费用页的"地点消费"统计,免费的(超市)不统计——用户在记录页点胶囊录入时,这笔记录的费用决定了它是否进统计,录入与统计的规则在记录页就已隐含。第四,快捷胶囊的扩展性——常用地点由数组渲染,新增地点只需追加数组条目(产品化后由使用频率自动生成),"数据驱动快捷入口"让常用地点列表可无限扩展而不改页面结构。这四个细节共同保证快捷录入既高效又灵活,是记录页的核心交互亮点。
十六、最后补充
记录页解析至此完整收尾:常用地点快捷胶囊、受控回填、四字段表单、保存校验全部覆盖。读者可重点回顾"受控输入组件"的写法(文本参数回显加更改同步)——这是快捷录入可见性的关键,也是本页区别于其他录入页的核心实现。停车记录从这一页开始:点一个常用地点、填两段时间、记一笔费用,每一次出行停车的成本就这样被捕捉下来。
补充说明:记录页的四字段与首页的展示字段一一对应——停车地点对应地点名、入场出场对应时长、费用对应金额,录入即展示,数据模型前后贯通,避免了两页字段口径不一致的常见问题。