From 076e4f55e680dbe589aa3c7bb74b5c0acd24d119 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 19 Apr 2026 15:36:29 +0800 Subject: [PATCH] feat: add workflow page feature toggles --- .../agent-workflow-settings-client.tsx | 91 ++++ app/api/site-settings/route.ts | 7 + .../[id]/CanvasEditorLegacyPageClient.tsx | 88 +-- .../agents/AgentGenerationWorkspaceDarkV2.tsx | 507 +++++++++++------- lib/agents/workflow-ui-config.ts | 44 ++ types/agent.ts | 7 + 6 files changed, 512 insertions(+), 232 deletions(-) diff --git a/app/(admin)/settings/agent-workflow/agent-workflow-settings-client.tsx b/app/(admin)/settings/agent-workflow/agent-workflow-settings-client.tsx index 3c8c539b6..6db54b247 100644 --- a/app/(admin)/settings/agent-workflow/agent-workflow-settings-client.tsx +++ b/app/(admin)/settings/agent-workflow/agent-workflow-settings-client.tsx @@ -33,6 +33,7 @@ import { import { AGENT_WORKFLOW_PROMPT_FRAGMENT_MAX_LENGTH, defaultAgentWorkflowConstraintSettings, + defaultAgentWorkflowPageFeatureSettings, AGENT_WORKFLOW_MAX_OPTION_DEPTH, AGENT_WORKFLOW_MAX_TREE_DEPTH, countAgentWorkflowOptions, @@ -46,6 +47,7 @@ import { getEnabledAgentWorkflowOptionTree, getAgentWorkflowCategoryLabels, getAgentWorkflowConstraintSettings, + getAgentWorkflowPageFeatureSettings, normalizeAgentWorkflowOptionOrdering, flattenAgentWorkflowSteps, normalizeAgentWorkflowStepOrdering, @@ -1511,6 +1513,10 @@ export function AgentWorkflowSettingsClient({ () => getAgentWorkflowConstraintSettings(config), [config] ); + const workflowPageFeatureSettings = useMemo( + () => getAgentWorkflowPageFeatureSettings(config), + [config] + ); const firstCategoryLabel = categoryLabels.firstCategory; const secondCategoryLabel = categoryLabels.secondCategory; const thirdCategoryLabel = categoryLabels.thirdCategory; @@ -4642,6 +4648,91 @@ export function AgentWorkflowSettingsClient({ variants={containerVariants} className="grid gap-6 lg:grid-cols-2" > + + + +
+
+ +
+
+ 前台流程功能开关 + + 控制风格化页面是否展示选择生图模式、选择 SPU / SKU 和套 PSD 模板入口 + +
+
+
+ +
+
+
开启第一步:选择生图模式
+
+ 关闭后前台不再要求选择生图模式,直接进入后续配置并选择模型生图。 +
+
+ + setConfig((prev) => ({ + ...prev, + pageFeatureSettings: { + ...(prev.pageFeatureSettings || + defaultAgentWorkflowPageFeatureSettings), + presetStepEnabled: checked, + }, + })) + } + /> +
+ +
+
+
开启第二步:选择 SPU / SKU
+
+ 关闭后前台跳过商品素材搜索,不再强制补齐 SPU / SKU 输入图槽位。 +
+
+ + setConfig((prev) => ({ + ...prev, + pageFeatureSettings: { + ...(prev.pageFeatureSettings || + defaultAgentWorkflowPageFeatureSettings), + materialStepEnabled: checked, + }, + })) + } + /> +
+ +
+
+
开启套 PSD 模板
+
+ 关闭后前台隐藏所有“套PSD”按钮和 PSD 模板编辑弹窗入口。 +
+
+ + setConfig((prev) => ({ + ...prev, + pageFeatureSettings: { + ...(prev.pageFeatureSettings || + defaultAgentWorkflowPageFeatureSettings), + psdTemplateEnabled: checked, + }, + })) + } + /> +
+
+
+
+ diff --git a/app/api/site-settings/route.ts b/app/api/site-settings/route.ts index d62bfd7de..e3c1e6c14 100644 --- a/app/api/site-settings/route.ts +++ b/app/api/site-settings/route.ts @@ -1060,6 +1060,13 @@ const updateSchema = z.object({ crossLevelMutualExclusionEnabled: z.boolean().optional(), }) .optional(), + pageFeatureSettings: z + .object({ + presetStepEnabled: z.boolean().optional(), + materialStepEnabled: z.boolean().optional(), + psdTemplateEnabled: z.boolean().optional(), + }) + .optional(), steps: z .array(agentWorkflowStepSchema) .max(20), diff --git a/app/canvas/[id]/CanvasEditorLegacyPageClient.tsx b/app/canvas/[id]/CanvasEditorLegacyPageClient.tsx index 69bddd6c9..68b832004 100644 --- a/app/canvas/[id]/CanvasEditorLegacyPageClient.tsx +++ b/app/canvas/[id]/CanvasEditorLegacyPageClient.tsx @@ -177,6 +177,7 @@ import { defaultAgentWorkflowUiConfig, flattenVisibleAgentWorkflowSteps, getEnabledAgentWorkflowStepTree, + getAgentWorkflowPageFeatureSettings, normalizeAgentWorkflowUiConfig, sanitizeAgentWorkflowSelections, } from "@/lib/agents/workflow-ui-config"; @@ -1552,6 +1553,11 @@ export default function CanvasEditorLegacyPage({ params }: PageProps) { agentWorkflowBrandModelConfig?: unknown; agentPsdTemplateCategoryTagIndex?: AgentPsdTemplateCategoryTagIndex; agentWorkflowUiConfig?: { + pageFeatureSettings?: { + presetStepEnabled?: boolean; + materialStepEnabled?: boolean; + psdTemplateEnabled?: boolean; + }; steps?: Array<{ key: AgentWorkflowStepKey; title: string; @@ -1600,6 +1606,14 @@ export default function CanvasEditorLegacyPage({ params }: PageProps) { normalizeAgentWorkflowUiConfig(siteSettingsData?.agentWorkflowUiConfig), [siteSettingsData?.agentWorkflowUiConfig] ); + const workflowPageFeatureSettings = useMemo( + () => getAgentWorkflowPageFeatureSettings(resolvedWorkflowUiConfig), + [resolvedWorkflowUiConfig] + ); + const isWorkflowPresetStepEnabled = + workflowPageFeatureSettings.presetStepEnabled !== false; + const isWorkflowMaterialStepEnabled = + workflowPageFeatureSettings.materialStepEnabled !== false; const sanitizedWorkflowSelections = useMemo( () => sanitizeAgentWorkflowSelections({ @@ -8152,11 +8166,14 @@ export default function CanvasEditorLegacyPage({ params }: PageProps) { selections: workflowSelectionsRef.current, }); }, [resolvedWorkflowUiConfig]); + const effectiveSelectedAgentPresetId = isWorkflowPresetStepEnabled + ? selectedAgentPresetId + : null; const { data: selectedAgentPresetResponse } = useSWR<{ success?: boolean; data?: AgentPreset; }>( - selectedAgentPresetId ? `/api/agents/${selectedAgentPresetId}` : null, + effectiveSelectedAgentPresetId ? `/api/agents/${effectiveSelectedAgentPresetId}` : null, fetcher ); const selectedAgentPresetData = useMemo( @@ -8519,35 +8536,39 @@ export default function CanvasEditorLegacyPage({ params }: PageProps) { /> } brandStorePanel={ - + isWorkflowPresetStepEnabled ? ( + + ) : null } chatId={agentModeChatId} deletedResultCollections={agentProjectDeletedResultCollections} getLatestWorkflowContext={getLatestWorkflowContext} materialSelectionPanel={ - + isWorkflowMaterialStepEnabled ? ( + + ) : null } onAgentModeStateChange={setAgentModeState} onClearPreset={() => setSelectedAgentPresetId(null)} @@ -8560,17 +8581,19 @@ export default function CanvasEditorLegacyPage({ params }: PageProps) { onResultPreviewUrlsChange={handleAgentResultPreviewUrlsChange} onWorkspaceStateChange={setAgentWorkspaceState} presetPanel={ - + isWorkflowPresetStepEnabled ? ( + + ) : null } projectId={agentProjectId} resultCollections={agentProjectResultCollections} selectedAgentPresetId={ - isAgentWorkspaceEnabled ? selectedAgentPresetId : null + isAgentWorkspaceEnabled ? effectiveSelectedAgentPresetId : null } selectedBrandModel={selectedBrandModel} showcaseConfig={siteSettingsData?.agentWorkflowShowcaseConfig} @@ -8580,6 +8603,7 @@ export default function CanvasEditorLegacyPage({ params }: PageProps) { syncGeneratedResultsToCanvas={false} topNavRightSlot={agentProjectNavSlot} workflowContext={workflowContext} + workflowFeatureSettings={workflowPageFeatureSettings} // 嵌入的风格化选择面板 workflowPromptOptimizationEnabled={appearanceRecognitionEnabled} workflowStylePanel={ diff --git a/components/agents/AgentGenerationWorkspaceDarkV2.tsx b/components/agents/AgentGenerationWorkspaceDarkV2.tsx index 290ee03da..125b0384a 100644 --- a/components/agents/AgentGenerationWorkspaceDarkV2.tsx +++ b/components/agents/AgentGenerationWorkspaceDarkV2.tsx @@ -82,6 +82,9 @@ import { import { normalizeAgentPresetModeConfig, } from "@/lib/agents/preset-mode-config"; +import { + normalizeAgentWorkflowPageFeatureSettings, +} from "@/lib/agents/workflow-ui-config"; import { buildAgentSceneImageRolePrompt } from "@/lib/agents/scene-image-role-prompt"; import type { AgentPsdTemplateMode } from "@/lib/agents/psd-template-config"; import type { Attachment, ChatMessage } from "@/lib/types"; @@ -100,6 +103,7 @@ import type { AgentWorkflowShowcaseConfig, AgentWorkflowComposerContext, AgentWorkflowMaterialQueryType, + AgentWorkflowPageFeatureSettings, AgentWorkflowSelectedBrandModel, } from "@/types/agent"; import { @@ -116,6 +120,7 @@ interface AgentGenerationWorkspaceDarkV2Props { selectedAgentPresetId?: string | null; selectedBrandModel?: AgentWorkflowSelectedBrandModel | null; workflowContext?: AgentWorkflowComposerContext | null; + workflowFeatureSettings?: AgentWorkflowPageFeatureSettings | null; workflowPromptOptimizationEnabled?: boolean; getLatestWorkflowContext?: () => AgentWorkflowComposerContext | null; onBack?: () => void; @@ -955,6 +960,7 @@ export function AgentGenerationWorkspaceDarkV2({ selectedAgentPresetId, selectedBrandModel = null, workflowContext = null, + workflowFeatureSettings = null, workflowPromptOptimizationEnabled = true, getLatestWorkflowContext, onBack, @@ -1018,6 +1024,16 @@ export function AgentGenerationWorkspaceDarkV2({ }).workspaceState || createEmptyAgentGenerationProjectWorkspaceState(), [workspaceState] ); + const resolvedWorkflowFeatureSettings = useMemo( + () => normalizeAgentWorkflowPageFeatureSettings(workflowFeatureSettings), + [workflowFeatureSettings] + ); + const isPresetStepEnabled = resolvedWorkflowFeatureSettings.presetStepEnabled !== false; + const isMaterialStepEnabled = resolvedWorkflowFeatureSettings.materialStepEnabled !== false; + const isPsdTemplateEnabled = resolvedWorkflowFeatureSettings.psdTemplateEnabled !== false; + const requiresPresetSelection = isPresetStepEnabled; + const isDirectModelGenerationMode = !requiresPresetSelection; + const hasMaterialStep = isMaterialStepEnabled && Boolean(materialSelectionPanel); const [input, setInput] = useState(normalizedIncomingWorkspaceState.input); const [attachments, setAttachments] = useState>( @@ -1088,6 +1104,10 @@ export function AgentGenerationWorkspaceDarkV2({ mode?: AgentPsdTemplateMode | null; resultModeType?: AgentGenerationResultModeType | null; }) => { + if (!isPsdTemplateEnabled) { + toast.info("套 PSD 模板功能已关闭"); + return; + } setPsdTemplateDialogSourceUrl( isValidChatHttpUrl(params?.sourceImageUrl) ? String(params?.sourceImageUrl).trim() @@ -1103,7 +1123,7 @@ export function AgentGenerationWorkspaceDarkV2({ ); setIsPsdTemplateDialogOpen(true); }, - [activeResultModeType] + [activeResultModeType, isPsdTemplateEnabled] ); const normalizedDeletedResultCollections = useMemo( () => ({ @@ -1144,11 +1164,17 @@ export function AgentGenerationWorkspaceDarkV2({ saveStoredAgentPendingGenerations(allItems); }, [chatId, pendingGeneration]); + useEffect(() => { + if (!isPsdTemplateEnabled && isPsdTemplateDialogOpen) { + closePsdTemplateDialog(); + } + }, [closePsdTemplateDialog, isPsdTemplateDialogOpen, isPsdTemplateEnabled]); + const { data: historyData } = useSWR<{ messages: ChatMessage[] }>( rawChatId ? `/api/messages?chatId=${rawChatId}` : null, fetcher, { - refreshInterval: selectedAgentPresetId ? 3000 : 0, + refreshInterval: selectedAgentPresetId || isDirectModelGenerationMode ? 3000 : 0, revalidateOnFocus: true, } ); @@ -1228,7 +1254,9 @@ export function AgentGenerationWorkspaceDarkV2({ : true : false; const modelSlotIndex = modelSlotEnabled ? productSlotCount : -1; - const hasAgentUploadSlots = Boolean(selectedAgentPreset && selectedAgentPreset.type !== "text"); + const hasAgentUploadSlots = Boolean( + hasMaterialStep && selectedAgentPreset && selectedAgentPreset.type !== "text" + ); const agentUploadSlots = useMemo( () => { if (!hasAgentUploadSlots) return []; @@ -1330,6 +1358,31 @@ export function AgentGenerationWorkspaceDarkV2({ }, [referenceImages]); const sceneSecondaryReferenceCount = sceneSecondaryReferenceAttachments.length; const sceneMessageAttachments = useMemo(() => { + if (isDirectModelGenerationMode && hasMaterialStep) { + const seenKeys = new Set(); + return referenceImages + .map((image) => + toWorkflowReferenceAttachment({ + url: image.url, + name: image.name, + label: image.label, + mimeType: image.mimeType, + assetId: image.assetId || null, + }) + ) + .filter((attachment): attachment is PickedAttachment => { + if (!attachment) return false; + const key = getAttachmentIdentityKey(attachment); + if (key && seenKeys.has(key)) { + return false; + } + if (key) { + seenKeys.add(key); + } + return true; + }); + } + if (!isSceneGenerationPreset) { return attachments .map((attachment) => sanitizeChatAttachment(attachment)) @@ -1384,7 +1437,10 @@ export function AgentGenerationWorkspaceDarkV2({ return ordered; }, [ attachments, + hasMaterialStep, + isDirectModelGenerationMode, isSceneGenerationPreset, + referenceImages, scenePrimaryReferenceCount, sceneSecondaryReferenceAttachments, selectedModelAttachment, @@ -1577,8 +1633,33 @@ export function AgentGenerationWorkspaceDarkV2({ ); const [activeToolStep, setActiveToolStep] = useState("preset"); const hasBrandModelStep = Boolean(brandModelPanel) && modelSlotEnabled; - const hasMaterialStep = Boolean(materialSelectionPanel); const hasWorkflowStyleStep = Boolean(workflowStylePanel) || hasBrandModelStep; + const visibleWorkflowSectionKeys = useMemo( + () => [ + ...(isPresetStepEnabled ? ["mode"] : []), + ...(hasMaterialStep ? ["materials"] : []), + ...(hasWorkflowStyleStep ? ["style"] : []), + "generate", + ], + [hasMaterialStep, hasWorkflowStyleStep, isPresetStepEnabled] + ); + const getWorkflowSectionBadge = useCallback( + (key: "mode" | "materials" | "style" | "generate") => { + const index = visibleWorkflowSectionKeys.indexOf(key); + return String(index >= 0 ? index + 1 : visibleWorkflowSectionKeys.length + 1).padStart( + 2, + "0" + ); + }, + [visibleWorkflowSectionKeys] + ); + const firstWorkflowSectionAnchor = isPresetStepEnabled + ? "agent-anchor-mode" + : hasMaterialStep + ? "agent-anchor-materials" + : hasWorkflowStyleStep + ? "agent-anchor-style" + : "agent-anchor-generate"; const { messages, setMessages, sendMessage, status, stop } = useChat({ id: chatId, @@ -1591,11 +1672,15 @@ export function AgentGenerationWorkspaceDarkV2({ prepareSendMessagesRequest(request) { const transportState = transportStateRef.current; const resolvedAgentPresetId = - transportState.selectedAgentPresetId || selectedAgentPresetId || undefined; + requiresPresetSelection + ? transportState.selectedAgentPresetId || selectedAgentPresetId || undefined + : undefined; const resolvedSelectedModelId = transportState.selectedModelId !== "auto" ? transportState.selectedModelId - : selectedAgentPreset?.defaultModelId || transportState.selectedModelId; + : selectedAgentPreset?.defaultModelId || + modelsData?.defaultModelId || + transportState.selectedModelId; const replacementModePayload = transportState.selectedAgentPresetType === "image" && (transportState.isDetailReplacementPreset || transportState.isSkuReplacementPreset) @@ -1652,12 +1737,14 @@ export function AgentGenerationWorkspaceDarkV2({ MAX_CHAT_HIDDEN_PROMPT_LENGTH ) || undefined, workflowPromptOptimizationEnabled, - agentPresetId: isValidChatUuid(resolvedAgentPresetId) + agentPresetId: requiresPresetSelection && isValidChatUuid(resolvedAgentPresetId) ? resolvedAgentPresetId : undefined, selectedChatModel: String(resolvedSelectedModelId || "auto").trim() || "auto", composerMode: - selectedAgentPreset?.type === "image" ? ("image" as const) : ("agent" as const), + isDirectModelGenerationMode || selectedAgentPreset?.type === "image" + ? ("image" as const) + : ("agent" as const), selectedVisibilityType: "private", imageParams: transportState.imageParams, contextPayload: { @@ -2268,25 +2355,26 @@ export function AgentGenerationWorkspaceDarkV2({ const mergedSettingsStepDescription = hasMaterialStep ? "搜索素材、补充要求、确认槽位并开始生成" : "补充要求并开始生成"; - const styleStepBadge = hasMaterialStep ? "03" : "02"; - const generateStepBadge = - hasMaterialStep && hasWorkflowStyleStep - ? "04" - : hasMaterialStep || hasWorkflowStyleStep - ? "03" - : "02"; + const modeStepBadge = getWorkflowSectionBadge("mode"); + const materialStepBadge = getWorkflowSectionBadge("materials"); + const styleStepBadge = getWorkflowSectionBadge("style"); + const generateStepBadge = getWorkflowSectionBadge("generate"); const toolSteps = useMemo( () => [ - { - key: "preset" as const, - label: "生图模式", - description: "先选择一个模式,再继续后续配置", - icon: Wand2, - disabled: false, - completed: Boolean(selectedAgentPresetId), - optional: false, - }, + ...(isPresetStepEnabled + ? [ + { + key: "preset" as const, + label: "生图模式", + description: "先选择一个模式,再继续后续配置", + icon: Wand2, + disabled: false, + completed: Boolean(selectedAgentPresetId), + optional: false, + }, + ] + : []), ...(hasBrandModelStep ? [ { @@ -2294,7 +2382,7 @@ export function AgentGenerationWorkspaceDarkV2({ label: "品牌模特", description: "根据品牌选择模特图", icon: UserRound, - disabled: !selectedAgentPresetId, + disabled: requiresPresetSelection && !selectedAgentPresetId, completed: hasBrandModelSelection, optional: true, }, @@ -2305,9 +2393,9 @@ export function AgentGenerationWorkspaceDarkV2({ label: mergedSettingsStepLabel, description: mergedSettingsStepDescription, icon: Settings2, - disabled: !selectedAgentPresetId, + disabled: requiresPresetSelection && !selectedAgentPresetId, completed: - Boolean(selectedAgentPresetId) && + (!requiresPresetSelection || Boolean(selectedAgentPresetId)) && (Boolean(input.trim()) || hasMaterialSelections || hasSatisfiedSlotRequirement), optional: false, }, @@ -2317,10 +2405,12 @@ export function AgentGenerationWorkspaceDarkV2({ hasBrandModelStep, hasMaterialSelections, hasSatisfiedSlotRequirement, + isPresetStepEnabled, mergedSettingsStepDescription, mergedSettingsStepLabel, hasMaterialStep, input, + requiresPresetSelection, selectedAgentPresetId, ] ); @@ -2492,7 +2582,7 @@ export function AgentGenerationWorkspaceDarkV2({ }, [hasAgentUploadSlots, selectedAgentPresetId]); useEffect(() => { - if (!selectedAgentPresetId) { + if (requiresPresetSelection && !selectedAgentPresetId) { setActiveToolStep("preset"); return; } @@ -2511,14 +2601,15 @@ export function AgentGenerationWorkspaceDarkV2({ }); }, [ hasBrandModelStep, + requiresPresetSelection, selectedAgentPresetId, ]); useEffect(() => { if (!toolSteps.some((step) => step.key === activeToolStep)) { - setActiveToolStep(selectedAgentPresetId ? "settings" : "preset"); + setActiveToolStep(toolSteps[0]?.key || "settings"); } - }, [activeToolStep, selectedAgentPresetId, toolSteps]); + }, [activeToolStep, toolSteps]); useEffect(() => { if (!syncGeneratedResultsToCanvas) { @@ -2705,7 +2796,7 @@ export function AgentGenerationWorkspaceDarkV2({ void handleRunAiDetailGeneration(); return; } - if (!selectedAgentPresetId) { + if (requiresPresetSelection && !selectedAgentPresetId) { toast.error("请先选择生图模式"); return; } @@ -2725,7 +2816,7 @@ export function AgentGenerationWorkspaceDarkV2({ const resolvedModelId = selectedModelId !== "auto" ? selectedModelId - : selectedAgentPreset?.defaultModelId || selectedModelId; + : selectedAgentPreset?.defaultModelId || modelsData?.defaultModelId || selectedModelId; if (!resolvedModelId) { toast.error("当前风格化未配置模型"); return; @@ -2846,7 +2937,9 @@ export function AgentGenerationWorkspaceDarkV2({ isDetailAiGenerationMethod, isDetailReplacementPreset, isSkuReplacementPreset, + modelsData?.defaultModelId, requiredProductUploadCount, + requiresPresetSelection, selectedAgentPreset, selectedAgentPresetId, selectableModels, @@ -3022,12 +3115,12 @@ export function AgentGenerationWorkspaceDarkV2({ const nextStep = toolSteps.find((step) => step.key === nextKey); if (!nextStep) return; if (nextStep.disabled) { - toast.error("请先选择生图模式"); + toast.error(requiresPresetSelection ? "请先选择生图模式" : "当前步骤暂不可用"); return; } setActiveToolStep(nextKey); }, - [toolSteps] + [requiresPresetSelection, toolSteps] ); const scrollToSection = useCallback((sectionId: string) => { if ( @@ -4323,9 +4416,9 @@ export function AgentGenerationWorkspaceDarkV2({ > 0 ? "查看结果预览" : undefined} - onActionClick={() => scrollToSection("agent-anchor-mode")} + onActionClick={() => scrollToSection(firstWorkflowSectionAnchor)} onSecondaryActionClick={() => scrollToSection("agent-anchor-results")} /> @@ -4338,85 +4431,87 @@ export function AgentGenerationWorkspaceDarkV2({ className="rounded-[28px] border border-white/12 bg-white/[0.04] p-5 shadow-[inset_0_1px_0_rgba(255,255,255,0.03)]" >
-
-
-
-
- - 01 - - 选择生图模式 -
-

- 先确定本次使用的模式,并完成品牌和店铺选择,后续的商品图、品牌模特、参考图和结果展示都会随之联动。 -

- {isDetailReplacementPreset || isSkuReplacementPreset ? ( -

- {isDetailReplacementPreset - ? isDetailAiGenerationMethod - ? "当前模板已切换到 AI详情生成,系统会直接复用智能规划详情链路生成整套详情图。" - : "当前模板属于参考图替换模式,结果区会按参考图顺序逐屏展示。" - : "当前模板属于 SKU 替换模式,结果区会按参考图顺序一对一展示。"} + {isPresetStepEnabled ? ( +

+
+
+
+ + {modeStepBadge} + + 选择生图模式 +
+

+ 先确定本次使用的模式,并完成品牌和店铺选择,后续的商品图、品牌模特、参考图和结果展示都会随之联动。

- ) : null} -
-
- {selectedAgentPreset ? ( -
- {selectedAgentPreset.avatarUrl ? ( - {selectedAgentPreset.name} - ) : ( -
- -
- )} -
-
{selectedAgentPreset.name}
-
- {selectedAgentPreset.type === "image" ? "生图模式" : "视频模式"} + {isDetailReplacementPreset || isSkuReplacementPreset ? ( +

+ {isDetailReplacementPreset + ? isDetailAiGenerationMethod + ? "当前模板已切换到 AI详情生成,系统会直接复用智能规划详情链路生成整套详情图。" + : "当前模板属于参考图替换模式,结果区会按参考图顺序逐屏展示。" + : "当前模板属于 SKU 替换模式,结果区会按参考图顺序一对一展示。"} +

+ ) : null} +
+
+ {selectedAgentPreset ? ( +
+ {selectedAgentPreset.avatarUrl ? ( + {selectedAgentPreset.name} + ) : ( +
+ +
+ )} +
+
{selectedAgentPreset.name}
+
+ {selectedAgentPreset.type === "image" ? "生图模式" : "视频模式"} +
-
- ) : null} - {selectedAgentPreset && onClearPreset ? ( - - ) : null} + ) : null} + {selectedAgentPreset && onClearPreset ? ( + + ) : null} +
-
- {presetPanel ? ( -
- {presetPanel} -
- ) : ( -
- 暂无可用的生图模式配置。 -
- )} + {presetPanel ? ( +
+ {presetPanel} +
+ ) : ( +
+ 暂无可用的生图模式配置。 +
+ )} - {brandStorePanel ? ( -
- {brandStorePanel} -
- ) : null} -
+ {brandStorePanel ? ( +
+ {brandStorePanel} +
+ ) : null} +
+ ) : null} {hasMaterialStep ? (
- 02 + {materialStepBadge} 选择 SPU / SKU
@@ -4607,7 +4702,7 @@ export function AgentGenerationWorkspaceDarkV2({ onClick={handleSend} disabled={ isAnyGenerationRunning || - !selectedAgentPresetId || + (requiresPresetSelection && !selectedAgentPresetId) || (isDetailAiGenerationMethod ? detailGenerationValidationIssues.length > 0 : hasAgentUploadSlots && !hasSatisfiedSlotRequirement) @@ -5067,18 +5162,20 @@ export function AgentGenerationWorkspaceDarkV2({ {detailGenerationExpectedCount}
) : null} - + {isPsdTemplateEnabled ? ( + + ) : null}
@@ -5913,28 +6010,30 @@ export function AgentGenerationWorkspaceDarkV2({ > - + {isPsdTemplateEnabled ? ( + + ) : null}
双击查看大图
) : null} - {!isSceneResultCard ? ( + {!isSceneResultCard && isPsdTemplateEnabled ? ( -

- 可直接进入模板页,并通过手动上传图片开始套版。 -

+ {isPsdTemplateEnabled ? ( + <> + +

+ 可直接进入模板页,并通过手动上传图片开始套版。 +

+ + ) : null}
)} @@ -6080,61 +6185,63 @@ export function AgentGenerationWorkspaceDarkV2({ /> ) : null} - { + if (!open) { + closePsdTemplateDialog(); + } + }} + > + + + PSD 模板暂时无法打开 + + 已拦截本次客户端异常,避免整个风格化工作台被一起打断。 + + +
+

请关闭弹窗后重试一次。

+

如果仍然出现,优先检查当前品牌下的 PSD 模板配置、图层数据或字体资源是否完整。

+
+
+ + } + > + { if (!open) { closePsdTemplateDialog(); } }} - > - - - PSD 模板暂时无法打开 - - 已拦截本次客户端异常,避免整个风格化工作台被一起打断。 - - -
-

请关闭弹窗后重试一次。

-

如果仍然出现,优先检查当前品牌下的 PSD 模板配置、图层数据或字体资源是否完整。

-
-
- - } - > - { - if (!open) { - closePsdTemplateDialog(); + sourceImageUrl={psdTemplateDialogSourceUrl} + brandName={workflowContext?.brand || null} + selectedCategoryNames={[workflowContext?.fifthCategory || ""].filter(Boolean)} + sizeInfoText={workflowContext?.summaryText || null} + templateMode={psdTemplateDialogMode} + onDerivedImageCreated={ + onResultPreviewUrlsChange ? handlePsdDerivedImageCreated : undefined } - }} - sourceImageUrl={psdTemplateDialogSourceUrl} - brandName={workflowContext?.brand || null} - selectedCategoryNames={[workflowContext?.fifthCategory || ""].filter(Boolean)} - sizeInfoText={workflowContext?.summaryText || null} - templateMode={psdTemplateDialogMode} - onDerivedImageCreated={ - onResultPreviewUrlsChange ? handlePsdDerivedImageCreated : undefined - } - onRenderedResultCreated={ - onResultPreviewUrlsChange ? handlePsdRenderedResultCreated : undefined - } - /> -
+ onRenderedResultCreated={ + onResultPreviewUrlsChange ? handlePsdRenderedResultCreated : undefined + } + /> + + ) : null} {/* Fullscreen Preview */} diff --git a/lib/agents/workflow-ui-config.ts b/lib/agents/workflow-ui-config.ts index 98247232e..aaae9b459 100644 --- a/lib/agents/workflow-ui-config.ts +++ b/lib/agents/workflow-ui-config.ts @@ -4,6 +4,7 @@ import type { AgentWorkflowConstraintSettings, AgentWorkflowFourthCategoryEntry, AgentWorkflowOptionUiConfig, + AgentWorkflowPageFeatureSettings, AgentWorkflowStoreEntry, AgentWorkflowThirdCategoryEntry, AgentWorkflowStepKey, @@ -25,6 +26,11 @@ export const defaultAgentWorkflowCategoryLabels: AgentWorkflowCategoryLabels = { export const defaultAgentWorkflowConstraintSettings: AgentWorkflowConstraintSettings = { crossLevelMutualExclusionEnabled: false, }; +export const defaultAgentWorkflowPageFeatureSettings: AgentWorkflowPageFeatureSettings = { + presetStepEnabled: true, + materialStepEnabled: true, + psdTemplateEnabled: true, +}; export interface AgentWorkflowBrandCategoryOption { value: string; @@ -169,6 +175,7 @@ export const defaultAgentWorkflowUiConfig: AgentWorkflowUiConfig = { brandStoreEntries: [], }, constraintSettings: defaultAgentWorkflowConstraintSettings, + pageFeatureSettings: defaultAgentWorkflowPageFeatureSettings, steps: defaultSteps.map((step, index) => cloneStep({ ...step, @@ -230,6 +237,30 @@ export function normalizeAgentWorkflowConstraintSettings( }; } +export function normalizeAgentWorkflowPageFeatureSettings( + value: unknown +): AgentWorkflowPageFeatureSettings { + const record = + value && typeof value === "object" && !Array.isArray(value) + ? (value as Record) + : {}; + + return { + presetStepEnabled: normalizeBoolean( + record.presetStepEnabled ?? record.modeStepEnabled ?? record.enablePresetStep, + defaultAgentWorkflowPageFeatureSettings.presetStepEnabled + ), + materialStepEnabled: normalizeBoolean( + record.materialStepEnabled ?? record.skuStepEnabled ?? record.enableMaterialStep, + defaultAgentWorkflowPageFeatureSettings.materialStepEnabled + ), + psdTemplateEnabled: normalizeBoolean( + record.psdTemplateEnabled ?? record.enablePsdTemplate, + defaultAgentWorkflowPageFeatureSettings.psdTemplateEnabled + ), + }; +} + export function flattenAgentWorkflowSteps( steps: AgentWorkflowStepUiConfig[], depth = 1, @@ -1036,6 +1067,13 @@ export function normalizeAgentWorkflowUiConfig(value: unknown): AgentWorkflowUiC crossLevelMutualExclusionEnabled: raw.crossLevelMutualExclusionEnabled, } ), + pageFeatureSettings: normalizeAgentWorkflowPageFeatureSettings( + raw.pageFeatureSettings ?? { + presetStepEnabled: raw.presetStepEnabled ?? raw.modeStepEnabled, + materialStepEnabled: raw.materialStepEnabled ?? raw.skuStepEnabled, + psdTemplateEnabled: raw.psdTemplateEnabled, + } + ), steps: sanitizedSteps, }; } @@ -1056,6 +1094,12 @@ export function getAgentWorkflowConstraintSettings( return normalizeAgentWorkflowConstraintSettings(config?.constraintSettings); } +export function getAgentWorkflowPageFeatureSettings( + config?: AgentWorkflowUiConfig | null +) { + return normalizeAgentWorkflowPageFeatureSettings(config?.pageFeatureSettings); +} + export function getAgentWorkflowCategoryOptionsByBrand(params: { config?: AgentWorkflowUiConfig | null; brand?: string | null; diff --git a/types/agent.ts b/types/agent.ts index 5e800c5d4..56b707a69 100644 --- a/types/agent.ts +++ b/types/agent.ts @@ -105,6 +105,12 @@ export interface AgentWorkflowConstraintSettings { crossLevelMutualExclusionEnabled: boolean; } +export interface AgentWorkflowPageFeatureSettings { + presetStepEnabled: boolean; + materialStepEnabled: boolean; + psdTemplateEnabled: boolean; +} + export interface AgentWorkflowUiConfig { inputOptions?: { brands: string[]; @@ -117,6 +123,7 @@ export interface AgentWorkflowUiConfig { brandStoreEntries?: AgentWorkflowBrandStoreEntry[]; }; constraintSettings?: AgentWorkflowConstraintSettings; + pageFeatureSettings?: AgentWorkflowPageFeatureSettings; steps: AgentWorkflowStepUiConfig[]; }