基于 ECharts 的二次封装

729 阅读1分钟

基于 ECharts 封装 MyChart 组件

项目需求中常常要做柱状图呀、折线图呀、雷达图呀等等,我们一般使用公司内部 UI 框架即可,很香。但某次搞出了个漏洞,也不知道是啥,我师父说升级就升级了,结果导致某些功能用升级后的 UI 框架无法实现,有些提单了运维也无法解决,他们说这是一个 bug,也不知道啥时候好,咱也不敢问。 最后才发现,还是原生最香。

@/components/MyChart.vue

<template>
  <div
    class="myChart"
    :class="{ 'chart-has-height': height }"
    :style="style"
  ></div>
</template>
<script>
import * as echarts from "echarts";
const events = [
  "click",
  "dbclick",
  "mousedown",
  "mouseup",
  "moveover",
  "mouseout",
];
export default {
  props: {
    op: {
      type: Object,
      required: true,
    },
    height: {
      type: [String, Number],
    },
    autoResize: {
      type: Boolean,
      default: true,
    },
  },
  computed: {
    // 处理 echarts 高度
    style() {
      const { height } = this;
      if (typeof height === "number" || /^\d+$/.test(height)) {
        return { height: `${height}px` };
      }
      return { height };
    },
  },
  watch: {
    // 监听配置项的变化
    op(val) {
      if (!this.chart) return;
      this.chart.setOption(val, true);
    },
  },
  mounted() {
    //  渲染 echarts
    this.chart = echarts.init(this.$el);
    this.chart.setOption(this.op);
    events.forEach((event) => {
      const opEvent = `on${this.firstToUpperCase(event)}`;
      this.chart.on(event, (params) => {
        this.$emit(`chart-${event}`, params);
        // 兼容处理
        if (typeof this.op[opEvent] === "function") {
          this.op[opEvent](params);
        }
      });
    });
    window.addEventListener("resize", this.resize);
  },
  methods: {
    // 分辨率变化,间隔时间内重新渲染 echarts
    resize() {
      if (!this.timer || !this.autoResize || !this.chart) return;
      this.timer = setTimeout(() => {
        delete this.timer;
        this.chart.resize();
      }, 1000 / 24);
    },
    // 获取 DOM
    getChart() {
      return this.chart;
    },
    // 首字母转为大写 click:Click
    firstToUpperCase(str = "") {
      return str.replace(/./, ($1) => $1.toUpperCase());
    },
  },
  // 组件销毁前,移除 resize 事件   
  beforeDestroy() {
    window.removeEventListener("resize", this.resize);
  },
};
</script>
<style lang="less" scoped>
.chart {
  height: 100%;
  &:not(.chart-has-height) {
    flex: 1;
  }
}
</style>

一、基于 MyChart 组件,实现一个柱状图

<template>
  <div class="echartBar">
    <MyChart :op="op" :height="'100%'" @chart-click="chartClick"></MyChart>
  </div>
</template>
<script>
import MyChart from "@/components/MyChart.vue";
export default {
  components: {
    MyChart,
  },
  data() {
    return {};
  },
  computed: {
    op() {
      return {
        // X轴样式
        xAxis: {
          type: "category",
          data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
        },
        // Y轴样式
        yAxis: {
          type: "value",
        },
        // 柱条样式
        series: [
          {
            data: [120, 200, 150, 80, 70, 110, 130],
            type: "bar",
            barWidth: 30,
            showBackground: true,
            backgroundStyle: {
              color: "rgba(180, 180, 180, 0.2)",
            },
          },
        ],
        // 内置弹窗
        tooltip: {
          show: true,
        },
      };
    },
  },
  methods: {
    // 柱状图点击事件
    chartClick(params) {
      console.log(params);
    },
  },
};
</script>
<style lang="less" scoped>
.echartBar {
  width: 400px;
  height: 300px;
}
</style>

image.png

二、基于 MyChart 组件,实现一个折线图

<template>
  <div class="echartLine">
    <MyChart :op="op" :height="'100%'" @chart-click="chartClick"></MyChart>
  </div>
</template>
<script>
import MyChart from "@/components/MyChart.vue";
export default {
  components: {
    MyChart,
  },
  data() {
    return {};
  },
  computed: {
    op() {
      return {
        // 标题
        title: {
          text: "Stacked Line",
        },
        // 弹窗
        tooltip: {
          trigger: "axis",
        },
        legend: {
          data: ["Email", "Union Ads"],
        },
        // 边距
        grid: {
          left: "3%",
          right: "4%",
          bottom: "3%",
          containLabel: true,
        },
        // 保存为图片
        toolbox: {
          feature: {
            saveAsImage: {},
          },
        },
        // X 轴
        xAxis: {
          type: "category",
          boundaryGap: false,
          data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
        },
        // Y 轴
        yAxis: {
          type: "value",
        },
        // 折线图
        series: [
          {
            name: "Email",
            type: "line",
            stack: "Total",
            data: [120, 132, 101, 134, 90, 230, 210],
          },
          {
            name: "Union Ads",
            type: "line",
            stack: "Total",
            data: [220, 182, 191, 234, 290, 330, 310],
          },
        ],
      };
    },
  },
  methods: {
    // 折线图点击事件
    chartClick(params) {
      console.log(params);
    },
  },
};
</script>
<style lang="less" scoped>
.echartLine {
  width: 400px;
  height: 300px;
}
</style>

image.png

三、基于 MyChart 组件,实现一个雷达图

<template>
  <div class="echartRadar">
    <MyChart :op="op" :height="'100%'" @chart-click="chartClick"></MyChart>
  </div>
</template>
<script>
import MyChart from "@/components/MyChart.vue";
export default {
  components: {
    MyChart,
  },
  data() {
    return {};
  },
  computed: {
    op() {
      return {
        radar: {
          shape: "circle", // 雷达图绘制类型, 有 'polygon' 和 'circle'
          center: ["50%", "50%"], // 中心坐标
          radius: "50%", // 半径
          indicator: [
            // 指定雷达图中的多个变量(维度)
            { name: "Sales", max: 6500 },
            { name: "Administration", max: 16000 },
            { name: "Information Technology", max: 30000 },
            { name: "Customer Support", max: 38000 },
            { name: "Development", max: 52000 },
            { name: "Marketing", max: 25000 },
          ],
          startAngle: 60, // 第一个指示器轴的角度
        },
        series: [
          {
            name: "Budget vs spending",
            type: "radar",
            data: [
              {
                value: [4200, 3000, 20000, 35000, 50000, 18000],
                name: "Allocated Budget",
              },
              {
                value: [5000, 14000, 28000, 26000, 42000, 21000],
                name: "Actual Spending",
              },
            ],
          },
        ],
      };
    },
  },
  methods: {
    // 雷达图点击事件
    chartClick(params) {
      console.log(params);
    },
  },
};
</script>
<style lang="less" scoped>
.echartRadar {
  width: 400px;
  height: 300px;
}
</style>

image.png