网站/小程序/APP个性化定制开发,二开,改版等服务,加扣:8582-36016

    这篇文章主要介绍了Vue echarts封装的实现,Echarts,它是一个与框架无关的JS图表库,但是它基于JS,这样很多框架都能使用它

    将echarts封装成组件,达到只要调用方法,传入数据和相应的参数就能生成图表的效果,避免在项目中编写大量重复和累赘的echarts的配置代码,实现的思路如下:

    接口返回的一般是json数据,所以首先要将json数据进行处理,处理成echarts需要的数据形式

    将echarts的配置代码封装在一个方法里,通过自定义的配置名进行调用

    下面对我自己封装的组件 EchartsGenerate 逐步解释

    首先看template

    <template>
      <div>
        <slot></slot>
      </div>
    </template>

    这里使用插槽slot是因为,有时候图表的样式要根据页面进行调整,所以用插槽好方便自定义样式,就比如下面的代码:

    <echarts-generate ref="echarts" name-data-key="title" value-data-key="score">
          <!-- 中间的元素设置id -->
          <div class="chart-container" id="chart-sample"></div>
    </echarts-generate>
    <style>
        .chart-container {
          position: relative;
          height: 50vh;
          overflow: hidden;
        }
    </style>

    通过class来设置图表的样式

    再看props

    props: {
        // 坐标对应的 传入数据指定的键值
        nameDataKey: {
          type: String,
          default: "name",
        },
        // 数据对应的 传入数据指定的键值
        valueDataKey: {
          type: String,
          default: "value",
        },
        // 图表标题
        chartTitle: {
          type: String,
        },
      },

    nameDataKey和valueDataKey分别对应传入数据的键的名字和值和名字,比如,假如数据是这样

    [
        {
           id: "physical1",
             // label
             title: "physical1",
             // 排序
             sort: 0,
             // 分数
             score: 11,
             desc: "户外活动1",
             // 分数
             point: 25,
           },
           {
             id: "taste1",
             title: "taste1",
             sort: 1,
             score: 25,
             desc: "味道1",
             // 分数
             point: 35,
           },
           {
             id: "acceptance1",
             title: "acceptance1",
             sort: 2,
             score: 55,
             desc: "接受度1",
             // 分数
             point: 45,
           },
         ];

    那么在组件上设置 name-data-key="title" value-data-key="score" 那图表的横坐标是title对应的值,竖坐标是score对应的值,这个在后面会详细说。

    最后在看主要的方法

    首先看处理json数据的方法

    generateChartInData(list) {
          let chartInData = {};
          // 保证list中的每个对象的属性名是相同的,也就是说一一对应
          for (let attr1 in list[0]) {
            // 以每个属性名为名字构建数组
            chartInData[attr1] = [];
          }
          list.forEach(function (item, index) {
            for (let attr2 in item) {
              // chartInData[attr2] 为underfined时 初始化为空数组
              if (!chartInData[attr2]) {
                chartInData[attr2] = [];
              }
              chartInData[attr2].push(item[attr2]);
            }
          });
          chartInData["length"] = list.length;
          return chartInData;
        },

    上面方法实现的效果是将json数组转换为一个包含以属性名命名数组的对象,例如传入的数据是这个格式

    [
        {
           id: "physical1",
             // label
             title: "physical1",
             // 排序
             sort: 0,
             // 分数
             score: 11,
             desc: "户外活动1",
             // 分数
             point: 25,
           },
           {
             id: "taste1",
             title: "taste1",
             sort: 1,
             score: 25,
             desc: "味道1",
             // 分数
             point: 35,
           },
           {
             id: "acceptance1",
             title: "acceptance1",
             sort: 2,
             score: 55,
             desc: "接受度1",
             // 分数
             point: 45,
           },
         ];

    通过generateChartInData方法生成的数据如下:

    {
        id: ["physical1", "taste1","acceptance1"],
        title: ["physical1", "taste1", "acceptance1"],
        sort: [ 0,1,2],
        score: [11,25, 55],
        desc: ["户外活动1","味道1","接受度1"],
        point: [25,35,45],
        length: 3
    }

    将通过generateChartInData生成的数据,传入下面的方法中

    // 生成图表数据
    chartDataFactory(dataType, chartInData) {
      let chartOutData = {};
      switch (dataType) {
        // 根据需求配置数据
        case "listData":
          // 生成数组数据
          // 单个数据格式为 [1,2,3]
          // 多个数据格式为 [[1,2,3],[1,2,4],[3,4,5]]
          if (Array.isArray(chartInData) && chartInData.length > 0) {
            let seriesList = [];
            chartInData.forEach((item) => {
              seriesList = [...seriesList, item[this.valueDataKey]];
            });
            chartOutData = {
              xAxisData: chartInData[0][this.nameDataKey],
              seriesData: seriesList,
            };
          } else {
            chartOutData = {
              xAxisData: chartInData[this.nameDataKey],
              seriesData: chartInData[this.valueDataKey],
            };
          }
          break;
        case "objectData":
          // 生成对象数据
          // 数据格式为
          // {name:"", value:""}
          chartOutData = {
            seriesData: this.generateObjectData(
              chartInData,
              this.nameDataKey,
              this.valueDataKey
            ),
          };
          break;
      }
      return chartOutData;
    },
    // 生成对象数据源
    // 属性为 name和value
    // chartInData  生成的图表数据
    // nameKey name对应的键
    // valueKey value对应的键
    generateObjectData(chartInData, nameKey, valueKey) {
      let objectList = [];
      for (var i = 0; i < chartInData["length"]; i++) {
        let objectItem = {
          name: "",
          value: "",
        };
        objectItem.name = chartInData[nameKey][i];
        objectItem.value = chartInData[valueKey][i];
        objectList = [...objectList, objectItem];
      }
      return objectList;
    },

    在chartDataFactory这个方法里面就用到了前面提到的nameDataKey和valueDataKey。然后这个方法处理了多条数据的,可以参考下

    下面是将echarts图表的配置都封装在getOption这个方法里面,同时把chartDataFactory生成的数据传入这个方法

    // 配置option
    getOption(optionType, chartOutData) {
      let option = {};
      let seriesList = [];
      // 如果seriesData有数据,且seriesData的第一个元素是数组,说明传入的数据是多对象数组
      // 否则说明传入的是单个对象
      if (
        chartOutData.seriesData.length > 0 &&
        Array.isArray(chartOutData.seriesData[0])
      ) {
        seriesList = chartOutData.seriesData.map((item) => {
          return (item = {
            data: item,
          });
        });
      } else {
        seriesList = [
          {
            data: chartOutData.seriesData,
          },
        ];
      }
      switch (optionType) {
        // 基础折线图
        case "lineOption":
          // 遍历后添加其他属性
          seriesList = seriesList.map((item) => {
            return (item = {
              data: item.data,
              type: "line",
            });
          });
          option = {
            title: {
              text: this.chartTitle,
            },
            xAxis: {
              type: "category",
              data: chartOutData.xAxisData,
            },
            yAxis: {
              type: "value",
            },
            series: seriesList,
          };
          break;
        // 基础柱状图
        case "barOption":
          seriesList = seriesList.map((item) => {
            return (item = {
              data: item.data,
              type: "bar",
            });
          });
          option = {
            xAxis: {
              type: "category",
              data: chartOutData.xAxisData,
            },
            yAxis: {
              type: "value",
            },
            series: seriesList,
          };
          break;
         // 基础饼图
        case "pieOption":
          option = {
            title: {
              text: this.chartTitle,
              left: "center",
            },
            tooltip: {
              trigger: "item",
            },
            legend: {
              orient: "vertical",
              left: "left",
            },
            series: [
              {
                name: "Access From",
                type: "pie",
                radius: "50%",
                data: chartOutData.seriesData,
                emphasis: {
                  itemStyle: {
                    shadowBlur: 10,
                    shadowOffsetX: 0,
                    shadowColor: "rgba(0, 0, 0, 0.5)",
                  },
                },
              },
            ],
          };
          break;
      }
      return option;
    },

    后续开发在getOption这个方法里添加配置

    最后是生成图表的方法

    // 生成图表
    // domRef 图表标识 id
    // dataType 图表数据类型
    // optionType option类型
    // list 要生成图表的数据列表
    generateChart(domRef, dataType, optionType, list) {
      let chartInData = null;
      if (document.getElementById(domRef) || this.$refs[domRef]) {
        let chartDom = this.initChartDom(domRef);
        // 存在表格的话先进行销毁
        if (chartDom) {
          let chart = this.getChart(domRef);
          // 存在表格的话先进行销毁
          if (chart) {
            chart.dispose();
          }
          // 如果传入数据为空,则返回
          if(list.length<=0){
            return
          }
          // 如果list的子元素是数组
          if ( Array.isArray(list[0])) {
            // list是包含多条数据的数组
            chartInData = list.map((item) => {
              // item 是数据列表
              return this.generateChartInData(item);
            });
          }
          // 如果list的子元素不是数组时对象
          if (!Array.isArray(list[0])) {
            chartInData = this.generateChartInData(list);
          }
          let data = this.chartDataFactory(dataType, chartInData);
          let option = this.getOption(optionType, data);
          if (option && typeof option === "object") {
            chartDom.setOption(option);
          }
        }
      }
    },

    其中图表标识最好是通过id,使用ref会没效果

    完整代码如下

    <template>
      <div>
        <slot></slot>
      </div>
    </template>
    <script>
    export default {
      name: "EchartsGenerate",
      data() {
        return {
          instances: {},
          chartDom: null,
        };
      },
      props: {
        // 坐标对应的 传入数据指定的键值
        nameDataKey: {
          type: String,
          default: "name",
        },
        // 数据对应的 传入数据指定的键值
        valueDataKey: {
          type: String,
          default: "value",
        },
        // 图表标题
        chartTitle: {
          type: String,
        },
      },
      created() {},
      mounted() {},
      components: {},
      methods: {
        // 用于用户数据不需要处理
        // 直接传入数据源生成图表
        generateChartWithData(domRef, optionType, data) {
          if (document.getElementById(domRef) || this.$refs[domRef]) {
            let chartDom = this.initChartDom(domRef);
            // 存在表格的话先进行销毁
            if (chartDom) {
              let chart = this.getChart(domRef);
              // 存在表格的话先进行销毁
              if (chart) {
                chart.dispose();
              }
              let option = this.getOption(optionType, data);
              if (option && typeof option === "object") {
                chartDom.setOption(option);
              }
            }
          }
        },
        // 生成图表
        // domRef 图表标识 id
        // dataType 图表数据类型
        // optionType option类型
        // list 要生成图表的数据列表
        generateChart(domRef, dataType, optionType, list) {
          let chartInData = null;
          if (document.getElementById(domRef) || this.$refs[domRef]) {
            let chartDom = this.initChartDom(domRef);
            // 存在表格的话先进行销毁
            if (chartDom) {
              let chart = this.getChart(domRef);
              // 存在表格的话先进行销毁
              if (chart) {
                chart.dispose();
              }
              // 如果传入数据为空,则返回
              if(list.length<=0){
                return
              }
              // 如果list的子元素是数组
              if ( Array.isArray(list[0])) {
                // list是包含多条数据的数组
                chartInData = list.map((item) => {
                  // item 是数据列表
                  return this.generateChartInData(item);
                });
              }
              // 如果list的子元素不是数组时对象
              if (!Array.isArray(list[0])) {
                chartInData = this.generateChartInData(list);
              }
              let data = this.chartDataFactory(dataType, chartInData);
              let option = this.getOption(optionType, data);
              if (option && typeof option === "object") {
                chartDom.setOption(option);
              }
            }
          }
        },
        getCanvas(item) {
          if (this.isDomSupported() && typeof item === "string") {
            item = document.getElementById(item) || this.$refs[item];
          } else if (item && item.length) {
            item = item[0];
          }
          if (item && item.canvas) {
            item = item.canvas;
          }
          return item;
        },
        // 获取图表
        getChart(key) {
          const canvas = this.getCanvas(key);
          return Object.values(this.instances)
            .filter((c) => c.canvas === canvas)
            .pop();
        },
        isDomSupported() {
          return typeof window !== "undefined" && typeof document !== "undefined";
        },
        // 初始化图表dom
        // 可以通过id和ref两种属性进行初始化
        initChartDom(domRef) {
          let chartDom = null;
          let initDom = document.getElementById(domRef) || this.$refs[domRef];
          chartDom = this.$echarts.init(initDom, null, {
            renderer: "canvas",
            useDirtyRect: false,
          });
          return chartDom;
        },
        // 生成图表数据
        chartDataFactory(dataType, chartInData) {
          let chartOutData = {};
          switch (dataType) {
            // 根据需求配置数据
            case "listData":
              // 生成数组数据
              // 单个数据格式为 [1,2,3]
              // 多个数据格式为 [[1,2,3],[1,2,4],[3,4,5]]
              if (Array.isArray(chartInData) && chartInData.length > 0) {
                let seriesList = [];
                chartInData.forEach((item) => {
                  seriesList = [...seriesList, item[this.valueDataKey]];
                });
                chartOutData = {
                  xAxisData: chartInData[0][this.nameDataKey],
                  seriesData: seriesList,
                };
              } else {
                chartOutData = {
                  xAxisData: chartInData[this.nameDataKey],
                  seriesData: chartInData[this.valueDataKey],
                };
              }
              break;
            case "objectData":
              // 生成对象数据
              // 数据格式为
              // {name:"", value:""}
              chartOutData = {
                seriesData: this.generateObjectData(
                  chartInData,
                  this.nameDataKey,
                  this.valueDataKey
                ),
              };
              break;
          }
          return chartOutData;
        },
        // 生成对象数据源
        // 属性为 name和value
        // chartInData  生成的图表数据
        // nameKey name对应的键
        // valueKey value对应的键
        generateObjectData(chartInData, nameKey, valueKey) {
          let objectList = [];
          for (var i = 0; i < chartInData["length"]; i++) {
            let objectItem = {
              name: "",
              value: "",
            };
            objectItem.name = chartInData[nameKey][i];
            objectItem.value = chartInData[valueKey][i];
            objectList = [...objectList, objectItem];
          }
          return objectList;
        },
        // 生成图表需要的数据
        // list - 对象数组
        generateChartInData(list) {
          let chartInData = {};
          // 保证list中的每个对象的属性名是相同的,也就是说一一对应
          for (let attr1 in list[0]) {
            // 以每个属性名为名字构建数组
            chartInData[attr1] = [];
          }
          list.forEach(function (item, index) {
            for (let attr2 in item) {
              // chartInData[attr2] 为underfined时 初始化为空数组
              if (!chartInData[attr2]) {
                chartInData[attr2] = [];
              }
              chartInData[attr2].push(item[attr2]);
            }
          });
          chartInData["length"] = list.length;
          return chartInData;
        },
        // 配置option
        getOption(optionType, chartOutData) {
          let option = {};
          let seriesList = [];
          // 如果seriesData有数据,且seriesData的第一个元素是数组,说明传入的数据是多对象数组
          // 否则说明传入的是单个对象
          if (
            chartOutData.seriesData.length > 0 &&
            Array.isArray(chartOutData.seriesData[0])
          ) {
            seriesList = chartOutData.seriesData.map((item) => {
              return (item = {
                data: item,
              });
            });
          } else {
            seriesList = [
              {
                data: chartOutData.seriesData,
              },
            ];
          }
          switch (optionType) {
            case "lineOption":
              // 遍历后添加其他属性
              seriesList = seriesList.map((item) => {
                return (item = {
                  data: item.data,
                  type: "line",
                });
              });
              option = {
                title: {
                  text: this.chartTitle,
                },
                xAxis: {
                  type: "category",
                  data: chartOutData.xAxisData,
                },
                yAxis: {
                  type: "value",
                },
                series: seriesList,
              };
              break;
            case "barOption":
              seriesList = seriesList.map((item) => {
                return (item = {
                  data: item.data,
                  type: "bar",
                });
              });
              option = {
                xAxis: {
                  type: "category",
                  data: chartOutData.xAxisData,
                },
                yAxis: {
                  type: "value",
                },
                series: seriesList,
              };
              break;
            case "pieOption":
              option = {
                title: {
                  text: this.chartTitle,
                  left: "center",
                },
                tooltip: {
                  trigger: "item",
                },
                legend: {
                  orient: "vertical",
                  left: "left",
                },
                series: [
                  {
                    name: "Access From",
                    type: "pie",
                    radius: "50%",
                    data: chartOutData.seriesData,
                    emphasis: {
                      itemStyle: {
                        shadowBlur: 10,
                        shadowOffsetX: 0,
                        shadowColor: "rgba(0, 0, 0, 0.5)",
                      },
                    },
                  },
                ],
              };
              break;
          }
          return option;
        },
      },
    };
    </script>
    <style>
    </style>

    使用的示例代码如下:

    <template>
      <div>
        <!-- 子组件设置ref -->
        <echarts-generate ref="echarts" name-data-key="title" value-data-key="score">
          <!-- 中间的元素设置id -->
          <div class="chart-container" id="chart-sample"></div>
        </echarts-generate>
      </div>
    </template>
    <script>
    import EchartsGenerate from "@/components/charts/EchartsGenerate";
    export default {
      name: "EchartsSample",
      data() {
        return {
          //传入的json数据
          chartData: [],
        };
      },
      created() {},
      async mounted() {
        await this.getData();
        // 通过调用子组件的方法生成图表,设置id获取元素
        // 无法通过ref获取
        this.$refs.echarts.generateChart(
          "chart-sample",
          "listData",
          "barOption",
          this.chartData
        );
      },
      components: {
        "echarts-generate": EchartsGenerate,
      },
      methods: {
        async getData() {
          // 多个数据
          // this.chartData = [
          //   [
          //   {
          //     id: "physical-activity",
          //     // label
          //     title: "physical activity",
          //     // 排序
          //     sort: 0,
          //     // 分数
          //     score: 13,
          //     desc: "户外活动",
          //     // 分数
          //     point: 20,
          //   },
          //   {
          //     id: "taste",
          //     title: "taste",
          //     sort: 1,
          //     score: 20,
          //     desc: "味道",
          //     // 分数
          //     point: 30,
          //   },
          //   {
          //     id: "acceptance",
          //     title: "acceptance",
          //     sort: 2,
          //     score: 50,
          //     desc: "接受度",
          //     // 分数
          //     point: 40,
          //   },
          // ],
          // [
          //   {
          //     id: "physical1",
          //     // label
          //     title: "physical1",
          //     // 排序
          //     sort: 0,
          //     // 分数
          //     score: 11,
          //     desc: "户外活动1",
          //     // 分数
          //     point: 25,
          //   },
          //   {
          //     id: "taste1",
          //     title: "taste1",
          //     sort: 1,
          //     score: 25,
          //     desc: "味道1",
          //     // 分数
          //     point: 35,
          //   },
          //   {
          //     id: "acceptance1",
          //     title: "acceptance1",
          //     sort: 2,
          //     score: 55,
          //     desc: "接受度1",
          //     // 分数
          //     point: 45,
          //   },
          // ]
          // ];
          // 单个数据
          this.chartData =
          [
            {
              id: "physical1",
              // label
              title: "physical1",
              // 排序
              sort: 0,
              // 分数
              score: 11,
              desc: "户外活动1",
              // 分数
              point: 25,
            },
            {
              id: "taste1",
              title: "taste1",
              sort: 1,
              score: 25,
              desc: "味道1",
              // 分数
              point: 35,
            },
            {
              id: "acceptance1",
              title: "acceptance1",
              sort: 2,
              score: 55,
              desc: "接受度1",
              // 分数
              point: 45,
            },
          ];
        },
      },
    };
    </script>
    <style>
    .chart-container {
      position: relative;
      height: 50vh;
      overflow: hidden;
    }
    </style>

    代码在下面

    项目地址 https://gitee.com/joeyan3/joe-vue-demo-project


    评论 0

    暂无评论
    0
    0
    0
    立即
    投稿
    发表
    评论
    返回
    顶部