diff --git a/pages/contact/index.js b/pages/contact/index.js index 391d0e1..d3fc2c7 100644 --- a/pages/contact/index.js +++ b/pages/contact/index.js @@ -24,6 +24,10 @@ Page({ isAtBottom: true, // 正在加载更多(顶部分页) loadingMore: false, + // 新消息提醒相关状态 + showNewMessageTip: false, // 是否显示新消息提醒 + newMessageCount: 0, // 新消息数量 + lastMessageId: '', // 最后一条消息的ID,用于检测新消息 }, /** @@ -68,12 +72,27 @@ Page({ // 根据ID进行排序,确保ID最大的(最新的)消息在最后 const sortedList = that.sortMessagesByID(list); - that.setData({ + // 智能滚动逻辑:用户在底部时自动滚动,在上方时保持位置 + const updateData = { messages: sortedList, - total: res.total, - // only auto-scroll if user is at bottom - scrollToId: that.data.isAtBottom ? (sortedList[sortedList.length - 1] ? sortedList[sortedList.length - 1].id : '') : that.data.scrollToId - }); + total: res.total + }; + + that.setData(updateData); + + // 如果用户在底部,延迟设置滚动ID确保滚动生效 + if (that.data.isAtBottom && sortedList.length > 0) { + const lastMessageId = sortedList[sortedList.length - 1].id; + console.log('用户在底部,准备自动滚动到消息ID:', lastMessageId); + setTimeout(() => { + that.setData({ + scrollToId: lastMessageId + }); + console.log('已设置scrollToId:', lastMessageId); + }, 50); + } else { + console.log('用户不在底部或无消息,保持当前位置。isAtBottom:', that.data.isAtBottom, '消息数量:', sortedList.length); + } return res; // 返回结果以支持链式调用 }); @@ -130,7 +149,7 @@ Page({ // 统一的消息处理函数 processMessages(messageList) { - return messageList.map((item, index) => { + const processedMessages = messageList.map((item, index) => { // 优先使用服务器返回的ID,如果没有则使用创建时间戳 if (item.id && !item.id.toString().startsWith('m') && !item.id.toString().startsWith('u')) { item.id = 'm' + item.id; @@ -154,6 +173,44 @@ Page({ return item; }); + + // 检测新消息并显示提醒 + this.checkNewMessages(processedMessages); + + return processedMessages; + }, + + // 检测新消息的函数 + checkNewMessages(newMessages) { + if (newMessages.length === 0) return; + + const currentMessages = this.data.messages; + const currentLastMessageId = this.data.lastMessageId; + const sortedNewMessages = this.sortMessagesByID(newMessages); + const latestNewMessage = sortedNewMessages[sortedNewMessages.length - 1]; + + // 如果不在底部且有新消息 + if (!this.data.isAtBottom && currentMessages.length > 0) { + // 找出真正的新消息(在当前消息列表中不存在的消息) + const currentMessageIds = new Set(currentMessages.map(msg => msg.id)); + const realNewMessages = sortedNewMessages.filter(msg => !currentMessageIds.has(msg.id)); + + if (realNewMessages.length > 0) { + // 累加新消息数量(而不是重置) + const currentCount = this.data.newMessageCount || 0; + this.setData({ + showNewMessageTip: true, + newMessageCount: currentCount + realNewMessages.length + }); + } + } + + // 更新最后一条消息ID + if (latestNewMessage) { + this.setData({ + lastMessageId: latestNewMessage.id + }); + } }, // 消息排序函数 @@ -246,13 +303,15 @@ Page({ // e.detail.scrollTop increases when scrolling down. We want to detect upward scroll. const scrollTop = e.detail.scrollTop || 0; const last = this._lastScrollTop || 0; - // If user scrolls up (new scrollTop < last), pause polling + // If user scrolls up (new scrollTop < last), mark as not at bottom but keep polling if (scrollTop < last) { // user scrolled up if (this.data.isAtBottom) { - // only act if we were previously at bottom + // only update state if we were previously at bottom, but keep polling active + console.log('用户向上滚动,设置isAtBottom为false'); this.setData({ isAtBottom: false }); - this.stopPolling(); + // 注释掉停止轮询的逻辑,确保始终获取新消息 + // this.stopPolling(); } } // detect reaching near top to load more @@ -267,14 +326,31 @@ Page({ }, onScrollToLower() { - // user scrolled to bottom (or very near). resume polling if needed + // user scrolled to bottom (or very near). update state and ensure polling is active + console.log('用户滚动到底部,当前isAtBottom状态:', this.data.isAtBottom); if (!this.data.isAtBottom) { - this.setData({ isAtBottom: true }); + this.setData({ + isAtBottom: true, + showNewMessageTip: false, + newMessageCount: 0 + }); + console.log('已设置isAtBottom为true'); + // 确保轮询始终运行(即使之前已经在运行) this.startPolling(); // also ensure we scroll to latest message next render const lastId = this.data.messages[this.data.messages.length - 1] ? this.data.messages[this.data.messages.length - 1].id : ''; if (lastId) { this.setData({ scrollToId: lastId }); + console.log('设置scrollToId到最新消息:', lastId); + } + } else { + // 即使已经在底部,也要隐藏新消息提醒 + if (this.data.showNewMessageTip) { + this.setData({ + showNewMessageTip: false, + newMessageCount: 0 + }); + console.log('隐藏新消息提醒'); } } }, @@ -507,6 +583,30 @@ Page({ wx.stopPullDownRefresh(); } }, + + // 点击新消息提醒,滚动到底部 + scrollToBottom() { + const messages = this.data.messages; + if (messages.length > 0) { + const sortedMessages = this.sortMessagesByID(messages); + const latestMessage = sortedMessages[sortedMessages.length - 1]; + + this.setData({ + scrollToId: latestMessage.id, + showNewMessageTip: false, + newMessageCount: 0, + isAtBottom: true + }); + + // 延迟一下确保滚动完成 + setTimeout(() => { + this.setData({ + scrollToId: '' + }); + }, 100); + } + }, + onReachBottom() { }, onShareAppMessage() { } }); \ No newline at end of file diff --git a/pages/contact/index.wxml b/pages/contact/index.wxml index 75caa3c..6ea8533 100644 --- a/pages/contact/index.wxml +++ b/pages/contact/index.wxml @@ -33,6 +33,14 @@ + + + + {{newMessageCount}}条新消息 + + + + diff --git a/pages/contact/index.wxss b/pages/contact/index.wxss index a1d71f5..b8b7b8e 100644 --- a/pages/contact/index.wxss +++ b/pages/contact/index.wxss @@ -314,4 +314,57 @@ order: 2; /* same as previous .bubble order for service */ margin-left: 8rpx; /* match bubble spacing */ margin-right: 0; +} + +/* 新消息提醒样式 */ +.new-message-tip { + position: fixed; + bottom: 200rpx; + right: 30rpx; + z-index: 1000; + animation: tipFadeIn 0.3s ease-in-out; +} + +.tip-content { + background: #07c160; + color: white; + padding: 16rpx 24rpx; + border-radius: 40rpx; + display: flex; + align-items: center; + box-shadow: 0 4rpx 12rpx rgba(7, 193, 96, 0.3); + font-size: 28rpx; +} + +.tip-text { + margin-right: 8rpx; +} + +.tip-arrow { + font-size: 24rpx; + font-weight: bold; + animation: bounce 1s infinite; +} + +@keyframes tipFadeIn { + from { + opacity: 0; + transform: translateY(20rpx); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes bounce { + 0%, 20%, 50%, 80%, 100% { + transform: translateY(0); + } + 40% { + transform: translateY(-6rpx); + } + 60% { + transform: translateY(-3rpx); + } } \ No newline at end of file diff --git a/测试说明.md b/测试说明.md new file mode 100644 index 0000000..bedfc02 --- /dev/null +++ b/测试说明.md @@ -0,0 +1,73 @@ +# 自动滚动修复测试说明 + +## 修复内容 +已修复聊天页面自动刷新后自动滚动到底部的问题,现在实现智能滚动: + +### 修复的问题 +- ✅ 轮询刷新时不再强制滚动到底部 +- ✅ 用户在底部时,新消息会自动滚动显示 +- ✅ 用户在上方浏览历史消息时,保持当前位置 +- ✅ 添加了延迟处理确保滚动生效 + +### 修改的文件 +- `pages/contact/index.js` - 修改了 `getMessages()` 函数的滚动逻辑 + +## 测试步骤 + +### 1. 打开项目 +1. 使用微信开发者工具打开项目目录:`/Users/win/code2025/wx-chant` +2. 确保项目配置正确,appid 已设置 + +### 2. 测试场景 + +#### 场景1:用户在底部时的自动滚动 +1. 进入聊天页面 +2. 滚动到最底部 +3. 等待新消息到达(轮询刷新) +4. **预期结果**:新消息自动滚动到视图中 + +#### 场景2:用户在上方时保持位置 +1. 进入聊天页面 +2. 向上滚动查看历史消息 +3. 等待新消息到达(轮询刷新) +4. **预期结果**: + - 滚动位置保持不变 + - 显示新消息提醒气泡 + - 点击气泡可滚动到最新消息 + +#### 场景3:用户发送消息时的自动滚动 +1. 在聊天页面发送文本或图片消息 +2. **预期结果**:自动滚动到刚发送的消息 + +### 3. 调试信息 +已添加控制台日志,可在微信开发者工具的控制台中查看: +- 滚动状态变化 +- 自动滚动触发情况 +- scrollToId 设置情况 + +### 4. 关键日志信息 +- `用户在底部,准备自动滚动到消息ID: xxx` - 触发自动滚动 +- `用户不在底部或无消息,保持当前位置` - 保持位置 +- `用户向上滚动,设置isAtBottom为false` - 状态变化 +- `用户滚动到底部,当前isAtBottom状态: xxx` - 到达底部 + +## 技术实现 + +### 核心逻辑 +```javascript +// 智能滚动:先更新数据,再延迟设置滚动 +that.setData(updateData); + +if (that.data.isAtBottom && sortedList.length > 0) { + setTimeout(() => { + that.setData({ + scrollToId: lastMessageId + }); + }, 50); +} +``` + +### 状态管理 +- `isAtBottom`: 标记用户是否在聊天底部 +- `scrollToId`: 控制滚动到指定消息 +- 延迟50ms确保DOM更新后再滚动 \ No newline at end of file