如何在vue中优雅的使用echart?

1,158 阅读5分钟

背景介绍

近期做了一个简单的vue+ele后台管理系统的项目,跟以前不一样的是,需要在vue后台管理系统最后一页中,实现3个echart数据图和一些数据展示。具体实现效果如图: image.png image.png

问题描述和办法总结

其实具体实现倒不复杂,但也不免踩了一些坑。在这里汇总如下

安装echart

安装好echarts后要做一个导入,这个步骤不能忘了,不然后面从初始化就无法执行。

import * as echarts from 'echarts';
获取Echart的DOM不成功

echart需要在一个具有宽高的盒子中绘制完成,因此需要先获取一个dom。但是获取dom的时候出现问题了,就是无法获取到dom
image.png
最后想了想,想到echart是要获取到一个dom,而相关的代码我却习惯性的放在created中,在created阶段还没有dom,怎么可能获取dom成功呢?
把相关代码改到了mounted中,又检查了dom的获取方法document.getElementById(document.getElementsByClassName也可),然后顺利获取到了dom。 另外需要注意PaymentOrderChart等初始化好后的echart图表需要保存到data中,因为重新获取到数据之后,需要执行PaymentOrderChart中的setOption对数据进行重新设置。 代码如下:

mounted() {
  // 缴费订单数趋势折线图
  let PaymentOrderChartDom = document.getElementsByClassName('payment_order_body')[0];
  this.PaymentOrderChart = echarts.init(PaymentOrderChartDom);
  let PaymentOrderChartOption = this.PaymentOrderChartOption
  PaymentOrderChartOption && this.PaymentOrderChart.setOption(PaymentOrderChartOption,true);
  // 缴费金额趋势折线图
  let PaymentAmountChartDom = document.getElementsByClassName('payment_amount_body')[0];
  this.PaymentAmountChart = echarts.init(PaymentAmountChartDom);
  let PaymentAmountChartOption = this.PaymentAmountChartOption
  PaymentAmountChartOption && this.PaymentAmountChart.setOption(PaymentAmountChartOption,true);
  // 缴费金额分布饼状图
  let PayContributeChartDom = document.getElementsByClassName('pay_contribute_body')[0];
  this.PayContributeChart = echarts.init(PayContributeChartDom);
  let payContributeOption = this.payContributeOption
  payContributeOption && this.PayContributeChart.setOption(payContributeOption,true);

  let that = this
  setTimeout(function (){
    window.onresize = function () {
      that.PaymentOrderChart.resize();
      that.PaymentAmountChart.resize();
      that.PayContributeChart.resize()
    }
  },200)
},
echarts图表不会变化尺寸

最开始在拖拽浏览器窗口的时候,图标只会按照最初渲染大小显示,会造成图表畸大或者畸小 image.png 也就是说图表的宽度需要随着浏览器窗口的大小变化,因此需要在mounted中增加监视函数,让图表在浏览器窗口变化时重置显示尺寸。代码如下:

  let that = this
  setTimeout(function (){
    window.onresize = function () {
      that.PaymentOrderChart.resize();
      that.PaymentAmountChart.resize();
      that.PayContributeChart.resize()
    }
  },200)
查询条件变化后再次获取数据后,图表显示数据堆叠

这个问题很简单,就是在查询条件变化后,需要将原先的数据存放数组清空,不然push进去之后,显示的数据就是原先的数据再加上新获取的数据。

// 获取缴费订单数 缴费金额趋势折线图
async getPaymentOrderCountAndAmount(){
  // 每次获取新数据前先清空图表数据
  this.PaymentOrderChartOption.xAxis.data = []
  this.PaymentOrderChartOption.series[0].data = []
  this.PaymentAmountChartOption.xAxis.data = []
  this.PaymentAmountChartOption.series[0].data = []
  let {data:arr}= await getPaymentOrder(this.queryBean)
  arr.forEach(item=>{
    this.PaymentOrderChartOption.xAxis.data.push(item.dateNo)
    this.PaymentOrderChartOption.series[0].data.push(item.totalCount)
    this.PaymentAmountChartOption.xAxis.data.push(item.dateNo)
    this.PaymentAmountChartOption.series[0].data.push(item.totalAmount)
  })
  // 更新缴费订单数趋势折线图
  this.PaymentOrderChart.clear()
  this.PaymentOrderChart.setOption(this.PaymentOrderChartOption,true)
  // 更新缴费订单金额趋势折线图
  this.PaymentAmountChart.clear()
  this.PaymentAmountChart.setOption(this.PaymentAmountChartOption,true)
},

获取缴费金额分布饼状图,又对后台返回的数据进行了改造

