diff --git a/web/static/js/chat.js b/web/static/js/chat.js index ffbab403..3ce9b9df 100644 --- a/web/static/js/chat.js +++ b/web/static/js/chat.js @@ -711,9 +711,18 @@ function addAttackChainButton(conversationId) { } if (conversationId) { - attackChainBtn.disabled = false; - attackChainBtn.title = '查看当前对话的攻击链'; - attackChainBtn.onclick = () => showAttackChain(conversationId); + const isRunning = typeof isConversationTaskRunning === 'function' + ? isConversationTaskRunning(conversationId) + : false; + if (isRunning) { + attackChainBtn.disabled = true; + attackChainBtn.title = '当前对话正在执行,请稍后再生成攻击链'; + attackChainBtn.onclick = null; + } else { + attackChainBtn.disabled = false; + attackChainBtn.title = '查看当前对话的攻击链'; + attackChainBtn.onclick = () => showAttackChain(conversationId); + } } else { attackChainBtn.disabled = true; attackChainBtn.title = '请选择一个对话以查看攻击链'; @@ -721,6 +730,10 @@ function addAttackChainButton(conversationId) { } } +function updateAttackChainAvailability() { + addAttackChainButton(currentConversationId); +} + // 显示攻击链模态框 async function showAttackChain(conversationId) { // 防止重复点击 diff --git a/web/static/js/monitor.js b/web/static/js/monitor.js index ade2b064..144dd7ee 100644 --- a/web/static/js/monitor.js +++ b/web/static/js/monitor.js @@ -1,6 +1,30 @@ const progressTaskState = new Map(); let activeTaskInterval = null; const ACTIVE_TASK_REFRESH_INTERVAL = 10000; // 10秒检查一次 +const TASK_FINAL_STATUSES = new Set(['failed', 'timeout', 'cancelled', 'completed']); + +const conversationExecutionTracker = { + activeConversations: new Set(), + update(tasks = []) { + this.activeConversations.clear(); + tasks.forEach(task => { + if ( + task && + task.conversationId && + !TASK_FINAL_STATUSES.has(task.status) + ) { + this.activeConversations.add(task.conversationId); + } + }); + }, + isRunning(conversationId) { + return !!conversationId && this.activeConversations.has(conversationId); + } +}; + +function isConversationTaskRunning(conversationId) { + return conversationExecutionTracker.isRunning(conversationId); +} function registerProgressTask(progressId, conversationId = null) { const state = progressTaskState.get(progressId) || {}; @@ -793,7 +817,13 @@ function renderActiveTasks(tasks) { const bar = document.getElementById('active-tasks-bar'); if (!bar) return; - if (!tasks || tasks.length === 0) { + const normalizedTasks = Array.isArray(tasks) ? tasks : []; + conversationExecutionTracker.update(normalizedTasks); + if (typeof updateAttackChainAvailability === 'function') { + updateAttackChainAvailability(); + } + + if (normalizedTasks.length === 0) { bar.style.display = 'none'; bar.innerHTML = ''; return; @@ -802,7 +832,7 @@ function renderActiveTasks(tasks) { bar.style.display = 'flex'; bar.innerHTML = ''; - tasks.forEach(task => { + normalizedTasks.forEach(task => { const item = document.createElement('div'); item.className = 'active-task-item';