| ¶Ô±ÈÐÂÎļþ | 
 |  |  | 
 |  |  | <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%"> | 
 |  |  |                 <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%"> | 
 |  |  |                         <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> |