news 2026/10/7 20:46:43

鸿蒙原生应用 ArkUI 实战:双卡分离表单 + 三档提醒胶囊 —— 续费添加页的录入架构

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
鸿蒙原生应用 ArkUI 实战:双卡分离表单 + 三档提醒胶囊 —— 续费添加页的录入架构

鸿蒙原生应用 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];

四个@State:name(服务名称)、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; }) }

前两件套:

  1. 服务名称:TextInput44 高淡灰底圆角 10,onChange绑定this.name——保存按钮校验的锚点字段(与 App 52 金额、App 53 名称同逻辑:校验最核心的标识字段)。
  2. 扣款金额: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) }

后两件套:

  1. 扣款周期 3 等分胶囊:每月/每季/每年——layoutWeight(1)三等分 +textAlign(TextAlign.Center)居中,选中红底白字(C.primary珊瑚红)。与 App 53 的"保修期限 4 等分"同款布局,选项少(3 项)用等分比换行更高效。
  2. 扣款日期: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 }) }

逐点拆解:

  1. 独立成卡:提醒设置不混在服务表单里,单独一张卡——"扣什么"与"何时提醒"是两类信息,分卡降低认知负荷。
  2. 天数直存:ForEach(this.remindOptions, (d: number)——遍历的是[1, 3, 7]数值数组,onClick直接this.remindDays = d存天数(非索引),语义直观。
  3. 默认 3 天:remindDays = 3——中位数默认值,与首页临期阈值(daysLeft <= 3)一致,用户添加的提醒与首页告警天然对齐。
  4. 胶囊文案带前缀:"提前 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 ? '提醒已添加' : '请填写服务名称' }); })

逐点拆解:

  1. 全宽红色 CTA:C.primary珊瑚红全宽 48 高——表单页标准 CTA 形态。
  2. 三元校验:this.name.length > 0 ? '提醒已添加' : '请填写服务名称'——以名称为校验锚点(服务名称是提醒的标识,没有名字的提醒无法识别)。
  3. 系列校验锚点对比: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 把提醒单列(规则 ≠ 属性),分卡的依据是信息的心智归属。

九、开发者视角:调试与验证技巧

  1. 天数存值 vs 存索引:remindDays = 3存天数(this.remindDays === d),activeCycle = 0存索引(this.activeCycle === idx)——同页两种选中态策略,天数选项语义化(值即含义)所以直存数值,周期选项是字符串所以存索引;混用不冲突但要在团队内统一约定。
  2. 扣款日期无绑定:76 行的TextInput无onChange——演示漏绑,产品化必须补onChange存状态,否则保存时取不到日期;更优方案是周期选"每月"时用数字选择器选日、选"每年"时用 DatePicker 选月日。
  3. 金额格式校验:InputType.Number只限键盘类型——产品化应amount.replace(/[^\d.]/g, '')过滤非法字符并toFixed(2)统一两位小数。
  4. 默认值联动:周期默认"每月"时日期占位符"每月 1 日"——产品化应让占位符随周期切换变化(选"每年"变"每年 1 月 1 日"),占位符是"活的提示语"。

十、演进方向

方向当前状态产品化演进
日期输入TextInput 无绑定按周期切换选择器(每月→选日/每年→DatePicker)
金额校验Number 键盘非法字符过滤 + 两位小数格式化
提醒持久化Toast 占位Preferences 存储 + 通知权限申请
周期扩展3 档固定支持自定义周期(每 N 天)
保存反馈Toast"提醒已添加"返回首页刷新 + 新提醒置顶

十一、一句话总结

添加页以"双卡分离表单 + 三档提醒胶囊"完成了续费提醒的录入闭环。FormCard 四件套(名称/金额 Number 键盘/周期 3 等分胶囊/日期)+ RemindCard 独立成卡(remindDays天数直存、默认 3 天与首页临期阈值同源)、名称三元校验——97 行代码,一个"录服务 → 设周期 → 定提醒 → 保存"的提醒建档页,也是"属性与规则分卡"型表单的标准范式。

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

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

无重复字符的最长子串:LeetCode滑动窗口经典题全解析

第一次刷 LeetCode 的同学&#xff0c;往往在第三题就卡住了。前面两题还停留在“暴力能不能过”的挣扎里&#xff0c;突然冒出来一个“无重复字符的最长子串”&#xff0c;嘴上念着“这不是用 substring 挨个检查吗”&#xff0c;心里已经开始发怵。这道题之所以是经典的不能再…

作者头像 李华
网站建设 2026/10/7 20:42:58

想降低论文AI率?免费提示词、嘎嘎降软件与1000字免费体验指南!

想降低论文AI率&#xff1f;免费提示词、嘎嘎降软件与1000字免费体验指南&#xff01; 论文已经改过几遍&#xff0c;打开AIGC检测报告&#xff0c;文献综述和讨论部分依然有大片标记AI痕迹?怎么降低论文检测的AI率&#xff1f; 2026年9月实测的免费降AI率技巧&#xff0c;手把…

作者头像 李华
网站建设 2026/10/7 20:41:11

Kotlin 泛型方差实战:out、in、星投影与类型擦除

很多人记得“out 是生产者&#xff0c;in 是消费者”&#xff0c;但一遇到 MutableList<Dog>、Array<Dog> 或 MutableList<*> 就又要靠猜。方差真正解决的是一个安全问题&#xff1a;当子类型关系穿过泛型容器时&#xff0c;哪些赋值不会让我们读错或写错类型…

作者头像 李华
网站建设 2026/10/7 20:40:46

基于Spring Boot和MySQL的路灯管理信息系统毕业设计实战

简介&#xff1a;一套面向高校毕业设计的Java Web路灯管理信息系统源码包&#xff0c;基于JSPServlet与MySQL数据库&#xff0c;采用B/S模式&#xff0c;可在MyEclipse或Eclipse中直接导入运行。压缩包共441个文件&#xff0c;约7.75MB&#xff0c;核心包含jsp动态页面、Java类…

作者头像 李华
网站建设 2026/10/7 20:40:41

Webpack太慢?Vite迁移实战:构建优化与配置指南

1. 为什么 Webpack 开发时那么慢&#xff1a;bundler 编译模型的瓶颈我之前接手过一个中后台项目&#xff0c;React TypeScript&#xff0c;页面模块大概 300 多个&#xff0c;node_modules里的依赖也不少。刚开始用 Webpack 4 开发&#xff0c;一次npm run dev冷启动要 28 秒…

作者头像 李华