diff --git a/web/app/dashboard/ai-workflows/_components/node-config-panel.tsx b/web/app/dashboard/ai-workflows/_components/node-config-panel.tsx index c7ef493..4b0cb7a 100644 --- a/web/app/dashboard/ai-workflows/_components/node-config-panel.tsx +++ b/web/app/dashboard/ai-workflows/_components/node-config-panel.tsx @@ -1,5 +1,6 @@ "use client" +import type { ReactNode } from "react" import { Trash2Icon } from "lucide-react" import { Button } from "@/components/ui/button" @@ -37,6 +38,9 @@ const CONDITION_OPERATOR_OPTIONS = [ { value: "empty", label: "为空" }, ] +const cardInputClassName = "h-8 rounded-md border-slate-200 bg-white text-xs shadow-none" +const cardComboboxClassName = "h-8 rounded-md border-slate-200 bg-white text-xs shadow-none" + export function NodeConfigPanel({ node, nodeSpec, @@ -125,25 +129,36 @@ export function NodeConfigPanel({ ) : null} -
-
- - updateData({ title: event.target.value })} - /> -
+
+ +
+ + updateData({ title: event.target.value })} + /> +
+ {nodeSpec?.description ? ( +

{nodeSpec.description}

+ ) : null} +
{inputSchema.length > 0 ? ( -
-
输入
+ +
{inputSchema.map((input) => { const value = inputsValues[input.name] return (
-
) })} -
+
+ ) : null} {showConditionBranches && (node.type === "condition" || branches.length > 0) ? ( @@ -221,51 +238,57 @@ export function ConditionBranchConfigPanel({ } return ( -
-
- - updateBranch({ ...branch, name: event.target.value })} - /> -
+
+ +
+
+ + updateBranch({ ...branch, name: event.target.value })} + /> +
-
- - updateBranch({ ...branch, targetNodeId })} - /> -
+
+ + updateBranch({ ...branch, targetNodeId })} + /> +
- + +
+
{branch.default ? ( -
+
默认分支不需要条件表达式,会在其他条件不匹配时执行。
) : ( - + + + )} {branch.default ? null : ( @@ -273,7 +296,7 @@ export function ConditionBranchConfigPanel({ type="button" variant="ghost" size="sm" - className="h-8 px-2 text-xs text-muted-foreground hover:text-destructive" + className="h-8 px-2 text-xs text-slate-500 hover:text-destructive" onClick={() => onDelete(branch.id)} > 删除条件 @@ -303,27 +326,32 @@ function ConditionBranchesEditor({ const targetOptions = buildTargetOptions(nodes, currentNodeId) return ( -
-
-
条件分支
- -
+ } + > {branches.length === 0 ? ( -
+
暂无分支。条件节点需要至少一个默认分支或条件分支。
) : null} -
+
{branches.map((branch) => { return ( -
+
+ + {branch.default ? "ELSE" : "IF"} + onChange({ ...branch, name: event.target.value })} /> {branch.default ? null : ( @@ -331,7 +359,7 @@ function ConditionBranchesEditor({ type="button" variant="ghost" size="sm" - className="h-8 px-2 text-xs text-muted-foreground hover:text-destructive" + className="h-7 px-2 text-xs text-slate-500 hover:text-destructive" onClick={() => onDelete(branch.id)} > 删除 @@ -339,21 +367,22 @@ function ConditionBranchesEditor({ )}
-
- +
+ onChange({ ...branch, targetNodeId })} />
-
) })}
-
+ ) } @@ -378,45 +409,51 @@ function ConditionFields({ branch, variables, onChange, + compact = false, }: { branch: WorkflowConditionBranch variables: WorkflowVariableRef[] onChange: (branch: WorkflowConditionBranch) => void + compact?: boolean }) { const condition = branch.condition ?? {} return ( -
-
- +
+
+ onChange({ ...branch, condition: { ...condition, left }, })} />
-
-
- +
+ {compact ? : null} +
+ {compact ? null : } onChange({ ...branch, condition: { ...condition, operator }, })} />
-
- +
+ {compact ? null : } onChange({ ...branch, condition: { ...condition, right: event.target.value }, @@ -428,6 +465,33 @@ function ConditionFields({ ) } +function ConfigCard({ + title, + meta, + action, + children, +}: { + title: string + meta?: string + action?: ReactNode + children: ReactNode +}) { + return ( +
+
+
+
{title}
+
+
+ {meta ? {meta} : null} + {action} +
+
+
{children}
+
+ ) +} + function buildTargetOptions(nodes: AIWorkflowDefinition["nodes"], currentNodeId: string) { return nodes .filter((node) => node.id !== currentNodeId && node.type !== "start") diff --git a/web/app/dashboard/ai-workflows/_components/variable-selector.tsx b/web/app/dashboard/ai-workflows/_components/variable-selector.tsx index 64d3cf2..02acc35 100644 --- a/web/app/dashboard/ai-workflows/_components/variable-selector.tsx +++ b/web/app/dashboard/ai-workflows/_components/variable-selector.tsx @@ -15,11 +15,13 @@ export function VariableSelector({ variables, onChange, placeholder = "选择变量", + triggerClassName, }: { value?: WorkflowVariableSelector variables: WorkflowVariableRef[] onChange: (value: WorkflowVariableSelector) => void placeholder?: string + triggerClassName?: string }) { const selected = value ? `${refNodeId(value)}.${refField(value)}` : "" const options = variables.map((variable) => ({ @@ -33,6 +35,7 @@ export function VariableSelector({ value={selected} options={options} placeholder={placeholder} + triggerClassName={triggerClassName} onChange={(next) => { const variable = variables.find((item) => `${item.nodeId}.${item.field}` === next) if (variable) { diff --git a/web/app/dashboard/ai-workflows/_components/workflow-config-sidebar.tsx b/web/app/dashboard/ai-workflows/_components/workflow-config-sidebar.tsx index 9f09011..fad2856 100644 --- a/web/app/dashboard/ai-workflows/_components/workflow-config-sidebar.tsx +++ b/web/app/dashboard/ai-workflows/_components/workflow-config-sidebar.tsx @@ -7,6 +7,7 @@ import type { AIWorkflowDefinition, AIWorkflowNodeSpec } from "@/lib/api/admin" import type { SelectedWorkflowBranch } from "./workflow-branch-selection" import { ConditionBranchConfigPanel, NodeConfigPanel } from "./node-config-panel" +import { WorkflowNodeIcon } from "./workflow-node-icon" import { getAvailableVariables, normalizeNodeConfig, @@ -49,6 +50,7 @@ export function WorkflowConfigPanel({ const panelDescription = selectedBranchItem ? "配置该条件分支的匹配规则和目标节点。" : selectedNodeSpec?.description || "" + const panelIcon = selectedBranchItem ? "GitBranchIcon" : selectedNodeSpec?.icon if (!selectedNode) { return null @@ -67,30 +69,52 @@ export function WorkflowConfigPanel({ } return ( -
-
-
-
-
{panelTitle}
+
+
+
+
+
+
+ +
+
+ {panelTitle} +
+ {panelDescription ? ( +
+ {panelDescription} +
+ ) : null} +
+
+ +
{panelMeta ? ( -
{panelMeta}
- ) : null} - {panelDescription ? ( -
- {panelDescription} +
+
+ + {panelMeta} + + + 可配置 + +
) : null}
-
{selectedBranchItem && selectedBranch ? ( @@ -109,7 +133,6 @@ export function WorkflowConfigPanel({ nodes={definition.nodes} availableVariables={availableVariables} showHeader={false} - showConditionBranches={selectedNode.type !== "condition"} onChange={onChangeNodeData} onDelete={onDeleteNode} /> diff --git a/web/components/option-combobox.tsx b/web/components/option-combobox.tsx index a8b7631..d5a019b 100644 --- a/web/components/option-combobox.tsx +++ b/web/components/option-combobox.tsx @@ -33,6 +33,7 @@ type OptionComboboxProps = { searchPlaceholder?: string emptyText?: string disabled?: boolean + triggerClassName?: string onChange: (value: string) => void renderOptionAction?: (option: ComboboxOption) => ReactNode } @@ -44,6 +45,7 @@ export function OptionCombobox({ searchPlaceholder, emptyText, disabled = false, + triggerClassName, onChange, renderOptionAction, }: OptionComboboxProps) { @@ -59,7 +61,7 @@ export function OptionCombobox({