gongxiangp2/components/upload/PreviewImageUpload.tsx
chenpei 4e89b57798 feat(upload): optimize upload UI and performance
- Expand batch upload list area and optimize preview image layout
- Improve upload speed tracking and display (server-side & client-side)
- Reduce network detection log noise
- Optimize Docker build process (caching, env validation)
- Add shadow environment deployment support
2026-01-28 15:07:50 +08:00

110 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client'
import Image from 'next/image'
import { PREVIEW_OPTIONAL_TYPES } from './upload-constants'
interface PreviewImageUploadProps {
materialType: string
previewImages: File[]
previewUrls: string[]
isPreviewDragging: boolean
previewInputRef: React.RefObject<HTMLInputElement>
previewDropZoneRef: React.RefObject<HTMLDivElement>
onPreviewChange: (e: React.ChangeEvent<HTMLInputElement>) => void
onPreviewDragEnter: (e: React.DragEvent) => void
onPreviewDragLeave: (e: React.DragEvent) => void
onPreviewDragOver: (e: React.DragEvent) => void
onPreviewDrop: (e: React.DragEvent) => void
onRemovePreviewImage: (index: number) => void
}
export default function PreviewImageUpload({
materialType,
previewImages,
previewUrls,
isPreviewDragging,
previewInputRef,
previewDropZoneRef,
onPreviewChange,
onPreviewDragEnter,
onPreviewDragLeave,
onPreviewDragOver,
onPreviewDrop,
onRemovePreviewImage,
}: PreviewImageUploadProps) {
return (
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
预览图片
{PREVIEW_OPTIONAL_TYPES.includes(materialType) ? '(可选)' : ' *'}
</label>
<div
ref={previewDropZoneRef}
className={`border-2 border-dashed rounded-apple p-4 transition-colors ${
isPreviewDragging
? 'border-blue-500 bg-blue-50 dark:bg-blue-900/20'
: 'border-gray-300 dark:border-gray-600'
}`}
onDragEnter={onPreviewDragEnter}
onDragLeave={onPreviewDragLeave}
onDragOver={onPreviewDragOver}
onDrop={onPreviewDrop}
>
<input
ref={previewInputRef}
type="file"
onChange={onPreviewChange}
accept="image/*"
multiple
className="hidden"
/>
<div className="grid grid-cols-4 gap-4">
{previewUrls.map((url, index) => (
<div key={index} className="relative group">
<div className="relative w-full h-32 rounded-apple overflow-hidden border-2 border-gray-200 dark:border-gray-600">
<Image
src={url}
alt={`预览图 ${index + 1}`}
fill
unoptimized
className="object-cover"
/>
{index === 0 && (
<div className="absolute top-1 left-1 bg-blue-500 text-white text-xs px-2 py-1 rounded">
主预览图
</div>
)}
</div>
<button
type="button"
onClick={() => onRemovePreviewImage(index)}
className="absolute -top-2 -right-2 bg-red-500 text-white rounded-full w-6 h-6 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity"
>
×
</button>
</div>
))}
<button
type="button"
onClick={() => previewInputRef.current?.click()}
className="group w-full h-32 rounded-apple bg-gray-100 dark:bg-gray-700 border-2 border-dashed border-gray-300 dark:border-gray-600 flex items-center justify-center hover:border-gray-400 dark:hover:border-gray-500 transition-colors"
>
<svg className="w-12 h-12 text-gray-400 group-hover:text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
</svg>
</button>
</div>
<div className="mt-3 space-y-1">
<p className="text-xs text-gray-500 dark:text-gray-400">
支持 JPG、PNG、WebP 等格式,可一次选择多张或拖拽上传
</p>
<p className="text-xs text-blue-600 dark:text-blue-400">
💡 第一张图会自动作为主预览图
</p>
</div>
</div>
</div>
)
}