搜索是纯前端过滤(整表本来就一次拉完),和班级筛选可以叠加。 学生查询原来没有 orderBy,前端默认按「已读」升序排之后,同分的一大批 (尤其一堆 0)落回聚合的任意顺序,刷新一次换一个样;按班级、学号兜底。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -123,7 +123,11 @@ adminLearnRoutes.get("/learn-analytics/students", requireTeacher, async (c) => {
|
|||||||
schema.user.username,
|
schema.user.username,
|
||||||
schema.userProfile.realName,
|
schema.userProfile.realName,
|
||||||
schema.user.className,
|
schema.user.className,
|
||||||
),
|
)
|
||||||
|
// 前端默认按「已读」升序排,同分的一大批(尤其一堆 0)就落回这里的次序。
|
||||||
|
// 不给 orderBy 的话那是聚合吐出来的任意顺序,刷一次换一个样 —— 按班级、
|
||||||
|
// 学号排稳住它。className 为空的(推不出班级的)ASC 默认排在最后
|
||||||
|
.orderBy(asc(schema.user.className), asc(schema.user.username)),
|
||||||
db
|
db
|
||||||
.select({
|
.select({
|
||||||
userId: schema.exerciseAttempt.userId,
|
userId: schema.exerciseAttempt.userId,
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ const type = ref<"python" | "c">("python")
|
|||||||
// 3-4 位是具体班级,1-2 位当年级前缀(后端 classFilter 分的岔)
|
// 3-4 位是具体班级,1-2 位当年级前缀(后端 classFilter 分的岔)
|
||||||
const className = ref("")
|
const className = ref("")
|
||||||
const tab = ref("students")
|
const tab = ref("students")
|
||||||
|
// 按学生那张表的姓名/学号搜索,纯前端过滤(整表本来就一次拉完)
|
||||||
|
const keyword = ref("")
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const students = ref<LearnStudentProgress[]>([])
|
const students = ref<LearnStudentProgress[]>([])
|
||||||
@@ -48,6 +50,18 @@ const startedCount = computed(
|
|||||||
() => students.value.filter((row) => row.readCount > 0).length,
|
() => 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<DataTableColumn<LearnStudentProgress>[]>(() => [
|
const studentColumns = computed<DataTableColumn<LearnStudentProgress>[]>(() => [
|
||||||
{ title: "班级", key: "className", width: 90, sorter: "default" },
|
{ title: "班级", key: "className", width: 90, sorter: "default" },
|
||||||
{ title: "学号", key: "username", width: 140 },
|
{ title: "学号", key: "username", width: 140 },
|
||||||
@@ -300,10 +314,21 @@ onMounted(load)
|
|||||||
|
|
||||||
<n-tabs v-model:value="tab" type="line" animated>
|
<n-tabs v-model:value="tab" type="line" animated>
|
||||||
<n-tab-pane name="students" tab="按学生">
|
<n-tab-pane name="students" tab="按学生">
|
||||||
|
<n-flex align="center" style="margin-bottom: 12px">
|
||||||
|
<n-input
|
||||||
|
v-model:value="keyword"
|
||||||
|
placeholder="搜索姓名或学号"
|
||||||
|
clearable
|
||||||
|
style="width: 200px"
|
||||||
|
/>
|
||||||
|
<n-text v-if="keyword.trim()" depth="3">
|
||||||
|
找到 {{ filteredStudents.length }} 人
|
||||||
|
</n-text>
|
||||||
|
</n-flex>
|
||||||
<n-data-table
|
<n-data-table
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:columns="studentColumns"
|
:columns="studentColumns"
|
||||||
:data="students"
|
:data="filteredStudents"
|
||||||
:row-key="(row: LearnStudentProgress) => row.userId"
|
:row-key="(row: LearnStudentProgress) => row.userId"
|
||||||
striped
|
striped
|
||||||
:pagination="{ pageSize: 20 }"
|
:pagination="{ pageSize: 20 }"
|
||||||
|
|||||||
Reference in New Issue
Block a user