feat(ems): 增加实时告警处置措施功能

- 新增 POST 方式查询告警措施步骤接口,解决 URL 编码问题
- 在 Navbar 组件中添加实时告警处置措施标签页
- 实现加载和显示实时告警处置措施的功能
- 优化实时告警弹窗,增加自动加载第一个告警规则的处置措施
boardTest
zch 3 weeks ago
parent dd9f9def4c
commit b2c4446745

@ -28,8 +28,12 @@ export function getEmsAlarmActionStepsByRuleId(ruleObjId) {
// 根据报警数据信息查询措施步骤列表(包含图片)
export function getEmsAlarmActionStepsByAlarmInfo(monitorId, cause) {
return request({
url: '/ems/base/emsAlarmActionStep/alarm/' + encodeURIComponent(monitorId) + '/' + encodeURIComponent(cause),
method: 'get'
url: '/ems/base/emsAlarmActionStep/getByAlarmInfo',
method: 'post',
data: {
monitorId: monitorId,
cause: cause
}
})
}

@ -212,136 +212,204 @@
<el-dialog
title="⚠️ 实时告警通知"
:visible.sync="realtimeAlarmDialog"
width="800px"
width="900px"
append-to-body
:close-on-click-modal="false"
:close-on-press-escape="false"
class="realtime-alarm-dialog"
>
<div v-if="currentRealtimeAlarm" class="alarm-content">
<!-- 设备信息 -->
<div class="alarm-section">
<h3 class="section-title">📟 设备信息</h3>
<el-row :gutter="16">
<el-col :span="12">
<div class="info-item">
<span class="label">设备ID</span>
<span class="value">{{ currentRealtimeAlarm.monitorId }}</span>
</div>
</el-col>
<el-col :span="12">
<div class="info-item">
<span class="label">告警时间</span>
<span class="value">{{ formatAlarmTime(currentRealtimeAlarm.recordTime) }}</span>
</div>
</el-col>
</el-row>
</div>
<el-tabs v-model="realtimeActiveTab" type="card">
<!-- 告警详情标签页 -->
<el-tab-pane label="告警详情" name="alarmDetail">
<div v-if="currentRealtimeAlarm" class="alarm-content">
<!-- 设备信息 -->
<div class="alarm-section">
<h3 class="section-title">📟 设备信息</h3>
<el-row :gutter="16">
<el-col :span="12">
<div class="info-item">
<span class="label">设备ID</span>
<span class="value">{{ currentRealtimeAlarm.monitorId }}</span>
</div>
</el-col>
<el-col :span="12">
<div class="info-item">
<span class="label">告警时间</span>
<span class="value">{{ formatAlarmTime(currentRealtimeAlarm.recordTime) }}</span>
</div>
</el-col>
</el-row>
</div>
<!-- 设备当前数据 -->
<div class="alarm-section" v-if="currentRealtimeAlarm.deviceParam">
<h3 class="section-title">📊 设备当前数据</h3>
<el-row :gutter="16">
<el-col :span="8" v-if="currentRealtimeAlarm.deviceParam.temperature !== null">
<div class="data-item">
<span class="data-label">温度</span>
<span class="data-value">{{ currentRealtimeAlarm.deviceParam.temperature }}°C</span>
</div>
</el-col>
<el-col :span="8" v-if="currentRealtimeAlarm.deviceParam.humidity !== null">
<div class="data-item">
<span class="data-label">湿度</span>
<span class="data-value">{{ currentRealtimeAlarm.deviceParam.humidity }}%</span>
</div>
</el-col>
<el-col :span="8" v-if="currentRealtimeAlarm.deviceParam.illuminance !== null">
<div class="data-item">
<span class="data-label">照度</span>
<span class="data-value">{{ currentRealtimeAlarm.deviceParam.illuminance }}lx</span>
</div>
</el-col>
<el-col :span="8" v-if="currentRealtimeAlarm.deviceParam.noise !== null">
<div class="data-item">
<span class="data-label">噪声</span>
<span class="data-value">{{ currentRealtimeAlarm.deviceParam.noise }}dB</span>
</div>
</el-col>
<el-col :span="8" v-if="currentRealtimeAlarm.deviceParam.concentration !== null">
<div class="data-item">
<span class="data-label">气体浓度</span>
<span class="data-value">{{ currentRealtimeAlarm.deviceParam.concentration }}ppm</span>
</div>
</el-col>
<el-col :span="8" v-if="currentRealtimeAlarm.deviceParam.vibrationSpeed !== null">
<div class="data-item">
<span class="data-label">振动速度</span>
<span class="data-value">{{ currentRealtimeAlarm.deviceParam.vibrationSpeed || currentRealtimeAlarm.deviceParam.VibrationSpeed }}mm/s</span>
</div>
</el-col>
<el-col :span="8" v-if="currentRealtimeAlarm.deviceParam.vibrationDisplacement !== null || currentRealtimeAlarm.deviceParam.VibrationDisplacement !== null">
<div class="data-item">
<span class="data-label">振动位移</span>
<span class="data-value">{{ currentRealtimeAlarm.deviceParam.vibrationDisplacement || currentRealtimeAlarm.deviceParam.VibrationDisplacement }}um</span>
</div>
</el-col>
<el-col :span="8" v-if="currentRealtimeAlarm.deviceParam.vibrationAcceleration !== null || currentRealtimeAlarm.deviceParam.VibrationAcceleration !== null">
<div class="data-item">
<span class="data-label">振动加速度</span>
<span class="data-value">{{ currentRealtimeAlarm.deviceParam.vibrationAcceleration || currentRealtimeAlarm.deviceParam.VibrationAcceleration }}g</span>
</div>
</el-col>
<el-col :span="8" v-if="currentRealtimeAlarm.deviceParam.vibrationTemp !== null || currentRealtimeAlarm.deviceParam.VibrationTemp !== null">
<div class="data-item">
<span class="data-label">振动温度</span>
<span class="data-value">{{ currentRealtimeAlarm.deviceParam.vibrationTemp || currentRealtimeAlarm.deviceParam.VibrationTemp }}</span>
</div>
</el-col>
</el-row>
</div>
<!-- 设备当前数据 -->
<div class="alarm-section" v-if="currentRealtimeAlarm.deviceParam">
<h3 class="section-title">📊 设备当前数据</h3>
<el-row :gutter="16">
<el-col :span="8" v-if="currentRealtimeAlarm.deviceParam.temperature !== null">
<div class="data-item">
<span class="data-label">温度</span>
<span class="data-value">{{ currentRealtimeAlarm.deviceParam.temperature }}°C</span>
</div>
</el-col>
<el-col :span="8" v-if="currentRealtimeAlarm.deviceParam.humidity !== null">
<div class="data-item">
<span class="data-label">湿度</span>
<span class="data-value">{{ currentRealtimeAlarm.deviceParam.humidity }}%</span>
</div>
</el-col>
<el-col :span="8" v-if="currentRealtimeAlarm.deviceParam.illuminance !== null">
<div class="data-item">
<span class="data-label">照度</span>
<span class="data-value">{{ currentRealtimeAlarm.deviceParam.illuminance }}lx</span>
</div>
</el-col>
<el-col :span="8" v-if="currentRealtimeAlarm.deviceParam.noise !== null">
<div class="data-item">
<span class="data-label">噪声</span>
<span class="data-value">{{ currentRealtimeAlarm.deviceParam.noise }}dB</span>
</div>
</el-col>
<el-col :span="8" v-if="currentRealtimeAlarm.deviceParam.concentration !== null">
<div class="data-item">
<span class="data-label">气体浓度</span>
<span class="data-value">{{ currentRealtimeAlarm.deviceParam.concentration }}ppm</span>
</div>
</el-col>
<el-col :span="8" v-if="currentRealtimeAlarm.deviceParam.vibrationSpeed !== null">
<div class="data-item">
<span class="data-label">振动速度</span>
<span class="data-value">{{ currentRealtimeAlarm.deviceParam.vibrationSpeed || currentRealtimeAlarm.deviceParam.VibrationSpeed }}mm/s</span>
</div>
</el-col>
<el-col :span="8" v-if="currentRealtimeAlarm.deviceParam.vibrationDisplacement !== null || currentRealtimeAlarm.deviceParam.VibrationDisplacement !== null">
<div class="data-item">
<span class="data-label">振动位移</span>
<span class="data-value">{{ currentRealtimeAlarm.deviceParam.vibrationDisplacement || currentRealtimeAlarm.deviceParam.VibrationDisplacement }}um</span>
</div>
</el-col>
<el-col :span="8" v-if="currentRealtimeAlarm.deviceParam.vibrationAcceleration !== null || currentRealtimeAlarm.deviceParam.VibrationAcceleration !== null">
<div class="data-item">
<span class="data-label">振动加速度</span>
<span class="data-value">{{ currentRealtimeAlarm.deviceParam.vibrationAcceleration || currentRealtimeAlarm.deviceParam.VibrationAcceleration }}g</span>
</div>
</el-col>
<el-col :span="8" v-if="currentRealtimeAlarm.deviceParam.vibrationTemp !== null || currentRealtimeAlarm.deviceParam.VibrationTemp !== null">
<div class="data-item">
<span class="data-label">振动温度</span>
<span class="data-value">{{ currentRealtimeAlarm.deviceParam.vibrationTemp || currentRealtimeAlarm.deviceParam.VibrationTemp }}</span>
</div>
</el-col>
</el-row>
</div>
<!-- 触发的告警规则 -->
<div class="alarm-section" v-if="currentRealtimeAlarm.alarmRules && currentRealtimeAlarm.alarmRules.length > 0">
<h3 class="section-title">🚨 触发的告警规则</h3>
<div class="alarm-rules">
<div
v-for="(rule, index) in currentRealtimeAlarm.alarmRules"
:key="index"
class="rule-item"
>
<div class="rule-header">
<span class="rule-name">{{ rule.ruleName }}</span>
<el-tag :type="rule.triggerRule === 0 ? 'danger' : 'warning'" size="small">
{{ rule.triggerRule === 0 ? '大于阈值' : '小于阈值' }}
</el-tag>
<!-- 触发的告警规则 -->
<div class="alarm-section" v-if="currentRealtimeAlarm.alarmRules && currentRealtimeAlarm.alarmRules.length > 0">
<h3 class="section-title">🚨 触发的告警规则</h3>
<div class="alarm-rules">
<div
v-for="(rule, index) in currentRealtimeAlarm.alarmRules"
:key="index"
class="rule-item"
>
<div class="rule-header">
<span class="rule-name">{{ rule.ruleName }}</span>
<el-tag :type="rule.triggerRule === 0 ? 'danger' : 'warning'" size="small">
{{ rule.triggerRule === 0 ? '大于阈值' : '小于阈值' }}
</el-tag>
</div>
<div class="rule-details">
<span class="detail-item">阈值{{ rule.triggerValue }}</span>
<span class="detail-item">监测字段{{ getFieldName(rule.monitorField) }}</span>
<span class="detail-item" v-if="rule.cause">{{ rule.cause }}</span>
</div>
</div>
</div>
<div class="rule-details">
<span class="detail-item">阈值{{ rule.triggerValue }}</span>
<span class="detail-item">监测字段{{ getFieldName(rule.monitorField) }}</span>
<span class="detail-item" v-if="rule.cause">{{ rule.cause }}</span>
</div>
<!-- 告警内容详情 -->
<div class="alarm-section" v-if="currentRealtimeAlarm.alarmContents && currentRealtimeAlarm.alarmContents.length > 0">
<h3 class="section-title">📋 告警内容详情</h3>
<div class="alarm-contents">
<div
v-for="(content, index) in currentRealtimeAlarm.alarmContents"
:key="index"
class="content-item"
>
<el-alert
:title="content"
type="error"
:closable="false"
show-icon
/>
</div>
</div>
</div>
</div>
</div>
</el-tab-pane>
<!-- 告警内容详情 -->
<div class="alarm-section" v-if="currentRealtimeAlarm.alarmContents && currentRealtimeAlarm.alarmContents.length > 0">
<h3 class="section-title">📋 告警内容详情</h3>
<div class="alarm-contents">
<div
v-for="(content, index) in currentRealtimeAlarm.alarmContents"
:key="index"
class="content-item"
>
<el-alert
:title="content"
type="error"
:closable="false"
show-icon
/>
<!-- 处置措施标签页 -->
<el-tab-pane label="处置措施" name="realtimeActionSteps">
<div v-if="currentRealtimeAlarm">
<el-alert
:title="`设备:${currentRealtimeAlarm.monitorId} | 告警时间:${formatAlarmTime(currentRealtimeAlarm.recordTime)}`"
type="warning"
:closable="false"
style="margin-bottom: 20px;"
/>
<div v-loading="realtimeActionStepsLoading">
<div v-if="realtimeActionSteps.length === 0" class="no-steps">
<el-empty description="该告警规则暂无配置处置措施"></el-empty>
</div>
<div v-else>
<el-timeline>
<el-timeline-item
v-for="(step, index) in realtimeActionSteps"
:key="step.objId"
:timestamp="`步骤 ${step.stepSequence}`"
placement="top"
type="primary"
size="large"
>
<el-card class="step-card">
<div slot="header" class="clearfix">
<span class="step-title">{{ step.stepSequence }}</span>
</div>
<!-- 步骤描述 -->
<div class="step-description">
<p>{{ step.description }}</p>
</div>
<!-- 步骤图片 -->
<div v-if="step.stepImages && step.stepImages.length > 0" class="step-images">
<div class="images-title">参考图片</div>
<div class="image-gallery">
<div
v-for="image in step.stepImages"
:key="image.objId"
class="image-item"
@click="previewImage(getFullImageUrl(image.imageUrl))"
>
<img :src="getFullImageUrl(image.imageUrl)" :alt="image.description" />
<div v-if="image.description" class="image-desc">{{ image.description }}</div>
</div>
</div>
</div>
<!-- 步骤备注 -->
<div v-if="step.remark" class="step-remark">
<el-tag type="info" size="small">备注{{ step.remark }}</el-tag>
</div>
</el-card>
</el-timeline-item>
</el-timeline>
</div>
</div>
</div>
</div>
</div>
</el-tab-pane>
</el-tabs>
<div slot="footer" class="dialog-footer">
<el-button
@ -423,7 +491,12 @@ export default {
alarmQueueStatusText: '',
queueStatusColor: '',
alarmQueueLength: 0,
queueStatusTimer: null
queueStatusTimer: null,
//
realtimeActiveTab: 'alarmDetail',
realtimeActionSteps: [],
realtimeActionStepsLoading: false
}
},
created() {
@ -617,12 +690,18 @@ export default {
this.alarmProcessedCallback = onProcessed
this.alarmTimeoutCallback = onTimeout
//
this.realtimeActiveTab = 'alarmDetail'
//
this.realtimeAlarmDialog = true
//
this.playAlarmSound()
//
this.loadRealtimeActionSteps(alarm)
console.log('告警弹窗已显示告警ID:', alarm.id, '优先级:', alarm.priority)
},
//
@ -812,6 +891,11 @@ export default {
this.currentAlarmId = null
this.alarmProcessedCallback = null
this.alarmTimeoutCallback = null
//
this.realtimeActiveTab = 'alarmDetail'
this.realtimeActionSteps = []
this.realtimeActionStepsLoading = false
},
//
formatAlarmTime(time) {
@ -926,6 +1010,29 @@ export default {
}
}
}
},
//
async loadRealtimeActionSteps(alarm) {
if (!alarm || !alarm.alarmRules || alarm.alarmRules.length === 0) {
this.realtimeActionSteps = []
return
}
this.realtimeActionStepsLoading = true
try {
// 使
const firstRule = alarm.alarmRules[0]
const fieldName = this.getFieldName(firstRule.monitorField)
const response = await getEmsAlarmActionStepsByAlarmInfo(alarm.monitorId, fieldName)
this.realtimeActionSteps = response.data || []
console.log('实时告警处置措施加载成功:', this.realtimeActionSteps.length, '个步骤')
} catch (error) {
console.error('加载实时告警处置措施失败:', error)
this.realtimeActionSteps = []
} finally {
this.realtimeActionStepsLoading = false
}
}
}
}

Loading…
Cancel
Save