This commit is contained in:
oneao committed 2025-10-14 17:30:21 +08:00
1 parent 35951777b0
commit 63f1d4d1dc
39 files changed
+880 -133

No files matched your search

@@ -4,18 +4,19 @@ import { ref } from 'vue'
export const useUserStore = defineStore(
'userInfo',
() => {
const userInfo = ref({
orgid: '',
orgid: 'g3hd',
userid: '',
token: '',
username: '',
})
// 设置用户信息
function setUserInfo(orgid, userid, token) {
function setUserInfo(orgid, userid, username, token) {
userInfo.value.orgid = orgid
userInfo.value.userid = userid
userInfo.value.token = token
userInfo.value.username = username
}
// 重置用户信息
@@ -1,7 +1,15 @@
// stores/websocket.js
import { ElNotification } from 'element-plus'
import { onScopeDispose, ref } from 'vue'
import { ref } from 'vue'
import { useUserStore } from './userStore'
let instance = null // 单例缓存
export function useWebSocket() {
if (instance)
return instance // 已存在则直接返回
const userStore = useUserStore()
const socket = ref(null)
const isConnected = ref(false)
const listeners = ref(new Set())
@@ -9,25 +17,19 @@ export function useWebSocket() {
let reconnectAttempts = 0
let pingTimer = null
let pongTimeout = null
const PING_INTERVAL = 10000 // 收到PONG后间隔再发送下一次PING
const PONG_TIMEOUT = 15000 // PING发送后15秒未收到PONG就重连
const PING_INTERVAL = 10000
const PONG_TIMEOUT = 15000
const currentUrl = 'ws://127.0.0.1:8090/ws'
let reconnectNotification = null
let currentUrl = null
let isReconnecting = false
let manualClose = false // ✅ 标记是否为手动关闭
// --------------------
// 私有方法
// --------------------
// ---------------- 清理 ----------------
function cleanup() {
if (pingTimer) {
clearTimeout(pingTimer)
pingTimer = null
}
if (pongTimeout) {
clearTimeout(pongTimeout)
pongTimeout = null
}
clearTimeout(pingTimer)
clearTimeout(pongTimeout)
pingTimer = null
pongTimeout = null
}
function handleDisconnect() {
@@ -39,23 +41,14 @@ export function useWebSocket() {
listeners.value.forEach(cb => cb(message))
}
// --------------------
// 心跳逻辑(链式)
// --------------------
// ---------------- 心跳 ----------------
function sendPing() {
if (!socket.value || socket.value.readyState !== WebSocket.OPEN)
return
try {
sendMessage('PING')
}
catch (err) {
console.error('发送 PING 失败', err)
}
sendMessage({ type: 'ping' })
// 设置 PONG 超时
if (pongTimeout)
clearTimeout(pongTimeout)
clearTimeout(pongTimeout)
pongTimeout = setTimeout(() => {
console.warn('未收到PONG,触发重连')
handleReconnect()
@@ -63,15 +56,8 @@ export function useWebSocket() {
}
function handlePong() {
// 收到 PONG,清理超时
if (pongTimeout) {
clearTimeout(pongTimeout)
pongTimeout = null
}
// 等 PING_INTERVAL 后再发送下一次 PING
if (pingTimer)
clearTimeout(pingTimer)
clearTimeout(pongTimeout)
clearTimeout(pingTimer)
pingTimer = setTimeout(sendPing, PING_INTERVAL)
}
@@ -79,62 +65,57 @@ export function useWebSocket() {
sendPing()
}
// --------------------
// 重连逻辑
// --------------------
// ---------------- 重连 ----------------
function handleReconnect() {
if (isReconnecting)
return
if (isReconnecting || manualClose)
return // ✅ 如果是手动断开则不重连
isReconnecting = true
cleanup()
isConnected.value = false
socket.value?.close()
reconnectAttempts++
if (reconnectNotification)
reconnectNotification.close()
reconnectNotification?.close()
reconnectNotification = ElNotification({
title: '提示',
message: `WebSocket 连接失败,正在第 ${reconnectAttempts} 次重连...`,
type: 'warning',
duration: 0,
key: 'websocket-reconnect',
})
const delay = Math.min(3000 * 2 ** (reconnectAttempts - 1), 30000)
setTimeout(() => {
isReconnecting = false
connect(currentUrl)
connect()
}, delay)
}
function clearReconnectNotification() {
if (reconnectNotification) {
reconnectNotification.close()
reconnectNotification = null
}
reconnectNotification?.close()
reconnectNotification = null
}
// --------------------
// 公共方法
// --------------------
function connect(url) {
currentUrl = url
// ---------------- 公共方法 ----------------
function connect() {
if (isConnected.value)
return
if (socket.value)
disconnect()
const newSocket = new WebSocket(url)
manualClose = false // ✅ 每次连接前重置为 false
const newSocket = new WebSocket(currentUrl)
newSocket.onopen = () => {
isConnected.value = true
isReconnecting = false
clearReconnectNotification()
if (userStore.isLogined)
sendLogin(userStore.userInfo.userid)
ElNotification({
title: '提示',
message: 'WebSocket 连接成功',
message: '上线成功',
type: 'success',
duration: 2000,
})
@@ -144,26 +125,42 @@ export function useWebSocket() {
}
newSocket.onmessage = (event) => {
let message
let resData
try {
message = JSON.parse(event.data)
console.log(event.data)
if (message.type === 'PONG') {
handlePong()
return
}
notifyListeners(message)
resData = JSON.parse(event.data)
}
catch (err) {
console.error('消息解析失败:', err)
notifyListeners({ type: 'ERROR', data: '消息解析失败' })
return
}
if (resData.code !== 200) {
ElNotification({
title: '错误',
message: resData.message || '发生未知错误',
type: 'error',
showClose: true,
duration: 3000,
})
return
}
if (resData.type === 'pong') {
handlePong()
}
else {
notifyListeners(resData)
}
}
newSocket.onclose = () => {
handleDisconnect()
if (!isReconnecting)
// ✅ 非手动断开才重连
if (!manualClose && !isReconnecting)
handleReconnect()
}
@@ -175,14 +172,25 @@ export function useWebSocket() {
}
function disconnect() {
if (!socket.value)
return
manualClose = true
socket.value?.close()
cleanup()
clearReconnectNotification()
ElNotification({
type: 'warning',
title: '提示',
message: '已下线',
})
}
function sendMessage(type, data) {
function sendMessage(data) {
if (isConnected.value && socket.value) {
const message = { type, data }
socket.value.send(JSON.stringify(message))
data.orgId = userStore.userInfo.orgid || ''
socket.value.send(JSON.stringify(data))
}
}
@@ -191,13 +199,23 @@ export function useWebSocket() {
return () => listeners.value.delete(callback)
}
onScopeDispose(() => disconnect())
function sendLogin(userId) {
if (!isConnected.value)
return
sendMessage({
type: 'login',
from: userId,
})
}
return {
instance = {
isConnected,
connect,
disconnect,
sendMessage,
onMessage,
sendLogin,
}
return instance
}