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

11 KiB
Raw Blame History

1. 项目结构准备

  • 1.1 创建 Web/src/views/budgetV2/ 目录
  • 1.2 创建 Web/src/views/budgetV2/modules/ 子目录
  • 1.3 创建 Web/src/views/budgetV2/Index.vue 主页面文件(空模板)
  • 1.4 创建 Web/src/views/budgetV2/modules/ExpenseBudgetContent.vue 支出预算子模块(空模板)
  • 1.5 创建 Web/src/views/budgetV2/modules/IncomeBudgetContent.vue 收入预算子模块(空模板)
  • 1.6 创建 Web/src/views/budgetV2/modules/SavingsBudgetContent.vue 存款计划子模块(空模板)

2. 路由配置

  • 2.1 在 Web/src/router/index.js 中添加 /budget-v2 路由,指向 budgetV2/Index.vue
  • 2.2 配置路由的 name: 'BudgetV2'meta 信息(用于 keep-alive
  • 2.3 保留旧路由 /budget 指向 BudgetView.vue(作为回退方案)
  • 2.4 在导航配置中将"预算"菜单指向新路由 /budget-v2

3. 主页面布局实现

  • 3.1 在 Index.vue 中引入 DateSelectHeader 组件
  • 3.2 添加 van-tabs 组件(支出/收入/计划三个 tab
  • 3.3 添加可滚动内容区域(包含 van-pull-refresh
  • 3.4 设置页面容器样式(page-container-flex + 底部安全距离)
  • 3.5 配置 defineOptions({ name: 'BudgetV2View' }) 用于 keep-alive

4. 状态管理和数据定义

  • 4.1 定义当前日期状态 currentDate = ref(new Date())
  • 4.2 定义业务 tab 状态 activeTab = ref(BudgetCategory.Expense)
  • 4.3 定义数据状态(expenseBudgetsincomeBudgetssavingsBudgetsoverallStats
  • 4.4 定义 UI 状态(loadingrefreshinghasErrorerrorMessage
  • 4.5 定义未覆盖分类状态 uncoveredCategories = ref([])
  • 4.6 定义归档总结状态(showSummaryPopuparchiveSummary
  • 4.7 定义日期选择器状态(showDatePickerpickerDate

5. DateSelectHeader 集成

  • 5.1 配置 DateSelectHeader 的 type 属性为 'month'
  • 5.2 绑定 DateSelectHeader 的 current-date 属性到 currentDate
  • 5.3 监听 DateSelectHeader 的 @prev 事件,实现 handlePrevMonth() 方法
  • 5.4 监听 DateSelectHeader 的 @next 事件,实现 handleNextMonth() 方法
  • 5.5 监听 DateSelectHeader 的 @jump 事件,打开日期选择器
  • 5.6 在页头右侧添加未覆盖分类警告图标(根据 uncoveredCategories 显示)
  • 5.7 在页头右侧添加归档图标(历史月份显示)

6. 业务 tabs 实现

  • 6.1 配置 van-tabs 的三个 tab支出、收入、计划
  • 6.2 绑定 van-tabsv-model:activeactiveTab
  • 6.3 监听 activeTab 的变化,触发数据刷新(使用 watch
  • 6.4 在每个 tab 中引入对应的子模块组件

7. 数据加载核心逻辑

  • 7.1 实现 loadBudgetData() 统一数据加载函数(月度)
  • 7.2 实现 loadMonthlyData(year, month) 加载月度预算数据
  • 7.3 实现 loadCategoryStats() 加载分类统计数据
  • 7.4 实现 loadUncoveredCategories() 加载未覆盖分类(仅支出和收入 tab
  • 7.5 实现 loadArchiveSummary() 加载归档总结(历史月份)
  • 7.6 使用 Promise.allSettled() 并发加载多个数据源

8. 月份切换逻辑

  • 8.1 实现 handlePrevMonth() 方法(切换到上一个月)
  • 8.2 实现 handleNextMonth() 方法(切换到下一个月,禁止未来月份)
  • 8.3 实现 isCurrentMonth() 方法(判断是否为当前月,禁用右箭头)
  • 8.4 在月份切换后自动调用 loadBudgetData() 刷新数据

9. 日期选择器实现

  • 9.1 添加 van-popup + van-date-picker 组件
  • 9.2 配置日期选择器为年月模式columns-type: ['year', 'month']
  • 9.3 实现 onDatePickerConfirm() 方法(确认日期选择)
  • 9.4 在日期选择器中限制不能选择未来的月份
  • 9.5 选择日期后自动调用 loadBudgetData() 刷新数据

10. 左右滑动手势实现

  • 10.1 定义触摸状态(touchStartX, touchStartY, touchEndX, touchEndY
  • 10.2 实现 handleTouchStart() 方法(记录触摸起始位置)
  • 10.3 实现 handleTouchMove() 方法(记录触摸移动位置)
  • 10.4 实现 handleTouchEnd() 方法(判断滑动方向和距离)
  • 10.5 在 handleTouchEnd() 中根据滑动方向调用 handlePrevMonth()handleNextMonth()
  • 10.6 设置最小滑动距离阈值50px避免误触
  • 10.7 确保垂直滑动(滚动内容)不触发月份切换

11. 下拉刷新实现

  • 11.1 配置 van-pull-refresh 组件,绑定 v-model="refreshing"
  • 11.2 实现 onRefresh() 方法(清除错误状态,重新加载数据)
  • 11.3 在数据加载完成后设置 refreshing = false
  • 11.4 显示刷新成功提示

12. 加载状态和错误处理

  • 12.1 在数据加载中显示 van-loading 组件
  • 12.2 在数据加载失败时显示 van-empty 组件(包含"重试"按钮)
  • 12.3 实现 retryLoad() 方法(清除错误状态,重新加载数据)
  • 12.4 在所有 API 调用中添加 try-catch 错误处理
  • 12.5 使用 Promise.allSettled() 并发加载,避免单点失败

13. 支出预算子模块实现

  • 13.1 在 ExpenseBudgetContent.vue 中定义 propsbudgets, stats, uncoveredCategories
  • 13.2 复用 BudgetChartAnalysis 组件显示统计图表
  • 13.3 复用 BudgetCard 组件显示预算列表
  • 13.4 使用 van-swipe-cell 实现左滑删除功能
  • 13.5 点击预算卡片打开 BudgetEditPopup 编辑弹窗
  • 13.6 添加悬浮按钮(van-floating-bubble)打开预算列表弹窗
  • 13.7 在预算列表弹窗中显示所有支出预算

14. 收入预算子模块实现

  • 14.1 在 IncomeBudgetContent.vue 中定义 propsbudgets, stats, uncoveredCategories
  • 14.2 复用 BudgetChartAnalysis 组件显示统计图表
  • 14.3 复用 BudgetCard 组件显示预算列表(调整显示字段:已收入、目标、差额)
  • 14.4 使用 van-swipe-cell 实现左滑删除功能
  • 14.5 点击预算卡片打开 BudgetEditPopup 编辑弹窗
  • 14.6 添加悬浮按钮打开预算列表弹窗

15. 存款计划子模块实现

  • 15.1 在 SavingsBudgetContent.vue 中定义 propsbudgets
  • 15.2 复用 BudgetCard 组件显示存款计划列表(调整显示字段:已存、目标、还差)
  • 15.3 在每个存款计划卡片底部添加日期切换按钮(左箭头、日期标签、右箭头)
  • 15.4 实现 handleSavingsNav(budget, offset) 方法(切换存款计划的日期)
  • 15.5 实现 getSavingsDateLabel(budget) 方法(格式化存款计划的日期标签)
  • 15.6 实现 disabledSavingsNextNav(budget) 方法(判断是否禁用右箭头)
  • 15.7 调用 getSavingsBudget(year, month, type) API 切换存款计划日期
  • 15.8 在页头右侧显示储蓄配置图标,点击打开 SavingsConfigPopup

16. 未覆盖分类弹窗实现

  • 16.1 复用 PopupContainer 组件创建未覆盖分类弹窗
  • 16.2 在弹窗中显示未覆盖分类列表(分类名称、交易笔数、总金额)
  • 16.3 使用卡片样式展示每个分类(uncovered-item
  • 16.4 在弹窗底部添加"我知道了"按钮关闭弹窗

17. 归档总结弹窗实现

  • 17.1 复用 PopupContainer 组件创建归档总结弹窗
  • 17.2 调用 getArchiveSummary(date) API 获取归档总结
  • 17.3 在弹窗中显示富文本内容(使用 v-html
  • 17.4 如果没有总结,显示"暂无总结"提示

18. 预算编辑和删除功能

  • 18.1 引入 BudgetEditPopup 组件
  • 18.2 点击悬浮按钮打开 BudgetEditPopup(新增预算)
  • 18.3 点击预算卡片打开 BudgetEditPopup(编辑预算)
  • 18.4 在 BudgetEditPopup@success 事件中调用 loadBudgetData() 刷新数据
  • 18.5 实现 handleDelete(budget) 方法(删除预算)
  • 18.6 在删除前显示确认对话框(showConfirmDialog
  • 18.7 调用 deleteBudget(id) API 删除预算
  • 18.8 删除成功后调用 loadBudgetData() 刷新数据

19. 储蓄配置功能

  • 19.1 引入 SavingsConfigPopup 组件
  • 19.2 在存款计划 tab 的页头右侧显示配置图标
  • 19.3 点击配置图标打开 SavingsConfigPopup
  • 19.4 在 SavingsConfigPopup@success 事件中调用 loadBudgetData() 刷新数据

20. 全局事件监听

  • 20.1 在 onMounted() 中监听 'transactions-changed' 全局事件
  • 20.2 实现 handleTransactionsChanged() 方法(刷新数据)
  • 20.3 在 onActivated() 中处理从缓存恢复时的数据刷新
  • 20.4 在 onBeforeUnmount() 中移除事件监听器

21. 样式和主题

  • 21.1 添加页面容器样式(.budget-v2-wrapper
  • 21.2 添加可滚动内容区域样式(.budget-scroll-content
  • 21.3 添加底部安全距离样式(calc(95px + env(safe-area-inset-bottom))
  • 21.4 配置主题变量(使用 var(--van-primary-color) 等)
  • 21.5 添加深色模式支持(通过 van-config-provider
  • 21.6 优化移动端滚动体验(-webkit-overflow-scrolling: touch

22. 辅助函数实现

  • 22.1 实现 formatMoney(val) 方法(格式化金额显示)
  • 22.2 实现 getPeriodLabel(type) 方法(获取周期标签:本月/本年)
  • 22.3 实现 getProgressColor(budget) 方法(计算进度条颜色)
  • 22.4 实现 isArchiveMonth(date) 方法(判断是否为历史月份)

23. 测试和调试

  • 23.1 测试页面初始加载(默认当前月、支出 tab
  • 23.2 测试月份切换(左右箭头、滑动手势)
  • 23.3 测试支出/收入/计划 tab 切换
  • 23.4 测试左右滑动手势切换月份
  • 23.5 测试下拉刷新功能
  • 23.6 测试日期选择器(选择历史月份、禁止未来月份)
  • 23.7 测试未覆盖分类功能(警告图标、弹窗)
  • 23.8 测试归档总结功能(历史月份)
  • 23.9 测试存款计划的独立日期切换
  • 23.10 测试预算新增、编辑、删除功能
  • 23.11 测试储蓄配置功能
  • 23.12 测试错误处理API 失败、网络错误)
  • 23.13 测试加载状态显示
  • 23.14 测试 keep-alive 缓存和全局事件刷新
  • 23.15 测试深色模式切换

24. 优化和完善

  • 24.1 检查并优化数据加载性能(使用 Promise.allSettled 并发加载)
  • 24.2 添加必要的加载动画和过渡效果
  • 24.3 添加代码注释(业务逻辑复杂的部分)
  • 24.4 检查并修复 ESLint 警告
  • 24.5 验证所有 API 调用的错误处理
  • 24.6 验证移动端适配(安全距离、触摸手势)

25. 文档和发布

  • 25.1 更新 README.md如果需要
  • 25.2 创建 PR 并填写详细的变更说明
  • 25.3 在 PR 中添加测试截图或录屏
  • 25.4 请求代码审查
  • 25.5 合并 PR 并部署到测试环境
  • 25.6 收集用户反馈并快速迭代