1.定义一个ChartCard.vue文件用来渲染echarts,主要是做echarts的初始化,定义画布大小,移动浏览器窗口大小要重新渲染等这些功能,方便后面直接使用这个组件
<template>
<div class="chart">
<div ref="main" :style="_getClsStyle"></div>
<slot></slot>
</div>
</template>
<script setup lang="ts">
import { ECharts, init } from "echarts";
import debounce from "lodash-es/debounce";
import {
ref,
Ref,
reactive,
computed,
onMounted,
onBeforeUnmount,
watch,
shallowRef,
nextTick,
defineExpose,
} from "vue";
const DEFAULT_THEME = reactive({
categoryAxis: {
axisLine: { show: false },
axisTick: { show: false },
splitLine: { show: false },
},
valueAxis: {
axisLine: { show: false },
},
tooltip: {
borderColor: "#ffffff",
},
line: {
smooth: true,
},
grid: {
containLabel: true,
left: 10,
right: 10,
},
});
const RESIZE_DELAY = ref(200);
const props = withDefaults(
defineProps<{
width: string;
height: string;
columns: number;
}>(),
{
width: "auto",
height: "400px",
columns: 1,
}
);
const bindResize = ref(false);
const _getClsStyle = computed(() => {
return { height: props.height };
});
let main: Ref<HTMLElement | any> = ref();
let instance = shallowRef<ECharts | any>(null);
const getInstance = () => {
if (!instance.value) {
instance.value = init(main.value, DEFAULT_THEME);
}
return instance.value;
};
const resize = () => {
instance.value && instance.value.resize();
};
const resizeHandler = debounce(resize, RESIZE_DELAY.value);
const addResizeListener = () => {
if (bindResize.value) return;
window.addEventListener("resize", resizeHandler);
bindResize.value = true;
};
const removeResizeListener = () => {
window.removeEventListener("resize", resizeHandler);
bindResize.value = false;
};
const initChart = () => {
getInstance();
addResizeListener();
};
onMounted(() => {
initChart();
});
const clean = () => {
if (bindResize.value) removeResizeListener();
instance.value && instance.value.dispose();
};
const setOption = function () {
instance.value && instance.value.setOption(...arguments);
};
const nextTickResize = () => {
nextTick(resize);
};
watch([props.width, props.height, props.columns], nextTickResize);
defineExpose({ setOption, instance });
onBeforeUnmount(() => {
clean();
});
</script>
<style lang="scss" scoped>
.chart {
width: 100%;
height: 100%;
position: relative;
}
</style>
2.可以定义一个公共配置属性文件chart.ts
export const chartConfig = {
render: function (instance: any, options: any) {
instance && instance.setOption(options, true);
return instance;
},
scaleSize: function (level: number, size: number) {
if (!size) {
const el = document.querySelector(".chart div");
if (el) {
size = el.clientHeight;
} else {
return level;
}
}
const baseSize = 400;
const minSize = 10;
const num = level * (size / baseSize);
const result = num % 2 === 0 ? num : num + 1;
return result < minSize ? minSize : result;
},
formatDataZoom: function (data: Array<any>, count: number) {
const dataZoom = [
{
filterMode: "empty",
bottom: 0,
type: "slider",
height: "9px",
show: true,
zoomLock: true,
start: 0,
end: 50,
backgroundColor: "#FFFFFF",
borderColor: "#FFFFFF",
handleStyle: {
color: "rgba(0,0,0,0)",
},
textStyle: {
color: "rgba(0,0,0,0)",
height: "10px",
},
fillerColor: "#EEEEEE",
dataBackground: {
areaStyle: {
color: "rgba(0,0,0,0)",
},
lineStyle: {
color: "rgba(0,0,0,0)",
},
},
},
];
if (!count) count = 15;
if (data.length > count) {
dataZoom[0].show = true;
} else {
dataZoom[0].show = false;
dataZoom[0].end = 100;
}
return dataZoom;
},
formatTooltip: function (newTooltip?: any) {
const tooltip = {
trigger: "axis",
axisPointer: {
type: "line",
},
borderWidth: 0,
backgroundColor: "rgba(111,111,111,.8)",
textStyle: {
color: "#fff",
},
};
return Object.assign({}, tooltip, newTooltip);
},
eventXAxis: function (echarts: any, xAxisTip: any) {
if (!echarts || !xAxisTip) return;
if (echarts._xAxisMouse) return;
echarts._xAxisMouse = true;
echarts.on("mouseover", (params: any) => {
if (params.componentType === "xAxis") {
const offsetX = params.event.event.offsetX;
const offsetY = params.event.event.offsetY;
xAxisTip.innerText = params.value;
xAxisTip.style.left = offsetX + "px";
xAxisTip.style.top = offsetY + 10 + "px";
xAxisTip.style.display = "block";
}
});
echarts.on("mouseout", () => {
xAxisTip.style.display = "none";
});
},
};
3.单独给某个echarts图配置属性,下面是我想做的环形图配置,定义为pie.ts,与下面那个vue文件搭配,你要做其他什么图,上面两个文件的内容都不需要做什么变化,主要就是单独创建这个文件并写配置和下面vue文件传入对应数据就行了
import { chartConfig } from "@/utils";
export default Object.assign({}, chartConfig, {
getOptions: function (options: any) {
const { data, color } = options;
const grid = {
left: "1%",
right: "4%",
bottom: "8%",
containLabel: true,
};
const series = [
{
name: "Access From",
type: "pie",
radius: ["50%", "70%"],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 2,
borderColor: "#fff",
borderWidth: 2,
},
label: {
show: false,
position: "center",
},
emphasis: {
label: {
show: true,
fontSize: 40,
fontWeight: "bold",
},
},
labelLine: {
show: false,
},
data,
},
];
return { color, grid, series };
},
});
4.最后一步,就是要实现的环形图,将要用到的数据传给pie.ts就行了
<template>
<div>
<chart-card ref="chart" :height="chartHeight">
<div ref="xAxisTip" class="xAxisTip"></div>
</chart-card>
</div>
</template>
<script setup lang="ts">
import ChartCard from "../ChartCard.vue";
import pie from "./pie";
import { ref, nextTick, computed, watch, onMounted } from "vue";
const props = withDefaults(
defineProps<{
chartHeight: string;
data: [];
color: [];
}>(),
{
chartHeight: "150px",
}
);
const chart = ref();
onMounted(() => {
renderChart();
});
const dealChartData = computed(() => {
return {
data: props.data,
color: props.color,
};
});
const renderChart = async () => {
await nextTick();
pie.render(chart.value, pie.getOptions(dealChartData.value));
};
watch(() => props.data, renderChart);
</script>
<style lang="scss" scoped></style>