- Removed unnecessary "peer" flags from various dependencies in package-lock.json to streamline package management. - Updated Vite dependencies and their corresponding metadata for improved performance and compatibility. - Adjusted import paths in CSS files to reflect the correct directory structure. - Deleted unused CSS files related to the "col" component to clean up the project.
439 lines
14 KiB
JavaScript
439 lines
14 KiB
JavaScript
const app = getApp()
|
||
|
||
Page({
|
||
data: {
|
||
matchCode: '',
|
||
match: {
|
||
players: []
|
||
},
|
||
myPlayer: null,
|
||
currentGame: null,
|
||
myScoreInput: '',
|
||
opponentScoreInput: '',
|
||
isReferee: false,
|
||
editingGame: null,
|
||
editScoreForm: {
|
||
player1Score: '',
|
||
player2Score: ''
|
||
}
|
||
},
|
||
|
||
onLoad(options) {
|
||
this.setData({ matchCode: options.code })
|
||
this.fetchMatchDetail()
|
||
},
|
||
|
||
onPullDownRefresh() {
|
||
this.fetchMatchDetail().then(() => {
|
||
wx.stopPullDownRefresh()
|
||
})
|
||
},
|
||
|
||
bindMyScoreInput(e) {
|
||
this.setData({ myScoreInput: e.detail.value })
|
||
},
|
||
|
||
bindOpponentScoreInput(e) {
|
||
this.setData({ opponentScoreInput: e.detail.value })
|
||
},
|
||
|
||
async handleSubmitScore() {
|
||
const { myScoreInput, opponentScoreInput, currentGame } = this.data
|
||
|
||
if (!myScoreInput || !opponentScoreInput) {
|
||
wx.showToast({ title: '请输入比分', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
if (!currentGame) return
|
||
|
||
wx.showLoading({ title: '提交中...' })
|
||
try {
|
||
if (!this.data.match || !this.data.match.id) {
|
||
throw new Error('比赛信息缺失')
|
||
}
|
||
|
||
await app.request('/api/match/ranking/submit-score', {
|
||
match_id: this.data.match.id,
|
||
my_score: parseInt(myScoreInput),
|
||
opponent_score: parseInt(opponentScoreInput)
|
||
}, 'POST')
|
||
|
||
wx.showToast({ title: '已提交,等待确认' })
|
||
this.fetchCurrentGame()
|
||
} catch (e) {
|
||
console.error('提交比分失败:', e)
|
||
wx.showToast({ title: '提交失败,请重试', icon: 'none' })
|
||
} finally {
|
||
wx.hideLoading()
|
||
}
|
||
},
|
||
|
||
async handleConfirmScore() {
|
||
if (!this.data.currentGame) return
|
||
|
||
wx.showModal({
|
||
title: '确认比分',
|
||
content: '确认比分无误并结束本局?',
|
||
success: async (res) => {
|
||
if (res.confirm) {
|
||
wx.showLoading({ title: '确认中...' })
|
||
try {
|
||
await app.request('/api/match/ranking/confirm-score', {
|
||
game_id: this.data.currentGame.id,
|
||
confirm: true
|
||
}, 'POST')
|
||
|
||
wx.showToast({ title: '已确认' })
|
||
// 刷新页面以获取最新状态(包括新对局)
|
||
this.fetchMatchDetail()
|
||
} catch (e) {
|
||
console.error('确认比分失败:', e)
|
||
} finally {
|
||
wx.hideLoading()
|
||
}
|
||
}
|
||
}
|
||
})
|
||
},
|
||
|
||
async handleDisputeScore() {
|
||
if (!this.data.currentGame) return
|
||
|
||
wx.showModal({
|
||
title: '标记争议',
|
||
content: '标记为有争议将重置本局比分,是否继续?',
|
||
success: async (res) => {
|
||
if (res.confirm) {
|
||
wx.showLoading({ title: '提交中...' })
|
||
try {
|
||
await app.request('/api/match/ranking/confirm-score', {
|
||
game_id: this.data.currentGame.id,
|
||
confirm: false
|
||
}, 'POST')
|
||
|
||
wx.showToast({ title: '已标记争议' })
|
||
this.fetchCurrentGame()
|
||
} catch (e) {
|
||
console.error('提交失败:', e)
|
||
} finally {
|
||
wx.hideLoading()
|
||
}
|
||
}
|
||
}
|
||
})
|
||
},
|
||
|
||
async fetchMatchDetail() {
|
||
try {
|
||
// 同时获取比赛详情和当前对局,确保数据完整刷新
|
||
const [detailRes, gameRes] = await Promise.all([
|
||
app.request(`/api/match/ranking/${this.data.matchCode}`),
|
||
app.request(`/api/match/ranking/${this.data.matchCode}/my-game`).catch(() => ({ data: { currentGame: null } }))
|
||
])
|
||
|
||
// 调试:打印完整的接口返回数据
|
||
console.log('[fetchMatchDetail] 接口返回数据:', {
|
||
matchCode: detailRes.data.matchCode,
|
||
rounds: detailRes.data.rounds,
|
||
roundsLength: detailRes.data.rounds?.length || 0,
|
||
isReferee: detailRes.data.isReferee,
|
||
playersCount: detailRes.data.players?.length || 0
|
||
})
|
||
|
||
// 详细打印对局数据(接口已直接返回选手名称)
|
||
if (detailRes.data.rounds && detailRes.data.rounds.length > 0) {
|
||
detailRes.data.rounds.forEach((round, roundIndex) => {
|
||
console.log(`[fetchMatchDetail] 轮次 ${roundIndex + 1}:`, {
|
||
roundName: round.roundName,
|
||
roundId: round.id,
|
||
gamesCount: round.games?.length || 0,
|
||
games: round.games?.map(g => ({
|
||
id: g.id,
|
||
player1Id: g.player1Id,
|
||
player2Id: g.player2Id,
|
||
player1Name: g.player1Name,
|
||
player2Name: g.player2Name,
|
||
player1Score: g.player1Score,
|
||
player2Score: g.player2Score
|
||
})) || []
|
||
})
|
||
|
||
// 检查每个对局的选手名称
|
||
if (round.games && round.games.length > 0) {
|
||
round.games.forEach((game, gameIndex) => {
|
||
console.log(`[fetchMatchDetail] 对局 ${gameIndex + 1}:`, {
|
||
id: game.id,
|
||
player1Id: game.player1Id,
|
||
player2Id: game.player2Id,
|
||
player1Name: game.player1Name,
|
||
player2Name: game.player2Name,
|
||
hasPlayer1Name: !!game.player1Name,
|
||
hasPlayer2Name: !!game.player2Name
|
||
})
|
||
})
|
||
}
|
||
})
|
||
} else {
|
||
console.warn('[fetchMatchDetail] 警告: rounds 为空或不存在', detailRes.data)
|
||
}
|
||
|
||
// 确保数据正确设置,深拷贝避免引用问题
|
||
const matchData = JSON.parse(JSON.stringify({
|
||
...detailRes.data,
|
||
rounds: (detailRes.data.rounds || []).map(round => ({
|
||
...round,
|
||
games: (round.games || []).map(game => ({
|
||
...game,
|
||
// 确保选手名称字段存在
|
||
player1Name: game.player1Name || `选手${game.player1Id}`,
|
||
player2Name: game.player2Name || `选手${game.player2Id}`
|
||
}))
|
||
}))
|
||
}))
|
||
|
||
console.log('[fetchMatchDetail] 设置到页面的数据:', {
|
||
matchCode: matchData.matchCode,
|
||
roundsLength: matchData.rounds.length,
|
||
firstRoundGames: matchData.rounds[0]?.games?.length || 0,
|
||
firstGame: matchData.rounds[0]?.games?.[0] || null,
|
||
firstGamePlayerNames: matchData.rounds[0]?.games?.[0] ? {
|
||
player1Name: matchData.rounds[0].games[0].player1Name,
|
||
player2Name: matchData.rounds[0].games[0].player2Name
|
||
} : null
|
||
})
|
||
|
||
this.setData({
|
||
match: matchData,
|
||
isReferee: detailRes.data.isReferee || false
|
||
}, () => {
|
||
// setData 完成后的回调
|
||
console.log('[fetchMatchDetail] setData 完成,页面数据:', {
|
||
matchRoundsLength: this.data.match?.rounds?.length || 0,
|
||
firstRoundGamesLength: this.data.match?.rounds?.[0]?.games?.length || 0,
|
||
firstGamePlayerNames: this.data.match?.rounds?.[0]?.games?.[0] ? {
|
||
player1Name: this.data.match.rounds[0].games[0].player1Name,
|
||
player2Name: this.data.match.rounds[0].games[0].player2Name,
|
||
player1Id: this.data.match.rounds[0].games[0].player1Id,
|
||
player2Id: this.data.match.rounds[0].games[0].player2Id
|
||
} : null
|
||
})
|
||
})
|
||
|
||
// 如果是裁判,不需要获取当前对局
|
||
if (detailRes.data.isReferee) {
|
||
console.log('[fetchMatchDetail] 裁判身份,跳过获取当前对局')
|
||
return
|
||
}
|
||
|
||
// 找到我的参赛信息
|
||
const ladderUser = app.globalData.ladderUser
|
||
if (ladderUser) {
|
||
// 使用 == 比较,避免类型不一致问题
|
||
const myPlayer = detailRes.data.players.find(p => p.ladderUserId == ladderUser.id)
|
||
this.setData({ myPlayer })
|
||
|
||
// 更新当前对局信息
|
||
const newGame = gameRes.data.currentGame || null
|
||
const prevGameId = this.data.currentGame && this.data.currentGame.id
|
||
const newGameId = newGame && newGame.id
|
||
const gameChanged = prevGameId !== newGameId
|
||
|
||
if (gameChanged) {
|
||
this.setData({
|
||
currentGame: newGame,
|
||
myScoreInput: '',
|
||
opponentScoreInput: ''
|
||
})
|
||
} else {
|
||
this.setData({ currentGame: newGame })
|
||
}
|
||
}
|
||
} catch (e) {
|
||
console.error('获取排位赛详情失败:', e)
|
||
}
|
||
},
|
||
|
||
async fetchCurrentGame() {
|
||
try {
|
||
const res = await app.request(`/api/match/ranking/${this.data.matchCode}/my-game`)
|
||
const newGame = res.data.currentGame || null
|
||
|
||
// 如果切换到了新的对局(或上一局结束变为 null),重置本地输入框
|
||
const prevGameId = this.data.currentGame && this.data.currentGame.id
|
||
const newGameId = newGame && newGame.id
|
||
const gameChanged = prevGameId !== newGameId
|
||
|
||
if (gameChanged) {
|
||
this.setData({
|
||
currentGame: newGame,
|
||
myScoreInput: '',
|
||
opponentScoreInput: ''
|
||
})
|
||
} else {
|
||
this.setData({ currentGame: newGame })
|
||
}
|
||
} catch (e) {
|
||
console.error('获取当前对局失败:', e)
|
||
}
|
||
},
|
||
|
||
// 裁判功能:编辑比分
|
||
handleRefereeEditScore(e) {
|
||
const game = e.currentTarget.dataset.game
|
||
if (!game) return
|
||
|
||
// 确保 game 对象包含必要的字段,包括选手名称
|
||
const gameData = {
|
||
id: game.id,
|
||
player1Id: game.player1Id,
|
||
player2Id: game.player2Id,
|
||
player1Name: game.player1Name || `选手${game.player1Id}`,
|
||
player2Name: game.player2Name || `选手${game.player2Id}`,
|
||
player1Score: game.player1Score,
|
||
player2Score: game.player2Score,
|
||
confirmStatus: game.confirmStatus
|
||
}
|
||
|
||
this.setData({
|
||
editingGame: gameData,
|
||
editScoreForm: {
|
||
player1Score: game.player1Score !== null && game.player1Score !== undefined ? String(game.player1Score) : '',
|
||
player2Score: game.player2Score !== null && game.player2Score !== undefined ? String(game.player2Score) : ''
|
||
}
|
||
})
|
||
},
|
||
|
||
bindPlayer1ScoreInput(e) {
|
||
this.setData({
|
||
'editScoreForm.player1Score': e.detail.value
|
||
})
|
||
},
|
||
|
||
bindPlayer2ScoreInput(e) {
|
||
this.setData({
|
||
'editScoreForm.player2Score': e.detail.value
|
||
})
|
||
},
|
||
|
||
async handleRefereeSaveScore() {
|
||
const { editingGame, editScoreForm } = this.data
|
||
if (!editingGame || !editScoreForm.player1Score || !editScoreForm.player2Score) {
|
||
wx.showToast({ title: '请输入完整比分', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
wx.showLoading({ title: '保存中...' })
|
||
try {
|
||
await app.request(`/api/match/referee/update-score`, {
|
||
match_id: this.data.match.id,
|
||
game_id: editingGame.id,
|
||
player1_score: parseInt(editScoreForm.player1Score),
|
||
player2_score: parseInt(editScoreForm.player2Score)
|
||
}, 'POST')
|
||
|
||
wx.showToast({ title: '保存成功', icon: 'success' })
|
||
this.setData({ editingGame: null })
|
||
this.fetchMatchDetail()
|
||
} catch (e) {
|
||
console.error('保存比分失败:', e)
|
||
wx.showToast({ title: e.message || '保存失败', icon: 'none' })
|
||
} finally {
|
||
wx.hideLoading()
|
||
}
|
||
},
|
||
|
||
// 裁判功能:确认比分
|
||
async handleRefereeConfirmScore(e) {
|
||
const game = e.currentTarget.dataset.game
|
||
wx.showModal({
|
||
title: '确认比分',
|
||
content: `确认该对局结果?确认后将计算战力值变动`,
|
||
success: async (res) => {
|
||
if (res.confirm) {
|
||
wx.showLoading({ title: '确认中...' })
|
||
try {
|
||
// 使用后台API确认比分(需要管理员权限,但裁判应该可以通过小程序API确认)
|
||
// 或者使用小程序端的确认接口,但需要检查权限
|
||
await app.request(`/api/match/ranking/confirm-score`, {
|
||
game_id: game.id,
|
||
confirm: true
|
||
}, 'POST')
|
||
wx.showToast({ title: '确认成功', icon: 'success' })
|
||
this.fetchMatchDetail()
|
||
} catch (e) {
|
||
console.error('确认比分失败:', e)
|
||
wx.showToast({ title: e.message || '确认失败', icon: 'none' })
|
||
} finally {
|
||
wx.hideLoading()
|
||
}
|
||
}
|
||
}
|
||
})
|
||
},
|
||
|
||
// 关闭编辑弹窗
|
||
handleCloseEditModal() {
|
||
this.setData({ editingGame: null })
|
||
},
|
||
|
||
// 阻止事件冒泡(用于弹窗内容区域)
|
||
stopPropagation() {
|
||
// 空函数,仅用于阻止事件冒泡
|
||
},
|
||
|
||
// 获取选手名称(用于裁判界面)
|
||
getPlayerName(ladderUserId) {
|
||
if (!ladderUserId) {
|
||
return '未知选手'
|
||
}
|
||
if (!this.data.match || !this.data.match.players || !Array.isArray(this.data.match.players)) {
|
||
return `选手${ladderUserId}`
|
||
}
|
||
// 使用 == 比较,避免类型不一致问题(支持数字和字符串比较)
|
||
const player = this.data.match.players.find(p => {
|
||
const pId = p.ladderUserId
|
||
const targetId = ladderUserId
|
||
return String(pId) === String(targetId) || Number(pId) === Number(targetId)
|
||
})
|
||
if (player && player.realName) {
|
||
return player.realName
|
||
}
|
||
// 如果找不到,返回默认名称
|
||
return `选手${ladderUserId}`
|
||
},
|
||
|
||
// 裁判功能:开始淘汰赛
|
||
handleRefereeStartElimination() {
|
||
wx.showModal({
|
||
title: '开始淘汰赛',
|
||
content: '确定要开始淘汰赛吗?',
|
||
success: async (res) => {
|
||
if (res.confirm) {
|
||
wx.showActionSheet({
|
||
itemList: ['2强(决赛)', '4强', '8强'],
|
||
success: async (actionRes) => {
|
||
const sizes = [2, 4, 8]
|
||
const size = sizes[actionRes.tapIndex]
|
||
wx.showLoading({ title: '开始中...' })
|
||
try {
|
||
await app.request(`/api/match/referee/start-elimination`, {
|
||
match_id: this.data.match.id,
|
||
elimination_size: size
|
||
}, 'POST')
|
||
wx.showToast({ title: '淘汰赛已开始', icon: 'success' })
|
||
this.fetchMatchDetail()
|
||
} catch (e) {
|
||
console.error('开始淘汰赛失败:', e)
|
||
wx.showToast({ title: e.message || '开始失败', icon: 'none' })
|
||
} finally {
|
||
wx.hideLoading()
|
||
}
|
||
}
|
||
})
|
||
}
|
||
}
|
||
})
|
||
}
|
||
})
|