// 获取缴费金额分布饼状图
async getPayContributeAmount(){
  let {data:obj} = await getPayContribute(this.queryBean)
  let arr = []
  for (let key in obj){
    let temp = {}
    temp.value = obj[key]
    if (key === 'totalAmount') continue
    if( key === 'fifty') temp.name='小于50元'
    if( key === 'hundred') temp.name='50~100元'
    if( key === 'twoHundred') temp.name='100~200元'
    if( key === 'threeHundred') temp.name='200~300元'
    if( key === 'moreHundred') temp.name='大于300元'
    arr.push(temp)
  }
  this.payContributeOption.series[0].data = arr
  // 更新缴费分布饼状图
  this.PayContributeChart.clear()
  this.PayContributeChart.setOption(this.payContributeOption,true)
}
echart中自定义tooltip

tooltip就是鼠标经过的时候,显示的每个数据点的信息提示框,这个提示框可以可以在图表数据option中自行设置。不管是折线图还是饼状图,都可以在定义的formatter的返回值中定义提示框的格式和显示内容,注意formatter函数获取到的参数params基本上包含了这个数据点的所有数据。 有意思的是折线图中formatter的返回模板字符串可以识别<br>,而饼状图中的formatter的返回模板字符串无法识别<br>,只能直接换行才能显示正确。

// 缴费订单数趋势
PaymentOrderChart:null,
PaymentOrderChartOption:{
  tooltip: {
    show:true,
    trigger: 'axis',
    formatter:(params)=>{
      return `${params[0].name.length === 8?'日期':'时间'}:${params[0].name}${params[0].name.length === 8?'':' 时'}<br>${params[0].marker}订单数:${params[0].value}个`
    }
  },
  xAxis: {
    type: 'category',
    name:'时 间',
    nameLocation:'middle',
    nameTextStyle:{
      fontSize:18,
    },
    nameGap:30,
    data: []
  },
  yAxis:{
      type: 'value',
      name:'缴 费 订 单 数(个)',
      nameLocation: 'middle',
      nameTextStyle:{
        fontSize:18,
      },
      nameGap:40
  },
  series: [
    {
      data: [],
      type: 'line'
    }
  ]
},
     // 缴费金额分布 饼状图数据
     PayContributeChart:null,
     payContributeOption:{
       tooltip: {
         trigger: 'item'
       },
       legend: {
         top: '25%',
         right:'20%',
         data:['小于50元','50~100元','100~200元','200~300元','大于300元'],
         // left: 'center'
         orient:'vertical',
         itemGap:25
       },
       series: [
         {
           name: '缴费金额分布',
           type: 'pie',
           left:'-30%',
           radius: ['40%', '70%'],
           avoidLabelOverlap: true,
           itemStyle: {
             borderRadius: 10,
             borderColor: '#fff',
             borderWidth: 2
           },
           label: {
             show: false,
             position: 'center'
           },
           emphasis: {
             label: {
               show: true,
               fontSize: '20',
               fontWeight: 'bold',
               formatter:(params)=>{
                 return `${params.name}
${params.percent}%`
               }
             }
           },
           labelLine: {
             show: false
           },
           data: [
             // 格式:
             // { value: 1048, name: 'Search Engine' },
           ]
         }
       ]
     }
页面所有显示数据和图标,需要随着搜索框选择值的变化而更新。

这一项就是说搜索框的选择值有变化时,需要重新获取页面数据

// 搜索框查询参数
queryBean: {
  deptId: undefined,
  chargeTimeBegin: undefined,
  chargeTimeEnd: undefined,
},

一个参数影响到重新获取数据,这不watch正合适么,(computed是不行的,因为computed中只支持同步操作),马上将获取参数的函数都安排到watch监视器里面。最终效果完美实现

watch:{
  queryBean:{
      handler(val,oldVal){
        // 获取充值人数
        this.getSelectConsNoCount()
        // 获取缴费信息
        this.getPayDataList()
        this.getSorderDataList()
        this.getRefundDataList()
        // 获取缴费订单数和金额
        this.getPaymentOrderCountAndAmount()
        // 机构贡献排行
        this.getOrganContributeList()
        // 设备贡献排行
        this.getEquipContributeList()
        // 获取缴费金额分布饼状图 数据
        this.getPayContributeAmount()
      },
      deep:true
    }
},

写在最后

其实我觉得发现问题并也不可怕,在项目中我发现的所有问题有些是百度解决的,其他很多都是根据代码原理想想也就改正过来了。就比如说的第一个例子“无法获取到DOM”,获取不到dom要不就是dom不存在,要不就是获取方法不正确。当能够确定写法正确的时候,那就一定是dom本身就不存在(nexttick也可以解决)。最终自然而然的就想到了vue的生命周期,也就顺利解决了这个问题。

好了,感谢您观看到这儿,有什么问题可以评论区留言。如果帮助到了您,欢迎点赞收藏哦!鞠躬!