HarmonyOS 应用 · 班费管理收支页深度解析:四状态表单与 Flex 分类胶囊

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

47 收支页首屏


一、四 @State 驱动的表单状态机

  @State isIncome: boolean = false;
  @State amount: string = '';
  @State remark: string = '';
  @State activeCat: number = 0;

  private incomeCats: string[] = ['班费收缴', '活动结余', '其他收入'];
  private expenseCats: string[] = ['活动采购', '物资费用', '团建费用', '奖品支出', '其他支出'];

  private getCats(): string[] {
    return this.isIncome ? this.incomeCats : this.expenseCats;
  }

收支页有四个 @State:isIncome(支出/收入切换)、amount(金额输入)、remark(备注输入)、activeCat(分类选中索引)。getCats() 是派生方法——根据 isIncome 返回对应分类数组,支出 5 个、收入 3 个。isIncome 变化时 getCats() 返回不同数组,分类列表即时刷新。


二、页面骨架:TypeSwitch + AmountCard + CatCard + RemarkCard + Button

  build() {
    Column() {
      this.Header()
      Scroll() {
        Column({ space: 16 }) {
          this.TypeSwitch()
          this.AmountCard()
          this.CatCard()
          this.RemarkCard()
          Button('保存记录').fontSize(15).fontColor('#FFFFFF').backgroundColor(C.primary)
            .width('100%').height(48).borderRadius(D.rMd)
            .onClick(() => { promptAction.showToast({ message: this.amount.length > 0 ? '记录已保存' : '请输入金额' }); })
        }
        .width('100%')
        .padding({ left: D.pad, right: D.pad, top: 16, bottom: D.pad + this.safeBottom + 20 })
      }
      .layoutWeight(1).scrollBar(BarState.Off).align(Alignment.Top)
    }
    .width('100%').height('100%').backgroundColor(C.bg)
  }

Header 在 Scroll 外固定,Scroll 内五块:TypeSwitch(类型切换)+ AmountCard(金额)+ CatCard(分类)+ RemarkCard(备注)+ Button(保存)。Button 直接内联在 build 中,未抽 Builder——因为它是单行组件,无需复用。保存按钮的 onClick 做了空值校验:amount.length > 0 才提示"记录已保存",否则提示"请输入金额"。


三、TypeSwitch:isIncome 驱动的红绿双选

  @Builder
  TypeSwitch() {
    Row() {
      Text('支出').fontSize(14)
        .fontColor(!this.isIncome ? '#FFFFFF' : C.textSub)
        .textAlign(TextAlign.Center).layoutWeight(1).padding({ top: 11, bottom: 11 })
        .backgroundColor(!this.isIncome ? C.danger : Color.Transparent).borderRadius(D.rSm)
        .onClick(() => { this.isIncome = false; this.activeCat = 0; })
      Text('收入').fontSize(14)
        .fontColor(this.isIncome ? '#FFFFFF' : C.textSub)
        .textAlign(TextAlign.Center).layoutWeight(1).padding({ top: 11, bottom: 11 })
        .backgroundColor(this.isIncome ? C.ok : Color.Transparent).borderRadius(D.rSm)
        .onClick(() => { this.isIncome = true; this.activeCat = 0; })
    }
    .width('100%').padding(4).backgroundColor(C.card).borderRadius(D.rMd).border({ width: 1, color: C.stroke })
  }

isIncome 驱动红绿双选——支出用 C.danger 红色、收入用 C.ok 绿色:

// 支出选中:红底白字
.backgroundColor(!this.isIncome ? C.danger : Color.Transparent)

// 收入选中:绿底白字
.backgroundColor(this.isIncome ? C.ok : Color.Transparent)

点击支出设 isIncome = false + activeCat = 0,点击收入设 isIncome = true + activeCat = 0——切换类型时重置分类选中索引,避免越界(支出 5 项 → 收入 3 项,索引 4 会越界)。

47 收支页收入切换


四、AmountCard:¥ 前缀 + TextInput 数字输入

  @Builder
  AmountCard() {
    Row({ space: 6 }) {
      Text('¥').fontSize(28).fontWeight(FontWeight.Bold).fontColor(C.text)
      TextInput({ placeholder: '0.00' }).type(InputType.Number)
        .backgroundColor(Color.Transparent).borderRadius(0).height(56)
        .fontSize(28).layoutWeight(1)
        .onChange((v: string) => { this.amount = v; })
    }
    .width('100%').padding({ left: 16, right: 16 }).backgroundColor(C.card).borderRadius(D.rLg).border({ width: 1, color: C.stroke })
  }

