diff --git a/pagesA/pages/expertChat/expertChat.js b/pagesA/pages/expertChat/expertChat.js index 6c2b12e..dd9cb30 100644 --- a/pagesA/pages/expertChat/expertChat.js +++ b/pagesA/pages/expertChat/expertChat.js @@ -1,343 +1,683 @@ -// 获取应用实例 -const app = getApp() +// pages/consult/consult.js +// import http from '../../../utils/api' // 您的注释代码保留 Page({ data: { // 专家信息 expertInfo: { - id: 1, - name: '张明专家', - title: '资深畜牧兽医', - expertise: '牛羊疾病防治', + id: 0, + name: '', + title: '', + expertise: '', avatar: '/images/avatars/expert1.png', - online: true, - phone: '13800138000' + online: false, + phone: '' }, // 用户信息 userInfo: { - id: 1001, - name: '养殖户', + id: 0, + name: '用户', avatar: '/images/avatars/user.png' }, // 消息列表 messageList: [], - scrollToView: '', + scrollTop: 0, + scrollAnimate: false, // 输入相关 inputValue: '', inputFocus: false, - inputMode: 'keyboard', // keyboard or voice - inputPlaceholder: '输入消息...', + inputMode: 'keyboard', + inputPlaceholder: '请输入消息...', // 多媒体 showMediaSheet: false, // 录音相关 isRecording: false, + isCanceling: false, recordingTime: 0, - recordingTip: '上滑取消录音', + recordingTip: '松开 发送', + voiceTip: '按住 说话', recordingTimer: null, recordManager: null, // 页面状态 - isFirstLoad: true, showDateDivider: true, todayDate: '', + loading: false, - // 消息ID计数器 - messageId: 1000, + // 滚动相关 + isScrolling: false, + lastScrollTop: 0, - // 存储键名 - storageKey: 'consult_messages_' + // 录音相关 + recordStartY: 0, + + // 当前专家ID + currentExpertId: 0, + + // 分页相关 + page: 1, + pageSize: 20, + hasMore: true, + + // 时间显示间隔(分钟) + timeInterval: 5 }, onLoad: function(options) { + console.log('页面加载,参数:', options); + // 初始化录音管理器 - this.initRecordManager() + this.initRecordManager(); // 获取今天日期 - this.setTodayDate() + this.setTodayDate(); // 加载用户信息 - this.loadUserInfo() + this.loadUserInfo(); // 加载专家信息 if (options.expertId) { - this.loadExpertInfo(options.expertId) + this.setData({ currentExpertId: options.expertId }); + this.loadExpertInfo(options.expertId); + } else { + // 如果没有传expertId,使用默认值 + this.setData({ + currentExpertId: 1, + expertInfo: { + id: 1, + name: '张明专家', + title: '资深畜牧兽医', + expertise: '牛羊疾病防治', + avatar: '/images/avatars/expert1.png', + online: true, + phone: '13800138000' + } + }, () => { + // 加载聊天记录 + this.loadChatHistory(); + }); } - // 加载聊天记录 - this.loadChatHistory() - // 设置键盘监听 - wx.onKeyboardHeightChange(this.onKeyboardHeightChange) - - // 模拟首次进入时的欢迎消息 - setTimeout(() => { - this.setData({ isFirstLoad: false }) - }, 2000) + wx.onKeyboardHeightChange(this.onKeyboardHeightChange.bind(this)); }, onUnload: function() { + console.log('页面卸载'); + // 清理定时器 if (this.data.recordingTimer) { - clearInterval(this.data.recordingTimer) + clearInterval(this.data.recordingTimer); } // 移除监听器 - wx.offKeyboardHeightChange() - - // 保存聊天记录 - this.saveChatHistory() + wx.offKeyboardHeightChange(); }, onShow: function() { - // 页面显示时自动滚动到底部 - setTimeout(() => { - this.scrollToBottom() - }, 300) + console.log('页面显示'); + }, + + onReady: function() { + console.log('页面初次渲染完成'); }, // 初始化录音管理器 initRecordManager: function() { - this.recordManager = wx.getRecorderManager() + this.recordManager = wx.getRecorderManager(); this.recordManager.onStart(() => { - console.log('录音开始') - }) + console.log('录音开始'); + this.setData({ + isRecording: true, + recordingTime: 0, + recordingTip: '松开 发送' + }); + + // 开始计时 + const timer = setInterval(() => { + const time = this.data.recordingTime + 1; + this.setData({ recordingTime: time }); + }, 1000); + + this.setData({ recordingTimer: timer }); + }); this.recordManager.onStop((res) => { - const { tempFilePath, duration } = res - if (tempFilePath) { - this.sendAudioMessage(tempFilePath, Math.floor(duration / 1000)) + console.log('录音停止', res); + if (this.data.recordingTimer) { + clearInterval(this.data.recordingTimer); + } + + const { tempFilePath, duration } = res; + if (tempFilePath && !this.data.isCanceling) { + this.sendAudioMessage(tempFilePath, Math.floor(duration / 1000)); } - }) + + this.setData({ + isRecording: false, + isCanceling: false, + recordingTime: 0 + }); + }); this.recordManager.onError((err) => { - console.error('录音失败:', err) + console.error('录音失败:', err); wx.showToast({ title: '录音失败', icon: 'none' - }) - this.setData({ isRecording: false }) - }) + }); + this.setData({ + isRecording: false, + isCanceling: false + }); + }); }, // 设置今天日期 setTodayDate: function() { - const now = new Date() - const month = now.getMonth() + 1 - const date = now.getDate() - const week = ['日', '一', '二', '三', '四', '五', '六'][now.getDay()] + 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() { - const userInfo = wx.getStorageSync('userInfo') || this.data.userInfo - this.setData({ userInfo }) + try { + // 从本地存储获取用户信息,如果没有则使用默认值 + const userInfo = wx.getStorageSync('userInfo'); + if (userInfo) { + this.setData({ userInfo }); + } else { + // 默认用户信息 + const defaultUserInfo = { + id: 1001, + name: '养殖户', + avatar: '/images/avatars/user.png' + }; + this.setData({ userInfo: defaultUserInfo }); + wx.setStorageSync('userInfo', defaultUserInfo); + } + } catch (e) { + console.error('加载用户信息失败:', e); + // 使用默认用户信息 + this.setData({ + userInfo: { + id: 1001, + name: '养殖户', + avatar: '/images/avatars/user.png' + } + }); + } }, - // 加载专家信息 + // 加载专家信息 - 使用您的接口调用方式 loadExpertInfo: function(expertId) { - // 这里应该是API请求,暂时使用模拟数据 - wx.showLoading({ title: '加载中...' }) + console.log('加载专家信息:', expertId); + wx.showLoading({ title: '加载中...' }); - setTimeout(() => { - const expertInfo = { - id: expertId, - name: ['张明', '李华', '王强', '赵刚'][expertId - 1] + '专家', + // 使用您的接口调用方式 + // http.getExpertInfo({ + // data: { expertId: expertId }, + // success: (res) => { + // console.log('专家信息:', res); + + // if (res.code === 0) { + // this.setData({ + // expertInfo: res.data, + // loading: false + // }); + + // // 加载聊天记录 + // this.loadChatHistory(); + // } else { + // // 如果接口返回失败,使用默认数据 + // this.loadDefaultExpertInfo(expertId); + // } + + // wx.hideLoading(); + // }, + // fail: (err) => { + // console.error('加载专家信息失败:', err); + // wx.hideLoading(); + // wx.showToast({ + // title: '加载失败', + // icon: 'none' + // }); + + // // 如果接口调用失败,使用默认数据 + // this.loadDefaultExpertInfo(expertId); + // } + // }); + }, + + // 加载默认专家信息(当接口失败时使用) + loadDefaultExpertInfo: function(expertId) { + const experts = [ + { + id: 1, + name: '张明专家', title: '资深畜牧兽医', expertise: '牛羊疾病防治', - avatar: `/images/avatars/expert${expertId}.png`, - online: Math.random() > 0.3, - phone: '138' + Math.floor(Math.random() * 100000000).toString().padStart(8, '0') + avatar: '/images/avatars/expert1.png', + online: true, + phone: '13800138000' + }, + { + id: 2, + name: '李华专家', + title: '高级畜牧师', + expertise: '饲料营养', + avatar: '/images/avatars/expert2.png', + online: false, + phone: '13800138001' + }, + { + id: 3, + name: '王强专家', + title: '兽医专家', + expertise: '疾病防治', + avatar: '/images/avatars/expert3.png', + online: true, + phone: '13800138002' } - - this.setData({ - expertInfo, - storageKey: `consult_messages_${expertId}_${this.data.userInfo.id}` - }) - - wx.hideLoading() - }, 500) + ]; + + const expertInfo = experts.find(e => e.id == expertId) || experts[0]; + + this.setData({ + expertInfo, + loading: false + }); + + // 加载聊天记录 + this.loadChatHistory(); }, - // 加载聊天记录 + // 加载聊天记录 - 使用您的接口调用方式 loadChatHistory: function() { - const storageKey = this.data.storageKey - const savedMessages = wx.getStorageSync(storageKey) || [] + const { currentExpertId, userInfo, page, pageSize } = this.data; - if (savedMessages.length > 0) { - // 处理消息时间显示 - const processedMessages = this.processMessageTimes(savedMessages) - this.setData({ - messageList: processedMessages, - isFirstLoad: false - }) - - // 滚动到底部 - setTimeout(() => { - this.scrollToBottom() - }, 200) - } else { - // 初始化第一条欢迎消息 - this.initWelcomeMessage() - } - }, - - // 处理消息时间显示 - processMessageTimes: function(messages) { - if (!messages || messages.length === 0) return messages + this.setData({ loading: true }); - let lastTimestamp = 0 + console.log('加载聊天记录:', { + expertId: currentExpertId, + userId: userInfo.id, + page: page, + pageSize: pageSize + }); - return messages.map((msg, index) => { - const currentTimestamp = msg.timestamp - - // 如果两条消息间隔超过5分钟,显示时间 - const timeDiff = currentTimestamp - lastTimestamp - msg.showTime = timeDiff > 5 * 60 * 1000 || index === 0 - - if (msg.showTime) { - lastTimestamp = currentTimestamp + // 使用您的接口调用方式 + // http.getChatHistory({ + // data: { + // expertId: currentExpertId, + // userId: userInfo.id, + // page: page, + // pageSize: pageSize + // }, + // success: (res) => { + // console.log('聊天记录:', res); + + // if (res.code === 0) { + // const messages = res.data.list || []; + + // if (messages.length > 0) { + // // 处理消息时间显示 + // const processedMessages = this.processMessageTimes(messages); + + // this.setData({ + // messageList: processedMessages, + // loading: false, + // hasMore: messages.length >= pageSize + // }, () => { + // // 滚动到底部 + // this.scrollToBottom(true); + // }); + // } else { + // // 如果没有历史记录,添加一条欢迎消息 + // this.addWelcomeMessage(); + // } + // } else { + // // 如果接口返回失败,使用测试数据 + // this.loadMockChatHistory(); + // } + // }, + // fail: (err) => { + // console.error('加载聊天记录失败:', err); + // this.setData({ loading: false }); + + // // 如果接口调用失败,使用测试数据 + // this.loadMockChatHistory(); + // } + // }); + }, + + // 加载模拟聊天记录(当接口失败时使用) + loadMockChatHistory: function() { + const now = Date.now(); + // 测试数据 - 确保有时间戳 + const mockMessages = [ + { + id: 'msg-1', + sender: 'expert', + type: 'text', + content: '您好,我是张明专家,有什么可以帮您?', + timestamp: now - 3600000, // 1小时前 + showTime: true + }, + { + id: 'msg-2', + sender: 'user', + type: 'text', + content: '您好,我养的牛最近食欲不振,请问是什么原因?', + timestamp: now - 1800000, // 30分钟前 + status: 'success', + showTime: false + }, + { + id: 'msg-3', + sender: 'expert', + type: 'text', + content: '可能是饲料问题或环境变化引起的,请描述一下具体情况。', + timestamp: now - 1200000, // 20分钟前 + showTime: false + }, + { + id: 'msg-4', + sender: 'user', + type: 'text', + content: '具体症状是...', + timestamp: now - 600000, // 10分钟前 + status: 'success', + showTime: true + }, + { + id: 'msg-5', + sender: 'expert', + type: 'text', + content: '明白了,建议您调整饲料配方。', + timestamp: now - 300000, // 5分钟前 + showTime: false } - - return msg - }) + ]; + + // 处理消息时间显示 + const processedMessages = this.processMessageTimes(mockMessages); + this.setData({ + messageList: processedMessages, + loading: false, + hasMore: false + }, () => { + // 滚动到底部 + this.scrollToBottom(true); + }); }, - // 初始化欢迎消息 - initWelcomeMessage: function() { + // 添加欢迎消息 + addWelcomeMessage: function() { const welcomeMessage = { id: 'welcome-' + Date.now(), sender: 'expert', type: 'text', - content: '您好,我是' + this.data.expertInfo.name + ',有什么可以帮您?', - timestamp: Date.now() - 60000, + content: `您好,我是${this.data.expertInfo.name},有什么可以帮您?`, + timestamp: Date.now(), showTime: true - } + }; this.setData({ - messageList: [welcomeMessage] - }) + messageList: [welcomeMessage], + loading: false + }, () => { + // 滚动到底部 + this.scrollToBottom(true); + }); }, - // 保存聊天记录 - saveChatHistory: function() { - const { storageKey, messageList } = this.data - if (messageList.length > 0) { - try { - wx.setStorageSync(storageKey, messageList) - } catch (e) { - console.error('保存聊天记录失败:', e) + // 处理消息时间显示 - 优化微信样式 + processMessageTimes: function(messages) { + if (!messages || messages.length === 0) return []; + + // 按时间排序(从早到晚) + const sortedMessages = [...messages].sort((a, b) => a.timestamp - b.timestamp); + + let lastShowTime = 0; + const processedMessages = []; + + 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); + + // 超过5分钟显示时间(微信默认5分钟) + msg.showTime = timeDiffMinutes >= this.data.timeInterval; + + if (msg.showTime) { + lastShowTime = msg.timestamp; + } + } + + processedMessages.push(msg); } + + return processedMessages; }, // 返回上一页 goBack: function() { - wx.navigateBack() + wx.navigateBack(); }, // 打电话 makePhoneCall: function() { - const phone = this.data.expertInfo.phone + const phone = this.data.expertInfo.phone; + if (!phone) { + wx.showToast({ + title: '暂无联系电话', + icon: 'none' + }); + return; + } + wx.makePhoneCall({ phoneNumber: phone, - fail: () => { + success: () => { + console.log('拨打电话成功'); + }, + fail: (err) => { + console.error('拨打电话失败:', err); wx.showToast({ title: '拨打失败', icon: 'none' - }) + }); } - }) + }); }, // 输入处理 onInput: function(e) { this.setData({ inputValue: e.detail.value - }) + }); + }, + + // 输入框获得焦点 + onInputFocus: function() { + this.setData({ + inputFocus: true + }); }, - // 发送文本消息 + // 输入框失去焦点 + 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 content = this.data.inputValue.trim(); + if (!content) return; + + console.log('发送文本消息:', content); const newMessage = { - id: 'msg-' + (++this.data.messageId), + id: 'msg-' + Date.now(), sender: 'user', type: 'text', content: content, timestamp: Date.now(), - status: 'sending' - } + status: 'sending', + showTime: this.shouldShowTime() + }; // 添加到消息列表 - this.addMessageToList(newMessage) + this.addMessageToList(newMessage); // 清空输入框 this.setData({ inputValue: '', inputFocus: false - }) + }); - // 模拟发送成功 - setTimeout(() => { - this.updateMessageStatus(newMessage.id, 'success') - - // 模拟专家回复 - setTimeout(() => { - this.receiveExpertReply() - }, 1000) - }, 500) + // 使用您的接口调用方式发送消息 + // http.sendTextMessage({ + // data: { + // expertId: this.data.currentExpertId, + // userId: this.data.userInfo.id, + // content: content, + // timestamp: Date.now() + // }, + // success: (res) => { + // console.log('发送消息成功:', res); + + // if (res.code === 0) { + // // 更新消息状态 + // this.updateMessageStatus(newMessage.id, 'success'); + + // // 模拟专家回复 + // setTimeout(() => { + // this.receiveExpertReply(); + // }, 1000 + Math.random() * 1000); + // } else { + // // 发送失败 + // this.updateMessageStatus(newMessage.id, 'error'); + // wx.showToast({ + // title: '发送失败', + // icon: 'none' + // }); + // } + // }, + // fail: (err) => { + // console.error('发送消息失败:', err); + // this.updateMessageStatus(newMessage.id, 'error'); + // wx.showToast({ + // title: '发送失败', + // icon: 'none' + // }); + // } + // }); + }, + + // 判断是否显示时间(基于时间间隔) + shouldShowTime: function() { + const { messageList, timeInterval } = this.data; + if (messageList.length === 0) return true; + + const lastMessage = messageList[messageList.length - 1]; + + // 如果上一条消息显示了时间,检查时间间隔 + if (lastMessage.showTime) { + const timeDiff = Date.now() - lastMessage.timestamp; + const timeDiffMinutes = timeDiff / (1000 * 60); + return timeDiffMinutes >= timeInterval; + } + + return true; }, // 添加消息到列表 addMessageToList: function(message) { - const { messageList } = this.data + const { messageList, timeInterval } = this.data; + + // 确保时间戳有效 + if (!message.timestamp || isNaN(message.timestamp) || message.timestamp <= 0) { + message.timestamp = Date.now(); + } - // 确定是否需要显示时间 - const lastMessage = messageList[messageList.length - 1] - const timeDiff = lastMessage ? message.timestamp - lastMessage.timestamp : 0 - message.showTime = timeDiff > 5 * 60 * 1000 || !lastMessage + if (messageList.length > 0) { + const lastMessage = messageList[messageList.length - 1]; + const timeDiff = message.timestamp - lastMessage.timestamp; + const timeDiffMinutes = timeDiff / (1000 * 60); + + // 根据时间间隔判断是否显示时间 + message.showTime = timeDiffMinutes >= timeInterval; + } else { + message.showTime = true; + } - messageList.push(message) + messageList.push(message); this.setData({ - messageList, - scrollToView: 'msg-' + message.id - }) + messageList + }, () => { + // 消息添加后滚动到底部 + this.scrollToBottom(); + }); }, // 更新消息状态 updateMessageStatus: function(messageId, status) { - const { messageList } = this.data - const index = messageList.findIndex(msg => msg.id === messageId) + const { messageList } = this.data; + const index = messageList.findIndex(msg => msg.id === messageId); if (index !== -1) { - messageList[index].status = status - this.setData({ messageList }) + messageList[index].status = status; + this.setData({ messageList }); } }, - // 接收专家回复 + // 接收专家回复 - 使用您的接口调用方式 receiveExpertReply: function() { + // 这里可以使用WebSocket或轮询获取新消息 + // 模拟专家回复 const replies = [ '收到您的消息,让我分析一下您说的情况。', '建议您提供更多细节,比如发病时间、具体症状等。', '根据描述,可能是饲料问题引起的,建议调整饲料配方。', '可以考虑添加一些维生素补充剂,改善食欲问题。', '最好能提供照片,这样我可以更准确地判断情况。' - ] + ]; - const randomReply = replies[Math.floor(Math.random() * replies.length)] + const randomReply = replies[Math.floor(Math.random() * replies.length)]; const newMessage = { id: 'exp-' + Date.now(), @@ -345,22 +685,86 @@ Page({ type: 'text', content: randomReply, timestamp: Date.now(), - showTime: false - } + showTime: this.shouldShowTime() + }; - this.addMessageToList(newMessage) + this.addMessageToList(newMessage); }, - // 切换输入模式 + // 切换输入模式(语音/键盘) switchInputMode: function() { - const newMode = this.data.inputMode === 'keyboard' ? 'voice' : 'keyboard' - const placeholder = newMode === 'voice' ? '按住说话' : '输入消息...' + const currentMode = this.data.inputMode; + const newMode = currentMode === 'keyboard' ? 'voice' : 'keyboard'; + + console.log('切换输入模式:', currentMode, '->', newMode); this.setData({ inputMode: newMode, - inputPlaceholder: placeholder, - inputFocus: newMode === 'keyboard' - }) + showMediaSheet: false + }); + + if (newMode === 'keyboard') { + // 切换到键盘模式 + setTimeout(() => { + this.setData({ + inputFocus: true, + inputPlaceholder: '请输入消息...' + }); + }, 100); + } else { + // 切换到语音模式 + this.setData({ + inputFocus: false, + inputPlaceholder: '按住说话' + }); + } + }, + + // 滚动事件处理 + 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.loading && this.data.hasMore) { + this.loadMoreMessages(); + } + }, + + // 加载更多消息 + loadMoreMessages: function() { + if (this.data.loading || !this.data.hasMore) return; + + this.setData({ + page: this.data.page + 1, + loading: true + }); + + // 加载更多聊天记录 + this.loadChatHistory(); + }, + + // 滚动到底部 + scrollToBottom: function(animate = true) { + if (this.data.isScrolling) return; + + this.setData({ + scrollAnimate: animate + }, () => { + // 设置一个足够大的值确保滚动到底部 + this.setData({ + scrollTop: 999999 + }); + }); }, // 显示多媒体选择面板 @@ -368,99 +772,147 @@ Page({ this.setData({ showMediaSheet: true, inputFocus: false - }) + }); }, // 隐藏多媒体选择面板 hideMediaActionSheet: function() { this.setData({ showMediaSheet: false - }) + }); + }, + + // 键盘高度变化 + onKeyboardHeightChange: function(res) { + console.log('键盘高度变化:', res.height); + if (res.height > 0) { + // 键盘弹出时隐藏多媒体面板 + this.setData({ showMediaSheet: false }); + + // 键盘弹出后滚动到底部 + setTimeout(() => { + this.scrollToBottom(); + }, 300); + } }, // 选择图片 chooseImage: function() { - this.hideMediaActionSheet() + this.hideMediaActionSheet(); wx.chooseImage({ count: 9, sizeType: ['compressed'], sourceType: ['album'], success: (res) => { - this.uploadImages(res.tempFilePaths) + console.log('选择图片成功:', res.tempFilePaths); + this.uploadImages(res.tempFilePaths); + }, + fail: (err) => { + console.error('选择图片失败:', err); + wx.showToast({ + title: '选择图片失败', + icon: 'none' + }); } - }) + }); }, // 拍照 takePhoto: function() { - this.hideMediaActionSheet() + this.hideMediaActionSheet(); wx.chooseImage({ count: 1, sizeType: ['compressed'], sourceType: ['camera'], success: (res) => { - this.uploadImages(res.tempFilePaths) + console.log('拍照成功:', res.tempFilePaths); + this.uploadImages(res.tempFilePaths); + }, + fail: (err) => { + console.error('拍照失败:', err); + wx.showToast({ + title: '拍照失败', + icon: 'none' + }); } - }) + }); }, // 选择视频 chooseVideo: function() { - this.hideMediaActionSheet() + this.hideMediaActionSheet(); wx.chooseVideo({ sourceType: ['album'], compressed: true, maxDuration: 60, success: (res) => { - this.uploadVideo(res.tempFilePath, res.thumbTempFilePath) + console.log('选择视频成功:', res); + this.uploadVideo(res.tempFilePath, res.thumbTempFilePath); + }, + fail: (err) => { + console.error('选择视频失败:', err); + wx.showToast({ + title: '选择视频失败', + icon: 'none' + }); } - }) + }); }, // 录制语音 recordAudio: function() { - this.hideMediaActionSheet() - this.startVoiceRecord() + this.hideMediaActionSheet(); + this.startVoiceRecord(); }, // 选择文件 chooseFile: function() { - this.hideMediaActionSheet() + this.hideMediaActionSheet(); wx.chooseMessageFile({ count: 1, type: 'all', success: (res) => { - const file = res.tempFiles[0] - this.uploadFile(file.path, file.name, file.size) + console.log('选择文件成功:', res); + const file = res.tempFiles[0]; + this.uploadFile(file.path, file.name, file.size); + }, + fail: (err) => { + console.error('选择文件失败:', 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') - }) + 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) + const fileName = `video_${Date.now()}.mp4`; + this.uploadFile(tempFilePath, fileName, 0, 'video', thumbPath); }, // 通用文件上传 uploadFile: function(tempFilePath, fileName, fileSize = 0, type = 'file', thumbPath = '') { + console.log('开始上传文件:', { fileName, type, fileSize }); + // 获取文件扩展名 - const extension = fileName.split('.').pop().toLowerCase() + const extension = fileName.split('.').pop().toLowerCase(); // 创建消息 - const messageId = 'file-' + Date.now() + const messageId = 'file-' + Date.now(); const message = { id: messageId, sender: 'user', @@ -472,48 +924,54 @@ Page({ extension: extension, timestamp: Date.now(), status: 'uploading', - progress: 0 - } + progress: 0, + showTime: this.shouldShowTime() + }; - this.addMessageToList(message) + this.addMessageToList(message); // 模拟上传过程 - let progress = 0 + this.simulateUpload(messageId, type); + }, + + // 模拟上传过程 + simulateUpload: function(messageId, type) { + let progress = 0; const uploadInterval = setInterval(() => { - progress += Math.random() * 20 + 10 + progress += Math.random() * 20 + 10; if (progress >= 100) { - progress = 100 - clearInterval(uploadInterval) + progress = 100; + clearInterval(uploadInterval); setTimeout(() => { - this.updateMessageStatus(messageId, 'success') + this.updateMessageStatus(messageId, 'success'); // 清除进度信息 - const { messageList } = this.data - const index = messageList.findIndex(msg => msg.id === messageId) + const { messageList } = this.data; + const index = messageList.findIndex(msg => msg.id === messageId); if (index !== -1) { - delete messageList[index].progress - this.setData({ messageList }) + delete messageList[index].progress; + this.setData({ messageList }); // 模拟专家回复 if (type === 'image' || type === 'video') { setTimeout(() => { - this.receiveMediaReply(type) - }, 800) + this.receiveMediaReply(type); + }, 800); } } - }, 200) + }, 200); } // 更新进度 - const { messageList } = this.data - const index = messageList.findIndex(msg => msg.id === messageId) + 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 }) + messageList[index].progress = Math.min(progress, 100); + this.setData({ messageList }); } - }, 100) + }, 100); }, // 接收媒体回复 @@ -522,16 +980,16 @@ Page({ '照片收到了,牛的状况看起来确实不太理想。', '从照片看,饲养环境需要改善一下。', '图片清晰,我可以更准确地判断问题了。' - ] + ]; const videoReplies = [ '视频看到了,动物的精神状态需要关注。', '从视频可以观察到更多细节,这很有帮助。', '视频内容很有价值,让我了解了具体情况。' - ] + ]; - const replies = type === 'image' ? imageReplies : videoReplies - const randomReply = replies[Math.floor(Math.random() * replies.length)] + const replies = type === 'image' ? imageReplies : videoReplies; + const randomReply = replies[Math.floor(Math.random() * replies.length)]; const newMessage = { id: 'exp-media-' + Date.now(), @@ -539,56 +997,71 @@ Page({ type: 'text', content: randomReply, timestamp: Date.now(), - showTime: false - } + showTime: this.shouldShowTime() + }; - this.addMessageToList(newMessage) + this.addMessageToList(newMessage); }, // 开始语音录制 startVoiceRecord: function(e) { - this.setData({ - isRecording: true, - recordingTime: 0, - recordingTip: '上滑取消录音' - }) + if (e && e.touches && e.touches[0]) { + this.setData({ + recordStartY: e.touches[0].clientY + }); + } - // 开始录音 this.recordManager.start({ - duration: 60000, // 最长60秒 + duration: 60000, sampleRate: 44100, numberOfChannels: 1, encodeBitRate: 192000, - format: 'mp3' - }) - - // 开始计时 - const timer = setInterval(() => { - const time = this.data.recordingTime + 1 - this.setData({ recordingTime: time }) - }, 1000) + format: 'aac' + }); + }, + + // 语音录制触摸移动 + onVoiceTouchMove: function(e) { + if (!this.data.isRecording) return; - this.setData({ recordingTimer: timer }) + if (e.touches && e.touches[0]) { + const currentY = e.touches[0].clientY; + const startY = this.data.recordStartY; + const deltaY = startY - currentY; + + const isCanceling = deltaY > 50; + + if (isCanceling !== this.data.isCanceling) { + this.setData({ + isCanceling: isCanceling, + recordingTip: isCanceling ? '松开取消' : '松开 发送', + voiceTip: isCanceling ? '松开取消' : '按住 说话' + }); + } + } }, // 结束语音录制 endVoiceRecord: function() { if (this.data.isRecording) { - this.recordManager.stop() - - if (this.data.recordingTimer) { - clearInterval(this.data.recordingTimer) - } - + this.recordManager.stop(); + } + }, + + // 取消语音录制 + cancelVoiceRecord: function() { + if (this.data.isRecording) { this.setData({ - isRecording: false, - recordingTime: 0 - }) + isCanceling: true + }); + this.recordManager.stop(); } }, // 发送语音消息 sendAudioMessage: function(tempFilePath, duration) { + console.log('发送语音消息:', { duration }); + const message = { id: 'audio-' + Date.now(), sender: 'user', @@ -596,14 +1069,15 @@ Page({ content: tempFilePath, duration: duration, timestamp: Date.now(), - status: 'sending' - } + status: 'sending', + showTime: this.shouldShowTime() + }; - this.addMessageToList(message) + this.addMessageToList(message); // 模拟发送成功 setTimeout(() => { - this.updateMessageStatus(message.id, 'success') + this.updateMessageStatus(message.id, 'success'); // 模拟专家回复 setTimeout(() => { @@ -613,35 +1087,42 @@ Page({ type: 'text', content: '语音收到了,我会仔细听取分析。', timestamp: Date.now(), - showTime: false - } - this.addMessageToList(reply) - }, 1500) - }, 800) + showTime: this.shouldShowTime() + }; + this.addMessageToList(reply); + }, 1500); + }, 800); }, // 预览图片 previewImage: function(e) { - const url = e.currentTarget.dataset.url + const url = e.currentTarget.dataset.url; wx.previewImage({ current: url, - urls: [url] - }) + urls: [url], + fail: (err) => { + console.error('预览图片失败:', err); + wx.showToast({ + title: '预览失败', + icon: 'none' + }); + } + }); }, // 下载文件 downloadFile: function(e) { - const url = e.currentTarget.dataset.url - wx.showLoading({ title: '下载中...' }) + const url = e.currentTarget.dataset.url; + wx.showLoading({ title: '下载中...' }); wx.downloadFile({ url: url, success: (res) => { - wx.hideLoading() + wx.hideLoading(); wx.showToast({ title: '下载成功', icon: 'success' - }) + }); // 保存到相册(如果是图片) if (url.match(/\.(jpg|jpeg|png|gif)$/i)) { @@ -651,103 +1132,110 @@ Page({ wx.showToast({ title: '已保存到相册', icon: 'success' - }) + }); + }, + fail: (err) => { + console.error('保存到相册失败:', err); } - }) + }); } }, - fail: () => { - wx.hideLoading() + fail: (err) => { + wx.hideLoading(); + console.error('下载失败:', err); wx.showToast({ title: '下载失败', icon: 'none' - }) + }); } - }) + }); }, - // 键盘高度变化 - onKeyboardHeightChange: function(res) { - if (res.height > 0) { - // 键盘弹出时隐藏多媒体面板 - this.setData({ showMediaSheet: false }) - - // 滚动到底部 - setTimeout(() => { - this.scrollToBottom() - }, 100) - } - }, - - // 滚动到底部 - scrollToBottom: function() { - if (this.data.messageList.length > 0) { - const lastMessage = this.data.messageList[this.data.messageList.length - 1] - this.setData({ - scrollToView: lastMessage.id - }) - } - }, - - // 格式化时间(微信样式) + // 优化:微信样式时间格式化 formatTime: function(timestamp) { - const now = new Date() - const date = new Date(timestamp) - const diff = now - date - - // 今天 - if (date.toDateString() === now.toDateString()) { - return this.formatMessageTime(timestamp) + if (!timestamp || timestamp <= 0) { + console.warn('无效的时间戳:', timestamp); + return ''; } - // 昨天 - const yesterday = new Date(now) - yesterday.setDate(yesterday.getDate() - 1) - if (date.toDateString() === yesterday.toDateString()) { - return '昨天 ' + this.formatMessageTime(timestamp) + const timeNum = Number(timestamp); + if (isNaN(timeNum)) { + return ''; } - // 一周内 - if (diff < 7 * 24 * 60 * 60 * 1000) { - const weekDays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'] - return weekDays[date.getDay()] + ' ' + this.formatMessageTime(timestamp) + const date = new Date(timeNum); + if (isNaN(date.getTime())) { + return ''; } - // 今年内 - if (date.getFullYear() === now.getFullYear()) { - return `${date.getMonth() + 1}月${date.getDate()}日 ${this.formatMessageTime(timestamp)}` - } + const now = new Date(); + const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); + const yesterday = new Date(today.getTime() - 86400000); - // 更早 - return `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()} ${this.formatMessageTime(timestamp)}` - }, - - // 格式化消息时间(HH:mm) - formatMessageTime: function(timestamp) { - const date = new Date(timestamp) - const hours = date.getHours().toString().padStart(2, '0') - const minutes = date.getMinutes().toString().padStart(2, '0') - return `${hours}:${minutes}` + const msgDate = new Date(date.getFullYear(), date.getMonth(), date.getDate()); + + const hours = date.getHours().toString().padStart(2, '0'); + const minutes = date.getMinutes().toString().padStart(2, '0'); + const timeStr = `${hours}:${minutes}`; + + // 今天 + if (msgDate.getTime() === today.getTime()) { + // 小于1分钟显示"刚刚" + const diffMinutes = (now.getTime() - date.getTime()) / (1000 * 60); + if (diffMinutes < 1) { + return '刚刚'; + } + // 小于1小时显示"X分钟前" + else if (diffMinutes < 60) { + return `${Math.floor(diffMinutes)}分钟前`; + } + // 今天超过1小时显示时间 + else { + return timeStr; + } + } + // 昨天 + else if (msgDate.getTime() === yesterday.getTime()) { + return `昨天 ${timeStr}`; + } + // 本周内(7天内) + else { + const diffDays = (today.getTime() - msgDate.getTime()) / (1000 * 60 * 60 * 24); + if (diffDays < 7) { + const weekDays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']; + return `${weekDays[date.getDay()]} ${timeStr}`; + } + // 今年内 + else if (date.getFullYear() === now.getFullYear()) { + const month = date.getMonth() + 1; + const day = date.getDate(); + return `${month}月${day}日 ${timeStr}`; + } + // 更早 + else { + return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${timeStr}`; + } + } }, // 格式化文件大小 formatFileSize: function(bytes) { - if (bytes === 0) return '未知大小' + if (bytes === 0 || !bytes) return '未知大小'; - const units = ['B', 'KB', 'MB', 'GB'] - let size = bytes - let unitIndex = 0 + const units = ['B', 'KB', 'MB', 'GB']; + let size = bytes; + let unitIndex = 0; while (size >= 1024 && unitIndex < units.length - 1) { - size /= 1024 - unitIndex++ + size /= 1024; + unitIndex++; } - return size.toFixed(1) + units[unitIndex] + return size.toFixed(1) + units[unitIndex]; }, // 阻止事件冒泡 stopPropagation: function() { // 空函数,仅用于阻止事件冒泡 } -}) \ No newline at end of file +}); \ No newline at end of file diff --git a/pagesA/pages/expertChat/expertChat.wxml b/pagesA/pages/expertChat/expertChat.wxml index 48c6c57..e0dde15 100644 --- a/pagesA/pages/expertChat/expertChat.wxml +++ b/pagesA/pages/expertChat/expertChat.wxml @@ -1,3 +1,4 @@ + @@ -9,10 +10,12 @@ - {{expertInfo.name}} - - - {{expertInfo.online ? '在线' : '离线'}} + + {{expertInfo.name}} + + + {{expertInfo.online ? '在线' : '离线'}} + @@ -25,17 +28,16 @@ - - - - - - - {{expertInfo.name}} - {{expertInfo.title}} · {{expertInfo.expertise}} - 您好,我是{{expertInfo.name}},很高兴为您服务! - - + {{todayDate}} @@ -43,128 +45,145 @@ - + - {{formatTime(item.timestamp)}} + + {{formatTime(item.timestamp)}} + - + - + {{item.content}} + - - + + + - - - - + + + + + - - + + - - - - - + + + - {{item.duration}}'' + {{item.duration || 0}}'' + - + - - {{item.extension}} + {{item.fileName}} {{formatFileSize(item.fileSize)}} - + - - {{formatMessageTime(item.timestamp)}} - + - - - - - - - - + - + {{item.content}} + - - + + - {{item.progress}}% + - - - - + + + + - {{item.progress}}% + - + - - - - - + + + - {{item.duration}}'' + {{item.duration || 0}}'' - + + - + - - {{item.extension}} + {{item.fileName}} @@ -172,63 +191,104 @@ - {{item.progress}}% + - - {{formatMessageTime(item.timestamp)}} - + + + + + 加载中... + + + + + + 暂无聊天记录,开始咨询吧 + - - - + + + + + + + + - - - - - - + + + + + + + - - - - @@ -239,58 +299,74 @@ 发送内容 - + + + + 最多可选择9张照片 + - - - - + + + {{recordingTip}} - {{recordingTime}}s + + + + + {{recordingTime}}s + \ No newline at end of file diff --git a/pagesA/pages/expertChat/expertChat.wxss b/pagesA/pages/expertChat/expertChat.wxss index 9f9d566..b7fe11d 100644 --- a/pagesA/pages/expertChat/expertChat.wxss +++ b/pagesA/pages/expertChat/expertChat.wxss @@ -1,928 +1,1009 @@ +/* pages/consult/consult.wxss */ + /* 页面整体样式 */ .consult-page { - width: 100vw; - height: 100vh; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - display: flex; - flex-direction: column; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif; - } - - /* 头部样式 */ - .consult-header { - background: rgba(255, 255, 255, 0.95); - backdrop-filter: blur(20px); - padding: 15rpx 30rpx; - border-bottom: 1rpx solid rgba(0, 0, 0, 0.1); - position: relative; - z-index: 100; - box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); - } - - .header-content { - display: flex; - align-items: center; - justify-content: space-between; - height: 90rpx; - } - - .header-left { - flex: 1; - display: flex; - justify-content: flex-start; - } - - .header-center { - flex: 2; - display: flex; - flex-direction: column; - align-items: center; - } - - .header-right { - flex: 1; - display: flex; - justify-content: flex-end; - } - - .back-btn { - width: 70rpx; - height: 70rpx; - border-radius: 50%; - border: none; - background: rgba(0, 0, 0, 0.05); - display: flex; - align-items: center; - justify-content: center; - transition: all 0.3s; - } - - .back-btn:active { - background: rgba(0, 0, 0, 0.1); - transform: scale(0.95); - } - - .back-icon { - width: 30rpx; - height: 30rpx; - } - - .expert-name { - font-size: 34rpx; - font-weight: 600; - color: #333; - margin-bottom: 5rpx; - } - - .expert-status { - display: flex; - align-items: center; - justify-content: center; - } - - .status-dot { - width: 14rpx; - height: 14rpx; - border-radius: 50%; - margin-right: 8rpx; - } - - .status-dot.online { - background: #07c160; - box-shadow: 0 0 10rpx rgba(7, 193, 96, 0.5); - } - - .status-dot.offline { - background: #999; - } - - .status-text { - font-size: 24rpx; - color: #666; - } - - .header-action-btn { - width: 70rpx; - height: 70rpx; - border-radius: 50%; - border: none; - background: rgba(0, 0, 0, 0.05); - display: flex; - align-items: center; - justify-content: center; - transition: all 0.3s; - } - - .header-action-btn:active { - background: rgba(0, 0, 0, 0.1); - transform: scale(0.95); - } - - .header-action-icon { - width: 32rpx; - height: 32rpx; - } - - /* 聊天容器 */ - .chat-container { - flex: 1; - padding: 30rpx 20rpx 0; - background: linear-gradient(180deg, #f5f7fa 0%, #f0f2f5 100%); - overflow-y: auto; - position: relative; - } - - /* 欢迎语 */ - .system-welcome { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 60rpx 40rpx; - text-align: center; - background: white; - border-radius: 30rpx; - margin: 0 auto 40rpx; - max-width: 80%; - box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.08); - animation: fadeInUp 0.6s ease; - } - - @keyframes fadeInUp { - from { - opacity: 0; - transform: translateY(50rpx); - } - to { - opacity: 1; - transform: translateY(0); - } - } - - .welcome-avatar { - width: 120rpx; - height: 120rpx; - border-radius: 50%; - overflow: hidden; - margin-bottom: 20rpx; - border: 4rpx solid white; - box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.1); - } - - .welcome-avatar image { - width: 100%; - height: 100%; - } - - .welcome-name { - font-size: 34rpx; - font-weight: 600; - color: #333; - margin-bottom: 8rpx; - } - - .welcome-title { - font-size: 26rpx; - color: #666; - margin-bottom: 25rpx; - } - - .welcome-tip { - font-size: 28rpx; - color: #4caf50; - line-height: 1.4; - } - - /* 日期分隔线 */ - .date-divider { - display: flex; - justify-content: center; - margin: 40rpx 0; - } - - .date-text { - background: rgba(0, 0, 0, 0.05); - padding: 8rpx 24rpx; - border-radius: 20rpx; - font-size: 24rpx; - color: #999; - font-weight: 500; - } - - /* 时间分隔 */ - .time-divider { - display: flex; - justify-content: center; - margin: 30rpx 0; - animation: fadeIn 0.3s ease; - } - - @keyframes fadeIn { - from { - opacity: 0; - } - to { - opacity: 1; - } - } - - .time-divider text { - background: rgba(0, 0, 0, 0.05); - padding: 6rpx 20rpx; - border-radius: 15rpx; - font-size: 22rpx; - color: #999; - } - - /* 消息项 */ - .message-item { - display: flex; - margin-bottom: 40rpx; - animation: slideIn 0.3s ease; - } - - @keyframes slideIn { - from { - opacity: 0; - transform: translateY(20rpx); - } - to { - opacity: 1; - transform: translateY(0); - } - } - - .message-left { - justify-content: flex-start; - } - - .message-right { - justify-content: flex-end; - } - - .message-avatar { - width: 80rpx; - height: 80rpx; - border-radius: 10rpx; - overflow: hidden; - flex-shrink: 0; - } - - .message-left .message-avatar { - margin-right: 20rpx; - } - - .message-right .message-avatar { - margin-left: 20rpx; - } - - .message-avatar image { - width: 100%; - height: 100%; - } - - .message-content-wrapper { - max-width: 65%; - display: flex; - flex-direction: column; - } - - .message-left .message-content-wrapper { - align-items: flex-start; - } - - .message-right .message-content-wrapper { - align-items: flex-end; - } - - /* 消息气泡 */ - .message-bubble { - padding: 20rpx 25rpx; - border-radius: 20rpx; - position: relative; - word-break: break-word; - line-height: 1.5; - transition: all 0.3s; - } - - .message-bubble-left { - background: white; - border-radius: 0 20rpx 20rpx 20rpx; - box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08); - } - - .message-bubble-right { - background: linear-gradient(135deg, #95ec69 0%, #5ac725 100%); - color: white; - border-radius: 20rpx 0 20rpx 20rpx; - box-shadow: 0 4rpx 20rpx rgba(90, 199, 37, 0.2); - } - - .message-text { - font-size: 30rpx; - line-height: 1.5; - } - - .message-bubble-left .message-text { - color: #333; - } - - .message-bubble-right .message-text { - color: white; - } - - /* 消息时间 */ - .message-time { - font-size: 22rpx; - color: #999; - margin-top: 8rpx; - text-align: center; - } - - /* 消息状态 */ - .message-status { - margin-bottom: 10rpx; - } - - .status-icon { - width: 28rpx; + width: 100vw; + height: 100vh; + background: #f5f5f5; + display: flex; + flex-direction: column; + font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif; +} + +/* ========== 头部样式 ========== */ +.consult-header { + background: #ffffff; + border-bottom: 1px solid #e5e5e5; + position: relative; + z-index: 1000; + box-shadow: 0 1px 6px rgba(0, 0, 0, 0.05); +} + +.header-content { + display: flex; + align-items: center; + padding: 12rpx 24rpx; + height: 96rpx; +} + +.header-left { + width: 80rpx; +} + +.header-center { + flex: 1; + display: flex; + justify-content: center; +} + +.header-right { + width: 80rpx; +} + +.back-btn { + width: 72rpx; + height: 72rpx; + border: none; + background: transparent; + padding: 0; + margin: 0; + line-height: 1; + border-radius: 50%; + transition: background-color 0.2s; +} + +.back-btn:active { + background-color: rgba(0, 0, 0, 0.05); +} + +.back-icon { + width: 36rpx; + height: 36rpx; +} + +.expert-info { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.expert-name { + font-size: 32rpx; + font-weight: 600; + color: #000000; + line-height: 44rpx; + margin-bottom: 4rpx; +} + +.expert-status { + display: flex; + align-items: center; + justify-content: center; +} + +.status-dot { + width: 16rpx; + height: 16rpx; + border-radius: 50%; + margin-right: 8rpx; +} + +.status-dot.online { + background: #07c160; + animation: pulse 2s infinite; +} + +.status-dot.offline { + background: #cccccc; +} + +@keyframes pulse { + 0% { + box-shadow: 0 0 0 0 rgba(7, 193, 96, 0.4); + } + 70% { + box-shadow: 0 0 0 8rpx rgba(7, 193, 96, 0); + } + 100% { + box-shadow: 0 0 0 0 rgba(7, 193, 96, 0); + } +} + +.status-text { + font-size: 24rpx; + color: #666666; +} + +.header-action-btn { + width: 72rpx; + height: 72rpx; + border: none; + background: transparent; + padding: 0; + margin: 0; + line-height: 1; + border-radius: 50%; + transition: background-color 0.2s; +} + +.header-action-btn:active { + background-color: rgba(0, 0, 0, 0.05); +} + +.header-action-icon { + width: 40rpx; + height: 40rpx; +} + +/* ========== 聊天容器 ========== */ +.chat-container { + flex: 1; + padding: 20rpx 0 0; + background: #f5f5f5; + overflow-y: auto; + position: relative; +} + +/* 日期分隔线 */ +.date-divider { + display: flex; + justify-content: center; + margin: 40rpx 0 30rpx; +} + +.date-text { + background: rgba(0, 0, 0, 0.1); + padding: 8rpx 32rpx; + border-radius: 100rpx; + font-size: 24rpx; + color: #ffffff; + font-weight: 400; + background-color: #d8d8d8; +} + +/* 微信样式时间分隔 */ +.time-divider { + display: flex; + justify-content: center; + align-items: center; + margin: 40rpx 0; + padding: 0 30rpx; +} + +.time-line { + flex: 1; + height: 1rpx; + background: rgba(0, 0, 0, 0.1); + margin: 0 20rpx; +} + +.time-text { + display: inline-block; + padding: 4rpx 16rpx; + border-radius: 20rpx; + font-size: 22rpx; + color: #999999; + background-color: rgba(0, 0, 0, 0.05); + line-height: 1.5; + text-align: center; + word-break: keep-all; + white-space: nowrap; +} + +/* 消息项 - 修复对齐 */ +.message-item { + display: flex; + margin-bottom: 24rpx; + padding: 0 30rpx; + opacity: 0; + animation: fadeIn 0.3s ease forwards; + align-items: flex-start; + position: relative; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(10rpx); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.message-left { + justify-content: flex-start; +} + +.message-right { + justify-content: flex-end; +} + +/* 头像 - 修复对齐 */ +.message-avatar { + width: 80rpx; + height: 80rpx; + border-radius: 8rpx; + overflow: hidden; + flex-shrink: 0; + background: #ffffff; + border: 1rpx solid #f0f0f0; + position: relative; + z-index: 1; +} + +.message-left .message-avatar { + margin-right: 16rpx; +} + +.message-right .message-avatar { + margin-left: 16rpx; +} + +.avatar-img { + width: 100%; + height: 100%; + object-fit: cover; +} + +/* 消息内容包装器 */ +.message-content-wrapper { + max-width: 480rpx; + position: relative; + display: flex; + flex-direction: column; +} + +.message-left .message-content-wrapper { + align-items: flex-start; +} + +.message-right .message-content-wrapper { + align-items: flex-end; +} + +/* 消息气泡通用样式 */ +.message-bubble { + position: relative; + padding: 16rpx 20rpx; + word-break: break-word; + box-sizing: border-box; + min-height: 80rpx; /* 与头像高度一致 */ + display: flex; + align-items: center; + justify-content: center; +} + +/* 左侧气泡(专家) */ +.bubble-left { + background: #ffffff; + border-radius: 10rpx; + border-top-left-radius: 4rpx; + box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05); +} + +/* 右侧气泡(用户) */ +.bubble-right { + background: #95ec69; + border-radius: 10rpx; + border-top-right-radius: 4rpx; + box-shadow: 0 2rpx 8rpx rgba(149, 236, 105, 0.2); +} + +/* 气泡箭头 - 固定在头像中间位置 */ +.message-arrow { + position: absolute; + width: 0; + height: 0; + border-style: solid; + border-width: 12rpx; + top: 50%; + margin-top: -12rpx; /* 箭头高度的一半 */ + z-index: 2; +} + +.arrow-left { + left: -24rpx; + border-color: transparent #ffffff transparent transparent; +} + +.arrow-right { + right: -24rpx; + border-color: transparent transparent transparent #95ec69; +} + +/* 文本消息 */ +.message-text { + font-size: 32rpx; + color: #000000; + line-height: 1.4; +} + +.bubble-right .message-text { + color: #000000; +} + +/* 媒体消息通用样式 */ +.media-bubble { + position: relative; + border-radius: 10rpx; + overflow: hidden; + box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05); + background: #ffffff; + min-height: 80rpx; /* 与头像高度一致 */ + display: flex; + align-items: center; + justify-content: center; +} + +.message-left .media-bubble { + border-top-left-radius: 4rpx; +} + +.message-right .media-bubble { + border-top-right-radius: 4rpx; +} + +/* 图片消息 */ +.message-image { + width: 280rpx; + height: 280rpx; + display: block; +} + +/* 视频消息 */ +.message-video { + width: 280rpx; + height: 280rpx; + background: #000000; +} + +.video-play-overlay { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 80rpx; + height: 80rpx; + border-radius: 50%; + background: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; +} + +.play-icon { + width: 40rpx; + height: 40rpx; + margin-left: 4rpx; +} + +/* 语音消息 */ +.message-audio { + min-width: 240rpx; + padding: 20rpx; + display: flex; + align-items: center; + min-height: 80rpx; +} + +.audio-icon-left, +.audio-icon-right { + width: 32rpx; + height: 32rpx; + flex-shrink: 0; +} + +.audio-icon-left { + margin-right: 16rpx; +} + +.audio-icon-right { + margin-left: 16rpx; +} + +.audio-content { + flex: 1; + display: flex; + align-items: center; + justify-content: space-between; +} + +.audio-wave { + display: flex; + align-items: flex-end; + height: 28rpx; + margin-right: 16rpx; +} + +.wave-bar { + width: 4rpx; + margin: 0 2rpx; + background: currentColor; + border-radius: 2rpx; + animation: wave 1s ease-in-out infinite; +} + +@keyframes wave { + 0%, 100% { + height: 12rpx; + } + 50% { height: 28rpx; } - - /* 图片消息 */ - .message-image { - max-width: 300rpx; - max-height: 400rpx; - border-radius: 12rpx; - display: block; - } - - /* 视频消息 */ - .message-video { - width: 300rpx; - height: 200rpx; - border-radius: 12rpx; - background: #000; - } - - .video-play-btn { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 80rpx; - height: 80rpx; - border-radius: 50%; - background: rgba(0, 0, 0, 0.6); - display: flex; - align-items: center; - justify-content: center; - } - - .video-play-btn image { - width: 40rpx; - height: 40rpx; - } - - /* 语音消息 */ - .message-audio { - display: flex; - align-items: center; - padding: 25rpx; - } - - .audio-icon-left { - width: 40rpx; - height: 40rpx; - margin-right: 20rpx; - } - - .audio-icon-right { - width: 40rpx; - height: 40rpx; - margin-left: 20rpx; - } - - .audio-content { - display: flex; - align-items: center; - } - - .audio-wave { - display: flex; - align-items: flex-end; - height: 40rpx; - margin-right: 15rpx; - } - - .wave-bar { - width: 4rpx; - margin: 0 2rpx; - background: currentColor; - animation: wave 1.2s ease-in-out infinite; - } - - .wave-bar:nth-child(1) { - height: 20rpx; - animation-delay: 0s; - } - - .wave-bar:nth-child(2) { - height: 30rpx; - animation-delay: 0.2s; - } - - .wave-bar:nth-child(3) { - height: 40rpx; - animation-delay: 0.4s; - } - - .wave-bar:nth-child(4) { - height: 30rpx; - animation-delay: 0.6s; - } - - .wave-bar:nth-child(5) { +} + +.audio-duration { + font-size: 26rpx; + font-weight: 500; + color: #000000; +} + +/* 文件消息 */ +.message-file { + min-width: 280rpx; + padding: 20rpx; + display: flex; + align-items: center; + min-height: 80rpx; +} + +.file-icon-box { + width: 56rpx; + height: 72rpx; + margin-right: 16rpx; + position: relative; + flex-shrink: 0; +} + +.file-icon { + width: 100%; + height: 100%; +} + +.file-info { + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + overflow: hidden; +} + +.file-name { + font-size: 28rpx; + font-weight: 500; + color: #000000; + margin-bottom: 6rpx; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.file-size { + font-size: 24rpx; + color: #666666; +} + +/* 上传进度 */ +.upload-progress { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + border-radius: inherit; +} + +.progress-circle { + width: 80rpx; + height: 80rpx; + border-radius: 50%; + border: 6rpx solid rgba(255, 255, 255, 0.3); + border-top-color: #ffffff; + animation: spin 1s linear infinite; + display: flex; + align-items: center; + justify-content: center; +} + +@keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +.progress-text { + font-size: 20rpx; + color: #ffffff; + font-weight: 600; +} + +/* 加载更多提示 */ +.load-more-tip { + display: flex; + justify-content: center; + align-items: center; + padding: 30rpx 0; + color: #999999; + font-size: 24rpx; +} + +/* 空状态提示 */ +.empty-tip { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 100rpx 0; + color: #999999; +} + +.empty-icon { + width: 200rpx; + height: 200rpx; + margin-bottom: 30rpx; + opacity: 0.6; +} + +.empty-text { + font-size: 28rpx; + color: #999999; +} + +/* ========== 输入区域 ========== */ +.input-section { + background: #ffffff; + border-top: 1rpx solid #e5e5e5; + padding: 20rpx 30rpx; + position: relative; + z-index: 100; + box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05); +} + +/* 语音输入面板 */ +.voice-input-panel { + display: flex; + align-items: center; + gap: 20rpx; + height: 80rpx; +} + +/* 文字输入面板 */ +.text-input-panel { + display: flex; + align-items: center; + gap: 20rpx; + height: 80rpx; +} + +/* 语音输入按钮(切换按钮) */ +.voice-input-btn { + width: 80rpx; + height: 80rpx; + border-radius: 50%; + border: none; + background: transparent; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s; + padding: 0; + margin: 0; + line-height: 1; + flex-shrink: 0; +} + +.voice-input-btn:active { + background: rgba(0, 0, 0, 0.05); +} + +.voice-btn-icon { + width: 40rpx; + height: 40rpx; +} + +/* 输入框包装器 */ +.input-wrapper { + flex: 1; + position: relative; + background: #f5f5f5; + border-radius: 40rpx; + height: 80rpx; + display: flex; + align-items: center; + transition: all 0.2s; + overflow: hidden; + min-width: 0; +} + +/* 语音输入模式下的输入框包装器 */ +.voice-input-panel .input-wrapper { + padding: 0; + background: #f5f5f5; +} + +/* 文字输入模式下的输入框包装器 */ +.text-input-panel .input-wrapper { + padding: 0 30rpx; +} + +.input-wrapper:active { + background: #e8e8e8; +} + +/* 语音输入按钮 */ +.voice-record-btn { + width: 100%; + height: 100%; + background: transparent; + border: none; + border-radius: 40rpx; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + transition: all 0.2s; + padding: 0; + margin: 0; +} + +.voice-record-btn:active { + background: #e0e0e0; +} + +.mic-icon { + width: 36rpx; + height: 36rpx; + margin-bottom: 6rpx; +} + +.voice-tip { + font-size: 24rpx; + color: #666666; +} + +/* 文字输入框 */ +.chat-input { + flex: 1; + height: 100%; + font-size: 32rpx; + color: #000000; + line-height: 80rpx; + min-width: 0; +} + +.input-placeholder { + color: #999999; + font-size: 30rpx; +} + +.input-actions { + position: absolute; + right: 20rpx; + top: 50%; + transform: translateY(-50%); +} + +.clear-btn { + width: 40rpx; + height: 40rpx; + border: none; + background: transparent; + padding: 0; + margin: 0; + line-height: 1; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; +} + +.clear-btn:active { + background: rgba(0, 0, 0, 0.05); +} + +.clear-icon { + width: 32rpx; + height: 32rpx; +} + +/* 更多按钮 */ +.more-btn { + width: 80rpx; + height: 80rpx; + border-radius: 50%; + border: none; + background: transparent; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s; + padding: 0; + margin: 0; + line-height: 1; + flex-shrink: 0; +} + +.more-btn:active { + background: rgba(0, 0, 0, 0.05); +} + +.more-icon { + width: 36rpx; + height: 36rpx; +} + +/* 发送按钮 */ +.send-btn { + background: #07c160; + width: 120rpx; + border-radius: 40rpx; + height: 80rpx; + border: none; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s; + padding: 0; + margin: 0; + line-height: 1; + flex-shrink: 0; +} + +.send-btn:active { + background: #06ad56; + transform: scale(0.98); +} + +.send-text { + font-size: 28rpx; + color: #ffffff; + font-weight: 500; +} + +/* ========== 多媒体选择面板 ========== */ +.media-action-sheet { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: flex-end; + justify-content: center; + z-index: 2000; + animation: fadeIn 0.3s ease; +} + +.media-sheet-content { + width: 100%; + background: #ffffff; + border-radius: 40rpx 40rpx 0 0; + padding: 40rpx 30rpx calc(80rpx + env(safe-area-inset-bottom)); + animation: slideUp 0.3s ease; + box-sizing: border-box; +} + +@keyframes slideUp { + from { + transform: translateY(100%); + } + to { + transform: translateY(0); + } +} + +.media-sheet-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 40rpx; + padding: 0 10rpx; +} + +.sheet-title { + font-size: 32rpx; + font-weight: 600; + color: #000000; +} + +.close-sheet-btn { + width: 60rpx; + height: 60rpx; + border-radius: 50%; + border: none; + background: #f5f5f5; + display: flex; + align-items: center; + justify-content: center; + padding: 0; + margin: 0; + line-height: 1; +} + +.close-sheet-btn image { + width: 24rpx; + height: 24rpx; +} + +.media-options-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 40rpx 30rpx; + margin-bottom: 40rpx; +} + +.media-option { + display: flex; + flex-direction: column; + align-items: center; + border: none; + background: transparent; + padding: 0; + margin: 0; + line-height: 1; +} + +.option-icon-box { + width: 120rpx; + height: 120rpx; + border-radius: 30rpx; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 16rpx; + transition: transform 0.2s; +} + +.media-option:active .option-icon-box { + transform: scale(0.95); +} + +.photo-icon { + background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%); +} + +.camera-icon { + background: linear-gradient(135deg, #f3e5f5 0%, #e1bee7 100%); +} + +.video-icon { + background: linear-gradient(135deg, #e8f5e9 0%, #c8e6c9 100%); +} + +.audio-icon { + background: linear-gradient(135deg, #fff3e0 0%, #ffe0b2 100%); +} + +.file-icon { + background: linear-gradient(135deg, #f5f5f5 0%, #e0e0e0 100%); +} + +.option-icon-box image { + width: 60rpx; + height: 60rpx; +} + +.option-text { + font-size: 26rpx; + color: #666666; +} + +.sheet-bottom { + text-align: center; +} + +.bottom-tip { + font-size: 24rpx; + color: #999999; +} + +/* ========== 录音模态框 ========== */ +.recording-modal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.7); + display: flex; + align-items: center; + justify-content: center; + z-index: 3000; + animation: fadeIn 0.2s ease; +} + +.recording-box { + background: rgba(0, 0, 0, 0.8); + border-radius: 40rpx; + padding: 60rpx 80rpx; + display: flex; + flex-direction: column; + align-items: center; + min-width: 400rpx; +} + +.recording-box.is-canceling { + background: rgba(0, 0, 0, 0.9); +} + +.recording-icon { + width: 160rpx; + height: 160rpx; + margin-bottom: 40rpx; + display: flex; + align-items: center; + justify-content: center; +} + +.recording-mic-icon { + width: 120rpx; + height: 120rpx; +} + +.recording-tip { + font-size: 32rpx; + color: #ffffff; + font-weight: 500; + margin-bottom: 30rpx; + text-align: center; +} + +.recording-box.is-canceling .recording-tip { + color: #ff4444; +} + +.recording-meter { + display: flex; + flex-direction: column; + align-items: center; +} + +.recording-waves { + display: flex; + align-items: flex-end; + height: 60rpx; + margin-bottom: 20rpx; +} + +.recording-wave { + width: 8rpx; + margin: 0 4rpx; + background: #07c160; + border-radius: 4rpx; + animation: recordingWave 1.2s ease-in-out infinite; +} + +.recording-box.is-canceling .recording-wave { + background: #ff4444; +} + +@keyframes recordingWave { + 0%, 100% { height: 20rpx; - animation-delay: 0.8s; - } - - @keyframes wave { - 0%, 100% { - transform: scaleY(0.5); - } - 50% { - transform: scaleY(1); - } } - - .audio-duration { - font-size: 28rpx; - font-weight: 500; - } - - .message-bubble-left .audio-duration { - color: #666; - } - - .message-bubble-right .audio-duration { - color: white; - } - - /* 文件消息 */ - .message-file { - display: flex; - align-items: center; - padding: 25rpx; - min-width: 300rpx; - } - - .file-icon-box { - position: relative; - margin-right: 20rpx; - } - - .file-icon { - width: 60rpx; - height: 60rpx; - } - - .file-extension { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - font-size: 18rpx; - font-weight: 600; - color: white; - } - - .file-info { - flex: 1; - display: flex; - flex-direction: column; - } - - .file-name { - font-size: 28rpx; - font-weight: 500; - margin-bottom: 8rpx; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - max-width: 200rpx; - } - - .message-bubble-left .file-name { - color: #333; - } - - .message-bubble-right .file-name { - color: white; - } - - .file-size { - font-size: 24rpx; - } - - .message-bubble-left .file-size { - color: #999; - } - - .message-bubble-right .file-size { - color: rgba(255, 255, 255, 0.9); - } - - .file-download-btn { - background: rgba(0, 0, 0, 0.1); - border: none; - padding: 10rpx 20rpx; - border-radius: 15rpx; - font-size: 24rpx; - color: white; - margin-left: 15rpx; - } - - .file-download-btn:active { - background: rgba(0, 0, 0, 0.2); - } - - /* 上传进度 */ - .upload-progress { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.5); - border-radius: 12rpx; - display: flex; - align-items: center; - justify-content: center; - } - - .progress-circle { - position: relative; - width: 80rpx; - height: 80rpx; - } - - .progress-fill { - position: absolute; - width: 100%; - height: 100%; - border: 6rpx solid #4caf50; - border-radius: 50%; - border-top-color: transparent; - border-right-color: transparent; - } - - .progress-text { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - font-size: 22rpx; - color: white; - font-weight: 600; - } - - /* 输入区域 */ - .input-section { - background: white; - border-top: 1rpx solid #e0e0e0; - padding: 20rpx 30rpx; - position: relative; - box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05); - } - - /* 语音面板 */ - .voice-panel { - display: flex; - align-items: center; - justify-content: center; - padding: 20rpx 0; - } - - .voice-record-btn { - width: 100%; - height: 100rpx; - background: linear-gradient(135deg, #f5f5f5 0%, #e0e0e0 100%); - border: none; - border-radius: 50rpx; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - transition: all 0.3s; - } - - .voice-record-btn:active { - background: linear-gradient(135deg, #e0e0e0 0%, #d0d0d0 100%); - transform: scale(0.98); - } - - .mic-icon { - width: 50rpx; - height: 50rpx; - margin-bottom: 10rpx; - } - - .voice-tip { - font-size: 26rpx; - color: #666; - } - - /* 输入面板 */ - .input-panel { - display: flex; - align-items: center; - gap: 20rpx; - } - - .input-mode-btn { - width: 80rpx; - height: 80rpx; - border-radius: 50%; - border: none; - background: #f5f5f5; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.3s; - } - - .input-mode-btn:active { - background: #e0e0e0; - transform: scale(0.95); - } - - .mode-icon { - width: 36rpx; - height: 36rpx; - } - - .input-box-wrapper { - flex: 1; - background: #f5f5f5; - border-radius: 40rpx; - padding: 0 30rpx; - height: 80rpx; - display: flex; - align-items: center; - transition: all 0.3s; - } - - .input-box-wrapper:active { - background: #e8e8e8; - } - - .chat-input { - flex: 1; - height: 100%; - font-size: 30rpx; - color: #333; - } - - .placeholder { - color: #999; - font-size: 28rpx; - } - - .media-btn, - .send-btn { - width: 80rpx; - height: 80rpx; - border-radius: 50%; - border: none; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.3s; - } - - .media-btn { - background: #f5f5f5; - } - - .media-btn:active { - background: #e0e0e0; - transform: scale(0.95); - } - - .send-btn { - background: linear-gradient(135deg, #95ec69 0%, #5ac725 100%); - } - - .send-btn:active { - background: linear-gradient(135deg, #5ac725 0%, #4caf50 100%); - transform: scale(0.95); - } - - .media-icon, - .send-icon { - width: 36rpx; - height: 36rpx; - } - - .send-icon { - filter: brightness(0) invert(1); - } - - /* 多媒体选择面板 */ - .media-action-sheet { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.5); - display: flex; - align-items: flex-end; - justify-content: center; - z-index: 1000; - animation: fadeIn 0.3s ease; - } - - .media-sheet-content { - width: 100%; - background: white; - border-radius: 40rpx 40rpx 0 0; - padding: 40rpx 30rpx 60rpx; - animation: slideUp 0.3s ease; - } - - @keyframes slideUp { - from { - transform: translateY(100%); - } - to { - transform: translateY(0); - } - } - - .media-sheet-header { - display: flex; - align-items: center; - justify-content: space-between; - margin-bottom: 40rpx; - } - - .sheet-title { - font-size: 32rpx; - font-weight: 600; - color: #333; - } - - .close-sheet-btn { - width: 60rpx; + 50% { height: 60rpx; - border-radius: 50%; - border: none; - background: #f5f5f5; - display: flex; - align-items: center; - justify-content: center; - } - - .close-sheet-btn image { - width: 24rpx; - height: 24rpx; - } - - .media-options { - display: grid; - grid-template-columns: repeat(4, 1fr); - gap: 30rpx; - } - - .media-option { - display: flex; - flex-direction: column; - align-items: center; - border: none; - background: none; - padding: 20rpx 10rpx; - border-radius: 20rpx; - transition: all 0.3s; - } - - .media-option:active { - background: #f5f5f5; - } - - .option-icon { - width: 100rpx; - height: 100rpx; - border-radius: 25rpx; - display: flex; - align-items: center; - justify-content: center; - margin-bottom: 20rpx; - } - - .photo-icon { - background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%); - } - - .camera-icon { - background: linear-gradient(135deg, #f3e5f5 0%, #e1bee7 100%); - } - - .video-icon { - background: linear-gradient(135deg, #e8f5e9 0%, #c8e6c9 100%); - } - - .audio-icon { - background: linear-gradient(135deg, #fff3e0 0%, #ffe0b2 100%); - } - - .file-icon { - background: linear-gradient(135deg, #f5f5f5 0%, #e0e0e0 100%); - } - - .option-icon image { - width: 50rpx; - height: 50rpx; - } - - .option-text { - font-size: 26rpx; - color: #666; - } - - /* 录音模态框 */ - .recording-modal { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.7); - display: flex; - align-items: center; - justify-content: center; - z-index: 1001; - animation: fadeIn 0.2s ease; - } - - .recording-box { - background: white; - border-radius: 40rpx; - padding: 60rpx; - display: flex; - flex-direction: column; - align-items: center; - box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.3); - } - - .recording-wave-box { - position: relative; - width: 200rpx; - height: 200rpx; - margin-bottom: 30rpx; - } - - .recording-wave { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - border: 8rpx solid #5ac725; - border-radius: 50%; - animation: pulse 1.5s ease-in-out infinite; - } - - @keyframes pulse { - 0% { - transform: scale(1); - opacity: 1; - } - 100% { - transform: scale(1.2); - opacity: 0; - } - } - - .recording-mic-icon { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 80rpx; - height: 80rpx; - } - - .recording-tip { - font-size: 32rpx; - color: #333; - font-weight: 500; - margin-bottom: 15rpx; } - - .recording-time { - font-size: 36rpx; - color: #5ac725; - font-weight: bold; - } \ No newline at end of file +} + +.recording-time { + font-size: 36rpx; + color: #ffffff; + font-weight: bold; +} + +/* 适配全面屏 */ +.safe-area-bottom { + padding-bottom: env(safe-area-inset-bottom); +} \ No newline at end of file