共计 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: 如果有帮忙到各位或者给各位封装组件提供了一点思路,无妨点个赞吧。。。
正文完