|
|
@@ -7,9 +7,9 @@
|
|
|
<el-icon><Warning /></el-icon>
|
|
|
<span class="title">可燃气体中心</span>
|
|
|
<div class="stats-badge">
|
|
|
- <span class="stat-item critical"><i class="dot"></i>实时报警 {{ realtimeAlerts.length }}</span>
|
|
|
- <span class="stat-item warning"><i class="dot"></i>历史报警 {{ historyAlerts.length }}</span>
|
|
|
- <span class="stat-item"><i class="dot"></i>接入场站 {{ totalStations }}</span>
|
|
|
+ <span class="stat-item critical"><i class="dot"></i>报警设备 {{ alarmStats.realtimeCount }}</span>
|
|
|
+ <span class="stat-item warning"><i class="dot"></i>设备总数 {{ equipments.length }}</span>
|
|
|
+ <span class="stat-item"><i class="dot"></i>最高浓度 {{ alarmStats.maxConcentration }}ppm</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="header-right">
|
|
|
@@ -33,7 +33,7 @@
|
|
|
<!-- KPI指标卡片 -->
|
|
|
<div class="kpi-cards">
|
|
|
<div class="kpi-card critical">
|
|
|
- <div class="kpi-value">{{ realtimeAlerts.length }}</div>
|
|
|
+ <div class="kpi-value">{{ alarmStats.realtimeCount }}</div>
|
|
|
<div class="kpi-label">实时报警</div>
|
|
|
<div class="kpi-trend">较昨日 ↑{{ realtimeTrend }}</div>
|
|
|
</div>
|
|
|
@@ -55,50 +55,45 @@
|
|
|
|
|
|
<!-- 主内容区:左侧实时报警 + 右侧历史报警 -->
|
|
|
<div class="main-layout">
|
|
|
- <!-- 左侧实时报警列表 -->
|
|
|
+ <!-- 左侧设备列表 -->
|
|
|
<div class="alert-panel">
|
|
|
<div class="panel-header">
|
|
|
- <span><el-icon><BellFilled /></el-icon> 实时报警</span>
|
|
|
- <el-tag type="danger" effect="dark">{{ realtimeAlerts.length }} 个报警</el-tag>
|
|
|
+ <span><el-icon><BellFilled /></el-icon> 探测器设备</span>
|
|
|
+ <el-tag type="danger" effect="dark">{{ alarmStats.realtimeCount }} 个报警</el-tag>
|
|
|
</div>
|
|
|
<div class="alert-list realtime">
|
|
|
<div
|
|
|
- v-for="alert in paginatedRealtime"
|
|
|
- :key="alert.id"
|
|
|
+ v-for="eq in equipments"
|
|
|
+ :key="eq.equipmentId"
|
|
|
class="alert-item"
|
|
|
- :class="{ critical: alert.level === 'critical', warning: alert.level === 'warning' }"
|
|
|
- @click="viewAlertDetail(alert)"
|
|
|
+ :class="{ critical: eq.level === 'critical', warning: eq.level === 'warning', selected: selectedEquipment?.equipmentId === eq.equipmentId }"
|
|
|
+ @click="selectEquipment(eq)"
|
|
|
>
|
|
|
<div class="alert-level">
|
|
|
- <el-tag :type="alert.level === 'critical' ? 'danger' : 'warning'" size="small">
|
|
|
- {{ alert.level === 'critical' ? '严重' : '预警' }}
|
|
|
+ <el-tag :type="eq.level === 'critical' ? 'danger' : eq.level === 'warning' ? 'warning' : 'info'" size="small">
|
|
|
+ {{ eq.level === 'critical' ? '严重' : eq.level === 'warning' ? '预警' : '正常' }}
|
|
|
</el-tag>
|
|
|
</div>
|
|
|
<div class="alert-content">
|
|
|
- <div class="alert-title">{{ alert.stationName }}</div>
|
|
|
+ <div class="alert-title">{{ eq.equipmentName }}</div>
|
|
|
<div class="alert-company">
|
|
|
- <el-icon><OfficeBuilding /></el-icon> {{ alert.companyName }}
|
|
|
+ <el-icon><OfficeBuilding /></el-icon> 沅陵燃气
|
|
|
</div>
|
|
|
<div class="alert-location">
|
|
|
- <el-icon><Location /></el-icon> {{ alert.address }}
|
|
|
+ <el-icon><Location /></el-icon> {{ eq.location }}
|
|
|
</div>
|
|
|
<div class="alert-data">
|
|
|
- <span class="data-item">甲烷浓度: <strong :class="{ 'critical-value': alert.concentration > 30 }">{{ alert.concentration }} ppm</strong></span>
|
|
|
- <span class="data-item">报警阈值: {{ alert.threshold }} ppm</span>
|
|
|
+ <span class="data-item" v-if="eq.latestConcentration != null">甲烷浓度: <strong :class="{ 'critical-value': eq.latestConcentration > 30 }">{{ eq.latestConcentration }} ppm</strong></span>
|
|
|
+ <span class="data-item" v-if="eq.latestThreshold != null">阈值: {{ eq.latestThreshold }} ppm</span>
|
|
|
+ <span class="data-item" v-if="eq.latestConcentration == null" style="color:#67c23a">无报警记录</span>
|
|
|
</div>
|
|
|
- <div class="alert-time">{{ alert.alertTime }}</div>
|
|
|
- </div>
|
|
|
- <div class="alert-action">
|
|
|
- <el-button size="small" type="primary" @click.stop="handleAlert(alert)">处理</el-button>
|
|
|
+ <div class="alert-time" v-if="eq.alertTime">{{ eq.alertTime }}</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <div v-if="realtimeAlerts.length === 0" class="empty-state">
|
|
|
- <el-empty description="暂无实时报警" :image-size="80" />
|
|
|
+ <div v-if="equipments.length === 0" class="empty-state">
|
|
|
+ <el-empty description="暂无探测器设备" :image-size="80" />
|
|
|
</div>
|
|
|
</div>
|
|
|
- <div class="pagination-area" v-if="realtimeAlerts.length > pageSize">
|
|
|
- <el-pagination small layout="prev, pager, next" :total="realtimeAlerts.length" :page-size="pageSize" v-model:current-page="realtimePage" />
|
|
|
- </div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 右侧历史报警列表 -->
|
|
|
@@ -151,9 +146,10 @@
|
|
|
<el-pagination
|
|
|
small
|
|
|
layout="total, prev, pager, next"
|
|
|
- :total="filteredHistory.length"
|
|
|
+ :total="historyTotal"
|
|
|
:page-size="historyPageSize"
|
|
|
v-model:current-page="historyPage"
|
|
|
+ @current-change="loadHistory"
|
|
|
/>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -204,149 +200,80 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref, computed, watch, nextTick, onBeforeUnmount } from 'vue'
|
|
|
+import { ref, computed, watch, nextTick, onBeforeUnmount, onMounted } from 'vue'
|
|
|
import { Warning, Refresh, Download, BellFilled, Document, Location, Search, OfficeBuilding } from '@element-plus/icons-vue'
|
|
|
import * as echarts from 'echarts'
|
|
|
import { ElMessage } from 'element-plus'
|
|
|
+import { getGasAlarmEquipment, getGasAlarmHistory, getGasAlarmStats, resolveGasAlarm, dispatchGasAlarm } from '@/api/pipeNetwork/basic'
|
|
|
|
|
|
-// 沅陵县场站可燃气体实时报警数据
|
|
|
-const realtimeAlerts = ref([
|
|
|
- { id: 1, stationCode: 'YL-LNG-01', stationName: '太常LNG储配站', companyName: '沅陵燃气', companyId: 'yuanling',
|
|
|
- address: '沅陵县太常片区辰州东路末段LNG储配站', concentration: 48, threshold: 20, level: 'critical',
|
|
|
- alertTime: '2026-06-13 15:23:45', status: 'pending',
|
|
|
- trendData: [12, 15, 18, 22, 28, 32, 38, 42, 45, 48, 48, 47, 45, 42, 38, 35, 32, 28, 25, 22, 20, 18, 16, 14] },
|
|
|
- { id: 2, stationCode: 'YL-TY-01', stationName: '太常调压站', companyName: '沅陵燃气', companyId: 'yuanling',
|
|
|
- address: '沅陵县太常片区古城中路与天宁路交叉口东侧', concentration: 28, threshold: 20, level: 'warning',
|
|
|
- alertTime: '2026-06-13 14:58:12', status: 'pending',
|
|
|
- trendData: [15, 17, 19, 22, 24, 26, 28, 28, 27, 26, 25, 24, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11] },
|
|
|
- { id: 3, stationCode: 'YL-GY-01', stationName: '工业园计量站', companyName: '沅陵燃气', companyId: 'yuanling',
|
|
|
- address: '沅陵县太常片区建设东路工业园入口东侧', concentration: 35, threshold: 20, level: 'critical',
|
|
|
- alertTime: '2026-06-13 14:32:05', status: 'pending',
|
|
|
- trendData: [18, 20, 22, 25, 28, 30, 32, 35, 35, 34, 33, 32, 30, 28, 26, 24, 22, 20, 19, 18, 17, 16, 15, 14] }
|
|
|
-])
|
|
|
+// 左侧: 所有探测器设备(去重)
|
|
|
+const equipments = ref([])
|
|
|
+const selectedEquipment = ref(null)
|
|
|
+// 右侧: 历史报警
|
|
|
+const historyAlerts = ref([])
|
|
|
+const historyPage = ref(1)
|
|
|
+const historyPageSize = 8
|
|
|
+const historyTotal = ref(0)
|
|
|
+const alarmStats = ref({ realtimeCount: 0, pendingCount: 0, maxConcentration: 0, totalStations: 0 })
|
|
|
|
|
|
-// 沅陵县场站可燃气体历史报警数据
|
|
|
-const historyAlerts = ref([
|
|
|
- { id: 101, stationCode: 'YL-MZ-01', stationName: '太常天然气门站', companyName: '沅陵燃气', companyId: 'yuanling',
|
|
|
- address: '沅陵县太常片区辰州北路末段天然气门站', concentration: 42, threshold: 20, level: 'critical',
|
|
|
- alertTime: '2026-06-13 08:30:00', status: 'resolved', resolveTime: '2026-06-13 09:45:00', handler: '张建国', remark: '法兰连接处微漏,紧固后恢复正常',
|
|
|
- trendData: [10, 12, 14, 18, 22, 28, 32, 36, 40, 42, 42, 41, 38, 34, 28, 22, 16, 12, 10, 9, 8, 8, 7, 7] },
|
|
|
- { id: 102, stationCode: 'YL-CBTY-01', stationName: '城北调压站', companyName: '沅陵燃气', companyId: 'yuanling',
|
|
|
- address: '沅陵县太常片区天宁西路与古城北路交叉口', concentration: 25, threshold: 20, level: 'warning',
|
|
|
- alertTime: '2026-06-13 07:15:00', status: 'resolved', resolveTime: '2026-06-13 08:20:00', handler: '李志强', remark: '通风后浓度下降至正常范围',
|
|
|
- trendData: [12, 13, 15, 17, 19, 21, 23, 25, 25, 24, 23, 21, 18, 15, 13, 11, 10, 9, 8, 8, 7, 7, 6, 6] },
|
|
|
- { id: 103, stationCode: 'YL-CNTY-01', stationName: '城南调压站', companyName: '沅陵燃气', companyId: 'yuanling',
|
|
|
- address: '沅陵县太常片区建设路与古城南路交叉口南侧', concentration: 52, threshold: 20, level: 'critical',
|
|
|
- alertTime: '2026-06-12 22:45:00', status: 'pending', resolveTime: null, handler: null, remark: null,
|
|
|
- trendData: [18, 20, 22, 26, 30, 34, 38, 42, 46, 50, 52, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40] },
|
|
|
- { id: 104, stationCode: 'YL-TY-01', stationName: '太常调压站', companyName: '沅陵燃气', companyId: 'yuanling',
|
|
|
- address: '沅陵县太常片区古城中路与天宁路交叉口东侧', concentration: 22, threshold: 20, level: 'warning',
|
|
|
- alertTime: '2026-06-12 14:20:00', status: 'resolved', resolveTime: '2026-06-12 15:10:00', handler: '王大明', remark: '巡检期间短暂波动,已自动恢复',
|
|
|
- trendData: [10, 11, 13, 15, 18, 20, 22, 22, 21, 20, 18, 16, 14, 12, 10, 9, 8, 8, 7, 7, 6, 6, 5, 5] },
|
|
|
- { id: 105, stationCode: 'YL-LNG-01', stationName: '太常LNG储配站', companyName: '沅陵燃气', companyId: 'yuanling',
|
|
|
- address: '沅陵县太常片区辰州东路末段LNG储配站', concentration: 38, threshold: 20, level: 'critical',
|
|
|
- alertTime: '2026-06-11 10:00:00', status: 'resolved', resolveTime: '2026-06-11 11:30:00', handler: '赵志远', remark: '储罐区检测器校准后读数正常',
|
|
|
- trendData: [8, 10, 14, 18, 24, 30, 35, 38, 38, 37, 35, 30, 24, 18, 14, 10, 8, 7, 6, 6, 5, 5, 4, 4] },
|
|
|
- { id: 106, stationCode: 'YL-GY-01', stationName: '工业园计量站', companyName: '沅陵燃气', companyId: 'yuanling',
|
|
|
- address: '沅陵县太常片区建设东路工业园入口东侧', concentration: 26, threshold: 20, level: 'warning',
|
|
|
- alertTime: '2026-06-11 16:10:00', status: 'pending', resolveTime: null, handler: null, remark: null,
|
|
|
- trendData: [14, 15, 16, 18, 20, 22, 24, 26, 26, 27, 28, 28, 27, 27, 26, 26, 25, 25, 24, 24, 23, 23, 22, 22] },
|
|
|
- { id: 107, stationCode: 'YL-CBTY-01', stationName: '城北调压站', companyName: '沅陵燃气', companyId: 'yuanling',
|
|
|
- address: '沅陵县太常片区天宁西路与古城北路交叉口', concentration: 24, threshold: 20, level: 'warning',
|
|
|
- alertTime: '2026-06-10 09:30:00', status: 'resolved', resolveTime: '2026-06-10 10:15:00', handler: '陈文斌', remark: '调压器维护后短暂波动,已稳定',
|
|
|
- trendData: [11, 13, 15, 18, 21, 23, 24, 24, 23, 21, 18, 15, 12, 10, 9, 8, 7, 7, 6, 6, 5, 5, 5, 4] },
|
|
|
- { id: 108, stationCode: 'YL-CNTY-01', stationName: '城南调压站', companyName: '沅陵燃气', companyId: 'yuanling',
|
|
|
- address: '沅陵县太常片区建设路与古城南路交叉口南侧', concentration: 31, threshold: 20, level: 'critical',
|
|
|
- alertTime: '2026-06-12 09:50:00', status: 'resolved', resolveTime: '2026-06-12 11:15:00', handler: '李志强', remark: '阀门密封圈老化,更换后恢复正常',
|
|
|
- trendData: [12, 14, 16, 19, 22, 26, 29, 31, 31, 30, 28, 25, 21, 16, 12, 9, 7, 6, 5, 5, 4, 4, 3, 3] },
|
|
|
- { id: 109, stationCode: 'YL-MZ-01', stationName: '太常天然气门站', companyName: '沅陵燃气', companyId: 'yuanling',
|
|
|
- address: '沅陵县太常片区辰州北路末段天然气门站', concentration: 23, threshold: 20, level: 'warning',
|
|
|
- alertTime: '2026-06-11 18:30:00', status: 'resolved', resolveTime: '2026-06-11 19:10:00', handler: '张建国', remark: '交接班巡检发现,通风处置后恢复',
|
|
|
- trendData: [13, 14, 16, 18, 20, 22, 23, 23, 22, 20, 17, 14, 12, 10, 9, 8, 7, 7, 6, 6, 5, 5, 5, 4] },
|
|
|
- { id: 110, stationCode: 'YL-TY-01', stationName: '太常调压站', companyName: '沅陵燃气', companyId: 'yuanling',
|
|
|
- address: '沅陵县太常片区古城中路与天宁路交叉口东侧', concentration: 33, threshold: 20, level: 'critical',
|
|
|
- alertTime: '2026-06-10 15:40:00', status: 'pending', resolveTime: null, handler: null, remark: null,
|
|
|
- trendData: [15, 17, 19, 22, 25, 28, 30, 32, 33, 33, 33, 32, 32, 31, 31, 30, 30, 29, 29, 28, 28, 27, 27, 26] },
|
|
|
- { id: 111, stationCode: 'YL-LNG-01', stationName: '太常LNG储配站', companyName: '沅陵燃气', companyId: 'yuanling',
|
|
|
- address: '沅陵县太常片区辰州东路末段LNG储配站', concentration: 21, threshold: 20, level: 'warning',
|
|
|
- alertTime: '2026-06-10 11:20:00', status: 'resolved', resolveTime: '2026-06-10 12:05:00', handler: '赵志远', remark: '槽车卸液作业期间瞬时波动',
|
|
|
- trendData: [8, 9, 11, 14, 17, 19, 21, 21, 20, 18, 15, 12, 10, 8, 7, 6, 5, 5, 4, 4, 3, 3, 3, 2] }
|
|
|
-])
|
|
|
+async function loadEquipment() {
|
|
|
+ try {
|
|
|
+ const res = await getGasAlarmEquipment()
|
|
|
+ equipments.value = (res.data || []).map(e => ({
|
|
|
+ ...e,
|
|
|
+ stationName: e.equipmentName,
|
|
|
+ concentration: e.latestConcentration,
|
|
|
+ threshold: e.latestThreshold || 20,
|
|
|
+ alertTime: e.latestAlarmTime,
|
|
|
+ level: e.hasAlarm ? (e.latestAlarmLevel === 1 ? 'critical' : 'warning') : 'normal',
|
|
|
+ status: e.hasAlarm ? 'pending' : 'normal'
|
|
|
+ }))
|
|
|
+ } catch (e) { console.error(e) }
|
|
|
+}
|
|
|
+const currentDeviceCode = ref('')
|
|
|
+async function loadHistory() {
|
|
|
+ try {
|
|
|
+ const d = dateRange.value
|
|
|
+ const res = await getGasAlarmHistory(historyPage.value, historyPageSize, d?.[0] || '', d?.[1] || '', currentDeviceCode.value || '')
|
|
|
+ historyAlerts.value = (res.data?.records || []).map(mapAlarm)
|
|
|
+ historyTotal.value = res.data?.total || 0
|
|
|
+ } catch (e) { console.error(e) }
|
|
|
+}
|
|
|
+function selectEquipment(eq) {
|
|
|
+ selectedEquipment.value = eq
|
|
|
+ currentDeviceCode.value = eq.equipmentCode
|
|
|
+ historyPage.value = 1
|
|
|
+ loadHistory()
|
|
|
+}
|
|
|
+function mapAlarm(a) {
|
|
|
+ const lv = (a.alarmLevel === 1 || a.alarmLevelText === '严重') ? 'critical' : 'warning'
|
|
|
+ const status = (a.alarmStatus === 0 || a.statusText === '待处理') ? 'pending' : 'resolved'
|
|
|
+ return {
|
|
|
+ id: a.id, deviceCode: a.deviceCode,
|
|
|
+ stationName: a.stationName || a.deviceCode,
|
|
|
+ companyName: '沅陵燃气', companyId: 'yuanling',
|
|
|
+ address: a.address || '',
|
|
|
+ concentration: a.concentration || a.actualValue || 0,
|
|
|
+ threshold: a.threshold || a.maxValue || 20,
|
|
|
+ level: lv, alertTime: a.alarmTime || '', status,
|
|
|
+ resolveTime: a.resolveTime || a.handleTime || null,
|
|
|
+ handler: a.handler || a.handleUser || null,
|
|
|
+ remark: a.remark || a.handleRemark || null
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
-// 统计数据
|
|
|
-const totalStations = computed(() => {
|
|
|
- const all = [...realtimeAlerts.value, ...historyAlerts.value]
|
|
|
- return new Set(all.map(a => a.stationCode)).size
|
|
|
-})
|
|
|
const realtimeTrend = 1
|
|
|
-const todayHistoryCount = computed(() => historyAlerts.value.filter(a => a.alertTime.startsWith('2026-06-13')).length)
|
|
|
-const pendingCount = computed(() => historyAlerts.value.filter(a => a.status === 'pending').length)
|
|
|
-const maxConcentration = computed(() => {
|
|
|
- const all = [...realtimeAlerts.value, ...historyAlerts.value]
|
|
|
- return Math.max(...all.map(a => a.concentration))
|
|
|
-})
|
|
|
-const maxConcentrationPoint = computed(() => {
|
|
|
- const all = [...realtimeAlerts.value, ...historyAlerts.value]
|
|
|
- const max = Math.max(...all.map(a => a.concentration))
|
|
|
- return all.find(a => a.concentration === max)?.stationName || '—'
|
|
|
-})
|
|
|
-const avgResponseTime = computed(() => {
|
|
|
- const resolved = historyAlerts.value.filter(a => a.status === 'resolved' && a.resolveTime)
|
|
|
- if (!resolved.length) return '--'
|
|
|
- const times = resolved.map(a => {
|
|
|
- const start = new Date(a.alertTime)
|
|
|
- const end = new Date(a.resolveTime)
|
|
|
- return (end - start) / 60000
|
|
|
- })
|
|
|
- return Math.round(times.reduce((a, b) => a + b, 0) / times.length)
|
|
|
-})
|
|
|
+const todayHistoryCount = computed(() => equipments.value.filter(e => e.alertTime && e.alertTime.startsWith(new Date().toISOString().substring(0,10))).length)
|
|
|
+const pendingCount = computed(() => alarmStats.value.pendingCount)
|
|
|
+const maxConcentration = computed(() => alarmStats.value.maxConcentration)
|
|
|
+const maxConcentrationPoint = computed(() => alarmStats.value.maxConcentrationPoint || '—')
|
|
|
+const avgResponseTime = computed(() => '--')
|
|
|
|
|
|
// 筛选
|
|
|
const dateRange = ref([])
|
|
|
const companyFilter = ref('')
|
|
|
const historySearch = ref('')
|
|
|
-const realtimePage = ref(1)
|
|
|
-const historyPage = ref(1)
|
|
|
-const pageSize = 5
|
|
|
-const historyPageSize = 8
|
|
|
-
|
|
|
-const filteredHistory = computed(() => {
|
|
|
- let list = historyAlerts.value
|
|
|
- if (dateRange.value && dateRange.value.length === 2) {
|
|
|
- const start = new Date(dateRange.value[0])
|
|
|
- const end = new Date(dateRange.value[1])
|
|
|
- list = list.filter(a => {
|
|
|
- const alertDate = new Date(a.alertTime)
|
|
|
- return alertDate >= start && alertDate <= end
|
|
|
- })
|
|
|
- }
|
|
|
- if (companyFilter.value) {
|
|
|
- list = list.filter(a => a.companyId === companyFilter.value)
|
|
|
- }
|
|
|
- if (historySearch.value) {
|
|
|
- const kw = historySearch.value.toLowerCase()
|
|
|
- list = list.filter(a => a.stationName.toLowerCase().includes(kw) || a.address.toLowerCase().includes(kw))
|
|
|
- }
|
|
|
- return list
|
|
|
-})
|
|
|
-
|
|
|
-const filteredRealtime = computed(() => {
|
|
|
- let list = realtimeAlerts.value
|
|
|
- if (companyFilter.value) {
|
|
|
- list = list.filter(a => a.companyId === companyFilter.value)
|
|
|
- }
|
|
|
- return list
|
|
|
-})
|
|
|
-
|
|
|
-const paginatedRealtime = computed(() => {
|
|
|
- const start = (realtimePage.value - 1) * pageSize
|
|
|
- return filteredRealtime.value.slice(start, start + pageSize)
|
|
|
-})
|
|
|
-
|
|
|
-const paginatedHistory = computed(() => {
|
|
|
- const start = (historyPage.value - 1) * historyPageSize
|
|
|
- return filteredHistory.value.slice(start, start + historyPageSize)
|
|
|
-})
|
|
|
+const paginatedHistory = computed(() => historyAlerts.value)
|
|
|
|
|
|
// 当前选中报警
|
|
|
const currentAlert = ref(null)
|
|
|
@@ -387,33 +314,43 @@ const handleAlert = (alert) => {
|
|
|
detailDrawerVisible.value = true
|
|
|
}
|
|
|
|
|
|
-const resolveAlert = () => {
|
|
|
- if (currentAlert.value.status === 'pending') {
|
|
|
- // 从实时报警移到历史报警
|
|
|
- const index = realtimeAlerts.value.findIndex(a => a.id === currentAlert.value.id)
|
|
|
- if (index !== -1) {
|
|
|
- const resolved = { ...currentAlert.value, status: 'resolved', resolveTime: new Date().toLocaleString(), handler: '当前用户' }
|
|
|
- realtimeAlerts.value.splice(index, 1)
|
|
|
- historyAlerts.value.unshift(resolved)
|
|
|
- } else {
|
|
|
- // 如果是历史报警中的待处理
|
|
|
- const histIndex = historyAlerts.value.findIndex(a => a.id === currentAlert.value.id)
|
|
|
- if (histIndex !== -1) {
|
|
|
- historyAlerts.value[histIndex].status = 'resolved'
|
|
|
- historyAlerts.value[histIndex].resolveTime = new Date().toLocaleString()
|
|
|
- historyAlerts.value[histIndex].handler = '当前用户'
|
|
|
- }
|
|
|
- }
|
|
|
- currentAlert.value.status = 'resolved'
|
|
|
- ElMessage.success('已标记为已处理')
|
|
|
- detailDrawerVisible.value = false
|
|
|
+const resolveAlert = async () => {
|
|
|
+ if (currentAlert.value?.status === 'pending') {
|
|
|
+ try {
|
|
|
+ await resolveGasAlarm(currentAlert.value.id)
|
|
|
+ currentAlert.value.status = 'resolved'
|
|
|
+ ElMessage.success('已标记为已处理')
|
|
|
+ detailDrawerVisible.value = false
|
|
|
+ loadEquipment(); loadHistory(); loadStats()
|
|
|
+ } catch (e) { ElMessage.error('处理失败') }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const dispatchWorkOrder = async () => {
|
|
|
+ if (currentAlert.value?.id) {
|
|
|
+ try {
|
|
|
+ await dispatchGasAlarm(currentAlert.value.id)
|
|
|
+ ElMessage.success('已派发现场处置工单(伪)')
|
|
|
+ } catch (e) { ElMessage.error('派发失败') }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-const dispatchWorkOrder = () => {
|
|
|
- ElMessage.success('已派发现场处置工单')
|
|
|
+async function loadStats() {
|
|
|
+ try {
|
|
|
+ const res = await getGasAlarmStats()
|
|
|
+ if (res.data) {
|
|
|
+ alarmStats.value = res.data
|
|
|
+ totalStations.value = res.data.totalStations || 0
|
|
|
+ }
|
|
|
+ } catch (e) { console.error(e) }
|
|
|
}
|
|
|
|
|
|
+onMounted(async () => {
|
|
|
+ await loadEquipment()
|
|
|
+ await loadHistory()
|
|
|
+ await loadStats()
|
|
|
+})
|
|
|
+
|
|
|
const refreshData = () => {
|
|
|
ElMessage.success('数据已刷新')
|
|
|
}
|
|
|
@@ -422,9 +359,8 @@ const exportReport = () => {
|
|
|
ElMessage.success('导出报表(演示)')
|
|
|
}
|
|
|
|
|
|
-// 监听筛选变化重置页码
|
|
|
-watch([dateRange, companyFilter, historySearch], () => { historyPage.value = 1 })
|
|
|
-watch(companyFilter, () => { realtimePage.value = 1 })
|
|
|
+// 日期范围变化重载历史
|
|
|
+watch(dateRange, () => { historyPage.value = 1; loadHistory() })
|
|
|
|
|
|
onBeforeUnmount(() => {
|
|
|
try { chartInstance?.dispose() } catch {}
|
|
|
@@ -523,6 +459,7 @@ onBeforeUnmount(() => {
|
|
|
}
|
|
|
.alert-item.critical { background: #fff8f8; border: 1px solid #ffd4d4; }
|
|
|
.alert-item.warning { background: #fffef5; border: 1px solid #ffe4b5; }
|
|
|
+.alert-item.selected { background: #ecf5ff; border: 2px solid #409eff; }
|
|
|
.alert-item:hover { transform: translateX(2px); box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
|
|
|
.alert-level { flex-shrink: 0; }
|
|
|
.alert-content { flex: 1; }
|