HarmonyOS 应用 · 班费管理统计页深度解析:手写柱状图与 Stack 占比条

项目编号:47-class-fund 技术栈:HarmonyOS ArkTS · ArkUI 声明式 UI · Stage 模型 本篇页面:统计页 ProfileTab.ets(116 行) 系列导航:(一)首页 · (二)收支 · (三)成员 · (四)统计

47 统计页首屏


一、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) 让"金额→柱→月份"从下往上排。

47 统计页柱状图


六、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) 让名称撑满左侧。

47 统计页占比图

47 统计页完整视图


七、跨页数据自洽:统计页与首页的联动

  • ¥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 双层占比条的叠加技巧,每一层都有明确的职责边界。

系列导航:(一)首页 · (二)收支 · (三)成员 · (四)统计

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