From 0daa74651d51b5020878cd4146d3c23eaa051446 Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Mon, 28 Sep 2026 12:08:45 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E9=A2=98=E7=9B=AE):=20=E9=80=9A=E8=BF=87?= =?UTF-8?q?=E5=90=8E=E9=A9=AC=E4=B8=8A=E7=82=B9=E3=80=8C=E4=B8=8B=E4=B8=80?= =?UTF-8?q?=E9=A2=98=E3=80=8D=E6=88=96=E6=8D=A2=E9=A2=98=EF=BC=8C=E5=BC=BA?= =?UTF-8?q?=E5=88=B6=E7=82=B9=E8=AF=84=E4=BC=9A=E8=A2=AB=E8=B7=B3=E8=BF=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 从答案正确到点评弹窗出来有 1.5 秒(外加查一次评过没有),而换题会取消弹窗的 定时器 ——「下一题」在通过的那一刻就出现,学生手快就绕过了强制点评。 现在这段时间里离开这道题的导航(下一题、题号直达、顶栏导航、相似题……)先拦下: 立刻弹点评,选完再去原来要去的地方。只改 query(左栏切页签)不拦;弹窗已经开着时 也不拦,免得点评接口一直失败时把学生关在这一页。 Co-Authored-By: Claude Opus 5.5 --- .../src/oj/problem/components/SubmitCode.vue | 62 +++++++++++++++++-- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/apps/web/src/oj/problem/components/SubmitCode.vue b/apps/web/src/oj/problem/components/SubmitCode.vue index 11afed3..7c80712 100644 --- a/apps/web/src/oj/problem/components/SubmitCode.vue +++ b/apps/web/src/oj/problem/components/SubmitCode.vue @@ -9,6 +9,7 @@ import { useFireworks } from "oj/problem/composables/useFireworks" import { useSubmissionMonitor } from "oj/problem/composables/useSubmissionMonitor" import { LANGUAGE_FORMAT_VALUE, SubmissionStatus } from "utils/constants" import type { SubmitCodePayload } from "utils/types" +import type { RouteLocationNormalized } from "vue-router" import { getSubmitButtonState } from "./submitButtonState" import { useBreakpoints } from "shared/composables/breakpoints" import { useUserStore } from "shared/store/user" @@ -40,6 +41,7 @@ function closeCommentPanel() { // 点评价弹窗时,结果面板会当成「点了外面」收起来 —— 而「下一题」就在面板里 // (LessonNext),评价完得把它重新打开 showResult.value = true + settleReview() } const { isDesktop } = useBreakpoints() @@ -79,17 +81,63 @@ const { start: startCooldown, isPending: isCooldown } = useTimeout(5000, { // ==================== AC 后弹出点评轮盘 ==================== // 只对已经能评价、且还没评过的人弹:后端要求先有 AC 才收评价,这里刚 AC 完正好; // mine 非 null 说明早就评过了,别再打扰。 -const { start: showCommentPanelDelayed, stop: cancelCommentPanel } = useTimeoutFn( - async () => { +// +// 点评是强制的,但从 AC 到弹窗出来有 1.5 秒(加上查一次「评过没有」),而换题会把 +// 定时器取消(见下面换题的 watch)—— 「下一题」在通过的那一刻就出现了,学生手快点 +// 下去,点评就跳过了。所以这段时间里欠着一次点评(reviewOwed),离开这道题的导航 +// 先拦下来:立刻弹点评,选完再去原来要去的地方。 +const reviewOwed = ref(false) +let reviewChecking = false +let navigationAfterReview: string | null = null + +async function openReview() { + if (reviewChecking) return + reviewChecking = true + try { const res = await getReaction(problem.value!.id) if (res.mine === null) { commentPanel.value = true + return } - }, + } catch { + // 查不到就不拦:宁可少一条点评,也不能把学生卡在这道题上 + } finally { + reviewChecking = false + } + settleReview() +} + +/** 这次点评了结(评完了、原来就评过、或者查不到),接着去刚才被拦下的地方 */ +function settleReview() { + reviewOwed.value = false + const target = navigationAfterReview + navigationAfterReview = null + if (target) router.push(target) +} + +const { start: showCommentPanelDelayed, stop: cancelCommentPanel } = useTimeoutFn( + openReview, 1500, { immediate: false }, ) +/** + * 只拦「离开这道题」:换 query(左栏切页签会改 `?tab=`)不算。弹窗已经开着的时候 + * 不拦 —— 那时页面被遮罩盖住,还能走的只有浏览器后退,拦下来就等于把人关在这一页, + * 点评接口一直失败时连退路都没有。 + */ +function holdForReview(to: RouteLocationNormalized, from: RouteLocationNormalized) { + if (!reviewOwed.value || commentPanel.value) return true + if (to.name === from.name && to.params.problemID === from.params.problemID) return true + navigationAfterReview = to.fullPath + cancelCommentPanel() + openReview() + return false +} + +onBeforeRouteUpdate(holdForReview) +onBeforeRouteLeave(holdForReview) + const { start: goToProblemSetDelayed, stop: cancelGoToProblemSet } = useTimeoutFn( () => { router.push({ @@ -105,8 +153,9 @@ const { start: goToProblemSetDelayed, stop: cancelGoToProblemSet } = useTimeoutF /** * 换题时把上一道题的东西全收掉:结果面板、提交前的语法错误、还在跟的那条提交,以及 - * 通过之后那两个 1.5 秒的定时器 —— 评价弹窗是按**当前**题目去查、去弹的,学生通过后 - * 马上点了「下一题」,不取消的话就会给一道还没做的题弹评价。 + * 通过之后那两个 1.5 秒的定时器 —— 评价弹窗是按**当前**题目去查、去弹的,不取消的话 + * 就会给一道还没做的题弹评价。欠着的点评走不到这里:离开这道题的导航已经被 + * holdForReview 拦下、评完才放行;还能走到这里的只有弹窗开着时按了浏览器后退。 * * 题目页换题是同一个组件复用(只换路由参数),以前几乎只有退回列表再点进来这一条路, * 有了顶栏题号直达和结果面板里的「下一题」之后,这成了常规操作。 @@ -119,6 +168,8 @@ watch( syntaxErrorInfo.value = "" commentPanel.value = false cancelCommentPanel() + reviewOwed.value = false + navigationAfterReview = null cancelGoToProblemSet() resetSubmission() }, @@ -246,6 +297,7 @@ watch( // 4. 弹出评价框。比赛里不打扰;题单里 1.5 秒后要跳回题单页,弹了也会被冲掉 if (!contestID && !problemSetId) { + reviewOwed.value = true showCommentPanelDelayed() }