refactor(蒸汽曲线图表展示和数据处理): 优化y轴

- 修改了数据抽样和断点处理逻辑,提高图表性能- 优化了停电数据统计和导出功能
- 调整了图表轴设置和提示格式,提升用户体验
- 过滤了无效和负值数据,确保数据准确性

-当时间范围扩大,数据量增加时,Y轴的范围显示异常,尤其是瞬时流量和压力图表的Y轴刻度以及标题中显示的平均值都变成了非常大的负数,而温度图表则表现正常。
 这强烈暗示,在查询大数据量时,后端返回的原始数据中,针对“瞬时流量”和“压力”这两个字段,可能混入了一些表示极大负值的异常数据点(例如,字符串形式的 "-500000" 或者一个非常大的负数)。而“温度”数据则没有这类异常值。
 我们之前的修改解决了 NaN 值对平均值计算的影响,但如果原始数据本身就是一个有效的负数(即使它在业务上不合理),parseFloat 会正确转换它,isNaN 也不会将其过滤,所以它会参与平均值计算,并可能影响Echarts的Y轴自动范围判断。
 用户您调整的 oneHourMs(停电判断阈值从1小时改为2小时)本身不直接导致这个问题,但它可能会改变“停电”标记点的数量。如果停电标记点变少或没有,Echarts Y轴的最小值设定会更多地依赖其自身的自动计算逻辑 (return value.min)。若此时自动计算的 value.min 因异常数据而变得非常小(即巨大负数),问题就会显现。
 为了解决这个问题,并考虑到这些指标(瞬时流量、压力、温度)在业务上通常不应为负值,我将进行以下两处修改:
 在 processDataBreaks 函数中:修改平均值的计算逻辑。在对 fluxFlow、press、temperature这些字段计算平均值之前,我会过滤掉所有小于0的数值。这样可以确保平均值基于有效的、非负的数据进行计算。
 在 createChartOption 函数中:对于“瞬时流量”、“压力”、“温度”这三个图表,我会将Y轴的最小值 (option.yAxis.min) 强制设置为 0。这样可以确保Y轴从一个合理的基准开始,避免显示不切实际的巨大负范围。
master
zch 1 month ago
parent 6b0ca7a207
commit b5eda717e1

