diff --git a/app.json b/app.json
index 19a3480..915dcf3 100644
--- a/app.json
+++ b/app.json
@@ -23,7 +23,8 @@
"pages/noticeListDetails/noticeListDetails",
"pages/todayInquiry/todayInquiry",
"pages/carouselDetail/carouselDetail",
- "pages/releaseSuffer/releaseSuffer"
+ "pages/releaseSuffer/releaseSuffer",
+ "pages/chatting/chatting"
]
},
{
diff --git a/pages/personal/personal.js b/pages/personal/personal.js
index e2c4256..1604b89 100644
--- a/pages/personal/personal.js
+++ b/pages/personal/personal.js
@@ -342,7 +342,7 @@ Page({
// 查看问答消息
goToQA() {
wx.navigateTo({
- url: '' // 请填写实际的问答消息页面路径
+ url: '/pagesA/pages/chatting/chatting'
})
},
diff --git a/pagesA/pages/chatting/chatting.js b/pagesA/pages/chatting/chatting.js
new file mode 100644
index 0000000..0622692
--- /dev/null
+++ b/pagesA/pages/chatting/chatting.js
@@ -0,0 +1,115 @@
+import http from '../../../utils/api';
+const baseUrl = require('../../../utils/baseUrl');
+
+Page({
+ /**
+ * 页面的初始数据
+ */
+ data: {
+ baseUrl: baseUrl,
+ chatSessions: [] // 存储聊天列表数据
+ },
+
+ /**
+ * 生命周期函数--监听页面加载
+ */
+ onLoad(options) {
+ this.getsessions();
+ },
+
+ // 获取聊天列表
+ getsessions() {
+ http.sessions({
+ data: {},
+ success: (res) => {
+ console.log('接口返回数据:', res);
+ if (res.code === 200 && Array.isArray(res.data)) {
+ // 预处理数据:为每条数据添加格式化后的时间字段
+ const formattedSessions = res.data.map(item => {
+ return {
+ ...item,
+ formattedTime: this.formatTime(item.lastMessageTime) // 新增字段,在JS里直接格式化好
+ };
+ });
+
+ this.setData({
+ chatSessions: formattedSessions
+ });
+ } else {
+ console.error('数据格式错误或接口异常', res);
+ }
+ },
+ fail: (err) => {
+ console.error('请求失败', err);
+ }
+ });
+ },
+
+ // 格式化时间显示 - 兼容多种格式
+ formatTime(timeValue) {
+ console.log('时间原始值:', timeValue);
+
+ if (!timeValue) return '';
+
+ let date;
+
+ // 判断是否是时间戳(数字或数字字符串)
+ if (typeof timeValue === 'number' || (typeof timeValue === 'string' && /^\d+$/.test(timeValue))) {
+ // 如果是时间戳,直接使用
+ date = new Date(Number(timeValue));
+ }
+ // 判断是否是日期字符串
+ else if (typeof timeValue === 'string') {
+ // 尝试解析字符串日期
+ // 将 '2026-03-06 15:50:11' 替换为 '2026/03/06 15:50:11' (兼容 iOS)
+ const formattedStr = timeValue.replace(/-/g, '/');
+ date = new Date(formattedStr);
+ }
+ else {
+ console.log('未知的时间格式:', timeValue);
+ return '';
+ }
+
+ // 检查日期是否有效
+ if (isNaN(date.getTime())) {
+ console.log('无效的日期:', timeValue);
+ return '';
+ }
+
+ const now = new Date();
+ const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
+ const yesterday = today - 24 * 60 * 60 * 1000;
+ const targetDate = new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();
+
+ if (targetDate === today) {
+ // 今天: 显示 时:分
+ const hours = date.getHours().toString().padStart(2, '0');
+ const minutes = date.getMinutes().toString().padStart(2, '0');
+ return `${hours}:${minutes}`;
+ } else if (targetDate === yesterday) {
+ return '昨天';
+ } else {
+ // 更早: 显示 月-日
+ const month = (date.getMonth() + 1).toString().padStart(2, '0');
+ const day = date.getDate().toString().padStart(2, '0');
+ return `${month}-${day}`;
+ }
+ },
+
+ // 跳转到聊天详情页面
+ goToChat(e) {
+ console.log(e);
+ const id = e.currentTarget.dataset.id
+ wx.navigateTo({
+ url: `/pagesA/pages/expertChat/expertChat?id=${id}`
+ });
+ },
+
+ /**
+ * 页面相关事件处理函数--监听用户下拉动作
+ */
+ onPullDownRefresh() {
+ this.getsessions();
+ wx.stopPullDownRefresh();
+ }
+});
\ No newline at end of file
diff --git a/pagesA/pages/chatting/chatting.json b/pagesA/pages/chatting/chatting.json
new file mode 100644
index 0000000..aabdd9c
--- /dev/null
+++ b/pagesA/pages/chatting/chatting.json
@@ -0,0 +1,4 @@
+{
+ "navigationBarTitleText":"聊天列表",
+ "usingComponents": {}
+}
\ No newline at end of file
diff --git a/pagesA/pages/chatting/chatting.wxml b/pagesA/pages/chatting/chatting.wxml
new file mode 100644
index 0000000..8a5c81b
--- /dev/null
+++ b/pagesA/pages/chatting/chatting.wxml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{item.otherUserName || '未知用户'}}
+ {{item.formattedTime}}
+
+
+
+ {{item.lastMessage || '[暂无消息]'}}
+
+
+ {{item.lastMessage}}
+
+
+
+
+
+
+
+
+ 暂无聊天记录
+
+
\ No newline at end of file
diff --git a/pagesA/pages/chatting/chatting.wxss b/pagesA/pages/chatting/chatting.wxss
new file mode 100644
index 0000000..39474b6
--- /dev/null
+++ b/pagesA/pages/chatting/chatting.wxss
@@ -0,0 +1,111 @@
+/* 页面背景 - 微信灰 */
+.chat-list {
+ background-color: #ededed;
+ min-height: 100vh;
+ }
+
+ /* 列表项 - 微信白色卡片 */
+ .list-item {
+ display: flex;
+ padding: 10px 15px;
+ background-color: #ffffff;
+ position: relative;
+ align-items: center; /* 关键:垂直居中对齐 */
+ }
+
+ /* 微信没有点击变色效果,如果要加可以保留,不加可以删除 */
+ .list-item:active {
+ background-color: #f4f4f4;
+ }
+
+ /* 头像区域 - 微信45px,固定大小不变 */
+ .avatar {
+ width: 45px;
+ height: 45px;
+ margin-right: 12px;
+ flex-shrink: 0;
+ }
+
+ .avatar image {
+ width: 100%;
+ height: 100%;
+ border-radius: 6px;
+ background-color: #f0f0f0;
+ }
+
+ /* 内容区域 - 占据剩余空间,垂直居中 */
+ .content {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ flex-direction: column;
+ justify-content: center; /* 内容垂直居中 */
+ height: 100%; /* 占满父容器高度 */
+ }
+
+ /* 第一行:用户名和时间 */
+ .info-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ width: 100%;
+ line-height: 1.2; /* 控制行高 */
+ }
+
+ /* 用户名 - 微信字体16px,粗体,不换行 */
+ .username {
+ font-size: 16px;
+ font-weight: bold;
+ color: #333333;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ flex: 1; /* 自动伸缩 */
+ min-width: 0; /* 防止溢出 */
+ }
+
+ /* 时间 - 微信浅灰色小字,固定宽度不换行 */
+ .time {
+ font-size: 12px;
+ color: #b3b3b3;
+ white-space: nowrap;
+ margin-left: 8px;
+ }
+
+ /* 第二行:最后一条消息 */
+ .message-row {
+ display: flex;
+ align-items: center;
+ width: 100%;
+ margin-top: 16rpx;
+ }
+
+ /* 消息基础样式 - 微信14px灰色 */
+ .last-message {
+ font-size: 14px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ flex: 1; /* 自动伸缩 */
+ min-width: 0; /* 防止溢出 */
+ }
+
+ /* 正常消息 - 微信灰色 #8a8a8a */
+ .last-message.normal {
+ color: #9E9E9E;
+ }
+
+ /* 空消息 - 微信红色 */
+ .last-message.empty {
+ color: #fa5151;
+ }
+
+ /* 空状态页面 */
+ .empty-state {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 80vh;
+ color: #b3b3b3;
+ font-size: 15px;
+ }
\ No newline at end of file
diff --git a/pagesA/pages/expert/expert.js b/pagesA/pages/expert/expert.js
index b47fba4..c612560 100644
--- a/pagesA/pages/expert/expert.js
+++ b/pagesA/pages/expert/expert.js
@@ -9,151 +9,7 @@ Page({
currentFilter: '全部',
// 所有专家数据
- allExperts: [{
- id: 1,
- name: '张建国',
- title: '畜牧学博士',
- expertise: '牛类养殖与疾病防治',
- experience: 15,
- online: true,
- senior: true,
- phone: '138-0013-8001',
- email: 'zhangjg@agri-expert.com',
- institution: '国家畜牧业研究所',
- address: '北京市朝阳区农业科技大厦',
- bio: '15年牛类养殖研究经验,发表SCI论文20余篇,擅长规模化养殖场管理与疾病防控。',
- tags: ['肉牛养殖', '疾病预防', '饲料配方', '规模化养殖'],
- successRate: 98,
- responseTime: '15分钟内响应',
- avatar: '/pagesA/images/1.png'
- },
- {
- id: 2,
- name: '李秀英',
- title: '兽医学硕士',
- expertise: '猪病诊断与治疗',
- experience: 12,
- online: true,
- senior: true,
- phone: '139-0013-9002',
- email: 'lixy@vet-hospital.cn',
- institution: '农业大学动物医学院',
- address: '上海市徐汇区农业路58号',
- bio: '资深猪病防治专家,参与多项国家级科研项目,成功解决多个大规模猪场疫情。',
- tags: ['猪病诊疗', '疫苗管理', '养殖场规划', '传染病防治'],
- successRate: 96,
- responseTime: '20分钟内响应',
- avatar: '/pagesA/images/2.png'
- },
- {
- id: 3,
- name: '王伟民',
- title: '高级畜牧师',
- expertise: '羊类繁殖与饲养管理',
- experience: 20,
- online: false,
- senior: true,
- phone: '137-0013-7003',
- email: 'wangwm@livestock.cn',
- institution: '草原畜牧业研究中心',
- address: '内蒙古呼和浩特市牧业科技园',
- bio: '羊类养殖领域权威专家,拥有20年草原牧区养殖经验,精通各类羊只繁殖技术。',
- tags: ['绵羊养殖', '山羊繁殖', '牧草种植', '草原管理'],
- successRate: 95,
- responseTime: '1小时内响应',
- avatar: '/pagesA/images/3.png'
- },
- {
- id: 4,
- name: '陈秀兰',
- title: '家禽养殖专家',
- expertise: '鸡鸭养殖技术',
- experience: 18,
- online: true,
- senior: false,
- phone: '136-0013-6004',
- email: 'chenxl@poultry-expert.com',
- institution: '家禽养殖技术中心',
- address: '广东省广州市禽业科技园',
- bio: '家禽养殖技术推广专家,帮助300+养殖户实现科学化、标准化养殖管理。',
- tags: ['蛋鸡养殖', '肉鸭饲养', '疾病防控', '禽舍设计'],
- successRate: 94,
- responseTime: '30分钟内响应',
- avatar: '/pagesA/images/1.png'
- },
- {
- id: 5,
- name: '刘志强',
- title: '特种养殖专家',
- expertise: '鹿、兔等特种动物养殖',
- experience: 10,
- online: false,
- senior: false,
- phone: '135-0013-5005',
- email: 'liuzq@special-livestock.cn',
- institution: '特种动物养殖研究所',
- address: '四川省成都市农业科技园',
- bio: '特种动物养殖新兴力量,专注于梅花鹿、肉兔等特种动物的现代化养殖技术。',
- tags: ['梅花鹿养殖', '肉兔饲养', '市场分析', '特色养殖'],
- successRate: 92,
- responseTime: '2小时内响应',
- avatar: '/pagesA/images/2.png'
- },
- {
- id: 6,
- name: '赵雪梅',
- title: '畜牧营养学博士',
- expertise: '牲畜饲料配方与营养',
- experience: 14,
- online: true,
- senior: true,
- phone: '134-0013-4006',
- email: 'zhaoxm@feed-research.cn',
- institution: '农业科学院饲料研究所',
- address: '江苏省南京市科研路88号',
- bio: '饲料营养学专家,研发多个高效饲料配方,帮助养殖户降低30%饲料成本。',
- tags: ['饲料配方', '营养管理', '成本控制', '饲料安全'],
- successRate: 97,
- responseTime: '25分钟内响应',
- avatar: '/pagesA/images/3.png'
- },
- {
- id: 7,
- name: '孙建国',
- title: '养殖场管理专家',
- expertise: '现代化养殖场设计与运营',
- experience: 22,
- online: true,
- senior: true,
- phone: '133-0013-3007',
- email: 'sunjg@farm-management.com',
- institution: '现代农业发展中心',
- address: '浙江省杭州市创新产业园',
- bio: '养殖场规划设计专家,参与设计200+现代化养殖场,精通自动化养殖设备应用。',
- tags: ['场舍设计', '设备管理', '成本核算', '环保处理'],
- successRate: 99,
- responseTime: '10分钟内响应',
- avatar: '/pagesA/images/2.png'
- },
- {
- id: 8,
- name: '周小芳',
- title: '兽医病理学硕士',
- expertise: '牲畜常见病与传染病防治',
- experience: 11,
- online: false,
- senior: false,
- phone: '132-0013-2008',
- email: 'zhouxf@vet-pathology.cn',
- institution: '动物疫病防控中心',
- address: '湖北省武汉市动物防疫站',
- bio: '专攻牲畜病理诊断与传染病防控,在快速诊断和精准治疗方面有丰富经验。',
- tags: ['传染病防治', '病理诊断', '用药指导', '疫情处理'],
- successRate: 93,
- responseTime: '1.5小时内响应',
- avatar: '/pagesA/images/3.png'
- }
- ],
+ allExperts: [],
baseUr: baseUr,
formzj: {
diff --git a/pagesA/pages/expertChat/expertChat.js b/pagesA/pages/expertChat/expertChat.js
index ec2bc04..e884140 100644
--- a/pagesA/pages/expertChat/expertChat.js
+++ b/pagesA/pages/expertChat/expertChat.js
@@ -55,6 +55,8 @@ Page({
pageSize: 20,
hasMore: true,
total: 0,
+ // 记录最早消息的时间戳,用于加载更早的消息
+ earliestMsgTime: 0,
// 时间显示间隔
timeInterval: 5,
@@ -66,7 +68,18 @@ Page({
// WebSocket状态
socketConnected: false,
reconnectCount: 0,
- maxReconnectCount: 5
+ maxReconnectCount: 5,
+
+ // 第一次加载完成标记
+ firstLoadComplete: false,
+ // 是否有新消息
+ hasNewMessage: false,
+
+ // 消息ID集合,用于去重
+ messageIds: new Set(),
+
+ // 正在发送中的消息ID集合
+ sendingMessages: new Set()
},
onLoad: function(options) {
@@ -82,7 +95,15 @@ Page({
if (options.id) {
this.setData({
otherUserId: options.id,
- currentExpertId: options.id
+ currentExpertId: options.id,
+ // 初始页码设置为1
+ page: 1,
+ messageList: [],
+ hasMore: true,
+ earliestMsgTime: 0,
+ firstLoadComplete: false,
+ messageIds: new Set(),
+ sendingMessages: new Set()
});
// 先创建对话,然后获取聊天记录
@@ -164,14 +185,21 @@ Page({
this.setData({
conversationId: conversationData.id || conversationData.conversationId || id,
conversation: conversationData,
- otherUserId: id // 确保对方用户ID正确
+ otherUserId: id,
+ // 重置分页参数
+ page: 1,
+ hasMore: true,
+ messageList: [],
+ earliestMsgTime: 0,
+ firstLoadComplete: false,
+ messageIds: new Set(),
+ sendingMessages: new Set()
});
+ // 获取聊天记录(从第一页开始)
+ this.getChatHistory(id, false);
- // 获取聊天记录
- this.getChatHistory(id);
-
- // 连接WebSocket(确保用户ID已加载)
+ // 连接WebSocket
const userId = this.getCurrentUserId();
if (userId) {
console.log('用户ID已存在,立即连接WebSocket');
@@ -199,50 +227,83 @@ Page({
},
// 获取聊天记录
- getChatHistory(id) {
+ 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: {
- otherUserId: id,
- page: this.data.page,
- pageSize: this.data.pageSize
- },
+ data: params,
success: res => {
console.log('获取聊天记录响应:', res);
- wx.hideLoading();
+
+ if (!isLoadMore) {
+ wx.hideLoading();
+ }
if (res && res.code === 200) {
// 处理返回的消息数据
- this.processChatHistory(res);
+ this.processChatHistory(res, isLoadMore);
} else {
// 如果没有聊天记录,显示空状态
this.setData({
messageList: [],
loading: false,
- hasMore: false
+ loadingMore: false,
+ hasMore: false,
+ firstLoadComplete: true
});
+
+ if (!isLoadMore) {
+ wx.showToast({
+ title: res?.msg || '获取聊天记录失败',
+ icon: 'none'
+ });
+ }
}
},
fail: err => {
console.error('获取聊天记录失败:', err);
- wx.hideLoading();
+
+ if (!isLoadMore) {
+ wx.hideLoading();
+ }
+
wx.showToast({
- title: '获取聊天记录失败',
+ title: '网络错误',
icon: 'none'
});
this.setData({
messageList: [],
- loading: false
+ loading: false,
+ loadingMore: false,
+ firstLoadComplete: true
});
}
});
},
// 处理聊天历史数据
- processChatHistory(response) {
+ processChatHistory(response, isLoadMore = false) {
let messages = [];
let total = 0;
- let hasMore = false;
// 根据实际返回的数据结构调整这里
if (response.rows && Array.isArray(response.rows)) {
@@ -256,26 +317,79 @@ Page({
total = messages.length;
}
- // 格式化消息数据,适配页面渲染
- const formattedMessages = this.formatMessages(messages);
+ console.log('处理消息数据:', {
+ messageCount: messages.length,
+ total,
+ isLoadMore,
+ currentPage: this.data.page
+ });
- // 处理消息时间显示
- const processedMessages = this.processMessageTimes(formattedMessages);
+ // 格式化消息数据
+ const formattedMessages = this.formatMessages(messages);
// 判断是否还有更多数据
- hasMore = this.data.page * this.data.pageSize < total;
+ 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: processedMessages,
+ messageIds: messageIds,
loading: false,
loadingMore: false,
hasMore: hasMore,
- total: total
+ total: total,
+ earliestMsgTime: earliestTime,
+ firstLoadComplete: true
}, () => {
- // 滚动到底部
- setTimeout(() => {
- this.scrollToBottom(true);
- }, 100);
+ if (isLoadMore) {
+ // 加载更多后,调整滚动位置以保持在当前查看的位置
+ setTimeout(() => {
+ this.setData({
+ scrollAnimate: false,
+ scrollTop: targetScrollTop
+ }, () => {
+ setTimeout(() => {
+ this.setData({ scrollAnimate: true });
+ }, 100);
+ });
+ }, 50);
+ } else {
+ // 首次加载,滚动到底部(显示最新消息)
+ setTimeout(() => {
+ this.scrollToBottom(false);
+ }, 100);
+ }
});
},
@@ -313,7 +427,43 @@ Page({
});
},
-
+ // 加载更多消息
+ 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({
+ page: nextPage
+ }, () => {
+ // 调用获取聊天记录方法,传入true表示是加载更多操作
+ this.getChatHistory(this.data.otherUserId, true);
+ });
+ });
+ },
// 加载用户信息
loadUserInfo: function() {
@@ -365,18 +515,6 @@ Page({
}
},
- // 加载更多消息
- loadMoreMessages: function() {
- if (this.data.loadingMore || !this.data.hasMore || !this.data.otherUserId) return;
-
- this.setData({
- loadingMore: true,
- page: this.data.page + 1
- }, () => {
- this.getChatHistory(this.data.otherUserId);
- });
- },
-
// ========== WebSocket相关方法 ==========
// 连接WebSocket
@@ -507,7 +645,7 @@ Page({
reconnectCount: this.data.reconnectCount + 1
});
this.connectWebSocket();
- }, 3000); // 3秒后重连
+ }, 3000);
},
// 开始心跳
@@ -525,7 +663,7 @@ Page({
timestamp: Date.now()
}));
}
- }, 30000); // 30秒发送一次心跳
+ }, 30000);
},
// 发送WebSocket消息
@@ -538,16 +676,13 @@ Page({
},
fail: (err) => {
console.error('WebSocket消息发送失败:', err);
- // 加入队列,等待重发
socketMsgQueue.push(data);
}
});
} else {
console.log('WebSocket未连接,消息加入队列');
- // 队列存储
socketMsgQueue.push(data);
- // 尝试重连
if (!isManualClose) {
this.connectWebSocket();
}
@@ -559,29 +694,24 @@ Page({
console.log('处理消息类型:', data.type);
switch (data.type) {
- case 'chat': // 聊天消息
- case 'message': // 兼容其他类型
- // 新消息
+ 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;
@@ -590,101 +720,129 @@ Page({
}
},
- // 处理新消息
+ // 处理新消息
handleNewMessage(data) {
- console.log('处理新消息:', data);
+ console.log('处理新消息 - 完整数据:', data);
- // 获取当前用户ID
- const currentUserId = this.getCurrentUserId();
+ // 提取消息内容
+ let messageData = data.data || data;
- // 检查消息是否属于当前对话
- const conversationId = data.conversationId || data.chatId || data.roomId;
- const senderId = data.senderId || data.fromUserId || data.userId;
- const receiverId = data.receiverId || data.toUserId;
-
- console.log('消息归属检查:', {
- currentConversationId: this.data.conversationId,
- messageConversationId: conversationId,
- currentUserId: currentUserId,
- senderId: senderId,
- receiverId: receiverId,
- otherUserId: this.data.otherUserId
- });
+ // 获取消息ID
+ const messageId = messageData.id || messageData.messageId || 'msg_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
- // 判断是否属于当前对话
- let isCurrentConversation = false;
+ console.log('消息ID:', messageId);
+ console.log('当前发送中消息集合:', this.data.sendingMessages);
- if (conversationId && conversationId === this.data.conversationId) {
- isCurrentConversation = true;
- } else if (!conversationId) {
- // 如果没有对话ID,则根据发送者和接收者判断
- // 消息发送者是我,接收者是对方
- if (senderId === currentUserId && receiverId === this.data.otherUserId) {
- isCurrentConversation = true;
- }
- // 消息发送者是对方,接收者是我
- else if (senderId === this.data.otherUserId && receiverId === currentUserId) {
- isCurrentConversation = true;
- }
+ // 检查是否是自己发送的消息(通过sendingMessages集合判断)
+ if (this.data.sendingMessages.has(messageId)) {
+ console.log('收到自己发送的消息回执,跳过处理:', messageId);
+
+ // 从发送中集合移除
+ const sendingMessages = new Set(this.data.sendingMessages);
+ sendingMessages.delete(messageId);
+ this.setData({ sendingMessages: sendingMessages });
+
+ // 更新本地消息状态为成功
+ this.updateMessageStatus(messageId, 'success');
+ return;
}
- if (!isCurrentConversation) {
- console.log('消息不属于当前对话,忽略');
+ // 检查消息是否已存在(去重)
+ if (this.data.messageIds.has(messageId)) {
+ console.log('消息已存在,跳过处理:', messageId);
return;
}
- // 判断发送者是否是当前用户
- const isMe = senderId === currentUserId;
+ const userId = this.getCurrentUserId();
+ const senderId = messageData.senderId || messageData.fromUserId || messageData.userId;
+
+ // 判断是否是自己发送的消息
+ const isMe = senderId === userId;
- console.log('消息归属判断结果:', { isMe, isCurrentConversation });
+ console.log('消息归属判断:', {
+ senderId: senderId,
+ userId: userId,
+ isMe: isMe,
+ messageId: messageId
+ });
- // 如果是自己发送的消息,检查是否已存在
+ // 如果是自己发送的消息但不在sendingMessages中,说明是通过其他方式发送的
+ // 也跳过处理,避免重复
if (isMe) {
- // 检查是否已存在相同ID的消息
- const exists = this.data.messageList.some(msg =>
- msg.id === data.messageId || msg.id === data.id
- );
- if (exists) {
- console.log('消息已存在,忽略重复添加');
- return;
- }
+ console.log('是自己发送的消息,但不在发送中集合,可能已存在,跳过处理');
+ return;
}
-
+
+ // 创建新消息对象
const newMessage = {
- id: data.messageId || data.id || 'msg_' + Date.now() + Math.random(),
+ id: messageId,
isMe: isMe,
sender: isMe ? 'user' : 'expert',
senderId: senderId,
- contentType: data.contentType || 'text',
- type: data.contentType || 'text',
- content: data.content,
- timestamp: data.timestamp || Date.now(),
- status: 'success'
+ contentType: messageData.contentType || messageData.type || 'text',
+ type: messageData.type || messageData.contentType || 'text',
+ content: messageData.content || '',
+ timestamp: messageData.timestamp || Date.now(),
+ status: 'success',
+ progress: 100
};
-
- console.log('添加新消息到列表:', newMessage);
-
- // 处理消息时间显示
- const { messageList } = this.data;
- const processedMessage = this.processSingleMessageTime(newMessage, messageList);
- // 添加新消息
- messageList.push(processedMessage);
+ console.log('创建的新消息对象:', newMessage);
- this.setData({ messageList }, () => {
- this.scrollToBottom();
+ // 获取当前消息列表
+ 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);
- // 如果不是自己发的消息,发送已读回执
- if (!newMessage.isMe) {
- this.sendReadReceipt(newMessage.id);
-
- // 播放提示音
- this.playNotificationSound();
-
- // 震动提示
- wx.vibrateShort({
- type: 'light'
- });
+ // 判断是否需要滚动到底部
+ 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) {
+ // wx.vibrateShort({ type: 'light' });
+ // 发送已读回执
+ this.sendReadReceipt(messageId);
}
});
},
@@ -699,21 +857,24 @@ Page({
messageList[index].status = data.status || 'success';
this.setData({ messageList });
}
+
+ // 如果收到成功状态,从发送中集合移除
+ if (data.status === 'success') {
+ const sendingMessages = new Set(this.data.sendingMessages);
+ sendingMessages.delete(messageId);
+ this.setData({ sendingMessages: sendingMessages });
+ }
},
// 处理正在输入状态
handleTypingStatus(data) {
const userId = data.userId || data.senderId;
if (userId === this.data.otherUserId) {
- this.setData({
- 'expertInfo.typing': true
- });
+ this.setData({ 'expertInfo.typing': true });
clearTimeout(this.typingTimer);
this.typingTimer = setTimeout(() => {
- this.setData({
- 'expertInfo.typing': false
- });
+ this.setData({ 'expertInfo.typing': false });
}, 3000);
}
},
@@ -728,7 +889,7 @@ Page({
}
},
- // 发送消息 - 修复用户ID获取
+ // 发送消息到服务器
sendMessageToServer: function(content, type, messageId) {
const receiverId = this.data.otherUserId;
const senderId = this.getCurrentUserId();
@@ -747,16 +908,25 @@ Page({
title: '发送失败,用户信息错误',
icon: 'none'
});
+
+ // 发送失败,从发送中集合移除
+ const sendingMessages = new Set(this.data.sendingMessages);
+ sendingMessages.delete(messageId);
+ this.setData({ sendingMessages: sendingMessages });
+
+ // 更新消息状态为失败
+ this.updateMessageStatus(messageId, 'failed');
return;
}
const message = {
- type: 'chat', // 指定为chat类型
- receiverId: receiverId, // 接收者ID,就是对方的ID
- senderId: senderId, // 发送者ID
- content: content, // 消息内容
- contentType: type || 'text', // 内容类型
- timestamp: Date.now()
+ type: 'chat',
+ receiverId: receiverId,
+ senderId: senderId,
+ content: content,
+ contentType: type || 'text',
+ timestamp: Date.now(),
+ messageId: messageId
};
const messageStr = JSON.stringify(message);
@@ -769,10 +939,7 @@ Page({
icon: 'none'
});
- // 尝试重连
this.connectWebSocket();
-
- // 加入队列
socketMsgQueue.push(messageStr);
return;
}
@@ -810,7 +977,7 @@ Page({
// ========== 消息发送相关方法 ==========
- // 发送文本消息
+ // 发送文本消息
sendTextMessage: function() {
const content = this.data.inputValue.trim();
if (!content) return;
@@ -819,6 +986,10 @@ Page({
console.log('发送文本消息,ID:', messageId);
+ // 将消息ID添加到发送中集合
+ const sendingMessages = new Set(this.data.sendingMessages);
+ sendingMessages.add(messageId);
+
// 创建本地消息
const newMessage = {
id: messageId,
@@ -835,18 +1006,32 @@ Page({
// 添加到列表
this.addMessageToList(newMessage);
+ // 更新发送中集合
+ this.setData({ sendingMessages: sendingMessages });
+
// 清空输入框
- this.setData({
- inputValue: ''
- });
+ this.setData({ inputValue: '' });
// 通过WebSocket发送到服务器
this.sendMessageToServer(content, 'text', messageId);
- // 模拟发送成功回调(实际应该通过WebSocket接收消息状态)
+ // 设置超时,如果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 });
+
+ wx.showToast({
+ title: '发送超时',
+ icon: 'none'
+ });
+ }
+ }, 5000);
},
// 上传图片后发送
@@ -866,6 +1051,11 @@ Page({
// 通用文件上传
uploadFile: function(tempFilePath, fileName, fileSize = 0, type = 'file', thumbPath = '') {
const messageId = 'file_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
+
+ // 将消息ID添加到发送中集合
+ const sendingMessages = new Set(this.data.sendingMessages);
+ sendingMessages.add(messageId);
+
const message = {
id: messageId,
isMe: true,
@@ -883,10 +1073,11 @@ Page({
};
this.addMessageToList(message);
+ this.setData({ sendingMessages: sendingMessages });
this.simulateUpload(messageId, type, tempFilePath);
},
- // 上传
+ // 模拟上传
simulateUpload: function(messageId, type, tempFilePath) {
let progress = 0;
const uploadInterval = setInterval(() => {
@@ -897,7 +1088,6 @@ Page({
clearInterval(uploadInterval);
setTimeout(() => {
- // 上传完成,更新消息状态
this.updateMessageStatus(messageId, 'success');
const { messageList } = this.data;
@@ -906,9 +1096,7 @@ Page({
delete messageList[index].progress;
this.setData({ messageList });
- // 上传完成后,通过WebSocket发送消息
-
- const fileUrl = tempFilePath; // 上传后的URL
+ const fileUrl = tempFilePath;
this.sendMessageToServer(fileUrl, type, messageId);
}
}, 200);
@@ -925,12 +1113,27 @@ Page({
// 添加消息到列表
addMessageToList: function(message) {
- const { messageList } = this.data;
- const processedMessage = this.processSingleMessageTime(message, messageList);
+ const { messageList, messageIds } = this.data;
+
+ // 检查是否已存在
+ if (messageIds.has(message.id)) {
+ console.log('消息已存在,不重复添加:', message.id);
+ return;
+ }
- messageList.push(processedMessage);
+ // 新消息添加到末尾
+ messageList.push(message);
- this.setData({ messageList }, () => {
+ // 更新消息ID集合
+ messageIds.add(message.id);
+
+ // 重新处理时间显示
+ const processedMessages = this.processMessageTimes(messageList);
+
+ this.setData({
+ messageList: processedMessages,
+ messageIds: messageIds
+ }, () => {
this.scrollToBottom();
});
},
@@ -946,15 +1149,10 @@ Page({
}
},
-
-
- // 输入处理(带防抖)
+ // 输入处理
onInput: function(e) {
- this.setData({
- inputValue: e.detail.value
- });
+ this.setData({ inputValue: e.detail.value });
- // 发送正在输入状态(防抖)
clearTimeout(this.typingDebounce);
this.typingDebounce = setTimeout(() => {
this.sendTypingStatus();
@@ -963,9 +1161,7 @@ Page({
// 输入框获得焦点
onInputFocus: function() {
- this.setData({
- inputFocus: true
- }, () => {
+ this.setData({ inputFocus: true }, () => {
setTimeout(() => {
this.scrollToBottom();
}, 200);
@@ -974,9 +1170,7 @@ Page({
// 输入框失去焦点
onInputBlur: function() {
- this.setData({
- inputFocus: false
- });
+ this.setData({ inputFocus: false });
},
// 清除输入
@@ -1000,36 +1194,47 @@ Page({
this.setData({ isScrolling: false });
}, 200);
- // 加载更多
- if (scrollTop <= 100 && !this.data.loadingMore && this.data.hasMore) {
+ // 当滚动到顶部附近时加载更多
+ 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
+ this.setData({
+ scrollAnimate: animate,
+ hasNewMessage: false
}, () => {
setTimeout(() => {
- this.setData({
- scrollTop: 999999
- });
+ this.setData({ scrollTop: 999999 });
}, 50);
});
},
// 键盘高度变化
onKeyboardHeightChange: function(res) {
- this.setData({
- keyboardHeight: res.height
- });
+ this.setData({ keyboardHeight: res.height });
if (res.height > 0) {
this.setData({ showMediaSheet: false });
-
setTimeout(() => {
this.scrollToBottom();
}, 100);
@@ -1046,9 +1251,7 @@ Page({
// 隐藏多媒体选择面板
hideMediaActionSheet: function() {
- this.setData({
- showMediaSheet: false
- });
+ this.setData({ showMediaSheet: false });
},
// 选择图片
@@ -1115,12 +1318,11 @@ Page({
processMessageTimes: function(messages) {
if (!messages || messages.length === 0) return [];
- const sortedMessages = [...messages].sort((a, b) => a.timestamp - b.timestamp);
const processedMessages = [];
let lastShowTime = null;
- for (let i = 0; i < sortedMessages.length; i++) {
- const msg = { ...sortedMessages[i] };
+ for (let i = 0; i < messages.length; i++) {
+ const msg = { ...messages[i] };
if (i === 0) {
msg.showTime = true;
@@ -1141,27 +1343,6 @@ Page({
return processedMessages;
},
- // 处理单条消息时间
- processSingleMessageTime: function(message, messageList) {
- const msg = { ...message };
-
- if (messageList.length === 0) {
- msg.showTime = true;
- this.setData({ lastShowTimeStamp: msg.timestamp });
- return msg;
- }
-
- const lastShowTime = this.data.lastShowTimeStamp;
- const timeDiffMinutes = (msg.timestamp - lastShowTime) / (1000 * 60);
-
- msg.showTime = timeDiffMinutes >= this.data.timeInterval;
- if (msg.showTime) {
- this.setData({ lastShowTimeStamp: msg.timestamp });
- }
-
- return msg;
- },
-
// 格式化时间
formatTime: function(timestamp) {
if (!timestamp) return '';
@@ -1188,21 +1369,6 @@ Page({
return size.toFixed(1) + units[unitIndex];
},
- // 播放提示音
- playNotificationSound() {
- try {
- const innerAudioContext = wx.createInnerAudioContext();
- innerAudioContext.src = '/assets/sounds/notification.mp3';
- innerAudioContext.play();
-
- innerAudioContext.onEnded(() => {
- innerAudioContext.destroy();
- });
- } catch (e) {
- console.log('播放提示音失败:', e);
- }
- },
-
// 阻止事件冒泡
stopPropagation: function() {}
});
\ No newline at end of file
diff --git a/pagesA/pages/expertChat/expertChat.wxml b/pagesA/pages/expertChat/expertChat.wxml
index 15ec09f..680be95 100644
--- a/pagesA/pages/expertChat/expertChat.wxml
+++ b/pagesA/pages/expertChat/expertChat.wxml
@@ -6,7 +6,7 @@
{{conversation.otherUserName}}
-
+
在线
@@ -31,7 +31,7 @@
-
+
diff --git a/utils/api.js b/utils/api.js
index 057a5b7..5770054 100644
--- a/utils/api.js
+++ b/utils/api.js
@@ -255,7 +255,6 @@ function fkfw(params) {
http('/system/dict/data/list', 'get', params)
}
-
// 获取聊天记录列表
function sessions(params) {
http('/system/chat/sessions', 'get', params)
@@ -280,4 +279,5 @@ export default { // 暴露接口
areaChildren,userCode,UserInfo,videoList,videoZd,videoDetails,forumList,forumAdd,forumDetails,
forumReply,commentReply,experience,experiencezd,experienceDetails,realName,revise,feedback,
warningType,disasterDetail,today,carouselDetail,planDetails,wzdDetails,create,direct,fkfw,
+ sessions
}
diff --git a/utils/tool.wxs b/utils/tool.wxs
index 797bd6e..7534e98 100644
--- a/utils/tool.wxs
+++ b/utils/tool.wxs
@@ -34,12 +34,8 @@ function medicineType(type) {
}
-
-
-
-
module.exports = {
type:type,
ztLevel:ztLevel,
- medicineType:medicineType
+ medicineType:medicineType,
}
\ No newline at end of file