zouyu
2025-11-14 989d547833c94ffc4e7395b0023ec24ee09f5ab6
src/views/productionManagement/safetyMonitoring/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,873 @@
<template>
   <div class="safety-monitoring">
      <el-row :gutter="20">
         <!-- å·¦ä¾§ï¼šå®žæ—¶ç›‘控区域 -->
         <el-col :span="16">
            <el-card class="monitoring-card">
               <div slot="header" class="card-header">
                  <span>实时气体浓度监控</span>
                  <el-tag :type="systemStatus === 'normal' ? 'success' : 'danger'">
                     {{ systemStatus === 'normal' ? '系统正常' : '系统告警' }}
                  </el-tag>
               </div>
               <!-- å‚¨ç½åŒºç›‘控 -->
               <div class="monitoring-section">
                  <h3>储罐区监控</h3>
                  <div class="sensor-grid">
                     <div class="sensor-item" v-for="sensor in tankSensors" :key="sensor.id">
                        <div class="sensor-header">
                           <span>{{ sensor.name }}</span>
                           <el-tag :type="sensor.status === 'normal' ? 'success' : 'danger'" size="small">
                              {{ sensor.status === 'normal' ? '正常' : '超标' }}
                           </el-tag>
                        </div>
                        <div class="sensor-data">
                           <div class="data-item">
                              <span>甲烷: {{ sensor.methane.toFixed(2) }}%</span>
                              <el-progress
                                 :percentage="Math.min(Math.round(sensor.methane * 40 * 100) / 100, 100)"
                                 :color="getProgressColor(Math.min(Math.round(sensor.methane * 40 * 100) / 100, 100), 80)"
                                 :format="formatProgress"
                                 :stroke-width="8"
                              />
                           </div>
                           <div class="data-item">
                              <span>硫化氢: {{ sensor.h2s.toFixed(2) }}ppm</span>
                              <el-progress
                                 :percentage="Math.min(Math.round((sensor.h2s / 20) * 100 * 100) / 100, 100)"
                                 :color="getProgressColor(Math.min(Math.round((sensor.h2s / 20) * 100 * 100) / 100, 100), 80)"
                                 :format="formatProgress"
                                 :stroke-width="8"
                              />
                           </div>
                        </div>
                     </div>
                  </div>
               </div>
               <!-- äº•口压缩机监控 -->
               <div class="monitoring-section">
                  <h3>井口压缩机监控</h3>
                  <div class="sensor-grid">
                     <div class="sensor-item" v-for="sensor in compressorSensors" :key="sensor.id">
                        <div class="sensor-header">
                           <span>{{ sensor.name }}</span>
                           <el-tag :type="sensor.status === 'normal' ? 'success' : 'danger'" size="small">
                              {{ sensor.status === 'normal' ? '正常' : '超标' }}
                           </el-tag>
                        </div>
                        <div class="sensor-data">
                           <div class="data-item">
                              <span>甲烷: {{ sensor.methane.toFixed(2) }}%</span>
                              <el-progress
                                 :percentage="Math.min(Math.round(sensor.methane * 40 * 100) / 100, 100)"
                                 :color="getProgressColor(sensor.methane, 2.5)"
                                 :format="formatProgress"
                                 :stroke-width="8"
                              />
                           </div>
                           <div class="data-item">
                              <span>硫化氢: {{ sensor.h2s.toFixed(2) }}ppm</span>
                              <el-progress
                                 :percentage="Math.min(Math.round((sensor.h2s / 20) * 100 * 100) / 100, 100)"
                                 :color="getProgressColor(sensor.h2s, 10)"
                                 :format="formatProgress"
                                 :stroke-width="8"
                              />
                           </div>
                        </div>
                     </div>
                  </div>
               </div>
               <!-- å®žæ—¶æ›²çº¿å›¾ -->
               <div class="chart-section">
                  <h3>实时浓度曲线</h3>
                  <div class="chart-container">
                     <div ref="chart" class="chart"></div>
                  </div>
               </div>
            </el-card>
         </el-col>
         <!-- å³ä¾§ï¼šæŽ§åˆ¶é¢æ¿ -->
         <el-col :span="8">
            <el-card class="control-card">
               <div slot="header" class="card-header">
                  <span>应急控制面板</span>
               </div>
               <!-- å–·æ·‹çŠ¶æ€ -->
               <div class="control-section">
                  <h4>喷淋系统状态</h4>
                  <div class="status-grid">
                     <div class="status-item" v-for="sprinkler in sprinklerSystems" :key="sprinkler.id">
                        <div class="status-indicator" :class="sprinkler.status">
                           <i class="el-icon-circle-check" v-if="sprinkler.status === 'active'"></i>
                           <i class="el-icon-circle-close" v-else></i>
                        </div>
                        <span>{{ sprinkler.name }}</span>
                        <el-tag :type="sprinkler.status === 'active' ? 'success' : 'info'" size="small">
                           {{ sprinkler.status === 'active' ? '运行中' : '待机' }}
                        </el-tag>
                     </div>
                  </div>
               </div>
               <!-- åº”急记录按钮 -->
               <h4>应急管理</h4>
               <div class="control-section1">
                  <el-button type="primary" @click="showEmergencyRecords" style="margin-bottom: 10px;">
                     åº”急记录
                  </el-button>
                  <el-button type="warning" @click="triggerEmergency" :disabled="!hasEmergency">
                     è§¦å‘应急响应
                  </el-button>
               </div>
               <!-- ç³»ç»Ÿæ—¥å¿— -->
               <div class="control-section">
                  <h4>系统日志</h4>
                  <div class="log-container">
                     <div class="log-item" v-for="log in systemLogs" :key="log.id">
                        <span class="log-time">{{ log.time }}</span>
                        <span class="log-content">{{ log.content }}</span>
                     </div>
                  </div>
               </div>
            </el-card>
         </el-col>
      </el-row>
      <!-- æ³„漏预警弹窗 -->
      <el-dialog
         title="⚠️ æ³„漏预警"
         :visible.sync="leakWarningVisible"
         width="500px"
         :close-on-click-modal="false"
         :close-on-press-escape="false"
         class="leak-warning-dialog"
      >
         <div class="warning-content">
            <div class="warning-icon">
               <i class="el-icon-warning"></i>
            </div>
            <div class="warning-text">
               <h3>检测到气体浓度超标!</h3>
               <p>位置:{{ currentWarning.location }}</p>
               <p>超标气体:{{ currentWarning.gas }}</p>
               <p>当前浓度:{{ currentWarning.value }}</p>
            </div>
         </div>
         <div slot="footer" class="dialog-footer">
            <el-button type="danger" @click="acknowledgeWarning">确认告警</el-button>
            <el-button type="primary" @click="viewDetails">查看详情</el-button>
         </div>
      </el-dialog>
      <!-- åº”急记录弹窗 -->
      <el-dialog
         title="应急记录"
         :visible.sync="emergencyRecordsVisible"
         width="800px"
      >
         <el-table :data="emergencyRecords" style="width: 100%" stripe>
            <el-table-column prop="time" label="时间" width="180"></el-table-column>
            <el-table-column prop="location" label="位置" width="150"></el-table-column>
            <el-table-column prop="type" label="类型" width="120"></el-table-column>
            <el-table-column prop="status" label="状态" width="100">
               <template slot-scope="scope">
                  <el-tag :type="scope.row.status === 'resolved' ? 'success' : 'warning'">
                     {{ scope.row.status === 'resolved' ? '已解决' : '处理中' }}
                  </el-tag>
               </template>
            </el-table-column>
            <el-table-column prop="description" label="描述"></el-table-column>
            <el-table-column label="操作" width="120">
               <template slot-scope="scope">
                  <el-button type="text" @click="viewBlockchainDetails(scope.row)">
                     åŒºå—链详情
                  </el-button>
               </template>
            </el-table-column>
         </el-table>
      </el-dialog>
      <!-- åŒºå—链存证详情弹窗 -->
      <el-dialog
         title="区块链存证详情"
         :visible.sync="blockchainDetailsVisible"
         width="900px"
      >
         <div class="blockchain-details">
            <el-descriptions :column="2" border>
               <el-descriptions-item label="事件ID">{{ currentEvent.id }}</el-descriptions-item>
               <el-descriptions-item label="时间戳">{{ currentEvent.timestamp }}</el-descriptions-item>
               <el-descriptions-item label="位置">{{ currentEvent.location }}</el-descriptions-item>
               <el-descriptions-item label="事件类型">{{ currentEvent.type }}</el-descriptions-item>
            </el-descriptions>
            <div class="sensor-data-section">
               <h4>传感器数据</h4>
               <el-table :data="currentEvent.sensorData" style="width: 100%" stripe>
                  <el-table-column prop="sensor" label="传感器"></el-table-column>
                  <el-table-column prop="methane" label="甲烷浓度"></el-table-column>
                  <el-table-column prop="h2s" label="硫化氢浓度"></el-table-column>
                  <el-table-column prop="timestamp" label="记录时间"></el-table-column>
               </el-table>
            </div>
            <div class="action-log-section">
               <h4>处置动作记录</h4>
               <el-timeline>
                  <el-timeline-item
                     v-for="action in currentEvent.actions"
                     :key="action.id"
                     :timestamp="action.timestamp"
                     :type="action.type === 'emergency' ? 'danger' : 'primary'"
                  >
                     {{ action.description }}
                  </el-timeline-item>
               </el-timeline>
            </div>
            <div class="blockchain-info">
               <h4>区块链信息</h4>
               <el-descriptions :column="1" border>
                  <el-descriptions-item label="区块哈希">{{ currentEvent.blockHash }}</el-descriptions-item>
                  <el-descriptions-item label="交易哈希">{{ currentEvent.txHash }}</el-descriptions-item>
                  <el-descriptions-item label="确认数">{{ currentEvent.confirmations }}</el-descriptions-item>
               </el-descriptions>
            </div>
         </div>
      </el-dialog>
   </div>
