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:
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>
|
||||
Reference in new issue
Block a user