鸿蒙 ArkUI 分页账单明细表:从零到一构建企业级财务数据展示组件

鸿蒙 ArkUI 分页账单明细表:从零到一构建企业级财务数据展示组件
一、项目概述
1.1 背景与意义
在移动端财务管理、企业后台、电商订单管理等场景中,数据表格是最基础也是最重要的信息载体之一。当数据量级从几十条增长到数千甚至数万条时,如何高效、流畅地展示数据,同时保持良好的用户体验,成为前端开发者必须面对的核心挑战。
本项目基于 HarmonyOS ArkUI(ArkTS 语言)实现了一个分页账单明细表组件,旨在解决大账单数据的分页展示问题。项目运行于 API 24(HarmonyOS 6.1.1)平台,采用 Stage 模型架构,严格遵循 ArkTS 的类型安全规范。
1.2 技术栈
| 技术 | 版本 | 说明 |
|---|---|---|
| HarmonyOS | 6.1.1 (API 24) | 目标操作系统 |
| ArkTS | 3.0+ | 声明式 UI 开发语言 |
| ArkUI | API 24 | 组件库与布局引擎 |
| Stage 模型 | — | 应用架构模型 |
| DevEco Studio | 6.26.1 | IDE 开发工具 |
| Hvigor | 6.26.1 | 构建工具 |
1.3 核心功能清单
- 分页展示:87 条模拟账单数据,每页 10 条,共 9 页
- 分页控件:上一页 / 页码按钮组(含智能省略号)/ 下一页
- 表格渲染:表头固定 + 数据行可滚动 + 交替行背景色
- 统计摘要:顶部显示总记录数、当前数据范围
- 金额着色:收入绿色(+)、支出红色(-)
- 状态着色:已完成(绿)、处理中(橙)、已退款(红)
- 数据统计卡片:总记录数、总收入、总支出
- 零第三方依赖:纯 ArkUI 原生实现
二、项目结构设计
2.1 目录组织
entry/src/main/ets/
├── model/
│ └── BillItem.ets # 数据模型层
├── components/
│ └── PaginatedBillTable.ets # UI 组件层(核心)
└── pages/
└── Index.ets # 页面入口 + 模拟数据
项目的架构清晰地划分为三层:
- Model 层(
BillItem.ets):定义账单数据的实体结构,包含字段、类型和构造函数。 - Component 层(
PaginatedBillTable.ets):封装表格展示和分页逻辑,是一个可复用的@Component,通过@Prop接收外部数据,通过@State维护内部分页状态。 - Page 层(
Index.ets):作为应用的入口页面,负责生成模拟数据、布局标题栏和统计卡片,并组合使用PaginatedBillTable组件。
这种分层设计的好处是:职责单一、高内聚低耦合。如果未来需要将分页表格复用到其他页面(如订单页、流水页),只需导入 PaginatedBillTable 组件即可。
2.2 组件树
Index (Page)
├── TitleBar (Row + Text)
├── SummaryCards (Row)
│ ├── SummaryCard (总记录数)
│ ├── SummaryCard (总收入)
│ └── SummaryCard (总支出)
└── PaginatedBillTable (Component)
├── StatsRow (统计信息栏)
├── TableArea (表格区域)
│ ├── HeaderRow (表头:6列)
│ ├── Scroll (可滚动区域)
│ │ ├── BillRow × N (数据行)
│ │ └── ...
│ └── ──
└── PaginationBar (分页控件)
├── PrevButton (上一页)
├── PageButtons (页码按钮组)
└── NextButton (下一页)
三、数据模型深度解析
3.1 BillItem 实体
export class BillItem {
id: string; // 交易编号,如 "B2026001"
date: string; // 交易日期,如 "2026-01-01"
description: string; // 交易描述,如 "公司食堂午餐"
category: string; // 交易分类,如 "餐饮"
amount: number; // 交易金额,如 35.50
isIncome: boolean; // 是否为收入
status: string; // 交易状态,如 "已完成"
}
设计这个模型时遵循了以下原则:
- 语义明确:每个字段名都直观表达其含义,
isIncome用布尔值而非字符串来区分收支,避免了 “income”/“expense” 字符串比较带来的性能损耗和拼写错误风险。 - 类型安全:所有字段都显式标注类型,充分利用 ArkTS 的静态类型检查。
- 金额处理:
amount使用number类型,在显示时通过toFixed(2)格式化为两位小数,避免浮点数精度问题暴露给用户。 - 日期处理:
date使用字符串类型而非 Date 对象,简化序列化和比较操作,显示时直接渲染。
3.2 参数接口设计
对于 ArkTS 中 @Builder 方法的参数传递,本项目定义了两个命名接口:
BillRowParams(组件内部使用):
export interface BillRowParams {
item: BillItem;
rowIndex: number;
}
SummaryCardParams(页面级使用):
interface SummaryCardParams {
label: string;
value: string;
unit: string;
color: string;
}
为什么需要接口而非内联类型?在 ArkTS 的严格模式(arkts-no-obj-literals-as-types 规则)下,@Builder 方法的参数类型必须是一个命名的 interface 或 class,不能是内联对象类型如 { label: string; value: string }。这一设计强制开发者以显式类型契约的方式传递参数,提升了代码的可读性和可维护性。
四、分页表格组件核心实现
4.1 组件声明与属性
@Component
export struct PaginatedBillTable {
@Prop data: BillItem[] = [];
@Prop pageSize: number = 10;
@State currentPage: number = 1;
// ...
}
@Prop data:父组件传入的账单数据数组。使用@Prop而非@State表明数据所有权属于父组件,子组件只读展示。@Prop pageSize:每页显示的记录数,默认 10 条,父组件可按需调整。@State currentPage:当前页码,从 1 开始。使用@State表明这是组件内部状态,当页码变化时,ArkUI 会自动触发 UI 重新渲染。
4.2 计算属性(Getter)
组件中定义了一系列计算属性,用于派生展示所需的数据:
get totalPages(): number {
return Math.ceil(this.data.length / this.pageSize);
}
get currentPageData(): BillItem[] {
const start = (this.currentPage - 1) * this.pageSize;
const end = Math.min(start + this.pageSize, this.data.length);
return this.data.slice(start, end);
}
get rangeStart(): number {
return (this.currentPage - 1) * this.pageSize + 1;
}
get rangeEnd(): number {
return Math.min(this.currentPage * this.pageSize, this.data.length);
}
这些计算属性的设计体现了 “数据驱动视图” 的思想:currentPage 是状态,所有展示数据都是由 currentPage 和 data 派生而来。当用户点击页码按钮改变 currentPage 时,currentPageData、rangeStart、rangeEnd 会自动重新计算,驱动表格内容更新。
4.3 分页页码生成算法
getPageNumbers(): number[] {
const total = this.totalPages;
const current = this.currentPage;
const pages: number[] = [];
if (total <= 7) {
for (let i = 1; i <= total; i++) pages.push(i);
} else {
pages.push(1); // 始终显示第一页
if (current > 3) pages.push(-1); // 左侧省略号
// 当前页 + 前后各一页
const start = Math.max(2, current - 1);
const end = Math.min(total - 1, current + 1);
for (let i = start; i <= end; i++) pages.push(i);
if (current < total - 2) pages.push(-1); // 右侧省略号
pages.push(total); // 始终显示最后一页
}
return pages;
}
这个算法的设计要点:
- 当总页数 ≤ 7 时:显示全部页码,因为按钮数量在可接受范围内。
- 当总页数 > 7 时:采用"首尾固定 + 中间折叠"策略:
- 始终显示第 1 页和最后 1 页
- 始终显示当前页及其前后各 1 页
- 用
-1作为省略号占位符,在渲染时判断page === -1则渲染...
这种策略保证了无论数据有多少页,页码按钮组最多只显示 7 个按钮(首 + 尾 + 省略号 ×2 + 当前页周边 ×3),在可用性和空间占用之间取得了良好平衡。
4.4 表格行渲染与交替背景色
@Builder
BillRow(params: BillRowParams) {
Row() {
// ... 各列 Text 组件
}
.backgroundColor(params.rowIndex % 2 === 0 ? '#FAFAFA' : '#FFFFFF')
.border({ width: { bottom: 1 }, color: { bottom: '#F0F0F0' } })
}
- 斑马纹效果:通过
rowIndex % 2 === 0判断奇偶行,偶数列(#FAFAFA浅灰)和奇数列(#FFFFFF纯白)交替,提升长表格的可读性。 - 下边框:使用
border({ width: { bottom: 1 }, color: { bottom: '...' } })的EdgeWidths语法,仅设置底部边框,形成表格线的视觉效果。
4.5 Scroll 可滚动区域
Scroll() → Column() → ForEach → BillRow × N
数据行区域包裹在 Scroll 容器中,当当前页数据行数较多超出可视区域时,用户可以通过滚动查看所有行。Scroll 组件通过 layoutWeight(1) 占据剩余空间,并使用 scrollBar(BarState.Auto) 自动显示/隐藏滚动条。
这里有一个值得注意的设计选择:为什么不在整个表格上滚动,而只让数据行区域滚动?因为表头需要固定不动,确保用户在滚动查看数据时始终能看到列名,这是优秀表格设计的通行做法。
五、分页控件深度剖析
5.1 控件结构
分页控件位于表格底部,其结构为:
Row (FlexAlign.Center)
├── Button "‹ 上一页" ← margin({ right: 8 })
├── Row (灵活宽度)
│ ├── Text "1" / Button "1" ← margin({ left: 3, right: 3 })
│ ├── Text "..." ← margin({ left: 3, right: 3 })
│ ├── Button "3" (高亮) ← margin({ left: 3, right: 3 })
│ └── ...
└── Button "下一页 ›" ← margin({ left: 8 })
5.2 控件状态管理
- 禁用状态:当前页为第 1 页时,「上一页」按钮的
enabled属性为false;当前页为最后一页时,「下一页」按钮的enabled属性为false。同时通过fontColor控制文字颜色变淡(#CCCCCC),给用户清晰的可交互暗示。 - 高亮状态:当前页码按钮使用白色文字 + 深蓝背景(#1A3A5C),且边框宽度为 0,其他页码按钮使用深色文字 + 白色背景 + 1px 灰色边框。两者形成鲜明的视觉对比。
5.3 交互事件
// 上一页
.prevPage() → this.currentPage - 1
// 指定页码
.goToPage(page) → 边界检查后赋值 this.currentPage = page
// 下一页
.nextPage() → this.currentPage + 1
所有分页操作最终都归结为修改 @State currentPage。ArkUI 的响应式系统会:
- 检测到
currentPage变化 - 重新计算所有依赖
currentPage的 Getter(currentPageData、rangeStart、rangeEnd等) - 驱动 UI 重新渲染(ForEach 刷新数据行、页码按钮高亮切换、按钮禁用状态更新)
这一过程无需开发者手动操作 DOM 或维护额外状态,充分体现了声明式 UI 框架的优势。
六、页面集成与模拟数据
6.1 Index 页面布局
@Entry
@Component
struct Index {
@State bills: BillItem[] = mockBillsData();
pageSize: number = 10;
build() {
Column() {
TitleBar // 顶部标题栏
SummaryCardsRow // 统计卡片行
PaginatedBillTable // 分页表格(占据剩余空间)
}
}
}
页面采用纵向 Column 布局,从上到下依次排列标题栏、统计卡片和分页表格。PaginatedBillTable 通过 layoutWeight(1) 占据除标题栏和统计卡片外的所有剩余空间。
6.2 统计卡片
@Builder
SummaryCard(params: SummaryCardParams) {
Column() {
Text(params.label) // "总记录数"
Row() {
Text(params.value) // "87"
Text(params.unit) // "条"
}
}
}
三张统计卡片(总记录数、总收入、总支出)并排显示在标题栏下方,收入使用绿色(#1B8A3D)标记,支出使用红色(#C4353D)标记,颜色值与表格中的金额着色保持一致,形成统一的视觉语言。
calcTotalIncome(): number {
let total = 0;
for (let i = 0; i < this.bills.length; i++) {
if (this.bills[i].isIncome) total += this.bills[i].amount;
}
return total;
}
6.3 模拟数据生成策略
mockBillsData() 函数生成 87 条结构完整、内容多样的模拟记录:
- 日期分布:以
2026-01-01为基准,每 3 条记录递增一天,覆盖 29 天的时间跨度 - 描述多样性:30 种真实的交易描述(“公司食堂午餐”“京东购物”"工资发放"等),循环使用
- 分类覆盖:10 种常见消费类别(餐饮/交通/购物/工资/转账/红包/生活缴费/娱乐/医疗/其他)
- 收支比例:每 4 条中约有 1 条为收入(工资/奖金/红包),模拟真实账单的收支分布
- 金额范围:收入 2000~20000 元,支出 5~500 元,贴合真实场景
- 状态分布:90% 已完成、少量处理中和已退款
const isIncome = (i % 4 === 0);
const amount = isIncome
? Math.round((2000 + Math.random() * 18000) * 100) / 100
: Math.round((5 + Math.random() * 500) * 100) / 100;
七、视觉设计理念
7.1 配色方案
| 用途 | 色值 | 说明 |
|---|---|---|
| 标题栏背景 | #0D2137 |
深海军蓝,传递专业感 |
| 表头背景 | #1A3A5C |
中深蓝,与标题栏形成层次 |
| 页面背景 | #F0F2F5 |
浅灰,减少视觉疲劳 |
| 收入金额 | #1B8A3D (Green) |
绿色代表正向收益 |
| 支出金额 | #C4353D (Red) |
红色代表支出消耗 |
| 已完成 | Color.Green |
绿色状态标识 |
| 处理中 | Color.Orange |
橙色表示进行中 |
| 已退款 | Color.Red |
红色表示逆向交易 |
| 当前页码 | #1A3A5C 背景 + 白色文字 |
高亮当前页 |
| 其他页码 | 白色背景 + #333 文字 |
默认状态 |
配色整体采用 “深蓝 + 白” 的主色调,搭配 绿/红 作为数据语义色,风格偏向专业财务软件,适合企业级应用场景。
7.2 阴影与圆角
- 表格区域:
shadow({ radius: 6, color: '#20000000' })轻微阴影提升层次感 - 统计卡片:
shadow({ radius: 4, color: '#15000000' })更柔和的阴影 - 表头:
borderRadius({ topLeft: 6, topRight: 6 })圆角与表格整体borderRadius(6)+clip(true)配合,实现优雅的圆角边框效果 - 按钮:统一使用
borderRadius(6)圆角按钮,保持视觉一致性
7.3 交互反馈
- 页码按钮:悬浮时无状态变化(Native 端默认),但点击时通过
onClick立即切换currentPage,UI 即时响应 - 禁用按钮:颜色变淡(#CCCCCC)且
enabled为 false,阻止点击事件并给予视觉暗示 - 文字溢出:交易描述列设置
maxLines(1)+textOverflow({ overflow: TextOverflow.Ellipsis }),超长文本以省略号截断,保证表格布局稳定
八、ArkTS 开发注意事项与踩坑记录
8.1 对象字面量规则(arkts-no-untyped-obj-literals)
ArkTS 编译器要求所有对象字面量必须对应一个已声明的类或接口。这意味着:
// ❌ 错误:内联对象类型
@Builder BillRow(params: { item: BillItem; rowIndex: number }) { }
// ❌ 错误:无类型匹配的对象字面量
this.BillRow({ item: item, rowIndex: index })
// ✅ 正确:预定义接口
export interface BillRowParams {
item: BillItem;
rowIndex: number;
}
// ✅ 正确:显式类型断言
this.BillRow({ item: item, rowIndex: index } as BillRowParams)
这一规则在最初开发时导致编译失败,错误信息为 arkts-no-obj-literals-as-types 和 arkts-no-untyped-obj-literals。解决方案是:始终为 @Builder 的参数定义命名接口,并在调用处使用 as Type 显式断言。
8.2 Border 的 EdgeWidths 语法
ArkUI 的 border() 方法接受 BorderOptions 对象,其 width 和 color 属性支持 EdgeWidths / EdgeColors 子对象语法:
// 完整边框
.border({ width: 1, color: '#E0E0E0', style: BorderStyle.Solid })
// 仅底部边框
.border({ width: { bottom: 1 }, color: { bottom: '#F0F0F0' } })
初次实现时尝试使用 .border({ bottom: { width: 1, color: '...' } }) 的方式,但这不符合 BorderOptions 类型定义(它没有 bottom 顶级字段),导致编译错误。正确用法是将 bottom 放在 width 和 color 的子对象中。
8.3 Row 的 spacing 属性
在当前的 API 版本中,Row 组件不支持 .space() 方法。如果需要控制子元素间距,有两种替代方案:
- 使用
Space()或Blank()占位组件(适合少量、固定的间距场景) - 使用子元素的
.margin()属性(适合灵活、可变的间距场景)
本项目采用方案二,通过 .margin({ left: 3, right: 3 }) 为每个页码按钮添加间距,通过 .margin({ right: 8 }) 和 .margin({ left: 8 }) 控制上一页/下一页按钮两侧的间距。
8.4 readonly 在 struct 中不可用
在 ArkTS 的 @Component struct 中,readonly 关键字不被支持。如果确实需要不可变属性,有两种方案:
- 不使用
readonly,靠代码约定保证不修改 - 将常量定义在 struct 外部(如文件级常量)
// ❌ 错误
struct Index {
readonly pageSize: number = 10;
}
// ✅ 正确
struct Index {
pageSize: number = 10;
}
8.5 函数声明顺序
在 ArkTS 中,如果一个函数在 struct 的初始化器中被调用(如 @State bills: BillItem[] = mockBillsData()),该函数必须在 struct 定义之前声明。虽然在某些 JavaScript/TypeScript 环境中函数声明会被提升(hoisting),但在 ArkTS 的编译上下文中,将函数定义放在使用位置之前是最稳妥的做法。
// ✅ 正确:函数先定义
function mockBillsData(): BillItem[] { ... }
@Entry
@Component
struct Index {
@State bills: BillItem[] = mockBillsData();
}
8.6 switch 语句的局限性
在 ArkTS 中,某些场景下的 switch 语句可能需要改写为 if/else if 链。虽然 ArkTS 不完全禁用 switch,但为了更好的兼容性和代码可读性,本项目统一使用 if/else if 来实现多分支逻辑。
// 推荐:使用 if/else if 替代 switch
getStatusColor(status: string): Color {
if (status === '已完成') return Color.Green;
if (status === '处理中') return Color.Orange;
if (status === '已退款') return Color.Red;
return Color.Gray;
}
九、性能考量与优化
9.1 数据切片 vs 一次性渲染
本项目采用前端数据切片策略:所有 87 条数据一次性传入组件,组件内部通过 Array.slice() 按当前页截取展示。这种策略适用于千条级以下的数据规模,优点是实现简单、无网络延迟。
对于万条级以上数据,建议改为服务端分页模式:组件仅维护当前页数据,通过回调通知父组件或接口层加载新数据。
9.2 ForEach 的键值优化
ForEach(this.currentPageData,
(item, index) => { this.BillRow(...) },
(item) => item.id // 唯一且稳定的键
)
ForEach 的第三个参数是键值生成函数,用于标识列表中每个项目的唯一性。使用 item.id 作为键值:
- 唯一性:每笔交易的 ID 都不同
- 稳定性:ID 在数据生命周期内不变
- 高效:字符串比较性能优异
正确的键值可以帮助 ArkUI 的 diff 算法在数据变化时最小化 DOM 操作,提升列表更新性能。
9.3 Scroll + layoutWeight 的布局优化
Column (布局容器)
├── Scroll
│ └── Column (数据行)
└── PaginationBar (分页控件)
Scroll 通过 layoutWeight(1) 占据 Column 中的剩余空间,PaginationBar(分页控件)的尺寸由内容决定(高度固定 50px)。这种布局确保:
- 分页控件始终位于表格底部,不会被挤出屏幕
- 数据行区域随可用空间自动伸缩
- 数据行过多时可滚动查看
9.4 条件渲染
在 PaginationBar 的 ForEach 中,通过 if (page === -1) 判断渲染省略号文本还是页码按钮。ArkUI 的编译器会将 if/else 转换为 If 组件,实现条件渲染:
ForEach(this.getPageNumbers(), (page) => {
if (page === -1) {
// 渲染 Text("...")
} else {
// 渲染 Button(页码)
}
})
这种内联条件渲染避免了提取额外 @Builder 方法的冗余,让代码更加紧凑。
十、扩展与自定义指南
10.1 自定义列
如需新增列(如"操作人"“备注”),需要同时修改三个位置:
- 数据模型
BillItem.ets:添加新字段 - 表头
PaginatedBillTable.ets的build()方法中:新增Text()组件并设置宽度 - 数据行
BillRow的@Builder中:新增对应的Text()渲染逻辑
同时需要相应调整其他列的 width() 百分比,确保总宽度仍为 100%。
10.2 自定义分页大小
在 Index.ets 中修改 pageSize 的值即可:
pageSize: number = 20; // 改为每页 20 条
也可在 PaginatedBillTable 的实例化时通过 @Prop 传入:
PaginatedBillTable({
data: this.bills,
pageSize: 20
})
10.3 数据源切换
将 mockBillsData() 替换为真实数据获取逻辑即可:
@State bills: BillItem[] = [];
aboutToAppear() {
fetchBillData().then(data => {
this.bills = data;
});
}
组件的 @Prop 数据源变化后,所有计算属性会自动重新求值,UI 随之更新。
10.4 样式定制
- 颜色:修改
#1A3A5C(表头/当前页码)、#0D2137(标题栏)等色值 - 圆角:调整
borderRadius(6)的值 - 阴影:修改
shadow()的参数 - 字体:修改
fontSize和fontWeight
十一、ArkTS 与常见框架对比
11.1 ArkTS vs Flutter
| 维度 | ArkTS (ArkUI) | Flutter |
|---|---|---|
| 语言 | TypeScript 生态 | Dart |
| 组件化 | @Component struct |
class Widget |
| 状态管理 | @State/@Prop/@Link |
setState / Provider |
| 布局 | Column/Row/Flex | Column/Row/Flex |
| 列表 | ForEach / List | ListView.builder |
| 构建工具 | Hvigor | Gradle / Dart |
| 学习曲线 | 较低(TS 开发者友好) | 中等(需学 Dart) |
11.2 ArkTS vs SwiftUI
两者在理念上非常接近:都采用声明式语法、属性包装器/装饰器管理状态、结构化组件体构建 UI。ArkTS 的 @State 对应 SwiftUI 的 @State,@Prop 对应 @Binding 的部分场景。
11.3 ArkTS vs Compose
ArkTS 的 build() 函数与 Jetpack Compose 的 @Composable 函数非常相似,都通过描述 UI 结构而非命令式操作 DOM 来构建界面。
十二、项目构建与运行
12.1 环境要求
- DevEco Studio 6.0+(推荐 6.26.1)
- HarmonyOS SDK API 24+
- Node.js 20+(Hvigor 依赖)
12.2 构建命令
# 在 DevEco Studio 中直接运行
Open Project → Select D:/hongmeng/MyApplication64 → Run 'entry'
# 或使用命令行(需配置 Hvigor 环境)
hvigor assembleHap
12.3 预览模式
DevEco Studio 支持实时预览(Previewer),在 Index.ets 文件中点击预览按钮即可查看效果。预览模式下,ArkTS 代码会被编译为 TypeScript 并执行,支持组件的实时交互(点击按钮切换页码、滚动数据行等)。
十三、总结与展望
13.1 项目成果
本项目成功实现了:
- 一个完整的、可直接运行的 HarmonyOS 分页账单明细表应用
- 一个可复用的分页表格组件
PaginatedBillTable - 一个合理的 ArkTS 项目结构(Model / Component / Page 三层)
- 一段可替换的模拟数据生成逻辑
- 一份完整的 ArkTS 开发踩坑记录
13.2 未来可扩展方向
- 服务端分页:对接 RESTful API,支持大数据量场景
- 排序功能:点击表头按日期/金额排序
- 筛选功能:按日期范围/分类/状态筛选数据
- 导出功能:将当前页或全部数据导出为 CSV/Excel
- 编辑功能:支持行内编辑或弹窗编辑
- 多级表头:如 “金额” 下拆分 “收入” 和 “支出”
- 暗色模式:通过
@Provide/@Consume实现主题切换 - 国际化:支持多语言文案切换
13.3 对 ArkTS 开发的思考
通过这个项目,可以明显感受到 ArkTS 作为鸿蒙生态的原生开发语言,在 Web 前端开发者转型移动端开发方面具有天然优势。TypeScript 语法基础 + 声明式 UI 范式 + 响应式状态管理,使得从 Web 开发(React/Vue)过渡到鸿蒙开发的门槛大大降低。
同时,ArkTS 也有一些需要开发者特别注意的约束(如对象字面量规则、部分 API 可用性差异等),这些约束虽然增加了初期学习的成本,但换来了更强的类型安全和更优的运行时性能。随着鸿蒙生态的不断成熟,相信这些体验会越来越好。
附录 A:完整文件索引
| 文件 | 行数 | 职责 |
|---|---|---|
entry/src/main/ets/model/BillItem.ets |
30 | 账单数据模型 |
entry/src/main/ets/components/PaginatedBillTable.ets |
363 | 分页表格组件 |
entry/src/main/ets/pages/Index.ets |
181 | 页面入口 + 模拟数据 |
entry/src/main/module.json5 |
50 | 模块配置 |
entry/build-profile.json5 |
33 | 构建配置 |
附录 B:关键技术决策记录
| 决策 | 选项 A | 选项 B | 最终选择 |
|---|---|---|---|
| 分页策略 | 服务端分页 | 前端切片 | 前端切片(数据量小) |
| 金额类型 | string | number | number(灵活计算) |
| 日期格式 | Date 对象 | 字符串 | 字符串(易序列化) |
| 间距方案 | Space() 组件 | margin() | margin()(精准控制) |
| 状态着色 | switch | if/else if | if/else if(兼容性好) |
| 参数类型 | 内联对象 | 命名接口 | 命名接口(合规要求) |
更多推荐



所有评论(0)