From 04102f7237efefa744090ed7c25f7b5d0807b679 Mon Sep 17 00:00:00 2001
From: quanwei <419654421@qq.com>
Date: Thu, 05 Feb 2026 18:11:57 +0800
Subject: [PATCH] 完成运营中心提现和运营中心权限管理

---
 mobile/pages/plus/business/chat/chat.vue |  258 +++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 187 insertions(+), 71 deletions(-)

diff --git a/mobile/pages/plus/business/chat/chat.vue b/mobile/pages/plus/business/chat/chat.vue
index 9a10aaa..3c1447c 100644
--- a/mobile/pages/plus/business/chat/chat.vue
+++ b/mobile/pages/plus/business/chat/chat.vue
@@ -59,12 +59,12 @@
 										<text class="message-text">{{ message.content }}</text>
 									</view>
 								</view>
-								<view class="message-status" :class="message.is_read ? 'read-read' : 'read-sent​'">
+								<!-- <view class="message-status" :class="message.is_read ? 'read-read' : 'read-sent'">
 									<text class="icon iconfont icon-eye "></text>
-								</view>
+								</view> -->
 							</view>
 							<view class="message-info">
-								<text class="message-time">{{ formatTime(message.create_time) }}</text>
+								<text class="message-time">{{ formatTime(message.send_time||message.create_time) }}</text>
 							</view>
 						</view>
 					</view>
@@ -73,7 +73,7 @@
 					<view class="message-mine" v-else>
 						<view class="message-content">
 							<view class="d-s-e">
-								<view class="message-status" :class="message.is_read ? 'read-read' : 'read-sent​'">
+								<view class="message-status" :class="message.is_read ? 'read-read' : 'read-sent'">
 								</view>
 								<view class="d-f">
 									<view class="message-bubble">
@@ -86,7 +86,7 @@
 
 							</view>
 							<view class="message-info">
-								<text class="message-time">{{ formatTime(message.create_time) }}</text>
+								<text class="message-time">{{ formatTime(message.send_time||message.create_time) }}</text>
 							</view>
 
 						</view>
@@ -193,6 +193,7 @@
 					avatarUrl: ''
 				},
 				card: {},
+				wsUrl:"",
 
 				// 聊天数据
 				messages: [],
@@ -214,7 +215,12 @@
 
 				// 定时器
 				typingTimer: null,
-				readTimer: null
+				readTimer: null,
+				reconnectTimer: null,
+				reconnectAttempts: 0,
+				maxReconnectAttempts: 5,
+				heartbeatTimer: null,
+				is_exit: false //是否退出
 			};
 		},
 		onLoad(options) {
@@ -224,24 +230,29 @@
 			this.businessCardId = parseInt(options.business_card_id) || 0;
 			this.conversationId = parseInt(options.conversation_id) || 0;
 			this.targetUser.nickName = options.nickName || '用户';
-
 			// 获取当前用户信息
 			this.getCurrentUser();
 			if (!this.conversationId) {
 				// 初始化会话
 				this.initConversation();
+			}else{
+				// 加载消息
+				this.loadMessages();
+				this.initWebSocket();
 			}
-			// 加载消息
-			this.loadMessages();
-
+			
 		},
 		onUnload() {
+			this.is_exit = true;
 			// 清理定时器
 			if (this.typingTimer) {
 				clearTimeout(this.typingTimer);
 			}
 			if (this.readTimer) {
 				clearTimeout(this.readTimer);
+			}
+			if (this.reconnectTimer) {
+				clearTimeout(this.reconnectTimer);
 			}
 			// 关闭WebSocket
 			this.closeWebSocket();
@@ -257,8 +268,6 @@
 				let self = this;
 				self._get('user.user/detail', {}, res => {
 					self.currentUser = res.data.userInfo;
-					// 初始化WebSocket连接
-					self.initWebSocket();
 				})
 			},
 
@@ -269,6 +278,10 @@
 					business_card_id: this.businessCardId
 				}, res => {
 					this.conversationId = res.data.conversation.conversation_id;
+					this.loadMessages();
+					this.wsUrl = res.data.wsUrl;
+					// 初始化WebSocket连接
+					this.initWebSocket();
 				});
 			},
 
