// pages/game/play/play.js import request from '../../../utils/request' Page({ data: { sessionId: null, session: null, players: [], detailPlayers: [], scoreboard: [], recentRecords: [], historyRecords: [], currentRound: 1, // 快速记账 selectedScore: 0, customScore: '', canSubmitQuick: false, // 详细记账 showDetailMode: false, totalBalance: 0, // 历史记录 showHistory: false, // 刷新定时器 refreshTimer: null }, onLoad(options) { if (!options.id) { wx.showToast({ title: '参数错误', icon: 'none' }) setTimeout(() => { wx.navigateBack() }, 1500) return } this.setData({ sessionId: options.id }) // 加载数据 this.loadSessionData() this.loadRecentRecords() // 设置自动刷新 this.startAutoRefresh() }, onUnload() { if (this.data.refreshTimer) { clearInterval(this.data.refreshTimer) } }, // 启动自动刷新 startAutoRefresh() { const timer = setInterval(() => { this.loadSessionData(true) this.loadRecentRecords(true) }, 10000) // 每10秒刷新一次 this.setData({ refreshTimer: timer }) }, // 加载牌局数据 async loadSessionData(silent = false) { if (!silent) { wx.showLoading({ title: '加载中...' }) } try { // 获取牌局详情 const sessionData = await request.get(`/rooms/${this.data.sessionId}`) // 获取当前积分和局数 const chips = await request.get(`/records/session/${this.data.sessionId}/chips`) // 准备玩家数据(用于快速记账) const players = sessionData.players.map(p => ({ id: p.player_id, nickname: p.nickname, avatar_url: p.avatar_url, selected: false, // 是否选为赢家 losing: false // 是否选为输家 })) // 准备详细记账玩家数据 const detailPlayers = sessionData.players.map(p => ({ id: p.player_id, nickname: p.nickname, avatar_url: p.avatar_url, current_chips: chips.chips[p.player_id] || 0, chips_change: 0 })) // 准备积分榜数据 const scoreboard = sessionData.players.map(p => ({ id: p.player_id, nickname: p.nickname, avatar_url: p.avatar_url, total_chips: chips.chips[p.player_id] || 0 })).sort((a, b) => b.total_chips - a.total_chips) this.setData({ session: sessionData.session, players, detailPlayers, scoreboard, currentRound: chips.total_rounds + 1 }) if (!silent) { wx.hideLoading() } } catch (error) { if (!silent) { wx.hideLoading() wx.showToast({ title: error.message || '加载失败', icon: 'none' }) } } }, // 加载最近记录 async loadRecentRecords(silent = false) { try { const recordsData = await request.get(`/records/session/${this.data.sessionId}`, { page: 1, pageSize: 5 }) // 格式化记录 const recentRecords = (recordsData.list || []).map(record => { const time = new Date(record.created_at * 1000) const now = Date.now() const diff = now - record.created_at * 1000 return { id: record.id, round_number: record.round_number, scores: record.playerScores || [], timeText: this.formatRecordTime(time), can_undo: diff < 300000 // 5分钟内可撤销 } }) this.setData({ recentRecords }) } catch (error) { if (!silent) { console.error('加载记录失败:', error) } } }, // 格式化时间 formatRecordTime(time) { const now = new Date() const diff = now - time if (diff < 60000) { return '刚刚' } else if (diff < 3600000) { return `${Math.floor(diff / 60000)}分钟前` } else { return `${time.getHours()}:${String(time.getMinutes()).padStart(2, '0')}` } }, // 切换赢家 toggleWinner(e) { const playerId = e.currentTarget.dataset.id const players = this.data.players.map(p => { if (p.id === playerId) { p.selected = !p.selected // 赢家不能同时是输家 if (p.selected) p.losing = false } return p }) this.setData({ players }) this.checkCanSubmitQuick() }, // 切换输家 toggleLoser(e) { const playerId = e.currentTarget.dataset.id const players = this.data.players.map(p => { if (p.id === playerId) { p.losing = !p.losing // 输家不能同时是赢家 if (p.losing) p.selected = false } return p }) this.setData({ players }) this.checkCanSubmitQuick() }, // 设置分数 setScore(e) { const score = parseInt(e.currentTarget.dataset.score) this.setData({ selectedScore: score, customScore: '' }) this.checkCanSubmitQuick() }, // 输入自定义分数 onScoreInput(e) { const score = parseInt(e.detail.value) || 0 this.setData({ customScore: e.detail.value, selectedScore: score }) this.checkCanSubmitQuick() }, // 检查是否可以提交快速记账 checkCanSubmitQuick() { const { players, selectedScore } = this.data const winners = players.filter(p => p.selected) const losers = players.filter(p => p.losing) const canSubmit = winners.length > 0 && losers.length > 0 && selectedScore > 0 this.setData({ canSubmitQuick: canSubmit }) }, // 提交快速记账 async submitQuickEntry() { const { players, selectedScore, currentRound } = this.data const winners = players.filter(p => p.selected) const losers = players.filter(p => p.losing) if (winners.length === 0 || losers.length === 0 || selectedScore <= 0) { wx.showToast({ title: '请完整填写信息', icon: 'none' }) return } // 计算分数变化 const winnerShare = Math.floor(selectedScore * losers.length / winners.length) const loserShare = selectedScore const scores = {} winners.forEach(w => { scores[w.id] = winnerShare }) losers.forEach(l => { scores[l.id] = -loserShare }) // 未参与的玩家分数为0 players.forEach(p => { if (!scores[p.id]) { scores[p.id] = 0 } }) wx.showLoading({ title: '提交中...' }) try { await request.post('/records/create', { session_id: this.data.sessionId, round_number: currentRound, scores }) wx.hideLoading() wx.showToast({ title: '记账成功', icon: 'success' }) // 重置选择 const resetPlayers = players.map(p => ({ ...p, selected: false, losing: false })) this.setData({ players: resetPlayers, selectedScore: 0, customScore: '', canSubmitQuick: false, currentRound: currentRound + 1 }) // 刷新数据 this.loadSessionData() this.loadRecentRecords() } catch (error) { wx.hideLoading() wx.showToast({ title: error.message || '提交失败', icon: 'none' }) } }, // 切换详细记账模式 toggleDetailMode() { this.setData({ showDetailMode: !this.data.showDetailMode }) }, // 详细分数输入 onDetailScoreInput(e) { const playerId = e.currentTarget.dataset.id const value = parseInt(e.detail.value) || 0 const detailPlayers = this.data.detailPlayers.map(p => { if (p.id === playerId) { p.chips_change = value } return p }) // 计算总平衡 const totalBalance = detailPlayers.reduce((sum, p) => sum + p.chips_change, 0) this.setData({ detailPlayers, totalBalance }) }, // 提交详细记账 async submitDetailEntry() { const { detailPlayers, totalBalance, currentRound } = this.data if (totalBalance !== 0) { wx.showToast({ title: '分数需要平衡', icon: 'none' }) return } // 检查是否有变化 const hasChange = detailPlayers.some(p => p.chips_change !== 0) if (!hasChange) { wx.showToast({ title: '请输入分数变化', icon: 'none' }) return } // 构建分数对象 const scores = {} detailPlayers.forEach(p => { scores[p.id] = p.chips_change }) wx.showLoading({ title: '提交中...' }) try { await request.post('/records/create', { session_id: this.data.sessionId, round_number: currentRound, scores }) wx.hideLoading() wx.showToast({ title: '记账成功', icon: 'success' }) // 重置详细记账 const resetPlayers = detailPlayers.map(p => ({ ...p, chips_change: 0 })) this.setData({ detailPlayers: resetPlayers, totalBalance: 0, showDetailMode: false, currentRound: currentRound + 1 }) // 刷新数据 this.loadSessionData() this.loadRecentRecords() } catch (error) { wx.hideLoading() wx.showToast({ title: error.message || '提交失败', icon: 'none' }) } }, // 撤销记录 async undoRecord(e) { const recordId = e.currentTarget.dataset.id wx.showModal({ title: '撤销记录', content: '确定要撤销这条记录吗?', success: async (res) => { if (res.confirm) { wx.showLoading({ title: '撤销中...' }) try { await request.delete(`/records/${recordId}`) wx.hideLoading() wx.showToast({ title: '已撤销', icon: 'success' }) // 刷新数据 this.loadSessionData() this.loadRecentRecords() } catch (error) { wx.hideLoading() wx.showToast({ title: error.message || '撤销失败', icon: 'none' }) } } } }) }, // 查看历史记录 async viewHistory() { wx.showLoading({ title: '加载中...' }) try { const recordsData = await request.get(`/records/session/${this.data.sessionId}`, { page: 1, pageSize: 50 }) const historyRecords = (recordsData.list || []).map(record => { const time = new Date(record.created_at * 1000) return { ...record, timeText: `${time.getMonth() + 1}/${time.getDate()} ${time.getHours()}:${String(time.getMinutes()).padStart(2, '0')}` } }) this.setData({ historyRecords, showHistory: true }) wx.hideLoading() } catch (error) { wx.hideLoading() wx.showToast({ title: '加载失败', icon: 'none' }) } }, // 关闭历史记录 closeHistory() { this.setData({ showHistory: false }) }, // 暂停游戏 pauseGame() { wx.showModal({ title: '暂停游戏', content: '确定要暂停游戏吗?', success: async (res) => { if (res.confirm) { try { await request.post(`/rooms/${this.data.sessionId}/pause`) wx.showToast({ title: '已暂停', icon: 'success' }) setTimeout(() => { wx.navigateBack() }, 1500) } catch (error) { wx.showToast({ title: error.message || '操作失败', icon: 'none' }) } } } }) }, // 结束游戏 endGame() { wx.showModal({ title: '结束牌局', content: '确定要结束牌局吗?结束后将无法继续记账', confirmColor: '#ff4444', success: async (res) => { if (res.confirm) { wx.showLoading({ title: '处理中...' }) try { await request.post(`/rooms/${this.data.sessionId}/end`) wx.hideLoading() wx.showToast({ title: '牌局已结束', icon: 'success' }) // 跳转到统计页 setTimeout(() => { wx.redirectTo({ url: `/pages/stats/session/session?id=${this.data.sessionId}` }) }, 1500) } catch (error) { wx.hideLoading() wx.showToast({ title: error.message || '操作失败', icon: 'none' }) } } } }) }, // 下拉刷新 onPullDownRefresh() { Promise.all([ this.loadSessionData(), this.loadRecentRecords() ]).then(() => { wx.stopPullDownRefresh() }) } })