zhangxiao
2024-08-20 e47b788ff5f5c699c682999c95da17eb284ca21d
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
<template>
    <div
        ref="baseChart"
        class="base-chart"
        :style="{
            width: props.width,
            height: props.height
        }"
    ></div>
</template>
<script lang="ts" setup name="TheChart">
import echarts from "@/config/echarts/index";
import chinaJSON from "@/config/echarts/map/chinaChange.json";
import eventBus, { EVENT_CHART_RESIZE } from "@/utils/tools/event-bus";
import theme from "@/views/utils/theme";
import { cloneDeepWith, isString, debounce } from "lodash-es";
import i18n from "@/locales";
import { useLocale } from "@/hooks/useLocale";
import { useTheme } from "@/hooks/useTheme";
 
const { currentLocale } = useLocale();
const { currentTheme } = useTheme();
 
const props = withDefaults(
    defineProps<{
        option: Record<string, any>;
        width?: string | number;
        height?: string | number;
        isMap?: boolean;
        isDark?: boolean;
        id?: string;
    }>(),
    {
        width: "100%",
        height: "100%",
        isMap: false,
        isDark: undefined,
        id: ""
    }
);
 
const baseChart = ref(null);
let charts = <echarts.ECharts | null>null;
 
const emits = defineEmits<{
    (e: "init", value: echarts.ECharts): void;
    (e: "click", value: any): void;
}>();
 
watch(
    [() => props.option, baseChart],
    (v1) => {
        if (v1[0] && v1[1]) {
            nextTick(() => {
                initChart();
            });
        }
    },
    {
        deep: true,
        immediate: true
    }
);
 
const resizeChart = debounce(() => {
    charts?.resize();
}, 150);
 
function initChart() {
    if (!props.option) {
        return;
    }
    /**
     * 根据 chartsType 判断是当前图表类型是否是地图(map)
     * 注意:该动作必须放在 init 之前执行
     */
    if (props.isMap) {
        //初始化地图
        echarts.registerMap("china", chinaJSON);
    }
 
    // 基于准备好的dom,初始化echarts实例
    if (!baseChart.value) {
        console.error("base-chart元素不存在");
        return;
    }
    if (charts) {
        charts.dispose?.();
    }
    let themeMode = "";
    if (props.isDark !== undefined) {
        themeMode = props.isDark ? "dark" : "light";
    } else {
        themeMode = currentTheme.value;
    }
    charts = echarts.init(baseChart.value, themeMode, { renderer: "svg" });
    const cloneOption = cloneDeepWith(props.option, (v, k) => {
        //适配深色模式
        if (isString(v) && v.startsWith("var(--")) {
            return theme.getCustom(v.replaceAll("var(", "").replaceAll(")", ""));
        }
        //适配国际化
        if (isString(v) && v.startsWith("$t(")) {
            const regex = /\$t\('(.+?)'\)/;
            const match = v.match(regex);
            if (match) {
                const result = match[1]; // 获取匹配到的第一个括号内的内容
                return result;
                // return i18n.global.t(result);
            }
        }
        //适配国际化
        if (isString(k) && ["name"].includes(k)) {
            return v;
            // return i18n.global.t(v);
        }
    });
    charts.setOption(cloneOption);
    charts.on("click", (e) => {
        emits("click", e);
    });
    emits("init", charts);
 
    //ECharts自适应宽度(resize)
    // 监听窗口变化 - 多个图表同时刷新
    window.addEventListener("resize", resizeChart);
}
 
//监听图表宽高变化,一般由vue-draggable-resizable组件触发
eventBus.on(EVENT_CHART_RESIZE, (id: string) => {
    if (id && id === props.id) {
        //这里取消了防抖,优化体验
        charts?.resize();
        // resizeChart();
    }
});
 
watch([() => currentTheme.value, () => currentLocale.value], () => {
    nextTick(() => {
        initChart();
    });
});
 
defineExpose({
    setOption: (option: any) => {
        if (charts) {
            charts.setOption(option);
        }
    }
});
 
onUnmounted(() => {
    window.removeEventListener("resize", resizeChart);
});
</script>
<style lang="scss" scoped></style>