鸿蒙原生应用 ArkUI 实战:红色渐变告警卡 + 临期双条件渲染 —— 续费提醒首页的扣款预警架构

App 55「自动续费提醒」首页(HomeTab),主题色 #FF6B6B 珊瑚红,4 个 Tab 分别为首页(⏰)、添加(➕)、日历(📅)、我的(👤)。本篇基于 55-renewal-reminder/entry/src/main/ets/pages/HomeTab.ets(共 92 行)逐段拆解,附 4 张实机截图。

首页整体布局

一、整体结构:三区"Header + 告警卡 + 列表"布局

首页是"品牌头部 + 扣款告警 + 近期扣款列表"的三区布局——3 个 @Builder 块:

build() {
  Column() {
    this.Header()
    Scroll() {
      Column({ space: 14 }) {
        this.AlertCard()
        this.SectionTitle('即将扣款')
        this.ItemList()
      }
      .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 块结构:Header(单行品牌头)→ AlertCard(红色渐变告警卡)→ ItemList(4 条扣款列表)。与 App 52/53/54 的"汇总卡"不同,本页第一卡是告警卡——不展示统计数字,而是直接喊出"2 天后有扣款"。续费提醒的核心不是"看账"而是"防偷袭",首页第一屏就要把最近的扣款威胁推到用户眼前。

二、Header:单行品牌头

Header() {
  Column() {
    Text('续费提醒').fontSize(22).fontWeight(FontWeight.Bold).fontColor(C.text)
      .width('100%').padding({ top: this.safeTop + 10, left: D.pad, right: D.pad, bottom: 12 })
  }.width('100%').backgroundColor(C.card)
}

22 号加粗、safeTop + 10 避让状态栏、白底全宽。与系列工具型首页(App 52/53/54)同款单行标题范式。

三、AlertCard:红色渐变告警卡

AlertCard() {
  Row({ space: 14 }) {
    Text('⏰').fontSize(40)
    Column({ space: 4 }) {
      Text('2 天后有扣款').fontSize(17).fontWeight(FontWeight.Bold).fontColor('#FFFFFF')
      Text('视频VIP会员将扣费 ¥25').fontSize(12).fontColor('#FFFFFF').opacity(0.9)
    }.alignItems(HorizontalAlign.Start).layoutWeight(1)
  }
  .width('100%').padding(18).borderRadius(D.rLg)
  .linearGradient({ angle: 135, colors: [[C.primary, 0.0], [C.accent, 1.0]] })
}

逐点拆解:

  1. 红色渐变告警:linearGradient 从 #FF6B6B 珊瑚红渐变到 #FFB0B0 浅红——红色在语义上是"警示/注意",与续费提醒"防漏扣"的产品气质契合(对比 App 53 生态绿 = 安全在保,55 珊瑚红 = 扣款预警)。
  2. 横排布局而非纵排:Row({ space: 14 })——40 号 ⏰ 大图标居左 + 文字区 layoutWeight(1) 撑满右侧。与 App 52/54 的纵排汇总卡(Column)不同,告警卡是"图标 + 文案"的横向横幅,更接近系统通知的视觉形态。
  3. 两级文案:17 号 Bold"2 天后有扣款"(结论)+ 12 号 90% 透明"视频VIP会员将扣费 ¥25"(细节)——先抛威胁等级,再给具体对象,一句话说清"谁、什么时候、扣多少"。
  4. 数据取自列表首项:告警内容与 items[0](视频VIP 2 天后 ¥25)完全一致——告警卡 = 最紧急项的放大呈现,产品化后应由"daysLeft 最小的未过期待扣款项"动态生成。

四、RenewItem 数据模型 + SectionTitle

interface RenewItem {
  id: number;
  emoji: string;
  name: string;
  amount: number;
  daysLeft: number;
}
private items: RenewItem[] = [
  { id: 1, emoji: '🎬', name: '视频VIP会员', amount: 25, daysLeft: 2 },
  { id: 2, emoji: '🎵', name: '音乐会员', amount: 15, daysLeft: 5 },
  { id: 3, emoji: '☁️', name: '云存储服务', amount: 6, daysLeft: 8 },
  { id: 4, emoji: '💻', name: '设计软件年费', amount: 688, daysLeft: 15 }
];

RenewItem 五字段:daysLeft(距扣款天数)是本页的核心驱动字段——列表按它升序(2→5→8→15,最紧急置顶),颜色与临期标签都由它判定。注意设计软件年费 688 元夹在列表末尾——金额最大但时间最远,按时间排序而非金额排序,续费提醒的正确姿势。

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

纯标题无"全部 >"入口——与 App 53 保修页同款(首页列表即全部近期项,无需跳转)。

五、ItemList:临期双条件渲染列表

ItemList() {
  Column({ space: 10 }) {
    ForEach(this.items, (r: RenewItem) => {
      Row({ space: 12 }) {
        Row() { Text(r.emoji).fontSize(24) }
        .width(48).height(48).backgroundColor(C.primarySoft).borderRadius(D.rSm).justifyContent(FlexAlign.Center)
        Column({ space: 4 }) {
          Text(r.name).fontSize(14).fontWeight(FontWeight.Medium).fontColor(C.text)
          Text(r.daysLeft + ' 天后扣款 ¥' + r.amount).fontSize(12)
            .fontColor(r.daysLeft <= 3 ? C.danger : C.textDim)
        }.alignItems(HorizontalAlign.Start).layoutWeight(1)
        if (r.daysLeft <= 3) {
          Text('临期').fontSize(11).fontColor(C.danger)
            .padding({ left: 8, right: 8, top: 3, bottom: 3 }).backgroundColor('#FDEAEA').borderRadius(6)
        }
      }
      .width('100%').padding(12).backgroundColor(C.card).borderRadius(D.rMd).border({ width: 1, color: C.stroke })
      .onClick(() => { promptAction.showToast({ message: r.name }); })
    }, (r: RenewItem) => r.id.toString())
  }.width('100%')
}

逐点拆解:

  1. 48×48 主色软底图标块:C.primarySoft(#FFE8E8 淡红)圆角底 + 24 号 emoji——与主题红同色系,品牌统一。
  2. 副文案条件变色(79-80 行):r.daysLeft <= 3 ? C.danger : C.textDim——≤3 天的扣款副文案变红("2 天后扣款 ¥25"红字),其余保持淡灰,文字本身即告警。
  3. 临期胶囊条件渲染(82-85 行):if (r.daysLeft <= 3) 才渲染"临期"胶囊(红字粉底 #FDEAEA 圆角 6)——胶囊是条件存在的,不是变色切换。与 App 53 的"三态胶囊恒在(在保/临期/过保)"不同,本页是"有威胁才出现"——列表中只有视频VIP(2 天)带临期标签,其余三条干净无标,视觉噪音最小化。
  4. 双条件同源:副文案变色与临期胶囊共用同一阈值 daysLeft <= 3——红字 + 红胶囊双重告警,一处判定两处呈现,不会出现"字红了但胶囊没出"的不一致。

首页列表与交互

六、跨页数据自洽:扣款预警的全局闭环

  { id: 1, emoji: '🎬', name: '视频VIP会员', amount: 25, daysLeft: 2 },
    { id: 1, emoji: '🎬', name: '视频VIP会员', month: 9, day: 1, amount: 25 },
  • 首页"视频VIP 25 元 2 天后" ↔ 日历页"视频VIP会员 9月1日 ¥25"——同一项扣款,金额一致,首页按"还剩几天"呈现、日历按"几月几日"呈现。
  • 首页 4 项 ↔ 日历页 5 项——首页是"近期即将扣款"的重点列表(4 项),日历是全年时间轴(含 10 月新闻订阅、1 月设计软件年费),近期 vs 全年的范围差异。
  • 首页"设计软件年费 688 元" ↔ 日历页"1月20日 ¥688" + 我的页"¥746 年度支出"——年费 688 是年度总额的最大单项。
  • 首页"临期 ≤3 天" ↔ 添加页"提前提醒 1/3/7 天"(remindDays = 3 默认)——告警阈值与提醒天数默认值一致,告警与提醒共用同一时间尺度。

点击扣款卡片弹出提示

七、珊瑚红主题的视觉策略

export class C {
  static readonly bg: string = '#F2F5FB';
  static readonly card: string = '#FFFFFF';
  static readonly cardSoft: string = '#F4F6FB';
  static readonly stroke: string = '#EAEEF6';
  static readonly text: string = '#1C2333';
  static readonly textSub: string = '#5A6483';
  static readonly textDim: string = '#9AA3BC';
  static readonly primary: string = '#FF6B6B';
  static readonly primarySoft: string = '#FFE8E8';
  static readonly accent: string = '#FFB0B0';
  static readonly warn: string = '#FF9F1C';
  static readonly danger: string = '#FF5A6E';
  static readonly ok: string = '#2BB673';
}

逐点拆解:

  1. primary = #FF6B6B:珊瑚红是系列中首个"红色系主色"应用——红色天然传达"警示/紧急",续费提醒用红色表达"你的钱即将被扣"的紧迫感。
  2. primarySoft = #FFE8E8:淡红用于图标块底色——主红的低浓度变体。
  3. 双红分工:primary #FF6B6B(主题红,告警卡/图标底)与 danger #FF5A6E(危险红,临期文字/胶囊)色值接近——主色与警示色同色系,整页红色语言统一,不会出现"红配绿"的冲突。

八、系列横向对比:首页第一卡的四种形态

首页代表 App第一卡类型核心信息主题色
告警卡App 55 续费红色渐变告警卡"2 天后有扣款"#FF6B6B 珊瑚红
汇总卡App 54 订阅紫色渐变汇总卡月度支出 53.30#9B51E0 梦幻紫
三列汇总App 53 保修绿色渐变三列卡8/2/1 三态统计#00A862 生态绿
金额汇总App 52 票据蓝色渐变金额卡本月总额 1010#0066CC 科技蓝

差异点:App 55 是系列中唯一用"告警卡"打头阵的首页——52/53/54 的第一卡都是"统计数字"(金额/数量),55 的第一卡是"即将发生的事件"(2 天后扣款)。统计卡回答"现在是什么状况",告警卡回答"马上要发生什么",后者的时间紧迫感是续费提醒场景的核心体验。

滚动查看全部扣款项目

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

  1. 列表排序依据:items 按 daysLeft 升序硬编码(2→5→8→15)——产品化后必须动态排序(sort((a,b) => a.daysLeft - b.daysLeft)),否则新增项目插入数组末尾会破坏"最紧急置顶"的信息架构。
  2. 双条件同源:副文案变色与临期胶囊共用 daysLeft <= 3——若后续调整阈值(如改为 5 天),两处必须同时改,建议提取常量 URGENT_DAYS = 3 统一管理。
  3. 告警卡数据联动:告警卡"2 天后有扣款"硬编码——产品化应从列表取 daysLeft 最小项动态生成,无待扣款时告警卡应切换为"近期无扣款"的安全态,避免空数据渲染出错误的告警。
  4. 条件渲染 vs 条件样式:临期胶囊用 if 条件渲染(不存在于组件树),副文案用三元条件样式(恒在只变色)——"出现/消失"用 if,"变色"用三元,两种条件策略各司其职,混用会造成布局抖动。

十、演进方向

方向当前状态产品化演进
告警动态化硬编码"2 天后"取 daysLeft 最小项动态生成
列表排序数组声明顺序按 daysLeft 升序动态排序
天数计算硬编码 2/5/8/15扣款日 - 今日 实时计算
临期阈值固定 3 天跟随添加页的提醒天数设置
卡片详情Toast 项目名跳详情页(扣款历史/关闭续费入口)

十一、一句话总结

首页以"红色渐变告警卡 + 临期双条件渲染列表"完成了续费扣款的预警总览。linearGradient 珊瑚红告警卡(⏰40 号横排横幅 + 两级文案)、daysLeft <= 3 同源双条件(副文案变红 + 临期胶囊条件渲染)、48×48 淡红底图标块、列表按剩余天数升序(最紧急置顶)——92 行代码,一个"先喊威胁 → 再列明细"的扣款预警页,也是"事件驱动"型首页的标准范式。

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

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