- 新增重庆、树人、文字logo资源(PNG和WebP格式) - 新增用户界面背景图片资源(内容背景、对框、主页背景等) - 优化ActionButton组件样式(自适应宽度、禁用状态样式) - 更新CompetitionLayout组件header布局,集成新logo资源 - 优化TianZiGe组件功能和交互体验 - 更新竞赛相关页面组件(QuestionConfig、draw、QuestionRenderer等) - 更新排名列表页面样式和功能 - 更新诗词常识题库数据 - 完善Competition类型定义
288 lines
7.4 KiB
Vue
288 lines
7.4 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 ActionButton from '@/components/custom/user/ActionButton.vue'
|
|
import { fetchUserRankList } from '@/service/api/rank'
|
|
import ExtraTimeModal from './components/ExtraTimeModal.vue'
|
|
|
|
const route = useRoute()
|
|
const message = useMessage()
|
|
const MainID = Number(route.query.activityId)
|
|
const RoundType = Number(route.query.RoundType) || 0
|
|
|
|
const hasExtraTimeBtn = RoundType === 0
|
|
|
|
const showExtraTimeModal = ref(false)
|
|
const teamOptions = ref<{ label: string, value: number }[]>([])
|
|
|
|
function handleAddTime() {
|
|
showExtraTimeModal.value = true
|
|
}
|
|
|
|
// 定义数据结构
|
|
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, RoundType)
|
|
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" class="!py-6 !text-3xl" round>🏆</NTag>
|
|
}
|
|
if (rank === 2) {
|
|
return <NTag type="info" size="large" class="!py-6 !text-3xl" round>🥈</NTag>
|
|
}
|
|
if (rank === 3) {
|
|
return <NTag type="success" size="large" class="!py-6 !text-3xl" 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
|
|
})
|
|
|
|
teamOptions.value = data.data.map(item => ({ label: item.Name, value: item.TeamID }))
|
|
}
|
|
}
|
|
|
|
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-lg text-#6b778d">
|
|
"勤学如春起之苗,不见其增,日有所长"
|
|
</p>
|
|
<ActionButton
|
|
v-if="hasExtraTimeBtn" class="position-absolute bottom-20px right-20px" type="text" text="加时赛"
|
|
@click="handleAddTime"
|
|
/>
|
|
</div>
|
|
<div class="leaderboard-content">
|
|
<NDataTable
|
|
:columns="columns" :data="tableData" :bordered="false" :bottom-bordered="false" size="large"
|
|
:row-class-name="rowClassName" class="rank-table"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<ExtraTimeModal v-model:show="showExtraTimeModal" :activity-id="MainID" :team-options="teamOptions" />
|
|
</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.webp') 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>
|