HarmonyOS 应用 · 班费管理统计页深度解析:手写柱状图与 Stack 占比条
HarmonyOS 应用 · 班费管理统计页深度解析:手写柱状图与 Stack 占比条
项目编号:47-class-fund 技术栈:HarmonyOS ArkTS · ArkUI 声明式 UI · Stage 模型 本篇页面:统计页
ProfileTab.ets(116 行) 系列导航:(一)首页 · (二)收支 · (三)成员 · (四)统计

一、CatStat interface 与月度数据
interface CatStat {
id: number;
name: string;
amount: number;
percent: number;
color: string;
}
五个字段:id 是 key 锚点,name 是分类名,amount 是金额,percent 是占比百分比,color 是占比条颜色。color 字段让每个分类用不同颜色——比统一色更易区分。
private months: string[] = ['9月', '10月', '11月', '12月'];
private monthValues: number[] = [480, 320, 650, 200];
private cats: CatStat[] = [
{ id: 1, name: '活动采购', amount: 480, percent: 33, color: C.primary },
{ id: 2, name: '团建费用', amount: 650, percent: 45, color: C.accent },
{ id: 3, name: '物资费用', amount: 320, percent: 22, color: C.warn }
];
月度数据:4 个月支出值 [480, 320, 650, 200]。分类占比:活动采购 33%(primary 绿)、团建费用 45%(accent 浅绿)、物资费用 22%(warn 橙)——三个颜色区分三类支出。
二、maxVal 方法:动态求最大值
private maxVal(): number {
let m = 1;
this.monthValues.forEach((v: number) => { if (v > m) { m = v; } });
let m = 1; return m;
}
maxVal() 遍历 monthValues 求最大值(650)。初始值 m = 1 而非 0——避免除零(柱状图高度计算 value * 120 / maxVal,maxVal 为 0 会除零)。柱状图最高柱对应最大值,其他柱按比例缩放。
三、页面骨架:OverviewCard + BarChart + CatChart
build() {
Column() {
this.Header()
Scroll() {
Column({ space: 14 }) {
this.OverviewCard()
this.BarChart()
this.CatChart()
}
.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)
}
Header 在 Scroll 外固定,Scroll 内三块:OverviewCard(三色概览)+ BarChart(月度柱状图)+ CatChart(分类占比条)。三块都是纯 ArkUI 组件手写——不引任何图表库。
四、OverviewCard:三色收支概览
@Builder
OverviewCard() {
Row() {
Column({ space: 4 }) {
Text('¥2000').fontSize(20).fontWeight(FontWeight.Bold).fontColor(C.ok)
Text('总收入').fontSize(11).fontColor(C.textDim)
}.layoutWeight(1)
Column({ space: 4 }) {
Text('¥1450').fontSize(20).fontWeight(FontWeight.Bold).fontColor(C.danger)
Text('总支出').fontSize(11).fontColor(C.textDim)
}.layoutWeight(1)
Column({ space: 4 }) {
Text('¥550').fontSize(20).fontWeight(FontWeight.Bold).fontColor(C.primary)
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 })
}
三色统计——收入绿(C.ok)、支出红(C.danger)、结余主色绿(C.primary)。与首页 BalanceCard 的数据一致(2000/1450/550),但展示形式不同——首页用渐变大卡,统计页用三列平铺,信息密度更高。
五、BarChart:ForEach 手写柱状图
@Builder
BarChart() {
Column({ space: 14 }) {
Text('月度支出趋势').fontSize(14).fontWeight(FontWeight.Bold).fontColor(C.text).width('100%')
Row() {
ForEach(this.months, (m: string, idx: number) => {
Column({ space: 8 }) {
Text('¥' + this.monthValues[idx]).fontSize(10).fontColor(C.textDim)
Column()
.width(28).height(this.monthValues[idx] * 120 / this.maxVal())
.backgroundColor(C.primary).borderRadius({ topLeft: 6, topRight: 6 })
Text(m).fontSize(11).fontColor(C.textSub)
}.layoutWeight(1).justifyContent(FlexAlign.End)
}, (m: string) => m)
}.width('100%').height(170).alignItems(VerticalAlign.Bottom)
}
.width('100%').padding(16).backgroundColor(C.card).borderRadius(D.rLg).border({ width: 1, color: C.stroke })
}
纯 ArkUI 柱状图——无图表库,用 Column 高度模拟柱体:
// 柱高 = 当前值 * 120 / 最大值
.height(this.monthValues[idx] * 120 / this.maxVal())
120 是柱高上限(px),maxVal() 是最大值(650)。最大柱高 120px,其他柱按比例缩放:480→88px、320→59px、650→120px、200→37px。
柱体样式:width(28) 固定宽 + backgroundColor(C.primary) 绿色 + borderRadius({ topLeft: 6, topRight: 6 }) 仅顶部圆角——柱状图的经典形态。外层 Row 的 alignItems(VerticalAlign.Bottom) 让所有柱底部对齐,height(170) 给柱状图区域固定高度。每个柱的 Column 用 justifyContent(FlexAlign.End) 让"金额→柱→月份"从下往上排。

六、CatChart:Stack 双层占比条
@Builder
CatChart() {
Column({ space: 14 }) {
Text('支出分类占比').fontSize(14).fontWeight(FontWeight.Bold).fontColor(C.text).width('100%')
ForEach(this.cats, (c: CatStat) => {
Column({ space: 6 }) {
Row() {
Text(c.name).fontSize(13).fontColor(C.text).layoutWeight(1)
Text('¥' + c.amount + ' · ' + c.percent + '%').fontSize(12).fontColor(C.textDim)
}.width('100%')
Stack({ alignContent: Alignment.Start }) {
Row().width('100%').height(8).backgroundColor(C.cardSoft).borderRadius(4)
Row().width(c.percent + '%').height(8).backgroundColor(c.color).borderRadius(4)
}.width('100%')
}.width('100%')
}, (c: CatStat) => c.id.toString())
}
.width('100%').padding(16).backgroundColor(C.card).borderRadius(D.rLg).border({ width: 1, color: C.stroke })
}
Stack 双层占比条——底灰条 + 彩色条叠加:
Stack({ alignContent: Alignment.Start }) {
// 底层:100% 宽浅灰条
Row().width('100%').height(8).backgroundColor(C.cardSoft).borderRadius(4)
// 上层:percent% 宽彩色条
Row().width(c.percent + '%').height(8).backgroundColor(c.color).borderRadius(4)
}
Alignment.Start 让彩色条从左对齐。底层 100% 灰条是"总轨道",上层 percent% 彩色条是"已填充"——进度条的变体。c.color 让每个分类用不同颜色(primary 绿 / accent 浅绿 / warn 橙),比统一色更易区分。
标签行:左侧分类名 + 右侧"¥金额 · 百分比%",layoutWeight(1) 让名称撑满左侧。


七、跨页数据自洽:统计页与首页的联动
- ¥2000 总收入 / ¥1450 总支出 / ¥550 结余 ↔ 首页 BalanceCard 同款数据——两页同源
- 月度支出 [480, 320, 650, 200] ↔ 首页 RecordList 的支出记录(中秋 480 / 运动会 320 / 团建 650)+ 12 月 200(首页未展示)
- 活动采购 480 / 团建费用 650 / 物资费用 320 ↔ 首页 RecordList 的支出标题——分类金额与记录金额一致
- 45% 团建占比最高 ↔ 首页团建 650 是最大支出——占比与金额排序一致
八、可演进方向
| 方向 | 现状 | 改法 | 收益 |
|---|---|---|---|
| 数据来源 | 静态数组 | 从 records 动态聚合 | 统计随记录更新 |
| 图表交互 | 无 onClick | 点击柱/条查看明细 | 下钻分析 |
| 时间范围 | 写死 4 个月 | 月份选择器 + 动态切换 | 按月查看趋势 |
| 占比计算 | 写死 percent | 从 amount / 总支出计算 | 占比自动准确 |
| 图表类型 | 柱状+占比条 | 增加环形图/折线图 | 多维度可视化 |
统计页是班费管理的分析中枢——OverviewCard 三色概览、BarChart 手写柱状图、CatChart Stack 占比条。不引任何图表库,纯 ArkUI 组件实现数据可视化。从 maxVal() 的动态求最大值到 Stack 双层占比条的叠加技巧,每一层都有明确的职责边界。

更多推荐

所有评论(0)