From 29ef3f50aff207529f84942254e2cf2fe2a40e0c Mon Sep 17 00:00:00 2001 From: ZhaoYang <565837861@qq.com> Date: Fri, 13 Mar 2026 17:48:06 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9B=E5=BB=BA=E4=B8=80=E5=AF=B9=E4=B8=80?= =?UTF-8?q?=E8=81=8A=E5=A4=A9=EF=BC=8C=E5=AE=9E=E6=97=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.json | 7 - pages/images/xx.png | Bin 4352 -> 0 bytes pages/images/xx1.png | Bin 4432 -> 0 bytes pagesA/pages/advisory/advisory.js | 213 ++- pagesA/pages/advisory/advisory.wxml | 26 +- pagesA/pages/advisory/advisory.wxss | 5 +- pagesA/pages/expertChat/expertChat.js | 1639 ++++++++++++++++++----- pagesA/pages/expertChat/expertChat.wxml | 157 +-- pagesA/pages/expertChat/expertChat.wxss | 1335 +++++++++--------- utils/api.js | 15 +- 10 files changed, 2117 insertions(+), 1280 deletions(-) delete mode 100644 pages/images/xx.png delete mode 100644 pages/images/xx1.png diff --git a/app.json b/app.json index 215bdd4..c0775fc 100644 --- a/app.json +++ b/app.json @@ -2,7 +2,6 @@ "pages": [ "pages/home/home", "pages/login/login", - "pages/message/message", "pages/personal/personal" ], "subPackages": [ @@ -48,12 +47,6 @@ "iconPath": "pages/images/home.png", "selectedIconPath": "pages/images/home1.png" }, - { - "pagePath": "pages/message/message", - "text": "消息", - "iconPath": "pages/images/xx.png", - "selectedIconPath": "pages/images/xx1.png" - }, { "pagePath": "pages/personal/personal", "text": "我的", diff --git a/pages/images/xx.png b/pages/images/xx.png deleted file mode 100644 index 62d55b9eba31ef10ca9c6044d00d1297f54a1683..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4352 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 F{{p#Wx!wQ( diff --git a/pages/images/xx1.png b/pages/images/xx1.png deleted file mode 100644 index 3aea74aa9e2921e6f75d77a68cbc583f8d193f89..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4432 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 diff --git a/pagesA/pages/advisory/advisory.js b/pagesA/pages/advisory/advisory.js index 1bdf7a6..1bc91ac 100644 --- a/pagesA/pages/advisory/advisory.js +++ b/pagesA/pages/advisory/advisory.js @@ -1,142 +1,129 @@ +import http from '../../../utils/api' +const baseUrl = require('../../../utils/baseUrl') + Page({ data: { - // 专家信息(新增在线状态) - expertInfo: { - avatar: 'https://api.dicebear.com/7.x/avataaars/png?seed=expert_pro&size=140', - name: '张振农', - specialty: '作物栽培 · 土壤改良', - experience: 15, - online: true // 在线状态 - }, + baseUrl: baseUrl, + user: {}, // 聊天申请列表数据 applyList: [], // 分页相关 page: 1, pageSize: 10, - hasMore: true, total: 0, + hasMore: true, // 是否还有更多数据 + isLoading: false, // 是否正在加载 + isFirstLoad: true, // 是否首次加载 }, onLoad() { - this.loadApplyList(1); + this.getzj() + this.getsessions(true) // true 表示首次加载(重置列表) }, - // 加载申请列表(增强模拟数据) - loadApplyList(page) { - const { pageSize } = this.data; - wx.showLoading({ title: '加载中...', mask: true }); - - setTimeout(() => { - const mockData = this.generateMockData(page, pageSize); - - if (page === 1) { - this.setData({ - applyList: mockData.list, - total: mockData.total, - hasMore: mockData.hasMore, - page: page - }); - } else { - this.setData({ - applyList: this.data.applyList.concat(mockData.list), - total: mockData.total, - hasMore: mockData.hasMore, - page: page - }); - } - wx.hideLoading(); - }, 600); + // 个人专家信息 + getzj() { + const user = wx.getStorageSync('userInfo') + console.log(222, user) + this.setData({ + user: user + }) }, - // 生成更丰富的模拟数据(含标签) - generateMockData(page, pageSize) { - const total = 23; - const start = (page - 1) * pageSize; - const end = start + pageSize; - const hasMore = end < total; - const list = []; + // 咨询列表 - 支持分页加载 + getsessions(isRefresh = false) { + // 防止重复请求 + if (this.data.isLoading) return + // 如果是加载更多但已无更多数据,直接返回 + if (!isRefresh && !this.data.hasMore) return - const seeds = ['farmer1', 'sheep', 'cattle', 'wheat', 'tractor', 'green', 'sun', 'rain', 'soil', 'seed']; - const firstNames = ['李', '王', '张', '刘', '赵', '陈', '杨', '周', '吴', '黄']; - const farmTypes = ['水稻种植', '牛羊养殖', '果树栽培', '蔬菜大棚', '有机农场', '蜂蜜养殖', '食用菌', '药材种植']; - const tagPool = [ - ['玉米', '虫害'], - ['羊羔', '腹泻'], - ['施肥', '建议'], - ['土壤', '检测'], - ['大棚', '温度'], - ['猪病', '防治'], - ['果树', '修剪'], - ['蜂蜜', '取蜜'] - ]; + this.setData({ isLoading: true }) - for (let i = start; i < Math.min(end, total); i++) { - const id = `apply_${i + 1}`; - const userId = `user_${(i % 10) + 1}`; - const seedIndex = i % seeds.length; - const nameIndex = i % firstNames.length; - const typeIndex = (i * 3) % farmTypes.length; - const unread = i % 4 === 0 ? 2 : (i % 7 === 0 ? 1 : 0); - const statuses = ['pending', 'accepted', 'completed']; - const status = statuses[i % 3]; - - // 随机标签 - const tags = tagPool[(i * 2) % tagPool.length]; + // 准备请求参数 + const params = { + page: isRefresh ? 1 : this.data.page, + pageSize: this.data.pageSize + } - const time = new Date(Date.now() - (i * 7200000) - Math.floor(Math.random() * 5000000)); - const timeStr = `${time.getMonth()+1}.${time.getDate()} ${time.getHours()}:${time.getMinutes().toString().padStart(2,'0')}`; + http.sessions({ + data: params, + success: (res) => { + console.log('接口返回数据:', res) + + let newList = [] + let total = 0 + + if (Array.isArray(res)) { + // 如果直接返回数组 + newList = res + total = res.length + } else if (res.data && Array.isArray(res.data)) { + // 如果返回 { data: [], total: 100 } + newList = res.data + total = res.total || res.data.length + } else if (res.list && Array.isArray(res.list)) { + // 如果返回 { list: [], total: 100 } + newList = res.list + total = res.total || res.list.length + } else { + console.error('接口返回格式不正确', res) + newList = [] + total = 0 + } + + // 计算是否有更多数据 + const currentPage = isRefresh ? 1 : this.data.page + const pageSize = this.data.pageSize + const hasMore = currentPage * pageSize < total - // 更真实的预览消息 - let lastMessage = ''; - if (i % 5 === 0) lastMessage = '专家您好,我家玉米出现黄叶,能帮看看吗?'; - else if (i % 3 === 0) lastMessage = '咨询一下羊羔腹泻的问题,急!'; - else if (i % 4 === 0) lastMessage = '请问什么时候方便通话?'; - else lastMessage = '请求添加您为咨询顾问'; + this.setData({ + applyList: isRefresh ? newList : [...this.data.applyList, ...newList], + total: total, + page: isRefresh ? 2 : this.data.page + 1, // 下次请求的页码 + hasMore: hasMore, + isLoading: false, + isFirstLoad: false + }) + }, + fail: (err) => { + console.error('加载失败:', err) + wx.showToast({ + title: '加载失败', + icon: 'none' + }) + this.setData({ + isLoading: false, + isFirstLoad: false + }) + } + }) + }, - list.push({ - id: id, - user: { - id: userId, - name: firstNames[nameIndex] + (i % 2 === 0 ? '建国' : '秀英') + (i + 1), - avatar: `https://api.dicebear.com/7.x/avataaars/png?seed=${seeds[seedIndex]}_${i}&size=96`, - farmType: farmTypes[typeIndex], - }, - applyTime: timeStr, - lastMessage: lastMessage, - unreadCount: unread, - status: status, - tags: tags, - }); + // 加载更多(上拉触底触发) + loadMore() { + // 如果有更多数据且不在加载中,则加载下一页 + if (this.data.hasMore && !this.data.isLoading) { + this.getsessions(false) } - - return { - list, - total, - hasMore, - }; }, // 处理点击申请项 handleApply(e) { - console.log(111,e); - const id = e.currentTarget.dataset.id - wx.navigateTo({ - url: `/pagesA/pages/expertChat/expertChat?id=${id}`, - }) - }, - - - - loadMore() { - const { hasMore, page } = this.data; - if (hasMore) { - this.loadApplyList(page + 1); - } + console.log(111, e) + const id = e.currentTarget.dataset.id + wx.navigateTo({ + url: `/pagesA/pages/expertChat/expertChat?id=${id}`, + }) }, + // 可选:下拉刷新功能 onPullDownRefresh() { - this.setData({ page: 1, hasMore: true }); - this.loadApplyList(1); - wx.stopPullDownRefresh(); + this.setData({ + page: 1, + hasMore: true + }) + this.getsessions(true) + // 停止下拉刷新 + wx.stopPullDownRefresh() } -}); \ No newline at end of file +}) \ No newline at end of file diff --git a/pagesA/pages/advisory/advisory.wxml b/pagesA/pages/advisory/advisory.wxml index 2674ac4..3f1fbdf 100644 --- a/pagesA/pages/advisory/advisory.wxml +++ b/pagesA/pages/advisory/advisory.wxml @@ -2,17 +2,18 @@ - - + + - {{expertInfo.name}} - {{expertInfo.online ? '在线' : '离线'}} + {{user.user.nickName}} + {{user.vetInfo.expertType}} + 在线 - {{expertInfo.specialty}} + {{user.vetInfo.specialty}} - 从业 {{expertInfo.experience}} 年 + 从业 {{user.vetInfo.workExperience}} @@ -25,28 +26,27 @@ - + - - + + - {{item.applyTime}} + {{item.lastMessageTime}} {{item.lastMessage || '请求咨询...'}} - {{item.unreadCount > 99 ? '99+' : item.unreadCount}} - + 暂无新的咨询申请 稍后刷新试试 diff --git a/pagesA/pages/advisory/advisory.wxss b/pagesA/pages/advisory/advisory.wxss index 982b2dd..0cb0441 100644 --- a/pagesA/pages/advisory/advisory.wxss +++ b/pagesA/pages/advisory/advisory.wxss @@ -81,10 +81,11 @@ text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.2); } + .online-text { - font-size: 26rpx; + font-size: 24rpx; background-color: rgba(255, 255, 255, 0.2); - padding: 3rpx 20rpx; + padding: 3rpx 14rpx; border-radius: 30rpx; backdrop-filter: blur(4px); border: 1rpx solid rgba(255, 255, 255, 0.15); diff --git a/pagesA/pages/expertChat/expertChat.js b/pagesA/pages/expertChat/expertChat.js index 742ccab..2527e73 100644 --- a/pagesA/pages/expertChat/expertChat.js +++ b/pagesA/pages/expertChat/expertChat.js @@ -1,389 +1,1316 @@ -// 专家端聊天页面 - 纯模拟数据,无真实接口 +import http from '../../../utils/api' +const baseUrl = require('../../../utils/baseUrl') + +// WebSocket实例 +let socketOpen = false +let socketMsgQueue = [] +let heartbeatInterval = null +let reconnectTimer = null +let isManualClose = false // 是否手动关闭 +let heartBeatTimer = null // 心跳定时器 + Page({ data: { + baseUrl: baseUrl, // 专家信息 - expertInfo: { - id: 'expert_001', - name: '张医生', - avatar: '/pages/images/tx.png' - }, - expertAvatar: '/pages/images/tx.png', + expertInfo: {}, - // 对话信息 - conversation: { - userId: 'user_001', - userName: '李华', - userAvatar: '/pages/images/tx.png' + // 用户信息 + userInfo: { + user: {} // 完整用户信息 }, - // 在线状态 - onlineStatus: true, + // 对话信息 + conversation: {}, // 消息列表 messageList: [], scrollTop: 0, - scrollAnimate: true, + scrollAnimate: false, // 输入相关 inputValue: '', inputFocus: false, - // 多媒体面板 + // 多媒体 showMediaSheet: false, - // 页面状态 + showDateDivider: true, + todayDate: '', + loading: false, loadingMore: false, - hasMore: true, + + // 滚动相关 + isScrolling: false, + lastScrollTop: 0, + + // 当前对话ID和对方用户ID + currentExpertId: '', + conversationId: '', + otherUserId: '', + + // 分页相关 page: 1, + pageSize: 20, + hasMore: true, + total: 0, + // 记录最早消息的时间戳,用于加载更早的消息 + earliestMsgTime: 0, - // 模拟数据 - mockMessages: [ - { - id: 'msg_1', - isMe: false, - type: 'text', - content: '医生您好,我想咨询一下皮肤问题', - timestamp: Date.now() - 3600000, - status: 'success', - avatar: '/pages/images/tx.png' - }, - { - id: 'msg_2', - isMe: true, - type: 'text', - content: '您好,请描述一下您的具体症状', - timestamp: Date.now() - 3500000, - status: 'success', - avatar: '/pages/images/tx.png' - }, - { - id: 'msg_3', - isMe: false, - type: 'text', - content: '脸上起了很多小红点,有点痒', - timestamp: Date.now() - 3400000, - status: 'success', - avatar: '/pages/images/tx.png' - }, - { - id: 'msg_4', - isMe: false, - type: 'image', - content: 'https://picsum.photos/200/200?random=1', - timestamp: Date.now() - 3300000, - status: 'success', - avatar: '/pages/images/tx.png' - }, - { - id: 'msg_5', - isMe: true, - type: 'text', - content: '看起来像是过敏反应,最近有接触什么新的东西吗?', - timestamp: Date.now() - 3200000, - status: 'success', - avatar: '/pages/images/tx.png' - } - ], + // 时间显示间隔 + timeInterval: 5, + lastShowTimeStamp: 0, + + // 键盘高度 + keyboardHeight: 0, + + // WebSocket状态 + socketConnected: false, + reconnectCount: 0, + maxReconnectCount: 5, + + // 第一次加载完成标记 + firstLoadComplete: false, + // 是否有新消息 + hasNewMessage: false, + + // 消息ID集合,用于去重 + messageIds: new Set(), + + // 正在发送中的消息ID集合 + sendingMessages: new Set(), + + // 显示发送中提示 + showSendingTip: false, + // 发送中的消息数量 + sendingCount: 0, - // 定时器 - typingTimer: null, - mockUserTimer: null + // 上传状态 + isUploading: false }, onLoad: function(options) { - console.log('专家端聊天页面加载', options); + console.log('接收到的参数:', options); - // 获取传递的参数 - if (options.userId) { - this.setData({ - 'conversation.userId': options.userId, - 'conversation.userName': options.userName || '用户' - }); - } + // 设置今天日期 + this.setTodayDate(); - // 加载模拟消息 - this.loadMockMessages(); + // 加载用户信息 + this.loadUserInfo(); - // 模拟用户在线状态变化 - this.simulateOnlineStatus(); + // 获取对方用户ID + if (options.id) { + this.setData({ + otherUserId: options.id, + currentExpertId: options.id, + // 初始页码设置为1 + page: 1, + messageList: [], + hasMore: true, + earliestMsgTime: 0, + firstLoadComplete: false, + messageIds: new Set(), + sendingMessages: new Set(), + showSendingTip: false, + sendingCount: 0 + }); + + // 先创建对话,然后获取聊天记录 + this.createConversation(options.id); + } - // 模拟用户发送消息 - this.startMockUserTyping(); + // 监听键盘高度变化 + wx.onKeyboardHeightChange(this.onKeyboardHeightChange.bind(this)); }, onShow: function() { - // 滚动到底部 - setTimeout(() => { - this.scrollToBottom(false); - }, 200); + // 页面显示时连接WebSocket + isManualClose = false; + + // 确保用户ID和对话ID都存在再连接 + const userId = this.getCurrentUserId(); + if (userId && this.data.conversationId) { + console.log('页面显示,准备连接WebSocket,用户ID:', userId); + this.connectWebSocket(); + } else { + console.log('用户ID或对话ID不存在,稍后再连接', { + userId: userId, + conversationId: this.data.conversationId + }); + } + }, + + onHide: function() { + // 页面隐藏时暂停心跳检查但不关闭连接 + console.log('页面隐藏,暂停心跳检查'); + if (heartBeatTimer) { + clearInterval(heartBeatTimer); + heartBeatTimer = null; + } }, onUnload: function() { + // 页面卸载时关闭WebSocket并清理资源 + isManualClose = true; + this.closeWebSocket(); + wx.offKeyboardHeightChange(); + // 清理定时器 - if (this.data.mockUserTimer) { - clearInterval(this.data.mockUserTimer); + if (heartbeatInterval) { + clearInterval(heartbeatInterval); + heartbeatInterval = null; } - if (this.data.typingTimer) { - clearTimeout(this.data.typingTimer); + if (reconnectTimer) { + clearTimeout(reconnectTimer); + reconnectTimer = null; } + if (heartBeatTimer) { + clearInterval(heartBeatTimer); + heartBeatTimer = null; + } + }, + + // 获取当前用户ID的辅助方法 + getCurrentUserId: function() { + const userId = this.data.userInfo?.user?.userId; + console.log('获取到的用户ID:', userId); + return userId; }, - // 返回上一页 - goBack: function() { - wx.navigateBack({ - delta: 1 + // 创建一对一聊天 + createConversation(id) { + wx.showLoading({ title: '加载中...' }); + + http.create({ + data: { + otherUserId: id + }, + success: res => { + console.log('创建对话响应:', res); + + if (res && res.data) { + // 保存对话ID + const conversationData = res.data; + this.setData({ + conversationId: conversationData.id || conversationData.conversationId || id, + conversation: conversationData, + otherUserId: id, + // 重置分页参数 + page: 1, + hasMore: true, + messageList: [], + earliestMsgTime: 0, + firstLoadComplete: false, + messageIds: new Set(), + sendingMessages: new Set() + }); + + // 获取聊天记录(从第一页开始) + this.getChatHistory(id, false); + + // 连接WebSocket + const userId = this.getCurrentUserId(); + if (userId) { + console.log('用户ID已存在,立即连接WebSocket'); + this.connectWebSocket(); + } else { + console.log('用户ID尚未加载,等待用户信息加载完成'); + } + } else { + wx.hideLoading(); + wx.showToast({ + title: '创建对话失败', + icon: 'none' + }); + } + }, + fail: err => { + console.error('创建对话失败:', err); + wx.hideLoading(); + wx.showToast({ + title: '网络错误', + icon: 'none' + }); + } }); }, + // 获取聊天记录 + getChatHistory(id, isLoadMore = false) { + // 如果是加载更多且没有更多数据,直接返回 + if (isLoadMore && !this.data.hasMore) { + console.log('没有更多消息了'); + this.setData({ loadingMore: false }); + return; + } + + // 如果不是加载更多,显示加载提示 + if (!isLoadMore) { + wx.showLoading({ title: '加载中...' }); + } + + const params = { + otherUserId: id, + page: this.data.page, + pageSize: this.data.pageSize + }; + + console.log('请求聊天记录参数:', params); + http.direct({ + data: params, + success: res => { + console.log('获取聊天记录响应:', res); + + if (!isLoadMore) { + wx.hideLoading(); + } + + if (res && res.code === 200) { + // 处理返回的消息数据 + this.processChatHistory(res, isLoadMore); + } else { + // 如果没有聊天记录,显示空状态 + this.setData({ + messageList: [], + loading: false, + loadingMore: false, + hasMore: false, + firstLoadComplete: true + }); + + if (!isLoadMore) { + wx.showToast({ + title: res?.msg || '获取聊天记录失败', + icon: 'none' + }); + } + } + }, + fail: err => { + console.error('获取聊天记录失败:', err); + + if (!isLoadMore) { + wx.hideLoading(); + } + + wx.showToast({ + title: '网络错误', + icon: 'none' + }); + + this.setData({ + messageList: [], + loading: false, + loadingMore: false, + firstLoadComplete: true + }); + } + }); + }, - // 加载模拟消息 - loadMockMessages: function() { - // 处理消息时间显示 - const messages = this.processMessageTimes(this.data.mockMessages); + // 处理聊天历史数据 + processChatHistory(response, isLoadMore = false) { + let messages = []; + let total = 0; + + // 根据实际返回的数据结构调整这里 + if (response.rows && Array.isArray(response.rows)) { + messages = response.rows; + total = response.total || messages.length; + } else if (response.data && Array.isArray(response.data)) { + messages = response.data; + total = response.total || messages.length; + } else if (Array.isArray(response)) { + messages = response; + total = messages.length; + } + + console.log('处理消息数据:', { + messageCount: messages.length, + total, + isLoadMore, + currentPage: this.data.page + }); + + // 格式化消息数据 + const formattedMessages = this.formatMessages(messages); + + // 判断是否还有更多数据 + const hasMore = this.data.page * this.data.pageSize < total; + + // 记录最早消息的时间戳(用于加载更多) + let earliestTime = this.data.earliestMsgTime; + if (formattedMessages.length > 0) { + // 因为接口返回的是正序,第一条就是最早的 + earliestTime = formattedMessages[0].timestamp; + } + + let newMessageList; + let targetScrollTop = 0; + + // 构建消息ID集合 + const messageIds = new Set(this.data.messageIds); + formattedMessages.forEach(msg => { + if (msg.id) { + messageIds.add(msg.id); + } + }); + + if (isLoadMore) { + // 加载更多:新加载的消息(更早的)应该放在现有消息列表的前面 + newMessageList = [...formattedMessages, ...this.data.messageList]; + + // 记录新消息的总高度,用于滚动定位 + targetScrollTop = formattedMessages.length * 120; // 估算每条消息高度 + } else { + // 首次加载 + newMessageList = formattedMessages; + } + + // 重新处理消息时间显示 + const processedMessages = this.processMessageTimes(newMessageList); this.setData({ - messageList: messages, - hasMore: false + messageList: processedMessages, + messageIds: messageIds, + loading: false, + loadingMore: false, + hasMore: hasMore, + total: total, + earliestMsgTime: earliestTime, + firstLoadComplete: true + }, () => { + if (isLoadMore) { + // 加载更多后,调整滚动位置以保持在当前查看的位置 + setTimeout(() => { + this.setData({ + scrollAnimate: false, + scrollTop: targetScrollTop + }, () => { + setTimeout(() => { + this.setData({ scrollAnimate: true }); + }, 100); + }); + }, 50); + } else { + // 首次加载,滚动到底部(显示最新消息) + setTimeout(() => { + this.scrollToBottom(false); + }, 100); + } }); }, - // 加载更多消息(模拟) - loadMoreMessages: function() { - if (this.data.loadingMore || !this.data.hasMore) return; + // 格式化消息数据 + formatMessages(messages) { + if (!messages || messages.length === 0) return []; - this.setData({ loadingMore: true }); + const userId = this.getCurrentUserId(); + console.log('格式化消息使用的用户ID:', userId); - // 模拟网络延迟 - setTimeout(() => { - // 模拟更早的消息 - const olderMessages = [ - { - id: 'old_' + Date.now(), - isMe: false, - type: 'text', - content: '之前也出现过类似情况', - timestamp: Date.now() - 86400000, - status: 'success', - avatar: '/pages/images/tx.png' - }, - { - id: 'old_' + (Date.now() + 1), - isMe: true, - type: 'text', - content: '那当时是怎么处理的呢?', - timestamp: Date.now() - 86000000, - status: 'success', - avatar: '/pages/images/tx.png' - } - ]; + return messages.map(msg => { + // 统一使用contentType字段 + let contentType = msg.contentType || msg.type || 'text'; + let content = msg.content || ''; + + // 判断发送者 + const isMe = msg.senderId === userId || + msg.fromUserId === userId || + msg.userId === userId || + msg.sendId === userId; - const processedOld = this.processMessageTimes(olderMessages); - const newList = [...processedOld, ...this.data.messageList]; + return { + id: msg.id || 'msg_' + Date.now() + Math.random(), + isMe: isMe, + sender: isMe ? 'user' : 'expert', + senderId: msg.senderId || msg.fromUserId || msg.userId, + contentType: contentType, + content: content, + timestamp: msg.createTime || msg.timestamp || Date.now(), + status: msg.status || 'success', + fileName: msg.fileName || '', + fileSize: msg.fileSize || 0, + thumb: msg.thumb || '', + progress: msg.progress || 100 + }; + }); + }, + + // 加载更多消息 + loadMoreMessages: function() { + // 防止重复加载 + if (this.data.loadingMore) { + console.log('正在加载中,请稍后'); + return; + } + + // 检查是否有更多数据 + if (!this.data.hasMore) { + console.log('没有更多消息了'); + return; + } + + // 检查对方用户ID是否存在 + if (!this.data.otherUserId) { + console.error('对方用户ID不存在'); + return; + } + + console.log('开始加载更多消息,当前页码:', this.data.page); + + // 设置加载状态 + this.setData({ + loadingMore: true + }, () => { + // 页码+1,加载更早的消息 + const nextPage = this.data.page + 1; this.setData({ - messageList: newList, - loadingMore: false, - hasMore: false // 模拟没有更多了 + page: nextPage + }, () => { + // 调用获取聊天记录方法,传入true表示是加载更多操作 + this.getChatHistory(this.data.otherUserId, true); }); + }); + }, + + // 加载用户信息 + loadUserInfo: function() { + try { + const userInfo = wx.getStorageSync('userInfo'); + console.log('从缓存加载的用户信息:', userInfo); - // 调整滚动位置 - setTimeout(() => { - this.setData({ - scrollAnimate: false, - scrollTop: 300 - }, () => { - setTimeout(() => { - this.setData({ scrollAnimate: true }); - }, 100); - }); - }, 50); - }, 800); + if (userInfo) { + // 处理用户信息,确保正确的结构 + let processedUserInfo = { ...this.data.userInfo }; + + if (userInfo.user) { + // 如果已经有user结构 + processedUserInfo = userInfo; + } else if (userInfo.userId) { + // 如果是直接返回的用户数据 + processedUserInfo = { + user: userInfo + }; + } + + // 确保有user对象 + if (!processedUserInfo.user) { + processedUserInfo.user = {}; + } + + // 确保userId存在 + if (userInfo.userId && !processedUserInfo.user.userId) { + processedUserInfo.user.userId = userInfo.userId; + } + + // 设置用户ID + const userId = processedUserInfo.user?.userId || userInfo.userId; + if (userId) { + processedUserInfo.id = userId; + } + + console.log('处理后的用户信息:', processedUserInfo); + this.setData({ userInfo: processedUserInfo }); + + // 用户信息加载完成后,如果对话ID已存在,连接WebSocket + if (this.data.conversationId && !this.data.socketConnected) { + console.log('用户信息加载完成,连接WebSocket'); + this.connectWebSocket(); + } + } + } catch (e) { + console.error('加载用户信息失败:', e); + } }, - // 模拟用户在线状态变化 - simulateOnlineStatus: function() { - setInterval(() => { - // 随机改变在线状态(模拟) - const random = Math.random(); + // ========== WebSocket相关方法 ========== + + // 连接WebSocket + connectWebSocket() { + // 如果已有连接,先检查是否可用 + if (socketOpen) { + console.log('WebSocket已连接,无需重复连接'); + return; + } + + const userId = this.getCurrentUserId(); + console.log('准备连接WebSocket,用户ID:', userId); + + if (!userId) { + console.error('用户ID不存在,无法连接WebSocket'); + wx.showToast({ + title: '用户信息加载中,请稍后', + icon: 'none' + }); + return; + } + + // WebSocket连接URL + const basurl = '192.168.101.109:8080'; // 从配置文件获取 + const wsUrl = `ws://${basurl}/ws/mini/chat?userId=${userId}`; + + console.log('开始连接WebSocket:', wsUrl); + + wx.connectSocket({ + url: wsUrl, + success: () => { + console.log('WebSocket连接初始化成功'); + }, + fail: (err) => { + console.error('WebSocket连接初始化失败:', err); + this.reconnectWebSocket(); + } + }); + + // 监听连接打开 + wx.onSocketOpen(() => { + console.log('WebSocket连接已打开'); + socketOpen = true; + this.setData({ socketConnected: true, reconnectCount: 0 }); + + // 发送队列中的消息 + while (socketMsgQueue.length > 0) { + const msg = socketMsgQueue.shift(); + this.sendWebSocketMessage(msg); + } + + // 开始心跳 + this.startHeartbeat(); + }); + + // 监听收到消息 + wx.onSocketMessage((res) => { + try { + console.log('收到原始WebSocket消息:', res.data); + const data = JSON.parse(res.data); + console.log('解析后的消息:', data); + + // 处理不同类型的消息 + this.handleWebSocketMessage(data); + } catch (e) { + console.error('解析消息失败:', e, '原始数据:', res.data); + } + }); + + // 监听连接关闭 + wx.onSocketClose(() => { + console.log('WebSocket连接已关闭'); + socketOpen = false; + this.setData({ socketConnected: false }); + + // 停止心跳 + if (heartBeatTimer) { + clearInterval(heartBeatTimer); + heartBeatTimer = null; + } + + // 如果不是手动关闭,尝试重连 + if (!isManualClose) { + this.reconnectWebSocket(); + } + }); + + // 监听错误 + wx.onSocketError((err) => { + console.error('WebSocket错误:', err); + socketOpen = false; + this.setData({ socketConnected: false }); + }); + }, + + // 关闭WebSocket + closeWebSocket() { + if (socketOpen) { + wx.closeSocket({ + success: () => { + console.log('WebSocket连接已主动关闭'); + } + }); + socketOpen = false; + this.setData({ socketConnected: false }); + } + + if (heartBeatTimer) { + clearInterval(heartBeatTimer); + heartBeatTimer = null; + } + }, + + // 重连WebSocket + reconnectWebSocket() { + if (this.data.reconnectCount >= this.data.maxReconnectCount) { + console.log('达到最大重连次数,停止重连'); + return; + } + + if (reconnectTimer) { + clearTimeout(reconnectTimer); + } + + reconnectTimer = setTimeout(() => { + console.log(`尝试第${this.data.reconnectCount + 1}次重连`); this.setData({ - onlineStatus: random > 0.3 // 70%时间在线 + reconnectCount: this.data.reconnectCount + 1 }); + this.connectWebSocket(); + }, 3000); + }, + + // 开始心跳 + startHeartbeat() { + if (heartBeatTimer) { + clearInterval(heartBeatTimer); + } + + heartBeatTimer = setInterval(() => { + if (socketOpen) { + console.log('发送心跳'); + this.sendWebSocketMessage(JSON.stringify({ + type: 'heartbeat', + userId: this.getCurrentUserId(), + timestamp: Date.now() + })); + } }, 30000); }, - // 模拟用户正在输入并发送消息 - startMockUserTyping: function() { - // 每45-90秒模拟用户发送一条消息 - const timer = setInterval(() => { - // 只有在线时才发送 - if (this.data.onlineStatus) { - this.simulateUserMessage(); + // 发送WebSocket消息 + sendWebSocketMessage(data) { + if (socketOpen) { + wx.sendSocketMessage({ + data: typeof data === 'string' ? data : JSON.stringify(data), + success: () => { + console.log('WebSocket消息发送成功:', data); + }, + fail: (err) => { + console.error('WebSocket消息发送失败:', err); + socketMsgQueue.push(data); + } + }); + } else { + console.log('WebSocket未连接,消息加入队列'); + socketMsgQueue.push(data); + + if (!isManualClose) { + this.connectWebSocket(); } - }, Math.random() * 45000 + 45000); // 45-90秒 - - this.setData({ mockUserTimer: timer }); - }, - - // 模拟用户发送消息 - simulateUserMessage: function() { - const messages = [ - '好的,我明白了', - '谢谢医生!', - '需要用什么药吗?', - '大概多久能好?', - '有没有什么需要注意的?', - '好的,我试试看', - '明白了,非常感谢!' - ]; - - const randomMsg = messages[Math.floor(Math.random() * messages.length)]; - - const newMsg = { - id: 'user_' + Date.now() + Math.random(), - isMe: false, - type: 'text', - content: randomMsg, + } + }, + + // 处理WebSocket消息 + handleWebSocketMessage(data) { + console.log('处理消息类型:', data.type); + + switch (data.type) { + case 'chat': + case 'message': + this.handleNewMessage(data); + break; + + case 'message_status': + this.handleMessageStatus(data); + break; + + case 'typing': + this.handleTypingStatus(data); + break; + + case 'online_status': + this.handleOnlineStatus(data); + break; + + case 'heartbeat_ack': + console.log('收到心跳响应'); + break; + + default: + console.log('未知消息类型:', data); + } + }, + + // 处理新消息 + handleNewMessage(data) { + console.log('处理新消息 - 完整数据:', data); + + // 提取消息内容 + let messageData = data.data || data; + + // 获取消息ID + const messageId = messageData.id || messageData.messageId || 'msg_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); + + console.log('消息ID:', messageId); + console.log('当前发送中消息集合:', this.data.sendingMessages); + + // 检查是否是自己发送的消息(通过sendingMessages集合判断) + if (this.data.sendingMessages.has(messageId)) { + console.log('收到自己发送的消息回执,跳过处理:', messageId); + + // 从发送中集合移除 + const sendingMessages = new Set(this.data.sendingMessages); + sendingMessages.delete(messageId); + this.setData({ + sendingMessages: sendingMessages, + sendingCount: sendingMessages.size, + showSendingTip: sendingMessages.size > 0 + }); + + // 更新本地消息状态为成功 + this.updateMessageStatus(messageId, 'success'); + return; + } + + // 检查消息是否已存在(去重) + if (this.data.messageIds.has(messageId)) { + console.log('消息已存在,跳过处理:', messageId); + return; + } + + const userId = this.getCurrentUserId(); + const senderId = messageData.senderId || messageData.fromUserId || messageData.userId; + + // 判断是否是自己发送的消息 + const isMe = senderId === userId; + + console.log('消息归属判断:', { + senderId: senderId, + userId: userId, + isMe: isMe, + messageId: messageId + }); + + // 如果是自己发送的消息但不在sendingMessages中,说明是通过其他方式发送的 + // 也跳过处理,避免重复 + if (isMe) { + console.log('是自己发送的消息,但不在发送中集合,可能已存在,跳过处理'); + return; + } + + // 创建新消息对象 - 统一使用contentType + const newMessage = { + id: messageId, + isMe: isMe, + sender: isMe ? 'user' : 'expert', + senderId: senderId, + contentType: messageData.contentType || messageData.type || 'text', + content: messageData.content || '', + timestamp: messageData.timestamp || Date.now(), + status: 'success', // 对方消息直接显示成功 + progress: 100, + fileName: messageData.fileName || '', + fileSize: messageData.fileSize || 0, + thumb: messageData.thumb || '' + }; + + console.log('创建的新消息对象:', newMessage); + + // 获取当前消息列表 + const currentList = [...this.data.messageList]; + console.log('当前消息列表长度:', currentList.length); + + // 添加新消息到列表末尾 + currentList.push(newMessage); + + // 更新消息ID集合 + const messageIds = new Set(this.data.messageIds); + messageIds.add(messageId); + + // 重新处理时间显示 + const processedMessages = this.processMessageTimes(currentList); + + console.log('处理后的消息列表长度:', processedMessages.length); + console.log('最后一条消息:', processedMessages[processedMessages.length - 1]); + + // 更新数据 + this.setData({ + messageList: processedMessages, + messageIds: messageIds, + hasNewMessage: !isMe // 如果不是自己发的,标记有新消息 + }, () => { + console.log('消息列表已更新,当前列表:', this.data.messageList); + + // 判断是否需要滚动到底部 + const query = wx.createSelectorQuery(); + query.select('#chatScroll').boundingClientRect(); + query.select('.chat-bottom-space').boundingClientRect(); + query.exec((res) => { + if (res[0] && res[1]) { + const scrollHeight = res[0].height; + const bottomOffset = res[1].top - res[0].top; + const scrollTop = this.data.lastScrollTop; + + // 如果在底部附近(距离底部小于200px)或是自己发的消息,则滚动到底部 + const shouldScroll = isMe || (scrollTop + scrollHeight >= bottomOffset - 200); + + if (shouldScroll) { + this.scrollToBottom(true); + } + } else { + // 如果获取不到位置信息,默认滚动到底部 + if (isMe) { + this.scrollToBottom(true); + } + } + }); + + // 如果不是自己发的消息,发送已读回执 + if (!isMe) { + this.sendReadReceipt(messageId); + } + }); + }, + + // 处理消息状态 + handleMessageStatus(data) { + const { messageList } = this.data; + const messageId = data.messageId || data.id; + const index = messageList.findIndex(msg => msg.id === messageId); + + if (index !== -1) { + messageList[index].status = data.status || 'success'; + this.setData({ messageList }); + } + + // 如果收到成功状态,从发送中集合移除 + if (data.status === 'success' || data.status === 'read') { + const sendingMessages = new Set(this.data.sendingMessages); + sendingMessages.delete(messageId); + this.setData({ + sendingMessages: sendingMessages, + sendingCount: sendingMessages.size, + showSendingTip: sendingMessages.size > 0 + }); + } + }, + + // 处理正在输入状态 + handleTypingStatus(data) { + const userId = data.userId || data.senderId; + if (userId === this.data.otherUserId) { + this.setData({ 'expertInfo.typing': true }); + + clearTimeout(this.typingTimer); + this.typingTimer = setTimeout(() => { + this.setData({ 'expertInfo.typing': false }); + }, 3000); + } + }, + + // 处理在线状态 + handleOnlineStatus(data) { + const userId = data.userId || data.senderId; + if (userId === this.data.otherUserId) { + this.setData({ + 'expertInfo.online': data.online || data.status === 'online' + }); + } + }, + + // 发送消息到服务器 + sendMessageToServer: function(content, contentType, messageId) { + const receiverId = this.data.otherUserId; + const senderId = this.getCurrentUserId(); + + console.log('发送消息参数:', { + senderId: senderId, + receiverId: receiverId, + content: content, + contentType: contentType, + messageId: messageId + }); + + if (!receiverId || !senderId) { + console.error('发送者或接收者ID不存在', { senderId, receiverId }); + wx.showToast({ + title: '发送失败,用户信息错误', + icon: 'none' + }); + + // 发送失败,从发送中集合移除 + const sendingMessages = new Set(this.data.sendingMessages); + sendingMessages.delete(messageId); + this.setData({ + sendingMessages: sendingMessages, + sendingCount: sendingMessages.size, + showSendingTip: sendingMessages.size > 0 + }); + + // 更新消息状态为失败 + this.updateMessageStatus(messageId, 'failed'); + return; + } + + const message = { + type: 'chat', + receiverId: receiverId, + senderId: senderId, + content: content, + contentType: contentType, timestamp: Date.now(), - status: 'success', - avatar: this.data.conversation.userAvatar || '/pages/images/tx.png' + messageId: messageId }; + + const messageStr = JSON.stringify(message); + console.log('发送消息到服务器:', JSON.parse(messageStr)); + + if (!socketOpen) { + console.log('WebSocket未连接,尝试连接'); + wx.showToast({ + title: '连接中,请稍后', + icon: 'none' + }); + + this.connectWebSocket(); + socketMsgQueue.push(messageStr); + return; + } + + this.sendWebSocketMessage(messageStr); + }, + + // 发送已读回执 + sendReadReceipt(messageId) { + if (!socketOpen) return; - this.addMessageToList(newMsg); + this.sendWebSocketMessage(JSON.stringify({ + type: 'message_status', + messageId: messageId, + status: 'read', + userId: this.getCurrentUserId(), + receiverId: this.data.otherUserId, + conversationId: this.data.conversationId, + timestamp: Date.now() + })); + }, + + // 发送正在输入状态 + sendTypingStatus() { + if (!socketOpen || !this.data.inputValue) return; - // 震动提示(可选) - wx.vibrateShort({ type: 'light' }); + this.sendWebSocketMessage(JSON.stringify({ + type: 'typing', + userId: this.getCurrentUserId(), + receiverId: this.data.otherUserId, + conversationId: this.data.conversationId, + timestamp: Date.now() + })); }, - // 发送文本消息 + // ========== 消息发送相关方法 ========== + + // 发送文本消息 sendTextMessage: function() { const content = this.data.inputValue.trim(); if (!content) return; const messageId = 'msg_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); - // 创建本地消息 + console.log('发送文本消息,ID:', messageId); + + // 将消息ID添加到发送中集合 + const sendingMessages = new Set(this.data.sendingMessages); + sendingMessages.add(messageId); + + // 创建本地消息 - 使用contentType const newMessage = { id: messageId, isMe: true, - type: 'text', + sender: 'user', + senderId: this.getCurrentUserId(), + contentType: 'text', content: content, timestamp: Date.now(), - status: 'sending', - avatar: this.data.expertAvatar + status: 'sending', // 设置为发送中状态 + progress: 100 }; // 添加到列表 this.addMessageToList(newMessage); + // 更新发送中集合和发送计数 + this.setData({ + sendingMessages: sendingMessages, + sendingCount: sendingMessages.size, + showSendingTip: sendingMessages.size > 0 + }); + // 清空输入框 this.setData({ inputValue: '' }); - // 模拟发送延迟 + // 通过WebSocket发送到服务器 + this.sendMessageToServer(content, 'text', messageId); + + // 设置超时,如果5秒后还没有收到回执,认为发送失败 setTimeout(() => { - this.updateMessageStatus(messageId, 'success'); - }, 500); + if (this.data.sendingMessages.has(messageId)) { + console.log('消息发送超时:', messageId); + this.updateMessageStatus(messageId, 'timeout'); + + // 从发送中集合移除 + const sendingMessages = new Set(this.data.sendingMessages); + sendingMessages.delete(messageId); + this.setData({ + sendingMessages: sendingMessages, + sendingCount: sendingMessages.size, + showSendingTip: sendingMessages.size > 0 + }); + + wx.showToast({ + title: '发送超时', + icon: 'none' + }); + } + }, 5000); }, - // 添加消息到列表 - addMessageToList: function(message) { - const { messageList } = this.data; + // 上传图片后发送 + uploadImages: function(tempFilePaths) { + // 改为依次上传,避免并发问题 + this.uploadNextImage(tempFilePaths, 0); + }, + + // 递归上传下一张图片 + uploadNextImage: function(tempFilePaths, index) { + if (index >= tempFilePaths.length) return; - // 新消息添加到末尾 - messageList.push(message); + const tempFilePath = tempFilePaths[index]; + const fileName = `image_${Date.now()}_${index}.jpg`; - // 重新处理时间显示 - const processedMessages = this.processMessageTimes(messageList); + this.uploadImage(tempFilePath, fileName, () => { + // 上传完成后,继续上传下一张 + this.uploadNextImage(tempFilePaths, index + 1); + }); + }, + + // 上传单张图片 - 修复版 + uploadImage: function(tempPath, fileName, callback) { + if (this.data.isUploading) { + wx.showToast({ + title: '已有上传任务,请稍后', + icon: 'none' + }); + return; + } + + this.setData({ isUploading: true }); + + // 显示上传中提示 + wx.showLoading({ + title: '上传中...', + mask: true + }); + + const messageId = 'img_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); + + // 将消息ID添加到发送中集合 + const sendingMessages = new Set(this.data.sendingMessages); + sendingMessages.add(messageId); + + // 先显示本地图片(临时路径) + const tempMessage = { + id: messageId, + isMe: true, + sender: 'user', + senderId: this.getCurrentUserId(), + contentType: 'image', + content: tempPath, // 先用临时路径显示 + timestamp: Date.now(), + status: 'uploading', + progress: 0, + fileName: fileName + }; + + this.addMessageToList(tempMessage); this.setData({ - messageList: processedMessages - }, () => { - this.scrollToBottom(true); + sendingMessages: sendingMessages, + sendingCount: sendingMessages.size, + showSendingTip: sendingMessages.size > 0 }); + + // 开始上传 + wx.uploadFile({ + url: baseUrl + '/common/upload', + header: { + 'Authorization': 'Bearer ' + wx.getStorageSync('token') + }, + filePath: tempPath, + name: 'file', + success: (uploadRes) => { + try { + console.log('上传响应:', uploadRes); + const result = JSON.parse(uploadRes.data); + + if (result.code === 200 || result.fileName) { + // 获取服务器返回的文件路径 + const serverPath = result.fileName || result.url; + console.log('服务器返回路径:', serverPath); + + // 更新消息内容为服务器路径 + this.updateMessageContent(messageId, serverPath); + + // 通过WebSocket发送消息 + this.sendMessageToServer(serverPath, 'image', messageId); + + wx.hideLoading(); + wx.showToast({ + title: '上传成功', + icon: 'success' + }); + } else { + throw new Error(result.msg || '上传失败'); + } + } catch (error) { + console.error('上传失败:', error); + wx.hideLoading(); + + // 更新消息状态为失败 + this.updateMessageStatus(messageId, 'failed'); + + // 从发送中集合移除 + const sendingMessages = new Set(this.data.sendingMessages); + sendingMessages.delete(messageId); + this.setData({ + sendingMessages: sendingMessages, + sendingCount: sendingMessages.size, + showSendingTip: sendingMessages.size > 0 + }); + + wx.showToast({ + title: error.message || '上传失败', + icon: 'none' + }); + } + }, + fail: (error) => { + console.error('网络请求失败:', error); + wx.hideLoading(); + + // 更新消息状态为失败 + this.updateMessageStatus(messageId, 'failed'); + + // 从发送中集合移除 + const sendingMessages = new Set(this.data.sendingMessages); + sendingMessages.delete(messageId); + this.setData({ + sendingMessages: sendingMessages, + sendingCount: sendingMessages.size, + showSendingTip: sendingMessages.size > 0 + }); + + wx.showToast({ + title: '网络请求失败', + icon: 'none' + }); + }, + complete: () => { + this.setData({ isUploading: false }); + if (callback) callback(); + } + }); + + // 模拟上传进度(因为wx.uploadFile不支持进度回调) + let progress = 0; + const progressInterval = setInterval(() => { + progress += Math.random() * 20 + 5; + if (progress >= 95) { + progress = 95; + clearInterval(progressInterval); + } + this.updateMessageProgress(messageId, Math.min(Math.floor(progress), 95)); + }, 200); }, - // 更新消息状态 - updateMessageStatus: function(messageId, status) { + // 更新消息内容 + updateMessageContent: function(messageId, serverPath) { const { messageList } = this.data; const index = messageList.findIndex(msg => msg.id === messageId); if (index !== -1) { - messageList[index].status = status; + messageList[index].content = serverPath; + messageList[index].status = 'success'; + messageList[index].progress = 100; this.setData({ messageList }); } }, - // 重试发送 - retrySend: function(e) { - const messageId = e.currentTarget.dataset.id; + // 更新消息进度 + updateMessageProgress: function(messageId, progress) { const { messageList } = this.data; - const msg = messageList.find(m => m.id === messageId); + const index = messageList.findIndex(msg => msg.id === messageId); - if (msg) { - msg.status = 'sending'; + if (index !== -1) { + messageList[index].progress = progress; this.setData({ messageList }); - - setTimeout(() => { - msg.status = 'success'; - this.setData({ messageList }); - }, 500); } }, - // 处理消息时间显示 - processMessageTimes: function(messages) { - if (!messages || messages.length === 0) return []; + // 添加消息到列表 + addMessageToList: function(message) { + const { messageList, messageIds } = this.data; + + // 检查是否已存在 + if (messageIds.has(message.id)) { + console.log('消息已存在,不重复添加:', message.id); + return; + } - const timeInterval = 5; // 5分钟间隔显示时间 + // 新消息添加到末尾 + messageList.push(message); - return messages.map((msg, index) => { - const showTime = index === 0 || - (msg.timestamp - messages[index - 1].timestamp) > timeInterval * 60 * 1000; - - return { - ...msg, - showTime - }; - }); - }, - - // 格式化时间 - formatTime: function(timestamp) { - if (!timestamp) return ''; + // 更新消息ID集合 + messageIds.add(message.id); - const date = new Date(Number(timestamp)); - const hours = date.getHours().toString().padStart(2, '0'); - const minutes = date.getMinutes().toString().padStart(2, '0'); - return `${hours}:${minutes}`; + // 重新处理时间显示 + const processedMessages = this.processMessageTimes(messageList); + + this.setData({ + messageList: processedMessages, + messageIds: messageIds + }, () => { + this.scrollToBottom(); + }); }, - // 格式化文件大小 - formatFileSize: function(bytes) { - if (!bytes || bytes === 0) return '0B'; - - const units = ['B', 'KB', 'MB', 'GB']; - let size = bytes; - let unitIndex = 0; + // 更新消息状态 + updateMessageStatus: function(messageId, status) { + const { messageList } = this.data; + const index = messageList.findIndex(msg => msg.id === messageId); - while (size >= 1024 && unitIndex < units.length - 1) { - size /= 1024; - unitIndex++; + if (index !== -1) { + messageList[index].status = status; + this.setData({ messageList }); + + // 如果是失败状态,从发送中集合移除 + if (status === 'failed' || status === 'timeout') { + const sendingMessages = new Set(this.data.sendingMessages); + sendingMessages.delete(messageId); + this.setData({ + sendingMessages: sendingMessages, + sendingCount: sendingMessages.size, + showSendingTip: sendingMessages.size > 0 + }); + } } - - return size.toFixed(1) + units[unitIndex]; }, // 输入处理 onInput: function(e) { this.setData({ inputValue: e.detail.value }); + + clearTimeout(this.typingDebounce); + this.typingDebounce = setTimeout(() => { + this.sendTypingStatus(); + }, 500); }, // 输入框获得焦点 onInputFocus: function() { this.setData({ inputFocus: true }, () => { setTimeout(() => { - this.scrollToBottom(true); + this.scrollToBottom(); }, 200); }); }, @@ -404,18 +1331,44 @@ Page({ // 滚动事件 onScroll: function(e) { const scrollTop = e.detail.scrollTop; - this.setData({ lastScrollTop: scrollTop }); + this.setData({ + lastScrollTop: scrollTop, + isScrolling: true + }); - // 滚动到顶部加载更多 - if (scrollTop <= 30 && !this.data.loadingMore && this.data.hasMore) { + clearTimeout(this.scrollTimer); + this.scrollTimer = setTimeout(() => { + this.setData({ isScrolling: false }); + }, 200); + + // 当滚动到顶部附近时加载更多 + if (scrollTop <= 50 && !this.data.loadingMore && this.data.hasMore && this.data.firstLoadComplete) { + console.log('触发加载更多,当前滚动位置:', scrollTop); this.loadMoreMessages(); } + + // 当滚动到底部时,清除新消息标记 + const query = wx.createSelectorQuery(); + query.select('#chatScroll').boundingClientRect(); + query.select('.chat-bottom-space').boundingClientRect(); + query.exec((res) => { + if (res[0] && res[1]) { + const scrollHeight = res[0].height; + const bottomOffset = res[1].top - res[0].top; + if (scrollTop + scrollHeight >= bottomOffset - 50) { + this.setData({ hasNewMessage: false }); + } + } + }); }, // 滚动到底部 scrollToBottom: function(animate = true) { + if (this.data.isScrolling) return; + this.setData({ - scrollAnimate: animate + scrollAnimate: animate, + hasNewMessage: false }, () => { setTimeout(() => { this.setData({ scrollTop: 999999 }); @@ -423,6 +1376,18 @@ Page({ }); }, + // 键盘高度变化 + onKeyboardHeightChange: function(res) { + this.setData({ keyboardHeight: res.height }); + + if (res.height > 0) { + this.setData({ showMediaSheet: false }); + setTimeout(() => { + this.scrollToBottom(); + }, 100); + } + }, + // 显示多媒体选择面板 showMediaActionSheet: function() { this.setData({ @@ -436,97 +1401,101 @@ Page({ this.setData({ showMediaSheet: false }); }, - // 选择图片(模拟) + // 选择图片 chooseImage: function() { this.hideMediaActionSheet(); - // 模拟选择图片 - const mockImages = [ - 'https://picsum.photos/200/200?random=2', - 'https://picsum.photos/200/200?random=3', - 'https://picsum.photos/200/200?random=4' - ]; - - const randomImage = mockImages[Math.floor(Math.random() * mockImages.length)]; - - const messageId = 'img_' + Date.now(); - - const newMessage = { - id: messageId, - isMe: true, - type: 'image', - content: randomImage, - timestamp: Date.now(), - status: 'uploading', - progress: 0, - avatar: this.data.expertAvatar - }; + wx.chooseImage({ + count: 9, + sizeType: ['compressed'], + sourceType: ['album', 'camera'], + success: (res) => { + console.log('选择的图片:', res.tempFilePaths); + this.uploadImages(res.tempFilePaths); + }, + fail: (err) => { + console.error('选择图片失败:', err); + wx.showToast({ + title: '选择图片失败', + icon: 'none' + }); + } + }); + }, + + // 预览图片 + previewImage: function(e) { + const url = e.currentTarget.dataset.url; + wx.previewImage({ + current: url, + urls: [url] + }); + }, + + // 设置今天日期 + setTodayDate: function() { + const now = new Date(); + const month = now.getMonth() + 1; + const date = now.getDate(); + const week = ['日', '一', '二', '三', '四', '五', '六'][now.getDay()]; + this.setData({ + todayDate: `${month}月${date}日 星期${week}` + }); + }, + + // 处理消息时间显示 + processMessageTimes: function(messages) { + if (!messages || messages.length === 0) return []; - this.addMessageToList(newMessage); + const processedMessages = []; + let lastShowTime = null; - // 模拟上传进度 - let progress = 0; - const interval = setInterval(() => { - progress += 20; + for (let i = 0; i < messages.length; i++) { + const msg = { ...messages[i] }; - if (progress >= 100) { - clearInterval(interval); - this.updateMessageStatus(messageId, 'success'); + if (i === 0) { + msg.showTime = true; + lastShowTime = msg.timestamp; } else { - const msgIndex = this.data.messageList.findIndex(m => m.id === messageId); - if (msgIndex !== -1) { - this.data.messageList[msgIndex].progress = progress; - this.setData({ messageList: this.data.messageList }); - } + const timeDiffMinutes = (msg.timestamp - lastShowTime) / (1000 * 60); + msg.showTime = timeDiffMinutes >= this.data.timeInterval; + if (msg.showTime) lastShowTime = msg.timestamp; } - }, 200); + + processedMessages.push(msg); + } + + if (lastShowTime) { + this.setData({ lastShowTimeStamp: lastShowTime }); + } + + return processedMessages; }, - // 选择视频(模拟) - chooseVideo: function() { - this.hideMediaActionSheet(); + // 格式化时间 + formatTime: function(timestamp) { + if (!timestamp) return ''; - const messageId = 'video_' + Date.now(); + const date = new Date(Number(timestamp)); + const hours = date.getHours().toString().padStart(2, '0'); + const minutes = date.getMinutes().toString().padStart(2, '0'); + return `${hours}:${minutes}`; + }, + + // 格式化文件大小 + formatFileSize: function(bytes) { + if (!bytes || bytes === 0) return '0B'; - const newMessage = { - id: messageId, - isMe: true, - type: 'video', - content: 'https://example.com/video.mp4', - thumb: 'https://picsum.photos/200/200?random=5', - timestamp: Date.now(), - status: 'uploading', - progress: 0, - avatar: this.data.expertAvatar - }; + const units = ['B', 'KB', 'MB', 'GB']; + let size = bytes; + let unitIndex = 0; - this.addMessageToList(newMessage); + while (size >= 1024 && unitIndex < units.length - 1) { + size /= 1024; + unitIndex++; + } - // 模拟上传进度 - let progress = 0; - const interval = setInterval(() => { - progress += 25; - - if (progress >= 100) { - clearInterval(interval); - this.updateMessageStatus(messageId, 'success'); - } else { - const msgIndex = this.data.messageList.findIndex(m => m.id === messageId); - if (msgIndex !== -1) { - this.data.messageList[msgIndex].progress = progress; - this.setData({ messageList: this.data.messageList }); - } - } - }, 300); - }, - - // 预览图片 - previewImage: function(e) { - const url = e.currentTarget.dataset.url; - wx.previewImage({ - current: url, - urls: [url] - }); + return size.toFixed(1) + units[unitIndex]; }, // 阻止事件冒泡 diff --git a/pagesA/pages/expertChat/expertChat.wxml b/pagesA/pages/expertChat/expertChat.wxml index 11c7c54..79a5d57 100644 --- a/pagesA/pages/expertChat/expertChat.wxml +++ b/pagesA/pages/expertChat/expertChat.wxml @@ -1,17 +1,14 @@ - + - - - - {{conversation.userName || '用户'}} + {{conversation.otherUserName}} - - {{onlineStatus ? '在线' : '离线'}} + + 在线 @@ -29,82 +26,60 @@ show-scrollbar="{{false}}" bindscroll="onScroll" > - - - 加载中... + + + {{todayDate}} - - + + - + - + - + {{item.content}} - + + - - - - - - - - - - - - - - - {{item.fileName}} - {{formatFileSize(item.fileSize)}} - + + + 已读 - - - {{formatTime(item.timestamp)}} - + - + {{item.content}} - + - - - - - - - - - - {{item.progress}}% - - - - - - - - - - - {{item.fileName}} - {{formatFileSize(item.fileSize)}} - - - - {{item.progress}}% - - - - - - - 发送中 - - - 发送失败 - 重试 - - - - {{formatTime(item.timestamp)}} - + - - - + + 加载中... + + + 暂无聊天记录,开始咨询吧 - - + @@ -209,7 +138,7 @@ disable-default-padding="{{true}}" > - + - + @@ -248,13 +177,6 @@ 照片 - - - - - - 视频 - @@ -262,19 +184,4 @@ - - - - - - 清空聊天记录 - - - 举报用户 - - - 拉黑用户 - - - \ No newline at end of file diff --git a/pagesA/pages/expertChat/expertChat.wxss b/pagesA/pages/expertChat/expertChat.wxss index 392581c..389e3c6 100644 --- a/pagesA/pages/expertChat/expertChat.wxss +++ b/pagesA/pages/expertChat/expertChat.wxss @@ -1,685 +1,652 @@ -/* 专家端聊天样式 */ +/* ========== 页面整体样式 ========== */ .consult-page { - width: 100vw; - height: 100vh; - background: #f5f5f5; - display: flex; - flex-direction: column; - overflow: hidden; - } - - /* 头部样式 */ - .consult-header { - background: #ffffff; - border-bottom: 1rpx solid #e5e5e5; - position: relative; - z-index: 1000; - box-shadow: 0 1rpx 6rpx rgba(0, 0, 0, 0.05); - flex-shrink: 0; - } - - .header-content { - display: flex; - align-items: center; - padding: 12rpx 24rpx; - height: 96rpx; - } - - .back-btn, .header-right { - width: 48rpx; - height: 48rpx; - display: flex; - align-items: center; - justify-content: center; - } - - .back-icon, .more-icon { - width: 40rpx; - height: 40rpx; - } - - .header-center { - flex: 1; - display: flex; - justify-content: center; - } - - .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; - } - - /* 聊天容器 */ - .chat-container { - flex: 1; - padding: 20rpx 0; - background: #f5f5f5; - overflow-y: auto; - position: relative; - height: 0; - } - - /* 消息项 */ - .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; - z-index: 2; - } - - .message-left .message-content-wrapper { align-items: flex-start; } - .message-right .message-content-wrapper { align-items: flex-end; } - - /* 气泡箭头 */ - .message-arrow { - position: absolute; - width: 0; - height: 0; - border-style: solid; - border-width: 12rpx; - top: 30rpx; - z-index: 1; - } - - .arrow-left { - left: -24rpx; - border-color: transparent #ffffff transparent transparent; - } - - .arrow-right { - right: -24rpx; - border-color: transparent transparent transparent #95ec69; - } - - /* 消息气泡 */ - .message-bubble { - position: relative; - padding: 16rpx 20rpx; - word-break: break-word; - box-sizing: border-box; - min-height: 60rpx; - display: flex; - align-items: 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-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: 60rpx; - 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-file { - min-width: 280rpx; - padding: 20rpx; - display: flex; - align-items: center; - min-height: 60rpx; - } - - .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; - } - - /* 消息时间 */ - .message-time { - font-size: 22rpx; - color: #999999; - margin-top: 8rpx; - align-self: flex-start; - } - - .message-right .message-time { - align-self: flex-end; - } - - /* 消息状态 */ - .message-status { - font-size: 22rpx; - color: #999999; - margin-top: 4rpx; - display: flex; - align-items: center; - gap: 8rpx; - } - - .message-status .fail { - color: #ff4d4f; - } - - .retry-text { - color: #1890ff; - text-decoration: underline; - margin-left: 8rpx; - } - - /* 上传进度 */ - .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; - } - - /* 底部留白 */ - .chat-bottom-space { height: 40rpx; } - - /* 加载更多 */ - .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-text { - font-size: 28rpx; - color: #999999; - } - - /* 输入区域 */ - .input-section { - background: #ffffff; - border-top: 1rpx solid #e5e5e5; - padding: 16rpx 30rpx; - position: relative; - z-index: 100; - box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05); - flex-shrink: 0; - width: 100%; - box-sizing: border-box; - } - - .text-input-panel { - display: flex; - align-items: center; - gap: 16rpx; - min-height: 72rpx; - } - - .add-btn { - width: 72rpx; - height: 72rpx; - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - } - - .add-icon { - width: 60rpx; - height: 60rpx; - } - - .input-wrapper { - flex: 1; - position: relative; - background: #f5f5f5; - border-radius: 10rpx; - min-height: 72rpx; - display: flex; - align-items: center; - padding: 0 24rpx; - transition: all 0.2s; - box-sizing: border-box; - } - - .input-wrapper:active { - background: #e8e8e8; - } - - .chat-textarea { - flex: 1; - width: 100%; - font-size: 30rpx; - color: #333333; - line-height: 1.4; - min-height: 48rpx; - max-height: 160rpx; - padding: 12rpx 0; - margin: 0; - background: transparent; - border: none; - box-sizing: border-box; - overflow-y: auto; - } - - .input-placeholder { - color: #999999; - font-size: 28rpx; - line-height: 1.4; - } - - .input-actions { - position: absolute; - right: 16rpx; - top: 50%; - transform: translateY(-50%); - z-index: 2; - flex-shrink: 0; - display: flex; - align-items: center; - justify-content: center; - } - - .clear-btn { - width: 36rpx; - height: 36rpx; - border: none; - background: transparent; - padding: 0; - margin: 0; - line-height: 1; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - } - - .clear-btn::after { - border: none; - } - - .clear-btn:active { - background: rgba(0, 0, 0, 0.1); - } - - .clear-icon { - width: 28rpx; - height: 28rpx; - } - - .send-btn { - background: linear-gradient(135deg, #07c160 0%, #06ae56 100%); - width: 112rpx; - height: 66rpx; - border-radius: 10rpx; - border: none; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.3s ease; - padding: 0; - margin: 0; - line-height: 1; - flex-shrink: 0; - box-shadow: 0 4rpx 8rpx rgba(7, 193, 96, 0.2); - position: relative; - overflow: hidden; - } - - .send-btn:active { - background: linear-gradient(135deg, #06ae56 0%, #059c4c 100%); - transform: scale(0.96); - box-shadow: 0 2rpx 4rpx rgba(7, 193, 96, 0.3); - } - - .send-text { - font-size: 28rpx; - color: #ffffff; - font-weight: 600; - letter-spacing: 2rpx; - } - - .send-placeholder { - width: 112rpx; - height: 72rpx; - flex-shrink: 0; - } - - /* 多媒体选择面板 */ - .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: #F7F7F7; - border-radius: 40rpx 40rpx 0 0; - padding: 40rpx 30rpx calc(10rpx + 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 20rpx; - border-bottom: 1px solid #E4E4E4; - } - - .sheet-title { - font-size: 32rpx; - font-weight: 600; - color: #000000; - } - - .close-sheet-btn image { - width: 60rpx; - height: 60rpx; - } - - .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; - } - - .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; - background-color: #fff; - } - - .media-option:active .option-icon-box { - transform: scale(0.95); - } - - .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; - } - - /* 更多菜单 */ - .more-menu { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.3); - z-index: 2000; - } - - .menu-content { - position: absolute; - top: 96rpx; - right: 24rpx; - background: #ffffff; - border-radius: 12rpx; - padding: 16rpx 0; - min-width: 200rpx; - box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.15); - } - - .menu-item { - padding: 24rpx 32rpx; - font-size: 28rpx; - color: #333333; - border-bottom: 1rpx solid #f0f0f0; - } - - .menu-item:last-child { - border-bottom: none; - } - - .menu-item:active { - background: #f5f5f5; - } \ No newline at end of file + width: 100vw; + height: 100vh; + background: #f5f5f5; + display: flex; + flex-direction: column; + overflow: hidden; +} + +/* ========== 头部样式 ========== */ +.consult-header { + background: #ffffff; + border-bottom: 1rpx solid #e5e5e5; + position: relative; + z-index: 1000; + box-shadow: 0 1rpx 6rpx rgba(0, 0, 0, 0.05); + flex-shrink: 0; +} + +.header-content { + display: flex; + align-items: center; + padding: 12rpx 24rpx; + height: 96rpx; +} + +.header-center { + flex: 1; + display: flex; + justify-content: center; +} + +.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; +} + +/* ========== 聊天容器 ========== */ +.chat-container { + flex: 1; + padding: 20rpx 0; + background: #f5f5f5; + overflow-y: auto; + position: relative; + height: 0; +} + +/* 日期分隔线 */ +.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; + background-color: #d8d8d8; +} + +/* ========== 消息项 ========== */ +.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; + z-index: 2; +} + +.message-left .message-content-wrapper { align-items: flex-start; } +.message-right .message-content-wrapper { align-items: flex-end; } + +/* 气泡箭头 */ +.message-arrow { + position: absolute; + width: 0; + height: 0; + border-style: solid; + border-width: 12rpx; + top: 30rpx; + z-index: 1; +} + +.arrow-left { + left: -24rpx; + border-color: transparent #ffffff transparent transparent; +} + +.arrow-right { + right: -24rpx; + border-color: transparent transparent transparent #95ec69; +} + +/* 消息气泡 */ +.message-bubble { + position: relative; + padding: 16rpx 20rpx; + word-break: break-word; + box-sizing: border-box; + min-height: 60rpx; + display: flex; + align-items: 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-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: 60rpx; + 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; +} + +/* 文件消息 */ +.message-file { + min-width: 280rpx; + padding: 20rpx; + display: flex; + align-items: center; + min-height: 60rpx; +} + +.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; +} + +/* 底部留白 */ +.chat-bottom-space { height: 40rpx; } + +/* 加载更多 */ +.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-text { + font-size: 28rpx; + color: #999999; +} + +/* ========== 输入区域 - 优化垂直对齐和按钮美化 ========== */ +.input-section { + background: #ffffff; + border-top: 1rpx solid #e5e5e5; + padding: 16rpx 30rpx; + position: relative; + z-index: 100; + box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05); + flex-shrink: 0; + width: 100%; + box-sizing: border-box; +} + +/* 文字输入面板 - 垂直居中优化 */ +.text-input-panel { + display: flex; + align-items: center; + gap: 16rpx; + min-height: 72rpx; +} + +/* 添加按钮 - 完美垂直居中 */ +.add-btn { + width: 72rpx; + height: 72rpx; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.add-icon { + width: 60rpx; + height: 60rpx; +} + +/* 输入框包装器 - 优化高度和内边距 */ +.input-wrapper { + flex: 1; + position: relative; + background: #f5f5f5; + border-radius: 10rpx; + min-height: 72rpx; + display: flex; + align-items: center; + padding: 0 24rpx; + transition: all 0.2s; + box-sizing: border-box; +} + +.input-wrapper:active { + background: #e8e8e8; +} + +/* 多行文本输入框 - 优化垂直居中 */ +.chat-textarea { + flex: 1; + width: 100%; + font-size: 30rpx; + color: #333333; + line-height: 1.4; + min-height: 48rpx; + max-height: 160rpx; + padding: 12rpx 0; + margin: 0; + background: transparent; + border: none; + box-sizing: border-box; + overflow-y: auto; +} + +/* 占位符样式 */ +.input-placeholder { + color: #999999; + font-size: 28rpx; + line-height: 1.4; +} + +/* 清空按钮 - 完美垂直居中 */ +.input-actions { + position: absolute; + right: 16rpx; + top: 50%; + transform: translateY(-50%); + z-index: 2; + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; +} + +.clear-btn { + width: 36rpx; + height: 36rpx; + border: none; + background: transparent; + padding: 0; + margin: 0; + line-height: 1; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; +} + +.clear-btn::after { + border: none; +} + +.clear-btn:active { + background: rgba(0, 0, 0, 0.1); +} + +.clear-icon { + width: 28rpx; + height: 28rpx; +} + +/* 发送按钮 */ +.send-btn { + background: linear-gradient(135deg, #07c160 0%, #06ae56 100%); + width: 112rpx; + height: 66rpx; + border-radius: 10rpx; + border: none; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s ease; + padding: 0; + margin: 0; + line-height: 1; + flex-shrink: 0; + box-shadow: 0 4rpx 8rpx rgba(7, 193, 96, 0.2); + position: relative; + overflow: hidden; +} + +/* 发送按钮点击效果 */ +.send-btn:active { + background: linear-gradient(135deg, #06ae56 0%, #059c4c 100%); + transform: scale(0.96); + box-shadow: 0 2rpx 4rpx rgba(7, 193, 96, 0.3); +} + +/* 发送按钮水波纹效果 */ +.send-btn::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 0; + height: 0; + border-radius: 50%; + background: rgba(255, 255, 255, 0.3); + transform: translate(-50%, -50%); + transition: width 0.3s, height 0.3s; +} + +.send-btn:active::after { + width: 200rpx; + height: 200rpx; + opacity: 0; +} + +.send-text { + font-size: 28rpx; + color: #ffffff; + font-weight: 600; + letter-spacing: 2rpx; +} + +/* 发送按钮占位 - 保持布局稳定 */ +.send-placeholder { + width: 112rpx; + height: 72rpx; + flex-shrink: 0; +} + +/* 适配小屏幕 */ +@media screen and (max-width: 320px) { + .send-btn { width: 100rpx; } + .send-placeholder { width: 100rpx; } +} + +/* ========== 多媒体选择面板 ========== */ +.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: #F7F7F7; + border-radius: 40rpx 40rpx 0 0; + padding: 40rpx 30rpx calc(10rpx + 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 20rpx; + border-bottom: 1px solid #E4E4E4; +} + +.sheet-title { + font-size: 32rpx; + font-weight: 600; + color: #000000; +} + +.close-sheet-btn { + display: flex; + align-items: center; + justify-content: center; +} + +.close-sheet-btn image { + width: 60rpx; + height: 60rpx; +} + +.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; + background-color: #fff; +} + +.media-option:active .option-icon-box { + transform: scale(0.95); +} + +.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; +} + +/* 适配全面屏 */ +.safe-area-bottom { + padding-bottom: env(safe-area-inset-bottom); +} \ No newline at end of file diff --git a/utils/api.js b/utils/api.js index 7145fa1..6a9704e 100644 --- a/utils/api.js +++ b/utils/api.js @@ -149,8 +149,21 @@ function feedback(params) { http('/muhu/feedback/list', 'get', params) } +//咨询列表 +function sessions(params) { + http('/system/chat/sessions', 'get', params) +} + +// 建立兽医一对一聊天 +function create(params) { + http('/system/chat/session/create', 'post', params) +} +// 查找一对一聊天的记录 +function direct(params) { + http('/system/chat/messages/direct', 'get', params) +} @@ -174,5 +187,5 @@ export default { // 暴露接口 login,carousel,getPhoneNumber,article,articleDetails,articleZd,wzd,wzdAdd,shareAdd, areaChildren,userCode,UserInfo,videoList,videoZd,videoDetails,forumList,forumAdd,forumDetails, forumReply,commentReply,experience,experiencezd,experienceDetails,realName,revise,feedback, - today,carouselDetail,videoAdd,articleAdd,wzdxq,fazdAdd + today,carouselDetail,videoAdd,articleAdd,wzdxq,fazdAdd,sessions,create,direct }