import { CloudUploadOutlined, CloseOutlined, FileImageOutlined, LoadingOutlined, QuestionCircleOutlined, ReloadOutlined, SettingOutlined, } from "@ant-design/icons"; import { createPortal } from "react-dom"; import type { CSSProperties, ChangeEvent, DragEvent, MutableRefObject, RefObject } from "react"; import { useRef, useState } from "react"; type ProductSetOutputKey = "set" | "detail" | "model" | "video"; type CloneOutputKey = ProductSetOutputKey | "hot" | "video-outfit"; type CloneSetCountKey = "selling" | "white" | "scene"; type CloneModelPanelTab = "scene" | "model"; type CloneReferenceMode = "upload" | "link"; type CloneReplicateLevelKey = "style" | "high"; type CloneVideoQualityKey = "standard" | "high" | "ultra"; type CloneBasicSelectKey = "platform" | "market" | "language" | "ratio"; type CloneModelSelectKey = "gender" | "age" | "ethnicity" | "body"; interface CloneImageItem { id: string; src: string; name: string; } interface CloneBasicSelectItem { key: CloneBasicSelectKey; label: string; value: string; options: string[]; onChange: (value: string) => void; } interface CloneModelSelectItem { key: CloneModelSelectKey; label: string; value: string; options: string[]; onChange: (value: string) => void; } interface CloneSetCountOption { key: CloneSetCountKey; title: string; desc: string; } interface CloneOutputOption { key: CloneOutputKey; label: string; } interface CloneReplicateLevelOption { key: CloneReplicateLevelKey; title: string; desc: string; } interface CloneVideoQualityOption { key: CloneVideoQualityKey; label: string; desc: string; } interface CloneDetailModule { id: string; title: string; desc: string; } interface EcommerceClonePanelProps { productInputRef: RefObject; cloneReferenceInputRef: RefObject; productImages: CloneImageItem[]; isProductUploadDragging: boolean; cloneOutput: CloneOutputKey; cloneOutputOptions: CloneOutputOption[]; cloneBasicSelects: CloneBasicSelectItem[]; openCloneBasicSelect: CloneBasicSelectKey | null; cloneReferenceMode: CloneReferenceMode; cloneReferenceImages: CloneImageItem[]; maxCloneReferenceImages: number; cloneReplicateLevel: CloneReplicateLevelKey; cloneReplicateLevelOptions: CloneReplicateLevelOption[]; cloneSetCounts: Record; cloneSetCountOptions: CloneSetCountOption[]; cloneSetTotal: number; minCloneSetTotal: number; maxCloneSetTotal: number; selectedCloneDetailModules: string[]; cloneDetailModules: CloneDetailModule[]; cloneModelPanelTab: CloneModelPanelTab; tryOnScenes: string[]; selectedCloneModelScenes: string[]; cloneModelCustomScene: string; cloneModelSelects: CloneModelSelectItem[]; openCloneModelSelect: CloneModelSelectKey | null; cloneModelSelectDropUp: boolean; cloneVideoQuality: CloneVideoQualityKey; cloneVideoQualityOptions: CloneVideoQualityOption[]; cloneVideoDuration: number; cloneVideoDurationMin: number; cloneVideoDurationMax: number; cloneVideoDurationStyle: CSSProperties; cloneVideoSmart: boolean; canGenerate: boolean; status: string; lastFailedActionRef: MutableRefObject<(() => void) | null>; setIsProductUploadDragging: (value: boolean) => void; handleProductDrop: (event: DragEvent) => void; removeProductImage: (id: string) => void; handleProductUpload: (event: ChangeEvent) => void; handleCloneOutputChange: (value: CloneOutputKey) => void; setOpenCloneBasicSelect: (value: CloneBasicSelectKey | null) => void; setCloneReferenceMode: (value: CloneReferenceMode) => void; handleCloneReferenceUpload: (event: ChangeEvent) => void; isCloneReferenceDragging: boolean; handleCloneReferenceDragOver: (event: DragEvent) => void; handleCloneReferenceDragLeave: (event: DragEvent) => void; handleCloneReferenceDrop: (event: DragEvent) => void; setCloneReplicateLevel: (value: CloneReplicateLevelKey) => void; startCloneSetCountHold: (key: CloneSetCountKey, delta: -1 | 1, disabled: boolean) => void; clearCloneSetCountHold: () => void; toggleCloneDetailModule: (id: string) => void; setCloneModelPanelTab: (value: CloneModelPanelTab) => void; toggleCloneModelScene: (scene: string) => void; setCloneModelCustomScene: (value: string) => void; setOpenCloneModelSelect: (value: CloneModelSelectKey | null) => void; setCloneModelSelectDropUp: (value: boolean) => void; setCloneVideoQuality: (value: CloneVideoQualityKey) => void; setCloneVideoDuration: (value: number) => void; clampCloneVideoDuration: (value: number) => number; setCloneVideoSmart: (updater: (current: boolean) => boolean) => void; handleGenerate: () => void; onCancelGenerate: () => void; formatRatioDisplayValue: (value: string) => string; setVideoOutfitFiles?: (video: File | null, ref: File | null) => void; onStartVideoPlan?: () => void; } export default function EcommerceClonePanel({ productInputRef, cloneReferenceInputRef, productImages, isProductUploadDragging, cloneOutput, cloneOutputOptions, cloneBasicSelects, openCloneBasicSelect, cloneReferenceMode, cloneReferenceImages, maxCloneReferenceImages, cloneReplicateLevel, cloneReplicateLevelOptions, cloneSetCounts, cloneSetCountOptions, cloneSetTotal, minCloneSetTotal, maxCloneSetTotal, selectedCloneDetailModules, cloneDetailModules, cloneModelPanelTab, tryOnScenes, selectedCloneModelScenes, cloneModelCustomScene, cloneModelSelects, openCloneModelSelect, cloneModelSelectDropUp, cloneVideoQuality, cloneVideoQualityOptions, cloneVideoDuration, cloneVideoDurationMin, cloneVideoDurationMax, cloneVideoDurationStyle, cloneVideoSmart, canGenerate, status, lastFailedActionRef, setIsProductUploadDragging, handleProductDrop, removeProductImage, handleProductUpload, handleCloneOutputChange, setOpenCloneBasicSelect, setCloneReferenceMode, handleCloneReferenceUpload, isCloneReferenceDragging, handleCloneReferenceDragOver, handleCloneReferenceDragLeave, handleCloneReferenceDrop, setCloneReplicateLevel, startCloneSetCountHold, clearCloneSetCountHold, toggleCloneDetailModule, setCloneModelPanelTab, toggleCloneModelScene, setCloneModelCustomScene, setOpenCloneModelSelect, setCloneModelSelectDropUp, setCloneVideoQuality, setCloneVideoDuration, clampCloneVideoDuration, setCloneVideoSmart, handleGenerate, onCancelGenerate, formatRatioDisplayValue, setVideoOutfitFiles, onStartVideoPlan, }: EcommerceClonePanelProps) { const videoOutfitVideoRef = useRef(null); const videoOutfitRefRef = useRef(null); const [videoOutfitVideoUrl, setVideoOutfitVideoUrl] = useState(null); const [videoOutfitRefUrl, setVideoOutfitRefUrl] = useState(null); const [zoomImage, setZoomImage] = useState<{ src: string; x: number; y: number } | null>(null); const handleFileMouseEnter = (src: string, event: React.MouseEvent) => { const rect = event.currentTarget.getBoundingClientRect(); setZoomImage({ src, x: rect.left + rect.width / 2, y: rect.top }); }; const handleFileMouseLeave = () => setZoomImage(null); const handleVideoOutfitVideoChange = () => { const file = videoOutfitVideoRef.current?.files?.[0] || null; if (file) setVideoOutfitVideoUrl(URL.createObjectURL(file)); setVideoOutfitFiles?.(file, videoOutfitRefRef.current?.files?.[0] || null); }; const handleVideoOutfitRefChange = () => { const file = videoOutfitRefRef.current?.files?.[0] || null; if (file) setVideoOutfitRefUrl(URL.createObjectURL(file)); setVideoOutfitFiles?.(videoOutfitVideoRef.current?.files?.[0] || null, file); }; return ( <>
AI 电商生成

上传商品原图

productInputRef.current?.click()} onKeyDown={(event) => { if (event.target !== event.currentTarget) return; if (event.key === "Enter" || event.key === " ") { event.preventDefault(); productInputRef.current?.click(); } }} onDragEnter={(event) => { event.preventDefault(); setIsProductUploadDragging(true); }} onDragOver={(event) => event.preventDefault()} onDragLeave={() => setIsProductUploadDragging(false)} onDrop={handleProductDrop} >
拖拽或点击上传 上传图片 同一产品,最多 7 张
{productImages.length ? (
{productImages.map((item) => (
{item.name}
))}
) : null}

生成设置

生成内容
{cloneOutputOptions.map((option) => ( ))}
基础设置
{cloneBasicSelects.map((item) => { const hasMultipleOptions = item.options.length > 1; const isOpen = hasMultipleOptions && openCloneBasicSelect === item.key; return (