feat: enhance workflow editor UI with improved node handling and visual updates

This commit is contained in:
mlogclub
2026-06-27 19:48:54 +08:00
parent d0cd01cbf6
commit 35ebb59b9a
3 changed files with 327 additions and 173 deletions
@@ -114,37 +114,38 @@ function NodeConfigForm({
}
return (
<div className="flex h-full min-h-0 flex-col gap-4 p-4">
<div>
<div className="text-sm font-medium">{panelTitle}</div>
<div className="mt-1 text-xs text-muted-foreground">
{node.data.nodeType && node.data.nodeType !== panelTitle
? `${node.id} · ${node.data.nodeType}`
: node.id}
<div className="flex min-h-full flex-col">
<div className="sticky top-0 z-10 shrink-0 border-b border-border/60 bg-background">
<div className="px-4 pb-2 pt-4">
<Input
id="workflow-node-name"
value={name}
onChange={(event) => setName(event.target.value)}
onBlur={() => commitChange({ name: name.trim() || node.data.nodeType || node.id })}
className="h-8 border-0 bg-transparent px-0 text-sm font-semibold uppercase shadow-none focus-visible:ring-0"
aria-label="节点名称"
/>
<div className="mt-1 truncate text-xs text-muted-foreground">
{node.data.nodeType && node.data.nodeType !== panelTitle
? `${node.id} · ${node.data.nodeType}`
: node.id}
</div>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="workflow-node-name"></Label>
<Input
id="workflow-node-name"
value={name}
onChange={(event) => setName(event.target.value)}
onBlur={() => commitChange({ name: name.trim() || node.data.nodeType || node.id })}
/>
</div>
{isConditionNode ? (
<ConditionNodePanel
branches={node.data.config?.branches ?? []}
branchSummaries={branchSummaries}
availableVariables={availableVariables}
outputSchema={outputSchema}
onChange={(branches) => commitChange({ config: { ...(node.data.config ?? {}), branches } })}
/>
) : (
<>
<div className="flex flex-1 flex-col">
{isConditionNode ? (
<ConditionNodePanel
branches={node.data.config?.branches ?? []}
branchSummaries={branchSummaries}
availableVariables={availableVariables}
outputSchema={outputSchema}
onChange={(branches) => commitChange({ config: { ...(node.data.config ?? {}), branches } })}
/>
) : (
<>
{inputSchema.length > 0 ? (
<div className="space-y-3">
<div className="text-sm font-medium"></div>
<div className="space-y-3 border-b border-border/60 p-4">
<div className="text-sm font-semibold uppercase"></div>
{availableVariables.length === 0 ? (
<div className="rounded-md border border-dashed p-2 text-xs text-muted-foreground">
@@ -185,7 +186,7 @@ function NodeConfigForm({
))}
</div>
) : null}
<details className="rounded-md border bg-background p-3">
<details className="border-b border-border/60 p-4">
<summary className="cursor-pointer text-sm font-medium"> JSON</summary>
<div className="mt-3 space-y-2">
<Textarea
@@ -201,9 +202,9 @@ function NodeConfigForm({
</div>
</details>
{outputSchema.length > 0 ? (
<div className="space-y-2">
<div className="text-sm font-medium"></div>
<div className="space-y-1 rounded-md border bg-background p-2">
<div className="space-y-2 p-4">
<div className="text-sm font-semibold uppercase"></div>
<div className="space-y-1 rounded-lg bg-muted/60 p-2">
{outputSchema.map((output) => (
<div key={output.name} className="space-y-0.5 rounded-sm px-1 py-0.5">
<div className="flex items-center justify-between gap-2 text-xs">
@@ -218,8 +219,9 @@ function NodeConfigForm({
</div>
</div>
) : null}
</>
)}
</>
)}
</div>
</div>
)
}
@@ -292,9 +294,9 @@ function ConditionNodePanel({
return (
<>
<div className="space-y-2">
<div className="space-y-3 border-b border-border/60 p-4">
<div className="flex items-center justify-between gap-2">
<div className="text-sm font-medium"></div>
<div className="text-sm font-semibold uppercase"></div>
<Button type="button" variant="outline" size="sm" onClick={addBranch}>
</Button>
@@ -310,12 +312,19 @@ function ConditionNodePanel({
? ""
: String(condition.right)
return (
<div key={branch.id} className="space-y-3 rounded-md border bg-background p-3">
<div className="flex items-center justify-between gap-2 text-xs">
<span className="min-w-0 truncate font-medium">
{branch.default ? "ELSE" : index === 0 ? "IF" : "ELSE IF"}
</span>
<span className="shrink-0 rounded-sm bg-muted px-1.5 py-0.5 text-muted-foreground">
<div key={branch.id} className="space-y-3 rounded-xl bg-muted/60 p-3">
<div className="flex h-6 items-center justify-between gap-2 text-xs">
<div className="flex min-w-0 items-center gap-2">
<span className="shrink-0 text-[11px] font-semibold text-muted-foreground">
{branch.default ? "ELSE" : index === 0 ? "IF" : "ELIF"}
</span>
{!branch.default ? (
<span className="truncate text-[10px] font-semibold text-muted-foreground/80">
CASE {index + 1}
</span>
) : null}
</div>
<span className="shrink-0 rounded-md bg-background px-1.5 py-0.5 text-muted-foreground">
{branch.default ? "默认" : "条件"}
</span>
</div>
@@ -325,22 +334,23 @@ function ConditionNodePanel({
value={branch.name ?? ""}
onChange={(event) => commitBranch(branch.id, { name: event.target.value })}
placeholder="例如:需要转人工"
className="h-8 bg-background"
/>
</div>
<div className="space-y-1.5">
<Label className="text-xs"></Label>
<div className="rounded-md border border-dashed bg-muted/20 px-2 py-2 text-xs text-muted-foreground">
<div className="rounded-md border border-dashed bg-background/70 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">
<div className="rounded-md bg-background/70 p-2 text-xs text-muted-foreground">
{summary?.targetName ?? (branch.targetNodeId || "未选择目标节点")}
</div>
) : (
<div className="space-y-3 rounded-md border bg-muted/20 p-2">
<div className="space-y-3 rounded-lg bg-background p-2">
<div className="space-y-1.5">
<Label className="text-xs"></Label>
<VariableSelector
@@ -377,22 +387,22 @@ function ConditionNodePanel({
)}
<div className="flex flex-wrap gap-2">
{!branch.default && index > 0 ? (
<Button type="button" size="sm" variant="outline" onClick={() => moveBranch(branch.id, -1)}>
<Button type="button" size="sm" variant="ghost" 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 type="button" size="sm" variant="ghost" onClick={() => moveBranch(branch.id, 1)}>
</Button>
) : null}
{!branch.default ? (
<Button type="button" size="sm" variant="outline" onClick={() => deleteBranch(branch.id)}>
<Button type="button" size="sm" variant="ghost" onClick={() => deleteBranch(branch.id)}>
</Button>
) : null}
</div>
<div className="line-clamp-2 text-xs text-muted-foreground">
<div className="line-clamp-2 rounded-md bg-background/70 px-2 py-1.5 text-xs text-muted-foreground">
{summary?.conditionLabel ?? "尚未完成分支配置"}
</div>
</div>
@@ -406,9 +416,9 @@ function ConditionNodePanel({
)}
</div>
{outputSchema.length > 0 ? (
<div className="space-y-2">
<div className="text-sm font-medium"></div>
<div className="space-y-1 rounded-md border bg-background p-2">
<div className="space-y-2 p-4">
<div className="text-sm font-semibold uppercase"></div>
<div className="space-y-1 rounded-lg bg-muted/60 p-2">
{outputSchema.map((output) => (
<div key={output.name} className="space-y-0.5 rounded-sm px-1 py-0.5">
<div className="flex items-center justify-between gap-2 text-xs">