feat(题目): 手机上「运行例子 / 提交」固定在屏幕底部,看着题目也能直接交

- 两个 44px 高的大按钮固定在底部,三个页签下都在;原来它们在「代码」页签顶上,
  和语言、更多挤成两行,看着题面想交一次还得先切回代码页签
- 「代码」页签的编辑器一直挂着(原来切走就卸载):没打开过代码页签也能交,撤销历史也不丢
- 编辑器高度让出底部那条,最后几行不再被按钮盖住

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-09-28 18:33:17 +08:00
1 parent 0c2ff68e9b
commit 9110c678fc
6 files changed
+96 -10

No files matched your search

@@ -269,8 +269,9 @@ const goEdit = () => {
@update:value="changeLanguage"
/>
<!-- 手机上「运行例子」「提交」在屏幕底部那条(MobileActionBar),三个页签都在 -->
<n-button
v-if="canRunSamples"
v-if="canRunSamples && isDesktop"
:size="buttonSize"
:loading="samplesRunning"
:disabled="!codeStore.code.value.trim()"
@@ -281,7 +282,7 @@ const goEdit = () => {
<SubmitFlowchart v-if="codeStore.code.language === 'Flowchart'" />
<SubmitCode v-else />
<SubmitCode v-else-if="isDesktop" />
<n-button
v-if="isDesktop && userStore.isTeacherOrAbove"
@@ -0,0 +1,70 @@
<script setup lang="ts">
import { useThemeVars } from "naive-ui"
import { storeToRefs } from "pinia"
import { useCodeStore } from "oj/store/code"
import { useProblemStore } from "oj/store/problem"
import { useSubmissionStore } from "oj/store/submission"
import SubmitCode from "./SubmitCode.vue"
/**
* 手机上屏幕底部固定的「运行例子 / 提交」(设计文档 5.6)。原来这两个按钮在「代码」页签
* 顶上,和语言、更多挤成两行;看着题面想交一次,还得先切回代码页签。
* 提交状态、代码都在 store 里,编辑器没挂着也能交。
*/
const theme = useThemeVars()
const codeStore = useCodeStore()
const submissionStore = useSubmissionStore()
const { samplesRunning } = submissionStore.trial
const { problem } = storeToRefs(useProblemStore())
// 和工具栏同一个条件:Judge0 跑不了 SQL,流程图没得跑,没有例子的题不给
const canRunSamples = computed(
() =>
!!problem.value?.samples.length &&
codeStore.code.language !== "Flowchart" &&
codeStore.code.language !== "SQL",
)
</script>
<template>
<div class="mobile-actions">
<n-button
v-if="canRunSamples"
size="large"
class="action"
:loading="samplesRunning"
:disabled="!codeStore.code.value.trim()"
@click="submissionStore.runSamples()"
>
运行例子
</n-button>
<div class="action">
<SubmitCode size="large" block />
</div>
</div>
</template>
<style scoped>
.mobile-actions {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 10;
display: flex;
gap: 10px;
padding: 8px 16px calc(8px + env(safe-area-inset-bottom));
background-color: v-bind("theme.bodyColor");
border-top: 1px solid rgba(128, 128, 128, 0.2);
backdrop-filter: blur(8px);
}
.action {
flex: 1;
min-width: 0;
}
.mobile-actions :deep(.n-button) {
height: 44px;
}
</style>
@@ -98,9 +98,10 @@ const storageKey = computed(() =>
: `problem_${problem.value!._id}_contest_${contestID}_lang_${codeStore.code.language}`,
)
// 协作条 40px + 和工具栏之间的 12px 间距,要从编辑器里让出来,不然页面底下多出一截滚动条
// 协作条 40px + 和工具栏之间的 12px 间距,要从编辑器里让出来,不然页面底下多出一截滚动条。
// 手机上:顶栏两行 + 课堂条 + 页签 + 工具栏约 236px,底部还有「运行例子 / 提交」那条约 64px
const editorHeight = computed(() => {
const base = isDesktop.value ? 133 : 172
const base = isDesktop.value ? 133 : 300
return `calc(100vh - ${base + (teacherCollab.value ? 52 : 0)}px)`
})
@@ -12,6 +12,9 @@ import { useUserStore } from "shared/store/user"
* 这个组件在编辑器的工具栏里,编辑器卸载时它也跟着卸载,所以什么都不能只存在这里。
*/
/** 手机底部那条操作栏里用大号、占满半宽 */
const props = defineProps<{ size?: "small" | "medium" | "large"; block?: boolean }>()
const userStore = useUserStore()
const codeStore = useCodeStore()
const submissionStore = useSubmissionStore()
@@ -41,7 +44,8 @@ function submit() {
<template>
<n-button
:size="isDesktop ? 'medium' : 'small'"
:size="props.size ?? (isDesktop ? 'medium' : 'small')"
:block="props.block"
type="primary"
:disabled="buttonState.disabled"
@click="submit"