10 changed files with 237 additions and 59 deletions
-
1.env
-
1package.json
-
1public/index.html
-
25public/manifest.json
-
25src/App.tsx
-
39src/pages/Measure.tsx
-
41src/services/httpRequest.ts
-
120src/services/socket.ts
-
16src/store/features/measureSlice.ts
-
25src/utils/bridge.ts
@ -0,0 +1 @@ |
|||||
|
REACT_APP_WS_URL=localhost:8080/ws |
@ -1,25 +0,0 @@ |
|||||
{ |
|
||||
"short_name": "React App", |
|
||||
"name": "Create React App Sample", |
|
||||
"icons": [ |
|
||||
{ |
|
||||
"src": "favicon.ico", |
|
||||
"sizes": "64x64 32x32 24x24 16x16", |
|
||||
"type": "image/x-icon" |
|
||||
}, |
|
||||
{ |
|
||||
"src": "logo192.png", |
|
||||
"type": "image/png", |
|
||||
"sizes": "192x192" |
|
||||
}, |
|
||||
{ |
|
||||
"src": "logo512.png", |
|
||||
"type": "image/png", |
|
||||
"sizes": "512x512" |
|
||||
} |
|
||||
], |
|
||||
"start_url": ".", |
|
||||
"display": "standalone", |
|
||||
"theme_color": "#000000", |
|
||||
"background_color": "#ffffff" |
|
||||
} |
|
@ -0,0 +1,41 @@ |
|||||
|
|
||||
|
type HttpReqParam = { |
||||
|
url: string; |
||||
|
method?: "GET" | "POST" | "PATCH" | "PUT" | "DELETE"; |
||||
|
params?: Record<string, any>; |
||||
|
encode?: "form" | "json"; // 入参编码类型
|
||||
|
headers?: Record<string, any>; |
||||
|
}; |
||||
|
|
||||
|
export default async function httpRequest<T>({ url, method = "GET", params = {}, encode = "json", headers = {} }: HttpReqParam) { |
||||
|
// const token = sessionStorage.getItem("token");
|
||||
|
// if (token) {
|
||||
|
// headers = { Authorization: token, ...headers };
|
||||
|
// }
|
||||
|
if (method === "GET") { |
||||
|
const query = urlEncode(params); |
||||
|
const _url = query ? url + "?" + query : url; |
||||
|
const res = await fetch(_url, { headers }); |
||||
|
return res.json() as Promise<T>; |
||||
|
} else { |
||||
|
const body = encode === "json" ? JSON.stringify(params) : urlEncode(params); |
||||
|
const _headers = |
||||
|
encode === "json" |
||||
|
? { "Content-Type": "application/json; charset=utf-8", ...headers } |
||||
|
: { "Content-Type": "application/x-www-form-urlencoded; charset=utf-8", ...headers }; |
||||
|
const res = await fetch(url, { method, headers: _headers, body }); |
||||
|
return res.json() as Promise<T>; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export function urlEncode(params?: Record<string, any>) { |
||||
|
let query = ""; |
||||
|
if (params && Object.keys(params).length > 0) { |
||||
|
const qs = []; |
||||
|
for (let attr in params) { |
||||
|
qs.push(`${attr}=${encodeURIComponent(params[attr])}`); |
||||
|
} |
||||
|
query = qs.join("&"); |
||||
|
} |
||||
|
return query; |
||||
|
} |
@ -0,0 +1,120 @@ |
|||||
|
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; |
||||
|
|
||||
|
private dataSub = new Subject<{ func: string; data: Record<string, any> | any[] }>(); |
||||
|
get dataOb() { |
||||
|
return this.dataSub.asObservable(); |
||||
|
} |
||||
|
private stateSub = new Subject<SocketState>(); |
||||
|
get stateOb() { |
||||
|
return this.stateSub.asObservable(); |
||||
|
} |
||||
|
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(); |
||||
|
} |
||||
|
localStorage.setItem('wsReadyState', `${this.ws.readyState}`) |
||||
|
} 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.stateSub.next("open"); |
||||
|
}; |
||||
|
|
||||
|
// 接收消息的处理
|
||||
|
this.ws.onmessage = (event: MessageEvent) => { |
||||
|
try { |
||||
|
const data = JSON.parse(event.data) as { func: string; data: Record<string, any> | any[] }; |
||||
|
// console.log("🚀 ~ WebSocketClient ~ bindEvents ~ data:", data);
|
||||
|
// if (data.type === "cmd") {
|
||||
|
// this.dataSub.next({ type: data.type, data: { ...data.data, success: data.data.status === "D0000" } });
|
||||
|
// } else {
|
||||
|
this.dataSub.next(data); |
||||
|
// }
|
||||
|
} catch (error) { |
||||
|
console.error("消息解析错误:", error); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
this.ws.onclose = () => { |
||||
|
this.stateSub.next("close"); |
||||
|
console.log("WebSocket 连接已关闭"); |
||||
|
this.reconnect(); |
||||
|
}; |
||||
|
|
||||
|
this.ws.onerror = error => { |
||||
|
this.stateSub.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<string, WebSocketClient>(); |
||||
|
|
||||
|
// 导出 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; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
export const sharedWsUrl = `ws://${process.env.REACT_APP_WS_URL}`; |
Write
Preview
Loading…
Cancel
Save
Reference in new issue