import { useState, useEffect, useCallback, useRef } from 'react' import { Search, Trash2, Loader2, Upload, Grid, List, FileText, Image, Music, File } from 'lucide-react' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Badge } from '@/components/ui/badge' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Checkbox } from '@/components/ui/checkbox' import { getFileList, uploadFile, deleteFile } from '@/api/systemCrud' import type { SystemOss } from '@/api/system' import { cn } from '@/lib/utils' const getFileIcon = (suffix?: string) => { if (!suffix) return if (['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'].includes(suffix)) return if (['mp3', 'wav', 'ogg', 'flac'].includes(suffix)) return return } const isImage = (suffix?: string) => suffix && ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'].includes(suffix) export default function FilesPage() { const [files, setFiles] = useState([]) const [loading, setLoading] = useState(true) const [total, setTotal] = useState(0) const [page, setPage] = useState(1) const [search, setSearch] = useState('') const [view, setView] = useState<'grid' | 'list'>('grid') const [selected, setSelected] = useState>(new Set()) const [uploading, setUploading] = useState(false) const [deleteOpen, setDeleteOpen] = useState(false) const [previewUrl, setPreviewUrl] = useState('') const fileRef = useRef(null) const fetchList = useCallback(async () => { setLoading(true) try { const res = await getFileList({ current: page, pageSize: 20, keyword: search || undefined }); if (res?.data) { setFiles(res.data.list || []); setTotal(res.data.total || 0) } } catch (e) { console.error(e) } finally { setLoading(false) } }, [page, search]) useEffect(() => { fetchList() }, [fetchList]) const handleUpload = async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return setUploading(true) try { await uploadFile(file); fetchList() } catch (err) { console.error(err) } finally { setUploading(false); if (fileRef.current) fileRef.current.value = '' } } const toggleSelect = (id: string) => { setSelected(prev => { const next = new Set(prev); if (next.has(id)) next.delete(id); else next.add(id); return next }) } const handleDelete = async () => { try { await deleteFile(Array.from(selected)); setSelected(new Set()); setDeleteOpen(false); fetchList() } catch (e) { console.error(e) } } const totalPages = Math.ceil(total / 20) return (
文件管理 {total} 管理上传的文件和图片资源
{ setSearch(e.target.value); setPage(1) }} />
{selected.size > 0 && }
{loading ?
: view === 'grid' ? (
{files.map(f => (
toggleSelect(f.id)} />
isImage(f.suffix) && setPreviewUrl(f.url)}> {isImage(f.suffix) ? {f.name} : getFileIcon(f.suffix)}

{f.name}

{f.suffix?.toUpperCase()}

))}
) : (
文件名类型创建时间{files.map(f => ( toggleSelect(f.id)} /> {getFileIcon(f.suffix)}{f.name} {f.suffix?.toUpperCase()} {new Date(f.createdAt).toLocaleDateString()} ))}
)} {totalPages > 1 && (

共 {total} 条

{page}/{totalPages}
)}
确认删除确定要删除选中的 {selected.size} 个文件吗? setPreviewUrl('')}> preview
) }