// pages/chat-detail/index.js const API = require('../../utils/api.js'); Page({ // 分享给朋友/群聊 onShareAppMessage() { return { title: '鸡蛋贸易平台 - 实时聊天,商务沟通', path: '/pages/chat-detail/index', imageUrl: '/images/你有好蛋.png' } }, // 分享到朋友圈 onShareTimeline() { return { title: '鸡蛋贸易平台 - 实时聊天,商务沟通', query: '', imageUrl: '/images/你有好蛋.png' } }, // 页面状态标志,用于检测页面是否已卸载 isUnloaded: false, data: { chatId: null, messages: [], inputValue: '', loading: false, chatTitle: '聊天对象', managerPhone: null, timer: null, scrollTop: 0, // 用于控制scroll-view的滚动位置 scrollWithAnimation: true, // 控制滚动是否使用动画 isAtBottom: true, // 标记用户是否在聊天记录底部 hasNewMessages: false, // 标记是否有未读的新消息 newMessageCount: 0, // 未读新消息数量 lastMessageCount: 0 // 上一次消息数量,用于检测新消息 }, onLoad: function (options) { console.log('聊天详情页面加载 - 传递的参数:', JSON.stringify(options, null, 2)); // 同时支持id和userId参数,优先使用userId const chatId = options.userId || options.id; if (chatId) { console.log('聊天详情页面 - 设置聊天ID:', chatId); this.setData({ chatId: chatId, managerPhone: options.phone || chatId // 使用传递的phone作为managerPhone,否则使用chatId }); console.log('聊天详情页面 - 设置客服手机号:', this.data.managerPhone); if (options.name || options.userName) { // 同时支持name和userName参数 const chatTitle = decodeURIComponent(options.name || options.userName); console.log('聊天详情页面 - 设置聊天标题:', chatTitle); this.setData({ chatTitle: chatTitle }); } else { this.loadChatTitle(); } } else { console.error('聊天详情页面 - 未传递有效的聊天ID'); } this.loadMessages(); // 使用默认值true,首次进入页面时自动滚动到底部 this.startTimer(); }, onShow: function () { if (!this.data.timer) { this.startTimer(); } }, loadChatTitle: function () { const managerPhone = this.data.managerPhone; if (managerPhone) { API.getSalesPersonnelInfo(managerPhone).then(personnelInfo => { // 检查页面是否已卸载 if (this.isUnloaded) return; if (personnelInfo && personnelInfo.alias) { this.setData({ chatTitle: personnelInfo.alias }); } }).catch(error => { // 检查页面是否已卸载 if (this.isUnloaded) return; console.error('获取业务员信息失败:', error); }); } }, onBack: function () { wx.navigateBack(); }, // 格式化时间显示 formatDateTime: function (dateString) { if (!dateString) return '刚刚'; const now = new Date(); const msgDate = new Date(dateString); const diffMs = now - msgDate; const diffMins = Math.floor(diffMs / 60000); const diffHours = Math.floor(diffMs / 3600000); const diffDays = Math.floor(diffMs / 86400000); if (diffMins < 1) { return '刚刚'; } else if (diffMins < 60) { return `${diffMins}分钟前`; } else if (diffHours < 24) { return `${diffHours}小时前`; } else if (diffDays < 7) { return `${diffDays}天前`; } else { const year = msgDate.getFullYear(); const month = (msgDate.getMonth() + 1).toString().padStart(2, '0'); const day = msgDate.getDate().toString().padStart(2, '0'); const hours = msgDate.getHours().toString().padStart(2, '0'); const minutes = msgDate.getMinutes().toString().padStart(2, '0'); return `${year}-${month}-${day} ${hours}:${minutes}`; } }, loadMessages: function (shouldScrollToBottom = true) { this.setData({ loading: true }); // 获取当前用户的手机号 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) { this.setData({ loading: false }); wx.showToast({ title: '请先登录', icon: 'none' }); return; } console.log('加载聊天消息 - 聊天ID:', this.data.chatId, '用户手机号:', userPhone); // 获取聊天消息 API.getChatMessages(this.data.chatId, userPhone).then(res => { // 检查页面是否已卸载 if (this.isUnloaded) { console.log('页面已卸载,忽略聊天消息加载结果'); return; } console.log('加载聊天消息 - API返回:', JSON.stringify(res, null, 2)); if (Array.isArray(res)) { console.log('加载聊天消息 - API返回消息数量:', res.length); // 处理消息并排序 const processedMessages = res.map(message => { const sender = (message.sender_phone === userPhone) ? 'me' : 'other'; const time = this.formatDateTime(message.created_at); // 尝试解析JSON格式的商品消息 let content = message.content; let goodsData = null; try { const parsed = JSON.parse(message.content); if (parsed.type === 'goods' && parsed.goodsData) { goodsData = parsed.goodsData; content = parsed.text || ''; } } catch (e) { // 不是JSON格式,使用原始内容 } return { id: message.id, content: content, goodsData: goodsData, sender: sender, time: time, originalTime: message.created_at, isNew: false // 服务器返回的消息不显示新消息动画 }; }); // 按创建时间升序排序(旧消息在前,新消息在后) processedMessages.sort((a, b) => { return new Date(a.originalTime) - new Date(b.originalTime); }); // 在更新数据之前,保存当前的最后一条消息,用于检测新消息 const currentLastMessage = this.data.messages[this.data.messages.length - 1]; const newLastMessage = processedMessages[processedMessages.length - 1]; // 更新消息列表 this.setData({ messages: processedMessages, loading: false }, () => { // 检查页面是否已卸载 if (this.isUnloaded) { console.log('页面已卸载,忽略滚动和新消息检测'); return; } // 在数据更新完成的回调中执行逻辑,确保DOM已更新 // 首次加载消息或明确需要滚动时,直接滚动到底部 if (shouldScrollToBottom) { this.scrollToBottom(false); } else { // 检查是否有新消息 // 比较最新消息的ID是否与当前显示的最后一条消息ID不同 // 有新消息的条件: // 1. 消息列表不为空 // 2. 新消息列表的最后一条消息与当前显示的最后一条消息ID不同 // 3. 最新消息是对方发送的(不是自己发送的) const hasNewMessages = newLastMessage && currentLastMessage && newLastMessage.id !== currentLastMessage.id && newLastMessage.sender === 'other'; // 只有对方发送的消息才视为新消息 console.log('新消息检测:', { hasNewMessages, isAtBottom: this.data.isAtBottom, sender: newLastMessage ? newLastMessage.sender : 'none' }); if (hasNewMessages) { if (this.data.isAtBottom) { // 如果用户当前在底部,自动滚动到底部 console.log('用户在底部,自动滚动到新消息'); this.scrollToBottom(true); } else { // 如果用户不在底部,显示新消息提示按钮 console.log('用户不在底部,显示新消息提示'); this.setData({ hasNewMessages: true, newMessageCount: this.data.newMessageCount + 1 }); } } } }); } else { console.log('加载聊天消息 - API返回非数组数据:', res); this.setData({ messages: [], loading: false }); } }).catch(error => { // 检查页面是否已卸载 if (this.isUnloaded) { console.log('页面已卸载,忽略聊天消息加载错误'); return; } console.error('加载聊天记录失败:', error); this.setData({ loading: false }); wx.showToast({ title: '加载聊天记录失败', icon: 'none' }); }); }, // 滚动到底部 scrollToBottom: function (withAnimation = true) { console.log('执行滚动到底部操作,是否使用动画:', withAnimation); // 检查页面是否已卸载 if (this.isUnloaded) { console.log('页面已卸载,忽略滚动操作'); return; } // 使用wx.nextTick确保在DOM更新后执行 wx.nextTick(() => { // 再次检查页面是否已卸载 if (this.isUnloaded) { console.log('页面已卸载,忽略滚动设置'); return; } this.setData({ scrollWithAnimation: withAnimation, scrollTop: 999999 }, () => { // 再次检查页面是否已卸载 if (this.isUnloaded) { console.log('页面已卸载,忽略滚动完成后的操作'); return; } // 滚动完成后清除新消息提示并显式设置isAtBottom为true setTimeout(() => { // 再次检查页面是否已卸载 if (this.isUnloaded) { console.log('页面已卸载,忽略滚动完成后的状态更新'); return; } this.setData({ hasNewMessages: false, newMessageCount: 0, isAtBottom: true // 显式设置为在底部 }); console.log('滚动完成,清除新消息提示并设置为在底部'); }, 100); }); }); }, // 监听滚动事件,检测是否在底部 onScroll: function (e) { const { scrollTop, scrollHeight, clientHeight } = e.detail; // 计算滚动位置与底部的距离 const distanceToBottom = scrollHeight - scrollTop - clientHeight; // 当距离底部小于100rpx(约50px)时,标记为在底部 const isAtBottom = distanceToBottom < 100; console.log('滚动检测:', { scrollTop, scrollHeight, clientHeight, distanceToBottom, isAtBottom }); // 如果滚动到底部,并且有新消息提示,自动隐藏提示 if (isAtBottom && this.data.hasNewMessages) { this.setData({ hasNewMessages: false, newMessageCount: 0 }); console.log('用户滑动到底部,自动隐藏新消息提示'); } // 直接更新状态,确保滚动位置实时反映 this.setData({ isAtBottom: isAtBottom }); }, onInputChange: function (e) { this.setData({ inputValue: e.detail.value }); }, sendMessage: function () { const content = this.data.inputValue.trim(); if (!content) return; // 获取当前用户的手机号 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) { wx.showToast({ title: '请先登录', icon: 'none' }); return; } const managerPhone = this.data.managerPhone; if (!managerPhone) { wx.showToast({ title: '获取聊天对象失败', icon: 'none' }); return; } // 创建临时消息 const tempMessage = { id: Date.now(), content: content, sender: 'me', time: '刚刚', originalTime: new Date().toISOString(), isNew: true // 标识新消息 }; // 更新消息列表 const newMessages = [...this.data.messages, tempMessage]; this.setData({ messages: newMessages, inputValue: '' }, () => { // 检查页面是否已卸载 if (this.isUnloaded) { console.log('页面已卸载,忽略发送消息后的操作'); return; } // 发送消息时无论用户在哪个位置,都自动滚动到底部 this.scrollToBottom(true); // 短暂延迟后移除isNew标记,确保动画完成 setTimeout(() => { // 检查页面是否已卸载 if (this.isUnloaded) { console.log('页面已卸载,忽略消息状态更新'); return; } const updatedMessages = this.data.messages.map(msg => ({ ...msg, isNew: false })); this.setData({ messages: updatedMessages }); }, 300); }); // 发送到服务器 API.sendMessage(userPhone, managerPhone, content).then(res => { // 检查页面是否已卸载 if (this.isUnloaded) { console.log('页面已卸载,忽略消息发送结果'); return; } console.log('消息发送成功:', res); // 发送消息成功后,立即将该聊天标记为已查看 const viewedChats = wx.getStorageSync('viewedChats') || {}; // 获取消息ID(如果服务器返回了消息ID) const messageId = res && res.id ? res.id : Date.now().toString(); viewedChats[managerPhone] = { hasViewed: true, lastViewedMessageId: messageId, viewedTime: Date.now() }; wx.setStorageSync('viewedChats', viewedChats); console.log('消息发送成功后,自动标记聊天为已查看:', managerPhone); }).catch(error => { // 检查页面是否已卸载 if (this.isUnloaded) { console.log('页面已卸载,忽略消息发送错误'); return; } console.error('消息发送失败:', error); wx.showToast({ title: '消息发送失败', icon: 'none' }); }); }, startTimer: function () { if (this.data.timer) { clearInterval(this.data.timer); } // 每5秒同步一次消息,但不自动滚动到底部 const timer = setInterval(() => { this.loadMessages(false); }, 5000); this.setData({ timer: timer }); }, onHide: function () { if (this.data.timer) { clearInterval(this.data.timer); this.setData({ timer: null }); } }, onUnload: function () { console.log('聊天详情页面卸载 - 清除定时器并设置卸载标志'); // 设置页面卸载标志 this.isUnloaded = true; if (this.data.timer) { clearInterval(this.data.timer); this.setData({ timer: null }); } // 用户退出页面时,自动将该聊天标记为已查看 const managerPhone = this.data.managerPhone; if (managerPhone) { const viewedChats = wx.getStorageSync('viewedChats') || {}; // 获取最后一条消息的ID let lastMessageId = Date.now().toString(); if (this.data.messages && this.data.messages.length > 0) { const lastMessage = this.data.messages[this.data.messages.length - 1]; lastMessageId = lastMessage.id || Date.now().toString(); } viewedChats[managerPhone] = { hasViewed: true, lastViewedMessageId: lastMessageId, viewedTime: Date.now() }; wx.setStorageSync('viewedChats', viewedChats); console.log('用户退出聊天详情页,自动标记聊天为已查看:', managerPhone); } } });