重置密码的60s冷却🐱🐱
This commit is contained in:
parent
4c15c42ebc
commit
4828d0bcd6
@ -1,8 +1,8 @@
|
||||
import axios from 'axios';
|
||||
import { logoutUser } from '../utils/jwt'; // logoutUser会处理清除存储和重定向
|
||||
|
||||
//const API_BASE_URL = 'https://api.zybdatasupport.online';
|
||||
const API_BASE_URL = 'http://hk.zybdatasupport.online:8000/';
|
||||
const API_BASE_URL = 'https://api.zybdatasupport.online';
|
||||
//const API_BASE_URL = 'http://hk.zybdatasupport.online:8000/';
|
||||
|
||||
const axiosInstance = axios.create({
|
||||
baseURL: API_BASE_URL,
|
||||
|
BIN
src/assets/login_4.jpg
Normal file
BIN
src/assets/login_4.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 1024 KiB |
BIN
src/assets/login_5.jpg
Normal file
BIN
src/assets/login_5.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 MiB |
@ -1,251 +1,437 @@
|
||||
<template>
|
||||
<div class="tournament-bracket-root">
|
||||
<h1>双败淘汰赛赛程图 (BO3)</h1>
|
||||
<h1>双败淘汰赛赛程树状图</h1>
|
||||
<div class="container">
|
||||
<div class="bracket-container">
|
||||
<div ref="bracket" class="bracket"></div>
|
||||
<!-- 控制面板 -->
|
||||
<div class="control-panel">
|
||||
<h2>选择比赛</h2>
|
||||
<div class="tournament-info">
|
||||
<select v-model="selectedTournamentId" @change="handleTournamentChange">
|
||||
<option value="">-- 请选择比赛 --</option>
|
||||
<option v-for="tournament in tournaments" :key="tournament.id" :value="tournament.id">
|
||||
{{ tournament.name }}
|
||||
</option>
|
||||
</select>
|
||||
<div v-if="selectedTournamentId">
|
||||
<strong>已选择:</strong> {{ selectedTournamentName }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>参赛者列表</h2>
|
||||
<div id="player-list">
|
||||
<div v-if="!selectedTournamentId" class="loading">请先选择比赛</div>
|
||||
<div v-else-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>
|
||||
|
||||
<!-- 胜者组 -->
|
||||
<div class="bracket-section">
|
||||
<h2>胜者组</h2>
|
||||
<ul>
|
||||
<li v-for="(round, rIndex) in winnersBracket" :key="'w-' + rIndex">
|
||||
<h3>胜者组 - 第 {{ rIndex + 1 }} 轮</h3>
|
||||
<ul>
|
||||
<li v-for="(match, mIndex) in round" :key="'w-' + rIndex + '-' + mIndex">
|
||||
<div class="match">
|
||||
<div class="participant" :class="{ winner: match.winner && match.participant1 && match.winner.id === match.participant1.id }">
|
||||
{{ match.participant1 ? match.participant1.name : '轮空' }}
|
||||
<input type="number" v-model.number="match.score1" :disabled="match.decided || !match.participant1 || !match.participant2" placeholder="0">
|
||||
</div>
|
||||
<div class="participant" :class="{ winner: match.winner && match.participant2 && match.winner.id === match.participant2.id }">
|
||||
{{ match.participant2 ? match.participant2.name : '轮空' }}
|
||||
<input type="number" v-model.number="match.score2" :disabled="match.decided || !match.participant1 || !match.participant2" placeholder="0">
|
||||
</div>
|
||||
<button @click="confirmScore(match, 'winners', rIndex, mIndex)" :disabled="match.decided || !match.participant1 || !match.participant2">
|
||||
确认比分
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 败者组 -->
|
||||
<div class="bracket-section">
|
||||
<h2>败者组</h2>
|
||||
<ul>
|
||||
<li v-for="(round, rIndex) in losersBracket" :key="'l-' + rIndex">
|
||||
<h3>败者组 - 第 {{ rIndex + 1 }} 轮</h3>
|
||||
<ul>
|
||||
<li v-for="(match, mIndex) in round" :key="'l-' + rIndex + '-' + mIndex">
|
||||
<div class="match">
|
||||
<div class="participant" :class="{ winner: match.winner && match.participant1 && match.winner.id === match.participant1.id }">
|
||||
{{ match.participant1 ? match.participant1.name : '轮空' }}
|
||||
<input type="number" v-model.number="match.score1" :disabled="match.decided || !match.participant1 || !match.participant2" placeholder="0">
|
||||
</div>
|
||||
<div class="participant" :class="{ winner: match.winner && match.participant2 && match.winner.id === match.participant2.id }">
|
||||
{{ match.participant2 ? match.participant2.name : '轮空' }}
|
||||
<input type="number" v-model.number="match.score2" :disabled="match.decided || !match.participant1 || !match.participant2" placeholder="0">
|
||||
</div>
|
||||
<button @click="confirmScore(match, 'losers', rIndex, mIndex)" :disabled="match.decided || !match.participant1 || !match.participant2">
|
||||
确认比分
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 决赛 -->
|
||||
<div class="bracket-section" v-if="finalMatch">
|
||||
<h2>决赛 (BO5)</h2>
|
||||
<div class="match">
|
||||
<div class="participant" :class="{ winner: finalMatch.decided && finalMatch.score1 > finalMatch.score2 }">
|
||||
{{ finalMatch.participant1 ? finalMatch.participant1.name : '待定' }}
|
||||
<input type="number" v-model.number="finalMatch.score1" :disabled="finalMatch.decided" placeholder="0">
|
||||
</div>
|
||||
<div class="participant" :class="{ winner: finalMatch.decided && finalMatch.score2 > finalMatch.score1 }">
|
||||
{{ finalMatch.participant2 ? finalMatch.participant2.name : '待定' }}
|
||||
<input type="number" v-model.number="finalMatch.score2" :disabled="finalMatch.decided" placeholder="0">
|
||||
</div>
|
||||
<button @click="confirmFinal" :disabled="finalMatch.decided">确认决赛比分</button>
|
||||
</div>
|
||||
|
||||
<div v-if="finalMatch.decided" 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>
|
||||
<div ref="finalRanking"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
<script>
|
||||
import { getSignUpResultList, updateSignUpResult, getTournamentList } from '@/api/tournament';
|
||||
|
||||
const totalPlayers = 8
|
||||
const sampleNames = ["小明", "小红", "小刚", "小美", "阿强", "阿花", "老李", "老王", "张三", "李四", "王五", "赵六"]
|
||||
const shuffledPlayers = sampleNames.sort(() => Math.random() - 0.5).slice(0, totalPlayers)
|
||||
|
||||
let rounds = {
|
||||
winners: [], // 胜者组轮次数组
|
||||
losers: [], // 败者组轮次数组
|
||||
finals: [] // 决赛
|
||||
}
|
||||
|
||||
const bracket = ref(null)
|
||||
const finalRanking = ref(null)
|
||||
|
||||
function initWinners() {
|
||||
const firstRound = []
|
||||
for(let i=0; i < totalPlayers; i+=2) {
|
||||
firstRound.push({
|
||||
player1: shuffledPlayers[i],
|
||||
player2: shuffledPlayers[i+1],
|
||||
score1: 0,
|
||||
score2: 0,
|
||||
done: false,
|
||||
winner: null,
|
||||
loser: null,
|
||||
})
|
||||
}
|
||||
rounds.winners.push(firstRound)
|
||||
}
|
||||
|
||||
function getLosersRoundsCount(nPlayers) {
|
||||
return Math.ceil(Math.log2(nPlayers)) + 1
|
||||
}
|
||||
|
||||
function initLosers() {
|
||||
let roundsCount = getLosersRoundsCount(totalPlayers)
|
||||
for(let i=0; i < roundsCount; i++) {
|
||||
rounds.losers.push([])
|
||||
}
|
||||
}
|
||||
|
||||
function validScore(s1, s2) {
|
||||
if(s1 === s2) return false
|
||||
if(s1 < 0 || s2 < 0) return false
|
||||
if(s1 > 2 || s2 > 2) return false
|
||||
if(s1 !== 2 && s2 !== 2) return false
|
||||
return true
|
||||
}
|
||||
|
||||
function generateBracket() {
|
||||
if (!bracket.value) return
|
||||
bracket.value.innerHTML = ''
|
||||
|
||||
function createRound(title, matches, type, roundIndex) {
|
||||
const roundDiv = document.createElement('div')
|
||||
roundDiv.className = 'round'
|
||||
|
||||
const titleDiv = document.createElement('div')
|
||||
titleDiv.className = 'round-title'
|
||||
titleDiv.textContent = title
|
||||
roundDiv.appendChild(titleDiv)
|
||||
|
||||
matches.forEach((match, matchIndex) => {
|
||||
const matchDiv = document.createElement('div')
|
||||
matchDiv.className = 'match'
|
||||
|
||||
const p1 = match.player1 || '待定'
|
||||
const p2 = match.player2 || '待定'
|
||||
|
||||
let p1Class = ''
|
||||
let p2Class = ''
|
||||
if(match.done && match.winner) {
|
||||
if(match.winner === match.player1) p1Class = 'participant winner'
|
||||
if(match.winner === match.player2) p2Class = 'participant winner'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
selectedTournamentId: '',
|
||||
selectedTournamentName: '',
|
||||
tournaments: [],
|
||||
participants: [],
|
||||
winnersBracket: [],
|
||||
losersBracket: [],
|
||||
finalMatch: null,
|
||||
champion: null,
|
||||
runnerUp: null,
|
||||
thirdPlace: null,
|
||||
loading: false,
|
||||
pointsMultiplier: {
|
||||
winners: 1,
|
||||
losers: 0.5
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
canGenerateBracket() {
|
||||
return this.participants.length >= 4;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.fetchTournamentList();
|
||||
},
|
||||
methods: {
|
||||
async fetchTournamentList() {
|
||||
try {
|
||||
const data = await getTournamentList();
|
||||
this.tournaments = data;
|
||||
} catch (err) {
|
||||
console.error('获取比赛列表失败:', err);
|
||||
}
|
||||
},
|
||||
async handleTournamentChange() {
|
||||
if (!this.selectedTournamentId) {
|
||||
this.selectedTournamentName = '';
|
||||
this.participants = [];
|
||||
return;
|
||||
}
|
||||
|
||||
const disabled = match.done ? 'disabled' : ''
|
||||
const selected = this.tournaments.find(t => t.id === this.selectedTournamentId);
|
||||
this.selectedTournamentName = selected ? selected.name : '';
|
||||
this.loadParticipants();
|
||||
},
|
||||
async loadParticipants() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const data = await getSignUpResultList();
|
||||
const filtered = data.filter(item => item.tournament_id == this.selectedTournamentId);
|
||||
this.participants = filtered.map(item => ({
|
||||
id: item.id,
|
||||
name: item.sign_name,
|
||||
team_name: item.team_name || '',
|
||||
win: item.win || '0',
|
||||
lose: item.lose || '0',
|
||||
qq_code: item.qq_code || '',
|
||||
status: item.status || ''
|
||||
}));
|
||||
} catch (err) {
|
||||
console.error('获取参赛者失败:', err);
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
generateDoubleEliminationBracket() {
|
||||
const round1 = [];
|
||||
for (let i = 0; i < this.participants.length; i += 2) {
|
||||
round1.push({
|
||||
participant1: this.participants[i],
|
||||
participant2: this.participants[i + 1] || null,
|
||||
score1: 0,
|
||||
score2: 0,
|
||||
decided: false,
|
||||
winner: null,
|
||||
loser: null
|
||||
});
|
||||
}
|
||||
this.winnersBracket = [round1];
|
||||
this.losersBracket = [];
|
||||
this.finalMatch = null;
|
||||
this.champion = this.runnerUp = this.thirdPlace = null;
|
||||
},
|
||||
async confirmScore(match, bracketType, rIndex, mIndex) {
|
||||
if (match.score1 === match.score2) return alert('不能平局');
|
||||
match.decided = true;
|
||||
match.winner = match.score1 > match.score2 ? match.participant1 : match.participant2;
|
||||
match.loser = match.score1 > match.score2 ? match.participant2 : match.participant1;
|
||||
|
||||
// 更新选手胜负场数据,传递当前比赛所在的组别
|
||||
await this.updatePlayerStats(match.participant1, match.participant2, match.score1, match.score2, bracketType);
|
||||
|
||||
matchDiv.innerHTML = `
|
||||
<div class="participant ${p1Class}">${p1}</div>
|
||||
<div class="participant ${p2Class}">${p2}</div>
|
||||
<div>
|
||||
<input type="number" min="0" max="2" class="score-input" id="${type}-${roundIndex}-${matchIndex}-score1" value="${match.score1}" ${disabled}>
|
||||
:
|
||||
<input type="number" min="0" max="2" class="score-input" id="${type}-${roundIndex}-${matchIndex}-score2" value="${match.score2}" ${disabled}>
|
||||
</div>
|
||||
<button class="score-btn" id="${type}-${roundIndex}-${matchIndex}-btn" ${disabled}>提交</button>
|
||||
`
|
||||
roundDiv.appendChild(matchDiv)
|
||||
// 胜者组和败者组推进下一轮
|
||||
const bracket = bracketType === 'winners' ? this.winnersBracket : this.losersBracket;
|
||||
if (!bracket[rIndex + 1]) {
|
||||
bracket[rIndex + 1] = [];
|
||||
}
|
||||
const nextRound = bracket[rIndex + 1];
|
||||
const insertIndex = Math.floor(mIndex / 2);
|
||||
const isEven = mIndex % 2 === 0;
|
||||
if (!nextRound[insertIndex]) {
|
||||
nextRound[insertIndex] = { participant1: null, participant2: null, score1: 0, score2: 0, decided: false, winner: null, loser: null };
|
||||
}
|
||||
if (isEven) {
|
||||
nextRound[insertIndex].participant1 = match.winner;
|
||||
} else {
|
||||
nextRound[insertIndex].participant2 = match.winner;
|
||||
}
|
||||
|
||||
// 事件绑定
|
||||
if (!match.done) {
|
||||
setTimeout(() => {
|
||||
const btn = document.getElementById(`${type}-${roundIndex}-${matchIndex}-btn`)
|
||||
if (btn) {
|
||||
btn.onclick = () => submitMatch(type, roundIndex, matchIndex)
|
||||
if (bracketType === 'winners') {
|
||||
this.winnersBracket[rIndex + 1] = nextRound;
|
||||
|
||||
// 输者进败者组当前轮
|
||||
if (!this.losersBracket[rIndex]) {
|
||||
this.losersBracket[rIndex] = [];
|
||||
}
|
||||
const losersR = this.losersBracket[rIndex];
|
||||
if (!losersR[insertIndex]) {
|
||||
losersR[insertIndex] = { participant1: null, participant2: null, score1: 0, score2: 0, decided: false, winner: null, loser: null };
|
||||
}
|
||||
if (isEven) {
|
||||
losersR[insertIndex].participant1 = match.loser;
|
||||
} else {
|
||||
losersR[insertIndex].participant2 = match.loser;
|
||||
}
|
||||
this.losersBracket[rIndex] = losersR;
|
||||
} else {
|
||||
// 败者组推进
|
||||
this.losersBracket[rIndex + 1] = nextRound;
|
||||
|
||||
// 判断是否该生成决赛,决赛由胜者组最后一轮胜者和败者组最后一轮胜者对决
|
||||
const lastWinnersRound = this.winnersBracket[this.winnersBracket.length - 1];
|
||||
const lastLosersRound = this.losersBracket[this.losersBracket.length - 1];
|
||||
if (lastWinnersRound && lastWinnersRound.length === 1 && lastLosersRound && lastLosersRound.length === 1) {
|
||||
const winnerFromWinners = lastWinnersRound[0].winner;
|
||||
const winnerFromLosers = lastLosersRound[0].winner;
|
||||
if (winnerFromWinners && winnerFromLosers) {
|
||||
this.finalMatch = {
|
||||
participant1: winnerFromWinners,
|
||||
participant2: winnerFromLosers,
|
||||
score1: 0,
|
||||
score2: 0,
|
||||
decided: false
|
||||
};
|
||||
}
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
})
|
||||
return roundDiv
|
||||
}
|
||||
},
|
||||
async confirmFinal() {
|
||||
const match = this.finalMatch;
|
||||
if (!match || match.score1 === match.score2) return alert('决赛不能平局');
|
||||
|
||||
match.decided = true;
|
||||
|
||||
// 确保胜者组选手是participant1,败者组选手是participant2
|
||||
// 这样在排名时可以确保胜者组的选手排名更高
|
||||
const winnerFromWinners = this.winnersBracket[this.winnersBracket.length - 1][0].winner;
|
||||
const winnerFromLosers = this.losersBracket[this.losersBracket.length - 1][0].winner;
|
||||
|
||||
// 根据比分确定冠亚军
|
||||
const winner = match.score1 > match.score2 ? match.participant1 : match.participant2;
|
||||
const loser = match.score1 > match.score2 ? match.participant2 : match.participant1;
|
||||
|
||||
rounds.winners.forEach((r, i) => {
|
||||
bracket.value.appendChild(createRound(`胜者组第 ${i+1} 轮`, r, 'winners', i))
|
||||
})
|
||||
rounds.losers.forEach((r, i) => {
|
||||
bracket.value.appendChild(createRound(`败者组第 ${i+1} 轮`, r, 'losers', i))
|
||||
})
|
||||
if(rounds.finals.length > 0) {
|
||||
bracket.value.appendChild(createRound('总决赛', rounds.finals, 'finals', 0))
|
||||
}
|
||||
}
|
||||
// 更新决赛选手胜负场数据 - 决赛使用胜者组积分标准
|
||||
await this.updatePlayerStats(match.participant1, match.participant2, match.score1, match.score2, 'winners');
|
||||
|
||||
function submitMatch(type, roundIndex, matchIndex) {
|
||||
let match = rounds[type][roundIndex][matchIndex]
|
||||
if(match.done) return
|
||||
// 设置冠军和亚军
|
||||
this.champion = winner.name || winner;
|
||||
this.runnerUp = loser.name || loser;
|
||||
|
||||
// 确保败者的排名低于胜者
|
||||
// 如果败者是来自胜者组的选手,那么他的排名应该高于季军
|
||||
// 如果败者是来自败者组的选手,那么他的排名应该低于季军
|
||||
|
||||
// 在双败赛制中,季军是败者组决赛的败者
|
||||
if (this.losersBracket.length > 0) {
|
||||
const lastLoserRound = this.losersBracket[this.losersBracket.length - 1];
|
||||
if (lastLoserRound && lastLoserRound.length > 0) {
|
||||
const lastLoserMatch = lastLoserRound[lastLoserRound.length - 1];
|
||||
if (lastLoserMatch && lastLoserMatch.loser) {
|
||||
this.thirdPlace = lastLoserMatch.loser.name || lastLoserMatch.loser;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const s1 = parseInt(document.getElementById(`${type}-${roundIndex}-${matchIndex}-score1`).value)
|
||||
const s2 = parseInt(document.getElementById(`${type}-${roundIndex}-${matchIndex}-score2`).value)
|
||||
|
||||
if(!validScore(s1, s2)) {
|
||||
alert('比分无效,请输入有效的 BO3 胜场数,且双方比分不能相同。')
|
||||
return
|
||||
}
|
||||
|
||||
match.score1 = s1
|
||||
match.score2 = s2
|
||||
match.done = true
|
||||
|
||||
if(s1 > s2) {
|
||||
match.winner = match.player1
|
||||
match.loser = match.player2
|
||||
} else {
|
||||
match.winner = match.player2
|
||||
match.loser = match.player1
|
||||
}
|
||||
|
||||
if(type === 'winners') {
|
||||
if(!rounds.winners[roundIndex+1]) rounds.winners[roundIndex+1] = []
|
||||
addPlayerToNextRound(rounds.winners[roundIndex+1], match.winner)
|
||||
if(!rounds.losers[roundIndex]) rounds.losers[roundIndex] = []
|
||||
addPlayerToNextRound(rounds.losers[roundIndex], match.loser)
|
||||
}
|
||||
else if(type === 'losers') {
|
||||
if(!rounds.losers[roundIndex+1]) rounds.losers[roundIndex+1] = []
|
||||
addPlayerToNextRound(rounds.losers[roundIndex+1], match.winner)
|
||||
}
|
||||
else if(type === 'finals') {
|
||||
handleFinalsResult(match)
|
||||
}
|
||||
|
||||
generateBracket()
|
||||
checkIfCanStartFinals()
|
||||
}
|
||||
|
||||
function addPlayerToNextRound(roundMatches, player) {
|
||||
if(!player) return
|
||||
for(let match of roundMatches) {
|
||||
if(!match.player1) {
|
||||
match.player1 = player
|
||||
match.done = false
|
||||
match.score1 = 0
|
||||
match.score2 = 0
|
||||
match.winner = null
|
||||
match.loser = null
|
||||
return
|
||||
}
|
||||
if(!match.player2) {
|
||||
match.player2 = player
|
||||
match.done = false
|
||||
match.score1 = 0
|
||||
match.score2 = 0
|
||||
match.winner = null
|
||||
match.loser = null
|
||||
return
|
||||
// 如果还没有确定季军,尝试从败者组倒数第二轮找
|
||||
if (!this.thirdPlace && this.losersBracket.length > 1) {
|
||||
const semiLoserRound = this.losersBracket[this.losersBracket.length - 2];
|
||||
if (semiLoserRound && semiLoserRound.length > 0) {
|
||||
const semiLoserMatch = semiLoserRound[semiLoserRound.length - 1];
|
||||
if (semiLoserMatch && semiLoserMatch.loser) {
|
||||
this.thirdPlace = semiLoserMatch.loser.name || semiLoserMatch.loser;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.thirdPlace) {
|
||||
this.thirdPlace = '待定';
|
||||
}
|
||||
|
||||
// 更新最终排名状态
|
||||
// 确保胜者的status为win,败者的status为lose
|
||||
try {
|
||||
const latestData = await getSignUpResultList();
|
||||
|
||||
// 更新冠军状态
|
||||
const championData = latestData.find(item => item.sign_name === this.champion);
|
||||
if (championData) {
|
||||
await updateSignUpResult(championData.id, {
|
||||
...championData,
|
||||
status: 'win',
|
||||
rank: '1'
|
||||
});
|
||||
}
|
||||
|
||||
// 更新亚军状态
|
||||
const runnerUpData = latestData.find(item => item.sign_name === this.runnerUp);
|
||||
if (runnerUpData) {
|
||||
await updateSignUpResult(runnerUpData.id, {
|
||||
...runnerUpData,
|
||||
status: 'lose',
|
||||
rank: '2'
|
||||
});
|
||||
}
|
||||
|
||||
// 更新季军状态
|
||||
if (this.thirdPlace && this.thirdPlace !== '待定') {
|
||||
const thirdPlaceData = latestData.find(item => item.sign_name === this.thirdPlace);
|
||||
if (thirdPlaceData) {
|
||||
await updateSignUpResult(thirdPlaceData.id, {
|
||||
...thirdPlaceData,
|
||||
status: 'lose',
|
||||
rank: '3'
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('更新最终排名失败:', err);
|
||||
}
|
||||
},
|
||||
// 格式化比赛显示(如果需要)
|
||||
formatMatch(match, isFinal = false) {
|
||||
if (!match) return '';
|
||||
const p1Name = match.participant1 ? match.participant1.name : '待定';
|
||||
const p2Name = match.participant2 ? match.participant2.name : '待定';
|
||||
return `${p1Name} vs ${p2Name}`;
|
||||
},
|
||||
|
||||
// 更新选手胜负场数据到API
|
||||
async updatePlayerStats(p1, p2, s1, s2, bracketType = 'winners') {
|
||||
if (!p1 || !p2) return;
|
||||
|
||||
try {
|
||||
// 先获取最新的报名数据
|
||||
const latestData = await getSignUpResultList();
|
||||
const p1Latest = latestData.find(item => item.id === p1.id);
|
||||
const p2Latest = latestData.find(item => item.id === p2.id);
|
||||
|
||||
if (!p1Latest || !p2Latest) return;
|
||||
|
||||
// 计算积分 - 胜者组获得全额积分,败者组获得一半积分
|
||||
const pointMultiplier = bracketType === 'winners' ? 1 : 0.5;
|
||||
const winPoints = s1 > s2 ? 1 * pointMultiplier : 0;
|
||||
const losePoints = s2 > s1 ? 1 * pointMultiplier : 0;
|
||||
|
||||
// 更新选手1的数据
|
||||
const p1Data = {
|
||||
tournament_id: this.selectedTournamentId,
|
||||
tournament_name: this.selectedTournamentName,
|
||||
team_name: p1.team_name || '',
|
||||
sign_name: p1.name,
|
||||
win: String(Number(p1Latest.win || 0) + s1),
|
||||
lose: String(Number(p1Latest.lose || 0) + s2),
|
||||
status: s1 > s2 ? 'win' : 'lose',
|
||||
qq_code: p1.qq_code || '',
|
||||
points: String(Number(p1Latest.points || 0) + (s1 > s2 ? winPoints : 0)),
|
||||
bracket_type: bracketType // 记录选手所在的组别,用于最终排名
|
||||
};
|
||||
|
||||
// 更新选手2的数据
|
||||
const p2Data = {
|
||||
tournament_id: this.selectedTournamentId,
|
||||
tournament_name: this.selectedTournamentName,
|
||||
team_name: p2.team_name || '',
|
||||
sign_name: p2.name,
|
||||
win: String(Number(p2Latest.win || 0) + s2),
|
||||
lose: String(Number(p2Latest.lose || 0) + s1),
|
||||
status: s2 > s1 ? 'win' : 'lose',
|
||||
qq_code: p2.qq_code || '',
|
||||
points: String(Number(p2Latest.points || 0) + (s2 > s1 ? winPoints : 0)),
|
||||
bracket_type: bracketType // 记录选手所在的组别,用于最终排名
|
||||
};
|
||||
|
||||
// 调用API更新数据
|
||||
await updateSignUpResult(p1.id, p1Data);
|
||||
await updateSignUpResult(p2.id, p2Data);
|
||||
|
||||
// 刷新参赛者列表
|
||||
await this.loadParticipants();
|
||||
} catch (err) {
|
||||
console.error('更新选手数据失败:', err);
|
||||
alert('更新选手数据失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
roundMatches.push({
|
||||
player1: player,
|
||||
player2: null,
|
||||
score1: 0,
|
||||
score2: 0,
|
||||
done: false,
|
||||
winner: null,
|
||||
loser: null,
|
||||
})
|
||||
}
|
||||
|
||||
function checkIfCanStartFinals() {
|
||||
const winnersLastRound = rounds.winners[rounds.winners.length-1]
|
||||
if(!winnersLastRound) return
|
||||
if(winnersLastRound.length !== 1) return
|
||||
if(!winnersLastRound[0].done) return
|
||||
|
||||
const losersLastRound = rounds.losers[rounds.losers.length-1]
|
||||
if(!losersLastRound) return
|
||||
if(losersLastRound.length !== 1) return
|
||||
if(!losersLastRound[0].done) return
|
||||
|
||||
if(rounds.finals.length === 0) {
|
||||
rounds.finals.push({
|
||||
player1: winnersLastRound[0].winner,
|
||||
player2: losersLastRound[0].winner,
|
||||
score1: 0,
|
||||
score2: 0,
|
||||
done: false,
|
||||
winner: null,
|
||||
loser: null,
|
||||
})
|
||||
generateBracket()
|
||||
}
|
||||
}
|
||||
|
||||
function handleFinalsResult(match) {
|
||||
if(!match.done) return
|
||||
const champion = match.winner
|
||||
const runnerUp = match.loser
|
||||
if (!finalRanking.value) return
|
||||
finalRanking.value.innerHTML = `
|
||||
<h3>比赛结束 - 最终排名</h3>
|
||||
<ol>
|
||||
<li>冠军:${champion}</li>
|
||||
<li>亚军:${runnerUp}</li>
|
||||
<li>季军:待定(可扩展实现季军赛)</li>
|
||||
</ol>
|
||||
`
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initWinners()
|
||||
initLosers()
|
||||
generateBracket()
|
||||
})
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@ -255,158 +441,88 @@ onMounted(() => {
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
.tournament-bracket-root * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.tournament-bracket-root ul {
|
||||
padding-left: 20px;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
.tournament-bracket-root li {
|
||||
margin-bottom: 4px;
|
||||
list-style: disc;
|
||||
}
|
||||
.tournament-bracket-root button {
|
||||
outline: none;
|
||||
}
|
||||
.tournament-bracket-root input[type=number]::-webkit-inner-spin-button,
|
||||
.tournament-bracket-root input[type=number]::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
.tournament-bracket-root input[type=number] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
.tournament-bracket-root h1 {
|
||||
text-align: center;
|
||||
}
|
||||
.tournament-bracket-root .container {
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
align-items: flex-start;
|
||||
min-height: 500px;
|
||||
}
|
||||
.tournament-bracket-root .control-panel {
|
||||
|
||||
.control-panel {
|
||||
flex: 1;
|
||||
min-width: 280px;
|
||||
background: #f0f0f0;
|
||||
min-width: 250px;
|
||||
background: #f5f5f5;
|
||||
padding: 15px;
|
||||
border-radius: 6px;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.tournament-bracket-root .bracket-container {
|
||||
flex: 3;
|
||||
position: relative;
|
||||
overflow-x: auto;
|
||||
|
||||
.bracket-section {
|
||||
flex: 2;
|
||||
min-width: 300px;
|
||||
background: #fff;
|
||||
padding: 15px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #ccc;
|
||||
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
|
||||
min-height: 400px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.tournament-bracket-root .bracket {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
grid-auto-columns: 220px;
|
||||
gap: 40px 10px;
|
||||
position: relative;
|
||||
padding-bottom: 50px;
|
||||
min-width: 600px;
|
||||
min-height: 350px;
|
||||
}
|
||||
.tournament-bracket-root .round {
|
||||
display: grid;
|
||||
grid-auto-rows: 70px;
|
||||
gap: 20px;
|
||||
}
|
||||
.tournament-bracket-root .round-title {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.tournament-bracket-root .match {
|
||||
background: #fafafa;
|
||||
|
||||
.match {
|
||||
background: #f9f9f9;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 6px;
|
||||
padding: 8px 12px;
|
||||
box-shadow: 0 1px 3px rgb(0 0 0 / 0.1);
|
||||
position: relative;
|
||||
font-size: 14px;
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.tournament-bracket-root .participant {
|
||||
|
||||
.participant {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 6px;
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
align-items: center;
|
||||
padding: 5px 0;
|
||||
}
|
||||
.tournament-bracket-root .participant.winner {
|
||||
|
||||
.participant.winner {
|
||||
font-weight: bold;
|
||||
color: #2a7f2a;
|
||||
}
|
||||
.tournament-bracket-root .score-input {
|
||||
|
||||
input[type="number"] {
|
||||
width: 40px;
|
||||
font-size: 14px;
|
||||
padding: 2px 4px;
|
||||
margin-left: 6px;
|
||||
border: 1px solid #aaa;
|
||||
border-radius: 3px;
|
||||
text-align: center;
|
||||
}
|
||||
.tournament-bracket-root .score-btn {
|
||||
margin-top: 4px;
|
||||
width: 100%;
|
||||
background: #007acc;
|
||||
border: none;
|
||||
|
||||
button {
|
||||
background: #4CAF50;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
padding: 6px 0;
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin-top: 5px;
|
||||
width: 100%;
|
||||
}
|
||||
.tournament-bracket-root .score-btn:disabled {
|
||||
background: #aaa;
|
||||
|
||||
button:disabled {
|
||||
background: #cccccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.tournament-bracket-root #finalRanking {
|
||||
margin-top: 15px;
|
||||
|
||||
.final-ranking {
|
||||
background: #fffbdb;
|
||||
border: 1px solid #f0e68c;
|
||||
padding: 12px;
|
||||
padding: 15px;
|
||||
border-radius: 6px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.tournament-bracket-root svg.bracket-lines {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
overflow: visible;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
.final-ranking h3 {
|
||||
margin-top: 0;
|
||||
color: #b8860b;
|
||||
}
|
||||
.tournament-bracket-root svg.bracket-lines path {
|
||||
stroke: #666;
|
||||
fill: none;
|
||||
stroke-width: 2;
|
||||
|
||||
.final-ranking p {
|
||||
margin: 5px 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
.tournament-bracket-root .tournament-info {
|
||||
margin-bottom: 15px;
|
||||
padding: 10px;
|
||||
background: #e6f7ff;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.tournament-bracket-root .loading {
|
||||
color: #666;
|
||||
font-style: italic;
|
||||
}
|
||||
.tournament-bracket-root select {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
margin-bottom: 10px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
</style>
|
||||
</style>
|
@ -12,7 +12,7 @@
|
||||
<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 class="player-score" :title="`积分: ${item.points}`">{{ item.score }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -31,7 +31,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, onMounted, watch } from 'vue'
|
||||
import { getSignUpResultList } from '@/api/tournament'
|
||||
|
||||
const props = defineProps({
|
||||
@ -46,29 +46,74 @@ const rankData = ref([])
|
||||
const fetchRankData = async () => {
|
||||
try {
|
||||
const response = await getSignUpResultList()
|
||||
// 筛选当前赛事的玩家并按胜场数排序
|
||||
const results = response
|
||||
.filter(player => player.tournament_id === props.tournamentId)
|
||||
.map((player, index) => ({
|
||||
rank: index + 1,
|
||||
// 筛选当前赛事的玩家
|
||||
const filteredPlayers = response.filter(player => player.tournament_id === props.tournamentId)
|
||||
|
||||
// 计算每个玩家的积分
|
||||
const playersWithPoints = filteredPlayers.map(player => {
|
||||
// 获取玩家积分,如果没有则计算
|
||||
let points = parseFloat(player.points || 0)
|
||||
|
||||
// 如果没有积分字段,根据胜场和所在组别计算积分
|
||||
if (!player.points) {
|
||||
const wins = parseInt(player.win) || 0
|
||||
// 败者组积分是胜者组的一半
|
||||
const multiplier = player.bracket_type === 'losers' ? 0.5 : 1
|
||||
points = wins * multiplier
|
||||
}
|
||||
|
||||
// 获取排名信息
|
||||
const rank = player.rank || ''
|
||||
|
||||
return {
|
||||
id: player.id,
|
||||
rank: rank,
|
||||
username: player.sign_name,
|
||||
faction: player.faction,
|
||||
faction: player.faction || player.team_name || '',
|
||||
win: parseInt(player.win) || 0,
|
||||
lose: parseInt(player.lose) || 0,
|
||||
score: `${player.win}胜${player.lose}负`
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
// 首先按胜场数排序
|
||||
if (b.win !== a.win) {
|
||||
return b.win - a.win
|
||||
}
|
||||
// 如果胜场数相同,则按负场数排序(负场数少的排前面)
|
||||
return a.lose - b.lose
|
||||
})
|
||||
.map((player, index) => ({
|
||||
...player,
|
||||
rank: index + 1
|
||||
}))
|
||||
points: points,
|
||||
status: player.status || '',
|
||||
bracket_type: player.bracket_type || 'winners', // 默认为胜者组
|
||||
score: `${player.win}胜${player.lose}负 (${points.toFixed(1)}分)`
|
||||
}
|
||||
})
|
||||
|
||||
// 排序逻辑
|
||||
const sortedPlayers = [...playersWithPoints].sort((a, b) => {
|
||||
// 首先检查是否有明确的排名
|
||||
if (a.rank && b.rank) {
|
||||
return parseInt(a.rank) - parseInt(b.rank)
|
||||
}
|
||||
|
||||
// 如果是决赛选手,按照状态排序(win在前,lose在后)
|
||||
if (a.status === 'win' && b.status === 'lose') return -1
|
||||
if (a.status === 'lose' && b.status === 'win') return 1
|
||||
|
||||
// 按积分排序
|
||||
if (b.points !== a.points) {
|
||||
return b.points - a.points
|
||||
}
|
||||
|
||||
// 如果积分相同,胜者组选手排在败者组选手前面
|
||||
if (a.bracket_type !== b.bracket_type) {
|
||||
return a.bracket_type === 'winners' ? -1 : 1
|
||||
}
|
||||
|
||||
// 如果积分和组别都相同,按胜场数排序
|
||||
if (b.win !== a.win) {
|
||||
return b.win - a.win
|
||||
}
|
||||
|
||||
// 如果胜场数相同,则按负场数排序(负场数少的排前面)
|
||||
return a.lose - b.lose
|
||||
})
|
||||
|
||||
// 重新分配排名
|
||||
const results = sortedPlayers.map((player, index) => ({
|
||||
...player,
|
||||
rank: player.rank || (index + 1).toString()
|
||||
}))
|
||||
|
||||
rankData.value = results
|
||||
} catch (error) {
|
||||
@ -79,6 +124,13 @@ const fetchRankData = async () => {
|
||||
onMounted(() => {
|
||||
fetchRankData()
|
||||
})
|
||||
|
||||
// 当比赛ID变化时重新获取数据
|
||||
watch(() => props.tournamentId, () => {
|
||||
if (props.tournamentId) {
|
||||
fetchRankData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
@ -170,7 +170,7 @@ router.beforeEach(async (to, from, next) => {
|
||||
// 登录校验:如果页面需要登录但本地没有有效 token,则跳转登录页
|
||||
if (requiresAuth && !hasValidToken()) {
|
||||
return next({
|
||||
path: '/maps',
|
||||
path: '/',
|
||||
query: { redirect: to.fullPath }
|
||||
})
|
||||
}
|
||||
@ -181,7 +181,7 @@ router.beforeEach(async (to, from, next) => {
|
||||
if (!user || !hasPrivilegeWithTemp(user, requiredPrivilege)) {
|
||||
// 通过事件总线通知弹窗
|
||||
eventBus.emit('no-privilege')
|
||||
return next({ path: '/maps', replace: true })
|
||||
return next({ path: '/', replace: true })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,12 +86,14 @@ import SuccessDialog from '@/components/SuccessDialog.vue'
|
||||
import loginBg from '@/assets/login_1.jpg'
|
||||
import loginBg1 from '@/assets/login_2.jpg'
|
||||
import loginBg3 from '@/assets/login_3.jpg'
|
||||
import loginBg4 from '@/assets/login_4.jpg'
|
||||
import loginBg5 from '@/assets/login_5.jpg'
|
||||
import {hasValidToken, logoutUser} from "@/utils/jwt.js";
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const images = [loginBg, loginBg1, loginBg3]
|
||||
const images = [loginBg, loginBg1, loginBg3,loginBg4, loginBg5]
|
||||
const randomIndex = Math.floor(Math.random() * images.length)
|
||||
const bgImg = ref(images[randomIndex])
|
||||
|
||||
|
@ -37,11 +37,13 @@ import { hasValidToken } from '@/utils/jwt'
|
||||
import loginBg from '@/assets/login_1.jpg'
|
||||
import loginBg1 from '@/assets/login_2.jpg'
|
||||
import loginBg3 from '@/assets/login_3.jpg'
|
||||
import loginBg4 from '@/assets/login_4.jpg'
|
||||
import loginBg5 from '@/assets/login_5.jpg'
|
||||
import ForgetModule from "@/components/forget_module.vue";
|
||||
import LoginModule from '@/components/login_module.vue'
|
||||
import RegisterModule from '@/components/register_module.vue'
|
||||
|
||||
const images = [loginBg, loginBg1,loginBg3]
|
||||
const images = [loginBg, loginBg1,loginBg3, loginBg4,loginBg5]
|
||||
const randomIndex = Math.floor(Math.random() * images.length)
|
||||
const bgImg = ref(images[randomIndex])
|
||||
|
||||
|
@ -324,7 +324,8 @@ function handlePasswordChangeError(errorMessage) {
|
||||
<div class="app">
|
||||
<nav class="navbar">
|
||||
<div class="nav-container">
|
||||
<div class="nav-brand">红色警戒3数据分析中心</div>
|
||||
<a href="/" class="nav-brand">红色警戒3数据分析中心</a>
|
||||
<!-- <div class="nav-brand">红色警戒3数据分析中心</div>-->
|
||||
<button class="mobile-menu-toggle" @click="toggleMobileMenu">
|
||||
<i class="fas fa-bars"></i>
|
||||
</button>
|
||||
@ -344,35 +345,9 @@ function handlePasswordChangeError(errorMessage) {
|
||||
<span class="nav-link">地形与纹理</span>
|
||||
<div class="dropdown-content">
|
||||
<router-link to="/terrain" class="nav-link">地形图列表</router-link>
|
||||
<!-- <router-link v-if="isLoggedIn" to="/terrainGenerate" class="nav-link" @click.prevent="handleNavClick('/terrainGenerate', ['lv-admin','lv-mod','lv-map','lv-competitor'])">地形纹理合成工具</router-link>-->
|
||||
<router-link to="/terrainGenerate" class="nav-link" @click.prevent="handleNavClick('/terrainGenerate', ['lv-admin','lv-mod','lv-map','lv-competitor'])">地形纹理合成工具</router-link>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <template v-if="isLoggedIn">-->
|
||||
<!-- <!– 在线工具 一级菜单 –>-->
|
||||
<!-- <div class="nav-dropdown">-->
|
||||
<!-- <span class="nav-link">在线工具</span>-->
|
||||
<!-- <div class="dropdown-content">-->
|
||||
<!-- <router-link to="/weapon-match" class="nav-link" @click.prevent="handleNavClick('/weapon-match', ['lv-admin','lv-mod'])">Weapon 匹配</router-link>-->
|
||||
<!-- <router-link to="/PIC2TGA" class="nav-link" @click.prevent="handleNavClick('/PIC2TGA', ['lv-admin','lv-mod','lv-map','lv-competitor'])">在线转tga工具</router-link>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <!– 赛事信息 一级菜单 –>-->
|
||||
<!-- <div class="nav-dropdown">-->
|
||||
<!-- <span class="nav-link">赛事信息</span>-->
|
||||
<!-- <div class="dropdown-content">-->
|
||||
<!--<!– <router-link to="/competition" class="nav-link" @click.prevent="handleNavClick('/competition', ['lv-admin','lv-competitor'])">赛程信息</router-link>–>-->
|
||||
<!-- <router-link to="/competition" class="nav-link">赛程信息</router-link>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <!– 公共信息区 一级菜单 –>-->
|
||||
<!-- <div class="nav-dropdown">-->
|
||||
<!-- <span class="nav-link">公共信息区</span>-->
|
||||
<!-- <div class="dropdown-content">-->
|
||||
<!-- <router-link to="/demands" class="nav-link">办事大厅</router-link>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </template>-->
|
||||
<!-- 需要登陆才能访问,如果没有登陆,则点击跳转到登陆页面-->
|
||||
<!-- 如果登陆了,才能执行权限判断-->
|
||||
<div class="nav-dropdown">
|
||||
|
@ -192,6 +192,7 @@
|
||||
v-for="demand in visibleDemands"
|
||||
:key="demand.id"
|
||||
class="demand-item"
|
||||
@click="viewDemand(demand.id)"
|
||||
>
|
||||
<div class="demand-header">
|
||||
<h4 class="demand-title">{{ demand.content }}</h4>
|
||||
@ -295,14 +296,6 @@ const fetchDemands = async () => {
|
||||
announcements.value = demands
|
||||
.slice()
|
||||
.sort((a, b) => new Date(b.date) - new Date(a.date))
|
||||
// .map((item, idx) => ({
|
||||
// id: item.id || idx + 1,
|
||||
// date: item.date,
|
||||
// title: item.sendcontent || item.content || '无标题',
|
||||
// content: item.sendcontent || item.content || '无标题',
|
||||
// reward: item.reward || '无',
|
||||
// requester: item.requester || '匿名',
|
||||
// status: item.status || 'NORMAL' // 假设status字段
|
||||
.map((item, idx) => ({
|
||||
id: item.id || idx + 1,
|
||||
date: item.date,
|
||||
@ -411,6 +404,10 @@ const viewCompetition = (id) => {
|
||||
router.push(`/competition/detail?id=${id}`)
|
||||
}
|
||||
|
||||
const viewDemand = (id) => {
|
||||
router.push(`/demands?id=${id}`)
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
const formatDate = (dateString) => {
|
||||
if (!dateString || dateString === 'Test_date') return '日期未提供'
|
||||
|
Loading…
x
Reference in New Issue
Block a user