2025-10-29 10:22:14 +08:00
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import SvgIcon from '@/components/SvgIcon/index.vue'
|
|
|
|
|
|
import { getAgentMapIcon } from '@/layout/components/config.ts'
|
2025-11-04 15:26:52 +08:00
|
|
|
|
import { type ConnectArg, Jsplumb } from '@/layout/components/Main/TaskTemplate/utils.ts'
|
2025-10-29 10:22:14 +08:00
|
|
|
|
import { type IRawStepTask, useAgentsStore } from '@/stores'
|
2026-01-09 13:54:32 +08:00
|
|
|
|
import { computed, ref, nextTick, watch, onMounted } from 'vue'
|
2025-10-29 10:22:14 +08:00
|
|
|
|
import { AnchorLocations } from '@jsplumb/browser-ui'
|
2026-01-21 15:18:15 +08:00
|
|
|
|
import { Loading } from '@element-plus/icons-vue'
|
2025-11-04 15:26:52 +08:00
|
|
|
|
import MultiLineTooltip from '@/components/MultiLineTooltip/index.vue'
|
|
|
|
|
|
import Bg from './Bg.vue'
|
2025-12-31 19:04:58 +08:00
|
|
|
|
import BranchButton from './components/BranchButton.vue'
|
2026-01-23 15:38:09 +08:00
|
|
|
|
import Notification from '@/components/Notification/Notification.vue'
|
|
|
|
|
|
import { useNotification } from '@/composables/useNotification'
|
2025-12-31 19:04:58 +08:00
|
|
|
|
|
|
|
|
|
|
// 判断计划是否就绪
|
|
|
|
|
|
const planReady = computed(() => {
|
|
|
|
|
|
return agentsStore.agentRawPlan.data !== undefined
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const openPlanModification = () => {
|
|
|
|
|
|
agentsStore.openPlanModification()
|
|
|
|
|
|
}
|
2025-10-29 10:22:14 +08:00
|
|
|
|
|
|
|
|
|
|
const emit = defineEmits<{
|
|
|
|
|
|
(el: 'resetAgentRepoLine'): void
|
|
|
|
|
|
(el: 'setCurrentTask', task: IRawStepTask): void
|
2025-12-21 15:28:59 +08:00
|
|
|
|
(el: 'add-output', outputName: string): void
|
|
|
|
|
|
(el: 'click-branch'): void
|
2025-10-29 10:22:14 +08:00
|
|
|
|
}>()
|
|
|
|
|
|
|
|
|
|
|
|
const jsplumb = new Jsplumb('task-syllabus')
|
|
|
|
|
|
|
|
|
|
|
|
const handleScroll = () => {
|
|
|
|
|
|
emit('resetAgentRepoLine')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const agentsStore = useAgentsStore()
|
|
|
|
|
|
|
|
|
|
|
|
const collaborationProcess = computed(() => {
|
|
|
|
|
|
return agentsStore.agentRawPlan.data?.['Collaboration Process'] ?? []
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2026-01-21 15:18:15 +08:00
|
|
|
|
// 检测是否正在填充详情(有步骤但没有 AgentSelection)
|
|
|
|
|
|
const isFillingDetails = computed(() => {
|
|
|
|
|
|
const process = agentsStore.agentRawPlan.data?.['Collaboration Process'] || []
|
2026-01-22 17:22:30 +08:00
|
|
|
|
return (
|
|
|
|
|
|
process.length > 0 &&
|
|
|
|
|
|
process.some(step => !step.AgentSelection || step.AgentSelection.length === 0)
|
|
|
|
|
|
)
|
2026-01-21 15:18:15 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 计算填充进度
|
|
|
|
|
|
const completedSteps = computed(() => {
|
|
|
|
|
|
const process = agentsStore.agentRawPlan.data?.['Collaboration Process'] || []
|
|
|
|
|
|
return process.filter(step => step.AgentSelection && step.AgentSelection.length > 0).length
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const totalSteps = computed(() => {
|
|
|
|
|
|
return agentsStore.agentRawPlan.data?.['Collaboration Process']?.length || 0
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2026-01-23 15:38:09 +08:00
|
|
|
|
// Notification system
|
|
|
|
|
|
const { notifications, progress: showProgress, updateProgressDetail, removeNotification } = useNotification()
|
|
|
|
|
|
const fillingProgressNotificationId = ref<string | null>(null)
|
|
|
|
|
|
|
|
|
|
|
|
// 监听填充进度,显示通知
|
|
|
|
|
|
watch(
|
|
|
|
|
|
[isFillingDetails, completedSteps, totalSteps, () => agentsStore.hasStoppedFilling],
|
|
|
|
|
|
([filling, completed, total, hasStopped]) => {
|
|
|
|
|
|
// 如果用户已停止,关闭进度通知
|
|
|
|
|
|
if (hasStopped && fillingProgressNotificationId.value) {
|
|
|
|
|
|
removeNotification(fillingProgressNotificationId.value)
|
|
|
|
|
|
fillingProgressNotificationId.value = null
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (filling && total > 0) {
|
|
|
|
|
|
if (!fillingProgressNotificationId.value) {
|
|
|
|
|
|
// 创建进度通知
|
|
|
|
|
|
fillingProgressNotificationId.value = showProgress(
|
|
|
|
|
|
'生成协作流程',
|
|
|
|
|
|
completed,
|
|
|
|
|
|
total
|
|
|
|
|
|
)
|
|
|
|
|
|
updateProgressDetail(
|
|
|
|
|
|
fillingProgressNotificationId.value,
|
|
|
|
|
|
`${completed}/${total}`,
|
|
|
|
|
|
'正在分配智能体...',
|
|
|
|
|
|
completed,
|
|
|
|
|
|
total
|
|
|
|
|
|
)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 更新进度通知
|
|
|
|
|
|
updateProgressDetail(
|
|
|
|
|
|
fillingProgressNotificationId.value,
|
|
|
|
|
|
`${completed}/${total}`,
|
|
|
|
|
|
'正在分配智能体...',
|
|
|
|
|
|
completed,
|
|
|
|
|
|
total
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if (fillingProgressNotificationId.value && !filling) {
|
|
|
|
|
|
// 填充完成,关闭进度通知
|
|
|
|
|
|
removeNotification(fillingProgressNotificationId.value)
|
|
|
|
|
|
fillingProgressNotificationId.value = null
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
{ immediate: true }
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2025-12-15 20:46:54 +08:00
|
|
|
|
// 编辑状态管理
|
|
|
|
|
|
const editingTaskId = ref<string | null>(null)
|
|
|
|
|
|
const editingContent = ref('')
|
|
|
|
|
|
|
|
|
|
|
|
// 添加新产物状态管理
|
2025-12-21 15:28:59 +08:00
|
|
|
|
const isAddingOutput = ref(false)
|
|
|
|
|
|
const newOutputInputRef = ref<HTMLElement>()
|
2025-12-15 20:46:54 +08:00
|
|
|
|
const newOutputName = ref('')
|
|
|
|
|
|
|
2025-12-21 15:28:59 +08:00
|
|
|
|
// 处理加号点击
|
|
|
|
|
|
const handleAddOutputClick = () => {
|
|
|
|
|
|
isAddingOutput.value = true
|
|
|
|
|
|
newOutputName.value = ''
|
|
|
|
|
|
|
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
if (newOutputInputRef.value) {
|
|
|
|
|
|
newOutputInputRef.value?.focus()
|
|
|
|
|
|
}
|
|
|
|
|
|
jsplumb.instance.repaintEverything()
|
|
|
|
|
|
}, 50)
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 保存新产物
|
|
|
|
|
|
const saveNewOutput = () => {
|
|
|
|
|
|
if (newOutputName.value.trim()) {
|
|
|
|
|
|
const outputName = newOutputName.value.trim()
|
|
|
|
|
|
const success = agentsStore.addNewOutput(outputName)
|
|
|
|
|
|
if (success) {
|
|
|
|
|
|
emit('add-output', outputName)
|
|
|
|
|
|
isAddingOutput.value = false
|
|
|
|
|
|
newOutputName.value = ''
|
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
jsplumb.instance.repaintEverything()
|
|
|
|
|
|
}, 50)
|
|
|
|
|
|
})
|
|
|
|
|
|
console.log('添加新产物成功', outputName)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 退出编辑状态
|
|
|
|
|
|
isAddingOutput.value = false
|
|
|
|
|
|
newOutputName.value = ''
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 取消添加产物
|
|
|
|
|
|
const cancelAddOutput = () => {
|
|
|
|
|
|
isAddingOutput.value = false
|
|
|
|
|
|
newOutputName.value = ''
|
|
|
|
|
|
|
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
jsplumb.instance.repaintEverything()
|
|
|
|
|
|
}, 50)
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理新产物的键盘事件
|
|
|
|
|
|
const handleNewOutputKeydown = (event: KeyboardEvent) => {
|
|
|
|
|
|
if (event.key === 'Enter') {
|
|
|
|
|
|
event.preventDefault()
|
|
|
|
|
|
saveNewOutput()
|
|
|
|
|
|
} else if (event.key === 'Escape') {
|
|
|
|
|
|
cancelAddOutput()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 新产物输入框失去焦点处理
|
|
|
|
|
|
const handleNewOutputBlur = () => {
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
if (newOutputName.value.trim() === '') {
|
|
|
|
|
|
cancelAddOutput()
|
|
|
|
|
|
}
|
|
|
|
|
|
}, 100)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-15 20:46:54 +08:00
|
|
|
|
// 开始编辑
|
|
|
|
|
|
const startEditing = (task: IRawStepTask) => {
|
|
|
|
|
|
if (!task.Id) {
|
|
|
|
|
|
console.warn('Task ID is missing, cannot start editing')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
editingTaskId.value = task.Id
|
|
|
|
|
|
editingContent.value = task.TaskContent || ''
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 保存编辑
|
|
|
|
|
|
const saveEditing = () => {
|
|
|
|
|
|
if (editingTaskId.value && editingContent.value.trim()) {
|
|
|
|
|
|
const taskToUpdate = collaborationProcess.value.find(item => item.Id === editingTaskId.value)
|
|
|
|
|
|
if (taskToUpdate) {
|
2026-01-26 15:06:17 +08:00
|
|
|
|
// 保存旧值用于比较
|
|
|
|
|
|
const oldValue = taskToUpdate.TaskContent
|
|
|
|
|
|
const newValue = editingContent.value.trim()
|
|
|
|
|
|
|
|
|
|
|
|
// 只有内容真正变化时才更新和记录修改
|
|
|
|
|
|
if (newValue !== oldValue) {
|
|
|
|
|
|
taskToUpdate.TaskContent = newValue
|
|
|
|
|
|
|
|
|
|
|
|
// 记录修改过的步骤索引到 store(用于重新执行)
|
|
|
|
|
|
const stepIndex = collaborationProcess.value.findIndex(item => item.Id === editingTaskId.value)
|
|
|
|
|
|
if (stepIndex >= 0) {
|
|
|
|
|
|
agentsStore.addModifiedStep(stepIndex)
|
|
|
|
|
|
console.log(`📝 步骤 ${stepIndex + 1} (${taskToUpdate.StepName}) 的 TaskContent 已被修改,将从该步骤重新执行`)
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.log(`ℹ️ 步骤 ${taskToUpdate.StepName} 的 TaskContent 未发生变化,不记录修改`)
|
|
|
|
|
|
}
|
2025-12-15 20:46:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
editingTaskId.value = null
|
|
|
|
|
|
editingContent.value = ''
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 取消编辑
|
|
|
|
|
|
const cancelEditing = () => {
|
|
|
|
|
|
editingTaskId.value = null
|
|
|
|
|
|
editingContent.value = ''
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理键盘事件
|
|
|
|
|
|
const handleKeydown = (event: KeyboardEvent) => {
|
|
|
|
|
|
if (event.key === 'Enter') {
|
|
|
|
|
|
event.preventDefault()
|
|
|
|
|
|
saveEditing()
|
|
|
|
|
|
} else if (event.key === 'Escape') {
|
|
|
|
|
|
cancelEditing()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-29 10:22:14 +08:00
|
|
|
|
function handleCurrentTask(task: IRawStepTask, transparent: boolean): ConnectArg[] {
|
|
|
|
|
|
// 创建当前流程与产出的连线
|
|
|
|
|
|
const arr: ConnectArg[] = [
|
|
|
|
|
|
{
|
|
|
|
|
|
sourceId: `task-syllabus-flow-${task.Id}`,
|
|
|
|
|
|
targetId: `task-syllabus-output-object-${task.Id}`,
|
|
|
|
|
|
anchor: [AnchorLocations.Right, AnchorLocations.Left],
|
|
|
|
|
|
config: {
|
2025-12-15 20:46:54 +08:00
|
|
|
|
transparent
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-29 10:22:14 +08:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
// 创建当前产出与流程的连线
|
2025-12-15 20:46:54 +08:00
|
|
|
|
task.InputObject_List?.forEach(item => {
|
|
|
|
|
|
const id = collaborationProcess.value.find(i => i.OutputObject === item)?.Id
|
2025-10-29 10:22:14 +08:00
|
|
|
|
if (id) {
|
|
|
|
|
|
arr.push({
|
|
|
|
|
|
sourceId: `task-syllabus-output-object-${id}`,
|
|
|
|
|
|
targetId: `task-syllabus-flow-${task.Id}`,
|
|
|
|
|
|
anchor: [AnchorLocations.Left, AnchorLocations.Right],
|
|
|
|
|
|
config: {
|
|
|
|
|
|
type: 'output',
|
2025-12-15 20:46:54 +08:00
|
|
|
|
transparent
|
|
|
|
|
|
}
|
2025-10-29 10:22:14 +08:00
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
return arr
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function changeTask(task?: IRawStepTask, isEmit?: boolean) {
|
|
|
|
|
|
jsplumb.reset()
|
|
|
|
|
|
const arr: ConnectArg[] = []
|
2025-12-15 20:46:54 +08:00
|
|
|
|
agentsStore.agentRawPlan.data?.['Collaboration Process']?.forEach(item => {
|
2025-10-29 10:22:14 +08:00
|
|
|
|
arr.push(...handleCurrentTask(item, item.Id !== task?.Id))
|
|
|
|
|
|
})
|
|
|
|
|
|
jsplumb.connects(arr)
|
|
|
|
|
|
if (isEmit && task) {
|
|
|
|
|
|
emit('setCurrentTask', task)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-31 18:42:31 +08:00
|
|
|
|
function clear() {
|
|
|
|
|
|
jsplumb.reset()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-14 17:54:00 +08:00
|
|
|
|
// 封装连线重绘方法
|
2026-01-09 13:54:32 +08:00
|
|
|
|
const redrawConnections = () => {
|
|
|
|
|
|
// 等待 DOM 更新完成
|
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
|
// 清除旧连线
|
|
|
|
|
|
jsplumb.reset()
|
|
|
|
|
|
|
|
|
|
|
|
// 等待 DOM 稳定后重新绘制
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
const arr: ConnectArg[] = []
|
|
|
|
|
|
const currentTaskId = agentsStore.currentTask?.Id
|
|
|
|
|
|
|
|
|
|
|
|
// 重新绘制所有连线
|
|
|
|
|
|
collaborationProcess.value.forEach(item => {
|
|
|
|
|
|
arr.push(...handleCurrentTask(item, item.Id !== currentTaskId))
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
jsplumb.connects(arr)
|
|
|
|
|
|
}, 100)
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-14 17:54:00 +08:00
|
|
|
|
// 监听 collaborationProcess 变化,自动重绘连线
|
2026-01-09 13:54:32 +08:00
|
|
|
|
watch(
|
|
|
|
|
|
() => collaborationProcess,
|
|
|
|
|
|
() => {
|
|
|
|
|
|
redrawConnections()
|
|
|
|
|
|
},
|
|
|
|
|
|
{ deep: true }
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-01-14 17:54:00 +08:00
|
|
|
|
// 组件挂载后初始化连线
|
2026-01-09 13:54:32 +08:00
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
// 初始化时绘制连线
|
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
const arr: ConnectArg[] = []
|
|
|
|
|
|
collaborationProcess.value.forEach(item => {
|
|
|
|
|
|
arr.push(...handleCurrentTask(item, true))
|
|
|
|
|
|
})
|
|
|
|
|
|
jsplumb.connects(arr)
|
|
|
|
|
|
}, 100)
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2025-10-29 10:22:14 +08:00
|
|
|
|
defineExpose({
|
|
|
|
|
|
changeTask,
|
2025-12-15 20:46:54 +08:00
|
|
|
|
clear
|
2025-10-29 10:22:14 +08:00
|
|
|
|
})
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
|
<div class="h-full flex flex-col">
|
2026-01-23 15:38:09 +08:00
|
|
|
|
<!-- Notification 通知系统 -->
|
|
|
|
|
|
<Notification
|
|
|
|
|
|
:notifications="notifications"
|
|
|
|
|
|
@close="(id) => removeNotification(id)"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
2025-12-15 20:46:54 +08:00
|
|
|
|
<div class="text-[18px] font-bold mb-[18px] text-[var(--color-text-title-header)]">
|
|
|
|
|
|
任务大纲
|
|
|
|
|
|
</div>
|
2025-10-29 10:22:14 +08:00
|
|
|
|
|
|
|
|
|
|
<div
|
|
|
|
|
|
v-loading="agentsStore.agentRawPlan.loading"
|
|
|
|
|
|
class="flex-1 w-full overflow-y-auto relative"
|
|
|
|
|
|
@scroll="handleScroll"
|
|
|
|
|
|
>
|
2025-12-15 20:46:54 +08:00
|
|
|
|
<div
|
|
|
|
|
|
v-show="collaborationProcess.length > 0"
|
|
|
|
|
|
class="w-full relative min-h-full"
|
|
|
|
|
|
id="task-syllabus"
|
|
|
|
|
|
>
|
2025-12-21 15:28:59 +08:00
|
|
|
|
<Bg :is-adding="isAddingOutput" @start-add-output="handleAddOutputClick" />
|
2025-11-04 15:26:52 +08:00
|
|
|
|
|
|
|
|
|
|
<div class="w-full flex items-center gap-[14%] mb-[35px]">
|
|
|
|
|
|
<div class="flex-1 flex justify-center">
|
2025-10-29 10:22:14 +08:00
|
|
|
|
<div
|
2025-12-15 20:46:54 +08:00
|
|
|
|
class="card-item w-[168px] h-[41px] flex justify-center relative z-99 items-center rounded-[20px] bg-[var(--color-bg-flow)]"
|
2025-10-29 10:22:14 +08:00
|
|
|
|
>
|
|
|
|
|
|
流程
|
|
|
|
|
|
</div>
|
2025-11-04 15:26:52 +08:00
|
|
|
|
</div>
|
2025-10-29 10:22:14 +08:00
|
|
|
|
|
2025-11-04 15:26:52 +08:00
|
|
|
|
<div class="flex-1 flex justify-center">
|
|
|
|
|
|
<div
|
2025-12-15 20:46:54 +08:00
|
|
|
|
class="card-item w-[168px] h-[41px] flex justify-center relative z-99 items-center rounded-[20px] bg-[var(--color-bg-flow)]"
|
2025-10-29 10:22:14 +08:00
|
|
|
|
>
|
2025-11-04 15:26:52 +08:00
|
|
|
|
产物
|
|
|
|
|
|
</div>
|
2025-10-29 10:22:14 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-12-21 15:28:59 +08:00
|
|
|
|
<!-- 添加新产物卡片 -->
|
|
|
|
|
|
<div
|
|
|
|
|
|
v-if="isAddingOutput"
|
|
|
|
|
|
class="card-it w-full flex items-center gap-[14%] bg-[var(--color-card-bg)] add-output-form mb-[100px]"
|
|
|
|
|
|
>
|
|
|
|
|
|
<!-- 左侧空白的流程卡片占位 -->
|
|
|
|
|
|
<div class="w-[43%] relative z-99" style="height: 20px"></div>
|
2025-11-04 15:26:52 +08:00
|
|
|
|
|
2025-12-21 15:28:59 +08:00
|
|
|
|
<!-- 右侧可编辑的产物卡片 -->
|
|
|
|
|
|
<el-card
|
|
|
|
|
|
class="w-[43%] relative task-syllabus-output-object-card border-dashed border-2 border-[var(--color-primary)]"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="h-full flex items-center justify-center">
|
|
|
|
|
|
<!-- 输入框 -->
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
ref="newOutputInputRef"
|
|
|
|
|
|
v-model="newOutputName"
|
|
|
|
|
|
placeholder="Enter保存,ESC取消"
|
|
|
|
|
|
@keydown="handleNewOutputKeydown"
|
|
|
|
|
|
@blur="handleNewOutputBlur"
|
|
|
|
|
|
size="large"
|
|
|
|
|
|
class="w-full"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 显示临时产物卡片 -->
|
|
|
|
|
|
<div
|
|
|
|
|
|
v-for="output in agentsStore.additionalOutputs"
|
|
|
|
|
|
:key="output"
|
|
|
|
|
|
class="card-it w-full flex items-center gap-[14%] bg-[var(--color-card-bg)] mb-[100px]"
|
|
|
|
|
|
>
|
|
|
|
|
|
<!-- 左侧空白的流程卡片占位 -->
|
|
|
|
|
|
<div class="w-[43%] relative z-99" style="height: 100px"></div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 右侧产物卡片 -->
|
|
|
|
|
|
<el-card class="w-[43%] relative task-syllabus-output-object-card" :shadow="true">
|
|
|
|
|
|
<div class="text-[18px] font-bold text-center">{{ output }}</div>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
</div>
|
2025-10-29 10:22:14 +08:00
|
|
|
|
<div
|
2025-11-04 15:26:52 +08:00
|
|
|
|
v-for="item in collaborationProcess"
|
|
|
|
|
|
:key="item.Id"
|
2025-12-21 18:10:37 +08:00
|
|
|
|
class="card-it w-full flex items-center gap-[14%] bg-[var(--color-card-bg)] mb-[100px]"
|
2025-10-29 10:22:14 +08:00
|
|
|
|
>
|
2025-11-04 15:26:52 +08:00
|
|
|
|
<!-- 流程卡片 -->
|
|
|
|
|
|
<el-card
|
|
|
|
|
|
class="w-[43%] overflow-y-auto relative z-99 task-syllabus-flow-card"
|
|
|
|
|
|
:class="agentsStore.currentTask?.StepName === item.StepName ? 'active-card' : ''"
|
2025-12-15 20:46:54 +08:00
|
|
|
|
:shadow="true"
|
2025-11-04 15:26:52 +08:00
|
|
|
|
:id="`task-syllabus-flow-${item.Id}`"
|
|
|
|
|
|
@click="changeTask(item, true)"
|
|
|
|
|
|
>
|
|
|
|
|
|
<MultiLineTooltip placement="right" :text="item.StepName" :lines="2">
|
2025-12-15 20:46:54 +08:00
|
|
|
|
<div class="text-[18px] font-bold text-center">{{ item.StepName }}</div>
|
2025-11-04 15:26:52 +08:00
|
|
|
|
</MultiLineTooltip>
|
2025-12-21 18:10:37 +08:00
|
|
|
|
<div class="h-[1px] w-full bg-[var(--color-border-separate)] my-[8px]"></div>
|
2025-12-15 20:46:54 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- 任务内容区域 - 支持双击编辑 -->
|
|
|
|
|
|
<div v-if="editingTaskId === item.Id" class="w-full">
|
|
|
|
|
|
<div class="flex flex-col gap-3">
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-model="editingContent"
|
|
|
|
|
|
type="textarea"
|
|
|
|
|
|
:autosize="{ minRows: 2, maxRows: 4 }"
|
|
|
|
|
|
placeholder="请输入任务内容"
|
|
|
|
|
|
@keydown="handleKeydown"
|
|
|
|
|
|
class="task-content-editor"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
/>
|
2025-12-31 19:04:58 +08:00
|
|
|
|
<div class="flex justify-end">
|
|
|
|
|
|
<svg-icon
|
|
|
|
|
|
icon-class="Check"
|
|
|
|
|
|
size="20px"
|
|
|
|
|
|
color="#328621"
|
|
|
|
|
|
class="cursor-pointer mr-4"
|
|
|
|
|
|
@click="saveEditing"
|
|
|
|
|
|
title="保存"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<svg-icon
|
|
|
|
|
|
icon-class="Cancel"
|
|
|
|
|
|
size="20px"
|
|
|
|
|
|
color="#8e0707"
|
|
|
|
|
|
class="cursor-pointer mr-1"
|
|
|
|
|
|
@click="cancelEditing"
|
|
|
|
|
|
title="取消"
|
|
|
|
|
|
/>
|
2025-12-15 20:46:54 +08:00
|
|
|
|
</div>
|
2025-10-29 10:22:14 +08:00
|
|
|
|
</div>
|
2025-12-15 20:46:54 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<div v-else @dblclick="startEditing(item)" class="w-full cursor-pointer">
|
|
|
|
|
|
<MultiLineTooltip placement="right" :text="item.TaskContent" :lines="3">
|
|
|
|
|
|
<div class="text-[14px] text-[var(--color-text-secondary)] task-content-display">
|
|
|
|
|
|
{{ item.TaskContent }}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</MultiLineTooltip>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2025-12-21 18:10:37 +08:00
|
|
|
|
<div class="h-[1px] w-full bg-[var(--color-border-separate)] my-[8px]"></div>
|
2025-11-04 15:26:52 +08:00
|
|
|
|
<div
|
2026-01-22 17:22:30 +08:00
|
|
|
|
class="flex items-center gap-2 flex-wrap relative w-full"
|
|
|
|
|
|
:class="!item.AgentSelection || item.AgentSelection.length === 0 ? 'min-h-[40px]' : 'overflow-y-auto max-h-[72px]'"
|
2025-11-04 15:26:52 +08:00
|
|
|
|
>
|
|
|
|
|
|
<!-- 连接到智能体库的连接点 -->
|
2025-10-29 10:22:14 +08:00
|
|
|
|
<div
|
2025-11-04 15:26:52 +08:00
|
|
|
|
class="absolute left-[-10px] top-1/2 transform -translate-y-1/2"
|
|
|
|
|
|
:id="`task-syllabus-flow-agents-${item.Id}`"
|
|
|
|
|
|
></div>
|
2026-01-22 17:22:30 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- 未填充智能体时显示Loading -->
|
|
|
|
|
|
<div
|
2026-01-23 15:38:09 +08:00
|
|
|
|
v-if="(!item.AgentSelection || item.AgentSelection.length === 0) && !agentsStore.hasStoppedFilling"
|
2026-01-22 17:22:30 +08:00
|
|
|
|
class="flex items-center gap-2 text-[var(--color-text-secondary)] text-[14px]"
|
2025-10-29 10:22:14 +08:00
|
|
|
|
>
|
2026-01-22 17:22:30 +08:00
|
|
|
|
<el-icon class="is-loading" :size="20">
|
|
|
|
|
|
<Loading />
|
|
|
|
|
|
</el-icon>
|
|
|
|
|
|
<span>正在分配智能体...</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 已填充智能体时显示智能体列表 -->
|
|
|
|
|
|
<template v-else>
|
|
|
|
|
|
<el-tooltip
|
|
|
|
|
|
v-for="agentSelection in item.AgentSelection"
|
|
|
|
|
|
:key="agentSelection"
|
|
|
|
|
|
effect="light"
|
|
|
|
|
|
placement="right"
|
|
|
|
|
|
>
|
|
|
|
|
|
<template #content>
|
|
|
|
|
|
<div class="w-[150px]">
|
|
|
|
|
|
<div class="text-[18px] font-bold">{{ agentSelection }}</div>
|
|
|
|
|
|
<div class="h-[1px] w-full bg-[#494B51] my-[8px]"></div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
{{
|
|
|
|
|
|
item.TaskProcess.find(i => i.AgentName === agentSelection)?.Description
|
|
|
|
|
|
}}
|
|
|
|
|
|
</div>
|
2025-11-04 15:26:52 +08:00
|
|
|
|
</div>
|
2026-01-22 17:22:30 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="w-[31px] h-[31px] rounded-full flex items-center justify-center"
|
|
|
|
|
|
:style="{ background: getAgentMapIcon(agentSelection).color }"
|
|
|
|
|
|
>
|
|
|
|
|
|
<svg-icon
|
|
|
|
|
|
:icon-class="getAgentMapIcon(agentSelection).icon"
|
|
|
|
|
|
color="#fff"
|
|
|
|
|
|
size="24px"
|
|
|
|
|
|
/>
|
2025-11-04 15:26:52 +08:00
|
|
|
|
</div>
|
2026-01-22 17:22:30 +08:00
|
|
|
|
</el-tooltip>
|
|
|
|
|
|
</template>
|
2025-10-29 10:22:14 +08:00
|
|
|
|
</div>
|
2025-11-04 15:26:52 +08:00
|
|
|
|
</el-card>
|
2025-12-15 20:46:54 +08:00
|
|
|
|
<!-- 产物卡片 -->
|
2025-11-04 15:26:52 +08:00
|
|
|
|
<el-card
|
2025-12-15 20:46:54 +08:00
|
|
|
|
class="w-[43%] relative task-syllabus-output-object-card"
|
|
|
|
|
|
:shadow="true"
|
2025-11-04 15:26:52 +08:00
|
|
|
|
:class="agentsStore.currentTask?.StepName === item.StepName ? 'active-card' : ''"
|
|
|
|
|
|
:id="`task-syllabus-output-object-${item.Id}`"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="text-[18px] font-bold text-center">{{ item.OutputObject }}</div>
|
|
|
|
|
|
</el-card>
|
2025-10-29 10:22:14 +08:00
|
|
|
|
</div>
|
2025-12-21 15:28:59 +08:00
|
|
|
|
</div>
|
2025-10-29 10:22:14 +08:00
|
|
|
|
</div>
|
2026-01-14 17:54:00 +08:00
|
|
|
|
<BranchButton v-if="planReady" @click="openPlanModification" />
|
2025-10-29 10:22:14 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
2025-11-04 15:26:52 +08:00
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
|
.task-syllabus-flow-card {
|
2025-12-15 20:46:54 +08:00
|
|
|
|
background-color: var(--color-card-bg-task);
|
|
|
|
|
|
border: 1px solid var(--color-card-border-task);
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
transition: border-color 0.2s ease;
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
background-color: var(--color-card-bg-task-hover);
|
|
|
|
|
|
border-color: var(--color-card-border-hover);
|
|
|
|
|
|
box-shadow: var(--color-card-shadow-hover);
|
|
|
|
|
|
}
|
2025-11-04 15:26:52 +08:00
|
|
|
|
:deep(.el-card__body) {
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
overflow: auto;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-12-15 20:46:54 +08:00
|
|
|
|
|
|
|
|
|
|
.task-syllabus-output-object-card {
|
|
|
|
|
|
background-color: var(--color-card-bg-task);
|
|
|
|
|
|
border: 1px solid var(--color-card-border-task);
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
transition: border-color 0.2s ease;
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
background-color: var(--color-card-bg-task-hover);
|
|
|
|
|
|
border-color: var(--color-card-border-hover);
|
|
|
|
|
|
box-shadow: var(--color-card-shadow-hover);
|
|
|
|
|
|
}
|
|
|
|
|
|
:deep(.el-card__body) {
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
overflow: auto;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.task-content-editor {
|
|
|
|
|
|
:deep(.el-textarea__inner) {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
border: 1px solid #dcdfe6;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
resize: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.task-content-display {
|
|
|
|
|
|
min-height: 40px;
|
|
|
|
|
|
word-break: break-word;
|
|
|
|
|
|
white-space: pre-wrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.add-output-btn {
|
|
|
|
|
|
opacity: 0.8;
|
|
|
|
|
|
transition: opacity 0.2s ease;
|
|
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
button {
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
background-color: rgba(59, 130, 246, 0.05);
|
|
|
|
|
|
transform: translateY(-1px);
|
|
|
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
&:active {
|
|
|
|
|
|
transform: translateY(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.add-output-form {
|
|
|
|
|
|
animation: slideDown 0.3s ease-out;
|
2025-12-21 15:28:59 +08:00
|
|
|
|
:deep(.el-card__body) {
|
|
|
|
|
|
padding: 16px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
:deep(.el-input__wrapper) {
|
|
|
|
|
|
border: 1px solid var(--color-text);
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
box-shadow: none;
|
|
|
|
|
|
|
|
|
|
|
|
&.is-focus {
|
|
|
|
|
|
border-color: var(--color-text);
|
|
|
|
|
|
box-shadow: 0 0 0 1px var(--color-primary-light);
|
|
|
|
|
|
}
|
|
|
|
|
|
:deep(.el-input__inner) {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-12-15 20:46:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@keyframes slideDown {
|
|
|
|
|
|
from {
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
transform: translateY(-10px);
|
|
|
|
|
|
}
|
|
|
|
|
|
to {
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
transform: translateY(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 输入框样式
|
|
|
|
|
|
:deep(.el-input__wrapper) {
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
border: 1px solid #dcdfe6;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
box-shadow: none;
|
|
|
|
|
|
transition: all 0.2s ease;
|
|
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
border-color: #c0c4cc;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
&.is-focus {
|
|
|
|
|
|
border-color: #409eff;
|
|
|
|
|
|
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-input__inner) {
|
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 任务内容编辑按钮样式 - 匹配执行结果编辑按钮样式
|
|
|
|
|
|
.task-content-editor {
|
|
|
|
|
|
.el-button {
|
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
|
|
|
|
|
|
&.el-button--small {
|
|
|
|
|
|
padding: 4px 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 在深色模式下,按钮背景会自动适配为深色
|
|
|
|
|
|
html.dark & {
|
|
|
|
|
|
.el-button {
|
|
|
|
|
|
&.el-button--primary {
|
|
|
|
|
|
background-color: var(--color-bg-detail);
|
|
|
|
|
|
border-color: var(--color-border);
|
|
|
|
|
|
color: var(--color-text);
|
|
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
background-color: var(--color-bg-hover);
|
|
|
|
|
|
border-color: var(--color-text-hover);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
&:not(.el-button--primary) {
|
|
|
|
|
|
background-color: var(--color-bg-detail);
|
|
|
|
|
|
border-color: var(--color-border);
|
|
|
|
|
|
color: var(--color-text);
|
|
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
background-color: var(--color-bg-hover);
|
|
|
|
|
|
border-color: var(--color-text-hover);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-04 15:26:52 +08:00
|
|
|
|
</style>
|