@ -43,13 +43,13 @@
style="width: 340px"
></date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<!-- 图表联动提示 -->
<el-alert
v-if="powerOutageSummary.count > 0"
@ -59,7 +59,7 @@
show-icon
style="margin-bottom: 10px"
/>
<!-- 停电统计信息 -->
<el-card v-if="powerOutageSummary.count > 0" class="power-outage-summary" shadow="hover">
<div slot="header">
@ -74,7 +74,7 @@
<p><strong>总停电时长:</strong> {{ powerOutageSummary.totalDuration }} 小时</p>
</div>
</el-card>
<!-- 图表区域 -->
<div class="charts-container">
<Chart ref="Chart1" class="chart1"/>
@ -296,7 +296,7 @@ export default {
}
}
},
/** 转换计量设备信息数据结构 */
normalizer(node) {
if (node.children && !node.children.length) {
@ -308,7 +308,7 @@ export default {
children: node.children
}
},
/** 查询电实时数据列表 */
getList() {
this.loading = true
@ -363,14 +363,14 @@ export default {
...this.queryParams
}, `recordSteamInstant_${new Date().getTime()}.xlsx`)
},
/** 导出停电数据 */
exportPowerOutageData() {
if (this.powerOutageList.length === 0) {
this.$message.warning('没有停电数据可导出');
return;
}
//
const columns = [
{label: '序号', prop: 'index'},
@ -378,7 +378,7 @@ export default {
{label: '结束时间', prop: 'endTime'},
{label: '持续时间(小时)', prop: 'duration'}
];
let table = '<table border="1" cellspacing="0" cellpadding="5">';
//
table += '<tr>';
@ -386,7 +386,7 @@ export default {
table += `<th>${col.label}</th>`;
});
table += '</tr>';
//
this.powerOutageList.forEach(item => {
table += '<tr>';
@ -396,7 +396,7 @@ export default {
table += '</tr>';
});
table += '</table>';
// HTML
const deviceName = this.selectMonitorName || '设备';
const fileName = `${deviceName}停电记录_${new Date().getTime()}.xls`;
@ -437,21 +437,21 @@ export default {
if (!dataArray || dataArray.length <= this.samplingThreshold) {
return dataArray;
}
//
const interval = Math.ceil(dataArray.length / this.samplingThreshold);
const sampledData = [];
//
for (let i = 0; i < dataArray.length; i += interval) {
sampledData.push(dataArray[i]);
}
//
if (sampledData[sampledData.length - 1] !== dataArray[dataArray.length - 1]) {
sampledData.push(dataArray[dataArray.length - 1]);
}
return sampledData;
},
@ -464,7 +464,7 @@ export default {
processDataBreaks(originalData, valueField) {
//
const sampledData = this.sampleData(originalData);
if (!sampledData || sampledData.length < 2) {
return {
processedData: sampledData || [],
@ -484,7 +484,7 @@ export default {
const markPoints = []
const markAreas = []
const powerOutages = []
const oneHourMs = 60 * 60 * 1000 // 1
const oneHourMs = 2 * 60 * 60 * 1000 // 1
//
for (let i = 0; i < sortedData.length; i++) {
@ -497,32 +497,32 @@ export default {
const nextTime = new Date(sortedData[i + 1].recordTime).getTime()
const timeDiff = nextTime - currentTime
// 1
// 2
if (timeDiff > oneHourMs) {
// - 使null线
//
const breakTime1 = new Date(currentTime + 60000)
const breakTime1Str = parseTime(breakTime1, '{y}-{m}-{d} {h}:{i}:{s}')
const nullPoint1 = {
const nullPoint1 = {
recordTime: breakTime1Str,
[valueField]: null, // 使null0
isBreakPoint: true
}
processedData.push(nullPoint1)
//
const breakTime2 = new Date(nextTime - 60000)
const breakTime2Str = parseTime(breakTime2, '{y}-{m}-{d} {h}:{i}:{s}')
const nullPoint2 = {
const nullPoint2 = {
recordTime: breakTime2Str,
[valueField]: null, // 使null0
isBreakPoint: true
}
processedData.push(nullPoint2)
const endBreakTime = new Date(nextTime - 60000)
const endBreakTimeStr = parseTime(endBreakTime, '{y}-{m}-{d} {h}:{i}:{s}')
//
markPoints.push({
value: '停电',
@ -535,7 +535,7 @@ export default {
color: 'red'
}
})
//
markAreas.push([
{
@ -546,7 +546,7 @@ export default {
xAxis: endBreakTimeStr,
}
])
//
const durationHours = (timeDiff / (1000 * 60 * 60)).toFixed(2);
powerOutages.push({
@ -557,22 +557,28 @@ export default {
}
}
}
//
this.updatePowerOutageSummary(powerOutages);
//
const timeData = processedData.map(e => e.recordTime)
const valueData = processedData.map(e => e[valueField])
//
const validData = processedData.filter(item => !item.isBreakPoint)
const validValues = validData.map(e => parseFloat(e[valueField]))
let validValues = validData.map(e => parseFloat(e[valueField]))
// ( NaN )
if (valueField === 'fluxFlow' || valueField === 'press' || valueField === 'temperature') {
validValues = validValues.filter(v => typeof v === 'number' && v >= 0);
}
// NaN
const validNumericValues = validValues.filter(v => !isNaN(v));
const average = validNumericValues.length > 0 ?
(validNumericValues.reduce((a, b) => a + b, 0) / validNumericValues.length).toFixed(2) : 0
return {
processedData,
timeData,
@ -582,7 +588,7 @@ export default {
average
}
},
/**
* 更新停电统计信息
* @param {Array} powerOutages 停电记录数组
@ -597,24 +603,24 @@ export default {
this.powerOutageList = [];
return;
}
//
let totalDuration = 0;
let longestDuration = 0;
powerOutages.forEach((outage, index) => {
const duration = parseFloat(outage.duration);
totalDuration += duration;
longestDuration = Math.max(longestDuration, duration);
});
//
this.powerOutageSummary = {
count: powerOutages.length,
totalDuration: totalDuration.toFixed(2),
longestDuration: longestDuration.toFixed(2)
};
//
this.powerOutageList = powerOutages.map((outage, index) => ({
index: index + 1,
@ -623,7 +629,7 @@ export default {
duration: outage.duration
}));
},
/**
* 创建图表配置
* @param {String} title 图表标题
@ -636,7 +642,7 @@ export default {
*/
createChartOption(title, dataResult, name, yAxisName, color, tooltipFormatter) {
const option = JSON.parse(JSON.stringify(this.baseChartOptions));
//
option.title = {
text: this.selectMonitorName + ' ' + title + ' (平均值:' + dataResult.average + ")",
@ -645,16 +651,30 @@ export default {
fontSize: 15
}
};
// X
option.xAxis.data = dataResult.timeData;
// Y
option.yAxis.name = yAxisName;
option.yAxis.nameTextStyle = {
color: '#000000'
};
// Y0
if (name === '瞬时流量' || name === '压力' || name === '温度') {
option.yAxis.min = 0;
} else {
// Echarts ()
if (dataResult.processedData.some(item => item.isBreakPoint)) {
option.yAxis.min = 0;
}
// Echartsmin
// else {
// option.yAxis.min = function(value) { return value.min; };
// }
}
//
option.tooltip.formatter = tooltipFormatter || function(params) {
const dataIndex = params[0].dataIndex;
@ -664,7 +684,7 @@ export default {
}
return params[0].seriesName + ': ' + params[0].value + '<br/>时间: ' + params[0].axisValue;
};
//
option.series = [{
name: name,
@ -717,18 +737,7 @@ export default {
}))
}
}];
// y0
if (!option.yAxis.min) {
option.yAxis.min = function(value) {
// 0
if (dataResult.processedData.some(item => item.isBreakPoint)) {
return 0;
}
return value.min;
};
}
return option;
},
@ -748,26 +757,26 @@ export default {
//
const option1 = this.createChartOption(
'瞬时流量',
fluxFlowResult,
'瞬时流量',
'瞬时流量y',
'瞬时流量',
fluxFlowResult,
'瞬时流量',
'瞬时流量y',
'#5470c6'
);
const option2 = this.createChartOption(
'温度',
temperatureResult,
'温度',
'温度y',
'温度',
temperatureResult,
'温度',
'温度y',
'#91cc75'
);
const option3 = this.createChartOption(
'压力',
pressResult,
'压力',
'压力y',
'压力',
pressResult,
'压力',
'压力y',
'#fac858'
);
@ -786,7 +795,7 @@ export default {
option3.dataZoom[0].end = e.end
this.$refs.Chart3.setData(option3)
})
this.$refs.Chart2.chart.on('datazoom', (e) => {
option1.dataZoom[0].start = e.start
option1.dataZoom[0].end = e.end
@ -795,7 +804,7 @@ export default {
option3.dataZoom[0].end = e.end
this.$refs.Chart3.setData(option3)
})
this.$refs.Chart3.chart.on('datazoom', (e) => {
option2.dataZoom[0].start = e.start
option2.dataZoom[0].end = e.end

Loading…
Cancel
Save