单败重构

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"> <div class="tournament-bracket-root">
<h1>双败淘汰赛赛程树状图</h1> <h1>双败淘汰赛赛程树状图</h1>
<div class="container"> <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"> <div class="bracket-main">
<!-- 胜者组 --> <!-- 胜者组 -->
@ -145,9 +108,7 @@ let winnerssvgSelection = null;
let losersZoomBehavior = null; let losersZoomBehavior = null;
let loserssvgSelection = null; let loserssvgSelection = null;
const canGenerateBracket = computed(() => {
return participants.value.length >= 4;
});
onMounted(() => { onMounted(() => {
loadTournamentData(); loadTournamentData();
@ -155,7 +116,6 @@ onMounted(() => {
// Add keyboard shortcuts for zoom control // Add keyboard shortcuts for zoom control
const handleKeydown = (event) => { const handleKeydown = (event) => {
if (event.target.tagName === 'INPUT') return; if (event.target.tagName === 'INPUT') return;
switch(event.key) { switch(event.key) {
case '+': case '+':
case '=': case '=':
@ -220,6 +180,11 @@ const loadTournamentData = async () => {
qq_code: item.qq_code || '', qq_code: item.qq_code || '',
status: item.status || '' status: item.status || ''
})); }));
//
if (participants.value.length >= 4) {
generateDoubleEliminationBracket();
}
} catch (err) { } catch (err) {
console.error('获取比赛数据失败:', err); console.error('获取比赛数据失败:', err);
} finally { } finally {
@ -248,21 +213,17 @@ const generateDoubleEliminationBracket = () => {
while (shuffledParticipants.length < totalSlots) { while (shuffledParticipants.length < totalSlots) {
shuffledParticipants.push(null); shuffledParticipants.push(null);
} }
console.log(`总参赛者: ${totalPlayers}, 总槽位: ${totalSlots}`); console.log(`总参赛者: ${totalPlayers}, 总槽位: ${totalSlots}`);
// //
const winnersRound1 = []; const winnersRound1 = [];
for (let i = 0; i < totalSlots; i += 2) { for (let i = 0; i < totalSlots; i += 2) {
const p1 = shuffledParticipants[i]; const p1 = shuffledParticipants[i];
const p2 = shuffledParticipants[i + 1]; const p2 = shuffledParticipants[i + 1];
// //
let winner = null; let winner = null;
let decided = false; let decided = false;
let score1 = 0; let score1 = 0;
let score2 = 0; let score2 = 0;
if (p1 && !p2) { if (p1 && !p2) {
// p1 // p1
winner = p1; winner = p1;
@ -297,7 +258,6 @@ const generateDoubleEliminationBracket = () => {
}); });
} }
winnersBracket.value = [winnersRound1]; winnersBracket.value = [winnersRound1];
// //
let currentRoundSize = Math.ceil(totalSlots / 2); let currentRoundSize = Math.ceil(totalSlots / 2);
while (currentRoundSize > 1) { while (currentRoundSize > 1) {
@ -318,12 +278,9 @@ const generateDoubleEliminationBracket = () => {
winnersBracket.value.push(nextRound); winnersBracket.value.push(nextRound);
} }
} }
console.log(`胜者组轮次数: ${winnersBracket.value.length}`); console.log(`胜者组轮次数: ${winnersBracket.value.length}`);
// //
generateLosersBracketStructure(totalPlayers); generateLosersBracketStructure(totalPlayers);
// //
winnersRound1.forEach((match, index) => { winnersRound1.forEach((match, index) => {
if (match.decided && match.winner) { if (match.decided && match.winner) {
@ -370,11 +327,9 @@ const generateLosersBracketStructure = (totalPlayers) => {
for (let round = 0; round < losersRounds; round++) { for (let round = 0; round < losersRounds; round++) {
const roundMatches = []; const roundMatches = [];
let matchCount = 1; let matchCount = 1;
// //
// LR1: 1 (1) // LR1: 1 (1)
// LR2: 1 (LR1 vs 2) // LR2: 1 (LR1 vs 2)
if (round === 0) { if (round === 0) {
// //
const firstRoundLosers = Math.floor(totalPlayers / 2); // const firstRoundLosers = Math.floor(totalPlayers / 2); //
@ -387,10 +342,8 @@ const generateLosersBracketStructure = (totalPlayers) => {
} else { } else {
matchCount = 1; // 1 matchCount = 1; // 1
} }
// 0 // 0
matchCount = Math.max(0, matchCount); matchCount = Math.max(0, matchCount);
if (matchCount > 0) { if (matchCount > 0) {
for (let i = 0; i < matchCount; i++) { for (let i = 0; i < matchCount; i++) {
roundMatches.push({ roundMatches.push({
@ -430,7 +383,6 @@ const drawD3LosersBracket = () => {
drawD3Bracket(svg, losersBracket.value, 'losersBracketContainer', 'losers'); drawD3Bracket(svg, losersBracket.value, 'losersBracketContainer', 'losers');
}; };
// Generic D3.js bracket drawing function // Generic D3.js bracket drawing function
const drawD3Bracket = (svg, bracket, containerId, bracketType) => { const drawD3Bracket = (svg, bracket, containerId, bracketType) => {
const margin = { top: 40, right: 40, bottom: 40, left: 40 }; const margin = { top: 40, right: 40, bottom: 40, left: 40 };
@ -1134,37 +1086,8 @@ h1 {
min-height: 500px; 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 { .bracket-main {
flex: 3; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 20px; gap: 20px;
@ -1336,31 +1259,5 @@ button:hover:not(:disabled) {
background: #66b1ff; 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> </style>

View File

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