From 3ca3b42997f29a8939975d36e65bc405f6dde3cb Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Mon, 12 Oct 2026 23:28:55 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E6=8E=92=E5=90=8D):=20=E8=80=81=E5=B8=88?= =?UTF-8?q?=E7=82=B9=E3=80=8C=E6=9C=AC=E7=8F=AD=E3=80=8D=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E9=9A=8F=E6=89=8B=E6=8C=91=E4=B8=AA=E4=BA=A4=E9=A2=98=E6=9C=80?= =?UTF-8?q?=E5=A4=9A=E7=9A=84=E7=8F=AD=EF=BC=8C=E6=94=B9=E6=88=90=E5=B8=83?= =?UTF-8?q?=E7=BD=AE=E8=BF=87=E4=BD=9C=E4=B8=9A=E7=9A=84=E7=8F=AD=E3=80=81?= =?UTF-8?q?=E4=B8=8A=E6=AC=A1=E9=80=89=E7=9A=84=E7=8F=AD=EF=BC=8C=E9=83=BD?= =?UTF-8?q?=E6=B2=A1=E6=9C=89=E5=B0=B1=E5=81=9C=E5=9C=A8=E5=85=A8=E9=83=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- apps/api/src/routes/admin/learn.ts | 2 +- apps/api/src/routes/ranking.ts | 5 +++-- apps/api/src/services/ranking.ts | 17 +++++++++++++---- apps/web/src/oj/rank/list.vue | 22 ++++++++++++++++++---- apps/web/src/utils/constants.ts | 1 + 5 files changed, 36 insertions(+), 11 deletions(-) diff --git a/apps/api/src/routes/admin/learn.ts b/apps/api/src/routes/admin/learn.ts index a12584d..5064c92 100644 --- a/apps/api/src/routes/admin/learn.ts +++ b/apps/api/src/routes/admin/learn.ts @@ -66,7 +66,7 @@ function studentCondition(value: string | null) { /** * 方块格:一个班的学生 × 这门课的每一课,打开过没有、学完没有(练一练全对)。 * - * 不选班就按排名页同一套规则猜老师在教哪个班(`defaultClassFor`);也能传年级前缀, + * 不选班就猜老师在教哪个班(`defaultClassFor`);也能传年级前缀, * 但一个年级两三百行方块,页面上主要还是按班看。 */ adminLearnRoutes.get("/learn-analytics/board", requireTeacher, async (c) => { diff --git a/apps/api/src/routes/ranking.ts b/apps/api/src/routes/ranking.ts index 282cda6..89eecb0 100644 --- a/apps/api/src/routes/ranking.ts +++ b/apps/api/src/routes/ranking.ts @@ -24,6 +24,7 @@ import { classDetail, classPk, defaultClassFor, + lessonClassFor, loadEntrants, PK_MAX_CLASSES, pkPartner, @@ -75,7 +76,7 @@ function periodStart(period: RankPeriod) { /** * 这次请求看的是哪个班 / 哪个年级。学生只能看自己的班和年级;老师用 `className` 选, - * 不给就猜(`defaultClassFor`)。返回 undefined 表示该报「没有班级」。 + * 不给就取最近布置过作业的班(`lessonClassFor`)。返回 undefined 表示该报「没有班级」。 */ async function resolveGroup( user: AppEnv["Variables"]["user"], @@ -84,7 +85,7 @@ async function resolveGroup( ): Promise { if (scope === "all") return null let className = user?.className || null - if (isTeacherOrAbove(user)) className = requested || (await defaultClassFor(user!.id)) + if (isTeacherOrAbove(user)) className = requested || (await lessonClassFor(user!.id)) if (!className) return undefined return scope === "class" ? className : className.slice(0, 2) } diff --git a/apps/api/src/services/ranking.ts b/apps/api/src/services/ranking.ts index 848e822..5de37b0 100644 --- a/apps/api/src/services/ranking.ts +++ b/apps/api/src/services/ranking.ts @@ -538,17 +538,26 @@ export async function classPk( } /** - * 老师打开排名页(和后台「自学情况」)默认看哪个班:自己最近布置过作业的班 → 最近一周交题人数最多的班。 - * 不记老师上次选的:统计面板吃过那个亏(上一节课的班悄悄留在框里)。 + * 老师自己最近布置过作业的班。老师账号没有班级,这是唯一能说明「这是他的班」的数据; + * 排名页点「本班」只认这个,猜不出就交给前端(用上次选的班,没有就停在全部)。 */ -export async function defaultClassFor(teacherId: number) { +export async function lessonClassFor(teacherId: number) { const [lesson] = await db .select({ className: schema.classLesson.className }) .from(schema.classLesson) .where(eq(schema.classLesson.createdBy, teacherId)) .orderBy(desc(schema.classLesson.day), desc(schema.classLesson.updatedAt)) .limit(1) - if (lesson) return lesson.className + return lesson?.className ?? null +} + +/** + * 后台「自学情况」和班级 PK 默认看哪个班:自己最近布置过作业的班 → 最近一周交题人数最多的班。 + * 不记老师上次选的:统计面板吃过那个亏(上一节课的班悄悄留在框里)。 + */ +export async function defaultClassFor(teacherId: number) { + const lesson = await lessonClassFor(teacherId) + if (lesson) return lesson const users = countDistinct(schema.submission.userId) const [busiest] = await db diff --git a/apps/web/src/oj/rank/list.vue b/apps/web/src/oj/rank/list.vue index 8dba795..864b56e 100644 --- a/apps/web/src/oj/rank/list.vue +++ b/apps/web/src/oj/rank/list.vue @@ -14,7 +14,9 @@ import UserName from "shared/components/UserName.vue" import { useBreakpoints } from "shared/composables/breakpoints" import { useConfigStore } from "shared/store/config" import { useUserStore } from "shared/store/user" -import { USERNAME_CLASS_RE } from "utils/constants" +import { errorCode } from "utils/api" +import { STORAGE_KEY, USERNAME_CLASS_RE } from "utils/constants" +import storage from "utils/storage" import ChampionList from "./components/ChampionList.vue" import ClassBattle from "./components/ClassBattle.vue" import ClassDetailDrawer from "./components/ClassDetailDrawer.vue" @@ -60,7 +62,7 @@ function pick(value: unknown, options: { value: T }[], fallbac const scope = ref("all") const period = ref(pick(route.query.period, PERIOD_OPTIONS, "term")) -/** 老师选的班;空 = 让后端挑最近上课的班,挑完回填 */ +/** 老师选的班;空 = 让后端挑最近布置过作业的班,挑完回填 */ const teacherClass = ref(typeof route.query.class === "string" ? route.query.class : "") const board = ref(null) @@ -86,13 +88,24 @@ async function load() { loading.value = true try { board.value = await fetchBoard(scope.value, period.value) - // 老师在「全部」时点了本班:后端挑了最近上课的班,回填到下拉框(这一下不用再取一遍) + // 老师在「全部」时点了本班:后端挑了最近布置过作业的班,回填到下拉框(这一下不用再取一遍) if (teacher.value && !teacherClass.value && board.value.scope === "class") { backfilling = true teacherClass.value = board.value.className ?? "" } - } catch { + } catch (err) { board.value = null + // 没布置过作业、后端猜不出:用上次在下拉框里选的班,没选过就回到全部(watch 会重取) + if (teacher.value && !teacherClass.value && errorCode(err) === "class-missing") { + loading.value = false + const saved = storage.get(STORAGE_KEY.RANK_CLASS) + if (typeof saved === "string" && saved) teacherClass.value = saved + else { + scope.value = "all" + message.info("先选一个班") + } + return + } } finally { loading.value = false } @@ -171,6 +184,7 @@ const pickedClass = computed({ get: () => (scope.value === "all" ? "" : teacherClass.value), set: (value: string) => { teacherClass.value = value + if (value) storage.set(STORAGE_KEY.RANK_CLASS, value) if (!value) scope.value = "all" else if (scope.value === "all") scope.value = "class" }, diff --git a/apps/web/src/utils/constants.ts b/apps/web/src/utils/constants.ts index ee61f47..abd5a46 100644 --- a/apps/web/src/utils/constants.ts +++ b/apps/web/src/utils/constants.ts @@ -157,6 +157,7 @@ export const STORAGE_KEY = { ADMIN_SIDER_COLLAPSED: "adminSiderCollapsed", DEMO_MODE: "demoMode", LOGIN_CLASS: "loginClass", + RANK_CLASS: "rankClass", } export const DIFFICULTY = {