总结一下自己最近项目中用echarts动态获取接口数据并画图的方法。
话不多说,先看下原型:
这里的需求是:选取el-date-picker中的自定义时间、点击本周、本月、本年时,将数据显示在饼图中
这里的需求是:选取el-date-picker中的自定义时间、点击本周、本月、本年时,将数据显示在饼图中这里的需求是:选取el-date-picker中的自定义时间、点击本周、本月时,折线图的X轴(时间轴)返回的日期格式是每天的统计量
这里的需求是:点击本年时,折线图的X轴(时间轴)返回的日期格式是每月的统计量
需求分析明确后,开始画图吧!
代码如下:
<template>
<div class="ai-wrapper">
<h3 class="flex-a-start"><div class="vertical"></div><div>AI预警系统</div></h3>
<div class="block-left">
<div class="block-title">各类型告警占比统计</div>
<div class="block-picker">
<el-date-picker
v-model="form.typeDate"
class="block-date-picker"
:editable="false"
type="daterange"
:picker-options="pickerOptions"
range-separator="~"
start-placeholder="开始时间"
end-placeholder="结束时间"
format="yyyy/MM/dd"
@change='getWarningType("type")'
/>
<el-button-group class="block-button-picker">
<el-button
@click='getWarningType("type", "week")'
>
本周
</el-button>
<el-button
@click='getWarningType("type", "month")'
>
本月
</el-button>
<el-button
@click='getWarningType("type", "year")'
>
本年
</el-button>
</el-button-group>
</div>
<div id="block-left-chart" />
</div>
<div class="block-right">
<div class="block-title">各监控点告警数量占比统计</div>
<div class="block-picker">
<el-date-picker
v-model="form.addressDate"
class="block-date-picker"
:editable="false"
type="daterange"
:picker-options="pickerOptions"
range-separator="~"
format="yyyy/MM/dd"
start-placeholder="开始时间"
end-placeholder="结束时间"
@change='getwarningAddress("address")'
/>
<el-button-group class="block-button-picker">
<el-button
@click='getwarningAddress("address", "week")'
>
本周
</el-button>
<el-button
@click='getwarningAddress("address", "month")'
>
本月
</el-button>
<el-button
@click='getwarningAddress("address", "year")'
>
本年
</el-button>
</el-button-group>
</div>
<div id="block-right-chart" />
</div>
<div class="block-bottom">
<div class="block-title">各类型告警发生量统计</div>
<div class="block-bottom-picker">
<el-date-picker
v-model="form.trendDate"
class="block-date-picker"
:editable="false"
type="daterange"
:picker-options="pickerOptions"
range-separator="~"
start-placeholder="开始时间"
end-placeholder="结束时间"
format="yyyy/MM/dd"
@change='getwarningTrend("trend")'
/>
<el-button-group class="block-button-picker">
<el-button
@click='getwarningTrend("trend", "week")'
>
本周
</el-button>
<el-button
@click='getwarningTrend("trend", "month")'
>
本月
</el-button>
<el-button
@click='getwarningTrend("trend", "year")'
>
本年
</el-button>
</el-button-group>
</div>
<div id="block-bottom-chart" />
</div>
</div>
</template>
绘制完图表后,我们来处理下数据:
在数据处理之前,接口的引入和接口定义的方法这里就不展示了,先来看下data中数据初始化吧:
data() {
return {
pickerOptions: {
disabledDate(time) {
return time.getTime() > Date.now();
}
},
form: {
typeDate: '',
addressDate: '',
trendDate: '',
// startTime 和 endTime是调用接口时往后台传递的参数
startTime: '',
endTime: ''
},
color: '#000',
color1: ['#3AA1FF', '#64BAFF', '#48a5ff', '#3b88e1', '#336fc2', '#325ea1'],
color2: ['#3fa1ff', '#78e7fc', '#23d8bc', '#7af0c8', '#77ca45', '#addf83', '#ffd05a', '#e88657', '#e55757', '#f7a798', '#a566fb'],
// 以下是用来存储接口返回的数据
warnPlaceDate: [],
warnTypeDate: [],
legendData: [],
xAxisData: [],
itemData: []
};
},
组件加载完后,需要调用接口,每个图默认显示的是本周的数据,如下:
这里调用接口的两个参数分别是事件类别和时间类别,后边会说到
mounted() {
this.getWarningType('type', 'week');
this.getwarningAddress('address', 'week');
this.getwarningTrend('trend', 'week');
},
接下来看看methods中都有哪些方法吧,因为要分开说,所以直接上方法吧:
首先是画图的方法:
drawLeftChart() {
const myChart = this.$echarts.init(document.getElementById('block-left-chart'));
myChart.setOption({
tooltip: {
trigger: 'item',
formatter: '{a}
{b} ({d}%)',
},
label: {
color: this.color
},
labelLine: {
lineStyle: {
color: this.color
},
smooth: 0.2,
length: 10,
length2: 20
},
series: [{
name: '预警类型',
type: 'pie',
roseType: 'radius',
radius: '75%',
data: this.warnTypeDate
}]
});
},
drawRightChart() {
const myChart = this.$echarts.init(document.getElementById('block-right-chart'));
myChart.setOption({
tooltip: {
trigger: 'item'
},
label: {
color: this.color,
formatter: '{b} {d}%'
},
labelLine: {
lineStyle: {
color: this.color
},
smooth: 0.2,
length: 10,
length2: 20
},
legend: {
bottom: 0,
data: this.warnPlaceDate
},
series: [{
name: '预警类型',
type: 'pie',
radius: ['35%', '55%'],
center: ['50%', '40%'],
data: this.warnPlaceDate
}]
});
},
drawBottomChart() {
const myChart = this.$echarts.init(document.getElementById('block-bottom-chart'));
myChart.clear();
const options = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
legend: {
icon: 'rect',
itemWidth: 10,
itemHeight: 10,
itemGap: 20,
data: this.legendData,
left: 30
},
grid: {
left: '3%',
right: '3%',
bottom: '3%',
containLabel: true
},
xAxis: [{
type: 'category',
axisLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
color: '#9AA1A9'
},
boundaryGap: false,
data: this.xAxisData
}],
yAxis: [{
type: 'value',
axisLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
color: '#9AA1A9'
}
}],
series: this.itemData
};
myChart.setOption(options);
myChart.resize();
},
接下来比较艰巨的一个任务就是对时间格式的处理,el-date-picker 的默认时间格式是 yyyy-MM-dd,而我们页面上需要显示的格式是yyyy/MM/dd,需要传递给后端的时间格式为yyyyMMdd,而后端返回来的时间格式为:yyyyMMdd,这里格式简单的说下,大家可以根据自己的需求来定
时间格式以及格式化的处理
// 封装时间查询的方法
// 参数 formType 和 dateType分别代表事件类别和时间类别
selectDate(formType, dateType) {
const formDate = {
...this.form };
let startDate;
let startDateView; // 展示在页面的数据
let endDate = moment(new Date()).format('YYYYMMDD');
const endDateView = moment(new Date()).format('YYYY/MM/DD'); // 展示在页面的数据
// 按时间查询
if (dateType === 'week') {
const d = new Date();
const day = d.getDay();
const date = d.getDate();
if (day === 0) {
d.setDate(date - 6);
} else {
d.setDate(date - day + 1);
}
startDate = moment(d).format('YYYYMMDD');
startDateView = moment(d).format('YYYY/MM/DD');
} else if (dateType === 'month') {
const startMoment = moment().startOf('month');
startDate = startMoment.format('YYYYMMDD');
startDateView = startMoment.format('YYYY/MM/DD');
} else if (dateType === 'year') {
const startMoment = moment().startOf('year');
startDate = startMoment.format('YYYYMMDD');
startDateView = startMoment.format('YYYY/MM/DD');
}
// 按类别(类型、地点、趋势)来查,设值
if (formType === 'type') {
if (!dateType) {
startDate = moment(this.form.typeDate[0]).format('YYYYMMDD');
endDate = moment((this.form.typeDate[1])).format('YYYYMMDD');
} else {
this.form.typeDate = [startDateView, endDateView];
}
} else if (formType === 'address') {
if (!dateType) {
startDate = moment(this.form.addressDate[0]).format('YYYYMMDD');
endDate = moment(this.form.addressDate[1]).format('YYYYMMDD');
} else {
this.form.addressDate = [startDateView, endDateView];
}
} else if (formType === 'trend') {
if (!dateType) {
startDate = moment(this.form.trendDate[0]).format('YYYYMMDD');
endDate = moment(this.form.trendDate[1]).format('YYYYMMDD');
} else {
this.form.trendDate = [startDateView, endDateView];
}
}
formDate.startTime = startDate;
formDate.endTime = endDate;
return formDate;
},
这个方法看着很庞大,不要害怕哈,上半部分主要是获取参数dateType,包含参数 startDate 和 endDate, 分为四种情况,type(week month year) 和 !type, type(week month year) 对应btn的数据, !type 对应 el-date-picker 的数据
下半部分主要是通过参数 formType 来获取 各自的 dateType 的情况
这两个参数的含义上边有提到,这里不说了
接下来处理时间的一些细节问题,也就是在折线图中的 x轴时间轴,时间的返回格式。
上边有说到过,如果是按照 !type和本周、本月查询,返回每日的数据,如果按照本年来查,返回的是每月的数据,这里数据的统计是后端处理好的,我们只需要拿到数据并渲染出来
时间轴时间的格式处理
有个细节需要注意下,如果某天或者某月没有数据时,后端是不会返回数据的,但是在前端页面上需要展示数据为0,所以如果我们像往常一样正常遍历的话,数据会出现错乱的问题
所以这块需要前端处理,后边会说到处理方法,先看下时间的获取吧
// 获取日期范围内所有天数
getYearAndMonthAndDay(start, end) {
const dateAll = [];
let i = 0;
const startTime = this.getDate(start);
const endTime = this.getDate(end);
while ((endTime.getTime() - startTime.getTime()) >= 0) {
const year = startTime.getFullYear();
const month = (startTime.getMonth() + 1).toString().length === 1 ? "0" + (startTime.getMonth()).toString() : (startTime.getMonth()).toString();
var day = startTime.getDate().toString().length === 1 ? "0"+startTime.getDate() : startTime.getDate();
dateAll[i] = year + "-" + month + "-" + day;
startTime.setDate(startTime.getDate() + 1);
i += 1;
}
return dateAll;
},
getDate(datestr) {
return new Date(datestr.substring(0, 4), datestr.substring(5, 7), datestr.substring(8, 10));
},
// 获取日期范围内所有月
getYearAndMonth(start, end) {
let result = [];
const starts = start.split('/');
const ends = end.split('/');
let staYear = parseInt(starts[0]);
let staMon = parseInt(starts[1]);
const endYear = parseInt(ends[0]);
const endMon = parseInt(ends[1]);
result.push(staYear + "-" + starts[1]);
// result.push(endYear+"-"+ends[1]);
while (staYear <= endYear) {
if (staYear === endYear) {
let staMonStr = '';
while (staMon < endMon) {
staMon++;
if (staMon < 10) {
staMonStr = "0"+staMon;
}
result.push(staYear + "-" + staMonStr);
}
staYear++;
} else {
staMon++;
var staMonStr = "";
if (staMon > 12) {
staMon = 1;
staYear++;
}
if (staMon < 10 ) {
staMonStr = "0"+staMon;
}
result.push(staYear+"-"+staMonStr);
}
}
return result;
},
时间格式处理完了,我们的参数都获取到了,接下来该调用接口了
接口调用的方法
饼图的方法比较简单,一起上吧:
async getWarningType(formType, type) {
const formDate = this.selectDate(formType, type);
const data = {
...formDate };
const resp = await warningType(data).then();
if (resp.code === '1000') {
if (resp.result) {
this.warnTypeDate = [];
const res = resp.result;
for (let i = 0; i < res.length; i++) {
const resData = res[i];
if (resData.eventName && resData.num) {
this.warnTypeDate[i] = {
name: resData.eventName + `\n` + resData.num,
value: resData.num,
itemStyle: {
color: this.color1[i] }
}
JSON.stringify(this.warnTypeDate);
this.warnTypeDate.sort(this.compareValue("value"));
}
}
this.drawLeftChart();
}
}
},
async getwarningAddress(formType, type) {
const formDate = this.selectDate(formType, type);
const data = {
...formDate };
const resp = await warningAddress(data).then();
if (resp.code === '1000') {
if (resp.result) {
this.warnPlaceDate = [];
const res = resp.result;
for (let i = 0; i < res.length; i += 1) {
const resData = res[i];
if (resData.monitorPoint && resData.num) {
this.warnPlaceDate[i] = {
name: resData.monitorPoint,
value: resData.num,
itemStyle: {
color: this.color2[i] }
};
}
}
this.drawRightChart();
}
}
},
大家有注意到第一个饼图的方法里有个没定义的方法吗,compareValue的方法是对数据进行排序,因为第一个饼图是马丁格尔图,排下序更美观一点,方法如下:
compareValue(protoTypeName) {
return function(object1,object2){
var val1 = object1[protoTypeName];
var val2 = object2[protoTypeName];
if(val1<val2){
return 1;
}else if(val1>val2){
return -1;
}else{
return 0;
}
}
},
接下来最重要的折线图的方法来了,因为这个折线图数据结构比较特殊,所以先上接口数据吧(以本年为例)
代码如下,可以对着数据看代码,比较好理解数据的处理
async getwarningTrend(formType, type) {
const formDate = this.selectDate(formType, type);
const data = {
...formDate };
const res = await warningTrend(data).then();
this.legendData = [];
this.xAxisData = [];
this.itemData = [];
let resultObj;
if (res.code === '1000') {
if (res.result) {
const resultObj = res.result;
if (type === 'year') {
this.xAxisData = this.getYearAndMonth(this.form.trendDate[0], this.form.trendDate[1]);
} else if (type === 'month' || type === 'week') {
this.xAxisData = this.getYearAndMonthAndDay(this.form.trendDate[0], this.form.trendDate[1]);
} else if (!type) {
this.xAxisData = this.getYearAndMonthAndDay(moment(this.form.trendDate[0]).format('YYYY/MM/DD'), moment(this.form.trendDate[1]).format('YYYY/MM/DD'));
}
for (let typeKey in resultObj) {
this.legendData.push(typeKey);
const values = resultObj[typeKey];
let dataList = [];
for (var dateKey in values) {
dataList.push(dateKey);
}
const num = [];
for (let i = 0; i < this.xAxisData.length; i++) {
let xAxis = this.xAxisData[i].replace(/-/g,'');
if ( dataList.includes(xAxis)) {
num.push(parseInt(values[xAxis]));
} else {
num.push(0);
}
}
const pointObj = {
name: typeKey,
type: 'line',
data: num,
symbol: 'circle',
symbolSize: 4,
};
this.itemData.push(pointObj)
};
this.drawBottomChart();
}
}
}
饼图的数据都是从result中获取的,就不说了,到这里,所有的图表就都完成了,晚安吧