<template>
|
<div class="app-container">
|
<!-- 实时报警状态 -->
|
<el-row :gutter="20" class="stat-row">
|
<el-col :span="6">
|
<el-card class="status-card danger">
|
<div class="status-title">紧急报警</div>
|
<div class="status-value">{{ alarmStats.urgent }}</div>
|
</el-card>
|
</el-col>
|
<el-col :span="6">
|
<el-card class="status-card warning">
|
<div class="status-title">重要报警</div>
|
<div class="status-value">{{ alarmStats.important }}</div>
|
</el-card>
|
</el-col>
|
<el-col :span="6">
|
<el-card class="status-card info">
|
<div class="status-title">一般报警</div>
|
<div class="status-value">{{ alarmStats.normal }}</div>
|
</el-card>
|
</el-col>
|
<el-col :span="6">
|
<el-card class="status-card success">
|
<div class="status-title">今日累计</div>
|
<div class="status-value">{{ alarmStats.total }}</div>
|
</el-card>
|
</el-col>
|
</el-row>
|
|
<!-- 实时报警监测 -->
|
<el-card class="box-card">
|
<template #header>
|
<div class="card-header">
|
<span>实时报警监测</span>
|
<el-radio-group v-model="refreshInterval" size="small">
|
<el-radio-button :label="5">5秒刷新</el-radio-button>
|
<el-radio-button :label="10">10秒刷新</el-radio-button>
|
<el-radio-button :label="30">30秒刷新</el-radio-button>
|
</el-radio-group>
|
</div>
|
</template>
|
|
<el-table :data="realtimeAlarms" v-loading="loading" border stripe>
|
<el-table-column prop="alarmTime" label="报警时间" width="160" align="center" />
|
<el-table-column prop="alarmLevel" label="级别" width="80" align="center">
|
<template #default="{ row }">
|
<el-tag :type="row.alarmLevel === '紧急' ? 'danger' : row.alarmLevel === '重要' ? 'warning' : 'info'" size="small">
|
{{ row.alarmLevel }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column prop="pointName" label="报警点位" min-width="180" />
|
<el-table-column prop="alarmType" label="报警类型" width="120" align="center" />
|
<el-table-column prop="alarmValue" label="报警值" width="100" align="center" />
|
<el-table-column prop="thresholdValue" label="阈值" width="100" align="center" />
|
<el-table-column prop="area" label="所属区域" width="120" align="center" />
|
<el-table-column prop="status" label="状态" width="100" align="center">
|
<template #default="{ row }">
|
<el-tag :type="row.status === '未确认' ? 'danger' : 'success'" size="small">
|
{{ row.status }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
<template #default="{ row }">
|
<el-button type="primary" link size="small" @click="handleConfirm(row)">确认</el-button>
|
<el-button type="warning" link size="small" @click="handleAnalyze(row)">分析</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
|
<pagination v-show="alarmTotal > 0" :total="alarmTotal" v-model:page="alarmQuery.pageNum" v-model:limit="alarmQuery.pageSize" @pagination="getRealtimeAlarms" />
|
</el-card>
|
|
<!-- 报警分析与故障诊断 -->
|
<el-row :gutter="20" style="margin-top: 20px;">
|
<el-col :span="12">
|
<el-card class="box-card">
|
<template #header>
|
<span>报警趋势分析</span>
|
</template>
|
<Echarts :xAxis="trendXAxis" :yAxis="trendYAxis" :grid="trendGrid" :series="trendSeries" :legend="trendLegend" :tooltip="trendTooltip" :chartStyle="{ height: '300px', width: '100%' }" />
|
</el-card>
|
</el-col>
|
<el-col :span="12">
|
<el-card class="box-card">
|
<template #header>
|
<span>报警类型分布</span>
|
</template>
|
<Echarts :series="typeSeries" :legend="typeLegend" :tooltip="typeTooltip" :chartStyle="{ height: '300px', width: '100%' }" />
|
</el-card>
|
</el-col>
|
</el-row>
|
|
<!-- 故障诊断与处置指导 -->
|
<el-card class="box-card" style="margin-top: 20px;">
|
<template #header>
|
<div class="card-header">
|
<span>故障诊断与处置指导</span>
|
<el-button type="primary" size="small" icon="Search" @click="handleFaultDiagnosis">智能诊断</el-button>
|
</div>
|
</template>
|
|
<el-table :data="faultList" border>
|
<el-table-column type="index" label="序号" width="60" align="center" />
|
<el-table-column prop="faultType" label="故障类型" width="150" align="center">
|
<template #default="{ row }">
|
<el-tag type="danger" size="small">{{ row.faultType }}</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column prop="alarmPoint" label="关联报警点位" min-width="180" />
|
<el-table-column prop="occurrenceCount" label="发生次数" width="100" align="center" />
|
<el-table-column prop="faultReason" label="可能原因" min-width="200" />
|
<el-table-column prop="solution" label="处置建议" min-width="250" />
|
<el-table-column label="操作" width="100" align="center">
|
<template #default="{ row }">
|
<el-button type="primary" link size="small" @click="viewDetail(row)">详情</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
</el-card>
|
|
<!-- 报警分析弹窗 -->
|
<el-dialog title="报警详情分析" v-model="analyzeVisible" width="700px" append-to-body>
|
<el-descriptions :column="2" border>
|
<el-descriptions-item label="报警点位">{{ currentAlarm.pointName }}</el-descriptions-item>
|
<el-descriptions-item label="报警时间">{{ currentAlarm.alarmTime }}</el-descriptions-item>
|
<el-descriptions-item label="报警级别">
|
<el-tag :type="currentAlarm.alarmLevel === '紧急' ? 'danger' : currentAlarm.alarmLevel === '重要' ? 'warning' : 'info'">
|
{{ currentAlarm.alarmLevel }}
|
</el-tag>
|
</el-descriptions-item>
|
<el-descriptions-item label="报警类型">{{ currentAlarm.alarmType }}</el-descriptions-item>
|
<el-descriptions-item label="报警值">{{ currentAlarm.alarmValue }}</el-descriptions-item>
|
<el-descriptions-item label="报警阈值">{{ currentAlarm.thresholdValue }}</el-descriptions-item>
|
</el-descriptions>
|
|
<div class="analysis-section">
|
<h4>故障分析</h4>
|
<el-alert :title="analysisResult.faultType" type="error" :description="analysisResult.faultReason" show-icon />
|
</div>
|
|
<div class="analysis-section">
|
<h4>处置指导</h4>
|
<el-steps direction="vertical" :active="1">
|
<el-step v-for="(step, index) in analysisResult.steps" :key="index" :title="step.title" :description="step.desc" />
|
</el-steps>
|
</div>
|
|
<template #footer>
|
<el-button @click="analyzeVisible = false">关闭</el-button>
|
<el-button type="primary" @click="handleConfirm(currentAlarm)">确认报警</el-button>
|
</template>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script setup>
|
import { ref, reactive, onMounted, onUnmounted } from 'vue';
|
import { ElMessage } from 'element-plus';
|
import Echarts from '@/components/Echarts/echarts.vue';
|
import Pagination from '@/components/Pagination/index.vue';
|
import {
|
getRealtimeAlarmStats,
|
listRealtimeAlarms,
|
confirmAlarm,
|
analyzeAlarm,
|
getAlarmTrend,
|
getAlarmTypeDistribution,
|
getFaultDiagnosisList
|
} from '@/api/alarmManagement/alarmAnalysis';
|
|
// 报警统计
|
const alarmStats = reactive({
|
urgent: 0,
|
important: 0,
|
normal: 0,
|
total: 0
|
});
|
|
// 实时报警列表
|
const loading = ref(false);
|
const realtimeAlarms = ref([]);
|
const alarmTotal = ref(0);
|
const alarmQuery = reactive({
|
pageNum: 1,
|
pageSize: 10
|
});
|
|
// 刷新间隔
|
const refreshInterval = ref(10);
|
let refreshTimer = null;
|
|
// 故障列表
|
const faultList = ref([]);
|
|
// 分析弹窗
|
const analyzeVisible = ref(false);
|
const currentAlarm = ref({});
|
const analysisResult = reactive({
|
faultType: '',
|
faultReason: '',
|
steps: []
|
});
|
|
// 图表配置
|
const trendXAxis = ref([{ type: 'category', data: [] }])
|
const trendYAxis = ref([{ type: 'value' }])
|
const trendGrid = reactive({ left: '3%', right: '4%', bottom: '3%', containLabel: true })
|
const trendSeries = ref([
|
{ name: '紧急', type: 'line', data: [] },
|
{ name: '重要', type: 'line', data: [] },
|
{ name: '一般', type: 'line', data: [] },
|
])
|
const trendLegend = reactive({ data: ['紧急', '重要', '一般'] })
|
const trendTooltip = reactive({ trigger: 'axis' })
|
|
const typeSeries = ref([{ type: 'pie', radius: '50%', data: [] }])
|
const typeLegend = reactive({ orient: 'vertical', left: 'left' })
|
const typeTooltip = reactive({ trigger: 'item' })
|
|
// 获取报警统计
|
async function getAlarmStats() {
|
const res = await getRealtimeAlarmStats();
|
if (res.code === 200) {
|
Object.assign(alarmStats, res.data);
|
}
|
}
|
|
// 获取实时报警列表
|
async function getRealtimeAlarms() {
|
loading.value = true;
|
const res = await listRealtimeAlarms(alarmQuery);
|
if (res.code === 200) {
|
realtimeAlarms.value = res.data.records;
|
alarmTotal.value = res.data.total;
|
}
|
loading.value = false;
|
}
|
|
// 确认报警
|
async function handleConfirm(row) {
|
const res = await confirmAlarm(row.alarmId);
|
if (res.code === 200) {
|
ElMessage.success('确认成功');
|
getRealtimeAlarms();
|
}
|
}
|
|
// 分析报警
|
async function handleAnalyze(row) {
|
currentAlarm.value = row;
|
const res = await analyzeAlarm(row.alarmId);
|
if (res.code === 200) {
|
Object.assign(analysisResult, res.data);
|
}
|
analyzeVisible.value = true;
|
}
|
|
// 获取报警趋势
|
async function getTrendData() {
|
const res = await getAlarmTrend();
|
if (res.code === 200) {
|
const data = res.data;
|
trendXAxis.value[0].data = data.dates
|
trendSeries.value[0].data = data.urgent
|
trendSeries.value[0].itemStyle = { color: '#f56c6c' }
|
trendSeries.value[1].data = data.important
|
trendSeries.value[1].itemStyle = { color: '#e6a23c' }
|
trendSeries.value[2].data = data.normal
|
trendSeries.value[2].itemStyle = { color: '#909399' }
|
}
|
}
|
|
// 获取报警类型分布
|
async function getTypeDistribution() {
|
const res = await getAlarmTypeDistribution();
|
if (res.code === 200) {
|
typeSeries.value[0].data = res.data
|
typeSeries.value[0].emphasis = { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' } }
|
}
|
}
|
|
// 获取故障诊断列表
|
async function getFaultList() {
|
const res = await getFaultDiagnosisList();
|
if (res.code === 200) {
|
faultList.value = res.data;
|
}
|
}
|
|
// 智能诊断
|
function handleFaultDiagnosis() {
|
ElMessage.success('智能诊断完成');
|
getFaultList();
|
}
|
|
function viewDetail(row) {
|
ElMessage.info('查看详情: ' + row.faultType);
|
}
|
|
// 定时刷新
|
function startAutoRefresh() {
|
refreshTimer = setInterval(() => {
|
getAlarmStats();
|
getRealtimeAlarms();
|
}, refreshInterval.value * 1000);
|
}
|
|
function stopAutoRefresh() {
|
if (refreshTimer) {
|
clearInterval(refreshTimer);
|
refreshTimer = null;
|
}
|
}
|
|
onMounted(() => {
|
getAlarmStats();
|
getRealtimeAlarms();
|
getTrendData();
|
getTypeDistribution();
|
getFaultList();
|
startAutoRefresh();
|
});
|
|
onUnmounted(() => {
|
stopAutoRefresh();
|
});
|
</script>
|
|
<style scoped>
|
.stat-row {
|
margin-bottom: 20px;
|
}
|
.status-card {
|
text-align: center;
|
}
|
.status-card.danger {
|
border-left: 4px solid #f56c6c;
|
}
|
.status-card.warning {
|
border-left: 4px solid #e6a23c;
|
}
|
.status-card.info {
|
border-left: 4px solid #909399;
|
}
|
.status-card.success {
|
border-left: 4px solid #67c23a;
|
}
|
.status-title {
|
font-size: 14px;
|
color: #666;
|
margin-bottom: 10px;
|
}
|
.status-value {
|
font-size: 32px;
|
font-weight: bold;
|
color: #303133;
|
}
|
.card-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
}
|
.analysis-section {
|
margin-top: 20px;
|
}
|
.analysis-section h4 {
|
margin: 0 0 15px 0;
|
font-size: 16px;
|
color: #303133;
|
}
|
</style>
|