You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
241 lines
5.1 KiB
241 lines
5.1 KiB
<template> |
|
<div class="device-data-chart"> |
|
<v-chart |
|
class="chart" |
|
ref="chartRef" |
|
:option="chartOption" |
|
:autoresize="autoresize" |
|
:loading-options="loadingOpt" |
|
:loading="loading" |
|
@legendselectchanged="changeLegend"/> |
|
</div> |
|
</template> |
|
|
|
<script setup lang="ts"> |
|
import VChart from 'vue-echarts' |
|
import { use } from 'echarts/core' |
|
import { CanvasRenderer } from 'echarts/renderers' |
|
import type { EChartsOption, SeriesOption } from 'echarts/types/dist/shared.js' |
|
import { LineChart } from 'echarts/charts' |
|
import { |
|
TooltipComponent, |
|
LegendComponent, |
|
DataZoomComponent, |
|
GridComponent, |
|
} from 'echarts/components' |
|
|
|
use([ |
|
CanvasRenderer, |
|
TooltipComponent, |
|
LineChart, |
|
LegendComponent, |
|
GridComponent, |
|
DataZoomComponent, |
|
]) |
|
const ZOOM_HEIGHT = 30 |
|
const ZOOM_BOTTOM = 10 |
|
|
|
type Legend = { |
|
addr: string |
|
label: string |
|
unit?: string |
|
} |
|
|
|
const autoresize = { |
|
throttle: 0, |
|
} |
|
|
|
const props = defineProps({ |
|
title: String, |
|
smooth: Boolean, //当前是曲线图还是阶梯图 |
|
legends: { |
|
type: Array as PropType<Legend[]>, |
|
default: () => [], |
|
}, |
|
axisData: { |
|
type: Array as PropType<string[]>, |
|
default: () => [], |
|
}, |
|
chartDatas: { |
|
type: Object as PropType<Map<string, any[]>>, |
|
default: () => new Map(), |
|
}, |
|
}) |
|
|
|
const loading = ref(true) |
|
const loadingOpt = { |
|
type: 'default', |
|
text: '暂无数据', |
|
color: '#c23531', |
|
textColor: '#666', |
|
maskColor: 'rgba(0, 0, 0, 0)', |
|
showSpinner: false, |
|
} |
|
|
|
const chartRef = ref() |
|
|
|
const chartOption = computed<EChartsOption>(() => { |
|
if (props.chartDatas.size === 0) { |
|
return {} |
|
} |
|
loading.value = false |
|
const tmpSeries: SeriesOption[] = [] |
|
for (const legend of props.legends.filter(item => item.addr !== 'ts')) { |
|
const entry = props.chartDatas.get(legend.addr) |
|
const lineData: SeriesOption = { |
|
name: legend.label, |
|
type: 'line', |
|
id: legend.addr, |
|
// symbol: "none", |
|
connectNulls: true, |
|
data: entry, |
|
} |
|
if (props.smooth) { |
|
lineData.smooth = true |
|
} else { |
|
lineData.step = 'middle' |
|
} |
|
tmpSeries.push(lineData) |
|
} |
|
|
|
const option: EChartsOption = { |
|
grid: { |
|
left: 60, |
|
right: 198, |
|
top: '5%', |
|
bottom: `25%`, |
|
}, |
|
tooltip: { |
|
trigger: 'axis', |
|
confine: true, |
|
appendToBody: true, |
|
// formatter: function (params: any) { |
|
// var relVal = params[0].name |
|
// |
|
// for (var i = 0, l = params.length; i < l; i++) { |
|
// relVal += `<div style="display: flex; justify-content: space-between; gap: 30px;"> |
|
// <div>${params[i].marker}${params[i].seriesName}:</div> <div">${params[i].value[1]}${props.legends.find(item => item.addr === params[i].seriesId)?.unit || ''}</div> |
|
// </div>` |
|
// } |
|
// return relVal |
|
// } |
|
}, |
|
legend: { |
|
type: 'scroll', |
|
orient: 'vertical', |
|
formatter: name => { |
|
return name.length > 15 ? name.substring(0, 15) + '...' : name |
|
}, |
|
tooltip: { |
|
show: true, |
|
}, |
|
right: 0, |
|
top: 20, |
|
itemWidth: 10, |
|
itemHeight: 10, |
|
pageIconSize: 12, |
|
data: props.legends.map(item => item.label), |
|
selected: unCheckArr.value.reduce((acc, cur) => { |
|
acc[cur] = false |
|
return acc |
|
}, {} as Record<string, boolean>), |
|
}, |
|
xAxis: { |
|
type: 'time', |
|
axisLine: { |
|
//y轴线的颜色以及宽度 |
|
show: true, |
|
lineStyle: { |
|
width: 1, |
|
type: 'solid', |
|
}, |
|
}, |
|
axisLabel: { |
|
show: false, |
|
} |
|
}, |
|
yAxis: { |
|
type: 'value', |
|
splitNumber: 6, |
|
axisLine: { |
|
//y轴线的颜色以及宽度 |
|
show: true, |
|
lineStyle: { |
|
width: 1, |
|
type: 'solid', |
|
}, |
|
}, |
|
axisLabel: { |
|
margin: 12, |
|
fontSize: 12, |
|
}, |
|
splitLine: { |
|
show: true, |
|
lineStyle: {}, |
|
}, |
|
}, |
|
dataZoom: [ |
|
{ |
|
type: 'inside', |
|
xAxisIndex: 0, |
|
filterMode: 'filter', |
|
start: 90, |
|
end: 100, |
|
}, |
|
{ |
|
type: 'slider', |
|
xAxisIndex: 0, |
|
filterMode: 'filter', |
|
start: 90, |
|
end: 100, |
|
height: ZOOM_HEIGHT, |
|
bottom: `20%`, |
|
borderColor: '#88abf5', |
|
dataBackground: { |
|
lineStyle: { |
|
color: '#d2dbee', |
|
}, |
|
}, |
|
selectedDataBackground: { |
|
lineStyle: { |
|
color: '#2c6cf7', |
|
}, |
|
}, |
|
moveHandleStyle: { |
|
color: '#2c6cf7', |
|
}, |
|
}, |
|
], |
|
series: tmpSeries, |
|
} |
|
|
|
return option |
|
}) |
|
|
|
const unCheckArr = ref<string[]>([]) |
|
|
|
function changeLegend(data: { name: string; selected: Record<string, boolean> }) { |
|
const { name, selected } = data |
|
if (!selected[name]) { |
|
unCheckArr.value.push(name) |
|
} else { |
|
unCheckArr.value = unCheckArr.value.filter(item => item !== name) |
|
} |
|
} |
|
</script> |
|
|
|
<style scoped lang="scss"> |
|
.device-data-chart { |
|
width: 100%; |
|
height: 100%; |
|
display: flex; |
|
justify-content: center; |
|
align-items: center; |
|
|
|
.chart { |
|
width: 100%; |
|
height: 90%; |
|
min-height: 100px; |
|
} |
|
} |
|
</style>
|
|
|