|
|
|
@ -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, // 使用null而不是0
|
|
|
|
|
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, // 使用null而不是0
|
|
|
|
|
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'
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 针对特定字段,如果业务上它们不应为负,则强制Y轴从0开始
|
|
|
|
|
if (name === '瞬时流量' || name === '压力' || name === '温度') {
|
|
|
|
|
option.yAxis.min = 0;
|
|
|
|
|
} else {
|
|
|
|
|
// 保留之前的逻辑或让Echarts自动计算 (如果其他图表需要不同处理)
|
|
|
|
|
if (dataResult.processedData.some(item => item.isBreakPoint)) {
|
|
|
|
|
option.yAxis.min = 0;
|
|
|
|
|
}
|
|
|
|
|
// 若希望Echarts自动计算其他图表的min,可以不设置,或显式提供回调
|
|
|
|
|
// 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 {
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
// 确保y轴包含0点
|
|
|
|
|
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
|
|
|
|
|