|
|
@@ -1,626 +0,0 @@
|
|
|
-<template>
|
|
|
- <div class="water-page alarm-page">
|
|
|
- <el-card shadow="never" class="water-panel map-card">
|
|
|
- <WaterGisPageShell
|
|
|
- embedded
|
|
|
- eyebrow="WATER SUPPLY ALARM GIS"
|
|
|
- title="监测报警一张图"
|
|
|
- subtitle="按监测点状态着色"
|
|
|
- :loading="loading"
|
|
|
- >
|
|
|
- <template #actions>
|
|
|
- <el-input v-model="query.keyword" class="search-input" placeholder="搜索设备/报警类型/位置" clearable
|
|
|
- :prefix-icon="Search"/>
|
|
|
- <el-button :icon="Refresh" @click="loadData">刷新</el-button>
|
|
|
- <el-button :icon="Aim" @click="fitAlarmMap">全图</el-button>
|
|
|
- </template>
|
|
|
- <template #metrics>
|
|
|
- <div class="metric">
|
|
|
- <div class="metric-icon"><el-icon><Bell/></el-icon></div>
|
|
|
- <div class="metric-body">
|
|
|
- <span>报警点位</span>
|
|
|
- <div class="metric-value"><strong>{{ mapStatistics.total }}</strong><small>个</small></div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="metric">
|
|
|
- <div class="metric-icon"><el-icon><Warning/></el-icon></div>
|
|
|
- <div class="metric-body">
|
|
|
- <span>高等级报警</span>
|
|
|
- <div class="metric-value"><strong class="danger">{{ mapStatistics.highLevel }}</strong><small>个</small></div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="metric">
|
|
|
- <div class="metric-icon"><el-icon><Tickets/></el-icon></div>
|
|
|
- <div class="metric-body">
|
|
|
- <span>有关联工单</span>
|
|
|
- <div class="metric-value"><strong class="primary">{{ mapStatistics.byStatus.workOrder }}</strong><small>个</small></div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="metric">
|
|
|
- <div class="metric-icon"><el-icon><Location/></el-icon></div>
|
|
|
- <div class="metric-body">
|
|
|
- <span>离线点位</span>
|
|
|
- <div class="metric-value"><strong>{{ mapStatistics.byStatus.offline }}</strong><small>个</small></div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <template #controls>
|
|
|
- <div class="panel-section alarm-detail">
|
|
|
- <h3>选中报警</h3>
|
|
|
- <template v-if="selectedPoint.id || selectedPoint.alarmId">
|
|
|
- <div class="detail-name">{{ selectedPoint.deviceName || selectedPoint.deviceCode || selectedPoint.name || '报警点' }}</div>
|
|
|
- <el-descriptions :column="1" border size="small">
|
|
|
- <el-descriptions-item label="报警等级">
|
|
|
- <el-tag :type="levelType(selectedPoint.alarmLevel)">{{ selectedPoint.alarmLevelText || levelText(selectedPoint.alarmLevel) }}</el-tag>
|
|
|
- </el-descriptions-item>
|
|
|
- <el-descriptions-item label="报警类型">{{ selectedPoint.warningType || selectedPoint.alarmType || '-' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="触发时间">{{ selectedPoint.createTime || '-' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="点位状态">
|
|
|
- <el-tag :type="statusTagType(selectedPoint)">{{ statusText(selectedPoint) }}</el-tag>
|
|
|
- </el-descriptions-item>
|
|
|
- <el-descriptions-item label="设备报警状态">
|
|
|
- <el-tag :type="selectedPoint.deviceAlarmStatus === undefined ? 'info' : Number(selectedPoint.deviceAlarmStatus) === 1 ? 'danger' : 'success'">
|
|
|
- {{ selectedPoint.deviceAlarmStatusText || '未获取' }}
|
|
|
- </el-tag>
|
|
|
- </el-descriptions-item>
|
|
|
- <el-descriptions-item label="报警记录状态">
|
|
|
- <el-tag :type="selectedPoint.recordStatus === undefined ? 'info' : Number(selectedPoint.recordStatus) === 1 ? 'success' : 'danger'">
|
|
|
- {{ selectedPoint.recordStatusText || '未获取' }}
|
|
|
- </el-tag>
|
|
|
- </el-descriptions-item>
|
|
|
- <el-descriptions-item label="关联工单">
|
|
|
- <span>{{ selectedPoint.orderNo || '-' }}</span>
|
|
|
- <span v-if="selectedPoint.orderStatus !== null && selectedPoint.orderStatus !== undefined">
|
|
|
- · {{ orderStatusText(selectedPoint.orderStatus) }}
|
|
|
- </span>
|
|
|
- </el-descriptions-item>
|
|
|
- <el-descriptions-item label="位置">{{ selectedPoint.equipmentLocation || selectedPoint.location || '-' }}</el-descriptions-item>
|
|
|
- </el-descriptions>
|
|
|
- <el-button class="detail-action" type="primary" link @click="openDetail(selectedPoint)">查看完整详情</el-button>
|
|
|
- </template>
|
|
|
- <el-empty v-else description="点击点位查看详情" :image-size="65"/>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <template #legend>
|
|
|
- <div class="panel-section">
|
|
|
- <h3>点位状态与图例</h3>
|
|
|
- <div class="map-legend">
|
|
|
- <div class="map-legend-item"><i class="map-legend-dot status-dot-work-order"/>关联工单 {{ mapStatistics.byStatus.workOrder }}</div>
|
|
|
- <div class="map-legend-item"><i class="map-legend-dot status-dot-alarm"/>报警 {{ mapStatistics.byStatus.alarm }}</div>
|
|
|
- <div class="map-legend-item"><i class="map-legend-dot status-dot-warning"/>预警 {{ mapStatistics.byStatus.warning }}</div>
|
|
|
- <div class="map-legend-item"><i class="map-legend-dot status-dot-offline"/>离线 {{ mapStatistics.byStatus.offline }}</div>
|
|
|
- <div class="map-legend-item"><i class="map-legend-dot status-dot-normal"/>正常 {{ mapStatistics.byStatus.normal }}</div>
|
|
|
- </div>
|
|
|
- <p class="muted legend-note">同一设备多点位合并,按关联工单 > 报警 > 预警 > 离线 > 正常取最高风险状态。</p>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <template #results>
|
|
|
- <div class="panel-section result-section">
|
|
|
- <h3>搜索报警</h3>
|
|
|
- <el-alert v-if="mapLoadError" type="error" show-icon :closable="false" :title="mapLoadError">
|
|
|
- <el-button link type="primary" @click="loadData">重试</el-button>
|
|
|
- </el-alert>
|
|
|
- <el-alert v-if="mapEnrichmentWarning" type="warning" show-icon :closable="false"
|
|
|
- :title="`${mapEnrichmentWarning}未能补全,点位状态按报警记录展示`"/>
|
|
|
- <el-empty v-if="!mapLoadError && !mapPoints.length" description="暂无报警点位" :image-size="72"/>
|
|
|
- <el-empty v-if="!mapLoadError && mapPoints.length && !filteredAlarmPoints.length" description="当前筛选下没有匹配的报警点位" :image-size="72"/>
|
|
|
- <div v-if="!mapLoadError && filteredAlarmPoints.length" class="device-result-list">
|
|
|
- <button v-for="point in filteredAlarmPoints" :key="point.id || point.alarmId || point.deviceCode"
|
|
|
- type="button" @click="locateAlarmPoint(point)">
|
|
|
- <span class="device-name">{{ point.deviceName || point.deviceCode || point.name || '报警点' }}</span>
|
|
|
- <span class="device-meta">{{ point.warningType || point.alarmType || '监测报警' }} · {{ point.createTime || '-' }}</span>
|
|
|
- <el-tag size="small" :type="statusTagType(point)">
|
|
|
- {{ statusText(point) }}
|
|
|
- </el-tag>
|
|
|
- </button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <template #map>
|
|
|
- <GisMapCanvas
|
|
|
- ref="gisMapRef"
|
|
|
- :points="gisMapPoints"
|
|
|
- cluster-label="报警集合"
|
|
|
- :center="{ lng: 110.3965, lat: 28.4638 }"
|
|
|
- :zoom="15"
|
|
|
- @ready="handleAlarmMapReady"
|
|
|
- @point-click="locateAlarmPoint"
|
|
|
- @map-click="detailVisible = false"
|
|
|
- @error="mapError = $event"
|
|
|
- />
|
|
|
- <el-alert v-if="mapError" class="map-error" type="warning" show-icon :closable="false" :title="mapError"/>
|
|
|
- <el-alert v-if="mapLoadError" class="map-error" type="error" show-icon :closable="false" :title="mapLoadError">
|
|
|
- <el-button link type="primary" @click="loadData">重试</el-button>
|
|
|
- </el-alert>
|
|
|
- <el-empty v-else-if="!loading && !gisMapPoints.length && !mapError" class="map-empty" description="当前筛选下没有可显示的报警点位"/>
|
|
|
- </template>
|
|
|
- </WaterGisPageShell>
|
|
|
- </el-card>
|
|
|
-
|
|
|
- <el-drawer v-model="detailVisible" title="报警详情" size="420px"
|
|
|
- direction="rtl" class="detail-drawer alarm-map-detail-drawer" append-to-body>
|
|
|
- <div v-loading="detailLoading" class="device-detail">
|
|
|
- <el-alert v-if="detailError" type="error" show-icon :closable="false" title="报警详情加载失败,请重试"/>
|
|
|
- <template v-if="detail && (detail.id || detail.alarmId)">
|
|
|
- <section class="detail-hero">
|
|
|
- <h3>{{ detail.deviceName || detail.deviceCode || '报警点' }}</h3>
|
|
|
- <p>{{ detail.warningType || '报警事件' }} · {{ detail.createTime || '-' }}</p>
|
|
|
- <div class="status-tags">
|
|
|
- <el-tag :type="levelType(detail.alarmLevel)">{{ detail.alarmLevelText || levelText(detail.alarmLevel) }}</el-tag>
|
|
|
- <el-tag :type="statusTagType(detail)">{{ statusText(detail) }}</el-tag>
|
|
|
- <el-tag :type="Number(detail.deviceAlarmStatus) === 1 ? 'danger' : 'success'">
|
|
|
- 设备报警状态:{{ detail.deviceAlarmStatusText || '未获取' }}
|
|
|
- </el-tag>
|
|
|
- <el-tag :type="Number(detail.recordStatus) === 1 ? 'success' : 'danger'">
|
|
|
- 报警记录状态:{{ detail.recordStatusText || '未获取' }}
|
|
|
- </el-tag>
|
|
|
- </div>
|
|
|
- </section>
|
|
|
- <section class="detail-section">
|
|
|
- <h4>报警信息</h4>
|
|
|
- <dl>
|
|
|
- <template v-for="field in detailFields" :key="field.label">
|
|
|
- <dt>{{ field.label }}</dt>
|
|
|
- <dd>{{ field.value }}</dd>
|
|
|
- </template>
|
|
|
- </dl>
|
|
|
- </section>
|
|
|
- </template>
|
|
|
- <el-empty v-else-if="!detailLoading && !detailError" description="暂无报警详情"/>
|
|
|
- </div>
|
|
|
- </el-drawer>
|
|
|
-
|
|
|
- </div>
|
|
|
-</template>
|
|
|
-<script setup>
|
|
|
-import {computed, nextTick, onMounted, ref} from 'vue'
|
|
|
-import {ElMessage} from 'element-plus'
|
|
|
-import {Aim, Bell, Location, Refresh, Search, Tickets, Warning} from '@element-plus/icons-vue'
|
|
|
-import {getAlarmDetail, getDeviceGisAllPoints, listAlarmLedger, listAlarmMapPoints} from '@/api/pipeNetwork/waterSupply'
|
|
|
- import GisMapCanvas from '@/components/GisMapCanvas.vue'
|
|
|
- import WaterGisPageShell from '@/components/WaterGisPageShell.vue'
|
|
|
- import {
|
|
|
- getMonitoringPointIcon,
|
|
|
- getMonitoringPointLabel,
|
|
|
- getMonitoringPointTagType
|
|
|
- } from '@/utils/gisMapColors'
|
|
|
- import {
|
|
|
- buildAlarmGisPoints,
|
|
|
- filterAlarmGisPoints,
|
|
|
- summarizeAlarmGisPoints
|
|
|
- } from '@/utils/waterAlarmGisModel'
|
|
|
- import { normalizeDeviceGisPoints, normalizeDeviceRow } from '@/utils/waterSupplyUiModel'
|
|
|
- import {normalizeMapPoints, normalizePage, unwrapResponse} from '@/utils/waterSupplyModel'
|
|
|
- import {getRequestErrorMessage} from '@/utils/waterFacilityQuery'
|
|
|
- import {orderStatusText} from '@/views/subSystem/pipeNetwork/ops/workOrder/workOrderDict.js'
|
|
|
-
|
|
|
-const props = defineProps({title: {type: String, default: '监测报警一张图'}})
|
|
|
-const query = ref({keyword: ''})
|
|
|
-const detailLoading = ref(false), detailError = ref('')
|
|
|
- const mapPoints = ref([]);
|
|
|
- const mapError = ref('')
|
|
|
- // 报警一张图的数据加载失败与状态补全降级分别提示,避免地图空白时用户无从判断
|
|
|
- const mapLoadError = ref('')
|
|
|
- const mapEnrichmentWarning = ref('')
|
|
|
- const mapReady = ref(false)
|
|
|
- const gisMapRef = ref(null)
|
|
|
- const loading = ref(false);
|
|
|
- const detailVisible = ref(false);
|
|
|
- const detail = ref({});
|
|
|
- const selectedPoint = computed(() => detail.value || {});
|
|
|
- const filteredAlarmPoints = computed(() => filterAlarmGisPoints(
|
|
|
- mapPoints.value,
|
|
|
- query.value.keyword
|
|
|
- ));
|
|
|
-
|
|
|
- const gisMapPoints = computed(() => filteredAlarmPoints.value.map(point => ({
|
|
|
- ...point,
|
|
|
- name: point.deviceName || point.deviceCode || point.name || '报警点',
|
|
|
- iconUrl: getMonitoringPointIcon(point)
|
|
|
- })))
|
|
|
-
|
|
|
- /** 左侧状态总览:五态数量来自点位推导结果,与地图着色使用同一口径 */
|
|
|
- const mapStatistics = computed(() => summarizeAlarmGisPoints(filteredAlarmPoints.value));
|
|
|
- const statusText = point => getMonitoringPointLabel(point);
|
|
|
- const statusTagType = point => getMonitoringPointTagType(point);
|
|
|
- /**
|
|
|
- * 详情抽屉字段:逐条报警流水,标签统一中文;
|
|
|
- * 工单状态复用统一工单字典,避免页面自带一套状态码文本。
|
|
|
- */
|
|
|
- const detailFields = computed(() => {
|
|
|
- const row = detail.value || {}
|
|
|
- const pairs = [
|
|
|
- ['报警编码', row.warningCode],
|
|
|
- ['报警时间', row.createTime],
|
|
|
- ['报警类型', row.warningType],
|
|
|
- ['设备名称', row.deviceName],
|
|
|
- ['设备编码', row.deviceCode],
|
|
|
- ['设备类型', row.deviceType],
|
|
|
- ['安装位置', row.equipmentLocation],
|
|
|
- ['报警参数值', row.actualValue],
|
|
|
- ['阈值区间', thresholdRange(row)],
|
|
|
- ['偏差率', deviationText(row)],
|
|
|
- ['报警等级', row.alarmLevelText || levelText(row.alarmLevel)],
|
|
|
- ['报警记录状态', row.recordStatusText || row.alarmStatusText],
|
|
|
- ['设备报警状态', row.deviceAlarmStatusText],
|
|
|
- ['设备当前状态', row.currentStatusText],
|
|
|
- ['在线状态', row.onlineStatusText],
|
|
|
- ['报警展示状态', row.displayStatusText],
|
|
|
- ['关联工单号', row.orderNo],
|
|
|
- ['工单状态', row.orderStatus === null || row.orderStatus === undefined ? '' : orderStatusText(row.orderStatus)],
|
|
|
- ['备注', row.remark],
|
|
|
- ['经度', row.longitude],
|
|
|
- ['纬度', row.latitude]
|
|
|
- ]
|
|
|
- return pairs
|
|
|
- .filter(([, value]) => value !== undefined && value !== null && value !== '')
|
|
|
- .map(([label, value]) => ({label, value}))
|
|
|
- })
|
|
|
- const levelText = value => ({1: '一级报警', 2: '二级报警', 3: '三级报警', 4: '四级报警'}[value] || '未知等级');
|
|
|
- const levelType = value => ({1: 'danger', 2: 'warning', 3: 'primary', 4: 'success'}[value] || 'info');
|
|
|
- /** 阈值区间文本:下限 ~ 上限;单边阈值时显示对应的比较方向 */
|
|
|
- const thresholdRange = row => {
|
|
|
- const hasMin = row.minValue !== null && row.minValue !== undefined && row.minValue !== '';
|
|
|
- const hasMax = row.maxValue !== null && row.maxValue !== undefined && row.maxValue !== '';
|
|
|
- if (hasMin && hasMax) return `${row.minValue} ~ ${row.maxValue}`;
|
|
|
- if (hasMin) return `≥ ${row.minValue}`;
|
|
|
- if (hasMax) return `≤ ${row.maxValue}`;
|
|
|
- return '未配置阈值';
|
|
|
- };
|
|
|
- const deviationText = row => {
|
|
|
- const value = Number(row.deviationRatio);
|
|
|
- return Number.isFinite(value) ? `${value.toFixed(2)}%` : '-';
|
|
|
- };
|
|
|
-async function loadData() {
|
|
|
- loading.value = true; mapLoadError.value = ''; mapEnrichmentWarning.value = ''
|
|
|
- try { await loadAlarmMapData() }
|
|
|
- catch (error) { mapLoadError.value = getRequestErrorMessage(error, '报警点位加载失败,请重试'); mapPoints.value = []; ElMessage.error(mapLoadError.value) }
|
|
|
- finally { loading.value = false }
|
|
|
-}
|
|
|
- /** 报警一张图台账补全条数:与列表页共用同一台账接口,避免同一状态出现两套口径 */
|
|
|
- const ALARM_MAP_LEDGER_PAGE_SIZE = 200
|
|
|
- const ALARM_MAP_LEDGER_MAX_PAGES = 50
|
|
|
-
|
|
|
- async function loadAllAlarmLedgerRows() {
|
|
|
- const collected = []
|
|
|
- let total = 0
|
|
|
- for (let pageNum = 1; pageNum <= ALARM_MAP_LEDGER_MAX_PAGES; pageNum += 1) {
|
|
|
- const response = await listAlarmLedger({ pageNum, pageSize: ALARM_MAP_LEDGER_PAGE_SIZE })
|
|
|
- const ledgerPage = normalizePage(response)
|
|
|
- collected.push(...ledgerPage.records)
|
|
|
- total = ledgerPage.total
|
|
|
- if (!ledgerPage.records.length || collected.length >= ledgerPage.total) {
|
|
|
- if (collected.length < total) throw new Error('报警台账分页未完整返回')
|
|
|
- return collected
|
|
|
- }
|
|
|
- }
|
|
|
- throw new Error('报警台账分页超过安全上限')
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 报警点位取自专用点位接口,再用报警台账补全设备/记录/工单状态。
|
|
|
- * 台账或设备点位不可用时只降级对应状态来源,不影响报警点位本身渲染。
|
|
|
- */
|
|
|
- async function loadAlarmMapData() {
|
|
|
- const [pointsResult, ledgerResult, deviceResult] = await Promise.allSettled([
|
|
|
- listAlarmMapPoints(),
|
|
|
- loadAllAlarmLedgerRows(),
|
|
|
- getDeviceGisAllPoints()
|
|
|
- ])
|
|
|
- if (pointsResult.status === 'rejected') throw pointsResult.reason
|
|
|
-
|
|
|
- const alarmPoints = normalizeMapPoints(unwrapResponse(pointsResult.value) || [])
|
|
|
- const ledgerRows = ledgerResult.status === 'fulfilled' ? ledgerResult.value : []
|
|
|
- const devicePoints = deviceResult.status === 'fulfilled'
|
|
|
- ? normalizeDeviceGisPoints(deviceResult.value).map(normalizeDeviceRow)
|
|
|
- : []
|
|
|
- mapPoints.value = buildAlarmGisPoints({alarmPoints, ledgerRows, devicePoints})
|
|
|
-
|
|
|
- const missingSources = [
|
|
|
- ledgerResult.status === 'rejected' ? '关联工单状态' : '',
|
|
|
- deviceResult.status === 'rejected' ? '设备在线与运行状态' : ''
|
|
|
- ].filter(Boolean)
|
|
|
- mapEnrichmentWarning.value = missingSources.join('、')
|
|
|
- if (missingSources.length) {
|
|
|
- console.warn(`[${props.title}] 状态补全降级:${mapEnrichmentWarning.value}`)
|
|
|
- }
|
|
|
- await nextTick()
|
|
|
- fitAlarmMap()
|
|
|
- }
|
|
|
-
|
|
|
- function handleAlarmMapReady() {
|
|
|
- mapReady.value = true
|
|
|
- if (mapPoints.value.length) nextTick(fitAlarmMap)
|
|
|
- }
|
|
|
-
|
|
|
- function fitAlarmMap() {
|
|
|
- if (mapReady.value) gisMapRef.value?.fitBounds()
|
|
|
- }
|
|
|
-
|
|
|
- async function openDetail(row) {
|
|
|
- detail.value = row
|
|
|
- detailVisible.value = true
|
|
|
- const id = row.id || row.alarmId
|
|
|
- if (!id) return
|
|
|
- detailLoading.value = true; detailError.value = ''
|
|
|
- try {
|
|
|
- const detailResponse = await getAlarmDetail(id)
|
|
|
- // 台账详情不返回设备在线/运行状态,合并保留点位侧已补全的状态,避免抽屉回退成单一状态
|
|
|
- detail.value = {...row, ...(unwrapResponse(detailResponse) || {})}
|
|
|
- } catch (error) {
|
|
|
- detail.value = row
|
|
|
- detailError.value = getRequestErrorMessage(error, '报警详情加载失败')
|
|
|
- } finally { detailLoading.value = false }
|
|
|
- }
|
|
|
-
|
|
|
- function alarmPointFocus(point) {
|
|
|
- return {
|
|
|
- lng: Number(point.lng ?? point.longitude),
|
|
|
- lat: Number(point.lat ?? point.latitude)
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- function locateAlarmPoint(point) {
|
|
|
- gisMapRef.value?.locatePoint(alarmPointFocus(point));
|
|
|
- openDetail(point);
|
|
|
- }
|
|
|
-
|
|
|
-onMounted(loadData)
|
|
|
-</script>
|
|
|
-<style scoped>
|
|
|
- .water-page {
|
|
|
- padding: 20px;
|
|
|
- background: linear-gradient(135deg, #eff6ff 0%, #f8fafc 45%, #eef2f7 100%);
|
|
|
- min-height: calc(100vh - 84px)
|
|
|
- }
|
|
|
-
|
|
|
- .muted {
|
|
|
- font-size: 13px;
|
|
|
- color: #909399
|
|
|
- }
|
|
|
-
|
|
|
- .metric {
|
|
|
- position: static;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 14px;
|
|
|
- min-height: 78px;
|
|
|
- padding: 14px 16px;
|
|
|
- background: #fff;
|
|
|
- border: 1px solid #e1ebf3;
|
|
|
- border-radius: 8px;
|
|
|
- box-shadow: 0 6px 14px rgba(15, 23, 42, 0.04)
|
|
|
- }
|
|
|
-
|
|
|
- .metric-icon {
|
|
|
- position: static;
|
|
|
- flex: 0 0 42px;
|
|
|
- display: grid;
|
|
|
- place-items: center;
|
|
|
- width: 42px;
|
|
|
- height: 42px;
|
|
|
- color: #2563eb;
|
|
|
- background: linear-gradient(135deg, #dbeafe 0%, #eff6ff 100%);
|
|
|
- border-radius: 8px;
|
|
|
- font-size: 22px
|
|
|
- }
|
|
|
-
|
|
|
- .metric-body span {
|
|
|
- display: block;
|
|
|
- margin-bottom: 3px;
|
|
|
- overflow: hidden;
|
|
|
- color: #64748b;
|
|
|
- font-size: 13px;
|
|
|
- text-overflow: ellipsis;
|
|
|
- white-space: nowrap
|
|
|
- }
|
|
|
-
|
|
|
- .metric-value {
|
|
|
- display: flex;
|
|
|
- align-items: baseline;
|
|
|
- gap: 5px
|
|
|
- }
|
|
|
-
|
|
|
- .metric-value strong {
|
|
|
- color: #1e293b;
|
|
|
- font-size: 24px;
|
|
|
- line-height: 1.2
|
|
|
- }
|
|
|
-
|
|
|
- .metric-value small {
|
|
|
- margin-left: 0;
|
|
|
- color: #94a3b8;
|
|
|
- font-size: 12px
|
|
|
- }
|
|
|
-
|
|
|
- .danger {
|
|
|
- color: #e45656
|
|
|
- }
|
|
|
-
|
|
|
- .primary {
|
|
|
- color: #2563eb
|
|
|
- }
|
|
|
-
|
|
|
- .map-card {
|
|
|
- min-height: 560px
|
|
|
- }
|
|
|
-
|
|
|
- .map-card .search-input {
|
|
|
- width: 220px
|
|
|
- }
|
|
|
-
|
|
|
- .map-card .panel-section h3 {
|
|
|
- margin: 0 0 8px;
|
|
|
- font-size: 14px;
|
|
|
- color: #29567e
|
|
|
- }
|
|
|
-
|
|
|
- .map-card .alarm-detail {
|
|
|
- margin-top: 0;
|
|
|
- padding-top: 0;
|
|
|
- border-top: 0
|
|
|
- }
|
|
|
-
|
|
|
- .map-card .device-result-list {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- gap: 6px
|
|
|
- }
|
|
|
-
|
|
|
- .map-card .device-result-list button {
|
|
|
- width: 100%;
|
|
|
- display: grid;
|
|
|
- grid-template-columns: minmax(0, 1fr) auto;
|
|
|
- gap: 3px 8px;
|
|
|
- align-items: center;
|
|
|
- border: 1px solid #dbe8f5;
|
|
|
- background: #fff;
|
|
|
- border-radius: 8px;
|
|
|
- padding: 8px 10px;
|
|
|
- text-align: left;
|
|
|
- cursor: pointer
|
|
|
- }
|
|
|
-
|
|
|
- .map-card .device-name {
|
|
|
- overflow: hidden;
|
|
|
- text-overflow: ellipsis;
|
|
|
- white-space: nowrap;
|
|
|
- font-weight: 600;
|
|
|
- color: #274b6d
|
|
|
- }
|
|
|
-
|
|
|
- .map-card .device-meta {
|
|
|
- grid-column: 1 / -1;
|
|
|
- font-size: 12px;
|
|
|
- color: #7793aa
|
|
|
- }
|
|
|
-
|
|
|
- .map-card .map-error {
|
|
|
- position: absolute;
|
|
|
- left: 16px;
|
|
|
- bottom: 16px;
|
|
|
- width: 360px;
|
|
|
- z-index: 1
|
|
|
- }
|
|
|
-
|
|
|
- .map-card .map-empty {
|
|
|
- position: absolute;
|
|
|
- left: 50%;
|
|
|
- top: 50%;
|
|
|
- transform: translate(-50%, -50%);
|
|
|
- z-index: 1
|
|
|
- }
|
|
|
-
|
|
|
-</style>
|
|
|
-<style lang="scss">
|
|
|
- @use './waterSupplyPageTheme.scss';
|
|
|
- .alarm-page .map-workspace .alarm-map {
|
|
|
- height: auto;
|
|
|
- min-height: 560px;
|
|
|
- border: 0;
|
|
|
- background-color: #f8fbff;
|
|
|
- background-image: linear-gradient(#eaf0f6 1px, transparent 1px), linear-gradient(90deg, #eaf0f6 1px, transparent 1px);
|
|
|
- background-size: 40px 40px;
|
|
|
- }
|
|
|
-
|
|
|
- .alarm-page .alarm-map {
|
|
|
- min-height: 560px;
|
|
|
- height: auto;
|
|
|
- border: 0;
|
|
|
- background-color: #f8fbff;
|
|
|
- background-image: linear-gradient(#eaf0f6 1px, transparent 1px), linear-gradient(90deg, #eaf0f6 1px, transparent 1px);
|
|
|
- background-size: 40px 40px;
|
|
|
- }
|
|
|
-
|
|
|
- .alarm-page .status-dot-normal {
|
|
|
- background: #2ecc71;
|
|
|
- }
|
|
|
-
|
|
|
- .alarm-page .status-dot-alarm {
|
|
|
- background: #e74c3c;
|
|
|
- }
|
|
|
-
|
|
|
- .alarm-page .status-dot-warning {
|
|
|
- background: #f1c40f;
|
|
|
- }
|
|
|
-
|
|
|
- .alarm-page .status-dot-work-order {
|
|
|
- background: #3498db;
|
|
|
- }
|
|
|
-
|
|
|
- .alarm-page .status-dot-offline {
|
|
|
- background: #95a5a6;
|
|
|
- }
|
|
|
- .alarm-page .alarm-detail {
|
|
|
- margin-top: 24px;
|
|
|
- padding-top: 16px;
|
|
|
- border-top: 1px solid #e1ebf3;
|
|
|
- }
|
|
|
-
|
|
|
- .alarm-page .detail-name {
|
|
|
- margin-bottom: 10px;
|
|
|
- color: #17324d;
|
|
|
- font-weight: 700;
|
|
|
- }
|
|
|
-
|
|
|
- .alarm-page .detail-action {
|
|
|
- margin-top: 10px;
|
|
|
- }
|
|
|
-
|
|
|
- /* 报警一张图详情抽屉:与监测设备/供水设施一张图保持同一抽屉结构 */
|
|
|
- .alarm-map-detail-drawer :deep(.el-drawer__body) {
|
|
|
- overflow: auto
|
|
|
- }
|
|
|
-
|
|
|
- .alarm-map-detail-drawer .device-detail {
|
|
|
- min-height: 240px
|
|
|
- }
|
|
|
-
|
|
|
- .alarm-map-detail-drawer .detail-hero h3,
|
|
|
- .alarm-map-detail-drawer .detail-section h4 {
|
|
|
- margin: 0 0 8px;
|
|
|
- color: #1c4b78
|
|
|
- }
|
|
|
-
|
|
|
- .alarm-map-detail-drawer .detail-hero p {
|
|
|
- margin: 0 0 10px;
|
|
|
- color: #678197;
|
|
|
- font-size: 13px
|
|
|
- }
|
|
|
-
|
|
|
- .alarm-map-detail-drawer .status-tags {
|
|
|
- display: flex;
|
|
|
- flex-wrap: wrap;
|
|
|
- gap: 6px
|
|
|
- }
|
|
|
-
|
|
|
- .alarm-map-detail-drawer .detail-section {
|
|
|
- margin-top: 18px;
|
|
|
- padding-top: 12px;
|
|
|
- border-top: 1px solid #e5eef7
|
|
|
- }
|
|
|
-
|
|
|
- .alarm-map-detail-drawer .detail-section dl {
|
|
|
- display: grid;
|
|
|
- grid-template-columns: 88px minmax(0, 1fr);
|
|
|
- gap: 7px 10px;
|
|
|
- margin: 0;
|
|
|
- font-size: 13px
|
|
|
- }
|
|
|
-
|
|
|
- .alarm-map-detail-drawer .detail-section dt {
|
|
|
- color: #71889c
|
|
|
- }
|
|
|
-
|
|
|
- .alarm-map-detail-drawer .detail-section dd {
|
|
|
- margin: 0;
|
|
|
- color: #2b4c67;
|
|
|
- overflow-wrap: anywhere
|
|
|
- }
|
|
|
-
|
|
|
- @media (max-width: 768px) {
|
|
|
- .alarm-map-detail-drawer {
|
|
|
- width: 100% !important
|
|
|
- }
|
|
|
- }
|
|
|
-</style>
|