Page({ data: { // 专家信息 expertInfo: { id: 1, name: '张明专家', title: '资深畜牧兽医', expertise: '牛羊疾病防治', avatar: '/images/avatars/expert1.png', online: true, phone: '13800138000' }, // 用户信息 userInfo: { id: 1001, name: '养殖户', avatar: '/images/avatars/user.png' }, // 消息列表 messageList: [], scrollTop: 0, scrollAnimate: false, // 输入相关 - 优化 inputValue: '', inputFocus: false, // 多媒体 showMediaSheet: false, // 页面状态 showDateDivider: true, todayDate: '', loading: false, loadingMore: false, // 滚动相关 isScrolling: false, lastScrollTop: 0, // 当前专家ID currentExpertId: 1, // 分页相关 page: 1, pageSize: 20, hasMore: true, // 时间显示间隔 timeInterval: 5, lastShowTimeStamp: 0, // 键盘高度 keyboardHeight: 0 }, onLoad: function(options) { this.setTodayDate(); this.loadUserInfo(); if (options.expertId) { this.setData({ currentExpertId: options.expertId }); this.loadExpertInfo(options.expertId); } else { this.loadChatHistory(); } wx.onKeyboardHeightChange(this.onKeyboardHeightChange.bind(this)); }, onUnload: function() { wx.offKeyboardHeightChange(); }, // ========== 输入框相关方法 ========== // 输入处理 onInput: function(e) { this.setData({ inputValue: e.detail.value }); }, // 输入框获得焦点 onInputFocus: function() { this.setData({ inputFocus: true }, () => { setTimeout(() => { this.scrollToBottom(); }, 200); }); }, // 输入框失去焦点 onInputBlur: function() { this.setData({ inputFocus: false }); }, // 清除输入 clearInput: function() { this.setData({ inputValue: '', inputFocus: true }); }, // 发送文本消息 sendTextMessage: function() { const content = this.data.inputValue.trim(); if (!content) return; const newMessage = { id: 'msg-' + Date.now(), sender: 'user', type: 'text', content: content, timestamp: Date.now(), status: 'sending' }; this.addMessageToList(newMessage); // 清空输入框 this.setData({ inputValue: '', inputFocus: false }); // 模拟发送成功 setTimeout(() => { this.updateMessageStatus(newMessage.id, 'success'); setTimeout(() => { this.receiveExpertReply(); }, 1000); }, 500); }, // 添加消息到列表 addMessageToList: function(message) { const { messageList } = this.data; const processedMessage = this.processSingleMessageTime(message, messageList); messageList.push(processedMessage); this.setData({ messageList }, () => { 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 }); } }, // 接收专家回复 receiveExpertReply: function() { const replies = [ '收到您的消息,让我分析一下您说的情况。', '建议您提供更多细节,比如发病时间、具体症状等。', '根据描述,可能是饲料问题引起的,建议调整饲料配方。', '可以考虑添加一些维生素补充剂,改善食欲问题。', '最好能提供照片,这样我可以更准确地判断情况。' ]; const randomReply = replies[Math.floor(Math.random() * replies.length)]; const newMessage = { id: 'exp-' + Date.now(), sender: 'expert', type: 'text', content: randomReply, timestamp: Date.now(), status: 'success' }; this.addMessageToList(newMessage); }, // ========== 滚动相关 ========== // 滚动事件 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 <= 100 && !this.data.loadingMore && this.data.hasMore) { this.loadMoreMessages(); } }, // 加载更多消息 loadMoreMessages: function() { if (this.data.loadingMore || !this.data.hasMore) return; this.setData({ loadingMore: true, page: this.data.page + 1 }, () => { this.loadChatHistory(); }); }, // 滚动到底部 scrollToBottom: function(animate = true) { if (this.data.isScrolling) return; this.setData({ scrollAnimate: animate }, () => { 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: (err) => { 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: (err) => { wx.showToast({ title: '选择视频失败', icon: 'none' }); } }); }, // 选择文件 chooseFile: function() { this.hideMediaActionSheet(); wx.chooseMessageFile({ count: 1, type: 'all', success: (res) => { const file = res.tempFiles[0]; this.uploadFile(file.path, file.name, file.size); }, fail: (err) => { wx.showToast({ title: '选择文件失败', icon: 'none' }); } }); }, // 上传图片 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, type = 'file', thumbPath = '') { const messageId = 'file-' + Date.now(); const message = { id: messageId, sender: 'user', type: type, content: tempFilePath, thumb: thumbPath, fileName: fileName, fileSize: fileSize, extension: fileName.split('.').pop().toLowerCase(), timestamp: Date.now(), status: 'uploading', progress: 0 }; this.addMessageToList(message); this.simulateUpload(messageId, type); }, // 模拟上传过程 simulateUpload: function(messageId, type) { 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 }); if (type === 'image' || type === 'video') { setTimeout(() => { this.receiveMediaReply(type); }, 800); } } }, 200); } const { messageList } = this.data; const index = messageList.findIndex(msg => msg.id === messageId); if (index !== -1) { messageList[index].progress = Math.min(progress, 100); this.setData({ messageList }); } }, 100); }, // 接收媒体回复 receiveMediaReply: function(type) { const imageReplies = [ '照片收到了,牛的状况看起来确实不太理想。', '从照片看,饲养环境需要改善一下。', '图片清晰,我可以更准确地判断问题了。' ]; const videoReplies = [ '视频看到了,动物的精神状态需要关注。', '从视频可以观察到更多细节,这很有帮助。', '视频内容很有价值,让我了解了具体情况。' ]; const replies = type === 'image' ? imageReplies : videoReplies; const randomReply = replies[Math.floor(Math.random() * replies.length)]; const newMessage = { id: 'exp-media-' + Date.now(), sender: 'expert', type: 'text', content: randomReply, timestamp: Date.now(), status: 'success' }; this.addMessageToList(newMessage); }, // 预览图片 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}` }); }, // 加载用户信息 loadUserInfo: function() { try { const userInfo = wx.getStorageSync('userInfo'); if (userInfo) { this.setData({ userInfo }); } } catch (e) { console.error('加载用户信息失败:', e); } }, // 加载专家信息 loadExpertInfo: function(expertId) { wx.showLoading({ title: '加载中...' }); // 模拟网络请求 setTimeout(() => { this.setData({ expertInfo: { id: Number(expertId), name: '张明专家', title: '资深畜牧兽医', expertise: '牛羊疾病防治', avatar: '/images/avatars/expert1.png', online: true, phone: '13800138000' }, loading: false }); wx.hideLoading(); this.loadChatHistory(); }, 500); }, // 加载聊天记录 loadChatHistory: function() { this.setData({ loading: true }); // 模拟历史消息 setTimeout(() => { const now = Date.now(); const mockMessages = [ { id: 'msg-1', sender: 'expert', type: 'text', content: '您好,我是张明专家,有什么可以帮您?', timestamp: now - 10 * 60 * 1000, status: 'success' }, { id: 'msg-2', sender: 'user', type: 'text', content: '您好,我养的牛最近食欲不振,请问是什么原因?', timestamp: now - 9 * 60 * 1000, status: 'success' }, { id: 'msg-3', sender: 'expert', type: 'text', content: '可能是饲料问题或环境变化引起的,请描述一下具体情况。', timestamp: now - 7 * 60 * 1000, status: 'success' }, { id: 'msg-4', sender: 'user', type: 'text', content: '具体症状是拉稀,体温偏高,精神状态不好。', timestamp: now - 2 * 60 * 1000, status: 'success' }, { id: 'msg-5', sender: 'expert', type: 'text', content: '明白了,建议您调整饲料配方,添加一些益生菌。', timestamp: now - 1 * 60 * 1000, status: 'success' } ]; const processedMessages = this.processMessageTimes(mockMessages); this.setData({ messageList: processedMessages, loading: false, hasMore: false }, () => { this.scrollToBottom(true); }); }, 800); }, // 处理消息时间显示 processMessageTimes: function(messages) { if (!messages || messages.length === 0) return []; const sortedMessages = [...messages].sort((a, b) => a.timestamp - b.timestamp); const processedMessages = []; let lastShowTime = null; for (let i = 0; i < sortedMessages.length; i++) { const msg = { ...sortedMessages[i] }; if (!msg.timestamp || isNaN(msg.timestamp) || msg.timestamp <= 0) { msg.timestamp = Date.now() - (sortedMessages.length - i) * 60000; } 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; }, // 处理单条消息时间 processSingleMessageTime: function(message, messageList) { const msg = { ...message }; if (!msg.timestamp || isNaN(msg.timestamp) || msg.timestamp <= 0) { msg.timestamp = Date.now(); } if (messageList.length === 0) { msg.showTime = true; this.setData({ lastShowTimeStamp: msg.timestamp }); return msg; } const lastShowTime = this.data.lastShowTimeStamp; const timeDiffMinutes = (msg.timestamp - lastShowTime) / (1000 * 60); msg.showTime = timeDiffMinutes >= this.data.timeInterval; if (msg.showTime) { this.setData({ lastShowTimeStamp: msg.timestamp }); } return msg; }, // ========== 工具方法 ========== // 格式化时间 formatTime: function(timestamp) { if (!timestamp || timestamp <= 0) return '未知时间'; const date = new Date(Number(timestamp)); if (isNaN(date.getTime())) return '未知时间'; 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() {} });