From 636ccc90047792d47d0950a47642c253c678d99f Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Thu, 16 Oct 2025 19:38:21 +0800 Subject: [PATCH] fix: #995 --- ui/src/domain/workflow.ts | 105 ++++++++++++----------- ui/src/i18n/locales/en/nls.workflow.json | 2 +- ui/src/i18n/locales/zh/nls.workflow.json | 2 +- ui/src/pages/workflows/WorkflowNew.tsx | 5 +- 4 files changed, 58 insertions(+), 56 deletions(-) diff --git a/ui/src/domain/workflow.ts b/ui/src/domain/workflow.ts index 8c4241a09..42ec3c753 100644 --- a/ui/src/domain/workflow.ts +++ b/ui/src/domain/workflow.ts @@ -327,74 +327,77 @@ export const newNode = (type: WorkflowNodeType, { i18n = getI18n() }: { i18n?: R } }; -export const duplicateNode = (node: WorkflowNode, options?: { withCopySuffix?: boolean }) => { +const _deepCloneNode = (node: WorkflowNode, { withCopySuffix, nodeIdMap }: { withCopySuffix: boolean; nodeIdMap: Map }) => { const { produce } = new Immer({ autoFreeze: false }); - const deepClone = (node: WorkflowNode, { withCopySuffix, nodeIdMap }: { withCopySuffix: boolean; nodeIdMap: Map }) => { - return produce(node, (draft) => { - draft.data ??= {}; - draft.id = newNodeId(); - draft.data.name = withCopySuffix ? `${draft.data?.name || ""}-copy` : `${draft.data?.name || ""}`; + 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 + nodeIdMap.set(node.id, draft.id); // 原节点 ID 映射到新节点 ID - if (draft.blocks) { - draft.blocks = draft.blocks.map((block) => deepClone(block as WorkflowNode, { withCopySuffix: false, nodeIdMap })); - } + 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), - }; - } + 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; + } + break; - case WORKFLOW_NODE_TYPES.CONDITION: - { - 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)!; - } + 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, - }; + if ("right" in n) { + stack.push(n.right); + } } + + draft.data.config = { + ...draft.data.config, + expression: expr, + }; } - break; - } + } + break; } + } - return draft; - }); - }; + return draft; + }); +}; - return deepClone(node, { withCopySuffix: options?.withCopySuffix ?? true, nodeIdMap: new Map() }); +export const duplicateNode = (node: WorkflowNode, options?: { withCopySuffix?: boolean }) => { + const map = new Map(); + return _deepCloneNode(node, { withCopySuffix: options?.withCopySuffix ?? true, nodeIdMap: map }); }; export const duplicateNodes = (nodes: WorkflowNode[], options?: { withCopySuffix?: boolean }) => { - return nodes.map((node) => duplicateNode(node, options)); + const map = new Map(); + return nodes.map((node) => _deepCloneNode(node, { withCopySuffix: options?.withCopySuffix ?? true, nodeIdMap: map })); }; // #endregion diff --git a/ui/src/i18n/locales/en/nls.workflow.json b/ui/src/i18n/locales/en/nls.workflow.json index fbf71de35..38057854d 100644 --- a/ui/src/i18n/locales/en/nls.workflow.json +++ b/ui/src/i18n/locales/en/nls.workflow.json @@ -51,7 +51,7 @@ "workflow.new.templates.template.certtest.title": "Monitoring template", "workflow.new.templates.template.certtest.description": "A monitoring operating procedure that includes monitoring, and notification steps.", "workflow.new.templates.default_name": "Untitled workflow", - "workflow.new.templates.default_description": "Created at {{date}}", + "workflow.new.templates.default_description": "", "workflow.detail.baseinfo.name.placeholder": "Please enter workflow name", "workflow.detail.baseinfo.description.placeholder": "Please enter workflow description", diff --git a/ui/src/i18n/locales/zh/nls.workflow.json b/ui/src/i18n/locales/zh/nls.workflow.json index 49cc80981..fe1532a5b 100644 --- a/ui/src/i18n/locales/zh/nls.workflow.json +++ b/ui/src/i18n/locales/zh/nls.workflow.json @@ -51,7 +51,7 @@ "workflow.new.templates.template.certtest.title": "域名证书监控", "workflow.new.templates.template.certtest.description": "一个包含证书监控 + 消息通知步骤的工作流程,可在线上证书到期前或已过期时发出告警。", "workflow.new.templates.default_name": "未命名工作流", - "workflow.new.templates.default_description": "创建于 {{date}}", + "workflow.new.templates.default_description": "", "workflow.detail.baseinfo.name.placeholder": "请输入工作流名称", "workflow.detail.baseinfo.description.placeholder": "请输入工作流描述", diff --git a/ui/src/pages/workflows/WorkflowNew.tsx b/ui/src/pages/workflows/WorkflowNew.tsx index 657997d82..b49f04b6d 100644 --- a/ui/src/pages/workflows/WorkflowNew.tsx +++ b/ui/src/pages/workflows/WorkflowNew.tsx @@ -3,7 +3,6 @@ import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import { IconArrowRight, IconCode, IconSquarePlus2 } from "@tabler/icons-react"; import { App, Button, Card, Spin, Typography } from "antd"; -import dayjs from "dayjs"; import Show from "@/components/Show"; import WorkflowGraphImportModal from "@/components/workflow/WorkflowGraphImportModal"; @@ -85,7 +84,7 @@ const WorkflowNew = () => { try { let workflow = {} as WorkflowModel; workflow.name = t("workflow.new.templates.default_name"); - workflow.description = t("workflow.new.templates.default_description", { date: dayjs().format("YYYY-MM-DD HH:mm") }); + workflow.description = t("workflow.new.templates.default_description"); workflow.graphDraft = { nodes: [] }; workflow.hasDraft = true; @@ -261,7 +260,7 @@ const WorkflowNew = () => { try { let workflow = {} as WorkflowModel; workflow.name = t("workflow.new.templates.default_name"); - workflow.description = t("workflow.new.templates.default_description", { date: dayjs().format("YYYY-MM-DD HH:mm") }); + workflow.description = t("workflow.new.templates.default_description"); workflow.graphDraft = graph; workflow.hasDraft = true; workflow = await saveWorkflow(workflow);