Files
EmailBill/openspec/changes/archive/2026-02-13-budget-page-v2/tasks.md
SunCheng 162b6d02dd
All checks were successful
Docker Build & Deploy / Build Docker Image (push) Successful in 26s
Docker Build & Deploy / Deploy to Production (push) Successful in 8s
Docker Build & Deploy / Cleanup Dangling Images (push) Successful in 2s
Docker Build & Deploy / WeChat Notification (push) Successful in 1s
fix
2026-02-13 22:49:07 +08:00

221 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 1. 项目结构准备
- [x] 1.1 创建 `Web/src/views/budgetV2/` 目录
- [x] 1.2 创建 `Web/src/views/budgetV2/modules/` 子目录
- [x] 1.3 创建 `Web/src/views/budgetV2/Index.vue` 主页面文件(空模板)
- [x] 1.4 创建 `Web/src/views/budgetV2/modules/ExpenseBudgetContent.vue` 支出预算子模块(空模板)
- [x] 1.5 创建 `Web/src/views/budgetV2/modules/IncomeBudgetContent.vue` 收入预算子模块(空模板)
- [x] 1.6 创建 `Web/src/views/budgetV2/modules/SavingsBudgetContent.vue` 存款计划子模块(空模板)
## 2. 路由配置
- [x] 2.1 在 `Web/src/router/index.js` 中添加 `/budget-v2` 路由,指向 `budgetV2/Index.vue`
- [x] 2.2 配置路由的 `name: 'BudgetV2'``meta` 信息(用于 keep-alive
- [x] 2.3 保留旧路由 `/budget` 指向 `BudgetView.vue`(作为回退方案)
- [x] 2.4 在导航配置中将"预算"菜单指向新路由 `/budget-v2`
## 3. 主页面布局实现
- [x] 3.1 在 `Index.vue` 中引入 `DateSelectHeader` 组件
- [x] 3.2 添加 `van-tabs` 组件(支出/收入/计划三个 tab
- [x] 3.3 添加可滚动内容区域(包含 `van-pull-refresh`
- [x] 3.4 设置页面容器样式(`page-container-flex` + 底部安全距离)
- [x] 3.5 配置 `defineOptions({ name: 'BudgetV2View' })` 用于 keep-alive
## 4. 状态管理和数据定义
- [x] 4.1 定义当前日期状态 `currentDate = ref(new Date())`
- [x] 4.2 定义业务 tab 状态 `activeTab = ref(BudgetCategory.Expense)`
- [x] 4.3 定义数据状态(`expenseBudgets``incomeBudgets``savingsBudgets``overallStats`
- [x] 4.4 定义 UI 状态(`loading``refreshing``hasError``errorMessage`
- [x] 4.5 定义未覆盖分类状态 `uncoveredCategories = ref([])`
- [x] 4.6 定义归档总结状态(`showSummaryPopup``archiveSummary`
- [x] 4.7 定义日期选择器状态(`showDatePicker``pickerDate`
## 5. DateSelectHeader 集成
- [x] 5.1 配置 DateSelectHeader 的 `type` 属性为 'month'
- [x] 5.2 绑定 DateSelectHeader 的 `current-date` 属性到 `currentDate`
- [x] 5.3 监听 DateSelectHeader 的 `@prev` 事件,实现 `handlePrevMonth()` 方法
- [x] 5.4 监听 DateSelectHeader 的 `@next` 事件,实现 `handleNextMonth()` 方法
- [x] 5.5 监听 DateSelectHeader 的 `@jump` 事件,打开日期选择器
- [x] 5.6 在页头右侧添加未覆盖分类警告图标(根据 `uncoveredCategories` 显示)
- [x] 5.7 在页头右侧添加归档图标(历史月份显示)
## 6. 业务 tabs 实现
- [x] 6.1 配置 `van-tabs` 的三个 tab支出、收入、计划
- [x] 6.2 绑定 `van-tabs``v-model:active``activeTab`
- [x] 6.3 监听 `activeTab` 的变化,触发数据刷新(使用 `watch`
- [x] 6.4 在每个 tab 中引入对应的子模块组件
## 7. 数据加载核心逻辑
- [x] 7.1 实现 `loadBudgetData()` 统一数据加载函数(月度)
- [x] 7.2 实现 `loadMonthlyData(year, month)` 加载月度预算数据
- [x] 7.3 实现 `loadCategoryStats()` 加载分类统计数据
- [x] 7.4 实现 `loadUncoveredCategories()` 加载未覆盖分类(仅支出和收入 tab
- [x] 7.5 实现 `loadArchiveSummary()` 加载归档总结(历史月份)
- [x] 7.6 使用 `Promise.allSettled()` 并发加载多个数据源
## 8. 月份切换逻辑
- [x] 8.1 实现 `handlePrevMonth()` 方法(切换到上一个月)
- [x] 8.2 实现 `handleNextMonth()` 方法(切换到下一个月,禁止未来月份)
- [x] 8.3 实现 `isCurrentMonth()` 方法(判断是否为当前月,禁用右箭头)
- [x] 8.4 在月份切换后自动调用 `loadBudgetData()` 刷新数据
## 9. 日期选择器实现
- [x] 9.1 添加 `van-popup` + `van-date-picker` 组件
- [x] 9.2 配置日期选择器为年月模式columns-type: ['year', 'month']
- [x] 9.3 实现 `onDatePickerConfirm()` 方法(确认日期选择)
- [x] 9.4 在日期选择器中限制不能选择未来的月份
- [x] 9.5 选择日期后自动调用 `loadBudgetData()` 刷新数据
## 10. 左右滑动手势实现
- [x] 10.1 定义触摸状态(`touchStartX`, `touchStartY`, `touchEndX`, `touchEndY`
- [x] 10.2 实现 `handleTouchStart()` 方法(记录触摸起始位置)
- [x] 10.3 实现 `handleTouchMove()` 方法(记录触摸移动位置)
- [x] 10.4 实现 `handleTouchEnd()` 方法(判断滑动方向和距离)
- [x] 10.5 在 `handleTouchEnd()` 中根据滑动方向调用 `handlePrevMonth()``handleNextMonth()`
- [x] 10.6 设置最小滑动距离阈值50px避免误触
- [x] 10.7 确保垂直滑动(滚动内容)不触发月份切换
## 11. 下拉刷新实现
- [x] 11.1 配置 `van-pull-refresh` 组件,绑定 `v-model="refreshing"`
- [x] 11.2 实现 `onRefresh()` 方法(清除错误状态,重新加载数据)
- [x] 11.3 在数据加载完成后设置 `refreshing = false`
- [x] 11.4 显示刷新成功提示
## 12. 加载状态和错误处理
- [x] 12.1 在数据加载中显示 `van-loading` 组件
- [x] 12.2 在数据加载失败时显示 `van-empty` 组件(包含"重试"按钮)
- [x] 12.3 实现 `retryLoad()` 方法(清除错误状态,重新加载数据)
- [x] 12.4 在所有 API 调用中添加 try-catch 错误处理
- [x] 12.5 使用 `Promise.allSettled()` 并发加载,避免单点失败
## 13. 支出预算子模块实现
- [x] 13.1 在 `ExpenseBudgetContent.vue` 中定义 props`budgets`, `stats`, `uncoveredCategories`
- [x] 13.2 复用 `BudgetChartAnalysis` 组件显示统计图表
- [x] 13.3 复用 `BudgetCard` 组件显示预算列表
- [x] 13.4 使用 `van-swipe-cell` 实现左滑删除功能
- [x] 13.5 点击预算卡片打开 `BudgetEditPopup` 编辑弹窗
- [x] 13.6 添加悬浮按钮(`van-floating-bubble`)打开预算列表弹窗
- [x] 13.7 在预算列表弹窗中显示所有支出预算
## 14. 收入预算子模块实现
- [x] 14.1 在 `IncomeBudgetContent.vue` 中定义 props`budgets`, `stats`, `uncoveredCategories`
- [x] 14.2 复用 `BudgetChartAnalysis` 组件显示统计图表
- [x] 14.3 复用 `BudgetCard` 组件显示预算列表(调整显示字段:已收入、目标、差额)
- [x] 14.4 使用 `van-swipe-cell` 实现左滑删除功能
- [x] 14.5 点击预算卡片打开 `BudgetEditPopup` 编辑弹窗
- [x] 14.6 添加悬浮按钮打开预算列表弹窗
## 15. 存款计划子模块实现
- [x] 15.1 在 `SavingsBudgetContent.vue` 中定义 props`budgets`
- [x] 15.2 复用 `BudgetCard` 组件显示存款计划列表(调整显示字段:已存、目标、还差)
- [x] 15.3 在每个存款计划卡片底部添加日期切换按钮(左箭头、日期标签、右箭头)
- [x] 15.4 实现 `handleSavingsNav(budget, offset)` 方法(切换存款计划的日期)
- [x] 15.5 实现 `getSavingsDateLabel(budget)` 方法(格式化存款计划的日期标签)
- [x] 15.6 实现 `disabledSavingsNextNav(budget)` 方法(判断是否禁用右箭头)
- [x] 15.7 调用 `getSavingsBudget(year, month, type)` API 切换存款计划日期
- [x] 15.8 在页头右侧显示储蓄配置图标,点击打开 `SavingsConfigPopup`
## 16. 未覆盖分类弹窗实现
- [x] 16.1 复用 `PopupContainer` 组件创建未覆盖分类弹窗
- [x] 16.2 在弹窗中显示未覆盖分类列表(分类名称、交易笔数、总金额)
- [x] 16.3 使用卡片样式展示每个分类(`uncovered-item`
- [x] 16.4 在弹窗底部添加"我知道了"按钮关闭弹窗
## 17. 归档总结弹窗实现
- [x] 17.1 复用 `PopupContainer` 组件创建归档总结弹窗
- [x] 17.2 调用 `getArchiveSummary(date)` API 获取归档总结
- [x] 17.3 在弹窗中显示富文本内容(使用 `v-html`
- [x] 17.4 如果没有总结,显示"暂无总结"提示
## 18. 预算编辑和删除功能
- [x] 18.1 引入 `BudgetEditPopup` 组件
- [x] 18.2 点击悬浮按钮打开 `BudgetEditPopup`(新增预算)
- [x] 18.3 点击预算卡片打开 `BudgetEditPopup`(编辑预算)
- [x] 18.4 在 `BudgetEditPopup``@success` 事件中调用 `loadBudgetData()` 刷新数据
- [x] 18.5 实现 `handleDelete(budget)` 方法(删除预算)
- [x] 18.6 在删除前显示确认对话框(`showConfirmDialog`
- [x] 18.7 调用 `deleteBudget(id)` API 删除预算
- [x] 18.8 删除成功后调用 `loadBudgetData()` 刷新数据
## 19. 储蓄配置功能
- [x] 19.1 引入 `SavingsConfigPopup` 组件
- [x] 19.2 在存款计划 tab 的页头右侧显示配置图标
- [x] 19.3 点击配置图标打开 `SavingsConfigPopup`
- [x] 19.4 在 `SavingsConfigPopup``@success` 事件中调用 `loadBudgetData()` 刷新数据
## 20. 全局事件监听
- [x] 20.1 在 `onMounted()` 中监听 'transactions-changed' 全局事件
- [x] 20.2 实现 `handleTransactionsChanged()` 方法(刷新数据)
- [x] 20.3 在 `onActivated()` 中处理从缓存恢复时的数据刷新
- [x] 20.4 在 `onBeforeUnmount()` 中移除事件监听器
## 21. 样式和主题
- [x] 21.1 添加页面容器样式(`.budget-v2-wrapper`
- [x] 21.2 添加可滚动内容区域样式(`.budget-scroll-content`
- [x] 21.3 添加底部安全距离样式(`calc(95px + env(safe-area-inset-bottom))`
- [x] 21.4 配置主题变量(使用 `var(--van-primary-color)` 等)
- [x] 21.5 添加深色模式支持(通过 `van-config-provider`
- [x] 21.6 优化移动端滚动体验(`-webkit-overflow-scrolling: touch`
## 22. 辅助函数实现
- [x] 22.1 实现 `formatMoney(val)` 方法(格式化金额显示)
- [x] 22.2 实现 `getPeriodLabel(type)` 方法(获取周期标签:本月/本年)
- [x] 22.3 实现 `getProgressColor(budget)` 方法(计算进度条颜色)
- [x] 22.4 实现 `isArchiveMonth(date)` 方法(判断是否为历史月份)
## 23. 测试和调试
- [x] 23.1 测试页面初始加载(默认当前月、支出 tab
- [x] 23.2 测试月份切换(左右箭头、滑动手势)
- [x] 23.3 测试支出/收入/计划 tab 切换
- [x] 23.4 测试左右滑动手势切换月份
- [x] 23.5 测试下拉刷新功能
- [x] 23.6 测试日期选择器(选择历史月份、禁止未来月份)
- [x] 23.7 测试未覆盖分类功能(警告图标、弹窗)
- [x] 23.8 测试归档总结功能(历史月份)
- [x] 23.9 测试存款计划的独立日期切换
- [x] 23.10 测试预算新增、编辑、删除功能
- [x] 23.11 测试储蓄配置功能
- [x] 23.12 测试错误处理API 失败、网络错误)
- [x] 23.13 测试加载状态显示
- [x] 23.14 测试 keep-alive 缓存和全局事件刷新
- [x] 23.15 测试深色模式切换
## 24. 优化和完善
- [x] 24.1 检查并优化数据加载性能(使用 `Promise.allSettled` 并发加载)
- [x] 24.2 添加必要的加载动画和过渡效果
- [x] 24.3 添加代码注释(业务逻辑复杂的部分)
- [x] 24.4 检查并修复 ESLint 警告
- [x] 24.5 验证所有 API 调用的错误处理
- [x] 24.6 验证移动端适配(安全距离、触摸手势)
## 25. 文档和发布
- [x] 25.1 更新 README.md如果需要
- [ ] 25.2 创建 PR 并填写详细的变更说明
- [ ] 25.3 在 PR 中添加测试截图或录屏
- [ ] 25.4 请求代码审查
- [ ] 25.5 合并 PR 并部署到测试环境
- [ ] 25.6 收集用户反馈并快速迭代