业务项目中Echarts图表组件的封装实践方案

背景:如果我们的项目是一个可视化类/营销看板类/大屏展示类业务项目,不可避免的会使用到各种图表展示。那在一个项目中如何封装一个图表组件既能够快速复用、UI统一,又可以灵活扩充Echarts的各种复杂配置项配置就变得极为重要。

封装目标

  • 符合当前系统的业务UI(轴线、分隔线、配色、面积色、legend 等等)及场景
  • 可基于基础配置项便捷扩充其他特殊配置项
  • 可支持Echarts原生配置项,不引入过多额外的配置项!!!

封装误区

  • 基于Echarts配置项封装了基础配置项组件,但是组件无法扩充额外的配置项,使用不灵活(即总是需要频繁修改封装组件)
  • 在Echarts的的配置项上又封装了一层,改变了很多的配置项内容,使用成本较高(往往需要查看组件源码才知道要传入什么配置项属性)

封装思路

在这里插入图片描述

Vue项目实践

线图封装

<template>
  <div v-if="notEmpty" :id="id" class="echarts-line"></div>
  <div v-else class="echarts-empty">暂无数据</div>
</template>

<script>
import echarts from 'echarts';
import deepmerge from 'deepmerge';

// 系统自定义区域
const colors = []; // 系统自定义的主题配色

export default {
  name: 'EchartsLine',
  props: {
    echartsData: {
      type: Object,
      required: true,
    },
  },
  data() {
    return {
      lineChart: null,
    };
  },
  computed: {
    id() {
      return `echarts_line_${this.echartsData.id}`;
    },
    notEmpty() {
      return this.echartsData && this.echartsData.category.length > 0 && this.echartsData.series.length > 0;
    },
  },
  watch: {
    echartsData(value) {
      if (this.lineChart) {
        this.lineChart.setOption(this.getMergeOptions(value));
        this.lineChart.resize();
      }
    },
  },
  mounted() {
    this.init();
  },
  beforeDestroy() {
    window.removeEventListener('resize', this._listenerResize);
  },
  methods: {
    // [private] 处理轴的类型配置项,支持x轴为类目轴 | y轴为类目轴 | 双数据轴
    _dealAxisType(type, category) {
      const categoryAxis = {
        type: 'category',
        boundaryGap: true,
        data: category,
      };
      const valueAxis = {
        type: 'value',
      };

      switch (type) {
        case 'xCategory':
          return {
            xAxis: categoryAxis,
            yAxis: valueAxis,
          };
        case 'yCategory':
          return {
            yAxis: categoryAxis,
            xAxis: valueAxis,
          };
        case 'doubleValue':
          return {
            xAxis: {
              max: 'dataMax',
              boundaryGap: true,
              splitLine: {
                show: false,
              },
            },
            yAxis: {},
          };
        default:
          return {
            xAxis: categoryAxis,
            yAxis: valueAxis,
          };
      }
    },

    // [private] 获取线图默认配置项,系统整体统一UI
    _getDefaultOptions(type, category) {
      return {
        title: {
          subtext: '',
          left: 'center',
          textStyle: {
            color: '#98a6ad',
            fontSize: 16,
            fontWeight: 'normal',
          },
        },
        legend: {
          type: 'scroll',
          bottom: '0',
        },
        grid: {
          top: '30px',
          bottom: '50px',
        },
        color: colors,
        tooltip: {
          trigger: 'axis',
          axisPointer: {
            type: 'cross',
          },
        },
        ...this._dealAxisType(type, category),
      };
    },

    // [private] 监听resize时间
    _listenerResize() {
      if (this.lineChart) {
        this.lineChart.resize();
      }
    },

    /**
     * [public] getMergeOptions 获取合并后的图表配置项,自定义配置项与默认配置项融合,若自定义配置项与默认配置项冲突则自定义配置项生效
     * 配置项合并规则:https://www.npmjs.com/package/deepmerge
     * @param type {String} 
     * @param category {Array}
     * @param series {Array}
     * @param echartsConfig {Object}
     */
    getMergeOptions({
      type = 'xCategory',
      category,
      series,
      echartsConfig = {},
    }) {

      // 01. 用户传进来的配置项和默认配置项进行合并
      const mergeOptions = deepmerge(
        this._getDefaultOptions(type, category),
        echartsConfig,
      );

      // 02. Series配置项合并【此处为示例】
      const mergeSeries = [];
      if (series && series.length > 0) {
        series.forEach((item) => {
          mergeSeries.push(
            deepmerge(item, {
              type: 'line',
            }),
          );
        });
      }

      // 03. 返回合并后的整体配置项 
      return {
        ...mergeOptions,
        series: mergeSeries,
      };
    },

    // [public] 交互点,获取图表实例,用于触发图表API
    getEchartsInstance() {
      if (this.lineChart) {
        return this.lineChart;
      }
      return null;
    },

    // [public] 初始化图表
    init() {
      this.$nextTick(() => {
        this.lineChart = echarts.init(document.getElementById(this.id));
        this.lineChart.setOption(
          this.getMergeOptions(this.echartsData),
        );
        window.addEventListener('resize', this._listenerResize);
      });
    },
  },

};
</script>