@@ -296,31 +309,24 @@
 						if (self.page === 1) {
 							// First page - sort messages by create_time ascending, newest at bottom
 							self.messages = newMessages.sort((a, b) => {
-								return new Date(a.create_time) - new Date(b.create_time);
+								return a.send_time - b.send_time;
 							});
 						} else {
 							// For pagination, prepend older messages
 							const sortedMessages = newMessages.sort((a, b) => {
-								return new Date(a.create_time) - new Date(b.create_time);
+								return a.send_time - b.send_time;
 							});
 							self.messages = [...sortedMessages, ...self.messages];
 						}
 
 						self.hasMore = self.messages.length < res.data.messages.total;
 						self.loadMore = false;
-						// 滚动到底部
-						self.$nextTick(() => {
-							self.scrollToBottom();
-						});
 						self.page++;
 						if (self.page == 2) {
+							console.log(res.data.heUser);
 							// 获取目标用户信息
 							this.targetUser = res.data.heUser;
 							this.card = res.data.card;
-							// 滚动到底部
-							self.$nextTick(() => {
-								self.scrollToBottom();
-							});
 							// 标记整个会话为已读
 							if (self.conversationId) {
 								self._post('plus.business.chat.chat/markAsRead', {
@@ -333,6 +339,11 @@
 											if (msg.sender_id !== self.currentUser.user_id) {
 												msg.is_read = true;
 											}
+										});
+
+										// 滚动到底部
+										self.$nextTick(() => {
+											self.scrollToBottom();
 										});
 									}
 								});
@@ -348,28 +359,9 @@
 			async sendMessage() {
 				let self = this;
 				if (!this.inputMessage.trim()) return;
-
 				const content = self.inputMessage.trim();
+				console.log(content);
 				self.sendWebSocketMessage(content);
-				/* self._post('plus.business.chat.chat/sendMessage', {
-					conversation_id: self.conversationId,
-					business_card_id: self.businessCardId,
-					content: content
-				}, res => {
-					if (res.code === 1) {
-						// 添加到消息列表
-						self.messages.push(res.data.message);
-						self.inputMessage = '';
-						// 滚动到底部
-						self.$nextTick(() => {
-							self.scrollToBottom();
-						});
-						// 发送WebSocket消息
-						self.sendWebSocketMessage(res.data.ws_message);
-					} else {
-						self.showError(res.msg || '发送失败');
-					}
-				}); */
 			},
 
 			// WebSocket相关方法
@@ -383,26 +375,26 @@
 				const currentUserId = this.currentUser.user_id;
 
 				// 构建WebSocket连接URL - 使用名片聊天专用端口2349
-				const wsUrl = `ws://localhost:2349?user_id=${currentUserId}`;
-
+				const wsUrl = `${this.wsUrl}:2349?user_id=${currentUserId}&usertype=supplier `;
 				try {
 					// 创建WebSocket连接
 					this.socketTask = uni.connectSocket({
 						url: wsUrl,
-						success: () => {
-							console.log('WebSocket连接成功');
-						},
+						success: () => {},
 						fail: (err) => {
-							console.error('WebSocket连接失败:', err);
+							// 尝试重连
+							this.reconnectWebSocket();
 						}
 					});
 
 					// 监听WebSocket打开事件
 					this.socketTask.onOpen(() => {
-						console.log('WebSocket已打开');
 						this.isOnline = true;
+						this.reconnectAttempts = 0; // 重置重连计数
 						// 发送心跳包
 						this.startHeartbeat();
+						// 绑定用户ID
+						this.bindUserId();
 					});
 
 					// 监听WebSocket消息事件
@@ -425,6 +417,8 @@
 					this.socketTask.onError((err) => {
 						console.error('WebSocket错误:', err);
 						this.isOnline = false;
+						// 尝试重连
+						this.reconnectWebSocket();
 					});
 
 					// 监听WebSocket关闭事件
@@ -438,11 +432,55 @@
 							clearInterval(this.heartbeatTimer);
 							this.heartbeatTimer = null;
 						}
+
+						// 尝试重连
+						this.reconnectWebSocket();
 					});
 
 				} catch (error) {
 					console.error('初始化WebSocket失败:', error);
+					// 尝试重连
+					this.reconnectWebSocket();
 				}
