From 25d477b0da0ab9be596e2f132180bb7c986cb5a3 Mon Sep 17 00:00:00 2001 From: zhangjiming Date: Tue, 21 Jan 2025 15:41:47 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E5=85=85=E7=9B=AE=E5=BD=95=E5=92=8C?= =?UTF-8?q?=E7=8E=AF=E5=A2=83=E5=8F=98=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 3 ++ src/main.ts | 27 +++++++------ src/services/axios.ts | 58 ++++++++++++++++++++++++++ src/services/socket.ts | 108 +++++++++++++++++++++++++++++++++++++++++++++++++ src/utils/index.ts | 9 +++++ 5 files changed, 193 insertions(+), 12 deletions(-) create mode 100644 .env create mode 100644 src/services/axios.ts create mode 100644 src/services/socket.ts create mode 100644 src/utils/index.ts diff --git a/.env b/.env new file mode 100644 index 0000000..3bb47b5 --- /dev/null +++ b/.env @@ -0,0 +1,3 @@ +VITE_API_HOST=window.location.hostname +VITE_API_PORT=80 +VITE_WS_PATH=/api/v1/app/ws/state \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 0a62e6d..a6ccb48 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,17 +1,20 @@ -import './style.css' -import './assets/main.css' +import "./style.css"; +import "./assets/main.css"; -import { createApp } from 'vue' -import { createPinia } from 'pinia' -import Vant from 'vant' -import App from './App.vue' -import router from './router' -import 'vant/lib/index.css'; +import { createApp } from "vue"; +import { createPinia } from "pinia"; +import Vant from "vant"; +import App from "./App.vue"; +import router from "./router"; +import "vant/lib/index.css"; -const app = createApp(App) +const app = createApp(App); -app.use(createPinia()) -app.use(router) +app.use(createPinia()); +app.use(router); app.use(Vant); -app.mount('#app') +app.mount("#app"); + +console.log("API HOST:", import.meta.env.VITE_API_HOST); +console.log("API PORT:", import.meta.env.VITE_API_PORT); diff --git a/src/services/axios.ts b/src/services/axios.ts new file mode 100644 index 0000000..385e935 --- /dev/null +++ b/src/services/axios.ts @@ -0,0 +1,58 @@ +import axios from "axios"; + +const url = `${window.location.protocol}://${import.meta.env.VITE_API_HOST}:${import.meta.env.VITE_API_PORT}`; + +const apiClient = axios.create({ + baseURL: url, // 设置请求的根路径 + timeout: 60000, + headers: { + "Content-Type": "application/json", + }, +}); + +// 请求拦截器 +// apiClient.interceptors.request.use( +// (config) => { +// const token = sessionStorage.getItem("token"); +// if (!config.headers) { +// config.headers = AxiosHeaders.from({}); // 确保 config.headers 是 AxiosHeaders 类型 +// } + +// if (token) { +// config.headers.set("Authorization", `Bearer ${encodeURIComponent(token)}`); // 使用 set 方法设置 Authorization +// } + +// return config; +// }, +// error => { +// return Promise.reject(error); +// } +// ); + +// 响应拦截器 +apiClient.interceptors.response.use( + (response) => { + if (response.data && response.data.dataType === "ZAppPromopt") { + if (response.data.ecode === "USR_NOT_EXIT") { + return Promise.resolve(response.data); + } else if (response.data.ecode === "USR_PASSWORD_ERROR") { + return Promise.resolve(response.data); + } else { + console.log("接口出错", response.data); + // eventBus.emit("show-error-modal", response.data.data); + return Promise.reject(response.data); + } + } + return response; + }, + error => { + // eventBus.emit("show-error-modal", { + // messageLevel: "Error", + // title: "网络请求失败", + // info: error.message, + // }); + return Promise.reject(error); + } +); + +export default apiClient; diff --git a/src/services/socket.ts b/src/services/socket.ts new file mode 100644 index 0000000..cfcd286 --- /dev/null +++ b/src/services/socket.ts @@ -0,0 +1,108 @@ +import { Subject } from "rxjs"; + +export type SocketState = 'open' | 'close' | 'error' + +class WebSocketClient { + private ws: WebSocket | null = null; + private url: string; + private reconnectAttempts: number = -1; + private maxReconnectAttempts: number = 5; + private reconnectInterval: number = 3000; + + readonly dataOb = new Subject() + readonly stateOb = new Subject() + + constructor(url: string) { + this.url = url; + } + + // 连接 WebSocket + connect(): void { + try { + // WebSocket.CONNECTING (0) WebSocket.OPEN (1) + if (this.ws && this.ws.readyState <= 1) { + // 已连接 + console.log(`${this.url} 正在连接或已连接,无需重复连接`); + } else { + this.ws = new WebSocket(this.url); + this.bindEvents(); + } + } catch (error) { + console.error("WebSocket 连接失败:", error); + this.reconnect(); + } + } + + // 绑定事件 + private bindEvents(): void { + if (!this.ws) return; + + // 连接建立时的处理 + this.ws.onopen = () => { + console.log("WebSocket 连接已建立"); + this.reconnectAttempts = -1; // 重置重连次数 + this.stateOb.next('open') + }; + + // 接收消息的处理 + this.ws.onmessage = (event: MessageEvent) => { + try { + const data = JSON.parse(event.data); + // console.log('🚀 ~ WebSocketClient ~ bindEvents ~ data:', data) + this.dataOb.next(data) + } catch (error) { + console.error("消息解析错误:", error); + } + }; + + this.ws.onclose = () => { + this.stateOb.next('close') + console.log("WebSocket 连接已关闭"); + this.reconnect(); + }; + + this.ws.onerror = error => { + this.stateOb.next('error') + console.error("WebSocket 错误:", error); + }; + } + + // 重连机制 + private reconnect(): void { + if (this.reconnectAttempts === -1) { + this.reconnectAttempts = 0; + } + if (this.reconnectAttempts >= this.maxReconnectAttempts) { + console.log("达到最大重连次数,停止重连"); + this.reconnectAttempts = -1; + return; + } + + setTimeout(() => { + console.log(`尝试第 ${this.reconnectAttempts + 1} 次重连...`); + this.reconnectAttempts++; + this.connect(); + }, this.reconnectInterval); + } + + // 关闭连接 + disconnect(): void { + if (this.ws) { + this.ws.close(); + this.ws = null; + } + } +} + +const urlSocketMap = new Map(); + +// 导出 WebSocket 客户端 +export const createWebSocket = (url: string): WebSocketClient => { + if (urlSocketMap.has(url)) { + return urlSocketMap.get(url)!; + } else { + const client = new WebSocketClient(url); + urlSocketMap.set(url, client); + return client; + } +}; diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..64ebd70 --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,9 @@ +export function isValidIPv4(ip: string) { + const ipv4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; + return ipv4Regex.test(ip); +} +export function formatRemainTime(seconds: number) { + const min = Math.floor(seconds / 60).toFixed(); + const sec = (seconds % 60).toFixed(); + return min.padStart(2, "0") + ":" + sec.padStart(2, "0"); +}