vue3 封装echarts组件

在开发项目过程中需要封装echarts组件,方便多次使用,以下是封装的详细过程。

前置动作:安装echarts以及resize-observer-polyfill插件

  • 新建echarts.ts文件
import * as echarts from "echarts/core";
/**
 * 这里按需引入使用到的图表类型
 */
import { BarChart, LineChart, type LineSeriesOption, PieChart, type PieSeriesOption } from "echarts/charts";
/**
 * 这里按需引入使用到的配置
 * 特别地 包括MarkPoint markLine等这些也要单独使用 不然在图表中无法显示
 */
import {
  LegendComponent,
  type LegendComponentOption,
  TitleComponent,
  // 组件类型的定义后缀都为 ComponentOption
  type TitleComponentOption,
  TooltipComponent,
  type TooltipComponentOption,
  GridComponent,
  type GridComponentOption,
  DataZoomComponent,
  type DataZoomComponentOption,
  // 数据集组件
  DatasetComponent,
  type DatasetComponentOption,
  // 标记组件
  MarkAreaComponent,
  type MarkAreaComponentOption,
  MarkLineComponent,
  type MarkLineComponentOption,
  MarkPointComponent,
  type MarkPointComponentOption,
  // 内置数据转换器组件 (filter, sort)
  TransformComponent,
} from "echarts/components";

import { LabelLayout, UniversalTransition } from "echarts/features";
import { CanvasRenderer } from "echarts/renderers";

import { ref, shallowRef, onMounted, onBeforeUnmount } from "vue";
import ResizeObserver from "resize-observer-polyfill";
import throttle from "lodash/throttle";

/**
 * 引入的配置通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型
 */
export type ECOption = echarts.ComposeOption<
  | LineSeriesOption
  | PieSeriesOption
  | LegendComponentOption
  | TitleComponentOption
  | TooltipComponentOption
  | DataZoomComponentOption
  | GridComponentOption
  | DatasetComponentOption
  | MarkAreaComponentOption
  | MarkLineComponentOption
  | MarkPointComponentOption
>;

/**
 * 注册按需引入的组件
 */
echarts.use([
  LegendComponent,
  TitleComponent,
  TooltipComponent,
  GridComponent,
  DataZoomComponent,
  DatasetComponent,
  MarkAreaComponent,
  MarkLineComponent,
  MarkPointComponent,
  TransformComponent,
  BarChart,
  LineChart,
  PieChart,
  LabelLayout,
  UniversalTransition,
  CanvasRenderer,
]);

export default function useChart() {
  const canvasEl = shallowRef();
  const myChart = shallowRef();
  /**
   * 监听容器宽度变化或者浏览器窗口变化对echarts图表进行自动展示
   * 不用window.addEventListener("resize", resizeFn)的原因是这个方法只是监听浏览器窗口的不变化
   *  -对所在容器的大小变化不进行监听 很难满足开发场景的需求
   */
  const resizeObserver = ref<ResizeObserver>();
  const bindResize = () => {
    const deboundResize = throttle(() => {
      myChart.value?.resize();
    }, 500);

    resizeObserver.value = new ResizeObserver(() => {
      deboundResize();
      // myChart.value?.resize();
    });
    resizeObserver.value.observe(canvasEl.value);
  };
  // 解绑 resize
  const unbindResize = () => {
    resizeObserver.value?.unobserve(canvasEl.value);
  };

  onMounted(() => {
    /**
     * 注册echarts并开启监听容器大小变化
     */
    myChart.value = echarts.init(canvasEl.value);
    bindResize();
  });

  onBeforeUnmount(() => {
    /**
     * 销毁监听和echarts
     */
    unbindResize();
    myChart.value?.dispose();
    myChart.value = null;
  });

  return {
    myChart,
    canvasEl,
  };
}

  • 页面封装
<template>
  <div>
    <div class="chart-container" :style="containerStyle">
      <div v-show="dataEmptyFlag" class="chart-empty">
        <span class="empty-title">暂无数据</span>
      </div>
      <div ref="canvasEl" :style="containerStyle" />
      <div v-show="loading" class="chart-loading"><Spin /></div>
    </div>
  </div>
</template>
<script setup lang="ts">
import { Spin } from "ant-design-vue";
import { ref, watch } from "vue";
import useChart from "./useECharts";
import type { ECOption } from "./useECharts";

interface ChartProps {
  containerStyle?: any;
  loading?: boolean;
  dataEmptyFlag?: boolean;
  options?: ECOption;
}

const props = withDefaults(defineProps<ChartProps>(), {
  containerStyle: {
    height: "990px",
    width: "100%",
  },
  loading: false,
  dataEmptyFlag: false,
});

const { myChart, canvasEl } = useChart();

watch(
  () => props?.options,
  (cur) => {
    if (myChart) {
      /**
       * 这里绘制图表前先清空上一次数据 否则有时候会出现数据残余的情况
       */
      myChart.value.clear();
      myChart.value.setOption(cur);
    }
  },
);
</script>
<style scoped>
.chart-container {
  position: relative;
  width: 100%;
}
.chart-loading {
  align-items: center;
  background-color: #ffffff;
  bottom: 0;
  display: flex;
  justify-content: center;
  left: 0;
  position: absolute;
  right: 0;
  top: 0;
  z-index: 1999;
}
.chart-empty {
  position: absolute;
  z-index: 1;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  color: #888;
  font-size: 14px;
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
}
.empty-title {
  font-size: 16px;
  font-weight: 500;
  color: #000;
}
</style>
  • 组件调用
/**
 * 参入对应的参数以及图表配置项
 */
<anewCharts :loading="loading" :data-empty-flag="dataEmptyFlag" :containerStyle="chartStyle" :options="chartOptions"></anewCharts>

你可能感兴趣的:(echarts,javascript,vue.js)