|
|
@ -29,6 +29,22 @@
|
|
|
|
}}
|
|
|
|
}}
|
|
|
|
</span>
|
|
|
|
</span>
|
|
|
|
</el-tooltip>
|
|
|
|
</el-tooltip>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- WebSocket连接状态指示器 -->
|
|
|
|
|
|
|
|
<el-tooltip :content="websocketStatusText" effect="dark" placement="bottom">
|
|
|
|
|
|
|
|
<span class="websocket-status right-menu-item">
|
|
|
|
|
|
|
|
<i :class="websocketStatusIcon" :style="{ color: websocketStatusColor }"></i>
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</el-tooltip>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 告警队列状态指示器 -->
|
|
|
|
|
|
|
|
<el-tooltip :content="alarmQueueStatusText" effect="dark" placement="bottom">
|
|
|
|
|
|
|
|
<span class="alarm-queue-status right-menu-item" @click="showQueueStatus">
|
|
|
|
|
|
|
|
<i class="el-icon-collection" :style="{ color: queueStatusColor }"></i>
|
|
|
|
|
|
|
|
<span v-if="alarmQueueLength > 0" class="queue-count">{{ alarmQueueLength }}</span>
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</el-tooltip>
|
|
|
|
|
|
|
|
|
|
|
|
<screenfull id="screenfull" class="right-menu-item hover-effect"/>
|
|
|
|
<screenfull id="screenfull" class="right-menu-item hover-effect"/>
|
|
|
|
|
|
|
|
|
|
|
|
<el-tooltip content="布局大小" effect="dark" placement="bottom">
|
|
|
|
<el-tooltip content="布局大小" effect="dark" placement="bottom">
|
|
|
@ -397,22 +413,53 @@ export default {
|
|
|
|
// WebSocket告警相关
|
|
|
|
// WebSocket告警相关
|
|
|
|
realtimeAlarmDialog: false,
|
|
|
|
realtimeAlarmDialog: false,
|
|
|
|
currentRealtimeAlarm: null,
|
|
|
|
currentRealtimeAlarm: null,
|
|
|
|
alarmProcessing: false
|
|
|
|
alarmProcessing: false,
|
|
|
|
|
|
|
|
currentAlarmId: null,
|
|
|
|
|
|
|
|
alarmProcessedCallback: null,
|
|
|
|
|
|
|
|
alarmTimeoutCallback: null,
|
|
|
|
|
|
|
|
websocketStatusText: '',
|
|
|
|
|
|
|
|
websocketStatusIcon: '',
|
|
|
|
|
|
|
|
websocketStatusColor: '',
|
|
|
|
|
|
|
|
alarmQueueStatusText: '',
|
|
|
|
|
|
|
|
queueStatusColor: '',
|
|
|
|
|
|
|
|
alarmQueueLength: 0,
|
|
|
|
|
|
|
|
queueStatusTimer: null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
created() {
|
|
|
|
created() {
|
|
|
|
localStorage.setItem('this.alarmDataTotal', 0)
|
|
|
|
localStorage.setItem('this.alarmDataTotal', 0)
|
|
|
|
|
|
|
|
|
|
|
|
// 监听WebSocket告警事件
|
|
|
|
// 监听新的队列化WebSocket告警事件
|
|
|
|
this.$bus.$on('websocket-alarm', this.handleRealtimeAlarm)
|
|
|
|
this.$bus.$on('websocket-alarm-with-callback', this.handleQueuedRealtimeAlarm)
|
|
|
|
|
|
|
|
// 监听自动超时保存事件
|
|
|
|
|
|
|
|
this.$bus.$on('websocket-alarm-auto-save', this.handleAutoSaveAlarm)
|
|
|
|
|
|
|
|
// 监听WebSocket连接状态变化
|
|
|
|
|
|
|
|
this.$bus.$on('websocket-connected', this.onWebSocketConnected)
|
|
|
|
|
|
|
|
this.$bus.$on('websocket-disconnected', this.onWebSocketDisconnected)
|
|
|
|
|
|
|
|
this.$bus.$on('websocket-max-retries-reached', this.onWebSocketMaxRetriesReached)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
beforeDestroy() {
|
|
|
|
beforeDestroy() {
|
|
|
|
// 移除事件监听
|
|
|
|
// 移除所有事件监听
|
|
|
|
this.$bus.$off('websocket-alarm', this.handleRealtimeAlarm)
|
|
|
|
this.$bus.$off('websocket-alarm-with-callback', this.handleQueuedRealtimeAlarm)
|
|
|
|
|
|
|
|
this.$bus.$off('websocket-alarm-auto-save', this.handleAutoSaveAlarm)
|
|
|
|
|
|
|
|
this.$bus.$off('websocket-connected', this.onWebSocketConnected)
|
|
|
|
|
|
|
|
this.$bus.$off('websocket-disconnected', this.onWebSocketDisconnected)
|
|
|
|
|
|
|
|
this.$bus.$off('websocket-max-retries-reached', this.onWebSocketMaxRetriesReached)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 清理定时器
|
|
|
|
|
|
|
|
if (this.queueStatusTimer) {
|
|
|
|
|
|
|
|
clearInterval(this.queueStatusTimer)
|
|
|
|
|
|
|
|
this.queueStatusTimer = null
|
|
|
|
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
mounted() {
|
|
|
|
mounted() {
|
|
|
|
// 初始获取告警数据,后续完全依赖WebSocket推送
|
|
|
|
// 初始获取告警数据,后续完全依赖WebSocket推送
|
|
|
|
this.getAlarmData()
|
|
|
|
this.getAlarmData()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 定期更新告警队列状态
|
|
|
|
|
|
|
|
this.queueStatusTimer = setInterval(() => {
|
|
|
|
|
|
|
|
this.updateQueueStatus()
|
|
|
|
|
|
|
|
}, 2000) // 每2秒更新一次状态
|
|
|
|
},
|
|
|
|
},
|
|
|
|
components: {
|
|
|
|
components: {
|
|
|
|
Breadcrumb,
|
|
|
|
Breadcrumb,
|
|
|
@ -558,17 +605,25 @@ export default {
|
|
|
|
const baseURL = process.env.VUE_APP_BASE_API || '';
|
|
|
|
const baseURL = process.env.VUE_APP_BASE_API || '';
|
|
|
|
return baseURL + relativePath;
|
|
|
|
return baseURL + relativePath;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// 处理WebSocket实时告警
|
|
|
|
// 处理队列化的WebSocket实时告警
|
|
|
|
handleRealtimeAlarm(alarmData) {
|
|
|
|
handleQueuedRealtimeAlarm(data) {
|
|
|
|
console.log('收到实时告警:', alarmData)
|
|
|
|
console.log('收到队列化实时告警:', data)
|
|
|
|
this.currentRealtimeAlarm = alarmData
|
|
|
|
|
|
|
|
|
|
|
|
// 解构获取告警数据和回调函数
|
|
|
|
|
|
|
|
const { alarm, onProcessed, onTimeout } = data
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.currentRealtimeAlarm = alarm
|
|
|
|
|
|
|
|
this.currentAlarmId = alarm.id
|
|
|
|
|
|
|
|
this.alarmProcessedCallback = onProcessed
|
|
|
|
|
|
|
|
this.alarmTimeoutCallback = onTimeout
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 显示告警弹窗
|
|
|
|
this.realtimeAlarmDialog = true
|
|
|
|
this.realtimeAlarmDialog = true
|
|
|
|
|
|
|
|
|
|
|
|
// 同时播放提示音(可选)
|
|
|
|
// 播放提示音
|
|
|
|
this.playAlarmSound()
|
|
|
|
this.playAlarmSound()
|
|
|
|
|
|
|
|
|
|
|
|
// 注意:这里不立即保存,等用户操作后再保存
|
|
|
|
console.log('告警弹窗已显示,告警ID:', alarm.id, '优先级:', alarm.priority)
|
|
|
|
// this.saveRealtimeAlarmData(alarmData)
|
|
|
|
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// 播放告警提示音
|
|
|
|
// 播放告警提示音
|
|
|
|
playAlarmSound() {
|
|
|
|
playAlarmSound() {
|
|
|
@ -709,6 +764,11 @@ export default {
|
|
|
|
const success = await this.saveRealtimeAlarmData(this.currentRealtimeAlarm, 1)
|
|
|
|
const success = await this.saveRealtimeAlarmData(this.currentRealtimeAlarm, 1)
|
|
|
|
if (success) {
|
|
|
|
if (success) {
|
|
|
|
this.$message.info('告警已记录,状态为未处理')
|
|
|
|
this.$message.info('告警已记录,状态为未处理')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 调用回调函数通知App.vue处理完成
|
|
|
|
|
|
|
|
if (this.alarmProcessedCallback) {
|
|
|
|
|
|
|
|
this.alarmProcessedCallback(this.currentAlarmId, 'later')
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
} catch (error) {
|
|
|
|
console.error('保存告警数据失败:', error)
|
|
|
|
console.error('保存告警数据失败:', error)
|
|
|
@ -718,8 +778,7 @@ export default {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.realtimeAlarmDialog = false
|
|
|
|
this.closeAlarmDialog()
|
|
|
|
this.currentRealtimeAlarm = null
|
|
|
|
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 确认知晓实时告警(保存为已处理状态)
|
|
|
|
// 确认知晓实时告警(保存为已处理状态)
|
|
|
@ -730,11 +789,15 @@ export default {
|
|
|
|
const success = await this.saveRealtimeAlarmData(this.currentRealtimeAlarm, 0)
|
|
|
|
const success = await this.saveRealtimeAlarmData(this.currentRealtimeAlarm, 0)
|
|
|
|
if (success) {
|
|
|
|
if (success) {
|
|
|
|
this.$message.success('告警已确认知晓并标记为已处理')
|
|
|
|
this.$message.success('告警已确认知晓并标记为已处理')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 调用回调函数通知App.vue处理完成
|
|
|
|
|
|
|
|
if (this.alarmProcessedCallback) {
|
|
|
|
|
|
|
|
this.alarmProcessedCallback(this.currentAlarmId, 'processed')
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 关闭弹窗
|
|
|
|
// 关闭弹窗
|
|
|
|
this.realtimeAlarmDialog = false
|
|
|
|
this.closeAlarmDialog()
|
|
|
|
this.currentRealtimeAlarm = null
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
} catch (error) {
|
|
|
|
console.error('处理告警失败:', error)
|
|
|
|
console.error('处理告警失败:', error)
|
|
|
|
this.$message.error('处理告警失败')
|
|
|
|
this.$message.error('处理告警失败')
|
|
|
@ -742,6 +805,14 @@ export default {
|
|
|
|
this.alarmProcessing = false
|
|
|
|
this.alarmProcessing = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
// 关闭告警弹窗
|
|
|
|
|
|
|
|
closeAlarmDialog() {
|
|
|
|
|
|
|
|
this.realtimeAlarmDialog = false
|
|
|
|
|
|
|
|
this.currentRealtimeAlarm = null
|
|
|
|
|
|
|
|
this.currentAlarmId = null
|
|
|
|
|
|
|
|
this.alarmProcessedCallback = null
|
|
|
|
|
|
|
|
this.alarmTimeoutCallback = null
|
|
|
|
|
|
|
|
},
|
|
|
|
// 格式化告警时间
|
|
|
|
// 格式化告警时间
|
|
|
|
formatAlarmTime(time) {
|
|
|
|
formatAlarmTime(time) {
|
|
|
|
if (!time) return '--'
|
|
|
|
if (!time) return '--'
|
|
|
@ -766,6 +837,95 @@ export default {
|
|
|
|
8: '气体浓度'
|
|
|
|
8: '气体浓度'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return fieldMap[fieldCode] || '未知字段'
|
|
|
|
return fieldMap[fieldCode] || '未知字段'
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
// 处理WebSocket连接状态变化
|
|
|
|
|
|
|
|
onWebSocketConnected() {
|
|
|
|
|
|
|
|
console.log('WebSocket连接成功')
|
|
|
|
|
|
|
|
this.websocketStatusText = 'WebSocket连接成功'
|
|
|
|
|
|
|
|
this.websocketStatusIcon = 'el-icon-success'
|
|
|
|
|
|
|
|
this.websocketStatusColor = '#67C23A'
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
onWebSocketDisconnected() {
|
|
|
|
|
|
|
|
console.log('WebSocket连接断开')
|
|
|
|
|
|
|
|
this.websocketStatusText = 'WebSocket连接断开'
|
|
|
|
|
|
|
|
this.websocketStatusIcon = 'el-icon-error'
|
|
|
|
|
|
|
|
this.websocketStatusColor = '#F56C6C'
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
onWebSocketMaxRetriesReached() {
|
|
|
|
|
|
|
|
console.log('WebSocket达到最大重试次数')
|
|
|
|
|
|
|
|
this.websocketStatusText = 'WebSocket达到最大重试次数'
|
|
|
|
|
|
|
|
this.websocketStatusIcon = 'el-icon-warning'
|
|
|
|
|
|
|
|
this.websocketStatusColor = '#E6A23C'
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
// 处理WebSocket自动超时保存事件
|
|
|
|
|
|
|
|
handleAutoSaveAlarm(data) {
|
|
|
|
|
|
|
|
console.log('收到自动超时保存事件:', data)
|
|
|
|
|
|
|
|
const { alarm, status } = data
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 自动保存告警数据
|
|
|
|
|
|
|
|
this.saveRealtimeAlarmData(alarm, status).then((success) => {
|
|
|
|
|
|
|
|
if (success) {
|
|
|
|
|
|
|
|
console.log('自动超时保存成功:', alarm.id)
|
|
|
|
|
|
|
|
this.alarmQueueLength = 0
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
console.error('自动超时保存失败:', alarm.id)
|
|
|
|
|
|
|
|
this.alarmQueueLength = 1
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
showQueueStatus() {
|
|
|
|
|
|
|
|
// 获取告警队列状态并显示
|
|
|
|
|
|
|
|
if (this.$root.getAlarmQueueStatus) {
|
|
|
|
|
|
|
|
const status = this.$root.getAlarmQueueStatus()
|
|
|
|
|
|
|
|
const message = `
|
|
|
|
|
|
|
|
告警队列状态:
|
|
|
|
|
|
|
|
• 队列长度:${status.queueLength}
|
|
|
|
|
|
|
|
• 正在处理:${status.isProcessing ? '是' : '否'}
|
|
|
|
|
|
|
|
• 总接收:${status.totalReceived}
|
|
|
|
|
|
|
|
• 已处理:${status.totalProcessed}
|
|
|
|
|
|
|
|
• 已丢弃:${status.totalDropped}
|
|
|
|
|
|
|
|
• 连接状态:${status.isConnected ? '已连接' : '已断开'}
|
|
|
|
|
|
|
|
`
|
|
|
|
|
|
|
|
this.$alert(message, '告警队列状态', {
|
|
|
|
|
|
|
|
confirmButtonText: '确定',
|
|
|
|
|
|
|
|
type: 'info'
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
this.$message.info('无法获取队列状态信息')
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
updateQueueStatus() {
|
|
|
|
|
|
|
|
// 从App.vue获取告警队列状态
|
|
|
|
|
|
|
|
if (this.$root.getAlarmQueueStatus) {
|
|
|
|
|
|
|
|
const status = this.$root.getAlarmQueueStatus()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.alarmQueueLength = status.queueLength
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 根据队列长度设置状态
|
|
|
|
|
|
|
|
if (status.queueLength === 0) {
|
|
|
|
|
|
|
|
this.alarmQueueStatusText = '告警队列为空'
|
|
|
|
|
|
|
|
this.queueStatusColor = '#67C23A' // 绿色
|
|
|
|
|
|
|
|
} else if (status.queueLength < 5) {
|
|
|
|
|
|
|
|
this.alarmQueueStatusText = `告警队列:${status.queueLength}个待处理`
|
|
|
|
|
|
|
|
this.queueStatusColor = '#E6A23C' // 黄色
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
this.alarmQueueStatusText = `告警队列:${status.queueLength}个待处理(队列较长)`
|
|
|
|
|
|
|
|
this.queueStatusColor = '#F56C6C' // 红色
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化WebSocket状态(如果还未设置)
|
|
|
|
|
|
|
|
if (!this.websocketStatusText) {
|
|
|
|
|
|
|
|
if (status.isConnected) {
|
|
|
|
|
|
|
|
this.websocketStatusText = 'WebSocket连接正常'
|
|
|
|
|
|
|
|
this.websocketStatusIcon = 'el-icon-success'
|
|
|
|
|
|
|
|
this.websocketStatusColor = '#67C23A'
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
this.websocketStatusText = 'WebSocket连接断开'
|
|
|
|
|
|
|
|
this.websocketStatusIcon = 'el-icon-error'
|
|
|
|
|
|
|
|
this.websocketStatusColor = '#F56C6C'
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -1077,4 +1237,62 @@ export default {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// WebSocket状态和队列状态指示器样式
|
|
|
|
|
|
|
|
.websocket-status {
|
|
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
cursor: default;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
i {
|
|
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
|
|
transition: all 0.3s ease;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.alarm-queue-status {
|
|
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
|
|
transition: all 0.3s ease;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
|
|
transform: scale(1.1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
i {
|
|
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
|
|
transition: all 0.3s ease;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.queue-count {
|
|
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
|
|
top: -8px;
|
|
|
|
|
|
|
|
right: -8px;
|
|
|
|
|
|
|
|
background: #ff4757;
|
|
|
|
|
|
|
|
color: white;
|
|
|
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
|
|
|
padding: 2px 4px;
|
|
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
|
|
min-width: 16px;
|
|
|
|
|
|
|
|
height: 16px;
|
|
|
|
|
|
|
|
line-height: 12px;
|
|
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
|
|
|
animation: pulse 2s infinite;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@keyframes pulse {
|
|
|
|
|
|
|
|
0% {
|
|
|
|
|
|
|
|
transform: scale(1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
50% {
|
|
|
|
|
|
|
|
transform: scale(1.2);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
100% {
|
|
|
|
|
|
|
|
transform: scale(1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
</style>
|
|
|
|
</style>
|
|
|
|