From 0392445dd240482e9405f713d8ffe5633047570c Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Thu, 27 Aug 2026 06:15:11 -0600 Subject: [PATCH] =?UTF-8?q?fix(=E6=B5=81=E7=A8=8B=E5=9B=BE):=20=E8=A1=A5?= =?UTF-8?q?=E4=B8=8A=E5=87=A0=E5=A4=84=E9=9D=99=E9=BB=98=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=20=E2=80=94=E2=80=94=20=E7=82=B9=E4=BA=86=E6=B2=A1=E5=8F=8D?= =?UTF-8?q?=E5=BA=94=E3=80=81=E5=BC=B9=E6=A1=86=E6=B0=B8=E4=B9=85=E8=BD=AC?= =?UTF-8?q?=E5=9C=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `utils/api.ts` 的拦截器只对 login-required / account-disabled / permission-denied 弹提示,其余业务错误一律静默 reject。而流程图这一块的调用点基本都没 catch, 于是失败时用户什么都看不到: - **重新判题**:后端会拒掉还在评分中的提交(409 retry-not-allowed),也可能撞上 限流。老师点下去完全没反应,连报错都没有。实测修复前 0 条提示,修复后弹出 「这条还在评分中,等出了结果再重新评分」。 提示按**错误码**分支而不是 match 文案(`utils/api.ts` 里写明的约定)—— 后端文案是英文的,直接弹给老师看不合适。 - **课堂统计**:请求失败后图表停在旧数据上,没有任何迹象表明这次没拉到。 同一批里 `SubmitFlowchart` 的三处(弹框翻页、打开评分详情、加载到编辑器)随 上一个提交一起改了,问题是一样的:前两处失败时 `rendering` 卡在 true,弹框 永久转圈。 Co-Authored-By: Claude Opus 5 --- apps/web/src/oj/submission/list.vue | 16 ++++++++++++++- .../components/FlowchartStatisticsPanel.vue | 20 +++++++++++++------ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/apps/web/src/oj/submission/list.vue b/apps/web/src/oj/submission/list.vue index d475c76..ad9a2a7 100644 --- a/apps/web/src/oj/submission/list.vue +++ b/apps/web/src/oj/submission/list.vue @@ -195,7 +195,21 @@ async function rejudge(submissionID: string) { } async function retryFlowchart(submissionId: string) { - await retryFlowchartSubmission(submissionId) + // 后端会拒掉「还在评分中」的提交(409 retry-not-allowed),也可能撞上限流。 + // 不兜住的话拦截器静默 reject,老师点下去完全没反应。 + // 按错误码分支而不是 match 文案(见 utils/api.ts 的约定)——后端文案是英文的, + // 直接弹给老师看不合适 + const retryTips: Record = { + "retry-not-allowed": "这条还在评分中,等出了结果再重新评分", + "too-many-submissions": "操作太频繁了,缓一下再试", + "flowchart-not-found": "提交不存在,或者没有权限", + } + try { + await retryFlowchartSubmission(submissionId) + } catch (err: any) { + message.error(retryTips[err?.error] ?? "重新评分失败") + return + } message.success("重新评分已提交") listSubmissions() } diff --git a/apps/web/src/shared/components/FlowchartStatisticsPanel.vue b/apps/web/src/shared/components/FlowchartStatisticsPanel.vue index b110438..79f2204 100644 --- a/apps/web/src/shared/components/FlowchartStatisticsPanel.vue +++ b/apps/web/src/shared/components/FlowchartStatisticsPanel.vue @@ -203,6 +203,8 @@ interface Props { const props = defineProps() +const message = useMessage() + const durationOptions: SelectOption[] = [ { label: "10分钟内", value: "minutes:10" }, { label: "20分钟内", value: "minutes:20" }, @@ -528,12 +530,18 @@ async function handleStatistics() { query.duration === "all" ? { end } : { start: formatISO(sub(current, subOptions.value)), end } - const res = await getFlowchartStatistics( - duration, - query.problem, - query.username, - ) - Object.assign(data, res) + try { + const res = await getFlowchartStatistics( + duration, + query.problem, + query.username, + ) + Object.assign(data, res) + } catch (error) { + message.error("获取流程图统计失败") + console.error("获取流程图统计失败:", error) + return + } await nextTick() renderWordCloud() }