2026-03-27 22:20:25 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<div class="review-page">
|
|
|
|
|
|
<a-card class="mb-4">
|
|
|
|
|
|
<template #title>评审作品</template>
|
|
|
|
|
|
</a-card>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 数据表格 -->
|
|
|
|
|
|
<a-table
|
|
|
|
|
|
:columns="columns"
|
|
|
|
|
|
:data-source="dataSource"
|
|
|
|
|
|
:loading="loading"
|
|
|
|
|
|
:pagination="false"
|
2026-04-03 19:10:44 +08:00
|
|
|
|
row-key="contestId"
|
2026-03-27 22:20:25 +08:00
|
|
|
|
>
|
|
|
|
|
|
<template #bodyCell="{ column, record, index }">
|
|
|
|
|
|
<template v-if="column.key === 'index'">
|
|
|
|
|
|
{{ index + 1 }}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<template v-else-if="column.key === 'contestName'">
|
|
|
|
|
|
<a @click="handleViewDetail(record)">{{ record.contestName }}</a>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<template v-else-if="column.key === 'progress'">
|
|
|
|
|
|
<a-progress
|
|
|
|
|
|
:percent="getProgressPercent(record)"
|
|
|
|
|
|
:status="getProgressStatus(record)"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
style="width: 120px"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<span class="progress-text">{{ record.reviewed }}/{{ record.totalAssigned }}</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<template v-else-if="column.key === 'reviewStatus'">
|
2026-04-08 10:53:07 +08:00
|
|
|
|
<a-tag v-if="record.totalAssigned > 0 && record.pending === 0" color="success">已完成</a-tag>
|
|
|
|
|
|
<a-tag v-else-if="record.totalAssigned === 0" color="default">无分配</a-tag>
|
2026-03-27 22:20:25 +08:00
|
|
|
|
<a-tag v-else color="processing">评审中</a-tag>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<template v-else-if="column.key === 'action'">
|
|
|
|
|
|
<a-button type="link" size="small" @click="handleViewDetail(record)">
|
|
|
|
|
|
进入评审
|
|
|
|
|
|
</a-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</a-table>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import { ref, onMounted } from "vue"
|
|
|
|
|
|
import { useRouter, useRoute } from "vue-router"
|
|
|
|
|
|
import { message } from "ant-design-vue"
|
|
|
|
|
|
import { reviewsApi } from "@/api/contests"
|
|
|
|
|
|
|
|
|
|
|
|
const router = useRouter()
|
|
|
|
|
|
const route = useRoute()
|
|
|
|
|
|
const tenantCode = route.params.tenantCode as string
|
|
|
|
|
|
|
|
|
|
|
|
// 表格数据
|
|
|
|
|
|
const loading = ref(false)
|
|
|
|
|
|
const dataSource = ref<any[]>([])
|
|
|
|
|
|
|
|
|
|
|
|
// 表格列定义
|
|
|
|
|
|
const columns = [
|
|
|
|
|
|
{
|
|
|
|
|
|
title: "序号",
|
|
|
|
|
|
key: "index",
|
|
|
|
|
|
width: 70,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: "活动名称",
|
|
|
|
|
|
key: "contestName",
|
|
|
|
|
|
dataIndex: "contestName",
|
|
|
|
|
|
width: 300,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: "评审进度",
|
|
|
|
|
|
key: "progress",
|
|
|
|
|
|
width: 200,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: "待评审",
|
|
|
|
|
|
key: "pending",
|
|
|
|
|
|
dataIndex: "pending",
|
|
|
|
|
|
width: 100,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: "状态",
|
|
|
|
|
|
key: "reviewStatus",
|
|
|
|
|
|
width: 100,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: "操作",
|
|
|
|
|
|
key: "action",
|
|
|
|
|
|
width: 120,
|
|
|
|
|
|
fixed: "right" as const,
|
|
|
|
|
|
},
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
// 计算进度百分比
|
|
|
|
|
|
const getProgressPercent = (record: any) => {
|
|
|
|
|
|
if (record.totalAssigned === 0) return 0
|
|
|
|
|
|
return Math.round((record.reviewed / record.totalAssigned) * 100)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-08 10:53:07 +08:00
|
|
|
|
// 获取进度状态(无分配时不显示 success,避免与「无分配」矛盾)
|
2026-03-27 22:20:25 +08:00
|
|
|
|
const getProgressStatus = (record: any) => {
|
2026-04-08 10:53:07 +08:00
|
|
|
|
if (record.totalAssigned === 0) return "normal"
|
2026-03-27 22:20:25 +08:00
|
|
|
|
if (record.pending === 0) return "success"
|
|
|
|
|
|
return "active"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加载数据
|
|
|
|
|
|
const fetchList = async () => {
|
|
|
|
|
|
loading.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
const data = await reviewsApi.getJudgeContests()
|
|
|
|
|
|
dataSource.value = data
|
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
|
message.error(error?.response?.data?.message || "获取数据失败")
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
loading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 查看详情
|
|
|
|
|
|
const handleViewDetail = (record: any) => {
|
2026-04-03 19:10:44 +08:00
|
|
|
|
router.push({
|
|
|
|
|
|
path: `/${tenantCode}/activities/review/${record.contestId}`,
|
|
|
|
|
|
query: { contestName: record.contestName },
|
|
|
|
|
|
})
|
2026-03-27 22:20:25 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
fetchList()
|
|
|
|
|
|
})
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
|
$primary: #1890ff;
|
|
|
|
|
|
$primary-dark: #0958d9;
|
|
|
|
|
|
$gradient-primary: linear-gradient(135deg, $primary 0%, $primary-dark 100%);
|
|
|
|
|
|
|
|
|
|
|
|
.review-page {
|
|
|
|
|
|
:deep(.ant-card) {
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
|
|
|
|
|
|
|
|
|
|
|
.ant-card-head {
|
|
|
|
|
|
border-bottom: none;
|
|
|
|
|
|
padding: 16px 24px;
|
|
|
|
|
|
|
|
|
|
|
|
.ant-card-head-title {
|
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: rgba(0, 0, 0, 0.85);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ant-card-body {
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.ant-btn-primary) {
|
|
|
|
|
|
background: $gradient-primary;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
box-shadow: 0 4px 12px rgba($primary, 0.35);
|
|
|
|
|
|
transition: all 0.3s ease;
|
|
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
background: linear-gradient(135deg, $primary-dark 0%, darken($primary-dark, 8%) 100%);
|
|
|
|
|
|
box-shadow: 0 6px 16px rgba($primary, 0.45);
|
|
|
|
|
|
transform: translateY(-1px);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
&:active {
|
|
|
|
|
|
transform: translateY(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.ant-table-wrapper) {
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
|
|
|
|
|
|
.ant-table {
|
|
|
|
|
|
.ant-table-thead > tr > th {
|
|
|
|
|
|
background: #fafafa;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: rgba(0, 0, 0, 0.85);
|
|
|
|
|
|
border-bottom: 1px solid #f0f0f0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ant-table-tbody > tr {
|
|
|
|
|
|
transition: all 0.2s ease;
|
|
|
|
|
|
|
|
|
|
|
|
&:hover > td {
|
|
|
|
|
|
background: rgba($primary, 0.04);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
> td {
|
|
|
|
|
|
border-bottom: 1px solid #f5f5f5;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ant-table-pagination {
|
|
|
|
|
|
padding: 16px;
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.mb-4 {
|
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.progress-text {
|
|
|
|
|
|
margin-left: 8px;
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|