代客下单重构
This commit is contained in:
@@ -1,360 +0,0 @@
|
||||
import {
|
||||
ref,
|
||||
onMounted,
|
||||
onBeforeUnmount
|
||||
} from 'vue';
|
||||
|
||||
const useWebSocket = (options = {}) => {
|
||||
const {
|
||||
heartbeatInterval = 10000, //心跳是10秒一次
|
||||
reconnectInterval = 3000, //重新连接间隔时间的一个参数
|
||||
maxReconnectAttempts = 3, //最大重连接次数
|
||||
initialReconnectInterval = 3000, // 初始重连间隔
|
||||
initMessage,
|
||||
initMessageRetryCount = 3, // 新增:初始化消息发送重试次数
|
||||
initMessageRetryInterval = 2000, // 新增:初始化消息重试间隔
|
||||
maxReconnectDuration = Infinity,
|
||||
onMessage
|
||||
} = options;
|
||||
const autoReconnect = ref(true); //是否自动重新连接
|
||||
const socketTask = ref(null);
|
||||
const isConnected = ref(false); //表示是否已连接上。
|
||||
const heartbeatTimer = ref(null); //心跳定时器
|
||||
const reconnectTimer = ref(null); //重连定时器
|
||||
const reconnectAttempts = ref(0); //重连的尝试次数
|
||||
const isNetworkConnected = ref(true); //监听当前网络连接状态
|
||||
const isManuallyClosed = ref(false); //是否是被手动关闭的
|
||||
const receivedMessages = ref(); //储从 WebSocket 服务器接收到的消息
|
||||
const initMessageSendAttempts = ref(0); //初始化连接多少次
|
||||
const reconnectStartTime = ref(0); //新增:记录重连开始时间
|
||||
const isPongReceived = ref(false)
|
||||
const allowReconnect = ref(true); // 新增:控制是否允许重连
|
||||
// 关闭现有连接并清理资源
|
||||
const closeExistingConnection = () => {
|
||||
if (socketTask.value) {
|
||||
// 关闭 WebSocket 连接
|
||||
socketTask.value.close({
|
||||
success: () => {
|
||||
console.log('WebSocket 连接已关闭');
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('关闭 WebSocket 连接失败:', err);
|
||||
}
|
||||
});
|
||||
|
||||
// 清除心跳定时器
|
||||
clearInterval(heartbeatTimer.value);
|
||||
heartbeatTimer.value = null;
|
||||
|
||||
// 清除重连定时器
|
||||
clearTimeout(reconnectTimer.value);
|
||||
reconnectTimer.value = null;
|
||||
|
||||
// 标记连接已断开
|
||||
isConnected.value = false;
|
||||
}
|
||||
};
|
||||
const websocketsendMessage = (data) => {
|
||||
uni.$u.debounce(sendMessage(data), 500)
|
||||
}
|
||||
// 连接 WebSocket
|
||||
const connect = (connectMsg) => {
|
||||
if (!isNetworkConnected.value) {
|
||||
uni.showToast({
|
||||
title: '网络未连接...',
|
||||
icon: 'none'
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.pro.switchTab('index/index')
|
||||
}, 1000)
|
||||
console.log('网络未连接,暂不尝试连接 WebSocket');
|
||||
return;
|
||||
}
|
||||
|
||||
// 关闭现有连接并清理资源
|
||||
closeExistingConnection();
|
||||
socketTask.value = uni.connectSocket({
|
||||
url: uni.conf.baseUrlwws,
|
||||
success: (res) => {
|
||||
isConnected.value = true;
|
||||
// 监听初始化成功在开启心跳
|
||||
startHeartbeat();
|
||||
},
|
||||
fail: () => {
|
||||
console.error('WebSocket 连接失败,尝试重连');
|
||||
if (autoReconnect.value && allowReconnect.value) {
|
||||
handleReconnect();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (socketTask.value) {
|
||||
socketTask.value.onOpen(() => {
|
||||
// 初始化 初始购物车
|
||||
sendMessage(connectMsg ? connectMsg : initMessage)
|
||||
});
|
||||
socketTask.value.onMessage((res) => {
|
||||
receivedMessages.value = JSON.parse(res.data)
|
||||
websocketsendMessage({
|
||||
type: 'receipt',
|
||||
msg_id: receivedMessages.value.msg_id
|
||||
})
|
||||
if (onMessage) {
|
||||
onMessage(receivedMessages.value)
|
||||
}
|
||||
// receivedMessages.value.push(list);
|
||||
if (receivedMessages.value == 'ok' || receivedMessages.value.operate_type == 'init') {
|
||||
console.log('初始化正常,心跳响应正常');
|
||||
// 清除重连定时器
|
||||
clearTimeout(reconnectTimer.value);
|
||||
allowReconnect.value = false
|
||||
reconnectTimer.value = null;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
socketTask.value.onClose((res) => {
|
||||
console.log(res, 'WebSocket 连接已关闭,尝试重连');
|
||||
isConnected.value = false;
|
||||
clearInterval(heartbeatTimer.value); // 停止心跳定时器
|
||||
clearTimeout(reconnectTimer.value); // 清除重连定时器
|
||||
if (res.code == '1006' && !allowReconnect.value) {
|
||||
uni.showToast({
|
||||
title: '网络异常,请重新扫码',
|
||||
icon: 'none'
|
||||
});
|
||||
autoReconnect.value = false;
|
||||
setTimeout(() => {
|
||||
uni.pro.switchTab('index/index');
|
||||
}, 1000)
|
||||
return false;
|
||||
}
|
||||
if (autoReconnect.value && !isManuallyClosed.value) {
|
||||
handleReconnect();
|
||||
}
|
||||
});
|
||||
|
||||
socketTask.value.onError((err) => {
|
||||
console.error('WebSocket 连接发生错误:', err);
|
||||
isConnected.value = false;
|
||||
clearInterval(heartbeatTimer.value);
|
||||
if (autoReconnect.value && !isManuallyClosed.value) {
|
||||
handleReconnect();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.error('socketTask 未正确初始化');
|
||||
}
|
||||
};
|
||||
|
||||
// 启动心跳机制
|
||||
const startHeartbeat = () => {
|
||||
if (!isNetworkConnected.value) {
|
||||
console.log('网络未连接,暂停心跳');
|
||||
uni.showToast({
|
||||
title: '网络未连接...',
|
||||
icon: 'none'
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.pro.switchTab('index/index');
|
||||
}, 1000);
|
||||
return;
|
||||
}
|
||||
heartbeatTimer.value = setInterval(() => {
|
||||
if (isConnected.value) {
|
||||
console.log('发送心跳消息');
|
||||
isPongReceived.value = false; // 每次发送心跳消息前重置标记
|
||||
socketTask.value.send({
|
||||
data: JSON.stringify({
|
||||
type: 'ping_interval',
|
||||
set: 'shopping'
|
||||
}),
|
||||
success: () => {
|
||||
console.log('心跳消息发送成功');
|
||||
const pongTimer = setTimeout(() => {
|
||||
if (!isPongReceived.value) {
|
||||
console.error('心跳超时,未收到响应,尝试重连');
|
||||
clearInterval(heartbeatTimer.value);
|
||||
if (autoReconnect.value && reconnectAttempts.value <
|
||||
maxReconnectAttempts && allowReconnect.value) {
|
||||
handleReconnect();
|
||||
} else {
|
||||
console.error('重连次数达到上限,停止重连和心跳');
|
||||
clearInterval(heartbeatTimer.value);
|
||||
autoReconnect.value = false;
|
||||
uni.pro.switchTab('index/index');
|
||||
}
|
||||
}
|
||||
}, heartbeatInterval * 1.2);
|
||||
|
||||
const handlePong = (res) => {
|
||||
try {
|
||||
let data = JSON.parse(res.data);
|
||||
if (data.operate_type == "init" || (data.msg === 'ok' &&
|
||||
data.msg_id ==
|
||||
'ping_interval')) {
|
||||
isPongReceived.value = true;
|
||||
console.log('收到心跳响应,清除超时定时器');
|
||||
clearTimeout(pongTimer);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('解析心跳响应数据时出错:', error);
|
||||
}
|
||||
};
|
||||
socketTask.value.onMessage(handlePong);
|
||||
},
|
||||
fail: () => {
|
||||
console.error('心跳消息发送失败,尝试重连');
|
||||
clearInterval(heartbeatTimer.value);
|
||||
if (autoReconnect.value && reconnectAttempts.value <
|
||||
maxReconnectAttempts && allowReconnect.value) {
|
||||
handleReconnect();
|
||||
} else {
|
||||
console.error('重连次数达到上限,停止重连和心跳');
|
||||
clearInterval(heartbeatTimer.value);
|
||||
autoReconnect.value = false;
|
||||
uni.pro.switchTab('index/index');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}, heartbeatInterval);
|
||||
};
|
||||
|
||||
|
||||
// 手动关闭连接
|
||||
const closeSocket = () => {
|
||||
isManuallyClosed.value = true;
|
||||
closeExistingConnection();
|
||||
};
|
||||
|
||||
// 发送消息
|
||||
const sendMessage = async (data) => {
|
||||
if (isConnected.value) {
|
||||
await socketTask.value.send({
|
||||
data: JSON.stringify(data),
|
||||
success: () => {
|
||||
// console.log('消息发送成功');
|
||||
},
|
||||
fail: () => {
|
||||
// console.error('消息发送失败');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.error('WebSocket 未连接,无法发送消息');
|
||||
}
|
||||
};
|
||||
|
||||
// 处理重连逻辑
|
||||
const handleReconnect = () => {
|
||||
if (!isNetworkConnected.value) {
|
||||
console.log('网络未连接,暂停重连');
|
||||
return;
|
||||
}
|
||||
if (isManuallyClosed.value) {
|
||||
console.log('手动关闭连接,不进行重连');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!allowReconnect.value) {
|
||||
console.log('重连功能已关闭,不进行重连');
|
||||
return;
|
||||
}
|
||||
|
||||
if (reconnectAttempts.value < maxReconnectAttempts) {
|
||||
reconnectAttempts.value++;
|
||||
const reconnectInterval = initialReconnectInterval * Math.pow(2, reconnectAttempts.value - 1);
|
||||
const randomizedInterval = reconnectInterval + Math.floor(Math.random() * 1000);
|
||||
uni.showLoading({
|
||||
title: `正在努力连接..`,
|
||||
mask: true
|
||||
})
|
||||
console.log(`尝试第 ${reconnectAttempts.value} 次重连,重连间隔: ${randomizedInterval}ms...`);
|
||||
|
||||
reconnectTimer.value = setTimeout(() => {
|
||||
connect();
|
||||
}, randomizedInterval);
|
||||
} else {
|
||||
console.error('重连次数达到上限,停止重连');
|
||||
uni.showToast({
|
||||
title: '重连次数达到上限,停止重连',
|
||||
icon: 'none'
|
||||
});
|
||||
clearInterval(heartbeatTimer.value);
|
||||
autoReconnect.value = false;
|
||||
setTimeout(() => {
|
||||
uni.hideLoading();
|
||||
uni.pro.switchTab('index/index');
|
||||
}, 1000)
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// / 网络状态监听
|
||||
const initNetworkListener = () => {
|
||||
uni.getSystemInfo({
|
||||
success: (res) => {
|
||||
if (res.platform !== 'devtools') {
|
||||
uni.onNetworkStatusChange((statusRes) => {
|
||||
isNetworkConnected.value = statusRes.isConnected;
|
||||
if (statusRes.isConnected && !isManuallyClosed.value) {
|
||||
console.log('网络已连接,尝试重新连接 WebSocket');
|
||||
if (!isConnected.value && autoReconnect.value) {
|
||||
connect();
|
||||
}
|
||||
} else if (!statusRes.isConnected) {
|
||||
console.log('网络已断开,暂停 WebSocket 操作');
|
||||
clearInterval(heartbeatTimer.value);
|
||||
clearTimeout(reconnectTimer.value);
|
||||
if (socketTask.value) {
|
||||
socketTask.value.close();
|
||||
isConnected.value = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('获取系统信息失败:', err);
|
||||
}
|
||||
});
|
||||
|
||||
uni.getNetworkType({
|
||||
success: (res) => {
|
||||
isNetworkConnected.value = res.networkType !== 'none';
|
||||
if (!isNetworkConnected.value) {
|
||||
console.log('初始网络未连接,暂不尝试连接 WebSocket');
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 页面显示,尝试连接 WebSocket
|
||||
const onShowconnect = () => {
|
||||
if (autoReconnect.value) {
|
||||
uni.showLoading({
|
||||
title: `尝试再次连接`,
|
||||
mask: true
|
||||
})
|
||||
connect();
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
closeSocket();
|
||||
});
|
||||
|
||||
return {
|
||||
isConnected,
|
||||
sendMessage,
|
||||
closeSocket,
|
||||
receivedMessages,
|
||||
closeExistingConnection,
|
||||
connect,
|
||||
onShowconnect,
|
||||
initNetworkListener,
|
||||
connect
|
||||
};
|
||||
};
|
||||
|
||||
export default useWebSocket;
|
||||
244
common/js/carts-websocket.ts
Normal file
244
common/js/carts-websocket.ts
Normal file
@@ -0,0 +1,244 @@
|
||||
import { error } from "../../uni_modules/uview-plus";
|
||||
|
||||
// import qs from "qs";
|
||||
const qs = {
|
||||
stringify: (obj) => {
|
||||
const str= Object.keys(obj).reduce((prve, key) => {
|
||||
return prve + `${key}=${obj[key]}&`
|
||||
}, '?')
|
||||
|
||||
return str.substring(0,str.length-1)
|
||||
}
|
||||
}
|
||||
function ElMessage() {
|
||||
|
||||
}
|
||||
function ElMessageBox() {
|
||||
|
||||
}
|
||||
|
||||
const user = {
|
||||
userInfo: {
|
||||
shopId: ''
|
||||
}
|
||||
}
|
||||
|
||||
export interface ApifoxModel {
|
||||
account : string;
|
||||
operate_type : string;
|
||||
shop_id : string;
|
||||
table_code ?: string;
|
||||
type : string;
|
||||
[property : string] : string | number | undefined; // 限制额外属性类型
|
||||
}
|
||||
|
||||
export type msgType = 'add' | 'reduce' | 'remove' | 'edit' | 'init' | 'cleanup' | 'del' | 'rottable' | 'batch' | 'disconnect' | 'clearOrder';
|
||||
|
||||
class WebSocketManager {
|
||||
private client : WebSocket | null = null;
|
||||
private connected : boolean = false;
|
||||
private shop_id = user.userInfo.shopId ? String(user.userInfo.shopId) : '';
|
||||
private autoConnect : boolean = true;
|
||||
private initParams : ApifoxModel = {
|
||||
type: 'shopping',
|
||||
account: this.shop_id || '', // 提供默认值
|
||||
operate_type: 'init',
|
||||
table_code: '',
|
||||
shop_id: this.shop_id || '', // 提供默认值
|
||||
};
|
||||
private onMessage : (message : any) => void = () => { };
|
||||
private messageHandlers : Map<string, ((message : string) => void)[]> = new Map();
|
||||
private type : string = 'shopping';
|
||||
private reconnectAttempts = 0;
|
||||
private maxReconnectAttempts = Infinity; // 自定义最大重试次数
|
||||
private reconnectDelay = 5000; // 重试延迟(单位:毫秒)
|
||||
private timer : any | null = null;
|
||||
private reconnectTimer : any | null = null
|
||||
|
||||
connectSocketSuccsss(){
|
||||
if(!this.client){
|
||||
return
|
||||
}
|
||||
this.client.onOpen(() => {
|
||||
this.connected = true;
|
||||
console.log("WebSocket 连接已建立");
|
||||
console.log(this.initParams);
|
||||
this.sendMessage(this.initParams);
|
||||
this.clearTimer();
|
||||
this.timer = setInterval(() => {
|
||||
this.sendMessage({ type: "ping_interval", set: 'shopping' }, false);
|
||||
}, 1000 * 10);
|
||||
})
|
||||
|
||||
this.client.onClose(() => {
|
||||
this.clearTimer();
|
||||
this.connected = false;
|
||||
console.log("WebSocket 连接已断开");
|
||||
if (this.autoConnect) {
|
||||
this.reconnect(); // 自动重连
|
||||
}
|
||||
})
|
||||
|
||||
this.client.onError((error : Event) => {
|
||||
this.clearTimer();
|
||||
console.error("WebSocket 发生错误:", error);
|
||||
this.reconnect(); // 自动重连
|
||||
})
|
||||
|
||||
this.client.onMessage((event : MessageEvent) => {
|
||||
try {
|
||||
const message = JSON.parse(event.data);
|
||||
if (message && message.msg_id) {
|
||||
this.onMessageHandler({ msg_id: message.msg_id });
|
||||
}
|
||||
this.onMessage(message);
|
||||
} catch (e) {
|
||||
console.error("消息解析失败:", e);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 初始化 WebSocket 客户端
|
||||
setupWebSocket() {
|
||||
// #ifdef H5
|
||||
const endpoint = "http://192.168.1.31:2348/"
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
const endpoint = "ws://192.168.1.31:2348/"
|
||||
// #endif
|
||||
|
||||
if (!endpoint) {
|
||||
console.warn("WebSocket 已被禁用,请在配置文件中配置 VITE_APP_WS_ENDPOINT");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.client && this.connected) {
|
||||
console.log("客户端已存在并且连接正常");
|
||||
return this.client;
|
||||
}
|
||||
|
||||
const url = qs.stringify(this.initParams);
|
||||
// this.client = new WebSocket(endpoint + '?' + url);
|
||||
|
||||
|
||||
|
||||
this.client = uni.connectSocket({
|
||||
url: endpoint + url,
|
||||
success: (res) => {
|
||||
console.log('连接成功');
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error(error(err))
|
||||
console.error('WebSocket 连接失败,尝试重连');
|
||||
}
|
||||
});
|
||||
this.connectSocketSuccsss()
|
||||
console.log('this.client');
|
||||
console.log(this.client);
|
||||
}
|
||||
|
||||
// 消息回执
|
||||
public onMessageHandler(data : any) {
|
||||
if (this.client && this.connected) {
|
||||
this.client.send({data:JSON.stringify({ ...data, type: 'receipt' })});
|
||||
}
|
||||
}
|
||||
|
||||
// 订阅主题
|
||||
public subscribeToTopic(initParams : ApifoxModel, onMessage : (message : any) => void) {
|
||||
console.log(`正在订阅主题: `, initParams);
|
||||
this.initParams = { ...this.initParams, ...initParams };
|
||||
this.disconnect();
|
||||
this.setupWebSocket();
|
||||
this.onMessage = onMessage;
|
||||
this.autoConnect = true
|
||||
}
|
||||
|
||||
public sendMessage(message : any, isSendInitParams : boolean = true) {
|
||||
if (!this.client || !this.connected) {
|
||||
// ElMessage.error('发送失败,已重新连接,请重新操作');
|
||||
this.reconnect();
|
||||
return;
|
||||
}
|
||||
if (message.operate_type == 'disconnect') {
|
||||
this.disconnect();
|
||||
return
|
||||
}
|
||||
const msg = JSON.stringify(isSendInitParams ? { ...this.initParams, ...message } : message);
|
||||
|
||||
try {
|
||||
this.client?.send({data:msg});
|
||||
} catch (error) {
|
||||
console.error("发送消息失败:", error);
|
||||
// ElMessage.error('发送失败,已重新连接,请重新操作');
|
||||
this.reconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public canSendMessage() {
|
||||
return this.client && this.connected;
|
||||
}
|
||||
|
||||
// 断开 WebSocket 连接
|
||||
public disconnect() {
|
||||
if (this.client && this.connected) {
|
||||
console.log("断开 WebSocket 连接");
|
||||
this.clearTimer();
|
||||
try {
|
||||
if(this.client){
|
||||
this.client.closeSocket();
|
||||
}
|
||||
} catch (error) {
|
||||
//TODO handle the exception
|
||||
console.error('关闭WebSocket连接失败')
|
||||
}
|
||||
|
||||
this.client = null;
|
||||
this.connected = false;
|
||||
this.autoConnect = false;
|
||||
}
|
||||
}
|
||||
// 自动重连机制
|
||||
private reconnect() {
|
||||
if (!this.autoConnect) {
|
||||
return;
|
||||
}
|
||||
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
||||
this.reconnectAttempts++;
|
||||
console.log(`尝试第 ${this.reconnectAttempts} 次重连...`);
|
||||
this.reconnectTimer = setTimeout(() => {
|
||||
this.setupWebSocket();
|
||||
}, this.reconnectDelay);
|
||||
} else {
|
||||
clearTimeout(this.reconnectTimer);
|
||||
console.error("达到最大重连次数,停止重连");
|
||||
ElMessageBox.confirm('达到最大重连次数' + this.maxReconnectAttempts + '次,已停止重连,是否立即重连?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
|
||||
callback: (action : string) => {
|
||||
console.log(action);
|
||||
if (action == 'confirm') {
|
||||
this.setupWebSocket();
|
||||
this.reconnectAttempts = 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 清除定时器
|
||||
private clearTimer() {
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
if (this.reconnectTimer) {
|
||||
clearInterval(this.reconnectTimer);
|
||||
this.reconnectTimer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new WebSocketManager();
|
||||
Reference in New Issue
Block a user