From 4fd04f106acf5a044b8fb4219e4bd70b1aabe472 Mon Sep 17 00:00:00 2001 From: qubiaobiao <3294694717@qq.com> Date: Wed, 21 Oct 2026 16:49:11 +0800 Subject: [PATCH] =?UTF-8?q?fix(ui):=20=E7=BB=84=E9=94=81=E6=88=90=E5=91=98?= =?UTF-8?q?=E5=8F=AF=E4=BB=8E=E6=8F=90=E4=BA=A4=E9=A1=B5=E8=BF=94=E5=9B=9E?= =?UTF-8?q?=E5=B7=A5=E4=BD=9C=E5=8F=B0=20+=20=E5=85=8D=E5=81=87=E8=AD=A6?= =?UTF-8?q?=E5=91=8A=EF=BC=88R192=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 现象(用户实测):本科组其他成员进入提交页 → 组锁生效不能提交,但页面无任何返回 入口,只能浏览器后退;移动端/内嵌浏览器更退不出去 ⇒ 被困在提交页。 根因三层,缺一不可: 1. /competition 是全屏页(App.vue 只有 ,无全局导航)⇒ 页面内不给 按钮就真的没有出口。 2. 上传模式的「被锁」分支只有 el-alert 文案 + disabled 上传控件,从来没有动作按钮 (客观题模式被锁是 router.push('/dashboard') 直接踢走,故历史上未暴露)。 3. onBeforeRouteLeave 对 examStarted && !isTimeUp 一律弹「离开将丢失当前提交进度」—— 对被锁用户是假警告(本机没有任何可提交内容),加了按钮也会被二次劝退。 改动(唯一文件): - 上传卡片 header 新增常驻「← 返回工作台」——不依赖 examStarted(顶部倒计时栏在未开始 时不存在,放那儿会漏)。 - 组锁 prompt 下方新增 .team-locked-actions:主按钮 + 引导「本组作品已由队友提交,您无需 任何操作」;alert description 补「您本人无需提交,可直接返回工作台」。 - 新增 leaveCompetition():仅「已选文件未上传」才确认一次 → resetExam() → push('/dashboard')。 ⛔ 必须先 resetExam() 再 push:否则 ①守卫再弹一次(双确认)②store 是全局单例, 定时器残留 ⇒ 回工作台后倒计时仍在跑。用 resetExam 而非只 stopExam,是为了与守卫里 「确认离开后」的清理同口径(两套清理逻辑必漏项)。 - onBeforeRouteLeave 新增豁免 lockedNoContent = isUploadMode && teamLocked。 ⛔ 豁免面必须写窄,不能只判 teamLocked:当前两者等价(考试组不拉 uploadStatus), 但只写 teamLocked 会把考试组一并纳入,将来考试组一旦拉 status 就静默取消「交卷前离开」的确认。 验证(真实组锁数据 + 真 chromium,非构造): - 库里本就有现场:benke_submissions 归属 contestant 11(郑佳怡),contestant 12 同 team_uid=1 ⇒ 真接口返回 teamLocked=true / memberCount=5 / canUpload=false。 - 登录态用 jwt_fallback_secret 自签 token 注入 localStorage(未改任何人口令)。 - 组锁视角 8/8 PASS(header 出口 · 组锁区块 + 引导 · 上传仍 disabled · 点击落 /dashboard · messageBox=0 · console 零错误);归属人视角 5/5 PASS(无组锁区块 · 出口仍在 · 不误弹)。 - 变异验证:两按钮加 v-if="false" ⇒ 4 条断言转红;备份还原后 diff 为空、复跑 8/8 复绿。 - 数据零改动:12 号口令未变 · benke_submissions 仍 1 行 · 落盘 PDF 字节数原样。 - 静态:verify_frontend_static.mjs PASS(TDZ 0 / F2 0)· SFC compileTemplate 0 error。 --- src/views/Competition.vue | 78 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 4 deletions(-) diff --git a/src/views/Competition.vue b/src/views/Competition.vue index 82599c4..b1c0564 100644 --- a/src/views/Competition.vue +++ b/src/views/Competition.vue @@ -21,7 +21,13 @@
@@ -107,8 +113,14 @@ show-icon style="margin-top:12px" title="组内作品唯一:本组已有成员上传,您无法再上传" - description="如需替换作品,请联系上传者本人操作(仅首个上传者可在截止前替换)。" + description="如需替换作品,请联系上传者本人操作(仅首个上传者可在截止前替换)。您本人无需提交,可直接返回工作台。" /> + +
+ 返回工作台 + 本组作品已由队友提交,您无需任何操作。 +
@@ -405,7 +417,7 @@ import { ref, computed, onMounted, onUnmounted, watch } from 'vue' import { useRouter, onBeforeRouteLeave } from 'vue-router' import { ElMessage, ElMessageBox, ElNotification } from 'element-plus' -import { Timer, UploadFilled } from '@element-plus/icons-vue' +import { Timer, UploadFilled, Back } from '@element-plus/icons-vue' import { useAuthStore } from '@/stores/auth' import { useExamStore } from '@/stores/exam' import * as examApi from '@/api/exam' @@ -516,6 +528,36 @@ function clearUploadFiles() { uploadRef.value?.clearFiles?.() } +/** + * 返回工作台(R192)—— 上传模式的统一出口。 + * + * 🔴 必须先 resetExam() 再 push,理由有三条: + * ① 不清 `examStarted` ⇒ `onBeforeRouteLeave` 会再弹一次「离开页面将丢失当前提交进度」。 + * 上传模式下用户常常只是放弃提交(甚至被组锁、本机根本没有可提交内容), + * 这句文案既失真又构成二次打扰; + * ② 不清 timer ⇒ store 是全局单例,回到工作台后倒计时仍在跑,下次进入会看到陈旧剩余时间; + * ③ 用 resetExam(而非只 stopExam)与 `onBeforeRouteLeave` 里确认离开后的清理**同一口径**, + * ⛔ 不留「按钮退出」与「路由守卫退出」两套清理逻辑(漏项会以「下次进入状态诡异」的形式出现)。 + * + * 只有「待上传列表里有已选文件」才算真有内容会丢,此时才确认一次。 + * 组锁用户的上传控件是 disabled 的,选不出文件 ⇒ 不弹确认,直接离开。 + */ +async function leaveCompetition() { + if (!teamLocked.value && uploadFileList.value.length > 0) { + try { + await ElMessageBox.confirm( + '已选择的文件尚未上传,离开后需要重新选择。确定返回工作台吗?', + '提示', + { confirmButtonText: '返回工作台', cancelButtonText: '继续上传', type: 'warning' } + ) + } catch { + return + } + } + examStore.resetExam() + router.push('/dashboard') +} + async function submitUploadFiles() { const files = pendingUploadFiles.value if (!files.length) return @@ -1031,7 +1073,13 @@ function preventKeyboard(e) { // 路由离开警告 onBeforeRouteLeave((to, from, next) => { - if (examStore.examStarted && !examStore.isTimeUp) { + // 🔴 组锁用户不弹:上传控件 disabled、无交卷动作 ⇒ 本机没有任何可丢的内容。 + // 对这类用户弹「离开将丢失提交进度」是假警告(体验反馈:被锁成员被困在提交页)。 + // 判定写成「上传模式 && teamLocked」而不是只看 teamLocked:后者当前虽等价 + // (客观题模式不拉 uploadStatus),但语义上把考试组也纳入了豁免面,将来一旦 + // 考试组也拉 status,就会静默取消「交卷前离开」的确认 —— 这是不可接受的放松。 + const lockedNoContent = isUploadMode.value && teamLocked.value + if (examStore.examStarted && !examStore.isTimeUp && !lockedNoContent) { const verb = isUploadMode.value ? '提交' : '答题' ElMessageBox.confirm( `离开页面将丢失当前${verb}进度,确定要离开吗?`, @@ -1579,6 +1627,28 @@ onUnmounted(() => { justify-content: space-between; } +/* ==================== R192:上传页出口(组锁 / 主动放弃提交) ==================== */ +/* 本页无全局导航(App.vue 只有 router-view)⇒ 出口按钮必须常驻,否则被组锁的 + 成员只能靠浏览器后退离开(移动端/内嵌浏览器常常退不出去)。 */ +.upload-header { + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 0.75rem; +} +.team-locked-actions { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 0.75rem; + margin-top: 0.75rem; +} +.team-locked-hint { + font-size: 0.85rem; + color: #909399; +} + /* ==================== R190:小组作品状态面板 ==================== */ .team-status-panel { border: 1px solid #c6e2ff;