import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { Button, Card, Col, Empty, Form, Input, Progress, Row, Select, Space, Tag } from 'antd'; import { CalendarOutlined, PlusOutlined, SearchOutlined, TeamOutlined } from '@ant-design/icons'; import dayjs from 'dayjs'; import { useNavigate } from 'react-router-dom'; import api from '../../api'; import { message } from '../../ui/app-message'; import ExamFormModal from './ExamFormModal'; import type { ClassOption, ExamFormValues, ExamItem } from './types'; import { EXAM_TYPE_OPTIONS } from './types'; import './style.css'; const ExamsPage: React.FC = () => { const navigate = useNavigate(); const [form] = Form.useForm(); const [data, setData] = useState([]); const [classes, setClasses] = useState([]); const [loading, setLoading] = useState(false); const [saving, setSaving] = useState(false); const [modalOpen, setModalOpen] = useState(false); const [keyword, setKeyword] = useState(''); const [examType, setExamType] = useState(); const [classId, setClassId] = useState(); const loadClasses = useCallback(async () => { const result = await api.get('/classes'); setClasses(result ?? []); }, []); const loadExams = useCallback(async () => { setLoading(true); try { const params = new URLSearchParams(); if (keyword.trim()) params.set('keyword', keyword.trim()); if (examType) params.set('examType', examType); if (classId) params.set('classId', String(classId)); const result = await api.get(`/exams?${params.toString()}`); setData(result ?? []); } catch (error) { message.error((error as { message?: string })?.message || '加载考试失败'); } finally { setLoading(false); } }, [classId, examType, keyword]); useEffect(() => { void loadClasses().catch((error: { message?: string }) => message.error(error?.message || '加载班级失败')); }, [loadClasses]); useEffect(() => { const timer = window.setTimeout(() => void loadExams(), 200); return () => window.clearTimeout(timer); }, [loadExams]); const classOptions = useMemo( () => classes.map((item) => ({ value: item.id, label: item.name })), [classes], ); const openCreate = () => { form.resetFields(); form.setFieldValue('examDate', dayjs()); setModalOpen(true); }; const submit = async () => { try { const values = await form.validateFields(); setSaving(true); const payload = { ...values, examDate: values.examDate.format('YYYY-MM-DD') }; await api.post('/exams', payload); message.success('考试已创建'); setModalOpen(false); await loadExams(); } catch (error) { if ((error as { errorFields?: unknown[] }).errorFields) return; message.error((error as { message?: string })?.message || '保存失败'); } finally { setSaving(false); } }; return (
setKeyword(event.target.value)} prefix={} placeholder="搜索考试名称" allowClear />
{data.length === 0 && !loading ? (
) : ( {data.map((exam) => { const percent = exam.totalStudents === 0 ? 0 : Math.round((exam.enteredScores / exam.totalStudents) * 100); return ( {exam.examType}{exam.examName}} extra={成绩录入} actions={[ navigate(`/exams/${exam.id}`)}>查看成绩, ]} >
科目{exam.subject}
班级{exam.className}
日期{exam.examDate}
成绩录入{exam.enteredScores}/{exam.totalStudents}
); })}
)} setModalOpen(false)} onSubmit={() => void submit()} />
); }; export default ExamsPage;