fix: 修复代码块复制、事件去重、任务结束、停止按钮bug
Phase 1 关键Bug修复: 1. 代码块复制功能 - 删除 index.html 中的全局事件监听(47-53行) - 避免与 Vue 方法的双重监听冲突 2. 事件去重机制 - 在 handleTaskEvent() 添加基于 idx 的去重检查 - 使用 Set 存储已处理的事件索引(最多1000个) - 在 restoreTaskState() 时清理已处理事件 3. 任务结束逻辑 - 修复 handleTaskComplete() 异步状态清理问题 - 新增 clearTaskState() 统一清理方法 - 在 stopPolling() 中清除 currentTaskId 4. 停止按钮状态同步 - 改进 stopTask() 错误处理 - 添加后端确认等待(300ms) - 确保失败时也清理前端状态 - 添加 finally 块清除 dropToolEvents Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
ce02ea21fb
commit
de82a37958
@ -44,13 +44,6 @@
|
||||
button.setAttribute('aria-label', button.dataset.originalLabel || '复制代码');
|
||||
});
|
||||
}
|
||||
document.addEventListener('click', function(e) {
|
||||
const target = e.target;
|
||||
if (target && target.classList && target.classList.contains('copy-code-btn')) {
|
||||
const blockId = target.getAttribute('data-code');
|
||||
if (blockId) copyCodeBlock(blockId);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script src="/static/easter-eggs/registry.js"></script>
|
||||
<script src="/static/easter-eggs/flood.js"></script>
|
||||
|
||||
@ -261,24 +261,40 @@ export const messageMethods = {
|
||||
this.stopRequested = true;
|
||||
this.dropToolEvents = shouldDropToolEvents;
|
||||
|
||||
// 使用 REST API 取消任务
|
||||
try {
|
||||
const { useTaskStore } = await import('../../stores/task');
|
||||
const taskStore = useTaskStore();
|
||||
|
||||
if (taskStore.currentTaskId) {
|
||||
await taskStore.cancelTask();
|
||||
debugLog('[Message] 任务已取消');
|
||||
}
|
||||
|
||||
// 等待后端确认
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
|
||||
// 清理前端状态
|
||||
this.clearPendingTools('user_stop');
|
||||
this.streamingMessage = false;
|
||||
this.taskInProgress = false;
|
||||
this.forceUnlockMonitor('user_stop');
|
||||
} catch (error) {
|
||||
console.error('[Message] 取消任务失败:', error);
|
||||
}
|
||||
|
||||
// 立即清理前端状态,避免出现"不可输入也不可停止"的卡死状态
|
||||
this.clearPendingTools('user_stop');
|
||||
this.streamingMessage = false;
|
||||
this.taskInProgress = false;
|
||||
this.forceUnlockMonitor('user_stop');
|
||||
// 即使失败也清理状态
|
||||
this.clearPendingTools('user_stop');
|
||||
this.streamingMessage = false;
|
||||
this.taskInProgress = false;
|
||||
this.forceUnlockMonitor('user_stop');
|
||||
|
||||
this.uiPushToast({
|
||||
title: '停止失败',
|
||||
message: '任务可能仍在运行,请刷新页面',
|
||||
type: 'warning'
|
||||
});
|
||||
} finally {
|
||||
// 确保清除 dropToolEvents 标志
|
||||
this.dropToolEvents = false;
|
||||
}
|
||||
},
|
||||
|
||||
async clearChat() {
|
||||
|
||||
@ -18,6 +18,26 @@ export const taskPollingMethods = {
|
||||
const eventData = event.data || {};
|
||||
const eventIdx = event.idx;
|
||||
|
||||
// 事件去重检查
|
||||
if (typeof eventIdx === 'number') {
|
||||
if (!this._processedEventIndices) {
|
||||
this._processedEventIndices = new Set();
|
||||
}
|
||||
|
||||
if (this._processedEventIndices.has(eventIdx)) {
|
||||
debugLog(`[TaskPolling] 跳过重复事件 #${eventIdx}`);
|
||||
return;
|
||||
}
|
||||
|
||||
this._processedEventIndices.add(eventIdx);
|
||||
|
||||
// 限制 Set 大小(保留最近 1000 个)
|
||||
if (this._processedEventIndices.size > 1000) {
|
||||
const firstIdx = Math.min(...this._processedEventIndices);
|
||||
this._processedEventIndices.delete(firstIdx);
|
||||
}
|
||||
}
|
||||
|
||||
// 检查事件的 conversation_id 是否匹配当前对话
|
||||
// 如果不匹配,忽略该事件(避免切换对话后旧任务的事件显示到新对话中)
|
||||
if (eventData.conversation_id && this.currentConversationId) {
|
||||
@ -529,31 +549,42 @@ export const taskPollingMethods = {
|
||||
} else {
|
||||
debugLog('[TaskPolling] 任务完成');
|
||||
}
|
||||
|
||||
// 同步处理状态更新
|
||||
this.streamingMessage = false;
|
||||
this.stopRequested = false;
|
||||
|
||||
if (hasRunningSubAgents) {
|
||||
this.taskInProgress = true;
|
||||
this.waitingForSubAgent = true;
|
||||
} else {
|
||||
this.taskInProgress = false;
|
||||
this.waitingForSubAgent = false;
|
||||
this.clearTaskState(); // 清理任务状态
|
||||
}
|
||||
|
||||
this.$forceUpdate();
|
||||
|
||||
// 停止轮询
|
||||
// 只更新统计,不重新加载历史
|
||||
if (this.currentConversationId) {
|
||||
setTimeout(() => {
|
||||
this.fetchConversationTokenStatistics();
|
||||
this.updateCurrentContextTokens();
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
|
||||
// 新增统一清理方法
|
||||
clearTaskState() {
|
||||
(async () => {
|
||||
const { useTaskStore } = await import('../../stores/task');
|
||||
const taskStore = useTaskStore();
|
||||
taskStore.stopPolling();
|
||||
taskStore.clearTask(); // 使用 clearTask 而不是 stopPolling
|
||||
})();
|
||||
|
||||
// 只更新统计,不重新加载历史(避免重复显示)
|
||||
setTimeout(() => {
|
||||
if (this.currentConversationId) {
|
||||
this.fetchConversationTokenStatistics();
|
||||
this.updateCurrentContextTokens();
|
||||
}
|
||||
}, 500);
|
||||
if (typeof this.clearProcessedEvents === 'function') {
|
||||
this.clearProcessedEvents();
|
||||
}
|
||||
},
|
||||
|
||||
handleUserMessage(data: any) {
|
||||
@ -631,10 +662,22 @@ export const taskPollingMethods = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 清理已处理的事件索引
|
||||
*/
|
||||
clearProcessedEvents() {
|
||||
if (this._processedEventIndices) {
|
||||
this._processedEventIndices.clear();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 恢复任务状态(页面刷新后调用)
|
||||
*/
|
||||
async restoreTaskState() {
|
||||
// 清理已处理的事件索引
|
||||
this.clearProcessedEvents();
|
||||
|
||||
try {
|
||||
const { useTaskStore } = await import('../../stores/task');
|
||||
const taskStore = useTaskStore();
|
||||
|
||||
@ -180,6 +180,7 @@ export const useTaskStore = defineStore('task', {
|
||||
this.pollingInterval = null;
|
||||
}
|
||||
this.isPolling = false;
|
||||
this.currentTaskId = null; // 清除任务 ID
|
||||
},
|
||||
|
||||
async cancelTask() {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user