ai-c/pages/counselor-detail/counselor-detail.js
2026-02-02 18:21:32 +08:00

1172 lines
32 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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' })
}
})