chore:进入客服页面不乱跳了
This commit is contained in:
@@ -5,8 +5,8 @@
|
|||||||
class="chat-messages"
|
class="chat-messages"
|
||||||
scroll-y
|
scroll-y
|
||||||
:scroll-top="scrollTop"
|
:scroll-top="scrollTop"
|
||||||
@scrolltoupper="loadMoreHistory"
|
@scroll="onScroll"
|
||||||
:scroll-with-animation="true">
|
:scroll-with-animation="false">
|
||||||
|
|
||||||
<!-- 加载更多历史消息 -->
|
<!-- 加载更多历史消息 -->
|
||||||
<view class="load-more" v-if="hasMoreHistory">
|
<view class="load-more" v-if="hasMoreHistory">
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
<!-- 消息列表 -->
|
<!-- 消息列表 -->
|
||||||
<view
|
<view
|
||||||
v-for="(message, index) in messages"
|
v-for="(message, index) in messages"
|
||||||
:key="message.id + '-' + index"
|
:key="`msg-${message.id || message.timestamp}-${index}`"
|
||||||
class="message-item"
|
class="message-item"
|
||||||
:class="[message.role, { 'first-message': index === 0 }]">
|
:class="[message.role, { 'first-message': index === 0 }]">
|
||||||
|
|
||||||
@@ -402,7 +402,7 @@ export default {
|
|||||||
default: 20
|
default: 20
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data(){
|
||||||
return {
|
return {
|
||||||
messages: [],
|
messages: [],
|
||||||
inputText: '',
|
inputText: '',
|
||||||
@@ -437,7 +437,11 @@ export default {
|
|||||||
isAIServiceAvailable: true,
|
isAIServiceAvailable: true,
|
||||||
aiServiceError: null,
|
aiServiceError: null,
|
||||||
isLoadingHistory: false, // 新增:标记历史加载状态
|
isLoadingHistory: false, // 新增:标记历史加载状态
|
||||||
shouldScrollToBottom: false
|
shouldScrollToBottom: false,
|
||||||
|
scrollTimer: null,
|
||||||
|
lastScrollTop: 0,
|
||||||
|
historyOffset: 0,
|
||||||
|
isFetchingHistory: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onShow() {
|
onShow() {
|
||||||
@@ -449,10 +453,17 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
this.currentConversationId = aiService.getConversationId();
|
this.currentConversationId = aiService.getConversationId();
|
||||||
}
|
}
|
||||||
this.loadChatHistoryIfExist();
|
// 加载初始历史后,同步historyOffset
|
||||||
|
this.loadChatHistoryIfExist().then(() => {
|
||||||
|
// 初始历史条数作为偏移量
|
||||||
},
|
this.historyOffset = this.messages.length;
|
||||||
|
});
|
||||||
|
// 移除:this.historyOffset = 0;(避免覆盖初始历史的偏移量)
|
||||||
|
},
|
||||||
|
// 新增:组件销毁时清除防抖定时器
|
||||||
|
beforeDestroy() {
|
||||||
|
if (this.scrollTimer) clearTimeout(this.scrollTimer);
|
||||||
|
},
|
||||||
created() {
|
created() {
|
||||||
this.messages = [...this.initialMessages]
|
this.messages = [...this.initialMessages]
|
||||||
this.messageId = this.messages.length
|
this.messageId = this.messages.length
|
||||||
@@ -505,7 +516,18 @@ export default {
|
|||||||
}
|
}
|
||||||
// #endif
|
// #endif
|
||||||
},
|
},
|
||||||
|
onScroll(e) {
|
||||||
|
const currentScrollTop = e.detail.scrollTop;
|
||||||
|
this.lastScrollTop = currentScrollTop;
|
||||||
|
// 防抖处理:50ms内只执行一次,避免频繁触发
|
||||||
|
if (this.scrollTimer) clearTimeout(this.scrollTimer);
|
||||||
|
this.scrollTimer = setTimeout(() => {
|
||||||
|
// 滚动到顶部附近(<20px)、不在加载中、显示加载更多时,触发加载
|
||||||
|
if (currentScrollTop < 20 && !this.isLoadingHistory && this.showLoadMore) {
|
||||||
|
this.loadMoreHistory();
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
},
|
||||||
// 初始化用户头像(支持缓存持久化)
|
// 初始化用户头像(支持缓存持久化)
|
||||||
initUserAvatarData() {
|
initUserAvatarData() {
|
||||||
// 1. 优先读取本地缓存的头像
|
// 1. 优先读取本地缓存的头像
|
||||||
@@ -927,68 +949,76 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// 加载更多历史消息
|
// 加载更多历史消息
|
||||||
async loadMoreHistory() {
|
async loadMoreHistory() {
|
||||||
if (!this.currentConversationId) {
|
if (!this.currentConversationId) {
|
||||||
this.currentConversationId = this.getConversationIdFromLocal();
|
this.currentConversationId = this.getConversationIdFromLocal();
|
||||||
aiService.setConversationId(this.currentConversationId);
|
aiService.setConversationId(this.currentConversationId);
|
||||||
}
|
}
|
||||||
if (!this.showLoadMore || !this.currentConversationId || this.isLoadingHistory) {
|
// 防止重复加载
|
||||||
return
|
if (!this.showLoadMore || !this.currentConversationId || this.isLoadingHistory) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
this.isLoadingHistory = true
|
this.isFetchingHistory = true;
|
||||||
this.loadingText = '加载历史消息中...'
|
this.isLoadingHistory = true;
|
||||||
this.hasMoreHistory = true
|
this.loadingText = '加载历史消息中...';
|
||||||
|
this.hasMoreHistory = true;
|
||||||
try {
|
|
||||||
const response = await aiService.getConversationHistory({
|
try {
|
||||||
conversation_id: this.currentConversationId,
|
// 关键修改:用独立的historyOffset作为偏移量,不再依赖messages.length
|
||||||
limit: 20,
|
const response = await aiService.getConversationHistory({
|
||||||
offset: this.messages.length
|
conversation_id: this.currentConversationId,
|
||||||
})
|
limit: 20,
|
||||||
|
offset: this.historyOffset // 改用独立偏移量
|
||||||
if (response.success && Array.isArray(response.messages)) {
|
});
|
||||||
const historyMessages = response.messages.map(msg => ({
|
|
||||||
id: msg.id,
|
if (response.success && Array.isArray(response.messages) && response.messages.length > 0) {
|
||||||
role: msg.role === 'user' ? 'user' : 'ai',
|
const historyMessages = response.messages.map(msg => ({
|
||||||
type: 'text',
|
id: msg.id || `history-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
||||||
content: msg.content,
|
role: msg.role === 'user' ? 'user' : 'ai',
|
||||||
timestamp: msg.create_time * 1000
|
type: 'text',
|
||||||
}))
|
content: msg.content,
|
||||||
|
timestamp: msg.create_time * 1000
|
||||||
if (historyMessages.length > 0) {
|
}));
|
||||||
this.messages = [...historyMessages, ...this.messages]
|
|
||||||
|
// 1. 记录新增消息数量
|
||||||
// 👇 注意:这里加了 async
|
const newMsgCount = historyMessages.length;
|
||||||
this.$nextTick(async () => {
|
// 2. 添加到消息列表开头
|
||||||
const query = uni.createSelectorQuery().in(this)
|
this.messages = [...historyMessages, ...this.messages];
|
||||||
query.select('.chat-messages').boundingClientRect()
|
// 3. 更新历史偏移量(累加,不是覆盖)
|
||||||
query.select('.chat-messages').scrollOffset()
|
this.historyOffset += newMsgCount;
|
||||||
|
|
||||||
const res = await new Promise((resolve) => {
|
// 4. 等待DOM渲染后,精准计算滚动位置
|
||||||
query.exec(resolve)
|
await this.$nextTick();
|
||||||
})
|
// 获取新增消息的实际DOM高度
|
||||||
|
const query = uni.createSelectorQuery().in(this);
|
||||||
const currentScrollTop = res[1]?.scrollTop || 0
|
const heightPromises = [];
|
||||||
const avgMessageHeight = 80
|
for (let i = 0; i < newMsgCount; i++) {
|
||||||
const addedHeight = historyMessages.length * avgMessageHeight
|
heightPromises.push(new Promise(resolve => {
|
||||||
|
query.select(`.message-item:nth-child(${i + 1})`).boundingClientRect(rect => {
|
||||||
this.scrollTop = currentScrollTop - addedHeight
|
resolve(rect ? rect.height + 32 : 0); // 加上消息间距(32rpx)
|
||||||
})
|
}).exec();
|
||||||
}
|
}));
|
||||||
} else {
|
}
|
||||||
this.showLoadMore = false
|
// 计算总高度
|
||||||
}
|
const heights = await Promise.all(heightPromises);
|
||||||
} catch (error) {
|
const totalNewHeight = heights.reduce((sum, h) => sum + h, 0);
|
||||||
console.error('加载历史失败:', error)
|
// 5. 设置scrollTop,保持滚动条在顶部附近
|
||||||
uni.showToast({ title: '加载历史失败', icon: 'none' })
|
this.scrollTop = totalNewHeight;
|
||||||
this.showLoadMore = false
|
} else {
|
||||||
} finally {
|
// 无更多历史
|
||||||
this.isLoadingHistory = false
|
this.showLoadMore = false;
|
||||||
this.hasMoreHistory = false
|
}
|
||||||
this.loadingText = '加载更多历史消息'
|
} catch (error) {
|
||||||
}
|
console.error('加载历史失败:', error);
|
||||||
},
|
uni.showToast({ title: '加载历史失败', icon: 'none' });
|
||||||
|
this.showLoadMore = false;
|
||||||
|
} finally {
|
||||||
|
this.isLoadingHistory = false;
|
||||||
|
this.isFetchingHistory = false;
|
||||||
|
this.hasMoreHistory = false;
|
||||||
|
this.loadingText = '加载更多历史消息';
|
||||||
|
}
|
||||||
|
},
|
||||||
// 显示更多工具
|
// 显示更多工具
|
||||||
showMoreTools() {
|
showMoreTools() {
|
||||||
this.showToolsPanel = true
|
this.showToolsPanel = true
|
||||||
|
|||||||
Reference in New Issue
Block a user