| | |
| | | <script setup> |
| | | import { ref, onMounted, onUnmounted, computed } from 'vue'; |
| | | import axios from 'axios'; |
| | | import request from '@/utils/request'; |
| | | |
| | | const devices = ref([]); |
| | | const ws = ref(null); |
| | | const activeTab = ref('line1'); |
| | | |
| | | const initWebSocket = () => { |
| | | const wsUrl = `ws://${window.location.host}/api/talk/mechanicalMonitor`; |
| | | ws.value = new WebSocket(wsUrl); |
| | | |
| | | ws.value.onmessage = (event) => { |
| | | const data = JSON.parse(event.data); |
| | | if (data.type === 'status_change') { |
| | | updateDeviceStatus(data.data); |
| | | const getMechanicalStatus = () => { |
| | | return request({ |
| | | url: '/deviceInteraction/mechanicalMonitor/getMechanicalStatus', |
| | | method: 'post', |
| | | headers: { |
| | | 'Content-Type': 'application/json' |
| | | } |
| | | }; |
| | | |
| | | ws.value.onclose = () => { |
| | | console.log('WebSocket连接关闭'); |
| | | setTimeout(initWebSocket, 5000); |
| | | }; |
| | | }; |
| | | |
| | | const updateDeviceStatus = (newStatus) => { |
| | | const device = devices.value.find(d => d.deviceId === newStatus.deviceId); |
| | | if (device) { |
| | | Object.assign(device, { |
| | | ...newStatus, |
| | | showAlarmInfo: device.showAlarmInfo, |
| | | alarmTime: newStatus.alarmTime ? new Date(newStatus.alarmTime).toLocaleString() : null, |
| | | disconnectTime: newStatus.disconnectTime ? new Date(newStatus.disconnectTime).toLocaleString() : null |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const fetchDeviceStatus = async () => { |
| | | try { |
| | | const response = await axios.post('/api/deviceInteraction/mechanicalMonitor/getMechanicalStatus'); |
| | | if (response.data.code === 200) { |
| | | const statusData = response.data.data.mechanicalStatus; |
| | | const response = await getMechanicalStatus(); |
| | | if (response.code === 200) { |
| | | const statusData = response.data.mechanicalStatus; |
| | | devices.value = statusData.map(device => ({ |
| | | ...device, |
| | | showAlarmInfo: false, |
| | |
| | | })); |
| | | } |
| | | } catch (error) { |
| | | console.error('获取设备状态失败:', error); |
| | | console.error('获取设备状态失败:', error.message || error); |
| | | } |
| | | }; |
| | | |
| | |
| | | |
| | | onMounted(() => { |
| | | fetchDeviceStatus(); |
| | | initWebSocket(); |
| | | }); |
| | | |
| | | onUnmounted(() => { |
| | | if (ws.value) { |
| | | ws.value.close(); |
| | | } |
| | | const timer = setInterval(fetchDeviceStatus, 30000); |
| | | |
| | | onUnmounted(() => { |
| | | clearInterval(timer); |
| | | }); |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <div class="monitoring-container"> |
| | | <h1>九牧设备连接及故障报警监控</h1> |
| | | |
| | | <!-- 添加标签切换组件 --> |
| | | <div class="tab-container"> |
| | | <div |
| | | class="tab-item" |
| | |
| | | 二线 |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 添加调试信息 --> |
| | | <div v-if="devices.length === 0">暂无设备数据</div> |
| | | <div v-else> |
| | | <p>总设备数量: {{ devices.length }}</p> |
| | | <p>一线设备数量: {{ groupedDevices.line1.length }}</p> |
| | | <p>二线设备数量: {{ groupedDevices.line2.length }}</p> |
| | | </div> |
| | | |
| | | <!-- 修改生产线显示逻辑 --> |
| | | <!-- 一线设备 --> |
| | | <div v-show="activeTab === 'line1'" class="line-section"> |
| | | <h2 class="line-title">一线</h2> |
| | | <div class="device-grid"> |
| | | <div v-for="device in groupedDevices.line1" :key="device.id" class="device-panel" @click="toggleAlarmInfo(device)"> |
| | | <div v-for="device in groupedDevices.line1" |
| | | :key="device.deviceId" |
| | | class="device-panel" |
| | | @click="toggleAlarmInfo(device)"> |
| | | <div class="device-image"> |
| | | <img :src="deviceIcon(device)" alt="设备图标"> |
| | | <p v-if="device.status === 1" class="status-text normal-status">正常</p> |
| | |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 二线设备 --> |
| | | <div v-show="activeTab === 'line2'" class="line-section"> |
| | | <h2 class="line-title">二线</h2> |
| | | <div class="device-grid"> |
| | |
| | | .monitoring-container { |
| | | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
| | | padding: 20px; |
| | | max-width: 1200px; |
| | | max-width: 1600px; |
| | | margin: 0 auto; |
| | | margin-bottom: 60px; |
| | | } |
| | |
| | | |
| | | .device-grid { |
| | | display: grid; |
| | | grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); |
| | | grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); |
| | | gap: 15px; |
| | | margin-bottom: 20px; |
| | | } |