update
This commit is contained in:
@@ -163,6 +163,14 @@ export function getSQLTestcaseScripts(problemId: number) {
|
||||
)
|
||||
}
|
||||
|
||||
// AI 根据标准答案生成一个 SQL 测试点初始化脚本
|
||||
export function generateSQLTestcase(data: {
|
||||
ref_sql: string
|
||||
mode: "query" | "modify"
|
||||
}) {
|
||||
return http.post<{ sql: string }>("admin/sql_test_case_ai_gen", data)
|
||||
}
|
||||
|
||||
export function createProblem(problem: BlankProblem) {
|
||||
return http.post("admin/problem", problem)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { downloadZip } from "client-zip"
|
||||
import type { LANGUAGE, SQLDisplay, Testcase } from "utils/types"
|
||||
import SQLDataTable from "oj/problem/components/SQLDataTable.vue"
|
||||
import {
|
||||
generateSQLTestcase,
|
||||
getSQLTestcaseScripts,
|
||||
previewSQLTestcase,
|
||||
uploadTestcases,
|
||||
@@ -35,7 +36,7 @@ function blankEntry(): ScriptEntry {
|
||||
return { id: nextId++, sql: "", display: null, error: "", stale: false }
|
||||
}
|
||||
|
||||
const scripts = ref<ScriptEntry[]>([blankEntry(), blankEntry()])
|
||||
const scripts = ref<ScriptEntry[]>([blankEntry(), blankEntry(), blankEntry()])
|
||||
|
||||
const refSQL = computed(
|
||||
() =>
|
||||
@@ -45,8 +46,10 @@ const refSQL = computed(
|
||||
|
||||
const isPreviewing = ref(false)
|
||||
const isUploading = ref(false)
|
||||
const isGenerating = ref(false)
|
||||
|
||||
const hasAnyScript = computed(() => scripts.value.some((s) => s.sql.trim()))
|
||||
const hasBlankScript = computed(() => scripts.value.some((s) => !s.sql.trim()))
|
||||
|
||||
const canUpload = computed(() => {
|
||||
const filled = scripts.value.filter((s) => s.sql.trim())
|
||||
@@ -83,7 +86,7 @@ function remove(index: number) {
|
||||
}
|
||||
|
||||
function reset() {
|
||||
scripts.value = [blankEntry(), blankEntry()]
|
||||
scripts.value = [blankEntry(), blankEntry(), blankEntry()]
|
||||
}
|
||||
|
||||
function expectedQuery(d: SQLDisplay) {
|
||||
@@ -94,6 +97,28 @@ function changedTables(d: SQLDisplay) {
|
||||
return "changed_tables" in d.expected ? d.expected.changed_tables : []
|
||||
}
|
||||
|
||||
async function generate() {
|
||||
const blanks = scripts.value.filter((s) => !s.sql.trim())
|
||||
if (!blanks.length) return
|
||||
isGenerating.value = true
|
||||
await Promise.all(
|
||||
blanks.map(async (s) => {
|
||||
try {
|
||||
const res = await generateSQLTestcase({
|
||||
ref_sql: refSQL.value,
|
||||
mode: props.mode,
|
||||
})
|
||||
s.sql = res.data.sql
|
||||
} catch (err) {
|
||||
const data = (err as { data?: unknown })?.data
|
||||
message.error(typeof data === "string" ? data : "AI 生成失败")
|
||||
}
|
||||
}),
|
||||
)
|
||||
isGenerating.value = false
|
||||
await preview()
|
||||
}
|
||||
|
||||
async function preview() {
|
||||
// 丢弃空脚本
|
||||
scripts.value = scripts.value.filter((s) => s.sql.trim())
|
||||
@@ -168,15 +193,33 @@ async function upload() {
|
||||
还没有填写 SQL 标准答案,请先在上方"本题参考答案"中填写,再来编写测试点
|
||||
</n-alert>
|
||||
<n-flex align="center" wrap>
|
||||
<n-button :disabled="isPreviewing" @click="reset">清空</n-button>
|
||||
<n-button :disabled="isPreviewing" @click="add">+1</n-button>
|
||||
<n-button :disabled="isPreviewing || isGenerating" @click="reset">
|
||||
清空
|
||||
</n-button>
|
||||
<n-button :disabled="isPreviewing || isGenerating" @click="add">
|
||||
+1
|
||||
</n-button>
|
||||
<n-tooltip :disabled="!!refSQL && hasBlankScript">
|
||||
<template #trigger>
|
||||
<span>
|
||||
<n-button
|
||||
:loading="isGenerating"
|
||||
:disabled="!refSQL || !hasBlankScript || isPreviewing"
|
||||
@click="generate"
|
||||
>
|
||||
AI 生成
|
||||
</n-button>
|
||||
</span>
|
||||
</template>
|
||||
{{ !refSQL ? "请先填写 SQL 标准答案" : "所有脚本都写好了,无需生成" }}
|
||||
</n-tooltip>
|
||||
<n-tooltip :disabled="!!refSQL && hasAnyScript">
|
||||
<template #trigger>
|
||||
<span>
|
||||
<n-button
|
||||
type="success"
|
||||
:loading="isPreviewing"
|
||||
:disabled="!refSQL || !hasAnyScript"
|
||||
:disabled="!refSQL || !hasAnyScript || isGenerating"
|
||||
@click="preview"
|
||||
>
|
||||
预览验证
|
||||
@@ -191,7 +234,7 @@ async function upload() {
|
||||
<n-button
|
||||
type="primary"
|
||||
:loading="isUploading"
|
||||
:disabled="!canUpload"
|
||||
:disabled="!canUpload || isGenerating"
|
||||
@click="upload"
|
||||
>
|
||||
上传
|
||||
@@ -207,7 +250,7 @@ async function upload() {
|
||||
<strong>{{ index + 1 }}.sql</strong>
|
||||
<n-button
|
||||
size="small"
|
||||
:disabled="scripts.length === 1 || isPreviewing"
|
||||
:disabled="scripts.length === 1 || isPreviewing || isGenerating"
|
||||
@click="remove(index)"
|
||||
>
|
||||
删除
|
||||
|
||||
@@ -784,10 +784,10 @@ watch(
|
||||
|
||||
<h2 class="title">测试用例区域</h2>
|
||||
|
||||
<n-flex align="center" style="margin-bottom: 12px">
|
||||
<n-flex v-if="!isSQLProblem" align="center" style="margin-bottom: 12px">
|
||||
<div>
|
||||
<n-button type="success" @click="showGeneratorModal = true">
|
||||
{{ isSQLProblem ? "(新)在线编写" : "(新)直接生成" }}
|
||||
(新)直接生成
|
||||
</n-button>
|
||||
</div>
|
||||
<div>
|
||||
@@ -796,26 +796,25 @@ watch(
|
||||
accept=".zip"
|
||||
:custom-request="handleUploadTestcases"
|
||||
>
|
||||
<n-button type="info">
|
||||
{{ isSQLProblem ? "上传数据脚本压缩包" : "(老)手动上传" }}
|
||||
</n-button>
|
||||
<n-button type="info">(老)手动上传</n-button>
|
||||
</n-upload>
|
||||
</div>
|
||||
<n-tooltip placement="right" style="max-width: 320px; white-space: normal">
|
||||
<template #trigger>
|
||||
<n-button text>温馨提醒</n-button>
|
||||
</template>
|
||||
<template v-if="isSQLProblem">
|
||||
压缩包内放 1.sql、2.sql…(每个文件是一个测试点的建表+插入数据脚本,
|
||||
判题时对每个测试点分别运行标准答案和学生 SQL 比对结果)。 建议至少 2-3
|
||||
个数据不同的测试点,防止学生硬编码答案
|
||||
</template>
|
||||
<template v-else>
|
||||
【测试用例】最好要有10个,要考虑边界情况,且不要跟【测试样例】一模一样
|
||||
</template>
|
||||
【测试用例】最好要有10个,要考虑边界情况,且不要跟【测试样例】一模一样
|
||||
</n-tooltip>
|
||||
</n-flex>
|
||||
|
||||
<SQLTestcaseEditor
|
||||
v-if="isSQLProblem"
|
||||
:answers="problem.answers"
|
||||
:mode="problem.sql_config?.mode ?? 'query'"
|
||||
:problem-id="problem.id"
|
||||
@uploaded="handleTestcasesGenerated"
|
||||
/>
|
||||
|
||||
<n-alert
|
||||
class="box"
|
||||
v-if="problem.test_case_score.length"
|
||||
@@ -845,20 +844,12 @@ watch(
|
||||
<n-modal
|
||||
v-model:show="showGeneratorModal"
|
||||
preset="card"
|
||||
:title="isSQLProblem ? 'SQL 测试点编辑器' : '测试用例生成器'"
|
||||
title="测试用例生成器"
|
||||
style="width: 80vw; max-width: 900px"
|
||||
:mask-closable="false"
|
||||
display-directive="show"
|
||||
>
|
||||
<SQLTestcaseEditor
|
||||
v-if="isSQLProblem"
|
||||
:answers="problem.answers"
|
||||
:mode="problem.sql_config?.mode ?? 'query'"
|
||||
:problem-id="problem.id"
|
||||
@uploaded="handleTestcasesGenerated"
|
||||
/>
|
||||
<TestcaseGenerator
|
||||
v-else
|
||||
:answers="problem.answers"
|
||||
:samples="problem.samples"
|
||||
@uploaded="handleTestcasesGenerated"
|
||||
@@ -909,7 +900,7 @@ watch(
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</template>
|
||||
<n-flex style="margin-bottom: 120px" align="center" justify="end">
|
||||
<n-flex style="margin: 16px 0 120px" align="center" justify="end">
|
||||
<n-button type="primary" @click="submit">提交</n-button>
|
||||
</n-flex>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user