
鸿蒙原生应用 ArkUI 实战双卡分离表单 三档提醒胶囊 —— 续费添加页的录入架构App 55「自动续费提醒」添加页Func1Tab主题色#FF6B6B珊瑚红4 个 Tab 分别为首页⏰、添加➕、日历、我的。本篇基于55-renewal-reminder/entry/src/main/ets/pages/Func1Tab.ets共 97 行逐段拆解附 4 张实机截图。一、整体结构三区Header 表单卡 提醒卡布局添加页是服务信息录入 提醒规则设置的组合布局——2 个 Builder 块 1 个按钮build() { Column() { this.Header() Scroll() { Column({ space: 16 }) { this.FormCard() this.RemindCard() Button(添加提醒).fontSize(15).fontColor(#FFFFFF).backgroundColor(C.primary) .width(100%).height(48).borderRadius(D.rMd) .onClick(() { promptAction.showToast({ message: this.name.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 块结构FormCard四件套服务表单→ RemindCard提前提醒三档胶囊→ 添加提醒按钮。与 App 52/53 的单卡表单不同本页把表单拆成两张卡——FormCard 管扣什么服务信息RemindCard 管何时提醒提醒规则。信息录入与规则设置分离成卡职责边界清晰。二、四状态声明 数据模型State name: string ; State amount: string ; State activeCycle: number 0; State remindDays: number 3;private cycles: string[] [每月, 每季, 每年]; private remindOptions: number[] [1, 3, 7];四个Statename服务名称、amount金额、activeCycle周期索引默认 0 每月、remindDays提醒天数默认 3。注意remindDays存的是天数数值而非索引——remindOptions是 [1, 3, 7]选中值直接用天数比较this.remindDays d比索引更语义化。三、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) }20 号加粗、safeTop 56固定高、文字贴底——系列功能页 Header 统一范式。添加提醒是动词短语动作型标题暗示这是一个待完成的操作。四、FormCard四件套服务表单FormCard() { Column({ space: 14 }) { Column({ space: 6 }) { Text(服务名称).fontSize(13).fontColor(C.textSub).width(100%) TextInput({ placeholder: 例如视频VIP会员 }) .backgroundColor(C.cardSoft).borderRadius(D.rSm).height(44) .onChange((v: string) { this.name v; }) } Column({ space: 6 }) { Text(扣款金额元).fontSize(13).fontColor(C.textSub).width(100%) TextInput({ placeholder: 0.00 }).type(InputType.Number) .backgroundColor(C.cardSoft).borderRadius(D.rSm).height(44) .onChange((v: string) { this.amount v; }) }前两件套服务名称TextInput44 高淡灰底圆角 10onChange绑定this.name——保存按钮校验的锚点字段与 App 52 金额、App 53 名称同逻辑校验最核心的标识字段。扣款金额InputType.Number数字键盘 占位符0.00——金额字段调起数字键盘减少输入错误与 App 52 金额输入同款处理。Column({ space: 6 }) { Text(扣款周期).fontSize(13).fontColor(C.textSub).width(100%) Row({ space: 10 }) { ForEach(this.cycles, (c: string, idx: number) { Text(c).fontSize(13) .fontColor(this.activeCycle idx ? #FFFFFF : C.textSub) .textAlign(TextAlign.Center).layoutWeight(1).padding({ top: 9, bottom: 9 }) .backgroundColor(this.activeCycle idx ? C.primary : C.cardSoft).borderRadius(D.rSm) .onClick(() { this.activeCycle idx; }) }, (c: string) c) }.width(100%) } Column({ space: 6 }) { Text(扣款日期).fontSize(13).fontColor(C.textSub).width(100%) TextInput({ placeholder: 每月 1 日 }) .backgroundColor(C.cardSoft).borderRadius(D.rSm).height(44) }后两件套扣款周期 3 等分胶囊每月/每季/每年——layoutWeight(1)三等分 textAlign(TextAlign.Center)居中选中红底白字C.primary珊瑚红。与 App 53 的保修期限 4 等分同款布局选项少3 项用等分比换行更高效。扣款日期TextInput占位符每月 1 日——注意此项无onChange绑定76 行无状态写入是四件套中唯一的展示型输入框产品化需补绑定或换 DatePicker。五、RemindCard提前提醒三档胶囊RemindCard() { Column({ space: 12 }) { Text(提前提醒).fontSize(14).fontWeight(FontWeight.Bold).fontColor(C.text).width(100%) Row({ space: 10 }) { ForEach(this.remindOptions, (d: number) { Text(提前 d 天).fontSize(12) .fontColor(this.remindDays d ? #FFFFFF : C.textSub) .textAlign(TextAlign.Center).layoutWeight(1).padding({ top: 9, bottom: 9 }) .backgroundColor(this.remindDays d ? C.primary : C.cardSoft).borderRadius(D.rSm) .onClick(() { this.remindDays d; }) }, (d: number) d.toString()) }.width(100%) } .width(100%).padding(16).backgroundColor(C.card).borderRadius(D.rLg).border({ width: 1, color: C.stroke }) }逐点拆解独立成卡提醒设置不混在服务表单里单独一张卡——扣什么与何时提醒是两类信息分卡降低认知负荷。天数直存ForEach(this.remindOptions, (d: number)——遍历的是[1, 3, 7]数值数组onClick直接this.remindDays d存天数非索引语义直观。默认 3 天remindDays 3——中位数默认值与首页临期阈值daysLeft 3一致用户添加的提醒与首页告警天然对齐。胶囊文案带前缀提前 1 天/提前 3 天/提前 7 天——文案自带提前语境比光秃秃的数字更易懂。六、保存按钮名称校验Button(添加提醒).fontSize(15).fontColor(#FFFFFF).backgroundColor(C.primary) .width(100%).height(48).borderRadius(D.rMd) .onClick(() { promptAction.showToast({ message: this.name.length 0 ? 提醒已添加 : 请填写服务名称 }); })逐点拆解全宽红色 CTAC.primary珊瑚红全宽 48 高——表单页标准 CTA 形态。三元校验this.name.length 0 ? 提醒已添加 : 请填写服务名称——以名称为校验锚点服务名称是提醒的标识没有名字的提醒无法识别。系列校验锚点对比App 52 校验金额票据核心是钱、App 53 校验名称商品核心是名、App 55 校验名称提醒核心是名——校验锚点永远指向没有它记录就没意义的字段。七、跨页数据自洽提醒是续费体系的录入口添加页扣款周期 每月/每季/每年↔ 日历页各项的月份分布——周期决定扣款频率日历时间轴按周期展开月付项每月出现、年费项一年一次。添加页提前提醒 3 天默认↔ 首页临期 ≤3 天——提醒天数 告警阈值用户设 3 天提醒首页就把 3 天内的扣款标红录入即生效。添加页服务名称 金额↔ 首页列表名称 金额——保存的新提醒进入首页列表产品化后items.push()录入即展示。添加页扣款日期↔ 日历页9月1日/9月5日——录入的日期决定日历时间轴上的位置。八、系列横向对比录入页的三种分卡策略录入页代表 App分卡策略胶囊布局主题色双卡分离App 55 续费服务卡 提醒卡周期 3 等分 提醒 3 等分#FF6B6B珊瑚红单卡五件套App 53 商品一卡全包分类 5 换行 期限 4 等分#00A862生态绿上传卡 表单App 52 票据来源卡 表单卡类型/分类双换行#0066CC科技蓝差异点App 55 的双卡分离——把提醒规则从服务表单中独立成卡因为提醒天数是本应用的差异化能力续费提醒的核心是何时提醒而非记录什么。App 53 把分类/期限混在一卡都是商品属性55 把提醒单列规则 ≠ 属性分卡的依据是信息的心智归属。九、开发者视角调试与验证技巧天数存值 vs 存索引remindDays 3存天数this.remindDays dactiveCycle 0存索引this.activeCycle idx——同页两种选中态策略天数选项语义化值即含义所以直存数值周期选项是字符串所以存索引混用不冲突但要在团队内统一约定。扣款日期无绑定76 行的TextInput无onChange——演示漏绑产品化必须补onChange存状态否则保存时取不到日期更优方案是周期选每月时用数字选择器选日、选每年时用 DatePicker 选月日。金额格式校验InputType.Number只限键盘类型——产品化应amount.replace(/[^\d.]/g, )过滤非法字符并toFixed(2)统一两位小数。默认值联动周期默认每月时日期占位符每月 1 日——产品化应让占位符随周期切换变化选每年变每年 1 月 1 日占位符是活的提示语。十、演进方向方向当前状态产品化演进日期输入TextInput 无绑定按周期切换选择器每月→选日/每年→DatePicker金额校验Number 键盘非法字符过滤 两位小数格式化提醒持久化Toast 占位Preferences 存储 通知权限申请周期扩展3 档固定支持自定义周期每 N 天保存反馈Toast提醒已添加返回首页刷新 新提醒置顶十一、一句话总结添加页以双卡分离表单 三档提醒胶囊完成了续费提醒的录入闭环。FormCard 四件套名称/金额 Number 键盘/周期 3 等分胶囊/日期 RemindCard 独立成卡remindDays天数直存、默认 3 天与首页临期阈值同源、名称三元校验——97 行代码一个录服务 → 设周期 → 定提醒 → 保存的提醒建档页也是属性与规则分卡型表单的标准范式。项目源码开源https://gitee.com/codenestFlow/HarmonyOSHub