From c713d4705e5cdc3f3f9c816334bd8ce2429a5a0a Mon Sep 17 00:00:00 2001 From: "Yoan.liu" Date: Thu, 22 May 2025 22:32:22 +0800 Subject: [PATCH] add clone alert --- ui/src/components/workflow/node/CloneNode.tsx | 2 +- ui/src/domain/workflow.ts | 42 +++++++++++++++++++ ui/src/pages/workflows/WorkflowDetail.tsx | 32 ++++++++++++-- ui/src/stores/workflow/index.ts | 24 +++++++++++ 4 files changed, 96 insertions(+), 4 deletions(-) diff --git a/ui/src/components/workflow/node/CloneNode.tsx b/ui/src/components/workflow/node/CloneNode.tsx index a0d75a54..2e63be62 100644 --- a/ui/src/components/workflow/node/CloneNode.tsx +++ b/ui/src/components/workflow/node/CloneNode.tsx @@ -6,7 +6,7 @@ export type UploadNodeProps = SharedNodeProps; const CloneNode = ({ node, disabled }: SharedNodeProps) => { return ( <> - +
选择节点复制到此处
diff --git a/ui/src/domain/workflow.ts b/ui/src/domain/workflow.ts index 29a2444e..a7306041 100644 --- a/ui/src/domain/workflow.ts +++ b/ui/src/domain/workflow.ts @@ -507,3 +507,45 @@ export const isAllNodesValidated = (node: WorkflowNode): boolean => { return true; }; +export const hasCloneNode = (node: WorkflowNode): boolean => { + let current = node as typeof node | undefined; + while (current) { + if (current.type === WorkflowNodeType.Clone) { + return true; + } + + if (isBranchLike(current)) { + for (const branch of current.branches!) { + if (hasCloneNode(branch)) { + return true; + } + } + } + + current = current.next; + } + + return false; +}; + +export const removeCloneNode = (node: WorkflowNode): WorkflowNode => { + return produce(node, (draft) => { + let current = draft as typeof draft | undefined; + + while (current) { + if (current.next?.type === WorkflowNodeType.Clone) { + current.next = current.next.next; + break; + } + + if (isBranchLike(current) && current.branches) { + current.branches = current.branches.map((branch) => removeCloneNode(branch)); + } + + current = current.next; + } + + return draft; + }); +}; + diff --git a/ui/src/pages/workflows/WorkflowDetail.tsx b/ui/src/pages/workflows/WorkflowDetail.tsx index 832269d0..12c40249 100644 --- a/ui/src/pages/workflows/WorkflowDetail.tsx +++ b/ui/src/pages/workflows/WorkflowDetail.tsx @@ -21,7 +21,7 @@ import ModalForm from "@/components/ModalForm"; import Show from "@/components/Show"; import WorkflowElementsContainer from "@/components/workflow/WorkflowElementsContainer"; import WorkflowRuns from "@/components/workflow/WorkflowRuns"; -import { isAllNodesValidated } from "@/domain/workflow"; +import { hasCloneNode, isAllNodesValidated } from "@/domain/workflow"; import { WORKFLOW_RUN_STATUSES } from "@/domain/workflowRun"; import { useAntdForm, useZustandShallowSelector } from "@/hooks"; import { remove as removeWorkflow, subscribe as subscribeWorkflow, unsubscribe as unsubscribeWorkflow } from "@/repository/workflow"; @@ -38,9 +38,12 @@ const WorkflowDetail = () => { const [notificationApi, NotificationContextHolder] = notification.useNotification(); const { id: workflowId } = useParams(); - const { workflow, initialized, ...workflowState } = useWorkflowStore( - useZustandShallowSelector(["workflow", "initialized", "init", "destroy", "setEnabled", "release", "discard"]) + const { workflow, initialized, cancelClone, ...workflowState } = useWorkflowStore( + useZustandShallowSelector(["workflow", "initialized", "cancelClone", "init", "destroy", "setEnabled", "release", "discard"]) ); + + const cloning = hasCloneNode(workflow.draft!); + useEffect(() => { workflowState.init(workflowId!); @@ -308,6 +311,28 @@ const WorkflowDetail = () => { + +
+ { + cancelClone(); + }} + > + 取消 + + } + /> +
+
+
@@ -395,3 +420,4 @@ const WorkflowBaseInfoModal = ({ trigger }: { trigger?: React.ReactNode }) => { }; export default WorkflowDetail; + diff --git a/ui/src/stores/workflow/index.ts b/ui/src/stores/workflow/index.ts index 8057fda5..a8d0a783 100644 --- a/ui/src/stores/workflow/index.ts +++ b/ui/src/stores/workflow/index.ts @@ -9,6 +9,7 @@ import { addNode, getOutputBeforeNodeId, removeBranch, + removeCloneNode, removeNode, updateNode, } from "@/domain/workflow"; @@ -28,6 +29,7 @@ export type WorkflowState = { addNode: (node: WorkflowNode, previousNodeId: string) => void; updateNode: (node: WorkflowNode) => void; removeNode: (nodeId: string) => void; + cancelClone: () => void; addBranch: (branchId: string) => void; removeBranch: (branchId: string, index: number) => void; @@ -203,6 +205,27 @@ export const useWorkflowStore = create((set, get) => ({ }); }, + cancelClone: async () => { + if (!get().initialized) throw "Workflow not initialized yet"; + + const root = removeCloneNode(get().workflow.draft!); + + const resp = await saveWorkflow({ + id: get().workflow.id!, + draft: root, + hasDraft: true, + }); + + set((state: WorkflowState) => { + return { + workflow: produce(state.workflow, (draft) => { + draft.draft = resp.draft; + draft.hasDraft = resp.hasDraft; + }), + }; + }); + }, + addBranch: async (branchId: string) => { if (!get().initialized) throw "Workflow not initialized yet"; @@ -247,3 +270,4 @@ export const useWorkflowStore = create((set, get) => ({ return getOutputBeforeNodeId(get().workflow.draft as WorkflowNode, nodeId, type); }, })); +