add python check
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-03-25 07:02:02 -06:00
parent 44b9e6d8dc
commit 52f274fdc9
4 changed files with 371 additions and 320 deletions

625
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,7 @@
"fmt": "prettier --write src *.ts" "fmt": "prettier --write src *.ts"
}, },
"dependencies": { "dependencies": {
"@codemirror/autocomplete": "^6.20.0", "@codemirror/autocomplete": "^6.20.1",
"@codemirror/lang-cpp": "^6.0.3", "@codemirror/lang-cpp": "^6.0.3",
"@codemirror/lang-python": "^6.2.1", "@codemirror/lang-python": "^6.2.1",
"@vue-flow/background": "^1.3.2", "@vue-flow/background": "^1.3.2",
@@ -21,7 +21,7 @@
"@vue-flow/node-toolbar": "^1.1.1", "@vue-flow/node-toolbar": "^1.1.1",
"@vueuse/core": "^14.2.1", "@vueuse/core": "^14.2.1",
"@vueuse/router": "^14.2.1", "@vueuse/router": "^14.2.1",
"@wangeditor-next/editor": "^5.6.49", "@wangeditor-next/editor": "^5.6.55",
"@wangeditor-next/editor-for-vue": "^5.1.14", "@wangeditor-next/editor-for-vue": "^5.1.14",
"axios": "^1.13.6", "axios": "^1.13.6",
"canvas-confetti": "^1.9.4", "canvas-confetti": "^1.9.4",
@@ -31,29 +31,30 @@
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"fflate": "^0.8.2", "fflate": "^0.8.2",
"highlight.js": "^11.11.1", "highlight.js": "^11.11.1",
"md-editor-v3": "^6.3.1", "md-editor-v3": "^6.4.1",
"mermaid": "^11.12.3", "mermaid": "^11.13.0",
"naive-ui": "^2.43.2", "naive-ui": "^2.44.1",
"nanoid": "^5.1.6", "nanoid": "^5.1.7",
"normalize.css": "^8.0.1", "normalize.css": "^8.0.1",
"pinia": "^3.0.4", "pinia": "^3.0.4",
"vue": "^3.5.29", "skulpt": "^1.2.0",
"vue": "^3.5.31",
"vue-chartjs": "^5.3.3", "vue-chartjs": "^5.3.3",
"vue-codemirror": "^6.1.1", "vue-codemirror": "^6.1.1",
"vue-router": "^5.0.3", "vue-router": "^5.0.4",
"y-codemirror.next": "^0.3.5", "y-codemirror.next": "^0.3.5",
"y-webrtc": "^10.3.0", "y-webrtc": "^10.3.0",
"yjs": "^13.6.29" "yjs": "^13.6.30"
}, },
"devDependencies": { "devDependencies": {
"@iconify/vue": "^5.0.0", "@iconify/vue": "^5.0.0",
"@rsbuild/core": "^1.7.3", "@rsbuild/core": "^1.7.3",
"@rsbuild/plugin-vue": "^1.2.6", "@rsbuild/plugin-vue": "^1.2.7",
"@types/canvas-confetti": "^1.9.0", "@types/canvas-confetti": "^1.9.0",
"@types/node": "^25.3.2", "@types/node": "^25.5.0",
"prettier": "^3.8.1", "prettier": "^3.8.1",
"typescript": "^5.9.3", "typescript": "^6.0.2",
"unplugin-auto-import": "^21.0.0", "unplugin-auto-import": "^21.0.0",
"unplugin-vue-components": "^31.0.0" "unplugin-vue-components": "^32.0.0"
} }
} }

View File

@@ -11,6 +11,7 @@ import type { SubmitCodePayload } from "utils/types"
import SubmissionResult from "./SubmissionResult.vue" import SubmissionResult from "./SubmissionResult.vue"
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"
// ==================== 异步组件 ==================== // ==================== 异步组件 ====================
const ProblemComment = defineAsyncComponent( const ProblemComment = defineAsyncComponent(
@@ -28,6 +29,7 @@ const problemSetId = <string>route.params.problemSetId ?? ""
const router = useRouter() const router = useRouter()
const [commentPanel] = useToggle() const [commentPanel] = useToggle()
const message = useMessage()
const { isDesktop } = useBreakpoints() const { isDesktop } = useBreakpoints()
@@ -44,6 +46,8 @@ const {
startMonitoring, startMonitoring,
} = useSubmissionMonitor() } = useSubmissionMonitor()
const showResult = ref(false)
// ==================== 提交冷却 ==================== // ==================== 提交冷却 ====================
const { start: startCooldown, isPending: isCooldown } = useTimeout(5000, { const { start: startCooldown, isPending: isCooldown } = useTimeout(5000, {
controls: true, controls: true,
@@ -106,6 +110,15 @@ const submitIcon = computed(() => {
async function submit() { async function submit() {
if (!userStore.isAuthed) return if (!userStore.isAuthed) return
// 0. Python3 语法检测
if (codeStore.code.language === "Python3") {
const syntaxError = checkPythonSyntax(codeStore.code.value)
if (syntaxError) {
message.warning(`${syntaxError.line} 行存在语法错误,请修正后再提交`)
return
}
}
// 1. 构建提交数据 // 1. 构建提交数据
const data: SubmitCodePayload = { const data: SubmitCodePayload = {
problem_id: problem.value!.id, problem_id: problem.value!.id,
@@ -122,6 +135,7 @@ async function submit() {
// 3. 启动冷却 + 监控 // 3. 启动冷却 + 监控
startCooldown() startCooldown()
startMonitoring(res.data.submission_id) startMonitoring(res.data.submission_id)
showResult.value = true
} }
// ==================== 失败计数 ==================== // ==================== 失败计数 ====================
@@ -178,11 +192,13 @@ watch(
<template> <template>
<!-- 提交按钮 + 结果弹窗 --> <!-- 提交按钮 + 结果弹窗 -->
<n-popover <n-popover
trigger="click" trigger="manual"
placement="bottom-end" placement="bottom-end"
scrollable scrollable
:show-arrow="false" :show-arrow="false"
style="max-height: 600px" style="max-height: 600px"
:show="showResult"
@clickoutside="showResult = false"
> >
<template #trigger> <template #trigger>
<n-button <n-button

View File

@@ -0,0 +1,21 @@
// @ts-ignore - skulpt has no type definitions
import Sk from "skulpt"
export interface PythonSyntaxError {
line: number
}
/**
* 用 Skulpt 检测 Python 代码中的语法错误。
* 只编译不执行,不受 input() 等 IO 调用影响。
*/
export function checkPythonSyntax(code: string): PythonSyntaxError | null {
Sk.configure({ output: () => {} })
try {
Sk.compile(code, "prog.py", "exec")
return null
} catch (e: any) {
const line: number = e?.traceback?.[0]?.lineno ?? 1
return { line }
}
}