diff --git a/web/components/ad-recreation-board.tsx b/web/components/ad-recreation-board.tsx index 0ed5d44..1b6f7de 100644 --- a/web/components/ad-recreation-board.tsx +++ b/web/components/ad-recreation-board.tsx @@ -1053,12 +1053,12 @@ function AudioStoryboardPlanPanel({ const itemSourceForRef = (ref: ImageRef) => productItems.find((item) => sameImageRef(item.ref, ref))?.source ?? "upload" - const buildAnalyzedProductItems = (refs: ImageRef[], analysisItems: ProductViewAnalysisItem[] = []) => refs.map((ref, index) => { + const buildAnalyzedProductItems = (refs: ImageRef[], analysisItems: ProductViewAnalysisItem[] = [], startIndex = 0) => refs.map((ref, index) => { const analysis = analysisItems.find((item) => item.index === index) const validView = analysis && PRODUCT_VIEW_SLOTS.some((slot) => slot.value === analysis.view) ? analysis.view : undefined return createProductRefItem( ref, - index, + startIndex + index, itemSourceForRef(ref), validView, analysis?.note, @@ -1119,14 +1119,41 @@ function AudioStoryboardPlanPanel({ } } + const analyzeUploadedProductRefs = async (refs: ImageRef[]) => { + if (!job || !refs.length) return + const baseItems = productItems + const startIndex = baseItems.length + setProductAnalyzing(true) + setProductItems([ + ...baseItems, + ...refs.map((ref, index) => createProductRefItem(ref, startIndex + index, "upload", undefined, "正在自动识别视角...")), + ]) + try { + const analysis = await analyzeProductViews(job.id, refs) + const newItems = buildAnalyzedProductItems(refs, analysis.items, startIndex) + const combined = [...baseItems, ...newItems] + setProductItems(combined) + const completed = await completeMissingProductAngles(combined) + toast.success(completed.length > combined.length ? "新产品图已识别,并已补齐缺失角度" : "新产品图已自动识别") + } catch (e) { + const fallback = refs.map((ref, index) => createProductRefItem(ref, startIndex + index, "upload")) + const combined = [...baseItems, ...fallback] + setProductItems(combined) + await completeMissingProductAngles(combined) + toast.warning("新产品图识别失败,已按默认顺序标注并尝试自动补图:" + (e instanceof Error ? e.message : String(e))) + } finally { + setProductAnalyzing(false) + setProductAngleBusy(null) + } + } + const uploadProductImages = async (files: FileList | null) => { if (!job || !files?.length) return const selected = Array.from(files) setProductUploading(true) try { const refs = await Promise.all(selected.map((file) => uploadStoryboardAsset(job.id, file))) - const nextRefs = [...productItems.map((item) => item.ref), ...refs] - await analyzeAndCompleteProductViews(nextRefs) + await analyzeUploadedProductRefs(refs) toast.success(`已上传 ${refs.length} 张产品白底图`) } catch (e) { toast.error("产品白底图上传失败:" + (e instanceof Error ? e.message : String(e)))