新增 SQL 题型前端支持
- LANGUAGE 类型与六张语言映射表补 SQL;编辑器 SQLite 方言高亮 - 出题表单:SQL 语言选项、题型/行序配置卡片、数据脚本压缩包上传、标准答案必填校验 - 修复做题页语言回退硬编码 Python3 的问题(SQL 题会提交被拒) - SQL 题隐藏自测猫/自测屏/复制到自测猫(外部运行器不支持 SQL) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
3
package-lock.json
generated
3
package-lock.json
generated
@@ -11,6 +11,7 @@
|
|||||||
"@codemirror/autocomplete": "^6.20.3",
|
"@codemirror/autocomplete": "^6.20.3",
|
||||||
"@codemirror/lang-cpp": "^6.0.3",
|
"@codemirror/lang-cpp": "^6.0.3",
|
||||||
"@codemirror/lang-python": "^6.2.1",
|
"@codemirror/lang-python": "^6.2.1",
|
||||||
|
"@codemirror/lang-sql": "^6.10.0",
|
||||||
"@vue-flow/background": "^1.3.2",
|
"@vue-flow/background": "^1.3.2",
|
||||||
"@vue-flow/controls": "^1.1.3",
|
"@vue-flow/controls": "^1.1.3",
|
||||||
"@vue-flow/core": "^1.48.2",
|
"@vue-flow/core": "^1.48.2",
|
||||||
@@ -424,7 +425,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@codemirror/lang-sql": {
|
"node_modules/@codemirror/lang-sql": {
|
||||||
"version": "6.10.0",
|
"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==",
|
"integrity": "sha512-6ayPkEd/yRw0XKBx5uAiToSgGECo/GY2NoJIHXIIQh1EVwLuKoU8BP/qK0qH5NLXAbtJRLuT73hx7P9X34iO4w==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
"@codemirror/autocomplete": "^6.20.3",
|
"@codemirror/autocomplete": "^6.20.3",
|
||||||
"@codemirror/lang-cpp": "^6.0.3",
|
"@codemirror/lang-cpp": "^6.0.3",
|
||||||
"@codemirror/lang-python": "^6.2.1",
|
"@codemirror/lang-python": "^6.2.1",
|
||||||
|
"@codemirror/lang-sql": "^6.10.0",
|
||||||
"@vue-flow/background": "^1.3.2",
|
"@vue-flow/background": "^1.3.2",
|
||||||
"@vue-flow/controls": "^1.1.3",
|
"@vue-flow/controls": "^1.1.3",
|
||||||
"@vue-flow/core": "^1.48.2",
|
"@vue-flow/core": "^1.48.2",
|
||||||
|
|||||||
@@ -133,10 +133,13 @@ export async function uploadImage(file: File): Promise<string> {
|
|||||||
return res.success ? res.file_path : ""
|
return res.success ? res.file_path : ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上传测试用例
|
// 上传测试用例;SQL 题的压缩包是 1.sql..N.sql(每个文件一个测试点的建表+数据脚本)
|
||||||
export function uploadTestcases(file: File) {
|
export function uploadTestcases(file: File, options: { sql?: boolean } = {}) {
|
||||||
const form = new window.FormData()
|
const form = new window.FormData()
|
||||||
form.append("file", file)
|
form.append("file", file)
|
||||||
|
if (options.sql) {
|
||||||
|
form.append("sql", "1")
|
||||||
|
}
|
||||||
return http.post<TestcaseUploadedReturns>("admin/test_case", form, {
|
return http.post<TestcaseUploadedReturns>("admin/test_case", form, {
|
||||||
headers: { "content-type": "multipart/form-data" },
|
headers: { "content-type": "multipart/form-data" },
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -10,7 +10,13 @@ import {
|
|||||||
} from "utils/constants"
|
} from "utils/constants"
|
||||||
import download from "utils/download"
|
import download from "utils/download"
|
||||||
import { unique } from "utils/functions"
|
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 {
|
import {
|
||||||
createContestProblem,
|
createContestProblem,
|
||||||
createProblem,
|
createProblem,
|
||||||
@@ -89,6 +95,7 @@ const problem = useLocalStorage<BlankProblem>(STORAGE_KEY.ADMIN_PROBLEM, {
|
|||||||
flowchart_hint: "",
|
flowchart_hint: "",
|
||||||
show_flowchart: false,
|
show_flowchart: false,
|
||||||
ast_rules: null as { [key: string]: any[] } | null,
|
ast_rules: null as { [key: string]: any[] } | null,
|
||||||
|
sql_config: null as SQLConfig | null,
|
||||||
})
|
})
|
||||||
|
|
||||||
// 从服务器来的tag列表
|
// 从服务器来的tag列表
|
||||||
@@ -171,8 +178,33 @@ const languageOptions = [
|
|||||||
{ label: LANGUAGE_SHOW_VALUE["Python3"], value: "Python3" },
|
{ 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["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() {
|
async function getProblemDetail() {
|
||||||
if (!props.problemID) {
|
if (!props.problemID) {
|
||||||
syncTagInputsFromProblemTags()
|
syncTagInputsFromProblemTags()
|
||||||
@@ -211,6 +243,7 @@ async function getProblemDetail() {
|
|||||||
problem.value.flowchart_hint = data.flowchart_hint ?? ""
|
problem.value.flowchart_hint = data.flowchart_hint ?? ""
|
||||||
problem.value.flowchart_data = data.flowchart_data
|
problem.value.flowchart_data = data.flowchart_data
|
||||||
problem.value.ast_rules = data.ast_rules ?? null
|
problem.value.ast_rules = data.ast_rules ?? null
|
||||||
|
problem.value.sql_config = data.sql_config ?? null
|
||||||
if (data.answers && data.answers.length) {
|
if (data.answers && data.answers.length) {
|
||||||
problem.value.answers = data.answers
|
problem.value.answers = data.answers
|
||||||
} else {
|
} else {
|
||||||
@@ -262,7 +295,7 @@ function resetTemplate(language: LANGUAGE) {
|
|||||||
|
|
||||||
async function handleUploadTestcases({ file }: UploadCustomRequestOptions) {
|
async function handleUploadTestcases({ file }: UploadCustomRequestOptions) {
|
||||||
try {
|
try {
|
||||||
const res = await uploadTestcases(file.file!)
|
const res = await uploadTestcases(file.file!, { sql: isSQLProblem.value })
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
message.error("上传测试用例失败")
|
message.error("上传测试用例失败")
|
||||||
@@ -332,6 +365,19 @@ async function validateProblem() {
|
|||||||
message.error("编程语言没有选择")
|
message.error("编程语言没有选择")
|
||||||
hasErrors = true
|
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) {
|
else if (problem.value.show_flowchart || problem.value.allow_flowchart) {
|
||||||
if (
|
if (
|
||||||
@@ -611,7 +657,7 @@ watch(
|
|||||||
</n-flex>
|
</n-flex>
|
||||||
</n-checkbox-group>
|
</n-checkbox-group>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item>
|
<n-form-item v-if="!isSQLProblem">
|
||||||
<n-checkbox
|
<n-checkbox
|
||||||
v-model:checked="needTemplate"
|
v-model:checked="needTemplate"
|
||||||
label="预制代码(显示在编辑器中,帮助快速上手)"
|
label="预制代码(显示在编辑器中,帮助快速上手)"
|
||||||
@@ -630,10 +676,37 @@ watch(
|
|||||||
</n-form-item>
|
</n-form-item>
|
||||||
</n-form>
|
</n-form>
|
||||||
|
|
||||||
|
<n-form
|
||||||
|
v-if="isSQLProblem && problem.sql_config"
|
||||||
|
inline
|
||||||
|
label-placement="left"
|
||||||
|
>
|
||||||
|
<n-form-item label="SQL 题型">
|
||||||
|
<n-radio-group v-model:value="problem.sql_config.mode">
|
||||||
|
<n-radio-button value="query">查询题(比对查询结果)</n-radio-button>
|
||||||
|
<n-radio-button value="modify">
|
||||||
|
增删改题(比对执行后的表数据)
|
||||||
|
</n-radio-button>
|
||||||
|
</n-radio-group>
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item label="严格比对行顺序">
|
||||||
|
<n-switch v-model:value="problem.sql_config.order_sensitive" />
|
||||||
|
<n-text depth="3" style="margin-left: 12px">
|
||||||
|
题目要求 ORDER BY 时开启;关闭则按无序集合比对
|
||||||
|
</n-text>
|
||||||
|
</n-form-item>
|
||||||
|
</n-form>
|
||||||
|
|
||||||
<n-grid :cols="2" x-gap="20">
|
<n-grid :cols="2" x-gap="20">
|
||||||
<n-gi>
|
<n-gi>
|
||||||
<n-form>
|
<n-form>
|
||||||
<n-form-item label="本题参考答案(选填,用于 AI 分析,不会泄露)">
|
<n-form-item
|
||||||
|
:label="
|
||||||
|
isSQLProblem
|
||||||
|
? '标准答案(必填,判题依据:每个测试点会运行它生成期望结果)'
|
||||||
|
: '本题参考答案(选填,用于 AI 分析,不会泄露)'
|
||||||
|
"
|
||||||
|
>
|
||||||
<n-tabs
|
<n-tabs
|
||||||
type="segment"
|
type="segment"
|
||||||
default-value="Python3"
|
default-value="Python3"
|
||||||
@@ -695,7 +768,7 @@ watch(
|
|||||||
<h2 class="title">测试用例区域</h2>
|
<h2 class="title">测试用例区域</h2>
|
||||||
|
|
||||||
<n-flex align="center" style="margin-bottom: 12px">
|
<n-flex align="center" style="margin-bottom: 12px">
|
||||||
<div>
|
<div v-if="!isSQLProblem">
|
||||||
<n-button type="success" @click="showGeneratorModal = true">
|
<n-button type="success" @click="showGeneratorModal = true">
|
||||||
(新)直接生成
|
(新)直接生成
|
||||||
</n-button>
|
</n-button>
|
||||||
@@ -706,14 +779,23 @@ watch(
|
|||||||
accept=".zip"
|
accept=".zip"
|
||||||
:custom-request="handleUploadTestcases"
|
:custom-request="handleUploadTestcases"
|
||||||
>
|
>
|
||||||
<n-button type="info">(老)手动上传</n-button>
|
<n-button type="info">
|
||||||
|
{{ isSQLProblem ? "上传数据脚本压缩包" : "(老)手动上传" }}
|
||||||
|
</n-button>
|
||||||
</n-upload>
|
</n-upload>
|
||||||
</div>
|
</div>
|
||||||
<n-tooltip placement="right">
|
<n-tooltip placement="right">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<n-button text>温馨提醒</n-button>
|
<n-button text>温馨提醒</n-button>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-if="isSQLProblem">
|
||||||
|
压缩包内放 1.sql、2.sql…(每个文件是一个测试点的建表+插入数据脚本,
|
||||||
|
判题时对每个测试点分别运行标准答案和学生 SQL 比对结果)。 建议至少 2-3
|
||||||
|
个数据不同的测试点,防止学生硬编码答案
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
【测试用例】最好要有10个,要考虑边界情况,且不要跟【测试样例】一模一样
|
【测试用例】最好要有10个,要考虑边界情况,且不要跟【测试样例】一模一样
|
||||||
|
</template>
|
||||||
</n-tooltip>
|
</n-tooltip>
|
||||||
</n-flex>
|
</n-flex>
|
||||||
|
|
||||||
|
|||||||
@@ -140,7 +140,8 @@ defineExpose({
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (!languages.value.includes(codeStore.code.language)) {
|
if (!languages.value.includes(codeStore.code.language)) {
|
||||||
codeStore.code.language = "Python3"
|
// 回退到题目支持的第一种语言(如 SQL 题只有 "SQL",硬编码 Python3 会被后端拒绝)
|
||||||
|
codeStore.code.language = languages.value[0] ?? "Python3"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
@@ -175,6 +176,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
<template v-if="codeStore.code.language !== 'Flowchart'">
|
<template v-if="codeStore.code.language !== 'Flowchart'">
|
||||||
<IconButton
|
<IconButton
|
||||||
|
v-if="codeStore.code.language !== 'SQL'"
|
||||||
icon="streamline-ultimate-color:business-lucky-cat"
|
icon="streamline-ultimate-color:business-lucky-cat"
|
||||||
tip="自测猫"
|
tip="自测猫"
|
||||||
@click="goTestCat"
|
@click="goTestCat"
|
||||||
|
|||||||
@@ -130,6 +130,16 @@ onBeforeUnmount(() => {
|
|||||||
watch(isMobile, (value) => {
|
watch(isMobile, (value) => {
|
||||||
if (value) screenModeStore.resetScreenMode()
|
if (value) screenModeStore.resetScreenMode()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// SQL 题不支持"自测"模式(外部代码运行器无法执行 SQL),切到该屏时自动跳到下一模式
|
||||||
|
watch(
|
||||||
|
() => screenModeStore.isCodeOnlyMode,
|
||||||
|
(codeOnly) => {
|
||||||
|
if (codeOnly && problem.value?.languages.includes("SQL")) {
|
||||||
|
screenModeStore.switchScreenMode()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -127,7 +127,13 @@ onMounted(init)
|
|||||||
</n-flex>
|
</n-flex>
|
||||||
</n-alert>
|
</n-alert>
|
||||||
<n-flex :vertical="isDesktop" justify="center">
|
<n-flex :vertical="isDesktop" justify="center">
|
||||||
<n-button secondary @click="copyToCat">复制到自测猫</n-button>
|
<n-button
|
||||||
|
v-if="submission.language !== 'SQL'"
|
||||||
|
secondary
|
||||||
|
@click="copyToCat"
|
||||||
|
>
|
||||||
|
复制到自测猫
|
||||||
|
</n-button>
|
||||||
<n-button secondary @click="copyToProblem">复制回到题目</n-button>
|
<n-button secondary @click="copyToProblem">复制回到题目</n-button>
|
||||||
</n-flex>
|
</n-flex>
|
||||||
</n-flex>
|
</n-flex>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { cpp } from "@codemirror/lang-cpp"
|
import { cpp } from "@codemirror/lang-cpp"
|
||||||
import { python } from "@codemirror/lang-python"
|
import { python } from "@codemirror/lang-python"
|
||||||
|
import { sql, SQLite } from "@codemirror/lang-sql"
|
||||||
import { bracketMatching } from "@codemirror/language"
|
import { bracketMatching } from "@codemirror/language"
|
||||||
import { EditorView } from "@codemirror/view"
|
import { EditorView } from "@codemirror/view"
|
||||||
import { Codemirror } from "vue-codemirror"
|
import { Codemirror } from "vue-codemirror"
|
||||||
@@ -57,6 +58,8 @@ const emit = defineEmits<{
|
|||||||
const { isDesktop } = useBreakpoints()
|
const { isDesktop } = useBreakpoints()
|
||||||
|
|
||||||
const langExtension = computed((): Extension => {
|
const langExtension = computed((): Extension => {
|
||||||
|
if (language === "SQL")
|
||||||
|
return sql({ dialect: SQLite, upperCaseKeywords: true })
|
||||||
return ["Python2", "Python3"].includes(language) ? python() : cpp()
|
return ["Python2", "Python3"].includes(language) ? python() : cpp()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ export function enhanceCompletion(language: LANGUAGE): CompletionSource {
|
|||||||
const word = context.matchBefore(/\w+/)
|
const word = context.matchBefore(/\w+/)
|
||||||
if (!word && !context.explicit) return null
|
if (!word && !context.explicit) return null
|
||||||
|
|
||||||
|
// SQL 没有中文注释提示,关键字补全由 @codemirror/lang-sql 提供
|
||||||
|
if (language === "SQL") return null
|
||||||
|
|
||||||
const trulyLanguage = language.startsWith("Python") ? "python" : "c"
|
const trulyLanguage = language.startsWith("Python") ? "python" : "c"
|
||||||
const completions: Completion[] = (
|
const completions: Completion[] = (
|
||||||
chineseAnnotations[trulyLanguage] || []
|
chineseAnnotations[trulyLanguage] || []
|
||||||
|
|||||||
@@ -175,6 +175,7 @@ export const SOURCES = {
|
|||||||
JavaScript: "",
|
JavaScript: "",
|
||||||
Golang: "",
|
Golang: "",
|
||||||
Flowchart: "",
|
Flowchart: "",
|
||||||
|
SQL: "",
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export const LANGUAGE_ID = {
|
export const LANGUAGE_ID = {
|
||||||
@@ -186,6 +187,7 @@ export const LANGUAGE_ID = {
|
|||||||
JavaScript: 0,
|
JavaScript: 0,
|
||||||
Golang: 0,
|
Golang: 0,
|
||||||
Flowchart: 0,
|
Flowchart: 0,
|
||||||
|
SQL: 0,
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export const LANGUAGE_FORMAT_VALUE = {
|
export const LANGUAGE_FORMAT_VALUE = {
|
||||||
@@ -197,6 +199,7 @@ export const LANGUAGE_FORMAT_VALUE = {
|
|||||||
JavaScript: "javascript",
|
JavaScript: "javascript",
|
||||||
Golang: "go",
|
Golang: "go",
|
||||||
Flowchart: "flowchart",
|
Flowchart: "flowchart",
|
||||||
|
SQL: "sql",
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export const LANGUAGE_SHOW_VALUE = {
|
export const LANGUAGE_SHOW_VALUE = {
|
||||||
@@ -208,6 +211,7 @@ export const LANGUAGE_SHOW_VALUE = {
|
|||||||
Python3: "Python",
|
Python3: "Python",
|
||||||
JavaScript: "JS",
|
JavaScript: "JS",
|
||||||
Golang: "Go",
|
Golang: "Go",
|
||||||
|
SQL: "SQL",
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export const ICON_SET = {
|
export const ICON_SET = {
|
||||||
@@ -219,6 +223,7 @@ export const ICON_SET = {
|
|||||||
Java: "devicon:java",
|
Java: "devicon:java",
|
||||||
JavaScript: "devicon:javascript",
|
JavaScript: "devicon:javascript",
|
||||||
Golang: "devicon:go",
|
Golang: "devicon:go",
|
||||||
|
SQL: "devicon:sqlite",
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
const cTemplate = `//TEMPLATE BEGIN
|
const cTemplate = `//TEMPLATE BEGIN
|
||||||
@@ -256,6 +261,7 @@ export const CODE_TEMPLATES = {
|
|||||||
JavaScript: blankTemplate,
|
JavaScript: blankTemplate,
|
||||||
Golang: blankTemplate,
|
Golang: blankTemplate,
|
||||||
Flowchart: blankTemplate,
|
Flowchart: blankTemplate,
|
||||||
|
SQL: blankTemplate,
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export enum ScreenMode {
|
export enum ScreenMode {
|
||||||
|
|||||||
@@ -65,6 +65,12 @@ export type LANGUAGE =
|
|||||||
| "JavaScript"
|
| "JavaScript"
|
||||||
| "Golang"
|
| "Golang"
|
||||||
| "Flowchart"
|
| "Flowchart"
|
||||||
|
| "SQL"
|
||||||
|
|
||||||
|
export interface SQLConfig {
|
||||||
|
mode: "query" | "modify"
|
||||||
|
order_sensitive: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export type LANGUAGE_SHOW_LABEL =
|
export type LANGUAGE_SHOW_LABEL =
|
||||||
(typeof LANGUAGE_SHOW_VALUE)[keyof typeof LANGUAGE_SHOW_VALUE]
|
(typeof LANGUAGE_SHOW_VALUE)[keyof typeof LANGUAGE_SHOW_VALUE]
|
||||||
@@ -164,6 +170,9 @@ export interface Problem {
|
|||||||
}[]
|
}[]
|
||||||
} | null
|
} | null
|
||||||
has_ast_rules?: boolean
|
has_ast_rules?: boolean
|
||||||
|
|
||||||
|
// SQL 题配置(非 SQL 题为 null)
|
||||||
|
sql_config?: SQLConfig | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AdminProblem = Problem & AlterProblem
|
export type AdminProblem = Problem & AlterProblem
|
||||||
|
|||||||
Reference in New Issue
Block a user