Files
EmailBill/openspec/changes/archive/2026-02-19-unify-bill-list-ui/design.md
SunCheng 6922dff5a9
All checks were successful
Docker Build & Deploy / Build Docker Image (push) Successful in 17s
Docker Build & Deploy / Deploy to Production (push) Successful in 7s
Docker Build & Deploy / Cleanup Dangling Images (push) Successful in 1s
Docker Build & Deploy / WeChat Notification (push) Successful in 2s
archive: unify-bill-list-ui (2026-02-19)
变更已完成并归档:
- 迁移 calendarV2/TransactionList.vue 使用 BillListComponent
- 代码从 403 行简化到 177 行
- 添加左滑删除功能
- Delta spec 已同步到主 specs

提交记录:
- f8e6029: refactor(calendar-v2): migrate TransactionList to BillListComponent
- 4fd190f: fix(calendar-v2): remove left/right padding
- 1ba446f: feat(calendar-v2): add delete functionality
- d324769: specs: sync bill-list-unified-ui
2026-02-19 22:08:10 +08:00

151 lines
6.0 KiB
Markdown

## Context
**当前状态**:
- 系统中真正显示交易账单的页面: TransactionsRecord.vue, EmailRecord.vue, calendarV2/TransactionList.vue
- `BillListComponent` 是统一的账单列表组件,位于 `Web/src/components/Bill/BillListComponent.vue`
- TransactionsRecord.vue 和 EmailRecord.vue 已使用 `BillListComponent`
- calendarV2/TransactionList.vue 使用自定义实现,有特殊的 UI 风格和交互(自定义 header、Smart 按钮)
**背景**:
- `BillListComponent` 提供完整的账单列表功能:筛选、排序、分页、左滑删除、多选、Iconify 图标支持
- 支持两种数据模式:API 模式(组件自动加载)和 Custom 模式(父组件传入数据)
- 已在 `/balance` 页面(TransactionsRecord.vue)和 EmailRecord.vue 成功使用
**约束**:
- 必须保持 calendarV2 的现有功能不变
- 需要保留自定义 header (Items 计数、Smart 按钮)
- 接受 UI 风格变化(从自定义卡片样式改为 BillListComponent 统一样式)
- 不能修改 `BillListComponent` 本身(除非发现 bug)
**实际发现**:
经过代码审查,发现原设计文档中列出的其他 5 个页面并非账单列表:
- MessageView.vue: 系统消息列表 (notifications)
- PeriodicRecord.vue: 周期性账单规则列表 (periodic billing rules)
- ClassificationEdit.vue: 分类管理列表 (categories)
- budgetV2/Index.vue: 预算卡片列表 (budget cards)
## Goals / Non-Goals
**Goals:**
- 将 calendarV2/TransactionList.vue 迁移至使用 `BillListComponent`
- 统一账单列表的视觉设计和交互体验
- 减少代码重复,提高可维护性(从 403 行减少到 177 行)
- 保留日历视图的特定功能(自定义 header、Smart 按钮)
**Non-Goals:**
- 不修改 `BillListComponent` 的核心功能
- 不改变业务逻辑或数据流
- 不涉及后端 API 修改
- 不迁移非账单列表页面(MessageView、PeriodicRecord、ClassificationEdit、budgetV2)
## Decisions
### 决策 1: 使用 Custom 数据模式
**决定**: 所有迁移页面使用 `data-source="custom"` 模式
**理由**:
- 每个页面有特定的数据加载逻辑(如 EmailRecord 加载关联账单、PeriodicRecord 加载周期性账单)
- Custom 模式允许父组件完全控制数据加载和筛选
- 避免修改 `BillListComponent` 的 API 加载逻辑
**替代方案**:
- 使用 API 模式并扩展 `BillListComponent` 支持不同 API endpoint → 拒绝,因为会增加组件复杂度
### 决策 2: 保留自定义 header 和 Smart 按钮
**决定**: calendarV2/TransactionList.vue 保留原有的自定义 header 部分
**理由**:
- 日历视图需要显示 Items 计数
- Smart 按钮是日历视图的特殊功能
- BillListComponent 只替换列表部分,不影响 header
**实现**:
- 在 template 中保留 txn-header 部分
- 将 BillListComponent 放在 header 下方
- 保留 header 相关的样式定义
### 决策 3: 保留特定事件处理
**决定**: 每个页面保留自己的事件处理逻辑,通过 `@click``@delete` 等 props 传递
**理由**:
- 不同页面对点击、删除的处理不同
- `BillListComponent` 已支持事件派发机制
- 保持业务逻辑在父组件中,组件只负责展示
### 决策 4: 禁用不需要的功能
**决定**: 通过 props 控制功能开关(如 `:enable-filter="false"``:show-checkbox="false"`)
**理由**:
- 不是所有页面都需要筛选栏和多选功能
- `BillListComponent` 已提供灵活的配置选项
- 保持页面简洁,符合原设计
## Risks / Trade-offs
### 风险 1: 业务逻辑遗漏
**风险**: 迁移过程中可能遗漏某些特定业务逻辑(如特殊的删除回调、数据刷新机制)
**缓解**:
- 迁移前仔细审查每个页面的现有实现
- 保留所有事件监听器(如 `transaction-deleted` 全局事件)
- 每个页面迁移后进行完整功能测试
### 风险 2: UI 细节差异
**风险**: `BillListComponent` 的样式可能与某些页面的原设计有细微差异
**缓解**:
- 使用 Custom 数据模式,保持数据加载逻辑不变
- 如果发现样式问题,优先通过 CSS 覆盖解决
- 必要时可以考虑为 `BillListComponent` 添加样式 prop(但需谨慎)
### 风险 3: 性能影响
**风险**: `BillListComponent` 可能比某些轻量级自定义实现更重
**缓解**:
- `BillListComponent` 已经过优化,支持虚拟滚动和分页
- Custom 模式下父组件完全控制数据加载,不会有额外请求
- 监控迁移后的页面性能指标
### Trade-off: 灵活性 vs 一致性
**取舍**: 采用统一组件会牺牲一定的定制灵活性
**接受理由**: 一致性和可维护性的收益大于灵活性的损失
**风险控制**: Custom 数据模式提供足够的业务逻辑控制空间
## Migration Plan
### 阶段 1: 准备(已完成)
- ✓ 分析所有账单列表页面
- ✓ 确认 `BillListComponent` 功能完整性
- ✓ 制定迁移顺序
### 阶段 2: 逐页迁移
每个页面遵循以下步骤:
1. **备份**: 保留原实现作为注释(如果需要回滚)
2. **替换**: 用 `BillListComponent` 替换自定义列表
3. **适配**: 调整数据格式和事件处理
4. **测试**: 验证所有功能(点击、删除、筛选、分页)
5. **清理**: 移除废弃代码和样式
### 阶段 3: 验收
- 所有 6 个页面迁移完成
- 功能回归测试通过
- 视觉一致性检查通过
- 代码审查通过
### 回滚策略
- 每个页面独立迁移,可以单独回滚
- Git commit 以页面为单位,便于 revert
- 如果某个页面迁移失败,不影响其他页面
## Open Questions
1. **budgetV2/Index.vue 的双列表问题**: 该页面有 "已使用预算列表" 和 "未分类账单列表" 两个列表,是否都需要迁移?
- **初步决定**: 都迁移,使用两个 `BillListComponent` 实例
2. **calendarV2/modules/TransactionList.vue**: 该组件本身是一个列表组件,是否需要完全替换还是部分复用?
- **初步决定**: 完全替换为 `BillListComponent`,因为注释已建议使用统一组件
3. **图标映射**: 某些页面可能使用特定的分类图标,是否需要特殊处理?
- **初步决定**: `BillListComponent` 已支持 Iconify 和 API 加载的图标映射,应该覆盖大部分场景