28 号 ¥ 前缀 + 28 号 TextInput,type(InputType.Number) 限制数字键盘——金额输入只允许数字。backgroundColor(Color.Transparent) 让输入框透明融入卡片。onChange 写入 this.amount 触发 @State 刷新。


五、CatCard:Flex wrap 自动换行分类胶囊

  @Builder
  CatCard() {
    Column({ space: 10 }) {
      Text('分类').fontSize(14).fontWeight(FontWeight.Bold).fontColor(C.text).width('100%')
      Flex({ wrap: FlexWrap.Wrap }) {
        ForEach(this.getCats(), (c: string, idx: number) => {
          Text(c).fontSize(12)
            .fontColor(this.activeCat === idx ? '#FFFFFF' : C.textSub)
            .padding({ left: 14, right: 14, top: 7, bottom: 7 })
            .backgroundColor(this.activeCat === idx ? C.primary : C.cardSoft).borderRadius(14)
            .margin({ right: 10, bottom: 10 })
            .onClick(() => { this.activeCat = idx; })
        }, (c: string, idx: number) => c + idx)
      }.width('100%')
    }
    .width('100%').padding(16).backgroundColor(C.card).borderRadius(D.rLg).border({ width: 1, color: C.stroke })
  }

Flex({ wrap: FlexWrap.Wrap }) 是本页特色——自动换行的弹性布局。支出有 5 个分类,一行放不下,FlexWrap.Wrap 让胶囊自动换行。每个胶囊 borderRadius(14) 全圆角 + margin({ right: 10, bottom: 10 }) 间距。

activeCat 驱动选中态——字色与底色两路分支:

// 选中:绿底白字
.backgroundColor(this.activeCat === idx ? C.primary : C.cardSoft)

// 未选:浅灰底灰字
.fontColor(this.activeCat === idx ? '#FFFFFF' : C.textSub)

getCats() 根据 isIncome 返回不同数组——切换类型时分类列表整体替换。


六、RemarkCard:备注输入

  @Builder
  RemarkCard() {
    Column({ space: 10 }) {
      Text('备注').fontSize(14).fontWeight(FontWeight.Bold).fontColor(C.text).width('100%')
      TextInput({ placeholder: '添加备注说明' })
        .backgroundColor(C.cardSoft).borderRadius(D.rSm).height(44)
        .onChange((v: string) => { this.remark = v; })
    }
    .width('100%').padding(16).backgroundColor(C.card).borderRadius(D.rLg).border({ width: 1, color: C.stroke })
  }

14 号标题 + TextInput 备注。与 AmountCard 不同,备注输入框用 C.cardSoft 浅灰底 + borderRadius(D.rSm) 小圆角——视觉权重低于金额输入,符合"金额是主信息、备注是辅助"的层级关系。

47 收支页保存校验

47 收支页完整视图


七、表单状态联动链路

四个 @State 形成联动链路:

TypeSwitch 点击 → isIncome 翻转 → getCats() 返回新数组 → CatCard 胶囊列表刷新
                                   → activeCat 重置为 0    → 选中态回到第一个
AmountCard 输入 → amount 更新 → 保存按钮校验 amount.length > 0
CatCard 点击   → activeCat 更新 → 选中胶囊高亮转移
RemarkCard 输入 → remark 更新 → 随记录保存

isIncome 是链路起点——它的变化同时影响 TypeSwitch 的红绿选中态、CatCard 的分类列表、activeCat 的重置,一次点击触发三处刷新。


八、可演进方向

方向现状改法收益
保存写入Toast 模拟写入 records 数组 + 余额重算记录真实保存
金额校验仅非空正数 + 两位小数 + 上限金额格式合法
分类管理写死数组服务端配置 + 自定义分类分类可扩展
日期选择无DatePicker 选日期记录日期可调
表单重置无保存后清空 amount/remark/activeCat连续录入

收支页是班费管理的录入入口——四 @State 驱动的表单状态机、isIncome 红绿双选、Flex wrap 自动换行分类胶囊。从 TypeSwitch 的类型联动到保存按钮的空值校验,每一层都有明确的职责边界。

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

Logo

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

更多推荐