可视化对于现在管理系统开发还是挺常见的,基本都会涉及一些可视化图表的展示。一般都是通过echarts
去开发的。
下面我们就来简单对 echarts 进行封装并举一些例子来带你快速开发可视化图表。
一般我们会创建一个配置文件引入当前项目中使用的图表类型,并进行注册。可以结合echarts 官网看这里。
简单封装
js
import * as echarts from "echarts/core";
import {
BarChart,
LineChart,
LinesChart,
MapChart,
PieChart,
EffectScatterChart,
ScatterChart,
GraphChart,
} from "echarts/charts";
import {
TitleComponent,
TooltipComponent,
GridComponent,
LegendComponent,
ToolboxComponent,
MarkLineComponent,
VisualMapComponent,
GeoComponent,
} from "echarts/components";
export type ECharts = echarts.ECharts;
import { CanvasRenderer } from "echarts/renderers";
// 进行注册
echarts.use([
CanvasRenderer,
PieChart,
BarChart,
LineChart,
LinesChart,
MapChart,
EffectScatterChart,
ScatterChart,
LegendComponent,
TitleComponent,
TooltipComponent,
GridComponent,
ToolboxComponent,
MarkLineComponent,
VisualMapComponent,
GeoComponent,
GraphChart,
]);
写一个使用图表的 hooks。大致的步骤如下:
- 我们可以通过 computed api 去缓存当前 echarts 配置。
- 通过
echarts.init
去初始化图表实例,并做响应式监听,当窗口改变,使用实例的resize
方法做图表响应式。 - 提供一个设置图表 options 的配置方法,可以先设置图表配置缓存,当整个组件加载完成再去渲染整个图表。
- 如果项目做主题切换,菜单折叠功能,我们还需要做些适配。这些状态都是全局的,所以通过 watch 监听对应变量就可以了。
js
watch(
() => themeStore,
(theme) => {
if (chartInstance) {
// 丢弃图表实例,重新创建
chartInstance.dispose();
initCharts(theme as "default");
setOptions(cacheOptions.value);
}
}
);
watch(() => collapseStore, () => {
setTimeout(() => {
resize();
}, 200);
});
- 最后组件卸载移除一些状态。
大致代码如下:
js
import type { EChartsOption } from "echarts";
import type { Ref } from "vue";
import { unref, nextTick, watch, computed, ref, onUnmounted } from "vue";
import echarts from "./echarts";
interface Fn<T = any, R = T> {
(...arg: T[]): R;
}
// 防抖
function deBounceFn(fn, delay = 200) {
return function () {
let timer = null;
if (timer) {
clearTimeout(timer);
}
setTimeout(() => {
fn();
}, delay);
};
}
export function useECharts(
elRef: Ref<HTMLDivElement>,
theme: "light" | "dark" | "default" = "default"
) {
// 图表实例
let chartInstance: echarts.ECharts | null = null;
// 响应式执行函数
let resizeFn: Fn = resize;
const cacheOptions = ref({}) as Ref<EChartsOption>;
let removeResizeFn: Fn = () => {};
resizeFn = deBounceFn(resize, 200);
// 缓存配置
const getOptions = computed(() => {
if (theme !== "dark") {
return cacheOptions.value as EChartsOption;
}
return {
backgroundColor: "transparent",
...cacheOptions.value,
} as EChartsOption;
});
// 初始化图表配置
function initCharts(t = theme) {
const el = unref(elRef);
if (!el || !unref(el)) {
return;
}
chartInstance = echarts.init(el, t);
window.addEventListener("resize", resizeFn);
const removeEvent = () => {
window.removeEventListener("resize", resizeFn);
};
removeResizeFn = removeEvent;
}
// 设置echart options
function setOptions(options: EChartsOption, clear = false) {
cacheOptions.value = options;
if (unref(elRef)?.offsetHeight === 0) {
setTimeout(() => {
setOptions(unref(getOptions));
}, 30);
return;
}
nextTick(() => {
setTimeout(() => {
// 如果未初始化先初始化
if (!chartInstance) {
initCharts("default");
if (!chartInstance) return;
}
clear && chartInstance?.clear();
chartInstance?.setOption(unref(getOptions), true);
}, 30);
});
}
// 图表响应式处理
function resize() {
chartInstance?.resize({
animation: {
duration: 300,
easing: "quadraticIn",
},
});
}
// TODO: 主题改变,菜单折叠,做响应式处理
// watch(
// () => themeStore,
// (theme) => {
// if (chartInstance) {
// // 丢弃图表实例,重新创建
// chartInstance.dispose();
// initCharts(theme as "default");
// setOptions(cacheOptions.value);
// }
// }
// );
// watch(() => collapseStore, () => {
// setTimeout(() => {
// resize();
// }, 200);
// });
// 组件卸载移除一些状态
onUnmounted(() => {
if (!chartInstance) return;
removeResizeFn();
chartInstance.dispose();
chartInstance = null;
});
function getInstance(): echarts.ECharts | null {
if (!chartInstance) {
initCharts("default");
}
return chartInstance;
}
return {
setOptions,
resize,
echarts,
getInstance,
};
}
一些图表的简单 demo
在介绍 echarts 图标之前,我们先来看数据如何和配置对象绑定,从而让代码看起来更舒服。
我们知道 echarts 渲染出一个图表需要设置很多配置,对于正常的程序员来说是不会将这个配置放在代码逻辑里面的。我们会提供一个配置文件将通过配置放在该文件中。在代码逻辑想办法去改变那些"变量"。
- 方式一:封装成函数,在外部调用传参。
- 方式二:直接提供配置对象,让外部获取属性的方式为其赋值。
折线与柱状图
js
<template>
<div class="echarts" ref="charts"></div>
</template>
<script lang="ts" setup>
import { onMounted, ref } from "vue";
import { lineBarEchartOption } from "./demo.data";
import { useECharts } from "./useSimpleEcharts";
const charts = ref(null);
const { setOptions } = useECharts(charts);
onMounted(() => {
setOptions(lineBarEchartOption);
});
</script>
<style scoped>
.echarts {
width: 100%;
height: 400px;
}
</style>

