feat: add validation for default condition branch position and enhance branch connection handling

This commit is contained in:
mlogclub
2026-06-27 17:00:41 +08:00
parent f85fb6d406
commit 2143bfa06b
6 changed files with 432 additions and 110 deletions
@@ -34,24 +34,17 @@ export type WorkflowBranchSummary = {
isDefault: boolean
}
export type WorkflowBranchTargetOption = {
value: string
label: string
}
export function NodeConfigPanel({
node,
nodeSpec,
availableVariables,
branchSummaries = [],
branchTargetOptions = [],
onChange,
}: {
node: Node<WorkflowNodeData> | null
nodeSpec?: WorkflowNodeSpec
availableVariables: WorkflowVariableRef[]
branchSummaries?: WorkflowBranchSummary[]
branchTargetOptions?: WorkflowBranchTargetOption[]
onChange: (nodeId: string, data: WorkflowNodeData) => void
}) {
if (!node) {
@@ -69,7 +62,6 @@ export function NodeConfigPanel({
nodeSpec={nodeSpec}
availableVariables={availableVariables}
branchSummaries={branchSummaries}
branchTargetOptions={branchTargetOptions}
onChange={onChange}
/>
)
@@ -80,14 +72,12 @@ function NodeConfigForm({
nodeSpec,
availableVariables,
branchSummaries,
branchTargetOptions,
onChange,
}: {
node: Node<WorkflowNodeData>
nodeSpec?: WorkflowNodeSpec
availableVariables: WorkflowVariableRef[]
branchSummaries: WorkflowBranchSummary[]
branchTargetOptions: WorkflowBranchTargetOption[]
onChange: (nodeId: string, data: WorkflowNodeData) => void
}) {
const [name, setName] = useState(node.data.name ?? "")
@@ -145,7 +135,6 @@ function NodeConfigForm({
<ConditionNodePanel
branches={node.data.config?.branches ?? []}
branchSummaries={branchSummaries}
branchTargetOptions={branchTargetOptions}
availableVariables={availableVariables}
outputSchema={outputSchema}
onChange={(branches) => commitChange({ config: { ...(node.data.config ?? {}), branches } })}
@@ -237,14 +226,12 @@ function NodeConfigForm({
function ConditionNodePanel({
branches,
branchSummaries,
branchTargetOptions,
availableVariables,
outputSchema,
onChange,
}: {
branches: WorkflowConditionBranch[]
branchSummaries: WorkflowBranchSummary[]
branchTargetOptions: WorkflowBranchTargetOption[]
availableVariables: WorkflowVariableRef[]
outputSchema: WorkflowVariableSpec[]
onChange: (branches: WorkflowConditionBranch[]) => void
@@ -257,25 +244,49 @@ function ConditionNodePanel({
}
const addBranch = () => {
const index = branches.length + 1
const nextBranch = {
id: `branch_${index}`,
name: `分支 ${index}`,
targetNodeId: "",
condition: { operator: "eq" },
}
const defaultIndex = branches.findIndex((branch) => branch.default)
if (defaultIndex >= 0) {
onChange([
...branches.slice(0, defaultIndex),
nextBranch,
...branches.slice(defaultIndex),
])
return
}
onChange([
...branches,
nextBranch,
{
id: `branch_${index}`,
name: `分支 ${index}`,
targetNodeId: branchTargetOptions[0]?.value ?? "",
condition: { operator: "eq" },
id: "default",
name: "其他情况",
targetNodeId: "",
default: true,
},
])
}
const deleteBranch = (branchId: string) => {
onChange(branches.filter((branch) => branch.id !== branchId))
onChange(branches.filter((branch) => branch.id !== branchId || branch.default))
}
const markDefault = (branchId: string) => {
onChange(branches.map((branch) => normalizeBranch({
...branch,
default: branch.id === branchId,
condition: branch.id === branchId ? undefined : branch.condition ?? { operator: "eq" },
})))
const moveBranch = (branchId: string, direction: -1 | 1) => {
const index = branches.findIndex((branch) => branch.id === branchId)
if (index < 0 || branches[index]?.default) {
return
}
const nextIndex = index + direction
if (nextIndex < 0 || nextIndex >= branches.length || branches[nextIndex]?.default) {
return
}
const next = [...branches]
const current = next[index]
next[index] = next[nextIndex]
next[nextIndex] = current
onChange(next)
}
return (
@@ -317,14 +328,11 @@ function ConditionNodePanel({
</div>
<div className="space-y-1.5">
<Label className="text-xs"></Label>
<OptionCombobox
value={branch.targetNodeId}
options={branchTargetOptions}
placeholder="选择目标节点"
searchPlaceholder="搜索目标节点"
emptyText="请先从条件节点连出下游节点"
onChange={(value) => commitBranch(branch.id, { targetNodeId: value })}
/>
<div className="rounded-md border border-dashed bg-muted/20 px-2 py-2 text-xs text-muted-foreground">
{summary?.targetNodeId
? `已连接到:${summary.targetName}`
: "请从画布中该分支右侧连接点拖线到目标节点"}
</div>
</div>
{branch.default ? (
<div className="rounded-md border border-dashed p-2 text-xs text-muted-foreground">
@@ -367,14 +375,21 @@ function ConditionNodePanel({
</div>
)}
<div className="flex flex-wrap gap-2">
{!branch.default ? (
<Button type="button" size="sm" variant="outline" onClick={() => markDefault(branch.id)}>
{!branch.default && index > 0 ? (
<Button type="button" size="sm" variant="outline" onClick={() => moveBranch(branch.id, -1)}>
</Button>
) : null}
{!branch.default && index < branches.findIndex((item) => item.default) - 1 ? (
<Button type="button" size="sm" variant="outline" onClick={() => moveBranch(branch.id, 1)}>
</Button>
) : null}
{!branch.default ? (
<Button type="button" size="sm" variant="outline" onClick={() => deleteBranch(branch.id)}>
</Button>
) : null}
<Button type="button" size="sm" variant="outline" onClick={() => deleteBranch(branch.id)}>
</Button>
</div>
<div className="line-clamp-2 text-xs text-muted-foreground">
{summary?.conditionLabel ?? "尚未完成分支配置"}