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