From 970aae1c5f3e39450c8b11bc8ae84396944c7c8f Mon Sep 17 00:00:00 2001 From: merlin Date: Mon, 23 Mar 2026 18:08:59 +0800 Subject: [PATCH] fix: dplayer can not playing --- proxy.js | 237 ++-- src/api/playroom.ts | 158 +-- src/assets/main.css | 72 +- src/store/playroom.ts | 165 +-- src/views/home/playroom.vue | 236 ++-- src/views/room/index.vue | 1053 ++++++++--------- .../{roomSocket.js => roomSocket.ts} | 311 ++--- vite.config.js | 5 - 8 files changed, 1117 insertions(+), 1120 deletions(-) rename src/websocket/{roomSocket.js => roomSocket.ts} (63%) diff --git a/proxy.js b/proxy.js index 68e6424..4566711 100644 --- a/proxy.js +++ b/proxy.js @@ -1,113 +1,124 @@ -import express from 'express'; -import fetch from 'node-fetch'; -import { URL } from 'url'; - -const app = express(); -const port = 3000; - -// 允许跨域请求 -app.use((req, res, next) => { - res.header('Access-Control-Allow-Origin', '*'); - res.header('Access-Control-Allow-Methods', 'GET, HEAD, OPTIONS'); - res.header('Access-Control-Allow-Headers', 'Range'); - next(); -}); - -// 通用代理路由 -app.get('/proxy', async (req, res) => { - console.log('Received Range header:', req.headers.range); - // 获取目标 URL - const targetUrl = req.query.url; - console.log('Fetching data from:', targetUrl); - if (!targetUrl) { - return res.status(400).json({ error: 'URL parameter is required' }); - } - - try { - // 设置请求头 - const headers = { - 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' - }; - if (req.headers.range) { - headers['Range'] = req.headers.range; // 转发 Range 请求头,支持视频分段加载 - } - - // 向目标 URL 发起请求 - const response = await fetch(targetUrl, { headers }); - - // 检查响应状态 - if (!response.ok) { - return res.status(response.status).json({ - error: `Failed to fetch: ${response.statusText}`, - status: response.status - }); - } - - // 设置响应头 - response.headers.forEach((value, key) => { - // 跳过一些可能导致问题的头信息 - if (key.toLowerCase() !== 'content-encoding' && - key.toLowerCase() !== 'transfer-encoding' && - key.toLowerCase() !== 'connection') { - res.setHeader(key, value); - } - }); - - // 特殊处理 Transfer-Encoding 和 Content-Length - if (response.headers.get('transfer-encoding') === 'chunked') { - res.removeHeader('Content-Length'); - } - - // 将响应体直接流式传输给客户端 - if (response.body) { - response.body.pipe(res, { end: true }); - - // 错误处理 - response.body.on('error', (err) => { - console.error('Error during data transfer:', err); - if (!res.headersSent) { - res.status(500).json({ error: 'Error during data transfer' }); - } - }); - } else { - res.status(500).json({ error: 'No response body' }); - } - } catch (error) { - console.error('Error fetching data:', error); - - // 根据错误类型返回更具体的错误信息 - let errorMessage = 'Error fetching data'; - let statusCode = 500; - let hostname = ''; - - try { - hostname = new URL(targetUrl).hostname; - } catch (e) { - hostname = 'unknown'; - } - - if (error.code === 'ENOTFOUND') { - errorMessage = `DNS解析失败:无法解析域名 "${hostname}"。请检查URL是否正确,或网络连接是否正常。`; - statusCode = 502; - } else if (error.code === 'ECONNREFUSED') { - errorMessage = `连接被拒绝:无法连接到目标服务器 "${hostname}"。`; - statusCode = 502; - } else if (error.code === 'ETIMEDOUT') { - errorMessage = `请求超时:目标服务器 "${hostname}" 响应时间过长。`; - statusCode = 504; - } - - if (!res.headersSent) { - res.status(statusCode).json({ - error: errorMessage, - code: error.code, - targetUrl: targetUrl - }); - } - } -}); - -app.listen(port, () => { - console.log(`Proxy server running at http://localhost:${port}`); -}); - +import express from 'express'; +import fetch from 'node-fetch'; +import { URL } from 'url'; + +const app = express(); +const port = 3000; + +// 允许跨域请求 +app.use((req, res, next) => { + res.header('Access-Control-Allow-Origin', '*'); + res.header('Access-Control-Allow-Methods', 'GET, HEAD, OPTIONS'); + res.header('Access-Control-Allow-Headers', 'Range'); + next(); +}); + +// 本地静态文件代理:/local -> C:\Users\xyf17\Merlin\data +const localDataPath = 'C:\\Users\\xyf17\\Merlin\\data'; +app.use('/local', express.static(localDataPath)); + +// 通用代理路由 +app.get('/proxy', async (req, res) => { + console.log('Received Range header:', req.headers.range); + // 获取目标 URL + const targetUrl = req.query.url; + console.log('Fetching data from:', targetUrl); + if (!targetUrl) { + return res.status(400).json({ error: 'URL parameter is required' }); + } + + try { + // 设置请求头 + const headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' + }; + + // 避免上游压缩导致 Range/MSE 解析异常(mp4 一般不会压缩,但这里保守处理) + headers['Accept-Encoding'] = 'identity'; + + if (req.headers.range) { + headers['Range'] = req.headers.range; // 转发 Range 请求头,支持视频分段加载 + } + + // 向目标 URL 发起请求 + const response = await fetch(targetUrl, { headers }); + + // 检查响应状态 + if (!response.ok) { + return res.status(response.status).json({ + error: `Failed to fetch: ${response.statusText}`, + status: response.status + }); + } + + // 关键:Range 请求上游通常返回 206;必须把状态码透传给浏览器 + res.status(response.status); + + // 设置响应头 + response.headers.forEach((value, key) => { + // 跳过一些可能导致问题的头信息 + if (key.toLowerCase() !== 'content-encoding' && + key.toLowerCase() !== 'transfer-encoding' && + key.toLowerCase() !== 'connection') { + res.setHeader(key, value); + } + }); + + // 特殊处理 Transfer-Encoding 和 Content-Length + if (response.headers.get('transfer-encoding') === 'chunked') { + res.removeHeader('Content-Length'); + } + + // 将响应体直接流式传输给客户端 + if (response.body) { + response.body.pipe(res, { end: true }); + + // 错误处理 + response.body.on('error', (err) => { + console.error('Error during data transfer:', err); + if (!res.headersSent) { + res.status(500).json({ error: 'Error during data transfer' }); + } + }); + } else { + res.status(500).json({ error: 'No response body' }); + } + } catch (error) { + console.error('Error fetching data:', error); + + // 根据错误类型返回更具体的错误信息 + let errorMessage = 'Error fetching data'; + let statusCode = 500; + let hostname = ''; + + try { + hostname = new URL(targetUrl).hostname; + } catch (e) { + hostname = 'unknown'; + } + + if (error.code === 'ENOTFOUND') { + errorMessage = `DNS解析失败:无法解析域名 "${hostname}"。请检查URL是否正确,或网络连接是否正常。`; + statusCode = 502; + } else if (error.code === 'ECONNREFUSED') { + errorMessage = `连接被拒绝:无法连接到目标服务器 "${hostname}"。`; + statusCode = 502; + } else if (error.code === 'ETIMEDOUT') { + errorMessage = `请求超时:目标服务器 "${hostname}" 响应时间过长。`; + statusCode = 504; + } + + if (!res.headersSent) { + res.status(statusCode).json({ + error: errorMessage, + code: error.code, + targetUrl: targetUrl + }); + } + } +}); + +app.listen(port, () => { + console.log(`Proxy server running at http://localhost:${port}`); +}); + diff --git a/src/api/playroom.ts b/src/api/playroom.ts index 143ffbd..0f0f83f 100644 --- a/src/api/playroom.ts +++ b/src/api/playroom.ts @@ -1,67 +1,93 @@ -import { userInfoStore } from "@/store/user"; -import axios from "axios"; - -const userinfo = userInfoStore(); - - -export const searchPlayRoom = async (inputValue: string) => { - const response = await axios.post('/api/playroom/search',{ - r_name: inputValue - },{ - headers:{ - 'Authorization': "Bearer " + userinfo.token - } - }) - if(response.data.code === "200"){ - return response.data.data.records - } else { - console.error(response.data.msg) - return [] - } -} - -export const createPlayroom = async (room_name: string) => { - const response = await axios.post('/api/playroom/create',{ - r_name: room_name - },{ - headers:{ - 'Authorization': "Bearer " + userinfo.token - } - }) - if(response.data.code === "200"){ - return true; - } else { - throw new Error(response.data.message); - } -} - -export const getPlayrooms = async ()=> { - const response = await axios.get('/api/playroom/get',{ - headers:{ - 'Authorization' : `Bearer ${userinfo.token}` - } - }) - if(response.data.code === "200"){ - return response.data.data; - } else { - throw new Error(response.data.message); - } -} - -export const joinPlayroom = async (r_id: number) => { - const response = await axios.post('/api/inviting/playroom',{ - inviter: userinfo.user.id, - target: userinfo.user.id, - status: 0, - room: r_id, - },{ - headers:{ - 'Authorization': "Bearer " + userinfo.token - } - }) - if(response.data.code === "200"){ - return true; - } else { - throw new Error(response.data.message); - } +import { userInfoStore } from "@/store/user"; +import axios from "axios"; + +const userinfo = userInfoStore(); + + +export const searchPlayRoom = async (inputValue: string) => { + const response = await axios.post('/api/playroom/search',{ + r_name: inputValue + },{ + headers:{ + 'Authorization': "Bearer " + userinfo.token + } + }) + if(response.data.code === "200"){ + return response.data.data.records + } else { + console.error(response.data.msg) + return [] + } +} + +export const createPlayroom = async (room_name: string) => { + const response = await axios.post('/api/playroom/create',{ + r_name: room_name + },{ + headers:{ + 'Authorization': "Bearer " + userinfo.token + } + }) + if(response.data.code === "200"){ + return true; + } else { + throw new Error(response.data.message); + } +} + +export const getPlayrooms = async ()=> { + const response = await axios.get('/api/playroom/get',{ + headers:{ + 'Authorization' : `Bearer ${userinfo.token}` + } + }) + if(response.data.code === "200"){ + return response.data.data; + } else { + throw new Error(response.data.message); + } +} + +export const joinPlayroom = async (r_id: number) => { + const response = await axios.post('/api/inviting/playroom',{ + inviter: userinfo.user.id, + target: userinfo.user.id, + status: 0, + room: r_id, + },{ + headers:{ + 'Authorization': "Bearer " + userinfo.token + } + }) + if(response.data.code === "200"){ + return true; + } else { + throw new Error(response.data.message); + } +} + +export const getPlayroomDetails = async (r_id:number) =>{ + const response = await axios.get('/api/playroom/detail/'+r_id,{ + headers:{ + 'Authorization': "Bearer " + userinfo.token + } + }) + if(response.data.code === "200"){ + return response.data.data; + } else { + throw new Error(response.data.message); + } +} + +export const getPlayroomMembers = async (r_id: number) => { + const response = await axios.get('/api/playroom/member/'+r_id,{ + headers:{ + 'Authorization': "Bearer " + userinfo.token + } + }) + if(response.data.code === "200"){ + return response.data.data; + } else { + throw new Error(response.data.message); + } } \ No newline at end of file diff --git a/src/assets/main.css b/src/assets/main.css index 28f54ef..be5cfb5 100644 --- a/src/assets/main.css +++ b/src/assets/main.css @@ -1,35 +1,37 @@ -@import './base.css'; - -#app { - max-width: 1280px; - /* margin: 0 auto; */ - /* padding: 2rem; */ - font-weight: normal; -} - -a, -.green { - text-decoration: none; - color: hsla(160, 100%, 37%, 1); - transition: 0.4s; - padding: 3px; -} - -@media (hover: hover) { - a:hover { - /* background-color: hsla(160, 100%, 37%, 0.2); */ - } -} - -@media (min-width: 1024px) { - body { - display: flex; - place-items: center; - } - - #app { - display: grid; - /* grid-template-columns: 1fr 1fr; - padding: 0 2rem; */ - } -} +@import './base.css'; + +#app { + max-width: 1280px; + width: 100%; + height: 100%; + /* margin: 0 auto; */ + /* padding: 2rem; */ + font-weight: normal; +} + +a, +.green { + text-decoration: none; + color: hsla(160, 100%, 37%, 1); + transition: 0.4s; + padding: 3px; +} + +@media (hover: hover) { + a:hover { + /* background-color: hsla(160, 100%, 37%, 0.2); */ + } +} + +@media (min-width: 1024px) { + body { + display: flex; + place-items: center; + } + + #app { + display: grid; + /* grid-template-columns: 1fr 1fr; + padding: 0 2rem; */ + } +} diff --git a/src/store/playroom.ts b/src/store/playroom.ts index 247df66..81a0fa4 100644 --- a/src/store/playroom.ts +++ b/src/store/playroom.ts @@ -1,73 +1,92 @@ -import { ref } from "vue"; -import { defineStore } from "pinia"; -import { connectWebSocket, disconnectWebSocket, sendMessage, setIsManualClose } from '@/websocket/roomSocket' - -interface PlayroomState { - id: number - r_id: number; - r_name: string; - r_introduction: string; - r_avatar: string; - role: number; -} - - -export const PlayroomStore = defineStore("PlayroomStore", - () =>{ - const currentPlayroom = ref(); - const currentUrl = ref(""); - - const setCurrentPlayroom = (playroom: PlayroomState) => { - currentPlayroom.value = playroom; - } - - const getCurrentPlayroom = () =>{ - return currentPlayroom.value; - } - - const getCurrentId = () =>{ - return currentPlayroom.value?.r_id; - } - - const setCurrentUrl = (url: string) => { - currentUrl.value = url; - } - - const clearPlayroom = () => { - currentPlayroom.value = undefined; - currentUrl.value = ""; - } - - return { - getCurrentPlayroom, - getCurrentId, - setCurrentPlayroom, - setCurrentUrl, - clearPlayroom, - } -}) - -export const videoSocketStore = defineStore("videoSocketStore",{ - state: () => ({ - isConnected: false, - hasGotMessage: false, - id: 0 - }), - - actions: { - connect(id: number) { - this.id = id; - if (this.isConnected === true) return - connectWebSocket(); - this.isConnected = true; - }, - disconnect() { - setIsManualClose(true); - disconnectWebSocket(); - this.isConnected = false; - }, - send(message: string) { - sendMessage(JSON.stringify(message)); - } - } - }) +import { ref } from "vue"; +import { defineStore } from "pinia"; +import { connectWebSocket, disconnectWebSocket, sendMessage, setIsManualClose } from "@/websocket/roomSocket"; + +interface PlayroomState { + id: number + r_id: number; + r_name: string; + r_introduction: string; + r_avatar: string; + role: number; +} + +interface member { + id: number; + u_id: string; + u_name: string; + u_avatar: string; +} + + +export const PlayroomStore = defineStore("PlayroomStore", + () =>{ + const currentPlayroom = ref(); + const members = ref([]); + const currentUrl = ref(""); + + const setCurrentPlayroom = (playroom: PlayroomState) => { + currentPlayroom.value = playroom; + } + + const getCurrentPlayroom = () =>{ + return currentPlayroom.value; + } + + const getCurrentId = () =>{ + return currentPlayroom.value?.r_id; + } + + const setCurrentUrl = (url: string) => { + currentUrl.value = url; + } + + const clearPlayroom = () => { + currentPlayroom.value = null; + currentUrl.value = ""; + members.value = []; + } + + const addmember = (member: member) => { + members.value.push(member); + } + + const getmembers = (page: number, pageSize: number) => { + return members.value.slice((page - 1) * pageSize, page * pageSize); + } + + + return { + currentPlayroom, + currentUrl, + setCurrentPlayroom, + clearPlayroom, + addmember, + getmembers, + } +}) + +export const videoSocketStore = defineStore("videoSocketStore",{ + state: () => ({ + isConnected: false, + hasGotMessage: false, + id: 0 + }), + + actions: { + connect(id: number) { + this.id = id; + if (this.isConnected === true) return + connectWebSocket(id); + this.isConnected = true; + }, + disconnect() { + setIsManualClose(true); + disconnectWebSocket(); + this.isConnected = false; + }, + send(message: string) { + sendMessage(JSON.stringify(message)); + } + } + }) diff --git a/src/views/home/playroom.vue b/src/views/home/playroom.vue index 6427e3f..7080e62 100644 --- a/src/views/home/playroom.vue +++ b/src/views/home/playroom.vue @@ -1,119 +1,119 @@ - - - - - \ No newline at end of file diff --git a/src/views/room/index.vue b/src/views/room/index.vue index 9500ce3..fbfa8a1 100644 --- a/src/views/room/index.vue +++ b/src/views/room/index.vue @@ -1,562 +1,493 @@ - - - - - \ No newline at end of file diff --git a/src/websocket/roomSocket.js b/src/websocket/roomSocket.ts similarity index 63% rename from src/websocket/roomSocket.js rename to src/websocket/roomSocket.ts index 0cfa87b..e3fdd83 100644 --- a/src/websocket/roomSocket.js +++ b/src/websocket/roomSocket.ts @@ -1,149 +1,162 @@ -import { ref } from "vue"; -import { userInfoStore } from "@/store/user"; -import { ElMessage } from "element-plus"; -import { PlayroomStore } from "@/store/playroom"; - -// userinfo 实例 -const userinfo = userInfoStore(); - -// 延迟获取 playroom 实例,避免循环依赖 -const getPlayroom = () => { - return PlayroomStore(); -}; - - -// WebSocket 实例 -const socket = ref(null); - -const isManualClose = ref(false); - -const reconnectScheduled = ref(false); - -const retryCount = ref(0); - -export const getRetryCount = () => { - return retryCount.value; -}; - -export const addRetryCount = () => { - retryCount.value = retryCount.value + 1; -}; - -export const resetRetryCount = () => { - retryCount.value = 0; -}; -export const setReconnectScheduled = (value) => { - reconnectScheduled.value = value; -}; - -export const getReconnectScheduled = () => { - return reconnectScheduled.value; -}; - -export const setIsManualClose = (value) => { - isManualClose.value = value; -}; - -export const getIsManualClose = () => { - return isManualClose.value; -}; - -// 连接WebSocket -export const connectWebSocket = () => { - const playroom = getPlayroom(); - const protocol = window.location.protocol === "https:" ? "wss://" : "ws://"; - const host = window.location.host; - const socketUrl = `${protocol}${host}/ws/video?r_id=${playroom.getCurrentId()}`; - - // const socketUrl = `ws://localhost:8080/online?u_id=${userinfo.user.u_id}&u_name=${userinfo.user.u_name}`; - if (socket.value && socket.value.readyState !== WebSocket.CLOSED) { - console.log("还在重连中..."); - return; - } - const retrytime = getRetryCount(); - if (retrytime >= 10) { - console.log("重连失败,请稍后再试"); - return; - } - console.log(retrytime); - socket.value = new WebSocket(socketUrl, "token-"+ userinfo.token); - - socket.value.onopen = (event) => { - console.log("WebSocket for video 连接已建立", event); - setReconnectScheduled(false); - setIsManualClose(false); - resetRetryCount(); - }; - - //处理消息逻辑 - socket.value.onmessage = (event) => { - console.log("从服务器收到消息:", event.data); - try{ - const MessageData = JSON.parse(event.data); - const cmd = MessageData.cmd; - switch(cmd){ - case "VIDEO_SYNC": - console.log("视频同步消息", MessageData); - break; - } - }catch(error){ - console.error("解析 JSON 失败:", error); - } - - }; - - socket.value.onerror = (error) => { - console.error("WebSocket for video 发生错误:", error); - // console.log(error); - setReconnectScheduled(true); - socket.value.close(); - }; - - socket.value.onclose = (event) => { - if (!getIsManualClose()) { - if (getReconnectScheduled()) { - socket.value = null; - addRetryCount(); - setTimeout(reConnectWebSocket, 5000); - setReconnectScheduled(false); - } else { - // console.log("websocket因为浏览器省电设置断开"); - console.log("WebSocket for video 连接已关闭", event); - } - } - }; -}; - -// 断开WebSocket连接 -export const disconnectWebSocket = () => { - if (socket.value && socket.value.readyState === WebSocket.OPEN) { - socket.value.close(); - } -}; - -// 重连机制 -export const reConnectWebSocket = () => { - connectWebSocket(); -}; - -// 发送消息 -export const sendMessage = (message) => { - if (socket.value && socket.value.readyState === WebSocket.OPEN) { - socket.value.send(message); - } else { - console.warn("WebSocket for video 未连接,无法发送消息"); - } -}; - -//没有错误的重连,只是浏览器在后台断开了连接 -document.addEventListener("visibilitychange", () => { - if (document.hidden) { - } else { - if (!getIsManualClose() && socket.value.readyState === WebSocket.CLOSED) { - if (getReconnectScheduled()) { - return; - } - reConnectWebSocket(); - } - } -}); +import { ref } from "vue"; +import { userInfoStore } from "@/store/user"; + + +// userinfo 实例 +const userinfo = userInfoStore(); + +const roomid = ref(0); + + +// WebSocket 实例 +const socket = ref(null); + +const isManualClose = ref(false); + +const reconnectScheduled = ref(false); + +const retryCount = ref(0); + +const getRetryCount = () => { + return retryCount.value; +}; + +const addRetryCount = () => { + retryCount.value = retryCount.value + 1; +}; + +const resetRetryCount = () => { + retryCount.value = 0; +}; +const setReconnectScheduled = (value: boolean) => { + reconnectScheduled.value = value; +}; + +const getReconnectScheduled = () => { + return reconnectScheduled.value; +}; + +export const setIsManualClose = (value: boolean) => { + isManualClose.value = value; +}; + +const getIsManualClose = () => { + return isManualClose.value; +}; + +// 连接WebSocket +export const connectWebSocket = (r_id: number) => { + roomid.value = r_id; + const protocol = window.location.protocol === "https:" ? "wss://" : "ws://"; + const host = window.location.host; + const socketUrl = `${protocol}${host}/ws/playroom?r_id=${r_id}`; + + // const socketUrl = `ws://localhost:8080/online?u_id=${userinfo.user.u_id}&u_name=${userinfo.user.u_name}`; + if (socket.value && socket.value.readyState !== WebSocket.CLOSED) { + console.log("还在重连中..."); + return; + } + const retrytime = getRetryCount(); + if (retrytime >= 10) { + console.log("重连失败,请稍后再试"); + return; + } + console.log(retrytime); + socket.value = new WebSocket(socketUrl, "token-"+ userinfo.token); + + socket.value.onopen = (event: any) => { + console.log("WebSocket for video 连接已建立", event); + setReconnectScheduled(false); + setIsManualClose(false); + resetRetryCount(); + }; + + //处理消息逻辑 + socket.value.onmessage = (event) => { + console.log("从服务器收到消息:", event.data); + try{ + const MessageData = JSON.parse(event.data); + const cmd = MessageData.cmd; + switch(cmd){ + case "PING": + console.log("收到PING消息"); + const msg = { + cmd: "PONG", + from: MessageData.to, + // 可扩展字段 + time: new Date().toLocaleString() + } + sendMessage(msg); + break; + case "VIDEO_SYNC": + // console.log("视频同步消息", MessageData); + break; + } + }catch(error){ + console.error("解析 JSON 失败:", error); + } + + }; + + socket.value.onerror = (error) => { + console.error("WebSocket for video 发生错误:", error); + // console.log(error); + setReconnectScheduled(true); + socket.value.close(); + }; + + socket.value.onclose = (event) => { + if (!getIsManualClose()) { + if (getReconnectScheduled()) { + socket.value = null; + addRetryCount(); + setTimeout(reConnectWebSocket, 5000); + setReconnectScheduled(false); + } else { + // console.log("websocket因为浏览器省电设置断开"); + console.log("WebSocket for video 连接已关闭", event); + } + } + }; +}; + +// 断开WebSocket连接 +export const disconnectWebSocket = () => { + if (socket.value && socket.value.readyState === WebSocket.OPEN) { + roomid.value = 0; + socket.value.close(); + } +}; + +// 重连机制 +export const reConnectWebSocket = () => { + connectWebSocket(roomid.value); +}; + +// 发送消息 +export const sendMessage = (message: any) => { + try{ + const jsonmessage = JSON.stringify(message); + if (socket.value && socket.value.readyState === WebSocket.OPEN) { + socket.value.send(jsonmessage); + } else { + console.warn("WebSocket for video 未连接,无法发送消息"); + } + } + catch(error){ + console.error("Failed to stringify message:", error); + } +}; + +//没有错误的重连,只是浏览器在后台断开了连接 +document.addEventListener("visibilitychange", () => { + if (document.hidden) { + } else { + if (!getIsManualClose() && socket.value.readyState === WebSocket.CLOSED) { + if (getReconnectScheduled()) { + return; + } + reConnectWebSocket(); + } + } +}); diff --git a/vite.config.js b/vite.config.js index 118ccd9..01aeb7e 100644 --- a/vite.config.js +++ b/vite.config.js @@ -29,11 +29,6 @@ export default defineConfig({ changeOrigin: true, ws: true, }, - // '/ws': { - // target: 'ws://localhost:8080', - // changeOrigin: true, - // ws: true, - // } }, host: '0.0.0.0', port: 5173,