From f90d01338e1d901481a974fe2ca0635018c5451b Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Thu, 17 Sep 2026 02:23:01 -0600 Subject: [PATCH] =?UTF-8?q?feat(=E8=87=AA=E5=AD=A6=E6=83=85=E5=86=B5):=20?= =?UTF-8?q?=E6=8C=89=E5=AD=A6=E7=94=9F=E5=8A=A0=E5=A7=93=E5=90=8D/?= =?UTF-8?q?=E5=AD=A6=E5=8F=B7=E6=90=9C=E7=B4=A2=EF=BC=8C=E5=90=8E=E7=AB=AF?= =?UTF-8?q?=E8=A1=A5=E7=A8=B3=E5=AE=9A=E6=8E=92=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 搜索是纯前端过滤(整表本来就一次拉完),和班级筛选可以叠加。 学生查询原来没有 orderBy,前端默认按「已读」升序排之后,同分的一大批 (尤其一堆 0)落回聚合的任意顺序,刷新一次换一个样;按班级、学号兜底。 Co-Authored-By: Claude Opus 5 --- apps/api/src/routes/admin/learn.ts | 6 +++++- apps/web/src/admin/learn/index.vue | 27 ++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/apps/api/src/routes/admin/learn.ts b/apps/api/src/routes/admin/learn.ts index 982ac96..fc022b3 100644 --- a/apps/api/src/routes/admin/learn.ts +++ b/apps/api/src/routes/admin/learn.ts @@ -123,7 +123,11 @@ adminLearnRoutes.get("/learn-analytics/students", requireTeacher, async (c) => { schema.user.username, schema.userProfile.realName, schema.user.className, - ), + ) + // 前端默认按「已读」升序排,同分的一大批(尤其一堆 0)就落回这里的次序。 + // 不给 orderBy 的话那是聚合吐出来的任意顺序,刷一次换一个样 —— 按班级、 + // 学号排稳住它。className 为空的(推不出班级的)ASC 默认排在最后 + .orderBy(asc(schema.user.className), asc(schema.user.username)), db .select({ userId: schema.exerciseAttempt.userId, diff --git a/apps/web/src/admin/learn/index.vue b/apps/web/src/admin/learn/index.vue index 04ec37e..d9e70cc 100644 --- a/apps/web/src/admin/learn/index.vue +++ b/apps/web/src/admin/learn/index.vue @@ -28,6 +28,8 @@ const type = ref<"python" | "c">("python") // 3-4 位是具体班级,1-2 位当年级前缀(后端 classFilter 分的岔) const className = ref("") const tab = ref("students") +// 按学生那张表的姓名/学号搜索,纯前端过滤(整表本来就一次拉完) +const keyword = ref("") const loading = ref(false) const students = ref([]) @@ -48,6 +50,18 @@ const startedCount = computed( () => students.value.filter((row) => row.readCount > 0).length, ) +// 姓名和学号都已经在手里,不再打接口。学号是纯数字,姓名是中文, +// 一个框同时匹配两列就够了 —— 老师要么记得学号要么记得名字 +const filteredStudents = computed(() => { + const value = keyword.value.trim().toLowerCase() + if (!value) return students.value + return students.value.filter( + (row) => + row.username.toLowerCase().includes(value) || + (row.realName ?? "").toLowerCase().includes(value), + ) +}) + const studentColumns = computed[]>(() => [ { title: "班级", key: "className", width: 90, sorter: "default" }, { title: "学号", key: "username", width: 140 }, @@ -300,10 +314,21 @@ onMounted(load) + + + + 找到 {{ filteredStudents.length }} 人 + +