diff --git a/components/agents/AgentGenerationWorkspaceDarkV2.tsx b/components/agents/AgentGenerationWorkspaceDarkV2.tsx index 125b0384a..ecc805559 100644 --- a/components/agents/AgentGenerationWorkspaceDarkV2.tsx +++ b/components/agents/AgentGenerationWorkspaceDarkV2.tsx @@ -160,6 +160,7 @@ const MAX_CHAT_HIDDEN_PROMPT_LENGTH = 4000; const MAX_CHAT_ATTACHMENT_NAME_LENGTH = 100; const MAX_AGENT_MODE_QUERY_LENGTH = 64; const MAX_AGENT_MODE_REFERENCE_COUNT = 40; +const DIRECT_MODEL_UPLOAD_SLOT_COUNT = 4; const CHAT_UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; const ALLOWED_CHAT_MEDIA_TYPES = new Set([ @@ -1274,6 +1275,19 @@ export function AgentGenerationWorkspaceDarkV2({ }, [hasAgentUploadSlots, modelSlotEnabled, modelSlotIndex, productSlotCount] ); + const isDirectReferenceUploadEnabled = isDirectModelGenerationMode && !hasMaterialStep; + const directReferenceUploadSlots = useMemo( + () => + Array.from({ length: DIRECT_MODEL_UPLOAD_SLOT_COUNT }, (_, index) => ({ + slotIndex: index, + label: `参考图${index + 1}`, + })), + [] + ); + const visibleAttachmentUploadSlots = isDirectReferenceUploadEnabled + ? directReferenceUploadSlots + : agentUploadSlots; + const hasVisibleAttachmentUploadSlots = visibleAttachmentUploadSlots.length > 0; const requiredProductUploadCount = hasAgentUploadSlots ? productSlotCount : 0; const detailReferenceTarget = presetModeConfig.detail.referenceImageSource; const skuReferenceTarget = presetModeConfig.sku.referenceImageSource; @@ -1383,6 +1397,13 @@ export function AgentGenerationWorkspaceDarkV2({ }); } + if (isDirectReferenceUploadEnabled) { + return attachments + .slice(0, DIRECT_MODEL_UPLOAD_SLOT_COUNT) + .map((attachment) => sanitizeChatAttachment(attachment)) + .filter((attachment): attachment is PickedAttachment => Boolean(attachment)); + } + if (!isSceneGenerationPreset) { return attachments .map((attachment) => sanitizeChatAttachment(attachment)) @@ -1439,6 +1460,7 @@ export function AgentGenerationWorkspaceDarkV2({ attachments, hasMaterialStep, isDirectModelGenerationMode, + isDirectReferenceUploadEnabled, isSceneGenerationPreset, referenceImages, scenePrimaryReferenceCount, @@ -1451,7 +1473,11 @@ export function AgentGenerationWorkspaceDarkV2({ requiredProductUploadCount - filledProductAttachmentCount, 0 ); + const directReferenceAttachmentCount = attachments + .slice(0, DIRECT_MODEL_UPLOAD_SLOT_COUNT) + .filter((item) => Boolean(item?.url)).length; const productAttachmentSummary = `${selectedProductAttachments.length}/${productSlotCount} 张产品图`; + const directReferenceAttachmentSummary = `已上传 ${directReferenceAttachmentCount}/${DIRECT_MODEL_UPLOAD_SLOT_COUNT} 张参考图`; const modelAttachmentSummary = modelSlotEnabled ? selectedModelAttachment ? "模特图已带入" @@ -2576,10 +2602,14 @@ export function AgentGenerationWorkspaceDarkV2({ }, [activeResultModeUrls, selectedResultUrl]); useEffect(() => { + if (isDirectReferenceUploadEnabled) { + setIsAttachmentSlotExpanded(true); + return; + } if (!selectedAgentPresetId || !hasAgentUploadSlots) { setIsAttachmentSlotExpanded(false); } - }, [hasAgentUploadSlots, selectedAgentPresetId]); + }, [hasAgentUploadSlots, isDirectReferenceUploadEnabled, selectedAgentPresetId]); useEffect(() => { if (requiresPresetSelection && !selectedAgentPresetId) { @@ -2653,7 +2683,7 @@ export function AgentGenerationWorkspaceDarkV2({ }, [addNode, latestGeneratedGroup, nodes.length, syncGeneratedResultsToCanvas]); const updateAttachmentAtSlot = useCallback((slotIndex: number, attachment: PickedAttachment | null) => { - const slotCount = Math.max(agentUploadSlots.length, 1); + const slotCount = Math.max(visibleAttachmentUploadSlots.length, 1); setAttachments((current) => { const slots: Array = Array.from( { length: slotCount }, @@ -2662,7 +2692,7 @@ export function AgentGenerationWorkspaceDarkV2({ slots[slotIndex] = attachment; return slots; }); - }, [agentUploadSlots.length]); + }, [visibleAttachmentUploadSlots.length]); const handleSlotFileChange = useCallback( async (slotIndex: number, event: React.ChangeEvent) => { @@ -2688,7 +2718,7 @@ export function AgentGenerationWorkspaceDarkV2({ contentType: file.type || "image/jpeg", assetId: typeof data.assetId === "string" ? data.assetId : undefined, }); - toast.success(`${agentUploadSlots[slotIndex]?.label || `槽位${slotIndex + 1}`}上传成功`); + toast.success(`${visibleAttachmentUploadSlots[slotIndex]?.label || `槽位${slotIndex + 1}`}上传成功`); } catch (error) { toast.error(error instanceof Error ? error.message : "上传失败"); } finally { @@ -2696,7 +2726,7 @@ export function AgentGenerationWorkspaceDarkV2({ event.currentTarget.value = ""; } }, - [agentUploadSlots, updateAttachmentAtSlot] + [updateAttachmentAtSlot, visibleAttachmentUploadSlots] ); const handleBatchDownloadImages = useCallback(async (params: { imageUrls: string[]; @@ -4244,43 +4274,56 @@ export function AgentGenerationWorkspaceDarkV2({
- 输入图槽位 + {isDirectReferenceUploadEnabled ? "上传参考图" : "输入图槽位"}
- {agentUploadSlots.length > 0 ? ( + {hasVisibleAttachmentUploadSlots ? ( <> -
- - {productAttachmentSummary} - - {modelAttachmentSummary ? ( + {isDirectReferenceUploadEnabled ? ( +
+ + {directReferenceAttachmentSummary} + + + 可选上传,也可纯文字生成 + +
+ ) : ( +
- {modelAttachmentSummary} + {productAttachmentSummary} - ) : null} - - 共 {agentUploadSlots.length} 个输入槽位 - -
+ {modelAttachmentSummary ? ( + + {modelAttachmentSummary} + + ) : null} + + 共 {agentUploadSlots.length} 个输入槽位 + +
+ )}

- {missingAttachmentCount > 0 - ? `当前还需补充 ${missingAttachmentCount} 张产品图,展开后可继续检查或替换。` - : "产品图已自动带入并准备完成;模特图槽位可按需补充,不选也可以继续生成。"} + {isDirectReferenceUploadEnabled + ? "第一步和第二步关闭时,可在这里直接上传商品图、参考图或场景图;生成时会和补充要求一起发送给模型。" + : missingAttachmentCount > 0 + ? `当前还需补充 ${missingAttachmentCount} 张产品图,展开后可继续检查或替换。` + : "产品图已自动带入并准备完成;模特图槽位可按需补充,不选也可以继续生成。"}

) : ( @@ -4290,14 +4333,20 @@ export function AgentGenerationWorkspaceDarkV2({ )}
- {agentUploadSlots.length > 0 ? ( + {hasVisibleAttachmentUploadSlots ? (