feat:执行过程编排新建分支单节点删除和整个分支节点删除
This commit is contained in:
@@ -2508,6 +2508,114 @@ def handle_delete_task_process_branch(data):
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@socketio.on('delete_task_process_node')
|
||||||
|
def handle_delete_task_process_node(data):
|
||||||
|
"""
|
||||||
|
WebSocket版本:删除任务过程分支中的单个节点
|
||||||
|
|
||||||
|
请求格式:
|
||||||
|
{
|
||||||
|
"id": "request-id",
|
||||||
|
"action": "delete_task_process_node",
|
||||||
|
"data": {
|
||||||
|
"task_id": "task-id", // 大任务ID(数据库主键)
|
||||||
|
"stepId": "step-id", // 小任务ID
|
||||||
|
"branchId": "branch-id", // 分支ID
|
||||||
|
"nodeId": "node-id" // 要删除的节点ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
request_id = data.get('id')
|
||||||
|
incoming_data = data.get('data', {})
|
||||||
|
task_id = incoming_data.get('task_id')
|
||||||
|
step_id = incoming_data.get('stepId')
|
||||||
|
branch_id = incoming_data.get('branchId')
|
||||||
|
node_id = incoming_data.get('nodeId')
|
||||||
|
edges = incoming_data.get('edges', []) # 更新后的 edges 数据
|
||||||
|
|
||||||
|
if not task_id or not step_id or not branch_id or not node_id:
|
||||||
|
emit('response', {
|
||||||
|
'id': request_id,
|
||||||
|
'status': 'error',
|
||||||
|
'error': '缺少必要参数:task_id, stepId, branchId, nodeId'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
with get_db_context() as db:
|
||||||
|
# 获取现有的 branches 数据
|
||||||
|
existing_task = MultiAgentTaskCRUD.get_by_id(db, task_id)
|
||||||
|
|
||||||
|
if existing_task:
|
||||||
|
# 使用深拷贝避免修改共享引用
|
||||||
|
existing_branches = copy.deepcopy(existing_task.branches) if existing_task.branches else {}
|
||||||
|
|
||||||
|
if isinstance(existing_branches, dict):
|
||||||
|
# 获取现有的 task_process_branches
|
||||||
|
task_process_branches = existing_branches.get('task_process_branches', {})
|
||||||
|
|
||||||
|
if step_id in task_process_branches:
|
||||||
|
step_branches = task_process_branches[step_id]
|
||||||
|
|
||||||
|
# 遍历所有 agentGroupKey 下的分支
|
||||||
|
for agent_key, branches_list in step_branches.items():
|
||||||
|
if isinstance(branches_list, list):
|
||||||
|
for branch in branches_list:
|
||||||
|
if branch.get('id') == branch_id:
|
||||||
|
# 找到目标分支,删除指定的节点
|
||||||
|
nodes = branch.get('nodes', [])
|
||||||
|
tasks = branch.get('tasks', [])
|
||||||
|
|
||||||
|
# 找到并删除节点
|
||||||
|
for i, node in enumerate(nodes):
|
||||||
|
if node.get('id') == node_id:
|
||||||
|
nodes.pop(i)
|
||||||
|
if i < len(tasks):
|
||||||
|
tasks.pop(i)
|
||||||
|
break
|
||||||
|
|
||||||
|
# 更新分支数据(包括 nodes, tasks, edges)
|
||||||
|
branch['nodes'] = nodes
|
||||||
|
branch['tasks'] = tasks
|
||||||
|
branch['edges'] = edges # 使用前端传入的更新后的 edges
|
||||||
|
break
|
||||||
|
|
||||||
|
# 更新 branches 数据
|
||||||
|
existing_branches['task_process_branches'] = task_process_branches
|
||||||
|
|
||||||
|
# 直接更新数据库
|
||||||
|
existing_task.branches = existing_branches
|
||||||
|
db.flush()
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
print(f"[delete_task_process_node] 删除成功,task_id={task_id}, step_id={step_id}, branch_id={branch_id}, node_id={node_id}")
|
||||||
|
|
||||||
|
emit('response', {
|
||||||
|
'id': request_id,
|
||||||
|
'status': 'success',
|
||||||
|
'data': {
|
||||||
|
"message": "节点删除成功",
|
||||||
|
"deleted_node_id": node_id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return
|
||||||
|
|
||||||
|
# 如果找不到对应的节点
|
||||||
|
print(f"[delete_task_process_node] 警告: 找不到要删除的节点,task_id={task_id}, step_id={step_id}, branch_id={branch_id}, node_id={node_id}")
|
||||||
|
emit('response', {
|
||||||
|
'id': request_id,
|
||||||
|
'status': 'error',
|
||||||
|
'error': '未找到要删除的节点'
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
emit('response', {
|
||||||
|
'id': request_id,
|
||||||
|
'status': 'error',
|
||||||
|
'error': str(e)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
@socketio.on('save_task_outline')
|
@socketio.on('save_task_outline')
|
||||||
def handle_save_task_outline(data):
|
def handle_save_task_outline(data):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -710,6 +710,43 @@ class Api {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除任务过程分支中的单个节点
|
||||||
|
* @param TaskID 大任务ID
|
||||||
|
* @param stepId 小任务ID
|
||||||
|
* @param branchId 分支ID
|
||||||
|
* @param nodeId 要删除的节点ID
|
||||||
|
* @param edges 更新后的 edges 数据
|
||||||
|
* @returns 是否删除成功
|
||||||
|
*/
|
||||||
|
deleteTaskProcessNode = async (
|
||||||
|
TaskID: string,
|
||||||
|
stepId: string,
|
||||||
|
branchId: string,
|
||||||
|
nodeId: string,
|
||||||
|
edges: any[] = [],
|
||||||
|
): Promise<boolean> => {
|
||||||
|
if (!websocket.connected) {
|
||||||
|
throw new Error('WebSocket未连接')
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const rawResponse = await websocket.send('delete_task_process_node', {
|
||||||
|
task_id: TaskID,
|
||||||
|
stepId,
|
||||||
|
branchId,
|
||||||
|
nodeId,
|
||||||
|
edges,
|
||||||
|
})
|
||||||
|
|
||||||
|
const response = this.extractResponse<{ status: string }>(rawResponse)
|
||||||
|
return response?.status === 'success' || false
|
||||||
|
} catch (error) {
|
||||||
|
console.error('删除任务过程节点数据失败:', error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新任务大纲数据
|
* 更新任务大纲数据
|
||||||
* @param taskId 任务ID
|
* @param taskId 任务ID
|
||||||
|
|||||||
@@ -39,6 +39,10 @@ const branchLoading = ref(false)
|
|||||||
const deleteDialogVisible = ref(false)
|
const deleteDialogVisible = ref(false)
|
||||||
const branchIdToDelete = ref<string | null>(null)
|
const branchIdToDelete = ref<string | null>(null)
|
||||||
|
|
||||||
|
// 删除单个节点弹窗相关状态
|
||||||
|
const deleteNodeDialogVisible = ref(false)
|
||||||
|
const nodeIdToDelete = ref<string | null>(null)
|
||||||
|
|
||||||
// 节点和边数据
|
// 节点和边数据
|
||||||
const nodes = ref<Node[]>([])
|
const nodes = ref<Node[]>([])
|
||||||
const edges = ref<Edge[]>([])
|
const edges = ref<Edge[]>([])
|
||||||
@@ -387,29 +391,75 @@ const getBranchParentChain = (targetNodeId: string): string[] => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 判断节点是否可删除(分支从底部出发连接的节点)
|
// 判断节点是否可删除(分支从底部出发连接的节点)
|
||||||
const getNodeDeletable = (nodeId: string): { isDeletable: boolean; branchId: string | null } => {
|
const getNodeDeletable = (
|
||||||
// 找到指向该节点的边
|
nodeId: string
|
||||||
const incomingEdges = edges.value.filter(edge => edge.target === nodeId)
|
): { isDeletable: boolean; branchId: string | null; parentNodeId: string | null } => {
|
||||||
|
// 直接从 store 的分支数据里用节点 id 查找它属于哪个分支
|
||||||
|
const taskStepId = currentTask.value?.Id
|
||||||
|
const currentAgents = currentTask.value?.AgentSelection || []
|
||||||
|
|
||||||
// 检查是否有从底部(sourceHandle='bottom')连接过来的边
|
if (!taskStepId) {
|
||||||
const bottomEdge = incomingEdges.find(edge => edge.sourceHandle === 'bottom')
|
return {
|
||||||
|
isDeletable: false,
|
||||||
|
branchId: null,
|
||||||
|
parentNodeId: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (bottomEdge && bottomEdge.data?.branchId) {
|
// 获取所有分支数据
|
||||||
|
const savedBranches = selectionStore.getTaskProcessBranches(taskStepId, currentAgents)
|
||||||
|
|
||||||
|
// 遍历所有分支,用节点 id 在 nodes 数组中查找
|
||||||
|
for (const branch of savedBranches) {
|
||||||
|
// 检查该节点是否是该分支的第一个节点
|
||||||
|
const firstNode = branch.nodes?.[0]
|
||||||
|
if (firstNode && firstNode.id === nodeId) {
|
||||||
|
// 只有该分支的第一个节点才显示删除分支按钮
|
||||||
return {
|
return {
|
||||||
isDeletable: true,
|
isDeletable: true,
|
||||||
branchId: bottomEdge.data.branchId
|
branchId: branch.id,
|
||||||
|
parentNodeId: branch.parentNodeId || null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isDeletable: false,
|
isDeletable: false,
|
||||||
branchId: null
|
branchId: null,
|
||||||
|
parentNodeId: null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除分支(显示确认对话框)
|
// 删除分支(显示确认对话框)
|
||||||
const handleDeleteBranch = (branchId: string) => {
|
const handleDeleteBranch = (nodeId: string) => {
|
||||||
branchIdToDelete.value = branchId
|
// 获取节点的删除信息(包括 parentNodeId)
|
||||||
|
const nodeInfo = getNodeDeletable(nodeId)
|
||||||
|
if (!nodeInfo.isDeletable || !nodeInfo.parentNodeId) {
|
||||||
|
ElMessage.error('无法删除该分支')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const taskStepId = currentTask.value?.Id
|
||||||
|
const currentAgents = currentTask.value?.AgentSelection || []
|
||||||
|
|
||||||
|
if (!taskStepId) return
|
||||||
|
|
||||||
|
// 通过 branchId 查找正确的分支(使用 branchId 精确匹配,而不是 parentNodeId)
|
||||||
|
const savedBranches = selectionStore.getTaskProcessBranches(taskStepId, currentAgents)
|
||||||
|
const targetBranch = savedBranches.find(branch => branch.id === nodeInfo.branchId)
|
||||||
|
|
||||||
|
if (!targetBranch) {
|
||||||
|
ElMessage.error('未找到该分支')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
'[删除分支] 找到的 branchId:',
|
||||||
|
targetBranch.id,
|
||||||
|
'parentNodeId:',
|
||||||
|
nodeInfo.parentNodeId
|
||||||
|
)
|
||||||
|
branchIdToDelete.value = targetBranch.id
|
||||||
deleteDialogVisible.value = true
|
deleteDialogVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -464,6 +514,137 @@ const confirmDeleteBranch = async () => {
|
|||||||
ElMessage.success('分支删除成功')
|
ElMessage.success('分支删除成功')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 删除单个节点(显示确认对话框)
|
||||||
|
const handleDeleteNode = (nodeId: string) => {
|
||||||
|
nodeIdToDelete.value = nodeId
|
||||||
|
deleteNodeDialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确认删除单个节点
|
||||||
|
const confirmDeleteNode = async () => {
|
||||||
|
if (!nodeIdToDelete.value) return
|
||||||
|
|
||||||
|
const nodeId = nodeIdToDelete.value
|
||||||
|
const taskStepId = currentTask.value?.Id
|
||||||
|
const currentAgents = currentTask.value?.AgentSelection || []
|
||||||
|
|
||||||
|
// 0. 检查被删除的节点是否是分支的第一个节点(带删除分支按钮)
|
||||||
|
const nodeDeletable = getNodeDeletable(nodeId)
|
||||||
|
const deletedBranchId = nodeDeletable.isDeletable ? nodeDeletable.branchId : null
|
||||||
|
|
||||||
|
// 1. 找到该节点的入边和出边
|
||||||
|
const incomingEdges = edges.value.filter(e => e.target === nodeId)
|
||||||
|
const outgoingEdges = edges.value.filter(e => e.source === nodeId)
|
||||||
|
|
||||||
|
// 1.1 保存删除前的子节点信息(用于后续更新 branchId)
|
||||||
|
const childNodeIds = outgoingEdges.map(e => e.target)
|
||||||
|
|
||||||
|
// 2. 将子节点重新连接到父节点(使用固定的 left/right handle)
|
||||||
|
const newEdges: Edge[] = []
|
||||||
|
outgoingEdges.forEach(outEdge => {
|
||||||
|
incomingEdges.forEach(inEdge => {
|
||||||
|
// 创建新边,连接父节点(右侧)到子节点(左侧)
|
||||||
|
const newEdge = {
|
||||||
|
id: `e-${inEdge.source}-${outEdge.target}-${Date.now()}`,
|
||||||
|
source: inEdge.source,
|
||||||
|
target: outEdge.target,
|
||||||
|
sourceHandle: 'right', // 父节点右侧
|
||||||
|
targetHandle: 'left', // 子节点左侧
|
||||||
|
type: 'smoothstep',
|
||||||
|
animated: true,
|
||||||
|
style: outEdge.style,
|
||||||
|
markerEnd: outEdge.markerEnd,
|
||||||
|
data: inEdge.data // 保留父节点的 branchId
|
||||||
|
}
|
||||||
|
edges.value.push(newEdge)
|
||||||
|
newEdges.push(newEdge)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 3. 删除该节点的入边和出边
|
||||||
|
const relatedEdgeIds = [...incomingEdges.map(e => e.id), ...outgoingEdges.map(e => e.id)]
|
||||||
|
edges.value = edges.value.filter(e => !relatedEdgeIds.includes(e.id))
|
||||||
|
|
||||||
|
// 4. 删除该节点
|
||||||
|
nodes.value = nodes.value.filter(n => n.id !== nodeId)
|
||||||
|
|
||||||
|
// 4.1 如果删除的是分支的第一个节点,需要更新新第一个节点的边信息
|
||||||
|
if (deletedBranchId && childNodeIds.length > 0) {
|
||||||
|
// 遍历所有子节点,更新它们的入边信息
|
||||||
|
childNodeIds.forEach(childNodeId => {
|
||||||
|
// 找到连接子节点的入边
|
||||||
|
const childIncomingEdge = edges.value.find(
|
||||||
|
e => e.target === childNodeId && e.sourceHandle === 'right'
|
||||||
|
)
|
||||||
|
if (childIncomingEdge) {
|
||||||
|
// 更新边的 sourceHandle 为 'bottom' 并添加 branchId,使新节点成为可删除分支的入口
|
||||||
|
childIncomingEdge.sourceHandle = 'bottom'
|
||||||
|
childIncomingEdge.data = {
|
||||||
|
...childIncomingEdge.data,
|
||||||
|
branchId: deletedBranchId
|
||||||
|
}
|
||||||
|
console.log('[删除节点] 已更新新节点的边信息:', childNodeId, deletedBranchId)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 精准删除 store 中对应分支的节点数据,并调用后端接口删除
|
||||||
|
if (taskStepId && currentAgents.length > 0) {
|
||||||
|
// 获取所有分支数据
|
||||||
|
const branches = selectionStore.getTaskProcessBranches(taskStepId, currentAgents)
|
||||||
|
|
||||||
|
// 找到被删除节点所属的分支
|
||||||
|
const targetBranch = branches.find(branch => branch.nodes?.some((n: any) => n.id === nodeId))
|
||||||
|
|
||||||
|
if (targetBranch) {
|
||||||
|
// 使用精准删除方法(前端 store)
|
||||||
|
const success = selectionStore.removeNodeFromBranch(
|
||||||
|
taskStepId,
|
||||||
|
currentAgents,
|
||||||
|
targetBranch.id,
|
||||||
|
nodeId,
|
||||||
|
incomingEdges,
|
||||||
|
outgoingEdges,
|
||||||
|
newEdges
|
||||||
|
)
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
console.log('[删除节点] 前端精准删除成功')
|
||||||
|
|
||||||
|
// 调用后端接口删除节点,传递更新后的 edges 数据
|
||||||
|
const TaskID = (window as any).__CURRENT_TASK_ID__
|
||||||
|
if (TaskID) {
|
||||||
|
const result = await selectionStore.deleteTaskProcessNodeFromDB(
|
||||||
|
TaskID,
|
||||||
|
taskStepId,
|
||||||
|
targetBranch.id,
|
||||||
|
nodeId,
|
||||||
|
edges.value
|
||||||
|
)
|
||||||
|
console.log('[删除节点] 后端删除结果:', result)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error('[删除节点] 前端精准删除失败')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error('[删除节点] 未找到节点所属的分支')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error('[删除节点] 缺少 taskStepId 或 currentAgents')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 刷新视图
|
||||||
|
nextTick(() => {
|
||||||
|
fit({ padding: 0.15, duration: 300 })
|
||||||
|
})
|
||||||
|
|
||||||
|
// 7. 清理状态
|
||||||
|
deleteNodeDialogVisible.value = false
|
||||||
|
nodeIdToDelete.value = null
|
||||||
|
|
||||||
|
ElMessage.success('节点删除成功')
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化流程图
|
// 初始化流程图
|
||||||
const initializeFlow = () => {
|
const initializeFlow = () => {
|
||||||
if (!currentTask.value) {
|
if (!currentTask.value) {
|
||||||
@@ -1043,7 +1224,7 @@ const submitBranch = async (branchContent: string) => {
|
|||||||
// 基于同一父节点下的分支数量计算位置
|
// 基于同一父节点下的分支数量计算位置
|
||||||
const currentAgents = currentTask.value?.AgentSelection || []
|
const currentAgents = currentTask.value?.AgentSelection || []
|
||||||
const sameParentBranches = selectionStore
|
const sameParentBranches = selectionStore
|
||||||
.getTaskProcessBranches(currentTask.value.Id, currentAgents)
|
.getTaskProcessBranches(currentTask.value?.Id || '', currentAgents)
|
||||||
.filter(b => b.parentNodeId === parentNodeId)
|
.filter(b => b.parentNodeId === parentNodeId)
|
||||||
// 新分支的索引 = 现有分支数量(放在最后)
|
// 新分支的索引 = 现有分支数量(放在最后)
|
||||||
const newBranchIndex = sameParentBranches.length
|
const newBranchIndex = sameParentBranches.length
|
||||||
@@ -1153,7 +1334,7 @@ const submitBranch = async (branchContent: string) => {
|
|||||||
// 基于同一父节点下的分支数量计算位置
|
// 基于同一父节点下的分支数量计算位置
|
||||||
const currentAgents = currentTask.value?.AgentSelection || []
|
const currentAgents = currentTask.value?.AgentSelection || []
|
||||||
const sameParentBranches = selectionStore
|
const sameParentBranches = selectionStore
|
||||||
.getTaskProcessBranches(currentTask.value!.Id, currentAgents)
|
.getTaskProcessBranches(currentTask.value?.Id || '', currentAgents)
|
||||||
.filter(b => b.parentNodeId === parentNodeId)
|
.filter(b => b.parentNodeId === parentNodeId)
|
||||||
const branchIndex = sameParentBranches.length
|
const branchIndex = sameParentBranches.length
|
||||||
|
|
||||||
@@ -1257,7 +1438,7 @@ defineExpose({
|
|||||||
v-model:edges="styledEdges"
|
v-model:edges="styledEdges"
|
||||||
:delete-key-code="null"
|
:delete-key-code="null"
|
||||||
:default-viewport="{ zoom: 0.5, x: 0, y: 0 }"
|
:default-viewport="{ zoom: 0.5, x: 0, y: 0 }"
|
||||||
:min-zoom="0.5"
|
:min-zoom="0.2"
|
||||||
:max-zoom="4"
|
:max-zoom="4"
|
||||||
@node-click="onNodeClick"
|
@node-click="onNodeClick"
|
||||||
class="vue-flow-container"
|
class="vue-flow-container"
|
||||||
@@ -1278,16 +1459,16 @@ defineExpose({
|
|||||||
<div
|
<div
|
||||||
v-if="addingBranchNodeId !== 'root'"
|
v-if="addingBranchNodeId !== 'root'"
|
||||||
class="external-add-btn"
|
class="external-add-btn"
|
||||||
@click="startAddBranch('root')"
|
@click.stop="startAddBranch('root')"
|
||||||
>
|
>
|
||||||
<el-icon :size="40" color="#000000">
|
<el-icon :size="12" color="#000000">
|
||||||
<CirclePlus />
|
<CirclePlus />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 取消按钮 -->
|
<!-- 取消按钮 -->
|
||||||
<div v-else class="external-add-btn cancel-btn" @click="cancelAddBranch">
|
<div v-else class="external-add-btn cancel-btn" @click.stop="cancelAddBranch">
|
||||||
<el-icon :size="40" color="#f56c6c">
|
<el-icon :size="12" color="#f56c6c">
|
||||||
<Remove />
|
<Remove />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</div>
|
</div>
|
||||||
@@ -1308,7 +1489,16 @@ defineExpose({
|
|||||||
<div
|
<div
|
||||||
v-if="getNodeDeletable(nodeProps.id).isDeletable"
|
v-if="getNodeDeletable(nodeProps.id).isDeletable"
|
||||||
class="node-delete-btn left"
|
class="node-delete-btn left"
|
||||||
@click="handleDeleteBranch(getNodeDeletable(nodeProps.id).branchId!)"
|
@click.stop="handleDeleteBranch(nodeProps.id)"
|
||||||
|
>
|
||||||
|
<svg-icon icon-class="close" size="14px" color="#d8d8d8" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 删除单个节点按钮 - 非root节点显示,在右上角 -->
|
||||||
|
<div
|
||||||
|
v-if="nodeProps.id !== 'root'"
|
||||||
|
class="node-delete-btn-single"
|
||||||
|
@click.stop="handleDeleteNode(nodeProps.id)"
|
||||||
>
|
>
|
||||||
<svg-icon icon-class="close" size="14px" color="#d8d8d8" />
|
<svg-icon icon-class="close" size="14px" color="#d8d8d8" />
|
||||||
</div>
|
</div>
|
||||||
@@ -1365,16 +1555,16 @@ defineExpose({
|
|||||||
<div
|
<div
|
||||||
v-if="addingBranchNodeId !== nodeProps.id"
|
v-if="addingBranchNodeId !== nodeProps.id"
|
||||||
class="external-add-btn"
|
class="external-add-btn"
|
||||||
@click="startAddBranch(nodeProps.id)"
|
@click.stop="startAddBranch(nodeProps.id)"
|
||||||
>
|
>
|
||||||
<el-icon :size="40" color="#000000">
|
<el-icon :size="12" color="#000000">
|
||||||
<CirclePlus />
|
<CirclePlus />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 取消按钮 -->
|
<!-- 取消按钮 -->
|
||||||
<div v-else class="external-add-btn cancel-btn" @click="cancelAddBranch">
|
<div v-else class="external-add-btn cancel-btn" @click.stop="cancelAddBranch">
|
||||||
<el-icon :size="40" color="#f56c6c">
|
<el-icon :size="12" color="#f56c6c">
|
||||||
<Remove />
|
<Remove />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</div>
|
</div>
|
||||||
@@ -1397,6 +1587,14 @@ defineExpose({
|
|||||||
content="删除后,该分支无法恢复 !"
|
content="删除后,该分支无法恢复 !"
|
||||||
@confirm="confirmDeleteBranch"
|
@confirm="confirmDeleteBranch"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- 删除单个节点确认对话框 -->
|
||||||
|
<DeleteConfirmDialog
|
||||||
|
v-model="deleteNodeDialogVisible"
|
||||||
|
title="确认删除该节点 ?"
|
||||||
|
content="删除后,该节点无法恢复 !"
|
||||||
|
@confirm="confirmDeleteNode"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -1520,14 +1718,13 @@ defineExpose({
|
|||||||
// 外部添加按钮
|
// 外部添加按钮
|
||||||
.external-add-btn {
|
.external-add-btn {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: -20px;
|
bottom: -5px;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
width: 32px;
|
width: 12px;
|
||||||
height: 32px;
|
height: 12px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border: 2px solid #409eff;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -1718,7 +1915,28 @@ defineExpose({
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: #ff4d4f;
|
background-color: #0c0c0c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 单节点删除按钮(右上角)- 与左侧分支删除按钮样式一致
|
||||||
|
.node-delete-btn-single {
|
||||||
|
position: absolute;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #2d2d2d;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
z-index: 100;
|
||||||
|
top: -12px;
|
||||||
|
right: -12px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #0c0c0c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -134,9 +134,11 @@ export const useSelectionStore = defineStore('selection', () => {
|
|||||||
nodes: Node[]
|
nodes: Node[]
|
||||||
edges: Edge[]
|
edges: Edge[]
|
||||||
tasks: IRawStepTask[]
|
tasks: IRawStepTask[]
|
||||||
|
id?: string // 允许传入自定义 id
|
||||||
},
|
},
|
||||||
): string {
|
): string {
|
||||||
const branchId = `task-process-branch-${uuidv4()}`
|
const branchId = data.id || `task-process-branch-${uuidv4()}`
|
||||||
|
console.log('[store.addTaskProcessBranch] 生成的 branchId:', branchId, '传入的 id:', data.id)
|
||||||
const agentGroupKey = getAgentGroupKey(agents)
|
const agentGroupKey = getAgentGroupKey(agents)
|
||||||
|
|
||||||
const newBranch: IBranchData = {
|
const newBranch: IBranchData = {
|
||||||
@@ -225,6 +227,61 @@ export const useSelectionStore = defineStore('selection', () => {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 精准删除分支中的单个节点
|
||||||
|
* @param taskStepId 任务步骤 ID
|
||||||
|
* @param agents Agent 列表
|
||||||
|
* @param branchId 分支 ID
|
||||||
|
* @param nodeId 要删除的节点 ID
|
||||||
|
* @param incomingEdges 入边(用于更新 edges)
|
||||||
|
* @param outgoingEdges 出边(用于更新 edges)
|
||||||
|
* @param newEdges 新创建的边(用于重新连接)
|
||||||
|
* @returns 是否删除成功
|
||||||
|
*/
|
||||||
|
function removeNodeFromBranch(
|
||||||
|
taskStepId: string,
|
||||||
|
agents: string[],
|
||||||
|
branchId: string,
|
||||||
|
nodeId: string,
|
||||||
|
incomingEdges: any[] = [],
|
||||||
|
outgoingEdges: any[] = [],
|
||||||
|
newEdges: any[] = [],
|
||||||
|
): boolean {
|
||||||
|
const agentGroupKey = getAgentGroupKey(agents)
|
||||||
|
const branches = taskProcessBranchesMap.value.get(taskStepId)?.get(agentGroupKey)
|
||||||
|
if (!branches) return false
|
||||||
|
|
||||||
|
const branchIndex = branches.findIndex((branch) => branch.id === branchId)
|
||||||
|
if (branchIndex === -1) return false
|
||||||
|
|
||||||
|
const branch = branches[branchIndex]
|
||||||
|
if (!branch || !branch.nodes || !branch.tasks) return false
|
||||||
|
|
||||||
|
// 找到节点在分支中的索引
|
||||||
|
const nodeIndex = branch.nodes.findIndex((n: any) => n.id === nodeId)
|
||||||
|
if (nodeIndex === -1) return false
|
||||||
|
|
||||||
|
// 精准删除:只删除对应索引的节点和任务
|
||||||
|
branch.nodes.splice(nodeIndex, 1)
|
||||||
|
branch.tasks.splice(nodeIndex, 1)
|
||||||
|
|
||||||
|
// 更新 edges:移除相关边,添加新边
|
||||||
|
if (incomingEdges.length > 0 || outgoingEdges.length > 0) {
|
||||||
|
const relatedEdgeIds = [
|
||||||
|
...incomingEdges.map((e: any) => e.id),
|
||||||
|
...outgoingEdges.map((e: any) => e.id),
|
||||||
|
]
|
||||||
|
// 过滤掉相关的旧边
|
||||||
|
branch.edges = (branch.edges || []).filter((e: any) => !relatedEdgeIds.includes(e.id))
|
||||||
|
// 添加新边
|
||||||
|
if (newEdges.length > 0) {
|
||||||
|
branch.edges.push(...newEdges)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 清除指定任务步骤和 agent 组合的所有分支
|
* 清除指定任务步骤和 agent 组合的所有分支
|
||||||
* @param taskStepId 任务步骤 ID
|
* @param taskStepId 任务步骤 ID
|
||||||
@@ -330,6 +387,29 @@ export const useSelectionStore = defineStore('selection', () => {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从数据库删除指定节点
|
||||||
|
* @param TaskID 大任务ID(数据库主键)
|
||||||
|
* @param stepId 小任务ID
|
||||||
|
* @param branchId 分支ID
|
||||||
|
* @param nodeId 要删除的节点ID
|
||||||
|
* @returns Promise<boolean> 是否删除成功
|
||||||
|
*/
|
||||||
|
async function deleteTaskProcessNodeFromDB(
|
||||||
|
TaskID: string,
|
||||||
|
stepId: string,
|
||||||
|
branchId: string,
|
||||||
|
nodeId: string,
|
||||||
|
edges: any[] = [],
|
||||||
|
): Promise<boolean> {
|
||||||
|
// 导入 api(避免循环导入问题)
|
||||||
|
const { default: api } = await import('@/api')
|
||||||
|
|
||||||
|
const result = await api.deleteTaskProcessNode(TaskID, stepId, branchId, nodeId, edges)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从数据库恢复任务过程分支数据
|
* 从数据库恢复任务过程分支数据
|
||||||
* @param dbBranches 从数据库读取的任务过程分支数据
|
* @param dbBranches 从数据库读取的任务过程分支数据
|
||||||
@@ -672,6 +752,7 @@ export const useSelectionStore = defineStore('selection', () => {
|
|||||||
getAllTaskProcessBranches,
|
getAllTaskProcessBranches,
|
||||||
getTaskProcessBranchesByParent,
|
getTaskProcessBranchesByParent,
|
||||||
removeTaskProcessBranch,
|
removeTaskProcessBranch,
|
||||||
|
removeNodeFromBranch,
|
||||||
clearTaskProcessBranches,
|
clearTaskProcessBranches,
|
||||||
|
|
||||||
// ==================== 任务过程分支生效状态管理方法 ====================
|
// ==================== 任务过程分支生效状态管理方法 ====================
|
||||||
@@ -697,6 +778,7 @@ export const useSelectionStore = defineStore('selection', () => {
|
|||||||
restoreAgentCombinationsFromDB,
|
restoreAgentCombinationsFromDB,
|
||||||
saveTaskProcessBranchesToDB,
|
saveTaskProcessBranchesToDB,
|
||||||
deleteTaskProcessBranchFromDB,
|
deleteTaskProcessBranchFromDB,
|
||||||
|
deleteTaskProcessNodeFromDB,
|
||||||
restoreTaskProcessBranchesFromDB,
|
restoreTaskProcessBranchesFromDB,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user