|
|
@@ -9,25 +9,9 @@
|
|
|
<div class="stats-badge">
|
|
|
<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>
|
|
|
+ <span class="stat-item"><i class="dot"></i>最高浓度 {{ alarmStats.maxConcentration }}%LEL</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <div class="header-right">
|
|
|
- <el-date-picker
|
|
|
- v-model="dateRange"
|
|
|
- type="daterange"
|
|
|
- range-separator="至"
|
|
|
- start-placeholder="开始日期"
|
|
|
- end-placeholder="结束日期"
|
|
|
- style="width: 260px"
|
|
|
- size="default"
|
|
|
- />
|
|
|
- <el-select v-model="companyFilter" placeholder="燃气公司" clearable style="width: 140px" size="default">
|
|
|
- <el-option label="沅陵燃气" value="yuanling" />
|
|
|
- </el-select>
|
|
|
- <el-button type="primary" plain :icon="Refresh" @click="refreshData">刷新</el-button>
|
|
|
- <el-button :icon="Download" @click="exportReport">导出报表</el-button>
|
|
|
- </div>
|
|
|
</div>
|
|
|
|
|
|
<!-- KPI指标卡片 -->
|
|
|
@@ -43,14 +27,10 @@
|
|
|
<div class="kpi-trend">待处理 {{ pendingCount }}</div>
|
|
|
</div>
|
|
|
<div class="kpi-card">
|
|
|
- <div class="kpi-value">{{ maxConcentration }}<span class="unit">ppm</span></div>
|
|
|
+ <div class="kpi-value">{{ maxConcentration }}<span class="unit">%LEL</span></div>
|
|
|
<div class="kpi-label">最高浓度</div>
|
|
|
<div class="kpi-trend">点位: {{ maxConcentrationPoint }}</div>
|
|
|
</div>
|
|
|
- <div class="kpi-card">
|
|
|
- <div class="kpi-value">{{ avgResponseTime }}</div>
|
|
|
- <div class="kpi-label">平均响应时间(分钟)</div>
|
|
|
- </div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 主内容区:左侧实时报警 + 右侧历史报警 -->
|
|
|
@@ -58,7 +38,7 @@
|
|
|
<!-- 左侧设备列表 -->
|
|
|
<div class="alert-panel">
|
|
|
<div class="panel-header">
|
|
|
- <span><el-icon><BellFilled /></el-icon> 探测器设备</span>
|
|
|
+ <span><el-icon><BellFilled /></el-icon> 实时报警</span>
|
|
|
<el-tag type="danger" effect="dark">{{ alarmStats.realtimeCount }} 个报警</el-tag>
|
|
|
</div>
|
|
|
<div class="alert-list realtime">
|
|
|
@@ -70,21 +50,17 @@
|
|
|
@click="selectEquipment(eq)"
|
|
|
>
|
|
|
<div class="alert-level">
|
|
|
- <dict-tag v-if="eq.hasAlarm" :options="alarm_level" :value="eq.latestAlarmLevel" />
|
|
|
+ <el-tag v-if="eq.hasAlarm" :type="levelTagType(eq.latestAlarmLevel)" size="small">{{ levelLabel(eq.latestAlarmLevel) }}</el-tag>
|
|
|
<el-tag v-else type="info" size="small">正常</el-tag>
|
|
|
</div>
|
|
|
<div class="alert-content">
|
|
|
<div class="alert-title">{{ eq.equipmentName }}</div>
|
|
|
- <div class="alert-company">
|
|
|
- <el-icon><OfficeBuilding /></el-icon> 沅陵燃气
|
|
|
- </div>
|
|
|
<div class="alert-location">
|
|
|
<el-icon><Location /></el-icon> {{ eq.location }}
|
|
|
</div>
|
|
|
<div class="alert-data">
|
|
|
- <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>
|
|
|
+ <span class="data-item" v-if="eq.latestAlarmContent">{{ eq.latestAlarmContent }}</span>
|
|
|
+ <span class="data-item" v-else style="color:#67c23a">无报警记录</span>
|
|
|
</div>
|
|
|
<div class="alert-time" v-if="eq.alertTime">{{ eq.alertTime }}</div>
|
|
|
</div>
|
|
|
@@ -99,44 +75,54 @@
|
|
|
<div class="history-panel">
|
|
|
<div class="panel-header">
|
|
|
<span><el-icon><Document /></el-icon> 历史报警</span>
|
|
|
- <el-input
|
|
|
- v-model="historySearch"
|
|
|
- placeholder="搜索场站/地址"
|
|
|
- clearable
|
|
|
- :prefix-icon="Search"
|
|
|
- style="width: 180px"
|
|
|
- size="small"
|
|
|
- />
|
|
|
+ </div>
|
|
|
+ <div class="history-filter">
|
|
|
+ <el-form :inline="true" @submit.prevent>
|
|
|
+ <el-form-item label="设备编码">
|
|
|
+ <el-input
|
|
|
+ v-model="searchDeviceCode"
|
|
|
+ placeholder="请输入设备编码"
|
|
|
+ clearable
|
|
|
+ style="width: 160px"
|
|
|
+ @keyup.enter="onSearchHistory"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="报警时间">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="searchDateRange"
|
|
|
+ type="daterange"
|
|
|
+ range-separator="至"
|
|
|
+ start-placeholder="开始日期"
|
|
|
+ end-placeholder="结束日期"
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
+ style="width: 240px"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" @click="onSearchHistory">搜索</el-button>
|
|
|
+ <el-button @click="onResetHistory">重置</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
</div>
|
|
|
<div class="alert-list history">
|
|
|
- <el-table :data="paginatedHistory" style="width: 100%" @row-click="viewAlertDetail">
|
|
|
- <el-table-column prop="stationName" label="场站名称" min-width="140" show-overflow-tooltip />
|
|
|
- <el-table-column prop="companyName" label="燃气公司" width="100" />
|
|
|
+ <el-table :data="paginatedHistory" style="width: 100%">
|
|
|
+ <el-table-column prop="deviceCode" label="设备编码" width="130" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="stationName" label="设备名称" min-width="140" show-overflow-tooltip />
|
|
|
<el-table-column prop="address" label="位置" min-width="150" show-overflow-tooltip />
|
|
|
- <el-table-column prop="concentration" label="甲烷浓度(ppm)" width="120" sortable>
|
|
|
+ <el-table-column prop="alarmContent" label="报警描述" min-width="200" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="alarmLevel" label="报警等级" width="90" align="center">
|
|
|
<template #default="{ row }">
|
|
|
- <span :class="{ 'critical-value': row.concentration > 30 }">{{ row.concentration }}</span>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="threshold" label="阈值(ppm)" width="90" />
|
|
|
- <el-table-column prop="alarmLevel" label="级别" width="80" align="center">
|
|
|
- <template #default="{ row }">
|
|
|
- <dict-tag :options="alarm_level" :value="row.alarmLevel" />
|
|
|
+ <el-tag :type="levelTagType(row.alarmLevel)" size="small">{{ levelLabel(row.alarmLevel) }}</el-tag>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
<el-table-column prop="alertTime" label="报警时间" width="150" sortable />
|
|
|
- <el-table-column prop="status" label="状态" width="80" align="center">
|
|
|
+ <el-table-column prop="alarmStatus" label="状态" width="90" align="center">
|
|
|
<template #default="{ row }">
|
|
|
- <el-tag :type="row.status === 'resolved' ? 'success' : 'warning'" size="small">
|
|
|
- {{ row.status === 'resolved' ? '已处理' : '待处理' }}
|
|
|
+ <el-tag :type="Number(row.alarmStatus) === 1 ? 'success' : 'danger'" size="small">
|
|
|
+ {{ Number(row.alarmStatus) === 1 ? '已处理' : '未处理' }}
|
|
|
</el-tag>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column label="操作" width="80" fixed="right" align="center">
|
|
|
- <template #default="{ row }">
|
|
|
- <el-button link type="primary" size="small" @click.stop="viewAlertDetail(row)">详情</el-button>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
</el-table>
|
|
|
</div>
|
|
|
<div class="pagination-area">
|
|
|
@@ -154,61 +140,30 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <!-- 报警详情抽屉 -->
|
|
|
- <el-drawer v-model="detailDrawerVisible" :title="`报警详情 - ${currentAlert?.stationName}`" size="480px" direction="rtl" @opened="onDrawerOpened" class="detail-drawer">
|
|
|
- <div v-if="currentAlert" class="alert-detail">
|
|
|
- <el-descriptions :column="1" border>
|
|
|
- <el-descriptions-item label="场站名称">{{ currentAlert.stationName }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="场站编号">{{ currentAlert.stationCode }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="燃气公司">{{ currentAlert.companyName }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="详细地址">{{ currentAlert.address }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="报警级别">
|
|
|
- <dict-tag :options="alarm_level" :value="currentAlert.alarmLevel" />
|
|
|
- </el-descriptions-item>
|
|
|
- <el-descriptions-item label="甲烷浓度">
|
|
|
- <span :class="{ 'critical-value': currentAlert.concentration > 30 }">{{ currentAlert.concentration }} ppm</span>
|
|
|
- </el-descriptions-item>
|
|
|
- <el-descriptions-item label="报警阈值">{{ currentAlert.threshold }} ppm</el-descriptions-item>
|
|
|
- <el-descriptions-item label="报警时间">{{ currentAlert.alertTime }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="处理状态">
|
|
|
- <el-tag :type="currentAlert.status === 'resolved' ? 'success' : 'danger'">
|
|
|
- {{ currentAlert.status === 'resolved' ? '已处理' : '待处理' }}
|
|
|
- </el-tag>
|
|
|
- </el-descriptions-item>
|
|
|
- <el-descriptions-item label="备注">{{ currentAlert.remark || '—' }}</el-descriptions-item>
|
|
|
- </el-descriptions>
|
|
|
-
|
|
|
- <!-- 浓度趋势图 -->
|
|
|
- <div class="trend-section">
|
|
|
- <div class="section-title">甲烷浓度趋势(最近1小时)</div>
|
|
|
- <div ref="chartRef" class="trend-chart"></div>
|
|
|
- </div>
|
|
|
|
|
|
- <!-- 处理操作 -->
|
|
|
- <div class="action-section" v-if="currentAlert.status === 'pending'">
|
|
|
- <el-button type="primary" @click="resolveAlert">标记已处理</el-button>
|
|
|
- <el-button
|
|
|
- type="warning"
|
|
|
- :loading="dispatching"
|
|
|
- :disabled="currentAlert.hasWorkOrder"
|
|
|
- @click="dispatchWorkOrder"
|
|
|
- >{{ currentAlert.hasWorkOrder ? `已关联工单${currentAlert.workOrderStatusText ? `(${currentAlert.workOrderStatusText})` : ''}` : '派发现场工单' }}</el-button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </el-drawer>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref, computed, watch, nextTick, onBeforeUnmount, onMounted } from 'vue'
|
|
|
-import { Warning, Refresh, Download, BellFilled, Document, Location, Search, OfficeBuilding } from '@element-plus/icons-vue'
|
|
|
+import { ref, computed, onMounted } from 'vue'
|
|
|
+import { Warning, BellFilled, Document, Location, OfficeBuilding } from '@element-plus/icons-vue'
|
|
|
import { useDict } from '@/utils/dict'
|
|
|
-import * as echarts from 'echarts'
|
|
|
-import { ElMessage } from 'element-plus'
|
|
|
-import { getGasAlarmEquipment, getGasAlarmHistory, getGasAlarmStats, resolveGasAlarm, dispatchGasAlarm } from '@/api/pipeNetwork/basic'
|
|
|
+import { getGasAlarmEquipment, getGasAlarmHistory, getGasAlarmStats } from '@/api/pipeNetwork/basic'
|
|
|
|
|
|
+// 报警等级字典(alarm_data.alarm_level),界面展示均关联该字典
|
|
|
const { alarm_level } = useDict('alarm_level')
|
|
|
|
|
|
+// 报警等级配色:与系统 alarm 模块保持一致(参考 pipeNetwork/pinteControl/realTimeShow.vue:1红 2橙 3蓝 4绿)
|
|
|
+const ALARM_LEVEL_TAG = { 1: 'danger', 2: 'warning', 3: 'primary', 4: 'success' }
|
|
|
+function levelTagType(level) {
|
|
|
+ return ALARM_LEVEL_TAG[level] || 'info'
|
|
|
+}
|
|
|
+// 报警等级名称关联字典
|
|
|
+function levelLabel(level) {
|
|
|
+ const found = alarm_level.value.find(item => Number(item.value) === Number(level))
|
|
|
+ return found ? found.label : (level != null && level !== '' ? level + '级' : '—')
|
|
|
+}
|
|
|
+
|
|
|
// 左侧: 所有探测器设备(去重)
|
|
|
const equipments = ref([])
|
|
|
const selectedEquipment = ref(null)
|
|
|
@@ -218,7 +173,10 @@ const historyPage = ref(1)
|
|
|
const historyPageSize = 8
|
|
|
const historyTotal = ref(0)
|
|
|
const alarmStats = ref({ realtimeCount: 0, pendingCount: 0, maxConcentration: 0, totalStations: 0 })
|
|
|
-const dispatching = ref(false)
|
|
|
+
|
|
|
+// 历史报警搜索条件
|
|
|
+const searchDeviceCode = ref('')
|
|
|
+const searchDateRange = ref(null)
|
|
|
|
|
|
async function loadEquipment() {
|
|
|
try {
|
|
|
@@ -244,21 +202,34 @@ async function loadEquipment() {
|
|
|
const currentDeviceCode = ref('')
|
|
|
async function loadHistory() {
|
|
|
try {
|
|
|
- const d = dateRange.value
|
|
|
- const res = await getGasAlarmHistory(historyPage.value, historyPageSize, d?.[0] || '', d?.[1] || '', currentDeviceCode.value || '')
|
|
|
+ const startDate = searchDateRange.value?.[0] || ''
|
|
|
+ const endDate = searchDateRange.value?.[1] || ''
|
|
|
+ const res = await getGasAlarmHistory(historyPage.value, historyPageSize, startDate, endDate, currentDeviceCode.value || '')
|
|
|
historyAlerts.value = (res.data?.records || []).map(mapAlarm)
|
|
|
historyTotal.value = res.data?.total || 0
|
|
|
} catch (e) { console.error(e) }
|
|
|
}
|
|
|
+function onSearchHistory() {
|
|
|
+ historyPage.value = 1
|
|
|
+ currentDeviceCode.value = searchDeviceCode.value.trim()
|
|
|
+ loadHistory()
|
|
|
+}
|
|
|
+function onResetHistory() {
|
|
|
+ searchDeviceCode.value = ''
|
|
|
+ searchDateRange.value = null
|
|
|
+ currentDeviceCode.value = ''
|
|
|
+ historyPage.value = 1
|
|
|
+ loadHistory()
|
|
|
+}
|
|
|
function selectEquipment(eq) {
|
|
|
selectedEquipment.value = eq
|
|
|
currentDeviceCode.value = eq.equipmentCode
|
|
|
+ searchDeviceCode.value = eq.equipmentCode
|
|
|
historyPage.value = 1
|
|
|
loadHistory()
|
|
|
}
|
|
|
function mapAlarm(a) {
|
|
|
const lv = String(a.alarmLevel) === '1' ? 'critical' : 'warning'
|
|
|
- const status = String(a.alarmStatus) === '0' ? 'pending' : 'resolved'
|
|
|
return {
|
|
|
id: a.id, deviceCode: a.deviceCode,
|
|
|
stationName: a.stationName || a.deviceCode,
|
|
|
@@ -267,7 +238,9 @@ function mapAlarm(a) {
|
|
|
concentration: a.concentration || a.actualValue || 0,
|
|
|
threshold: a.threshold || a.maxValue || 20,
|
|
|
alarmLevel: a.alarmLevel,
|
|
|
- level: lv, alertTime: a.createTime || '', status,
|
|
|
+ alarmContent: a.alarmContent || '',
|
|
|
+ alarmStatus: a.alarmStatus,
|
|
|
+ level: lv, alertTime: a.createTime || '',
|
|
|
remark: a.remark || null,
|
|
|
hasWorkOrder: Boolean(a.hasWorkOrder), workOrderId: a.workOrderId || null,
|
|
|
workOrderStatus: a.workOrderStatus ?? null, workOrderStatusText: a.workOrderStatusText || ''
|
|
|
@@ -279,85 +252,10 @@ const todayHistoryCount = computed(() => equipments.value.filter(e => e.alertTim
|
|
|
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 paginatedHistory = computed(() => historyAlerts.value)
|
|
|
|
|
|
-// 当前选中报警
|
|
|
-const currentAlert = ref(null)
|
|
|
-const detailDrawerVisible = ref(false)
|
|
|
-const chartRef = ref(null)
|
|
|
-let chartInstance = null
|
|
|
-
|
|
|
-function renderChart() {
|
|
|
- if (!chartRef.value || !currentAlert.value?.trendData) return
|
|
|
- if (!chartInstance) {
|
|
|
- chartInstance = echarts.init(chartRef.value)
|
|
|
- }
|
|
|
- const times = Array.from({ length: 24 }, (_, i) => `${i}:00`)
|
|
|
- const threshold = currentAlert.value.threshold
|
|
|
- chartInstance.setOption({
|
|
|
- tooltip: { trigger: 'axis' },
|
|
|
- xAxis: { type: 'category', data: times, axisLabel: { rotate: 45, interval: 4 } },
|
|
|
- yAxis: { type: 'value', name: '甲烷浓度 (ppm)' },
|
|
|
- series: [
|
|
|
- { name: '甲烷浓度', type: 'line', data: currentAlert.value.trendData, smooth: true, lineStyle: { color: '#f56c6c', width: 2 }, areaStyle: { opacity: 0.1 } },
|
|
|
- { name: '报警阈值', type: 'line', data: Array(24).fill(threshold), lineStyle: { color: '#e6a23c', width: 2, type: 'dashed' }, symbol: 'none' }
|
|
|
- ]
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-function onDrawerOpened() {
|
|
|
- nextTick(() => renderChart())
|
|
|
-}
|
|
|
-
|
|
|
-// 方法
|
|
|
-const viewAlertDetail = (alert) => {
|
|
|
- currentAlert.value = alert
|
|
|
- detailDrawerVisible.value = true
|
|
|
-}
|
|
|
-
|
|
|
-const handleAlert = (alert) => {
|
|
|
- currentAlert.value = alert
|
|
|
- detailDrawerVisible.value = true
|
|
|
-}
|
|
|
-
|
|
|
-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 || currentAlert.value.hasWorkOrder || dispatching.value) return
|
|
|
- dispatching.value = true
|
|
|
- try {
|
|
|
- const res = await dispatchGasAlarm(currentAlert.value.id, {
|
|
|
- orderDesc: `${currentAlert.value.stationName || '可燃气体'}浓度报警,请现场核实处理`
|
|
|
- })
|
|
|
- currentAlert.value.hasWorkOrder = true
|
|
|
- currentAlert.value.workOrderId = res.data || null
|
|
|
- currentAlert.value.workOrderStatusText = '待派单'
|
|
|
- currentAlert.value.status = 'resolved'
|
|
|
- ElMessage.success(res.msg || '报警已转为待派单工单')
|
|
|
- await Promise.all([loadEquipment(), loadHistory(), loadStats()])
|
|
|
- } catch (e) {
|
|
|
- ElMessage.error(e?.message || '工单派发失败')
|
|
|
- } finally {
|
|
|
- dispatching.value = false
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
async function loadStats() {
|
|
|
try {
|
|
|
const res = await getGasAlarmStats()
|
|
|
@@ -373,21 +271,6 @@ onMounted(async () => {
|
|
|
await loadStats()
|
|
|
})
|
|
|
|
|
|
-const refreshData = () => {
|
|
|
- ElMessage.success('数据已刷新')
|
|
|
-}
|
|
|
-
|
|
|
-const exportReport = () => {
|
|
|
- ElMessage.success('导出报表(演示)')
|
|
|
-}
|
|
|
-
|
|
|
-// 日期范围变化重载历史
|
|
|
-watch(dateRange, () => { historyPage.value = 1; loadHistory() })
|
|
|
-
|
|
|
-onBeforeUnmount(() => {
|
|
|
- try { chartInstance?.dispose() } catch {}
|
|
|
- chartInstance = null
|
|
|
-})
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
@@ -416,11 +299,9 @@ onBeforeUnmount(() => {
|
|
|
.stat-item .dot { width: 8px; height: 8px; border-radius: 50%; background: #67c23a; }
|
|
|
.stat-item.critical .dot { background: #f56c6c; }
|
|
|
.stat-item.warning .dot { background: #e6a23c; }
|
|
|
-.header-right { display: flex; gap: 12px; flex-wrap: wrap; }
|
|
|
-
|
|
|
.kpi-cards {
|
|
|
display: grid;
|
|
|
- grid-template-columns: repeat(4, 1fr);
|
|
|
+ grid-template-columns: repeat(3, 1fr);
|
|
|
gap: 16px;
|
|
|
margin-bottom: 20px;
|
|
|
}
|
|
|
@@ -437,6 +318,14 @@ onBeforeUnmount(() => {
|
|
|
.kpi-label { font-size: 13px; color: #909399; margin-top: 4px; }
|
|
|
.kpi-trend { font-size: 11px; color: #67c23a; margin-top: 4px; }
|
|
|
|
|
|
+.history-filter {
|
|
|
+ padding: 12px 16px;
|
|
|
+ background: #fafafa;
|
|
|
+ border-radius: 8px;
|
|
|
+ margin-bottom: 12px;
|
|
|
+}
|
|
|
+.history-filter .el-form-item { margin-bottom: 0; }
|
|
|
+
|
|
|
.main-layout {
|
|
|
display: flex;
|
|
|
gap: 20px;
|
|
|
@@ -496,10 +385,6 @@ onBeforeUnmount(() => {
|
|
|
.empty-state { padding: 40px 0; text-align: center; }
|
|
|
.pagination-area { padding: 12px; text-align: center; border-top: 1px solid #eee; }
|
|
|
|
|
|
-.alert-detail { padding: 8px; }
|
|
|
-.section-title { font-weight: 600; margin: 16px 0 12px 0; }
|
|
|
-.trend-chart { height: 200px; }
|
|
|
-.action-section { margin-top: 20px; display: flex; gap: 12px; justify-content: flex-end; }
|
|
|
/* 历史记录分页:沿用若依分页组件,去掉其自带的底色与上下留白 */
|
|
|
.history-pagination :deep(.pagination-container) {
|
|
|
padding: 12px 0 0;
|