+			},
+
+			// 绑定用户ID
+			bindUserId() {
+				if (this.socketTask && this.currentUser.user_id) {
+					const bindMsg = {
+						type: 'bind',
+						user_id: this.currentUser.user_id
+					};
+					try {
+						this.socketTask.send({
+							data: JSON.stringify(bindMsg)
+						});
+						console.log('用户ID绑定消息已发送');
+					} catch (error) {
+						console.log('发送用户ID绑定消息失败:', error);
+					}
+				}
+			},
+
+			// WebSocket重连机制
+			reconnectWebSocket() {
+				if (this.is_exit) {
+					console.log(this.is_exit);
+					return false;
+				}
+				this.reconnectAttempts++;
+				console.log(`尝试第${this.reconnectAttempts}次重连WebSocket`);
+
+				// 清除之前的重连定时器
+				if (this.reconnectTimer) {
+					clearTimeout(this.reconnectTimer);
+				}
+
+				// 设置重连延迟,指数退避策略
+				const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 10000);
+				this.reconnectTimer = setTimeout(() => {
+					this.initWebSocket();
+				}, delay);
 			},
 
 			// 处理WebSocket消息
@@ -451,6 +489,10 @@
 					case 'message':
 						// 收到新消息
 						this.handleNewMessage(data);
+						break;
+					case 'read':
+						// 收到已读确认
+						this.handleReadMessage(data);
 						break;
 					case 'ping':
 						// 心跳响应
@@ -468,7 +510,7 @@
 			// 处理新消息
 			handleNewMessage(data) {
 				// 检查消息是否属于当前会话
-				if (data.business_card_id === this.businessCardId ||
+				if ((data.business_card_id && data.business_card_id === this.businessCardId) ||
 					(data.conversation_id && data.conversation_id === this.conversationId)) {
 
 					// 添加消息到列表
@@ -477,7 +519,8 @@
 						conversation_id: data.conversation_id,
 						sender_id: data.user_id,
 						content: data.content,
-						create_time: Math.floor(Date.now() / 1000),
+						send_time: data.send_time,
+						create_time: data.create_time || Math.floor(Date.now() / 1000),
 						is_read: false
 					};
 
@@ -492,7 +535,6 @@
 					setTimeout(() => {
 						// 通过WebSocket发送已读标记
 						this.markMessageAsRead(newMessage.chat_id);
-
 						// 调用后端API标记消息为已读
 						this._post('plus.business.chat.chat/markAsRead', {
 							chat_id: newMessage.chat_id,
@@ -512,33 +554,110 @@
 				}
 			},
 
+			// 处理已读消息
+			handleReadMessage(data) {
+				// 筛选出当前用户发送的、尚未标记为已读的消息
+				const unreadMessages = this.messages.filter(msg => {
+					return msg.sender_id === this.currentUser.user_id && !msg.is_read;
+				});
+				if (unreadMessages.length === 0) {
+					console.log('没有需要标记已读的消息');
+					return;
+				}
+				console.log(`找到 ${unreadMessages.length} 条未读消息,开始批量标记`);
+				// 对每条未读消息调用markMessageAsRead方法
+				unreadMessages.forEach(msg => {
+					this.markMessageAsRead(msg.chat_id);
+					// 同时更新本地消息状态
+					msg.is_read = true;
+				});
+				console.log('所有未读消息已标记为已读');
+			},
+			sendMessages(message){
+				if(message==''){
+					return 
+				}
+				this._post('plus.business.chat.chat/sendMessage',{
+					content:this.inputMessage,
+					conversation_id: this.conversationId,
+					business_card_id: this.businessCardId,
+				},res=>{
+					// 添加消息到本地列表
+					const localMessage = {
+						chat_id: Date.now(),
+						conversation_id: this.conversationId,
+						sender_id: this.currentUser.user_id,
+						content: this.inputMessage,
+						create_time: Math.floor(Date.now() / 1000),
+						send_time: (Date.now() / 1000),
+						is_read: false
+					};
+					this.messages.push(localMessage);
+					this.inputMessage = '';
+				})
+			},
 			// 发送WebSocket消息
 			sendWebSocketMessage(message) {
 				let self = this;
-				if (!this.socketTask || !this.isOnline) {
-					console.warn('WebSocket未连接,无法发送消息');
+				if (!this.socketTask) {
 					self.initWebSocket();
+					self.sendMessages(message);
+					// 等待连接建立后再发送消息
+					setTimeout(() => {
+						self.sendWebSocketMessage('');
+					}, 1000);
 					return;
 				}
+
 				const wsMessage = {
 					type: 'message',
 					user_id: this.currentUser.user_id,
 					to_user_id: this.targetUserId,
 					content: message,
-					conversation_id: this.conversationId,
-					business_card_id: this.businessCardId,
 					app_id: this.currentUser.grade.app_id
 				};
+
+				// 检查连接状态
+				if (!this.isOnline) {
+					self.initWebSocket();
+					// 等待连接建立后再发送消息
+					self.sendMessages(message);
+					setTimeout(() => {
+						self.sendWebSocketMessage('');
+					}, 1000);
+					return;
+				}
+				if(message==''){
+					return 
+				}
 				try {
 					self.socketTask.send({
 						data: JSON.stringify(wsMessage)
 					});
-					self.page = 1
-					self.loadMessages()
+					// 添加消息到本地列表
+					const localMessage = {
+						chat_id: Date.now(),
+						conversation_id: this.conversationId,
+						sender_id: this.currentUser.user_id,
+						content: message,
+						create_time: Math.floor(Date.now() / 1000),
+						send_time: (Date.now() / 1000),
+						is_read: false
+					};
+					self.messages.push(localMessage);
 					self.inputMessage = '';
-					console.log('WebSocket消息发送成功:', wsMessage);
+
+					// 滚动到底部 - 使用更强的保证方式
+					this.$nextTick(() => {
+						// 延迟一小段时间确保DOM更新完成
+						setTimeout(() => {
+							this.scrollToBottom();
+						}, 50);
+					});
 				} catch (error) {
-					console.error('发送WebSocket消息失败:', error);
+					console.log('发送WebSocket消息失败:', error);
+					// 尝试重连
+					this.reconnectWebSocket();
 				}
 			},
 
@@ -562,10 +681,10 @@
 								data: JSON.stringify(heartbeatMsg)
 							});
 						} catch (error) {
-							console.error('发送心跳失败:', error);
+							console.log('发送心跳失败:', error);
 						}
 					}
-				}, 3000); // 30秒
+				}, 30000); // 30秒
 			},
 
 			// 标记消息为已读
