diff --git a/components/agents/AgentGenerationWorkspaceDarkV2.tsx b/components/agents/AgentGenerationWorkspaceDarkV2.tsx index b66e05e7a..bb6069684 100644 --- a/components/agents/AgentGenerationWorkspaceDarkV2.tsx +++ b/components/agents/AgentGenerationWorkspaceDarkV2.tsx @@ -279,6 +279,70 @@ function formatCandidateMeta(candidate: AgentModeReferenceCandidate, target: "de return parts.join(" · "); } +function getCandidatePreviewUrl( + candidate: AgentModeReferenceCandidate, + target: "detail" | "sku" +) { + const targetUrls = + target === "detail" ? candidate.detailImageUrls || [] : candidate.skuImageUrls || []; + return ( + targetUrls[0] || + candidate.mainImageUrls[0] || + candidate.allImageUrls[0] || + null + ); +} + +type ReplacementCardStatus = "waiting" | "queued" | "generating" | "completed" | "missing"; + +function getReplacementCardStatus(params: { + index: number; + resultUrl: string | null; + generatedCount: number; + isGenerating: boolean; +}) : ReplacementCardStatus { + if (params.resultUrl) { + return "completed"; + } + if (params.isGenerating) { + return params.index === params.generatedCount ? "generating" : "queued"; + } + if (params.generatedCount > 0 && params.index >= params.generatedCount) { + return "missing"; + } + return "waiting"; +} + +function getReplacementCardStatusMeta(status: ReplacementCardStatus) { + switch (status) { + case "completed": + return { + label: "已生成", + className: "border-emerald-400/30 bg-emerald-500/[0.10] text-emerald-200", + }; + case "generating": + return { + label: "生成中", + className: "border-sky-400/30 bg-sky-500/[0.10] text-sky-200", + }; + case "queued": + return { + label: "排队中", + className: "border-white/12 bg-white/[0.05] text-white/60", + }; + case "missing": + return { + label: "未回传", + className: "border-amber-400/30 bg-amber-500/[0.10] text-amber-200", + }; + default: + return { + label: "待生成", + className: "border-white/12 bg-white/[0.05] text-white/60", + }; + } +} + export function AgentGenerationWorkspaceDarkV2({ chatId, projectId, @@ -360,6 +424,7 @@ export function AgentGenerationWorkspaceDarkV2({ isLoading: false, lastQuery: null, }); + const [activeDetailReferenceCardId, setActiveDetailReferenceCardId] = useState(null); const [skuReplacementSearchState, setSkuReplacementSearchState] = useState<{ isLoading: boolean; lastQuery: string | null; @@ -367,6 +432,7 @@ export function AgentGenerationWorkspaceDarkV2({ isLoading: false, lastQuery: null, }); + const [activeSkuReferenceCardId, setActiveSkuReferenceCardId] = useState(null); const [imageParams, setImageParams] = useState({ aspectRatio: normalizedIncomingWorkspaceState.imageParams?.aspectRatio || "auto", imageSize: normalizedIncomingWorkspaceState.imageParams?.imageSize || "2K", @@ -770,6 +836,42 @@ export function AgentGenerationWorkspaceDarkV2({ const latestReplacementResultUrls = latestGeneratedGroup?.imageUrls || []; const displayedResultUrl = selectedResultUrl || latestGeneratedGroup?.imageUrls[0] || null; const isGenerating = status === "streaming" || status === "submitted"; + const detailResultCards = useMemo( + () => + detailReplacementReferenceCards.map((card, index) => { + const resultUrl = latestReplacementResultUrls[index] || null; + return { + ...card, + index, + resultUrl, + status: getReplacementCardStatus({ + index, + resultUrl, + generatedCount: latestReplacementResultUrls.length, + isGenerating, + }), + }; + }), + [detailReplacementReferenceCards, isGenerating, latestReplacementResultUrls] + ); + const skuResultCards = useMemo( + () => + skuReplacementReferenceCards.map((card, index) => { + const resultUrl = latestReplacementResultUrls[index] || null; + return { + ...card, + index, + resultUrl, + status: getReplacementCardStatus({ + index, + resultUrl, + generatedCount: latestReplacementResultUrls.length, + isGenerating, + }), + }; + }), + [isGenerating, latestReplacementResultUrls, skuReplacementReferenceCards] + ); const sceneResultCardCount = Math.max( allGeneratedImages.length, imageParams.imageCount || 1, @@ -897,6 +999,22 @@ export function AgentGenerationWorkspaceDarkV2({ selectedAgentPresetId, ] ); + + useEffect(() => { + setActiveDetailReferenceCardId((current) => + detailReplacementReferenceCards.some((card) => card.id === current) + ? current + : detailReplacementReferenceCards[0]?.id || null + ); + }, [detailReplacementReferenceCards]); + + useEffect(() => { + setActiveSkuReferenceCardId((current) => + skuReplacementReferenceCards.some((card) => card.id === current) + ? current + : skuReplacementReferenceCards[0]?.id || null + ); + }, [skuReplacementReferenceCards]); const activeToolStepIndex = Math.max( toolSteps.findIndex((step) => step.key === activeToolStep), 0 @@ -2220,29 +2338,66 @@ export function AgentGenerationWorkspaceDarkV2({ {detailReplacementCandidates.length > 0 ? (
-
- 候选整套素材 {detailReplacementCandidates.length} 套 +
+
+ 候选整套素材 {detailReplacementCandidates.length} 套 +
+
+ 默认载入最新一套,可随时切换 +
{detailReplacementCandidates.map((candidate) => { const isSelected = detailReplacementSelectedCandidateId === candidate.id; + const previewUrl = getCandidatePreviewUrl(candidate, "detail"); return ( ); @@ -2254,9 +2409,18 @@ export function AgentGenerationWorkspaceDarkV2({
{detailReplacementReferenceCards.length > 0 ? ( detailReplacementReferenceCards.map((card, index) => ( + (() => { + const isActive = activeDetailReferenceCardId === card.id; + return (
setActiveDetailReferenceCardId(card.id)} >
) : ( -
+
setActiveDetailReferenceCardId(card.id)} + >
+
+ {statusMeta.label} +
- 结果图 {index + 1} + 结果图 {card.index + 1}
- 将按“输入图组 + 第 {index + 1} 张详情参考图”生成 + 将按“输入图组 + 第 {card.index + 1} 张详情参考图”生成
)}
- {latestReplacementResultUrls[index] + {card.resultUrl ? "已生成,可继续逐屏调整文字或重新执行。" - : "生成完成后,这里会接入现有无限画布的文字编辑能力。"} + : card.status === "generating" + ? "当前正在生成这一屏,请稍候。" + : card.status === "queued" + ? "前面的屏生成完成后,会继续处理这一屏。" + : card.status === "missing" + ? "当前这屏还没有回传结果,可重新执行整套生成。" + : "生成完成后,这里会接入现有无限画布的文字编辑能力。"}
+ ); + })() )) ) : (
@@ -2527,29 +2735,66 @@ export function AgentGenerationWorkspaceDarkV2({ {skuReplacementCandidates.length > 0 ? (
-
- 候选整套素材 {skuReplacementCandidates.length} 套 +
+
+ 候选整套素材 {skuReplacementCandidates.length} 套 +
+
+ 默认载入最新一套,可随时切换 +
{skuReplacementCandidates.map((candidate) => { const isSelected = skuReplacementSelectedCandidateId === candidate.id; + const previewUrl = getCandidatePreviewUrl(candidate, "sku"); return ( ); @@ -2563,7 +2808,13 @@ export function AgentGenerationWorkspaceDarkV2({ skuReplacementReferenceCards.map((card, index) => (
setActiveSkuReferenceCardId(card.id)} > {(() => { const resolvedAssignedProductIndex = @@ -2592,6 +2843,14 @@ export function AgentGenerationWorkspaceDarkV2({ className="h-full w-full object-cover" /> ) : null} +
+ 参考图 {index + 1} +
+ {activeSkuReferenceCardId === card.id ? ( +
+ 当前联动 +
+ ) : null}
{card.label}
@@ -2664,24 +2923,37 @@ export function AgentGenerationWorkspaceDarkV2({
- {skuReplacementReferenceCards.length > 0 ? ( - skuReplacementReferenceCards.map((card, index) => ( + {skuResultCards.length > 0 ? ( + skuResultCards.map((card) => { + const statusMeta = getReplacementCardStatusMeta(card.status); + return (
- {latestReplacementResultUrls[index] ? ( + {card.resultUrl ? (
{`结果图 setFullscreenPreview(latestReplacementResultUrls[index]!)} + onClick={() => { + setActiveSkuReferenceCardId(card.id); + setFullscreenPreview(card.resultUrl!); + }} /> +
+ {statusMeta.label} +
) : ( -
+
setActiveSkuReferenceCardId(card.id)} + >
+
+ {statusMeta.label} +
- 结果图 {index + 1} + 结果图 {card.index + 1}
- 将按“输入图 + 第 {index + 1} 张 SKU 参考图”生成 + 将按“输入图 + 第 {card.index + 1} 张 SKU 参考图”生成
)}
- {latestReplacementResultUrls[index] + {card.resultUrl ? "已生成,可继续逐张检查并后续批量下载。" - : "生成完成后,这里会接入统一文字编辑和批量下载能力。"} + : card.status === "generating" + ? "当前正在生成这一张,请稍候。" + : card.status === "queued" + ? "前面的图生成完成后,会继续处理这一张。" + : card.status === "missing" + ? "当前这张还没有回传结果,可重新执行整套生成。" + : "生成完成后,这里会接入统一文字编辑和批量下载能力。"}
- )) + ); + }) ) : (
等待替换结果
diff --git a/components/agents/AgentPresetDialog.tsx b/components/agents/AgentPresetDialog.tsx index 6932a44db..d861ef118 100644 --- a/components/agents/AgentPresetDialog.tsx +++ b/components/agents/AgentPresetDialog.tsx @@ -554,11 +554,12 @@ export function AgentPresetDialog({ level2: selectedPresetLevel2, level3: selectedPresetLevel3, }); + const createCategoryPath = nextCategoryPath || undefined; const basePayload: AgentCreatePayload = { name: name.trim(), type, visibility, - categoryPath: nextCategoryPath, + categoryPath: createCategoryPath, description: description.trim() || undefined, defaultModelId, defaultParams: nextDefaultParams || undefined, @@ -577,6 +578,7 @@ export function AgentPresetDialog({ const result = initialPreset ? await agentService.updatePreset(initialPreset.id, { ...basePayload, + categoryPath: nextCategoryPath, secondaryType: null, } satisfies AgentUpdatePayload) : await agentService.createPreset(basePayload); @@ -625,8 +627,7 @@ export function AgentPresetDialog({
-
- {!isDetailReplacementModeBuilder ? ( +
预览图
@@ -684,7 +685,6 @@ export function AgentPresetDialog({
- ) : null}
{entityNameLabel} @@ -1284,7 +1284,7 @@ export function AgentPresetDialog({
{isDetailReplacementModeBuilder - ? "详情替换模式不需要上传模板预览图,第二步会通过 SPU 获取详情参考图,再把输入图逐张替换到详情参考图中。" + ? "详情替换模式不需要上传模板参考图,第二步会通过 SPU 获取详情参考图,再把输入图逐张替换到详情参考图中。" : "SKU替换模式会把用户选中的白底图与参考图建立一对一关系,后续在第二步专用工作台中执行替换。"}
diff --git a/lib/agents/preset-library-config.ts b/lib/agents/preset-library-config.ts index 3c25d0282..58ea53471 100644 --- a/lib/agents/preset-library-config.ts +++ b/lib/agents/preset-library-config.ts @@ -318,18 +318,25 @@ export function buildAgentPresetCategoryPath(params: { return null; } - return { - sectionId: section?.id || null, - sectionName: section?.label || null, - level1Id: level1?.id || null, - level1Name: level1?.name || null, - level2Id: level2?.id || null, - level2Name: level2?.name || null, - level3Id: level3?.id || null, - level3Name: level3?.name || null, - productCategory: level1?.name || null, - audienceTag: level2?.name || null, - usageScenario: level3?.name || null, - brand: null, - }; + const nextPath: AgentCategoryPath = {}; + + if (section?.id) nextPath.sectionId = section.id; + if (section?.label) nextPath.sectionName = section.label; + if (level1?.id) nextPath.level1Id = level1.id; + if (level1?.name) { + nextPath.level1Name = level1.name; + nextPath.productCategory = level1.name; + } + if (level2?.id) nextPath.level2Id = level2.id; + if (level2?.name) { + nextPath.level2Name = level2.name; + nextPath.audienceTag = level2.name; + } + if (level3?.id) nextPath.level3Id = level3.id; + if (level3?.name) { + nextPath.level3Name = level3.name; + nextPath.usageScenario = level3.name; + } + + return Object.keys(nextPath).length > 0 ? nextPath : null; }