2026-01-08 09:17:46 +08:00
|
|
|
<template>
|
|
|
|
|
<div class="judges-page">
|
|
|
|
|
<a-card class="mb-4">
|
2026-01-16 16:35:43 +08:00
|
|
|
<template #title>评委管理</template>
|
2026-01-08 09:17:46 +08:00
|
|
|
<template #extra>
|
|
|
|
|
<a-space>
|
|
|
|
|
<a-button
|
|
|
|
|
v-permission="'judge:create'"
|
|
|
|
|
type="primary"
|
|
|
|
|
@click="handleAdd"
|
|
|
|
|
>
|
|
|
|
|
<template #icon><PlusOutlined /></template>
|
|
|
|
|
新增
|
|
|
|
|
</a-button>
|
2026-03-31 20:02:24 +08:00
|
|
|
<a-tooltip title="功能开发中,敬请期待">
|
|
|
|
|
<a-button v-permission="'judge:create'" disabled>
|
|
|
|
|
<template #icon><UploadOutlined /></template>
|
|
|
|
|
导入
|
|
|
|
|
</a-button>
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
<a-tooltip title="功能开发中,敬请期待">
|
|
|
|
|
<a-button v-permission="'judge:read'" disabled>
|
|
|
|
|
<template #icon><DownloadOutlined /></template>
|
|
|
|
|
导出
|
|
|
|
|
</a-button>
|
|
|
|
|
</a-tooltip>
|
2026-01-08 09:17:46 +08:00
|
|
|
<a-popconfirm
|
|
|
|
|
v-permission="'judge:delete'"
|
|
|
|
|
title="确定要删除选中的评委吗?"
|
2026-04-07 17:10:40 +08:00
|
|
|
:disabled="selectedRowKeys.length === 0 || selectedRows.every(r => r.isPlatform)"
|
2026-01-08 09:17:46 +08:00
|
|
|
@confirm="handleBatchDelete"
|
|
|
|
|
>
|
2026-04-07 17:10:40 +08:00
|
|
|
<a-button danger :disabled="selectedRowKeys.length === 0 || selectedRows.every(r => r.isPlatform)">
|
2026-01-08 09:17:46 +08:00
|
|
|
<template #icon><DeleteOutlined /></template>
|
|
|
|
|
删除
|
|
|
|
|
</a-button>
|
|
|
|
|
</a-popconfirm>
|
|
|
|
|
</a-space>
|
|
|
|
|
</template>
|
|
|
|
|
</a-card>
|
|
|
|
|
|
|
|
|
|
<!-- 搜索表单 -->
|
|
|
|
|
<a-form
|
|
|
|
|
:model="searchParams"
|
|
|
|
|
layout="inline"
|
|
|
|
|
class="search-form"
|
|
|
|
|
@finish="handleSearch"
|
|
|
|
|
>
|
2026-01-12 16:06:34 +08:00
|
|
|
<a-form-item label="所属单位">
|
|
|
|
|
<a-input
|
|
|
|
|
v-model:value="searchParams.organization"
|
|
|
|
|
placeholder="请输入所属单位"
|
2026-01-08 09:17:46 +08:00
|
|
|
allow-clear
|
|
|
|
|
style="width: 200px"
|
2026-01-12 16:06:34 +08:00
|
|
|
/>
|
2026-01-08 09:17:46 +08:00
|
|
|
</a-form-item>
|
|
|
|
|
<a-form-item label="姓名">
|
|
|
|
|
<a-input
|
|
|
|
|
v-model:value="searchParams.nickname"
|
|
|
|
|
placeholder="请输入姓名"
|
|
|
|
|
allow-clear
|
|
|
|
|
style="width: 150px"
|
|
|
|
|
/>
|
|
|
|
|
</a-form-item>
|
|
|
|
|
<a-form-item label="账号">
|
|
|
|
|
<a-input
|
|
|
|
|
v-model:value="searchParams.username"
|
|
|
|
|
placeholder="请输入账号"
|
|
|
|
|
allow-clear
|
|
|
|
|
style="width: 150px"
|
|
|
|
|
/>
|
|
|
|
|
</a-form-item>
|
|
|
|
|
<a-form-item label="状态">
|
|
|
|
|
<a-select
|
|
|
|
|
v-model:value="searchParams.status"
|
|
|
|
|
placeholder="请选择状态"
|
|
|
|
|
allow-clear
|
|
|
|
|
style="width: 120px"
|
2026-03-31 20:02:24 +08:00
|
|
|
@change="handleSearch"
|
2026-01-08 09:17:46 +08:00
|
|
|
>
|
|
|
|
|
<a-select-option value="disabled">停用</a-select-option>
|
|
|
|
|
<a-select-option value="enabled">启用</a-select-option>
|
|
|
|
|
</a-select>
|
|
|
|
|
</a-form-item>
|
|
|
|
|
<a-form-item>
|
|
|
|
|
<a-button type="primary" html-type="submit">
|
|
|
|
|
<template #icon><SearchOutlined /></template>
|
|
|
|
|
搜索
|
|
|
|
|
</a-button>
|
|
|
|
|
<a-button style="margin-left: 8px" @click="handleReset">
|
|
|
|
|
<template #icon><ReloadOutlined /></template>
|
|
|
|
|
重置
|
|
|
|
|
</a-button>
|
|
|
|
|
</a-form-item>
|
|
|
|
|
</a-form>
|
|
|
|
|
|
|
|
|
|
<!-- 数据表格 -->
|
|
|
|
|
<a-table
|
|
|
|
|
:columns="columns"
|
|
|
|
|
:data-source="dataSource"
|
|
|
|
|
:loading="loading"
|
|
|
|
|
:pagination="pagination"
|
|
|
|
|
:row-selection="rowSelection"
|
|
|
|
|
row-key="id"
|
|
|
|
|
@change="handleTableChange"
|
|
|
|
|
>
|
|
|
|
|
<template #bodyCell="{ column, record, index }">
|
|
|
|
|
<template v-if="column.key === 'index'">
|
|
|
|
|
{{ (pagination.current - 1) * pagination.pageSize + index + 1 }}
|
|
|
|
|
</template>
|
2026-01-12 16:06:34 +08:00
|
|
|
<template v-else-if="column.key === 'organization'">
|
|
|
|
|
{{ record.organization || "-" }}
|
2026-01-08 09:17:46 +08:00
|
|
|
</template>
|
|
|
|
|
<template v-else-if="column.key === 'gender'">
|
|
|
|
|
<span v-if="record.gender === 'male'">男</span>
|
|
|
|
|
<span v-else-if="record.gender === 'female'">女</span>
|
|
|
|
|
<span v-else>-</span>
|
|
|
|
|
</template>
|
2026-04-07 17:10:40 +08:00
|
|
|
<template v-else-if="column.key === 'source'">
|
|
|
|
|
<a-tag v-if="record.isPlatform" color="blue">平台</a-tag>
|
|
|
|
|
<span v-else>-</span>
|
|
|
|
|
</template>
|
2026-01-08 09:17:46 +08:00
|
|
|
<template v-else-if="column.key === 'status'">
|
|
|
|
|
<a-tag :color="record.status === 'enabled' ? 'success' : 'error'">
|
|
|
|
|
{{ record.status === "enabled" ? "启用" : "停用" }}
|
|
|
|
|
</a-tag>
|
|
|
|
|
</template>
|
|
|
|
|
<template v-else-if="column.key === 'ongoingContests'">
|
|
|
|
|
<template
|
|
|
|
|
v-if="record.contestJudges && record.contestJudges.length > 0"
|
|
|
|
|
>
|
|
|
|
|
<a-tooltip>
|
|
|
|
|
<template #title>
|
|
|
|
|
<div v-for="cj in record.contestJudges" :key="cj.contest.id">
|
|
|
|
|
{{ cj.contest.contestName }}
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
<a-tag color="processing">
|
2026-03-27 22:20:25 +08:00
|
|
|
{{ record.contestJudges.length }}个活动
|
2026-01-08 09:17:46 +08:00
|
|
|
</a-tag>
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
</template>
|
|
|
|
|
<span v-else>-</span>
|
|
|
|
|
</template>
|
|
|
|
|
<template v-else-if="column.key === 'action'">
|
|
|
|
|
<a-space>
|
2026-04-07 17:10:40 +08:00
|
|
|
<template v-if="!record.isPlatform">
|
|
|
|
|
<a-button
|
|
|
|
|
v-permission="'judge:update'"
|
|
|
|
|
type="link"
|
|
|
|
|
size="small"
|
|
|
|
|
@click="handleToggleStatus(record)"
|
|
|
|
|
>
|
|
|
|
|
{{ record.status === "enabled" ? "冻结" : "解冻" }}
|
|
|
|
|
</a-button>
|
|
|
|
|
</template>
|
|
|
|
|
<template v-if="!record.isPlatform">
|
|
|
|
|
<a-button
|
|
|
|
|
v-permission="'judge:update'"
|
|
|
|
|
type="link"
|
|
|
|
|
size="small"
|
|
|
|
|
@click="handleEdit(record)"
|
|
|
|
|
>
|
|
|
|
|
编辑
|
|
|
|
|
</a-button>
|
|
|
|
|
</template>
|
2026-01-08 09:17:46 +08:00
|
|
|
<a-popconfirm
|
2026-04-07 17:10:40 +08:00
|
|
|
v-if="!record.isPlatform"
|
2026-01-08 09:17:46 +08:00
|
|
|
v-permission="'judge:delete'"
|
|
|
|
|
title="确定要删除这个评委吗?"
|
|
|
|
|
@confirm="handleDelete(record.id)"
|
|
|
|
|
>
|
|
|
|
|
<a-button type="link" danger size="small">删除</a-button>
|
|
|
|
|
</a-popconfirm>
|
|
|
|
|
</a-space>
|
|
|
|
|
</template>
|
|
|
|
|
</template>
|
|
|
|
|
</a-table>
|
|
|
|
|
|
|
|
|
|
<!-- 新增/编辑评委抽屉 -->
|
|
|
|
|
<a-drawer
|
|
|
|
|
v-model:open="drawerVisible"
|
|
|
|
|
:title="isEditing ? '编辑评委' : '新增评委'"
|
|
|
|
|
placement="right"
|
|
|
|
|
width="500px"
|
|
|
|
|
:footer-style="{ textAlign: 'right' }"
|
|
|
|
|
>
|
|
|
|
|
<a-form
|
|
|
|
|
ref="formRef"
|
|
|
|
|
:model="form"
|
|
|
|
|
:rules="rules"
|
|
|
|
|
:label-col="{ span: 6 }"
|
|
|
|
|
:wrapper-col="{ span: 18 }"
|
|
|
|
|
>
|
2026-01-09 18:14:35 +08:00
|
|
|
<a-form-item label="账号" name="username">
|
|
|
|
|
<a-input
|
|
|
|
|
v-model:value="form.username"
|
|
|
|
|
placeholder="请输入账号"
|
|
|
|
|
:maxlength="50"
|
|
|
|
|
:disabled="isEditing"
|
|
|
|
|
/>
|
|
|
|
|
</a-form-item>
|
2026-01-08 09:17:46 +08:00
|
|
|
<a-form-item label="姓名" name="nickname">
|
|
|
|
|
<a-input
|
|
|
|
|
v-model:value="form.nickname"
|
|
|
|
|
placeholder="请输入姓名"
|
|
|
|
|
:maxlength="50"
|
|
|
|
|
/>
|
|
|
|
|
</a-form-item>
|
|
|
|
|
<a-form-item label="性别" name="gender">
|
|
|
|
|
<a-radio-group v-model:value="form.gender">
|
|
|
|
|
<a-radio value="male">男</a-radio>
|
|
|
|
|
<a-radio value="female">女</a-radio>
|
|
|
|
|
</a-radio-group>
|
|
|
|
|
</a-form-item>
|
2026-01-12 16:06:34 +08:00
|
|
|
<a-form-item label="所属单位" name="organization">
|
2026-01-08 09:17:46 +08:00
|
|
|
<a-input
|
2026-01-12 16:06:34 +08:00
|
|
|
v-model:value="form.organization"
|
|
|
|
|
placeholder="请输入所属单位"
|
|
|
|
|
:maxlength="100"
|
2026-01-08 09:17:46 +08:00
|
|
|
/>
|
|
|
|
|
</a-form-item>
|
|
|
|
|
<a-form-item label="联系方式" name="phone">
|
|
|
|
|
<a-input
|
|
|
|
|
v-model:value="form.phone"
|
|
|
|
|
placeholder="请输入联系方式"
|
|
|
|
|
:maxlength="20"
|
|
|
|
|
/>
|
|
|
|
|
</a-form-item>
|
|
|
|
|
<a-form-item label="初始密码" name="password">
|
|
|
|
|
<a-input-password
|
|
|
|
|
v-model:value="form.password"
|
|
|
|
|
:placeholder="isEditing ? '请输入新密码' : '请输入初始密码'"
|
|
|
|
|
:maxlength="50"
|
|
|
|
|
/>
|
|
|
|
|
</a-form-item>
|
|
|
|
|
</a-form>
|
|
|
|
|
<template #footer>
|
|
|
|
|
<a-button style="margin-right: 8px" @click="handleCancel"
|
|
|
|
|
>取消</a-button
|
|
|
|
|
>
|
|
|
|
|
<a-button type="primary" :loading="submitLoading" @click="handleSubmit">
|
|
|
|
|
确定
|
|
|
|
|
</a-button>
|
|
|
|
|
</template>
|
|
|
|
|
</a-drawer>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { ref, reactive, computed, onMounted } from "vue"
|
|
|
|
|
import { useRoute } from "vue-router"
|
2026-03-31 20:02:24 +08:00
|
|
|
import { message, Modal } from "ant-design-vue"
|
2026-01-08 09:17:46 +08:00
|
|
|
import type { FormInstance, TableProps } from "ant-design-vue"
|
|
|
|
|
import {
|
|
|
|
|
PlusOutlined,
|
|
|
|
|
UploadOutlined,
|
|
|
|
|
DownloadOutlined,
|
|
|
|
|
DeleteOutlined,
|
|
|
|
|
SearchOutlined,
|
|
|
|
|
ReloadOutlined,
|
|
|
|
|
} from "@ant-design/icons-vue"
|
|
|
|
|
import { useListRequest } from "@/composables/useListRequest"
|
|
|
|
|
import { contestsApi } from "@/api/contests"
|
|
|
|
|
import {
|
|
|
|
|
judgesManagementApi,
|
|
|
|
|
type Judge,
|
|
|
|
|
type QueryJudgeParams,
|
|
|
|
|
} from "@/api/judges-management"
|
|
|
|
|
|
|
|
|
|
const route = useRoute()
|
|
|
|
|
const contestId = route.params.id ? Number(route.params.id) : null
|
|
|
|
|
const tenantCode = route.params.tenantCode as string
|
|
|
|
|
|
|
|
|
|
// 检查 contestId 是否有效
|
|
|
|
|
const isValidContestId =
|
|
|
|
|
contestId !== null && !isNaN(contestId) && contestId > 0
|
|
|
|
|
|
|
|
|
|
// 获取评委列表
|
|
|
|
|
const fetchJudgesList = async (params: QueryJudgeParams) => {
|
|
|
|
|
const response = await judgesManagementApi.getList(params)
|
|
|
|
|
return response
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 使用列表请求组合函数
|
|
|
|
|
const {
|
|
|
|
|
loading,
|
|
|
|
|
dataSource,
|
|
|
|
|
pagination,
|
|
|
|
|
searchParams,
|
|
|
|
|
fetchList,
|
|
|
|
|
resetSearch,
|
|
|
|
|
search,
|
|
|
|
|
handleTableChange,
|
|
|
|
|
} = useListRequest<Judge, QueryJudgeParams>({
|
|
|
|
|
requestFn: fetchJudgesList,
|
|
|
|
|
defaultSearchParams: {} as QueryJudgeParams,
|
|
|
|
|
defaultPageSize: 10,
|
|
|
|
|
errorMessage: "获取评委列表失败",
|
|
|
|
|
})
|
|
|
|
|
|
2026-03-27 22:20:25 +08:00
|
|
|
// 活动信息
|
2026-01-08 09:17:46 +08:00
|
|
|
const contestName = ref("")
|
|
|
|
|
|
|
|
|
|
// 表格选择
|
|
|
|
|
const selectedRowKeys = ref<number[]>([])
|
2026-01-16 16:35:43 +08:00
|
|
|
const rowSelection = computed<TableProps["rowSelection"]>(() => ({
|
2026-01-08 09:17:46 +08:00
|
|
|
selectedRowKeys: selectedRowKeys.value,
|
|
|
|
|
onChange: (keys: any) => {
|
|
|
|
|
selectedRowKeys.value = keys
|
|
|
|
|
},
|
2026-01-16 16:35:43 +08:00
|
|
|
}))
|
2026-01-08 09:17:46 +08:00
|
|
|
|
|
|
|
|
// 抽屉相关
|
|
|
|
|
const drawerVisible = ref(false)
|
|
|
|
|
const isEditing = ref(false)
|
|
|
|
|
const editingId = ref<number | null>(null)
|
|
|
|
|
const submitLoading = ref(false)
|
|
|
|
|
const formRef = ref<FormInstance>()
|
|
|
|
|
|
|
|
|
|
const form = reactive<{
|
2026-01-09 18:14:35 +08:00
|
|
|
username: string
|
2026-01-08 09:17:46 +08:00
|
|
|
nickname: string
|
|
|
|
|
gender: "male" | "female" | undefined
|
2026-01-12 16:06:34 +08:00
|
|
|
organization: string
|
2026-01-08 09:17:46 +08:00
|
|
|
phone: string
|
|
|
|
|
password: string
|
|
|
|
|
}>({
|
2026-01-09 18:14:35 +08:00
|
|
|
username: "",
|
2026-01-08 09:17:46 +08:00
|
|
|
nickname: "",
|
|
|
|
|
gender: undefined,
|
2026-01-12 16:06:34 +08:00
|
|
|
organization: "",
|
2026-01-08 09:17:46 +08:00
|
|
|
phone: "",
|
|
|
|
|
password: "",
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 表单验证规则
|
|
|
|
|
const rules = computed(() => ({
|
2026-01-09 18:14:35 +08:00
|
|
|
username: isEditing.value
|
|
|
|
|
? []
|
|
|
|
|
: [{ required: true, message: "请输入账号", trigger: "blur" }],
|
2026-01-08 09:17:46 +08:00
|
|
|
nickname: [{ required: true, message: "请输入姓名", trigger: "blur" }],
|
|
|
|
|
gender: [{ required: true, message: "请选择性别", trigger: "change" }],
|
2026-01-12 16:06:34 +08:00
|
|
|
organization: [{ required: true, message: "请输入所属单位", trigger: "blur" }],
|
2026-01-08 09:17:46 +08:00
|
|
|
phone: [{ required: true, message: "请输入联系方式", trigger: "blur" }],
|
|
|
|
|
password: isEditing.value
|
|
|
|
|
? []
|
|
|
|
|
: [{ required: true, message: "请输入初始密码", trigger: "blur" }],
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
// 表格列定义
|
|
|
|
|
const columns = [
|
|
|
|
|
{
|
|
|
|
|
title: "序号",
|
|
|
|
|
key: "index",
|
|
|
|
|
width: 70,
|
|
|
|
|
},
|
2026-04-07 17:10:40 +08:00
|
|
|
{
|
|
|
|
|
title: "来源",
|
|
|
|
|
key: "source",
|
|
|
|
|
width: 80,
|
|
|
|
|
},
|
2026-01-08 09:17:46 +08:00
|
|
|
{
|
|
|
|
|
title: "所属单位",
|
2026-01-12 16:06:34 +08:00
|
|
|
key: "organization",
|
|
|
|
|
dataIndex: "organization",
|
2026-01-08 09:17:46 +08:00
|
|
|
width: 150,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: "姓名",
|
|
|
|
|
dataIndex: "nickname",
|
|
|
|
|
key: "nickname",
|
|
|
|
|
width: 120,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: "性别",
|
|
|
|
|
key: "gender",
|
|
|
|
|
width: 80,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: "账号",
|
|
|
|
|
dataIndex: "username",
|
|
|
|
|
key: "username",
|
|
|
|
|
width: 150,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: "联系方式",
|
|
|
|
|
dataIndex: "phone",
|
|
|
|
|
key: "phone",
|
|
|
|
|
width: 130,
|
|
|
|
|
},
|
|
|
|
|
{
|
2026-03-27 22:20:25 +08:00
|
|
|
title: "关联进行中的活动",
|
2026-01-08 09:17:46 +08:00
|
|
|
key: "ongoingContests",
|
|
|
|
|
width: 150,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: "状态",
|
|
|
|
|
key: "status",
|
|
|
|
|
width: 80,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: "操作",
|
|
|
|
|
key: "action",
|
|
|
|
|
width: 180,
|
|
|
|
|
fixed: "right" as const,
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
|
2026-03-27 22:20:25 +08:00
|
|
|
// 加载活动信息
|
2026-01-08 09:17:46 +08:00
|
|
|
const loadContestInfo = async () => {
|
|
|
|
|
// 如果 contestId 无效,跳过加载
|
|
|
|
|
if (!isValidContestId) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
const contest = await contestsApi.getDetail(contestId!)
|
|
|
|
|
contestName.value = contest.contestName
|
|
|
|
|
} catch (error) {
|
2026-03-27 22:20:25 +08:00
|
|
|
console.error("获取活动信息失败", error)
|
2026-01-08 09:17:46 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 搜索
|
|
|
|
|
const handleSearch = () => {
|
|
|
|
|
search()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 重置搜索
|
|
|
|
|
const handleReset = () => {
|
|
|
|
|
resetSearch()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 新增
|
|
|
|
|
const handleAdd = () => {
|
|
|
|
|
isEditing.value = false
|
|
|
|
|
editingId.value = null
|
|
|
|
|
drawerVisible.value = true
|
2026-01-09 18:14:35 +08:00
|
|
|
form.username = ""
|
2026-01-08 09:17:46 +08:00
|
|
|
form.nickname = ""
|
|
|
|
|
form.gender = undefined
|
2026-01-12 16:06:34 +08:00
|
|
|
form.organization = ""
|
2026-01-08 09:17:46 +08:00
|
|
|
form.phone = ""
|
|
|
|
|
form.password = ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 编辑
|
|
|
|
|
const handleEdit = (record: Judge) => {
|
|
|
|
|
isEditing.value = true
|
|
|
|
|
editingId.value = record.id
|
|
|
|
|
drawerVisible.value = true
|
2026-01-09 18:14:35 +08:00
|
|
|
form.username = record.username || ""
|
2026-01-08 09:17:46 +08:00
|
|
|
form.nickname = record.nickname || ""
|
|
|
|
|
form.gender = record.gender as "male" | "female" | undefined
|
2026-01-12 16:06:34 +08:00
|
|
|
form.organization = record.organization || ""
|
2026-01-08 09:17:46 +08:00
|
|
|
form.phone = record.phone || ""
|
|
|
|
|
form.password = ""
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 20:02:24 +08:00
|
|
|
// 切换状态(冻结/解冻)+ 二次确认
|
|
|
|
|
const handleToggleStatus = (record: Judge) => {
|
|
|
|
|
const isFreeze = record.status === "enabled"
|
|
|
|
|
Modal.confirm({
|
|
|
|
|
title: isFreeze ? '确定冻结该评委?' : '确定解冻该评委?',
|
|
|
|
|
content: isFreeze
|
|
|
|
|
? `冻结后「${record.nickname}」将无法登录评委端,进行中的评审任务将暂停`
|
|
|
|
|
: `解冻后「${record.nickname}」将恢复登录和评审功能`,
|
|
|
|
|
okText: isFreeze ? '确定冻结' : '确定解冻',
|
|
|
|
|
okType: isFreeze ? 'danger' : 'primary',
|
|
|
|
|
onOk: async () => {
|
|
|
|
|
try {
|
|
|
|
|
if (isFreeze) {
|
|
|
|
|
await judgesManagementApi.freeze(record.id)
|
|
|
|
|
message.success("冻结成功")
|
|
|
|
|
} else {
|
|
|
|
|
await judgesManagementApi.unfreeze(record.id)
|
|
|
|
|
message.success("解冻成功")
|
|
|
|
|
}
|
|
|
|
|
fetchList()
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
message.error(error?.response?.data?.message || "操作失败")
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
})
|
2026-01-08 09:17:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 删除
|
|
|
|
|
const handleDelete = async (id: number) => {
|
|
|
|
|
try {
|
|
|
|
|
await judgesManagementApi.delete(id)
|
|
|
|
|
message.success("删除成功")
|
|
|
|
|
fetchList()
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
message.error(error?.response?.data?.message || "删除失败")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 批量删除
|
|
|
|
|
const handleBatchDelete = async () => {
|
|
|
|
|
if (selectedRowKeys.value.length === 0) return
|
|
|
|
|
try {
|
|
|
|
|
await judgesManagementApi.batchDelete(selectedRowKeys.value)
|
|
|
|
|
message.success("批量删除成功")
|
|
|
|
|
selectedRowKeys.value = []
|
|
|
|
|
fetchList()
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
message.error(error?.response?.data?.message || "批量删除失败")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 提交表单
|
|
|
|
|
const handleSubmit = async () => {
|
|
|
|
|
try {
|
|
|
|
|
await formRef.value?.validate()
|
|
|
|
|
submitLoading.value = true
|
|
|
|
|
|
|
|
|
|
if (isEditing.value && editingId.value) {
|
|
|
|
|
// 编辑评委
|
|
|
|
|
await judgesManagementApi.update(editingId.value, {
|
|
|
|
|
nickname: form.nickname,
|
|
|
|
|
gender: form.gender,
|
2026-01-12 16:06:34 +08:00
|
|
|
organization: form.organization,
|
2026-01-08 09:17:46 +08:00
|
|
|
phone: form.phone,
|
|
|
|
|
...(form.password && { password: form.password }),
|
|
|
|
|
})
|
|
|
|
|
message.success("编辑成功")
|
|
|
|
|
} else {
|
|
|
|
|
// 新增评委
|
|
|
|
|
await judgesManagementApi.create({
|
2026-01-09 18:14:35 +08:00
|
|
|
username: form.username,
|
2026-01-08 09:17:46 +08:00
|
|
|
nickname: form.nickname,
|
|
|
|
|
gender: form.gender!,
|
2026-01-12 16:06:34 +08:00
|
|
|
organization: form.organization,
|
2026-01-08 09:17:46 +08:00
|
|
|
phone: form.phone,
|
|
|
|
|
password: form.password,
|
|
|
|
|
status: "enabled",
|
|
|
|
|
})
|
|
|
|
|
message.success("创建成功")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
drawerVisible.value = false
|
|
|
|
|
fetchList()
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
if (error?.errorFields) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
message.error(
|
|
|
|
|
error?.response?.data?.message ||
|
|
|
|
|
(isEditing.value ? "编辑失败" : "创建失败")
|
|
|
|
|
)
|
|
|
|
|
} finally {
|
|
|
|
|
submitLoading.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 取消
|
|
|
|
|
const handleCancel = () => {
|
|
|
|
|
drawerVisible.value = false
|
|
|
|
|
formRef.value?.resetFields()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
loadContestInfo()
|
|
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
|
|
2026-01-16 16:35:43 +08:00
|
|
|
<style scoped lang="scss">
|
2026-03-31 20:02:24 +08:00
|
|
|
$primary: #6366f1;
|
2026-01-16 16:35:43 +08:00
|
|
|
|
|
|
|
|
.judges-page {
|
|
|
|
|
:deep(.ant-card) {
|
|
|
|
|
border: none;
|
|
|
|
|
border-radius: 12px;
|
2026-03-31 20:02:24 +08:00
|
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
|
2026-01-16 16:35:43 +08:00
|
|
|
margin-bottom: 16px;
|
|
|
|
|
|
|
|
|
|
.ant-card-head {
|
|
|
|
|
border-bottom: none;
|
2026-03-31 20:02:24 +08:00
|
|
|
.ant-card-head-title { font-size: 18px; font-weight: 600; }
|
2026-01-16 16:35:43 +08:00
|
|
|
}
|
2026-03-31 20:02:24 +08:00
|
|
|
.ant-card-body { padding: 0; }
|
2026-01-16 16:35:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.ant-table-wrapper) {
|
|
|
|
|
background: #fff;
|
|
|
|
|
border-radius: 12px;
|
2026-03-31 20:02:24 +08:00
|
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
|
2026-01-16 16:35:43 +08:00
|
|
|
overflow: hidden;
|
|
|
|
|
|
2026-03-31 20:02:24 +08:00
|
|
|
.ant-table-thead > tr > th { background: #fafafa; font-weight: 600; }
|
|
|
|
|
.ant-table-tbody > tr:hover > td { background: rgba($primary, 0.03); }
|
|
|
|
|
.ant-table-pagination { padding: 16px; margin: 0; }
|
2026-01-16 16:35:43 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-08 09:17:46 +08:00
|
|
|
.search-form {
|
|
|
|
|
margin-bottom: 16px;
|
2026-01-16 16:35:43 +08:00
|
|
|
padding: 20px 24px;
|
|
|
|
|
background: #fff;
|
|
|
|
|
border-radius: 12px;
|
2026-03-31 20:02:24 +08:00
|
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
|
2026-01-08 09:17:46 +08:00
|
|
|
}
|
|
|
|
|
</style>
|