feat:RESTful API架构改WebSocket架构-执行结果可以分步显示版本

This commit is contained in:
liailing1026
2026-01-22 17:22:30 +08:00
parent 1c8036adf1
commit 786c674d21
15 changed files with 2591 additions and 308 deletions

View File

@@ -2,15 +2,17 @@
import { computed, onUnmounted, ref, reactive, nextTick, watch, onMounted } from 'vue'
import { throttle } from 'lodash'
import { AnchorLocations, BezierConnector } from '@jsplumb/browser-ui'
import { ElMessage, ElMessageBox } from 'element-plus'
import AdditionalOutputCard from './AdditionalOutputCard.vue'
import SvgIcon from '@/components/SvgIcon/index.vue'
import { getActionTypeDisplay, getAgentMapIcon } from '@/layout/components/config.ts'
import { type ConnectArg, Jsplumb } from '@/layout/components/Main/TaskTemplate/utils.ts'
import variables from '@/styles/variables.module.scss'
import { type IRawStepTask, useAgentsStore } from '@/stores'
import { type IRawStepTask, useAgentsStore, type IRawPlanResponse } from '@/stores'
import api, { type StreamingEvent } from '@/api'
import ProcessCard from '../TaskProcess/ProcessCard.vue'
import ExecutePlan from './ExecutePlan.vue'
import websocket from '@/utils/websocket'
const emit = defineEmits<{
(e: 'refreshLine'): void
@@ -24,7 +26,106 @@ const collaborationProcess = computed(() => {
return agentsStore.agentRawPlan.data?.['Collaboration Process'] ?? []
})
// 监听额外产物变化
// Step execution status enum
enum StepExecutionStatus {
WAITING = 'waiting', // Waiting for data
READY = 'ready', // Ready to execute
RUNNING = 'running', // Currently running
COMPLETED = 'completed', // Execution completed
FAILED = 'failed' // Execution failed
}
// Execution status for each step
const stepExecutionStatus = ref<Record<string, StepExecutionStatus>>({})
// Check if step is ready to execute (has TaskProcess data)
const isStepReady = (step: IRawStepTask) => {
return step.TaskProcess && step.TaskProcess.length > 0
}
// 判断动作是否有执行结果
const hasActionResult = (step: IRawStepTask, actionId: string) => {
const stepResult = agentsStore.executePlan.find(
r => r.NodeId === step.StepName && r.LogNodeType === 'step'
)
if (!stepResult || !stepResult.ActionHistory) {
return false
}
return stepResult.ActionHistory.some(action => action.ID === actionId)
}
// 判断 OutputObject 是否有执行结果
const hasObjectResult = (outputObject?: string) => {
if (!outputObject) return false
return agentsStore.executePlan.some(r => r.NodeId === outputObject && r.LogNodeType === 'object')
}
// Get execution status of a step
const getStepStatus = (step: IRawStepTask): StepExecutionStatus => {
const stepName = step.StepName || step.Id || ''
// If status is already recorded, return it
if (stepExecutionStatus.value[stepName]) {
return stepExecutionStatus.value[stepName]
}
// Check if has TaskProcess data
if (isStepReady(step)) {
return StepExecutionStatus.READY
} else {
return StepExecutionStatus.WAITING
}
}
// Calculate preparation status of all steps
const stepsReadyStatus = computed(() => {
const steps = collaborationProcess.value
const readySteps: string[] = []
const waitingSteps: string[] = []
steps.forEach(step => {
if (isStepReady(step)) {
readySteps.push(step.StepName || 'Unknown step')
} else {
waitingSteps.push(step.StepName || 'Unknown step')
}
})
return {
ready: readySteps,
waiting: waitingSteps,
allReady: waitingSteps.length === 0,
totalCount: steps.length,
readyCount: readySteps.length
}
})
// Watch step data changes, update waiting step status
watch(
() => collaborationProcess.value,
newSteps => {
newSteps.forEach(step => {
const stepName = step.StepName || step.Id || ''
const currentStatus = stepExecutionStatus.value[stepName]
// If step was waiting and now has data, set to ready
if (currentStatus === StepExecutionStatus.WAITING && isStepReady(step)) {
stepExecutionStatus.value[stepName] = StepExecutionStatus.READY
// 如果正在执行中,自动执行下一批就绪的步骤
if (autoExecuteEnabled.value && loading.value) {
executeNextReadyBatch()
}
}
})
},
{ deep: true }
)
// Enable auto-execution (auto-execute when new steps are ready)
const autoExecuteEnabled = ref(true)
// Watch additional outputs changes
watch(
() => agentsStore.additionalOutputs,
() => {
@@ -37,7 +138,7 @@ watch(
{ deep: true }
)
// 编辑逻辑
// Edit logic
const editMode = ref(false)
const editMap = reactive<Record<string, boolean>>({})
const editBuffer = reactive<Record<string, string | undefined>>({})
@@ -76,7 +177,7 @@ const jsplumb = new Jsplumb('task-results-main', {
}
})
// 操作折叠面板时要实时的刷新连线
// Refresh connections in real-time when collapsing panels
let timer: ReturnType<typeof setInterval> | null = null
function handleCollapse() {
if (timer) {
@@ -87,7 +188,7 @@ function handleCollapse() {
emit('refreshLine')
}, 1) as ReturnType<typeof setInterval>
// 默认三秒后已经完全打开
// Default fully open after 3 seconds
const timer1 = setTimeout(() => {
if (timer) {
clearInterval(timer)
@@ -105,7 +206,7 @@ function handleCollapse() {
})
}
// 创建内部连线
// Create internal connections
function createInternalLine(id?: string) {
const arr: ConnectArg[] = []
jsplumb.reset()
@@ -188,160 +289,340 @@ const executionProgress = ref({
currentAction: 0,
totalActions: 0,
currentStepName: '',
message: '正在执行...'
message: '准备执行任务...'
})
async function handleRun() {
// 清空之前的执行结果
agentsStore.setExecutePlan([])
const tempResults: any[] = []
// Pause functionality state
const isPaused = ref(false) // Whether paused
const isStreaming = ref(false) // Whether streaming data (backend started returning)
const isButtonLoading = ref(false) // Button brief loading state (prevent double-click)
try {
loading.value = true
// Store current step execution index (for sequential execution)
const currentExecutionIndex = ref(0)
// 使用优化版流式API阶段1+2步骤级流式 + 动作级智能并行)
api.executePlanOptimized(
agentsStore.agentRawPlan.data!,
// onMessage: 处理每个事件
(event: StreamingEvent) => {
switch (event.type) {
case 'step_start':
// 步骤开始
executionProgress.value = {
currentStep: event.step_index + 1,
totalSteps: event.total_steps,
currentAction: 0,
totalActions: 0,
currentStepName: event.step_name,
message: `正在执行步骤 ${event.step_index + 1}/${event.total_steps}: ${
event.step_name
}`
}
console.log(
`📋 步骤 ${event.step_index + 1}/${event.total_steps} 开始: ${event.step_name}`
)
break
// Execute next batch of ready steps (batch execution to maintain dependencies)
async function executeNextReadyBatch() {
const steps = collaborationProcess.value
case 'action_complete':
// 动作完成
const parallelInfo = event.batch_info?.is_parallel
? ` [批次 ${event.batch_info!.batch_index + 1}, 并行 ${
event.batch_info!.batch_size
} 个]`
: ''
// Collect all ready but unexecuted steps (in order, until hitting unready step)
const readySteps: IRawStepTask[] = []
executionProgress.value = {
...executionProgress.value,
currentAction: event.completed_actions,
totalActions: event.total_actions,
message: `步骤 ${event.step_index + 1}/${executionProgress.value.totalSteps}: ${
event.step_name
} - 动作 ${event.completed_actions}/${event.total_actions} 完成${parallelInfo}`
}
for (let i = 0; i < steps.length; i++) {
const step = steps[i]
if (!step) continue
console.log(
`✅ 动作 ${event.completed_actions}/${event.total_actions} 完成${parallelInfo}: ${event.action_result.ActionType} by ${event.action_result.AgentName}`
)
// 如果步骤已就绪,加入批量执行列表
if (isStepReady(step)) {
const stepName = step.StepName || step.Id || ''
const status = stepExecutionStatus.value[stepName]
// 实时更新到 store找到对应的步骤并添加 ActionHistory
const step = collaborationProcess.value.find(s => s.StepName === event.step_name)
if (step) {
const stepLogNode = tempResults.find(
r => r.NodeId === event.step_name && r.LogNodeType === 'step'
)
if (!stepLogNode) {
// 创建步骤日志节点
const newStepLog = {
LogNodeType: 'step',
NodeId: event.step_name,
InputName_List: step.InputObject_List || [],
OutputName: step.OutputObject || '',
chatLog: [],
inputObject_Record: [],
ActionHistory: [event.action_result]
}
tempResults.push(newStepLog)
} else {
// 追加动作结果
stepLogNode.ActionHistory.push(event.action_result)
}
// 更新 store
agentsStore.setExecutePlan([...tempResults])
}
break
case 'step_complete':
// 步骤完成
console.log(`🎯 步骤完成: ${event.step_name}`)
// 更新步骤日志节点
const existingStepLog = tempResults.find(
r => r.NodeId === event.step_name && r.LogNodeType === 'step'
)
if (existingStepLog) {
existingStepLog.ActionHistory = event.step_log_node.ActionHistory
} else {
tempResults.push(event.step_log_node)
}
// 添加对象日志节点
tempResults.push(event.object_log_node)
// 更新 store
agentsStore.setExecutePlan([...tempResults])
break
case 'execution_complete':
// 执行完成
executionProgress.value.message = `执行完成!共 ${event.total_steps} 个步骤`
console.log(`🎉 执行完成,共 ${event.total_steps} 个步骤`)
// 确保所有结果都保存到 store
agentsStore.setExecutePlan([...tempResults])
break
case 'error':
// 错误
console.error('❌ 执行错误:', event.message)
executionProgress.value.message = `执行错误: ${event.message}`
break
}
},
// onError: 处理错误
(error: Error) => {
console.error('❌ 流式执行错误:', error)
executionProgress.value.message = `执行失败: ${error.message}`
},
// onComplete: 执行完成
() => {
console.log('✅ 流式执行完成')
loading.value = false
// Only collect unexecuted steps
if (!status || status === StepExecutionStatus.READY) {
readySteps.push(step)
}
)
} catch (error) {
console.error('执行失败:', error)
executionProgress.value.message = '执行失败,请重试'
} finally {
// loading 会在 onComplete 中设置为 false
} else {
// Stop at first unready step (maintain step order)
break
}
}
if (readySteps.length > 0) {
try {
// Mark all steps to be executed as running
readySteps.forEach(step => {
const stepName = step.StepName || step.Id || ''
stepExecutionStatus.value[stepName] = StepExecutionStatus.RUNNING
})
// 构建包含所有已就绪步骤的计划数据(批量发送,保持依赖关系)
const batchPlan: IRawPlanResponse = {
'General Goal': agentsStore.agentRawPlan.data?.['General Goal'] || '',
'Initial Input Object': agentsStore.agentRawPlan.data?.['Initial Input Object'] || [],
'Collaboration Process': readySteps // Key: batch send steps
}
const tempResults: any[] = []
// Execute these steps in batch
await new Promise<void>((resolve, reject) => {
api.executePlanOptimized(
batchPlan,
// onMessage: handle each event
(event: StreamingEvent) => {
// When backend starts returning data, set isStreaming (only once)
if (!isStreaming.value) {
isStreaming.value = true
}
// If paused, ignore events
if (isPaused.value) {
return
}
switch (event.type) {
case 'step_start':
// 使用后端返回的 step_index 和 total_steps
executionProgress.value = {
currentStep: (event.step_index || 0) + 1,
totalSteps: event.total_steps || collaborationProcess.value.length,
currentAction: 0,
totalActions: 0,
currentStepName: event.step_name,
message: `正在执行步骤 ${event.step_index + 1}/${
event.total_steps || collaborationProcess.value.length
}: ${event.step_name}`
}
break
case 'action_complete':
const parallelInfo = event.batch_info?.is_parallel
? ` [并行 ${event.batch_info!.batch_size} 个动作]`
: ''
// 使用后端返回的 step_indextotal_steps 使用当前进度中的值
const stepIndexForAction = event.step_index || 0
const totalStepsValue =
executionProgress.value.totalSteps || collaborationProcess.value.length
executionProgress.value = {
...executionProgress.value,
currentAction: event.completed_actions,
totalActions: event.total_actions,
message: `步骤 ${stepIndexForAction + 1}/${totalStepsValue}: ${
event.step_name
} - 动作 ${event.completed_actions}/${event.total_actions} 完成${parallelInfo}`
}
// Update store in real-time
const existingStep = collaborationProcess.value.find(
s => s.StepName === event.step_name
)
if (existingStep) {
const currentResults = agentsStore.executePlan
const stepLogNode = currentResults.find(
r => r.NodeId === event.step_name && r.LogNodeType === 'step'
)
if (!stepLogNode) {
const newStepLog = {
LogNodeType: 'step',
NodeId: event.step_name,
InputName_List: existingStep.InputObject_List || [],
OutputName: existingStep.OutputObject || '',
chatLog: [],
inputObject_Record: [],
ActionHistory: [event.action_result]
}
tempResults.push(newStepLog)
agentsStore.setExecutePlan([...currentResults, newStepLog])
} else {
stepLogNode.ActionHistory.push(event.action_result)
agentsStore.setExecutePlan([...currentResults])
}
}
break
case 'step_complete':
stepExecutionStatus.value[event.step_name] = StepExecutionStatus.COMPLETED
// Update complete step log
const currentResults = agentsStore.executePlan
const existingLog = currentResults.find(
r => r.NodeId === event.step_name && r.LogNodeType === 'step'
)
if (existingLog) {
existingLog.ActionHistory = event.step_log_node.ActionHistory
// 触发响应式更新
agentsStore.setExecutePlan([...currentResults])
} else if (event.step_log_node) {
// 添加新的 step_log_node
agentsStore.setExecutePlan([...currentResults, event.step_log_node])
}
// 添加 object_log_node
const updatedResults = agentsStore.executePlan
if (event.object_log_node) {
agentsStore.setExecutePlan([...updatedResults, event.object_log_node])
}
break
case 'execution_complete':
// 所有步骤都标记为完成
readySteps.forEach(step => {
const stepName = step.StepName || step.Id || ''
if (stepExecutionStatus.value[stepName] !== StepExecutionStatus.COMPLETED) {
stepExecutionStatus.value[stepName] = StepExecutionStatus.COMPLETED
}
})
resolve()
break
case 'error':
console.error(' 执行错误:', event.message)
executionProgress.value.message = `执行错误: ${event.message}`
readySteps.forEach(step => {
const stepName = step.StepName || step.Id || ''
stepExecutionStatus.value[stepName] = StepExecutionStatus.FAILED
})
reject(new Error(event.message))
break
}
},
// onError
(error: Error) => {
console.error(' 流式执行错误:', error)
executionProgress.value.message = `执行失败: ${error.message}`
readySteps.forEach(step => {
const stepName = step.StepName || step.Id || ''
stepExecutionStatus.value[stepName] = StepExecutionStatus.FAILED
})
reject(error)
},
// onComplete
() => {
resolve()
}
)
})
// 批量执行成功后,递归执行下一批
await executeNextReadyBatch()
} catch (error) {
ElMessage.error('批量执行失败')
// 重置所有执行状态
loading.value = false
isPaused.value = false
isStreaming.value = false
}
} else {
// No more ready steps
loading.value = false
// 重置暂停和流式状态
isPaused.value = false
isStreaming.value = false
// Check if there are still waiting steps
const hasWaitingSteps = steps.some(step => step && !isStepReady(step))
if (hasWaitingSteps) {
const waitingStepNames = steps
.filter(step => step && !isStepReady(step))
.map(step => step?.StepName || '未知')
executionProgress.value.message = `等待 ${waitingStepNames.length} 个步骤数据填充中...`
ElMessage.info(`等待 ${waitingStepNames.length} 个步骤数据填充中...`)
} else {
executionProgress.value.message = '所有步骤已完成'
ElMessage.success('所有步骤已完成')
}
}
}
// 查看任务过程
// Pause/Resume handler
async function handlePauseResume() {
if (isPaused.value) {
// Resume execution
try {
if (websocket.connected) {
await websocket.send('resume_execution', {
goal: agentsStore.agentRawPlan.data?.['General Goal'] || ''
})
// 只有在收到成功响应后才更新状态
isPaused.value = false
ElMessage.success('已恢复执行')
} else {
ElMessage.warning('WebSocket未连接无法恢复执行')
}
} catch (error) {
ElMessage.error('恢复执行失败')
// 恢复失败时,保持原状态不变(仍然是暂停状态)
}
} else {
// Pause execution
try {
if (websocket.connected) {
await websocket.send('pause_execution', {
goal: agentsStore.agentRawPlan.data?.['General Goal'] || ''
})
// 只有在收到成功响应后才更新状态
isPaused.value = true
ElMessage.success('已暂停执行,可稍后继续')
} else {
ElMessage.warning('WebSocket未连接无法暂停')
}
} catch (error) {
ElMessage.error('暂停执行失败')
// 暂停失败时,保持原状态不变(仍然是非暂停状态)
}
}
}
// Handle execute button click
async function handleExecuteButtonClick() {
// If streaming, show pause/resume functionality
if (isStreaming.value) {
await handlePauseResume()
return
}
// Otherwise, execute normal task execution logic
await handleRun()
}
async function handleRun() {
// Check if there are ready steps
const readySteps = stepsReadyStatus.value.ready
const waitingSteps = stepsReadyStatus.value.waiting
if (readySteps.length === 0 && waitingSteps.length > 0) {
ElMessageBox.confirm(
`All ${waitingSteps.length} steps的数据还在填充中\n\n${waitingSteps.join(
'、'
)}\n\n建议等待数据填充完成后再执行。`,
'Step data not ready',
{
confirmButtonText: 'I Understand',
cancelButtonText: 'Close',
type: 'warning'
}
)
return
}
// Set button brief loading state (prevent double-click)
isButtonLoading.value = true
setTimeout(() => {
isButtonLoading.value = false
}, 1000)
// Reset pause and streaming state
isPaused.value = false
isStreaming.value = false
// Start execution
loading.value = true
currentExecutionIndex.value = 0
// Clear previous execution results and status
agentsStore.setExecutePlan([])
stepExecutionStatus.value = {}
// Start batch executing first batch of ready steps
await executeNextReadyBatch()
}
// View task process
async function handleTaskProcess() {
drawerVisible.value = true
}
// 重置执行结果
// Reset execution results
function handleRefresh() {
agentsStore.setExecutePlan([])
}
// 添加滚动状态标识
// Add scroll state indicator
const isScrolling = ref(false)
let scrollTimer: ReturnType<typeof setTimeout> | null = null
// 修改滚动处理函数
// Modify scroll handler
function handleScroll() {
isScrolling.value = true
emit('refreshLine')
@@ -357,7 +638,7 @@ function handleScroll() {
}, 300) as ReturnType<typeof setTimeout>
}
// 修改鼠标事件处理函数
// Modify mouse event handler
const handleMouseEnter = throttle(id => {
if (!isScrolling.value) {
createInternalLine(id)
@@ -374,21 +655,21 @@ function clear() {
jsplumb.reset()
}
//封装连线重绘方法
// Encapsulate line redraw method
const redrawInternalLines = (highlightId?: string) => {
// 等待 DOM 更新完成
// Waiting DOM 更新完成
nextTick(() => {
// 清除旧连线
jsplumb.reset()
// 等待 DOM 稳定后重新绘制
// Waiting DOM 稳定后重新绘制
setTimeout(() => {
createInternalLine(highlightId)
}, 100)
})
}
//监听 collaborationProcess 变化,自动重绘连线
// Watch collaborationProcess changes, auto redraw connections
watch(
() => collaborationProcess,
() => {
@@ -397,7 +678,7 @@ watch(
{ deep: true }
)
// 组件挂载后初始化连线
// Initialize connections after component mount
onMounted(() => {
// 初始化时绘制连线
nextTick(() => {
@@ -407,7 +688,7 @@ onMounted(() => {
})
})
//按钮交互状态管理
// Button interaction state management
const buttonHoverState = ref<'process' | 'execute' | 'refresh' | null>(null)
let buttonHoverTimer: ReturnType<typeof setTimeout> | null = null
const handleProcessMouseEnter = () => {
@@ -448,13 +729,13 @@ const handleButtonMouseLeave = () => {
}, 50) // 适当减少延迟时间
}
// 添加离开组件时的清理
// Cleanup when leaving component
onUnmounted(() => {
if (buttonHoverTimer) {
clearTimeout(buttonHoverTimer)
}
})
// 计算按钮类名
// Calculate button class names
const processBtnClass = computed(() => {
if (buttonHoverState.value === 'refresh' || buttonHoverState.value === 'execute') {
return 'circle'
@@ -476,7 +757,7 @@ const refreshBtnClass = computed(() => {
return agentsStore.executePlan.length > 0 ? 'ellipse' : 'circle'
})
// 计算按钮是否显示文字
// Calculate whether to show button text
const showProcessText = computed(() => {
return buttonHoverState.value === 'process'
})
@@ -490,17 +771,17 @@ const showRefreshText = computed(() => {
return buttonHoverState.value === 'refresh'
})
// 计算按钮标题
// Calculate button titles
const processBtnTitle = computed(() => {
return buttonHoverState.value === 'process' ? '任务程' : '点击查看任务程'
return buttonHoverState.value === 'process' ? '查看任务程' : '点击查看任务程'
})
const executeBtnTitle = computed(() => {
return showExecuteText.value ? '任务执行' : '点击运行'
return showExecuteText.value ? '任务执行' : '点击执行任务'
})
const refreshBtnTitle = computed(() => {
return showRefreshText.value ? '重置执行结果' : '点击重置执行状态'
return showRefreshText.value ? '重置结果' : '点击重置执行状态'
})
defineExpose({
@@ -544,7 +825,7 @@ defineExpose({
<svg-icon icon-class="refresh" />
<span v-if="showRefreshText" class="btn-text">重置</span>
</el-button>
<!-- 任务过程按钮 -->
<!-- Task Process按钮 -->
<el-button
:class="processBtnClass"
:color="variables.tertiary"
@@ -557,10 +838,10 @@ defineExpose({
<span v-if="showProcessText" class="btn-text">任务过程</span>
</el-button>
<!-- 任务执行按钮 -->
<!-- Execute按钮 -->
<el-popover
:disabled="Boolean(agentsStore.agentRawPlan.data)"
title="请先输入要执行的任务"
title="请先输入任务再执行"
:visible="showPopover"
@hide="showPopover = false"
style="order: 2"
@@ -569,14 +850,35 @@ defineExpose({
<el-button
:class="executeBtnClass"
:color="variables.tertiary"
:title="executeBtnTitle"
:disabled="!agentsStore.agentRawPlan.data || loading"
:title="isStreaming ? (isPaused ? '点击继续执行' : '点击暂停执行') : executeBtnTitle"
:disabled="
!agentsStore.agentRawPlan.data || (!isStreaming && loading) || isButtonLoading
"
@mouseenter="handleExecuteMouseEnter"
@click="handleRun"
@click="handleExecuteButtonClick"
>
<svg-icon v-if="loading" icon-class="loading" class="animate-spin" />
<!-- 按钮短暂加载状态防止双击 -->
<svg-icon v-if="isButtonLoading" icon-class="loading" class="animate-spin" />
<!-- 执行中加载状态已废弃保留以防万一 -->
<svg-icon
v-else-if="loading && !isStreaming"
icon-class="loading"
class="animate-spin"
/>
<!-- 流式传输中且未Pause显示Pause图标 -->
<svg-icon v-else-if="isStreaming && !isPaused" icon-class="Pause" size="20px" />
<!-- 流式传输中且已Pause显示播放/Resume图标 -->
<svg-icon v-else-if="isStreaming && isPaused" icon-class="video-play" size="20px" />
<!-- 默认状态显示 action 图标 -->
<svg-icon v-else icon-class="action" />
<span v-if="showExecuteText" class="btn-text">任务执行</span>
<span v-if="showExecuteText && !isStreaming" class="btn-text">任务执行</span>
<span v-else-if="isStreaming && isPaused" class="btn-text">继续执行</span>
<span v-else-if="isStreaming" class="btn-text">暂停执行</span>
</el-button>
</template>
</el-popover>
@@ -597,7 +899,7 @@ defineExpose({
</div>
</template>
<el-scrollbar height="calc(100vh - 120px)">
<el-empty v-if="!collaborationProcess.length" description="暂无任务程" />
<el-empty v-if="!collaborationProcess.length" description="暂无任务程" />
<div v-else class="process-list">
<!-- 使用ProcessCard组件显示每个AgentSelection -->
<ProcessCard
@@ -648,23 +950,26 @@ defineExpose({
v-for="item1 in item.TaskProcess"
:key="`task-results-${item.Id}-${item1.ID}`"
:name="`task-results-${item.Id}-${item1.ID}`"
:disabled="Boolean(!agentsStore.executePlan.length || loading)"
:disabled="!hasActionResult(item, item1.ID)"
@mouseenter="() => handleMouseEnter(`task-results-${item.Id}-0-${item1.ID}`)"
@mouseleave="handleMouseLeave"
>
<template v-if="loading" #icon>
<!-- 执行中且没有结果时显示 loading 图标 -->
<template v-if="loading && !hasActionResult(item, item1.ID)" #icon>
<SvgIcon icon-class="loading" size="20px" class="animate-spin" />
</template>
<!-- 没有执行计划时隐藏图标 -->
<template v-else-if="!agentsStore.executePlan.length" #icon>
<span></span>
</template>
<!-- 有结果时不提供 #icon Element Plus 显示默认箭头 -->
<template #title>
<!-- 运行之前背景颜色是var(--color-bg-detail-list),运行之后背景颜色是var(--color-bg-detail-list-run) -->
<div
class="flex items-center gap-[15px] rounded-[20px]"
:class="{
'bg-[var(--color-bg-detail-list)]': !agentsStore.executePlan.length,
'bg-[var(--color-bg-detail-list-run)]': agentsStore.executePlan.length
'bg-[var(--color-bg-detail-list)]': !hasActionResult(item, item1.ID),
'bg-[var(--color-bg-detail-list-run)]': hasActionResult(item, item1.ID)
}"
>
<!-- 右侧链接点 -->
@@ -685,9 +990,14 @@ defineExpose({
<div class="text-[16px]">
<span
:class="{
'text-[var(--color-text-result-detail)]': !agentsStore.executePlan.length,
'text-[var(--color-text-result-detail-run)]':
agentsStore.executePlan.length
'text-[var(--color-text-result-detail)]': !hasActionResult(
item,
item1.ID
),
'text-[var(--color-text-result-detail-run)]': hasActionResult(
item,
item1.ID
)
}"
>{{ item1.AgentName }}:&nbsp; &nbsp;</span
>
@@ -713,23 +1023,28 @@ defineExpose({
@click="emit('setCurrentTask', item)"
>
<!-- <div class="text-[18px]">{{ item.OutputObject }}</div>-->
<el-collapse @change="handleCollapse">
<el-collapse @change="handleCollapse" :key="agentsStore.executePlan.length">
<el-collapse-item
class="output-object"
:disabled="Boolean(!agentsStore.executePlan.length || loading)"
:disabled="!hasObjectResult(item.OutputObject)"
>
<template v-if="loading" #icon>
<!-- 执行中且没有结果时显示 loading 图标 -->
<template v-if="loading && !hasObjectResult(item.OutputObject)" #icon>
<SvgIcon icon-class="loading" size="20px" class="animate-spin" />
</template>
<!-- 没有执行计划时隐藏图标 -->
<template v-else-if="!agentsStore.executePlan.length" #icon>
<span></span>
</template>
<!-- 有结果时不提供 #icon Element Plus 显示默认箭头 -->
<template #title>
<div
class="text-[18px]"
:class="{
'text-[var(--color-text-result-detail)]': !agentsStore.executePlan.length,
'text-[var(--color-text-result-detail-run)]': agentsStore.executePlan.length
'text-[var(--color-text-result-detail)]': !hasObjectResult(item.OutputObject),
'text-[var(--color-text-result-detail-run)]': hasObjectResult(
item.OutputObject
)
}"
>
{{ item.OutputObject }}
@@ -980,7 +1295,7 @@ defineExpose({
}
}
// 圆形状态
// Circle state
.circle {
width: 40px !important;
height: 40px !important;
@@ -994,14 +1309,14 @@ defineExpose({
}
}
// 椭圆形状态
// Ellipse state
.ellipse {
height: 40px !important;
border-radius: 20px !important;
padding: 0 16px !important;
gap: 8px;
// 任务过程按钮 - 左边固定,向右展开
// Task process button - fixed left, expand right
&:nth-child(1) {
justify-content: flex-start !important;
@@ -1016,7 +1331,7 @@ defineExpose({
}
}
// 任务执行按钮 - 右边固定,向左展开
// Task execution button - fixed right, expand left
&:nth-child(2) {
justify-content: flex-end !important;