Vue echarts封装实现流程
作者:loyd3
将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
到此这篇关于Vue echarts封装实现流程的文章就介绍到这了,更多相关Vue echarts封装内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!