折线与折线图
js
<template>
<div class="echarts" ref="charts"></div>
</template>
<script lang="ts" setup>
import { onMounted, ref } from "vue";
import { DLineEchartOptionFactory } from "./demo.data";
import { useECharts } from "./useSimpleEcharts";
const charts = ref(null);
const { setOptions } = useECharts(charts);
onMounted(() => {
const chartOption = DLineEchartOptionFactory({
tooltip: {
trigger: "axis",
confine: true,
appendToBody: false,
valueFormatter: (value) => `${value}%`,
},
yAxis: [
{
axisLabel: {
formatter: "{value}%",
},
},
],
series: [
{
data: ["92", "52", "126", "125", "141", "64", "60", "36", "82", "45"],
},
{
data: [
"20.8",
"20.5",
"113.1",
"86.1",
"62.2",
"42.2",
"82.4",
"74.5",
"91.5",
"54.4",
],
},
],
});
setOptions(chartOption);
});
</script>
<style scoped>
.echarts {
width: 100%;
height: 400px;
}
</style>

柱状与柱状图
js
<template>
<div class="echarts" ref="charts"></div>
</template>
<script lang="ts" setup>
import { onMounted, ref } from "vue";
import { DBarChartOption } from "./demo.data";
import { useECharts } from "./useSimpleEcharts";
const charts = ref(null);
const { setOptions } = useECharts(charts);
onMounted(() => {
setOptions(DBarChartOption);
});
</script>
<style scoped>
.echarts {
width: 100%;
height: 400px;
}
</style>

饼图
js
<template>
<div class="echarts" ref="charts"></div>
</template>
<script lang="ts" setup>
import { onMounted, ref } from "vue";
import { productPredictEchartOption } from "./demo.data";
import { useECharts } from "./useSimpleEcharts";
const charts = ref(null);
const { setOptions } = useECharts(charts);
onMounted(() => {
const rich = {
name: {
color: "#4E5969",
fontSize: 12,
padding: [3, 0, 3, 3],
},
value: {
color: "#4E5666",
fontSize: 18,
padding: [0, 0, 0, 3],
},
labelName: {
color: "#4E5666",
align: "center",
fontSize: 12,
},
percent: {
color: "#4E5666",
fontSize: 12,
align: "center",
padding: [3, 0],
},
};
const data = [
{
name: "demo1",
value: 20,
},
{
name: "demo2",
value: 930,
},
{
name: "demo3",
value: 245,
},
{
name: "demo4",
value: 10000,
},
{
name: "demo5",
value: 2434,
},
{
name: "demo6",
value: 1256,
},
];
setOptions(productPredictEchartOption(data, rich));
});
</script>
<style scoped>
.echarts {
width: 100%;
height: 400px;
}
</style>

如果你只是简单实现这些图表,可以直接去代码仓库粘贴复制就行,可以快速搭建一个图表,只需要赋值对应的数据即可。
其他图表,可以参考一些echarts的demo,也可快速开发。
往期文章
- 近三个月的排错,原来的憧憬消失喽
- 带你从0开始了解vue3核心(运行时)
- 带你从0开始了解vue3核心(computed, watch)
- 带你从0开始了解vue3核心(响应式)
- 3w+字的后台管理通用功能解决方案送给你
- 入职之前,狂补技术,4w字的前端技术解决方案送给你(vue3 + vite )
专栏文章
最近也在学习nestjs,有一起小伙伴的@我哦。
已辞职,虽然丢了应届生身份,但是看清了一些人的嘴脸,技术还是被很多人认可的。 🎇🎉✨