diff --git a/pages/chat-detail/index.js b/pages/chat-detail/index.js index db6e3d8..3ddf3b5 100644 --- a/pages/chat-detail/index.js +++ b/pages/chat-detail/index.js @@ -1,5 +1,6 @@ // pages/chat-detail/index.js const API = require('../../utils/api.js'); +const SocketIO = require('../../utils/socket.js'); Page({ // 分享给朋友/群聊 @@ -65,7 +66,9 @@ Page({ console.error('聊天详情页面 - 未传递有效的聊天ID'); } this.loadMessages(); // 使用默认值true,首次进入页面时自动滚动到底部 - this.startTimer(); + + // 初始化Socket.IO连接 + this.initSocketConnection(); }, onShow: function () { @@ -97,6 +100,120 @@ Page({ wx.navigateBack(); }, + // 初始化Socket.IO连接 + initSocketConnection: function () { + console.log('初始化Socket.IO连接'); + + // 获取当前用户的手机号 + const users = wx.getStorageSync('users') || {}; + const userId = wx.getStorageSync('userId'); + let userPhone = null; + + if (userId && users[userId]) { + userPhone = users[userId].phoneNumber || users[userId].phone; + } + + if (!userPhone) { + const userInfo = wx.getStorageSync('userInfo'); + userPhone = userInfo ? (userInfo.phoneNumber || userInfo.phone) : null; + } + + if (!userPhone) { + userPhone = wx.getStorageSync('phoneNumber') || wx.getStorageSync('phone'); + } + + if (!userPhone) { + console.warn('用户未登录,无法建立Socket.IO连接'); + return; + } + + const chatId = this.data.chatId; + if (!chatId) { + console.warn('聊天ID不存在,无法建立Socket.IO连接'); + return; + } + + // 初始化Socket.IO + SocketIO.initSocket(userPhone, chatId) + .then(() => { + console.log('Socket.IO连接成功'); + + // 添加消息接收事件监听器 + SocketIO.on('message', (message) => { + this.handleSocketMessage(message); + }); + }) + .catch((error) => { + console.error('Socket.IO连接失败:', error); + // 连接失败时,回退到轮询模式 + console.log('回退到轮询模式'); + this.startTimer(); + }); + }, + + // 处理Socket.IO消息 + handleSocketMessage: function (message) { + console.log('收到Socket.IO消息:', message); + + // 获取当前用户的手机号 + const users = wx.getStorageSync('users') || {}; + const userId = wx.getStorageSync('userId'); + let userPhone = null; + + if (userId && users[userId]) { + userPhone = users[userId].phoneNumber || users[userId].phone; + } + + if (!userPhone) { + const userInfo = wx.getStorageSync('userInfo'); + userPhone = userInfo ? (userInfo.phoneNumber || userInfo.phone) : null; + } + + if (!userPhone) { + userPhone = wx.getStorageSync('phoneNumber') || wx.getStorageSync('phone'); + } + + // 处理消息格式 + const sender = (message.sender_phone === userPhone) ? 'me' : 'other'; + const processedMessage = { + id: message.id || Date.now(), + content: message.content, + goodsData: message.goodsData, + sender: sender, + time: this.formatDateTime(message.created_at || new Date().toISOString()), + originalTime: message.created_at || new Date().toISOString(), + isNew: true + }; + + // 更新消息列表 + const newMessages = [...this.data.messages, processedMessage]; + this.setData({ + messages: newMessages + }, () => { + // 检查是否需要滚动到底部 + if (this.data.isAtBottom) { + this.scrollToBottom(true); + } else { + // 显示新消息提示 + this.setData({ + hasNewMessages: true, + newMessageCount: this.data.newMessageCount + 1 + }); + } + + // 短暂延迟后移除isNew标记 + setTimeout(() => { + if (this.isUnloaded) return; + + const updatedMessages = this.data.messages.map(msg => ({ + ...msg, + isNew: false + })); + this.setData({ messages: updatedMessages }); + }, 300); + }); + }, + // 格式化时间显示 formatDateTime: function (dateString) { if (!dateString) return '刚刚'; @@ -459,7 +576,46 @@ Page({ }, 300); }); - // 发送到服务器 + // 消息数据 + const messageData = { + sender_phone: userPhone, + receiver_phone: managerPhone, + content: content + }; + + // 优先使用Socket.IO发送消息 + if (SocketIO.isConnected()) { + console.log('使用Socket.IO发送消息'); + SocketIO.sendMessage(messageData) + .then(res => { + // 检查页面是否已卸载 + if (this.isUnloaded) { + console.log('页面已卸载,忽略消息发送结果'); + return; + } + + console.log('Socket.IO消息发送成功:', res); + }) + .catch(error => { + // 检查页面是否已卸载 + if (this.isUnloaded) { + console.log('页面已卸载,忽略消息发送错误'); + return; + } + + console.error('Socket.IO消息发送失败,回退到HTTP请求:', error); + // Socket.IO发送失败,回退到HTTP请求 + this.sendHttpMessage(userPhone, managerPhone, content); + }); + } else { + // Socket.IO未连接,使用HTTP请求 + console.log('Socket.IO未连接,使用HTTP请求发送消息'); + this.sendHttpMessage(userPhone, managerPhone, content); + } + }, + + // 使用HTTP请求发送消息(回退方法) + sendHttpMessage: function (userPhone, managerPhone, content) { API.sendMessage(userPhone, managerPhone, content).then(res => { // 检查页面是否已卸载 if (this.isUnloaded) { @@ -467,7 +623,7 @@ Page({ return; } - console.log('消息发送成功:', res); + console.log('HTTP消息发送成功:', res); // 发送消息成功后,立即将该聊天标记为已查看 const viewedChats = wx.getStorageSync('viewedChats') || {}; @@ -489,7 +645,7 @@ Page({ return; } - console.error('消息发送失败:', error); + console.error('HTTP消息发送失败:', error); wx.showToast({ title: '消息发送失败', icon: 'none' @@ -517,6 +673,10 @@ Page({ clearInterval(this.data.timer); this.setData({ timer: null }); } + + // 页面隐藏时,断开Socket.IO连接 + console.log('页面隐藏,断开Socket.IO连接'); + SocketIO.disconnect(); }, onUnload: function () { @@ -529,6 +689,10 @@ Page({ this.setData({ timer: null }); } + // 页面卸载时,断开Socket.IO连接 + console.log('页面卸载,断开Socket.IO连接'); + SocketIO.disconnect(); + // 用户退出页面时,自动将该聊天标记为已查看 const managerPhone = this.data.managerPhone; if (managerPhone) { diff --git a/pages/cooperation/index.js b/pages/cooperation/index.js index 27dd536..d626757 100644 --- a/pages/cooperation/index.js +++ b/pages/cooperation/index.js @@ -86,7 +86,7 @@ Page({ */ makePhoneCall: function () { wx.makePhoneCall({ - phoneNumber: '18280108971', + phoneNumber: '19381602346', success: function () { console.log('拨打电话成功'); }, diff --git a/pages/cooperation/index.wxml b/pages/cooperation/index.wxml index 925fc94..bd8549b 100644 --- a/pages/cooperation/index.wxml +++ b/pages/cooperation/index.wxml @@ -52,7 +52,7 @@ 联系电话: - 18280108971 + 19381602346 联系地址: diff --git a/utils/socket.js b/utils/socket.js new file mode 100644 index 0000000..a9be077 --- /dev/null +++ b/utils/socket.js @@ -0,0 +1,246 @@ +// WebSocket实时通信工具 +// 专为小程序设计的WebSocket封装,模拟Socket.IO功能 + +// 导入配置 +const API = require('./api.js'); + +// WebSocket连接实例 +let socket = null; +let isConnecting = false; +let reconnectAttempts = 0; +const maxReconnectAttempts = 5; +const reconnectDelay = 3000; // 重连延迟时间(毫秒) + +// 事件监听器存储 +const eventListeners = {}; + +// 存储当前连接的用户信息 +let currentUserPhone = null; +let currentChatId = null; + +// 获取WebSocket服务器URL +function getSocketUrl() { + // 从API的BASE_URL获取服务器地址 + // 注意:小程序中无法直接获取API的BASE_URL,需要根据环境设置 + const baseUrl = API.isTestMode() ? API.SERVER_CONFIG.DEFAULT_LOCAL_IP : API.SERVER_CONFIG.PRODUCTION; + // 替换http为ws,https为wss + return baseUrl.replace(/^http/, 'ws'); +} + +// 初始化WebSocket连接 +function initSocket(userPhone, chatId) { + return new Promise((resolve, reject) => { + // 存储当前用户信息 + currentUserPhone = userPhone; + currentChatId = chatId; + + // 如果已经连接,直接返回 + if (socket && socket.connected) { + console.log('WebSocket已经连接'); + resolve(socket); + return; + } + + // 如果正在连接中,等待连接完成 + if (isConnecting) { + console.log('WebSocket正在连接中...'); + // 等待连接完成 + const checkConnection = setInterval(() => { + if (socket && socket.connected) { + clearInterval(checkConnection); + resolve(socket); + } else if (!isConnecting) { + clearInterval(checkConnection); + reject(new Error('WebSocket连接失败')); + } + }, 100); + return; + } + + isConnecting = true; + reconnectAttempts = 0; + + try { + const socketUrl = getSocketUrl(); + const fullUrl = `${socketUrl}?user_phone=${userPhone}&chat_id=${chatId}`; + console.log('正在连接WebSocket服务器:', fullUrl); + + // 创建WebSocket连接 + socket = wx.connectSocket({ + url: fullUrl, + header: { + 'content-type': 'application/json' + }, + protocols: ['chat'], + success: () => { + console.log('WebSocket连接创建请求发送成功'); + }, + fail: (error) => { + console.error('WebSocket连接创建失败:', error); + isConnecting = false; + reject(error); + } + }); + + // 连接成功事件 + socket.onOpen(() => { + console.log('WebSocket连接成功'); + socket.connected = true; + isConnecting = false; + reconnectAttempts = 0; + resolve(socket); + }); + + // 连接错误事件 + socket.onError((error) => { + console.error('WebSocket连接错误:', error); + socket.connected = false; + isConnecting = false; + reject(error); + }); + + // 断开连接事件 + socket.onClose((res) => { + console.log('WebSocket断开连接:', res); + socket.connected = false; + isConnecting = false; + // 如果不是手动断开(code !== 1000),尝试重连 + if (res.code !== 1000) { + reconnect(); + } + }); + + // 接收消息事件 + socket.onMessage((res) => { + console.log('收到WebSocket消息:', res); + try { + const message = JSON.parse(res.data); + // 触发对应类型的事件 + if (message.type) { + emitEvent(message.type, message.data); + } else { + // 兼容旧格式消息 + emitEvent('message', message); + } + } catch (error) { + console.error('消息解析失败:', error); + } + }); + + } catch (error) { + console.error('初始化WebSocket失败:', error); + isConnecting = false; + reject(error); + } + }); +} + +// 重连函数 +function reconnect() { + if (reconnectAttempts >= maxReconnectAttempts) { + console.error('WebSocket重连次数超过限制,停止重连'); + return; + } + + reconnectAttempts++; + console.log(`WebSocket正在重连,尝试次数: ${reconnectAttempts}/${maxReconnectAttempts}`); + + // 延迟重连 + setTimeout(() => { + if (currentUserPhone && currentChatId) { + initSocket(currentUserPhone, currentChatId) + .then(() => { + console.log('WebSocket重连成功'); + }) + .catch((error) => { + console.error('WebSocket重连失败:', error); + }); + } + }, reconnectDelay); +} + +// 发送消息 +function sendMessage(messageData) { + return new Promise((resolve, reject) => { + if (!socket || !socket.connected) { + reject(new Error('WebSocket未连接')); + return; + } + + try { + // 构造消息格式,模拟Socket.IO的事件机制 + const message = { + type: 'chat message', + data: messageData + }; + + socket.send({ + data: JSON.stringify(message), + success: () => { + console.log('WebSocket消息发送成功'); + resolve({ success: true }); + }, + fail: (error) => { + console.error('WebSocket消息发送失败:', error); + reject(error); + } + }); + } catch (error) { + console.error('发送WebSocket消息失败:', error); + reject(error); + } + }); +} + +// 断开连接 +function disconnect() { + if (socket) { + console.log('手动断开WebSocket连接'); + socket.close(1000, 'normal closure'); + socket = null; + } + isConnecting = false; + reconnectAttempts = 0; +} + +// 注册事件监听器 +function on(eventName, callback) { + if (!eventListeners[eventName]) { + eventListeners[eventName] = []; + } + eventListeners[eventName].push(callback); +} + +// 移除事件监听器 +function off(eventName, callback) { + if (eventListeners[eventName]) { + if (callback) { + eventListeners[eventName] = eventListeners[eventName].filter(cb => cb !== callback); + } else { + delete eventListeners[eventName]; + } + } +} + +// 触发事件 +function emitEvent(eventName, data) { + if (eventListeners[eventName]) { + eventListeners[eventName].forEach(callback => { + try { + callback(data); + } catch (error) { + console.error('事件监听器执行错误:', error); + } + }); + } +} + +// 导出WebSocket工具 +module.exports = { + initSocket, + sendMessage, + disconnect, + on, + off, + isConnected: () => socket && socket.connected +};