From 726abe42083d1382f3230bd797bb406b4700b5d6 Mon Sep 17 00:00:00 2001 From: Soybean Date: Fri, 19 Jan 2024 02:17:17 +0800 Subject: [PATCH] feat(hooks): add use-echarts --- package.json | 1 + pnpm-lock.yaml | 20 ++++ src/hooks/chart/use-echarts.ts | 184 +++++++++++++++++++++++++++++++++ 3 files changed, 205 insertions(+) create mode 100644 src/hooks/chart/use-echarts.ts diff --git a/package.json b/package.json index 4ac1e05d..a568d7e4 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "@vueuse/core": "10.7.2", "clipboard": "2.0.11", "dayjs": "1.11.10", + "echarts": "^5.4.3", "lodash-es": "4.17.21", "naive-ui": "2.37.3", "nprogress": "0.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 231665e3..5b30f17f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,6 +38,9 @@ importers: dayjs: specifier: 1.11.10 version: 1.11.10 + echarts: + specifier: ^5.4.3 + version: 5.4.3 lodash-es: specifier: 4.17.21 version: 4.17.21 @@ -3541,6 +3544,13 @@ packages: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} dev: true + /echarts@5.4.3: + resolution: {integrity: sha512-mYKxLxhzy6zyTi/FaEbJMOZU1ULGEQHaeIeuMR5L+JnJTpz+YR03mnnpBhbR4+UYJAgiXgpyTVLffPAjOTLkZA==} + dependencies: + tslib: 2.3.0 + zrender: 5.4.4 + dev: false + /electron-to-chromium@1.4.637: resolution: {integrity: sha512-G7j3UCOukFtxVO1vWrPQUoDk3kL70mtvjc/DC/k2o7lE0wAdq+Vwp1ipagOow+BH0uVztFysLWbkM/RTIrbK3w==} dev: true @@ -7979,6 +7989,10 @@ packages: typescript: 5.3.3 dev: true + /tslib@2.3.0: + resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==} + dev: false + /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} dev: true @@ -8855,3 +8869,9 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} dev: true + + /zrender@5.4.4: + resolution: {integrity: sha512-0VxCNJ7AGOMCWeHVyTrGzUgrK4asT4ml9PEkeGirAkKNYXYzoPJCLvmyfdoOXcjTHPs10OZVMfD1Rwg16AZyYw==} + dependencies: + tslib: 2.3.0 + dev: false diff --git a/src/hooks/chart/use-echarts.ts b/src/hooks/chart/use-echarts.ts new file mode 100644 index 00000000..b74bfd1c --- /dev/null +++ b/src/hooks/chart/use-echarts.ts @@ -0,0 +1,184 @@ +import { effectScope, nextTick, onScopeDispose, ref, watch } from 'vue'; +import type { ComputedRef, Ref } from 'vue'; +import * as echarts from 'echarts/core'; +import { BarChart, GaugeChart, LineChart, PictorialBarChart, PieChart, RadarChart, ScatterChart } from 'echarts/charts'; +import type { + BarSeriesOption, + GaugeSeriesOption, + LineSeriesOption, + PictorialBarSeriesOption, + PieSeriesOption, + RadarSeriesOption, + ScatterSeriesOption +} from 'echarts/charts'; +import { + DatasetComponent, + GridComponent, + LegendComponent, + TitleComponent, + ToolboxComponent, + TooltipComponent, + TransformComponent +} from 'echarts/components'; +import type { + DatasetComponentOption, + GridComponentOption, + LegendComponentOption, + TitleComponentOption, + ToolboxComponentOption, + TooltipComponentOption +} from 'echarts/components'; +import { LabelLayout, UniversalTransition } from 'echarts/features'; +import { CanvasRenderer } from 'echarts/renderers'; +import { useElementSize } from '@vueuse/core'; + +export type ECOption = echarts.ComposeOption< + | BarSeriesOption + | LineSeriesOption + | PieSeriesOption + | ScatterSeriesOption + | PictorialBarSeriesOption + | RadarSeriesOption + | GaugeSeriesOption + | TitleComponentOption + | LegendComponentOption + | TooltipComponentOption + | GridComponentOption + | ToolboxComponentOption + | DatasetComponentOption +>; + +echarts.use([ + TitleComponent, + LegendComponent, + TooltipComponent, + GridComponent, + DatasetComponent, + TransformComponent, + ToolboxComponent, + BarChart, + LineChart, + PieChart, + ScatterChart, + PictorialBarChart, + RadarChart, + GaugeChart, + LabelLayout, + UniversalTransition, + CanvasRenderer +]); + +interface ChartHooks { + onRender?: (chart: echarts.ECharts) => void | Promise; + onDestroy?: (chart: echarts.ECharts) => void | Promise; +} + +/** + * use echarts + * + * @param options echarts options + * @param darkMode dark mode + */ +export function useEcharts(options: ECOption, darkMode: Ref | ComputedRef, hooks?: ChartHooks) { + const scope = effectScope(); + + const domRef = ref(null); + + const initialSize = { width: 0, height: 0 }; + const { width, height } = useElementSize(domRef, initialSize); + + let chart: echarts.ECharts | null = null; + + function canRender() { + return initialSize.width > 0 && initialSize.height > 0; + } + + function isRendered() { + return Boolean(domRef.value && chart); + } + + function setOptions(opts: ECOption) { + if (isRendered()) { + chart?.clear(); + chart?.setOption({ ...opts, backgroundColor: 'transparent' }); + } + } + + async function render() { + if (domRef.value) { + const chartTheme = darkMode.value ? 'dark' : 'light'; + + await nextTick(); + + chart = echarts.init(domRef.value, chartTheme); + + setOptions(options); + + await hooks?.onRender?.(chart); + } + } + + function resize() { + chart?.resize(); + } + + async function destroy() { + if (!chart) return; + + await hooks?.onDestroy?.(chart); + chart?.dispose(); + chart = null; + } + + async function changeTheme() { + await destroy(); + await render(); + } + + /** + * render chart by size + * + * @param w + * @param h + */ + async function renderChartBySize(w: number, h: number) { + initialSize.width = w; + initialSize.height = h; + + // size is abnormal, destroy chart + if (!canRender()) { + await destroy(); + + return; + } + + // render chart + if (!isRendered()) { + await render(); + return; + } + + // resize chart + resize(); + } + + scope.run(() => { + watch([width, height], ([newWidth, newHeight]) => { + renderChartBySize(newWidth, newHeight); + }); + + watch(darkMode, () => { + changeTheme(); + }); + }); + + onScopeDispose(() => { + destroy(); + scope.stop(); + }); + + return { + domRef, + setOptions + }; +}