import React, { useRef, useState } from "react"; import { Plus, X } from "lucide-react"; import { toast } from "sonner"; import { cn } from "./utils"; import { PICTURE_UPLOAD_MAX_BYTES, resolvePictureUrlForDisplay, uploadImageFile } from "../../services/imageUploadService"; export type ImageUrlUploadProps = { value: string; onChange: (url: string) => void; disabled?: boolean; /** 辅助说明,显示在方框下方 */ hint?: string; /** 空状态主文案 */ emptyLabel?: string; accept?: string; /** 默认 5MB,与平台 picture 上传接口一致 */ maxSizeMb?: number; className?: string; /** 上传区域宽度(tailwind),默认 max-w-[200px] */ boxClassName?: string; /** 传给后端的 multipart `subDir`(如 category、product) */ uploadSubDir?: string; /** 明确单图:隐藏多选、提示文案 */ oneImageOnly?: boolean; }; export function ImageUrlUpload({ value, onChange, disabled, hint, emptyLabel = "Click to upload image", accept = "image/jpeg,image/png,image/webp,image/gif", maxSizeMb = PICTURE_UPLOAD_MAX_BYTES / (1024 * 1024), className, boxClassName, uploadSubDir, oneImageOnly, }: ImageUrlUploadProps) { const inputRef = useRef(null); const [uploading, setUploading] = useState(false); const onFileChange = async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; e.target.value = ""; if (!file) return; if (!file.type.startsWith("image/")) { toast.error("Please select an image file."); return; } if (file.size > maxSizeMb * 1024 * 1024) { toast.error(`Image must be ${maxSizeMb} MB or smaller.`); return; } setUploading(true); try { const url = await uploadImageFile(file, { subDir: uploadSubDir }); onChange(url); toast.success("Image uploaded."); } catch (err: unknown) { const msg = err instanceof Error ? err.message : String(err); toast.error("Upload failed", { description: msg || undefined }); } finally { setUploading(false); } }; const busy = disabled || uploading; const openPicker = () => { if (!busy) inputRef.current?.click(); }; const boxBase = "w-full max-w-[200px] aspect-square rounded-md transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"; return (
{!value ? ( ) : (
)} {oneImageOnly ? (

One image only. Replace or clear to change.

) : null} {hint ?

{hint}

: null}
); }