diff --git a/internal/domain/workflow.go b/internal/domain/workflow.go index 2fa0a8f42..b3f39d65c 100644 --- a/internal/domain/workflow.go +++ b/internal/domain/workflow.go @@ -102,8 +102,9 @@ const ( ) type WorkflowNodeData struct { - Name string `json:"name"` - Config WorkflowNodeConfig `json:"config"` + Name string `json:"name"` + Disabled bool `json:"disabled,omitempty,omitzero"` + Config WorkflowNodeConfig `json:"config,omitempty,omitzero"` } type WorkflowNodeConfig map[string]any diff --git a/internal/workflow/engine/engine.go b/internal/workflow/engine/engine.go index 772002740..9c32af7da 100644 --- a/internal/workflow/engine/engine.go +++ b/internal/workflow/engine/engine.go @@ -195,6 +195,11 @@ func (we *workflowEngine) executeBlocks(wfCtx *WorkflowContext, blocks []*Node) default: } + // 节点已禁用,直接跳过执行 + if node.Data.Disabled { + continue + } + err := we.executeNode(wfCtx, node) if err != nil { return err diff --git a/ui/src/components/workflow/designer/forms/_shared.tsx b/ui/src/components/workflow/designer/forms/_shared.tsx index 16e245f1b..320d1413c 100644 --- a/ui/src/components/workflow/designer/forms/_shared.tsx +++ b/ui/src/components/workflow/designer/forms/_shared.tsx @@ -1,11 +1,12 @@ -import { useRef, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; -import { type FlowNodeEntity, getNodeForm } from "@flowgram.ai/fixed-layout-editor"; -import { IconX } from "@tabler/icons-react"; +import { type FlowNodeEntity, getNodeForm, useClientContext, useRefresh, useWatchFormValueIn } from "@flowgram.ai/fixed-layout-editor"; +import { IconEye, IconEyeOff, IconX } from "@tabler/icons-react"; import { useControllableValue } from "ahooks"; -import { Anchor, type AnchorProps, App, Button, Drawer, Flex, type FormInstance, Typography } from "antd"; +import { Anchor, type AnchorProps, App, Button, Drawer, Flex, type FormInstance, Tooltip, Typography } from "antd"; import { isEqual } from "radash"; +import Show from "@/components/Show"; import { getErrMsg } from "@/utils/error"; import { type NodeRegistry } from "../nodes/typings"; @@ -25,6 +26,11 @@ export interface NodeConfigDrawerProps { export const NodeConfigDrawer = ({ children, afterClose, anchor, footer = true, form: formInst, loading, node, ...props }: NodeConfigDrawerProps) => { const { t } = useTranslation(); + const ctx = useClientContext(); + const { playground } = ctx; + + const refresh = useRefresh(); + const { modal, notification } = App.useApp(); const [open, setOpen] = useControllableValue(props, { @@ -52,6 +58,22 @@ export const NodeConfigDrawer = ({ children, afterClose, anchor, footer = true, ); + const [isNodeDisabled, setIsNodeDisabled] = useState(() => { + if (node) { + return getNodeForm(node)?.getValueIn("disabled"); + } + return false; + }); + useEffect(() => { + const d1 = playground.config.onDataChange(() => refresh()); + const d2 = node?.onDataChange(() => setIsNodeDisabled(getNodeForm(node)?.getValueIn("disabled"))); + + return () => { + d1.dispose(); + d2.dispose(); + }; + }); + const handleOkClick = async () => { if (node == null) { setOpen(false); @@ -127,6 +149,10 @@ export const NodeConfigDrawer = ({ children, afterClose, anchor, footer = true, promise.then(() => setOpen(false)); }; + const handleDisableNodeClick = () => { + getNodeForm(node)!.setValueIn("disabled", !isNodeDisabled); + }; + return (
{renderNodeIcon()}
{node?.toJSON()?.data?.name}
+ + +