From e51d0dccdd1749a9f8c30e563603c92cc143917c Mon Sep 17 00:00:00 2001 From: Wenjie Zhang Date: Wed, 27 Aug 2025 11:52:49 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E6=BB=9A=E5=8A=A8=E6=8E=A7=E5=88=B6):=20?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=BB=9A=E5=8A=A8=E9=80=BB=E8=BE=91=E5=B9=B6?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=B5=81=E5=BC=8F=E6=B6=88=E6=81=AF=E6=97=B6?= =?UTF-8?q?=E7=9A=84=E6=BB=9A=E5=8A=A8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/components/AgentChatComponent.vue | 35 ++++++++++++-- web/src/components/AgentMessageComponent.vue | 15 +++++- web/src/components/MessageInputComponent.vue | 2 +- web/src/utils/scrollController.js | 48 +++++++++++++++----- 4 files changed, 83 insertions(+), 17 deletions(-) diff --git a/web/src/components/AgentChatComponent.vue b/web/src/components/AgentChatComponent.vue index 104f46d9..906d31af 100644 --- a/web/src/components/AgentChatComponent.vue +++ b/web/src/components/AgentChatComponent.vue @@ -34,7 +34,7 @@
-
+
{{ agentStore.currentThread?.title }}
@@ -234,6 +234,8 @@ const selectChat = async (chatId) => { if (!AgentValidator.validateAgentIdWithError(agentStore.selectedAgentId, '选择对话', handleValidationError)) return; agentStore.selectThread(chatId); await agentStore.fetchThreadMessages(chatId); + await nextTick(); + scrollController.scrollToBottomStaticForce(); }; const deleteChat = async (chatId) => { @@ -262,7 +264,6 @@ const handleSendMessage = async () => { userInput.value = ''; // Enable auto scroll before sending message to ensure proper scrolling during streaming - scrollController.enableAutoScroll(); await nextTick(); await scrollController.scrollToBottom(true); @@ -328,6 +329,27 @@ const toggleSidebar = () => { const openAgentModal = () => emit('open-agent-modal'); +// ==================== CONVERSATION INFO LOGGING ==================== + +const logConversationInfo = () => { + console.log(agentStore.currentThread); + + // 输出对话历史消息 + console.group('📜 对话历史消息'); + console.log('原始消息数组:', agentStore.currentThreadMessages); + console.log('消息总数:', agentStore.currentThreadMessages.length); + console.groupEnd(); + + // 输出流式对话状态 + if (agentStore.isStreaming || agentStore.onGoingConvMessages.length > 0) { + console.log('进行中的消息:', agentStore.onGoingConvMessages); + console.log('消息块:', agentStore.onGoingConv.msgChunks); + console.groupEnd(); + } + + console.groupEnd(); +}; + // ==================== HELPER FUNCTIONS ==================== const getLastMessage = (conv) => { @@ -375,14 +397,19 @@ const loadChatsList = async () => { onMounted(async () => { await initAll(); + scrollController.enableAutoScroll(); watch(() => agentStore.selectedAgentId, (newAgentId, oldAgentId) => { if (newAgentId && newAgentId !== oldAgentId) { initAll(); } }); + + // 只监听streaming状态,用于流式消息的智能滚动 watch(conversations, () => { - scrollController.scrollToBottom(); - }, { deep: true }); + if (isProcessing.value) { + scrollController.scrollToBottom(); + } + }, { deep: true, flush: 'post' }); }); diff --git a/web/src/components/AgentMessageComponent.vue b/web/src/components/AgentMessageComponent.vue index b7d00dcf..7c9162ca 100644 --- a/web/src/components/AgentMessageComponent.vue +++ b/web/src/components/AgentMessageComponent.vue @@ -546,6 +546,19 @@ const toggleToolCall = (toolCallId) => { color: var(--main-700); } + .md-editor-code { + border: var(--gray-50); + border-radius: 8px; + + .md-editor-code-head { + background-color: var(--gray-50); + + .md-editor-collapse-tips { + color: var(--gray-400); + } + } + } + code { font-size: 13px; font-family: 'Menlo', 'Monaco', 'Consolas', 'PingFang SC', 'Noto Sans SC', 'Microsoft YaHei', 'Hiragino Sans GB', 'Source Han Sans CN', 'Courier New', monospace; @@ -553,7 +566,7 @@ const toggleToolCall = (toolCallId) => { letter-spacing: 0.025em; tab-size: 4; -moz-tab-size: 4; - background-color: var(--gray-100); + background-color: var(--gray-25); } p:last-child { diff --git a/web/src/components/MessageInputComponent.vue b/web/src/components/MessageInputComponent.vue index 13c6c46b..4f3ef962 100644 --- a/web/src/components/MessageInputComponent.vue +++ b/web/src/components/MessageInputComponent.vue @@ -241,7 +241,7 @@ watch(inputValue, () => { // Wait for component to mount before setting up onStartTyping onMounted(() => { - console.log('Component mounted'); + // console.log('Component mounted'); checkOptionsLeft(); nextTick(() => { if (inputRef.value) { diff --git a/web/src/utils/scrollController.js b/web/src/utils/scrollController.js index 002d4d55..8572ad50 100644 --- a/web/src/utils/scrollController.js +++ b/web/src/utils/scrollController.js @@ -8,14 +8,15 @@ export class ScrollController { this.containerSelector = containerSelector; this.options = { threshold: 100, - scrollDelay: 150, - retryDelays: [50, 150, 300], + scrollDelay: 100, + retryDelays: [50, 150], ...options }; - + this.scrollTimer = null; this.isUserScrolling = false; this.shouldAutoScroll = true; + this.isProgrammaticScroll = false; // Bind the context of 'this' for the event handler this.handleScroll = this.handleScroll.bind(this); @@ -49,12 +50,17 @@ export class ScrollController { clearTimeout(this.scrollTimer); } + // 如果是程序性滚动,忽略此次事件 + if (this.isProgrammaticScroll) { + this.isProgrammaticScroll = false; + return; + } + // 标记用户正在滚动 this.isUserScrolling = true; // 检查是否在底部 - const atBottom = this.isAtBottom(); - this.shouldAutoScroll = atBottom; + this.shouldAutoScroll = this.isAtBottom(); // 滚动结束后一段时间重置用户滚动状态 this.scrollTimer = setTimeout(() => { @@ -75,9 +81,12 @@ export class ScrollController { const container = this.getContainer(); if (!container) return; - const scrollOptions = { - top: container.scrollHeight, - behavior: 'smooth' + // 标记为程序性滚动 + this.isProgrammaticScroll = true; + + const scrollOptions = { + top: container.scrollHeight, + behavior: 'smooth' }; // 立即滚动 @@ -87,16 +96,32 @@ export class ScrollController { this.options.retryDelays.forEach((delay, index) => { setTimeout(() => { if (force || this.shouldAutoScroll) { + this.isProgrammaticScroll = true; const behavior = index === this.options.retryDelays.length - 1 ? 'auto' : 'smooth'; - container.scrollTo({ - top: container.scrollHeight, - behavior + container.scrollTo({ + top: container.scrollHeight, + behavior }); } }, delay); }); } + async scrollToBottomStaticForce() { + const container = this.getContainer(); + if (!container) return; + + // 标记为程序性滚动 + this.isProgrammaticScroll = true; + + const scrollOptions = { + top: container.scrollHeight, + behavior: 'auto' + }; + + container.scrollTo(scrollOptions); + } + /** * 启用自动滚动 */ @@ -139,6 +164,7 @@ export class ScrollController { this.cleanup(); this.isUserScrolling = false; this.shouldAutoScroll = true; + this.isProgrammaticScroll = false; } }