基于 ECharts 封装 MyChart 组件
项目需求中常常要做柱状图呀、折线图呀、雷达图呀等等,我们一般使用公司内部 UI 框架即可,很香。但某次搞出了个漏洞,也不知道是啥,我师父说升级就升级了,结果导致某些功能用升级后的 UI 框架无法实现,有些提单了运维也无法解决,他们说这是一个 bug,也不知道啥时候好,咱也不敢问。 最后才发现,还是原生最香。
@/components/MyChart.vue
<template>
<div
class="myChart"
:class="{ 'chart-has-height': height }"
:style="style"
></div>
</template>
<script>
import * as echarts from "echarts";
const events = [
"click",
"dbclick",
"mousedown",
"mouseup",
"moveover",
"mouseout",
];
export default {
props: {
op: {
type: Object,
required: true,
},
height: {
type: [String, Number],
},
autoResize: {
type: Boolean,
default: true,
},
},
computed: {
// 处理 echarts 高度
style() {
const { height } = this;
if (typeof height === "number" || /^\d+$/.test(height)) {
return { height: `${height}px` };
}
return { height };
},
},
watch: {
// 监听配置项的变化
op(val) {
if (!this.chart) return;
this.chart.setOption(val, true);
},
},
mounted() {
// 渲染 echarts
this.chart = echarts.init(this.$el);
this.chart.setOption(this.op);
events.forEach((event) => {
const opEvent = `on${this.firstToUpperCase(event)}`;
this.chart.on(event, (params) => {
this.$emit(`chart-${event}`, params);
// 兼容处理
if (typeof this.op[opEvent] === "function") {
this.op[opEvent](params);
}
});
});
window.addEventListener("resize", this.resize);
},
methods: {
// 分辨率变化,间隔时间内重新渲染 echarts
resize() {
if (!this.timer || !this.autoResize || !this.chart) return;
this.timer = setTimeout(() => {
delete this.timer;
this.chart.resize();
}, 1000 / 24);
},
// 获取 DOM
getChart() {
return this.chart;
},
// 首字母转为大写 click:Click
firstToUpperCase(str = "") {
return str.replace(/./, ($1) => $1.toUpperCase());
},
},
// 组件销毁前,移除 resize 事件
beforeDestroy() {
window.removeEventListener("resize", this.resize);
},
};
</script>
<style lang="less" scoped>
.chart {
height: 100%;
&:not(.chart-has-height) {
flex: 1;
}
}
</style>
一、基于 MyChart 组件,实现一个柱状图
<template>
<div class="echartBar">
<MyChart :op="op" :height="'100%'" @chart-click="chartClick"></MyChart>
</div>
</template>
<script>
import MyChart from "@/components/MyChart.vue";
export default {
components: {
MyChart,
},
data() {
return {};
},
computed: {
op() {
return {
// X轴样式
xAxis: {
type: "category",
data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
},
// Y轴样式
yAxis: {
type: "value",
},
// 柱条样式
series: [
{
data: [120, 200, 150, 80, 70, 110, 130],
type: "bar",
barWidth: 30,
showBackground: true,
backgroundStyle: {
color: "rgba(180, 180, 180, 0.2)",
},
},
],
// 内置弹窗
tooltip: {
show: true,
},
};
},
},
methods: {
// 柱状图点击事件
chartClick(params) {
console.log(params);
},
},
};
</script>
<style lang="less" scoped>
.echartBar {
width: 400px;
height: 300px;
}
</style>
二、基于 MyChart 组件,实现一个折线图
<template>
<div class="echartLine">
<MyChart :op="op" :height="'100%'" @chart-click="chartClick"></MyChart>
</div>
</template>
<script>
import MyChart from "@/components/MyChart.vue";
export default {
components: {
MyChart,
},
data() {
return {};
},
computed: {
op() {
return {
// 标题
title: {
text: "Stacked Line",
},
// 弹窗
tooltip: {
trigger: "axis",
},
legend: {
data: ["Email", "Union Ads"],
},
// 边距
grid: {
left: "3%",
right: "4%",
bottom: "3%",
containLabel: true,
},
// 保存为图片
toolbox: {
feature: {
saveAsImage: {},
},
},
// X 轴
xAxis: {
type: "category",
boundaryGap: false,
data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
},
// Y 轴
yAxis: {
type: "value",
},
// 折线图
series: [
{
name: "Email",
type: "line",
stack: "Total",
data: [120, 132, 101, 134, 90, 230, 210],
},
{
name: "Union Ads",
type: "line",
stack: "Total",
data: [220, 182, 191, 234, 290, 330, 310],
},
],
};
},
},
methods: {
// 折线图点击事件
chartClick(params) {
console.log(params);
},
},
};
</script>
<style lang="less" scoped>
.echartLine {
width: 400px;
height: 300px;
}
</style>
三、基于 MyChart 组件,实现一个雷达图
<template>
<div class="echartRadar">
<MyChart :op="op" :height="'100%'" @chart-click="chartClick"></MyChart>
</div>
</template>
<script>
import MyChart from "@/components/MyChart.vue";
export default {
components: {
MyChart,
},
data() {
return {};
},
computed: {
op() {
return {
radar: {
shape: "circle", // 雷达图绘制类型, 有 'polygon' 和 'circle'
center: ["50%", "50%"], // 中心坐标
radius: "50%", // 半径
indicator: [
// 指定雷达图中的多个变量(维度)
{ name: "Sales", max: 6500 },
{ name: "Administration", max: 16000 },
{ name: "Information Technology", max: 30000 },
{ name: "Customer Support", max: 38000 },
{ name: "Development", max: 52000 },
{ name: "Marketing", max: 25000 },
],
startAngle: 60, // 第一个指示器轴的角度
},
series: [
{
name: "Budget vs spending",
type: "radar",
data: [
{
value: [4200, 3000, 20000, 35000, 50000, 18000],
name: "Allocated Budget",
},
{
value: [5000, 14000, 28000, 26000, 42000, 21000],
name: "Actual Spending",
},
],
},
],
};
},
},
methods: {
// 雷达图点击事件
chartClick(params) {
console.log(params);
},
},
};
</script>
<style lang="less" scoped>
.echartRadar {
width: 400px;
height: 300px;
}
</style>