在开发项目过程中需要封装echarts组件,方便多次使用,以下是封装的详细过程。
前置动作:安装echarts以及resize-observer-polyfill插件
import * as echarts from "echarts/core";
/**
* 这里按需引入使用到的图表类型
*/
import { BarChart, LineChart, type LineSeriesOption, PieChart, type PieSeriesOption } from "echarts/charts";
/**
* 这里按需引入使用到的配置
* 特别地 包括MarkPoint markLine等这些也要单独使用 不然在图表中无法显示
*/
import {
LegendComponent,
type LegendComponentOption,
TitleComponent,
// 组件类型的定义后缀都为 ComponentOption
type TitleComponentOption,
TooltipComponent,
type TooltipComponentOption,
GridComponent,
type GridComponentOption,
DataZoomComponent,
type DataZoomComponentOption,
// 数据集组件
DatasetComponent,
type DatasetComponentOption,
// 标记组件
MarkAreaComponent,
type MarkAreaComponentOption,
MarkLineComponent,
type MarkLineComponentOption,
MarkPointComponent,
type MarkPointComponentOption,
// 内置数据转换器组件 (filter, sort)
TransformComponent,
} from "echarts/components";
import { LabelLayout, UniversalTransition } from "echarts/features";
import { CanvasRenderer } from "echarts/renderers";
import { ref, shallowRef, onMounted, onBeforeUnmount } from "vue";
import ResizeObserver from "resize-observer-polyfill";
import throttle from "lodash/throttle";
/**
* 引入的配置通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型
*/
export type ECOption = echarts.ComposeOption<
| LineSeriesOption
| PieSeriesOption
| LegendComponentOption
| TitleComponentOption
| TooltipComponentOption
| DataZoomComponentOption
| GridComponentOption
| DatasetComponentOption
| MarkAreaComponentOption
| MarkLineComponentOption
| MarkPointComponentOption
>;
/**
* 注册按需引入的组件
*/
echarts.use([
LegendComponent,
TitleComponent,
TooltipComponent,
GridComponent,
DataZoomComponent,
DatasetComponent,
MarkAreaComponent,
MarkLineComponent,
MarkPointComponent,
TransformComponent,
BarChart,
LineChart,
PieChart,
LabelLayout,
UniversalTransition,
CanvasRenderer,
]);
export default function useChart() {
const canvasEl = shallowRef();
const myChart = shallowRef();
/**
* 监听容器宽度变化或者浏览器窗口变化对echarts图表进行自动展示
* 不用window.addEventListener("resize", resizeFn)的原因是这个方法只是监听浏览器窗口的不变化
* -对所在容器的大小变化不进行监听 很难满足开发场景的需求
*/
const resizeObserver = ref<ResizeObserver>();
const bindResize = () => {
const deboundResize = throttle(() => {
myChart.value?.resize();
}, 500);
resizeObserver.value = new ResizeObserver(() => {
deboundResize();
// myChart.value?.resize();
});
resizeObserver.value.observe(canvasEl.value);
};
// 解绑 resize
const unbindResize = () => {
resizeObserver.value?.unobserve(canvasEl.value);
};
onMounted(() => {
/**
* 注册echarts并开启监听容器大小变化
*/
myChart.value = echarts.init(canvasEl.value);
bindResize();
});
onBeforeUnmount(() => {
/**
* 销毁监听和echarts
*/
unbindResize();
myChart.value?.dispose();
myChart.value = null;
});
return {
myChart,
canvasEl,
};
}
<template>
<div>
<div class="chart-container" :style="containerStyle">
<div v-show="dataEmptyFlag" class="chart-empty">
<span class="empty-title">暂无数据</span>
</div>
<div ref="canvasEl" :style="containerStyle" />
<div v-show="loading" class="chart-loading"><Spin /></div>
</div>
</div>
</template>
<script setup lang="ts">
import { Spin } from "ant-design-vue";
import { ref, watch } from "vue";
import useChart from "./useECharts";
import type { ECOption } from "./useECharts";
interface ChartProps {
containerStyle?: any;
loading?: boolean;
dataEmptyFlag?: boolean;
options?: ECOption;
}
const props = withDefaults(defineProps<ChartProps>(), {
containerStyle: {
height: "990px",
width: "100%",
},
loading: false,
dataEmptyFlag: false,
});
const { myChart, canvasEl } = useChart();
watch(
() => props?.options,
(cur) => {
if (myChart) {
/**
* 这里绘制图表前先清空上一次数据 否则有时候会出现数据残余的情况
*/
myChart.value.clear();
myChart.value.setOption(cur);
}
},
);
</script>
<style scoped>
.chart-container {
position: relative;
width: 100%;
}
.chart-loading {
align-items: center;
background-color: #ffffff;
bottom: 0;
display: flex;
justify-content: center;
left: 0;
position: absolute;
right: 0;
top: 0;
z-index: 1999;
}
.chart-empty {
position: absolute;
z-index: 1;
left: 0;
right: 0;
top: 0;
bottom: 0;
color: #888;
font-size: 14px;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.empty-title {
font-size: 16px;
font-weight: 500;
color: #000;
}
</style>
/**
* 参入对应的参数以及图表配置项
*/
<anewCharts :loading="loading" :data-empty-flag="dataEmptyFlag" :containerStyle="chartStyle" :options="chartOptions"></anewCharts>