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:
113
Web/src/plugins/chartjs-gauge-plugin.ts
Normal file
113
Web/src/plugins/chartjs-gauge-plugin.ts
Normal 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], // 半圆形,总共 200(100% * 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]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user