Vue echarts封裝實現(xiàn)流程
將echarts封裝成組件,達(dá)到只要調(diào)用方法,傳入數(shù)據(jù)和相應(yīng)的參數(shù)就能生成圖表的效果,避免在項目中編寫大量重復(fù)和累贅的echarts的配置代碼,實現(xiàn)的思路如下:
接口返回的一般是json數(shù)據(jù),所以首先要將json數(shù)據(jù)進(jìn)行處理,處理成echarts需要的數(shù)據(jù)形式
將echarts的配置代碼封裝在一個方法里,通過自定義的配置名進(jìn)行調(diào)用
下面對我自己封裝的組件 EchartsGenerate 逐步解釋
首先看template
<template> <div> <slot></slot> </div> </template>
這里使用插槽slot是因為,有時候圖表的樣式要根據(jù)頁面進(jìn)行調(diào)整,所以用插槽好方便自定義樣式,就比如下面的代碼:
<echarts-generate ref="echarts" name-data-key="title" value-data-key="score"> <!-- 中間的元素設(shè)置id --> <div class="chart-container" id="chart-sample"></div> </echarts-generate> <style> .chart-container { position: relative; height: 50vh; overflow: hidden; } </style>
通過class來設(shè)置圖表的樣式
再看props
props: { // 坐標(biāo)對應(yīng)的 傳入數(shù)據(jù)指定的鍵值 nameDataKey: { type: String, default: "name", }, // 數(shù)據(jù)對應(yīng)的 傳入數(shù)據(jù)指定的鍵值 valueDataKey: { type: String, default: "value", }, // 圖表標(biāo)題 chartTitle: { type: String, }, },
nameDataKey和valueDataKey分別對應(yīng)傳入數(shù)據(jù)的鍵的名字和值和名字,比如,假如數(shù)據(jù)是這樣
[ { id: "physical1", // label title: "physical1", // 排序 sort: 0, // 分?jǐn)?shù) score: 11, desc: "戶外活動1", // 分?jǐn)?shù) point: 25, }, { id: "taste1", title: "taste1", sort: 1, score: 25, desc: "味道1", // 分?jǐn)?shù) point: 35, }, { id: "acceptance1", title: "acceptance1", sort: 2, score: 55, desc: "接受度1", // 分?jǐn)?shù) point: 45, }, ];
那么在組件上設(shè)置 name-data-key="title" value-data-key="score"
那圖表的橫坐標(biāo)是title對應(yīng)的值,豎坐標(biāo)是score對應(yīng)的值,這個在后面會詳細(xì)說。
最后在看主要的方法
首先看處理json數(shù)據(jù)的方法
generateChartInData(list) { let chartInData = {}; // 保證list中的每個對象的屬性名是相同的,也就是說一一對應(yīng) for (let attr1 in list[0]) { // 以每個屬性名為名字構(gòu)建數(shù)組 chartInData[attr1] = []; } list.forEach(function (item, index) { for (let attr2 in item) { // chartInData[attr2] 為underfined時 初始化為空數(shù)組 if (!chartInData[attr2]) { chartInData[attr2] = []; } chartInData[attr2].push(item[attr2]); } }); chartInData["length"] = list.length; return chartInData; },
上面方法實現(xiàn)的效果是將json數(shù)組轉(zhuǎn)換為一個包含以屬性名命名數(shù)組的對象,例如傳入的數(shù)據(jù)是這個格式
[ { id: "physical1", // label title: "physical1", // 排序 sort: 0, // 分?jǐn)?shù) score: 11, desc: "戶外活動1", // 分?jǐn)?shù) point: 25, }, { id: "taste1", title: "taste1", sort: 1, score: 25, desc: "味道1", // 分?jǐn)?shù) point: 35, }, { id: "acceptance1", title: "acceptance1", sort: 2, score: 55, desc: "接受度1", // 分?jǐn)?shù) point: 45, }, ];
通過generateChartInData方法生成的數(shù)據(jù)如下:
{ 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生成的數(shù)據(jù),傳入下面的方法中
// 生成圖表數(shù)據(jù) chartDataFactory(dataType, chartInData) { let chartOutData = {}; switch (dataType) { // 根據(jù)需求配置數(shù)據(jù) case "listData": // 生成數(shù)組數(shù)據(jù) // 單個數(shù)據(jù)格式為 [1,2,3] // 多個數(shù)據(jù)格式為 [[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": // 生成對象數(shù)據(jù) // 數(shù)據(jù)格式為 // {name:"", value:""} chartOutData = { seriesData: this.generateObjectData( chartInData, this.nameDataKey, this.valueDataKey ), }; break; } return chartOutData; }, // 生成對象數(shù)據(jù)源 // 屬性為 name和value // chartInData 生成的圖表數(shù)據(jù) // nameKey name對應(yīng)的鍵 // valueKey value對應(yīng)的鍵 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。然后這個方法處理了多條數(shù)據(jù)的,可以參考下
下面是將echarts圖表的配置都封裝在getOption這個方法里面,同時把chartDataFactory生成的數(shù)據(jù)傳入這個方法
// 配置option getOption(optionType, chartOutData) { let option = {}; let seriesList = []; // 如果seriesData有數(shù)據(jù),且seriesData的第一個元素是數(shù)組,說明傳入的數(shù)據(jù)是多對象數(shù)組 // 否則說明傳入的是單個對象 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) { // 基礎(chǔ)折線圖 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; // 基礎(chǔ)柱狀圖 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; // 基礎(chǔ)餅圖 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; },
后續(xù)開發(fā)在getOption這個方法里添加配置
最后是生成圖表的方法
// 生成圖表 // domRef 圖表標(biāo)識 id // dataType 圖表數(shù)據(jù)類型 // optionType option類型 // list 要生成圖表的數(shù)據(jù)列表 generateChart(domRef, dataType, optionType, list) { let chartInData = null; if (document.getElementById(domRef) || this.$refs[domRef]) { let chartDom = this.initChartDom(domRef); // 存在表格的話先進(jìn)行銷毀 if (chartDom) { let chart = this.getChart(domRef); // 存在表格的話先進(jìn)行銷毀 if (chart) { chart.dispose(); } // 如果傳入數(shù)據(jù)為空,則返回 if(list.length<=0){ return } // 如果list的子元素是數(shù)組 if ( Array.isArray(list[0])) { // list是包含多條數(shù)據(jù)的數(shù)組 chartInData = list.map((item) => { // item 是數(shù)據(jù)列表 return this.generateChartInData(item); }); } // 如果list的子元素不是數(shù)組時對象 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); } } } },
其中圖表標(biāo)識最好是通過id,使用ref會沒效果
完整代碼如下
<template> <div> <slot></slot> </div> </template> <script> export default { name: "EchartsGenerate", data() { return { instances: {}, chartDom: null, }; }, props: { // 坐標(biāo)對應(yīng)的 傳入數(shù)據(jù)指定的鍵值 nameDataKey: { type: String, default: "name", }, // 數(shù)據(jù)對應(yīng)的 傳入數(shù)據(jù)指定的鍵值 valueDataKey: { type: String, default: "value", }, // 圖表標(biāo)題 chartTitle: { type: String, }, }, created() {}, mounted() {}, components: {}, methods: { // 用于用戶數(shù)據(jù)不需要處理 // 直接傳入數(shù)據(jù)源生成圖表 generateChartWithData(domRef, optionType, data) { if (document.getElementById(domRef) || this.$refs[domRef]) { let chartDom = this.initChartDom(domRef); // 存在表格的話先進(jìn)行銷毀 if (chartDom) { let chart = this.getChart(domRef); // 存在表格的話先進(jìn)行銷毀 if (chart) { chart.dispose(); } let option = this.getOption(optionType, data); if (option && typeof option === "object") { chartDom.setOption(option); } } } }, // 生成圖表 // domRef 圖表標(biāo)識 id // dataType 圖表數(shù)據(jù)類型 // optionType option類型 // list 要生成圖表的數(shù)據(jù)列表 generateChart(domRef, dataType, optionType, list) { let chartInData = null; if (document.getElementById(domRef) || this.$refs[domRef]) { let chartDom = this.initChartDom(domRef); // 存在表格的話先進(jìn)行銷毀 if (chartDom) { let chart = this.getChart(domRef); // 存在表格的話先進(jìn)行銷毀 if (chart) { chart.dispose(); } // 如果傳入數(shù)據(jù)為空,則返回 if(list.length<=0){ return } // 如果list的子元素是數(shù)組 if ( Array.isArray(list[0])) { // list是包含多條數(shù)據(jù)的數(shù)組 chartInData = list.map((item) => { // item 是數(shù)據(jù)列表 return this.generateChartInData(item); }); } // 如果list的子元素不是數(shù)組時對象 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兩種屬性進(jìn)行初始化 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; }, // 生成圖表數(shù)據(jù) chartDataFactory(dataType, chartInData) { let chartOutData = {}; switch (dataType) { // 根據(jù)需求配置數(shù)據(jù) case "listData": // 生成數(shù)組數(shù)據(jù) // 單個數(shù)據(jù)格式為 [1,2,3] // 多個數(shù)據(jù)格式為 [[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": // 生成對象數(shù)據(jù) // 數(shù)據(jù)格式為 // {name:"", value:""} chartOutData = { seriesData: this.generateObjectData( chartInData, this.nameDataKey, this.valueDataKey ), }; break; } return chartOutData; }, // 生成對象數(shù)據(jù)源 // 屬性為 name和value // chartInData 生成的圖表數(shù)據(jù) // nameKey name對應(yīng)的鍵 // valueKey value對應(yīng)的鍵 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; }, // 生成圖表需要的數(shù)據(jù) // list - 對象數(shù)組 generateChartInData(list) { let chartInData = {}; // 保證list中的每個對象的屬性名是相同的,也就是說一一對應(yīng) for (let attr1 in list[0]) { // 以每個屬性名為名字構(gòu)建數(shù)組 chartInData[attr1] = []; } list.forEach(function (item, index) { for (let attr2 in item) { // chartInData[attr2] 為underfined時 初始化為空數(shù)組 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有數(shù)據(jù),且seriesData的第一個元素是數(shù)組,說明傳入的數(shù)據(jù)是多對象數(shù)組 // 否則說明傳入的是單個對象 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> <!-- 子組件設(shè)置ref --> <echarts-generate ref="echarts" name-data-key="title" value-data-key="score"> <!-- 中間的元素設(shè)置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數(shù)據(jù) chartData: [], }; }, created() {}, async mounted() { await this.getData(); // 通過調(diào)用子組件的方法生成圖表,設(shè)置id獲取元素 // 無法通過ref獲取 this.$refs.echarts.generateChart( "chart-sample", "listData", "barOption", this.chartData ); }, components: { "echarts-generate": EchartsGenerate, }, methods: { async getData() { // 多個數(shù)據(jù) // this.chartData = [ // [ // { // id: "physical-activity", // // label // title: "physical activity", // // 排序 // sort: 0, // // 分?jǐn)?shù) // score: 13, // desc: "戶外活動", // // 分?jǐn)?shù) // point: 20, // }, // { // id: "taste", // title: "taste", // sort: 1, // score: 20, // desc: "味道", // // 分?jǐn)?shù) // point: 30, // }, // { // id: "acceptance", // title: "acceptance", // sort: 2, // score: 50, // desc: "接受度", // // 分?jǐn)?shù) // point: 40, // }, // ], // [ // { // id: "physical1", // // label // title: "physical1", // // 排序 // sort: 0, // // 分?jǐn)?shù) // score: 11, // desc: "戶外活動1", // // 分?jǐn)?shù) // point: 25, // }, // { // id: "taste1", // title: "taste1", // sort: 1, // score: 25, // desc: "味道1", // // 分?jǐn)?shù) // point: 35, // }, // { // id: "acceptance1", // title: "acceptance1", // sort: 2, // score: 55, // desc: "接受度1", // // 分?jǐn)?shù) // point: 45, // }, // ] // ]; // 單個數(shù)據(jù) this.chartData = [ { id: "physical1", // label title: "physical1", // 排序 sort: 0, // 分?jǐn)?shù) score: 11, desc: "戶外活動1", // 分?jǐn)?shù) point: 25, }, { id: "taste1", title: "taste1", sort: 1, score: 25, desc: "味道1", // 分?jǐn)?shù) point: 35, }, { id: "acceptance1", title: "acceptance1", sort: 2, score: 55, desc: "接受度1", // 分?jǐn)?shù) point: 45, }, ]; }, }, }; </script> <style> .chart-container { position: relative; height: 50vh; overflow: hidden; } </style>
代碼在下面
項目地址 https://gitee.com/joeyan3/joe-vue-demo-project
到此這篇關(guān)于Vue echarts封裝實現(xiàn)流程的文章就介紹到這了,更多相關(guān)Vue echarts封裝內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
實現(xiàn)Vue的markdown文檔可以在線運(yùn)行的方法示例
這篇文章主要介紹了實現(xiàn)Vue的markdown文檔可以在線運(yùn)行的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-12-12vue-seamless-scroll無縫滾動組件使用方法詳解
這篇文章主要為大家詳細(xì)介紹了vue-seamless-scroll無縫滾動組件的使用方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-04-04策略模式實現(xiàn) Vue 動態(tài)表單驗證的方法
策略模式(Strategy Pattern)又稱政策模式,其定義一系列的算法,把它們一個個封裝起來,并且使它們可以互相替換。封裝的策略算法一般是獨(dú)立的,策略模式根據(jù)輸入來調(diào)整采用哪個算法。這篇文章主要介紹了策略模式實現(xiàn) Vue 動態(tài)表單驗證,需要的朋友可以參考下2019-09-09詳解.vue文件中style標(biāo)簽的幾個標(biāo)識符
這篇文章主要介紹了詳解.vue文件中style標(biāo)簽的幾個標(biāo)識符,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-07-07