Bootstrap

应用开发平台能力扩展——集成echarts组件实现图表展现能力

背景

图表展现能力是平台需具备的基础能力,目前echarts是最佳选择。
在早期版本的图表中,不同的图表样式,需要不同的数据格式,需要进行复杂封装才能易于使用。百度官方也意识到这个问题,在echarts 4.0版本提供了dataset属性支持,提供了统一的数据格式,也曾考虑基于这一新特性将常用图表进行封装。
后来,发现了饿了么团队出品的开源组件v-charts,统一提供了一种对前后端都友好的数据格式,只需设置简单的配置项,便可轻松生成常见的图表。
但v-charts停留在了vue2.x版本,vue3.x完全没有动静。
官方自己出了vue-echarts,本文从实战角度介绍下如何与平台进行集成,包括集成方案、具体实现和注意事项等。

技术预研

vue-echarts 官方地址
https://github.com/ecomfe/vue-echarts/blob/HEAD/README.zh-Hans.md

说明过于简单了,如果只看这里的资料,不了解echarts自身的概念和配置,集成和使用时会非常困难,推荐了解以下相关知识。

查看echarts图表官网文档,了解基本概念,如坐标轴、图例、数据集等
https://echarts.apache.org/handbook/zh/concepts/chart-size

查看echarts图表官网文档 配置说明
https://echarts.apache.org/zh/option.html#legend.top

集成目标

echarts提供了种类繁多的图表,大概有40多种大类,详见https://echarts.apache.org/examples/zh/index.html#chart-type-line

在实际软件系统中,最常见最实用实际就是三大类型图表:折线图、柱状图、饼图。
平台的集成主要目标是实现这三大类图表集成。如某个特定的业务需求使用到了其他类型图表样式,也可以通过直接使用原生的echarts图表组件来实现。

注:对于企业应用而言,图表需求并非只有echarts这一种实现方式。实际复杂的报表需求,特别是管理驾驶舱,往往使用的是类似于帆软这种专门的报表工具。图表工具与报表工具,定位上有差异,但功能又有重叠。图表工具依托后端服务接口,前端进行友好展现,数据的查询、转换都在应用后端完成;而报表功能往往是直连数据库,进行报表模板的开发和部署,数据的查询、转换主要在报表应用端上完成,然后集成到应用系统中,实际的方式机制是不同的。

配置选项

要考虑集成方案,先了解下echarts图表组件现状,对于这三类图表的配置选项要求,具体如下:

饼图

option = {
  series: [
    {
      type: 'pie',
      data: [
        {
          value: 335,
          name: '直接访问'
        },
        {
          value: 234,
          name: '联盟广告'
        },
        {
          value: 1548,
          name: '搜索引擎'
        }
      ]
    }
  ]
};

柱状图

option = {
  xAxis: {
    data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
  },
  yAxis: {},
  series: [
    {
      type: 'bar',
      data: [23, 24, 18, 25, 27, 28, 25]
    }
  ]
};

折线图

option = {
  xAxis: {   
    data: ['A', 'B', 'C']
  },
  yAxis: {    
  },
  series: [
    {
      data: [120, 200, 150],
      type: 'line'
    }
  ]
};

配置分析

相同点

图表类型,统一放在series.type属性中

不同点

饼图则没有x轴和y轴的概念,在series.data里配置一个对象数组,name代表类目,value代表值
折线图和柱状图相同,基于直角坐标系,有x轴和y轴,xAxis.data类配置一个一维数组作为类目,series.data里配置一个一维数组作为值

集成方案

实现模式——传统模式VS数据集模式

基于上述分析,可以为饼图、柱状图和折线图各封装出一个组件来,组件内部提供默认设置,将关键属性,如数据data,属性option作为属性暴露,供调用方使用。
这种方式能走得通,但并非最佳方案。在最开始背景部分,提到过echart产品的演进。在早期版本的图表中,不同的图表样式,需要不同的数据格式,需要进行封装才能易于使用。官方也意识到这个问题,在echarts 4.0版本提供了dataset属性支持,提供了统一的数据格式。

使用数据集的优势主要自于将数据与配置分离,使数据可以多组件复用,以下引用官方说明:

数据集(dataset)是专门用来管理数据的组件。虽然每个系列都可以在 series.data 中设置数据,但是从 ECharts4 支持数据集开始,更推荐使用数据集来管理数据。因为这样,数据可以被多个组件复用,也方便进行 “数据和其他配置” 分离的配置风格。毕竟,在运行时,数据是最常改变的,而其他配置大多并不会改变。

在系列中设置数据
如果数据设置在 系列(series) 中,例如:

