0.1功能要求
多饼图共用一个legend,并且要求legend可以控制所有饼图。
如图所示:
1.legend可以控制某一块饼显隐
2.鼠标放上 饼图强调,并显示tip,移走隐藏
![image-20230524152340906](..\..\img\image-20230524152340906.png)
0.2实现思路
1.分析:
这四个饼图的legend都是一样的,数据格式也是一样的,只是具体数据内容不一样
2.思路
因此独立出来的图例实际上也是一个饼图,但是只显示legend,饼图给隐藏了,然后在操作legend的时候使用bus把操作的块传给了其他饼图,并执行函数同步操作,就形成了共用legend,并可操作其他饼图。
代码:
一、lengend和 饼图盒子代码
<template>
<el-card class="pie-card">
<div slot="header" class="clearfix">
<span>各能源消耗占比</span>
</div>
<div class="container">
<div class="pie-legend">
// legend 随便传入了一个饼图数据,为了获取相同的lengend
<PieLegend :pieData="pieData.总体" />
</div>
<div class="pie-container">
<div class="pie-box">
<MediumPie :title="cardTitle[0]" :pieData="pieData.总体" />
</div>
<div class="pie-box">
<MediumPie :title="cardTitle[1]" :pieData="pieData.一号炉" />
</div>
<div class="pie-box">
<MediumPie :title="cardTitle[2]" :pieData="pieData.二号炉" />
</div>
<div class="pie-box">
<MediumPie :title="cardTitle[3]" :pieData="pieData.三号炉" />
</div>
</div>
</div>
</el-card>
</template>
<script>
import MediumPie from "../echarts/MediumPie.vue";
import PieLegend from "../echarts/PieLegend.vue";
export default {
components: { MediumPie, PieLegend },
name: "MediumPiePanel",
props: ["cardTitle", "pieData"],
data() {
return {};
},
};
</script>
<style lang="scss" scoped>
.pie-card {
height: 80vh;
min-height: 580px;
}
.container {
position: relative;
}
.pie-container {
justify-content: center;
align-items: center;
display: flex;
flex-wrap: wrap; // 允许flex布局换行
margin-left: 190px;
}
.pie-box {
width: 47.6%;
background-color: #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
border-radius: 5px;
transition: all 0.3s ease-in-out;
margin: 5px;
}
.pie-box:hover {
transform: translateY(-5px);
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.3);
background-color: #f1f1f1;
}
.pie-legend {
background-color: #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
border-radius: 0px;
margin: 5px;
position: absolute;
top: 0px;
left: 0px;
height: 98%;
width: 180px;
}
</style>
二、legend代码
<template>
<el-card style="height: 100%">
<div class="line-container">
<div ref="PieChart" class="line-box" style="height: 64vh"></div>
</div>
</el-card>
</template>
<script>
import echarts from "echarts";
export default {
name: "PieLegend",
props: ["pieData"],
data() {
return {
mychart: null,
};
},
created() {},
methods: {
init() {
// 基于准备好的dom,初始化echars实例
this.mychart = echarts.init(this.$refs.PieChart);
// 指定图表的配置项和数据
var option = {
legend: {
// orient: "horizontal", // 设置legend的方向
orient: "vertical",
top: "center", // 设置legend的位置
left: "left",
type: "scroll", // 设置legend是否有轮动条模式
// selectedMode: false, // 取消图例点击事件
},
series: [
{
// 这是legend的颜色,这里的颜色必须和其他饼图一样
color: [
"#D0104C",
"#00CED1",
"#FF8C00",
"#BA55D3",
"#32CD32",
"#FFD700",
"#5ab1ef",
"#b6a2de",
"#67e0e3",
"#2ec7c9",
"#FFC0CB",
"#9370DB",
"#AFEEEE",
"#1B813E",
],
// name: "Access From",
type: "pie",
radius: "0%", // 饼图大小 设置0为了隐藏饼图
center: ["5000%", "5000%"], // 饼图位置
data: this.pieData,// 饼图的数据
},
],
};
this.mychart.setOption(option);
},
// 点击图例触发修改其他四个饼图
handleLegendClick(params) {
// 触发兄弟组件,也就是其他饼图的方法,并传入params.name 参数
this.$bus.$emit("changeLegendSelected", params.name);
},
handleLegendMouseover(event) {
// 获取鼠标现在所在的位置
const legendDataIndex = event.topTarget.parent.__legendDataIndex; // 每个legend的index
// 该监听器正在监听一个`echarts 事件`。
// 因为不仅仅是图例可以触发鼠标经过事件,不是图例的没用index,所以要过滤一下
if (legendDataIndex != undefined && legendDataIndex >= 0) {
this.$bus.$emit("legendMouseover", legendDataIndex);
}
},
handleLegendMouseout(event) {
// 有的鼠标移出触发的事件没用parent属性所以要加?为可选的
if (event.topTarget?.parent != undefined) {
const legendDataIndex = event.topTarget.parent.__legendDataIndex; // 每个legend的index
// 该监听器正在监听一个`echarts 事件`。
if (legendDataIndex != undefined && legendDataIndex >= 0) {
this.$bus.$emit("legendMouseout", legendDataIndex);
}
}
},
},
mounted() {
this.$nextTick(() => {
this.init();
// 监听图例点击事件
this.mychart.on("legendselectchanged", this.handleLegendClick);
// 监听鼠标经过图例事件,注意这不仅仅是监听图例的,也会监听其他的
// 但是监听图例会有一个index也就是鼠标在第几个图例上,具体看handle代码
this.mychart.getZr().on("mouseover", this.handleLegendMouseover);
// 监听鼠标移除图例事件
this.mychart.getZr().on("mouseout", this.handleLegendMouseout);
});
},
beforeDestroy() {
// 销毁各个监听器
this.mychart.off("legendselectchanged", this.handleLegendClick);
this.mychart.getZr().off("mouseover", this.handleLegendMouseover);
this.mychart.getZr().off("mouseout", this.handleLegendMouseout);
this.mychart.dispose(); // 销毁图表对象
this.mychart = null; // 清空对图表对象的引用
},
watch: {
// 每次仪表盘数据
pieData: {
handler(newValue, oldValue) {
// 重新绘制
this.init();
},
deep: true,
},
},
computed: {
chartHeight() {
const ratio = 0.8; // 高度与窗口高度的比例系数
return `${this.LineBoxHeight * ratio}px`;
},
},
};
</script>
<style lang="scss" scoped>
.line-container {
height: 100%;
}
</style>
三、饼图代码:
<template>
<el-card style="height: 100%">
<div slot="header" class="clearfix">
// 标题
<span>{{ title }}</span>
</div>
// 饼图容器
<div class="line-container">
<div ref="PieChart" class="line-box" style="height: 23vh"></div>
</div>
</el-card>
</template>
<script>
import echarts from "echarts";
export default {
name: "MediumPie",
props: ["title", "pieData"],
data() {
return {
mychart: null,
};
},
created() {},
methods: {
handleChangeLegendSelected(params) { // params就是bus传过来 的参数
// 修改图例选中状态
this.mychart.dispatchAction({
type: "legendToggleSelect",
// 图例名称
name: params,
});
},
// 鼠标移入图例高亮 show tip
handleLegendMouseover(legendIndex) {
this.mychart.dispatchAction({
type: "highlight",
seriesIndex: 0,// 这个是数据系列,有的一个echarts里面放了多组数据,本案例只有一组所以是0
dataIndex: legendIndex,// 第几个图例
});
// 显示tip
this.mychart.dispatchAction({
type: "showTip",
seriesIndex: 0,
dataIndex: legendIndex,
});
},
// 鼠标移出图例恢复正常
handleLegendMouseout(legendIndex) {
this.mychart.dispatchAction({
type: "downplay",
seriesIndex: 0,
dataIndex: legendIndex,
});
// 隐藏 tip
this.mychart.dispatchAction({
type: "hideTip",
seriesIndex: 0,
dataIndex: legendIndex,
});
},
init() {
// 基于准备好的dom,初始化echars实例
this.mychart = echarts.init(this.$refs.PieChart);
// 指定图表的配置项和数据
var option = {
// legend选项必须要加,不然就操作不了要求的功能
legend: {
// orient: "horizontal",
orient: "vertical",
top: "bottom",
left: "left",
type: "scroll",
// 是否显示图例,这个一定要加,不然饼图会显示自己的图例
show: false,
},
// 鼠标hover提示内容
tooltip: {
trigger: "item",
// formatter: "{a} <br/>{b} : {c} ({d}%)",
formatter: "{b} : {c} ({d}%)",
// 下面的函数,用于tip可以完整显示,不用被盒子挡住
position: function (point, params, dom, rect, size) {
// 鼠标坐标和提示框位置的参考坐标系是:以外层div的左上角那一点为原点,x轴向右,y轴向下
// 提示框位置
var x = 0; // x坐标位置
var y = 0; // y坐标位置
// 当前鼠标位置
var pointX = point[0];
var pointY = point[1];
// 外层div大小
// var viewWidth = size.viewSize[0];
// var viewHeight = size.viewSize[1];
// 提示框大小
var boxWidth = size.contentSize[0];
var boxHeight = size.contentSize[1];
// boxWidth > pointX 说明鼠标左边放不下提示框
if (boxWidth > pointX) {
x = 5;
} else {
// 左边放的下
x = pointX - boxWidth;
}
// boxHeight > pointY 说明鼠标上边放不下提示框
if (boxHeight > pointY) {
y = 5;
} else {
// 上边放得下
y = pointY - boxHeight;
}
return [x, y];
}, // 设置显示位置
},
series: [
{
color: [
"#D0104C",
"#00CED1",
"#FF8C00",
"#BA55D3",
"#32CD32",
"#FFD700",
"#5ab1ef",
"#b6a2de",
"#67e0e3",
"#2ec7c9",
"#FFC0CB",
"#9370DB",
"#AFEEEE",
"#1B813E",
],
// name: "Access From",
type: "pie",
radius: "90%", // 饼图大小
center: ["50%", "50%"], // 饼图位置
label: {
normal: {
show: false,
},
},
labelLine: {
normal: {
show: false,
},
},
data: this.pieData,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: "rgba(0, 0, 0, 0.5)",
},
},
},
],
};
this.mychart.setOption(option);
},
// 从传过来的值分解出仪表盘的值
getLineValue() {},
},
mounted() {
this.$nextTick(() => {
this.init();
// 修改图例选中状态,当图例组件的bus被触发时,这些函数就会被执行
this.$bus.$on("changeLegendSelected", this.handleChangeLegendSelected);
// 鼠标经过
this.$bus.$on("legendMouseover", this.handleLegendMouseover);
// 鼠标移除
this.$bus.$on("legendMouseout", this.handleLegendMouseout);
});
},
watch: {
// 每次仪表盘数据
pieData: {
handler(newValue, oldValue) {
// 重新绘制
this.init();
},
deep: true,
},
},
computed: {
chartHeight() {
const ratio = 0.8; // 高度与窗口高度的比例系数
return `${this.LineBoxHeight * ratio}px`;
},
},
};
</script>
<style lang="scss" scoped>
</style>