echarts-vue项目:散点图、散点图下钻、xy轴平均线
一、案例图片展示
1、一级散点图:
2、二级散点图:
二、scatterEchart.vue文件全部代码
1、以下代码演示会有详细注释说明; 2、目录三会分步骤拆分该文件代码; 3、建议搭配echarts官网配置项食用风味更佳~~<template>
<div class="page-wrap" v-loading="loading">
<!-- 下钻后展示:返回上一层的按钮icon、上一层进来的项名称 -->
<i class="el-icon-d-arrow-left back-icon page-title"
v-show="!!currentCategoryName"
@click="backToMainEchart"
> {{ currentCategoryName }}</i>
<!-- ecahrt散点图容器 -->
<div class="echarts-wrap" ref="echartRef"></div>
</div>
</template>
<script>
import * as echarts from 'echarts'
import { getFirstEchart, getSecondEchart } from '@/api/xxxxxx' // 后台请求接口 - 这里写假数据
export default {
name: 'echarts',
data() {
return {
loading: false, // 正在加载标识
mychart: null, // echarts图实例
firstEchartData: {}, // 一级散点图数据
secondEchartData: {}, // 二级散点图数据
currentCategoryName: '', // 展示一级散点图项标题
}
},
computed: {
/* 一级散点图 - echarts配置项 */
firstEchartOption() {
// 毛利的平均值(y轴) 销量的平均值(x轴) 项数组
const { profitPriceAvg, saleQuantityAvg, dateListResponseDTO } = this.firstEchartData
// 平均线变量,每一项数据都会用到,见下面seriesData
// 由于ecahrt原因,我的每一项都需要使用平均线,所有平均线都是这个
const markLine = {
data: [
{ xAxis: saleQuantityAvg }, // x轴平均线
{ yAxis: profitPriceAvg }, // y轴平均线
],
silent: true, // 不响应鼠标事件,也就是鼠标移上平均线时候,平均线是否会有变粗的一个交互样式,我这边是没有
symbol: ['none', 'none'], // 平均线两端的标记类型,我这边没有标记类型
lineStyle: { // 平均线样式
type: 'dashed', // 平均线的类型,虚线
color: '#ffa24c' // 平均线的颜色
}
}
// 图例、系列数据 - 配置项会用到
const legendDataList = [] // 图例数据
const seriesData = [] // 系列数据 - 具体看echarts的series,type为scatter的配置项
dateListResponseDTO.forEach(item => {
legendDataList.push(item.categoryName)
seriesData.push({
name: item.categoryName,
symbolSize: 20,
type: 'scatter',
markLine,
data: [[item.saleQuantitySum, item.profitPriceSum]]
})
})
// 一级散点图配置项
const option = {
// 图例
legend: {
orient: 'vertical',
top: 'middle',
right: 0,
data: legendDataList // 图例数据
},
// 画布位置
grid: {
left: '3%', // 距离左边3%的距离
right: '15%', // 距离右边边15%的距离
bottom: '3%', // 距离底部3%的距离
containLabel: true // 画布包含了坐标轴的刻度标签
},
// 提示框
tooltip: {
trigger: 'item', // 移到每一项上才展示提示框
formatter: params => { // 提示框的样式 - 使用函数的方式
const item = dateListResponseDTO[params.seriesIndex]
const htmlStr =
`<div>
<div>类目:${item.categoryName}</div>
<div>销量:${numberFormat(item.saleQuantitySum)}</div>
<div>利润:${numberFormat(item.profitPriceSum)}</div>
</div>`
return htmlStr
},
borderColor: '#484848' , // 提示框的边框颜色
backgroundColor: '#484848' , // 提示框的背景颜色
padding: [5, 30, 5, 5], // 提示框的padding
textStyle: { // 提示框文字的样式
color: '#FFFFFF' // 提示框文字的颜色
}
},
// x轴
xAxis: {
name: '销量', // 名称
splitLine: { // 在画布中的分割线
show: false // 不展示分割线
}
},
// y轴
yAxis: {
name: '毛利', // 名称
splitLine: { // 在画布中的分割线
show: false // 不展示分割线
},
},
// 系列
series: seriesData
}
return option
},
/* 二级散点图 - echarts配置项 */
secondEchartOption() {
// 毛利的平均值(y轴) 销量的平均值(x轴) 项数组
const { profitPriceAvg, saleQuantityAvg, typeListResponseDTO } = this.secondEchartData
// 系列数据 - 以下会用到
const data = typeListResponseDTO.map(item => {
return [item.saleQuantitySum, item.profitPriceSum]
})
// 二级散点图配置项
const option = {
// 提示框
tooltip: {
trigger: 'item', // 移到每一项上才展示提示框
formatter: params => { // 提示框的样式 - 使用函数的方式
const item = typeListResponseDTO[params.dataIndex]
const htmlStr =
`<div>
<div>${item.productCode}</div>
<div>${item.productName}</div>
</div>`
return htmlStr
},
borderColor: '#484848' , // 提示框的边框颜色
backgroundColor: '#484848' , // 提示框的背景颜色
padding: [5, 30, 5, 5], // 提示框的padding
textStyle: { // 提示框文字的样式
color: '#FFFFFF' // 提示框文字的颜色
}
},
// x轴
xAxis: {
name: '销量', // 名称
axisLine: { // 在画布中的分割线
show: false // 不展示分割线
},
axisTick: { // 是否显示坐标轴刻度
show: false // 不显示坐标轴刻度
}
},
// y轴
yAxis: {
name: '毛利', // 名称
axisLine: { // 在画布中的分割线
show: false // 不展示分割线
},
axisTick: { // 是否显示坐标轴刻度
show: false // 不显示坐标轴刻度
}
},
// 系列
series: [
{
symbolSize: 20, // 散点的大小
data, // 系列数据
type: 'scatter', // 图类型 - 散点图
markLine: { // 平均线 - 二级散点图是只需要一个平均线
data: [
{ xAxis: saleQuantityAvg },
{ yAxis: profitPriceAvg },
],
silent: true, // 不响应鼠标事件,也就是鼠标移上平均线时候,平均线是否会有变粗的一个交互样式,我这边是没有
symbol: ['none', 'none'], // 平均线两端的标记类型,我这边没有标记类型
lineStyle: { // 平均线样式
type: 'dashed', // 平均线的类型,虚线
color: '#ffa24c' // 平均线的颜色
}
}
}
],
}
return option
},
},
mounted() {
this.queryFirstEchart()
},
methods: {
/**
* 请求一级散点图数据
* 1、向后端发送请求,拿到一级散点图数据
* 2、一级散点图渲染、点击事件绑定
*/
queryFirstEchart() {
this.loading = true
const params = { id: 1 } // 请求参数
getFirstEchart(params).then(data => {
this.firstEchartData = data || {}
this.drawEcharts(this.firstEchartOption, true)
}).finally(() => this.loading = false)
},
/**
* 请求二级散点图数据
* 1、向后端发送请求,拿到二级散点图数据
* 2、二级散点图渲染
*/
querySecondEchart() {
this.loading = true
const params = { category: this.category } // 请求参数:一级散点图点击项的 category
getSecondEchart(params).then(data => {
this.secondEchartData = data || {}
this.drawEcharts(this.secondEchartOption, false)
}).finally(() => this.loading = false)
},
/**
* 散点图渲染
* @param {Object} option 散点图配置项
* @param {Boolean} isHasClick 是否有点击事件 - 本案例是一级散点图有点击事件、二级散点图没有点击事件
*/
drawEcharts(option, isHasClick) {
// 如果有echarts图实例,则销毁echarts图实例
// 作用1:点击一级散点图,请求并渲染二级散点图时,销毁掉一级散点图的实例,页面同一个元素div渲染新的二级散点图
// 作用2:点击二级散点图的【返回】按钮时,销毁掉二级散点图的实例,页面同一个元素div重新渲染一级散点图
if (this.mychart) this.mychart.dispose()
this.$nextTick(() => { // $nextTick是为了确保页面上已经挂载上了echartRef
this.mychart = echarts.init(this.$refs.echartRef) // 初始化echarts实例
this.mychart.setOption(option, true) // 给ecahrts实例配置对应的配置项:一级散点图用一级散点图的配置项、二级散点图用二级散点图的配置项
if (isHasClick) { // 如果该echarts图需要绑定点击事件,则进行绑定点击事件 - 本案例一级散点图需要绑定点击事件
this.mychart.on('click', async event => {
this.category = this.firstEchartData.dateListResponseDTO[event.seriesIndex].category // 一级散点图的点击项的 类别id
this.currentCategoryName = this.firstEchartData.dateListResponseDTO[event.seriesIndex].categoryName // 一级散点图的点击项的 标题
this.loading = true // 页面转圈
await this.querySecondEchart() // 点击一级散点图下钻请求二级散点图数据
this.loading = false // 二级散点图数据请求完并渲染后,关掉转圈等待效果
})
}
})
},
/**
* 返回主散点图 - 二级散点图的返回按钮
* step1: 清空一级散点图当前点击项的名称
* step2: 用之前的一级散点图数据+配置项,重新渲染一级散点图
*/
backToMainEchart() {
this.currentCategoryName = ''
this.drawEcharts('echartRef', this.mainEchartOption, true)
},
}
}
</script>
<style lang="scss" scoped>
// 大概的页面样式自己个性化编写即可
.page-wrap {
width: 700px;
height: 450px;
display: flex;
flex-direction: column;
.page-title {
font-size: 16px;
height: 40px;
line-height: 40px;
width: 100%;
margin-bottom: 20px;
}
.echarts-wrap { // 一定要给echarts宽高,作为echarts图的容器
width: 100%;
height: 90%;
}
}
</style>
三、分步骤说明代码思路:
1、npm 安装 echarts 包
直接在终端输入命令npm install echarts --save
如下图所示,则表示安装成功:
2、.vue单文件引入echarts包
在.vue文件中引入echarts包:
<script>
import * as echarts from 'echarts'
</script>
3、html页面
<template>
<div class="page-wrap" v-loading="loading">
<!-- 下钻后展示:返回上一层的按钮icon、上一层进来的项名称 -->
<i class="el-icon-d-arrow-left back-icon page-title"
v-show="!!currentCategoryName"
@click="backToMainEchart"
> {{ currentCategoryName }}</i>
<!-- ecahrt散点图容器 -->
<div class="echarts-wrap" ref="echartRef"></div>
</div>
</template>
4、js相关准备
<script>
import * as echarts from 'echarts'
import { getFirstEchart, getSecondEchart } from '@/api/xxxxxx' // 后台请求接口 - 这里写假数据
export default {
name: 'echarts',
data() {
return {
loading: false, // 正在加载标识
mychart: null, // echarts图实例
firstEchartData: {}, // 一级散点图数据
secondEchartData: {}, // 二级散点图数据
currentCategoryName: '', // 展示一级散点图项标题
}
},
}
</script>
5、进来请求一级图数据
mounted() {
this.queryFirstEchart()
},
6、请求一级图
methods: {
/**
* 请求一级散点图数据
* 1、向后端发送请求,拿到一级散点图数据
* 2、一级散点图渲染、点击事件绑定
*/
queryFirstEchart() {
this.loading = true
const params = { id: 1 } // 请求参数
getFirstEchart(params).then(data => {
this.firstEchartData = data || {}
this.drawEcharts(this.firstEchartOption, true)
}).finally(() => this.loading = false)
},
}
7、一级图数据响应结果展示
8、一级图数据、配置项处理
computed: {
/* 一级散点图 - echarts配置项 */
firstEchartOption() {
// 毛利的平均值(y轴) 销量的平均值(x轴) 项数组
const { profitPriceAvg, saleQuantityAvg, dateListResponseDTO } = this.firstEchartData
// 平均线变量,每一项数据都会用到,见下面seriesData
// 由于ecahrt原因,我的每一项都需要使用平均线,所有平均线都是这个
const markLine = {
data: [
{ xAxis: saleQuantityAvg }, // x轴平均线
{ yAxis: profitPriceAvg }, // y轴平均线
],
silent: true, // 不响应鼠标事件,也就是鼠标移上平均线时候,平均线是否会有变粗的一个交互样式,我这边是没有
symbol: ['none', 'none'], // 平均线两端的标记类型,我这边没有标记类型
lineStyle: { // 平均线样式
type: 'dashed', // 平均线的类型,虚线
color: '#ffa24c' // 平均线的颜色
}
}
// 图例、系列数据 - 配置项会用到
const legendDataList = [] // 图例数据
const seriesData = [] // 系列数据 - 具体看echarts的series,type为scatter的配置项
dateListResponseDTO.forEach(item => {
legendDataList.push(item.categoryName)
seriesData.push({
name: item.categoryName,
symbolSize: 20,
type: 'scatter',
markLine,
data: [[item.saleQuantitySum, item.profitPriceSum]]
})
})
// 一级散点图配置项
const option = {
// 图例
legend: {
orient: 'vertical',
top: 'middle',
right: 0,
data: legendDataList // 图例数据
},
// 画布位置
grid: {
left: '3%', // 距离左边3%的距离
right: '15%', // 距离右边边15%的距离
bottom: '3%', // 距离底部3%的距离
containLabel: true // 画布包含了坐标轴的刻度标签
},
// 提示框
tooltip: {
trigger: 'item', // 移到每一项上才展示提示框
formatter: params => { // 提示框的样式 - 使用函数的方式
const item = dateListResponseDTO[params.seriesIndex]
const htmlStr =
`<div>
<div>类目:${item.categoryName}</div>
<div>销量:${numberFormat(item.saleQuantitySum)}</div>
<div>利润:${numberFormat(item.profitPriceSum)}</div>
</div>`
return htmlStr
},
borderColor: '#484848' , // 提示框的边框颜色
backgroundColor: '#484848' , // 提示框的背景颜色
padding: [5, 30, 5, 5], // 提示框的padding
textStyle: { // 提示框文字的样式
color: '#FFFFFF' // 提示框文字的颜色
}
},
// x轴
xAxis: {
name: '销量', // 名称
splitLine: { // 在画布中的分割线
show: false // 不展示分割线
}
},
// y轴
yAxis: {
name: '毛利', // 名称
splitLine: { // 在画布中的分割线
show: false // 不展示分割线
},
},
// 系列
series: seriesData
}
return option
},
}
9、一级图渲染
methods: {
/**
* 散点图渲染
* @param {Object} option 散点图配置项
* @param {Boolean} isHasClick 是否有点击事件 - 本案例是一级散点图有点击事件、二级散点图没有点击事件
*/
drawEcharts(option, isHasClick) {
// 如果有echarts图实例,则销毁echarts图实例
// 作用1:点击一级散点图,请求并渲染二级散点图时,销毁掉一级散点图的实例,页面同一个元素div渲染新的二级散点图
// 作用2:点击二级散点图的【返回】按钮时,销毁掉二级散点图的实例,页面同一个元素div重新渲染一级散点图
if (this.mychart) this.mychart.dispose()
this.$nextTick(() => { // $nextTick是为了确保页面上已经挂载上了echartRef
this.mychart = echarts.init(this.$refs.echartRef) // 初始化echarts实例
this.mychart.setOption(option, true) // 给ecahrts实例配置对应的配置项:一级散点图用一级散点图的配置项、二级散点图用二级散点图的配置项
if (isHasClick) { // 如果该echarts图需要绑定点击事件,则进行绑定点击事件 - 本案例一级散点图需要绑定点击事件
this.mychart.on('click', async event => {
this.category = this.firstEchartData.dateListResponseDTO[event.seriesIndex].category // 一级散点图的点击项的 类别id
this.currentCategoryName = this.firstEchartData.dateListResponseDTO[event.seriesIndex].categoryName // 一级散点图的点击项的 标题
this.loading = true // 页面转圈
await this.querySecondEchart() // 点击一级散点图下钻请求二级散点图数据
this.loading = false // 二级散点图数据请求完并渲染后,关掉转圈等待效果
})
}
})
},
}
10、一级图样式展示
11、点击一级图“运动产品”项,请求二级图数据
methods: {
/**
* 请求二级散点图数据
* 1、向后端发送请求,拿到二级散点图数据
* 2、二级散点图渲染
*/
querySecondEchart() {
this.loading = true
const params = { category: this.category } // 请求参数:一级散点图点击项的 category
getSecondEchart(params).then(data => {
this.secondEchartData = data || {}
this.drawEcharts(this.secondEchartOption, false)
}).finally(() => this.loading = false)
},
}
12、二级图数据响应结果
13、二级图数据、配置项处理
computed: {
/* 二级散点图 - echarts配置项 */
secondEchartOption() {
// 毛利的平均值(y轴) 销量的平均值(x轴) 项数组
const { profitPriceAvg, saleQuantityAvg, typeListResponseDTO } = this.secondEchartData
// 系列数据 - 以下会用到
const data = typeListResponseDTO.map(item => {
return [item.saleQuantitySum, item.profitPriceSum]
})
// 二级散点图配置项
const option = {
// 提示框
tooltip: {
trigger: 'item', // 移到每一项上才展示提示框
formatter: params => { // 提示框的样式 - 使用函数的方式
const item = typeListResponseDTO[params.dataIndex]
const htmlStr =
`<div>
<div>${item.productCode}</div>
<div>${item.productName}</div>
</div>`
return htmlStr
},
borderColor: '#484848' , // 提示框的边框颜色
backgroundColor: '#484848' , // 提示框的背景颜色
padding: [5, 30, 5, 5], // 提示框的padding
textStyle: { // 提示框文字的样式
color: '#FFFFFF' // 提示框文字的颜色
}
},
// x轴
xAxis: {
name: '销量', // 名称
axisLine: { // 在画布中的分割线
show: false // 不展示分割线
},
axisTick: { // 是否显示坐标轴刻度
show: false // 不显示坐标轴刻度
}
},
// y轴
yAxis: {
name: '毛利', // 名称
axisLine: { // 在画布中的分割线
show: false // 不展示分割线
},
axisTick: { // 是否显示坐标轴刻度
show: false // 不显示坐标轴刻度
}
},
// 系列
series: [
{
symbolSize: 20, // 散点的大小
data, // 系列数据
type: 'scatter', // 图类型 - 散点图
markLine: { // 平均线 - 二级散点图是只需要一个平均线
data: [
{ xAxis: saleQuantityAvg },
{ yAxis: profitPriceAvg },
],
silent: true, // 不响应鼠标事件,也就是鼠标移上平均线时候,平均线是否会有变粗的一个交互样式,我这边是没有
symbol: ['none', 'none'], // 平均线两端的标记类型,我这边没有标记类型
lineStyle: { // 平均线样式
type: 'dashed', // 平均线的类型,虚线
color: '#ffa24c' // 平均线的颜色
}
}
}
],
}
return option
},
}
14、二级图渲染
methods: {
/**
* 散点图渲染
* @param {Object} option 散点图配置项
* @param {Boolean} isHasClick 是否有点击事件 - 本案例是一级散点图有点击事件、二级散点图没有点击事件
*/
drawEcharts(option, isHasClick) {
// 如果有echarts图实例,则销毁echarts图实例
// 作用1:点击一级散点图,请求并渲染二级散点图时,销毁掉一级散点图的实例,页面同一个元素div渲染新的二级散点图
// 作用2:点击二级散点图的【返回】按钮时,销毁掉二级散点图的实例,页面同一个元素div重新渲染一级散点图
if (this.mychart) this.mychart.dispose()
this.$nextTick(() => { // $nextTick是为了确保页面上已经挂载上了echartRef
this.mychart = echarts.init(this.$refs.echartRef) // 初始化echarts实例
this.mychart.setOption(option, true) // 给ecahrts实例配置对应的配置项:一级散点图用一级散点图的配置项、二级散点图用二级散点图的配置项
if (isHasClick) { // 如果该echarts图需要绑定点击事件,则进行绑定点击事件 - 本案例一级散点图需要绑定点击事件
this.mychart.on('click', async event => {
this.category = this.firstEchartData.dateListResponseDTO[event.seriesIndex].category // 一级散点图的点击项的 类别id
this.currentCategoryName = this.firstEchartData.dateListResponseDTO[event.seriesIndex].categoryName // 一级散点图的点击项的 标题
this.loading = true // 页面转圈
await this.querySecondEchart() // 点击一级散点图下钻请求二级散点图数据
this.loading = false // 二级散点图数据请求完并渲染后,关掉转圈等待效果
})
}
})
},
}
14、返回按钮,返回到一级图
methods: {
/**
* 返回主散点图 - 二级散点图的返回按钮
* step1: 清空一级散点图当前点击项的名称
* step2: 用之前的一级散点图数据+配置项,重新渲染一级散点图
*/
backToMainEchart() {
this.currentCategoryName = ''
this.drawEcharts('echartRef', this.mainEchartOption, true)
},
}
15、css
<style lang="scss" scoped>
// 大概的页面样式自己个性化编写即可
.page-wrap {
width: 700px;
height: 450px;
display: flex;
flex-direction: column;
.page-title {
font-size: 16px;
height: 40px;
line-height: 40px;
width: 100%;
margin-bottom: 20px;
}
.echarts-wrap { // 一定要给echarts宽高,作为echarts图的容器
width: 100%;
height: 90%;
}
}
</style>