|
|
import http from '../../../utils/api'const baseUrl = require('../../../utils/baseUrl')
// WebSocket实例
let socketOpen = falselet socketMsgQueue = []let heartbeatInterval = nulllet reconnectTimer = nulllet isManualClose = false // 是否手动关闭
let heartBeatTimer = null // 心跳定时器
Page({ data: { baseUrl: baseUrl, // 专家信息
expertInfo: {}, // 用户信息
userInfo: { user: {} // 完整用户信息
}, // 对话信息
conversation: {}, // 消息列表
messageList: [], scrollTop: 0, scrollAnimate: false, // 输入相关
inputValue: '', inputFocus: false, // 多媒体
showMediaSheet: false, // 页面状态
showDateDivider: true, todayDate: '', loading: false, loadingMore: false, // 滚动相关
isScrolling: false, lastScrollTop: 0, // 当前对话ID和对方用户ID
currentExpertId: '', conversationId: '', otherUserId: '', // 分页相关
page: 1, pageSize: 20, hasMore: true, total: 0, // 记录最早消息的时间戳,用于加载更早的消息
earliestMsgTime: 0, // 时间显示间隔
timeInterval: 5, lastShowTimeStamp: 0, // 键盘高度
keyboardHeight: 0,
// WebSocket状态
socketConnected: false, reconnectCount: 0, maxReconnectCount: 5, // 第一次加载完成标记
firstLoadComplete: false, // 是否有新消息
hasNewMessage: false, // 消息ID集合,用于去重
messageIds: new Set(), // 正在发送中的消息ID集合
sendingMessages: new Set(),
// 显示发送中提示
showSendingTip: false, // 发送中的消息数量
sendingCount: 0 },
onLoad: function(options) { console.log('接收到的参数:', options); // 设置今天日期
this.setTodayDate(); // 加载用户信息
this.loadUserInfo(); // 获取对方用户ID
if (options.id) { this.setData({ otherUserId: options.id, currentExpertId: options.id, // 初始页码设置为1
page: 1, messageList: [], hasMore: true, earliestMsgTime: 0, firstLoadComplete: false, messageIds: new Set(), sendingMessages: new Set(), showSendingTip: false, sendingCount: 0 }); // 先创建对话,然后获取聊天记录
this.createConversation(options.id); } // 监听键盘高度变化
wx.onKeyboardHeightChange(this.onKeyboardHeightChange.bind(this)); },
onShow: function() { // 页面显示时连接WebSocket
isManualClose = false; // 确保用户ID和对话ID都存在再连接
const userId = this.getCurrentUserId(); if (userId && this.data.conversationId) { console.log('页面显示,准备连接WebSocket,用户ID:', userId); this.connectWebSocket(); } else { console.log('用户ID或对话ID不存在,稍后再连接', { userId: userId, conversationId: this.data.conversationId }); } },
onHide: function() { // 页面隐藏时暂停心跳检查但不关闭连接
console.log('页面隐藏,暂停心跳检查'); if (heartBeatTimer) { clearInterval(heartBeatTimer); heartBeatTimer = null; } },
onUnload: function() { // 页面卸载时关闭WebSocket并清理资源
isManualClose = true; this.closeWebSocket(); wx.offKeyboardHeightChange(); // 清理定时器
if (heartbeatInterval) { clearInterval(heartbeatInterval); heartbeatInterval = null; } if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; } if (heartBeatTimer) { clearInterval(heartBeatTimer); heartBeatTimer = null; } },
// 获取当前用户ID的辅助方法
getCurrentUserId: function() { const userId = this.data.userInfo?.user?.userId; console.log('获取到的用户ID:', userId); return userId; },
// 创建一对一聊天
createConversation(id) { wx.showLoading({ title: '加载中...' }); http.create({ data: { otherUserId: id }, success: res => { console.log('创建对话响应:', res); if (res && res.data) { // 保存对话ID
const conversationData = res.data; this.setData({ conversationId: conversationData.id || conversationData.conversationId || id, conversation: conversationData, otherUserId: id, // 重置分页参数
page: 1, hasMore: true, messageList: [], earliestMsgTime: 0, firstLoadComplete: false, messageIds: new Set(), sendingMessages: new Set() }); // 获取聊天记录(从第一页开始)
this.getChatHistory(id, false); // 连接WebSocket
const userId = this.getCurrentUserId(); if (userId) { console.log('用户ID已存在,立即连接WebSocket'); this.connectWebSocket(); } else { console.log('用户ID尚未加载,等待用户信息加载完成'); } } else { wx.hideLoading(); wx.showToast({ title: '创建对话失败', icon: 'none' }); } }, fail: err => { console.error('创建对话失败:', err); wx.hideLoading(); wx.showToast({ title: '网络错误', icon: 'none' }); } }); },
// 获取聊天记录
getChatHistory(id, isLoadMore = false) { // 如果是加载更多且没有更多数据,直接返回
if (isLoadMore && !this.data.hasMore) { console.log('没有更多消息了'); this.setData({ loadingMore: false }); return; }
// 如果不是加载更多,显示加载提示
if (!isLoadMore) { wx.showLoading({ title: '加载中...' }); }
const params = { otherUserId: id, page: this.data.page, pageSize: this.data.pageSize };
console.log('请求聊天记录参数:', params);
http.direct({ data: params, success: res => { console.log('获取聊天记录响应:', res); if (!isLoadMore) { wx.hideLoading(); } if (res && res.code === 200) { // 处理返回的消息数据
this.processChatHistory(res, isLoadMore); } else { // 如果没有聊天记录,显示空状态
this.setData({ messageList: [], loading: false, loadingMore: false, hasMore: false, firstLoadComplete: true }); if (!isLoadMore) { wx.showToast({ title: res?.msg || '获取聊天记录失败', icon: 'none' }); } } }, fail: err => { console.error('获取聊天记录失败:', err); if (!isLoadMore) { wx.hideLoading(); } wx.showToast({ title: '网络错误', icon: 'none' }); this.setData({ messageList: [], loading: false, loadingMore: false, firstLoadComplete: true }); } }); },
// 处理聊天历史数据
processChatHistory(response, isLoadMore = false) { let messages = []; let total = 0; // 根据实际返回的数据结构调整这里
if (response.rows && Array.isArray(response.rows)) { messages = response.rows; total = response.total || messages.length; } else if (response.data && Array.isArray(response.data)) { messages = response.data; total = response.total || messages.length; } else if (Array.isArray(response)) { messages = response; total = messages.length; } console.log('处理消息数据:', { messageCount: messages.length, total, isLoadMore, currentPage: this.data.page }); // 格式化消息数据
const formattedMessages = this.formatMessages(messages); // 判断是否还有更多数据
const hasMore = this.data.page * this.data.pageSize < total; // 记录最早消息的时间戳(用于加载更多)
let earliestTime = this.data.earliestMsgTime; if (formattedMessages.length > 0) { // 因为接口返回的是正序,第一条就是最早的
earliestTime = formattedMessages[0].timestamp; } let newMessageList; let targetScrollTop = 0; // 构建消息ID集合
const messageIds = new Set(this.data.messageIds); formattedMessages.forEach(msg => { if (msg.id) { messageIds.add(msg.id); } }); if (isLoadMore) { // 加载更多:新加载的消息(更早的)应该放在现有消息列表的前面
newMessageList = [...formattedMessages, ...this.data.messageList]; // 记录新消息的总高度,用于滚动定位
targetScrollTop = formattedMessages.length * 120; // 估算每条消息高度
} else { // 首次加载
newMessageList = formattedMessages; } // 重新处理消息时间显示
const processedMessages = this.processMessageTimes(newMessageList); this.setData({ messageList: processedMessages, messageIds: messageIds, loading: false, loadingMore: false, hasMore: hasMore, total: total, earliestMsgTime: earliestTime, firstLoadComplete: true }, () => { if (isLoadMore) { // 加载更多后,调整滚动位置以保持在当前查看的位置
setTimeout(() => { this.setData({ scrollAnimate: false, scrollTop: targetScrollTop }, () => { setTimeout(() => { this.setData({ scrollAnimate: true }); }, 100); }); }, 50); } else { // 首次加载,滚动到底部(显示最新消息)
setTimeout(() => { this.scrollToBottom(false); }, 100); } }); },
// 格式化消息数据
formatMessages(messages) { if (!messages || messages.length === 0) return []; const userId = this.getCurrentUserId(); console.log('格式化消息使用的用户ID:', userId); return messages.map(msg => { // 统一使用contentType字段
let contentType = msg.contentType || msg.type || 'text'; let content = msg.content || ''; // 判断发送者
const isMe = msg.senderId === userId || msg.fromUserId === userId || msg.userId === userId || msg.sendId === userId; return { id: msg.id || 'msg_' + Date.now() + Math.random(), isMe: isMe, sender: isMe ? 'user' : 'expert', senderId: msg.senderId || msg.fromUserId || msg.userId, contentType: contentType, content: content, timestamp: msg.createTime || msg.timestamp || Date.now(), status: msg.status || 'success', fileName: msg.fileName || '', fileSize: msg.fileSize || 0, thumb: msg.thumb || '', progress: msg.progress || 100 }; }); },
// 加载更多消息
loadMoreMessages: function() { // 防止重复加载
if (this.data.loadingMore) { console.log('正在加载中,请稍后'); return; } // 检查是否有更多数据
if (!this.data.hasMore) { console.log('没有更多消息了'); return; } // 检查对方用户ID是否存在
if (!this.data.otherUserId) { console.error('对方用户ID不存在'); return; } console.log('开始加载更多消息,当前页码:', this.data.page); // 设置加载状态
this.setData({ loadingMore: true }, () => { // 页码+1,加载更早的消息
const nextPage = this.data.page + 1; this.setData({ page: nextPage }, () => { // 调用获取聊天记录方法,传入true表示是加载更多操作
this.getChatHistory(this.data.otherUserId, true); }); }); },
// 加载用户信息
loadUserInfo: function() { try { const userInfo = wx.getStorageSync('userInfo'); console.log('从缓存加载的用户信息:', userInfo); if (userInfo) { // 处理用户信息,确保正确的结构
let processedUserInfo = { ...this.data.userInfo }; if (userInfo.user) { // 如果已经有user结构
processedUserInfo = userInfo; } else if (userInfo.userId) { // 如果是直接返回的用户数据
processedUserInfo = { user: userInfo }; } // 确保有user对象
if (!processedUserInfo.user) { processedUserInfo.user = {}; } // 确保userId存在
if (userInfo.userId && !processedUserInfo.user.userId) { processedUserInfo.user.userId = userInfo.userId; } // 设置用户ID
const userId = processedUserInfo.user?.userId || userInfo.userId; if (userId) { processedUserInfo.id = userId; } console.log('处理后的用户信息:', processedUserInfo); this.setData({ userInfo: processedUserInfo }); // 用户信息加载完成后,如果对话ID已存在,连接WebSocket
if (this.data.conversationId && !this.data.socketConnected) { console.log('用户信息加载完成,连接WebSocket'); this.connectWebSocket(); } } } catch (e) { console.error('加载用户信息失败:', e); } },
// ========== WebSocket相关方法 ==========
// 连接WebSocket
connectWebSocket() { // 如果已有连接,先检查是否可用
if (socketOpen) { console.log('WebSocket已连接,无需重复连接'); return; }
const userId = this.getCurrentUserId(); console.log('准备连接WebSocket,用户ID:', userId); if (!userId) { console.error('用户ID不存在,无法连接WebSocket'); wx.showToast({ title: '用户信息加载中,请稍后', icon: 'none' }); return; }
// WebSocket连接URL
const basurl = '192.168.101.109:8080'; // 从配置文件获取
const wsUrl = `ws://${basurl}/ws/mini/chat?userId=${userId}`; console.log('开始连接WebSocket:', wsUrl); wx.connectSocket({ url: wsUrl, success: () => { console.log('WebSocket连接初始化成功'); }, fail: (err) => { console.error('WebSocket连接初始化失败:', err); this.reconnectWebSocket(); } });
// 监听连接打开
wx.onSocketOpen(() => { console.log('WebSocket连接已打开'); socketOpen = true; this.setData({ socketConnected: true, reconnectCount: 0 }); // 发送队列中的消息
while (socketMsgQueue.length > 0) { const msg = socketMsgQueue.shift(); this.sendWebSocketMessage(msg); } // 开始心跳
this.startHeartbeat(); });
// 监听收到消息
wx.onSocketMessage((res) => { try { console.log('收到原始WebSocket消息:', res.data); const data = JSON.parse(res.data); console.log('解析后的消息:', data); // 处理不同类型的消息
this.handleWebSocketMessage(data); } catch (e) { console.error('解析消息失败:', e, '原始数据:', res.data); } });
// 监听连接关闭
wx.onSocketClose(() => { console.log('WebSocket连接已关闭'); socketOpen = false; this.setData({ socketConnected: false }); // 停止心跳
if (heartBeatTimer) { clearInterval(heartBeatTimer); heartBeatTimer = null; } // 如果不是手动关闭,尝试重连
if (!isManualClose) { this.reconnectWebSocket(); } });
// 监听错误
wx.onSocketError((err) => { console.error('WebSocket错误:', err); socketOpen = false; this.setData({ socketConnected: false }); }); },
// 关闭WebSocket
closeWebSocket() { if (socketOpen) { wx.closeSocket({ success: () => { console.log('WebSocket连接已主动关闭'); } }); socketOpen = false; this.setData({ socketConnected: false }); } if (heartBeatTimer) { clearInterval(heartBeatTimer); heartBeatTimer = null; } },
// 重连WebSocket
reconnectWebSocket() { if (this.data.reconnectCount >= this.data.maxReconnectCount) { console.log('达到最大重连次数,停止重连'); return; }
if (reconnectTimer) { clearTimeout(reconnectTimer); }
reconnectTimer = setTimeout(() => { console.log(`尝试第${this.data.reconnectCount + 1}次重连`); this.setData({ reconnectCount: this.data.reconnectCount + 1 }); this.connectWebSocket(); }, 3000); },
// 开始心跳
startHeartbeat() { if (heartBeatTimer) { clearInterval(heartBeatTimer); } heartBeatTimer = setInterval(() => { if (socketOpen) { console.log('发送心跳'); this.sendWebSocketMessage(JSON.stringify({ type: 'heartbeat', userId: this.getCurrentUserId(), timestamp: Date.now() })); } }, 30000); },
// 发送WebSocket消息
sendWebSocketMessage(data) { if (socketOpen) { wx.sendSocketMessage({ data: typeof data === 'string' ? data : JSON.stringify(data), success: () => { console.log('WebSocket消息发送成功:', data); }, fail: (err) => { console.error('WebSocket消息发送失败:', err); socketMsgQueue.push(data); } }); } else { console.log('WebSocket未连接,消息加入队列'); socketMsgQueue.push(data); if (!isManualClose) { this.connectWebSocket(); } } },
// 处理WebSocket消息
handleWebSocketMessage(data) { console.log('处理消息类型:', data.type); switch (data.type) { case 'chat': case 'message': this.handleNewMessage(data); break; case 'message_status': this.handleMessageStatus(data); break; case 'typing': this.handleTypingStatus(data); break; case 'online_status': this.handleOnlineStatus(data); break; case 'heartbeat_ack': console.log('收到心跳响应'); break; default: console.log('未知消息类型:', data); } },
// 处理新消息
handleNewMessage(data) { console.log('处理新消息 - 完整数据:', data); // 提取消息内容
let messageData = data.data || data; // 获取消息ID
const messageId = messageData.id || messageData.messageId || 'msg_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); console.log('消息ID:', messageId); console.log('当前发送中消息集合:', this.data.sendingMessages); // 检查是否是自己发送的消息(通过sendingMessages集合判断)
if (this.data.sendingMessages.has(messageId)) { console.log('收到自己发送的消息回执,跳过处理:', messageId); // 从发送中集合移除
const sendingMessages = new Set(this.data.sendingMessages); sendingMessages.delete(messageId); this.setData({ sendingMessages: sendingMessages, sendingCount: sendingMessages.size, showSendingTip: sendingMessages.size > 0 }); // 更新本地消息状态为成功
this.updateMessageStatus(messageId, 'success'); return; } // 检查消息是否已存在(去重)
if (this.data.messageIds.has(messageId)) { console.log('消息已存在,跳过处理:', messageId); return; } const userId = this.getCurrentUserId(); const senderId = messageData.senderId || messageData.fromUserId || messageData.userId; // 判断是否是自己发送的消息
const isMe = senderId === userId; console.log('消息归属判断:', { senderId: senderId, userId: userId, isMe: isMe, messageId: messageId }); // 如果是自己发送的消息但不在sendingMessages中,说明是通过其他方式发送的
// 也跳过处理,避免重复
if (isMe) { console.log('是自己发送的消息,但不在发送中集合,可能已存在,跳过处理'); return; } // 创建新消息对象 - 统一使用contentType
const newMessage = { id: messageId, isMe: isMe, sender: isMe ? 'user' : 'expert', senderId: senderId, contentType: messageData.contentType || messageData.type || 'text', content: messageData.content || '', timestamp: messageData.timestamp || Date.now(), status: 'success', // 对方消息直接显示成功
progress: 100, fileName: messageData.fileName || '', fileSize: messageData.fileSize || 0, thumb: messageData.thumb || '' }; console.log('创建的新消息对象:', newMessage); // 获取当前消息列表
const currentList = [...this.data.messageList]; console.log('当前消息列表长度:', currentList.length); // 添加新消息到列表末尾
currentList.push(newMessage); // 更新消息ID集合
const messageIds = new Set(this.data.messageIds); messageIds.add(messageId); // 重新处理时间显示
const processedMessages = this.processMessageTimes(currentList); console.log('处理后的消息列表长度:', processedMessages.length); console.log('最后一条消息:', processedMessages[processedMessages.length - 1]); // 更新数据
this.setData({ messageList: processedMessages, messageIds: messageIds, hasNewMessage: !isMe // 如果不是自己发的,标记有新消息
}, () => { console.log('消息列表已更新,当前列表:', this.data.messageList); // 判断是否需要滚动到底部
const query = wx.createSelectorQuery(); query.select('#chatScroll').boundingClientRect(); query.select('.chat-bottom-space').boundingClientRect(); query.exec((res) => { if (res[0] && res[1]) { const scrollHeight = res[0].height; const bottomOffset = res[1].top - res[0].top; const scrollTop = this.data.lastScrollTop; // 如果在底部附近(距离底部小于200px)或是自己发的消息,则滚动到底部
const shouldScroll = isMe || (scrollTop + scrollHeight >= bottomOffset - 200); if (shouldScroll) { this.scrollToBottom(true); } } else { // 如果获取不到位置信息,默认滚动到底部
if (isMe) { this.scrollToBottom(true); } } }); // 如果不是自己发的消息,发送已读回执
if (!isMe) { this.sendReadReceipt(messageId); } }); },
// 处理消息状态
handleMessageStatus(data) { const { messageList } = this.data; const messageId = data.messageId || data.id; const index = messageList.findIndex(msg => msg.id === messageId); if (index !== -1) { messageList[index].status = data.status || 'success'; this.setData({ messageList }); } // 如果收到成功状态,从发送中集合移除
if (data.status === 'success' || data.status === 'read') { const sendingMessages = new Set(this.data.sendingMessages); sendingMessages.delete(messageId); this.setData({ sendingMessages: sendingMessages, sendingCount: sendingMessages.size, showSendingTip: sendingMessages.size > 0 }); } },
// 处理正在输入状态
handleTypingStatus(data) { const userId = data.userId || data.senderId; if (userId === this.data.otherUserId) { this.setData({ 'expertInfo.typing': true }); clearTimeout(this.typingTimer); this.typingTimer = setTimeout(() => { this.setData({ 'expertInfo.typing': false }); }, 3000); } },
// 处理在线状态
handleOnlineStatus(data) { const userId = data.userId || data.senderId; if (userId === this.data.otherUserId) { this.setData({ 'expertInfo.online': data.online || data.status === 'online' }); } },
// 发送消息到服务器
sendMessageToServer: function(content, contentType, messageId) { const receiverId = this.data.otherUserId; const senderId = this.getCurrentUserId(); console.log('发送消息参数:', { senderId: senderId, receiverId: receiverId, content: content, contentType: contentType, messageId: messageId });
if (!receiverId || !senderId) { console.error('发送者或接收者ID不存在', { senderId, receiverId }); wx.showToast({ title: '发送失败,用户信息错误', icon: 'none' }); // 发送失败,从发送中集合移除
const sendingMessages = new Set(this.data.sendingMessages); sendingMessages.delete(messageId); this.setData({ sendingMessages: sendingMessages, sendingCount: sendingMessages.size, showSendingTip: sendingMessages.size > 0 }); // 更新消息状态为失败
this.updateMessageStatus(messageId, 'failed'); return; }
const message = { type: 'chat', receiverId: receiverId, senderId: senderId, content: content, contentType: contentType, timestamp: Date.now(), messageId: messageId };
const messageStr = JSON.stringify(message); console.log('发送消息到服务器:', JSON.parse(messageStr));
if (!socketOpen) { console.log('WebSocket未连接,尝试连接'); wx.showToast({ title: '连接中,请稍后', icon: 'none' }); this.connectWebSocket(); socketMsgQueue.push(messageStr); return; }
this.sendWebSocketMessage(messageStr); },
// 发送已读回执
sendReadReceipt(messageId) { if (!socketOpen) return; this.sendWebSocketMessage(JSON.stringify({ type: 'message_status', messageId: messageId, status: 'read', userId: this.getCurrentUserId(), receiverId: this.data.otherUserId, conversationId: this.data.conversationId, timestamp: Date.now() })); },
// 发送正在输入状态
sendTypingStatus() { if (!socketOpen || !this.data.inputValue) return; this.sendWebSocketMessage(JSON.stringify({ type: 'typing', userId: this.getCurrentUserId(), receiverId: this.data.otherUserId, conversationId: this.data.conversationId, timestamp: Date.now() })); },
// ========== 消息发送相关方法 ==========
// 发送文本消息
sendTextMessage: function() { const content = this.data.inputValue.trim(); if (!content) return; const messageId = 'msg_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); console.log('发送文本消息,ID:', messageId); // 将消息ID添加到发送中集合
const sendingMessages = new Set(this.data.sendingMessages); sendingMessages.add(messageId); // 创建本地消息 - 使用contentType
const newMessage = { id: messageId, isMe: true, sender: 'user', senderId: this.getCurrentUserId(), contentType: 'text', content: content, timestamp: Date.now(), status: 'sending', // 设置为发送中状态
progress: 100 }; // 添加到列表
this.addMessageToList(newMessage); // 更新发送中集合和发送计数
this.setData({ sendingMessages: sendingMessages, sendingCount: sendingMessages.size, showSendingTip: sendingMessages.size > 0 }); // 清空输入框
this.setData({ inputValue: '' }); // 通过WebSocket发送到服务器
this.sendMessageToServer(content, 'text', messageId); // 设置超时,如果5秒后还没有收到回执,认为发送失败
setTimeout(() => { if (this.data.sendingMessages.has(messageId)) { console.log('消息发送超时:', messageId); this.updateMessageStatus(messageId, 'timeout'); // 从发送中集合移除
const sendingMessages = new Set(this.data.sendingMessages); sendingMessages.delete(messageId); this.setData({ sendingMessages: sendingMessages, sendingCount: sendingMessages.size, showSendingTip: sendingMessages.size > 0 }); wx.showToast({ title: '发送超时', icon: 'none' }); } }, 5000); },
// 上传图片后发送
uploadImages: function(tempFilePaths) { tempFilePaths.forEach((tempFilePath, index) => { const fileName = `image_${Date.now()}_${index}.jpg`; this.uploadFile(tempFilePath, fileName, 0, 'image'); }); },
// 上传视频后发送
uploadVideo: function(tempFilePath, thumbPath) { const fileName = `video_${Date.now()}.mp4`; this.uploadFile(tempFilePath, fileName, 0, 'video', thumbPath); },
// 通用文件上传
uploadFile: function(tempFilePath, fileName, fileSize = 0, contentType = 'file', thumbPath = '') { const messageId = 'file_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); // 将消息ID添加到发送中集合
const sendingMessages = new Set(this.data.sendingMessages); sendingMessages.add(messageId); const message = { id: messageId, isMe: true, sender: 'user', contentType: contentType, content: tempFilePath, thumb: thumbPath, fileName: fileName, fileSize: fileSize, extension: fileName.split('.').pop().toLowerCase(), timestamp: Date.now(), status: 'uploading', progress: 0 }; this.addMessageToList(message); this.setData({ sendingMessages: sendingMessages, sendingCount: sendingMessages.size, showSendingTip: sendingMessages.size > 0 }); this.simulateUpload(messageId, contentType, tempFilePath); },
// 模拟上传
simulateUpload: function(messageId, contentType, tempFilePath) { let progress = 0; const uploadInterval = setInterval(() => { progress += Math.random() * 20 + 10; if (progress >= 100) { progress = 100; clearInterval(uploadInterval); setTimeout(() => { this.updateMessageStatus(messageId, 'success'); const { messageList } = this.data; const index = messageList.findIndex(msg => msg.id === messageId); if (index !== -1) { delete messageList[index].progress; this.setData({ messageList }); const fileUrl = tempFilePath; this.sendMessageToServer(fileUrl, contentType, messageId); } }, 200); } const { messageList } = this.data; const index = messageList.findIndex(msg => msg.id === messageId); if (index !== -1) { messageList[index].progress = Math.min(Math.floor(progress), 100); this.setData({ messageList }); } }, 100); },
// 添加消息到列表
addMessageToList: function(message) { const { messageList, messageIds } = this.data; // 检查是否已存在
if (messageIds.has(message.id)) { console.log('消息已存在,不重复添加:', message.id); return; } // 新消息添加到末尾
messageList.push(message); // 更新消息ID集合
messageIds.add(message.id); // 重新处理时间显示
const processedMessages = this.processMessageTimes(messageList); this.setData({ messageList: processedMessages, messageIds: messageIds }, () => { this.scrollToBottom(); }); },
// 更新消息状态
updateMessageStatus: function(messageId, status) { const { messageList } = this.data; const index = messageList.findIndex(msg => msg.id === messageId); if (index !== -1) { messageList[index].status = status; this.setData({ messageList }); // 如果是失败状态,从发送中集合移除
if (status === 'failed' || status === 'timeout') { const sendingMessages = new Set(this.data.sendingMessages); sendingMessages.delete(messageId); this.setData({ sendingMessages: sendingMessages, sendingCount: sendingMessages.size, showSendingTip: sendingMessages.size > 0 }); } } },
// 输入处理
onInput: function(e) { this.setData({ inputValue: e.detail.value }); clearTimeout(this.typingDebounce); this.typingDebounce = setTimeout(() => { this.sendTypingStatus(); }, 500); },
// 输入框获得焦点
onInputFocus: function() { this.setData({ inputFocus: true }, () => { setTimeout(() => { this.scrollToBottom(); }, 200); }); },
// 输入框失去焦点
onInputBlur: function() { this.setData({ inputFocus: false }); },
// 清除输入
clearInput: function() { this.setData({ inputValue: '', inputFocus: true }); },
// 滚动事件
onScroll: function(e) { const scrollTop = e.detail.scrollTop; this.setData({ lastScrollTop: scrollTop, isScrolling: true }); clearTimeout(this.scrollTimer); this.scrollTimer = setTimeout(() => { this.setData({ isScrolling: false }); }, 200); // 当滚动到顶部附近时加载更多
if (scrollTop <= 50 && !this.data.loadingMore && this.data.hasMore && this.data.firstLoadComplete) { console.log('触发加载更多,当前滚动位置:', scrollTop); this.loadMoreMessages(); } // 当滚动到底部时,清除新消息标记
const query = wx.createSelectorQuery(); query.select('#chatScroll').boundingClientRect(); query.select('.chat-bottom-space').boundingClientRect(); query.exec((res) => { if (res[0] && res[1]) { const scrollHeight = res[0].height; const bottomOffset = res[1].top - res[0].top; if (scrollTop + scrollHeight >= bottomOffset - 50) { this.setData({ hasNewMessage: false }); } } }); },
// 滚动到底部
scrollToBottom: function(animate = true) { if (this.data.isScrolling) return; this.setData({ scrollAnimate: animate, hasNewMessage: false }, () => { setTimeout(() => { this.setData({ scrollTop: 999999 }); }, 50); }); },
// 键盘高度变化
onKeyboardHeightChange: function(res) { this.setData({ keyboardHeight: res.height }); if (res.height > 0) { this.setData({ showMediaSheet: false }); setTimeout(() => { this.scrollToBottom(); }, 100); } },
// 显示多媒体选择面板
showMediaActionSheet: function() { this.setData({ showMediaSheet: true, inputFocus: false }); },
// 隐藏多媒体选择面板
hideMediaActionSheet: function() { this.setData({ showMediaSheet: false }); },
// 选择图片
chooseImage: function() { this.hideMediaActionSheet(); wx.chooseImage({ count: 9, sizeType: ['compressed'], sourceType: ['album'], success: (res) => { this.uploadImages(res.tempFilePaths); }, fail: () => { wx.showToast({ title: '选择图片失败', icon: 'none' }); } }); },
// 选择视频
chooseVideo: function() { this.hideMediaActionSheet(); wx.chooseVideo({ sourceType: ['album'], compressed: true, maxDuration: 60, success: (res) => { this.uploadVideo(res.tempFilePath, res.thumbTempFilePath); }, fail: () => { wx.showToast({ title: '选择视频失败', icon: 'none' }); } }); },
// 预览图片
previewImage: function(e) { const url = e.currentTarget.dataset.url; wx.previewImage({ current: url, urls: [url] }); },
// 设置今天日期
setTodayDate: function() { const now = new Date(); const month = now.getMonth() + 1; const date = now.getDate(); const week = ['日', '一', '二', '三', '四', '五', '六'][now.getDay()]; this.setData({ todayDate: `${month}月${date}日 星期${week}` }); },
// 处理消息时间显示
processMessageTimes: function(messages) { if (!messages || messages.length === 0) return []; const processedMessages = []; let lastShowTime = null; for (let i = 0; i < messages.length; i++) { const msg = { ...messages[i] }; if (i === 0) { msg.showTime = true; lastShowTime = msg.timestamp; } else { const timeDiffMinutes = (msg.timestamp - lastShowTime) / (1000 * 60); msg.showTime = timeDiffMinutes >= this.data.timeInterval; if (msg.showTime) lastShowTime = msg.timestamp; } processedMessages.push(msg); } if (lastShowTime) { this.setData({ lastShowTimeStamp: lastShowTime }); } return processedMessages; },
// 格式化时间
formatTime: function(timestamp) { if (!timestamp) return ''; const date = new Date(Number(timestamp)); const hours = date.getHours().toString().padStart(2, '0'); const minutes = date.getMinutes().toString().padStart(2, '0'); return `${hours}:${minutes}`; },
// 格式化文件大小
formatFileSize: function(bytes) { if (!bytes || bytes === 0) return '0B'; const units = ['B', 'KB', 'MB', 'GB']; let size = bytes; let unitIndex = 0; while (size >= 1024 && unitIndex < units.length - 1) { size /= 1024; unitIndex++; } return size.toFixed(1) + units[unitIndex]; },
// 阻止事件冒泡
stopPropagation: function() {}});
|