- 新增用户排行榜页面和待发布页面,支持查看和发布排行榜 - 新增排行榜相关API接口,包括获取活动排行列表、修改队伍分数、发布排名和获取用户排名列表 - 新增RankPublishStatus枚举,用于管理排行榜发布状态 - 在活动卡片中新增结束活动按钮,点击后调用结束活动接口并生成排行榜数据 - 优化用户活动首页,只显示已发布和处理中的活动 - 调整游戏页面定时刷新间隔从2秒改为3秒,优化性能 - 修复排行榜详情页分数输入最大值限制问题 - 清理未使用的代码文件,移除results-detail-copy相关组件和路由 - 更新路由配置和类型定义,支持新的排行榜页面
269 lines
6.5 KiB
Vue
269 lines
6.5 KiB
Vue
<script setup lang="tsx">
|
|
import type { DataTableColumns } from 'naive-ui'
|
|
import { NDataTable, NImage, NTag, useMessage } from 'naive-ui'
|
|
import { onMounted, ref } from 'vue'
|
|
import { useRoute } from 'vue-router'
|
|
import title from '@/assets/imgs/user/rank-title.svg'
|
|
import { fetchUserRankList } from '@/service/api/rank'
|
|
|
|
const route = useRoute()
|
|
const message = useMessage()
|
|
const MainID = Number(route.query.activityId)
|
|
|
|
// 定义数据结构
|
|
interface TeamData {
|
|
id: string
|
|
rank: number
|
|
teamName: string
|
|
[key: string]: number | string // 允许任意数量的题目分数
|
|
total: number
|
|
}
|
|
|
|
const columns = ref<DataTableColumns<TeamData>>([
|
|
{
|
|
title: '排名',
|
|
key: 'rank',
|
|
align: 'center',
|
|
width: 80,
|
|
render(row: TeamData) {
|
|
const rank = row.rank
|
|
if (rank === 1) {
|
|
return <NTag type="warning" size="large" round>🏆</NTag>
|
|
}
|
|
if (rank === 2) {
|
|
return <NTag type="info" size="large" round>🥈</NTag>
|
|
}
|
|
if (rank === 3) {
|
|
return <NTag type="success" size="large" round>🥉</NTag>
|
|
}
|
|
return <span>{rank}</span>
|
|
},
|
|
},
|
|
{ title: '队伍名称', key: 'teamName', align: 'left', width: 150, className: 'team-name' },
|
|
{ title: '总积分', key: 'total', align: 'center', className: 'total-score' },
|
|
])
|
|
|
|
const tableData = ref<TeamData[]>([])
|
|
|
|
async function getUserRankList() {
|
|
const { data, error } = await fetchUserRankList(MainID)
|
|
if (error) {
|
|
message.error(error.message || '获取排行榜失败')
|
|
return
|
|
}
|
|
|
|
if (data?.data && data.data.length > 0) {
|
|
// 动态生成题目列
|
|
const firstTeam = data.data[0]
|
|
const questionColumns = firstTeam.Questions.map(q => ({
|
|
title: `第${q.QuestionNO}题`,
|
|
key: `q${q.QuestionNO}`,
|
|
align: 'center' as const,
|
|
}))
|
|
|
|
// 更新表头
|
|
columns.value = [
|
|
{
|
|
title: '排名',
|
|
key: 'rank',
|
|
align: 'center',
|
|
width: 80,
|
|
render(row: TeamData) {
|
|
const rank = row.rank
|
|
if (rank === 1) {
|
|
return <NTag type="warning" size="large" round>🏆</NTag>
|
|
}
|
|
if (rank === 2) {
|
|
return <NTag type="info" size="large" round>🥈</NTag>
|
|
}
|
|
if (rank === 3) {
|
|
return <NTag type="success" size="large" round>🥉</NTag>
|
|
}
|
|
return <span>{rank}</span>
|
|
},
|
|
},
|
|
{ title: '队伍名称', key: 'teamName', align: 'left', width: 150, className: 'team-name' },
|
|
...questionColumns,
|
|
{ title: '总积分', key: 'total', align: 'center', className: 'total-score' },
|
|
]
|
|
|
|
tableData.value = data.data.map((item, index) => {
|
|
const questions: Record<string, number> = {}
|
|
item.Questions.forEach((q) => {
|
|
questions[`q${q.QuestionNO}`] = q.ResultPotin
|
|
})
|
|
|
|
return {
|
|
id: String(item.TeamID),
|
|
rank: index + 1,
|
|
teamName: item.Name,
|
|
total: item.TotalPoint,
|
|
...questions,
|
|
} as TeamData
|
|
})
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
if (MainID) {
|
|
getUserRankList()
|
|
}
|
|
})
|
|
|
|
// 动态赋予行样式:根据排名和奇偶数设置 class
|
|
function rowClassName(row: TeamData) {
|
|
const rank = row.rank
|
|
const classes = ['rank-row']
|
|
if (rank >= 1 && rank <= 3) {
|
|
classes.push(`rank-top-${rank}`)
|
|
}
|
|
return classes.join(' ')
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<CompetitionLayout :show-back="false" :show-next="false" :show-title="false">
|
|
<div class="leaderboard-container">
|
|
<div class="leaderboard-header">
|
|
<NImage :src="title" alt="logo" preview-disabled class="mt--60px" object-fit="contain" />
|
|
<p class="mt--20px text-center text-sm text-#6b778d">
|
|
"勤学如春起之苗,不见其增,日有所长"
|
|
</p>
|
|
</div>
|
|
<div class="leaderboard-content">
|
|
<NDataTable
|
|
:columns="columns" :data="tableData" :bordered="false" :bottom-bordered="false"
|
|
:row-class-name="rowClassName" class="rank-table"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</CompetitionLayout>
|
|
</template>
|
|
|
|
<style scoped>
|
|
/* 1. 整体容器和背景 */
|
|
.leaderboard-container {
|
|
width: 100%;
|
|
min-height: 100vh;
|
|
background-image: url('https://raw.githubusercontent.com/0x3f3f3f3f/figure-bed/main/img/20240524150352.png');
|
|
background-size: cover;
|
|
background-position: center;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
padding: 40px 20px;
|
|
font-family: 'PingFang SC', 'Helvetica Neue', Arial, sans-serif;
|
|
/* width: 100%; */
|
|
/* height: 781px;
|
|
position: relative;
|
|
display: flex;
|
|
flex-direction: column;
|
|
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: url('@/assets/imgs/user/team-bg.svg') no-repeat center center;
|
|
background-size: 100% 100%;
|
|
transform-origin: center;
|
|
padding: 40px 20px;
|
|
font-family: 'PingFang SC', 'Helvetica Neue', Arial, sans-serif; */
|
|
}
|
|
|
|
/* 2. 页面标题 */
|
|
.leaderboard-header {
|
|
text-align: center;
|
|
}
|
|
|
|
.leaderboard-header h1 {
|
|
font-size: 2.5rem;
|
|
font-weight: 700;
|
|
color: #1a2a44;
|
|
margin: 0;
|
|
}
|
|
|
|
/* .leaderboard-header p {
|
|
font-size: 1rem;
|
|
color: #6b778d;
|
|
margin-top: 8px;
|
|
} */
|
|
|
|
/* 3. 表格内容区 */
|
|
.leaderboard-content {
|
|
width: 100%;
|
|
/* max-width: 1200px; */
|
|
background-color: #fff;
|
|
border-radius: 12px;
|
|
box-shadow: 0 8px 24px rgba(26, 42, 68, 0.05);
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* 4. Naive UI 表格深度定制 */
|
|
:deep(.rank-table .n-data-table-thead) {
|
|
background-color: #f8f9fa;
|
|
}
|
|
|
|
:deep(.rank-table .n-data-table-th) {
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
color: #495057;
|
|
border-bottom: 2px solid #dee2e6 !important;
|
|
}
|
|
|
|
:deep(.rank-table .n-data-table-tr) {
|
|
transition: background-color 0.2s;
|
|
}
|
|
|
|
:deep(.rank-table .n-data-table-tr:hover) {
|
|
background-color: #f1f3f5 !important;
|
|
}
|
|
|
|
:deep(.rank-table .n-data-table-td) {
|
|
font-size: 15px;
|
|
color: #333;
|
|
border-bottom: 1px solid #e9ecef !important;
|
|
padding-top: 16px !important;
|
|
padding-bottom: 16px !important;
|
|
}
|
|
|
|
/* 奇偶行交替色 */
|
|
:deep(.rank-table .n-data-table-tr:nth-of-type(odd)) {
|
|
background-color: #fdfdfd;
|
|
}
|
|
|
|
/* 队伍名称和总分加粗 */
|
|
:deep(.team-name) {
|
|
font-weight: 600;
|
|
color: #2c3e50;
|
|
}
|
|
|
|
:deep(.total-score) {
|
|
font-weight: 700;
|
|
font-size: 1.1rem;
|
|
color: #e67e22;
|
|
}
|
|
|
|
/* 前三名高亮 */
|
|
:deep(.rank-top-1 .n-data-table-td) {
|
|
background-color: #fff9e6 !important;
|
|
}
|
|
|
|
:deep(.rank-top-2 .n-data-table-td) {
|
|
background-color: #f0f4ff !important;
|
|
}
|
|
|
|
:deep(.rank-top-3 .n-data-table-td) {
|
|
background-color: #e6f4ea !important;
|
|
}
|
|
|
|
:deep(.rank-top-1 .total-score) {
|
|
color: #d4af37;
|
|
}
|
|
|
|
:deep(.rank-top-2 .total-score) {
|
|
color: #a0a0a0;
|
|
}
|
|
|
|
:deep(.rank-top-3 .total-score) {
|
|
color: #cd7f32;
|
|
}
|
|
</style>
|