1.优化AI聊天页面效果

This commit is contained in:
2025-08-19 10:32:35 +08:00
parent a2f6d5573b
commit 5988c81e3e

View File

@@ -168,6 +168,13 @@ export default {
// 组件销毁标志 // 组件销毁标志
isDestroyed: false, isDestroyed: false,
// 性能优化相关
scrollThrottleTimer: null, // 滚动节流定时器
contentUpdateTimer: null, // 内容更新节流定时器
isUserAtBottom: true, // 用户是否在底部
lastContentLength: 0, // 上次内容长度
scrollPending: false, // 滚动待处理标志
// 性能优化和错误边界 // 性能优化和错误边界
requestQueue: [], requestQueue: [],
maxRetries: 2, maxRetries: 2,
@@ -283,6 +290,14 @@ export default {
clearTimeout(this.loadDebounceTimer) clearTimeout(this.loadDebounceTimer)
this.loadDebounceTimer = null this.loadDebounceTimer = null
} }
if (this.scrollThrottleTimer) {
clearTimeout(this.scrollThrottleTimer)
this.scrollThrottleTimer = null
}
if (this.contentUpdateTimer) {
clearTimeout(this.contentUpdateTimer)
this.contentUpdateTimer = null
}
// 取消正在进行的请求 // 取消正在进行的请求
if (this.currentCancel) { if (this.currentCancel) {
@@ -809,8 +824,8 @@ export default {
if (data.event === 'message' && data.answer) { if (data.event === 'message' && data.answer) {
const currentContent = aiMsg.content const currentContent = aiMsg.content
const newContent = (currentContent === '正在思考...' ? '' : currentContent) + data.answer const newContent = (currentContent === '正在思考...' ? '' : currentContent) + data.answer
aiMsg.content = newContent // 使用节流更新方法减少频繁的DOM更新和滚动
this.scrollToBottom(true) // 流式响应时使用平滑滚动 this.throttledContentUpdate(aiMsg, newContent)
} }
// 处理结束消息 // 处理结束消息
@@ -825,12 +840,19 @@ export default {
userMsg.messageId = data.message_id userMsg.messageId = data.message_id
aiMsg.messageId = 'ai-' + data.message_id aiMsg.messageId = 'ai-' + data.message_id
} }
if (data.retriever_resources) { if (data.metadata && data.metadata.retriever_resources) {
aiMsg.retrieverResources = data.retriever_resources // 使用Vue的响应式方法确保引用信息正确更新
aiMsg.groupedReferences = this.getGroupedReferences(data.retriever_resources) // 预计算分组引用 this.$set(aiMsg, 'retrieverResources', data.metadata.retriever_resources)
this.$set(aiMsg, 'groupedReferences', this.getGroupedReferences(data.metadata.retriever_resources))
} }
// 标记流输出完成 // 标记流输出完成并触发最终滚动
aiMsg.streamCompleted = true aiMsg.streamCompleted = true
// 重置内容长度计数器
this.lastContentLength = 0
// 流式响应结束后强制滚动到底部
this.$nextTick(() => {
this.scrollToBottom(true, true) // 使用平滑滚动并强制执行
})
} }
// 处理错误事件 // 处理错误事件
@@ -933,45 +955,48 @@ export default {
}, },
/** /**
* 滚动到底部 * 滚动到底部(优化版本)
*/ */
scrollToBottom(smooth = false) { scrollToBottom(smooth = false, force = false) {
// 如果用户不在底部且不是强制滚动,则跳过
if (!force && !this.isUserAtBottom) {
return
}
// 节流处理,避免频繁滚动
if (this.scrollThrottleTimer) {
clearTimeout(this.scrollThrottleTimer)
}
this.scrollThrottleTimer = setTimeout(() => {
this.$nextTick(() => { this.$nextTick(() => {
if (this.$refs.messageList) { if (this.$refs.messageList && !this.isDestroyed) {
const targetTop = this.$refs.messageList.scrollHeight
const messageList = this.$refs.messageList const messageList = this.$refs.messageList
const scrollHeight = messageList.scrollHeight
if (smooth && messageList.scrollTo && !this.isLoadingHistory) { if (smooth && messageList.scrollTo && !this.isLoadingHistory) {
// 只在非加载状态时使用平滑滚动 // 只在非加载状态时使用平滑滚动
messageList.scrollTo({ messageList.scrollTo({
top: messageList.scrollHeight, top: scrollHeight,
behavior: 'smooth' behavior: 'smooth'
}) })
} else { } else {
// 强制立即滚动到底部 // 立即滚动到底部
const forceScrollToBottom = () => { messageList.scrollTop = scrollHeight
messageList.scrollTop = messageList.scrollHeight
} }
// 立即执行一次 // 更新用户位置状态
forceScrollToBottom() this.isUserAtBottom = true
// 再次确保滚动到底部 // 延迟更新lastScrollTop
this.$nextTick(() => {
forceScrollToBottom()
// 第三次确保
setTimeout(() => { setTimeout(() => {
forceScrollToBottom() if (!this.isDestroyed) {
}, 10) this.lastScrollTop = scrollHeight
})
} }
}, smooth ? 300 : 50)
// 延迟更新lastScrollTop避免干扰滚动检测
setTimeout(() => {
this.lastScrollTop = this.$refs.messageList.scrollHeight
}, smooth ? 300 : 50) // 非平滑滚动时更快更新
} }
}) })
}, smooth ? 0 : 16) // 非平滑滚动时使用16ms节流约60fps
}, },
/** /**
@@ -979,6 +1004,11 @@ export default {
*/ */
onScroll(e) { onScroll(e) {
const scrollTop = e.target.scrollTop const scrollTop = e.target.scrollTop
const scrollHeight = e.target.scrollHeight
const clientHeight = e.target.clientHeight
// 检测用户是否在底部允许10px的误差
this.isUserAtBottom = (scrollTop + clientHeight >= scrollHeight - 10)
// 检查是否需要加载历史记录 // 检查是否需要加载历史记录
// 当滚动到距离顶部阈值范围内且向上滚动时触发加载 // 当滚动到距离顶部阈值范围内且向上滚动时触发加载
@@ -1036,6 +1066,37 @@ export default {
return sanitizedHtml return sanitizedHtml
}, },
/**
* 节流更新内容
*/
throttledContentUpdate(aiMsg, newContent) {
// 检查内容是否真的有变化
if (aiMsg.content === newContent) {
return
}
// 立即更新内容(保证响应性)
aiMsg.content = newContent
// 只有当内容长度显著增加时才滚动
const contentLengthDiff = newContent.length - this.lastContentLength
if (contentLengthDiff > 20) { // 内容增加超过20个字符才滚动
this.lastContentLength = newContent.length
// 清除之前的定时器
if (this.contentUpdateTimer) {
clearTimeout(this.contentUpdateTimer)
}
// 节流滚动更新
this.contentUpdateTimer = setTimeout(() => {
if (this.isUserAtBottom && !this.isDestroyed) {
this.scrollToBottom(false)
}
}, 100) // 100ms节流减少滚动频率
}
},
/** /**
* 处理点赞 * 处理点赞
*/ */