refactor(题单后台): 添加 / 编辑弹窗两两合并成 ProblemModal、BadgeModal

AddBadgeModal / EditBadgeModal、AddProblemModal / EditProblemModal 各是两份八成相同
的组件,改一处表单项另一处总要漏。合并成一个组件,传了 badge / problem 就是编辑、
null 就是添加;两种模式分别 emit create / update,detail.vue 的四个 handler 不变。

表单项、校验、文案、提交的数据形状都照旧(题目弹窗添加时带 problemId、编辑时只显示
标题)。有一处刻意的行为变化:编辑弹窗原来只在换了一条记录时才重新填表,取消后再打开
同一条会看到上次没保存的改动;现在每次打开都按库里的值重新填。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-09-27 15:16:03 +08:00
1 parent f49834f19f
commit 6b2da41882
6 files changed
+182 -436

No files matched your search

@@ -0,0 +1,110 @@
<script setup lang="ts">
import type { AdminProblemSetProblem } from "utils/types"
// 添加和编辑共用一个弹窗:传了 problem 就是编辑,null 就是添加。
// 两种模式只差第一栏 —— 添加时填题目 ID,编辑时题目已定、只显示标题
interface Props {
show: boolean
problem: AdminProblemSetProblem | null
}
type ProblemFormData = {
order: number
isRequired: boolean
score: number
hint: string
}
interface Emits {
(e: "update:show", value: boolean): void
(e: "create", data: ProblemFormData & { problemId: string }): void
(e: "update", data: ProblemFormData): void
}
const props = defineProps<Props>()
const emit = defineEmits<Emits>()
const problemId = ref("")
const problemOrder = ref(0)
const problemRequired = ref(true)
const problemScore = ref(0)
const problemHint = ref("")
function handleConfirm() {
const data: ProblemFormData = {
order: problemOrder.value,
isRequired: problemRequired.value,
score: problemScore.value,
hint: problemHint.value,
}
if (props.problem) emit("update", data)
else emit("create", { problemId: problemId.value, ...data })
}
function handleCancel() {
emit("update:show", false)
}
// 每次打开都按当前模式重新填表:编辑就填这道题的值,添加就清空。
// 编辑取消后再打开同一道,看到的是库里的值,而不是上次没保存的改动
watch(
() => props.show,
(open) => {
if (!open) return
const problem = props.problem
problemId.value = ""
problemOrder.value = problem?.order ?? 0
problemRequired.value = problem?.isRequired ?? true
problemScore.value = problem?.score ?? 0
problemHint.value = problem?.hint || ""
},
{ immediate: true },
)
</script>
<template>
<n-modal
:show="show"
preset="card"
:title="problem ? '编辑题目' : '添加题目'"
style="width: 500px"
@update:show="emit('update:show', $event)"
>
<n-form>
<n-form-item v-if="problem" label="题目标题">
<n-input :value="problem.title" disabled />
</n-form-item>
<n-form-item v-else label="题目ID" required>
<n-input
v-model:value="problemId"
placeholder="请输入题目的显示ID(如:1001)"
/>
</n-form-item>
<n-form-item label="顺序">
<n-input-number
v-model:value="problemOrder"
placeholder="题目在题单中的顺序"
/>
</n-form-item>
<n-form-item label="是否必做">
<n-switch v-model:value="problemRequired" />
</n-form-item>
<n-form-item label="分数">
<n-input-number v-model:value="problemScore" placeholder="题目分数" />
</n-form-item>
<n-form-item label="提示">
<n-input
v-model:value="problemHint"
type="textarea"
placeholder="题目提示"
/>
</n-form-item>
</n-form>
<template #footer>
<n-flex justify="end">
<n-button @click="handleCancel">取消</n-button>
<n-button type="primary" @click="handleConfirm">确认</n-button>
</n-flex>
</template>
</n-modal>
</template>