// 聊天页面逻辑 const { API, WebSocketManager, util, constants } = require('../../utils/api.js') const { formatRelativeTime } = util const { MESSAGE_TYPE, WS_MESSAGE_TYPE } = constants Page({ data: { messages: [], inputValue: '', sending: false, websocketManager: null, lastMessageId: '', isConnected: false }, onLoad() { this.initWebSocket() this.loadChatHistory() }, onUnload() { if (this.data.websocketManager) { this.data.websocketManager.disconnect() } }, // 初始化WebSocket连接 initWebSocket() { const manager = new WebSocketManager() const app = getApp() manager.onMessage(WS_MESSAGE_TYPE.MESSAGE, (data) => { this.handleNewMessage(data) }) manager.onMessage(WS_MESSAGE_TYPE.SYSTEM, (data) => { this.handleSystemMessage(data) }) manager.connect(app.globalData.websocketUrl) this.setData({ websocketManager: manager, isConnected: true }) }, // 加载聊天记录 async loadChatHistory() { try { const result = await API.getChatHistory(1, 50) if (result.success && result.data) { const messages = result.data.messages.map(msg => ({ id: msg.id, content: msg.content, type: msg.type || MESSAGE_TYPE.TEXT, isMe: msg.isMe || false, nickname: msg.nickname || 'AI助手', avatar: msg.avatar || '/assets/images/ai-avatar.png', time: formatRelativeTime(new Date(msg.timestamp)) })) this.setData({ messages: messages.reverse() }) this.scrollToBottom() } } catch (error) { console.error('加载聊天记录失败:', error) } }, // 处理新消息 handleNewMessage(data) { const message = { id: data.id || util.generateUniqueId(), content: data.content, type: data.type || MESSAGE_TYPE.TEXT, isMe: false, nickname: data.nickname || 'AI助手', avatar: data.avatar || '/assets/images/ai-avatar.png', time: formatRelativeTime(new Date()) } this.addMessage(message) }, // 处理系统消息 handleSystemMessage(data) { const message = { id: data.id || util.generateUniqueId(), content: data.content, type: MESSAGE_TYPE.SYSTEM, isMe: false, nickname: '系统', avatar: '/assets/images/system-avatar.png', time: formatRelativeTime(new Date()) } this.addMessage(message) }, // 添加消息 addMessage(message) { const messages = [...this.data.messages, message] this.setData({ messages: messages, lastMessageId: message.id }) this.scrollToBottom() }, // 滚动到底部 scrollToBottom() { if (this.data.messages.length > 0) { const lastMessage = this.data.messages[this.data.messages.length - 1] this.setData({ lastMessageId: lastMessage.id }) } }, // 输入变化 onInputChange(e) { this.setData({ inputValue: e.detail.value }) }, // 发送消息 async sendMessage() { const content = this.data.inputValue.trim() if (!content || this.data.sending) { return } this.setData({ sending: true }) // 添加用户消息到界面 const userMessage = { id: util.generateUniqueId(), content: content, type: MESSAGE_TYPE.TEXT, isMe: true, nickname: '我', avatar: '/assets/images/user-avatar.png', time: formatRelativeTime(new Date()) } this.addMessage(userMessage) try { // 通过WebSocket发送消息 if (this.data.websocketManager && this.data.isConnected) { this.data.websocketManager.send({ type: WS_MESSAGE_TYPE.MESSAGE, content: content, messageType: MESSAGE_TYPE.TEXT, timestamp: Date.now() }) } else { // 通过HTTP API发送消息 await API.sendMessage(content, MESSAGE_TYPE.TEXT) } this.setData({ inputValue: '', sending: false }) } catch (error) { console.error('发送消息失败:', error) util.showError('发送失败,请重试') this.setData({ sending: false }) } }, // 选择图片 chooseImage() { wx.chooseImage({ count: 1, sizeType: ['compressed'], sourceType: ['album', 'camera'], success: (res) => { this.uploadImage(res.tempFilePaths[0]) }, fail: (error) => { console.error('选择图片失败:', error) } }) }, // 上传图片 async uploadImage(filePath) { try { util.showLoading('上传中...') const result = await API.uploadFile(filePath, { type: 'image', messageType: MESSAGE_TYPE.IMAGE }) util.hideLoading() if (result.success) { // 发送图片消息 const imageMessage = { id: util.generateUniqueId(), content: result.data.url, type: MESSAGE_TYPE.IMAGE, isMe: true, nickname: '我', avatar: '/assets/images/user-avatar.png', time: formatRelativeTime(new Date()) } this.addMessage(imageMessage) // 通过WebSocket发送图片消息 if (this.data.websocketManager && this.data.isConnected) { this.data.websocketManager.send({ type: WS_MESSAGE_TYPE.MESSAGE, content: result.data.url, messageType: MESSAGE_TYPE.IMAGE, timestamp: Date.now() }) } } } catch (error) { util.hideLoading() console.error('上传图片失败:', error) util.showError('上传失败') } }, // 显示更多操作 showMoreActions() { wx.showActionSheet({ itemList: ['发送文件', '语音输入', '清空聊天记录'], success: (res) => { switch (res.tapIndex) { case 0: this.chooseFile() break case 1: this.startVoiceInput() break case 2: this.clearChatHistory() break } } }) }, // 选择文件 chooseFile() { wx.chooseMessageFile({ count: 1, type: 'file', success: (res) => { this.uploadFile(res.tempFiles[0]) }, fail: (error) => { console.error('选择文件失败:', error) } }) }, // 上传文件 async uploadFile(file) { try { util.showLoading('上传中...') const result = await API.uploadFile(file.path, { type: 'file', messageType: MESSAGE_TYPE.FILE, fileName: file.name, fileSize: file.size }) util.hideLoading() if (result.success) { // 发送文件消息 const fileMessage = { id: util.generateUniqueId(), content: result.data.url, type: MESSAGE_TYPE.FILE, isMe: true, nickname: '我', avatar: '/assets/images/user-avatar.png', time: formatRelativeTime(new Date()), fileName: file.name, fileSize: util.formatFileSize(file.size) } this.addMessage(fileMessage) // 通过WebSocket发送文件消息 if (this.data.websocketManager && this.data.isConnected) { this.data.websocketManager.send({ type: WS_MESSAGE_TYPE.MESSAGE, content: result.data.url, messageType: MESSAGE_TYPE.FILE, fileName: file.name, fileSize: file.size, timestamp: Date.now() }) } } } catch (error) { util.hideLoading() console.error('上传文件失败:', error) util.showError('上传失败') } }, // 开始语音输入 startVoiceInput() { wx.showModal({ title: '语音输入', content: '按住录音按钮开始录音', showCancel: true, confirmText: '开始录音', success: (res) => { if (res.confirm) { this.startRecording() } } }) }, // 开始录音 startRecording() { const recorderManager = wx.getRecorderManager() recorderManager.onStart(() => { console.log('录音开始') }) recorderManager.onStop((res) => { console.log('录音结束', res) if (res.tempFilePath) { this.uploadAudio(res.tempFilePath) } }) recorderManager.start({ duration: 60000, // 最长60秒 sampleRate: 16000, numberOfChannels: 1, encodeBitRate: 96000, format: 'mp3' }) // 5秒后自动停止录音 setTimeout(() => { recorderManager.stop() }, 5000) }, // 上传音频文件 async uploadAudio(filePath) { try { util.showLoading('处理中...') const result = await API.uploadFile(filePath, { type: 'audio', messageType: MESSAGE_TYPE.AUDIO }) util.hideLoading() if (result.success) { // 发送音频消息 const audioMessage = { id: util.generateUniqueId(), content: result.data.url, type: MESSAGE_TYPE.AUDIO, isMe: true, nickname: '我', avatar: '/assets/images/user-avatar.png', time: formatRelativeTime(new Date()) } this.addMessage(audioMessage) // 通过WebSocket发送音频消息 if (this.data.websocketManager && this.data.isConnected) { this.data.websocketManager.send({ type: WS_MESSAGE_TYPE.MESSAGE, content: result.data.url, messageType: MESSAGE_TYPE.AUDIO, timestamp: Date.now() }) } } } catch (error) { util.hideLoading() console.error('上传音频失败:', error) util.showError('处理失败') } }, // 清空聊天记录 clearChatHistory() { wx.showModal({ title: '清空聊天记录', content: '确定要清空所有聊天记录吗?此操作不可恢复。', confirmText: '清空', confirmColor: '#dd524d', success: (res) => { if (res.confirm) { this.setData({ messages: [], lastMessageId: '' }) // 清空本地存储 wx.removeStorageSync('chatHistory') wx.showToast({ title: '已清空', icon: 'success' }) } } }) } })