gongxiangp2/components/upload/StandardModelSkuZipUpload.tsx

153 lines
5.2 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 { useState } from 'react'
import { FileArchive, Plus } from 'lucide-react'
import {
hasAllowedFileExtension,
hasDragLeftDropZone,
prepareFileDrag,
} from './drag-drop-utils'
import { dropZoneActiveClass, dropZoneIdleClass, fieldHintClass, fieldLabelClass } from './upload-field-styles'
interface StandardModelSkuZipUploadProps {
selectedFile: File | null
existingFileName?: string
existingFileSize?: string | number
uploading?: boolean
error?: string
onSelectFile: (file: File) => void
onRemoveFile: () => void
}
function formatFileSize(bytes: string | number | undefined): string {
const size = typeof bytes === 'string' ? parseInt(bytes, 10) : Number(bytes || 0)
if (!size || Number.isNaN(size)) {
return '未知'
}
const units = ['B', 'KB', 'MB', 'GB']
const index = Math.min(Math.floor(Math.log(size) / Math.log(1024)), units.length - 1)
return `${(size / Math.pow(1024, index)).toFixed(2)} ${units[index]}`
}
function isSupportedZipFile(file: File): boolean {
return hasAllowedFileExtension(file, ['zip', 'rar', '7z'])
}
export default function StandardModelSkuZipUpload({
selectedFile,
existingFileName = '',
existingFileSize,
uploading = false,
error,
onSelectFile,
onRemoveFile,
}: StandardModelSkuZipUploadProps) {
const [isDragging, setIsDragging] = useState(false)
const [validationError, setValidationError] = useState('')
const hasExistingFile = !!existingFileName.trim()
const handleDrop = (event: React.DragEvent<HTMLLabelElement>) => {
prepareFileDrag(event, uploading)
setIsDragging(false)
if (uploading) {
setValidationError('文件正在上传,请等待完成后再更换')
return
}
const file = event.dataTransfer.files?.[0]
if (!file || !isSupportedZipFile(file)) {
setValidationError('压缩包仅支持 ZIP、RAR、7Z 格式')
return
}
setValidationError('')
onSelectFile(file)
}
return (
<div>
<label htmlFor="standard-model-sku-zip-upload" className={fieldLabelClass}>模型压缩包 *</label>
{(error || validationError) && (
<div className="mb-2 rounded-[8px] border border-[#fecdca] bg-[#fff1f0] px-3 py-2 text-sm text-[#b42318]">
{error || validationError}
</div>
)}
<div>
<input
id="standard-model-sku-zip-upload"
type="file"
accept=".zip,.rar,.7z"
className="hidden"
disabled={uploading}
onChange={(event) => {
const file = event.target.files?.[0]
if (file && isSupportedZipFile(file)) {
setValidationError('')
onSelectFile(file)
} else if (file) {
setValidationError('压缩包仅支持 ZIP、RAR、7Z 格式')
}
event.currentTarget.value = ''
}}
/>
{/* biome-ignore lint/a11y/noStaticElementInteractions: 上传槽位需要拖拽和点击交互 */}
<label
htmlFor="standard-model-sku-zip-upload"
aria-disabled={uploading}
className={`group block p-3 ${
uploading ? 'cursor-not-allowed opacity-60' : 'cursor-pointer'
} ${isDragging ? dropZoneActiveClass : dropZoneIdleClass}`}
onDragEnter={(event) => {
prepareFileDrag(event, uploading)
if (uploading) return
setIsDragging(true)
}}
onDragLeave={(event) => {
event.preventDefault()
event.stopPropagation()
if (hasDragLeftDropZone(event)) {
setIsDragging(false)
}
}}
onDragOver={(event) => prepareFileDrag(event, uploading)}
onDrop={handleDrop}
>
{selectedFile ? (
<div className="flex min-h-12 items-center gap-3">
<FileArchive className="h-5 w-5 shrink-0 text-[#6b7075]" aria-hidden="true" />
<span className="min-w-0 flex-1 truncate text-sm font-medium text-[#1a1c1d]">{selectedFile.name}</span>
<span className={fieldHintClass}>{formatFileSize(selectedFile.size)}</span>
<button
type="button"
aria-label="移除已选择的模型压缩包"
disabled={uploading}
className="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-[8px] text-[#b42318] hover:bg-[#fff1f0] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#1677ff]"
onClick={(event) => {
event.preventDefault()
event.stopPropagation()
onRemoveFile()
}}
>
×
</button>
</div>
) : (
<div className="flex min-h-12 items-center gap-3">
<Plus className="h-5 w-5 text-[#6b7075]" aria-hidden="true" />
<span className="text-sm font-medium text-[#1a1c1d]">选择模型压缩包,或拖入文件</span>
</div>
)}
</label>
</div>
<p className={`mt-1.5 ${fieldHintClass}`}>
ZIP、RAR、7Z{hasExistingFile && !selectedFile ? ` · 当前模型:${existingFileName}(${formatFileSize(existingFileSize)})` : ''}
</p>
</div>
)
}