7 changed files with 2289 additions and 1475 deletions
-
2package.json
-
2src/router/index.js
-
219src/utils/websocket.js
-
1370src/views/modules/dashboard/inventory-board.vue
-
97src/views/modules/dashboard/robot-picking-board.vue
-
192src/views/modules/dashboard/slitting-board.vue
-
1882src/views/modules/dashboard/warehouse-3d-board.vue
@ -0,0 +1,219 @@ |
|||||
|
/** |
||||
|
* WebSocket 工具类 |
||||
|
* |
||||
|
* 功能说明: |
||||
|
* 1. 封装WebSocket连接管理 |
||||
|
* 2. 自动重连机制 |
||||
|
* 3. 心跳检测 |
||||
|
* 4. 订阅管理 |
||||
|
* |
||||
|
* 使用示例: |
||||
|
* import WebSocketClient from '@/utils/websocket' |
||||
|
* |
||||
|
* // 连接
|
||||
|
* WebSocketClient.connect('http://localhost:8080/ws/dashboard', () => { |
||||
|
* console.log('连接成功') |
||||
|
* // 订阅主题
|
||||
|
* WebSocketClient.subscribe('/topic/dashboard/robot-picking', (data) => { |
||||
|
* console.log('收到数据:', data) |
||||
|
* }) |
||||
|
* }) |
||||
|
*/ |
||||
|
|
||||
|
import SockJS from 'sockjs-client' |
||||
|
import Stomp from 'stompjs' |
||||
|
|
||||
|
class WebSocketClient { |
||||
|
constructor() { |
||||
|
this.stompClient = null |
||||
|
this.connected = false |
||||
|
this.reconnectAttempts = 0 |
||||
|
this.maxReconnectAttempts = 20 |
||||
|
this.reconnectInterval = 3000 |
||||
|
this.subscriptions = new Map() |
||||
|
this.heartbeatInterval = null |
||||
|
this.url = null |
||||
|
this.onConnectedCallback = null |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 连接WebSocket服务器 |
||||
|
* |
||||
|
* @param {string} url WebSocket服务器地址 |
||||
|
* @param {function} onConnected 连接成功回调 |
||||
|
* @param {function} onError 连接错误回调 |
||||
|
*/ |
||||
|
connect(url, onConnected, onError) { |
||||
|
this.url = url |
||||
|
this.onConnectedCallback = onConnected |
||||
|
|
||||
|
console.log('[WebSocket] 正在连接服务器...', url) |
||||
|
|
||||
|
try { |
||||
|
const socket = new SockJS(url) |
||||
|
this.stompClient = Stomp.over(socket) |
||||
|
|
||||
|
// 禁用调试日志(生产环境)
|
||||
|
// 开发环境可以设置为 console.log
|
||||
|
this.stompClient.debug = null |
||||
|
|
||||
|
this.stompClient.connect( |
||||
|
{}, |
||||
|
frame => { |
||||
|
console.log('[WebSocket] ✅ 连接成功') |
||||
|
this.connected = true |
||||
|
this.reconnectAttempts = 0 |
||||
|
this.startHeartbeat() |
||||
|
|
||||
|
if (onConnected) { |
||||
|
onConnected(frame) |
||||
|
} |
||||
|
}, |
||||
|
error => { |
||||
|
console.error('[WebSocket] ❌ 连接失败:', error) |
||||
|
this.connected = false |
||||
|
this.handleDisconnect() |
||||
|
|
||||
|
if (onError) { |
||||
|
onError(error) |
||||
|
} |
||||
|
} |
||||
|
) |
||||
|
} catch (error) { |
||||
|
console.error('[WebSocket] 连接异常:', error) |
||||
|
if (onError) { |
||||
|
onError(error) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 订阅主题 |
||||
|
* |
||||
|
* @param {string} topic 主题名称 |
||||
|
* @param {function} callback 消息回调函数 |
||||
|
* @returns {string} 订阅ID |
||||
|
*/ |
||||
|
subscribe(topic, callback) { |
||||
|
if (!this.connected || !this.stompClient) { |
||||
|
console.warn('[WebSocket] 未连接,无法订阅主题:', topic) |
||||
|
return null |
||||
|
} |
||||
|
|
||||
|
console.log('[WebSocket] 订阅主题:', topic) |
||||
|
|
||||
|
const subscription = this.stompClient.subscribe(topic, message => { |
||||
|
try { |
||||
|
const data = JSON.parse(message.body) |
||||
|
callback(data) |
||||
|
} catch (error) { |
||||
|
console.error('[WebSocket] 解析消息失败:', error) |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
const subscriptionId = topic |
||||
|
this.subscriptions.set(subscriptionId, subscription) |
||||
|
|
||||
|
return subscriptionId |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 取消订阅 |
||||
|
* |
||||
|
* @param {string} subscriptionId 订阅ID |
||||
|
*/ |
||||
|
unsubscribe(subscriptionId) { |
||||
|
const subscription = this.subscriptions.get(subscriptionId) |
||||
|
if (subscription) { |
||||
|
subscription.unsubscribe() |
||||
|
this.subscriptions.delete(subscriptionId) |
||||
|
console.log('[WebSocket] 取消订阅:', subscriptionId) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 发送消息 |
||||
|
* |
||||
|
* @param {string} destination 目标地址 |
||||
|
* @param {object} data 消息数据 |
||||
|
*/ |
||||
|
send(destination, data) { |
||||
|
if (!this.connected || !this.stompClient) { |
||||
|
console.warn('[WebSocket] 未连接,无法发送消息') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
this.stompClient.send(destination, {}, JSON.stringify(data)) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 断开连接 |
||||
|
*/ |
||||
|
disconnect() { |
||||
|
if (this.stompClient) { |
||||
|
this.stompClient.disconnect(() => { |
||||
|
console.log('[WebSocket] 已断开连接') |
||||
|
}) |
||||
|
this.connected = false |
||||
|
this.stopHeartbeat() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 处理断开连接 |
||||
|
*/ |
||||
|
handleDisconnect() { |
||||
|
this.connected = false |
||||
|
this.stopHeartbeat() |
||||
|
|
||||
|
// 自动重连
|
||||
|
if (this.reconnectAttempts < this.maxReconnectAttempts) { |
||||
|
this.reconnectAttempts++ |
||||
|
console.log(`[WebSocket] 尝试重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`) |
||||
|
|
||||
|
setTimeout(() => { |
||||
|
if (this.url && this.onConnectedCallback) { |
||||
|
this.connect(this.url, this.onConnectedCallback) |
||||
|
} |
||||
|
}, this.reconnectInterval) |
||||
|
} else { |
||||
|
console.error('[WebSocket] 达到最大重连次数,放弃重连') |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 启动心跳检测 |
||||
|
*/ |
||||
|
startHeartbeat() { |
||||
|
this.heartbeatInterval = setInterval(() => { |
||||
|
if (this.connected && this.stompClient) { |
||||
|
// 发送心跳消息
|
||||
|
// 注意:如果后端没有心跳接口,可以注释掉这行
|
||||
|
// this.send('/app/heartbeat', { timestamp: Date.now() })
|
||||
|
} |
||||
|
}, 30000) // 每30秒发送一次心跳
|
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 停止心跳检测 |
||||
|
*/ |
||||
|
stopHeartbeat() { |
||||
|
if (this.heartbeatInterval) { |
||||
|
clearInterval(this.heartbeatInterval) |
||||
|
this.heartbeatInterval = null |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 检查连接状态 |
||||
|
* |
||||
|
* @returns {boolean} 是否已连接 |
||||
|
*/ |
||||
|
isConnected() { |
||||
|
return this.connected |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 导出单例
|
||||
|
export default new WebSocketClient() |
||||
|
|
||||
1370
src/views/modules/dashboard/inventory-board.vue
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1882
src/views/modules/dashboard/warehouse-3d-board.vue
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
Write
Preview
Loading…
Cancel
Save
Reference in new issue