鸿蒙原生应用 ArkUI 实战:三列三色汇总卡 + 红点竖线时间轴 —— 续费日历页的全年扣款可视化架构

发布时间:2026/10/7 3:10:17
鸿蒙原生应用 ArkUI 实战:三列三色汇总卡 + 红点竖线时间轴 —— 续费日历页的全年扣款可视化架构
鸿蒙原生应用 ArkUI 实战三列三色汇总卡 红点竖线时间轴 —— 续费日历页的全年扣款可视化架构App 55「自动续费提醒」日历页Func2Tab主题色#FF6B6B珊瑚红4 个 Tab 分别为首页⏰、添加➕、日历、我的。本篇基于55-renewal-reminder/entry/src/main/ets/pages/Func2Tab.ets共 103 行逐段拆解附 4 张实机截图。一、整体结构三区Header 汇总卡 时间轴布局日历页是年度总览 全年时间轴的组合布局——3 个 Builder 块build() { Column() { this.Header() Scroll() { Column({ space: 14 }) { this.YearSummary() this.SectionTitle(全年续费时间轴) this.Timeline() } .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 块结构YearSummary三列三色年度汇总→ SectionTitle全年续费时间轴→ Timeline5 条红点竖线时间轴。与 App 54 的月历网格不同本页是纵向时间轴——不按日历格子排列而按时间顺序把全年扣款事件串成一条线。月历看哪天有空时间轴看接下来发生什么续费场景后者更贴切。二、数据模型跨月全年五项interface RenewItem { id: number; emoji: string; name: string; month: number; day: number; amount: number; }private items: RenewItem[] [ { id: 1, emoji: , name: 视频VIP会员, month: 9, day: 1, amount: 25 }, { id: 2, emoji: , name: 音乐会员, month: 9, day: 5, amount: 15 }, { id: 3, emoji: ☁️, name: 云存储服务, month: 9, day: 10, amount: 6 }, { id: 4, emoji: , name: 新闻订阅, month: 10, day: 3, amount: 12 }, { id: 5, emoji: , name: 设计软件年费, month: 1, day: 20, amount: 688 } ];RenewItem六字段比首页多了month——日历页需要跨月表达9 月三项、10 月一项、1 月一项。注意排序是近期优先而非月份升序9月近在前、1月远在末时间轴按距离当前的远近组织而非自然月份顺序——这正是提醒类应用的时间观。三、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 统一范式。与 App 54 日历页同名续费日历但 54 用月历网格、55 用时间轴同名不同形。四、YearSummary三列三色汇总卡YearSummary() { Row() { Column({ space: 4 }) { Text(¥746).fontSize(22).fontWeight(FontWeight.Bold).fontColor(C.primary) Text(年度总额).fontSize(11).fontColor(C.textDim) }.layoutWeight(1) Column({ space: 4 }) { Text(5).fontSize(22).fontWeight(FontWeight.Bold).fontColor(C.accent) Text(续费项目).fontSize(11).fontColor(C.textDim) }.layoutWeight(1) Column({ space: 4 }) { Text(¥62).fontSize(22).fontWeight(FontWeight.Bold).fontColor(C.ok) Text(月均).fontSize(11).fontColor(C.textDim) }.layoutWeight(1) } .width(100%).padding({ top: 16, bottom: 16 }).backgroundColor(C.card).borderRadius(D.rLg).border({ width: 1, color: C.stroke }) }逐点拆解白底卡而非渐变卡backgroundColor(C.card) 细描边——与首页/我的页的渐变卡不同汇总卡是白底彩字信息卡而非品牌卡三个数字各用一色。三列三色年度总额C.primary红钱警示/ 续费项目C.accent浅红数量主题辅色/ 月均C.ok绿均值健康参考——颜色编码数据语义红要花的钱、浅红项目规模、绿月均水平一卡三色各司其职。三列均分layoutWeight(1)×3——与 App 53 首页三列汇总同款布局但 53 是渐变底白字、55 是白底彩字同构异形。数据自洽月均 ¥62 × 12 ≈ ¥746 年度总额——月均与年度互为换算演示数据自洽。五、Timeline红点竖线时间轴Timeline() { Column() { ForEach(this.items, (r: RenewItem, idx: number) { Row({ space: 12 }) { Column() { Row().width(12).height(12).backgroundColor(C.primary).borderRadius(6) if (idx this.items.length - 1) { Column().width(2).height(50).backgroundColor(C.stroke) } }.width(12) Row({ space: 12 }) { Text(r.emoji).fontSize(22) Column({ space: 3 }) { Text(r.name).fontSize(14).fontWeight(FontWeight.Medium).fontColor(C.text) Text(r.month 月 r.day 日).fontSize(12).fontColor(C.textDim) }.alignItems(HorizontalAlign.Start).layoutWeight(1) Text(¥ r.amount).fontSize(14).fontWeight(FontWeight.Bold).fontColor(C.danger) } .layoutWeight(1).padding(12).margin({ bottom: 12 }) .backgroundColor(C.card).borderRadius(D.rMd).border({ width: 1, color: C.stroke }) } .width(100%).alignItems(VerticalAlign.Top) }, (r: RenewItem) r.id.toString()) }.width(100%) }逐点拆解时间轴三段式左侧竖轨圆点 连接线 右侧内容卡——Row内左Column12 宽轨道右RowlayoutWeight(1)内容区轨道与内容分离。12×12 红色圆点Row().width(12).height(12).backgroundColor(C.primary).borderRadius(6)——宽高 12、圆角 6 正圆珊瑚红圆点是时间轴的节点。条件竖线84-86 行if (idx this.items.length - 1)才渲染2×50灰色连接线——最后一项无竖线时间轴在末尾自然收束。这是时间轴组件的经典细节连接线是通向下一项的语义末项无后续故无线。alignItems(VerticalAlign.Top)外层 Row 顶部对齐——圆点与卡片顶部对齐而非垂直居中时间轴节点对齐卡片标题行视觉锚点统一。红色金额C.danger红字¥688——扣款金额统一警示红与 App 54 日历页同款语义扣钱 红色。六、跨页数据自洽全年扣款的全局闭环Text(¥746).fontSize(22).fontWeight(FontWeight.Bold).fontColor(C.primary){ value: ¥746, label: 年度支出 },日历页¥746 年度总额 / 5 续费项目↔ 我的页¥746 年度支出 / 5 提醒项目——数字完全一致日历从时间轴、我的从统计展示同一批数据。日历页视频VIP 9月1日 ¥25↔ 首页视频VIP 2 天后扣款 ¥25——同一扣款的两种表达日历给日期、首页给倒计时。日历页设计软件年费 1月20日 ¥688↔ 首页设计软件年费 15 天后 ¥688——年费 688 是最大单项两页均收录。日历页 5 项↔ 首页 4 项——日历是全年全量含 10 月新闻订阅首页是近期重点4 项全量 vs 精选的范围分工。七、系列横向对比时间可视化的两种形态日历页代表 App可视化形态数据范围主题色纵向时间轴App 55 续费红点 竖线 事件卡全年跨月 5 项#FF6B6B珊瑚红月历网格App 54 订阅7 列 Grid 续费日圆点单月 31 天#9B51E0梦幻紫差异点App 54 用月历网格空间化哪天有空一眼看分布App 55 用纵向时间轴线性化接下来发生什么按序呈现。月历适合找日期时间轴适合看流程——续费提醒的核心是接下来还有哪些扣款时间轴的线性叙事比月历的格子分布更贴合。两 App 同日历主题、不同可视化选型是系列中同题异构的典型案例。八、开发者视角调试与验证技巧条件竖线的索引判定idx this.items.length - 1——依赖 ForEach 的第二参数idx若数据动态增删如删除中间项连接线自动适配无需额外处理但若用filter过滤后索引重排需确认渲染用的是过滤后数组的索引。竖线高度 50 硬编码Column().width(2).height(50)——连接线长度写死若内容卡高度变化如名称换行变两行50 的竖线会与下一圆点脱节产品化应用layoutWeight(1)让竖线自适应内容高度。跨月排序数据按近期优先9月→10月→1月而非月份升序——产品化应sort按距今天数排序跨年场景12 月 vs 1 月需正确处理月份回绕1 月排在 12 月后而非前。金额红色语义C.danger统一红字金额——与首页的条件变红仅临期红不同日历页金额恒红日历页所有金额都是要扣的钱同 App 内两处红色语义有细微差异首页红紧急、日历红支出需设计文档明确。九、演进方向方向当前状态产品化演进动态排序数组声明顺序按距今天数 sort跨年回绕处理竖线自适应高度 50 硬编码layoutWeight(1)随内容卡伸缩今天定位无时间轴滚动定位到今天节点并高亮已完成态无已过扣款日期的项目灰置 勾选标记金额合计仅年度/月均时间轴按月分组 每月小计十、一句话总结日历页以三列三色汇总卡 红点竖线时间轴完成了全年扣款的线性可视化。白底彩字汇总卡C.primary红 /C.accent浅红 /C.ok绿 三色编码金额/数量/均值、12×12 珊瑚红圆点 idx length - 1条件竖线末项自然收束、alignItems(VerticalAlign.Top)节点与卡片顶对齐——103 行代码一个年度看总账 → 时间轴看流程的续费日历页也是纵向时间轴型日历的标准范式。项目源码开源https://gitee.com/codenestFlow/HarmonyOSHub