yingsa/miniprogram/pages/match/challenge-detail/index.js
Ethanfly edd977d5d2 refactor: Simplify match detail refresh logic and improve error handling
- Replaced setTimeout with direct await calls to loadMatchDetail after successful API responses, ensuring immediate data refresh.
- Enhanced error handling by displaying specific backend error messages in toast notifications for better user feedback.
2026-02-09 17:23:06 +08:00

617 lines
23 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const app = getApp()
Page({
data: {
matchId: null,
matchInfo: null,
challengerAvatarSrc: "/images/avatar-default.svg",
defenderAvatarSrc: "/images/avatar-default.svg",
myRole: null, // 'challenger' | 'defender' | null
canAccept: false,
canReject: false,
canSubmitScore: false,
canConfirmScore: false,
showScoreModal: false,
myScore: '',
opponentScore: '',
loading: false
},
onLoad(options) {
if (options.id) {
this.setData({ matchId: options.id })
this.loadMatchDetail()
}
},
onShow() {
// 每次显示页面时刷新数据
if (this.data.matchId) {
this.loadMatchDetail()
}
},
// 加载比赛详情
async loadMatchDetail() {
this.setData({ loading: true })
try {
const res = await app.request(`/api/match/${this.data.matchId}`)
console.log('API完整响应:', JSON.stringify(res, null, 2))
// app.request 返回的是 { code: 0, message, data }
// 所以 res.data 才是真正的数据
const matchInfo = res.data
console.log('比赛详情数据:', matchInfo)
console.log('数据字段:', Object.keys(matchInfo || {}))
if (!matchInfo) {
console.error('比赛详情数据为空')
wx.showToast({ title: '数据格式错误', icon: 'none' })
this.setData({ loading: false })
return
}
// 检查关键字段
console.log('关键字段检查:', {
hasMyRole: 'myRole' in matchInfo,
myRole: matchInfo.myRole,
myRoleType: typeof matchInfo.myRole,
hasCanAccept: 'canAccept' in matchInfo,
canAccept: matchInfo.canAccept,
canAcceptType: typeof matchInfo.canAccept,
hasCanReject: 'canReject' in matchInfo,
canReject: matchInfo.canReject,
canRejectType: typeof matchInfo.canReject,
status: matchInfo.status,
statusType: typeof matchInfo.status
})
// 确保布尔值正确设置
let canAccept = Boolean(matchInfo.canAccept)
let canReject = Boolean(matchInfo.canReject)
let canSubmitScore = Boolean(matchInfo.canSubmitScore)
let canConfirmScore = Boolean(matchInfo.canConfirmScore)
let myRole = matchInfo.myRole || null
// 临时方案:如果 myRole 为 null尝试通过其他方式判断角色
// 适用于待接受状态status=0和进行中状态status=1
if (!myRole && (matchInfo.status === 0 || matchInfo.status === 1)) {
const currentUser = app.globalData.userInfo
console.log('尝试临时方案识别角色:', {
status: matchInfo.status,
currentUser: currentUser ? { id: currentUser.id, phone: currentUser.phone } : null,
defender: matchInfo.defender ? { userId: matchInfo.defender.userId, phone: matchInfo.defender.phone } : null,
challenger: matchInfo.challenger ? { userId: matchInfo.challenger.userId, phone: matchInfo.challenger.phone } : null
})
if (currentUser) {
// 尝试通过 user_id 判断
if (matchInfo.defender && matchInfo.defender.userId && matchInfo.defender.userId == currentUser.id) {
myRole = 'defender'
console.log('临时方案通过defender.userId识别为被挑战者', {
defenderUserId: matchInfo.defender.userId,
currentUserId: currentUser.id
})
}
// 尝试通过手机号判断
else if (matchInfo.defender && currentUser.phone && matchInfo.defender.phone && matchInfo.defender.phone === currentUser.phone) {
myRole = 'defender'
console.log('临时方案:通过手机号识别为被挑战者', {
defenderPhone: matchInfo.defender.phone,
currentUserPhone: currentUser.phone
})
}
// 如果都不匹配,检查是否是挑战者
else if (matchInfo.challenger && matchInfo.challenger.userId && matchInfo.challenger.userId == currentUser.id) {
myRole = 'challenger'
console.log('临时方案识别为挑战者通过userId')
}
// 通过手机号识别挑战者
else if (matchInfo.challenger && currentUser.phone && matchInfo.challenger.phone && matchInfo.challenger.phone === currentUser.phone) {
myRole = 'challenger'
console.log('临时方案:识别为挑战者(通过手机号)')
}
}
}
// 如果状态是待接受且角色是被挑战者,强制设置权限(即使后端没有返回)
if (matchInfo.status === 0 && myRole === 'defender') {
canAccept = true
canReject = true
console.log('强制设置接受/拒绝权限(状态=0角色=defender')
}
// 最后的备用方案:如果状态是待接受,且当前用户不是挑战者,则可能是被挑战者
// 这种情况下,显示按钮让用户尝试接受/拒绝(如果用户不是被挑战者,后端会拒绝)
if (matchInfo.status === 0 && !myRole && !canAccept && !canReject) {
// 检查当前用户是否是挑战者
const currentUser = app.globalData.userInfo
const isChallenger = currentUser && matchInfo.challenger &&
(matchInfo.challenger.userId == currentUser.id ||
(currentUser.phone && matchInfo.challenger.phone === currentUser.phone))
// 如果不是挑战者,可能是被挑战者,显示按钮
if (!isChallenger) {
myRole = 'defender'
canAccept = true
canReject = true
console.log('备用方案:状态为待接受且不是挑战者,假设是被挑战者,显示按钮')
}
}
// 处理"进行中"状态status=1的操作权限
if (matchInfo.status === 1) {
const game = matchInfo.games && matchInfo.games[0]
if (game) {
console.log('处理进行中状态的操作权限:', {
gameStatus: game.status,
submitBy: game.submitBy,
confirmStatus: game.confirmStatus,
myRole,
canSubmitScore,
canConfirmScore
})
// 如果游戏状态为1进行中且未提交比分双方都可以填写比分
if (game.status === 1 && !game.submitBy) {
// 如果后端没有返回 canSubmitScore但状态允许则设置权限
// 即使 myRole 为 null也允许填写后续会通过游戏信息识别角色
if (!canSubmitScore) {
canSubmitScore = true
console.log('进行中状态:设置填写比分权限(游戏状态=1未提交')
}
}
// 如果游戏状态为2已提交且对方已提交等待我确认
else if (game.status === 2 && game.submitBy) {
// 判断当前用户是否是提交者submitBy 是 ladder_user_id
let isSubmitter = false
if (myRole) {
// 如果已识别角色,通过比较 submitBy 和 challenger/defender 的 idladder_user_id来判断
if (myRole === 'challenger' && matchInfo.challenger && game.submitBy == matchInfo.challenger.id) {
isSubmitter = true
console.log('当前用户是提交者(挑战者)')
} else if (myRole === 'defender' && matchInfo.defender && game.submitBy == matchInfo.defender.id) {
isSubmitter = true
console.log('当前用户是提交者(被挑战者)')
}
} else {
// 如果 myRole 为 null通过比较当前用户的 user_id 和 challenger/defender 的 user_id 来判断
const currentUser = app.globalData.userInfo
if (currentUser) {
// 检查提交者是否是挑战者
if (matchInfo.challenger && game.submitBy == matchInfo.challenger.id &&
currentUser.id == matchInfo.challenger.userId) {
isSubmitter = true
console.log('当前用户是提交者通过challenger判断')
}
// 检查提交者是否是被挑战者
else if (matchInfo.defender && game.submitBy == matchInfo.defender.id &&
currentUser.id == matchInfo.defender.userId) {
isSubmitter = true
console.log('当前用户是提交者通过defender判断')
}
}
}
// 如果不是提交者,且确认状态为待确认,则可以确认比分
if (!isSubmitter && game.confirmStatus === 0) {
if (!canConfirmScore) {
canConfirmScore = true
console.log('进行中状态:设置确认比分权限(对方已提交,等待确认)', {
submitBy: game.submitBy,
challengerId: matchInfo.challenger ? matchInfo.challenger.id : null,
defenderId: matchInfo.defender ? matchInfo.defender.id : null,
myRole
})
}
}
}
}
// 如果 myRole 仍然为 null但状态是进行中尝试通过游戏中的 player1_id 和 player2_id 判断
if (!myRole && game) {
const currentUser = app.globalData.userInfo
if (currentUser) {
console.log('尝试通过游戏信息识别角色:', {
player1Id: game.player1Id,
player2Id: game.player2Id,
challengerId: matchInfo.challenger ? matchInfo.challenger.id : null,
defenderId: matchInfo.defender ? matchInfo.defender.id : null,
currentUserId: currentUser.id,
challengerUserId: matchInfo.challenger ? matchInfo.challenger.userId : null,
defenderUserId: matchInfo.defender ? matchInfo.defender.userId : null
})
// 通过比较 challenger/defender 的 idladder_user_id和 player1_id/player2_id 来判断
if (matchInfo.challenger && matchInfo.challenger.id == game.player1Id) {
// 如果当前用户是挑战者,且挑战者是 player1
if (currentUser.id == matchInfo.challenger.userId) {
myRole = 'challenger'
console.log('通过游戏player1Id识别为挑战者')
}
} else if (matchInfo.challenger && matchInfo.challenger.id == game.player2Id) {
// 如果当前用户是挑战者,且挑战者是 player2
if (currentUser.id == matchInfo.challenger.userId) {
myRole = 'challenger'
console.log('通过游戏player2Id识别为挑战者')
}
}
if (matchInfo.defender && matchInfo.defender.id == game.player1Id) {
// 如果当前用户是被挑战者,且被挑战者是 player1
if (currentUser.id == matchInfo.defender.userId) {
myRole = 'defender'
console.log('通过游戏player1Id识别为被挑战者')
}
} else if (matchInfo.defender && matchInfo.defender.id == game.player2Id) {
// 如果当前用户是被挑战者,且被挑战者是 player2
if (currentUser.id == matchInfo.defender.userId) {
myRole = 'defender'
console.log('通过游戏player2Id识别为被挑战者')
}
}
// 如果识别到角色,重新检查操作权限
if (myRole) {
console.log('识别到角色后,重新检查操作权限:', { myRole, gameStatus: game.status, submitBy: game.submitBy })
// 如果游戏状态为1进行中且未提交比分可以填写比分
if (game.status === 1 && !game.submitBy) {
canSubmitScore = true
console.log('识别角色后,设置填写比分权限')
}
// 如果游戏状态为2已提交且对方已提交等待我确认
else if (game.status === 2 && game.submitBy) {
// 判断当前用户是否是提交者
const isSubmitter = (myRole === 'challenger' && matchInfo.challenger && game.submitBy == matchInfo.challenger.id) ||
(myRole === 'defender' && matchInfo.defender && game.submitBy == matchInfo.defender.id)
if (!isSubmitter && game.confirmStatus === 0) {
canConfirmScore = true
console.log('识别角色后,设置确认比分权限')
}
}
}
}
}
}
console.log('最终设置的操作权限:', {
canAccept,
canReject,
canSubmitScore,
canConfirmScore,
myRole,
status: matchInfo.status,
defenderInfo: matchInfo.defender ? {
id: matchInfo.defender.id,
userId: matchInfo.defender.userId,
phone: matchInfo.defender.phone,
realName: matchInfo.defender.realName
} : null,
challengerInfo: matchInfo.challenger ? {
id: matchInfo.challenger.id,
userId: matchInfo.challenger.userId,
phone: matchInfo.challenger.phone,
realName: matchInfo.challenger.realName
} : null,
currentUser: app.globalData.userInfo ? {
id: app.globalData.userInfo.id,
phone: app.globalData.userInfo.phone
} : null
})
this.setData({
matchInfo,
challengerAvatarSrc: this.normalizeAvatarSrc(matchInfo?.challenger?.avatar),
defenderAvatarSrc: this.normalizeAvatarSrc(matchInfo?.defender?.avatar),
myRole,
canAccept,
canReject,
canSubmitScore,
canConfirmScore,
loading: false
})
// 再次检查,如果还是没有按钮,输出详细日志
if (matchInfo.status === 0 && !canAccept && !canReject) {
console.error('警告:状态为待接受但没有操作按钮!', {
myRole,
canAccept,
canReject,
matchInfoStatus: matchInfo.status,
defenderUserId: matchInfo.defender ? matchInfo.defender.userId : null,
currentUserId: app.globalData.userInfo ? app.globalData.userInfo.id : null,
defenderPhone: matchInfo.defender ? matchInfo.defender.phone : null,
currentUserPhone: app.globalData.userInfo ? app.globalData.userInfo.phone : null
})
}
} catch (e) {
this.setData({ loading: false })
console.error('加载比赛详情失败:', e)
console.error('错误详情:', e.message, e.data, e)
wx.showToast({ title: '加载失败: ' + (e.message || '未知错误'), icon: 'none', duration: 3000 })
}
},
normalizeAvatarSrc(avatar) {
const fallback = "/images/avatar-default.svg";
if (!avatar) return fallback;
if (typeof avatar !== "string") return fallback;
const a = avatar.trim();
if (!a) return fallback;
if (a === "null" || a === "undefined") return fallback;
// 已经是网络地址或小程序临时文件
if (/^https?:\/\//i.test(a) || a.startsWith("wxfile://")) return a;
// 服务端可能返回 /uploads/xxx 或 uploads/xxx 之类的相对路径
const baseUrl = app?.globalData?.baseUrl || "";
if (!baseUrl) return a; // 没有 baseUrl 时保持原值,让 binderror 兜底
if (a.startsWith("/")) return `${baseUrl}${a}`;
return `${baseUrl}/${a}`;
},
onAvatarError(e) {
const role = e?.currentTarget?.dataset?.role;
const fallback = "/images/avatar-default.svg";
if (role === "challenger") {
this.setData({ challengerAvatarSrc: fallback });
} else if (role === "defender") {
this.setData({ defenderAvatarSrc: fallback });
}
},
// 处理挑战请求从WebSocket调用
handleChallengeRequest(challengeData) {
// 如果当前页面是挑战赛详情且是同一个比赛,显示弹框
if (this.data.matchId == challengeData.matchId) {
this.showChallengeModal(challengeData)
} else {
// 否则跳转到挑战赛详情页面
wx.navigateTo({
url: `/pages/match/challenge-detail/index?id=${challengeData.matchId}`
})
}
},
// 显示挑战弹框
showChallengeModal(challengeData) {
wx.showModal({
title: '收到挑战',
content: `${challengeData.challenger.realName}(Lv${challengeData.challenger.level}, 战力${challengeData.challenger.powerScore}) 向你发起挑战`,
confirmText: '接受',
cancelText: '拒绝',
success: (res) => {
this.respondChallenge(res.confirm)
}
})
},
// 响应挑战
async respondChallenge(accept) {
wx.showLoading({ title: accept ? '接受中...' : '拒绝中...' })
try {
await app.request('/api/match/challenge/respond', {
match_id: this.data.matchId,
accept: accept
}, 'POST')
wx.hideLoading()
wx.showToast({
title: accept ? '已接受挑战' : '已拒绝挑战',
icon: 'success'
})
// 接口执行成功后刷新当前比赛详情
await this.loadMatchDetail()
} catch (e) {
wx.hideLoading()
console.error('响应挑战失败:', e)
wx.showToast({ title: '操作失败', icon: 'none' })
}
},
// 接受挑战
acceptChallenge() {
this.respondChallenge(true)
},
// 拒绝挑战
rejectChallenge() {
wx.showModal({
title: '确认拒绝',
content: '确定要拒绝这个挑战吗?',
success: (res) => {
if (res.confirm) {
this.respondChallenge(false)
}
}
})
},
// 打开填写比分弹框
openScoreModal() {
this.setData({
showScoreModal: true,
myScore: '',
opponentScore: ''
})
},
// 关闭填写比分弹框
closeScoreModal() {
this.setData({ showScoreModal: false })
},
// 输入我的比分
onMyScoreInput(e) {
this.setData({ myScore: e.detail.value })
},
// 输入对手比分
onOpponentScoreInput(e) {
this.setData({ opponentScore: e.detail.value })
},
// 提交比分
async submitScore() {
const { myScore, opponentScore } = this.data
if (!myScore || !opponentScore) {
wx.showToast({ title: '请填写完整比分', icon: 'none' })
return
}
const myScoreNum = parseInt(myScore)
const opponentScoreNum = parseInt(opponentScore)
if (isNaN(myScoreNum) || isNaN(opponentScoreNum)) {
wx.showToast({ title: '请输入有效数字', icon: 'none' })
return
}
if (myScoreNum === opponentScoreNum) {
wx.showToast({ title: '比分不能相同', icon: 'none' })
return
}
wx.showLoading({ title: '提交中...' })
try {
await app.request('/api/match/challenge/submit-score', {
match_id: this.data.matchId,
my_score: myScoreNum,
opponent_score: opponentScoreNum
}, 'POST')
wx.hideLoading()
wx.showToast({ title: '比分已提交,等待对方确认', icon: 'success' })
this.closeScoreModal()
// 接口执行成功后刷新当前比赛详情
await this.loadMatchDetail()
} catch (e) {
wx.hideLoading()
console.error('提交比分失败:', e)
wx.showToast({ title: e.message || '提交失败', icon: 'none' })
}
},
// 确认比分
async confirmScore(confirm) {
const game = this.data.matchInfo.games && this.data.matchInfo.games[0]
if (!game) {
wx.showToast({ title: '比赛信息错误', icon: 'none' })
return
}
wx.showLoading({ title: '处理中...' })
try {
await app.request('/api/match/challenge/confirm-score', {
game_id: game.id,
confirm: confirm
}, 'POST')
wx.hideLoading()
wx.showToast({
title: confirm ? '已确认比分' : '已标记争议',
icon: 'success'
})
// 接口执行成功后刷新当前比赛详情
await this.loadMatchDetail()
} catch (e) {
wx.hideLoading()
console.error('确认比分失败:', e)
// 显示后端返回的具体错误信息,便于排查
wx.showToast({ title: e.message || '操作失败', icon: 'none' })
}
},
// 确认比分按钮
confirmScoreBtn() {
const game = this.data.matchInfo.games && this.data.matchInfo.games[0]
if (!game) {
wx.showToast({ title: '比赛信息错误', icon: 'none' })
return
}
// 根据当前用户角色显示正确的比分信息
let myScore = 0
let opponentScore = 0
let myName = ''
let opponentName = ''
if (this.data.myRole === 'challenger') {
// 挑战者是 player1 还是 player2
if (this.data.matchInfo.challenger && this.data.matchInfo.challenger.id == game.player1Id) {
myScore = game.player1Score || 0
opponentScore = game.player2Score || 0
myName = this.data.matchInfo.challenger.realName || '挑战者'
opponentName = (this.data.matchInfo.defender && this.data.matchInfo.defender.realName) || '被挑战者'
} else if (this.data.matchInfo.challenger && this.data.matchInfo.challenger.id == game.player2Id) {
myScore = game.player2Score || 0
opponentScore = game.player1Score || 0
myName = this.data.matchInfo.challenger.realName || '挑战者'
opponentName = (this.data.matchInfo.defender && this.data.matchInfo.defender.realName) || '被挑战者'
} else {
// 如果无法确定,使用默认显示
myScore = game.player1Score || 0
opponentScore = game.player2Score || 0
}
} else if (this.data.myRole === 'defender') {
// 被挑战者是 player1 还是 player2
if (this.data.matchInfo.defender && this.data.matchInfo.defender.id == game.player1Id) {
myScore = game.player1Score || 0
opponentScore = game.player2Score || 0
myName = this.data.matchInfo.defender.realName || '被挑战者'
opponentName = (this.data.matchInfo.challenger && this.data.matchInfo.challenger.realName) || '挑战者'
} else if (this.data.matchInfo.defender && this.data.matchInfo.defender.id == game.player2Id) {
myScore = game.player2Score || 0
opponentScore = game.player1Score || 0
myName = this.data.matchInfo.defender.realName || '被挑战者'
opponentName = (this.data.matchInfo.challenger && this.data.matchInfo.challenger.realName) || '挑战者'
} else {
// 如果无法确定,使用默认显示
myScore = game.player1Score || 0
opponentScore = game.player2Score || 0
}
} else {
// 如果角色未知,使用默认显示
myScore = game.player1Score || 0
opponentScore = game.player2Score || 0
}
wx.showModal({
title: '确认比分',
content: `对方提交的比分为:\n${opponentName}: ${opponentScore}\n${myName}: ${myScore}\n\n请确认此比分是否正确?`,
confirmText: '确认',
cancelText: '有争议',
success: (res) => {
if (res.confirm) {
this.confirmScore(true)
} else {
// 有争议
wx.showModal({
title: '确认争议',
content: '确定要标记为有争议吗?标记后需要重新比赛。',
confirmText: '确定',
cancelText: '取消',
success: (res2) => {
if (res2.confirm) {
this.confirmScore(false)
}
}
})
}
}
})
},
// 阻止事件冒泡
stopPropagation() {
// 空函数,用于阻止事件冒泡
}
})