修复详情替换产品图上传预览
This commit is contained in:
parent
35a1b75df9
commit
049f01059f
@ -92,6 +92,7 @@ export function SceneFusionView({ onBack, initialProductImage, initialProductNam
|
||||
const [productName, setProductName] = useState<string>(initialProductName || ""); // 产品名称(如"沙发")
|
||||
const [scenes, setScenes] = useState<SceneItem[]>([]);
|
||||
const [isProcessing, setIsProcessing] = useState(false);
|
||||
const [isProductUploading, setIsProductUploading] = useState(false);
|
||||
const [aspectRatio, setAspectRatio] = useState<string>("auto"); // 图片比例
|
||||
const [imageSize, setImageSize] = useState<string>("2K"); // 图片分辨率
|
||||
|
||||
@ -99,6 +100,7 @@ export function SceneFusionView({ onBack, initialProductImage, initialProductNam
|
||||
const productInputRef = useRef<HTMLInputElement>(null);
|
||||
const sceneInputRef = useRef<HTMLInputElement>(null);
|
||||
const productPreviewObjectUrlRef = useRef<string | null>(null);
|
||||
const productUploadIdRef = useRef(0);
|
||||
|
||||
// 如果外部传入了初始产品图,则在首次挂载或传入值变化时自动带入
|
||||
const updateProductPreview = (previewUrl: string | null, isObjectUrl = false) => {
|
||||
@ -115,6 +117,14 @@ export function SceneFusionView({ onBack, initialProductImage, initialProductNam
|
||||
const clearProductImage = () => {
|
||||
setProductImage(null);
|
||||
updateProductPreview(null);
|
||||
setIsProductUploading(false);
|
||||
productUploadIdRef.current += 1;
|
||||
};
|
||||
|
||||
const handleProductPreviewError = () => {
|
||||
if (productImage && productImagePreview !== productImage) {
|
||||
updateProductPreview(productImage);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@ -134,8 +144,36 @@ export function SceneFusionView({ onBack, initialProductImage, initialProductNam
|
||||
}, []);
|
||||
|
||||
// 处理产品图上传
|
||||
const handleProductUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
const uploadProductImageFile = async (file: File): Promise<string> => {
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
const response = await fetch("/api/files/upload", {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
let errorMessage = "上传失败";
|
||||
try {
|
||||
const errorData = await response.json();
|
||||
errorMessage = errorData?.error || errorData?.message || errorMessage;
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
if (!result?.url) {
|
||||
throw new Error("上传失败");
|
||||
}
|
||||
|
||||
return result.url as string;
|
||||
};
|
||||
|
||||
const handleProductUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const input = e.target;
|
||||
const file = input.files?.[0];
|
||||
if (!file) return;
|
||||
|
||||
if (file.size > 10 * 1024 * 1024) {
|
||||
@ -146,9 +184,12 @@ export function SceneFusionView({ onBack, initialProductImage, initialProductNam
|
||||
setProductImage(null);
|
||||
const previewUrl = URL.createObjectURL(file);
|
||||
updateProductPreview(previewUrl, true);
|
||||
setIsProductUploading(true);
|
||||
const uploadId = ++productUploadIdRef.current;
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
if (uploadId !== productUploadIdRef.current) return;
|
||||
const result = e.target?.result;
|
||||
if (typeof result === "string") {
|
||||
setProductImage(result);
|
||||
@ -158,12 +199,29 @@ export function SceneFusionView({ onBack, initialProductImage, initialProductNam
|
||||
}
|
||||
};
|
||||
reader.onerror = () => {
|
||||
if (uploadId !== productUploadIdRef.current) return;
|
||||
toast.error("图片读取失败,请重试");
|
||||
clearProductImage();
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
|
||||
try {
|
||||
toast.info("正在上传产品图...");
|
||||
const uploadedUrl = await uploadProductImageFile(file);
|
||||
if (uploadId === productUploadIdRef.current) {
|
||||
setProductImage(uploadedUrl);
|
||||
updateProductPreview(uploadedUrl);
|
||||
toast.success("产品图上传成功");
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error(error instanceof Error ? error.message : "产品图上传失败");
|
||||
} finally {
|
||||
if (uploadId === productUploadIdRef.current) {
|
||||
setIsProductUploading(false);
|
||||
}
|
||||
}
|
||||
// 重置 input,允许重复选择同一文件
|
||||
e.target.value = "";
|
||||
input.value = "";
|
||||
};
|
||||
|
||||
// 处理场景图上传
|
||||
@ -632,11 +690,19 @@ export function SceneFusionView({ onBack, initialProductImage, initialProductNam
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="text-base font-medium">产品图 A (必须)</Label>
|
||||
{(productImagePreview || productImage) && (
|
||||
<Button variant="ghost" size="sm" onClick={clearProductImage} className="h-6 text-xs text-muted-foreground hover:text-destructive">
|
||||
清除
|
||||
</Button>
|
||||
)}
|
||||
<div className="flex items-center gap-2">
|
||||
{isProductUploading && (
|
||||
<span className="flex items-center gap-1 text-xs text-muted-foreground">
|
||||
<Loader2 className="h-3 w-3 animate-spin" />
|
||||
上传中...
|
||||
</span>
|
||||
)}
|
||||
{(productImagePreview || productImage) && (
|
||||
<Button variant="ghost" size="sm" onClick={clearProductImage} className="h-6 text-xs text-muted-foreground hover:text-destructive">
|
||||
清除
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
@ -656,7 +722,12 @@ export function SceneFusionView({ onBack, initialProductImage, initialProductNam
|
||||
|
||||
{(productImagePreview || productImage) ? (
|
||||
<div className="relative aspect-square w-full max-w-[200px] mx-auto">
|
||||
<img src={productImagePreview || productImage || ""} alt="产品图" className="w-full h-full object-contain rounded-md" />
|
||||
<img
|
||||
src={productImagePreview || productImage || ""}
|
||||
alt="产品图"
|
||||
className="w-full h-full object-contain rounded-md"
|
||||
onError={handleProductPreviewError}
|
||||
/>
|
||||
<div className="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center rounded-md">
|
||||
<Button variant="secondary" size="sm" onClick={(e) => { e.stopPropagation(); productInputRef.current?.click(); }}>
|
||||
更换图片
|
||||
@ -823,7 +894,7 @@ export function SceneFusionView({ onBack, initialProductImage, initialProductNam
|
||||
className="w-full"
|
||||
size="lg"
|
||||
onClick={startFusion}
|
||||
disabled={isProcessing || !productImage || scenes.length === 0}
|
||||
disabled={isProcessing || isProductUploading || !productImage || scenes.length === 0}
|
||||
>
|
||||
{isProcessing ? (
|
||||
<>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user