option = {
  xAxis: {
    type: 'category',
    data: ['Matcha Latte', 'Milk Tea', 'Cheese Cocoa', 'Walnut Brownie']
  },
  yAxis: {},
  series: [
    {
      type: 'bar',
      name: '2015',
      data: [89.3, 92.1, 94.4, 85.4]
    },
    {
      type: 'bar',
      name: '2016',
      data: [95.8, 89.4, 91.2, 76.9]
    },
    {
      type: 'bar',
      name: '2017',
      data: [97.7, 83.1, 92.5, 78.1]
    }
  ]

这种方式的优点是,适于对一些特殊的数据结构(如“树”、“图”、超大数据)进行一定的数据类型定制。 但是缺点是,常需要用户先处理数据,把数据分割设置到各个系列(和类目轴)中。此外,不利于多个系列共享一份数据,也不利于基于原始数据进行图表类型、系列的映射安排。
在数据集中设置数据
而数据设置在 数据集(dataset) 中,会有这些好处:

  • 能够贴近数据可视化常见思维方式:(I)提供数据,(II)指定数据到视觉的映射,从而形成图表。
  • 数据和其他配置可以被分离开来。数据常变,其他配置常不变。分开易于分别管理。
  • 数据可以被多个系列或者组件复用,对于大数据量的场景,不必为每个系列创建一份数据。
  • 支持更多的数据的常用格式,例如二维数组、对象数组等,一定程度上避免使用者为了数据格式而进行转换。

来源:https://echarts.apache.org/handbook/zh/concepts/dataset/

官方对于数据集的优势已经说得很清楚了,不再赘述,选择数据集模式的优势是显而易见的。

组件封装的必要性

官方的vue-echarts,实际已经对echarts进行了良好的封装,包括提供默认配置、对外暴露属性,以及提供事件和方法。
下面来探讨下二次封装的必要性,平台集成是否还需要再二次封装呢?
如果直接使用vue-echarts组件,则优点是该组件质量比较高,且以后还会持续更新,缺点是需要了解echarts的基本概念和配置信息,遵循echarts的规范和要求,有一定的学习成本。
如果二次封装,可以将echarts的配置信息给封装掉,例如为饼图专门封装一个组件,暴露一个属性data用来接收数据就好了,其他细节一概不管,由平台提供默认实现,缺点在于,将vue-echarts组件的属性、事件、方法再包装一层提供出去,价值和意义不大。
此外,vue-element-plus-admin框架自带的封装组件中,有对vue-echarts组件的二次封装,使用时只需要输入配置option和高度height即可。

考虑到官方vue-echarts自身封装质量高,灵活性和扩展性强,暂不进行封装,以后有了更明确的封装的必要性后再考虑二次封装。
同时,鉴于echart自身提供了数据集模式,以及提供了数据映射功能,因此专门封装前后端交互的vo对象这件事也不需要做了,完全可以复用现有的rest请求,返回实体的对象数组,在echarts中配置数据映射。

具体实现

安装

pnpm install echarts vue-echarts

注意,需要同时安装这两个,而不是只安装vue-echarts就行了。

引入

修改main.js文件,增加echarts的引入和初始化

// echart图表
import "echarts"
import ECharts from "vue-echarts"



// 创建实例
const setupAll = async () => {
  const app = createApp(App)// echart图表
  app.component('v-chart', ECharts)

  app.mount('#app')
}

这里实际采用的全量引入模式,后面再优化,实现仅引入折线图、柱状图和饼图这三大类用到的图表,缩减体积。

使用

在前端项目modules目录下新建一个echart的目录,用于存放示例图表,在其下方创建了三个典型图表。并使用了平台的Portlet功能进行了定义。
image.png
可以直接通过拖动来配置为自己的桌面,如下图
image.png

最终效果如下:
image.png
以下是代码实现,注意使用了数据集的方式,

饼图:用户来源

<template>
  <v-chart :option="option" theme="auto" :autoresize="true" style="width: 100%; height: 300px" />
</template>

<script>
export default {
  data() {
    return {
      option: {
        dataset: {
          source: [
            ['数量', '来源'],
            [335, '直接访问'],
            [310, '邮件营销'],
            [234, '联盟广告'],
            [135, '视频广告'],
            [1548, '搜索引擎']
          ]
        },
        title: {
          show: false
        },
        grid: {
          top: 20,
          bottom: 0
        },
        tooltip: {
          trigger: 'item',
          formatter: function (params) {
            // 只返回第一列数据
            return params.data[0] + '(' + params.percent + '%)'
          }
        },
        legend: {
          orient: 'horizontal',
          top: 'bottom',
          left: 'center'
        },
        series: [
          {
            type: 'pie',
            radius: '55%',
            center: ['50%', '60%'],
            encode: {
              tooltip: [0, 1],
              value: 0,
              itemName: 1
            },
            emphasis: {
              itemStyle: {
                shadowBlur: 10,
                shadowOffsetX: 0,
                shadowColor: 'rgba(0, 0, 0, 0.5)'
              }
            }
          }
        ]
      }
    }
  },
  methods: {}
}
</script>

<style></style>

官网基于数据集和数据映射的内容介绍比较分散,实际还是需要自己多尝试修改下配置,来确认参数作用,实现最终效果。
注意点有两个地方。
一是数据映射,如下图。value:0代表将数据集dataset中第0列作为数据,itemName: 1代表将数据集dataset中第1列作为维度。映射错了图表很可能就显示残缺或干脆显示白屏。

encode: {
  tooltip: [0, 1],
  value: 0,
  itemName: 1
}

二是鼠标悬停某个区域时,浮动显示进一步信息,默认的配置是
image.png
如果想显示占比 ,需要自己重写formater方法,如下图所示

 tooltip: {
  trigger: 'item',
  formatter: function (params) {
    // 只返回第一列数据
    return params.data[0] + '(' + params.percent + '%)'
  }
}

效果如下:
image.png
显示效果确实不如官方默认的美观,不过还是可以进一步控制的,这里只是提供一种定制的思路。

这些内容是官网里没提到的,自己摸索,用console.log打印出了params信息,发现有了percent属性,就不用自己去做计算了。

周活跃用户数:柱状图

<template>
  <v-chart :option="option" theme="auto" :autoresize="true" style="width: 100%; height: 300px" />
</template>

<script>
export default {
  data() {
    return {
      option: {
        dataset: {
          source: [
            ['count', 'week'],
            [13253, '周一'],
            [34235, '周二'],
            [26321, '周三'],
            [12340, '周四'],
            [24643, '周五'],
            [1322, '周六'],
            [1324, '周日']
          ]
        },
        xAxis: { type: 'category' },
        yAxis: {},
        series: [
          {
            type: 'bar',
            encode: {
              x: 'week',
              y: 'count'
            }
          }
        ]
      }
    }
  },
  methods: {}
}
</script>

<style></style>

注意以下属性必须有
xAxis: { type: ‘category’ },
yAxis: {},
要不然会报一个莫名奇妙的错误uncaught (in promise) Error: xAxis “0” not found

月销售量:折线图

下面实际是一个双折线图

<template>
  <v-chart :option="option" theme="auto" :autoresize="true" style="width: 100%; height: 300px" />
</template>

<script>
export default {
  data() {
    return {
      option: {
        dataset: {
          source: [
            ['estimate', 'actual', 'month'],
            [100, 120, '一月'],
            [120, 82, '二月'],
            [161, 91, '三月'],
            [134, 154, '四月'],
            [105, 162, '五月'],
            [160, 140, '六月'],
            [165, 145, '七月'],
            [114, 250, '八月'],
            [163, 134, '九月'],
            [185, 56, '十月'],
            [118, 99, '十一月'],
            [123, 123, '十二月']
          ]
        },
        xAxis: {
          type: 'category'
        },
        yAxis: {
          axisTick: {
            show: false
          }
        },
        tooltip: {
          trigger: 'axis',
          axisPointer: {
            type: 'cross'
          },
          padding: [5, 10]
        },
        legend: {
          data: ['预计', '实际'],
          top: 20
        },
        series: [
          {
            type: 'line',
            name: '预计',
            smooth: true,
            encode: {
              x: 'month',
              y: 'estimate'
            },
            animationDuration: 2800,
            animationEasing: 'cubicInOut'
          },
          {
            type: 'line',
            name: '实际',
            smooth: true,
            encode: {
              x: 'month',
              y: 'actual'
            },
            animationDuration: 2800,
            animationEasing: 'quadraticOut'
          }
        ]
      }
    }
  },
  methods: {}
}
</script>

<style></style>

总结

从上面具体使用可以看出来,直接使用官方已经封装好的vue-echarts组件还是比较方便的,配置option是复杂多变的,二次封装的意义不大。在此基础上使用其他类型的图表,也很容易。

echarts自身的概念和配置选项不可避免要花点时间去了解和熟悉,总体而言学习成本还好,注意使用数据集模式。

开发平台资料

平台名称:一二三开发平台
简介: 企业级通用开发平台
设计资料:csdn专栏
开源地址:Gitee
开源协议:MIT
开源不易,欢迎收藏、点赞、评论。

;