Files
pqs-9100_client/frontend/src/views/home/components/compareDataCheckChart.vue
2025-12-31 15:00:20 +08:00

430 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<!-- 历史趋势数据 -->
<div class="history_chart">
<MyEchart ref="historyChart" :options="echartsData"/>
</div>
</template>
<script lang="ts" setup>
import { nextTick, ref, watch } from 'vue'
import { yMethod } from '@/utils/echartMethod'
import MyEchart from '@/components/echarts/line/index.vue'
import { CheckData } from '@/api/check/interface'
const prop = defineProps({
tableData: {
type: Array as () => CheckData.TableRow[],
default: []
},
})
const color = [
'var(--el-color-primary)',
'#07CCCA',
'#00BFF5',
'#FFBF00',
'#77DA63',
'#D5FF6B',
'#Ff6600',
'#FF9100',
'#5B6E96',
'#66FFCC',
'#B3B3B3'
]
const chartsList = ref<any>([])
const echartsData = ref<any>(null)
//初始化趋势图
const lineStyle = [{ type: 'solid' }, { type: 'dashed' }, { type: 'dotted' }]
const setEchart = () => {
echartsData.value = {}
// y轴单位数组
let unitList: any = []
let groupedData = chartsList.value.reduce((acc: any, item: any) => {
let key = ''
if (item.phase == null) {
key = item.unit
} else {
key = item.anotherName
}
if (!acc[key]) {
acc[key] = []
}
acc[key].push(item)
return acc
}, {})
let result = Object.values(groupedData)
if (chartsList.value.length > 0) {
unitList = result.map((item: any) => {
return item[0].unit
})
}
echartsData.value = {
legend: {
itemWidth: 20,
itemHeight: 20,
itemStyle: { opacity: 0 }, //去圆点
type: 'scroll', // 开启滚动分页
// orient: 'vertical', // 垂直排列
top: 5,
right: 70
// width: 550,
// height: 50
},
grid: {
top: '30px',
},
tooltip: {
axisPointer: {
type: 'cross',
label: {
color: '#fff',
fontSize: 16
}
},
textStyle: {
color: '#fff',
fontStyle: 'normal',
opacity: 0.35,
fontSize: 14
},
backgroundColor: 'rgba(0,0,0,0.55)',
borderWidth: 0,
formatter(params: any) {
const xname = params[0].value[0]
let str = `${xname}<br>`
params.forEach((el: any, index: any) => {
let marker = ''
if (el.value[3] == 'dashed') {
for (let i = 0; i < 3; i++) {
marker += `<span style="display:inline-block;border: 2px ${el.color} solid;margin-right:5px;width:10px;height:0px;background-color:#ffffff00;"></span>`
}
} else {
marker = `<span style="display:inline-block;border: 2px ${el.color} ${el.value[3]};margin-right:5px;width:40px;height:0px;background-color:#ffffff00;"></span>`
}
let unit = el.value[2] ? el.value[2] : ''
// 格式化数值显示为4位小数
const value = parseFloat(el.value[1]);
const formattedValue = value.toFixed(4);
str += `${marker}${el.seriesName.split('(')[0]}${formattedValue}${unit}<br>`
})
return str
}
},
color: ['#DAA520', '#2E8B57', '#A52a2a', ...color],
xAxis: {
type: 'time',
axisLabel: {
formatter: function(value) {
const date = new Date(value);
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
return `${hours}:${minutes}:${seconds}`;
},
},
splitNumber: 8
},
yAxis: [{}],
options: {
series: []
}
}
if (chartsList.value.length > 0) {
let yData: any = []
echartsData.value.yAxis = []
let setList = [...new Set(unitList)]
setList.forEach((item: any, index: any) => {
if (index > 2) {
echartsData.value.grid.right = (index - 1) * 80
}
yData.push([])
let right = {
position: 'right',
offset: (index - 1) * 80
}
echartsData.value.yAxis.push({
name: item,
yAxisIndex: index,
splitNumber: 5,
minInterval: 0,
splitLine: {
show: false
},
axisLabel: {
// 添加标签格式化,支持小数显示
formatter: function(value) {
return value.toFixed(5);
}
},
...(index > 0 ? right : null)
})
})
let ABCName = [
...new Set(
chartsList.value.map((item: any) => {
return item.anotherName == '电压负序分量'
? '电压不平衡'
: item.anotherName == '电压正序分量'
? '电压不平衡'
: item.anotherName == '电压零序分量'
? '电压不平衡'
: item.anotherName
})
)
]
result.forEach((item: any, index: any) => {
let yMethodList: any = []
let ABCList = Object.values(
item.reduce((acc, item) => {
let key = ''
if (item.phase == null) {
key = item.anotherName
} else {
key = item.phase
}
if (!acc[key]) {
acc[key] = []
}
acc[key].push(item)
return acc
}, {})
)
ABCList.forEach((kk: any) => {
let colorName = kk[0].phase?.charAt(0).toUpperCase()
let lineS = ABCName.findIndex(
item =>
item ===
(kk[0].anotherName == '电压负序分量'
? '电压不平衡'
: kk[0].anotherName == '电压正序分量'
? '电压不平衡'
: kk[0].anotherName == '电压零序分量'
? '电压不平衡'
: kk[0].anotherName)
)
let seriesList: any = []
kk.forEach((cc: any) => {
if (cc.statisticalData !== null) {
yData[setList.indexOf(kk[0].unit)].push(cc.statisticalData?.toFixed(4))
}
seriesList.push([cc.time, cc.statisticalData, cc.unit, lineStyle[lineS].type])
})
echartsData.value.options.series.push({
name: kk[0].phase ? kk[0].phase + '相' + kk[0].anotherName : kk[0].anotherName,
type: 'line',
smooth: true,
color:
colorName == 'A' ? '#DAA520' : colorName == 'B' ? '#2E8B57' : colorName == 'C' ? '#A52a2a' : '#DAA520',
symbol: 'none',
data: seriesList,
lineStyle: lineStyle[lineS],
yAxisIndex: setList.indexOf(kk[0].unit)
})
})
})
// yData.forEach((item: any, index: any) => {
// let [min, max] = yMethod(item)
// echartsData.value.yAxis[index].min = min
// echartsData.value.yAxis[index].max = max
// })
let allValues: number[] = [];
yData.forEach(item => {
item.forEach((val: string) => {
allValues.push(val);
});
});
// 计算全局最大最小值
let globalMin = Math.min(...allValues) - 0.0001;
let globalMax = Math.max(...allValues) +0.0001
// 确保最小值不小于0
globalMin = Math.max(0, globalMin);
// 特殊情况如果所有值都是0
if (globalMin === 0 && globalMax === 0) {
globalMax = 1;
}
// 为所有Y轴应用相同的范围
yData.forEach((item: any, index: any) => {
echartsData.value.yAxis[index].min = globalMin ;
echartsData.value.yAxis[index].max = globalMax;
});
}
}
// 监听 tableData 变化并触发 setEchart
watch(() => prop.tableData, (newTableData) => {
// 处理数据转换
const processedData: any[] = []
newTableData.forEach(item => {
// 处理标准设备数据
processDeviceData(item, '标准设备', item.uaStdDev, item.ubStdDev, item.ucStdDev, item.utStdDev, item.timeStdDev, item.unit)
.forEach(data => processedData.push(data));
// 处理被检设备数据
processDeviceData(item, '被检设备', item.uaDev, item.ubDev, item.ucDev, item.utDev, item.timeDev, item.unit)
.forEach(data => processedData.push(data));
});
// 更新 chartsList 数据
chartsList.value = processedData
// 延迟执行确保 DOM 已经渲染
nextTick(() => {
setTimeout(() => {
setEchart()
}, 100)
})
}, {
immediate: true, // 立即执行一次
deep: true // 深度监听
})
// 处理单个设备的数据
function processDeviceData(
item: any,
deviceType: string,
aValue: number | null,
bValue: number | null,
cValue: number | null,
tValue: number | null,
time: string,
unit: string
): any[] {
const result: any[] = [];
// 判断各相是否存在有效数据
const hasA = aValue !== null;
const hasB = bValue !== null;
const hasC = cValue !== null;
const hasT = tValue !== null;
// 计算有多少相有数据
const phaseCount = (hasA ? 1 : 0) + (hasB ? 1 : 0) + (hasC ? 1 : 0);
// 时间四舍五入到秒
const roundedTime = roundTimeToSecond(time);
if (hasA) {
result.push({
anotherName: deviceType,
phase: phaseCount > 1 ? 'A' : null,
statisticalData: aValue,
time: roundedTime,
unit: unit,
});
}
if (hasB) {
result.push({
anotherName: deviceType,
phase: phaseCount > 1 ? 'B' : null,
statisticalData: bValue,
time: roundedTime,
unit: unit,
});
}
if (hasC) {
result.push({
anotherName: deviceType,
phase: phaseCount > 1 ? 'C' : null,
statisticalData: cValue,
time: roundedTime,
unit: unit,
});
}
if (hasT) {
result.push({
anotherName: deviceType,
phase: null,
statisticalData: tValue,
time: roundedTime,
unit: unit,
});
}
return result;
}
// 时间四舍五入到秒的辅助函数
function roundTimeToSecond(timeStr: string): string {
if (!timeStr) return timeStr;
try {
// 直接使用本地时间解析,避免时区转换问题
// 替换空格为 T 以符合 ISO 格式
const isoString = timeStr.replace(' ', 'T');
const date = new Date(isoString);
// 检查日期是否有效
if (isNaN(date.getTime())) {
return timeStr;
}
// 获取毫秒部分
const milliseconds = date.getMilliseconds();
// 如果毫秒数大于等于500则加一秒
if (milliseconds >= 500) {
date.setSeconds(date.getSeconds() + 1);
}
// 清除毫秒部分
date.setMilliseconds(0);
// 手动格式化为 YYYY-MM-DD HH:mm:ss 格式(使用本地时间)
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
const formattedTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
return formattedTime;
} catch (error) {
// 如果解析失败,返回原始时间字符串
console.error('时间解析错误:', error);
return timeStr;
}
}
</script>
<style lang="scss" scoped>
.history_chart {
width: 100%;
height: 360px; /* 明确指定高度 */
margin-top: 0px;
}
/* 或者设置最小高度 */
.history_chart {
width: 100%;
min-height: 300px; /* 确保有最小高度 */
margin-top: 10px;
}
</style>