1.小节概述
当多个图表一起展现时,有时图表之间的数据是有关系的,这时候需要图表之间能否联动起来。例如有个地图展示了河南省各个区域(豫东、豫西、豫南、豫北、豫中)的销售额,另外有个柱状图展示了各个市区的销售额,那么在点击地图上某个区域时,柱状图也同时联动展示该区域辖下的各个市区的销售额数据。在本小节中,主要介绍在当前Vue项目中使用EventBus配合echarts实例中事件绑定API实现上述的图表联动场景。
2.操作步骤
2.1.准备工作
1.在主页HomeView.vue中,存放文字链接(图表联动),点击跳转对应的路由页面(chartLinkage),在router的index.js中配置路由信息。在chartLinkage演示页面中,进行左右栅格布局,准备存放地图(展示区域销售额)和柱状图(展示市区销售额)。
2.在RAP接口管理平台中新建接口,提供区域销售额数据以及各区域辖下的市区销售额数据。
3.在src/components/linkage下创建CascadeMap.vue,引入Echarts地图展示区域销售额数据,并放到chartLinkage演示页面中的左侧区域,参考代码:
注意事项:前面小节中,我们是通过document.getElementById进行获取元素的,这个方法其实有个明显的弊端(最终多个组件都要放到一起,假如一不小心设置的元素id有重复就会出现问题),所以此处及后续将使用ref进行获取DOM元素。
<template>
<div class="mainDiv">
<div class="titleDiv">区域销售金额</div>
<div class="chartDiv" ref="chart"></div>
</div>
</template>
<script>
import HenanAreaMap from '@/assets/maps/HenanArea.json'
import elementResizeDetectorMaker from 'element-resize-detector'
const erd = elementResizeDetectorMaker()
export default {
data() {
return {
chartData: {},
}
},
name: 'CascadeMap',
mounted() {
this.getChartData().then(() => {
// 数据请求到后渲染图表
this.renderChart()
})
},
beforeDestroy() {
erd.uninstall(this.$refs.chart)
},
methods: {
// 请求数据回来
async getChartData() {
this.chartData = await this.$axios({ url: 'sales/cas' })
},
// 渲染图表
renderChart() {
// 使用registerMap注册地图json数据
this.$echarts.registerMap('HenanArea', HenanAreaMap)
let myChart = this.$echarts.init(this.$refs.chart)
let chartData = this.chartData
var option = {
tooltip: {},
visualMap: {
min: 3000,
max: 30000,
left: 'left',
top: 'bottom',
text: ['高', '低'],
inRange: {
color: ['#e0ffff', '#5470C6'],
},
show: true,
},
series: [
{
name: '销售额',
type: 'map',
map: 'HenanArea',
zoom: 1.2,
label: {
show: true,
fontSize: 14,
color: '#640000',
formatter: '{b}\n{c}',
},
data: chartData.data.areaData,
select: {
itemStyle: {
// 设置地图点击后的颜色
color: null,
},
},
},
],
}
// 使用刚指定的配置项和数据显示图表
myChart.setOption(option)
// 监听图表所在容器,大小有变化时自适应
erd.listenTo(this.$refs.chart, function () {
myChart.resize()
})
},
},
}
</script>
<style scoped>
.titleDiv {
cursor: pointer;
}
</style>
4.在src/components/linkage下创建CascadeBar.vue,引入Echarts柱状图展示市区销售额数据(默认展示全部市区数据),并放到chartLinkage演示页面中的右侧区域,参考代码:
<template>
<div class="mainDiv">
<div class="titleDiv">{{ title }}</div>
<div class="chartDiv" ref="chart"></div>
</div>
</template>
<script>
import elementResizeDetectorMaker from 'element-resize-detector'
const erd = elementResizeDetectorMaker()
export default {
data() {
return {
chartData: {},
title: '河南省各市销售额',
}
},
name: 'CascadeBar',
mounted() {
this.getChartData().then(() => {
// 数据请求到后渲染图表
this.renderChart()
})
},
beforeDestroy() {
erd.uninstall(this.$refs.chart)
},
methods: {
// 请求数据回来
async getChartData() {
this.chartData = await this.$axios({ url: 'sales/cas' })
},
// 渲染图表
renderChart() {
let myChart = this.$echarts.init(this.$refs.chart)
let chartData = this.chartData
var option = {
tooltip: {
show: true,
textStyle: {
color: '#000000',
fontStyle: 'normal',
fontSize: 14,
align: 'center',
},
},
legend: {
show: true,
data: ['销额'],
icon: 'roundRect',
x: 'center',
y: 'bottom',
padding: [0, 0, 10, 0],
textStyle: {
fontStyle: 'normal',
fontSize: 12,
},
},
grid: {
top: '10%',
bottom: '10%',
left: '3%',
right: '3%',
containLabel: true,
},
xAxis: {
type: 'category',
data: chartData.data.allCityData.city,
axisTick: {
show: true,
alignWithLabel: true,
},
axisLabel: {
show: true,
interval: 0,
},
splitLine: {
show: false,
},
},
yAxis: {
type: 'value',
splitLine: {
// 分隔线设置
show: true,
lineStyle: {
color: ['#E5EAF3'],
width: 1,
type: 'dashed',
},
},
},
series: [
{
name: '销量',
type: 'bar',
data: chartData.data.allCityData.amount,
label: {
show: true,
position: 'top',
fontSize: 11,
},
itemStyle: {
// 顺时针设置圆角:左上、右上、右下、左下
borderRadius: [6, 6, 0, 0],
// 根据值的情况设置不同颜色
color: function (params) {
let colorList = ['#5470C6', '#91CC75', '#FAC858', '#EE6666']
let amount = params.data
if (amount >= 7000) {
return colorList[0]
} else if (amount < 7000 && amount >= 5000) {
return colorList[1]
} else if (amount < 5000 && amount >= 3000) {
return colorList[2]
} else {
return colorList[3]
}
},
},
},
],
}
// 使用刚指定的配置项和数据显示图表
myChart.setOption(option)
// 监听图表所在容器,大小有变化时自适应
erd.listenTo(this.$refs.chart, function () {
myChart.resize()
})
},
},
}
</script>
<style scoped>
.titleDiv {
cursor: pointer;
}
</style>
2.2.组件间通信
1.两个图表组件(地图和柱状图)已经呈现,数据也已经渲染。那么要想联动,就需要图表间可以进行通信,例如点击左侧区域能够触发右侧图表组件方法进行数据更新。此处采用的是EventBus进行组件间通信,首先在main.js中全局初始化EventBus,如下图:
2.接下来将使用EventBus配合echarts实例中事件绑定来实现点击区域同时柱状图数据更新。
2.3.实现图表联动
1.在地图组件中向EventBus发送事件,并传递参数(所点击的区域快信息)。当然,由于点击图表区域块触发CascadeBar显示对应区域的数据,那么同理设置点击其他地方(此处设置的是点击标题的div容器)触发CascadeBar回归到所有区域下的数据展示。所以,在地图组件中有两处地方需要向EventBus发送事件,参考代码:
注意事项:1.在echarts点击事件绑定时,因为echarts当中的this并不是vue的实例vueComponent,需要先定义一个变量名 _this=this 进行接收。2.在鼠标划过地图时,默认有高亮颜色显示,可以通过绑定mouseover事件设置鼠标移入区域时颜色不变。
<template>
<div class="mainDiv">
<div class="titleDiv" @click="cascadeHenan">区域销售金额</div>
<div class="chartDiv" ref="chart"></div>
</div>
</template>
<script>
import HenanAreaMap from '@/assets/maps/HenanArea.json'
import elementResizeDetectorMaker from 'element-resize-detector'
const erd = elementResizeDetectorMaker()
export default {
data() {
return {
chartData: {},
}
},
name: 'CascadeMap',
mounted() {
this.getChartData().then(() => {
// 数据请求到后渲染图表
this.renderChart()
})
},
beforeDestroy() {
// 移除元素监听
erd.uninstall(this.$refs.chart)
},
methods: {
// 请求数据回来
async getChartData() {
this.chartData = await this.$axios({ url: 'sales/cas' })
},
cascadeHenan() {
this.$EventBus.$emit('casAreaChart', '河南省')
},
// 渲染图表
renderChart() {
// 使用registerMap注册地图json数据
this.$echarts.registerMap('HenanArea', HenanAreaMap)
let myChart = this.$echarts.init(this.$refs.chart)
let chartData = this.chartData
var option = {
tooltip: {},
visualMap: {
min: 3000,
max: 30000,
left: 'left',
top: 'bottom',
text: ['高', '低'],
inRange: {
color: ['#e0ffff', '#5470C6'],
},
show: true,
},
series: [
{
name: '销额',
type: 'map',
map: 'HenanArea',
zoom: 1.2,
label: {
show: true,
fontSize: 14,
color: '#640000',
formatter: '{b}\n{c}',
},
data: chartData.data.areaData,
select: {
itemStyle: {
// 设置地图点击后的颜色
color: null,
},
},
},
],
}
// 使用刚指定的配置项和数据显示图表
myChart.setOption(option)
// 设置点击事件,和市区销售额数据进行图表联动
// 因为echarts当中的this并不是vue的实例vueComponent,需要定义一个变量名接收
let _this = this
myChart.on('click', function (params) {
_this.$EventBus.$emit('casAreaChart', params.name)
})
// 设置鼠标移入区域颜色不变
myChart.on('mouseover', function (params) {
if (params.data.value != undefined) {
myChart.dispatchAction({
type: 'downplay',
})
}
})
// 监听图表所在容器,大小有变化时自适应
erd.listenTo(this.$refs.chart, function () {
myChart.resize()
})
},
},
}
</script>
<style scoped>
.titleDiv {
cursor: pointer;
}
</style>
2.在柱状图组件中进行EventBus监听接收事件,接收地图组件传递过来的区域参数,然后更新echarts配置数据。当然,在柱状图组件被销毁前,需要把EventBus监听事件给移除,以免下次再重复创建监听。参考代码:
<template>
<div class="mainDiv">
<div class="titleDiv">{{ title }}</div>
<div class="chartDiv" ref="chart"></div>
</div>
</template>
<script>
import elementResizeDetectorMaker from 'element-resize-detector'
const erd = elementResizeDetectorMaker()
export default {
data() {
return {
chartData: {},
title: '河南省各市销售额',
}
},
name: 'CascadeBar',
mounted() {
this.getChartData().then(() => {
// 数据请求到后渲染图表
this.renderChart()
})
// EventBus监听接收事件
this.$EventBus.$on('casAreaChart', (data) => {
this.title = data + '各市销售额'
let myChart = this.$echarts.getInstanceByDom(this.$refs.chart)
if (data == '河南省') {
myChart.setOption({
xAxis: {
data: this.chartData.data.allCityData.city,
},
series: [
{
data: this.chartData.data.allCityData.amount,
},
],
})
} else {
myChart.setOption({
xAxis: {
data: this.chartData.data.areaCityData[data].city,
},
series: [
{
data: this.chartData.data.areaCityData[data].amount,
},
],
})
}
})
},
beforeDestroy() {
// 移除EventBus监听
this.$EventBus.$off('casAreaChart')
// 移除元素监听
erd.uninstall(this.$refs.chart)
},
methods: {
// 请求数据回来
async getChartData() {
this.chartData = await this.$axios({ url: 'sales/cas' })
},
// 渲染图表
renderChart() {
let myChart = this.$echarts.init(this.$refs.chart)
let chartData = this.chartData
var option = {
tooltip: {
show: true,
textStyle: {
color: '#000000',
fontStyle: 'normal',
fontSize: 14,
align: 'center',
},
},
legend: {
show: true,
data: ['销额'],
icon: 'roundRect',
x: 'center',
y: 'bottom',
padding: [0, 0, 10, 0],
textStyle: {
fontStyle: 'normal',
fontSize: 12,
},
},
grid: {
top: '10%',
bottom: '10%',
left: '3%',
right: '3%',
containLabel: true,
},
xAxis: {
type: 'category',
data: chartData.data.allCityData.city,
axisTick: {
show: true,
alignWithLabel: true,
},
axisLabel: {
show: true,
interval: 0,
},
splitLine: {
show: false,
},
},
yAxis: {
type: 'value',
splitLine: {
// 分隔线设置
show: true,
lineStyle: {
color: ['#E5EAF3'],
width: 1,
type: 'dashed',
},
},
},
series: [
{
name: '销额',
type: 'bar',
data: chartData.data.allCityData.amount,
label: {
show: true,
position: 'top',
fontSize: 11,
},
itemStyle: {
// 顺时针设置圆角:左上、右上、右下、左下
borderRadius: [6, 6, 0, 0],
// 根据值的情况设置不同颜色
color: function (params) {
let colorList = ['#5470C6', '#91CC75', '#FAC858', '#EE6666']
let amount = params.data
if (amount >= 7000) {
return colorList[0]
} else if (amount < 7000 && amount >= 5000) {
return colorList[1]
} else if (amount < 5000 && amount >= 3000) {
return colorList[2]
} else {
return colorList[3]
}
},
},
},
],
}
// 使用刚指定的配置项和数据显示图表
myChart.setOption(option)
// 监听图表所在容器,大小有变化时自适应
erd.listenTo(this.$refs.chart, function () {
myChart.resize()
})
},
},
}
</script>
<style scoped>
.titleDiv {
cursor: pointer;
}
</style>
3.至此,图表联动处理完成,启动Vue项目,查看联动效果。
3.小节总结
当多个ECharts图表呈现的数据存在关系时,有时需要能够进行图表联动。在本小节中,通过使用EventBus解决组件间通信,并配合echarts实例中点击事件绑定实现了图表间的联动。