Files
codexautopool/frontend/src/components/upload/FileDropzone.tsx

115 lines
3.4 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.
import { useCallback, useState } from 'react'
import { Upload, FileJson, AlertCircle } from 'lucide-react'
interface FileDropzoneProps {
onFileSelect: (file: File) => void
disabled?: boolean
error?: string | null
}
export default function FileDropzone({ onFileSelect, disabled = false, error }: FileDropzoneProps) {
const [isDragging, setIsDragging] = useState(false)
const handleDragOver = useCallback(
(e: React.DragEvent) => {
e.preventDefault()
if (!disabled) {
setIsDragging(true)
}
},
[disabled]
)
const handleDragLeave = useCallback((e: React.DragEvent) => {
e.preventDefault()
setIsDragging(false)
}, [])
const handleDrop = useCallback(
(e: React.DragEvent) => {
e.preventDefault()
setIsDragging(false)
if (disabled) return
const files = e.dataTransfer.files
if (files.length > 0) {
const file = files[0]
if (file.type === 'application/json' || file.name.endsWith('.json')) {
onFileSelect(file)
}
}
},
[disabled, onFileSelect]
)
const handleFileInput = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const files = e.target.files
if (files && files.length > 0) {
onFileSelect(files[0])
}
// Reset input value to allow selecting the same file again
e.target.value = ''
},
[onFileSelect]
)
return (
<div className="space-y-2">
<div
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
className={`relative border-2 border-dashed rounded-xl p-5 sm:p-8 text-center transition-colors ${
disabled
? 'border-slate-200 dark:border-slate-700 bg-slate-50 dark:bg-slate-800/50 cursor-not-allowed'
: isDragging
? 'border-blue-500 bg-blue-50 dark:bg-blue-900/20'
: 'border-slate-300 dark:border-slate-600 hover:border-blue-400 dark:hover:border-blue-500 cursor-pointer'
}`}
>
<input
type="file"
accept=".json,application/json"
onChange={handleFileInput}
disabled={disabled}
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer disabled:cursor-not-allowed"
/>
<div className="flex flex-col items-center gap-3">
<div
className={`p-4 rounded-full ${
isDragging ? 'bg-blue-100 dark:bg-blue-900/30' : 'bg-slate-100 dark:bg-slate-700'
}`}
>
{isDragging ? (
<Upload className="h-8 w-8 text-blue-600 dark:text-blue-400" />
) : (
<FileJson className="h-8 w-8 text-slate-400 dark:text-slate-500" />
)}
</div>
<div>
<p className="text-sm font-medium text-slate-700 dark:text-slate-300">
{isDragging ? '释放文件以上传' : '拖拽 JSON 文件到此处'}
</p>
<p className="mt-1 text-xs text-slate-500 dark:text-slate-400"></p>
</div>
<div className="text-xs text-slate-400 dark:text-slate-500">
支持格式: JSON //JSONL
</div>
</div>
</div>
{error && (
<div className="flex items-center gap-2 text-sm text-red-600 dark:text-red-400">
<AlertCircle className="h-4 w-4" />
<span>{error}</span>
</div>
)}
</div>
)
}