修改websocket为全局只有一个

This commit is contained in:
YeMingfei666 2025-05-19 09:07:35 +08:00
parent ac28ee855f
commit c7e4e5e26b
8 changed files with 3780 additions and 269 deletions

View File

@ -0,0 +1,364 @@
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;
}
// 关闭现有连接并清理资源
try{
closeExistingConnection();
}catch(err){
}
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,allowReconnect
};
};
export default useWebSocket;

View File

@ -31,8 +31,7 @@ const useWebSocket = (options = {}) => {
const allowReconnect = ref(true); // 新增:控制是否允许重连 const allowReconnect = ref(true); // 新增:控制是否允许重连
// 关闭现有连接并清理资源 // 关闭现有连接并清理资源
const closeExistingConnection = () => { const closeExistingConnection = () => {
if (socketTask.value) { try {
// 关闭 WebSocket 连接
socketTask.value.close({ socketTask.value.close({
success: () => { success: () => {
console.log('WebSocket 连接已关闭'); console.log('WebSocket 连接已关闭');
@ -52,13 +51,32 @@ const useWebSocket = (options = {}) => {
// 标记连接已断开 // 标记连接已断开
isConnected.value = false; isConnected.value = false;
} catch (error) {
console.error(error)
//TODO handle the exception
} }
}; };
const websocketsendMessage = (data) => { const websocketsendMessage = (data) => {
uni.$u.debounce(sendMessage(data), 500) uni.$u.debounce(sendMessage(data), 500)
} }
const safeConnect=(connectMsg)=>{
return new Promise((resolve,reject)=>{
try {
sendMessage(connectMsg ? connectMsg : initMessage)
resolve(true)
} catch (error) {
reject(false)
//TODO handle the exception
}
})
}
// 连接 WebSocket // 连接 WebSocket
const connect = (connectMsg) => { const connect =async (connectMsg) => {
if (!isNetworkConnected.value) { if (!isNetworkConnected.value) {
uni.showToast({ uni.showToast({
title: '网络未连接...', title: '网络未连接...',
@ -72,13 +90,14 @@ const useWebSocket = (options = {}) => {
} }
// 关闭现有连接并清理资源 // 关闭现有连接并清理资源
try{ const sendSuccess= await safeConnect(connectMsg)
closeExistingConnection(); if(sendSuccess){
}catch(err){ return
}else{
closeExistingConnection()
} }
socketTask.value = uni.connectSocket({ socketTask.value = uni.connectSocket({
url: uni.conf.baseUrlwws, url: uni.conf.baseUrlwws + '?' + Date.now(),
success: (res) => { success: (res) => {
isConnected.value = true; isConnected.value = true;
// 监听初始化成功在开启心跳 // 监听初始化成功在开启心跳
@ -228,6 +247,7 @@ const useWebSocket = (options = {}) => {
// 手动关闭连接 // 手动关闭连接
const closeSocket = () => { const closeSocket = () => {
console.log('手动关闭连接');
isManuallyClosed.value = true; isManuallyClosed.value = true;
closeExistingConnection(); closeExistingConnection();
}; };
@ -345,7 +365,7 @@ const useWebSocket = (options = {}) => {
} }
onBeforeUnmount(() => { onBeforeUnmount(() => {
closeSocket(); // closeSocket();
}); });
return { return {
@ -357,7 +377,8 @@ const useWebSocket = (options = {}) => {
connect, connect,
onShowconnect, onShowconnect,
initNetworkListener, initNetworkListener,
connect,allowReconnect connect,
allowReconnect
}; };
}; };

View File

@ -63,7 +63,8 @@
<view class="u-m-r-30 u-flex u-flex-y-center"> <view class="u-m-r-30 u-flex u-flex-y-center">
<template v-if="ordershopUserInfo.registerType=='before'"> <template v-if="ordershopUserInfo.registerType=='before'">
<up-button shape="circle" @click="toJiacai" plain :custom-style="customStyle"> <up-button shape="circle" v-if="!listinfo.id" @click="toJiacai" plain
:custom-style="customStyle">
<view class="u-flex u-flex-y-center"> <view class="u-flex u-flex-y-center">
<image style="width: 40rpx;height: 40rpx;" src="@/static/icon/add-goods.png" <image style="width: 40rpx;height: 40rpx;" src="@/static/icon/add-goods.png"
mode=""></image> mode=""></image>
@ -106,6 +107,9 @@
import { import {
back back
} from '@/utils/uniapp.js' } from '@/utils/uniapp.js'
import {
onLoad
} from '@dcloudio/uni-app'
function onback() { function onback() {
closeSocket() closeSocket()
@ -148,12 +152,11 @@
import { import {
useCartsStore useCartsStore
} from '@/stores/carts.js'; } from '@/stores/carts.js';
import useWebSocket from '@/common/js/carts-websocket.js'; import {
useWebSocket
} from '@/stores/carts-websocket.js';
const cartStore = useCartsStore() const cartStore = useCartsStore()
console.log(cartStore.goodsIsloading);
console.log(cartStore.goodsMap);
async function onMessage(Message) { async function onMessage(Message) {
console.log('onMessage');
cartStore.onMessage(Message, cartsSocket) cartStore.onMessage(Message, cartsSocket)
listinfo.totalPrices = historyTotalPrices * 1 + cartStore.totalPrice * 1 listinfo.totalPrices = historyTotalPrices * 1 + cartStore.totalPrice * 1
if (Message.operate_type == "init") { if (Message.operate_type == "init") {
@ -176,7 +179,6 @@
}, 1000); }, 1000);
return; return;
} }
console.log(res);
} }
} }
if (Message.operate_type == "del") { if (Message.operate_type == "del") {
@ -192,34 +194,21 @@
.operate_type == 'product_update') { .operate_type == 'product_update') {
youhuiReset() youhuiReset()
} }
return Message
} }
let cartsSocket = null let cartsSocket = null
const socketInitOptions = { cartStore.goodsInit()
initMessage: { const socketInitPar = {
type: 'shopping', type: 'shopping',
operate_type: 'init', operate_type: 'init',
table_code: uni.cache.get('tableCode'), table_code: uni.cache.get('tableCode'),
shop_id: uni.cache.get('shopId'), shop_id: uni.cache.get('shopId'),
},
onMessage
}
watch(() => cartStore.goodsIsloading, (newValue) => {
console.log('cartStore.goodsIsloading', cartStore.goodsIsloading);
if (!newValue) {
cartsSocket = useWebSocket(socketInitOptions);
cartsSocket.allowReconnect = true;
cartsSocket.connect();
}
})
if (cartStore.goodsIsloading) {
cartStore.goodsInit()
} else {
cartsSocket = useWebSocket(socketInitOptions);
cartsSocket.allowReconnect = true;
cartsSocket.connect();
} }
function socketInit() {
cartsSocket = useWebSocket();
cartsSocket.connect(socketInitPar, onMessage);
}
const noPayStatus = { const noPayStatus = {
cancelled: "订单已取消", cancelled: "订单已取消",
done: "订单已关闭", done: "订单已关闭",
@ -233,7 +222,11 @@
listinfo.pointsDiscountAmount = 0 listinfo.pointsDiscountAmount = 0
listinfo.Productroll = 0 listinfo.Productroll = 0
uniqueIds.value = [] uniqueIds.value = []
orderInfoAfterRef.value.IntegralInputclose(); try {
orderInfoAfterRef.value?.IntegralInputclose();
} catch {
}
} }
@ -308,18 +301,30 @@
let historyTotalPrices = 0; let historyTotalPrices = 0;
// * // *
const orderorderInfo = async () => { const orderorderInfo = async () => {
console.log('listinfo.id', listinfo.id);
let res = listinfo.id ? await APIgetOrderById({ let res = listinfo.id ? await APIgetOrderById({
orderId: listinfo.id orderId: listinfo.id
}) : await APIhistoryOrder({ }) : await APIhistoryOrder({
tableCode: options.tableCode tableCode: options.tableCode
}) })
if (res && res.tableCode) {
socketInitPar.table_code = res.tableCode
let tableRes = await getTableInfo({
tableCode: options.tableCode
})
console.log(tableRes);
listinfo.tableName = tableRes.name;
} else {
if (options.tableCode) { if (options.tableCode) {
socketInitPar.table_code = options.tableCode
let tableRes = await getTableInfo({ let tableRes = await getTableInfo({
tableCode: options.tableCode tableCode: options.tableCode
}) })
console.log(tableRes); console.log(tableRes);
listinfo.tableName = tableRes.name; listinfo.tableName = tableRes.name;
} }
}
socketInit()
console.log(res); console.log(res);
getOrderInfoAfterCalcInit(res) getOrderInfoAfterCalcInit(res)
} }
@ -393,12 +398,9 @@
listinfo.originAmount = Math.round(sum * 100) / 100; listinfo.originAmount = Math.round(sum * 100) / 100;
// packFeess totalPrices Seatcharge Productroll coupondiscountAmount listinfo.pointsDiscountAmount // packFeess totalPrices Seatcharge Productroll coupondiscountAmount listinfo.pointsDiscountAmount
console.log('listinfo');
console.log(listinfo);
let sums = nowCartPackFee + historyOrderPackFee + listinfo.totalPrices + seatFee - (listinfo let sums = nowCartPackFee + historyOrderPackFee + listinfo.totalPrices + seatFee - (listinfo
.Productroll || 0) - (listinfo.coupondiscountAmount || 0) - (listinfo .Productroll || 0) - (listinfo.coupondiscountAmount || 0) - (listinfo
.pointsDiscountAmount || 0); .pointsDiscountAmount || 0);
console.log('listinfo', listinfo);
listinfo.totalCost = Math.round(sums * 100) / 100; listinfo.totalCost = Math.round(sums * 100) / 100;
// //
@ -571,15 +573,52 @@
} }
function isPayBefor() {
return ordershopUserInfo.value.registerType == 'before' ? true : false
}
function pay_lock() {
cartsSocket.sendMessage({
...socketInitPar,
operate_type: 'pay_lock'
})
}
function pay_unlock() {
clearInterval(payStatusTimer)
cartsSocket.sendMessage({
...socketInitPar,
operate_type: 'pay_unlock'
})
}
async function search_pay_lock() {
cartsSocket.sendMessage({
...socketInitPar,
operate_type: 'search_pay_lock'
})
return new Promise((relove, reject) => {
cartsSocket.socketTask.onMessage(res => {
relove(JSON.parse(res.data))
})
})
}
let payStatusTimer = null
let orderIsLock = true;
const istoricalorders = async () => { const istoricalorders = async () => {
//
if (isPayBefor()) {
console.log('isPayBefor');
// //
if (!cartStore.isEmpty) { if (!cartStore.isEmpty) {
await createOrder() const res = await createOrder()
goToPay() goToPay()
} }
} else {
//
// //
let APIhistoryOrderres = await APIhistoryOrder({ let APIhistoryOrderres = await APIhistoryOrder({
tableCode: listinfo.tableCode tableCode: listinfo.tableCode
@ -614,8 +653,12 @@
} }
} }
}
// * // *
const goToPay = async () => { const goToPay = async (isCreateOrder = false) => {
// //
if (paymentmethod.payType == 'accountPay') { if (paymentmethod.payType == 'accountPay') {
if (orderVIP.value.isVip == 0) { if (orderVIP.value.isVip == 0) {
@ -659,6 +702,23 @@
} }
return false return false
} }
const canPayRes = await search_pay_lock()
console.log('canPayRes', canPayRes);
const canPay = canPayRes.status == 1 ? true : false;
console.log('canPay', canPay);
if (!canPay) {
uni.showToast({
title: '有人正在付款中!',
icon: 'none'
})
return
}
pay_lock()
payStatusTimer = setInterval(() => {
pay_lock()
}, 1000)
if (orderVIP.value.freeDineConfig.enable && isBwc.value) { if (orderVIP.value.freeDineConfig.enable && isBwc.value) {
await storeMemberpay.actionspayltPayVip({ await storeMemberpay.actionspayltPayVip({
shopId: orderVIP.value.shopId, shopId: orderVIP.value.shopId,
@ -670,6 +730,7 @@
buyerRemark: '', buyerRemark: '',
seatNum: is_type.value == 0 ? listinfo.seatNum : 0, // seatNum: is_type.value == 0 ? listinfo.seatNum : 0, //
}) })
orderorderInfo() orderorderInfo()
return false; return false;
} else { } else {
@ -700,9 +761,11 @@
returnUrl: '' returnUrl: ''
}) })
} catch (error) { } catch (error) {
pay_unlock()
//TODO handle the exception //TODO handle the exception
} }
} }
pay_unlock()
orderorderInfo() orderorderInfo()
} }
@ -712,7 +775,7 @@
shopUserId: orderVIP.value.id, shopUserId: orderVIP.value.id,
orderAmount: listinfo.totalCost, orderAmount: listinfo.totalCost,
}) })
orderInfoAfterRef.value.getCalcUsablePoints(res) orderInfoAfterRef.value?.getCalcUsablePoints(res)
} }
// //
const clickPointsamount = (Pointsamount) => { const clickPointsamount = (Pointsamount) => {
@ -760,13 +823,16 @@
onBeforeUnmount(() => { onBeforeUnmount(() => {
uni.$off('returnData', handleReturnData); uni.$off('returnData', handleReturnData);
clearTimeout(backtimer) clearTimeout(backtimer)
clearInterval(payStatusTimer)
closeSocket() closeSocket()
}); });
onHide(() => { onHide(() => {
closeSocket() closeSocket()
clearInterval(payStatusTimer)
}) })
function closeSocket() { function closeSocket() {
// cartsSocket.closeExistingConnection() // cartsSocket.closeExistingConnection()
} }
@ -777,10 +843,9 @@
try { try {
// //
if (orderVIP.value) { if (orderVIP.value) {
console.log(uni.cache.get('orderVIP'))
orderVIP.value = uni.cache.get('orderVIP') orderVIP.value = uni.cache.get('orderVIP')
paymentMethodref.value.orderVIPfun(uni.cache.get('orderVIP')) paymentMethodref.value.orderVIPfun(uni.cache.get('orderVIP'))
if (listinfo.id) { if (listinfo.id && ordershopUserInfo.value.registerType != 'before') {
orderorderInfo() orderorderInfo()
} }
} }
@ -789,28 +854,7 @@
} }
}); });
let options = {} let options = {}
//
onMounted(async () => {
//
const pages = getCurrentPages();
//
const currentPage = pages[pages.length - 1];
//
options = currentPage.options;
listinfo.id = options.orderId;
if (options.shopId) {
// shopId
uni.cache.set('shopId', options.shopId, 30)
uni.$on('returnData', handleReturnData);
}
// *
await storeuser.actionsproductqueryProduct()
await nextTick()
orderVIP.value = uni.cache.get('orderVIP')
//
orderorderInfo()
})
// //
function toJiacai() { function toJiacai() {
back() back()
@ -845,12 +889,41 @@
'cancelled': '已取消' 'cancelled': '已取消'
} }
const navTitle = computed(() => { const navTitle = computed(() => {
if (!listinfo.id && !cartStore.isEmpty) {
return '待确认'
}
if (cartStore.isEmpty && listinfo.id) { if (cartStore.isEmpty && listinfo.id) {
return payStatus[listinfo.status] || '待确认' return payStatus[listinfo.status] || '待确认'
} else { } else {
return '待确认' return '待确认'
} }
}) })
async function init(opt) {
Object.assign(options, opt)
listinfo.id = options.orderId;
console.log('init', listinfo);
if (options.shopId) {
// shopId
uni.cache.set('shopId', options.shopId, 30)
uni.$on('returnData', handleReturnData);
}
// *
await storeuser.actionsproductqueryProduct()
await nextTick()
orderVIP.value = uni.cache.get('orderVIP')
if (options.tableCode) {
socketInitPar.table_code = options.tableCode
socketInit()
} else {
orderorderInfo()
}
}
onLoad((opt) => {
init(opt)
})
</script> </script>
<style lang="scss"> <style lang="scss">
@ -1270,6 +1343,7 @@
bottom: 0; bottom: 0;
left: 0; left: 0;
width: 100%; width: 100%;
z-index: 999;
// height: 80rpx; // height: 80rpx;
.flex-between { .flex-between {

View File

@ -218,6 +218,14 @@
} }
const orderinfo = (e) => { const orderinfo = (e) => {
if(e.status=='unpaid'){
uni.pro.navigateTo('order/confirm-order', {
orderId: e.id,
shopId: e.shopId,
})
return
}
uni.pro.navigateTo('order/detail', { uni.pro.navigateTo('order/detail', {
orderId: e.id, orderId: e.id,
shopId: e.shopId shopId: e.shopId

File diff suppressed because it is too large Load Diff

View File

@ -240,7 +240,7 @@
</view> </view>
</view> </view>
<view class="Controls" v-else> <view class="Controls" v-else>
<view class="btn" v-if="item1.cartNumber != '0'"> <view class="btn" v-if="item1.cartNumber*1 != '0'">
<up-icon name="minus-circle-fill" color="#E9AB7A" size="25"></up-icon> <up-icon name="minus-circle-fill" color="#E9AB7A" size="25"></up-icon>
<view class="btnClick" <view class="btnClick"
@tap.stop="$u.throttle(() => singleclick(item1, '-'), 500)"> @tap.stop="$u.throttle(() => singleclick(item1, '-'), 500)">
@ -418,7 +418,7 @@
<image class="img" src="@/static/history.png" mode=""></image> <image class="img" src="@/static/history.png" mode=""></image>
<text>已下单菜品</text> <text>已下单菜品</text>
</view> </view>
<Loading :isLoading="isLoading" /> <Loading :isLoading="!useSocket.isConnected" />
</view> </view>
</template> </template>
@ -472,7 +472,9 @@
// websocket // websocket
// import useWebSocket from '@/common/js/websocket.js'; // import useWebSocket from '@/common/js/websocket.js';
import useWebSocket from '@/common/js/carts-websocket.js'; import {
useWebSocket
} from '@/stores/carts-websocket.js';
// pinia // pinia
import { import {
@ -1135,16 +1137,8 @@
shop_id: uni.cache.get('shopId') shop_id: uni.cache.get('shopId')
} }
} }
const { const useSocket = useWebSocket();
isConnected, useSocket.connect(options.initMessage, onMessage)
sendMessage,
closeSocket: manualClose,
receivedMessages,
closeExistingConnection,
onShowconnect,
initNetworkListener
} = useWebSocket(options);
// //
const showCart = ref(false) const showCart = ref(false)
@ -1154,6 +1148,7 @@
// //
const updateProductQuantities = () => { const updateProductQuantities = () => {
console.log('updateProductQuantities');
// cartNumber 0 // cartNumber 0
shopProductList.hots.forEach((i) => { shopProductList.hots.forEach((i) => {
i.cartNumber = 0 i.cartNumber = 0
@ -1171,7 +1166,7 @@
group.productList.forEach((product) => { group.productList.forEach((product) => {
if (product.id == cartItem.product_id && product.skuId == cartItem if (product.id == cartItem.product_id && product.skuId == cartItem
.sku_id) { .sku_id) {
product.cartNumber = cartItem.number product.cartNumber = cartItem.number || 0
product.cartListId = cartItem.id product.cartListId = cartItem.id
} }
}); });
@ -1185,7 +1180,7 @@
if (group.id == cartItem.product_id) { if (group.id == cartItem.product_id) {
// //
group.cartListId = cartItem.id group.cartListId = cartItem.id
group.cartNumber = cartItem.number group.cartNumber = cartItem.number || 0
} }
}); });
}); });
@ -1193,18 +1188,42 @@
//websocket //websocket
const websocketsendMessage = (data) => { const websocketsendMessage = (data) => {
uni.$u.debounce(sendMessage(data), 500) console.log(data);
uni.$u.debounce(useSocket.sendMessage(data), 500)
} }
// msg_id // msg_id
const processedMessageIds = new Set(); const processedMessageIds = new Set();
//
watchEffect(async () => { //
if (isDataLoaded.value && receivedMessages.value) { function setGoodsInitSel(cartsArr) {
const Message = receivedMessages.value console.log('setGoodsInitSel');
const arr = (cartsArr && cartsArr.length) ? cartsArr : cartStore.carts
if (arr.length <= 0) {
return
}
shopProductList.hots.map(v => {
const item = arr.find(cart => cart.product_id == v.id)
if (item) {
v.cartNumber = `${item.number}`;
}
})
shopProductList.productInfo.map(info => {
info.productList.map(v => {
const item = arr.find(cart => cart.product_id == v.id)
if (item) {
v.cartNumber = `${item.number}`;
console.log(v.cartNumber);
}
})
})
}
//
async function onMessage(Message) {
if (Message) { if (Message) {
console.log(Message.data); console.log('product index 收到消息',Message);
// //
if (Message.type == "ping_interval" || Message.msg_id == "ping_interval") { if (Message.type == "ping_interval" || Message.msg_id == "ping_interval") {
isLoading.value = false; isLoading.value = false;
@ -1221,6 +1240,8 @@
cartStore.carts = Message.data cartStore.carts = Message.data
uni.hideLoading(); uni.hideLoading();
isLoading.value = false; isLoading.value = false;
//
// setGoodsInitSel(Message.data)
} }
// //
@ -1301,7 +1322,7 @@
} }
} }
})
// shopProductList.hots // shopProductList.hots
const matchedProducts = computed(() => { const matchedProducts = computed(() => {
@ -1322,6 +1343,7 @@
for (const group of Specialstop) { for (const group of Specialstop) {
for (const product of group.productList) { for (const product of group.productList) {
if (product.id == cartItem.product_id) { if (product.id == cartItem.product_id) {
console.log(cartItem);
return { return {
...product, ...product,
cartListinfo: cartItem, cartListinfo: cartItem,
@ -1490,16 +1512,15 @@
}); });
// ifcartNumber // ifcartNumber
const ifcartNumber = computed(() => { function ifcartNumber(item) {
return (item) => {
// item cartNumber 0 // item cartNumber 0
if (!item || typeof item.cartNumber !== 'string') { if (!item || item.cartNumber * 1 == 0) {
return 0; return '';
} }
let numValue = parseFloat(item.cartNumber); let numValue = parseFloat(item.cartNumber);
if (isNaN(numValue)) { if (isNaN(numValue)) {
// NaN cartNumber 0 // NaN cartNumber 0
return 0; return '';
} }
// type string single- sku- package- weight- coupon- // type string single- sku- package- weight- coupon-
if (item.type === 'weight') { if (item.type === 'weight') {
@ -1511,8 +1532,30 @@
} }
// //
return item.cartNumber; return item.cartNumber;
}; }
}) // const ifcartNumber = computed(() => {
// return (item) => {
// // item cartNumber 0
// if (!item || typeof item.cartNumber !== 'string') {
// return 0;
// }
// let numValue = parseFloat(item.cartNumber);
// if (isNaN(numValue)) {
// // NaN cartNumber 0
// return 0;
// }
// // type string single- sku- package- weight- coupon-
// if (item.type === 'weight') {
// //
// return parseFloat(numValue.toFixed(2));
// } else {
// //
// return Math.round(numValue);
// }
// //
// return item.cartNumber;
// };
// })
// // // //
const cartListFilter = computed(() => { const cartListFilter = computed(() => {
@ -1609,6 +1652,7 @@
isDataLoaded.value = true; isDataLoaded.value = true;
// //
Historicalorders() Historicalorders()
updateProductQuantities()
} else { } else {
uni.showToast({ uni.showToast({
title: '暂无列表数据,请重新扫码', title: '暂无列表数据,请重新扫码',
@ -1628,27 +1672,28 @@
await proxy.$onLaunched; await proxy.$onLaunched;
}) })
onShow(async() => { onShow(async () => {
// //
onShowconnect() useSocket.setOnMessage(onMessage)
useSocket.onShowconnect()
let res = await APIhistoryOrder({ let res = await APIhistoryOrder({
tableCode: uni.cache.get('tableCode'), tableCode: uni.cache.get('tableCode'),
}) })
if(res){ if (res) {
orderinfo.value = { orderinfo.value = {
id: res.id, id: res.id,
detailMap: res.detailMap, detailMap: res.detailMap,
placeNum: res.placeNum placeNum: res.placeNum
} }
}else{ } else {
orderinfo.value = { orderinfo.value = {
id:'' id: ''
} }
} }
}) })
onHide(() => { onHide(() => {
closeExistingConnection()
}) })
onMounted(async () => { onMounted(async () => {
@ -1670,7 +1715,7 @@
} }
setTimeout(() => { setTimeout(() => {
// //
initNetworkListener() useSocket.initNetworkListener()
getElementTop() getElementTop()
}, 500) }, 500)
}) })

368
stores/carts-websocket.js Normal file
View File

@ -0,0 +1,368 @@
import {
defineStore
} from 'pinia';
import {
ref,
computed,
reactive,
onMounted,
onBeforeUnmount,
watchEffect
} from 'vue';
export const useWebSocket = defineStore('socketTask', () => {
const options = {}
let {
heartbeatInterval = 10000, //心跳是10秒一次
reconnectInterval = 3000, //重新连接间隔时间的一个参数
maxReconnectAttempts = 3, //最大重连接次数
initialReconnectInterval = 3000, // 初始重连间隔
initMessage,
initMessageRetryCount = 3, // 新增:初始化消息发送重试次数
initMessageRetryInterval = 2000, // 新增:初始化消息重试间隔
maxReconnectDuration = Infinity,
} = 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 = () => {
try {
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;
} catch (error) {
console.error(error)
//TODO handle the exception
}
};
const websocketsendMessage = (data) => {
uni.$u.debounce(sendMessage(data), 500)
}
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);
}
};
let onMessage = () => {
}
function setOnMessage(onMessageBallBack){
onMessage=onMessageBallBack
}
// 连接 WebSocket
const connect = async (connectMsg, onMessageBallBack) => {
if (!isNetworkConnected.value) {
uni.showToast({
title: '网络未连接...',
icon: 'none'
})
setTimeout(() => {
uni.pro.switchTab('index/index')
}, 1000)
console.log('网络未连接,暂不尝试连接 WebSocket');
return;
}
if (connectMsg) {
initMessage = connectMsg
}
if (onMessageBallBack) {
onMessage = onMessageBallBack
}
if (socketTask.value && isConnected.value) {
try {
sendMessage(connectMsg ? connectMsg : initMessage)
} catch (error) {
//TODO handle the exception
}
return
}
socketTask.value = uni.connectSocket({
url: uni.conf.baseUrlwws + '?' + Date.now(),
success: (res) => {
console.log('连接成功');
isConnected.value = true;
// 监听初始化成功在开启心跳
startHeartbeat();
},
fail: () => {
console.error('WebSocket 连接失败,尝试重连');
if (autoReconnect.value && allowReconnect.value) {
handleReconnect();
}
}
});
if (socketTask.value) {
socketTask.value.onOpen(() => {
// 初始化 初始购物车
sendMessage(connectMsg ? connectMsg : initMessage)
isConnected.value = true
});
socketTask.value.onMessage((res) => {
receivedMessages.value = JSON.parse(res.data)
console.log('收到消息',receivedMessages.value)
sendMessage({
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;
}
clearInterval(heartbeatTimer.value)
heartbeatTimer.value = setInterval(() => {
sendMessage({
type: 'ping_interval',
set: 'shopping'
})
}, heartbeatInterval);
};
// 手动关闭连接
const closeSocket = () => {
console.log('手动关闭连接');
isManuallyClosed.value = true;
closeExistingConnection();
};
// 发送消息
const sendMessage = (data) => {
if (isConnected.value && data) {
// console.log('发送消息', data);
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,
allowReconnect,
socketTask,setOnMessage
};
})

View File

@ -27,7 +27,7 @@ export const useCartsStore = defineStore('cart',
const goodsMap = reactive({}) const goodsMap = reactive({})
//获取商品数据 //获取商品数据
async function goodsInit() { async function goodsInit() {
goodsIsloading.value=true; goodsIsloading.value = true;
//获取招牌菜商品 //获取招牌菜商品
const hotres = await productminiApphotsquery(); const hotres = await productminiApphotsquery();
for (let product of hotres) { for (let product of hotres) {
@ -106,8 +106,6 @@ export const useCartsStore = defineStore('cart',
//收到socket消息时对购物车进行处理 //收到socket消息时对购物车进行处理
async function onMessage(Message) { async function onMessage(Message) {
console.log('Message');
console.log(Message);
if (Message) { if (Message) {
// 心跳返回 过滤 // 心跳返回 过滤
if (Message.type == "ping_interval" || Message.msg_id == "ping_interval") { if (Message.type == "ping_interval" || Message.msg_id == "ping_interval") {
@ -219,13 +217,13 @@ export const useCartsStore = defineStore('cart',
//是否使用会员价 //是否使用会员价
const useVipPrice = computed(() => { const useVipPrice = computed(() => {
const isUse=(orderVIP.value.isVip && shopInfo.value.isMemberPrice)?true:false const isUse = (orderVIP.value.isVip && shopInfo.value.isMemberPrice) ? true : false
console.log('useVipPrice',isUse);
return isUse return isUse
}) })
function currentCalcMpneyNumber(item){
const n=item.number-(item.returnNum||0) function currentCalcMpneyNumber(item) {
return n<=0?0:n const n = item.number - (item.returnNum || 0)
return n <= 0 ? 0 : n
} }
//历史订单商品价格总和 //历史订单商品价格总和
const oldOrderMoney = computed(() => { const oldOrderMoney = computed(() => {
@ -239,7 +237,7 @@ export const useCartsStore = defineStore('cart',
const memberPrice = cur.skuData ? (cur.skuData.memberPrice || cur.skuData const memberPrice = cur.skuData ? (cur.skuData.memberPrice || cur.skuData
.salePrice) : 0 .salePrice) : 0
const price = (discount_sale_amount || cur.salePrice || 0) const price = (discount_sale_amount || cur.salePrice || 0)
const number =currentCalcMpneyNumber(cur) const number = currentCalcMpneyNumber(cur)
return prve + (number <= 0 ? 0 : number) * (discount_sale_amount || (useVipPrice return prve + (number <= 0 ? 0 : number) * (discount_sale_amount || (useVipPrice
.value ? memberPrice : price)) .value ? memberPrice : price))
}, 0) }, 0)
@ -351,7 +349,7 @@ export const useCartsStore = defineStore('cart',
console.log('isBwc'); console.log('isBwc');
console.log(isBwc); console.log(isBwc);
let cart = matchedProducts.reduce((total, item) => { let cart = matchedProducts.reduce((total, item) => {
if(isBwc===true){ if (isBwc === true) {
return total + (parseFloat(item.price) * parseFloat(item.num - item.returnNum)); return total + (parseFloat(item.price) * parseFloat(item.num - item.returnNum));
} }
// 是否启用会员价 0否1是 // 是否启用会员价 0否1是
@ -447,7 +445,8 @@ export const useCartsStore = defineStore('cart',
totalPrice, totalPrice,
totalPackFee, totalPackFee,
updateData, updateData,
useVipPrice,totalOriginPrice useVipPrice,
totalOriginPrice
}; };
} }
); );