UNPKG

@oiij/e-charts

Version:

A Vue Composable for ECharts

187 lines (186 loc) 4.35 kB
import { createEventHook, useDebounceFn, useElementSize } from "@vueuse/core"; import { BarChart, LineChart, PieChart } from "echarts/charts"; import { DatasetComponent, GridComponent, LegendComponent, TitleComponent, ToolboxComponent, TooltipComponent, TransformComponent } from "echarts/components"; import { init, use } from "echarts/core"; import { LabelLayout, UniversalTransition } from "echarts/features"; import { CanvasRenderer } from "echarts/renderers"; import { nextTick, onUnmounted, ref, shallowRef, toRaw, toValue, watch, watchEffect } from "vue"; //#region src/_utils.ts function watchRefOrGetter(value, callback) { const refValue = ref(toValue(value)); watchEffect(() => { refValue.value = toValue(value); }); watch(refValue, (val) => { callback?.(val); }); return refValue; } //#endregion //#region src/index.ts /** * 基础图表类型 */ const BASE_CHARTS = [ BarChart, LineChart, PieChart ]; use([ TitleComponent, LegendComponent, TooltipComponent, GridComponent, DatasetComponent, TransformComponent, ToolboxComponent, LabelLayout, UniversalTransition, CanvasRenderer, ...BASE_CHARTS ]); /** * 注册 ECharts 扩展 * * @param ext - 扩展组件数组 * * @example * ```ts * import { register } from '@oiij/e-charts' * import { RadarChart } from 'echarts/charts' * * register([RadarChart]) * ``` */ function register(ext) { use(ext); } /** * 使用 ECharts * * @param templateRef - 图表容器的模板引用 * @param options - 图表选项 * @returns ECharts 实例和工具方法 * * @example * ```vue * <script setup> * import { ref } from 'vue' * import { useECharts } from '@oiij/e-charts' * * const chartRef = ref() * const { chartOption, onRendered } = useECharts(chartRef, { * chartOption: { * title: { * text: '示例图表' * }, * series: [ * { * type: 'bar', * data: [10, 20, 30, 40, 50] * } * ] * } * }) * * onRendered((instance) => { * console.log('图表渲染完成', instance) * }) * <\/script> * * <template> * <div ref="chartRef" style="width: 100%; height: 400px;"></div> * </template> * ``` */ function useECharts(templateRef, options) { const { chartOption, darkMode, initOptions } = options ?? {}; const eChartInst = shallowRef(null); const chartOptionRef = watchRefOrGetter(chartOption, () => setOption()); const darkModeRef = watchRefOrGetter(darkMode, () => setDarkMode()); const onRenderedEvent = createEventHook(); const onUpdateEvent = createEventHook(); const onResizeEvent = createEventHook(); const onDisposeEvent = createEventHook(); /** * 设置图表配置 * * @param option - 图表配置 */ function setOption(option) { if (option !== void 0) chartOptionRef.value = option; const chartOption = toRaw(chartOptionRef.value); if (chartOption) { eChartInst.value?.setOption(chartOption); onUpdateEvent.trigger(chartOption); } } /** * 更新图表主题 * * @param darkMode - 是否开启暗黑模式 */ function setDarkMode(darkMode) { if (darkMode !== void 0) darkModeRef.value = darkMode; destroy(); render(); } /** * 渲染图表 */ async function render() { if (templateRef.value && !eChartInst.value) { const theme = darkModeRef?.value ? "dark" : "default"; await nextTick(); eChartInst.value = init(templateRef.value, theme, { ...initOptions }); onRenderedEvent.trigger(eChartInst.value); setOption(); } } /** * 调整图表大小 * * @param width - 宽度 * @param height - 高度 */ function resize(width, height) { eChartInst.value?.resize(); onResizeEvent.trigger({ width, height }); } const debounceResize = useDebounceFn(resize, 100); const { width, height } = useElementSize(templateRef); watch([width, height], ([width, height]) => { if (width > 0 && height > 0) { debounceResize(width, height); render(); } }); /** * 销毁图表 */ function destroy() { eChartInst.value?.dispose(); eChartInst.value = null; onDisposeEvent.trigger(); } onUnmounted(() => { destroy(); }); return { templateRef, eChartInst, chartOption: chartOptionRef, darkMode: darkModeRef, setOption, setDarkMode, onRendered: onRenderedEvent.on, onUpdate: onUpdateEvent.on, onResize: onResizeEvent.on, onDispose: onDisposeEvent.on }; } //#endregion export { BASE_CHARTS, register, useECharts };