'use client' import { useMemo, useState } from 'react' import Image from 'next/image' import { STANDARD_MODEL_WHITE_BG_VIEWS, type StandardModelWhiteBgKey, } from '@/lib/standard-model-white-background' interface OtherImagePreview { id: string url: string name: string } interface StandardModelWhiteBackgroundUploadProps { imageUrls: Partial> otherImages: OtherImagePreview[] uploading?: boolean error?: string onSelectImage: (key: StandardModelWhiteBgKey, file: File) => void onRemoveImage: (key: StandardModelWhiteBgKey) => void onSelectOtherImages: (files: FileList | File[]) => void onRemoveOtherImage: (id: string) => void } export default function StandardModelWhiteBackgroundUpload({ imageUrls, otherImages, uploading = false, error, onSelectImage, onRemoveImage, onSelectOtherImages, onRemoveOtherImage, }: StandardModelWhiteBackgroundUploadProps) { const [draggingKey, setDraggingKey] = useState(null) const [isOtherDragging, setIsOtherDragging] = useState(false) const uploadedRequiredCount = useMemo( () => STANDARD_MODEL_WHITE_BG_VIEWS.filter((view) => !!imageUrls[view.key]).length, [imageUrls], ) const missingLabels = useMemo( () => STANDARD_MODEL_WHITE_BG_VIEWS .filter((view) => !imageUrls[view.key]) .map((view) => view.label), [imageUrls], ) const handleDragEnter = (event: React.DragEvent, key: StandardModelWhiteBgKey) => { event.preventDefault() event.stopPropagation() setDraggingKey(key) } const handleDragLeave = (event: React.DragEvent, key: StandardModelWhiteBgKey) => { event.preventDefault() event.stopPropagation() const rect = (event.currentTarget as HTMLElement).getBoundingClientRect() const { clientX, clientY } = event if (clientX <= rect.left || clientX >= rect.right || clientY <= rect.top || clientY >= rect.bottom) { if (draggingKey === key) { setDraggingKey(null) } } } const handleDragOver = (event: React.DragEvent) => { event.preventDefault() event.stopPropagation() event.dataTransfer.dropEffect = 'copy' } const handleDrop = (event: React.DragEvent, key: StandardModelWhiteBgKey) => { event.preventDefault() event.stopPropagation() setDraggingKey(null) const file = event.dataTransfer.files?.[0] if (!file) { return } onSelectImage(key, file) } const handleOtherDrop = (event: React.DragEvent) => { event.preventDefault() event.stopPropagation() setIsOtherDragging(false) const files = event.dataTransfer.files if (!files || files.length === 0) { return } onSelectOtherImages(files) } return (
固定角度 {uploadedRequiredCount} / {STANDARD_MODEL_WHITE_BG_VIEWS.length} 可分次补传 {otherImages.length > 0 && ( 其他图片 {otherImages.length} 张 )} {uploading && ( 上传处理中 )}
{missingLabels.length > 0 ? `未上传:${missingLabels.join('、')}(可先保存,后续再补)` : '8 张固定角度已齐,可直接保存。'}
{error && (
{error}
)}

固定角度

可选,建议补齐 8 张
{STANDARD_MODEL_WHITE_BG_VIEWS.map((view) => { const inputId = `white-bg-${view.key}` const imageUrl = imageUrls[view.key] const isDragging = draggingKey === view.key return (

{view.label}

可选
{ const file = event.target.files?.[0] if (file) { onSelectImage(view.key, file) } event.currentTarget.value = '' }} /> {/* biome-ignore lint/a11y/noStaticElementInteractions: 上传槽位需要拖拽和点击交互 */}
) })}

其他图片

可选,多张
{ if (event.target.files && event.target.files.length > 0) { onSelectOtherImages(event.target.files) } event.currentTarget.value = '' }} /> {/* biome-ignore lint/a11y/noStaticElementInteractions: 其他图片区需要拖拽上传 */}
) }