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) }} /> setView('grid')}> setView('list')}> {selected.size > 0 && setDeleteOpen(true)}>删除({selected.size})} fileRef.current?.click()} disabled={uploading} className="h-10 gap-2 shadow-sm"> {uploading ? : }上传 {loading ? : view === 'grid' ? ( {files.map(f => ( toggleSelect(f.id)} /> isImage(f.suffix) && setPreviewUrl(f.url)}> {isImage(f.suffix) ? : 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} 条 setPage(p => p - 1)}>上一页 {page}/{totalPages} = totalPages} onClick={() => setPage(p => p + 1)}>下一页 )} 确认删除确定要删除选中的 {selected.size} 个文件吗? setDeleteOpen(false)}>取消删除 setPreviewUrl('')}> ) }
{f.name}
{f.suffix?.toUpperCase()}
共 {total} 条