关于vue.js:vue3-ts-echarts封装一个通用echarts组件20版

8次阅读

共计 6319 个字符,预计需要花费 16 分钟才能阅读完成。

闲时基于上次封装的图表组件做了一层优化,话不多说,间接上代码吧

Step-1: 新建一个 config.ts 文件

export const config = {
  labelKey: "name",
  valueKey: "value",
};

export type chartDataItemType = {[key: string]: string | number;
};

// 以后反对显示的图表类型 (折线,柱形)
export type chartSupportType = "line" | "bar";

export type chartDataType = {
  name: string;
  data: chartDataItemType[];
  type: chartSupportType;
  labelKey?: string;
  valueKey?: string;
  options?: any;
};

export type chartOptionsType = {[key: string]: any;
};

// 根底图例色彩数据
export const baseColorArr = [
  "#3CD7D7",
  "#F9CB28",
  "#A089FF",
  "#5CC4FF",
  "#FF9292",
  "#5CC4FF",
  "#FF8AEC",
  "#FFB35B",
];

// 暗影区域色彩数据
export const areaColorArr = [
  "#C5F3F3",
  "#FDEFBF",
  "#dcd3ff",
  "#bde7ff",
  "#ffd3d3",
  "#bde7ff",
  "#ffd0f7",
  "#ffe0bd",
];

// 图例顶部字体大小
export const defaultLabelFontsize = 8;

export const getColor = (index, type: "default" | "area" = "default") => {
  const colorArr = type === "default" ? baseColorArr : areaColorArr;
  const colorIndex = index % colorArr.length;
  return colorArr[colorIndex];
};

// 图表底部滚动条默认款式
export const defaultDataZoomOptions: any[] = [
  {
    type: "slider",
    show: true,
    xAxisIndex: [0],
    start: 0,
    end: 30,
    textStyle: {color: "#ccd7d7",},
  },
];

// 获取滚动条款式
export const getDataZoom = (seriesLength, showZoomLength) => {if (seriesLength > showZoomLength) {return defaultDataZoomOptions;} else {return [];
  }
};

// 获取折线图、柱形图根底配置
export const getLineBarBaseOptions = (seriesLength, showZoomLength = 7) => {
  return {
    tooltip: {
      trigger: "axis",
      axisPointer: {type: "shadow",},
    },
    grid: {
      left: "3%",
      right: "4%",
      bottom: "3%",
      containLabel: true,
    },
    yAxis: {type: "value",},
    dataZoom: getDataZoom(seriesLength, showZoomLength),
  };
};

// 获取折线系列配置
export const getLineSeriesOptions = (index) => {
  return {
    tooltip: {trigger: "axis",},
    areaStyle: {color: getColor(index, "area"),
    },
    smooth: true,
    label: {
      show: true, // 开启显示
      position: "top", // 在上方显示
      // 数值款式
      color: getColor(index, "default"),
      fontSize: defaultLabelFontsize,
    },
  };
};

// 获取柱形系列配置
export const getBarSeriesOptions = (index) => {
  return {
    barWidth: 10,
    label: {
      show: true, // 开启显示
      position: "top", // 在上方显示
      // 数值款式
      color: getColor(index, "default"),
      fontSize: defaultLabelFontsize,
    },
    itemStyle: {borderRadius: [6, 6, 0, 0],
    },
  };
};

Step-2: 新建一个 index.vue 文件

<!--
/**
* Author: 前端小高
* Date: 2023-05-08 14:58
* Desc: MyChart 文件形容
*/
-->

<template>
  <div ref="chartDom" class="chart-dom"></div>
</template>

<script name="MyChart" lang="ts" setup>
import {ref, computed, watch, onMounted, onBeforeUnmount, PropType, nextTick} from "vue";
import * as ECharts from "echarts";
import {debounce} from "lodash";
import {
  baseColorArr,
  chartDataType,
  chartOptionsType,
  chartSupportType,
  chartDataItemType,
  getLineBarBaseOptions,
  getLineSeriesOptions,
  getBarSeriesOptions,
  config,
} from "./config";

const props = defineProps({
  color: {type: Array as PropType<string[]>,
    default: () => {return baseColorArr;},
  },
  // 超过该数字时显示滚动条
  showZoomLimit: {
    type: Number,
    default: 7,
  },
  // 坐标轴是否两边留白
  isBoundaryGap: {
    type: Boolean,
    default: false,
  },
  baseOptions: {
    type: Object as PropType<chartOptionsType>,
    default: () => {return {};
    },
  },
  data: {type: Array as PropType<chartDataType[]>,
    require: true,
  },
});

const emits = defineEmits(["chart-click"]);

