import { useEffect, useRef, useState } from "react";
|
import { Button, Card, Form, Input, Select, Table } from "antd";
|
import { useNavigate, useSearchParams } from "react-router-dom";
|
import { api } from "../api";
|
import {
|
attemptDetailPath,
|
readScoreListQuery,
|
writeScoreListQuery,
|
type ScoreListQuery,
|
} from "../scoreQueryParams";
|
import "./ScoreQueryPage.css";
|
|
type ScoreRow = {
|
examinee_id: string;
|
name: string;
|
id_card: string;
|
subject_name: string;
|
score: number;
|
attempt_id: string;
|
start_time?: string | null;
|
end_time?: string | null;
|
};
|
|
export default function ScoreQueryPage() {
|
const nav = useNavigate();
|
const [searchParams, setSearchParams] = useSearchParams();
|
const query = readScoreListQuery(searchParams);
|
const [form] = Form.useForm<ScoreListQuery>();
|
const tableWrapRef = useRef<HTMLDivElement>(null);
|
const [subjects, setSubjects] = useState<string[]>([]);
|
const [data, setData] = useState<ScoreRow[]>([]);
|
const [total, setTotal] = useState(0);
|
const [page, setPage] = useState(query.page ?? 1);
|
const [pageSize, setPageSize] = useState(query.page_size ?? 20);
|
const [filters, setFilters] = useState<Pick<ScoreListQuery, "name" | "id_card" | "subject">>({
|
name: query.name,
|
id_card: query.id_card,
|
subject: query.subject,
|
});
|
const [tableScrollY, setTableScrollY] = useState(320);
|
|
useEffect(() => {
|
api.get<{ items: string[] }>("/api/subjects").then((r) => setSubjects(r.data.items));
|
}, []);
|
|
useEffect(() => {
|
const q = readScoreListQuery(searchParams);
|
setPage(q.page ?? 1);
|
setPageSize(q.page_size ?? 20);
|
setFilters({ name: q.name, id_card: q.id_card, subject: q.subject });
|
form.setFieldsValue({
|
name: q.name,
|
id_card: q.id_card,
|
subject: q.subject,
|
});
|
}, [searchParams, form]);
|
|
const load = async () => {
|
const { data: res } = await api.get("/api/scores", {
|
params: { page, page_size: pageSize, ...filters },
|
});
|
setData(res.items);
|
setTotal(res.total);
|
};
|
|
useEffect(() => {
|
load();
|
}, [page, pageSize, filters]);
|
|
useEffect(() => {
|
const el = tableWrapRef.current;
|
if (!el) return;
|
const update = () => {
|
const h = el.clientHeight;
|
setTableScrollY(Math.max(120, h - 56 - 52));
|
};
|
update();
|
const ro = new ResizeObserver(update);
|
ro.observe(el);
|
return () => ro.disconnect();
|
}, []);
|
|
const listQuery = (): ScoreListQuery => ({
|
...filters,
|
page,
|
page_size: pageSize,
|
});
|
|
const pushQuery = (q: ScoreListQuery) => {
|
setSearchParams(writeScoreListQuery(q), { replace: true });
|
};
|
|
return (
|
<div className="score-query-page">
|
<Card bordered className="score-query-page__card">
|
<Form
|
form={form}
|
layout="inline"
|
className="score-query-page__form"
|
onFinish={(v) => {
|
pushQuery({ ...v, page: 1, page_size: pageSize });
|
}}
|
>
|
<Form.Item name="name" label="姓名">
|
<Input allowClear style={{ width: 120 }} />
|
</Form.Item>
|
<Form.Item name="id_card" label="证件号">
|
<Input allowClear style={{ width: 180 }} />
|
</Form.Item>
|
<Form.Item name="subject" label="科目">
|
<Select
|
allowClear
|
style={{ width: 220 }}
|
options={subjects.map((s) => ({ value: s, label: s }))}
|
/>
|
</Form.Item>
|
<Form.Item>
|
<Button type="primary" htmlType="submit">查询</Button>
|
</Form.Item>
|
</Form>
|
<div ref={tableWrapRef} className="score-query-page__table-wrap">
|
<Table
|
rowKey="attempt_id"
|
scroll={{ x: 1100, y: tableScrollY }}
|
dataSource={data}
|
pagination={{
|
current: page,
|
pageSize,
|
total,
|
showSizeChanger: true,
|
showTotal: (t) => `共 ${t} 条`,
|
position: ["bottomRight"],
|
onChange: (p, ps) => {
|
pushQuery({ ...filters, page: p, page_size: ps });
|
},
|
}}
|
columns={[
|
{
|
title: "序号",
|
width: 64,
|
fixed: "left",
|
render: (_, __, index) => (page - 1) * pageSize + index + 1,
|
},
|
{ title: "姓名", dataIndex: "name", width: 100 },
|
{ title: "证件号", dataIndex: "id_card", width: 180 },
|
{ title: "科目", dataIndex: "subject_name", width: 200, ellipsis: true },
|
{ title: "开始考试时间", dataIndex: "start_time", width: 170, render: (v) => v || "—" },
|
{ title: "结束考试时间", dataIndex: "end_time", width: 170, render: (v) => v || "—" },
|
{ title: "成绩", dataIndex: "score", width: 80 },
|
{
|
title: "操作",
|
width: 80,
|
fixed: "right",
|
render: (_, row) => (
|
<Button
|
type="link"
|
onClick={() => nav(attemptDetailPath(row.attempt_id, listQuery()))}
|
>
|
查看
|
</Button>
|
),
|
},
|
]}
|
/>
|
</div>
|
</Card>
|
</div>
|
);
|
}
|