1038 lines
26 KiB
JavaScript
1038 lines
26 KiB
JavaScript
|
|
// pages/game/detail/detail.js
|
|||
|
|
import request from '../../../utils/request'
|
|||
|
|
import { formatTime } from '../../../utils/format'
|
|||
|
|
|
|||
|
|
// 格式化积分显示(超过1000显示k)
|
|||
|
|
function formatScore(score) {
|
|||
|
|
const num = parseFloat(score) || 0
|
|||
|
|
if (Math.abs(num) >= 1000) {
|
|||
|
|
return (num / 1000).toFixed(1) + 'k'
|
|||
|
|
}
|
|||
|
|
return num.toString()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Page({
|
|||
|
|
data: {
|
|||
|
|
sessionId: null,
|
|||
|
|
session: null,
|
|||
|
|
players: [],
|
|||
|
|
records: [],
|
|||
|
|
logs: [],
|
|||
|
|
logsScrollTop: 0,
|
|||
|
|
hasMoreLogs: false,
|
|||
|
|
logsPage: 1,
|
|||
|
|
userInfo: null,
|
|||
|
|
isHost: false,
|
|||
|
|
isInSession: false,
|
|||
|
|
totalRounds: 0,
|
|||
|
|
showShareModal: false,
|
|||
|
|
showPlayerModal: false,
|
|||
|
|
showExpenseModal: false,
|
|||
|
|
showSettingsModal: false,
|
|||
|
|
selectedPlayer: null,
|
|||
|
|
scoreInput: '',
|
|||
|
|
otherPlayers: [],
|
|||
|
|
expenseInputs: {},
|
|||
|
|
tableFee: 0,
|
|||
|
|
quickInputs: [50, 100, 200, 500],
|
|||
|
|
ttsEnabled: true, // 语音播报开关,默认开启
|
|||
|
|
refreshTimer: null,
|
|||
|
|
navTitle: '房间详情',
|
|||
|
|
announcement: '',
|
|||
|
|
navbarHeight: 0, // 导航栏高度
|
|||
|
|
qrcodeUrl: '', // 二维码URL
|
|||
|
|
hasShownRejoinPrompt: false, // 标记是否已显示过重新加入提示
|
|||
|
|
|
|||
|
|
statusText: {
|
|||
|
|
'waiting': '等待中',
|
|||
|
|
'playing': '游戏中',
|
|||
|
|
'paused': '已暂停',
|
|||
|
|
'finished': '已结束'
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
gameTypeText: {
|
|||
|
|
'mahjong': '麻将',
|
|||
|
|
'poker': '扑克',
|
|||
|
|
'other': '其他'
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
onLoad(options) {
|
|||
|
|
// 计算导航栏高度
|
|||
|
|
const windowInfo = wx.getWindowInfo()
|
|||
|
|
const menuButtonInfo = wx.getMenuButtonBoundingClientRect()
|
|||
|
|
const statusBarHeight = windowInfo.statusBarHeight
|
|||
|
|
const menuButtonTop = menuButtonInfo.top
|
|||
|
|
const navbarHeight = menuButtonInfo.bottom + (menuButtonTop - statusBarHeight)
|
|||
|
|
this.setData({ navbarHeight })
|
|||
|
|
|
|||
|
|
if (!options.id) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '牌局不存在',
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
setTimeout(() => {
|
|||
|
|
wx.navigateBack()
|
|||
|
|
}, 1500)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
console.log(options.id)
|
|||
|
|
this.setData({
|
|||
|
|
sessionId: options.id
|
|||
|
|
})
|
|||
|
|
// 获取用户信息
|
|||
|
|
const app = getApp()
|
|||
|
|
const userInfo = app.getUserInfo()
|
|||
|
|
if (!userInfo) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '请先登录',
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
setTimeout(() => {
|
|||
|
|
wx.navigateBack()
|
|||
|
|
}, 1500)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this.setData({ userInfo })
|
|||
|
|
|
|||
|
|
// 加载TTS设置
|
|||
|
|
const ttsEnabled = wx.getStorageSync('ttsEnabled')
|
|||
|
|
if (ttsEnabled !== '') {
|
|||
|
|
this.setData({ ttsEnabled: ttsEnabled })
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 加载公告
|
|||
|
|
this.loadAnnouncements()
|
|||
|
|
|
|||
|
|
// 加载牌局详情(包含日志)
|
|||
|
|
this.loadSessionDetail()
|
|||
|
|
|
|||
|
|
// 设置自动刷新
|
|||
|
|
this.startAutoRefresh()
|
|||
|
|
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
onShow() {
|
|||
|
|
if (this.data.sessionId) {
|
|||
|
|
this.loadSessionDetail()
|
|||
|
|
// 页面重新显示时,重启自动刷新
|
|||
|
|
this.startAutoRefresh()
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
onHide() {
|
|||
|
|
// 页面隐藏时清除定时器,避免后台继续请求
|
|||
|
|
this.stopAutoRefresh()
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
onUnload() {
|
|||
|
|
// 页面卸载时清除定时器
|
|||
|
|
this.stopAutoRefresh()
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 停止自动刷新
|
|||
|
|
stopAutoRefresh() {
|
|||
|
|
if (this.data.refreshTimer) {
|
|||
|
|
clearInterval(this.data.refreshTimer)
|
|||
|
|
this.setData({ refreshTimer: null })
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 启动自动刷新
|
|||
|
|
startAutoRefresh() {
|
|||
|
|
// 如果已有定时器,先清除
|
|||
|
|
this.stopAutoRefresh()
|
|||
|
|
|
|||
|
|
// 每5秒刷新一次
|
|||
|
|
const timer = setInterval(() => {
|
|||
|
|
if (this.data.session) {
|
|||
|
|
this.loadSessionDetail(true)
|
|||
|
|
}
|
|||
|
|
}, 5000)
|
|||
|
|
|
|||
|
|
this.setData({ refreshTimer: timer })
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 加载公告
|
|||
|
|
async loadAnnouncements() {
|
|||
|
|
try {
|
|||
|
|
const data = await request.get('/announcements')
|
|||
|
|
if (data && data.length > 0) {
|
|||
|
|
// 将最新的2条公告用"|"连接
|
|||
|
|
const announcementText = data.map(item => item.content).join(' | ')
|
|||
|
|
this.setData({ announcement: announcementText })
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('加载公告失败:', error)
|
|||
|
|
// 使用默认公告
|
|||
|
|
this.setData({ announcement: '欢迎来到打牌记账,祝大家游戏愉快!' })
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 加载牌局实时数据(统一接口)
|
|||
|
|
async loadSessionDetail(silent = false) {
|
|||
|
|
if (!silent) {
|
|||
|
|
wx.showLoading({ title: '加载中...' })
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 调用统一的实时数据接口
|
|||
|
|
const realtimeData = await request.get(`/rooms/${this.data.sessionId}/realtime`)
|
|||
|
|
|
|||
|
|
const { room, players, records, logs, user_info } = realtimeData
|
|||
|
|
|
|||
|
|
// 检测用户是否曾经在房间但已离开(仅在初次加载时,不是自动刷新,并且未显示过提示)
|
|||
|
|
if (!silent && user_info.has_left && !this.data.hasShownRejoinPrompt) {
|
|||
|
|
if (!silent) {
|
|||
|
|
wx.hideLoading()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 标记已显示过提示,避免重复弹窗
|
|||
|
|
this.setData({ hasShownRejoinPrompt: true })
|
|||
|
|
|
|||
|
|
// 停止自动刷新
|
|||
|
|
this.stopAutoRefresh()
|
|||
|
|
|
|||
|
|
wx.showModal({
|
|||
|
|
title: '检测到您已离开此房间',
|
|||
|
|
content: '您之前已离开过此房间,是否重新加入?',
|
|||
|
|
confirmText: '重新加入',
|
|||
|
|
cancelText: '返回首页',
|
|||
|
|
success: async (res) => {
|
|||
|
|
if (res.confirm) {
|
|||
|
|
// 重新加入房间
|
|||
|
|
try {
|
|||
|
|
wx.showLoading({ title: '加入中...' })
|
|||
|
|
await request.post('/rooms/join', {
|
|||
|
|
room_code: room.room_code
|
|||
|
|
})
|
|||
|
|
wx.hideLoading()
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '重新加入成功',
|
|||
|
|
icon: 'success'
|
|||
|
|
})
|
|||
|
|
// 刷新页面并重启自动刷新
|
|||
|
|
this.loadSessionDetail()
|
|||
|
|
this.startAutoRefresh()
|
|||
|
|
} catch (error) {
|
|||
|
|
wx.hideLoading()
|
|||
|
|
wx.showToast({
|
|||
|
|
title: error.message || '加入失败',
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
// 返回首页
|
|||
|
|
setTimeout(() => {
|
|||
|
|
wx.switchTab({
|
|||
|
|
url: '/pages/index/index'
|
|||
|
|
})
|
|||
|
|
}, 1500)
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 返回首页(tabBar页面使用switchTab)
|
|||
|
|
wx.switchTab({
|
|||
|
|
url: '/pages/index/index'
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检测房间状态变化(仅在自动刷新时)
|
|||
|
|
if (silent && this.data.session) {
|
|||
|
|
const oldStatus = this.data.session.status
|
|||
|
|
const newStatus = room.status
|
|||
|
|
const oldIsInSession = this.data.isInSession
|
|||
|
|
const newIsInSession = user_info.is_in_session
|
|||
|
|
|
|||
|
|
// 1. 检测房间是否已关闭
|
|||
|
|
if (oldStatus === 'playing' && newStatus === 'finished') {
|
|||
|
|
// 停止自动刷新
|
|||
|
|
this.stopAutoRefresh()
|
|||
|
|
|
|||
|
|
wx.showModal({
|
|||
|
|
title: '牌局已结束',
|
|||
|
|
content: '牌局已经结束,是否查看战绩?',
|
|||
|
|
confirmText: '查看战绩',
|
|||
|
|
cancelText: '退出',
|
|||
|
|
success: (res) => {
|
|||
|
|
if (res.confirm) {
|
|||
|
|
// 跳转到统计页(tabBar页面使用switchTab)
|
|||
|
|
wx.switchTab({
|
|||
|
|
url: '/pages/stats/personal/personal'
|
|||
|
|
})
|
|||
|
|
} else {
|
|||
|
|
// 返回首页(tabBar页面使用switchTab)
|
|||
|
|
wx.switchTab({
|
|||
|
|
url: '/pages/index/index'
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2. 检测用户是否离开了房间
|
|||
|
|
if (oldIsInSession && !newIsInSession) {
|
|||
|
|
// 停止自动刷新
|
|||
|
|
this.stopAutoRefresh()
|
|||
|
|
|
|||
|
|
wx.showModal({
|
|||
|
|
title: '已离开房间',
|
|||
|
|
content: '您已离开此牌局,是否重新加入?',
|
|||
|
|
confirmText: '重新加入',
|
|||
|
|
cancelText: '返回首页',
|
|||
|
|
success: async (res) => {
|
|||
|
|
if (res.confirm) {
|
|||
|
|
// 重新加入房间
|
|||
|
|
try {
|
|||
|
|
wx.showLoading({ title: '加入中...' })
|
|||
|
|
await request.post('/rooms/join', {
|
|||
|
|
room_code: this.data.session.room_code
|
|||
|
|
})
|
|||
|
|
wx.hideLoading()
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '重新加入成功',
|
|||
|
|
icon: 'success'
|
|||
|
|
})
|
|||
|
|
// 刷新页面并重启自动刷新
|
|||
|
|
this.loadSessionDetail()
|
|||
|
|
this.startAutoRefresh()
|
|||
|
|
} catch (error) {
|
|||
|
|
wx.hideLoading()
|
|||
|
|
wx.showToast({
|
|||
|
|
title: error.message || '加入失败',
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
// 返回首页
|
|||
|
|
setTimeout(() => {
|
|||
|
|
wx.redirectTo({
|
|||
|
|
url: '/pages/index/index'
|
|||
|
|
})
|
|||
|
|
}, 1500)
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 返回首页(tabBar页面使用switchTab)
|
|||
|
|
wx.switchTab({
|
|||
|
|
url: '/pages/index/index'
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 格式化创建时间
|
|||
|
|
const createTime = formatTime(new Date(room.created_at * 1000))
|
|||
|
|
|
|||
|
|
// 格式化战绩时间
|
|||
|
|
const formattedRecords = (records || []).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')}`
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 格式化日志时间
|
|||
|
|
const formattedLogs = (logs || []).map(log => {
|
|||
|
|
const time = new Date(log.created_at * 1000)
|
|||
|
|
const month = String(time.getMonth() + 1).padStart(2, '0')
|
|||
|
|
const day = String(time.getDate()).padStart(2, '0')
|
|||
|
|
const hours = String(time.getHours()).padStart(2, '0')
|
|||
|
|
const minutes = String(time.getMinutes()).padStart(2, '0')
|
|||
|
|
const seconds = String(time.getSeconds()).padStart(2, '0')
|
|||
|
|
return {
|
|||
|
|
...log,
|
|||
|
|
timeText: `${month}-${day} ${hours}:${minutes}:${seconds}`
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 查找房主昵称
|
|||
|
|
const hostPlayer = players.find(p => p.is_host)
|
|||
|
|
//const hostNickname = hostPlayer ? hostPlayer.nickname : '房主'
|
|||
|
|
const navTitle = `${room.room_name}`
|
|||
|
|
|
|||
|
|
// 获取除自己外的其他玩家
|
|||
|
|
const otherPlayers = players.filter(p => p.player_id !== this.data.userInfo.id)
|
|||
|
|
|
|||
|
|
// 更新快捷输入设置
|
|||
|
|
const quickInputs = room.quick_settings || [10, 20, 30]
|
|||
|
|
|
|||
|
|
// 格式化玩家积分显示
|
|||
|
|
const formattedPlayers = players.map(p => ({
|
|||
|
|
...p,
|
|||
|
|
formatted_score: formatScore(p.total_win_loss)
|
|||
|
|
}))
|
|||
|
|
|
|||
|
|
this.setData({
|
|||
|
|
session: room,
|
|||
|
|
players: formattedPlayers,
|
|||
|
|
records: formattedRecords,
|
|||
|
|
logs: formattedLogs,
|
|||
|
|
isInSession: user_info.is_in_session,
|
|||
|
|
isHost: user_info.is_host,
|
|||
|
|
createTime,
|
|||
|
|
totalRounds: room.total_rounds || 0,
|
|||
|
|
navTitle,
|
|||
|
|
otherPlayers,
|
|||
|
|
quickInputs,
|
|||
|
|
tableFee: room.table_fee || 0
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
if (!silent) {
|
|||
|
|
wx.hideLoading()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
// 静默刷新时忽略错误(网络问题等),避免打扰用户
|
|||
|
|
if (!silent) {
|
|||
|
|
wx.hideLoading()
|
|||
|
|
wx.showToast({
|
|||
|
|
title: error.message || '加载失败',
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
} else {
|
|||
|
|
// 静默刷新失败时,仅在控制台记录错误,不显示给用户
|
|||
|
|
console.warn('自动刷新失败:', error)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 开始游戏
|
|||
|
|
async startGame() {
|
|||
|
|
if (this.data.players.length < 2) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '至少需要2人才能开始',
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
wx.showModal({
|
|||
|
|
title: '开始游戏',
|
|||
|
|
content: `确定要开始游戏吗?当前${this.data.players.length}人`,
|
|||
|
|
success: async (res) => {
|
|||
|
|
if (res.confirm) {
|
|||
|
|
wx.showLoading({ title: '启动中...' })
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
await request.post(`/rooms/${this.data.sessionId}/start`)
|
|||
|
|
|
|||
|
|
wx.hideLoading()
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '游戏已开始',
|
|||
|
|
icon: 'success'
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 刷新页面
|
|||
|
|
this.loadSessionDetail()
|
|||
|
|
|
|||
|
|
// 跳转到记账页
|
|||
|
|
setTimeout(() => {
|
|||
|
|
this.goToPlay()
|
|||
|
|
}, 1500)
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
wx.hideLoading()
|
|||
|
|
wx.showToast({
|
|||
|
|
title: error.message || '启动失败',
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 暂停游戏
|
|||
|
|
async 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'
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
this.loadSessionDetail()
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: error.message || '操作失败',
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 结束牌局
|
|||
|
|
confirmEndSession() {
|
|||
|
|
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'
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
this.loadSessionDetail()
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
wx.hideLoading()
|
|||
|
|
wx.showToast({
|
|||
|
|
title: error.message || '操作失败',
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 返回上一页(不离开房间)
|
|||
|
|
confirmLeaveSession() {
|
|||
|
|
wx.navigateBack()
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 加入牌局
|
|||
|
|
async joinSession() {
|
|||
|
|
wx.showLoading({ title: '加入中...' })
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
await request.post('/rooms/join', {
|
|||
|
|
session_id: this.data.sessionId
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
wx.hideLoading()
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '加入成功',
|
|||
|
|
icon: 'success'
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 刷新页面
|
|||
|
|
this.loadSessionDetail()
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
wx.hideLoading()
|
|||
|
|
wx.showToast({
|
|||
|
|
title: error.message || '加入失败',
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 显示分享弹窗
|
|||
|
|
async shareSession() {
|
|||
|
|
// 打开分享弹窗时暂停自动刷新
|
|||
|
|
this.stopAutoRefresh()
|
|||
|
|
this.setData({
|
|||
|
|
showShareModal: true,
|
|||
|
|
qrcodeUrl: '' // 重置二维码
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 生成二维码
|
|||
|
|
await this.generateQRCode()
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 生成二维码
|
|||
|
|
async generateQRCode() {
|
|||
|
|
if (!this.data.session || !this.data.session.invite_code) {
|
|||
|
|
console.error('无法生成二维码: session或invite_code不存在', this.data.session)
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '房间信息不完整',
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const app = getApp()
|
|||
|
|
const scene = `qrcode&code=${this.data.session.invite_code}`
|
|||
|
|
|
|||
|
|
console.log('开始生成二维码, scene:', scene, 'invite_code:', this.data.session.invite_code)
|
|||
|
|
|
|||
|
|
wx.showLoading({ title: '生成二维码...' })
|
|||
|
|
|
|||
|
|
const qrcodeBuffer = await app.getQrcode(scene)
|
|||
|
|
|
|||
|
|
console.log('二维码数据返回:', qrcodeBuffer)
|
|||
|
|
|
|||
|
|
wx.hideLoading()
|
|||
|
|
|
|||
|
|
// 检查返回数据类型
|
|||
|
|
if (!qrcodeBuffer) {
|
|||
|
|
throw new Error('二维码数据为空')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 将arraybuffer转换为base64
|
|||
|
|
const base64 = wx.arrayBufferToBase64(qrcodeBuffer)
|
|||
|
|
const qrcodeUrl = `data:image/png;base64,${base64}`
|
|||
|
|
|
|||
|
|
console.log('base64长度:', base64.length)
|
|||
|
|
|
|||
|
|
this.setData({
|
|||
|
|
qrcodeUrl: qrcodeUrl
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
wx.hideLoading()
|
|||
|
|
console.error('生成二维码失败:', error)
|
|||
|
|
wx.showToast({
|
|||
|
|
title: error.message || '二维码生成失败',
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 关闭分享弹窗
|
|||
|
|
closeShareModal() {
|
|||
|
|
this.setData({
|
|||
|
|
showShareModal: false
|
|||
|
|
})
|
|||
|
|
// 关闭分享弹窗后恢复自动刷新
|
|||
|
|
this.startAutoRefresh()
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 复制邀请码
|
|||
|
|
copyInviteCode() {
|
|||
|
|
wx.setClipboardData({
|
|||
|
|
data: this.data.session.invite_code,
|
|||
|
|
success: () => {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '已复制邀请码',
|
|||
|
|
icon: 'success'
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 进入记账页
|
|||
|
|
goToPlay() {
|
|||
|
|
wx.navigateTo({
|
|||
|
|
url: `/pages/game/settlement/settlement?id=${this.data.sessionId}`
|
|||
|
|
})
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 查看统计
|
|||
|
|
goToStats() {
|
|||
|
|
wx.navigateTo({
|
|||
|
|
url: `/pages/stats/session/session?id=${this.data.sessionId}`
|
|||
|
|
})
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 查看所有战绩
|
|||
|
|
goToRecords() {
|
|||
|
|
wx.navigateTo({
|
|||
|
|
url: `/pages/game/records/records?id=${this.data.sessionId}`
|
|||
|
|
})
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 再来一局
|
|||
|
|
createNewSession() {
|
|||
|
|
wx.showModal({
|
|||
|
|
title: '再来一局',
|
|||
|
|
content: '是否使用相同设置创建新牌局?',
|
|||
|
|
success: (res) => {
|
|||
|
|
if (res.confirm) {
|
|||
|
|
wx.navigateTo({
|
|||
|
|
url: `/pages/game/create/create?copy=${this.data.sessionId}`
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 下拉刷新
|
|||
|
|
onPullDownRefresh() {
|
|||
|
|
this.loadSessionDetail().then(() => {
|
|||
|
|
wx.stopPullDownRefresh()
|
|||
|
|
})
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 点击玩家卡片
|
|||
|
|
onPlayerTap(e) {
|
|||
|
|
const player = e.currentTarget.dataset.player
|
|||
|
|
// 打开玩家弹窗时暂停自动刷新
|
|||
|
|
this.stopAutoRefresh()
|
|||
|
|
this.setData({
|
|||
|
|
selectedPlayer: player,
|
|||
|
|
showPlayerModal: true,
|
|||
|
|
scoreInput: ''
|
|||
|
|
})
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 关闭玩家弹窗
|
|||
|
|
closePlayerModal() {
|
|||
|
|
this.setData({
|
|||
|
|
showPlayerModal: false,
|
|||
|
|
selectedPlayer: null,
|
|||
|
|
scoreInput: ''
|
|||
|
|
})
|
|||
|
|
// 关闭玩家弹窗后恢复自动刷新
|
|||
|
|
this.startAutoRefresh()
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 分数输入
|
|||
|
|
onScoreInput(e) {
|
|||
|
|
this.setData({
|
|||
|
|
scoreInput: e.detail.value
|
|||
|
|
})
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 快捷输入
|
|||
|
|
quickInput(e) {
|
|||
|
|
const value = e.currentTarget.dataset.value
|
|||
|
|
this.setData({
|
|||
|
|
scoreInput: value
|
|||
|
|
})
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 提交分数
|
|||
|
|
async submitScore() {
|
|||
|
|
const score = parseFloat(this.data.scoreInput)
|
|||
|
|
|
|||
|
|
// 验证金额格式(正数,最多2位小数)
|
|||
|
|
if (isNaN(score) || score <= 0) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '请输入有效金额',
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 验证最大值99999
|
|||
|
|
if (score > 99999) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '金额不能超过99999',
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 验证最多2位小数
|
|||
|
|
if (!/^\d+(\.\d{1,2})?$/.test(this.data.scoreInput)) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '金额最多保留2位小数',
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
wx.showLoading({ title: '提交中...' })
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 调用记分转账API(当前用户输给选中的玩家)
|
|||
|
|
await request.post('/records/transfer', {
|
|||
|
|
room_id: this.data.sessionId,
|
|||
|
|
to_player_id: this.data.selectedPlayer.player_id,
|
|||
|
|
amount: score
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
wx.hideLoading()
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '记分成功',
|
|||
|
|
icon: 'success'
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 播放TTS语音
|
|||
|
|
const ttsText = `${this.data.userInfo.nickname}付给${this.data.selectedPlayer.nickname}${score}元`
|
|||
|
|
this.playTTS(ttsText)
|
|||
|
|
|
|||
|
|
this.closePlayerModal()
|
|||
|
|
this.loadSessionDetail()
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
wx.hideLoading()
|
|||
|
|
wx.showToast({
|
|||
|
|
title: error.message || '提交失败',
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
|
|||
|
|
// 显示支出弹窗
|
|||
|
|
showExpenseModal() {
|
|||
|
|
// 打开支出弹窗时暂停自动刷新
|
|||
|
|
this.stopAutoRefresh()
|
|||
|
|
this.setData({
|
|||
|
|
showExpenseModal: true,
|
|||
|
|
expenseInputs: {}
|
|||
|
|
})
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 关闭支出弹窗
|
|||
|
|
closeExpenseModal() {
|
|||
|
|
this.setData({
|
|||
|
|
showExpenseModal: false,
|
|||
|
|
expenseInputs: {}
|
|||
|
|
})
|
|||
|
|
// 关闭支出弹窗后恢复自动刷新
|
|||
|
|
this.startAutoRefresh()
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 支出金额输入
|
|||
|
|
onExpenseInput(e) {
|
|||
|
|
const index = e.currentTarget.dataset.index
|
|||
|
|
const value = e.detail.value
|
|||
|
|
const expenseInputs = {...this.data.expenseInputs}
|
|||
|
|
expenseInputs[index] = value
|
|||
|
|
this.setData({ expenseInputs })
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 提交支出
|
|||
|
|
async submitExpense() {
|
|||
|
|
const expenses = []
|
|||
|
|
Object.keys(this.data.expenseInputs).forEach(index => {
|
|||
|
|
const amount = parseFloat(this.data.expenseInputs[index])
|
|||
|
|
if (!isNaN(amount) && amount > 0) {
|
|||
|
|
// 验证最大值99999
|
|||
|
|
if (amount > 99999) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '金额不能超过99999',
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 验证最多2位小数
|
|||
|
|
if (!/^\d+(\.\d{1,2})?$/.test(this.data.expenseInputs[index])) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '金额最多保留2位小数',
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
expenses.push({
|
|||
|
|
playerId: this.data.otherPlayers[index].player_id,
|
|||
|
|
amount: amount
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
if (expenses.length === 0) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '请至少输入一项支出',
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
wx.showLoading({ title: '提交中...' })
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 串行调用记分转账API(避免数据库死锁)
|
|||
|
|
let successCount = 0
|
|||
|
|
const successExpenses = []
|
|||
|
|
for (const expense of expenses) {
|
|||
|
|
try {
|
|||
|
|
await request.post('/records/transfer', {
|
|||
|
|
room_id: this.data.sessionId,
|
|||
|
|
to_player_id: expense.playerId,
|
|||
|
|
amount: expense.amount
|
|||
|
|
})
|
|||
|
|
successCount++
|
|||
|
|
successExpenses.push(expense)
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('记分失败:', expense, error)
|
|||
|
|
// 继续执行其他记分,不中断
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
wx.hideLoading()
|
|||
|
|
|
|||
|
|
if (successCount === expenses.length) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: `成功记录${successCount}笔支出`,
|
|||
|
|
icon: 'success'
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 播放批量支出语音
|
|||
|
|
if (successExpenses.length > 0) {
|
|||
|
|
const ttsTexts = successExpenses.map(exp => {
|
|||
|
|
const player = this.data.otherPlayers.find(p => p.player_id === exp.playerId)
|
|||
|
|
return `${this.data.userInfo.nickname}付给${player.nickname}${exp.amount}元`
|
|||
|
|
})
|
|||
|
|
// 播放第一条(可以根据需要调整为播放所有或汇总)
|
|||
|
|
this.playTTS(ttsTexts.join(','))
|
|||
|
|
}
|
|||
|
|
} else if (successCount > 0) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: `成功${successCount}笔,失败${expenses.length - successCount}笔`,
|
|||
|
|
icon: 'none',
|
|||
|
|
duration: 3000
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 播放成功的语音
|
|||
|
|
if (successExpenses.length > 0) {
|
|||
|
|
const ttsTexts = successExpenses.map(exp => {
|
|||
|
|
const player = this.data.otherPlayers.find(p => p.player_id === exp.playerId)
|
|||
|
|
return `${this.data.userInfo.nickname}付给${player.nickname}${exp.amount}元`
|
|||
|
|
})
|
|||
|
|
this.playTTS(ttsTexts.join(','))
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '所有记分都失败了',
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this.closeExpenseModal()
|
|||
|
|
this.loadSessionDetail()
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
wx.hideLoading()
|
|||
|
|
wx.showToast({
|
|||
|
|
title: error.message || '提交失败',
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 显示设置弹窗
|
|||
|
|
showSettingsModal() {
|
|||
|
|
// 打开设置弹窗时暂停自动刷新
|
|||
|
|
this.stopAutoRefresh()
|
|||
|
|
this.setData({
|
|||
|
|
showSettingsModal: true
|
|||
|
|
})
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 关闭设置弹窗
|
|||
|
|
closeSettingsModal() {
|
|||
|
|
this.setData({
|
|||
|
|
showSettingsModal: false
|
|||
|
|
})
|
|||
|
|
// 关闭设置弹窗后恢复自动刷新
|
|||
|
|
this.startAutoRefresh()
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 桌位费输入
|
|||
|
|
onTableFeeInput(e) {
|
|||
|
|
this.setData({
|
|||
|
|
tableFee: e.detail.value
|
|||
|
|
})
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 语音播报开关切换
|
|||
|
|
onTtsToggle(e) {
|
|||
|
|
const ttsEnabled = e.detail.value
|
|||
|
|
this.setData({ ttsEnabled })
|
|||
|
|
// 保存到本地存储
|
|||
|
|
wx.setStorageSync('ttsEnabled', ttsEnabled)
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// TTS语音播放
|
|||
|
|
async playTTS(text) {
|
|||
|
|
// 如果语音播报关闭,直接返回
|
|||
|
|
if (!this.data.ttsEnabled) {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const result = await request.post('/tts/convert', { text })
|
|||
|
|
|
|||
|
|
if (result.audioUrl) {
|
|||
|
|
// 使用音频URL直接播放
|
|||
|
|
const innerAudioContext = wx.createInnerAudioContext()
|
|||
|
|
innerAudioContext.src = `https://ca.miniappapi.com/mp${result.audioUrl}`
|
|||
|
|
innerAudioContext.play()
|
|||
|
|
|
|||
|
|
// 播放完成后销毁音频上下文
|
|||
|
|
innerAudioContext.onEnded(() => {
|
|||
|
|
innerAudioContext.destroy()
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 播放失败处理
|
|||
|
|
innerAudioContext.onError((error) => {
|
|||
|
|
console.error('音频播放失败:', error)
|
|||
|
|
innerAudioContext.destroy()
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('TTS转换失败:', error)
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 快捷输入值修改
|
|||
|
|
onQuickInputChange(e) {
|
|||
|
|
const index = e.currentTarget.dataset.index
|
|||
|
|
const value = e.detail.value
|
|||
|
|
const quickInputs = [...this.data.quickInputs]
|
|||
|
|
quickInputs[index] = value
|
|||
|
|
this.setData({ quickInputs })
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 删除快捷输入
|
|||
|
|
deleteQuickInput(e) {
|
|||
|
|
const index = e.currentTarget.dataset.index
|
|||
|
|
const quickInputs = [...this.data.quickInputs]
|
|||
|
|
quickInputs.splice(index, 1)
|
|||
|
|
this.setData({ quickInputs })
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 添加快捷输入
|
|||
|
|
addQuickInput() {
|
|||
|
|
const quickInputs = [...this.data.quickInputs]
|
|||
|
|
quickInputs.push('')
|
|||
|
|
this.setData({ quickInputs })
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 保存设置
|
|||
|
|
async saveSettings() {
|
|||
|
|
wx.showLoading({ title: '保存中...' })
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
await request.put(`/rooms/${this.data.sessionId}/settings`, {
|
|||
|
|
table_fee: parseFloat(this.data.tableFee) || 0,
|
|||
|
|
quick_settings: this.data.quickInputs.map(v => parseFloat(v) || 0)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
wx.hideLoading()
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '保存成功',
|
|||
|
|
icon: 'success'
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
this.closeSettingsModal()
|
|||
|
|
this.loadSessionDetail(true)
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
wx.hideLoading()
|
|||
|
|
wx.showToast({
|
|||
|
|
title: error.message || '保存失败',
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 分享给朋友
|
|||
|
|
onShareAppMessage() {
|
|||
|
|
return {
|
|||
|
|
title: `邀请你加入牌局:${this.data.session.room_name}`,
|
|||
|
|
path: `/pages/game/join/join?code=${this.data.session.invite_code}`,
|
|||
|
|
imageUrl: '/images/share-bg.png'
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
})
|