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
如下图所示,则表示安装成功:
npm安装echarts包

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>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值