• vue2+echarts:后台传递一天有多类数据的时候,如何渲染柱状图


    1.需求描述

    后台传输过来的json数据:

    • 一天可能有多个支付渠道进行交易,从而产生交易记录
    • 每天的支付渠道类型不定,需要进行判断

    需求描述: 两个展示各渠道支付环境(支付渠道可能有多重)的柱状图,如下:

    • 柱状图1:

      • 需求: 每天需要展示的数据有:各个渠道的支付金额、退款金额、实收金额
      • 注意:
        • 每个渠道的所有都要展示完
        • 如果某渠道当天没有数据,已经在后台置零处理,故前台不再做处理
    • 柱状图2:

      • 需求:每天需要展示的数据有:各个渠道的支付笔数、退款笔数
      • 注意:
        • 每个渠道的所有都要展示完
        • 如果某渠道当天没有数据,已经在后台置零处理,故前台不再做处理

    2.需求实现

    柱状图样式代码:

            <div id="channelPayMoneyCharts" style="width: 100%;height:400px;" />
            <div id="channelPayTimeCharts" style="width: 100%;height:400px;" />
    

    js:

    • 注意:channelData是父容器调用后台接口后传输过来的数据,数据格式如上所示
    <script>
    export default {
      name: 'channelPie',
      props: ['channelData'],
      data () {
        return {
          // 动态赋值给柱状图
          channelPayMoneyOptionData : [],
          channelPayMoneyOptionLegend : [],
          channelPayTimeOptionData : [],
          channelPayTimeOptionLegend : [],
    	  
    	  channelPayMoneyOption: {
            // 提示框组件,用于配置鼠标滑过或点击图表时的显示框
            tooltip: {
              trigger: 'axis',
              axisPointer: {
                type: 'shadow',
              },
            },
            // 标题
            title: {
              text: '各渠道支付次数汇总分析[单位:元]',
            },
            // 左侧显示图例(按钮)
            legend: {
              type: 'scroll',
              orient: 'horizontal',
              bottom: 10,
              // data: this.channelPayMoneyOptionLegend,
              data: []
            },
            toolbox: {
              show: true,
              orient: 'vertical',
              left: 'right',
              top: 'center',
              feature: {
                mark: { show: true, },
                dataView: { show: true, readOnly: false, },
                magicType: { show: true, type: ['line', 'bar', 'stack', 'tiled'], },
                restore: { show: true, },
                saveAsImage: { show: true, },
              },
            },
            xAxis: [
              {
                type: 'category',
                axisTick: { show: false, },
                data: [],
              }
            ],
            // y轴
            yAxis: [
              {
                type: 'value',
              }
            ],
            series: []
          },
          channelPayTimeOption: {
            tooltip: {
              trigger: 'axis',
              axisPointer: {
                type: 'shadow',
              },
            },
            title: {
              text: '各渠道支付次数汇总分析',
            },
            legend: {
              type: 'scroll',
              orient: 'horizontal',
              bottom: 10,
              data: [],
            },
            toolbox: {
              show: true,
              orient: 'vertical',
              left: 'right',
              top: 'center',
              feature: {
                mark: { show: true, },
                dataView: { show: true, readOnly: false, },
                magicType: { show: true, type: ['line', 'bar', 'stack', 'tiled'], },
                restore: { show: true, },
                saveAsImage: { show: true, },
              },
            },
            xAxis: [
              {
                type: 'category',
                axisTick: { show: false, },
                data: [],
              }
            ],
            yAxis: [
              {
                type: 'value',
                minInterval: 1,
                axisLabel: {
                  formatter: '{value} 笔',
                },
              }
            ],
            series: []
          },
        }
      },
      watch: {
        channelData: {
          handler (newValue, oldValue) {
            this.setChannelCharts(newValue)
          },
          immediate: true,
        },
      },
      mounted () {
        this.showChannelPayMoneyCharts()
        this.showChannelPayTimeCharts()
      },
      methods: {
        // 初始化series的data数组的对象元素
        dataItem() {
          return {
            // 每个柱的名字
            name: '',
            type: '',
            data: []
          }
        },
        showChannelPayMoneyCharts () {
          const channelPayMoneyCharts = this.$echarts.init(document.getElementById('channelPayMoneyCharts'))
          channelPayMoneyCharts.setOption(this.channelPayMoneyOption,true)
        },
        showChannelPayTimeCharts () {
          const channelPayTimeCharts = this.$echarts.init(document.getElementById('channelPayTimeCharts'))
          channelPayTimeCharts.setOption(this.channelPayTimeOption,true)
        },
        setChannelCharts (data) {
    
    	  // 每次赋值之前,先将数据置空一次
          this.channelPayMoneyOptionData = []
          this.channelPayMoneyOptionLegend = []
          this.channelPayTimeOptionData = []
          this.channelPayTimeOptionLegend = []
    
          if (data.channelTotals.length > 0 && data.channelDays.length > 0) {
    
            // region 柱状图数据获取
            // x轴的值
            const x = []
            for (let i = 0; i < data.channelDays.length; i++) {
              x.push(data.channelDays[i].day.substring(data.channelDays[i].day.length - 2, data.channelDays[i].day.length))
            }
    
            for (let i = 0; i < data.channelTotals.length; i++) {
              // 初始化5个对象,并给name赋值,有n个渠道,这里就初始化n*5个对象
              //截取name来判断支付渠道的类型
              const channelPayMoneyOptionDataItem1 = this.dataItem()
              channelPayMoneyOptionDataItem1.name = data.channelTotals[i].chName + "支付金额"
              channelPayMoneyOptionDataItem1.type = 'line'
              this.channelPayMoneyOptionData.push(channelPayMoneyOptionDataItem1)
    
              const channelPayMoneyOptionDataItem2 = this.dataItem()
              channelPayMoneyOptionDataItem2.type = 'line'
              channelPayMoneyOptionDataItem2.name = data.channelTotals[i].chName + "退款金额"
              this.channelPayMoneyOptionData.push(channelPayMoneyOptionDataItem2)
    
              const channelPayMoneyOptionDataItem3 = this.dataItem()
              channelPayMoneyOptionDataItem3.type = 'bar'
              channelPayMoneyOptionDataItem3.name = data.channelTotals[i].chName + "实收金额"
              this.channelPayMoneyOptionData.push(channelPayMoneyOptionDataItem3)
    
              const channelPayTimeOptionDataItem1 = this.dataItem()
              channelPayTimeOptionDataItem1.name = data.channelTotals[i].chName + "支付笔数"
              channelPayTimeOptionDataItem1.type = 'bar'
              this.channelPayTimeOptionData.push(channelPayTimeOptionDataItem1)
    
              const channelPayTimeOptionDataItem2 = this.dataItem()
              channelPayTimeOptionDataItem2.name = data.channelTotals[i].chName + "退款笔数"
              channelPayTimeOptionDataItem2.type = 'line'
              this.channelPayTimeOptionData.push(channelPayTimeOptionDataItem2)
    
              // 给legend数组的名字赋值
              this.channelPayMoneyOptionLegend.push(data.channelTotals[i].chName + "支付金额")
              this.channelPayMoneyOptionLegend.push(data.channelTotals[i].chName + "退款金额")
              this.channelPayMoneyOptionLegend.push(data.channelTotals[i].chName + "实收金额")
    
              this.channelPayTimeOptionLegend.push(data.channelTotals[i].chName + "支付笔数")
              this.channelPayTimeOptionLegend.push(data.channelTotals[i].chName + "退款笔数")
            }
    
            // 给柱状图标题赋值
            this.channelPayTimeOption.title.text = `${data.channelDays[0].day.substr(0, 4)}${data.channelDays[0].day.substr(5, 2)}月每天各支付渠道支付次数汇总统计`
            this.channelPayMoneyOption.title.text = `${data.channelDays[0].day.substr(0, 4)}${data.channelDays[0].day.substr(5, 2)}月每天各支付渠道支付金额汇总统计[单位:元]`
    
    
            for (let i = 0; i < data.channelDays.length; i++) {
              // 一天的数据
              for (let j = 0; j < data.channelDays[i].channelDayDetails.length; j++) {
    
                for (let k = 0; k < this.channelPayMoneyOptionData.length; k++) {
                  if (data.channelDays[i].channelDayDetails[j].chName === this.channelPayMoneyOptionData[k].name.substring(0,this.channelPayMoneyOptionData[k].name.length-4)) {
                    this.channelPayMoneyOptionData[k].data.push(data.channelDays[i].channelDayDetails[j].totalMoney)
                    this.channelPayMoneyOptionData[k + 1].data.push(data.channelDays[i].channelDayDetails[j].refundMoney)
                    this.channelPayMoneyOptionData[k + 2].data.push(data.channelDays[i].channelDayDetails[j].realPay)
                    k += 2
                  }
                }
                for (let l = 0; l < this.channelPayTimeOptionData.length; l++) {
                  if (data.channelDays[i].channelDayDetails[j].chName === this.channelPayTimeOptionData[l].name.substring(0,this.channelPayTimeOptionData[l].name.length-4)) {
                    this.channelPayTimeOptionData[l].data.push(data.channelDays[i].channelDayDetails[j].payTime)
                    this.channelPayTimeOptionData[l + 1].data.push(data.channelDays[i].channelDayDetails[j].refundTimes)
                    l++
                  }
                }
              }
            }
    
            this.channelPayTimeOption.xAxis[0].data = x
            this.channelPayMoneyOption.xAxis[0].data = x
            this.channelPayMoneyOption.legend.data = this.channelPayMoneyOptionLegend
            this.channelPayMoneyOption.series = this.channelPayMoneyOptionData
            this.channelPayTimeOption.legend.data = this.channelPayTimeOptionLegend
            this.channelPayTimeOption.series = this.channelPayTimeOptionData
    
            // endregion
    
            //region 数据渲染
            this.showChannelPayMoneyCharts()
            this.showChannelPayTimeCharts()
            // endregion
          } else {
    		// 清空数据
            if (this.channelPayTimeOption.series.length > 0) {
              for (let i = 0; i < this.channelPayTimeOption.series.length; i++) {
                this.channelPayTimeOption.series[i].data = []
              }
              this.channelPayTimeOption.xAxis[0].data = []
            }
            if (this.channelPayMoneyOption.series.length > 0) {
              for (let i = 0; i < this.channelPayMoneyOption.series.length; i++) {
                this.channelPayMoneyOption.series[i].data = []
              }
              this.channelPayMoneyOption.xAxis[0].data = []
            }
    
            this.showChannelPayCharts()
            this.showChannelTimesCharts()
            this.showChannelPayMoneyCharts()
            this.showChannelPayTimeCharts()
          }
          console.log(this.channelPayMoneyOption)
          console.log(this.channelPayTimeOption)
        },
      },
    }
    </script>
    

    3.小结

    • legend的data赋值的时候,直接声明一个数组(存放字符串),然后将后台数据装进数组,然后再把数组的值传给legend.data就可以了

    • series赋值的时候,直接声明一个数组(存放对象),需要声明一个构造器来实例化对象,然后存进数组中,构造器的内容参考官方文档后,其属性有:name、type、data

          dataItem() {
            return {
              // 每个柱的名字
              name: '',
              type: '',
              data: []
            }
          },
      
    • 当一天的柱状图有 n 条,那么legenddata就需要装 n 个字符串,series就需要装 n 个对象

    4.优化代码思路

    这是一个前后端分离的项目,先说一下后台优化的思路,即如何数据置零:

    • 先遍历总体数据的所有支付渠道

    • 然后给每天的数据中都添加进所有支付渠道,即使那天渠道数据为空

      	这一步很简单,先实例化pojo赋初值,然后判断数据库数据是否为空
      		- 如果为空,那么久直接把pojo对象传进去(这时候,pojo所有值都是初始值,主要用来占位,保证数据与x轴日期一一对应)
      		- 如果不为空,那就将数据库数据赋值给pojo对象
      
    • 优化之后,后台传过来的每日数据如下:

    接着是对上面的前端js代码的优化思路

    • legendseries 直接指向空数组,然后在赋值方法中声明对应的数组对象,最后给 legend和series赋值

              legend: {
                type: 'scroll',
                orient: 'horizontal',
                bottom: 10,
                data: [],
              },
              series: [],
              
              ...
              
              // Series==>需要显示的每一项数据名字、类型、以及数据
              const channelPayMoneySeries = []
              const channelPayTimeSeries = []
      
              // legend==>需要显示的标签(数据标签)
              const channelPayMoneyLegend = []
              const channelPayTimeLegend = []
      		...
      		// 给option赋值
      		this.channelPayTimeOption.xAxis[0].data = x
              this.channelPayMoneyOption.xAxis[0].data = x
              this.channelPayMoneyOption.legend.data = channelPayMoneyLegend
              this.channelPayMoneyOption.series = channelPayMoneySeriesNew
              this.channelPayTimeOption.legend.data = channelPayTimeLegend
              this.channelPayTimeOption.series = channelPayTimeSeriesNew
      
    • 因为后台数据已经优化,所以前端赋值代码也要改变,给legendseries的name和type的赋值只需要遍历 channelDays[0].channelDayDetails 就可
      因为js弱类型语言的特性,所以可以直接给series的data中的对象添加chId属性进去,进行类型判断,就不需要进行字符串截取判断了,这样更方便

      data.channelDays[0].channelDayDetails.forEach((channelDayDetail) => {
            const channelPayMoneyItem = this.dataItem()
            channelPayMoneyItem.name = channelDayDetail.chName + '支付金额'
            channelPayMoneyItem.chId = channelDayDetail.chId
            channelPayMoneyItem.type = 'line'
      
    • 现在给series赋值,其逻辑如下:

      • 循环赋值的语句依旧可以使用上述循环赋值的逻辑,不过现在是根据chId进行匹配对应的渠道数据
                data.channelDays.forEach((channelDay) => {
                  channelDay.channelDayDetails.forEach((channelDayDetail) => {
                    channelPayMoneySeries.forEach((channelPayMoneySerie) => {
                      if (channelDayDetail.chId == channelPayMoneySerie.chId) {
        
      • 在赋值完成之后,为了显示的美观,需要删除没有数据的渠道(支付渠道存在多种,有的渠道在total中统计了,但是实际上并额没有人通过该渠道付款,如果不清除,会影响数据的直观显示)
                // 删除没有统计数据的渠道
                const channelPayMoneySeriesNew = channelPayMoneySeries.filter((channelPayMoneySerie) => {
                  let dataSum = 0
                  channelPayMoneySerie.data.forEach((dataItem) => {
                    dataSum += dataItem
                  })
                  return dataSum > 0
                })
        
                const channelPayTimeSeriesNew = channelPayTimeSeries.filter((channelPayTimeSerie) => {
                  let dataSum = 0
                  channelPayTimeSerie.data.forEach((dataItem) => {
                    dataSum += dataItem
                  })
                  return dataSum > 0
                })
        
  • 相关阅读:
    树、二叉树、斜树、满二叉树、完全二叉树、二叉排序树、平衡二叉搜索树(AVL树) 、哈夫曼树(Huffman tree)、B树、B+Tree、B*树
    stm32f4_奇怪的bug_串口数据错乱,一个串口收到另一个串口的数据
    CocosCreator 面试题(三)JavaScript闭包原理和作用
    java基于springcloud+vue+elementui的养老院管理系统 前后端分离
    Datastage部署与使用
    Java集合相关知识
    【滤波器】基于matlab实现微波带低通高通带通滤波器设计
    浅析:OMS和ERP、WMS、TMS之间的关系?
    【总结】Idea 编译maven项目报错NoSuchMethodError DefaultModelValidator
    深度学习 RNN循环神经网络原理与Pytorch正余弦值预测
  • 原文地址:https://blog.csdn.net/m0_54355172/article/details/127100739