const initSeriesData = () => {if (props?.data?.length) {getXAxisDataArr();
    const tempSeriesArr = props?.data.map((item: chartDataType, index: number) => {const chartOptions = getSeriesItemOptions(item, index);
      return {
        name: item.name,
        data: getValueArr(item.data, item?.valueKey),
        type: item.type,
        ...chartOptions,
      };
    });
    const tempLegendDataArr = props?.data.map((item: chartDataType) => item.name);
    legendDataArr.value = tempLegendDataArr;
    seriesArr.value = tempSeriesArr;
  }
};

const getXAxisDataArr = () => {const labelName = props?.data?.[0]?.labelKey || config.labelKey;
  const xDataArr = props?.data?.[0]?.data.map((item) => item[labelName]);
  dataArr.value = xDataArr as string[];};

const callbackMap = {
  line: getLineSeriesOptions,
  bar: getBarSeriesOptions,
};

const getSeriesItemDefaultOptions = (type: chartSupportType = "line") => {return callbackMap[type];
};

const getChartBaseOptions = () => {return getLineBarBaseOptions;};

const getSeriesItemOptions = (chartItem: chartDataType, index = 0) => {const cb = getSeriesItemDefaultOptions(chartItem.type);
  const defaultOptions = cb(index);
  if (chartItem?.options) {const mergeOptions = Object.assign({}, defaultOptions, chartItem?.options);
    return mergeOptions;
  } else {return defaultOptions;}
};

const getBaseChartOptions = () => {const cb = getChartBaseOptions();
  const baseOptions = cb(dataArr.value.length, props.showZoomLimit);
  if (props?.baseOptions) {const mergeOptions = Object.assign({}, baseOptions, props?.baseOptions);
    return mergeOptions;
  } else {return baseOptions;}
};

const getValueArr = (arr: chartDataItemType[], valueKey?: string) => {
  const keyName = valueKey ? valueKey : config.valueKey;
  return arr.map((item) => Number(item[keyName]));
};

const resizeHandler = () => {chartExample.resize();
};
const resizeHandlerOrigin = debounce(resizeHandler, 300);

const dataArr = ref<any[]>([]);
const seriesArr = ref<any[]>([]);
const legendDataArr = ref<string[]>([]);

const getOptions = computed(() => {const baseOptions = getBaseChartOptions();
  const options = {
    color: props.color,
    ...baseOptions,
    legend: {data: legendDataArr.value,},
    xAxis: {
      type: "category",
      data: dataArr.value,
      boundaryGap: props.isBoundaryGap,
      axisTick: {alignWithLabel: true,},
    },
    series: seriesArr.value,
  };
  return options;
});

watch(() => props.data,
  () => {init();
  },
  {deep: true,}
);

const chartDom = ref();
let chartExample: any = null;
const initChart = () => {if (chartExample) {
    // 若存在图表实例,则先执行销毁操作
    chartExample.dispose();}
  nextTick(() => {chartExample = ECharts.init(chartDom.value);
    const options = getOptions.value;
    chartExample.setOption(options, true);
    initResizerListener();
    initChartEvent();});
};

const initResizerListener = () => {window.removeEventListener("resize", resizeHandlerOrigin);
  window.addEventListener("resize", resizeHandlerOrigin);
};

const initChartEvent = () => {cancelClickEvent();
  chartExample.on("click", (params) => {emits("chart-click", params);
  });
};

const cancelClickEvent = () => {chartExample.off("click");
};

const init = () => {initSeriesData();
  initChart();};

onMounted(() => {init();
});

onBeforeUnmount(() => {cancelClickEvent();
  window.removeEventListener("resize", resizeHandlerOrigin);
  chartExample.dispose();});
</script>
<style lang="scss" scoped>
.chart-dom {height: 300px;}
</style>

Step-3:页面下的援用形式

<!--
/**
* Author: 前端小高
* Date: 2023-05-08 14:55
* Desc: ChartComp 图表组件
*/
-->

<template>
  <my-chart :data="dataArr"></my-chart>
</template>

<script name="ChartComp" lang="ts" setup>
import {ref} from "vue";
import {chartDataType} from "@/common/MyChart/config";

const dataArr = ref<chartDataType[]>([
  {
    name: "测试数据列 1",
    type: "line",
    data: [
      {
        name: "1.1",
        value: 1,
      },
      {
        name: "1.2",
        value: 2,
      },
      {
        name: "1.3",
        value: 3,
      },
      {
        name: "1.1",
        value: 1,
      },
    ],
  },
]);
</script>

页面展现成果如下

代码 Gitee 地址

比照根底版本的 echarts 封装组件,2.0 版本做了如下优化

  • 只需传入渲染数据及展现数据类型,即可渲染
  • 其余图表配置能够按需传入
  • (不足之处:以后只做了折线图及柱形图的反对,其余类型图表各位能够按需扩大)

Remark: 如果有帮忙到各位或者给各位封装组件提供了一点思路,无妨点个赞吧。。。

正文完
 0