管理员首页

This commit is contained in:
2026-04-15 08:32:42 +08:00
parent 608b645828
commit 3503fada5e
2 changed files with 1263 additions and 103 deletions

View File

@@ -1,72 +1,29 @@
import { getToken } from '@/utils/auth'
/**
* 当前 WebSocket 实例
*/
let ws = null
/**
* 重连定时器
*/
let reconnectTimer = null
/**
* 是否人为关闭(用于区分主动关闭 vs 异常断开)
*/
let manualClose = false
/**
* 当前连接的部门ID用于断线重连
*/
let currentDeptId = null
/**
* 当前消息回调函数
*/
let currentOnMessage = null
let retryCount = 0
/**
* WebSocket路径
*/
const WS_PATH = '/ws'
const MAX_RECONNECT_DELAY = 30000
/**
* 重连间隔(毫秒)
*/
const RECONNECT_INTERVAL = 5000
/**
* 获取 WebSocket 基础地址
* 适配:
* 1. VITE_APP_BASE_API 是完整URLhttp://xxx
* 2. VITE_APP_BASE_API 是相对路径(/dev-api
*/
function getWsBaseUrl() {
const baseApi = import.meta.env.VITE_APP_BASE_API || ''
// 如果是完整URLhttp/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) {
function buildWsUrl() {
const url = new URL(`${getWsBaseUrl()}${WS_PATH}`)
const token = getToken()
// 当前后端仍依赖 deptId 做数据隔离
url.searchParams.set('deptId', deptId)
// token 用于后续鉴权升级(预留)
if (token) {
url.searchParams.set('token', token)
}
@@ -74,88 +31,59 @@ function buildWsUrl(deptId) {
return url.toString()
}
/**
* 建立 WebSocket 连接
* @param deptId 当前用户部门ID
* @param onMessage 消息回调函数
*/
export function connectWs(deptId, onMessage) {
// 必须传 deptId当前后端依赖
if (!deptId) {
console.warn('[WebSocket] 缺少 deptId')
export function connectWs(onMessage) {
if (!getToken()) {
console.warn('[WebSocket] missing token')
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 = new WebSocket(buildWsUrl())
/**
* 连接成功
*/
ws.onopen = () => {
console.log('[WebSocket] 连接成功 deptId=', deptId)
retryCount = 0
console.log('[WebSocket] connected')
}
/**
* 收到消息
*/
ws.onmessage = (event) => {
let data = event.data
// 尝试解析 JSON
try {
data = JSON.parse(event.data)
} catch (e) {
// 非JSON消息保持原样
// Keep plain text messages as-is.
}
// 回调给业务层
currentOnMessage && currentOnMessage(data, event)
}
/**
* 连接关闭
*/
ws.onclose = () => {
console.log('[WebSocket] 连接关闭 deptId=', currentDeptId)
console.log('[WebSocket] closed')
ws = null
// 非人为关闭 → 自动重连
if (!manualClose) {
reconnectTimer = setTimeout(() => {
console.log('[WebSocket] 开始重连 deptId=', currentDeptId)
connectWs(currentDeptId, currentOnMessage)
}, RECONNECT_INTERVAL)
console.log('[WebSocket] reconnecting')
connectWs(currentOnMessage)
}, getReconnectDelay())
}
}
/**
* 连接异常
*/
ws.onerror = (error) => {
console.error('[WebSocket] 连接异常', error)
console.error('[WebSocket] error', error)
}
}
/**
* 主动关闭 WebSocket
*/
export function closeWs() {
manualClose = true
// 清除重连任务
retryCount = 0
clearReconnectTimer()
if (ws) {
@@ -164,36 +92,34 @@ export function closeWs() {
}
}
/**
* 发送消息
* @param data 支持字符串或对象
* @returns 是否发送成功
*/
export function reconnectWs(onMessage = currentOnMessage) {
closeWs()
manualClose = false
connectWs(onMessage)
}
export function sendWs(data) {
// 连接未就绪直接返回
if (!ws || ws.readyState !== WebSocket.OPEN) {
console.warn('[WebSocket] 发送失败,连接未建立')
console.warn('[WebSocket] send failed, socket is not open')
return false
}
// 自动转JSON
ws.send(typeof data === 'string' ? data : JSON.stringify(data))
return true
}
/**
* 获取当前连接状态
*/
export function getWsState() {
return ws ? ws.readyState : WebSocket.CLOSED
}
/**
* 清除重连定时器
*/
function getReconnectDelay() {
retryCount += 1
return Math.min(5000 * retryCount, MAX_RECONNECT_DELAY)
}
function clearReconnectTimer() {
if (reconnectTimer) {
clearTimeout(reconnectTimer)
reconnectTimer = null
}
}
}

File diff suppressed because it is too large Load Diff