From e3e6db2d9974588f91ac1567a58fa4846655ce1b Mon Sep 17 00:00:00 2001
From: yearning <10538594+wangweifeng1999@user.noreply.gitee.com>
Date: 星期三, 30 九月 2026 12:54:55 +0800
Subject: [PATCH] 1.判断题优化;2.新增考评员是否能查看考生隐私开关

---
 frontend/src/pages/ScoreQueryPage.tsx |  136 +++++++++++++++++++++++++++++++-------------
 1 files changed, 95 insertions(+), 41 deletions(-)

diff --git a/frontend/src/pages/ScoreQueryPage.tsx b/frontend/src/pages/ScoreQueryPage.tsx
index e3f3141..99e9b47 100644
--- a/frontend/src/pages/ScoreQueryPage.tsx
+++ b/frontend/src/pages/ScoreQueryPage.tsx
@@ -1,5 +1,5 @@
 import { useEffect, useRef, useState } from "react";
-import { Button, Card, Form, Input, Select, Table } from "antd";
+import { Button, Card, Form, Input, message, Select, Table } from "antd";
 import { useNavigate, useSearchParams } from "react-router-dom";
 import { api } from "../api";
 import {
@@ -8,12 +8,15 @@
   writeScoreListQuery,
   type ScoreListQuery,
 } from "../scoreQueryParams";
+import { useSystemSettings } from "../systemSettings";
+import type { UserMe } from "../App";
 import "./ScoreQueryPage.css";
 
 type ScoreRow = {
   examinee_id: string;
-  name: string;
-  id_card: string;
+  student_no: string;
+  name: string | null;
+  id_card: string | null;
   subject_name: string;
   score: number;
   attempt_id: string;
@@ -38,6 +41,18 @@
     subject: query.subject,
   });
   const [tableScrollY, setTableScrollY] = useState(320);
+  const [user, setUser] = useState<UserMe | null>(null);
+  const { settings } = useSystemSettings();
+
+  const hidePrivacy =
+    user?.role === "grader" && !settings.grader_can_see_privacy;
+
+  useEffect(() => {
+    api
+      .get<UserMe>("/api/auth/me")
+      .then((r) => setUser(r.data))
+      .catch(() => setUser(null));
+  }, []);
 
   useEffect(() => {
     api.get<{ items: string[] }>("/api/subjects").then((r) => setSubjects(r.data.items));
@@ -56,16 +71,20 @@
   }, [searchParams, form]);
 
   const load = async () => {
-    const { data: res } = await api.get("/api/scores", {
-      params: { page, page_size: pageSize, ...filters },
-    });
+    const params: Record<string, string | number> = { page, page_size: pageSize };
+    if (!hidePrivacy) {
+      if (filters.name) params.name = filters.name;
+      if (filters.id_card) params.id_card = filters.id_card;
+    }
+    if (filters.subject) params.subject = filters.subject;
+    const { data: res } = await api.get("/api/scores", { params });
     setData(res.items);
     setTotal(res.total);
   };
 
   useEffect(() => {
     load();
-  }, [page, pageSize, filters]);
+  }, [page, pageSize, filters, hidePrivacy]);
 
   useEffect(() => {
     const el = tableWrapRef.current;
@@ -90,6 +109,52 @@
     setSearchParams(writeScoreListQuery(q), { replace: true });
   };
 
+  const [studentNo, setStudentNo] = useState("");
+  const handleLookup = async () => {
+    if (!studentNo.trim()) return;
+    try {
+      const { data } = await api.get<{ attempt_id: string }>(
+        `/api/attempts/lookup/${encodeURIComponent(studentNo.trim().toUpperCase())}`
+      );
+      nav(attemptDetailPath(data.attempt_id, listQuery()));
+    } catch {
+      message.error("鑰冪敓缂栧彿涓嶅瓨鍦�");
+    }
+  };
+
+  const columns = [
+    {
+      title: "搴忓彿",
+      width: 64,
+      fixed: "left" as const,
+      render: (_: unknown, __: unknown, index: number) => (page - 1) * pageSize + index + 1,
+    },
+    { title: "鑰冪敓缂栧彿", dataIndex: "student_no", width: 180, fixed: "left" as const },
+    ...(hidePrivacy
+      ? []
+      : [
+          { 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: string | null) => v || "鈥�" },
+    { title: "缁撴潫鑰冭瘯鏃堕棿", dataIndex: "end_time", width: 170, render: (v: string | null) => v || "鈥�" },
+    { title: "鎴愮哗", dataIndex: "score", width: 80 },
+    {
+      title: "鎿嶄綔",
+      width: 80,
+      fixed: "right" as const,
+      render: (_: unknown, row: ScoreRow) => (
+        <Button
+          type="link"
+          onClick={() => nav(attemptDetailPath(row.attempt_id, listQuery()))}
+        >
+          鏌ョ湅
+        </Button>
+      ),
+    },
+  ];
+
   return (
     <div className="score-query-page">
       <Card bordered className="score-query-page__card">
@@ -101,12 +166,16 @@
             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>
+          {!hidePrivacy && (
+            <>
+              <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
@@ -118,10 +187,21 @@
             <Button type="primary" htmlType="submit">鏌ヨ</Button>
           </Form.Item>
         </Form>
+        <div style={{ marginBottom: 12, display: "flex", gap: 8, alignItems: "center" }}>
+          <Input
+            placeholder="杈撳叆鑰冪敓缂栧彿鐩存帴鏌ョ湅绛斿嵎"
+            value={studentNo}
+            onChange={(e) => setStudentNo(e.target.value.toUpperCase())}
+            onPressEnter={handleLookup}
+            style={{ width: 200, fontFamily: "monospace" }}
+            maxLength={10}
+          />
+          <Button onClick={handleLookup} disabled={!studentNo.trim()}>鏌ョ湅绛斿嵎</Button>
+        </div>
         <div ref={tableWrapRef} className="score-query-page__table-wrap">
           <Table
             rowKey="attempt_id"
-            scroll={{ x: 1100, y: tableScrollY }}
+            scroll={{ x: hidePrivacy ? 800 : 1100, y: tableScrollY }}
             dataSource={data}
             pagination={{
               current: page,
@@ -134,33 +214,7 @@
                 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>
-                ),
-              },
-            ]}
+            columns={columns}
           />
         </div>
       </Card>

--
Gitblit v1.8.0