312 lines
7.2 KiB
Vue
312 lines
7.2 KiB
Vue
|
|
<template>
|
||
|
|
<div class="results-detail-page">
|
||
|
|
<!-- 顶部导航 -->
|
||
|
|
<div class="page-header">
|
||
|
|
<div class="header-left">
|
||
|
|
<a-button type="text" @click="handleBack">
|
||
|
|
<template #icon><ArrowLeftOutlined /></template>
|
||
|
|
返回
|
||
|
|
</a-button>
|
||
|
|
<span class="page-title">{{ contestInfo?.contestName || "赛果发布" }}</span>
|
||
|
|
</div>
|
||
|
|
<div class="header-right">
|
||
|
|
<a-button
|
||
|
|
type="primary"
|
||
|
|
:loading="publishLoading"
|
||
|
|
@click="handlePublish"
|
||
|
|
>
|
||
|
|
{{ contestInfo?.resultState === "published" ? "撤回发布" : "发布赛果" }}
|
||
|
|
</a-button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- 搜索表单 -->
|
||
|
|
<a-form
|
||
|
|
:model="searchParams"
|
||
|
|
layout="inline"
|
||
|
|
class="search-form"
|
||
|
|
@finish="handleSearch"
|
||
|
|
>
|
||
|
|
<a-form-item label="作品编号">
|
||
|
|
<a-input
|
||
|
|
v-model:value="searchParams.workNo"
|
||
|
|
placeholder="请输入作品编号"
|
||
|
|
allow-clear
|
||
|
|
style="width: 180px"
|
||
|
|
/>
|
||
|
|
</a-form-item>
|
||
|
|
<a-form-item label="报名账号">
|
||
|
|
<a-input
|
||
|
|
v-model:value="searchParams.accountNo"
|
||
|
|
placeholder="请输入报名账号"
|
||
|
|
allow-clear
|
||
|
|
style="width: 180px"
|
||
|
|
/>
|
||
|
|
</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-key="id"
|
||
|
|
@change="handleTableChange"
|
||
|
|
>
|
||
|
|
<template #bodyCell="{ column, record, index }">
|
||
|
|
<template v-if="column.key === 'index'">
|
||
|
|
{{ (pagination.current - 1) * pagination.pageSize + index + 1 }}
|
||
|
|
</template>
|
||
|
|
<template v-else-if="column.key === 'finalScore'">
|
||
|
|
<span v-if="record.finalScore !== null" class="score">
|
||
|
|
{{ Number(record.finalScore).toFixed(2) }}
|
||
|
|
</span>
|
||
|
|
<span v-else>-</span>
|
||
|
|
</template>
|
||
|
|
<template v-else-if="column.key === 'nickname'">
|
||
|
|
{{ record.registration?.user?.nickname || "-" }}
|
||
|
|
</template>
|
||
|
|
<template v-else-if="column.key === 'username'">
|
||
|
|
{{ record.registration?.user?.username || "-" }}
|
||
|
|
</template>
|
||
|
|
<template v-else-if="column.key === 'org'">
|
||
|
|
<div>
|
||
|
|
<div>{{ record.registration?.user?.tenant?.name || "-" }}</div>
|
||
|
|
<div v-if="record.registration?.user?.student?.class" class="org-detail">
|
||
|
|
{{ record.registration.user.student.class.grade?.name || "" }}
|
||
|
|
{{ record.registration.user.student.class.name || "" }}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</template>
|
||
|
|
<template v-else-if="column.key === 'teachers'">
|
||
|
|
{{ formatTeachers(record.registration?.teachers) }}
|
||
|
|
</template>
|
||
|
|
</template>
|
||
|
|
</a-table>
|
||
|
|
</div>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script setup lang="ts">
|
||
|
|
import { ref, reactive, onMounted } from "vue"
|
||
|
|
import { useRoute, useRouter } from "vue-router"
|
||
|
|
import { message, Modal } from "ant-design-vue"
|
||
|
|
import {
|
||
|
|
ArrowLeftOutlined,
|
||
|
|
SearchOutlined,
|
||
|
|
ReloadOutlined,
|
||
|
|
} from "@ant-design/icons-vue"
|
||
|
|
import { resultsApi } from "@/api/contests"
|
||
|
|
|
||
|
|
const route = useRoute()
|
||
|
|
const router = useRouter()
|
||
|
|
const tenantCode = route.params.tenantCode as string
|
||
|
|
const contestId = Number(route.params.id)
|
||
|
|
|
||
|
|
// 赛事信息
|
||
|
|
const contestInfo = ref<{
|
||
|
|
id: number
|
||
|
|
contestName: string
|
||
|
|
resultState: string
|
||
|
|
} | null>(null)
|
||
|
|
|
||
|
|
// 列表状态
|
||
|
|
const loading = ref(false)
|
||
|
|
const publishLoading = ref(false)
|
||
|
|
const dataSource = ref<any[]>([])
|
||
|
|
const pagination = reactive({
|
||
|
|
current: 1,
|
||
|
|
pageSize: 10,
|
||
|
|
total: 0,
|
||
|
|
})
|
||
|
|
|
||
|
|
// 搜索参数
|
||
|
|
const searchParams = reactive({
|
||
|
|
workNo: "",
|
||
|
|
accountNo: "",
|
||
|
|
})
|
||
|
|
|
||
|
|
// 表格列定义
|
||
|
|
const columns = [
|
||
|
|
{
|
||
|
|
title: "序号",
|
||
|
|
key: "index",
|
||
|
|
width: 70,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
title: "作品编号",
|
||
|
|
dataIndex: "workNo",
|
||
|
|
key: "workNo",
|
||
|
|
width: 120,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
title: "评委评分",
|
||
|
|
key: "finalScore",
|
||
|
|
width: 100,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
title: "姓名",
|
||
|
|
key: "nickname",
|
||
|
|
width: 120,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
title: "账号",
|
||
|
|
key: "username",
|
||
|
|
width: 120,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
title: "机构信息",
|
||
|
|
key: "org",
|
||
|
|
width: 180,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
title: "指导老师",
|
||
|
|
key: "teachers",
|
||
|
|
width: 150,
|
||
|
|
},
|
||
|
|
]
|
||
|
|
|
||
|
|
// 格式化指导老师
|
||
|
|
const formatTeachers = (teachers: any[] | undefined) => {
|
||
|
|
if (!teachers || teachers.length === 0) return "-"
|
||
|
|
return teachers
|
||
|
|
.map((t) => t.user?.nickname || t.user?.username)
|
||
|
|
.filter(Boolean)
|
||
|
|
.join("、") || "-"
|
||
|
|
}
|
||
|
|
|
||
|
|
// 获取列表数据
|
||
|
|
const fetchList = async () => {
|
||
|
|
loading.value = true
|
||
|
|
try {
|
||
|
|
const response = await resultsApi.getResults(contestId, {
|
||
|
|
page: pagination.current,
|
||
|
|
pageSize: pagination.pageSize,
|
||
|
|
workNo: searchParams.workNo || undefined,
|
||
|
|
accountNo: searchParams.accountNo || undefined,
|
||
|
|
})
|
||
|
|
contestInfo.value = response.contest
|
||
|
|
dataSource.value = response.list
|
||
|
|
pagination.total = response.total
|
||
|
|
} catch (error: any) {
|
||
|
|
message.error(error?.response?.data?.message || "获取列表失败")
|
||
|
|
} finally {
|
||
|
|
loading.value = false
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 搜索
|
||
|
|
const handleSearch = () => {
|
||
|
|
pagination.current = 1
|
||
|
|
fetchList()
|
||
|
|
}
|
||
|
|
|
||
|
|
// 重置
|
||
|
|
const handleReset = () => {
|
||
|
|
searchParams.workNo = ""
|
||
|
|
searchParams.accountNo = ""
|
||
|
|
pagination.current = 1
|
||
|
|
fetchList()
|
||
|
|
}
|
||
|
|
|
||
|
|
// 表格变化
|
||
|
|
const handleTableChange = (pag: any) => {
|
||
|
|
pagination.current = pag.current
|
||
|
|
pagination.pageSize = pag.pageSize
|
||
|
|
fetchList()
|
||
|
|
}
|
||
|
|
|
||
|
|
// 返回
|
||
|
|
const handleBack = () => {
|
||
|
|
router.push(`/${tenantCode}/contests/results`)
|
||
|
|
}
|
||
|
|
|
||
|
|
// 发布/撤回赛果
|
||
|
|
const handlePublish = () => {
|
||
|
|
const isPublished = contestInfo.value?.resultState === "published"
|
||
|
|
|
||
|
|
Modal.confirm({
|
||
|
|
title: isPublished ? "确定撤回赛果发布吗?" : "确定发布赛果吗?",
|
||
|
|
content: isPublished
|
||
|
|
? "撤回后,赛果将不再对外公开显示"
|
||
|
|
: "发布后,比赛结果将公开显示",
|
||
|
|
onOk: async () => {
|
||
|
|
publishLoading.value = true
|
||
|
|
try {
|
||
|
|
if (isPublished) {
|
||
|
|
await resultsApi.unpublish(contestId)
|
||
|
|
message.success("已撤回发布")
|
||
|
|
} else {
|
||
|
|
await resultsApi.publish(contestId)
|
||
|
|
message.success("发布成功")
|
||
|
|
}
|
||
|
|
fetchList()
|
||
|
|
} catch (error: any) {
|
||
|
|
message.error(error?.response?.data?.message || "操作失败")
|
||
|
|
} finally {
|
||
|
|
publishLoading.value = false
|
||
|
|
}
|
||
|
|
},
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
onMounted(() => {
|
||
|
|
fetchList()
|
||
|
|
})
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<style scoped>
|
||
|
|
.results-detail-page {
|
||
|
|
padding: 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.page-header {
|
||
|
|
display: flex;
|
||
|
|
justify-content: space-between;
|
||
|
|
align-items: center;
|
||
|
|
padding: 16px;
|
||
|
|
background: #fff;
|
||
|
|
border-radius: 8px;
|
||
|
|
margin-bottom: 16px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.header-left {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 8px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.page-title {
|
||
|
|
font-size: 16px;
|
||
|
|
font-weight: 500;
|
||
|
|
}
|
||
|
|
|
||
|
|
.search-form {
|
||
|
|
margin-bottom: 16px;
|
||
|
|
padding: 16px;
|
||
|
|
background: #fff;
|
||
|
|
border-radius: 8px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.org-detail {
|
||
|
|
font-size: 12px;
|
||
|
|
color: #666;
|
||
|
|
margin-top: 2px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.score {
|
||
|
|
font-weight: bold;
|
||
|
|
color: #52c41a;
|
||
|
|
}
|
||
|
|
</style>
|