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 }} 人 + +