"use client"; import { useMemo } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { ArrowRight, Check, Copy, RotateCcw, Sparkles, Wand2 } from "lucide-react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { ScrollArea } from "@/components/ui/scroll-area"; import { SearchableSelect } from "@/components/ui/searchable-select"; import { Textarea } from "@/components/ui/textarea"; import { cn } from "@/lib/utils"; import { defaultAgentWorkflowUiConfig, getAgentWorkflowCategoryLabels, getAgentWorkflowBrandStoreEntries, getAgentWorkflowFifthCategoryOptionsByBrandStoreThirdFourth, getAgentWorkflowFourthCategoryOptionsByBrandStoreThirdCategory, getAgentWorkflowPriceBandOptions, getAgentWorkflowThirdCategoryOptionsByBrandStore, getAgentWorkflowStoreOptionsByBrand, } from "@/lib/agents/workflow-ui-config"; import { createEmptyAgentWorkflowMaterialSelection } from "@/lib/agents/workflow-material-config"; import { buildAgentWorkflowComposerContext, getAgentWorkflowProgress, } from "@/lib/agents/workflow-composer"; import type { AgentWorkflowComposerSelection, AgentWorkflowMaterialSelections, AgentWorkflowStepKey, AgentWorkflowUiConfig, } from "@/types/agent"; import { AGENT_PRESET_PREVIEW_IMAGE_SIZES, getAgentPresetPreviewImageUrl, } from "./agent-preset-preview"; export interface AgentWorkflowInputs { primarySku: string; secondarySku: string; brand: string; store: string; officialSecondCategory: string; officialThirdCategory: string; thirdCategory: string; fourthCategory: string; fifthCategory: string; priceBand: string; } interface AgentWorkflowPanelNewProps { config?: AgentWorkflowUiConfig | null; inputs: AgentWorkflowInputs; onInputsChange: (next: AgentWorkflowInputs) => void; materials: AgentWorkflowMaterialSelections; onMaterialsChange: (next: AgentWorkflowMaterialSelections) => void; selections: Partial>; onSelectionsChange: (next: Partial>) => void; onReset?: () => void; embedded?: boolean; onNextStep?: () => void; onSkipStep?: () => void; } function buildSelectionSummary(params: { config: AgentWorkflowUiConfig; selections: Partial>; }) { const result: AgentWorkflowComposerSelection[] = []; for (const step of params.config.steps) { const selectedOptionKey = params.selections[step.key]; if (!selectedOptionKey) continue; const option = step.options.find((item) => item.key === selectedOptionKey && item.enabled); if (!option) continue; result.push({ stepKey: step.key, stepTitle: step.title, optionKey: option.key, optionName: option.buttonName, promptFragment: option.promptFragment || "", }); } return result; } function StepCard({ option, step, selected, onClick, index, }: { option: { key: string; buttonName: string; description?: string | null; previewImageUrl?: string | null }; step: { title: string }; selected: boolean; onClick: () => void; index: number; }) { const previewUrl = getAgentPresetPreviewImageUrl(option.previewImageUrl || ""); return ( {/* 选中指示器 */}
{/* 预览图 */}
{previewUrl ? ( ) : (
)} {/* 选中标记 */} {selected && ( )} {/* 步骤标签 */}
{step.title}
{/* 信息区 */}

{option.buttonName}

