Vue echarts封裝實(shí)現(xiàn)流程
將echarts封裝成組件,達(dá)到只要調(diào)用方法,傳入數(shù)據(jù)和相應(yīng)的參數(shù)就能生成圖表的效果,避免在項(xiàng)目中編寫(xiě)大量重復(fù)和累贅的echarts的配置代碼,實(shí)現(xiàn)的思路如下:
接口返回的一般是json數(shù)據(jù),所以首先要將json數(shù)據(jù)進(jìn)行處理,處理成echarts需要的數(shù)據(jù)形式
將echarts的配置代碼封裝在一個(gè)方法里,通過(guò)自定義的配置名進(jìn)行調(diào)用
下面對(duì)我自己封裝的組件 EchartsGenerate 逐步解釋
首先看template
<template>
<div>
<slot></slot>
</div>
</template>
這里使用插槽slot是因?yàn)?,有時(shí)候圖表的樣式要根據(jù)頁(yè)面進(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>
通過(guò)class來(lái)設(shè)置圖表的樣式
再看props
props: {
// 坐標(biāo)對(duì)應(yīng)的 傳入數(shù)據(jù)指定的鍵值
nameDataKey: {
type: String,
default: "name",
},
// 數(shù)據(jù)對(duì)應(yīng)的 傳入數(shù)據(jù)指定的鍵值
valueDataKey: {
type: String,
default: "value",
},
// 圖表標(biāo)題
chartTitle: {
type: String,
},
},
nameDataKey和valueDataKey分別對(duì)應(yīng)傳入數(shù)據(jù)的鍵的名字和值和名字,比如,假如數(shù)據(jù)是這樣
[
{
id: "physical1",
// label
title: "physical1",
// 排序
sort: 0,
// 分?jǐn)?shù)
score: 11,
desc: "戶(hù)外活動(dòng)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對(duì)應(yīng)的值,豎坐標(biāo)是score對(duì)應(yīng)的值,這個(gè)在后面會(huì)詳細(xì)說(shuō)。
最后在看主要的方法
首先看處理json數(shù)據(jù)的方法
generateChartInData(list) {
let chartInData = {};
// 保證list中的每個(gè)對(duì)象的屬性名是相同的,也就是說(shuō)一一對(duì)應(yīng)
for (let attr1 in list[0]) {
// 以每個(gè)屬性名為名字構(gòu)建數(shù)組
chartInData[attr1] = [];
}
list.forEach(function (item, index) {
for (let attr2 in item) {
// chartInData[attr2] 為underfined時(shí) 初始化為空數(shù)組
if (!chartInData[attr2]) {
chartInData[attr2] = [];
}
chartInData[attr2].push(item[attr2]);
}
});
chartInData["length"] = list.length;
return chartInData;
},
上面方法實(shí)現(xiàn)的效果是將json數(shù)組轉(zhuǎn)換為一個(gè)包含以屬性名命名數(shù)組的對(duì)象,例如傳入的數(shù)據(jù)是這個(gè)格式
[
{
id: "physical1",
// label
title: "physical1",
// 排序
sort: 0,
// 分?jǐn)?shù)
score: 11,
desc: "戶(hù)外活動(dòng)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,
},
];
通過(guò)generateChartInData方法生成的數(shù)據(jù)如下:
{
id: ["physical1", "taste1","acceptance1"],
title: ["physical1", "taste1", "acceptance1"],
sort: [ 0,1,2],
score: [11,25, 55],
desc: ["戶(hù)外活動(dòng)1","味道1","接受度1"],
point: [25,35,45],
length: 3
}
將通過(guò)generateChartInData生成的數(shù)據(jù),傳入下面的方法中
// 生成圖表數(shù)據(jù)
chartDataFactory(dataType, chartInData) {
let chartOutData = {};
switch (dataType) {
// 根據(jù)需求配置數(shù)據(jù)
case "listData":
// 生成數(shù)組數(shù)據(jù)
// 單個(gè)數(shù)據(jù)格式為 [1,2,3]
// 多個(gè)數(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":
// 生成對(duì)象數(shù)據(jù)
// 數(shù)據(jù)格式為
// {name:"", value:""}
chartOutData = {
seriesData: this.generateObjectData(
chartInData,
this.nameDataKey,
this.valueDataKey
),
};
break;
}
return chartOutData;
},
// 生成對(duì)象數(shù)據(jù)源
// 屬性為 name和value
// chartInData 生成的圖表數(shù)據(jù)
// nameKey name對(duì)應(yīng)的鍵
// valueKey value對(duì)應(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這個(gè)方法里面就用到了前面提到的nameDataKey和valueDataKey。然后這個(gè)方法處理了多條數(shù)據(jù)的,可以參考下
下面是將echarts圖表的配置都封裝在getOption這個(gè)方法里面,同時(shí)把chartDataFactory生成的數(shù)據(jù)傳入這個(gè)方法
// 配置option
getOption(optionType, chartOutData) {
let option = {};
let seriesList = [];
// 如果seriesData有數(shù)據(jù),且seriesData的第一個(gè)元素是數(shù)組,說(shuō)明傳入的數(shù)據(jù)是多對(duì)象數(shù)組
// 否則說(shuō)明傳入的是單個(gè)對(duì)象
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ǔ)折線(xiàn)圖
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ù)開(kāi)發(fā)在getOption這個(gè)方法里添加配置
最后是生成圖表的方法
// 生成圖表
// domRef 圖表標(biāo)識(shí) id
// dataType 圖表數(shù)據(jù)類(lèi)型
// optionType option類(lèi)型
// list 要生成圖表的數(shù)據(jù)列表
generateChart(domRef, dataType, optionType, list) {
let chartInData = null;
if (document.getElementById(domRef) || this.$refs[domRef]) {
let chartDom = this.initChartDom(domRef);
// 存在表格的話(huà)先進(jìn)行銷(xiāo)毀
if (chartDom) {
let chart = this.getChart(domRef);
// 存在表格的話(huà)先進(jìn)行銷(xiāo)毀
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ù)組時(shí)對(duì)象
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)識(shí)最好是通過(guò)id,使用ref會(huì)沒(méi)效果
完整代碼如下
<template>
<div>
<slot></slot>
</div>
</template>
<script>
export default {
name: "EchartsGenerate",
data() {
return {
instances: {},
chartDom: null,
};
},
props: {
// 坐標(biāo)對(duì)應(yīng)的 傳入數(shù)據(jù)指定的鍵值
nameDataKey: {
type: String,
default: "name",
},
// 數(shù)據(jù)對(duì)應(yīng)的 傳入數(shù)據(jù)指定的鍵值
valueDataKey: {
type: String,
default: "value",
},
// 圖表標(biāo)題
chartTitle: {
type: String,
},
},
created() {},
mounted() {},
components: {},
methods: {
// 用于用戶(hù)數(shù)據(jù)不需要處理
// 直接傳入數(shù)據(jù)源生成圖表
generateChartWithData(domRef, optionType, data) {
if (document.getElementById(domRef) || this.$refs[domRef]) {
let chartDom = this.initChartDom(domRef);
// 存在表格的話(huà)先進(jìn)行銷(xiāo)毀
if (chartDom) {
let chart = this.getChart(domRef);
// 存在表格的話(huà)先進(jìn)行銷(xiāo)毀
if (chart) {
chart.dispose();
}
let option = this.getOption(optionType, data);
if (option && typeof option === "object") {
chartDom.setOption(option);
}
}
}
},
// 生成圖表
// domRef 圖表標(biāo)識(shí) id
// dataType 圖表數(shù)據(jù)類(lèi)型
// optionType option類(lèi)型
// list 要生成圖表的數(shù)據(jù)列表
generateChart(domRef, dataType, optionType, list) {
let chartInData = null;
if (document.getElementById(domRef) || this.$refs[domRef]) {
let chartDom = this.initChartDom(domRef);
// 存在表格的話(huà)先進(jìn)行銷(xiāo)毀
if (chartDom) {
let chart = this.getChart(domRef);
// 存在表格的話(huà)先進(jìn)行銷(xiāo)毀
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ù)組時(shí)對(duì)象
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
// 可以通過(guò)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ù)
// 單個(gè)數(shù)據(jù)格式為 [1,2,3]
// 多個(gè)數(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":
// 生成對(duì)象數(shù)據(jù)
// 數(shù)據(jù)格式為
// {name:"", value:""}
chartOutData = {
seriesData: this.generateObjectData(
chartInData,
this.nameDataKey,
this.valueDataKey
),
};
break;
}
return chartOutData;
},
// 生成對(duì)象數(shù)據(jù)源
// 屬性為 name和value
// chartInData 生成的圖表數(shù)據(jù)
// nameKey name對(duì)應(yīng)的鍵
// valueKey value對(duì)應(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 - 對(duì)象數(shù)組
generateChartInData(list) {
let chartInData = {};
// 保證list中的每個(gè)對(duì)象的屬性名是相同的,也就是說(shuō)一一對(duì)應(yīng)
for (let attr1 in list[0]) {
// 以每個(gè)屬性名為名字構(gòu)建數(shù)組
chartInData[attr1] = [];
}
list.forEach(function (item, index) {
for (let attr2 in item) {
// chartInData[attr2] 為underfined時(shí) 初始化為空數(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的第一個(gè)元素是數(shù)組,說(shuō)明傳入的數(shù)據(jù)是多對(duì)象數(shù)組
// 否則說(shuō)明傳入的是單個(gè)對(duì)象
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();
// 通過(guò)調(diào)用子組件的方法生成圖表,設(shè)置id獲取元素
// 無(wú)法通過(guò)ref獲取
this.$refs.echarts.generateChart(
"chart-sample",
"listData",
"barOption",
this.chartData
);
},
components: {
"echarts-generate": EchartsGenerate,
},
methods: {
async getData() {
// 多個(gè)數(shù)據(jù)
// this.chartData = [
// [
// {
// id: "physical-activity",
// // label
// title: "physical activity",
// // 排序
// sort: 0,
// // 分?jǐn)?shù)
// score: 13,
// desc: "戶(hù)外活動(dòng)",
// // 分?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: "戶(hù)外活動(dòng)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,
// },
// ]
// ];
// 單個(gè)數(shù)據(jù)
this.chartData =
[
{
id: "physical1",
// label
title: "physical1",
// 排序
sort: 0,
// 分?jǐn)?shù)
score: 11,
desc: "戶(hù)外活動(dòng)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>代碼在下面
項(xiàng)目地址 https://gitee.com/joeyan3/joe-vue-demo-project

到此這篇關(guān)于Vue echarts封裝實(shí)現(xiàn)流程的文章就介紹到這了,更多相關(guān)Vue echarts封裝內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
實(shí)現(xiàn)Vue的markdown文檔可以在線(xiàn)運(yùn)行的方法示例
這篇文章主要介紹了實(shí)現(xiàn)Vue的markdown文檔可以在線(xiàn)運(yùn)行的方法示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-12-12
vue-seamless-scroll無(wú)縫滾動(dòng)組件使用方法詳解
這篇文章主要為大家詳細(xì)介紹了vue-seamless-scroll無(wú)縫滾動(dòng)組件的使用方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
vue實(shí)現(xiàn)移動(dòng)端懸浮窗效果
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)移動(dòng)端懸浮窗效果,vuejs實(shí)現(xiàn)div拖拽移動(dòng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12
策略模式實(shí)現(xiàn) Vue 動(dòng)態(tài)表單驗(yàn)證的方法
策略模式(Strategy Pattern)又稱(chēng)政策模式,其定義一系列的算法,把它們一個(gè)個(gè)封裝起來(lái),并且使它們可以互相替換。封裝的策略算法一般是獨(dú)立的,策略模式根據(jù)輸入來(lái)調(diào)整采用哪個(gè)算法。這篇文章主要介紹了策略模式實(shí)現(xiàn) Vue 動(dòng)態(tài)表單驗(yàn)證,需要的朋友可以參考下2019-09-09
詳解.vue文件中style標(biāo)簽的幾個(gè)標(biāo)識(shí)符
這篇文章主要介紹了詳解.vue文件中style標(biāo)簽的幾個(gè)標(biāo)識(shí)符,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-07-07

