This commit is contained in:
Fu Diwei
2025-10-16 19:38:21 +08:00
parent 06e92c6bc0
commit 636ccc9004
4 changed files with 58 additions and 56 deletions
+54 -51
View File
@@ -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<string, string> }) => {
const { produce } = new Immer({ autoFreeze: false });
const deepClone = (node: WorkflowNode, { withCopySuffix, nodeIdMap }: { withCopySuffix: boolean; nodeIdMap: Map<string, string> }) => {
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<string, string>();
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<string, string>();
return nodes.map((node) => _deepCloneNode(node, { withCopySuffix: options?.withCopySuffix ?? true, nodeIdMap: map }));
};
// #endregion
+1 -1
View File
@@ -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",
+1 -1
View File
@@ -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": "请输入工作流描述",
+2 -3
View File
@@ -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);