websocket工具类
This commit is contained in:
199
src/utils/ws.js
Normal file
199
src/utils/ws.js
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
import { getToken } from '@/utils/auth'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前 WebSocket 实例
|
||||||
|
*/
|
||||||
|
let ws = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重连定时器
|
||||||
|
*/
|
||||||
|
let reconnectTimer = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否人为关闭(用于区分主动关闭 vs 异常断开)
|
||||||
|
*/
|
||||||
|
let manualClose = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前连接的部门ID(用于断线重连)
|
||||||
|
*/
|
||||||
|
let currentDeptId = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前消息回调函数
|
||||||
|
*/
|
||||||
|
let currentOnMessage = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebSocket路径
|
||||||
|
*/
|
||||||
|
const WS_PATH = '/ws'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重连间隔(毫秒)
|
||||||
|
*/
|
||||||
|
const RECONNECT_INTERVAL = 5000
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 WebSocket 基础地址
|
||||||
|
* 适配:
|
||||||
|
* 1. VITE_APP_BASE_API 是完整URL(http://xxx)
|
||||||
|
* 2. VITE_APP_BASE_API 是相对路径(/dev-api)
|
||||||
|
*/
|
||||||
|
function getWsBaseUrl() {
|
||||||
|
const baseApi = import.meta.env.VITE_APP_BASE_API || ''
|
||||||
|
|
||||||
|
// 如果是完整URL(http/https)
|
||||||
|
if (/^https?:\/\//.test(baseApi)) {
|
||||||
|
// 转换为 ws/wss
|
||||||
|
return baseApi.replace(/^http/, 'ws').replace(/\/$/, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据当前页面协议自动判断 ws / wss
|
||||||
|
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
|
||||||
|
return `${protocol}//${window.location.host}${baseApi}`.replace(/\/$/, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建 WebSocket 连接地址
|
||||||
|
* @param deptId 部门ID(用于数据隔离)
|
||||||
|
*/
|
||||||
|
function buildWsUrl(deptId) {
|
||||||
|
const url = new URL(`${getWsBaseUrl()}${WS_PATH}`)
|
||||||
|
const token = getToken()
|
||||||
|
|
||||||
|
// 当前后端仍依赖 deptId 做数据隔离
|
||||||
|
url.searchParams.set('deptId', deptId)
|
||||||
|
|
||||||
|
// token 用于后续鉴权升级(预留)
|
||||||
|
if (token) {
|
||||||
|
url.searchParams.set('token', token)
|
||||||
|
}
|
||||||
|
|
||||||
|
return url.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 建立 WebSocket 连接
|
||||||
|
* @param deptId 当前用户部门ID
|
||||||
|
* @param onMessage 消息回调函数
|
||||||
|
*/
|
||||||
|
export function connectWs(deptId, onMessage) {
|
||||||
|
// 必须传 deptId(当前后端依赖)
|
||||||
|
if (!deptId) {
|
||||||
|
console.warn('[WebSocket] 缺少 deptId')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
currentDeptId = deptId
|
||||||
|
currentOnMessage = onMessage
|
||||||
|
manualClose = false
|
||||||
|
|
||||||
|
// 清除旧的重连任务
|
||||||
|
clearReconnectTimer()
|
||||||
|
|
||||||
|
// 如果已经连接或正在连接,直接返回(防止重复连接)
|
||||||
|
if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建 WebSocket 连接
|
||||||
|
ws = new WebSocket(buildWsUrl(deptId))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接成功
|
||||||
|
*/
|
||||||
|
ws.onopen = () => {
|
||||||
|
console.log('[WebSocket] 连接成功 deptId=', deptId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 收到消息
|
||||||
|
*/
|
||||||
|
ws.onmessage = (event) => {
|
||||||
|
let data = event.data
|
||||||
|
|
||||||
|
// 尝试解析 JSON
|
||||||
|
try {
|
||||||
|
data = JSON.parse(event.data)
|
||||||
|
} catch (e) {
|
||||||
|
// 非JSON消息保持原样
|
||||||
|
}
|
||||||
|
|
||||||
|
// 回调给业务层
|
||||||
|
currentOnMessage && currentOnMessage(data, event)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接关闭
|
||||||
|
*/
|
||||||
|
ws.onclose = () => {
|
||||||
|
console.log('[WebSocket] 连接关闭 deptId=', currentDeptId)
|
||||||
|
ws = null
|
||||||
|
|
||||||
|
// 非人为关闭 → 自动重连
|
||||||
|
if (!manualClose) {
|
||||||
|
reconnectTimer = setTimeout(() => {
|
||||||
|
console.log('[WebSocket] 开始重连 deptId=', currentDeptId)
|
||||||
|
connectWs(currentDeptId, currentOnMessage)
|
||||||
|
}, RECONNECT_INTERVAL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接异常
|
||||||
|
*/
|
||||||
|
ws.onerror = (error) => {
|
||||||
|
console.error('[WebSocket] 连接异常', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主动关闭 WebSocket
|
||||||
|
*/
|
||||||
|
export function closeWs() {
|
||||||
|
manualClose = true
|
||||||
|
|
||||||
|
// 清除重连任务
|
||||||
|
clearReconnectTimer()
|
||||||
|
|
||||||
|
if (ws) {
|
||||||
|
ws.close()
|
||||||
|
ws = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送消息
|
||||||
|
* @param data 支持字符串或对象
|
||||||
|
* @returns 是否发送成功
|
||||||
|
*/
|
||||||
|
export function sendWs(data) {
|
||||||
|
// 连接未就绪直接返回
|
||||||
|
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
||||||
|
console.warn('[WebSocket] 发送失败,连接未建立')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自动转JSON
|
||||||
|
ws.send(typeof data === 'string' ? data : JSON.stringify(data))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前连接状态
|
||||||
|
*/
|
||||||
|
export function getWsState() {
|
||||||
|
return ws ? ws.readyState : WebSocket.CLOSED
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除重连定时器
|
||||||
|
*/
|
||||||
|
function clearReconnectTimer() {
|
||||||
|
if (reconnectTimer) {
|
||||||
|
clearTimeout(reconnectTimer)
|
||||||
|
reconnectTimer = null
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user