From 38500671afde789617a5aa6008b1d11cf46da67d Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Thu, 2 Jul 2026 17:23:23 -0600 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20SQL=20=E9=A2=98=E5=9E=8B?= =?UTF-8?q?=E5=89=8D=E7=AB=AF=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - LANGUAGE 类型与六张语言映射表补 SQL;编辑器 SQLite 方言高亮 - 出题表单:SQL 语言选项、题型/行序配置卡片、数据脚本压缩包上传、标准答案必填校验 - 修复做题页语言回退硬编码 Python3 的问题(SQL 题会提交被拒) - SQL 题隐藏自测猫/自测屏/复制到自测猫(外部运行器不支持 SQL) Co-Authored-By: Claude Fable 5 --- package-lock.json | 3 +- package.json | 1 + src/admin/api.ts | 7 +- src/admin/problem/detail.vue | 96 ++++++++++++++++++++++-- src/oj/problem/components/Form.vue | 4 +- src/oj/problem/detail.vue | 10 +++ src/oj/submission/detail.vue | 8 +- src/shared/components/SyncCodeEditor.vue | 3 + src/shared/extensions/autocompletion.ts | 3 + src/utils/constants.ts | 6 ++ src/utils/types.ts | 9 +++ 11 files changed, 138 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index d95621c..88b006a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@codemirror/autocomplete": "^6.20.3", "@codemirror/lang-cpp": "^6.0.3", "@codemirror/lang-python": "^6.2.1", + "@codemirror/lang-sql": "^6.10.0", "@vue-flow/background": "^1.3.2", "@vue-flow/controls": "^1.1.3", "@vue-flow/core": "^1.48.2", @@ -424,7 +425,7 @@ }, "node_modules/@codemirror/lang-sql": { "version": "6.10.0", - "resolved": "https://registry.npmmirror.com/@codemirror/lang-sql/-/lang-sql-6.10.0.tgz", + "resolved": "https://registry.npmjs.org/@codemirror/lang-sql/-/lang-sql-6.10.0.tgz", "integrity": "sha512-6ayPkEd/yRw0XKBx5uAiToSgGECo/GY2NoJIHXIIQh1EVwLuKoU8BP/qK0qH5NLXAbtJRLuT73hx7P9X34iO4w==", "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 52ed651..0e82a8e 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@codemirror/autocomplete": "^6.20.3", "@codemirror/lang-cpp": "^6.0.3", "@codemirror/lang-python": "^6.2.1", + "@codemirror/lang-sql": "^6.10.0", "@vue-flow/background": "^1.3.2", "@vue-flow/controls": "^1.1.3", "@vue-flow/core": "^1.48.2", diff --git a/src/admin/api.ts b/src/admin/api.ts index ebb2d94..e88a099 100644 --- a/src/admin/api.ts +++ b/src/admin/api.ts @@ -133,10 +133,13 @@ export async function uploadImage(file: File): Promise { return res.success ? res.file_path : "" } -// 上传测试用例 -export function uploadTestcases(file: File) { +// 上传测试用例;SQL 题的压缩包是 1.sql..N.sql(每个文件一个测试点的建表+数据脚本) +export function uploadTestcases(file: File, options: { sql?: boolean } = {}) { const form = new window.FormData() form.append("file", file) + if (options.sql) { + form.append("sql", "1") + } return http.post("admin/test_case", form, { headers: { "content-type": "multipart/form-data" }, }) diff --git a/src/admin/problem/detail.vue b/src/admin/problem/detail.vue index 721ab01..adcd8d8 100644 --- a/src/admin/problem/detail.vue +++ b/src/admin/problem/detail.vue @@ -10,7 +10,13 @@ import { } from "utils/constants" import download from "utils/download" import { unique } from "utils/functions" -import type { BlankProblem, LANGUAGE, Tag, Testcase } from "utils/types" +import type { + BlankProblem, + LANGUAGE, + SQLConfig, + Tag, + Testcase, +} from "utils/types" import { createContestProblem, createProblem, @@ -89,6 +95,7 @@ const problem = useLocalStorage(STORAGE_KEY.ADMIN_PROBLEM, { flowchart_hint: "", show_flowchart: false, ast_rules: null as { [key: string]: any[] } | null, + sql_config: null as SQLConfig | null, }) // 从服务器来的tag列表 @@ -171,8 +178,33 @@ const languageOptions = [ { label: LANGUAGE_SHOW_VALUE["Python3"], value: "Python3" }, { label: LANGUAGE_SHOW_VALUE["C"], value: "C" }, { label: LANGUAGE_SHOW_VALUE["C++"], value: "C++" }, + { label: LANGUAGE_SHOW_VALUE["SQL"], value: "SQL" }, ] +const isSQLProblem = computed(() => !!problem.value?.languages.includes("SQL")) + +// SQL 题联动:SQL 必须是唯一语言(后端强校验),不需要预制代码,自动初始化 sql_config +watch( + () => problem.value?.languages, + (langs) => { + if (!langs) return + if (langs.includes("SQL")) { + if (langs.length > 1) { + problem.value.languages = ["SQL"] + return + } + needTemplate.value = false + if (!problem.value.sql_config) { + problem.value.sql_config = { mode: "query", order_sensitive: false } + } + currentActiveAnswer.value = "SQL" + } else if (problem.value.sql_config) { + problem.value.sql_config = null + } + }, + { immediate: true }, +) + async function getProblemDetail() { if (!props.problemID) { syncTagInputsFromProblemTags() @@ -211,6 +243,7 @@ async function getProblemDetail() { problem.value.flowchart_hint = data.flowchart_hint ?? "" problem.value.flowchart_data = data.flowchart_data problem.value.ast_rules = data.ast_rules ?? null + problem.value.sql_config = data.sql_config ?? null if (data.answers && data.answers.length) { problem.value.answers = data.answers } else { @@ -262,7 +295,7 @@ function resetTemplate(language: LANGUAGE) { async function handleUploadTestcases({ file }: UploadCustomRequestOptions) { try { - const res = await uploadTestcases(file.file!) + const res = await uploadTestcases(file.file!, { sql: isSQLProblem.value }) // @ts-ignore if (res.error) { message.error("上传测试用例失败") @@ -332,6 +365,19 @@ async function validateProblem() { message.error("编程语言没有选择") hasErrors = true } + // SQL 题验证 + else if (isSQLProblem.value && !problem.value.sql_config?.mode) { + message.error("SQL 题需要选择题型(查询题/增删改题)") + hasErrors = true + } else if ( + isSQLProblem.value && + !problem.value.answers.find( + (ans) => ans.language === "SQL" && ans.code.trim() !== "", + ) + ) { + message.error("SQL 题必须填写标准答案(判题时用它生成期望结果)") + hasErrors = true + } // 流程图验证 else if (problem.value.show_flowchart || problem.value.allow_flowchart) { if ( @@ -611,7 +657,7 @@ watch( - + + + + + 查询题(比对查询结果) + + 增删改题(比对执行后的表数据) + + + + + + + 题目要求 ORDER BY 时开启;关闭则按无序集合比对 + + + + - + 测试用例区域 -
+
(新)直接生成 @@ -706,14 +779,23 @@ watch( accept=".zip" :custom-request="handleUploadTestcases" > - (老)手动上传 + + {{ isSQLProblem ? "上传数据脚本压缩包" : "(老)手动上传" }} +
- 【测试用例】最好要有10个,要考虑边界情况,且不要跟【测试样例】一模一样 + + diff --git a/src/oj/problem/components/Form.vue b/src/oj/problem/components/Form.vue index 2bd2a9d..021de22 100644 --- a/src/oj/problem/components/Form.vue +++ b/src/oj/problem/components/Form.vue @@ -140,7 +140,8 @@ defineExpose({ onMounted(() => { if (!languages.value.includes(codeStore.code.language)) { - codeStore.code.language = "Python3" + // 回退到题目支持的第一种语言(如 SQL 题只有 "SQL",硬编码 Python3 会被后端拒绝) + codeStore.code.language = languages.value[0] ?? "Python3" } }) @@ -175,6 +176,7 @@ onMounted(() => {