diff --git a/openspec/changes/unify-bill-list-ui/design.md b/openspec/changes/unify-bill-list-ui/design.md new file mode 100644 index 0000000..625e117 --- /dev/null +++ b/openspec/changes/unify-bill-list-ui/design.md @@ -0,0 +1,150 @@ +## 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 加载的图标映射,应该覆盖大部分场景 diff --git a/openspec/changes/unify-bill-list-ui/proposal.md b/openspec/changes/unify-bill-list-ui/proposal.md new file mode 100644 index 0000000..f165e79 --- /dev/null +++ b/openspec/changes/unify-bill-list-ui/proposal.md @@ -0,0 +1,42 @@ +## Why + +系统中存在多处账单列表实现,各自使用不同的样式和交互方式。经过代码审查,发现只有 `calendarV2/modules/TransactionList.vue` 需要迁移,其他页面要么已使用 `BillListComponent`,要么不是账单列表。统一账单列表 UI 可以提供一致的用户体验和更好的可维护性。 + +## What Changes + +- 将 `calendarV2/modules/TransactionList.vue` 的自定义账单列表替换为 `BillListComponent` 组件 +- 保留日历视图的特殊功能:自定义 header (Items 计数) 和 Smart 按钮 +- 移除自定义的数据格式转换逻辑和账单卡片渲染代码 +- 简化代码从 403 行减少到 177 行 + +**备注**: 原设计文档中列出的其他 5 个页面经审查后: +- `TransactionsRecord.vue` (balance页面): ✅ 已使用 `BillListComponent` +- `EmailRecord.vue`: ✅ 已使用 `BillListComponent` +- `MessageView.vue`: ❌ 系统消息列表,不是账单 +- `PeriodicRecord.vue`: ❌ 周期性账单规则列表,不是交易账单 +- `ClassificationEdit.vue`: ❌ 分类管理列表,不是账单 +- `budgetV2/Index.vue`: ❌ 预算卡片列表,不是账单 + +## Capabilities + +### New Capabilities +- `bill-list-unified-ui`: 统一的账单列表 UI 规范,定义所有账单列表页面必须遵循的视觉设计和交互模式 + +### Modified Capabilities + + +## Impact + +**受影响的代码**: +- `Web/src/views/calendarV2/modules/TransactionList.vue` (已完成迁移) + +**依赖组件**: +- `Web/src/components/Bill/BillListComponent.vue` (已存在,无需修改) + +**API**: +- 无 API 变更,仅前端 UI 统一 + +**用户体验影响**: +- 正向影响:日历视图的账单列表与 /balance 页面保持一致 +- UI 风格变化:从自定义卡片样式改为统一的 BillListComponent 样式 +- 功能保留:自定义 header、Items 计数、Smart 按钮均已保留 diff --git a/openspec/changes/unify-bill-list-ui/tasks.md b/openspec/changes/unify-bill-list-ui/tasks.md new file mode 100644 index 0000000..f00a532 --- /dev/null +++ b/openspec/changes/unify-bill-list-ui/tasks.md @@ -0,0 +1,123 @@ +## 1. 准备和验证 + +- [x] 1.1 审查 `BillListComponent` 组件,确认支持 Custom 数据模式和所有必需的 props +- [x] 1.2 审查目标页面 - calendarV2/TransactionList.vue (其他页面不是账单列表) +- [x] 1.3 确认 calendarV2/TransactionList.vue 使用的账单数据 API 和数据格式 +- [x] 1.4 设计迁移方案(保留自定义header和Smart按钮,接受UI风格变化) + +## 2. 迁移 calendarV2/modules/TransactionList.vue + +- [x] 2.1 导入 `BillListComponent` +- [x] 2.2 替换自定义列表为 `` +- [x] 2.3 配置 `data-source="custom"` 和 `:transactions` prop +- [x] 2.4 配置功能开关 props (`:enable-filter="false"`, `:show-delete="false"`) +- [x] 2.5 实现 `@click` 事件处理器,保持原有点击逻辑 +- [x] 2.6 移除数据格式转换逻辑(formatTime, formatAmount, getIconByClassify等) +- [x] 2.7 移除废弃的自定义列表代码和样式 +- [x] 2.8 运行 lint 检查 +- [ ] 2.9 提交代码: "refactor(calendar-v2): migrate TransactionList to BillListComponent" + +## 3. 代码验证和文档更新 + +- [ ] 3.1 手动测试日历视图功能流程 +- [ ] 3.2 验证视觉效果:对比迁移前后的账单列表样式 +- [ ] 3.3 验证交互:确认点击账单、Smart按钮功能正常 +- [ ] 3.4 更新 `openspec/changes/unify-bill-list-ui/` 文档,记录实际迁移范围 +- [ ] 3.5 在 `.doc/` 目录下创建迁移记录文档 (可选) +- [ ] 3.6 最终提交: "docs: update unify-bill-list-ui change scope" + +## 2. 迁移 MessageView.vue + +- [ ] 2.1 在 `MessageView.vue` 中导入 `BillListComponent` +- [ ] 2.2 替换自定义 `van-swipe-cell` 列表为 `` +- [ ] 2.3 配置 `data-source="custom"` 和 `:transactions` prop +- [ ] 2.4 配置功能开关 props (`:enable-filter="false"`, `:show-delete` 等) +- [ ] 2.5 实现 `@click` 事件处理器,保持原有点击逻辑 +- [ ] 2.6 实现 `@delete` 事件处理器,更新本地数据列表 +- [ ] 2.7 移除废弃的自定义列表代码和样式 +- [ ] 2.8 测试所有功能:点击、删除、数据刷新 +- [ ] 2.9 提交代码: "refactor(message): migrate to BillListComponent" + +## 3. 迁移 EmailRecord.vue + +- [ ] 3.1 在 `EmailRecord.vue` 中导入 `BillListComponent` +- [ ] 3.2 替换关联账单列表的自定义 `van-swipe-cell` 为 `` +- [ ] 3.3 配置 `data-source="custom"` 和 `:transactions` prop +- [ ] 3.4 配置功能开关 props +- [ ] 3.5 实现 `@click` 事件处理器(关联账单详情弹窗逻辑) +- [ ] 3.6 实现 `@delete` 事件处理器 +- [ ] 3.7 保留全局事件监听器 (`transaction-deleted`) +- [ ] 3.8 移除废弃的自定义列表代码和样式 +- [ ] 3.9 测试:查看关联账单列表、点击账单、删除账单 +- [ ] 3.10 提交代码: "refactor(email): migrate bill list to BillListComponent" + +## 4. 迁移 PeriodicRecord.vue + +- [ ] 4.1 在 `PeriodicRecord.vue` 中导入 `BillListComponent` +- [ ] 4.2 替换周期性账单列表的自定义 `van-swipe-cell` 为 `` +- [ ] 4.3 配置 `data-source="custom"` 和 `:transactions` prop +- [ ] 4.4 适配周期性账单的数据格式(如需要转换为标准 transaction 格式) +- [ ] 4.5 配置功能开关 props +- [ ] 4.6 实现 `@click` 事件处理器(编辑周期性账单逻辑) +- [ ] 4.7 实现 `@delete` 事件处理器(删除周期性账单的特定逻辑) +- [ ] 4.8 移除废弃的自定义列表代码和样式 +- [ ] 4.9 测试:查看周期性账单、编辑、删除、暂停/恢复 +- [ ] 4.10 提交代码: "refactor(periodic): migrate to BillListComponent" + +## 5. 迁移 ClassificationEdit.vue + +- [ ] 5.1 在 `ClassificationEdit.vue` 中导入 `BillListComponent` +- [ ] 5.2 替换分类编辑页面的自定义 `van-swipe-cell` 列表为 `` +- [ ] 5.3 配置 `data-source="custom"` 和 `:transactions` prop +- [ ] 5.4 配置功能开关 props (可能需要禁用筛选栏) +- [ ] 5.5 实现 `@click` 事件处理器(选择账单进行分类编辑) +- [ ] 5.6 实现 `@delete` 事件处理器 +- [ ] 5.7 保留分类筛选逻辑(如果有) +- [ ] 5.8 移除废弃的自定义列表代码和样式 +- [ ] 5.9 测试:选择账单、修改分类、删除账单 +- [ ] 5.10 提交代码: "refactor(classification-edit): migrate to BillListComponent" + +## 6. 迁移 calendarV2/modules/TransactionList.vue + +- [ ] 6.1 在 `calendarV2/modules/TransactionList.vue` 中导入 `BillListComponent` +- [ ] 6.2 完全替换现有的 TransactionList 组件实现为 `` +- [ ] 6.3 配置 `data-source="custom"` 和 `:transactions` prop +- [ ] 6.4 配置功能开关 props (根据日历视图需求) +- [ ] 6.5 实现 `@click` 事件处理器(日历视图的账单详情逻辑) +- [ ] 6.6 实现 `@delete` 事件处理器 +- [ ] 6.7 确保与日历组件的数据传递正常 +- [ ] 6.8 移除废弃的自定义列表代码和样式 +- [ ] 6.9 测试:日历选择日期、查看账单列表、点击账单、删除账单 +- [ ] 6.10 提交代码: "refactor(calendar-v2): migrate TransactionList to BillListComponent" + +## 7. 迁移 budgetV2/Index.vue + +- [ ] 7.1 在 `budgetV2/Index.vue` 中导入 `BillListComponent` +- [ ] 7.2 识别页面中的两个账单列表:"已使用预算列表" 和 "未分类账单列表" +- [ ] 7.3 替换 "已使用预算列表" 的自定义 `van-swipe-cell` 为第一个 `` 实例 +- [ ] 7.4 配置第一个组件: `data-source="custom"`, `:transactions`, `:enable-filter="false"` +- [ ] 7.5 替换 "未分类账单列表" 的自定义 `van-swipe-cell` 为第二个 `` 实例 +- [ ] 7.6 配置第二个组件: `data-source="custom"`, `:transactions`, `:enable-filter="false"` +- [ ] 7.7 实现两个列表的 `@click` 事件处理器 +- [ ] 7.8 实现两个列表的 `@delete` 事件处理器,更新对应的本地数据 +- [ ] 7.9 保留预算统计逻辑和全局事件监听器 +- [ ] 7.10 移除废弃的自定义列表代码和样式 +- [ ] 7.11 测试:预算统计、已使用列表、未分类列表、点击、删除 +- [ ] 7.12 提交代码: "refactor(budget-v2): migrate to BillListComponent" + +## 8. 代码清理和验证 + +- [ ] 8.1 审查所有迁移页面,确认没有遗留的自定义列表代码 +- [ ] 8.2 审查所有迁移页面,确认没有未使用的列表样式定义 +- [ ] 8.3 运行前端 lint: `cd Web && pnpm lint` +- [ ] 8.4 运行前端构建: `cd Web && pnpm build` +- [ ] 8.5 修复任何 lint 错误或构建警告 + +## 9. 集成测试和文档 + +- [ ] 9.1 手动测试所有 6 个迁移页面的完整功能流程 +- [ ] 9.2 验证视觉一致性:对比每个页面与 `/balance` 页面的账单列表样式 +- [ ] 9.3 验证交互一致性:确认点击、左滑删除、分页行为一致 +- [ ] 9.4 更新 `Web/src/views/AGENTS.md` (如需要),记录统一使用 BillListComponent +- [ ] 9.5 在 `.doc/` 目录下创建迁移记录文档 (可选) +- [ ] 9.6 最终提交: "refactor: unify all bill lists to BillListComponent"