gongxiangp2/components/upload/ModelImagesUpload.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

79 lines
2.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'
interface ModelImagesUploadProps {
modelImageUrls: string[]
onModelImagesChange: (e: React.ChangeEvent<HTMLInputElement>) => void
onRemoveModelImage: (index: number) => void
}
export default function ModelImagesUpload({
modelImageUrls,
onModelImagesChange,
onRemoveModelImage,
}: ModelImagesUploadProps) {
return (
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
模特展示图片 * (可上传多张)
</label>
<div className="border-2 border-dashed rounded-apple p-4 border-gray-300 dark:border-gray-600">
<input
type="file"
onChange={onModelImagesChange}
accept="image/*"
multiple
className="hidden"
id="modelImagesInput"
/>
<div className="grid grid-cols-4 gap-4">
{modelImageUrls.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={() => onRemoveModelImage(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={() => document.getElementById('modelImagesInput')?.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">
可一次选择多张图片上传
</p>
<p className="text-xs text-blue-600 dark:text-blue-400">
💡 第一张图会自动作为预览图,高度超过1500px会自动切割
</p>
</div>
</div>
</div>
)
}