HarmonyOS 校园应用系列之鸿蒙原生实战:用 ArkUI 搭水电缴费首页 —— 消费趋势柱图与排行列表
HarmonyOS 校园应用系列之鸿蒙原生实战:用 ArkUI 搭水电缴费首页 —— 消费趋势柱图与排行列表
应用背景:10 水电缴费(utility-pay)是鸿蒙原生校园工具系列第 10 个应用。本文逐行拆解
HomeTab.ets(310 行),聚焦 HeroCard 渐变统计卡、WeekChart 柱状趋势图、BillList 近期消费列表、RankList 消费排行 四大核心组件。

一、页面整体布局
// HomeTab.ets — 页面根结构
Column() {
this.Header()
Scroll() {
Column({ space: 14 }) {
this.HeroCard()
this.SearchBar()
this.QuickAction()
this.WeekChart()
this.SectionTitle('近期消费', '全部 >')
this.BillList()
this.SectionTitle('消费排行', '详情 >')
this.RankList()
}
.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)
根布局为 Column 两段式:Header 固定在 Scroll 外部不随内容滚动,滚动区内部 Column({ space: 14 }) 纵向排布 8 个模块,隐藏滚动条。底部 padding 动态叠加 safeBottom 安全区高度,避免内容被系统导航条遮挡。
Header 是白底标题栏:"水电缴费" 22vp 粗体,顶部 padding 叠加 safeTop 适配状态栏:
// Header — 标题栏
@Builder
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)
}

