"use client"; import { useCallback, useEffect, useRef, useState } from "react"; import { useActiveClass } from "@/hooks/use-active-class"; import { fetchAPI, postAPI, deleteAPI, uploadAPI, getErrorMessage } from "@/lib/api"; import { Card, CardContent } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Badge } from "@/components/ui/badge"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { RoleGuard } from "@/components/role-guard"; import { ConfirmDialog } from "@/components/confirm-dialog"; import { ErrorState } from "@/components/error-state"; import { Pagination } from "@/components/pagination"; import { toast } from "sonner"; import type { PageResponse, Resource } from "@/lib/types"; type ResourceDownloadResponse = { file_url: string; }; const RESOURCE_CATEGORIES: Record = { all: "全部", course_material: "课件资料", assignment: "作业", reading: "阅读材料", other: "其他", }; function formatFileSize(bytes: number): string { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; } function getFileIcon(fileType: string): string { if (fileType.startsWith("image/")) return "🖼️"; if (fileType.includes("pdf")) return "📄"; if (fileType.includes("word") || fileType.includes("document")) return "📝"; if (fileType.includes("sheet") || fileType.includes("excel")) return "📊"; if (fileType.includes("presentation") || fileType.includes("powerpoint")) return "📽️"; if (fileType.includes("zip")) return "📦"; return "📎"; } export default function ResourcesPage() { const { activeClassId } = useActiveClass(); const [resources, setResources] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [category, setCategory] = useState("all"); const [page, setPage] = useState(1); const [totalPages, setTotalPages] = useState(1); // Upload dialog state const [dialogOpen, setDialogOpen] = useState(false); const [formTitle, setFormTitle] = useState(""); const [formDesc, setFormDesc] = useState(""); const [formCategory, setFormCategory] = useState("course_material"); const [selectedFile, setSelectedFile] = useState(null); const [submitting, setSubmitting] = useState(false); const fileInputRef = useRef(null); // Delete state const [deleteTarget, setDeleteTarget] = useState(null); const loadResources = useCallback(async () => { if (!activeClassId) { setResources([]); setTotalPages(1); setLoading(false); return; } setError(null); setLoading(true); try { const params: Record = { page_size: "20", page: String(page), class_id: String(activeClassId), }; if (category !== "all") params.category = category; const res = await fetchAPI>("/api/resources/", params); setResources(res.items || []); setTotalPages(res.total_pages || 1); } catch (err: unknown) { setError(getErrorMessage(err, "加载失败")); } finally { setLoading(false); } }, [activeClassId, category, page]); useEffect(() => { void loadResources(); }, [loadResources]); const resetForm = () => { setFormTitle(""); setFormDesc(""); setFormCategory("course_material"); setSelectedFile(null); if (fileInputRef.current) fileInputRef.current.value = ""; }; const handleSubmit = async () => { if (!formTitle.trim() || !selectedFile) return; setSubmitting(true); try { const formData = new FormData(); formData.append("title", formTitle); formData.append("category", formCategory); formData.append("file", selectedFile); if (formDesc.trim()) formData.append("description", formDesc); if (activeClassId) formData.append("class_id", String(activeClassId)); await uploadAPI("/api/resources/", formData); toast.success("资源已上传"); setDialogOpen(false); resetForm(); loadResources(); } catch (err: unknown) { toast.error(getErrorMessage(err, "上传失败")); } finally { setSubmitting(false); } }; const handleDownload = async (resource: Resource) => { try { const res = await postAPI(`/api/resources/${resource.id}/download`); window.open(res.file_url, "_blank"); } catch (err: unknown) { toast.error(getErrorMessage(err, "下载失败")); } }; const handleDelete = async (id: number) => { try { await deleteAPI(`/api/resources/${id}`); toast.success("已删除"); setDeleteTarget(null); loadResources(); } catch (err: unknown) { toast.error(getErrorMessage(err, "删除失败")); } }; return (
Library

资源库

共享课件、阅读材料与班级文档档案

{ setDialogOpen(open); if (!open) resetForm(); }}> 上传资源
setFormTitle(e.target.value)} />