diff --git a/app/canvas/[id]/page.tsx b/app/canvas/[id]/page.tsx index aa4ea2d66..4edc2271b 100644 --- a/app/canvas/[id]/page.tsx +++ b/app/canvas/[id]/page.tsx @@ -90,6 +90,7 @@ import { createEmptyAgentWorkflowMaterialSelections, normalizeAgentWorkflowMaterialSelections, } from "@/lib/agents/workflow-material-config"; +import { normalizeAgentWorkflowBrandModelConfig } from "@/lib/agents/workflow-brand-model-config"; import { buildAgentWorkflowComposerContext, getAgentWorkflowProgress, @@ -274,6 +275,7 @@ export default function CanvasEditorPage({ params }: PageProps) { realtimeCanvasEnabled?: boolean; realtimeCanvasServiceEnabled?: boolean; agentWorkflowShowcaseConfig?: AgentWorkflowShowcaseConfig; + agentWorkflowBrandModelConfig?: unknown; agentWorkflowUiConfig?: { steps?: Array<{ key: AgentWorkflowStepKey; @@ -300,10 +302,16 @@ export default function CanvasEditorPage({ params }: PageProps) { const [aspectRatio, setAspectRatio] = useState("auto"); const [imageCount, setImageCount] = useState(1); const previousNodeIdsRef = useRef>(new Set()); + const previousBrandForModelRef = useRef(""); + const autoSelectedBrandModelBrandRef = useRef(""); const requestedAgentMode = mode === "agent"; const isAgentFeatureEnabled = siteSettingsData?.agentEnabled !== false; const isAgentMode = isAgentFeatureEnabled && requestedAgentMode; + const resolvedBrandModelConfig = useMemo( + () => normalizeAgentWorkflowBrandModelConfig(siteSettingsData?.agentWorkflowBrandModelConfig), + [siteSettingsData?.agentWorkflowBrandModelConfig] + ); // 获取选中的图片节点 const selectedImageNode = selectedNodeIds.length === 1 @@ -903,16 +911,51 @@ export default function CanvasEditorPage({ params }: PageProps) { }, []); useEffect(() => { - if (!selectedBrandModel) return; const normalizedBrand = workflowInputs.brand.trim().toLowerCase(); if (!normalizedBrand) { + previousBrandForModelRef.current = ""; + autoSelectedBrandModelBrandRef.current = ""; + if (selectedBrandModel) { + setSelectedBrandModel(null); + } + return; + } + + const brandChanged = previousBrandForModelRef.current !== normalizedBrand; + if (brandChanged) { + previousBrandForModelRef.current = normalizedBrand; + autoSelectedBrandModelBrandRef.current = ""; + } + + if (selectedBrandModel && selectedBrandModel.brandName.toLowerCase() !== normalizedBrand) { setSelectedBrandModel(null); return; } - if (selectedBrandModel.brandName.toLowerCase() !== normalizedBrand) { - setSelectedBrandModel(null); + + if (selectedBrandModel) { + return; } - }, [selectedBrandModel, workflowInputs.brand]); + + if (autoSelectedBrandModelBrandRef.current === normalizedBrand) { + return; + } + + const matchedEntry = + resolvedBrandModelConfig.entries.find( + (entry) => entry.brandName.trim().toLowerCase() === normalizedBrand + ) || null; + const defaultBrandModel = matchedEntry?.images[0] || null; + + if (!defaultBrandModel) { + return; + } + + autoSelectedBrandModelBrandRef.current = normalizedBrand; + setSelectedBrandModel({ + ...defaultBrandModel, + brandName: matchedEntry?.brandName || workflowInputs.brand.trim(), + }); + }, [resolvedBrandModelConfig.entries, selectedBrandModel, workflowInputs.brand]); const persistCanvasState = useCallback( async (reason: "autosave" | "delete" | "beforeunload") => { diff --git a/components/agents/AgentGenerationWorkspaceDarkV2.tsx b/components/agents/AgentGenerationWorkspaceDarkV2.tsx index 5a1a8f471..9a37d0fca 100644 --- a/components/agents/AgentGenerationWorkspaceDarkV2.tsx +++ b/components/agents/AgentGenerationWorkspaceDarkV2.tsx @@ -411,7 +411,6 @@ export function AgentGenerationWorkspaceDarkV2({ : null ) ); - const [activeAssignmentSlotIndex, setActiveAssignmentSlotIndex] = useState(null); const [slotUploading, setSlotUploading] = useState(null); const [selectedModelId, setSelectedModelId] = useState( normalizedIncomingWorkspaceState.selectedModelId || "auto" @@ -534,32 +533,6 @@ export function AgentGenerationWorkspaceDarkV2({ ); const selectedModelAttachment = modelSlotIndex >= 0 && attachments[modelSlotIndex] ? attachments[modelSlotIndex] : null; - const slotSourceLibrary = useMemo( - () => - referenceImages.map((image, index) => ({ - id: `${image.source}-${image.id || index}`, - source: image.source, - sourceLabel: image.sourceLabel, - label: image.label, - name: image.name, - url: image.url, - assetId: image.assetId || undefined, - mimeType: image.mimeType || undefined, - })), - [referenceImages] - ); - const activeAssignmentIsModelSlot = - activeAssignmentSlotIndex !== null && - modelSlotIndex >= 0 && - activeAssignmentSlotIndex === modelSlotIndex; - const assignableSourceLibrary = useMemo(() => { - if (!slotSourceLibrary.length) return []; - if (activeAssignmentIsModelSlot) { - const brandModelOnly = slotSourceLibrary.filter((item) => item.source === "brandModel"); - return brandModelOnly; - } - return slotSourceLibrary.filter((item) => item.source !== "brandModel"); - }, [activeAssignmentIsModelSlot, slotSourceLibrary]); const resolvedAgentModeState = agentModeState || localAgentModeState; const emitAgentModeState = onAgentModeStateChange || setLocalAgentModeState; const detailReplacementState = resolvedAgentModeState.detailReplacement; @@ -1104,19 +1077,6 @@ export function AgentGenerationWorkspaceDarkV2({ } }, [latestGeneratedGroup?.imageUrls, selectedResultUrl]); - useEffect(() => { - if (agentUploadSlots.length === 0) { - setActiveAssignmentSlotIndex(null); - return; - } - setActiveAssignmentSlotIndex((current) => { - if (current !== null && current >= 0 && current < agentUploadSlots.length) { - return current; - } - return 0; - }); - }, [agentUploadSlots.length]); - useEffect(() => { if (!selectedAgentPresetId) { setActiveToolStep("preset"); @@ -1233,18 +1193,6 @@ export function AgentGenerationWorkspaceDarkV2({ }, [agentUploadSlots, updateAttachmentAtSlot] ); - const handleAssignSourceToSlot = useCallback( - (slotIndex: number, source: (typeof slotSourceLibrary)[number]) => { - updateAttachmentAtSlot(slotIndex, { - url: source.url, - name: source.name || source.label || `槽位${slotIndex + 1}`, - contentType: source.mimeType || "image/png", - assetId: source.assetId, - }); - toast.success(`${source.label || source.name || "已选素材"} 已填入 ${agentUploadSlots[slotIndex]?.label || `槽位${slotIndex + 1}`}`); - }, - [agentUploadSlots, slotSourceLibrary, updateAttachmentAtSlot] - ); const handleBatchDownloadImages = useCallback(async (params: { imageUrls: string[]; zipName: string; @@ -1668,13 +1616,12 @@ export function AgentGenerationWorkspaceDarkV2({ 输入图槽位
- 上一步已选的白底图会自动带入对应产品槽位,这里只作为“检查 / 替换 / 微调映射”的调整区,不再单独占一个步骤页。 + 上一步已选的白底图和品牌模特图会自动带入对应槽位,这里只保留最终检查与单张上传替换,不再重复展示已选素材列表。
{agentUploadSlots.length > 0 ? (
{agentUploadSlots.map((slot, index) => { - const isActiveAssignmentSlot = activeAssignmentSlotIndex === index; return ( {attachments[index] ? "更换" : "上传"} -
); })}
- - {slotSourceLibrary.length > 0 ? ( -
-
-
-
从已选素材快速替换槽位
-
- 先点击上方槽位的“设为目标”,再点击下方素材图,将它放入对应槽位。 -
-
-
- 当前目标: - {activeAssignmentSlotIndex !== null - ? agentUploadSlots[activeAssignmentSlotIndex]?.label || "未选择" - : "未选择"} -
-
- - {assignableSourceLibrary.length > 0 ? ( -
- {assignableSourceLibrary.map((source) => { - const assignedToActiveSlot = - activeAssignmentSlotIndex !== null && - attachments[activeAssignmentSlotIndex]?.url === source.url; - return ( - - ); - })} -
- ) : ( -
- {activeAssignmentIsModelSlot - ? "当前模特槽位还没有可分配的品牌模特图,请先在“品牌模特”步骤里选择。" - : "当前产品槽位还没有来自已选素材区的图片,请先在当前页上方完成 SKU / SPU 选图。"} -
- )} -
- ) : null} ) : (