150 lines
3.2 KiB
TypeScript
150 lines
3.2 KiB
TypeScript
import type { EChartsOption } from 'echarts';
|
||
|
||
import type { Ref } from 'vue';
|
||
|
||
import type { Nullable } from '@vben/types';
|
||
|
||
import type EchartsUI from './echarts-ui.vue';
|
||
|
||
import { computed, nextTick, watch } from 'vue';
|
||
|
||
import { usePreferences } from '@vben/preferences';
|
||
|
||
import {
|
||
tryOnUnmounted,
|
||
useDebounceFn,
|
||
useResizeObserver,
|
||
useTimeoutFn,
|
||
useWindowSize,
|
||
} from '@vueuse/core';
|
||
|
||
import echarts from './echarts';
|
||
// TODO @xingyu:有 500kb,china.json 会影响打包么?
|
||
import chinaMap2 from './map/china2.json';
|
||
import chinaMap from './map/china.json';
|
||
|
||
type EchartsUIType = typeof EchartsUI | undefined;
|
||
|
||
type EchartsThemeType = 'dark' | 'light' | null;
|
||
|
||
function useEcharts(chartRef: Ref<EchartsUIType>) {
|
||
let chartInstance: echarts.ECharts | null = null;
|
||
let cacheOptions: EChartsOption = {};
|
||
|
||
const { isDark } = usePreferences();
|
||
const { height, width } = useWindowSize();
|
||
const resizeHandler: () => void = useDebounceFn(resize, 200);
|
||
|
||
echarts.registerMap('china', {
|
||
geoJSON: chinaMap as any,
|
||
specialAreas: {
|
||
china: {
|
||
left: 500,
|
||
top: 500,
|
||
width: 1000,
|
||
height: 1000,
|
||
},
|
||
},
|
||
});
|
||
|
||
echarts.registerMap('china2', {
|
||
geoJSON: chinaMap2 as any,
|
||
specialAreas: {
|
||
china: {
|
||
left: 500,
|
||
top: 500,
|
||
width: 1000,
|
||
height: 1000,
|
||
},
|
||
},
|
||
});
|
||
|
||
const getOptions = computed((): EChartsOption => {
|
||
if (!isDark.value) {
|
||
return {};
|
||
}
|
||
|
||
return {
|
||
backgroundColor: 'transparent',
|
||
};
|
||
});
|
||
|
||
const initCharts = (t?: EchartsThemeType) => {
|
||
const el = chartRef?.value?.$el;
|
||
if (!el) {
|
||
return;
|
||
}
|
||
chartInstance = echarts.init(el, t || isDark.value ? 'dark' : null);
|
||
|
||
return chartInstance;
|
||
};
|
||
|
||
const renderEcharts = (
|
||
options: EChartsOption,
|
||
clear = true,
|
||
): Promise<Nullable<echarts.ECharts>> => {
|
||
cacheOptions = options;
|
||
const currentOptions = {
|
||
...options,
|
||
...getOptions.value,
|
||
};
|
||
return new Promise((resolve) => {
|
||
if (chartRef.value?.offsetHeight === 0) {
|
||
useTimeoutFn(async () => {
|
||
resolve(await renderEcharts(currentOptions));
|
||
}, 30);
|
||
return;
|
||
}
|
||
nextTick(() => {
|
||
useTimeoutFn(() => {
|
||
if (!chartInstance) {
|
||
const instance = initCharts();
|
||
if (!instance) return;
|
||
}
|
||
clear && chartInstance?.clear();
|
||
chartInstance?.setOption(currentOptions);
|
||
resolve(chartInstance);
|
||
}, 30);
|
||
});
|
||
});
|
||
};
|
||
|
||
function resize() {
|
||
chartInstance?.resize({
|
||
animation: {
|
||
duration: 300,
|
||
easing: 'quadraticIn',
|
||
},
|
||
});
|
||
}
|
||
|
||
watch([width, height], () => {
|
||
resizeHandler?.();
|
||
});
|
||
|
||
useResizeObserver(chartRef as never, resizeHandler);
|
||
|
||
watch(isDark, () => {
|
||
if (chartInstance) {
|
||
chartInstance.dispose();
|
||
initCharts();
|
||
renderEcharts(cacheOptions);
|
||
resize();
|
||
}
|
||
});
|
||
|
||
tryOnUnmounted(() => {
|
||
// 销毁实例,释放资源
|
||
chartInstance?.dispose();
|
||
});
|
||
return {
|
||
renderEcharts,
|
||
resize,
|
||
getChartInstance: () => chartInstance,
|
||
};
|
||
}
|
||
|
||
export { useEcharts };
|
||
|
||
export type { EchartsUIType };
|