<style lang="less" rel="stylesheet/less" scoped>
.echarts-line{
  height: 350px;
}

.echarts-empty{
  height: 200px;
  line-height: 200px;
  text-align: center;
}
</style>

业务调用

<v-line-chart class="echarts-item" :echarts-data="chartData"></v-line-chart>
 this.chartData = {
   id: 'lineChart',
   category:['test1','test2','test3','test4'],
   series: [{
     name: '测试图表',
     data:[10,20,30,40],
   }],
   echartsConfig: { // 所有Echarts原生配置项放在该属性下
     legend: {
       show: false,
     },
   },
};

参考

业务方案简单封装,不作为公共UI库使用,欢迎讨论其他实现方案

  • 12
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
作为一名AI语言模型,我无法进行编程操作,但我可以提供您一个完整的echarts图表vue组件的基本结构和代码示例,您可以根据它进行修改和完善。 基本结构: ``` <template> <div class="echarts-chart"></div> </template> <script> import echarts from 'echarts' export default { name: 'EchartsChart', props: { // 接收外部传入的数据 chartData: { type: Object, required: true }, // 配置项 options: { type: Object, default() { return {} } } }, data() { return { // 定义一个echarts实例 chart: null } }, mounted() { // 在组件挂载后初始化echarts实例 this.chart = echarts.init( this.$el, null, this.options ) // 渲染图表 this.renderChart(this.chartData) }, methods: { // 渲染图表 renderChart(chartData) { this.chart.setOption(chartData) } }, watch: { // 监听chartData的变化,重新渲染图表 chartData(newValue) { this.renderChart(newValue) } }, beforeDestroy() { // 销毁echarts实例 this.chart.dispose() this.chart = null } } </script> <style> .echarts-chart { width: 100%; height: 100%; } </style> ``` 代码示例: ``` <template> <div class="echarts-chart"></div> </template> <script> import echarts from 'echarts' export default { name: 'EchartsChart', props: { // 接收外部传入的数据 chartData: { type: Object, required: true }, // 配置项 options: { type: Object, default() { return {} } } }, data() { return { // 定义一个echarts实例 chart: null } }, mounted() { // 在组件挂载后初始化echarts实例 this.chart = echarts.init( this.$el, null, this.options ) // 渲染图表 this.renderChart(this.chartData) }, methods: { // 渲染图表 renderChart(chartData) { this.chart.setOption(chartData) } }, watch: { // 监听chartData的变化,重新渲染图表 chartData(newValue) { this.renderChart(newValue) } }, beforeDestroy() { // 销毁echarts实例 this.chart.dispose() this.chart = null } } </script> <style> .echarts-chart { width: 100%; height: 100%; } </style> ``` 使用方法: ``` <template> <div class="app"> <EchartsChart :chartData="chartData" :options="options"></EchartsChart> </div> </template> <script> import EchartsChart from './components/EchartsChart.vue' export default { name: 'App', components: { EchartsChart }, data() { return { chartData: { // echarts图表数据 }, options: { // echarts配置项 } } } } </script> <style> .app { width: 100%; height: 100%; } </style> ``` 以上是一个基本的echarts图表vue组件的结构和代码示例,您可以根据自己的需求进行修改和完善。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值