// pages/counselor-detail/counselor-detail.js - 陪聊师详情页面 // 对接后端API const api = require('../../utils/api') const app = getApp() Page({ data: { statusBarHeight: 44, navBarHeight: 44, totalNavHeight: 88, // 总导航栏高度(状态栏+导航内容) menuButtonTop: 0, menuButtonHeight: 32, loading: true, counselor: null, messages: [], currentTime: '', inputText: '', inputFocus: false, // 语音录音相关 isVoiceMode: false, isRecording: false, voiceCancelHint: false, recordingDuration: 0, recordingStartY: 0, // 下单相关 showOrderModal: false, selectedDuration: 30, selectedServiceType: 'text', // 服务类型:text/voice durations: [ { value: 15, label: '15分钟' }, { value: 30, label: '30分钟' }, { value: 60, label: '60分钟' } ], // 人物介绍弹窗 showProfileModal: false, // 评价弹窗 showReviewModal: false, reviews: [], reviewStats: { totalCount: 0, goodRate: 100 }, hasMoreReviews: true, loadingReviews: false, reviewPage: 1, // 更多功能面板 showMorePanel: false, // 表情面板 showEmoji: false, emojis: [ "😊", "😀", "😁", "😃", "😂", "🤣", "😅", "😆", "😉", "😋", "😎", "😍", "😘", "🥰", "😗", "😙", "🙂", "🤗", "🤩", "🤔", "😐", "😑", "😶", "🙄", "😏", "😣", "😥", "😮", "😯", "😪", "😫", "😴", "🥱", "😌", "😛", "😜", "😝", "🤤", "😒", "😓", "😔", "😕", "🙃", "🤑", "😲", "☹️", "🙁", "😖", "😞", "😟", "😤", "😢", "😭", "😦", "😧", "😨", "😩", "🤯", "😬", "😰", "😱", "🥵", "🥶", "😳", "🤪", "😵", "🥴", "😠", "😡", "🤬", "😷", "🤒", "🤕", "🤢", "🤮", "🤧", "😇", "🥳", "🥺", "🤠", "❤️", "🧡", "💛", "💚", "💙", "💜", "🖤", "🤍", "🤎", "💔", "❣️", "💕", "💞", "💓", "💗", "💖", "💘", "💝", "💟", "👍", "👎", "👏", "🙌", "👐", "🤲", "🤝", "🙏", "✌️", "🤞", "🤟", "🤘", "👌" ] }, onLoad(options) { const systemInfo = wx.getSystemInfoSync() const statusBarHeight = systemInfo.statusBarHeight || 44 // 获取胶囊按钮位置信息 const menuButton = wx.getMenuButtonBoundingClientRect() // 正确计算导航栏高度的方法: // 导航内容高度 = 胶囊按钮高度 + 上下边距 // 胶囊按钮距离状态栏的间距 = menuButton.top - statusBarHeight // 导航内容高度 = 胶囊按钮高度 + 2 * 间距(上下对称) const navContentHeight = menuButton.height + (menuButton.top - statusBarHeight) * 2 // 整个导航栏高度(包含状态栏)= 状态栏高度 + 导航内容高度 const totalNavHeight = statusBarHeight + navContentHeight // 获取当前时间 const now = new Date() const hours = now.getHours() const minutes = now.getMinutes().toString().padStart(2, '0') const period = hours < 12 ? '上午' : (hours < 18 ? '下午' : '晚上') const displayHour = hours > 12 ? hours - 12 : hours this.setData({ statusBarHeight, navBarHeight: navContentHeight, // 导航内容高度(不含状态栏) totalNavHeight, // 总高度(含状态栏) menuButtonTop: menuButton.top, menuButtonHeight: menuButton.height, currentTime: `${period} ${displayHour}:${minutes}` }) // 加载陪聊师数据 if (options.id) { this.loadCounselorDetail(options.id) } else { wx.showToast({ title: '参数错误', icon: 'none' }) setTimeout(() => wx.navigateBack(), 1500) } }, /** * 页面卸载时清理资源 */ onUnload() { // 停止录音 if (this.data.isRecording && this.recorderManager) { this.voiceCanceled = true this.recorderManager.stop() } // 清除录音计时器 if (this.recordingTimer) { clearInterval(this.recordingTimer) this.recordingTimer = null } // 清理录音管理器 if (this.recorderManager) { this.recorderManager = null } }, /** * 页面隐藏时 */ onHide() { // 停止录音 if (this.data.isRecording && this.recorderManager) { this.voiceCanceled = true this.recorderManager.stop() this.setData({ isRecording: false }) } }, /** * 加载陪聊师详情 */ async loadCounselorDetail(id) { this.setData({ loading: true }) try { console.log('加载陪聊师详情,ID:', id) const res = await api.companion.getDetail(id) console.log('陪聊师详情响应:', res) // 兼容两种返回格式:{ success: true, data: {...} } 或 { code: 0, data: {...} } if ((res.success || res.code === 0) && res.data) { const counselor = this.transformCounselor(res.data) console.log('转换后的陪聊师数据:', counselor) // 设置欢迎消息 const welcomeMsg = { id: 1, content: `您好,我是${counselor.name}。很高兴在这里陪伴您。有什么可以帮助您的吗?【系统自动回复】` } this.setData({ counselor, messages: [welcomeMsg], loading: false }) } else { console.error('API返回失败:', res.error || res.message) wx.showToast({ title: res.error || '加载失败', icon: 'none' }) this.setData({ loading: false }) setTimeout(() => wx.navigateBack(), 1500) } } catch (err) { console.error('加载陪聊师详情失败', err) wx.showToast({ title: '网络错误', icon: 'none' }) this.setData({ loading: false }) setTimeout(() => wx.navigateBack(), 1500) } }, /** * 加载模拟陪聊师数据 */ loadMockCounselor(id) { const mockData = { 'c001': { id: 'c001', name: '林心怡', avatarColor: '#e8b4d8', avatarColorEnd: '#c984cd', location: '北京', city: '北京', age: '28岁', experience: '5年咨询经验', education: '心理学硕士', serviceCount: 1286, repeatCount: 423, rating: 4.96, quote: '每一次倾诉,都是心灵的释放', certification: '国家二级心理咨询师 | 情感咨询专家认证', status: 'online', statusText: '在线', isBusy: false, levelCode: 'junior', levelName: '初级', textPrice: 0.5, voicePrice: 1 }, 'c002': { id: 'c002', name: '张明辉', avatarColor: '#a8d8ea', avatarColorEnd: '#6bb3d9', location: '上海', city: '上海', age: '35岁', experience: '8年咨询经验', education: '应用心理学博士', serviceCount: 2156, repeatCount: 687, rating: 4.92, quote: '用专业的态度,温暖每一颗心', certification: '高级心理咨询师 | 职场心理专家', status: 'online', statusText: '在线', isBusy: false, levelCode: 'senior', levelName: '高级', textPrice: 1.5, voicePrice: 3 }, 'c003': { id: 'c003', name: '王雨萱', avatarColor: '#f8c8dc', avatarColorEnd: '#e89bb8', location: '深圳', city: '深圳', age: '26岁', experience: '3年咨询经验', education: '心理学学士', serviceCount: 856, repeatCount: 298, rating: 4.89, quote: '倾听你的故事,陪伴你的成长', certification: '情感咨询师 | 青年心理辅导员', status: 'offline', statusText: '离线', isBusy: true, levelCode: 'intermediate', levelName: '中级', textPrice: 1, voicePrice: 2 }, 'c004': { id: 'c004', name: '李思远', avatarColor: '#b8d4e3', avatarColorEnd: '#8ab4cf', location: '广州', city: '广州', age: '42岁', experience: '15年咨询经验', education: '临床心理学硕士', serviceCount: 3421, repeatCount: 1156, rating: 4.98, quote: '专业倾听,用心陪伴每一刻', certification: '资深心理治疗师 | 家庭治疗师认证', status: 'online', statusText: '在线', isBusy: false, levelCode: 'expert', levelName: '资深', textPrice: 2, voicePrice: 4 }, 'c005': { id: 'c005', name: '陈晓琳', avatarColor: '#d4b8e8', avatarColorEnd: '#b088d4', location: '杭州', city: '杭州', age: '31岁', experience: '6年咨询经验', education: '发展心理学硕士', serviceCount: 1567, repeatCount: 512, rating: 4.94, quote: '让每一次对话都充满温暖', certification: '心理咨询师 | 情绪管理专家', status: 'online', statusText: '在线', isBusy: false, levelCode: 'senior', levelName: '高级', textPrice: 1.5, voicePrice: 3 }, 'c006': { id: 'c006', name: '赵文博', avatarColor: '#a8e6cf', avatarColorEnd: '#7bc9a6', location: '成都', city: '成都', age: '38岁', experience: '10年咨询经验', education: '社会心理学博士', serviceCount: 1892, repeatCount: 634, rating: 4.91, quote: '理性分析,感性陪伴', certification: '高级心理顾问 | 企业EAP咨询师', status: 'busy', statusText: '忙碌中', isBusy: true, levelCode: 'intermediate', levelName: '中级', textPrice: 1, voicePrice: 2 } } const counselor = mockData[id] || mockData['c001'] // 设置欢迎消息 const welcomeMsg = { id: 1, content: `您好,我是${counselor.name}。很高兴在这里陪伴您。有什么可以帮助您的吗?【系统自动回复】` } this.setData({ counselor, messages: [welcomeMsg], loading: false }) }, /** * 转换陪聊师数据格式 */ transformCounselor(data) { const config = require('../../config/index') console.log('原始陪聊师数据:', data) const statusMap = { online: { text: '在线', isBusy: false }, busy: { text: '忙碌中', isBusy: true }, offline: { text: '离线', isBusy: true } } const status = data.status || data.onlineStatus || data.online_status || 'offline' const statusInfo = statusMap[status] || statusMap.offline // 等级名称映射 const levelNameMap = { junior: '初级', intermediate: '中级', senior: '高级', expert: '资深' } // 优先使用 displayName,然后是 name,最后是 nickname const name = data.displayName || data.display_name || data.name || data.nickname || '未知' // 处理地址,只显示城市名 let location = data.location || data.city || '' if (location) { // 如果地址包含多个部分(如"北京 北京市 朝阳区"),只取第一个城市名 const parts = location.split(/[\s,,]+/).filter(p => p.trim()) if (parts.length > 0) { location = parts[0].replace(/[省市区县]$/, '') || parts[0] } } // 处理头像URL - 如果是相对路径,拼接完整域名 let avatar = data.avatar || '' if (avatar && avatar.startsWith('/')) { // 从 API_BASE_URL 提取域名(去掉 /api 后缀) const baseUrl = config.API_BASE_URL.replace(/\/api$/, '') avatar = baseUrl + avatar } const result = { id: data.id, name: name, avatarColor: data.avatar_color || data.avatarColor || '#c984cd', avatarColorEnd: data.avatar_color_end || data.avatarColorEnd || '#b06ab3', avatar: avatar, location: location, city: location, age: data.age_group || data.ageGroup || data.age || '', experience: data.experience || '', education: data.education || '', serviceCount: data.service_count || data.serviceCount || data.totalOrders || data.total_orders || 0, repeatCount: data.repeat_count || data.repeatCount || 0, rating: data.rating || 5.0, quote: data.quote || data.bio || data.introduction || '', certification: data.certification || '', status: status, statusText: data.statusText || statusInfo.text, isBusy: data.isBusy !== undefined ? data.isBusy : statusInfo.isBusy, // 等级信息 levelCode: data.levelCode || data.level_code || 'junior', levelName: data.levelName || data.level_name || levelNameMap[data.levelCode || data.level_code] || '初级', // 基于等级的价格 textPrice: data.textPrice || data.text_price || data.pricePerMinute || data.price_per_minute || 0.5, voicePrice: data.voicePrice || data.voice_price || 1 } console.log('转换后的陪聊师数据:', result) return result }, onBack() { wx.navigateBack() }, onMore() { wx.showActionSheet({ itemList: ['举报', '拉黑', '分享'], success: (res) => { const actions = ['举报', '拉黑', '分享'] if (res.tapIndex === 1) { // 拉黑 this.addToBlacklist() } else { wx.showToast({ title: actions[res.tapIndex], icon: 'none' }) } } }) }, /** * 添加到黑名单 */ async addToBlacklist() { try { const res = await api.settings.addToBlacklist(this.data.counselor.id) if (res.success) { wx.showToast({ title: '已拉黑', icon: 'success' }) } } catch (err) { wx.showToast({ title: '操作失败', icon: 'none' }) } }, /** * 免费倾诉/下单 */ onFreeConsult() { const { counselor } = this.data if (counselor.isBusy) { wx.showToast({ title: '陪聊师当前不在线', icon: 'none' }) return } // 显示下单弹窗 this.setData({ showOrderModal: true }) }, /** * 关闭下单弹窗 */ closeOrderModal() { this.setData({ showOrderModal: false }) }, /** * 选择时长 */ selectDuration(e) { const duration = e.currentTarget.dataset.duration this.setData({ selectedDuration: duration }) }, /** * 选择服务类型 */ selectServiceType(e) { const type = e.currentTarget.dataset.type this.setData({ selectedServiceType: type }) }, /** * 计算订单价格 */ calculatePrice() { const { counselor, selectedDuration, selectedServiceType } = this.data if (!counselor) return 0 const unitPrice = selectedServiceType === 'voice' ? counselor.voicePrice : counselor.textPrice return (unitPrice * selectedDuration).toFixed(2) }, /** * 确认下单 */ async confirmOrder() { const { counselor, selectedDuration, selectedServiceType } = this.data // 检查登录 if (app.checkNeedLogin && app.checkNeedLogin()) return wx.showLoading({ title: '创建订单...' }) try { const res = await api.order.createCompanionOrder({ companion_id: counselor.id, duration: selectedDuration, service_type: selectedServiceType, message: '' }) wx.hideLoading() if (res.success && res.data) { this.setData({ showOrderModal: false }) // 跳转到陪聊聊天页 wx.navigateTo({ url: `/pages/companion-chat/companion-chat?orderId=${res.data.id}&companionId=${counselor.id}&name=${encodeURIComponent(counselor.name)}` }) } else { wx.showToast({ title: res.message || '下单失败', icon: 'none' }) } } catch (err) { wx.hideLoading() console.error('下单失败', err) wx.showToast({ title: '下单失败', icon: 'none' }) } }, onViewProfile() { this.setData({ showProfileModal: true }) }, closeProfileModal() { this.setData({ showProfileModal: false }) }, onViewReviews() { this.setData({ showReviewModal: true, reviews: [], reviewPage: 1, hasMoreReviews: true }) this.loadReviews() }, closeReviewModal() { this.setData({ showReviewModal: false }) }, /** * 加载评价列表 */ async loadReviews() { if (this.data.loadingReviews || !this.data.hasMoreReviews) return this.setData({ loadingReviews: true }) try { const res = await api.companion.getReviews(this.data.counselor.id, { page: this.data.reviewPage, limit: 10 }) if (res.success && res.data) { const newReviews = res.data.reviews || res.data.list || [] const formattedReviews = newReviews.map(review => ({ ...review, userName: this.maskPhone(review.userName || review.user_name || '匿名用户'), createdAt: this.formatDate(review.createdAt || review.created_at), expanded: false })) // 获取统计数据(兼容多种返回格式) const stats = res.data.stats || {} const totalCount = stats.reviewCount || res.data.totalCount || res.data.total || formattedReviews.length const goodRate = stats.goodRate || res.data.goodRate || 100 const avgRating = stats.avgRating || res.data.avgRating || this.data.counselor?.rating || 5 this.setData({ reviews: [...this.data.reviews, ...formattedReviews], reviewStats: { totalCount: totalCount, goodRate: goodRate, avgRating: avgRating }, hasMoreReviews: res.data.hasMore !== undefined ? res.data.hasMore : formattedReviews.length >= 10, reviewPage: this.data.reviewPage + 1, loadingReviews: false }) } else { // 使用模拟数据 this.loadMockReviews() } } catch (err) { console.error('加载评价失败', err) this.loadMockReviews() } }, /** * 加载模拟评价数据 */ loadMockReviews() { const mockReviews = [ { id: 1, userName: '138****6172', userAvatar: '', rating: 5, content: '老师很有耐心,倾听我的问题后给出了很中肯的建议。咨询后感觉心里轻松了很多,对未来也有了新的规划...', tags: ['专业', '耐心', '有效果'], reply: '谢谢您的信任,很高兴能够帮助到您。希望您能继续保持积极的心态,有任何问题随时可以来找我交流。', likeCount: 23, createdAt: '2024-12-15 14:32' }, { id: 2, userName: '186****3298', userAvatar: '', rating: 5, content: '第一次尝试心理咨询,老师非常专业,让我感觉很放松。通过几次咨询,我对自己的情绪有了更好的认识...', tags: ['专业', '温暖', '有帮助'], reply: '能够陪伴您成长是我的荣幸,继续加油!', likeCount: 15, createdAt: '2024-12-10 09:15' }, { id: 3, userName: '159****7721', userAvatar: '', rating: 5, content: '老师的声音很温柔,聊天的过程中感觉很舒服。虽然问题还在,但是心态好了很多,会继续找老师咨询的...', tags: ['温柔', '善于倾听'], reply: '', likeCount: 8, createdAt: '2024-12-05 20:48' }, { id: 4, userName: '177****4532', userAvatar: '', rating: 5, content: '咨询师很专业,能够快速理解我的问题并给出建议。性价比很高,会推荐给朋友...', tags: ['专业', '高效'], reply: '', likeCount: 12, createdAt: '2024-11-28 16:22' }, { id: 5, userName: '133****8965', userAvatar: '', rating: 5, content: '非常好的一次体验,老师很有同理心,让我感受到了被理解和支持...', tags: ['有同理心', '支持'], reply: '感谢您的认可,祝您生活愉快!', likeCount: 6, createdAt: '2024-11-20 11:05' } ] this.setData({ reviews: mockReviews, reviewStats: { totalCount: this.data.counselor?.serviceCount || 2952, goodRate: 100 }, hasMoreReviews: false, loadingReviews: false }) }, /** * 加载更多评价 */ loadMoreReviews() { this.loadReviews() }, /** * 展开评价内容 */ expandReview(e) { const index = e.currentTarget.dataset.index const reviews = this.data.reviews reviews[index].expanded = true this.setData({ reviews }) }, /** * 点赞评价 */ async likeReview(e) { const reviewId = e.currentTarget.dataset.id const reviews = this.data.reviews const index = reviews.findIndex(r => r.id === reviewId) if (index !== -1) { // 检查是否已点赞 if (reviews[index].liked) { wx.showToast({ title: '已点赞过了', icon: 'none' }) return } // 乐观更新UI reviews[index].likeCount = (reviews[index].likeCount || 0) + 1 reviews[index].liked = true this.setData({ reviews }) // 调用API try { const res = await api.companion.likeReview(reviewId) if (!res.success) { // 失败时回滚 reviews[index].likeCount -= 1 reviews[index].liked = false this.setData({ reviews }) wx.showToast({ title: '点赞失败', icon: 'none' }) } } catch (err) { // 失败时回滚 reviews[index].likeCount -= 1 reviews[index].liked = false this.setData({ reviews }) console.log('点赞API调用失败', err) wx.showToast({ title: '点赞失败', icon: 'none' }) } } }, /** * 手机号脱敏 */ maskPhone(phone) { if (!phone || phone.length < 7) return phone if (phone.includes('****')) return phone return phone.substring(0, 3) + '****' + phone.substring(phone.length - 4) }, /** * 格式化日期 */ formatDate(dateStr) { if (!dateStr) return '' const date = new Date(dateStr) const year = date.getFullYear() const month = String(date.getMonth() + 1).padStart(2, '0') const day = String(date.getDate()).padStart(2, '0') const hours = String(date.getHours()).padStart(2, '0') const minutes = String(date.getMinutes()).padStart(2, '0') return `${year}-${month}-${day} ${hours}:${minutes}` }, onRemind() { wx.showToast({ title: '已设置提醒', icon: 'success' }) }, onVoiceMode() { const isVoiceMode = !this.data.isVoiceMode this.setData({ isVoiceMode, inputFocus: !isVoiceMode }) }, /** * 语音按钮触摸开始 - 开始录音 */ onVoiceTouchStart(e) { // 记录起始Y坐标 const startY = e.touches[0].clientY this.setData({ recordingStartY: startY, voiceCancelHint: false, recordingDuration: 0 }) // 检查录音权限 wx.authorize({ scope: 'scope.record', success: () => { // 开始录音 this.startVoiceRecord() }, fail: () => { wx.showModal({ title: '需要录音权限', content: '请在设置中开启录音权限', confirmText: '去设置', success: (res) => { if (res.confirm) { wx.openSetting() } } }) } }) }, /** * 语音按钮触摸移动 - 检测上划取消 */ onVoiceTouchMove(e) { if (!this.data.isRecording) return const currentY = e.touches[0].clientY const startY = this.data.recordingStartY const moveDistance = startY - currentY // 上划超过80px显示取消提示 const shouldCancel = moveDistance > 80 if (shouldCancel !== this.data.voiceCancelHint) { this.setData({ voiceCancelHint: shouldCancel }) // 震动反馈 if (shouldCancel) { wx.vibrateShort({ type: 'light' }) } } }, /** * 语音按钮触摸结束 - 停止录音并发送/取消 */ onVoiceTouchEnd() { if (!this.data.isRecording) return const { voiceCancelHint } = this.data // 标记是否取消 this.voiceCanceled = voiceCancelHint // 停止录音 if (this.recorderManager) { this.recorderManager.stop() } // 清除录音计时器 if (this.recordingTimer) { clearInterval(this.recordingTimer) this.recordingTimer = null } this.setData({ isRecording: false, voiceCancelHint: false, recordingDuration: 0 }) }, /** * 语音按钮触摸取消 */ onVoiceTouchCancel() { this.voiceCanceled = true this.onVoiceTouchEnd() }, /** * 开始语音录音 */ startVoiceRecord() { this.setData({ isRecording: true, voiceCancelHint: false, recordingDuration: 0 }) // 初始化录音管理器 const recorderManager = wx.getRecorderManager() this.recorderManager = recorderManager // 监听录音结束 recorderManager.onStop((res) => { // 清除计时器 if (this.recordingTimer) { clearInterval(this.recordingTimer) this.recordingTimer = null } this.setData({ isRecording: false }) // 如果是取消的,不发送 if (this.voiceCanceled) { this.voiceCanceled = false wx.showToast({ title: '已取消', icon: 'none' }) return } // 录音时间太短 if (res.duration < 1000) { wx.showToast({ title: '录音时间太短', icon: 'none' }) return } // 发送语音消息 this.sendVoiceMessage(res.tempFilePath, Math.ceil(res.duration / 1000)) }) recorderManager.onError((err) => { console.error('录音失败', err) // 清除计时器 if (this.recordingTimer) { clearInterval(this.recordingTimer) this.recordingTimer = null } this.setData({ isRecording: false, voiceCancelHint: false, recordingDuration: 0 }) // 模拟器不支持录音,给出友好提示 if (err.errMsg && err.errMsg.includes('NotFoundError')) { wx.showToast({ title: '请在真机上测试录音', icon: 'none' }) } else { wx.showToast({ title: '录音失败', icon: 'none' }) } }) // 开始录音 recorderManager.start({ duration: 60000, format: 'mp3', sampleRate: 16000, numberOfChannels: 1 }) // 录音计时器 this.recordingTimer = setInterval(() => { const duration = this.data.recordingDuration + 1 this.setData({ recordingDuration: duration }) // 最长60秒自动停止 if (duration >= 60) { this.onVoiceTouchEnd() } }, 1000) }, /** * 发送语音消息 */ async sendVoiceMessage(filePath, duration) { const { counselor, messages } = this.data // 添加语音消息到列表 const voiceMessage = { id: Date.now(), content: `[语音消息 ${duration}″]`, isUser: true, type: 'voice', audioUrl: filePath, duration: duration } this.setData({ messages: [...messages, voiceMessage] }) wx.showToast({ title: '语音已发送', icon: 'success' }) // 如果陪聊师在线,提示开始对话 if (!counselor.isBusy) { setTimeout(() => { wx.showModal({ title: '提示', content: '是否开始与陪聊师对话?', confirmText: '开始对话', cancelText: '继续留言', success: (res) => { if (res.confirm) { this.onFreeConsult() } } }) }, 500) } // TODO: 上传语音文件到服务器 }, onInput(e) { this.setData({ inputText: e.detail.value }) }, /** * 发送消息 */ onSendMessage() { const { inputText, messages, counselor } = this.data if (!inputText.trim()) return // 添加用户消息 const userMsg = { id: Date.now(), content: inputText, isUser: true } this.setData({ messages: [...messages, userMsg], inputText: '' }) // 如果陪聊师在线,可以跳转到聊天页面 if (!counselor.isBusy) { wx.showModal({ title: '提示', content: '是否开始与陪聊师对话?', confirmText: '开始对话', cancelText: '继续留言', success: (res) => { if (res.confirm) { this.onFreeConsult() } } }) } }, onEmoji() { // 切换表情面板 this.setData({ showEmoji: !this.data.showEmoji, showMorePanel: false, isVoiceMode: false }) }, /** * 选择表情 */ onEmojiSelect(e) { const emoji = e.currentTarget.dataset.emoji this.setData({ inputText: this.data.inputText + emoji }) }, /** * 删除表情/文字 */ onEmojiDelete() { const text = this.data.inputText if (text.length > 0) { // 处理emoji字符(可能占用多个字符位置) const arr = Array.from(text) arr.pop() this.setData({ inputText: arr.join('') }) } }, onAdd() { // 切换更多功能面板显示状态 this.setData({ showMorePanel: !this.data.showMorePanel, showEmoji: false, isVoiceMode: false }) }, // 关闭更多功能面板和表情面板 closeMorePanel() { this.setData({ showMorePanel: false, showEmoji: false }) }, // 拍照 onTakePhoto() { this.setData({ showMorePanel: false }) wx.chooseMedia({ count: 1, mediaType: ['image'], sourceType: ['camera'], camera: 'back', success: (res) => { const tempFilePath = res.tempFiles[0].tempFilePath wx.showToast({ title: '照片已选择', icon: 'success' }) // TODO: 发送图片消息 }, fail: (err) => { if (err.errMsg !== 'chooseMedia:fail cancel') { wx.showToast({ title: '拍照失败', icon: 'none' }) } } }) }, // 从相册选择图片 onChooseImage() { this.setData({ showMorePanel: false }) wx.chooseMedia({ count: 9, mediaType: ['image'], sourceType: ['album'], success: (res) => { wx.showToast({ title: `已选择${res.tempFiles.length}张图片`, icon: 'success' }) // TODO: 发送图片消息 }, fail: (err) => { if (err.errMsg !== 'chooseMedia:fail cancel') { wx.showToast({ title: '选择图片失败', icon: 'none' }) } } }) }, // 发送礼物 onSendGift() { this.setData({ showMorePanel: false }) wx.showToast({ title: '礼物功能开发中', icon: 'none' }) }, // 语音通话 onVoiceCall() { this.setData({ showMorePanel: false }) wx.showToast({ title: '语音通话功能开发中', icon: 'none' }) }, // 常用语 onQuickReply() { this.setData({ showMorePanel: false }) const quickReplies = [ '你好,很高兴认识你~', '最近怎么样?', '有什么想聊的吗?', '今天心情如何?', '晚安,好梦~' ] wx.showActionSheet({ itemList: quickReplies, success: (res) => { this.setData({ inputText: quickReplies[res.tapIndex] }) } }) }, // 约时间 onScheduleTime() { this.setData({ showMorePanel: false }) wx.showToast({ title: '约时间功能开发中', icon: 'none' }) }, // 抢红包 onRedPacket() { this.setData({ showMorePanel: false }) wx.showToast({ title: '红包功能开发中', icon: 'none' }) }, // 测结果 onTestResult() { this.setData({ showMorePanel: false }) wx.showToast({ title: '测结果功能开发中', icon: 'none' }) } })