HarmonyOS 应用 · 班费管理成员页深度解析:paid 布尔位与三色缴费统计
HarmonyOS 应用 · 班费管理成员页深度解析:paid 布尔位与三色缴费统计
项目编号:47-class-fund 技术栈:HarmonyOS ArkTS · ArkUI 声明式 UI · Stage 模型 本篇页面:成员页
Func2Tab.ets(101 行) 系列导航:(一)首页 · (二)收支 · (三)成员 · (四)统计

一、Member interface:paid 缴费布尔位
interface Member {
id: number;
emoji: string;
name: string;
paid: boolean;
}
四个字段:id 是 key 锚点,emoji 是头像图标,name 是姓名,paid 是"是否已缴费"的布尔位。paid 是成员页的核心状态字段——驱动缴费标签的文案/颜色/底色三态变化。
@State tab: number = 0;
private tabs: string[] = ['全部', '已缴费', '未缴费'];
private members: Member[] = [
{ id: 1, emoji: '🧑', name: '李强', paid: true },
{ id: 2, emoji: '👩', name: '王梅', paid: true },
{ id: 3, emoji: '👨', name: '陈浩', paid: false },
{ id: 4, emoji: '👩', name: '张娜', paid: true },
{ id: 5, emoji: '🧑', name: '刘洋', paid: false },
{ id: 6, emoji: '👨', name: '赵磊', paid: true }
];
@State tab 是分段筛选状态(0=全部/1=已缴费/2=未缴费)。六位成员:4 人已缴费 + 2 人未缴费。tabs 数组定义三个分段标签。
二、页面骨架:Header + SummaryCard + MemberList
build() {
Column() {
this.Header()
Scroll() {
Column({ space: 14 }) {
this.SummaryCard()
this.MemberList()
}
.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 内两块:SummaryCard(三色统计)+ MemberList(成员列表)。Header 包含标题和分段筛选 Tab——分段筛选随 Header 固定,滚动时不消失。
三、Header:标题 + tab 分段筛选
@Builder
Header() {
Column() {
Row() { Text('成员缴费').fontSize(20).fontWeight(FontWeight.Bold).fontColor(C.text) }
.width('100%').height(this.safeTop + 52).padding({ top: this.safeTop, left: D.pad, right: D.pad })
.alignItems(VerticalAlign.Bottom)
Row() {
ForEach(this.tabs, (t: string, idx: number) => {
Text(t).fontSize(14)
.fontColor(this.tab === idx ? C.primary : C.textSub)
.fontWeight(this.tab === idx ? FontWeight.Bold : FontWeight.Normal)
.layoutWeight(1).textAlign(TextAlign.Center).padding({ bottom: 12 })
.onClick(() => { this.tab = idx; })
}, (t: string) => t)
}.width('100%')
}.width('100%').backgroundColor(C.card)
}
双行 Header——标题行 + 分段筛选行:
- 标题行:20 号加粗"成员缴费",
safeTop + 52避让状态栏 - 筛选行:
ForEach遍历tabs,三段等分。tab === idx驱动字色与字重双态——选中绿色加粗、未选灰色常规。无底色区分,仅靠字色/字重表达选中态——比胶囊式分段更轻量
onClick 设 this.tab = idx 触发 @State 刷新,筛选成员列表。
四、SummaryCard:三色缴费统计
@Builder
SummaryCard() {
Row() {
Column({ space: 4 }) {
Text('40').fontSize(22).fontWeight(FontWeight.Bold).fontColor(C.primary)
Text('应缴人数').fontSize(11).fontColor(C.textDim)
}.layoutWeight(1)
Column({ space: 4 }) {
Text('36').fontSize(22).fontWeight(FontWeight.Bold).fontColor(C.ok)
Text('已缴费').fontSize(11).fontColor(C.textDim)
}.layoutWeight(1)
Column({ space: 4 }) {
Text('4').fontSize(22).fontWeight(FontWeight.Bold).fontColor(C.danger)
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 })
}
三色统计——应缴/已缴/未缴各用不同颜色:
// 应缴 40:主色绿
Text('40').fontColor(C.primary) // #00A862
// 已缴 36:成功绿
Text('36').fontColor(C.ok) // #2BB673
// 未缴 4:危险红
Text('4').fontColor(C.danger) // #FF5A6E
三列 layoutWeight(1) 等分,22 号加粗数字 + 11 号灰色标签。红色"4"突出未缴人数——催缴的核心目标。
五、MemberList:paid 驱动的双态缴费标签
@Builder
MemberList() {
Column({ space: 10 }) {
ForEach(this.members, (m: Member) => {
Row({ space: 12 }) {
Row() { Text(m.emoji).fontSize(22) }
.width(42).height(42).backgroundColor(C.cardSoft).borderRadius(21).justifyContent(FlexAlign.Center)
Text(m.name).fontSize(14).fontColor(C.text).layoutWeight(1)
Text(m.paid ? '已缴费' : '待缴费').fontSize(12)
.fontColor(m.paid ? C.ok : C.danger)
.padding({ left: 10, right: 10, top: 5, bottom: 5 })
.backgroundColor(m.paid ? '#E8F7EE' : '#FDEAEA').borderRadius(D.rSm)
.onClick(() => { promptAction.showToast({ message: m.paid ? '已缴费' : '催缴 ' + m.name }); })
}
.width('100%').padding(12).backgroundColor(C.card).borderRadius(D.rMd).border({ width: 1, color: C.stroke })
}, (m: Member) => m.id.toString())
}.width('100%')
}
paid 驱动三元变化——文案、字色、底色三路分支:
// 文案:已缴费 vs 待缴费
Text(m.paid ? '已缴费' : '待缴费')
// 字色:绿色 vs 红色
.fontColor(m.paid ? C.ok : C.danger)
// 底色:浅绿 vs 浅红
.backgroundColor(m.paid ? '#E8F7EE' : '#FDEAEA')
paid === true 时标签"已缴费"绿字浅绿底(C.ok + #E8F7EE),paid === false 时标签"待缴费"红字浅红底(C.danger + #FDEAEA)。#E8F7EE 和 #FDEAEA 是绿/红的浅化版,做标签底色不抢眼。
标签独立 onClick:已缴费点击弹"已缴费"(无操作),待缴费点击弹"催缴 + 姓名"——催缴入口仅对未缴费成员开放。



六、tab 筛选的实现路径
Demo 中 tab 已绑定 onClick,但 MemberList 仍遍历完整 this.members——筛选未接入。实现筛选需用过滤函数:
private getFiltered(): Member[] {
if (this.tab === 0) {
return this.members;
}
if (this.tab === 1) {
return this.members.filter((m: Member) => m.paid);
}
return this.members.filter((m: Member) => !m.paid);
}
tab === 0 全部、tab === 1 已缴费(paid === true)、tab === 2 未缴费(paid === false)。ForEach 遍历 getFiltered() 而非 this.members——点击分段即时过滤列表。
七、可演进方向
| 方向 | 现状 | 改法 | 收益 |
|---|---|---|---|
| 分段筛选 | tab 未接入 | filter + getFiltered() | 列表按缴费状态过滤 |
| 催缴通知 | Toast 模拟 | 推送通知 + 记录催缴次数 | 真实催缴触达 |
| 缴费标记 | 静态 paid | 手动标记缴费 + 时间记录 | 线下缴费可标记 |
| 数据源 | 静态 6 人 | 服务端拉取全班成员 | 完整成员名单 |
| 统计联动 | 写死 40/36/4 | 从 members 动态统计 | 统计随数据更新 |
成员页是班费管理的缴费跟踪——SummaryCard 的三色统计量化缴费进度、MemberList 的 paid 驱动双态标签区分已缴/待缴。从分段筛选的轻量选中态到催缴入口的条件开放,每一层都有明确的职责边界。

更多推荐

所有评论(0)