From c018c147c1e7a891842605ffde23ffb6a5a33559 Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Wed, 14 Oct 2026 09:21:23 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E6=8F=90=E4=BA=A4=E5=88=97=E8=A1=A8):=20?= =?UTF-8?q?=E6=89=8B=E6=9C=BA=E4=B8=8A=E5=A5=BD=E7=94=A8=E5=A4=9A=E4=BA=86?= =?UTF-8?q?=EF=BC=9A=E7=AD=9B=E9=80=89=E9=87=8C=E8=A1=A5=E4=B8=8A=E3=80=8C?= =?UTF-8?q?=E8=BF=99=E8=8A=82=E8=AF=BE=E3=80=8D=E3=80=81=E7=8F=AD=E7=BA=A7?= =?UTF-8?q?=E3=80=81=E5=8F=AA=E7=9C=8B=E6=9F=90=E4=B8=AA=E5=AD=A6=E7=94=9F?= =?UTF-8?q?=EF=BC=8C=E9=A1=B6=E4=B8=8A=E5=8A=A0=E4=BA=86=E7=BB=9F=E8=AE=A1?= =?UTF-8?q?=E5=85=A5=E5=8F=A3=EF=BC=9B=E7=82=B9=E5=BC=80=E8=AF=A6=E6=83=85?= =?UTF-8?q?=E5=90=8D=E5=AD=97=E3=80=81=E9=A2=98=E7=9B=AE=E3=80=81=E6=8C=89?= =?UTF-8?q?=E9=92=AE=E5=88=86=E4=B8=89=E8=A1=8C=E6=8E=92=EF=BC=8C=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=AD=97=E5=8F=B7=E6=94=B9=E5=B0=8F=EF=BC=9B=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E5=9B=BE=E7=9A=84=E8=AF=84=E5=88=86=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E8=A2=AB=E5=8E=8B=E6=88=90=E4=B8=80=E6=9D=A1=E7=BC=9D=EF=BC=9B?= =?UTF-8?q?=E7=BF=BB=E9=A1=B5=E4=B8=8D=E5=86=8D=E9=A1=B6=E5=87=BA=E5=B1=8F?= =?UTF-8?q?=E5=B9=95=E3=80=81=E7=BF=BB=E5=AE=8C=E5=9B=9E=E5=88=B0=E9=A1=B6?= =?UTF-8?q?=E4=B8=8A=EF=BC=9B=E7=94=B5=E8=84=91=E4=B8=8A=E3=80=8C=E5=A4=8D?= =?UTF-8?q?=E5=88=B6=E5=9B=9E=E5=88=B0=E9=A2=98=E7=9B=AE=E3=80=8D=E3=80=8C?= =?UTF-8?q?=E5=A4=8D=E5=88=B6=E5=88=B0=E8=87=AA=E6=B5=8B=E7=8C=AB=E3=80=8D?= =?UTF-8?q?=E6=8C=AA=E5=88=B0=E5=8F=B3=E6=A0=8F=E5=BA=95=E6=A0=8F=EF=BC=8C?= =?UTF-8?q?=E8=80=81=E5=B8=88=E4=B8=8D=E7=94=A8=E5=86=8D=E8=BF=9B=E3=80=8C?= =?UTF-8?q?=E2=8B=AF=E3=80=8D=E6=89=BE=EF=BC=8C=E9=A2=98=E7=9B=AE=E5=90=8D?= =?UTF-8?q?=E4=B9=9F=E4=B8=8D=E5=86=8D=E8=A2=AB=E6=8C=89=E9=92=AE=E6=8C=A4?= =?UTF-8?q?=E6=B2=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../submission/components/FlowchartPane.vue | 80 ++++-- .../submission/components/SubmissionPane.vue | 267 +++++++++++------- apps/web/src/oj/submission/list.vue | 244 ++++++++++++++-- apps/web/src/shared/components/Pagination.vue | 2 +- 4 files changed, 449 insertions(+), 144 deletions(-) diff --git a/apps/web/src/oj/submission/components/FlowchartPane.vue b/apps/web/src/oj/submission/components/FlowchartPane.vue index eac785a..6ec3f70 100644 --- a/apps/web/src/oj/submission/components/FlowchartPane.vue +++ b/apps/web/src/oj/submission/components/FlowchartPane.vue @@ -169,24 +169,27 @@ const canRetry = computed( - · - - {{ row.problemDisplayId }} - {{ row.problemTitle }} - - + · + + + + {{ row.problemDisplayId }} + {{ row.problemTitle }} + + +
props.teacher && !props.contest) /** - * 老师的「⋯」:复制两项 + 重新判题。重判不常用、点错了会真的重判,所以不放在外面当最显眼的 - * 那个按钮。代码还没拉下来时只有重判一项 —— 代码拉不下来的那条,恰恰可能要重判 + * 老师的「⋯」只剩重新判题:不常用、点错了会真的重判,所以不摆在外面。 + * 复制两项和学生一样摆出来(原来也收在这里,老师要多点一下) */ -const moreOptions = computed(() => { - const copy = shown.value - ? [ - { label: "复制回到题目", key: "problem" }, - ...(shown.value.language !== "SQL" ? [{ label: "复制到自测猫", key: "cat" }] : []), - ] - : [] - if (!canRejudge.value) return copy - return [ - ...copy, - ...(copy.length ? [{ type: "divider", key: "divider" }] : []), - { label: "重新判题", key: "rejudge" }, - ] -}) +const moreOptions = computed(() => + canRejudge.value ? [{ label: "重新判题", key: "rejudge" }] : [], +) function handleMore(key: string) { - if (key === "rejudge") return emit("rejudge", props.row.id) - if (!shown.value) return - if (key === "problem") copyToProblem(shown.value) - else copyToCat(shown.value) + if (key === "rejudge") emit("rejudge", props.row.id) } function openStandalone() { @@ -198,79 +184,87 @@ function openStandalone() { {{ classLabel(row.className) }} - · - - {{ row.problemDisplayId }} - {{ row.problemTitle }} - - - - 题单 {{ row.problemSet.title }} - + · + + + + {{ row.problemDisplayId }} + {{ row.problemTitle }} + + + + 题单 {{ row.problemSet.title }} + +
- +
@@ -323,6 +317,9 @@ function openStandalone() { :code="shown.code" :language="LANGUAGE_FORMAT_VALUE[shown.language]" :mark-line="explain?.line ?? null" + :font-size="narrow ? 14 : 20" + :line-height="narrow ? 24 : 32" + class="code" />
@@ -335,14 +332,23 @@ function openStandalone() {
↑↓ - - {{ - teacher - ? "换上一条 / 下一条,到本页头尾自动翻页" - : "换上一条 / 下一条(只在能看代码的提交之间走)" - }} + + {{ teacher ? "换上一条 / 下一条,到本页头尾自动翻页" : "换上一条 / 下一条,锁着的会跳过" }}
+ 本页第 {{ position.index }} / {{ position.count }} 条
@@ -380,17 +386,74 @@ function openStandalone() { flex: 0 1 auto; } +.problem-group, +.actions { + display: contents; +} + +/* 手机整屏详情:第一行名字(+ 班级),第二行题目,第三行按钮。 + 整个面板一起滚,代码有多长画多长,不在框里再套一层上下滚 */ .narrow { - padding: 12px 14px; + padding: 12px 14px 16px; + overflow-y: auto; } .narrow .head { flex-wrap: wrap; - row-gap: 8px; + row-gap: 10px; + white-space: normal; +} + +.narrow .head .who { + flex-shrink: 1; + flex-wrap: wrap; + row-gap: 6px; } .narrow .head .spacer { + order: 1; +} + +.narrow .problem-group { + order: 3; flex-basis: 100%; + min-width: 0; + display: flex; + align-items: center; + gap: 8px; +} + +.narrow .problem { + white-space: normal; +} + +.narrow .actions { + order: 4; + flex-basis: 100%; + display: flex; + align-items: center; + gap: 8px; +} + +/* 手指点的:漏斗按钮放大一圈 */ +.narrow .filter-pill { + width: 32px; + height: 28px; + border-radius: 14px; +} + +.narrow .filter-pill.wide { + width: auto; + padding: 0 10px; +} + +.narrow .code { + flex: none; +} + +.narrow .locked { + flex: 1 0 auto; + min-height: 280px; } .user-link { @@ -556,14 +619,28 @@ function openStandalone() { font-weight: 600; } +/* 高度按按钮定死:代码还在拉、按钮没出来时这一栏不变矮,代码区不跟着上下跳 */ .foot { + min-height: 28px; display: flex; align-items: center; gap: 8px; + white-space: nowrap; font-size: 12px; color: v-bind("theme.textColor3"); } +/* 1280 的屏上这一栏只剩几像素:字体宽一点的机器上让提示省略,别把按钮挤出去 */ +.hint { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; +} + +.foot > :not(.hint) { + flex-shrink: 0; +} + .keys { display: inline-flex; gap: 3px; diff --git a/apps/web/src/oj/submission/list.vue b/apps/web/src/oj/submission/list.vue index b075c68..c53e0fc 100644 --- a/apps/web/src/oj/submission/list.vue +++ b/apps/web/src/oj/submission/list.vue @@ -175,6 +175,8 @@ async function listSubmissions() { // 翻了页、换了条件就回到列表顶上(往回翻到上一页末尾的,settleSelection 会滚到那一条) const scroller = document.querySelector(".rows") if (scroller) scroller.scrollTop = 0 + // 手机上整页滚,滚的是外层 n-layout 的容器:翻到下一页原来停在页尾 + if (isMobile.value) pageScroller()?.scrollTo({ top: 0 }) settleSelection() } catch { // 失败了别把「落地后停在哪一头」留给下一次不相干的加载 @@ -184,6 +186,19 @@ async function listSubmissions() { } } +const pageRoot = useTemplateRef("pageRoot") + +/** 往上找真正在滚的那层(同自学页:n-layout-content 也带 scroll-container 类却不滚) */ +function pageScroller(): HTMLElement | null { + for (let el = pageRoot.value?.parentElement; el; el = el.parentElement) { + const overflow = getComputedStyle(el).overflowY + if ((overflow === "auto" || overflow === "scroll") && el.scrollHeight > el.clientHeight) { + return el + } + } + return null +} + async function getTodayCount() { todayCount.value = await getTodaySubmissionCount(flowMode.value ? "Flowchart" : undefined) } @@ -589,6 +604,20 @@ function openStatistics() { statsPanel.value = true } +/** 手机上的「统计」:同样的条件,带去统计页 */ +function openStatisticsPage() { + router.push({ + name: "statistics", + query: { + tab: flowMode.value ? "flow" : "code", + ...(query.className ? { className: query.className } : {}), + ...(query.username ? { username: query.username } : {}), + ...(query.problem ? { problem: query.problem } : {}), + ...(query.today === "1" ? { period: "today" } : {}), + }, + }) +} + /** 统计里的「在提交列表里看」:直接改背后这张列表的筛选,关掉弹框 */ function listFromStatistics(next: Record) { statsPanel.value = false @@ -702,7 +731,7 @@ function dayBreak(index: number) {
@@ -979,10 +1079,17 @@ function dayBreak(index: number) {
- {{ submissionClockText(row.createTime) }} + {{ submissionClockText(row.createTime) }} {{ row.problemDisplayId }} {{ row.problemTitle }} + 题单
- {{ languageText(row) }} + {{ languageText(row) }} +
@@ -1180,12 +1287,59 @@ function dayBreak(index: number) { padding: 0 14px; } +.mobile-filters { + height: 56px; + gap: 4px; +} + .mobile-panel { display: flex; flex-direction: column; - gap: 8px; - padding: 10px 14px; + align-items: stretch; + gap: 10px; + padding: 12px 14px 14px; border-bottom: 1px solid v-bind("theme.dividerColor"); + background: v-bind("theme.actionColor"); +} + +.mobile-panel > .n-radio-group, +.mobile-panel > .chip { + align-self: flex-start; +} + +.mobile-panel .lesson { + max-width: 100%; + min-width: 0; + align-self: flex-start; +} + +.lesson-text { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.mobile-panel .chip.person { + max-width: 100%; + align-self: flex-start; +} + +/* 结果、语言两个下拉并排:各占一半 */ +.mobile-pair { + display: flex; + gap: 8px; +} + +.mobile-pair > * { + flex: 1 1 0; + min-width: 0; +} + +.mobile-panel-foot { + display: flex; + align-items: center; + gap: 8px; } .vsep { @@ -1416,11 +1570,41 @@ function dayBreak(index: number) { } .m-line .title { + min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.m-time, +.m-lang { + flex: none; + white-space: nowrap; + font-variant-numeric: tabular-nums; +} + +.m-lock { + flex: none; + opacity: 0.7; +} + +/* 手机上的 day 行和列表行一样贴两边 */ +.mobile .day { + padding: 0 14px; +} + +/* 手机:「共 N 条」和翻页上下排,翻页按钮居中,不再顶出屏幕右边 */ +.mobile .list-foot { + height: auto; + padding: 12px 14px 16px; + flex-direction: column; + gap: 8px; +} + +.mobile .list-foot .spacer { + display: none; +} + /* 「23:59:59」8 个等宽数字 */ .c-time { width: 64px; diff --git a/apps/web/src/shared/components/Pagination.vue b/apps/web/src/shared/components/Pagination.vue index bf83cc8..8f0622f 100644 --- a/apps/web/src/shared/components/Pagination.vue +++ b/apps/web/src/shared/components/Pagination.vue @@ -42,7 +42,7 @@ watch( v-model:page-size="limit" :page-sizes="sizes" :page-slot="isDesktop ? 7 : 5" - show-size-picker + :show-size-picker="isDesktop" />