|
|
@ -144,6 +144,7 @@ |
|
|
<div class="device-group"> |
|
|
<div class="device-group"> |
|
|
<div class="group-title">🚗 AGV搬运车</div> |
|
|
<div class="group-title">🚗 AGV搬运车</div> |
|
|
<div class="device-list agv-grid"> |
|
|
<div class="device-list agv-grid"> |
|
|
|
|
|
<!-- AGV 列表 --> |
|
|
<div |
|
|
<div |
|
|
v-for="agv in agvData" |
|
|
v-for="agv in agvData" |
|
|
:key="agv.id" |
|
|
:key="agv.id" |
|
|
@ -161,6 +162,12 @@ |
|
|
<span>任务: {{ agv.tasks }}</span> |
|
|
<span>任务: {{ agv.tasks }}</span> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 空状态提示 --> |
|
|
|
|
|
<div v-if="agvData.length === 0" class="empty-state"> |
|
|
|
|
|
<i class="el-icon-warning"></i> |
|
|
|
|
|
<span>暂无AGV数据</span> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
@ -299,12 +306,19 @@ |
|
|
</template> |
|
|
</template> |
|
|
|
|
|
|
|
|
<script> |
|
|
<script> |
|
|
|
|
|
import WebSocketClient from '@/utils/websocket' |
|
|
|
|
|
|
|
|
export default { |
|
|
export default { |
|
|
name: 'Warehouse3DBoard', |
|
|
name: 'Warehouse3DBoard', |
|
|
data() { |
|
|
data() { |
|
|
return { |
|
|
return { |
|
|
currentTime: '', |
|
|
currentTime: '', |
|
|
|
|
|
|
|
|
|
|
|
// WebSocket相关 |
|
|
|
|
|
useWebSocket: true, // 是否使用WebSocket(可切换为false降级到本地数据) |
|
|
|
|
|
wsConnected: false, // WebSocket连接状态 |
|
|
|
|
|
wsSubscription: null, // WebSocket订阅ID |
|
|
|
|
|
|
|
|
// 任务统计数据 |
|
|
// 任务统计数据 |
|
|
taskData: { |
|
|
taskData: { |
|
|
totalTasks: 125680, |
|
|
totalTasks: 125680, |
|
|
@ -331,13 +345,8 @@ export default { |
|
|
{ id: 2, name: '机器人#2', status: 'working', statusText: '工作中', efficiency: 92, tasks: 45 } |
|
|
{ id: 2, name: '机器人#2', status: 'working', statusText: '工作中', efficiency: 92, tasks: 45 } |
|
|
], |
|
|
], |
|
|
|
|
|
|
|
|
// AGV数据 |
|
|
|
|
|
agvData: [ |
|
|
|
|
|
{ id: 1, name: 'AGV#1', status: 'working', statusText: '工作中', battery: 85, tasks: 12 }, |
|
|
|
|
|
{ id: 2, name: 'AGV#2', status: 'working', statusText: '工作中', battery: 78, tasks: 15 }, |
|
|
|
|
|
{ id: 3, name: 'AGV#3', status: 'idle', statusText: '空闲', battery: 92, tasks: 0 }, |
|
|
|
|
|
{ id: 4, name: 'AGV#4', status: 'charging', statusText: '充电中', battery: 45, tasks: 0 } |
|
|
|
|
|
], |
|
|
|
|
|
|
|
|
// AGV数据(从TUSK系统实时获取) |
|
|
|
|
|
agvData: [], |
|
|
|
|
|
|
|
|
// 领料申请单数据 |
|
|
// 领料申请单数据 |
|
|
materialRequestData: { |
|
|
materialRequestData: { |
|
|
@ -379,6 +388,11 @@ export default { |
|
|
|
|
|
|
|
|
// 监听窗口大小变化 |
|
|
// 监听窗口大小变化 |
|
|
window.addEventListener('resize', this.handleResize) |
|
|
window.addEventListener('resize', this.handleResize) |
|
|
|
|
|
|
|
|
|
|
|
// 根据配置选择使用WebSocket或本地数据 |
|
|
|
|
|
if (this.useWebSocket) { |
|
|
|
|
|
this.initWebSocket() |
|
|
|
|
|
} |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
beforeDestroy() { |
|
|
beforeDestroy() { |
|
|
@ -391,6 +405,8 @@ export default { |
|
|
Object.values(this.charts).forEach(chart => { |
|
|
Object.values(this.charts).forEach(chart => { |
|
|
if (chart) chart.dispose() |
|
|
if (chart) chart.dispose() |
|
|
}) |
|
|
}) |
|
|
|
|
|
// 断开WebSocket连接 |
|
|
|
|
|
this.disconnectWebSocket() |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
methods: { |
|
|
methods: { |
|
|
@ -423,6 +439,117 @@ export default { |
|
|
this.currentTime = `${year}-${month}-${date} ${hours}:${minutes}:${seconds} ${weekDay}` |
|
|
this.currentTime = `${year}-${month}-${date} ${hours}:${minutes}:${seconds} ${weekDay}` |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 初始化WebSocket连接 |
|
|
|
|
|
*/ |
|
|
|
|
|
initWebSocket() { |
|
|
|
|
|
var apiServer = (process.env.NODE_ENV !== 'production' && process.env.OPEN_PROXY ? '/proxyApi/' : window.SITE_CONFIG.baseUrl); |
|
|
|
|
|
// 连接WebSocket服务器 |
|
|
|
|
|
const wsUrl = apiServer + 'ws/dashboard' |
|
|
|
|
|
|
|
|
|
|
|
WebSocketClient.connect( |
|
|
|
|
|
wsUrl, |
|
|
|
|
|
() => { |
|
|
|
|
|
this.wsConnected = true |
|
|
|
|
|
console.log('[智能立体仓库看板] WebSocket连接成功') |
|
|
|
|
|
|
|
|
|
|
|
// 订阅智能立体仓库看板主题 |
|
|
|
|
|
this.wsSubscription = WebSocketClient.subscribe( |
|
|
|
|
|
'/topic/dashboard/warehouse-3d', |
|
|
|
|
|
this.handleWebSocketMessage |
|
|
|
|
|
) |
|
|
|
|
|
console.log('[智能立体仓库看板] 已订阅主题: /topic/dashboard/warehouse-3d') |
|
|
|
|
|
}, |
|
|
|
|
|
(error) => { |
|
|
|
|
|
// 连接失败回调 |
|
|
|
|
|
console.error('[智能立体仓库看板] WebSocket连接失败,将继续使用本地数据', error) |
|
|
|
|
|
this.wsConnected = false |
|
|
|
|
|
} |
|
|
|
|
|
) |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 处理WebSocket推送的消息 |
|
|
|
|
|
* |
|
|
|
|
|
* <p><b>数据结构说明:</b></p> |
|
|
|
|
|
* <ul> |
|
|
|
|
|
* <li>taskData - 任务统计数据</li> |
|
|
|
|
|
* <li>storageData - 库位利用率数据</li> |
|
|
|
|
|
* <li>robotData - 机器人状态数据</li> |
|
|
|
|
|
* <li>agvData - AGV状态数据</li> |
|
|
|
|
|
* <li>materialRequestData - 领料申请单统计</li> |
|
|
|
|
|
* <li>shipmentData - 发货统计</li> |
|
|
|
|
|
* </ul> |
|
|
|
|
|
* |
|
|
|
|
|
* @param {object} message WebSocket推送的消息 |
|
|
|
|
|
*/ |
|
|
|
|
|
handleWebSocketMessage(message) { |
|
|
|
|
|
if (message && message.code === 0) { |
|
|
|
|
|
console.log('[智能立体仓库看板] 收到WebSocket推送数据:', message.data) |
|
|
|
|
|
|
|
|
|
|
|
// 更新任务统计数据 |
|
|
|
|
|
if (message.data.taskData && Object.keys(message.data.taskData).length > 0) { |
|
|
|
|
|
this.taskData = Object.assign({}, this.taskData, message.data.taskData) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 更新库位数据 |
|
|
|
|
|
if (message.data.storageData && Object.keys(message.data.storageData).length > 0) { |
|
|
|
|
|
this.storageData = Object.assign({}, this.storageData, message.data.storageData) |
|
|
|
|
|
// 重新初始化库位利用率图表 |
|
|
|
|
|
this.$nextTick(() => { |
|
|
|
|
|
this.initStorageChart() |
|
|
|
|
|
}) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 更新机器人数据 |
|
|
|
|
|
if (message.data.robotData !== undefined) { |
|
|
|
|
|
//this.robotData = message.data.robotData |
|
|
|
|
|
console.log('[智能立体仓库看板] 机器人数据已更新:', this.robotData.length, '条') |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 更新AGV数据(使用真实数据,包括空数组) |
|
|
|
|
|
if (message.data.agvData !== undefined) { |
|
|
|
|
|
this.agvData = message.data.agvData |
|
|
|
|
|
console.log('[智能立体仓库看板] AGV数据已更新:', this.agvData.length, '条') |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 更新领料申请单数据 |
|
|
|
|
|
if (message.data.materialRequestData && Object.keys(message.data.materialRequestData).length > 0) { |
|
|
|
|
|
this.materialRequestData = Object.assign({}, this.materialRequestData, message.data.materialRequestData) |
|
|
|
|
|
// 重新初始化领料申请单图表 |
|
|
|
|
|
this.$nextTick(() => { |
|
|
|
|
|
this.initMaterialRequestChart() |
|
|
|
|
|
}) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 更新发货数据 |
|
|
|
|
|
if (message.data.shipmentData && Object.keys(message.data.shipmentData).length > 0) { |
|
|
|
|
|
this.shipmentData = Object.assign({}, this.shipmentData, message.data.shipmentData) |
|
|
|
|
|
// 重新初始化发货图表 |
|
|
|
|
|
this.$nextTick(() => { |
|
|
|
|
|
this.initShipmentChart() |
|
|
|
|
|
}) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 断开WebSocket连接 |
|
|
|
|
|
*/ |
|
|
|
|
|
disconnectWebSocket() { |
|
|
|
|
|
if (this.wsSubscription) { |
|
|
|
|
|
WebSocketClient.unsubscribe(this.wsSubscription) |
|
|
|
|
|
this.wsSubscription = null |
|
|
|
|
|
console.log('[智能立体仓库看板] 已取消订阅') |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (this.wsConnected) { |
|
|
|
|
|
WebSocketClient.disconnect() |
|
|
|
|
|
this.wsConnected = false |
|
|
|
|
|
console.log('[智能立体仓库看板] WebSocket已断开') |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 初始化所有图表 |
|
|
* 初始化所有图表 |
|
|
*/ |
|
|
*/ |
|
|
@ -1725,6 +1852,28 @@ export default { |
|
|
50% { opacity: 0.5; } |
|
|
50% { opacity: 0.5; } |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* ========== 空状态提示 ========== */ |
|
|
|
|
|
.empty-state { |
|
|
|
|
|
display: flex; |
|
|
|
|
|
flex-direction: column; |
|
|
|
|
|
align-items: center; |
|
|
|
|
|
justify-content: center; |
|
|
|
|
|
padding: 20px; |
|
|
|
|
|
color: rgba(255, 255, 255, 0.4); |
|
|
|
|
|
font-size: 13px; |
|
|
|
|
|
grid-column: 1 / -1; /* 占据整个网格 */ |
|
|
|
|
|
|
|
|
|
|
|
i { |
|
|
|
|
|
font-size: 28px; |
|
|
|
|
|
margin-bottom: 8px; |
|
|
|
|
|
opacity: 0.6; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
span { |
|
|
|
|
|
opacity: 0.8; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/* ========== 库存趋势卡片 ========== */ |
|
|
/* ========== 库存趋势卡片 ========== */ |
|
|
.inventory-trend { |
|
|
.inventory-trend { |
|
|
.chart-container { |
|
|
.chart-container { |
|
|
|