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
This commit is contained in:
SunCheng
2026-02-16 21:55:38 +08:00
parent a88556c784
commit 9921cd5fdf
77 changed files with 6964 additions and 1632 deletions

View File

@@ -0,0 +1,113 @@
import { Plugin } from 'chart.js'
/**
* Chart.js Gauge 插件
* 在 Doughnut 图表中心显示文本(用于实现仪表盘效果)
*/
export interface GaugePluginOptions {
centerText?: {
label?: string
value?: string
labelColor?: string
valueColor?: string
labelFontSize?: number
valueFontSize?: number
}
}
export const chartjsGaugePlugin: Plugin = {
id: 'gaugePlugin',
afterDraw: (chart: any) => {
const { ctx, chartArea } = chart
if (!chartArea) return
const centerX = (chartArea.left + chartArea.right) / 2
const centerY = (chartArea.top + chartArea.bottom) / 2
// 从图表配置中获取插件选项
const pluginOptions = chart.options.plugins?.gaugePlugin as GaugePluginOptions | undefined
if (!pluginOptions?.centerText) return
const { label, value, labelColor, valueColor, labelFontSize, valueFontSize } = pluginOptions.centerText
ctx.save()
ctx.textAlign = 'center'
ctx.textBaseline = 'middle'
// 绘制标签
if (label) {
ctx.font = `${labelFontSize || 14}px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif`
ctx.fillStyle = labelColor || '#969799'
ctx.fillText(label, centerX, centerY - 20)
}
// 绘制值
if (value) {
ctx.font = `bold ${valueFontSize || 28}px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif`
ctx.fillStyle = valueColor || '#323233'
ctx.fillText(value, centerX, centerY + 10)
}
ctx.restore()
}
}
/**
* 创建仪表盘图表配置
* @param value 当前值
* @param limit 限额
* @param label 标签文字(如 "余额"、"差额"
* @param colors 颜色配置
*/
export function createGaugeConfig(
value: number,
limit: number,
label: string,
colors: { primary: string; danger: string; success: string; background: string }
) {
const percentage = limit > 0 ? Math.min((value / limit) * 100, 200) : 0
const remaining = Math.abs(limit - value)
const isOver = value > limit
// 确定颜色:超支使用 danger否则使用 primary
const activeColor = isOver ? colors.danger : colors.primary
return {
data: {
datasets: [
{
data: [percentage, 200 - percentage], // 半圆形,总共 200100% * 2
backgroundColor: [activeColor, colors.background],
borderWidth: 0,
circumference: 180, // 半圆
rotation: 270 // 从底部开始
}
]
},
options: {
cutout: '75%', // 内圈大小
plugins: {
legend: {
display: false
},
tooltip: {
enabled: false
},
gaugePlugin: {
centerText: {
label: label,
value: `¥${remaining.toFixed(0)}`,
labelColor: '#969799',
valueColor: isOver ? colors.danger : '#323233',
labelFontSize: 14,
valueFontSize: 24
}
}
}
},
plugins: [chartjsGaugePlugin]
}
}