[VUE2/VUE3]基于echarts的动态折线图组件
时间格式化代码
export default function formatSecond(value: number) {
let millisecond: string | number = value % 1000; // 秒
let second: string | number = Math.floor(value / 1000); // 秒
let minute = 0; // 分
let result = '';
if (second >= 60) {
minute = Math.floor(second / 60);
second = Math.floor(second % 60);
}
if (millisecond >= 0 && millisecond < 10) {
millisecond = '00';
} else if (millisecond >= 10 && millisecond < 100) {
millisecond = '0' + millisecond.toString().slice(0, 1);
} else {
millisecond = millisecond.toString().slice(0, 2);
}
if (second >= 0 && second < 10) {
second = '0' + second;
}
if (minute >= 0 && minute < 10) {
result = '0' + Math.floor(minute) + ':' + second + ':' + millisecond;
} else {
result = Math.floor(minute) + ':' + second + ':' + millisecond;
}
return result.slice(0, -3);
}
组件代码
<template>
<div class="echart-container">
<div id="hzs-echart" style="height: 250px" ref="echart"></div>
<div class="echart-btns">
<button v-show="!isStartPaint" @click="startPaint">开始绘图</button>
<button v-show="isStartPaint" @click="stopPaint">停止绘图</button>
<button @click="printOptions">打印配置信息</button>
<button @click="addTag">打标签</button>
<button @click="viewHistory">查看历史数据</button>
<button @click="viewNow">查看当前数据</button>
</div>
</div>
</template>
<script>
import { defineComponent, markRaw } from 'vue';
import * as echarts from 'echarts';
// import cloneDeep from '@/util/deepCopy';
import formatSecond from '@/util/formatSecond';
const colorList = ['#0f78f4', '#dd536b', '#9462e5', '#a6a6a6', '#e1bb22', '#39c362', '#3ed1cf'];
export default defineComponent({
name: 'ChangeAxisChart',
props: {
allDataDict: {
type: Object,
default() {
return {
data1: [100, 100, 100],
data2: [100, 100, 100],
data3: [100, 100, 100]
};
}
},
checkedKeyList: {
type: Array,
default() {
return ['data1', 'data2'];
}
},
timeList: {
type: Array,
default() {
return [1654896594373, 1654896595373, 1654896596373];
}
},
initTime: {
type: Number,
default() {
return new Date().getTime();
}
}
},
computed: {
// 设定要修改的option 和 目标数据集
targetOption: function () {
return this.isHistory ? this.optionsHistory : this.options;
},
targetDataDict: function () {
return this.isHistory ? this.chartData.allData : this.chartData.data;
}
},
watch: {
timeList: {
immediate: true,
deep: true,
handler(newVal, oldVal) {
// console.log("重置chart")
// console.log(newVal)
// console.log('更新timeList');
if (!this.isStartPaint) {
// console.log('没开始画');
return;
}
if (this.isHistory) {
return;
}
if (this.checkedKeyList.length === 0) {
return;
}
this.initChart();
// this.refreshChart();
}
},
// 更新选择键
checkedKeyList: {
immediate: true,
deep: true,
handler(newVal, oldVal) {
// console.log("重置chart")
// console.log('更新选择键');
// console.log(newVal);
if (!this.isStartPaint) {
return;
}
if (this.isHistory) {
return;
}
if (newVal.length === 0) {
return;
}
// this.refreshChart();
this.initChart();
}
}
},
data() {
return {
// 画图
echart: null,
echartHistory: null,
// 画图数据
chartData: {
data: [],
allData: []
},
// 当前数据的配置项
options: {
// 图相对于容器的位置
grid: {
left: '10%',
right: '10%',
bottom: '10%',
top: '10%'
},
legend: {
data: []
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
},
formatter: function (params) {
console.log('axis');
console.log(params);
if (params.length > 1) {
return `<div class='tooltips'>
<p>时刻:${formatSecond(params[0].axisValue)}</p>
<p>该点左侧y值:${params[0].value[1]}</p>
<p>该点右侧y值:${params[1].value[1]}</p>
</div> `;
} else {
return `<div class='tooltips'>
<p>时刻:${formatSecond(params[0].axisValue)}</p>
<p>该点的值:${params[0].value[1]}</p>
</div> `;
}
}
},
color: colorList,
xAxis: {
name: '',
// type: 'category',
type: 'value',
// 以x秒为间隔
// minInterval: 2000,
minInterval: 2000,
axisLabel: {
// 坐标轴标签
show: true, // 是否显示
inside: false, // 是否朝内
rotate: 0, // 旋转角度
margin: 5, // 刻度标签与轴线之间的距离
color: '#999', // 默认取轴线的颜色
formatter: function (value) {
return formatSecond(value);
}
},
axisPointer: {
label: {
formatter: function (params) {
return formatSecond(params.value);
}
}
}
},
yAxis: [
{
name: '',
type: 'value',
// scale: true,
show: true,
axisLine: {
lineStyle: {
color: colorList[0]
}
},
axisLabel: {
// formatter: '{value} °C',
color: colorList[0]
},
max: function (value) {
const h = value.max - value.min;
return Math.floor(value.max + Math.floor(h) * 0.2 + 2);
},
min: function (value) {
const h = value.max - value.min;
return Math.floor(value.min - Math.floor(h) * 0.2 - 2);
},
splitNumber: 5
},
{
name: '',
type: 'value',
show: true,
axisLine: {
lineStyle: {
color: colorList[1]
}
},
axisLabel: {
color: colorList[1]
},
max: function (value) {
const h = value.max - value.min;
return Math.floor(value.max + Math.floor(h) * 0.2 + 2);
},
min: function (value) {
const h = value.max - value.min;
return Math.floor(value.min - Math.floor(h) * 0.2 - 2);
},
splitNumber: 5
}
],
series: []
},
// 查看历史数据时候的配置项
optionsHistory: {
// 图相对于容器的位置
grid: {
left: '10%',
right: '10%',
bottom: '20%',
top: '10%'
},
dataZoom: [
{
type: 'inside',
start: 0
},
{
type: 'slider',
show: true,
start: 0,
height: 16,
bottom: '5%'
}
],
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
animation: false,
label: {
backgroundColor: '#505765'
}
},
formatter: function (params) {
console.log(params);
return `<div class='tooltips'>
<p>时刻:${params[0].axisValue}</p>
<p>值:${params[0].value[1]}</p>
</div> `;
}
},
color: ['#0f78f4', '#dd536b', '#9462e5', '#a6a6a6', '#e1bb22', '#39c362', '#3ed1cf'],
xAxis: {
// name: '秒',
type: 'value',
// 以一秒为间隔
minInterval: 1000,
axisLabel: {
// 坐标轴标签
show: true, // 是否显示
inside: false, // 是否朝内
rotate: 0, // 旋转角度
margin: 5, // 刻度标签与轴线之间的距离
color: '#999', // 默认取轴线的颜色
formatter: function (value) {
return formatSecond(value);
}
}
},
yAxis: {
name: '',
type: 'value',
scale: true,
show: true,
max: function (value) {
return 1.2 * value.max;
}
},
series: []
},
// 标记点列表
markPointList: [],
// 是否是历史数据
isHistory: false,
// 是否开始绘图
isStartPaint: false
};
},
methods: {
// 取数据,组合成[x,y]
dealData() {
const res = {};
// 取出y的data
this.checkedKeyList.forEach((key) => {
// res[key] = []
// 一条线的数据[[x1,y1],[x2,y2],[x3,y3]]
// [y1,y2,y3,y4]
const yList = this.allDataDict[key];
// this.timeList [x1,x2,x3]
// 组合 [[x1,y1],[x2,y2],[x3,y3]]
res[key] = this.mergeList(this.timeList, yList);
});
// console.log(res)
return res;
},
// 合并[x1,x2,x3] [y1,y2,y3]成一条线 [[x1,y1],[x2,y2],[x3,y3]]
mergeList(aList, bList) {
// console.log(aList);
// console.log(bList);
if (aList.length !== bList.length) {
return [];
}
const res = [];
for (let i = 0; i < aList.length; i++) {
if (bList[i] !== '') {
res.push([aList[i] - this.initTime, bList[i]]);
}
}
return res;
},
// 设置数据
initChartData() {
// 画多条线
const series = [];
// for (let lineData of this.targetDataDict) {
// series.push(this.getSeries(lineData))
// }
Object.keys(this.targetDataDict).forEach((key, index) => {
series.push(this.getSeries(this.targetDataDict[key], key));
series[index]['yAxisIndex'] = index;
// 设置一下颜色
series[index]['itemStyle'] = {
normal: {
lineStyle: {
color: colorList[index]
}
}
};
});
this.options.series = series;
// console.log("要画图了!")
// console.log(this.options)
// 配置一下已经打标记的点
for (let i = 0; i < this.targetOption.series.length; i++) {
const line = this.targetOption.series[i];
line.markPoint.data = this.markPointList[i];
}
},
// 把echart画出来
initChart() {
// 处理一下数据
this.chartData.data = this.dealData();
// console.log(this.chartData)
// 处理其他选项
this.options.legend.data = this.checkedKeyList;
// 配置图数据
this.initChartData();
// 画当前的数据
if (!this.echart) {
this.echart = markRaw(echarts.init(this.$refs.echart));
// this.echart = echarts.init(this.$refs.echart);
}
// this.clearChart()
// console.log('将按照如下配置绘制图像');
// console.log(this.targetOption);
try {
setTimeout(() => {
this.echart.setOption(this.targetOption);
}, 30);
} catch (e) {
console.log('捕捉到异常');
console.log(e);
}
// 存入
localStorage.setItem('chartOption', JSON.stringify(this.targetOption));
// 清除
// localStorage.removeItem('chartOption');
},
// 开始画图
drawChart() {
const chartOption = localStorage.getItem('chartOption');
if (!chartOption) {
return;
}
if (this.echart) {
this.clearChart();
// this.echart = echarts.init(this.$refs.echart);
this.echart = markRaw(echarts.init(this.$refs.echart));
}
try {
setTimeout(() => {
this.echart.setOption(chartOption);
}, 30);
} catch (e) {
console.log('捕捉到异常');
console.log(e);
}
},
// resize重新布局
resizeChart() {
this.echart ? this.echart.resize() : '';
this.echartHistory ? this.echartHistory.resize() : '';
},
// 打标签
addTag() {
// console.log("add tag")
// 对每条折线,都获取到现在最后一个点,然后推入标记标签列表中
for (let i = 0; i < this.options.series.length; i++) {
const line = this.options.series[i];
const lastPoint = line['data'].slice(-1)[0];
// 设置一下markPoint的列表
if (this.markPointList[i] === undefined) {
this.markPointList[i] = [];
}
// 推入标记点列表
this.markPointList[i].push({
name: '标记点',
value: lastPoint[1],
coord: lastPoint
});
}
// 标签画入图中
this.setMarkPoints();
// 刷新一下
// this.refreshChart();
this.initChart();
},
// 将标记的点添加到配置项中
setMarkPoints() {
for (let i = 0; i < this.targetOption.series.length; i++) {
const line = this.targetOption.series[i];
line.markPoint.data = this.markPointList[i];
}
},
// 配置series
getSeries(dataList, name = '') {
return {
name: name,
type: 'line',
animation: false,
symbol: 'none',
lineStyle: {
width: 1
},
data: dataList,
markPoint: {
effect: {
show: true,
shadowBlur: 0
},
data: []
}
};
},
//判断是不是有多条线
isMultiLine() {
return Object.keys(this.targetDataDict).length > 1;
},
// 新版本查看历史数据
viewHistory() {
// console.log("viewHistory")
this.isHistory = true;
const historyOption = {};
Object.assign(historyOption, this.options);
historyOption.grid = {
left: '10%',
right: '10%',
bottom: '20%',
top: '10%'
};
historyOption.dataZoom = [
{
type: 'inside',
start: 0
},
{
type: 'slider',
show: true,
start: 0,
height: 16,
bottom: '5%'
}
];
// // 配置一下历史选项
this.echart.clear();
setTimeout(() => {
this.echart.setOption(historyOption);
}, 50);
},
// 查看当前数据
viewNow() {
this.clearChart();
this.isHistory = false;
this.initChart();
},
// 清空图标
clearChart() {
if (this.echart) {
this.echart.clear();
}
},
// 彻底清空图表
clearChartPlus() {
if (this.echart) {
this.echart.clear();
}
this.options = {
// 图相对于容器的位置
grid: {
left: '10%',
right: '10%',
bottom: '10%',
top: '10%'
},
legend: {
data: []
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
animation: false,
label: {
backgroundColor: '#505765'
}
},
formatter: (params) => {
console.log(params);
if (params.length > 1) {
return `<div class='tooltips'>
<p>时刻:${formatSecond(params[0].axisValue)}</p>
<p>该点左侧y值:${params[0].value[1]}</p>
<p>该点右侧y值:${params[1].value[1]}</p>
</div> `;
} else {
return `<div class='tooltips'>
<p>时刻:${formatSecond(params[0].axisValue)}</p>
<p>该点的值:${params[0].value[1]}</p>
</div> `;
}
}
},
color: colorList,
xAxis: {
name: '',
type: 'value',
minInterval: 2000,
// minInterval: 2000,
axisLabel: {
// 坐标轴标签
show: true, // 是否显示
inside: false, // 是否朝内
rotate: 0, // 旋转角度
margin: 5, // 刻度标签与轴线之间的距离
color: '#999', // 默认取轴线的颜色
formatter: function (value) {
return formatSecond(value);
}
}
},
yAxis: [
{
name: '',
type: 'value',
// scale: true,
show: true,
axisLine: {
lineStyle: {
color: colorList[0]
}
},
axisLabel: {
// formatter: '{value} °C',
color: colorList[0]
},
max: function (value) {
const h = value.max - value.min;
return Math.floor(value.max + Math.floor(h) * 0.2 + 2);
},
min: function (value) {
const h = value.max - value.min;
return Math.floor(value.min - Math.floor(h) * 0.2 - 2);
},
splitNumber: 5
},
{
name: '',
type: 'value',
// scale: true,
show: true,
axisLine: {
lineStyle: {
color: colorList[1]
}
},
axisLabel: {
// formatter: '{value} °C',
color: colorList[1]
},
max: function (value) {
const h = value.max - value.min;
return Math.floor(value.max + Math.floor(h) * 0.2 + 2);
},
min: function (value) {
const h = value.max - value.min;
return Math.floor(value.min - Math.floor(h) * 0.2 - 2);
},
splitNumber: 5
}
],
series: []
};
// 清空标记点
this.markPointList = [];
},
// 打印配置信息,测试用
printOptions() {
console.log(this.targetOption);
},
// 开始画图
startPaint() {
// 初始时间
// this.initTime = new Date().getTime()
// if (!this.initTime) {
// this.initTime = new Date().getTime();
// }
// 是否开始画
this.isStartPaint = true;
console.log('开始绘图');
console.log(this.isStartPaint);
console.log(this.initTime);
},
// 停止画图
stopPaint() {
// 是否开始画
this.isStartPaint = false;
},
// 手动刷新一下
refreshChart() {
if (!this.isStartPaint) {
return;
}
if (this.isHistory) {
return;
}
if (this.checkedKeyList.length === 0) {
return;
}
// console.log("绘图initChart")
this.clearChart();
this.initChart();
}
// 重新设置开始时间
// resetStartTime() {
// this.initTime = new Date().getTime();
// }
},
// 生命周期相关
mounted() {
// this.initTime = new Date().getTime();
window.addEventListener('resize', this.resizeChart);
},
unmounted() {
window.removeEventListener('resize', this.resizeChart);
}
});
</script>
<style lang="less" scoped>
.echart-container {
padding: 10px;
//min-height: 1000px;
//border: 1px solid black;
}
.echart-btns {
display: flex;
justify-content: flex-end;
button {
//width: 270px; /* 宽度 */
//height: 40px; /* 高度 */
padding: 5px 10px;
border-width: 0; /* 边框宽度 */
border-radius: 4px; /* 边框半径 */
//background: #1E90FF; /* 背景颜色 */
cursor: pointer; /* 鼠标移入按钮范围时出现手势 */
outline: none; /* 不显示轮廓线 */
color: black; /* 字体颜色 */
font-size: 14px; /* 字体大小 */
background-image: linear-gradient(180deg, #f2f2f2, #cfcfcf);
margin: 10px 5px 0;
}
}
.echart-btns button {
}
</style>