* chore: update deps * feat: use jsonc/x language * chore: update eslint 10.0 * fix: no-useless-assignment * feat: add CLAUDE.md * chore: ignore * feat: claude * fix: lint * chore: suppot eslint v10 * fix: lint * fix: lint * fix: type check * fix: unit test * fix: Suggested fix * fix: unit test * chore: update stylelint v17 * chore: update all major deps * fix: echarts console warn * chore: update vitest v4 * feat: add skills ignores * chore: update deps * chore: update deps * fix: cspell * chore: update deps * chore: update tailwindcss v4 * chore: remove postcss config * fix: no use catalog * chore: tailwind v4 config * fix: tailwindcss v4 sort * feat: use eslint-plugin-better-tailwindcss * fix: Interference between enforce-consistent-line-wrapping, jsx-curly-brace-presence and Prettier * fix: Interference between enforce-consistent-line-wrapping, jsx-curly-brace-presence and Prettier * fix(lint): resolve prettier and better-tailwindcss formatting conflicts * fix(tailwind): update theme references and lint sources * style(format): normalize apps docs and playground vue files * style(format): normalize core ui-kit components * style(format): normalize effects ui and layout components
202 lines
4.8 KiB
TypeScript
202 lines
4.8 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,
|
||
onActivated,
|
||
onBeforeUnmount,
|
||
onDeactivated,
|
||
onMounted,
|
||
ref,
|
||
unref,
|
||
watch,
|
||
} from 'vue';
|
||
|
||
import { usePreferences } from '@vben/preferences';
|
||
|
||
import {
|
||
tryOnUnmounted,
|
||
useDebounceFn,
|
||
useResizeObserver,
|
||
useTimeoutFn,
|
||
useWindowSize,
|
||
} from '@vueuse/core';
|
||
|
||
import echarts from './echarts';
|
||
|
||
type EchartsUIType = typeof EchartsUI | undefined;
|
||
|
||
type EchartsThemeType = 'dark' | 'light' | null;
|
||
|
||
function useEcharts(chartRef: Ref<EchartsUIType>) {
|
||
let chartInstance: echarts.ECharts | null = null;
|
||
let cacheOptions: EChartsOption = {};
|
||
// echarts是否处于激活状态
|
||
const isActiveRef = ref(false);
|
||
|
||
const { isDark } = usePreferences();
|
||
const { height, width } = useWindowSize();
|
||
const resizeHandler: () => void = useDebounceFn(resize, 200);
|
||
|
||
const getChartEl = (): HTMLElement | null => {
|
||
const refValue = chartRef?.value as unknown;
|
||
if (!refValue) return null;
|
||
if (refValue instanceof HTMLElement) {
|
||
return refValue;
|
||
}
|
||
const maybeComponent = refValue as { $el?: HTMLElement };
|
||
return maybeComponent.$el ?? null;
|
||
};
|
||
|
||
onMounted(() => (isActiveRef.value = true));
|
||
onActivated(() => (isActiveRef.value = true));
|
||
onDeactivated(() => (isActiveRef.value = false));
|
||
onBeforeUnmount(() => (isActiveRef.value = false));
|
||
|
||
const isElHidden = (el: HTMLElement | null): boolean => {
|
||
if (!el) return true;
|
||
return el.offsetHeight === 0 || el.offsetWidth === 0;
|
||
};
|
||
|
||
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>> => {
|
||
if (!unref(isActiveRef)) {
|
||
return Promise.resolve(null);
|
||
}
|
||
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(() => {
|
||
const el = getChartEl();
|
||
if (isElHidden(el)) {
|
||
useTimeoutFn(async () => {
|
||
resolve(await renderEcharts(currentOptions));
|
||
}, 30);
|
||
return;
|
||
}
|
||
useTimeoutFn(() => {
|
||
if (!chartInstance || chartInstance?.getDom() !== el) {
|
||
chartInstance?.dispose();
|
||
const instance = initCharts();
|
||
if (!instance) return;
|
||
}
|
||
clear && chartInstance?.clear();
|
||
chartInstance?.setOption(currentOptions);
|
||
resolve(chartInstance);
|
||
}, 30);
|
||
});
|
||
});
|
||
};
|
||
|
||
const updateData = (
|
||
option: EChartsOption,
|
||
notMerge = false, // false = 合并(保留动画),true = 完全替换
|
||
lazyUpdate = false, // true 时不立即重绘,适合短时间内多次调用
|
||
): Promise<echarts.ECharts | null> => {
|
||
return new Promise((resolve) => {
|
||
nextTick(() => {
|
||
if (!chartInstance) {
|
||
// 还没初始化 → 当作首次渲染
|
||
renderEcharts(option).then(resolve);
|
||
return;
|
||
}
|
||
|
||
// 合并你原有的全局配置(比如 backgroundColor)
|
||
const finalOption = {
|
||
...option,
|
||
...getOptions.value,
|
||
};
|
||
|
||
chartInstance.setOption(finalOption, {
|
||
notMerge,
|
||
lazyUpdate,
|
||
// silent: true, // 如果追求极致性能可开启(关闭所有事件)
|
||
});
|
||
|
||
resolve(chartInstance);
|
||
});
|
||
});
|
||
};
|
||
|
||
function resize() {
|
||
const el = getChartEl();
|
||
if (isElHidden(el)) {
|
||
return;
|
||
}
|
||
chartInstance?.resize({
|
||
animation: {
|
||
duration: 300,
|
||
easing: 'quadraticIn',
|
||
},
|
||
});
|
||
}
|
||
|
||
watch([width, height], () => {
|
||
resizeHandler?.();
|
||
});
|
||
|
||
useResizeObserver(chartRef as never, resizeHandler);
|
||
|
||
watch([isDark, isActiveRef], () => {
|
||
if (chartInstance && unref(isActiveRef)) {
|
||
chartInstance.dispose();
|
||
initCharts();
|
||
renderEcharts(cacheOptions);
|
||
resize();
|
||
}
|
||
});
|
||
|
||
tryOnUnmounted(() => {
|
||
// 销毁实例,释放资源
|
||
chartInstance?.dispose();
|
||
});
|
||
return {
|
||
isActive: isActiveRef,
|
||
renderEcharts,
|
||
resize,
|
||
updateData,
|
||
getChartInstance: () => chartInstance,
|
||
};
|
||
}
|
||
|
||
export { useEcharts };
|
||
|
||
export type { EchartsUIType };
|