<script setup>
|
import { onMounted, ref, reactive, watchEffect, computed, watch, nextTick, onBeforeUnmount } from "vue";
|
import * as echarts from 'echarts';
|
import baseChart from "./BaseChart.vue";
|
|
const chart = ref(null);
|
const props = defineProps({
|
type: {
|
type: String,
|
default: '电流'
|
},
|
yLabels: {
|
type: Array,
|
default: () => []
|
},
|
startIdx: {
|
type: [Number, String],
|
default: 0
|
},
|
modeCount: {
|
type: [Number, String],
|
default: 4
|
},
|
title: {
|
type: String,
|
default: ''
|
},
|
unit: {
|
type: String,
|
default: ''
|
}
|
});
|
|
const fullFlag = ref(false);
|
|
// 这两个数值是百分比
|
const baseTop = 10;
|
let gridHeight = 20;
|
|
let gridCount = computed(() => {
|
return props.yLabels.length || 1;
|
});
|
|
const emit = defineEmits(['scroll']);
|
|
|
function makeXAxis(gridIndex, opt) {
|
let res = echarts.util.merge({
|
type: 'category',
|
boundaryGap: false,
|
gridIndex: gridIndex,
|
axisLine: { onZero: false, lineStyle: { color: '#ddd' } },
|
axisTick: { show: false },
|
axisLabel: { show: false },
|
splitLine: { show: false, lineStyle: { color: '#ddd' } },
|
}, opt || {}, true);
|
return JSON.parse(JSON.stringify(res));
|
}
|
|
function makeYAxis(gridIndex, opt) {
|
let res = echarts.util.merge({
|
type: 'value',
|
gridIndex: gridIndex,
|
nameLocation: 'middle',
|
nameTextStyle: {
|
color: '#fff',
|
lineHeight: 18
|
},
|
nameRotate: 0,
|
boundaryGap: ['30%', '30%'],
|
axisTick: { show: false },
|
axisLine: { lineStyle: { color: '#ccc' } },
|
axisLabel: { show: false },
|
splitLine: { show: false }
|
}, opt || {}, true);
|
return JSON.parse(JSON.stringify(res));
|
}
|
|
|
function makeGrid(top, opt) {
|
let res = echarts.util.merge({
|
top: top + '%',
|
height: gridHeight - 0.5 + '%',
|
bottom: top + gridHeight - 0.5 + '%',
|
left: 90,
|
right: 14,
|
}, opt || {}, true);
|
return JSON.parse(JSON.stringify(res));
|
}
|
|
|
function getSeries(datas) {
|
let res = [];
|
|
datas.forEach((item, idx) => {
|
res.push({
|
type: 'line',
|
name: props.yLabels[idx],
|
smooth: true,
|
symbol: 'circle',
|
symbolSize: 5,
|
showSymbol: true,
|
xAxisIndex: idx,
|
yAxisIndex: idx,
|
gridIndex: idx,
|
seriesIndex: idx,
|
// 禁用动画,确保立即渲染
|
animation: false,
|
// silent: true,
|
lineStyle: {
|
width: 1
|
},
|
tooltip: {
|
show: true,
|
},
|
data: datas[idx].map(v => v + 3),
|
});
|
});
|
|
// return JSON.parse(JSON.stringify(res));
|
return res;
|
}
|
|
let lastXLabels = [];
|
let lastDatas = [];
|
|
watch(
|
() => props.yLabels,
|
() => {
|
nextTick(() => {
|
chart.value.setOption(getOptions(lastXLabels, lastDatas));
|
});
|
},
|
);
|
|
|
function getOptions(xLabels, datas) {
|
xLabels = xLabels || [];
|
datas = datas || [];
|
|
lastXLabels = xLabels;
|
lastDatas = datas;
|
|
|
let series = getSeries(datas);
|
|
let grid = [];
|
let xAxis = [];
|
let yAxis = [];
|
let counter = gridCount.value;
|
gridHeight = (100 - 20) / counter;
|
|
for (let i = 0; i < counter; i++) {
|
grid.push(makeGrid(baseTop + gridHeight * i));
|
let xAxisOption = i == counter - 1 ? {
|
data: xLabels,
|
axisTick: { show: true },
|
axisLabel: { show: true },
|
axisLine: {
|
lineStyle: {
|
color: '#fff'
|
}
|
},
|
} : { axisLine: { show: false }};
|
xAxis.push(makeXAxis(i, xAxisOption));
|
yAxis.push(makeYAxis(i, {
|
name: props.yLabels.length ? props.yLabels[i] : '',
|
}));
|
}
|
|
const option = {
|
animation: false,
|
color: ['#1186ce', '#e5c619', '#1d1dfd', '#2dbfae'],
|
tooltip: {
|
trigger: 'axis',
|
// 强制显示所有系列
|
alwaysShowContent: true,
|
axisPointer: {
|
snap: true,
|
lineStyle: {
|
color: '#fff'
|
}
|
},
|
formatter: function (params) {
|
// console.log('params:', params);
|
// console.log('捕获的seriesIndex:', params.map(p => p.seriesIndex));
|
if (params.length) {
|
// params.unshift({ seriesName: 'time', value: params[0].name, color: '#5193f2' })
|
let _label = '';
|
let res = props.yLabels.map((seriesName, idx) => {
|
for (var i = 0; i < params.length; i++) {
|
var param = params[i];
|
var style = 'color: ' + param.color;
|
if (param.seriesIndex === idx) {
|
_label = param.name;
|
return '<span style="' + style + '">'
|
+ seriesName + props.type
|
+ ':</span><span style="'
|
+ style + '">' + param.value + '</span>';
|
}
|
}
|
});
|
res.push('<span style="color: #000">' + _label + '</span>');
|
res = res.join('<br>');
|
// console.log('res', res, params, '=====tooltip========', series, datas, xLabels[xLabels.length - 1]);
|
return res;
|
|
}
|
}
|
},
|
axisPointer: {
|
link: [{ xAxisIndex: 'all' }],
|
lineStyle: {
|
color: '#fff'
|
},
|
snap: true
|
},
|
grid,
|
xAxis,
|
yAxis,
|
series
|
};
|
|
// console.log('option', option, '=======dcout======');
|
|
return option;
|
}
|
|
let myChart = null;
|
const startIndex = computed(() => {
|
return Math.floor(props.startIdx);
|
});
|
|
function initChart() {
|
if (chart.value) {
|
// myChart = chart.value.getChart();
|
|
let option = getOptions();
|
chart.value.setOption(option);
|
}
|
}
|
|
function updateChart(xLabels, datas) {
|
if (chart.value) {
|
let option = getOptions(xLabels, datas);
|
// chart.value.setOption(option, {notMerge: true});
|
chart.value.setOption(option);
|
|
// tooltip Bug 重新渲染了一下 就没问题了
|
if (props.startIdx == 0) {
|
nextTick(() => {
|
emit('scroll', 0.1);
|
});
|
}
|
}
|
}
|
|
function scrollToTop() {
|
if (startIndex.value > 0) {
|
emit('scroll', -1);
|
}
|
|
}
|
|
function scrollToBottom() {
|
if (startIndex.value < props.modeCount - 4) {
|
nextTick(() => {
|
emit('scroll', 1);
|
});
|
}
|
}
|
|
defineExpose({
|
updateChart
|
});
|
|
onMounted(() => {
|
// console.log('line mounted', '=============');
|
|
initChart();
|
});
|
</script>
|
|
<template>
|
<div class="line-chart">
|
<base-chart ref="chart" v-model:fullFlag="fullFlag" :key="startIdx">
|
<template #tools v-if="modeCount > 4">
|
<div class="t-contain">
|
<div :class="['btn', 'btn-top', {disabled: startIndex <= 0}]" @click="scrollToTop">
|
<svg-icon icon-class="arrow-right" ></svg-icon>
|
</div>
|
<div :class="['btn', 'btn-bottom', {disabled: startIndex == modeCount - 4}]" @click="scrollToBottom">
|
<svg-icon icon-class="arrow-right" ></svg-icon>
|
</div>
|
</div>
|
</template>
|
</base-chart>
|
</div>
|
</template>
|
|
<style scoped lang="less">
|
.line-chart {
|
width: 100%;
|
height: 100%;
|
|
.t-contain {
|
position: absolute;
|
right: 4px;
|
top: 0;
|
bottom: 0;
|
padding-top: 40px;
|
padding-bottom: 40px;
|
display: flex;
|
flex-direction: column;
|
justify-content: space-between;
|
|
|
.btn {
|
cursor: pointer;
|
font-size: 14px;
|
&:hover {
|
color: #0ff;
|
}
|
&.disabled {
|
cursor: not-allowed;
|
color: #666;
|
}
|
&.btn-top {
|
transform: rotate(-90deg);
|
}
|
&.btn-bottom {
|
transform: rotate(90deg);
|
}
|
}
|
}
|
}
|
</style>
|