</template>
<script>
import * as echarts from 'echarts'
export default {
   name: 'SafetyMonitoring',
   data() {
      return {
         systemStatus: 'normal',
         leakWarningVisible: false,
         emergencyRecordsVisible: false,
         blockchainDetailsVisible: false,
         currentWarning: {},
         currentEvent: {},
         hasEmergency: false,
         // å‚¨ç½åŒºä¼ æ„Ÿå™¨æ•°æ®
         tankSensors: [
            { id: 1, name: '储罐T-001', methane: 1.20, h2s: 2.10, status: 'normal' },
            { id: 2, name: '储罐T-002', methane: 0.80, h2s: 1.50, status: 'normal' },
            { id: 3, name: '储罐T-003', methane: 3.20, h2s: 8.50, status: 'warning' },
            { id: 4, name: '储罐T-004', methane: 0.60, h2s: 0.80, status: 'normal' }
         ],
         // äº•口压缩机传感器数据
         compressorSensors: [
            { id: 5, name: '压缩机C-001', methane: 2.10, h2s: 3.20, status: 'normal' },
            { id: 6, name: '压缩机C-002', methane: 4.80, h2s: 12.50, status: 'warning' },
            { id: 7, name: '压缩机C-003', methane: 1.80, h2s: 2.80, status: 'normal' }
         ],
         // å–·æ·‹ç³»ç»ŸçŠ¶æ€
         sprinklerSystems: [
            { id: 1, name: '储罐区喷淋', status: 'active' },
            { id: 2, name: '压缩机区喷淋', status: 'standby' },
            { id: 3, name: '紧急喷淋', status: 'standby' }
         ],
         // ç³»ç»Ÿæ—¥å¿—
         systemLogs: [
            { id: 1, time: '14:30:25', content: '系统启动完成,所有传感器正常' },
            { id: 2, time: '14:35:12', content: '储罐T-003甲烷浓度超标,触发预警' },
            { id: 3, time: '14:35:15', content: '启动储罐区喷淋系统' },
            { id: 4, time: '14:35:20', content: '发送紧急疏散广播' }
         ],
         // åº”急记录
         emergencyRecords: [
            {
               id: 'EM001',
               time: '2024-01-15 14:35:12',
               location: '储罐T-003',
               type: '甲烷超标',
               status: 'resolved',
               description: '储罐T-003甲烷浓度达到3.2%,超过安全阈值2.5%'
            },
            {
               id: 'EM002',
               time: '2024-01-15 14:35:15',
               location: '压缩机C-002',
               type: '硫化氢超标',
               status: 'processing',
               description: '压缩机C-002硫化氢浓度达到12.5ppm,超过安全阈值10ppm'
            }
         ],
         // å›¾è¡¨å®žä¾‹
         chart: null,
         // å®šæ—¶å™¨
         timer: null
      }
   },
   mounted() {
      this.initChart()
      this.startDataRefresh()
      this.checkEmergencyStatus()
   },
   beforeDestroy() {
      if (this.timer) {
         clearInterval(this.timer)
      }
      if (this.chart) {
         this.chart.dispose()
      }
   },
   methods: {
      // ç»Ÿä¸€è¿›åº¦æ¡æ ¼å¼åŒ–为两位小数,避免浮点误差显示
      formatProgress(percentage) {
         if (percentage == null || isNaN(percentage)) return '0.00%'
         const val = Math.round(Number(percentage) * 100) / 100
         return `${val.toFixed(2)}%`
      },
      // åˆå§‹åŒ–图表
      initChart() {
         this.chart = echarts.init(this.$refs.chart)
         this.updateChart()
      },
      // æ›´æ–°å›¾è¡¨æ•°æ®
      updateChart() {
         const option = {
            title: {
               text: '实时气体浓度监控',
               left: 'center'
            },
            tooltip: {
               trigger: 'axis',
               axisPointer: {
                  type: 'cross'
               }
            },
            legend: {
               data: ['储罐区甲烷', '储罐区硫化氢', '压缩机甲烷', '压缩机硫化氢'],
               top: 30
            },
            grid: {
               left: '3%',
               right: '4%',
               bottom: '3%',
               top: '15%',
               containLabel: true
            },
            xAxis: {
               type: 'category',
               data: this.generateTimeData()
            },
            yAxis: [
               {
                  type: 'value',
                  name: '甲烷浓度(%)',
                  position: 'left'
               },
               {
                  type: 'value',
                  name: '硫化氢浓度(ppm)',
                  position: 'right'
               }
            ],
            series: [
               {
                  name: '储罐区甲烷',
                  type: 'line',
                  data: this.generateRandomData(20, 0.5, 3.5),
                  smooth: true,
                  yAxisIndex: 0
               },
               {
                  name: '储罐区硫化氢',
                  type: 'line',
                  data: this.generateRandomData(20, 0.5, 12),
                  smooth: true,
                  yAxisIndex: 1
               },
               {
                  name: '压缩机甲烷',
                  type: 'line',
                  data: this.generateRandomData(20, 1.0, 5.0),
                  smooth: true,
                  yAxisIndex: 0
               },
               {
                  name: '压缩机硫化氢',
                  type: 'line',
                  data: this.generateRandomData(20, 1.0, 15),
                  smooth: true,
                  yAxisIndex: 1
               }
            ]
         }
         this.chart.setOption(option)
      },
      // ç”Ÿæˆæ—¶é—´æ•°æ®
      generateTimeData() {
         const times = []
         const now = new Date()
         for (let i = 19; i >= 0; i--) {
            const time = new Date(now.getTime() - i * 5 * 60 * 1000)
            times.push(time.toLocaleTimeString('zh-CN', { hour12: false }))
         }
         return times
      },
      // ç”Ÿæˆéšæœºæ•°æ®
      generateRandomData(count, min, max) {
         const data = []
         for (let i = 0; i < count; i++) {
            data.push(+(Math.random() * (max - min) + min).toFixed(2))
         }
         return data
      },
      // å¼€å§‹æ•°æ®åˆ·æ–°
      startDataRefresh() {
         this.timer = setInterval(() => {
            this.refreshSensorData()
            this.updateChart()
            this.checkEmergencyStatus()
         }, 5000) // æ¯5秒刷新一次
      },
      // åˆ·æ–°ä¼ æ„Ÿå™¨æ•°æ®
      refreshSensorData() {
         // æ›´æ–°å‚¨ç½åŒºä¼ æ„Ÿå™¨æ•°æ®
         this.tankSensors.forEach(sensor => {
            sensor.methane = +(Math.random() * 4).toFixed(2)
            sensor.h2s = +(Math.random() * 15).toFixed(2)
            sensor.status = this.getSensorStatus(sensor.methane, sensor.h2s)
         })
         // æ›´æ–°åŽ‹ç¼©æœºä¼ æ„Ÿå™¨æ•°æ®
         this.compressorSensors.forEach(sensor => {
            sensor.methane = +(Math.random() * 6).toFixed(2)
            sensor.h2s = +(Math.random() * 20).toFixed(2)
            sensor.status = this.getSensorStatus(sensor.methane, sensor.h2s)
         })
         // æ£€æŸ¥æ˜¯å¦éœ€è¦è§¦å‘预警
         this.checkLeakWarning()
      },
      // èŽ·å–ä¼ æ„Ÿå™¨çŠ¶æ€
      getSensorStatus(methane, h2s) {
         const methanePct = Math.min(Math.round(methane * 40 * 100) / 100, 100)
         const h2sPct = Math.min(Math.round((h2s / 20) * 100 * 100) / 100, 100)
         if (methanePct >= 80 || h2sPct >= 80) {
            return 'warning'
         }
         return 'normal'
      },
      // æ£€æŸ¥æ³„漏预警
      checkLeakWarning() {
         const allSensors = [...this.tankSensors, ...this.compressorSensors]
         const warningSensor = allSensors.find(sensor => this.getSensorStatus(sensor.methane, sensor.h2s) === 'warning')
         if (warningSensor && !this.leakWarningVisible) {
            this.triggerLeakWarning(warningSensor)
         }
      },
      // è§¦å‘泄漏预警
      triggerLeakWarning(sensor) {
         const methanePct = Math.min(Math.round(sensor.methane * 40 * 100) / 100, 100)
         const h2sPct = Math.min(Math.round((sensor.h2s / 20) * 100 * 100) / 100, 100)
         const isMethaneMajor = methanePct >= h2sPct
         const overGas = isMethaneMajor ? '甲烷' : '硫化氢'
         const percent = (isMethaneMajor ? methanePct : h2sPct).toFixed(2)
         this.currentWarning = {
            location: sensor.name,
            gas: overGas,
            value: `${percent}%`
         }
         this.leakWarningVisible = true
         this.hasEmergency = true
         // è‡ªåŠ¨è§¦å‘åº”æ€¥å“åº”
         this.autoEmergencyResponse(sensor)
         // æ·»åŠ ç³»ç»Ÿæ—¥å¿—
         this.addSystemLog(`检测到${sensor.name}气体浓度超标,触发泄漏预警`)
      },
      // è‡ªåŠ¨åº”æ€¥å“åº”
      autoEmergencyResponse(sensor) {
         // å¯åŠ¨å–·æ·‹ç³»ç»Ÿ
         if (sensor.name.includes('储罐')) {
            this.sprinklerSystems[0].status = 'active'
         } else if (sensor.name.includes('压缩机')) {
            this.sprinklerSystems[1].status = 'active'
         }
         // æ·»åŠ ç³»ç»Ÿæ—¥å¿—
         this.addSystemLog(`启动${sensor.name}区域喷淋系统`)
         this.addSystemLog(`发送紧急疏散广播`)
         // åˆ›å»ºåº”急记录
         this.createEmergencyRecord(sensor)
      },
      // æ·»åŠ ç³»ç»Ÿæ—¥å¿—
      addSystemLog(content) {
         const now = new Date()
         const time = now.toLocaleTimeString('zh-CN', { hour12: false })
         this.systemLogs.unshift({
            id: Date.now(),
            time: time,
            content: content
         })
         // ä¿æŒæœ€å¤š20条日志
         if (this.systemLogs.length > 20) {
            this.systemLogs = this.systemLogs.slice(0, 20)
         }
      },
      // åˆ›å»ºåº”急记录
      createEmergencyRecord(sensor) {
         const now = new Date()
         const record = {
            id: `EM${Date.now()}`,
            time: now.toLocaleString('zh-CN'),
            location: sensor.name,
            type: sensor.methane > 2.5 ? '甲烷超标' : '硫化氢超标',
            status: 'processing',
            description: `${sensor.name}检测到${sensor.methane > 2.5 ? '甲烷' : '硫化氢'}浓度超标`
         }
         this.emergencyRecords.unshift(record)
      },
      // èŽ·å–è¿›åº¦æ¡é¢œè‰²
      getProgressColor(value, threshold) {
         if (value > threshold) {
            return '#F56C6C'
         } else if (value > threshold * 0.8) {
            return '#E6A23C'
         }
         return '#67C23A'
      },
      // æ£€æŸ¥åº”急状态
      checkEmergencyStatus() {
         const allSensors = [...this.tankSensors, ...this.compressorSensors]
         const has = allSensors.some(sensor => this.getSensorStatus(sensor.methane, sensor.h2s) === 'warning')
         this.hasEmergency = has
         this.systemStatus = has ? 'warning' : 'normal'
      },
      // ç¡®è®¤å‘Šè­¦
      acknowledgeWarning() {
         this.leakWarningVisible = false
         this.addSystemLog('泄漏预警已确认')
      },
      // æŸ¥çœ‹è¯¦æƒ…
      viewDetails() {
         this.leakWarningVisible = false
         // è¿™é‡Œå¯ä»¥è·³è½¬åˆ°è¯¦ç»†é¡µé¢æˆ–显示更多信息
      },
      // æ˜¾ç¤ºåº”急记录
      showEmergencyRecords() {
         this.emergencyRecordsVisible = true
      },
      // æŸ¥çœ‹åŒºå—链详情
      viewBlockchainDetails(record) {
         this.currentEvent = {
            id: record.id,
            timestamp: record.time,
            location: record.location,
            type: record.type,
            sensorData: [
               {
                  sensor: '甲烷传感器',
                  methane: '3.2%',
                  h2s: '8.5ppm',
                  timestamp: record.time
               },
               {
                  sensor: '硫化氢传感器',
                  methane: '2.8%',
                  h2s: '12.5ppm',
                  timestamp: record.time
               }
            ],
            actions: [
               {
                  id: 1,
                  timestamp: record.time,
                  type: 'emergency',
                  description: '检测到气体浓度超标,触发预警'
               },
               {
                  id: 2,
                  timestamp: new Date(new Date(record.time).getTime() + 3000).toLocaleString('zh-CN'),
                  type: 'action',
                  description: '启动喷淋系统降温'
               },
               {
                  id: 3,
                  timestamp: new Date(new Date(record.time).getTime() + 5000).toLocaleString('zh-CN'),
                  type: 'action',
                  description: '发送紧急疏散广播'
               }
            ],
            blockHash: '0x1234567890abcdef...',
            txHash: '0xabcdef1234567890...',
            confirmations: 12
         }
         this.emergencyRecordsVisible = false
         this.blockchainDetailsVisible = true
      },
      // è§¦å‘应急响应
      triggerEmergency() {
         this.$message.success('应急响应已触发')
         this.addSystemLog('手动触发应急响应')
      }
   }
}
</script>
<style scoped>
.safety-monitoring {
   padding: 20px;
   background-color: #f5f7fa;
   min-height: calc(100vh - 84px);
}
.monitoring-card, .control-card {
   margin-bottom: 20px;
}
.card-header {
   display: flex;
   justify-content: space-between;
   align-items: center;
}
.monitoring-section {
   margin-bottom: 30px;
}
.monitoring-section h3 {
   color: #303133;
   margin-bottom: 15px;
   padding-bottom: 8px;
   border-bottom: 2px solid #409EFF;
}
.sensor-grid {
   display: grid;
   grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
   gap: 15px;
}
.sensor-item {
   background: #fff;
   border: 1px solid #e4e7ed;
   border-radius: 8px;
   padding: 15px;
   box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.sensor-header {
   display: flex;
   justify-content: space-between;
   align-items: center;
   margin-bottom: 15px;
   font-weight: bold;
}
.sensor-data .data-item {
   margin-bottom: 12px;
}
.sensor-data .data-item span {
   display: block;
   margin-bottom: 5px;
   font-size: 14px;
   color: #606266;
}
.chart-section {
   margin-top: 30px;
}
.chart-section h3 {
   color: #303133;
   margin-bottom: 15px;
   padding-bottom: 8px;
   border-bottom: 2px solid #409EFF;
}
.chart-container {
   background: #fff;
   border-radius: 8px;
   padding: 20px;
}
.chart {
   width: 100%;
   height: 400px;
}
.control-section {
   margin-bottom: 25px;
}
.control-section1 {
   display: flex;
}
.control-section h4 {
   color: #303133;
   margin-bottom: 15px;
   font-size: 16px;
}
.status-grid {
   display: grid;
   gap: 10px;
}
.status-item {
   display: flex;
   align-items: center;
   gap: 10px;
   padding: 10px;
   background: #f8f9fa;
   border-radius: 6px;
}
.status-indicator {
   width: 20px;
   height: 20px;
   border-radius: 50%;
   display: flex;
   align-items: center;
   justify-content: center;
}
.status-indicator.active {
   color: #67C23A;
}
.status-indicator.standby {
   color: #909399;
}
.log-container {
   max-height: 200px;
   overflow-y: auto;
   background: #f8f9fa;
   border-radius: 6px;
   padding: 10px;
}
.log-item {
   display: flex;
   gap: 10px;
   margin-bottom: 8px;
   font-size: 12px;
}
.log-time {
   color: #909399;
   min-width: 60px;
}
.log-content {
   color: #606266;
}
/* æ³„漏预警弹窗样式 */
.leak-warning-dialog {
   background: #fff5f5;
}
.warning-content {
   text-align: center;
   padding: 20px 0;
}
.warning-icon {
   font-size: 60px;
   color: #F56C6C;
   margin-bottom: 20px;
}
.warning-text h3 {
   color: #F56C6C;
   margin-bottom: 15px;
}
.warning-text p {
   margin: 8px 0;
   color: #606266;
}
/* åŒºå—链详情样式 */
.blockchain-details {
   padding: 20px 0;
}
.sensor-data-section, .action-log-section, .blockchain-info {
   margin-top: 25px;
}
.sensor-data-section h4, .action-log-section h4, .blockchain-info h4 {
   color: #303133;
   margin-bottom: 15px;
   padding-bottom: 8px;
   border-bottom: 1px solid #e4e7ed;
}
/* å“åº”式设计 */
@media (max-width: 1200px) {
   .sensor-grid {
      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
   }
}
@media (max-width: 768px) {
   .safety-monitoring {
      padding: 10px;
   }
   .sensor-grid {
      grid-template-columns: 1fr;
   }
   .chart {
      height: 300px;
   }
}
</style>