## Context **当前状态**: - 项目已建立 Chart.js 基础设施(BaseChart.vue、useChartTheme、chartHelpers) - 4 个组件仍使用 ECharts API 或通过环境变量 `VITE_USE_CHARTJS` 双模式运行 - ECharts 依赖已从 package.json 移除,导致构建失败 **技术栈**: - Chart.js 4.5.1 + vue-chartjs 5.3.3 - Vue 3 Composition API + Vant UI - 移动端优先设计 **约束**: - 必须保持图表视觉效果一致(颜色、布局、标签格式) - 不能影响用户交互行为(点击、hover、tooltip) - 需兼容暗色模式(通过 useChartTheme) ## Goals / Non-Goals **Goals:** - 彻底移除 ECharts 代码和环境变量开关 - 统一使用 BaseChart.vue 包装组件 - 确保所有图表类型正确渲染: - 仪表盘图表(Gauge Chart)→ Doughnut + 中心文本叠加 - 折线图(Line Chart)→ Chart.js Line - 饼图/环形图(Pie/Doughnut Chart)→ Chart.js Doughnut - 通过白盒和黑盒测试验证功能正确性 **Non-Goals:** - 不重新设计图表样式或交互 - 不优化图表性能(除非迁移过程中发现明显问题) - 不添加新的图表功能 - 不修改业务逻辑或数据处理代码 ## Decisions ### 1. 仪表盘图表实现方案 **决策**:使用 Doughnut 图表 + CSS 绝对定位的中心文本覆盖层 **理由**: - Chart.js 无原生 Gauge 图表类型 - Doughnut 图表可以通过 `rotation` 和 `circumference` 配置实现半圆仪表盘效果 - 项目中 `BudgetChartAnalysis.vue` 已使用 ECharts Gauge,需保持视觉一致性 **替代方案**: - ❌ 使用第三方插件(如 chartjs-gauge):增加依赖复杂度 - ❌ 使用 Canvas 自绘:维护成本高,不符合项目标准 **实现细节**: ```javascript { type: 'doughnut', data: { datasets: [{ data: [current, limit - current], backgroundColor: [progressColor, '#f0f0f0'], rotation: -90, // 从顶部开始 circumference: 180 // 半圆 }] }, options: { cutout: '70%', // 内环大小 plugins: { legend: { display: false }, tooltip: { enabled: false } } } } ``` ### 2. 图表数据转换策略 **决策**:复用现有的 `prepareChartData()` 函数,仅修改图表配置部分 **理由**: - 所有组件都已有数据准备逻辑(`prepareChartData()`) - 数据格式(labels + datasets)在 ECharts 和 Chart.js 之间相似 - 减少代码改动,降低引入 bug 风险 **迁移模式**: ```javascript // 保留 const { xAxisLabels, expenseData, incomeData } = prepareChartData() // 修改:从 ECharts option 转为 Chart.js data + options const chartData = { labels: xAxisLabels, datasets: [{ label: '支出', data: expenseData, borderColor: colors.value.danger, backgroundColor: 'rgba(238, 10, 36, 0.1)' }] } const chartOptions = getChartOptions({ plugins: { legend: { display: false } } }) ``` ### 3. 环境变量清理 **决策**:删除所有 `VITE_USE_CHARTJS` 相关的条件渲染和双分支代码 **理由**: - 项目已标准化使用 Chart.js,无需保留回退选项 - 双模式代码增加维护成本和测试复杂度 - ECharts 依赖已移除,无法回退 **清理范围**: ```vue
``` ### 4. 测试策略 **决策**:分层测试 = 单元测试(白盒)+ E2E 测试(黑盒)+ 视觉回归 **白盒测试(Jest + Vue Test Utils)**: - 测试组件能否正确挂载 - 测试 props 传入后 chartData 和 chartOptions 的正确性 - 测试事件发射(如 @chart:render) **黑盒测试(Playwright)**: - 测试图表在真实浏览器中的渲染 - 测试用户交互(点击、hover、tooltip) - 测试暗色模式切换 **视觉回归测试**: - 截图对比迁移前后的图表外观 - 确保颜色、字体、布局一致 ## Risks / Trade-offs ### Risk 1: 仪表盘视觉差异 **风险**:Chart.js Doughnut 无法完美复现 ECharts Gauge 的细节(如指针动画) **缓解**:接受静态圆弧进度条,使用颜色渐变和动画过渡弥补视觉效果 ### Risk 2: 第三方样式冲突 **风险**:Chart.js 的全局样式可能与 Vant UI 冲突 **缓解**:使用 scoped styles,通过 `useChartTheme` 统一管理颜色变量 ### Risk 3: 移动端性能 **风险**:Chart.js 在低端移动设备上可能卡顿 **缓解**: - 使用 `chartHelpers.ts` 中的数据抽样功能 - 配置 `animation.duration` 为合理值(750ms) - 监控 `prefers-reduced-motion` 媒体查询 ### Risk 4: 无法回退 **风险**:迁移后如果发现严重问题,无法快速回退到 ECharts **缓解**: - 迁移前创建 git tag - 分组件逐步迁移,每个组件验证通过后再迁移下一个 - 保留完整的测试套件 ## Migration Plan ### Phase 1: 单组件迁移(按复杂度排序) 1. **MonthlyExpenseCard.vue**(简单折线图) 2. **DailyTrendChart.vue**(双系列折线图) 3. **ExpenseCategoryCard.vue**(环形图 + 列表) 4. **BudgetChartAnalysis.vue**(仪表盘 + 复杂布局) ### Phase 2: 每个组件的迁移步骤 1. 备份原始 ECharts 代码(注释) 2. 替换为 BaseChart.vue + 数据转换 3. 运行单元测试 4. 本地浏览器验证(Chrome + Firefox) 5. 移除注释的 ECharts 代码 ### Phase 3: 集成测试 1. 运行完整的 E2E 测试套件 2. 视觉回归测试(截图对比) 3. 性能测试(Lighthouse) ### Rollback Strategy 如果迁移失败: 1. `git revert` 到迁移前的 commit 2. 临时恢复 `echarts` 依赖:`pnpm add echarts` 3. 重新评估迁移方案 ## Open Questions 1. ✅ **是否需要自定义 Chart.js 插件?** 答:仪表盘图表需要中心文本叠加层,但使用 CSS 实现,无需插件 2. ✅ **是否需要保留 ECharts 作为 devDependency?** 答:不需要,项目已决定完全移除 3. ⏳ **是否需要更新用户文档?** 答:图表功能对用户透明,无需更新文档 4. ⏳ **是否需要通知后端团队?** 答:纯前端技术栈变更,无需通知