|
|
@@ -52,6 +52,14 @@ const statusTag = (status?: ContestVo['status']) => {
|
|
|
const statusLabel = (status?: ContestVo['status']) =>
|
|
|
statusOptions.find((s) => s.value === status)?.label || status || '-'
|
|
|
|
|
|
+const difficultyOptions = [
|
|
|
+ { label: '简单', value: 'easy' },
|
|
|
+ { label: '中等', value: 'medium' },
|
|
|
+ { label: '困难', value: 'hard' }
|
|
|
+]
|
|
|
+
|
|
|
+const difficultyLabel = (d?: string) => difficultyOptions.find((i) => i.value === d)?.label || d || '-'
|
|
|
+
|
|
|
// ---------- 搜索 ----------
|
|
|
const searchForm = reactive({
|
|
|
title: '',
|
|
|
@@ -126,7 +134,7 @@ const formRules: FormRules = {
|
|
|
password: [
|
|
|
{
|
|
|
validator: (_rule, value, callback) => {
|
|
|
- if (contestForm.type === 'password' && !value) {
|
|
|
+ if (contestForm.type === 'password' && !value && !contestForm.id) {
|
|
|
callback(new Error('密码型比赛请输入访问密码'))
|
|
|
} else {
|
|
|
callback()
|
|
|
@@ -161,6 +169,17 @@ const handleEdit = (row: ContestVo) => {
|
|
|
contestForm.startDatetime = row.startDatetime || ''
|
|
|
contestForm.endDatetime = row.endDatetime || ''
|
|
|
dialogVisible.value = true
|
|
|
+ if (row.id !== undefined && row.type !== 'password') {
|
|
|
+ api
|
|
|
+ .getDetail2(row.id)
|
|
|
+ .then((res) => {
|
|
|
+ const d = res?.data
|
|
|
+ if (d && contestForm.id === row.id) {
|
|
|
+ contestForm.description = d.description || ''
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch(() => {})
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
const handleSave = async () => {
|
|
|
@@ -176,12 +195,12 @@ const handleSave = async () => {
|
|
|
title: contestForm.title!,
|
|
|
description: contestForm.description || undefined,
|
|
|
type: contestForm.type as ContestDto['type'],
|
|
|
- password: contestForm.type === 'password' ? contestForm.password : undefined,
|
|
|
+ password: contestForm.type === 'password' && contestForm.password ? contestForm.password : undefined,
|
|
|
startDatetime: contestForm.startDatetime!,
|
|
|
endDatetime: contestForm.endDatetime!
|
|
|
}
|
|
|
const res = isEdit.value ? await api.edit2(data) : await api.add2(data)
|
|
|
- if (res?.code === 200) {
|
|
|
+ if (res?.code === 0 || res?.code === 200) {
|
|
|
ElMessage.success(isEdit.value ? '修改成功' : '新增成功')
|
|
|
dialogVisible.value = false
|
|
|
fetchData()
|
|
|
@@ -196,7 +215,7 @@ const handleDelete = (row: ContestVo) => {
|
|
|
.then(async () => {
|
|
|
try {
|
|
|
const res = await api.deleteByIds1({ ids: [row.id!] })
|
|
|
- if (res?.code === 200) {
|
|
|
+ if (res?.code === 0 || res?.code === 200) {
|
|
|
ElMessage.success('删除成功')
|
|
|
fetchData()
|
|
|
}
|
|
|
@@ -206,6 +225,10 @@ const handleDelete = (row: ContestVo) => {
|
|
|
.catch(() => {})
|
|
|
}
|
|
|
|
|
|
+const handleDialogClosed = () => {
|
|
|
+ formRef.value?.resetFields()
|
|
|
+}
|
|
|
+
|
|
|
// ---------- 题目配置 ----------
|
|
|
const configDialogVisible = ref(false)
|
|
|
const configContestId = ref<number | undefined>(undefined)
|
|
|
@@ -265,7 +288,7 @@ const loadConfig = async () => {
|
|
|
}
|
|
|
try {
|
|
|
const res = await api.getDetail2(configContestId.value, configPassword.value ? { password: configPassword.value } : undefined)
|
|
|
- if (res?.code === 200 && res.data) {
|
|
|
+ if ((res?.code === 0 || res?.code === 200) && res.data) {
|
|
|
const d = res.data
|
|
|
if (d.type === 'password' && !d.creatorUsername) {
|
|
|
ElMessage.error('比赛密码错误')
|
|
|
@@ -350,8 +373,11 @@ const openConfig = (row: ContestVo) => {
|
|
|
configLoaded.value = false
|
|
|
existingProblems.value = []
|
|
|
configProblems.value = []
|
|
|
+ candSearch.title = ''
|
|
|
+ candPagination.pageNum = 1
|
|
|
configDialogVisible.value = true
|
|
|
loadConfig()
|
|
|
+ loadCandidates()
|
|
|
}
|
|
|
|
|
|
// ---------- 报名名单 ----------
|
|
|
@@ -370,7 +396,7 @@ const openRegistrations = (row: ContestVo) => {
|
|
|
api
|
|
|
.queryRegistrations(id)
|
|
|
.then((res) => {
|
|
|
- if (res?.code === 200) {
|
|
|
+ if (res?.code === 0 || res?.code === 200) {
|
|
|
regTable.value = res.data || []
|
|
|
}
|
|
|
})
|
|
|
@@ -435,7 +461,7 @@ onMounted(fetchData)
|
|
|
<el-table-column label="结束时间" width="150" align="center">
|
|
|
<template #default="{ row }">{{ formatTime(row.endDatetime) }}</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column label="操作" width="250" align="center" fixed="right">
|
|
|
+ <el-table-column label="操作" width="340" align="center" fixed="right" class-name="op-column">
|
|
|
<template #default="{ row }">
|
|
|
<el-button type="primary" link @click="handleEdit(row)">
|
|
|
<el-icon><Edit /></el-icon>编辑
|
|
|
@@ -444,7 +470,7 @@ onMounted(fetchData)
|
|
|
<el-icon><View /></el-icon>配置题目
|
|
|
</el-button>
|
|
|
<el-button type="primary" link @click="openRegistrations(row)">
|
|
|
- <el-icon><UserFilled /></el-icon>报名
|
|
|
+ <el-icon><UserFilled /></el-icon>报名名单
|
|
|
</el-button>
|
|
|
<el-button v-if="isAdmin" type="danger" link @click="handleDelete(row)">
|
|
|
<el-icon><Delete /></el-icon>删除
|
|
|
@@ -467,13 +493,14 @@ onMounted(fetchData)
|
|
|
</el-card>
|
|
|
|
|
|
<!-- 新增/编辑比赛 -->
|
|
|
- <el-dialog v-model="dialogVisible" :title="dialogTitle" width="560px" draggable>
|
|
|
- <el-form ref="formRef" :model="contestForm" :rules="formRules" label-width="100px">
|
|
|
+ <el-dialog v-model="dialogVisible" :title="dialogTitle" width="560px" draggable @closed="handleDialogClosed">
|
|
|
+ <el-form ref="formRef" :model="contestForm" :rules="formRules" label-position="top">
|
|
|
<el-form-item label="标题" prop="title">
|
|
|
<el-input v-model="contestForm.title" placeholder="请输入比赛标题" />
|
|
|
</el-form-item>
|
|
|
<el-form-item label="描述">
|
|
|
<el-input v-model="contestForm.description" type="textarea" :rows="3" placeholder="请输入比赛描述" />
|
|
|
+ <span v-if="isEdit" class="tip">留空则不修改描述</span>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="类型" prop="type">
|
|
|
<el-select v-model="contestForm.type" style="width: 200px">
|
|
|
@@ -482,6 +509,7 @@ onMounted(fetchData)
|
|
|
</el-form-item>
|
|
|
<el-form-item v-if="contestForm.type === 'password'" label="访问密码" prop="password">
|
|
|
<el-input v-model="contestForm.password" type="password" show-password placeholder="请输入访问密码" />
|
|
|
+ <span v-if="isEdit" class="tip">留空则不修改密码</span>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="开始时间" prop="startDatetime">
|
|
|
<el-date-picker
|
|
|
@@ -540,7 +568,7 @@ onMounted(fetchData)
|
|
|
<el-table-column prop="id" label="ID" width="60" align="center" />
|
|
|
<el-table-column prop="title" label="标题" min-width="150" show-overflow-tooltip />
|
|
|
<el-table-column prop="difficulty" label="难度" width="80" align="center">
|
|
|
- <template #default="{ row }">{{ row.difficulty || '-' }}</template>
|
|
|
+ <template #default="{ row }">{{ difficultyLabel(row.difficulty) }}</template>
|
|
|
</el-table-column>
|
|
|
</el-table>
|
|
|
<div class="pagination">
|
|
|
@@ -559,7 +587,6 @@ onMounted(fetchData)
|
|
|
<div class="config-panel">
|
|
|
<div class="config-header">
|
|
|
<span class="card-title">已选题目({{ configProblems.length }})</span>
|
|
|
- <el-button v-if="configLoaded" type="primary" size="small" :loading="configSaving" @click="saveConfig">保存配置</el-button>
|
|
|
</div>
|
|
|
<el-table v-if="configProblems.length" :data="configProblems" border stripe size="small" height="420">
|
|
|
<el-table-column label="题号" width="90" align="center">
|
|
|
@@ -573,7 +600,7 @@ onMounted(fetchData)
|
|
|
<el-input-number v-model="row.score" :min="0" :max="1000" size="small" controls-position="right" style="width: 90px" />
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column label="操作" width="60" align="center">
|
|
|
+ <el-table-column label="操作" width="60" align="center" class-name="op-column">
|
|
|
<template #default="{ row }">
|
|
|
<el-button type="danger" link size="small" @click="removeConfigured(row.problemId)">移除</el-button>
|
|
|
</template>
|
|
|
@@ -584,7 +611,7 @@ onMounted(fetchData)
|
|
|
</div>
|
|
|
<template #footer>
|
|
|
<el-button @click="configDialogVisible = false">关闭</el-button>
|
|
|
- <el-button type="primary" :loading="configSaving" @click="saveConfig">保存配置</el-button>
|
|
|
+ <el-button type="primary" :disabled="!configLoaded" :loading="configSaving" @click="saveConfig">保存配置</el-button>
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
|
|
|
@@ -620,6 +647,10 @@ onMounted(fetchData)
|
|
|
margin-bottom: 16px;
|
|
|
}
|
|
|
|
|
|
+:deep(.el-table .op-column .cell) {
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
+
|
|
|
.pagination {
|
|
|
display: flex;
|
|
|
justify-content: flex-end;
|
|
|
@@ -660,4 +691,10 @@ onMounted(fetchData)
|
|
|
.card-title {
|
|
|
font-weight: 600;
|
|
|
}
|
|
|
+
|
|
|
+.tip {
|
|
|
+ font-size: 12px;
|
|
|
+ color: var(--el-text-color-secondary);
|
|
|
+ line-height: 1.4;
|
|
|
+}
|
|
|
</style>
|