@@ -587,7 +706,7 @@
 						data: JSON.stringify(readMsg)
 					});
 				} catch (error) {
-					console.error('发送已读标记失败:', error);
+					console.log('发送已读标记失败:', error);
 				}
 			},
 
@@ -605,7 +724,7 @@
 							data: JSON.stringify(closeMsg)
 						});
 					} catch (error) {
-						console.error('发送关闭消息失败:', error);
+						console.log('发送关闭消息失败:', error);
 					}
 
 					// 关闭WebSocket连接
@@ -620,16 +739,13 @@
 				}
 
 				this.isOnline = false;
-				console.log('WebSocket连接已关闭');
 			},
 
 			// 滚动相关方法
 			scrollToBottom() {
-				let self=this;
-				this.$nextTick(() => {
-					console.log(self.scrollTop);
-					self.scrollTop = self.oldScrollTop + 1;
-				});
+				console.log(this.scrollTop);
+				// 增加滚动距离确保到达底部
+				this.scrollTop = this.oldScrollTop + 10000;
 			},
 
 			onScroll(e) {
@@ -778,7 +894,7 @@
 				if (!timeStr) return '';
 
 				// 处理日期字符串,将yyyy-MM-dd转换为yyyy/MM/dd以兼容iOS
-				const formattedTimeStr = typeof timeStr === 'string' ? timeStr.replace(/\-/g, '/') : timeStr;
+				const formattedTimeStr = typeof timeStr === 'string' ? timeStr.replace(/\-/g, '/') : timeStr * 1000;
 				const date = new Date(formattedTimeStr);
 				const now = new Date();
 

--
Gitblit v1.9.2