单败重构

This commit is contained in:
Kunagisa 2025-08-03 13:53:54 +08:00
parent f5bf2f01f5
commit afd5323ade
3 changed files with 13 additions and 117 deletions

View File

@ -2,43 +2,6 @@
<div class="tournament-bracket-root">
<h1>双败淘汰赛赛程树状图</h1>
<div class="container">
<!-- 控制面板 -->
<div class="control-panel">
<div class="tournament-info">
<div>
<strong>当前比赛:</strong> {{ tournament.name || '加载中...' }}
</div>
</div>
<h2>参赛者列表</h2>
<div id="player-list">
<div v-if="loading" class="loading">加载中...</div>
<div v-else>
<p v-if="participants.length === 0">暂无参赛者</p>
<template v-else>
<p> {{ participants.length }} 位参赛者</p>
<ul>
<li v-for="participant in participants" :key="participant.id">
{{ participant.name }}
</li>
</ul>
</template>
</div>
</div>
<h2>生成赛程</h2>
<button @click="generateDoubleEliminationBracket" :disabled="!canGenerateBracket">
生成双败淘汰赛程
</button>
<div v-if="champion" class="final-ranking">
<h3>最终排名</h3>
<p v-if="champion">🏆 冠军: {{ champion }}</p>
<p v-if="runnerUp">🥈 亚军: {{ runnerUp }}</p>
<p v-if="thirdPlace">🥉 季军: {{ thirdPlace }}</p>
</div>
</div>
<!-- 比赛对战表 -->
<div class="bracket-main">
<!-- 胜者组 -->
@ -145,9 +108,7 @@ let winnerssvgSelection = null;
let losersZoomBehavior = null;
let loserssvgSelection = null;
const canGenerateBracket = computed(() => {
return participants.value.length >= 4;
});
onMounted(() => {
loadTournamentData();
@ -155,7 +116,6 @@ onMounted(() => {
// Add keyboard shortcuts for zoom control
const handleKeydown = (event) => {
if (event.target.tagName === 'INPUT') return;
switch(event.key) {
case '+':
case '=':
@ -220,6 +180,11 @@ const loadTournamentData = async () => {
qq_code: item.qq_code || '',
status: item.status || ''
}));
//
if (participants.value.length >= 4) {
generateDoubleEliminationBracket();
}
} catch (err) {
console.error('获取比赛数据失败:', err);
} finally {
@ -248,21 +213,17 @@ const generateDoubleEliminationBracket = () => {
while (shuffledParticipants.length < totalSlots) {
shuffledParticipants.push(null);
}
console.log(`总参赛者: ${totalPlayers}, 总槽位: ${totalSlots}`);
//
const winnersRound1 = [];
for (let i = 0; i < totalSlots; i += 2) {
const p1 = shuffledParticipants[i];
const p2 = shuffledParticipants[i + 1];
//
let winner = null;
let decided = false;
let score1 = 0;
let score2 = 0;
if (p1 && !p2) {
// p1
winner = p1;
@ -297,7 +258,6 @@ const generateDoubleEliminationBracket = () => {
});
}
winnersBracket.value = [winnersRound1];
//
let currentRoundSize = Math.ceil(totalSlots / 2);
while (currentRoundSize > 1) {
@ -318,12 +278,9 @@ const generateDoubleEliminationBracket = () => {
winnersBracket.value.push(nextRound);
}
}
console.log(`胜者组轮次数: ${winnersBracket.value.length}`);
//
generateLosersBracketStructure(totalPlayers);
//
winnersRound1.forEach((match, index) => {
if (match.decided && match.winner) {
@ -370,11 +327,9 @@ const generateLosersBracketStructure = (totalPlayers) => {
for (let round = 0; round < losersRounds; round++) {
const roundMatches = [];
let matchCount = 1;
//
// LR1: 1 (1)
// LR2: 1 (LR1 vs 2)
if (round === 0) {
//
const firstRoundLosers = Math.floor(totalPlayers / 2); //
@ -387,10 +342,8 @@ const generateLosersBracketStructure = (totalPlayers) => {
} else {
matchCount = 1; // 1
}
// 0
matchCount = Math.max(0, matchCount);
if (matchCount > 0) {
for (let i = 0; i < matchCount; i++) {
roundMatches.push({
@ -430,7 +383,6 @@ const drawD3LosersBracket = () => {
drawD3Bracket(svg, losersBracket.value, 'losersBracketContainer', 'losers');
};
// Generic D3.js bracket drawing function
const drawD3Bracket = (svg, bracket, containerId, bracketType) => {
const margin = { top: 40, right: 40, bottom: 40, left: 40 };
@ -1134,37 +1086,8 @@ h1 {
min-height: 500px;
}
.control-panel {
flex: 1;
min-width: 280px;
background: #f0f0f0;
padding: 15px;
border-radius: 6px;
height: 100%;
box-sizing: border-box;
}
.tournament-info {
margin-bottom: 15px;
padding: 10px;
background: #e6f7ff;
border-radius: 4px;
}
.control-panel h2 {
color: #1a237e;
font-size: 1.2rem;
font-weight: 600;
margin: 20px 0 10px 0;
}
.loading {
color: #666;
font-style: italic;
}
.bracket-main {
flex: 3;
flex: 1;
display: flex;
flex-direction: column;
gap: 20px;
@ -1336,31 +1259,5 @@ button:hover:not(:disabled) {
background: #66b1ff;
}
.final-ranking {
background: #fffbdb;
border: 1px solid #f0e68c;
padding: 15px;
border-radius: 6px;
margin-top: 20px;
}
.final-ranking h3 {
margin-top: 0;
color: #b8860b;
}
.final-ranking p {
margin: 5px 0;
font-size: 16px;
}
ul {
padding-left: 20px;
margin: 0 0 10px 0;
}
li {
margin-bottom: 4px;
list-style: disc;
}
</style>

View File

@ -291,18 +291,18 @@ const generateRoundMatches = async (round, roundData) => {
//
for (const player of roundData) {
if (usedPlayers.has(player.id)) {
if (usedPlayers.has(player.id)) {
console.log(`玩家 ${player.sign_name} (ID: ${player.id}) 已被使用,跳过`);
continue;
}
continue;
}
const rivalName = player.rival_name;
const isBye = rivalName === '轮空' || rivalName === 'bye' || rivalName === 'BYE' || rivalName === 'Bye';
const isPending = rivalName === '待定' || rivalName === 'pending' || rivalName === 'PENDING' || rivalName === 'Pending';
if (isBye || isPending) {
if (isBye || isPending) {
console.log(`玩家 ${player.sign_name} 的对手是 ${rivalName},跳过正常比赛处理`);
continue;
continue;
}
//
@ -665,7 +665,7 @@ const matchOpponentsInRound = async (round) => {
} else {
console.log(`${round}轮待定玩家数量为${roundData.length},无法匹配`);
}
} catch (error) {
} catch (error) {
console.error(`匹配第${round}轮对手失败:`, error);
}
};

View File

@ -366,7 +366,6 @@ function handlePasswordChangeError(errorMessage) {
<router-link to="competition" class="nav-link" @click.prevent="handleNavClick('/competition')">赛程信息</router-link>
</div>
</div>
<!-- 公共信息区 一级菜单 -->
<div class="nav-dropdown">
<span class="nav-link">公共信息区</span>