From 0d4f6c9e56c5868f4aaa40c9aba6646e627be93c Mon Sep 17 00:00:00 2001 From: JOJO <1498581755@qq.com> Date: Mon, 8 Jun 2026 02:27:37 +0800 Subject: [PATCH] =?UTF-8?q?fix(frontend):=20=E4=BF=AE=E5=A4=8D=E5=AF=B9?= =?UTF-8?q?=E8=AF=9D=E5=8E=8B=E7=BC=A9=E5=90=8E=E9=A1=B5=E9=9D=A2=E6=8A=BD?= =?UTF-8?q?=E6=90=90=E3=80=81=E9=87=8D=E5=A4=8D=E6=98=BE=E7=A4=BA=E4=B8=8E?= =?UTF-8?q?=E6=BB=9A=E5=8A=A8=E9=87=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 三管齐下,消除 in-place 压缩触发的多余重载: 1. handleTaskEvent: _rebuildingFromScratch 重放时跳过 compression_finished/compression_state/shallow_compression, 打破 restoreTaskState 重放 → 再次触发压缩完成的无限循环。 2. handleCompressionFinished + compressConversation: in-place 压缩(对话 id 不变)跳过 loadConversation + restoreTaskState + loadConversationsList, 避免 resetAllStates 重置滚动状态和清空消息列表。 3. useLegacySocket conversation_loaded: 同对话 clear_ui 跳过 resetAllStates + fetchAndDisplayHistory, 避免后端 Socket.IO 推送触发二次重置。 --- static/src/app/methods/message.ts | 13 +++++++---- static/src/app/methods/taskPolling.ts | 28 +++++++++++++++-------- static/src/composables/useLegacySocket.ts | 4 ++++ 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/static/src/app/methods/message.ts b/static/src/app/methods/message.ts index adbc6c2..2a5aebf 100644 --- a/static/src/app/methods/message.ts +++ b/static/src/app/methods/message.ts @@ -1073,9 +1073,11 @@ export const messageMethods = { if (response.ok && result.success) { this.compressionStage = 'switching'; - // in-place 压缩:对话 id 不变,重新加载以刷新“已压缩历史”展示。 const newId = result.compressed_conversation_id; - if (newId) { + const isInPlace = newId && newId === this.currentConversationId; + // in-place 压缩:对话 id 不变,不重新加载(避免 resetAllStates + // 重置滚动状态 + fetchAndDisplayHistory 清空重渲染导致闪烁和锁死)。 + if (newId && !isInPlace) { await this.loadConversation(newId, { force: true }); } const guideMessage = (result.guide_message || '').trim(); @@ -1099,15 +1101,16 @@ export const messageMethods = { } } else if (compressBehavior === 'wait') { // 等待用户:后端已把引导语作为 user 消息追加进历史,这里仅刷新展示,不触发请求。 - if (newId) { + if (newId && !isInPlace) { await this.loadConversation(newId, { force: true }); } } else if (guideMessage) { await this.sendAutoUserMessage(guideMessage); } - this.conversationsOffset = 0; - await this.loadConversationsList(); + if (!isInPlace) { + await this.loadConversationsList(); + } debugLog('对话压缩完成:', result); this.uiPushToast({ diff --git a/static/src/app/methods/taskPolling.ts b/static/src/app/methods/taskPolling.ts index 03d262b..d80eaf2 100644 --- a/static/src/app/methods/taskPolling.ts +++ b/static/src/app/methods/taskPolling.ts @@ -756,12 +756,18 @@ export const taskPollingMethods = { this.handleConversationResolved(eventData, eventIdx); break; case 'compression_state': + // 重放历史事件时不处理管理类事件,避免触发副作用(如 toast 闪烁 / 状态回退)。 + if (this._rebuildingFromScratch) break; this.handleCompressionState(eventData, eventIdx); break; case 'compression_finished': + // 重放历史事件时不处理管理类事件,避免 restoreTaskState → 重放 → 再次触发 compression_finished 的无限循环。 + if (this._rebuildingFromScratch) break; this.handleCompressionFinished(eventData, eventIdx); break; case 'shallow_compression': + // 同上。 + if (this._rebuildingFromScratch) break; this.handleShallowCompression(eventData, eventIdx); break; @@ -2431,19 +2437,21 @@ export const taskPollingMethods = { this.compressionMode = ''; this.compressionStage = ''; this.compressionError = ''; - // in-place 压缩:对话 id 不变。重新加载该对话以刷新“已压缩历史”的展示; - // 若返回的 id 与当前不一致(兼容旧行为)也按其加载。 const newId = data?.conversation_id; - if (newId) { + const isInPlace = newId && newId === this.currentConversationId; + if (newId && !isInPlace) { + // 旧行为兼容:压缩产生了新对话 id(非 in-place),需要切换并重新加载。 await this.loadConversation(newId, { force: true }); + this.conversationsOffset = 0; + if (typeof this.loadConversationsList === 'function') { + await this.loadConversationsList(); + } + await this.refreshRunningWorkspaceTasks?.(); + await this.restoreTaskState?.(); } - this.conversationsOffset = 0; - if (typeof this.loadConversationsList === 'function') { - await this.loadConversationsList(); - } - await this.refreshRunningWorkspaceTasks?.(); - // 续接任务仍在同一对话内运行,恢复轮询以持续显示进度。 - await this.restoreTaskState?.(); + // in-place 压缩:对话 id 不变,不重新加载对话,也不触发 restoreTaskState。 + // loadConversation 会 clearTask 停止轮询才需要 restore;in-place 跳过了 + // loadConversation,轮询仍在运行,restoreTaskState 的 rebuild 反而会 this.uiPushToast({ title: '压缩完成', message: '已压缩较早的对话内容', diff --git a/static/src/composables/useLegacySocket.ts b/static/src/composables/useLegacySocket.ts index bdfaa77..f0df98b 100644 --- a/static/src/composables/useLegacySocket.ts +++ b/static/src/composables/useLegacySocket.ts @@ -841,6 +841,10 @@ export async function initializeLegacySocket(ctx: any) { if (!forceReload && (historyLoadingSame || historyReady)) { socketLog('conversation_loaded: 历史已加载/加载中,跳过重复刷新'); + } else if (data.clear_ui && targetConversationId === ctx.currentConversationId) { + // in-place 压缩等同一对话内的刷新:不 resetAllStates(避免重置滚动状态 + // 导致锁死),不重新 fetchAndDisplayHistory(避免消息闪烁)。 + socketLog('conversation_loaded: 同对话 clear_ui,跳过重置以保持滚动状态'); } else { if (data.clear_ui) { // 清理当前UI状态,准备显示历史内容