add python check
This commit is contained in:
625
package-lock.json
generated
625
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
27
package.json
27
package.json
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
21
src/oj/problem/utils/pythonSyntaxCheck.ts
Normal file
21
src/oj/problem/utils/pythonSyntaxCheck.ts
Normal 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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user