diff --git a/frontend/src/layout/components/Main/TaskTemplate/TaskResult/index.vue b/frontend/src/layout/components/Main/TaskTemplate/TaskResult/index.vue index 731a923..c0c1182 100644 --- a/frontend/src/layout/components/Main/TaskTemplate/TaskResult/index.vue +++ b/frontend/src/layout/components/Main/TaskTemplate/TaskResult/index.vue @@ -43,31 +43,6 @@ const stepExecutionStatus = ref>({}) // 用于标记暂停时的"最后动作完成"状态 const isPausing = ref(false) // 正在请求暂停(等待当前动作完成) -// ==================== 步骤版本追踪 ==================== -// 步骤版本信息接口 -interface StepVersionInfo { - stepId: string // 步骤ID - stepIndex: number // 步骤索引(0-based) - originalHash: string // 原始配置hash(初始化时生成) - currentHash: string // 当前配置hash(编辑后更新) - isModified: boolean // 是否已修改 -} - -// 重执行配置接口 -interface ReExecuteConfig { - shouldReExecute: boolean // 是否需要重新执行 - startFromStepIndex: number // 从哪个步骤开始(-1表示从头开始) - modifiedSteps: string[] // 被修改的步骤ID列表 -} - -// 步骤版本追踪 -const stepVersions = ref>({}) -const reExecuteConfig = ref({ - shouldReExecute: false, - startFromStepIndex: -1, - modifiedSteps: [] -}) - // Check if step is ready to execute (has TaskProcess data) const isStepReady = (step: IRawStepTask) => { return step.TaskProcess && step.TaskProcess.length > 0 @@ -130,155 +105,6 @@ const stepsReadyStatus = computed(() => { } }) -// ==================== 步骤版本追踪函数 ==================== -/** - * 生成步骤配置的hash(用于检测修改) - * @param step 步骤对象 - * @returns hash字符串 - */ -function generateStepHash(step: IRawStepTask): string { - // 只考虑TaskProcess中的Description字段 - // 因为这是用户可以编辑的部分 - const processDescriptions = step.TaskProcess.map(p => `${p.ID}:${p.Description}`).join('|') - - // 简单hash算法 - let hash = 0 - for (let i = 0; i < processDescriptions.length; i++) { - const char = processDescriptions.charCodeAt(i) - hash = (hash << 5) - hash + char - hash = hash & hash // Convert to 32bit integer - } - return Math.abs(hash).toString(36) -} - -/** - * 初始化步骤版本(在任务加载完成后调用) - */ -function initializeStepVersions() { - const steps = collaborationProcess.value - stepVersions.value = {} - - steps.forEach((step, index) => { - const stepId = step.Id || step.StepName || `step-${index}` - const hash = generateStepHash(step) - - stepVersions.value[stepId] = { - stepId, - stepIndex: index, - originalHash: hash, - currentHash: hash, - isModified: false - } - }) - - // 重置重执行配置 - reExecuteConfig.value = { - shouldReExecute: false, - startFromStepIndex: -1, - modifiedSteps: [] - } -} - -/** - * 更新步骤版本(编辑保存后调用) - */ -function updateStepVersion(stepId: string) { - const step = collaborationProcess.value.find(s => (s.Id || s.StepName) === stepId) - - if (step && stepVersions.value[stepId]) { - const newHash = generateStepHash(step) - const versionInfo = stepVersions.value[stepId] - - versionInfo.currentHash = newHash - versionInfo.isModified = newHash !== versionInfo.originalHash - - // 重新计算重执行配置 - calculateReExecuteConfig() - } -} - -/** - * 计算重执行配置 - */ -function calculateReExecuteConfig() { - const modifiedSteps: string[] = [] - let minModifiedIndex = Infinity - - // 找出所有被修改的步骤 - Object.values(stepVersions.value).forEach(versionInfo => { - if (versionInfo.isModified) { - modifiedSteps.push(versionInfo.stepId) - minModifiedIndex = Math.min(minModifiedIndex, versionInfo.stepIndex) - } - }) - - // 设置重执行配置 - if (modifiedSteps.length > 0) { - reExecuteConfig.value = { - shouldReExecute: true, - startFromStepIndex: minModifiedIndex, - modifiedSteps - } - } else { - reExecuteConfig.value = { - shouldReExecute: false, - startFromStepIndex: -1, - modifiedSteps: [] - } - } -} - -/** - * 重置步骤版本标记 - */ -function resetStepVersions() { - Object.values(stepVersions.value).forEach(versionInfo => { - versionInfo.originalHash = versionInfo.currentHash - versionInfo.isModified = false - }) - reExecuteConfig.value = { - shouldReExecute: false, - startFromStepIndex: -1, - modifiedSteps: [] - } -} - -/** - * 清理指定步骤索引之后的所有执行结果 - * @param fromStepIndex 起始步骤索引(该索引之后的结果将被清理) - */ -function clearExecutionResultsAfter(fromStepIndex: number) { - const steps = collaborationProcess.value - const currentResults = agentsStore.executePlan - - // 找出需要清理的步骤名称 - const stepsToClear = new Set() - for (let i = fromStepIndex; i < steps.length; i++) { - const step = steps[i] - if (!step) continue - if (step.StepName) { - stepsToClear.add(step.StepName) - } - if (step.OutputObject) { - stepsToClear.add(step.OutputObject) - } - } - - // 过滤掉需要清理的结果 - const filteredResults = currentResults.filter(result => !stepsToClear.has(result.NodeId)) - - // 更新store - agentsStore.setExecutePlan(filteredResults) - - // 重置步骤执行状态 - Object.keys(stepExecutionStatus.value).forEach(stepName => { - const stepIndex = steps.findIndex(s => s.StepName === stepName) - if (stepIndex >= fromStepIndex) { - stepExecutionStatus.value[stepName] = StepExecutionStatus.READY - } - }) -} - /** * 监听步骤数据变化,更新步骤状态并动态追加新步骤 */ @@ -317,11 +143,6 @@ watch( const totalStepsCount = collaborationProcess.value.length const currentStep = executionProgress.value.currentStep || 1 executionProgress.value.totalSteps = totalStepsCount - - // 使用 Notification 显示追加成功通知 - // info('步骤追加成功', `${stepName} (${currentStep}/${totalStepsCount})`, { - // duration: 2000 - // }) } else { console.warn(`⚠️ 追加步骤失败: ${stepName} - 执行ID不存在或已结束`) // 追加失败,移除标记以便重试 @@ -346,11 +167,6 @@ watch( } } }) - - // 初始化步骤版本(首次加载时) - if (newSteps.length > 0 && Object.keys(stepVersions.value).length === 0) { - initializeStepVersions() - } }, { deep: true } ) @@ -396,15 +212,6 @@ function handleSaveEdit(stepId: string, processId: string, value: string) { const process = step.TaskProcess.find(p => p.ID === processId) if (process) { process.Description = value - - // 更新步骤版本 - updateStepVersion(stepId) - - // 显示修改提示 - const versionInfo = stepVersions.value[stepId] - if (versionInfo?.isModified) { - warning('步骤已修改', `步骤 "${step.StepName}" 已修改,继续执行时将从该步骤重新开始`) - } } } editMap[key] = false @@ -649,9 +456,11 @@ async function executeNextReadyBatch() { switch (event.type) { case 'step_start': + // 使用全局步骤索引计算当前步骤 + const globalStepIndex = collaborationProcess.value.findIndex(s => s.StepName === event.step_name) executionProgress.value = { - currentStep: (event.step_index || 0) + 1, - totalSteps: event.total_steps || collaborationProcess.value.length, + currentStep: globalStepIndex >= 0 ? globalStepIndex + 1 : (event.step_index || 0) + 1, + totalSteps: collaborationProcess.value.length, currentAction: 0, totalActions: 0, currentStepName: event.step_name @@ -662,24 +471,20 @@ async function executeNextReadyBatch() { currentProgressNotificationId.value = showProgress( '任务执行中', executionProgress.value.currentStep, - executionProgress.value.totalSteps || 1 + executionProgress.value.totalSteps ) updateProgressDetail( currentProgressNotificationId.value, - `步骤 ${executionProgress.value.currentStep}/${ - executionProgress.value.totalSteps || 1 - }`, + `步骤 ${executionProgress.value.currentStep}/${executionProgress.value.totalSteps}`, `正在执行: ${event.step_name}` ) } else { updateProgressDetail( currentProgressNotificationId.value, - `步骤 ${executionProgress.value.currentStep}/${ - executionProgress.value.totalSteps || 1 - }`, + `步骤 ${executionProgress.value.currentStep}/${executionProgress.value.totalSteps}`, `正在执行: ${event.step_name}`, executionProgress.value.currentStep, - executionProgress.value.totalSteps || 1 + executionProgress.value.totalSteps ) } break @@ -689,22 +494,32 @@ async function executeNextReadyBatch() { ? ` [并行 ${event.batch_info!.batch_size} 个动作]` : '' - const stepIndexForAction = event.step_index || 0 - const totalStepsValue = - executionProgress.value.totalSteps || collaborationProcess.value.length + // 使用全局步骤索引 + const globalStepIndexForAction = collaborationProcess.value.findIndex(s => s.StepName === event.step_name) + const stepIndexForAction = globalStepIndexForAction >= 0 ? globalStepIndexForAction + 1 : (event.step_index || 0) + 1 + const totalStepsValue = collaborationProcess.value.length + executionProgress.value = { ...executionProgress.value, currentAction: event.completed_actions, totalActions: event.total_actions } + // 检测是否正在暂停(等待当前动作完成) + if (isPausing.value) { + // 当前动作完成,完成暂停 + isPaused.value = true + isPausing.value = false + success('已暂停', '已暂停执行,可稍后继续') + } + // 更新详细进度信息,显示动作级别进度 if (currentProgressNotificationId.value) { updateProgressDetail( currentProgressNotificationId.value, - `步骤 ${stepIndexForAction + 1}/${totalStepsValue}`, + `步骤 ${stepIndexForAction}/${totalStepsValue}`, `${event.step_name} - 动作 ${event.completed_actions}/${event.total_actions} 完成${parallelInfo}`, - stepIndexForAction + 1, + stepIndexForAction, totalStepsValue ) } @@ -880,62 +695,22 @@ function removeNotification(id: string) { // Pause/Resume handler async function handlePauseResume() { if (isPaused.value) { - // Resume execution - - // 检查是否需要重新执行(有步骤被修改) - if (reExecuteConfig.value.shouldReExecute) { - const startStepIndex = reExecuteConfig.value.startFromStepIndex - const startStep = collaborationProcess.value[startStepIndex] - - try { - // 确认对话框 - await ElMessageBox.confirm( - `检测到 ${reExecuteConfig.value.modifiedSteps.length} 个步骤已被修改\n` + - `将从步骤 "${startStep?.StepName}" 重新开始执行\n\n` + - `是否继续?`, - '检测到步骤修改', - { - confirmButtonText: '从修改步骤重新执行', - cancelButtonText: '取消', - type: 'warning' - } - ) - - // 清理执行结果 - clearExecutionResultsAfter(startStepIndex) - - // 重置暂停状态 + // Resume execution - 正常恢复执行 + try { + if (websocket.connected) { + await websocket.send('resume_execution', { + goal: agentsStore.agentRawPlan.data?.['General Goal'] || '' + }) + // 只有在收到成功响应后才更新状态 isPaused.value = false isPausing.value = false - isStreaming.value = false - - // 从指定步骤重新执行 - await reExecuteFromStep(startStepIndex) - - // 重置修改标记 - resetStepVersions() - } catch { - // 用户取消 - info('已取消', '已取消重新执行') - } - } else { - // 没有修改,正常恢复执行 - try { - if (websocket.connected) { - await websocket.send('resume_execution', { - goal: agentsStore.agentRawPlan.data?.['General Goal'] || '' - }) - // 只有在收到成功响应后才更新状态 - isPaused.value = false - isPausing.value = false - success('已恢复', '已恢复执行') - } else { - warning('无法恢复', 'WebSocket未连接,无法恢复执行') - } - } catch (error) { - error('恢复失败', '恢复执行失败') - // 恢复失败时,保持原状态不变(仍然是暂停状态) + success('已恢复', '已恢复执行') + } else { + warning('无法恢复', 'WebSocket未连接,无法恢复执行') } + } catch (error) { + error('恢复失败', '恢复执行失败') + // 恢复失败时,保持原状态不变(仍然是暂停状态) } } else { // Pause execution @@ -949,10 +724,9 @@ async function handlePauseResume() { goal: agentsStore.agentRawPlan.data?.['General Goal'] || '' }) - // 收到成功响应后,设置完全暂停状态 - isPaused.value = true - isPausing.value = false - success('已暂停', '已暂停执行,可稍后继续') + // 注意:不立即设置 isPaused = true + // 而是等待当前动作完成后,在 action_complete 事件中设置 + // 这样可以确保在动作真正完成后才显示"已暂停" } else { warning('无法暂停', 'WebSocket未连接,无法暂停') isPausing.value = false @@ -977,35 +751,6 @@ async function handleExecuteButtonClick() { await handleRun() } -/** - * 从指定步骤重新执行(支持边填充边执行) - * @param fromStepIndex 起始步骤索引 - */ -async function reExecuteFromStep(fromStepIndex: number) { - const steps = collaborationProcess.value - - // 设置执行状态 - loading.value = true - isStreaming.value = true - - // 重置将要执行的步骤的状态 - for (let i = fromStepIndex; i < steps.length; i++) { - const step = steps[i] - if (step) { - const stepName = step.StepName || step.Id || '' - stepExecutionStatus.value[stepName] = StepExecutionStatus.READY - } - } - - // 使用批量执行模式,会自动处理依赖和边填充边执行 - await executeNextReadyBatch() - - success('重新执行完成', '所有步骤已重新执行完成') - loading.value = false - isPaused.value = false - isStreaming.value = false -} - async function handleRun() { // Check if there are ready steps const readySteps = stepsReadyStatus.value.ready @@ -1302,15 +1047,23 @@ defineExpose({ class="animate-spin" /> - + + + + - + 任务执行 继续执行 + 暂停中... 暂停执行