wujingjing
2025-02-12 9b3c556251698578e0f43e4d4d82f72d22a757bd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
<template>
    <div ref="chartRef" :style="{ width, height }" />
</template>
 
<script lang="ts" setup>
import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
import * as echarts from 'echarts/core';
import { BarChart, LineChart, PieChart } from 'echarts/charts';
import {
    TitleComponent,
    TooltipComponent,
    GridComponent,
    DatasetComponent,
    TransformComponent,
    LegendComponent,
} from 'echarts/components';
import { LabelLayout, UniversalTransition } from 'echarts/features';
import { CanvasRenderer } from 'echarts/renderers';
import type { ChartProps } from './types';
import { defaultChartConfig } from './config'
import { processNumberFormat } from './utils';
import { cloneDeep } from 'lodash-es';
 
// 按需注册必要的组件
echarts.use([
    BarChart,
    LineChart,
    PieChart,
    TitleComponent,
    TooltipComponent,
    GridComponent,
    DatasetComponent,
    TransformComponent,
    LabelLayout,
    UniversalTransition,
    CanvasRenderer,
    LegendComponent,
]);
 
const props = defineProps<ChartProps>();
 
const emit = defineEmits<{
    chartReady: [instance: echarts.ECharts];
}>();
 
const chartRef = ref<HTMLElement>();
let chartInstance: echarts.ECharts | null = null;
 
// 初始化图表
const initChart = () => {
    if (!chartRef.value) return;
    
    try {
        chartInstance = echarts.init(chartRef.value, props.theme);
        
        // 先应用默认配置
        if (Object.keys(defaultChartConfig).length > 0) {
            chartInstance.setOption(defaultChartConfig);
        }
        
        // 再应用用户配置
        let finalOptions = cloneDeep(props.options);
        if (props.extendedConfig?.enableNumberFormat) {
            finalOptions = processNumberFormat(finalOptions);
        }
        
        chartInstance.setOption(finalOptions, { notMerge: false });
        emit('chartReady', chartInstance);
    } catch (error) {
        console.error('Chart initialization failed:', error);
    }
};
 
// 监听options变化
watch(
    () => props.options,
    (newVal) => {
        if (!chartInstance) return;
        
        try {
            let finalOptions = cloneDeep(newVal);
            if (props.extendedConfig?.enableNumberFormat) {
                finalOptions = processNumberFormat(finalOptions);
            }
            
            chartInstance.setOption(finalOptions, { 
                notMerge: false,
                lazyUpdate: true
            });
        } catch (error) {
            console.error('Chart update failed:', error);
        }
    },
    { deep: true }
);
 
// 监听主题变化
watch(
    () => props.theme,
    () => {
        if (chartInstance) {
            chartInstance.dispose();
            initChart();
        }
    }
);
 
// 处理窗口大小变化
const handleResize = () => {
    chartInstance?.resize();
};
 
onMounted(() => {
    initChart();
    window.addEventListener('resize', handleResize);
});
 
onBeforeUnmount(() => {
    if (chartInstance) {
        chartInstance.dispose();
        chartInstance = null;
    }
    window.removeEventListener('resize', handleResize);
});
</script>