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

Loading…
Cancel
Save