Files
EmailBill/openspec/changes/migrate-to-chartjs/design.md
SunCheng 9921cd5fdf chore: migrate remaining ECharts components to Chart.js
- Migrated 4 components from ECharts to Chart.js:
  * MonthlyExpenseCard.vue (折线图)
  * DailyTrendChart.vue (双系列折线图)
  * ExpenseCategoryCard.vue (环形图)
  * BudgetChartAnalysis.vue (仪表盘 + 多种图表)

- Removed all ECharts imports and environment variable switches
- Unified all charts to use BaseChart.vue component
- Build verified: pnpm build success ✓
- No echarts imports remaining ✓

Refs: openspec/changes/migrate-remaining-echarts-to-chartjs
2026-02-16 21:55:38 +08:00

166 lines
6.1 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.
## Context
EmailBill 是一个移动端预算追踪应用,使用 Vue 3 + Vite + Vant UI 构建。当前使用 ECharts 6.0 作为图表库,涵盖了以下图表类型:
- **仪表盘Gauge**:预算健康度展示
- **折线图Line**:日趋势、燃尽图
- **柱状图Bar**:月度对比、方差分析
- **饼图Pie**:分类统计
**约束条件**
- 必须保持所有图表的现有功能和交互逻辑不变
- 必须适配移动端触控交互tap, pinch, swipe
- 必须兼容 Vant UI 的主题系统(支持暗色模式)
- 必须保持现有的响应式布局
## Goals / Non-Goals
**Goals:**
- 使用 Chart.js 替换 ECharts减少 bundle 体积约 600KB
- 提升图表渲染性能和动画流畅度
- 统一图表配色方案,使用更现代化的视觉风格
- 提供通用的 Chart.js 封装组件,便于后续扩展
**Non-Goals:**
- 不改变现有业务逻辑和数据流
- 不添加新的图表类型或功能
- 不重构非图表相关的组件
- 不改变图表的数据格式(仅转换配置项)
## Decisions
### 1. 图表库选择Chart.js vs Recharts vs Victory
**决策**:使用 **Chart.js 4.x + vue-chartjs 5.x**
**理由**
- **包体积**Chart.js (~200KB) << ECharts (~800KB)
- **Vue 集成**vue-chartjs 提供了开箱即用的 Composition API 支持
- **移动端优化**原生支持触控手势HammerJS 集成
- **社区成熟度**GitHub 66k+ stars文档完善
- **主题定制**:支持 CSS 变量集成,易于适配 Vant 主题
**替代方案**
- RechartsReact 生态,不适用
- Victory包体积更大学习曲线陡峭
- uCharts功能较简单扩展性不足
### 2. 组件封装策略:包装器 vs 直接使用
**决策**:创建通用包装器组件 `BaseChart.vue`
**理由**
- 统一主题配置(颜色、字体、动画)
- 统一响应式处理resize observer
- 统一错误边界和加载状态
- 减少重复代码4 个组件共享配置)
**实现**
```vue
<template>
<div class="base-chart" ref="chartContainer">
<component
:is="chartComponent"
:data="chartData"
:options="mergedOptions"
/>
</div>
</template>
<script setup>
import { Line, Bar, Pie, Doughnut } from 'vue-chartjs'
// 统一主题配置
const baseOptions = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { /* Vant 主题配色 */ }
}
}
</script>
```
### 3. 图表类型映射
| ECharts 类型 | Chart.js 类型 | 组件 |
|-------------|--------------|------|
| gauge (仪表盘) | doughnut + 自定义插件 | BudgetChartAnalysis.vue |
| line (折线图) | line | DailyTrendChart.vue, Burndown |
| bar (柱状图) | bar | MonthlyExpenseCard.vue, Variance |
| pie (饼图) | pie | ExpenseCategoryCard.vue |
**特殊处理**
- **仪表盘**Chart.js 无原生 gauge使用 Doughnut + 自定义 centerText 插件模拟
- **燃尽图**:使用双 Y 轴配置(理想线 + 实际线)
### 4. 迁移顺序
**阶段 1**基础设施1-2 小时)
1. 安装依赖:`pnpm add chart.js vue-chartjs`
2. 创建 `BaseChart.vue` 和主题配置文件
3. 创建 Gauge 插件(仪表盘专用)
**阶段 2**组件迁移3-4 小时)
1. MonthlyExpenseCard.vue柱状图最简单
2. ExpenseCategoryCard.vue饼图
3. DailyTrendChart.vue折线图
4. BudgetChartAnalysis.vue5 个图表,最复杂)
**阶段 3**验证与清理1 小时)
1. 功能测试(所有图表交互)
2. 视觉回归测试(截图对比)
3. 移除 ECharts 依赖
4. 构建产物分析(验证体积优化)
## Risks / Trade-offs
### 风险 1仪表盘实现复杂度
**[风险]** Chart.js 无原生 gauge 支持,需要自定义插件
**→ 缓解措施**:使用社区验证的 centerText 插件方案(参考 Chart.js Doughnut with center text预先实现并测试
### 风险 2动画效果差异
**[风险]** Chart.js 的默认动画可能与 ECharts 不一致,影响用户体验
**→ 缓解措施**:保留 ECharts 动画时长和缓动函数配置Chart.js 支持 `animation.duration``easing` 自定义
### 风险 3暗色模式适配
**[风险]** Vant 暗色模式下,图表颜色需要动态切换
**→ 缓解措施**:使用 CSS 变量(`var(--van-text-color)`Chart.js 配置支持响应式更新
### 风险 4性能回归
**[风险]** 大数据量场景下(如年度数据 365 个点),性能可能不如预期
**→ 缓解措施**
- 启用 Chart.js 的 `decimation` 插件(数据抽样)
- 使用 `parsing: false` 跳过数据解析
- 移动端限制数据点上限(最多 100 个)
### Trade-off功能丰富度 vs 包体积
**[取舍]** Chart.js 功能不如 ECharts 全面(如 3D 图表、地图)
**→ 项目影响**EmailBill 仅使用基础图表类型,不受影响;未来如需高级图表,可按需引入 ECharts 特定模块
## Migration Plan
### 部署策略
1. **Feature Flag**:使用环境变量 `VITE_USE_CHARTJS=true` 控制新旧图表切换
2. **灰度发布**:先在测试环境验证 1 周观察性能指标Lighthouse 分数、FCP
3. **回滚方案**:保留 ECharts 代码至少 1 个版本,通过 Git revert 快速回滚
### 验证指标
- **包体积**`pnpm build``dist/` 大小减少 > 500KB
- **性能**Lighthouse Performance 分数提升 > 5 分
- **功能**:所有图表的交互测试通过(手动测试清单见 `docs/chart-migration-checklist.md`
### 回滚触发条件
- 任何核心图表功能失效(如仪表盘无法显示)
- Lighthouse 性能分数下降 > 3 分
- 用户报告严重视觉 Bug如图表错位、颜色错误
## Open Questions
1. **是否需要支持图表导出功能?**
Chart.js 支持 `toBase64Image()` 导出 PNGECharts 支持 SVG 导出。如果需要矢量图导出,需额外集成 `chartjs-plugin-export`
2. **是否保留图表动画?**
移动端用户可能更关注首屏加载速度。可考虑通过 `prefers-reduced-motion` 媒体查询禁用动画。
3. **是否需要国际化i18n**
Chart.js 的日期格式化依赖 `date-fns``dayjs`。项目已使用 `dayjs`,可直接集成。