import { useState, useEffect, useCallback } from 'react' import { materialsApi, subjectsApi, type MaterialFile, type MaterialQueryParams, type MaterialSubject, type MaterialCreateRequest, type MaterialUpdateRequest } from '@/api' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' 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 { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog' import { PaginationNavigator } from '@/components/ui/pagination' import { Label } from '@/components/ui/label' import { Textarea } from '@/components/ui/textarea' import { Loader2, Plus, Pencil, Trash2, Search, FileText, Download, Eye } from 'lucide-react' import { format } from 'date-fns' // 文件类型配置 const FILE_TYPE_CONFIG = { pdf: { icon: '📄', color: 'bg-red-100 text-red-800', label: 'PDF' }, word: { icon: '📝', color: 'bg-blue-100 text-blue-800', label: 'Word' }, ppt: { icon: '📊', color: 'bg-orange-100 text-orange-800', label: 'PPT' }, } as const // 格式化文件大小 const formatFileSize = (bytes: number) => { if (bytes === 0) return '0 B' const k = 1024 const sizes = ['B', 'KB', 'MB', 'GB'] const i = Math.floor(Math.log(bytes) / Math.log(k)) return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i] } export default function Materials() { const [materials, setMaterials] = useState([]) const [subjects, setSubjects] = useState([]) const [loading, setLoading] = useState(true) const [total, setTotal] = useState(0) const [page, setPage] = useState(1) const pageSize = 10 // 筛选 const [subjectFilter, setSubjectFilter] = useState('all') const [fileTypeFilter, setFileTypeFilter] = useState('all') const [statusFilter, setStatusFilter] = useState('all') const [keyword, setKeyword] = useState('') // 创建/编辑对话框 const [dialogOpen, setDialogOpen] = useState(false) const [editingMaterial, setEditingMaterial] = useState(null) const [formData, setFormData] = useState({ subject_id: '', title: '', description: '', file_type: 'pdf', file_size: 0, file_url: '', file_name: '', tags: [], }) const [dialogLoading, setDialogLoading] = useState(false) // 删除确认对话框 const [deleteOpen, setDeleteOpen] = useState(false) const [deletingMaterial, setDeletingMaterial] = useState(null) const [deleteLoading, setDeleteLoading] = useState(false) // 详情对话框 const [detailOpen, setDetailOpen] = useState(false) const [selectedMaterial, setSelectedMaterial] = useState(null) const [detailLoading, setDetailLoading] = useState(false) // 加载资料列表 const loadMaterials = useCallback(async () => { setLoading(true) try { const params: MaterialQueryParams = { page, page_size: pageSize, keyword: keyword || undefined, subject_id: subjectFilter !== 'all' ? subjectFilter : undefined, file_type: fileTypeFilter !== 'all' ? fileTypeFilter as any : undefined, status: statusFilter !== 'all' ? statusFilter : undefined, } const response = await materialsApi.getMaterials(params) const data = response.data.data setMaterials(data.list || []) setTotal(data.total || 0) } catch (error) { console.error('Failed to load materials:', error) setMaterials([]) setTotal(0) } finally { setLoading(false) } }, [page, pageSize, keyword, subjectFilter, fileTypeFilter, statusFilter]) // 加载学科列表(用于筛选和表单) const loadSubjects = useCallback(async () => { try { const response = await subjectsApi.getSubjects() setSubjects(response.data.data || []) } catch (error) { console.error('Failed to load subjects:', error) } }, []) useEffect(() => { loadSubjects() }, [loadSubjects]) useEffect(() => { loadMaterials() }, [loadMaterials]) // 搜索 const handleSearch = () => { setPage(1) loadMaterials() } // 打开创建对话框 const handleOpenCreate = () => { setEditingMaterial(null) setFormData({ subject_id: subjects[0]?.id || '', title: '', description: '', file_type: 'pdf', file_size: 0, file_url: '', file_name: '', tags: [], }) setDialogOpen(true) } // 打开编辑对话框 const handleOpenEdit = (material: MaterialFile) => { setEditingMaterial(material) setFormData({ subject_id: material.subject_id, title: material.title, description: material.description || '', file_type: material.file_type, file_size: material.file_size, file_url: material.file_url, file_name: material.file_name || '', tags: material.tags || [], }) setDialogOpen(true) } // 保存资料 const handleSave = async () => { if (!formData.subject_id || !formData.title?.trim() || !formData.file_url?.trim()) return setDialogLoading(true) try { if (editingMaterial) { const updateData: MaterialUpdateRequest = { ...formData } await materialsApi.updateMaterial(editingMaterial.id, updateData) } else { await materialsApi.createMaterial(formData) } setDialogOpen(false) loadMaterials() } catch (error) { console.error('Failed to save material:', error) } finally { setDialogLoading(false) } } // 打开删除确认 const handleOpenDelete = (material: MaterialFile) => { setDeletingMaterial(material) setDeleteOpen(true) } // 确认删除 const handleConfirmDelete = async () => { if (!deletingMaterial) return setDeleteLoading(true) try { await materialsApi.deleteMaterial(deletingMaterial.id) setDeleteOpen(false) loadMaterials() } catch (error) { console.error('Failed to delete material:', error) } finally { setDeleteLoading(false) } } // 查看详情 const handleViewDetail = async (material: MaterialFile) => { setDetailLoading(true) setDetailOpen(true) try { const response = await materialsApi.getMaterialById(material.id) setSelectedMaterial(response.data.data) } catch (error) { console.error('Failed to load material detail:', error) setSelectedMaterial(null) } finally { setDetailLoading(false) } } // 格式化日期 const formatDate = (dateStr: string) => { try { return format(new Date(dateStr), 'yyyy-MM-dd HH:mm') } catch { return dateStr } } const totalPages = Math.ceil(total / pageSize) return (

学习资料管理

管理各学科的学习资料文件

资料列表 共 {total} 个资料
{/* 筛选栏 */}
setKeyword(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleSearch()} className="w-[200px]" />
类型 标题 学科 大小 下载 状态 创建时间 操作 {loading ? (

加载中...

) : materials.length === 0 ? ( 暂无数据 ) : ( materials.map((material) => { const typeConfig = FILE_TYPE_CONFIG[material.file_type] || FILE_TYPE_CONFIG.pdf const subject = subjects.find((s) => s.id === material.subject_id) return ( {typeConfig.icon} {typeConfig.label}

{material.title}

{material.file_name || '未命名文件'}

{subject ? ( {subject.icon} {subject.name} ) : ( - )} {formatFileSize(material.file_size)}
{material.download_count}
{material.status === 'active' ? '启用' : '禁用'} {formatDate(material.created_at)}
) }) )}
{totalPages > 1 && (
)}
{/* 创建/编辑对话框 */} {editingMaterial ? '编辑资料' : '添加资料'} {editingMaterial ? '修改资料信息' : '创建新的学习资料'}
setFormData({ ...formData, title: e.target.value })} placeholder="如:高等数学期末复习资料" />