274 lines
6.9 KiB
Vue
274 lines
6.9 KiB
Vue
<template>
|
|
<div class="rank-contestant">
|
|
<div class="rank-contestant-header">
|
|
<h2>选手排名</h2>
|
|
</div>
|
|
<div class="rank-content">
|
|
<div class="top-three">
|
|
<div v-for="(item, idx) in rankData.slice(0, 3)" :key="idx" class="rank-card">
|
|
<div class="rank-number">
|
|
{{ item.rank }}
|
|
</div>
|
|
<div class="player-info">
|
|
<div class="player-name">{{ item.username }}</div>
|
|
<div class="player-faction">{{ item.faction }}</div>
|
|
<div class="player-score" :title="`积分: ${item.points}`">{{ item.score }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="rank-list">
|
|
<div v-for="(item, idx) in rankData.slice(3)" :key="idx + 3" class="rank-item">
|
|
<div class="rank">{{ item.rank }}</div>
|
|
<div class="player-info">
|
|
<div class="player-name">{{ item.username }}</div>
|
|
<div class="player-faction">{{ item.faction }}</div>
|
|
<div class="player-score">{{ item.score }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted, watch } from 'vue'
|
|
import { getSignUpResultList } from '@/api/tournament'
|
|
|
|
const props = defineProps({
|
|
tournamentId: {
|
|
type: Number,
|
|
required: true
|
|
}
|
|
})
|
|
|
|
const rankData = ref([])
|
|
|
|
const fetchRankData = async () => {
|
|
try {
|
|
const response = await getSignUpResultList()
|
|
console.log('RankContestant 原始数据:', response)
|
|
|
|
// 筛选当前赛事的玩家
|
|
const filteredPlayers = response.filter(player => player.tournament_id === props.tournamentId)
|
|
console.log('RankContestant 筛选后数据:', filteredPlayers)
|
|
|
|
// 按玩家名称分组,计算总胜利数和总失败数
|
|
const playerStats = {}
|
|
|
|
filteredPlayers.forEach((player, index) => {
|
|
const playerName = player.sign_name
|
|
const win = parseInt(player.win || 0)
|
|
const lose = parseInt(player.lose || 0)
|
|
const round = player.round || '0'
|
|
|
|
console.log(`RankContestant 处理第${index + 1}条数据:`, {
|
|
id: player.id,
|
|
sign_name: playerName,
|
|
round: round,
|
|
win: win,
|
|
lose: lose,
|
|
status: player.status
|
|
})
|
|
|
|
if (!playerStats[playerName]) {
|
|
playerStats[playerName] = {
|
|
username: playerName,
|
|
faction: player.faction || player.team_name || '',
|
|
totalWin: 0,
|
|
totalLose: 0,
|
|
totalPoints: 0,
|
|
rounds: []
|
|
}
|
|
}
|
|
|
|
playerStats[playerName].totalWin += win
|
|
playerStats[playerName].totalLose += lose
|
|
playerStats[playerName].rounds.push(round)
|
|
|
|
// 计算积分(胜场数)
|
|
playerStats[playerName].totalPoints = playerStats[playerName].totalWin
|
|
})
|
|
|
|
console.log('RankContestant 分组后统计:', playerStats)
|
|
|
|
// 转换为数组并按总胜利数排序
|
|
const sortedPlayers = Object.values(playerStats)
|
|
.sort((a, b) => {
|
|
// 首先按总胜利数排序
|
|
if (b.totalWin !== a.totalWin) {
|
|
return b.totalWin - a.totalWin
|
|
}
|
|
// 如果胜利数相同,按失败数排序(失败数少的排名靠前)
|
|
return a.totalLose - b.totalLose
|
|
})
|
|
.map((player, index) => ({
|
|
id: index + 1, // 使用索引作为ID
|
|
rank: (index + 1).toString(),
|
|
username: player.username,
|
|
faction: player.faction,
|
|
win: player.totalWin,
|
|
lose: player.totalLose,
|
|
points: player.totalPoints,
|
|
status: '',
|
|
bracket_type: 'winners',
|
|
score: `${player.totalWin}胜${player.totalLose}负 (${player.totalPoints.toFixed(1)}分)`,
|
|
rounds: player.rounds.join(',')
|
|
}))
|
|
|
|
console.log('RankContestant 最终排名结果:', sortedPlayers)
|
|
rankData.value = sortedPlayers
|
|
} catch (error) {
|
|
console.error('获取排名数据失败:', error)
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
fetchRankData()
|
|
})
|
|
|
|
// 当比赛ID变化时重新获取数据
|
|
watch(() => props.tournamentId, () => {
|
|
if (props.tournamentId) {
|
|
fetchRankData()
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.rank-contestant {
|
|
background: white;
|
|
border-radius: 8px;
|
|
padding: 24px;
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.rank-contestant-header {
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.rank-contestant-header h2 {
|
|
font-size: 20px;
|
|
color: #303133;
|
|
margin: 0;
|
|
}
|
|
|
|
.rank-content {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 24px;
|
|
}
|
|
|
|
.top-three {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr);
|
|
gap: 20px;
|
|
}
|
|
|
|
.rank-card {
|
|
background: white;
|
|
border-radius: 8px;
|
|
padding: 20px;
|
|
display: flex;
|
|
align-items: center;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
|
border: 1px solid #EBEEF5;
|
|
}
|
|
|
|
.rank-card:nth-child(1) {
|
|
background: #FFF9EB;
|
|
border: 1px solid #FFE4B5;
|
|
}
|
|
|
|
.rank-card:nth-child(2) {
|
|
background: #F8F9FA;
|
|
border: 1px solid #E4E7ED;
|
|
}
|
|
|
|
.rank-card:nth-child(3) {
|
|
background: #FDF6EC;
|
|
border: 1px solid #F3D19E;
|
|
}
|
|
|
|
.rank-number {
|
|
font-size: 24px;
|
|
font-weight: bold;
|
|
margin-right: 16px;
|
|
min-width: 50px;
|
|
text-align: center;
|
|
}
|
|
|
|
.rank-card:nth-child(1) .rank-number {
|
|
color: #E6A23C;
|
|
}
|
|
|
|
.rank-card:nth-child(2) .rank-number {
|
|
color: #909399;
|
|
}
|
|
|
|
.rank-card:nth-child(3) .rank-number {
|
|
color: #F56C6C;
|
|
}
|
|
|
|
.player-info {
|
|
flex: 1;
|
|
}
|
|
|
|
.player-name {
|
|
font-size: 16px;
|
|
font-weight: 500;
|
|
color: #303133;
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.player-qq {
|
|
font-size: 14px;
|
|
color: #909399;
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.player-faction {
|
|
font-size: 14px;
|
|
color: #409EFF;
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.player-score {
|
|
font-size: 14px;
|
|
color: #67C23A;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.rank-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
}
|
|
|
|
.rank-item {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 16px;
|
|
background: white;
|
|
border-radius: 4px;
|
|
border: 1px solid #EBEEF5;
|
|
}
|
|
|
|
.rank {
|
|
width: 40px;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: #909399;
|
|
text-align: center;
|
|
margin-right: 16px;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.top-three {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.rank-contestant {
|
|
padding: 16px;
|
|
}
|
|
}
|
|
</style> |