diff --git a/ui/src/domain/workflow.ts b/ui/src/domain/workflow.ts index 4b275c1d..56623dbb 100644 --- a/ui/src/domain/workflow.ts +++ b/ui/src/domain/workflow.ts @@ -330,77 +330,76 @@ export const newNode = (type: WorkflowNodeType, { i18n = getI18n() }: { i18n?: R } }; -const _deepCloneNode = (node: WorkflowNode, { withCopySuffix, nodeIdMap }: { withCopySuffix: boolean; nodeIdMap: Map }) => { - const { produce } = new Immer({ autoFreeze: false }); - return produce(node, (draft) => { - draft.data ??= {}; - draft.id = newNodeId(); - draft.data.name = withCopySuffix ? `${draft.data?.name || ""}-copy` : `${draft.data?.name || ""}`; - - nodeIdMap.set(node.id, draft.id); // 原节点 ID 映射到新节点 ID - - if (draft.blocks) { - draft.blocks = draft.blocks.map((block) => _deepCloneNode(block, { withCopySuffix: false, nodeIdMap })); - } - - if (draft.data?.config) { - switch (draft.type) { - case WORKFLOW_NODE_TYPES.BIZ_DEPLOY: - { - const prevNodeId = draft.data.config.certificateOutputNodeId as string; - if (nodeIdMap.has(prevNodeId)) { - draft.data.config = { - ...draft.data.config, - certificateOutputNodeId: nodeIdMap.get(prevNodeId), - }; - } - } - break; - - case WORKFLOW_NODE_TYPES.BRANCHBLOCK: - { - const stack = [] as Expr[]; - const expr = draft.data.config.expression as Expr; - if (expr) { - stack.push(expr); - while (stack.length > 0) { - const n = stack.pop()!; - if ("left" in n) { - stack.push(n.left); - if ("selector" in n.left) { - const prevNodeId = n.left.selector.id; - if (nodeIdMap.has(prevNodeId)) { - n.left.selector.id = nodeIdMap.get(prevNodeId)!; - } - } - } - if ("right" in n) { - stack.push(n.right); - } - } - - draft.data.config = { - ...draft.data.config, - expression: expr, - }; - } - } - break; - } - } - - return draft; - }); -}; - export const duplicateNode = (node: WorkflowNode, options?: { withCopySuffix?: boolean }) => { - const map = new Map(); - return _deepCloneNode(node, { withCopySuffix: options?.withCopySuffix ?? true, nodeIdMap: map }); + return duplicateNodes([node], options)[0]; }; export const duplicateNodes = (nodes: WorkflowNode[], options?: { withCopySuffix?: boolean }) => { + function duplicate(node: WorkflowNode, { withCopySuffix, nodeIdMap }: { withCopySuffix: boolean; nodeIdMap: Map }) { + const { produce } = new Immer({ autoFreeze: false }); + return produce(node, (draft) => { + draft.data ??= {}; + draft.id = newNodeId(); + draft.data.name = withCopySuffix ? `${draft.data?.name || ""}-copy` : `${draft.data?.name || ""}`; + + nodeIdMap.set(node.id, draft.id); // 原节点 ID 映射到新节点 ID + + if (draft.blocks) { + draft.blocks = draft.blocks.map((block) => duplicate(block, { withCopySuffix: false, nodeIdMap })); + } + + if (draft.data?.config) { + switch (draft.type) { + case WORKFLOW_NODE_TYPES.BIZ_DEPLOY: + { + const prevNodeId = draft.data.config.certificateOutputNodeId as string; + if (nodeIdMap.has(prevNodeId)) { + draft.data.config = { + ...draft.data.config, + certificateOutputNodeId: nodeIdMap.get(prevNodeId), + }; + } + } + break; + + case WORKFLOW_NODE_TYPES.BRANCHBLOCK: + { + const stack = [] as Expr[]; + const expr = draft.data.config.expression as Expr; + if (expr) { + stack.push(expr); + while (stack.length > 0) { + const n = stack.pop()!; + if ("left" in n) { + stack.push(n.left); + if ("selector" in n.left) { + const prevNodeId = n.left.selector.id; + if (nodeIdMap.has(prevNodeId)) { + n.left.selector.id = nodeIdMap.get(prevNodeId)!; + } + } + } + if ("right" in n) { + stack.push(n.right); + } + } + + draft.data.config = { + ...draft.data.config, + expression: expr, + }; + } + } + break; + } + } + + return draft; + }); + } + const map = new Map(); - return nodes.map((node) => _deepCloneNode(node, { withCopySuffix: options?.withCopySuffix ?? true, nodeIdMap: map })); + return nodes.map((node) => duplicate(node, { withCopySuffix: options?.withCopySuffix ?? true, nodeIdMap: map })); }; // #endregion diff --git a/ui/src/utils/file.ts b/ui/src/utils/file.ts index 2b7fc7a9..4b78ff7a 100644 --- a/ui/src/utils/file.ts +++ b/ui/src/utils/file.ts @@ -1,17 +1,16 @@ -export function readFileAsText(file: File): Promise { - return new Promise((resolve, reject) => { - const reader = new FileReader(); +export const readFileAsText = (file: File): Promise => { + const { promise, resolve, reject } = Promise.withResolvers(); - reader.onload = () => { - if (reader.result) { - resolve(reader.result.toString()); - } else { - reject("No content found"); - } - }; + const reader = new FileReader(); + reader.onload = () => { + if (reader.result != null) { + resolve(reader.result.toString()); + } else { + reject(new Error("read file failed: result is null")); + } + }; + reader.onerror = () => reject(reader.error); + reader.readAsText(file, "utf-8"); - reader.onerror = () => reject(reader.error); - - reader.readAsText(file, "utf-8"); - }); -} + return promise; +};