import type { JSX } from "react" import Link from "next/link" import { PlusCircle, BarChart3, ClipboardList } from "lucide-react" import { Button } from "@/shared/components/ui/button" import { EmptyState } from "@/shared/components/ui/empty-state" import { ListPagination, computePagination } from "@/shared/components/ui/list-pagination" import { requirePermission } from "@/shared/lib/auth-guard" import { Permissions } from "@/shared/types/permissions" import { getParam, type SearchParams } from "@/shared/lib/search-params" import { getTeacherClasses } from "@/modules/classes/data-access" import { getGradeRecords } from "@/modules/grades/data-access" import { getSubjectOptions } from "@/modules/school/data-access" import { GradeQueryFilters } from "@/modules/grades/components/grade-query-filters" import { GradeRecordList } from "@/modules/grades/components/grade-record-list" import { ExportButton } from "@/modules/grades/components/export-button" import type { GradeRecordType, GradeRecordSemester } from "@/modules/grades/types" export const dynamic = "force-dynamic" const VALID_GRADE_TYPES: ReadonlySet = new Set(["exam", "quiz", "homework", "other"]) const VALID_SEMESTERS: ReadonlySet = new Set(["1", "2"]) function parseGradeType(v?: string): GradeRecordType | undefined { return v && VALID_GRADE_TYPES.has(v) ? (v as GradeRecordType) : undefined } function parseSemester(v?: string): GradeRecordSemester | undefined { return v && VALID_SEMESTERS.has(v) ? (v as GradeRecordSemester) : undefined } const PAGE_SIZE = 20 export default async function TeacherGradesPage({ searchParams, }: { searchParams: Promise }): Promise { const sp = await searchParams const ctx = await requirePermission(Permissions.GRADE_RECORD_READ) const classId = getParam(sp, "classId") const subjectId = getParam(sp, "subjectId") const type = getParam(sp, "type") const semester = getParam(sp, "semester") // P3 修复:使用 DB 层分页,移除重复计算 const { page } = computePagination(sp, PAGE_SIZE) const offset = (page - 1) * PAGE_SIZE const [classes, allSubjects, result] = await Promise.all([ getTeacherClasses(), getSubjectOptions(), getGradeRecords({ scope: ctx.dataScope, currentUserId: ctx.userId, classId: classId && classId !== "all" ? classId : undefined, subjectId: subjectId && subjectId !== "all" ? subjectId : undefined, type: type && type !== "all" ? parseGradeType(type) : undefined, semester: semester && semester !== "all" ? parseSemester(semester) : undefined, limit: PAGE_SIZE, offset, }), ]) const classOptions = classes.map((c) => ({ id: c.id, name: c.name })) const subjectOptions = allSubjects.map((s) => ({ id: s.id, name: s.name })) // 使用 DB 返回的 total 和 totalPages,移除重复计算 const total = result.total const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE)) const currentPage = Math.min(page, totalPages) const pagedRecords = result.records const hasFilters = Boolean(classId || subjectId || type || semester) return (

成绩记录

管理学生成绩记录。

{total === 0 && !hasFilters ? ( ) : (
{total > 0 ? ( ) : null}
)}
) }