二、HeroCard 渐变头部统计卡
2.1 渐变背景与圆角策略
// HeroCard — 外层容器
Column({ space: 16 }) {
// 宿舍信息行
Row() {
Text('梅园 6 栋 · 302 室').fontSize(13).fontColor('#FFFFFF').opacity(0.85)
Blank()
Text('本月统计').fontSize(11).fontColor('#FFFFFF').opacity(0.7)
}
.width('100%')
// 三维统计
Row({ space: 12 }) {
ForEach(this.stats, (s: StatItem) => {
Column({ space: 4 }) {
Text(s.value).fontSize(22).fontWeight(FontWeight.Bold).fontColor('#FFFFFF')
Text(s.label).fontSize(11).fontColor('#FFFFFF').opacity(0.8)
}
.layoutWeight(1).alignItems(HorizontalAlign.Center)
}, (s: StatItem) => s.label)
}
.width('100%')
// 双按钮行
Row({ space: 12 }) {
Button('立即缴费')
.fontSize(14).fontColor(C.primary).backgroundColor('#FFFFFF')
.borderRadius(D.rSm).height(40).layoutWeight(1)
.onClick(() => { promptAction.showToast({ message: '前往缴费' }); })
Button('查看账单')
.fontSize(14).fontColor('#FFFFFF').backgroundColor('#33FFFFFF')
.borderRadius(D.rSm).height(40).layoutWeight(1)
.onClick(() => { promptAction.showToast({ message: '查看账单' }); })
}
.width('100%')
}
.width('100%')
.padding(20)
.borderRadius(D.rLg)
.linearGradient({ angle: 135, colors: [[C.primary, 0.0], [C.accent, 1.0]] })
外层 Column 设 .linearGradient(...) 从主色青蓝 C.primary(#0891B2)135° 过渡到亮青 C.accent(#22D3EE),四角统一 D.rLg(20vp)圆角,内边距 20。
2.2 三维统计
// stats 数据定义
private stats: StatItem[] = [
{ value: '¥86', label: '电费余额', color: '#FFFFFF' },
{ value: '¥42', label: '水费余额', color: '#FFFFFF' },
{ value: '¥478', label: '累计缴费', color: '#FFFFFF' }
]
三项统计通过 ForEach + layoutWeight(1) 三等分:22vp 白色粗体数值 + 80% 透明度小标签,不设分隔线,靠 12vp 间距自然分区。
2.3 双按钮操作区
两个 .layoutWeight(1) 等宽按钮:主按钮"立即缴费"白底主色字,次按钮"查看账单"20% 透明白底(#33FFFFFF)白字,均高 40vp、D.rSm 圆角,点击弹 toast。

三、SearchBar 搜索栏
// SearchBar — 搜索组件
Row({ space: 10 }) {
Text('🔍').fontSize(18)
TextInput({ placeholder: '搜索缴费记录', text: this.searchText })
.backgroundColor('transparent').borderRadius(D.rSm).height(40)
.layoutWeight(1).placeholderColor(C.textDim).placeholderFont({ size: 14 })
.onChange((val: string) => { this.searchText = val; })
}
.width('100%')
.padding({ left: 14, right: 14 })
.backgroundColor(C.card)
.borderRadius(D.rMd)
.border({ width: 1, color: C.stroke })
外层 Row 包裹 Emoji 图标 + TextInput,白卡底 + 1vp 描边 + D.rMd 圆角。TextInput 自身背景透明、layoutWeight(1) 占满剩余宽度,onChange 双向绑定 @State searchText。
四、QuickAction 四宫格快捷入口
// QuickAction — 快捷入口
Row({ space: 10 }) {
ForEach(this.quickActions, (item: QuickItem) => {
Column({ space: 6 }) {
Text(item.emoji).fontSize(26)
Text(item.label).fontSize(12).fontColor(C.textSub)
}
.layoutWeight(1).padding({ top: 14, bottom: 14 })
.backgroundColor(C.card).borderRadius(D.rMd)
.border({ width: 1, color: C.stroke })
.onClick(() => { promptAction.showToast({ message: item.label }); })
}, (item: QuickItem) => item.label)
}
.width('100%')
本页用 Emoji 字符 作图标(⚡💧📜🔔):零资源依赖。四项功能:缴电费 / 缴水费 / 账单 / 余额提醒。每项 layoutWeight(1) 等分,白卡底+描边独立成卡,点击 toast 提示。

五、WeekChart 柱状趋势图
5.1 数据定义
// 周数据定义
private weekBars: WeekBar[] = [
{ day: '一', value: 45 },
{ day: '二', value: 72 },
{ day: '三', value: 38 },
{ day: '四', value: 90 },
{ day: '五', value: 55 },
{ day: '六', value: 68 },
{ day: '日', value: 42 }
];
private maxBar: number = 90;
maxBar 固定为 90(本周峰值),作为柱高归一化基准。
5.2 柱状图渲染逻辑
// WeekChart — 柱状图主体
Column({ space: 12 }) {
// 标题行
Row() {
Text('本周用电趋势').fontSize(15).fontWeight(FontWeight.Bold).fontColor(C.text)
Blank()
Text('峰值 90 度').fontSize(12).fontColor(C.textDim)
}
.width('100%')
// 柱体区域
Row({ space: 6 }) {
ForEach(this.weekBars, (w: WeekBar) => {
Column({ space: 6 }) {
Column() {
Column()
.width('100%')
.height(Math.floor(w.value / this.maxBar * 80))
.backgroundColor(C.primary)
.borderRadius(4)
}
.width('100%').height(80)
Text(w.day).fontSize(11).fontColor(C.textDim)
}
.layoutWeight(1).alignItems(HorizontalAlign.Center)
}, (w: WeekBar) => w.day)
}
.width('100%')
}
Math.floor(w.value / this.maxBar * 80):把 090 的原始值线性映射到 080vp 柱高。外层 80vp 高的 Column 作固定轨道,内层柱体 width('100%') 铺满、borderRadius(4) 圆角、主色纯色填充。
5.3 设计要点
柱体宽度不写死,由 layoutWeight(1) 七等分自适应屏幕;标题行右侧"峰值 90 度"与 maxBar 数值呼应,数据与展示强一致。

六、SectionTitle 通用标题行
// SectionTitle — 通用标题
@Builder
SectionTitle(title: string, action: string) {
Row() {
Text(title).fontSize(16).fontWeight(FontWeight.Bold).fontColor(C.text)
Blank()
Text(action).fontSize(12).fontColor(C.textDim)
.onClick(() => { promptAction.showToast({ message: action }); })
}
.width('100%')
}
带参 @Builder 复用两次:SectionTitle('近期消费', '全部 >') 与 SectionTitle('消费排行', '详情 >'),右侧动作文字点击 toast。
七、BillList 近期消费列表
// BillList — 消费列表
Column({ space: 10 }) {
ForEach(this.bills, (b: Bill) => {
Column({ space: 10 }) {
Row({ space: 12 }) {
Row() {
Text(b.emoji).fontSize(22)
}
.width(46).height(46)
.backgroundColor(C.primarySoft).borderRadius(D.rSm)
.justifyContent(FlexAlign.Center)
Column({ space: 4 }) {
Row({ space: 8 }) {
Text(b.name).fontSize(14).fontWeight(FontWeight.Medium).fontColor(C.text)
Text(b.tag).fontSize(10).fontColor(C.primary)
.padding({ left: 6, right: 6, top: 2, bottom: 2 })
.backgroundColor(C.primarySoft).borderRadius(4)
}
.width('100%')
Text(b.time).fontSize(11).fontColor(C.textDim)
Row({ space: 2 }) {
ForEach([1, 2, 3, 4, 5], (n: number) => {
Text(n <= b.rating ? '★' : '☆').fontSize(12).fontColor(n <= b.rating ? C.warn : C.textDim)
}, (n: number) => n.toString())
}
.width('100%')
}
.alignItems(HorizontalAlign.Start).layoutWeight(1)
Column({ space: 4 }) {
Text(b.amount).fontSize(15).fontColor(C.danger).fontWeight(FontWeight.Bold)
Text(b.progress === 100 ? '已完成' : '处理中').fontSize(10).fontColor(C.textDim)
}
.alignItems(HorizontalAlign.End)
}
.width('100%')
Progress({ value: b.progress, total: 100 }).color(C.primary).width('100%')
}
.width('100%')
.padding(14)
.backgroundColor(C.card).borderRadius(D.rMd)
.border({ width: 1, color: C.stroke })
.onClick(() => { promptAction.showToast({ message: b.name }); })
}, (b: Bill) => b.id.toString())
}
.width('100%')
每条记录含:Emoji 图标(46vp 圆角方块浅青底)、名称+标签徽章(已缴费/自动扣)、时间、五星评分(ForEach([1,2,3,4,5]) 逐位判断实心★/空心☆)、金额(danger 红)与"已完成/处理中"状态。进度条直接用系统 Progress 组件——value/total 驱动、主色填充,无需自绘轨道。

八、RankList 消费排行榜
// RankList — 排行榜
Column({ space: 10 }) {
ForEach(this.ranks, (r: RankItem) => {
Row({ space: 12 }) {
Text(r.rank.toString()).fontSize(16).fontWeight(FontWeight.Bold)
.fontColor(r.rank === 1 ? C.warn : C.textDim).width(24)
Text(r.emoji).fontSize(20)
Text(r.name).fontSize(14).fontColor(C.text).layoutWeight(1)
Text(r.value).fontSize(14).fontColor(C.danger).fontWeight(FontWeight.Medium)
}
.width('100%')
.padding({ left: 14, right: 14, top: 10, bottom: 10 })
.backgroundColor(C.card).borderRadius(D.rSm)
.border({ width: 1, color: C.stroke })
.onClick(() => { promptAction.showToast({ message: r.name }); })
}, (r: RankItem) => r.id.toString())
}
.width('100%')
排行榜位于首页底部:第 1 名序号用 C.warn 橙色突出,其余灰色;金额统一 danger 红。三项数据:电费支出 ¥320 / 水费支出 ¥158 / 燃气费 ¥0。
九、本章小结
| 组件 | 核心技术 | 学习价值 |
|---|---|---|
| HeroCard | linearGradient 渐变卡 | 渐变头部标准模式 |
| QuickAction | Emoji 图标 + layoutWeight 等分 | 零资源快捷入口 |
| WeekChart | Math.floor 归一化柱高 | 数据可视化最佳实践 |
| SectionTitle | 带参 @Builder 复用 | 通用标题行方案 |
| BillList | Progress 组件 + 五星评分 | 列表项内嵌进度指示 |
| RankList | 条件序号配色 | 排行榜视觉层次 |
WeekChart 柱状图 最具学习价值——Math.floor(value / maxBar * 80) 一行完成数据到高度的归一化映射,配合 layoutWeight 等分实现纯声明式图表。
更多推荐





所有评论(0)