@oiij/e-charts
Version:
A Vue Composable for ECharts
187 lines (186 loc) • 4.35 kB
JavaScript
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 };