add state for submitting button
Some checks failed
Deploy / deploy (build, debian, 22, /root/OJDeploy/data/clientnext) (push) Has been cancelled
Deploy / deploy (build:staging, school, 8822, /root/OJ/data/dist) (push) Has been cancelled

This commit is contained in:
2026-06-14 08:52:47 -06:00
parent 43a5c923b4
commit be0bc87d47
3 changed files with 137 additions and 43 deletions

View File

@@ -14,6 +14,7 @@ import { useSubmissionMonitor } from "oj/problem/composables/useSubmissionMonito
import { LANGUAGE_FORMAT_VALUE, SubmissionStatus } from "utils/constants" import { LANGUAGE_FORMAT_VALUE, SubmissionStatus } from "utils/constants"
import type { SubmitCodePayload } from "utils/types" import type { SubmitCodePayload } from "utils/types"
import SubmissionResult from "./SubmissionResult.vue" import SubmissionResult from "./SubmissionResult.vue"
import { getSubmitButtonState } from "./submitButtonState"
import { useBreakpoints } from "shared/composables/breakpoints" import { useBreakpoints } from "shared/composables/breakpoints"
import { useUserStore } from "shared/store/user" import { useUserStore } from "shared/store/user"
import { checkPythonSyntax } from "oj/problem/utils/pythonSyntaxCheck" import { checkPythonSyntax } from "oj/problem/utils/pythonSyntaxCheck"
@@ -42,16 +43,12 @@ const { isDesktop } = useBreakpoints()
const { celebrate } = useFireworks() const { celebrate } = useFireworks()
// ==================== 判题监控 ==================== // ==================== 判题监控 ====================
const { const { submission, judging, pending, submitting, startMonitoring } =
submission, useSubmissionMonitor()
judging,
pending,
submitting,
isProcessing,
startMonitoring,
} = useSubmissionMonitor()
const showResult = ref(false) const showResult = ref(false)
const isFormatting = ref(false)
const isSubmittingRequest = ref(false)
// ==================== 提交冷却 ==================== // ==================== 提交冷却 ====================
const { start: startCooldown, isPending: isCooldown } = useTimeout(5000, { const { start: startCooldown, isPending: isCooldown } = useTimeout(5000, {
@@ -85,35 +82,20 @@ const { start: goToProblemSetDelayed } = useTimeoutFn(
) )
// ==================== 计算属性 ==================== // ==================== 计算属性 ====================
// 按钮禁用逻辑 const buttonState = computed(() =>
const submitDisabled = computed(() => { getSubmitButtonState({
return ( isAuthed: userStore.isAuthed,
!userStore.isAuthed || hasCode: codeStore.code.value.trim() !== "",
codeStore.code.value.trim() === "" || isFormatting: isFormatting.value,
isProcessing.value || isSubmitting: isSubmittingRequest.value || submitting.value,
isCooldown.value isJudging: judging.value || pending.value,
) isCooldown: isCooldown.value,
}) }),
)
// 按钮文案
const submitLabel = computed(() => {
if (!userStore.isAuthed) return "请先登录"
if (submitting.value) return "正在提交"
if (judging.value || pending.value) return "正在评分"
if (isCooldown.value) return "正在冷却"
return "提交代码"
})
// 按钮图标
const submitIcon = computed(() => {
if (isProcessing.value) return "eos-icons:loading"
if (isCooldown.value) return "ph:lightbulb-fill"
return "ph:play-fill"
})
// ==================== 提交函数 ==================== // ==================== 提交函数 ====================
async function submit() { async function submit() {
if (!userStore.isAuthed) return if (buttonState.value.disabled) return
// 0. Python3 语法检测 // 0. Python3 语法检测
if (codeStore.code.language === "Python3") { if (codeStore.code.language === "Python3") {
@@ -127,6 +109,7 @@ async function submit() {
// 0.5 提交前自动格式化Python3 用 ruffC/C++ 用 clang-format // 0.5 提交前自动格式化Python3 用 ruffC/C++ 用 clang-format
const formatLang = LANGUAGE_FORMAT_VALUE[codeStore.code.language] const formatLang = LANGUAGE_FORMAT_VALUE[codeStore.code.language]
if (["python", "c", "cpp"].includes(formatLang)) { if (["python", "c", "cpp"].includes(formatLang)) {
isFormatting.value = true
try { try {
const res = await formatCode({ const res = await formatCode({
code: codeStore.code.value, code: codeStore.code.value,
@@ -140,6 +123,8 @@ async function submit() {
return return
} }
// server-error / 网络异常:格式化工具问题,静默降级,提交原代码 // server-error / 网络异常:格式化工具问题,静默降级,提交原代码
} finally {
isFormatting.value = false
} }
} }
@@ -153,13 +138,18 @@ async function submit() {
data.contest_id = parseInt(contestID) data.contest_id = parseInt(contestID)
} }
// 2. 提交代码到后端 // 2. 提交代码到后端
const res = await submitCode(data) isSubmittingRequest.value = true
console.log(`[Submit] 代码已提交: ID=${res.data.submission_id}`) try {
const res = await submitCode(data)
console.log(`[Submit] 代码已提交: ID=${res.data.submission_id}`)
// 3. 启动冷却 + 监控 // 3. 启动冷却 + 监控
startCooldown() startCooldown()
startMonitoring(res.data.submission_id) startMonitoring(res.data.submission_id)
showResult.value = true showResult.value = true
} finally {
isSubmittingRequest.value = false
}
} }
// ==================== 失败计数 ==================== // ==================== 失败计数 ====================
@@ -237,15 +227,15 @@ watch(
<n-button <n-button
:size="isDesktop ? 'medium' : 'small'" :size="isDesktop ? 'medium' : 'small'"
type="primary" type="primary"
:disabled="submitDisabled" :disabled="buttonState.disabled"
@click="submit" @click="submit"
> >
<template #icon> <template #icon>
<n-icon> <n-icon>
<Icon :icon="submitIcon" /> <Icon :icon="buttonState.icon" />
</n-icon> </n-icon>
</template> </template>
{{ submitLabel }} {{ buttonState.label }}
</n-button> </n-button>
</template> </template>

View File

@@ -0,0 +1,53 @@
export interface SubmitButtonStateInput {
isAuthed: boolean
hasCode: boolean
isFormatting: boolean
isSubmitting: boolean
isJudging: boolean
isCooldown: boolean
}
export interface SubmitButtonState {
disabled: boolean
label: string
icon: string
}
export function getSubmitButtonState({
isAuthed,
hasCode,
isFormatting,
isSubmitting,
isJudging,
isCooldown,
}: SubmitButtonStateInput): SubmitButtonState {
const disabled =
!isAuthed ||
!hasCode ||
isFormatting ||
isSubmitting ||
isJudging ||
isCooldown
let label = "提交代码"
if (!isAuthed) {
label = "请先登录"
} else if (isFormatting) {
label = "格式化中"
} else if (isSubmitting) {
label = "正在提交"
} else if (isJudging) {
label = "正在评分"
} else if (isCooldown) {
label = "正在冷却"
}
const icon =
isFormatting || isSubmitting || isJudging
? "eos-icons:loading"
: isCooldown
? "ph:lightbulb-fill"
: "ph:play-fill"
return { disabled, label, icon }
}

View File

@@ -0,0 +1,51 @@
import assert from "node:assert/strict"
import test from "node:test"
import { getSubmitButtonState } from "../src/oj/problem/components/submitButtonState.ts"
const idleInput = {
isAuthed: true,
hasCode: true,
isFormatting: false,
isSubmitting: false,
isJudging: false,
isCooldown: false,
}
test("shows a disabled loading state while formatting", () => {
assert.deepEqual(getSubmitButtonState({ ...idleInput, isFormatting: true }), {
disabled: true,
label: "格式化中",
icon: "eos-icons:loading",
})
})
test("shows submitting immediately after formatting", () => {
assert.deepEqual(getSubmitButtonState({ ...idleInput, isSubmitting: true }), {
disabled: true,
label: "正在提交",
icon: "eos-icons:loading",
})
})
test("preserves existing login, judging, cooldown, and idle states", () => {
assert.deepEqual(getSubmitButtonState({ ...idleInput, isAuthed: false }), {
disabled: true,
label: "请先登录",
icon: "ph:play-fill",
})
assert.deepEqual(getSubmitButtonState({ ...idleInput, isJudging: true }), {
disabled: true,
label: "正在评分",
icon: "eos-icons:loading",
})
assert.deepEqual(getSubmitButtonState({ ...idleInput, isCooldown: true }), {
disabled: true,
label: "正在冷却",
icon: "ph:lightbulb-fill",
})
assert.deepEqual(getSubmitButtonState(idleInput), {
disabled: false,
label: "提交代码",
icon: "ph:play-fill",
})
})