1、实现的效果:****
2、共用组件封装:不是本节重点,所以直接代码:展示了组件名为:VueEcharts.vue
<template>
<div ref="echarts" :id="id" :style="{ width, height }" class="echarts" />
</template>
<script>
export default {
name: 'VueEcharts',
props: {
options: {
type: Object,
required: true,
},
id: {
type: String,
default: () => {
return 'VueEcharts';
},
},
source: {
type: String,
default: '',
},
echarts: {
type: Object,
default: () => {},
},
width: {
type: String,
default: '100%',
},
height: {
type: String,
default: '100%',
},
click: {
type: String,
default: '100%',
},
tab: {
type: String,
default: '',
},
},
data() {
return {
option: {},
echart: null,
chartTimer: null,
};
},
mounted() {
this.echart = this.$echarts.init(this.$refs.echarts);
this.echart?.setOption(this.options, true);
// key 对应模块
const obj = {
resource_lng_forecast: 1,
lng_enteringShip: 2,
lng_shipment: 3,
nationalMarket_facility_ReceivingStation: 4,
};
if (this.click === 'lng_shipment') {
this.echart.on('click', 'xAxis.category', params => {
this.$store.dispatch('facility/lng_shipment_xAxis', {
source: this.source,
params,
clickCode: obj[this.click],
tab: this.tab,
});
});
} else {
this.echart.on('click', 'xAxis.category', params => {
this.$store.dispatch('facility/queryxAxis', {
source: this.source,
params,
clickCode: obj[this.click],
tab: this.tab,
});
});
}
this.$emit('update:echarts', this.echart);
},
watch: {
options: {
handler(value) {
this.option = value;
// 右侧内容为全屏时切换子tabs导致下方时间轴还是220px 所以重新设置气宽度
if (!this.$isEmpty(this.echartsWidth)) {
if (!this.$isEmpty(value.dataZoom)) {
value.dataZoom[0].width = this.echartsWidth - 20;
}
this.echart.resize();
}
this.echart?.setOption(value, true);
},
deep: true,
},
},
destroyed() {
clearTimeout(this.chartTimer);
},
};
</script>
3、引用公用组件:
<Charts :options="optionCustody" :echarts.sync="echarts" height="300px" />
import Charts from '@/components/common/VueEcharts.vue';
components: {
Charts,
},
4、业务开始数据类型:
const data = [
{
value: 28.5,
startDate: '2022-08-29 00:00:00',
endDate: '2022-09-05 00:00:00',
},
{
stock: 27.4286,
startDate: '2022-09-05 00:00:00',
endDate: '2022-09-12 00:00:00',
},
{
stock: 28.1,
startDate: '2022-09-12 00:00:00',
endDate: '2022-09-19 00:00:00',
},
{
stock: 28.1,
startDate: '2022-09-19 00:00:00',
endDate: '2022-09-26 00:00:00',
},
{
stock: 28.1,
startDate: '2022-09-26 00:00:00',
endDate: '2022-10-03 00:00:00',
},
{
stock: 26.7,
startDate: '2022-10-03 00:00:00',
endDate: '2022-10-10 00:00:00',
},
{
stock: 27.4286,
startDate: '2022-10-10 00:00:00',
endDate: '2022-10-17 00:00:00',
},
{
stock: 26.6,
startDate: '2022-10-17 00:00:00',
endDate: '2022-10-24 00:00:00',
},
{
stock: 26.6,
startDate: '2022-10-24 00:00:00',
endDate: '2022-10-31 00:00:00',
},
{
stock: 28.5,
startDate: '2022-10-31 00:00:00',
endDate: '2022-11-07 00:00:00',
},
{
stock: 26.7,
startDate: '2022-11-07 00:00:00',
endDate: '2022-11-14 00:00:00',
},
{
stock: 28.5,
startDate: '2022-11-14 00:00:00',
endDate: '2022-11-21 00:00:00',
},
{
stock: 26.6,
startDate: '2022-11-21 00:00:00',
endDate: '2022-11-28 00:00:00',
},
{
stock: 26.7,
startDate: '2022-11-28 00:00:00',
endDate: '2022-12-05 00:00:00',
},
];
5、业务要求实现如ui图表所示根据数据来源分为 ['低', '中低', '中', '中高', '高']五个不同的尺寸。图表只展示相对应的时间,等级即可,无需展示具体数据值,且与ui保持一致。
数据等级区分规则为:
const labels = ['低', '中低', '中', '中高', '高'];
if (value < 26.7) {
return labels[0];
} else if (value >= 26.7 && value < 27.2) {
return labels[1];
} else if (value >= 27.2 && value < 27.7) {
return labels[2];
} else if (value >= 27.7 && value < 28.2) {
return labels[3];
} else if (value >= 28.2) {
return labels[4];
}
},
6、Y轴样式:如何达到ui效果并且完成相对应的功能呢,我们可以看到数据的幅度其实也是很大的,而我们的echart图表若要想控制固定刻度,那么必不可少的就需要限制y轴的最小值与最大值:所以这里我们用到了一个属性,那就是设置属性yAxis中的min以及max,那么设置多少呢??其实就是根据等级规则的最大值最小值进行限制 则min:26.7 ;max :28.2,到这里可能就会有人说了,你设置最大最小值,那么那些小于26.7才是低,你这样写那岂不是26.7也是低了。到这里我这个疑问,我待会解惑哈。好的,我们设置尺寸最大最小值已经规定了,但是现在我们的还需要规定y刻度的间隔,所以这里我们将interval属性设置为0.5,与业务保持一致,其后就是将间隔刻度线以及y轴隐藏掉分别涉及属性为axisLine,splitLine都设置为false就行了,因为他们是默认为true的, 好的,现在我们可以将刻度尺寸展示出来了,这里涉及到的属性就是axisLabel,它里面提供格式化函数的,所以我们根据格式化函数将对应尺寸的逻辑写上去就好了,具体逻辑我就将整个控制Y轴的属性yAxis的代码展示出来吧;
yAxis: [
{
show: true,
min: 26.2,
max: 28.2,
interval: 0.5,
splitNumber: 5,
name: '',
nameTextStyle: {
color: '#666',
align: 'left',
},
scale: true,
axisLine: { show: false },
splitLine: {
show: false,
},
axisLabel: {
formatter: function (value) {
const labels = ['低', '中低', '中', '中高', '高'];
if (value < 26.7) {
return labels[0];
} else if (value >= 26.7 && value < 27.2) {
return labels[1];
} else if (value >= 27.2 && value < 27.7) {
return labels[2];
} else if (value >= 27.7 && value < 28.2) {
return labels[3];
} else if (value >= 28.2) {
return labels[4];
}
},
},
},
],
7、说完Y轴样式那么我们就需要将真实数据的转换成eharts图表各属性需要且支持的数据格式了: 上面我们知道所有的数据是包含在data对象当中的,所以我们需要对data数据做处理 xAxisData: X轴的数据源,我们直接遍历data将每个结束时间push进去即可,seriesData数据源的数据,首先我们看见ui上的弹窗是需要结束时间和开始时间的,所以我们就需要两个时间也得放进这个数组当中,反正就是后续需要什么就加什么,当然最重要的还是value属性,因为echarts是去数组当中默认查找对应value属性的值的,下面有个注意点就是什么呢,就是我们将所有的数据都对应的写死了,其实就是五个不同,层次的等级,因为我们知道ui上具体数据是不需要展示真实的数据值的,只需要展示等级即可,所有我们前端要做的就是在视觉上呈现出相对应的效果即可。
const xAxisData = [];
const seriesData = [];
data.forEach(item => {
if (item.stock < 26.7) {
item.stock = 26.2;
} else if (item.stock >= 26.7 && item.stock < 27.2) {
item.stock = 26.7;
} else if (item.stock <= 27.2 && item.stock < 27.7) {
item.stock = 27.2;
} else if (item.stock >= 27.7 && item.stock < 28.2) {
item.stock = 27.7;
} else if (item.stock >= 28.2) {
item.stock = 28.2;
}
xAxisData.push(item.endDate.slice(0, 10));
seriesData.push({ ...item, value: item.stock });
});
8、X轴样式:用到属性xAxis,这里我们看到ui稿上,可以发现具体样式以及展示规则好像没有做固定限制所以我们只需要微调样式,填充数据即可:具体如下:
xAxis: [
{
type: 'category',
// 控制刻度显示问题
axisTick: {
show: false,
},
// 控制x轴的样式
axisLine: {
lineStyle: {
color: '#333', // 颜色
width: 0.5, // 粗细
},
},
// x轴的数据源,我们看到ui的数据时间那么只需要将所有时间push到xAxisData数组里就好了,
// 这个数据是一个一维形式的数组,格式为:['2022-02-22','2022-02-22']
data: xAxisData,
},
],
9、弹窗样式及其相关属性:tooltip:其中confine设置为true,目的是为了防止弹窗超出了我们的可视区域,导致部分内容无法显示出来,然后就是弹窗中的内容,弹窗也是提供格式化函数的,所以直接用到formatter函数具体如下:
tooltip: {
trigger: 'axis',
confine: true,
formatter: params => this.toolTipTempFn(params),
},
toolTipTempFn(params) {
// params就是我们点击某一个数据点的内容,包括对应的数据
// startDate这就是我们的业务数据中所点击的起始时间,我们这里只展示年月日,所以截取一部分内容
// endDate同理
// stock就是我们的数据源真实数据,到时这里我们调用的getSize,其实也就是为了将我们的
// 数据区分到具体的等级当中,因为UI上的弹窗也是需要将等级展示出来就行了
// 可以看到我们之间返回的是一个div Dom元素,所以我们直接可以在里面编辑样式就行了,
// 因为ui需要将不同等级样式的颜色区分一下,所以我们这里用到了getColor函数,
//且使用行内样式进行样式修改
const startDate = params[0].data.startDate.slice(0, 10).split('-').join('/');
const endDate = params[0].data.endDate.slice(0, 10).split('-').join('/');
const stock = this.getSize(params[0].data.stock);
return `<div>
<div style="font-weight: bold;">${startDate} - ${endDate}
<div>
<div>库存水平:
<span style='color:${this.getColor(params[0].data.stock)}'>
${stock ? stock : '-'}</span>
</div>
</div>
</div>`;
},
getSize(total) {
const val = Number(total);
switch (true) {
case val < 26.7:
return '低';
case val >= 26.7 && val < 27.2:
return '中低';
case val >= 27.2 && val < 27.7:
return '中';
case val >= 27.7 && val < 28.2:
return '中高';
case val >= 28.2:
return '高';
}
},
getColor(total) {
const value = Number(total);
if (value < 26.7) {
return '#28ada7';
} else if (value >= 26.7 && value < 27.2) {
return '#0473ff';
} else if (value >= 27.2 && value < 27.7) {
return '#ffbf00';
} else if (value >= 27.7 && value < 28.2) {
return '#ff9200';
} else if (value >= 28.2) {
return '#F55858';
}
},
10、数据源series
series: [
{
// smooth让线条更加平滑,默认为false
// symbol:数据点类型:'emptyCircle'则是空心圆
// type:图形展示类型我们用到的是线状图所以为line
// showSymbol:展示数据点
// lineStyle:线条展示的样式,可以看到ui线条在不同的等级区间展示的是不同的颜色,所以这里我们就需要用到渐变色
// colorStops:将整个区域的分为1个单位,其中offset相对于将这一个单位切割,我们就可以在不同的位置更显不同的颜色了
// itemStyle:就是我们的数据点的样式,ui上不同的等级也会展示不同颜色的数据点,所以color属性也是用一个
// getColor函数去获取不同等级区间的颜色,其中的params就是当前数据点的所有内容params.data.value就是具体的数据值
// emphasis:就是聚焦时数据点的样式,其余的原理就是和itemStyle属性一模一样
// data就是我们的每个点的数据:格式则是[{endDate:'2022-02-22',value:'67.5'},{endDate:'2022-02-22',value:'67.5'}]
// areaStyle属性就是区域面积的样式,当然他同样的也是可以支持渐变色的
// 只是这里有一个技巧就是需要根据图表的真实效果来定义几个等级的区域范围此处是灵活的哈,
smooth: true,
name: '管网管存水平',
type: 'line',
symbol: 'emptyCircle',
// lineStyle: { width: 0 },
showSymbol: true,
lineStyle: {
normal: {
width: 2,
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: '#F55858' },
{ offset: 0.2, color: '#F09918' },
{ offset: 0.4, color: '#FFCC00' },
{ offset: 0.81, color: '#0473FF' },
{ offset: 0.91, color: '#16B57D' },
{ offset: 1, color: '#16B57D' },
],
},
},
},
itemStyle: {
color: params => {
const value = params.data.value;
return this.getColor(value);
},
borderWidth: 2, // 设置数据点边框宽度
},
emphasis: {
color: params => {
const value = params.data.value;
return this.getColor(value);
},
},
data: seriesData,
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 1,
x2: 0,
y2: 0,
colorStops: [
{ offset: 0, color: 'rgba(138,218,189,0.2)' },
{ offset: 0.15, color: 'rgba(138,218,189,1)' },
{ offset: 0.16, color: 'rgba(4,115,255,0.2)' },
{ offset: 0.4, color: 'rgba(4,115,255,0.8)' },
{ offset: 0.41, color: 'rgba(255, 204, 0, 0.2)' },
{ offset: 0.65, color: 'rgba(255, 204, 0, 0.8)' },
{ offset: 0.66, color: 'rgba(240, 153, 24, 0.2)' },
{ offset: 0.84, color: 'rgba(240, 153, 24, 0.8)' },
{ offset: 0.86, color: 'rgba(245, 88, 88, 0.2)' },
{ offset: 1, color: 'rgba(245, 88, 88, 1)' },
],
},
},
},
],
11、整体代码逻辑
<template>
<div class="pipe-network-repository nav-item">
<div class="common-content">
<p class="common-content-hint">数据时段:近3个月</p>
<Charts :options="optionCustody" :echarts.sync="echarts" height="300px" />
</div>
</div>
</template>
<script>
import Charts from '@/components/common/VueEcharts.vue';
export default {
name: 'PipeNetworkAndRepository',
data() {
return {
echarts: {},
optionCustody: {},
};
},
mounted() {
this.getNationalPipeStorageFn(0);
},
methods: {
getSize(total) {
const val = Number(total);
switch (true) {
case val < 26.7:
return '低';
case val >= 26.7 && val < 27.2:
return '中低';
case val >= 27.2 && val < 27.7:
return '中';
case val >= 27.7 && val < 28.2:
return '中高';
case val >= 28.2:
return '高';
}
},
toolTipTempFn(params) {
const startDate = params[0].data.startDate.slice(0, 10).split('-').join('/');
const endDate = params[0].data.endDate.slice(0, 10).split('-').join('/');
const stock = this.getSize(params[0].data.stock);
return `<div>
<div style="font-weight: bold;">${startDate} - ${endDate}
<div>
<div>库存水平:
<span style='color:${this.getColor(params[0].data.stock)}'>
${stock ? stock : '-'}</span>
</div>
</div>
</div>`;
},
async getNationalPipeStorageFn() {
const xAxisData = [];
const seriesData = [];
const data = [
{
value: 28.5,
startDate: '2022-08-29 00:00:00',
endDate: '2022-09-05 00:00:00',
},
{
stock: 27.4286,
startDate: '2022-09-05 00:00:00',
endDate: '2022-09-12 00:00:00',
},
{
stock: 28.1,
startDate: '2022-09-12 00:00:00',
endDate: '2022-09-19 00:00:00',
},
{
stock: 28.1,
startDate: '2022-09-19 00:00:00',
endDate: '2022-09-26 00:00:00',
},
{
stock: 28.1,
startDate: '2022-09-26 00:00:00',
endDate: '2022-10-03 00:00:00',
},
{
stock: 26.7,
startDate: '2022-10-03 00:00:00',
endDate: '2022-10-10 00:00:00',
},
{
stock: 27.4286,
startDate: '2022-10-10 00:00:00',
endDate: '2022-10-17 00:00:00',
},
{
stock: 26.6,
startDate: '2022-10-17 00:00:00',
endDate: '2022-10-24 00:00:00',
},
{
stock: 26.6,
startDate: '2022-10-24 00:00:00',
endDate: '2022-10-31 00:00:00',
},
{
stock: 28.5,
startDate: '2022-10-31 00:00:00',
endDate: '2022-11-07 00:00:00',
},
{
stock: 26.7,
startDate: '2022-11-07 00:00:00',
endDate: '2022-11-14 00:00:00',
},
{
stock: 28.5,
startDate: '2022-11-14 00:00:00',
endDate: '2022-11-21 00:00:00',
},
{
stock: 26.6,
startDate: '2022-11-21 00:00:00',
endDate: '2022-11-28 00:00:00',
},
{
stock: 26.7,
startDate: '2022-11-28 00:00:00',
endDate: '2022-12-05 00:00:00',
},
];
data.forEach(item => {
if (item.stock < 26.7) {
item.stock = 26.2;
} else if (item.stock >= 26.7 && item.stock < 27.2) {
item.stock = 26.7;
} else if (item.stock <= 27.2 && item.stock < 27.7) {
item.stock = 27.2;
} else if (item.stock >= 27.7 && item.stock < 28.2) {
item.stock = 27.7;
} else if (item.stock >= 28.2) {
item.stock = 28.2;
}
xAxisData.push(item.endDate.slice(0, 10));
seriesData.push({ ...item, value: item.stock });
});
this.optionCustody = {
tooltip: {
trigger: 'axis',
confine: true,
formatter: params => this.toolTipTempFn(params),
},
xAxis: [
{
type: 'category',
// 控制刻度显示问题
axisTick: {
show: false,
},
// 控制x轴的样式
axisLine: {
lineStyle: {
color: '#333', // 颜色
width: 0.5, // 粗细
},
},
data: xAxisData,
},
],
yAxis: [
{
show: true,
min: 26.2,
max: 28.2,
interval: 0.5,
splitNumber: 5,
name: '',
nameTextStyle: {
color: '#666',
align: 'left',
},
scale: true,
axisLine: { show: false },
splitLine: {
show: false,
},
axisLabel: {
formatter: function (value) {
const labels = ['低', '中低', '中', '中高', '高'];
if (value < 26.7) {
return labels[0];
} else if (value >= 26.7 && value < 27.2) {
return labels[1];
} else if (value >= 27.2 && value < 27.7) {
return labels[2];
} else if (value >= 27.7 && value < 28.2) {
return labels[3];
} else if (value >= 28.2) {
return labels[4];
}
},
// textStyle: {
// lineHeight: 5,
// },
},
},
],
grid: {
left: 40, // 调整这个属性
right: 12,
// containLabel: true,
top: 20,
bottom: 20,
},
legend: {
show: false,
data: [
{
name: '管网管存水平',
},
],
},
// dataZoom: this.$dataZoom(70, 100, -10, 290),
series: [
{
smooth: true,
name: '管网管存水平',
type: 'line',
symbol: 'emptyCircle',
// lineStyle: { width: 0 },
showSymbol: true,
lineStyle: {
normal: {
width: 2,
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: '#F55858' },
{ offset: 0.2, color: '#F09918' },
{ offset: 0.4, color: '#FFCC00' },
{ offset: 0.81, color: '#0473FF' },
{ offset: 0.91, color: '#16B57D' },
{ offset: 1, color: '#16B57D' },
],
},
},
},
itemStyle: {
color: params => {
const value = params.data.value;
return this.getColor(value);
},
borderWidth: 2, // 设置数据点边框宽度
},
emphasis: {
color: params => {
const value = params.data.value;
return this.getColor(value);
},
},
data: seriesData,
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 1,
x2: 0,
y2: 0,
colorStops: [
{ offset: 0, color: 'rgba(138,218,189,0.2)' },
{ offset: 0.15, color: 'rgba(138,218,189,1)' },
{ offset: 0.16, color: 'rgba(4,115,255,0.2)' },
{ offset: 0.4, color: 'rgba(4,115,255,0.8)' },
{ offset: 0.41, color: 'rgba(255, 204, 0, 0.2)' },
{ offset: 0.65, color: 'rgba(255, 204, 0, 0.8)' },
{ offset: 0.66, color: 'rgba(240, 153, 24, 0.2)' },
{ offset: 0.84, color: 'rgba(240, 153, 24, 0.8)' },
{ offset: 0.86, color: 'rgba(245, 88, 88, 0.2)' },
{ offset: 1, color: 'rgba(245, 88, 88, 1)' },
],
},
},
},
],
};
},
getColor(total) {
const value = Number(total);
if (value < 26.7) {
return '#28ada7';
} else if (value >= 26.7 && value < 27.2) {
return '#0473ff';
} else if (value >= 27.2 && value < 27.7) {
return '#ffbf00';
} else if (value >= 27.7 && value < 28.2) {
return '#ff9200';
} else if (value >= 28.2) {
return '#F55858';
}
},
},
components: {
Charts,
},
};
</script>
结尾:好了以上就是本节内容谢谢大家的观看。