/** * 特殊批量上传UI组件 * 包含音乐、视频、3D场景、品牌VI、买家秀、小红书的批量上传界面 */ 'use client' import Image from 'next/image' import { formatBytes } from '@/components/upload/upload-utils' import { getSpuProductModelValidationError, isPlausibleSpuProductModel } from '@/lib/product-model' import VideoPreviewImageUpload from './VideoPreviewImageUpload' /** * 音乐/视频/品牌VI 批量文件项 */ export interface BatchFileWithSpu { file: File productModel: string } /** * 3D场景批量文件项(带预览图) */ export interface Batch3DFile { file: File productModel: string previewImage?: File } /** * 买家秀/小红书批量文件项(多图) */ export interface BatchImageGroup { productModel: string images: File[] } // ========== 音乐批量上传列表 ========== interface BatchMusicListProps { files: BatchFileWithSpu[] onUpdateProductModel: (index: number, value: string) => void onDelete: (index: number) => void } export function BatchMusicList({ files, onUpdateProductModel, onDelete }: BatchMusicListProps) { if (files.length === 0) return null return (
{files.map((item, index) => (

{item.file.name}

{formatBytes(item.file.size)}

onUpdateProductModel(index, e.target.value)} className="flex-1 px-3 py-1.5 text-sm rounded-apple border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-white" placeholder="SPU型号" />
))}
) } // ========== 视频批量上传列表 ========== export interface BatchVideoFile extends BatchFileWithSpu { productCategoryName?: string previewImage?: File previewImageUrl?: string } interface BatchVideoListProps { files: BatchVideoFile[] productCategories: Array<{ id: string; name: string }> onUpdateProductModel: (index: number, value: string) => void onUpdateProductCategory?: (index: number, value: string) => void onUpdatePreviewImage: (index: number, file: File) => void | Promise onRemovePreviewImage: (index: number) => void onValidationError?: (message: string) => void onPreviewProcessingChange?: (processing: boolean) => void disabled?: boolean onDelete: (index: number) => void } export function BatchVideoList({ files, productCategories, onUpdateProductModel, onUpdateProductCategory, onUpdatePreviewImage, onRemovePreviewImage, onValidationError = () => undefined, onPreviewProcessingChange, disabled = false, onDelete }: BatchVideoListProps) { if (files.length === 0) return null return (
{files.map((item, index) => (

{item.file.name}

{formatBytes(item.file.size)}

onUpdateProductModel(index, e.target.value)} disabled={disabled} className={`w-full rounded-apple border px-3 py-1.5 text-sm outline-none disabled:cursor-not-allowed disabled:opacity-60 dark:bg-gray-700 dark:text-white ${ !isPlausibleSpuProductModel(item.productModel) ? 'border-red-400 focus:border-red-500 focus:ring-2 focus:ring-red-100 dark:border-red-600 dark:focus:ring-red-900' : 'border-gray-300 focus:border-gray-900 focus:ring-2 focus:ring-gray-200 dark:border-gray-600 dark:focus:border-gray-400 dark:focus:ring-gray-600' }`} placeholder="SPU型号" /> {!isPlausibleSpuProductModel(item.productModel) && ( )}
{/* 品类字段 */} {productCategories.length > 0 && onUpdateProductCategory && (
)} onUpdatePreviewImage(index, file)} onRemove={() => onRemovePreviewImage(index)} onValidationError={onValidationError} onProcessingChange={onPreviewProcessingChange} />
))}
) } // ========== 3D场景批量上传列表 ========== interface Batch3DListProps { files: Batch3DFile[] onUpdateProductModel: (index: number, value: string) => void onUpdatePreviewImage: (index: number, file: File) => void onDelete: (index: number) => void } export function Batch3DList({ files, onUpdateProductModel, onUpdatePreviewImage, onDelete }: Batch3DListProps) { if (files.length === 0) return null return (
{files.map((item, index) => (

{item.file.name}

{formatBytes(item.file.size)}

onUpdateProductModel(index, e.target.value)} className="flex-1 px-3 py-1.5 text-sm rounded-apple border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-white" placeholder="SPU型号" /> { const file = e.target.files?.[0] if (file) { onUpdatePreviewImage(index, file) } }} className="hidden" id={`3d-preview-${index}`} />
))}
) } // ========== 品牌VI批量上传列表 ========== interface BatchBrandVIListProps { files: BatchFileWithSpu[] onUpdateProductModel: (index: number, value: string) => void onDelete: (index: number) => void } export function BatchBrandVIList({ files, onUpdateProductModel, onDelete }: BatchBrandVIListProps) { if (files.length === 0) return null return (
{files.map((item, index) => (

{item.file.name}

{formatBytes(item.file.size)}

onUpdateProductModel(index, e.target.value)} className="flex-1 px-3 py-1.5 text-sm rounded-apple border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-white" placeholder="SPU型号" />
))}
) } // ========== 买家秀批量上传UI ========== interface BatchBuyerShowUIProps { batches: BatchImageGroup[] onUpdateProductModel: (batchIndex: number, value: string) => void onImageSelect: (e: React.ChangeEvent, batchIndex: number) => void onRemoveImage: (batchIndex: number, imageIndex: number) => void onDeleteBatch: (batchIndex: number) => void onAddBatch: () => void } export function BatchBuyerShowUI({ batches, onUpdateProductModel, onImageSelect, onRemoveImage, onDeleteBatch, onAddBatch, }: BatchBuyerShowUIProps) { return (
{batches.map((batch, batchIndex) => (
onUpdateProductModel(batchIndex, e.target.value)} className="flex-1 px-3 py-2 text-sm rounded-apple border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-white" placeholder="SPU型号 *" /> onImageSelect(e, batchIndex)} className="hidden" id={`buyer-show-images-${batchIndex}`} />
{batch.images.length > 0 && (
{batch.images.map((img, imgIndex) => (
{`图片
))}
)} {batch.images.length > 0 && batch.images.length < 8 && (

每批次至少需要8张图片(当前:{batch.images.length}张)

)}
))}
) } // ========== 小红书批量上传UI ========== interface BatchXiaohongshuUIProps { batches: BatchImageGroup[] onUpdateProductModel: (batchIndex: number, value: string) => void onImageSelect: (e: React.ChangeEvent, batchIndex: number) => void onRemoveImage: (batchIndex: number, imageIndex: number) => void onDeleteBatch: (batchIndex: number) => void onAddBatch: () => void } export function BatchXiaohongshuUI({ batches, onUpdateProductModel, onImageSelect, onRemoveImage, onDeleteBatch, onAddBatch, }: BatchXiaohongshuUIProps) { return (
{batches.map((batch, batchIndex) => (
onUpdateProductModel(batchIndex, e.target.value)} className="flex-1 px-3 py-2 text-sm rounded-apple border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-white" placeholder="SPU型号 *" /> onImageSelect(e, batchIndex)} className="hidden" id={`xiaohongshu-images-${batchIndex}`} />
{batch.images.length > 0 && (
{batch.images.map((img, imgIndex) => (
{`图片
))}
)}
))}
) }