{option.description && (

{option.description}

)}
); } export function AgentWorkflowPanelNew({ config, inputs, onInputsChange, materials, onMaterialsChange, selections, onSelectionsChange, onReset, embedded = false, onNextStep, onSkipStep, }: AgentWorkflowPanelNewProps) { const categoryLabels = useMemo( () => getAgentWorkflowCategoryLabels(config), [config] ); const firstCategoryLabel = categoryLabels.firstCategory; const secondCategoryLabel = categoryLabels.secondCategory; const thirdCategoryLabel = categoryLabels.thirdCategory; const fourthCategoryLabel = categoryLabels.fourthCategory; const fifthCategoryLabel = categoryLabels.fifthCategory; const brandOptions = useMemo( () => config?.inputOptions?.brands || defaultAgentWorkflowUiConfig.inputOptions?.brands || [], [config?.inputOptions?.brands] ); const brandStoreEntries = useMemo( () => getAgentWorkflowBrandStoreEntries(config), [config] ); const storeOptions = useMemo( () => getAgentWorkflowStoreOptionsByBrand({ config, brand: inputs.brand }), [config, inputs.brand] ); const thirdCategoryOptions = useMemo( () => getAgentWorkflowThirdCategoryOptionsByBrandStore({ config, brand: inputs.brand, store: inputs.store, }), [config, inputs.brand, inputs.store] ); const fourthCategoryOptions = useMemo( () => getAgentWorkflowFourthCategoryOptionsByBrandStoreThirdCategory({ config, brand: inputs.brand, store: inputs.store, thirdCategory: inputs.thirdCategory, }), [config, inputs.brand, inputs.store, inputs.thirdCategory] ); const fifthCategoryOptions = useMemo( () => getAgentWorkflowFifthCategoryOptionsByBrandStoreThirdFourth({ config, brand: inputs.brand, store: inputs.store, thirdCategory: inputs.thirdCategory, fourthCategory: inputs.fourthCategory, }), [config, inputs.brand, inputs.store, inputs.thirdCategory, inputs.fourthCategory] ); const priceBandOptions = useMemo( () => getAgentWorkflowPriceBandOptions(config), [config] ); const resolvedConfig = useMemo(() => { const source = config || defaultAgentWorkflowUiConfig; return { steps: [...source.steps] .filter((step) => step.enabled) .map((step) => ({ ...step, options: [...step.options] .filter((option) => option.enabled) .sort((left, right) => left.sortOrder - right.sortOrder), })) .filter((step) => step.options.length > 0) .sort((left, right) => left.sortOrder - right.sortOrder), }; }, [config]); const summary = useMemo( () => buildSelectionSummary({ config: resolvedConfig, selections }), [resolvedConfig, selections] ); const composerContext = useMemo( () => buildAgentWorkflowComposerContext({ config: resolvedConfig, inputs, materials, selections, }), [inputs, materials, resolvedConfig, selections] ); const progress = useMemo( () => getAgentWorkflowProgress({ config: resolvedConfig, selections }), [resolvedConfig, selections] ); const handleCopyPrompt = async () => { if (!composerContext?.promptText) { toast.error("当前还没有可复制的 Prompt"); return; } try { await navigator.clipboard.writeText(composerContext.promptText); toast.success("Prompt 已复制到剪贴板"); } catch { toast.error("复制失败,请手动复制"); } }; return (
{/* 头部 */}

风格化工作流

先完成工作流设定,下一步再进入生图模式执行
{/* 进度指示器 */}
完成进度 {progress.completed}/{progress.total}
0 ? (progress.completed / progress.total) * 100 : 0}%` }} transition={{ duration: 0.5, ease: "easeOut" }} />
{/* 基础配置 */}

1 基础配置

onInputsChange({ ...inputs, brand: value, store: value.trim().toLowerCase() === inputs.brand.trim().toLowerCase() ? inputs.store : "", thirdCategory: value.trim().toLowerCase() === inputs.brand.trim().toLowerCase() ? inputs.thirdCategory : "", fourthCategory: "", fifthCategory: "", }) } /> 0 && storeOptions.length === 0)} onValueChange={(value) => onInputsChange({ ...inputs, store: value, thirdCategory: value.trim().toLowerCase() === inputs.store.trim().toLowerCase() ? inputs.thirdCategory : "", fourthCategory: "", fifthCategory: "", }) } /> onInputsChange({ ...inputs, thirdCategory: value, fourthCategory: value.trim().toLowerCase() === inputs.thirdCategory.trim().toLowerCase() ? inputs.fourthCategory : "", fifthCategory: "", }) } /> onInputsChange({ ...inputs, fourthCategory: value, fifthCategory: value.trim().toLowerCase() === inputs.fourthCategory.trim().toLowerCase() ? inputs.fifthCategory : "", }) } /> onInputsChange({ ...inputs, fifthCategory: value })} /> onInputsChange({ ...inputs, priceBand: value })} />
{/* 工作流步骤 */} {resolvedConfig.steps.length > 0 ? ( resolvedConfig.steps.map((step, stepIndex) => (

{stepIndex + 2} {step.title}

{step.description && (

{step.description}

)}
{step.options.map((option, optionIndex) => ( onSelectionsChange({ ...selections, [step.key]: selections[step.key] === option.key ? undefined : option.key, }) } index={optionIndex} /> ))}
)) ) : ( 当前还没有启用的工作流步骤 )} {/* 摘要区域 */} {summary.length > 0 && (

当前选择

{summary.map((item) => (
{item.stepTitle} {item.optionName}
))}
)} {/* Prompt 预览 */}

Prompt 预览