| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713 |
- <script setup lang="ts">
- import { getProblemController } from '@/api/problem-controller'
- import { getCategoryController } from '@/api/category-controller'
- import type {
- CategoryVo,
- ProblemCaseDto,
- ProblemCaseVo,
- ProblemDetailVo,
- ProblemDto,
- ProblemTestDto,
- ProblemTestVo,
- ProblemVo,
- QueryByPage1Params
- } from '@/api/models'
- import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus'
- import { Delete, Edit, Plus, RefreshRight, Search, View } from '@element-plus/icons-vue'
- import { computed, onMounted, reactive, ref } from 'vue'
- import { useLoginUserStore } from '@/store'
- import { formatTime } from '@/util/format-time'
- import { MdEditor, mdPlugins } from '@/util/markdown'
- const api = getProblemController()
- const loginUserStore = useLoginUserStore()
- const isAdmin = computed(() => loginUserStore.loginUser.user?.role === 'admin')
- // 搜索
- const searchForm = reactive({
- title: '',
- difficulty: '',
- isPublic: undefined as number | undefined,
- categoryId: undefined as number | undefined
- })
- const categoryOptions = ref<CategoryVo[]>([])
- const loadCategories = async () => {
- try {
- const res = await getCategoryController().list()
- categoryOptions.value = res?.data || []
- } catch {
- categoryOptions.value = []
- }
- }
- const difficultyOptions = [
- { label: '简单', value: 'easy' },
- { label: '中等', value: 'medium' },
- { label: '困难', value: 'hard' }
- ]
- const difficultyTag = (d?: string) => {
- if (d === 'easy') return 'success'
- if (d === 'medium') return 'warning'
- return 'danger'
- }
- const difficultyLabel = (d?: string) => difficultyOptions.find((i) => i.value === d)?.label || d || '-'
- // 表格
- const tableData = ref<ProblemVo[]>([])
- const loading = ref(false)
- const selectedRows = ref<ProblemVo[]>([])
- const pagination = reactive({ pageNum: 1, pageSize: 10, total: 0 })
- // 新增/编辑
- const dialogVisible = ref(false)
- const dialogTitle = ref('新增题目')
- const formRef = ref<FormInstance>()
- const problemForm = reactive<Partial<ProblemDto>>({
- id: undefined,
- title: '',
- description: '',
- timeLimitMs: 1000,
- memoryLimitMb: 256,
- isPublic: 1 as ProblemDto['isPublic'],
- difficulty: 'easy' as ProblemDto['difficulty'],
- categoryIds: []
- })
- const formRules: FormRules = {
- title: [{ required: true, message: '请输入题目标题', trigger: 'blur' }],
- description: [{ required: true, message: '请输入题目描述', trigger: 'blur' }],
- timeLimitMs: [{ required: true, message: '请输入时间限制', trigger: 'blur' }],
- memoryLimitMb: [{ required: true, message: '请输入内存限制', trigger: 'blur' }],
- difficulty: [{ required: true, message: '请选择难度', trigger: 'change' }],
- isPublic: [{ required: true, message: '请选择公开状态', trigger: 'change' }]
- }
- const isEdit = computed(() => !!problemForm.id)
- // 详情弹窗(样例 + 评测用例)
- const detailVisible = ref(false)
- const detail = ref<ProblemDetailVo>({})
- const currentProblemId = ref<number | undefined>(undefined)
- // 样例
- const caseTable = ref<ProblemCaseVo[]>([])
- const caseDialogVisible = ref(false)
- const caseFormRef = ref<FormInstance>()
- const caseForm = reactive<Partial<ProblemCaseDto>>({})
- const caseRules: FormRules = {
- input: [{ required: true, message: '请输入样例输入', trigger: 'blur' }],
- output: [{ required: true, message: '请输入样例输出', trigger: 'blur' }]
- }
- // 评测用例
- const testTable = ref<ProblemTestVo[]>([])
- const testDialogVisible = ref(false)
- const testFormRef = ref<FormInstance>()
- const testForm = reactive<Partial<ProblemTestDto>>({})
- const testRules: FormRules = {
- input: [{ required: true, message: '请输入测试输入', trigger: 'blur' }],
- output: [{ required: true, message: '请输入测试输出', trigger: 'blur' }]
- }
- // ---------- 列表 ----------
- const fetchData = async () => {
- loading.value = true
- try {
- const condition: QueryByPage1Params = {
- title: searchForm.title || undefined,
- difficulty: searchForm.difficulty || undefined,
- isPublic: searchForm.isPublic,
- categoryId: searchForm.categoryId,
- pageNum: pagination.pageNum,
- pageSize: pagination.pageSize
- }
- const res = await api.queryByPage1(condition)
- const pageData = res?.data
- tableData.value = pageData?.data || []
- pagination.total = pageData?.total || 0
- } catch {
- tableData.value = []
- } finally {
- loading.value = false
- }
- }
- const handleSearch = () => {
- pagination.pageNum = 1
- fetchData()
- }
- const handleReset = () => {
- searchForm.title = ''
- searchForm.difficulty = ''
- searchForm.isPublic = undefined
- searchForm.categoryId = undefined
- handleSearch()
- }
- const handlePageChange = (page: number) => {
- pagination.pageNum = page
- fetchData()
- }
- const handleSizeChange = (size: number) => {
- pagination.pageSize = size
- pagination.pageNum = 1
- fetchData()
- }
- // ---------- 新增/编辑 ----------
- const handleAdd = () => {
- dialogTitle.value = '新增题目'
- problemForm.id = undefined
- problemForm.title = ''
- problemForm.description = ''
- problemForm.timeLimitMs = 1000
- problemForm.memoryLimitMb = 256
- problemForm.isPublic = 1 as ProblemDto['isPublic']
- problemForm.difficulty = 'easy' as ProblemDto['difficulty']
- problemForm.categoryIds = []
- dialogVisible.value = true
- }
- const handleEdit = (row: ProblemVo) => {
- dialogTitle.value = '编辑题目'
- const id = row.id
- if (id === undefined) return
- api
- .getDetail1(id)
- .then((res) => {
- const d = res?.data
- if (!d) return
- problemForm.id = d.id
- problemForm.title = d.title || ''
- problemForm.description = d.description || ''
- problemForm.timeLimitMs = d.timeLimitMs
- problemForm.memoryLimitMb = d.memoryLimitMb
- problemForm.isPublic = d.isPublic
- problemForm.difficulty = d.difficulty
- problemForm.categoryIds = d.categories?.map((c) => c.id).filter((c): c is number => c !== undefined) || []
- dialogVisible.value = true
- })
- .catch(() => {})
- }
- const handleSave = async () => {
- const valid = await formRef.value?.validate().catch(() => false)
- if (!valid) return
- try {
- const data: ProblemDto = {
- id: problemForm.id,
- title: problemForm.title!,
- description: problemForm.description!,
- timeLimitMs: problemForm.timeLimitMs!,
- memoryLimitMb: problemForm.memoryLimitMb!,
- isPublic: problemForm.isPublic as ProblemDto['isPublic'],
- difficulty: problemForm.difficulty as ProblemDto['difficulty'],
- categoryIds: problemForm.categoryIds || []
- }
- const res = isEdit.value ? await api.edit1(data) : await api.add1(data)
- if (res?.code === 200) {
- ElMessage.success(isEdit.value ? '修改成功' : '新增成功')
- dialogVisible.value = false
- fetchData()
- }
- } catch {
- }
- }
- const handleDelete = (row: ProblemVo) => {
- if (row.id === undefined) return
- ElMessageBox.confirm('确认删除该题目吗?', '提示', { type: 'warning' })
- .then(async () => {
- try {
- const res = await api.deleteByIds({ ids: [row.id!] })
- if (res?.code === 200) {
- ElMessage.success('删除成功')
- fetchData()
- }
- } catch {
- }
- })
- .catch(() => {})
- }
- const handleBatchDelete = () => {
- if (!selectedRows.value.length) {
- ElMessage.warning('请选择要删除的数据')
- return
- }
- ElMessageBox.confirm(`确认删除选中的 ${selectedRows.value.length} 条数据吗?`, '提示', { type: 'warning' })
- .then(async () => {
- try {
- const ids = selectedRows.value.map((r) => r.id).filter((id): id is number => id !== undefined)
- const res = await api.deleteByIds({ ids })
- if (res?.code === 200) {
- ElMessage.success('删除成功')
- selectedRows.value = []
- fetchData()
- }
- } catch {
- }
- })
- .catch(() => {})
- }
- // ---------- 详情弹窗 ----------
- const loadDetail = async (id: number) => {
- const [detailRes, testRes] = await Promise.all([api.getDetail1(id), api.queryTests(id)])
- detail.value = detailRes?.data || {}
- caseTable.value = detail.value.cases || []
- testTable.value = testRes?.data || []
- }
- const openDetail = (row: ProblemVo) => {
- const id = row.id
- if (id === undefined) return
- currentProblemId.value = id
- detailVisible.value = true
- loadDetail(id)
- }
- // 样例
- const openCaseAdd = () => {
- caseForm.id = undefined
- caseForm.problemId = currentProblemId.value
- caseForm.input = ''
- caseForm.output = ''
- caseForm.description = ''
- caseForm.sortOrder = caseTable.value.length + 1
- caseDialogVisible.value = true
- }
- const openCaseEdit = (row: ProblemCaseVo) => {
- caseForm.id = row.id
- caseForm.problemId = row.problemId
- caseForm.input = row.input || ''
- caseForm.output = row.output || ''
- caseForm.description = row.description || ''
- caseForm.sortOrder = row.sortOrder
- caseDialogVisible.value = true
- }
- const saveCase = async () => {
- const valid = await caseFormRef.value?.validate().catch(() => false)
- if (!valid) return
- try {
- const data: ProblemCaseDto = {
- id: caseForm.id,
- problemId: currentProblemId.value!,
- input: caseForm.input!,
- output: caseForm.output!,
- description: caseForm.description || undefined,
- sortOrder: caseForm.sortOrder
- }
- const res = caseForm.id ? await api.updateCase(data) : await api.addCase(currentProblemId.value!, data)
- if (res?.code === 200) {
- ElMessage.success('保存成功')
- caseDialogVisible.value = false
- loadDetail(currentProblemId.value!)
- }
- } catch {
- }
- }
- const deleteCase = (row: ProblemCaseVo) => {
- if (row.id === undefined) return
- ElMessageBox.confirm('确认删除该样例吗?', '提示', { type: 'warning' })
- .then(async () => {
- try {
- const res = await api.deleteCase({ id: row.id! })
- if (res?.code === 200) {
- ElMessage.success('删除成功')
- loadDetail(currentProblemId.value!)
- }
- } catch {
- }
- })
- .catch(() => {})
- }
- // 评测用例
- const openTestAdd = () => {
- testForm.id = undefined
- testForm.problemId = currentProblemId.value
- testForm.input = ''
- testForm.output = ''
- testForm.sortOrder = testTable.value.length + 1
- testForm.isHidden = 0 as ProblemTestDto['isHidden']
- testDialogVisible.value = true
- }
- const openTestEdit = (row: ProblemTestVo) => {
- testForm.id = row.id
- testForm.problemId = row.problemId
- testForm.input = row.input || ''
- testForm.output = row.output || ''
- testForm.sortOrder = row.sortOrder
- testForm.isHidden = row.isHidden
- testDialogVisible.value = true
- }
- const saveTest = async () => {
- const valid = await testFormRef.value?.validate().catch(() => false)
- if (!valid) return
- try {
- const data: ProblemTestDto = {
- id: testForm.id,
- problemId: currentProblemId.value!,
- input: testForm.input!,
- output: testForm.output!,
- sortOrder: testForm.sortOrder,
- isHidden: testForm.isHidden as ProblemTestDto['isHidden']
- }
- const res = testForm.id ? await api.updateTest(data) : await api.addTest(currentProblemId.value!, data)
- if (res?.code === 200) {
- ElMessage.success('保存成功')
- testDialogVisible.value = false
- loadDetail(currentProblemId.value!)
- }
- } catch {
- }
- }
- const deleteTest = (row: ProblemTestVo) => {
- if (row.id === undefined) return
- ElMessageBox.confirm('确认删除该评测用例吗?', '提示', { type: 'warning' })
- .then(async () => {
- try {
- const res = await api.deleteTest({ id: row.id! })
- if (res?.code === 200) {
- ElMessage.success('删除成功')
- loadDetail(currentProblemId.value!)
- }
- } catch {
- }
- })
- .catch(() => {})
- }
- const toggleTestHidden = async (row: ProblemTestVo, val: number) => {
- const old = row.isHidden
- row.isHidden = val as ProblemTestVo['isHidden']
- try {
- const data: ProblemTestDto = {
- id: row.id,
- problemId: row.problemId!,
- input: row.input,
- output: row.output,
- sortOrder: row.sortOrder,
- isHidden: val as ProblemTestDto['isHidden']
- }
- const res = await api.updateTest(data)
- if (res?.code === 200) {
- ElMessage.success('更新成功')
- }
- } catch {
- row.isHidden = old
- }
- }
- const handleSelectionChange = (rows: ProblemVo[]) => {
- selectedRows.value = rows
- }
- onMounted(() => {
- loadCategories()
- fetchData()
- })
- </script>
- <template>
- <div class="problem-manage-view">
- <el-card class="search-card" shadow="never">
- <el-form inline>
- <el-form-item label="标题">
- <el-input v-model="searchForm.title" placeholder="请输入题目标题" clearable style="width: 200px" @keyup.enter="handleSearch" />
- </el-form-item>
- <el-form-item label="难度">
- <el-select v-model="searchForm.difficulty" placeholder="全部" clearable style="width: 120px">
- <el-option v-for="item in difficultyOptions" :key="item.value" :label="item.label" :value="item.value" />
- </el-select>
- </el-form-item>
- <el-form-item label="公开状态">
- <el-select v-model="searchForm.isPublic" placeholder="全部" clearable style="width: 120px">
- <el-option label="公开" :value="1" />
- <el-option label="未公开" :value="0" />
- </el-select>
- </el-form-item>
- <el-form-item label="分类">
- <el-select v-model="searchForm.categoryId" placeholder="全部" clearable style="width: 160px">
- <el-option v-for="item in categoryOptions" :key="item.id" :label="item.categoryName" :value="item.id" />
- </el-select>
- </el-form-item>
- <el-form-item>
- <el-button type="primary" @click="handleSearch">
- <el-icon class="el-icon--left"><Search /></el-icon>搜索
- </el-button>
- <el-button @click="handleReset">
- <el-icon class="el-icon--left"><RefreshRight /></el-icon>重置
- </el-button>
- </el-form-item>
- </el-form>
- </el-card>
- <div class="toolbar">
- <el-button type="primary" @click="handleAdd">
- <el-icon class="el-icon--left"><Plus /></el-icon>新增
- </el-button>
- <el-button v-if="isAdmin" type="danger" :disabled="!selectedRows.length" @click="handleBatchDelete">
- <el-icon class="el-icon--left"><Delete /></el-icon>批量删除
- </el-button>
- </div>
- <el-card class="table-card" shadow="never">
- <el-table v-loading="loading" :data="tableData" border stripe @selection-change="handleSelectionChange" style="width: 100%">
- <el-table-column type="selection" width="55" align="center" />
- <el-table-column prop="id" label="ID" width="80" align="center" />
- <el-table-column prop="title" label="标题" min-width="200" />
- <el-table-column label="难度" width="100" align="center">
- <template #default="{ row }">
- <el-tag :type="difficultyTag(row.difficulty)">{{ difficultyLabel(row.difficulty) }}</el-tag>
- </template>
- </el-table-column>
- <el-table-column label="公开状态" width="100" align="center">
- <template #default="{ row }">
- <el-tag :type="row.isPublic === 1 ? 'success' : 'info'">{{ row.isPublic === 1 ? '公开' : '未公开' }}</el-tag>
- </template>
- </el-table-column>
- <el-table-column label="时间/内存" width="140" align="center">
- <template #default="{ row }">{{ row.timeLimitMs ?? '-' }}ms / {{ row.memoryLimitMb ?? '-' }}MB</template>
- </el-table-column>
- <el-table-column label="通过/提交" width="110" align="center">
- <template #default="{ row }">{{ row.acceptedCount ?? 0 }} / {{ row.tryCount ?? 0 }}</template>
- </el-table-column>
- <el-table-column label="创建时间" width="170" align="center">
- <template #default="{ row }">{{ formatTime(row.createdAt) }}</template>
- </el-table-column>
- <el-table-column label="操作" width="200" align="center" fixed="right">
- <template #default="{ row }">
- <el-button type="primary" link @click="openDetail(row)">
- <el-icon><View /></el-icon>详情
- </el-button>
- <el-button type="primary" link @click="handleEdit(row)">
- <el-icon><Edit /></el-icon>编辑
- </el-button>
- <el-button v-if="isAdmin" type="danger" link @click="handleDelete(row)">
- <el-icon><Delete /></el-icon>删除
- </el-button>
- </template>
- </el-table-column>
- </el-table>
- <div class="pagination">
- <el-pagination
- v-model:current-page="pagination.pageNum"
- v-model:page-size="pagination.pageSize"
- :page-sizes="[10, 20, 50, 100]"
- :total="pagination.total"
- layout="total, sizes, prev, pager, next, jumper"
- @current-change="handlePageChange"
- @size-change="handleSizeChange"
- />
- </div>
- </el-card>
- <!-- 新增/编辑题目 -->
- <el-dialog v-model="dialogVisible" width="960px" draggable>
- <template #header>
- <div class="dialog-header">{{ dialogTitle }}</div>
- </template>
- <el-form ref="formRef" :model="problemForm" :rules="formRules" label-width="90px" label-position="left">
- <el-form-item label="标题" prop="title">
- <el-input v-model="problemForm.title" placeholder="请输入题目标题" />
- </el-form-item>
- <el-form-item label="描述" prop="description">
- <div class="markdown-editor-wrapper">
- <MdEditor :value="problemForm.description || ''" :plugins="mdPlugins" @change="(v: string) => (problemForm.description = v)" />
- </div>
- </el-form-item>
- <el-form-item label="时间限制" prop="timeLimitMs">
- <el-input-number v-model="problemForm.timeLimitMs" :min="1" /> ms
- </el-form-item>
- <el-form-item label="内存限制" prop="memoryLimitMb">
- <el-input-number v-model="problemForm.memoryLimitMb" :min="1" /> MB
- </el-form-item>
- <el-form-item label="难度" prop="difficulty">
- <el-select v-model="problemForm.difficulty" style="width: 200px">
- <el-option v-for="item in difficultyOptions" :key="item.value" :label="item.label" :value="item.value" />
- </el-select>
- </el-form-item>
- <el-form-item label="公开" prop="isPublic">
- <el-switch v-model="problemForm.isPublic" :active-value="1" :inactive-value="0" active-text="公开" inactive-text="未公开" />
- </el-form-item>
- <el-form-item label="分类">
- <el-checkbox-group v-model="problemForm.categoryIds">
- <el-checkbox v-for="item in categoryOptions" :key="item.id" :value="item.id" :label="item.id">
- {{ item.categoryName }}
- </el-checkbox>
- </el-checkbox-group>
- </el-form-item>
- </el-form>
- <template #footer>
- <div class="dialog-footer">
- <el-button @click="dialogVisible = false">取消</el-button>
- <el-button type="primary" @click="handleSave">确定</el-button>
- </div>
- </template>
- </el-dialog>
- <!-- 详情弹窗(样例 + 评测用例) -->
- <el-dialog v-model="detailVisible" :title="`题目详情 - ${detail.title || ''}`" width="900px" draggable>
- <el-tabs>
- <el-tab-pane label="样例" name="cases">
- <div class="sub-toolbar">
- <el-button type="primary" size="small" @click="openCaseAdd">
- <el-icon class="el-icon--left"><Plus /></el-icon>新增样例
- </el-button>
- </div>
- <el-table :data="caseTable" border stripe size="small">
- <el-table-column label="序号" width="80" align="center">
- <template #default="{ row }">{{ row.sortOrder ?? '-' }}</template>
- </el-table-column>
- <el-table-column prop="input" label="输入" min-width="180" show-overflow-tooltip />
- <el-table-column prop="output" label="输出" min-width="180" show-overflow-tooltip />
- <el-table-column prop="description" label="说明" min-width="140" show-overflow-tooltip />
- <el-table-column label="操作" width="120" align="center">
- <template #default="{ row }">
- <el-button type="primary" link size="small" @click="openCaseEdit(row)">编辑</el-button>
- <el-button type="danger" link size="small" @click="deleteCase(row)">删除</el-button>
- </template>
- </el-table-column>
- </el-table>
- </el-tab-pane>
- <el-tab-pane label="评测用例" name="tests">
- <div class="sub-toolbar">
- <el-button type="primary" size="small" @click="openTestAdd">
- <el-icon class="el-icon--left"><Plus /></el-icon>新增用例
- </el-button>
- </div>
- <el-table :data="testTable" border stripe size="small">
- <el-table-column label="序号" width="80" align="center">
- <template #default="{ row }">{{ row.sortOrder ?? '-' }}</template>
- </el-table-column>
- <el-table-column prop="input" label="输入" min-width="180" show-overflow-tooltip />
- <el-table-column prop="output" label="输出" min-width="180" show-overflow-tooltip />
- <el-table-column label="隐藏" width="100" align="center">
- <template #default="{ row }">
- <el-switch
- :model-value="row.isHidden"
- :active-value="1"
- :inactive-value="0"
- @change="(val: number) => toggleTestHidden(row, val)"
- />
- </template>
- </el-table-column>
- <el-table-column label="操作" width="120" align="center">
- <template #default="{ row }">
- <el-button type="primary" link size="small" @click="openTestEdit(row)">编辑</el-button>
- <el-button type="danger" link size="small" @click="deleteTest(row)">删除</el-button>
- </template>
- </el-table-column>
- </el-table>
- </el-tab-pane>
- </el-tabs>
- </el-dialog>
- <!-- 样例编辑 -->
- <el-dialog v-model="caseDialogVisible" :title="caseForm.id ? '编辑样例' : '新增样例'" width="700px" draggable>
- <el-form ref="caseFormRef" :model="caseForm" :rules="caseRules" label-width="80px">
- <el-form-item label="序号">
- <el-input-number v-model="caseForm.sortOrder" :min="1" />
- </el-form-item>
- <el-form-item label="输入" prop="input">
- <el-input v-model="caseForm.input" type="textarea" :rows="4" />
- </el-form-item>
- <el-form-item label="输出" prop="output">
- <el-input v-model="caseForm.output" type="textarea" :rows="4" />
- </el-form-item>
- <el-form-item label="说明">
- <div class="markdown-editor-wrapper case-description">
- <MdEditor :value="caseForm.description || ''" :plugins="mdPlugins" @change="(v: string) => (caseForm.description = v)" />
- </div>
- </el-form-item>
- </el-form>
- <template #footer>
- <el-button @click="caseDialogVisible = false">取消</el-button>
- <el-button type="primary" @click="saveCase">确定</el-button>
- </template>
- </el-dialog>
- <!-- 评测用例编辑 -->
- <el-dialog v-model="testDialogVisible" :title="testForm.id ? '编辑评测用例' : '新增评测用例'" width="560px" draggable>
- <el-form ref="testFormRef" :model="testForm" :rules="testRules" label-width="80px">
- <el-form-item label="序号">
- <el-input-number v-model="testForm.sortOrder" :min="1" />
- </el-form-item>
- <el-form-item label="输入" prop="input">
- <el-input v-model="testForm.input" type="textarea" :rows="4" />
- </el-form-item>
- <el-form-item label="输出" prop="output">
- <el-input v-model="testForm.output" type="textarea" :rows="4" />
- </el-form-item>
- <el-form-item label="隐藏">
- <el-switch v-model="testForm.isHidden" :active-value="1" :inactive-value="0" active-text="隐藏" inactive-text="显示" />
- </el-form-item>
- </el-form>
- <template #footer>
- <el-button @click="testDialogVisible = false">取消</el-button>
- <el-button type="primary" @click="saveTest">确定</el-button>
- </template>
- </el-dialog>
- </div>
- </template>
- <style scoped>
- .problem-manage-view {
- padding: 20px;
- }
- .search-card {
- margin-bottom: 16px;
- }
- .toolbar,
- .sub-toolbar {
- margin-bottom: 16px;
- }
- .table-card {
- margin-bottom: 16px;
- }
- .pagination {
- display: flex;
- justify-content: flex-end;
- margin-top: 16px;
- }
- .dialog-header {
- text-align: center;
- }
- .dialog-footer {
- display: flex;
- justify-content: center;
- gap: 12px;
- }
- .markdown-editor-wrapper {
- width: 100%;
- flex: 1;
- }
- .markdown-editor-wrapper :deep(.bytemd) {
- height: 300px;
- width: 100%;
- }
- .markdown-editor-wrapper.case-description :deep(.bytemd) {
- height: 180px;
- }
- </style>
|