Files
-----/scoring/pages/index/singleplay/singleplay.vue
2025-12-09 10:54:56 +08:00

1098 lines
30 KiB
Vue
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.

<template>
<view class="singleplay">
<!-- 顶部功能区 -->
<view class="top-functions">
<view class="function-btn" @click="addPlayer">
<image src="/static/add.png" class="btn-icon" mode="aspectFill"></image>
<text>添加玩家</text>
</view>
<view class="function-btn" @click="transferScorer">
<image src="/static/transfer.png" class="btn-icon" mode="aspectFill"></image>
<text>转让计分员</text>
</view>
<view class="switch-group">
<text>语音播报</text>
<switch :checked="voiceBroadcast" @change="voiceBroadcast = $event.detail.value" color="#007aff" />
</view>
<view class="switch-group">
<text>台板</text>
<switch :checked="tableMode" @change="tableMode = $event.detail.value" color="#007aff" />
</view>
</view>
<!-- 对局记录区域 -->
<view class="game-record">
<view class="record-title">
对局记录
<text class="hint">点击对局分数进行修改</text>
</view>
<!-- 用户信息区域 -->
<view class="user-section">
<text class="user-hint">点击自己头像编辑用户头像和昵称~</text>
<view class="user-info" @click="editUserInfo">
<image class="user-avatar" :src="currentUser.avatar" mode="aspectFill"></image>
<view class="user-details">
<view class="user-name">
{{ currentUser.name }}
<text class="self-tag">自己</text>
</view>
</view>
</view>
</view>
<!-- 玩家列表 -->
<view class="players-section">
<view class="section-header">
<text class="header-title">玩家</text>
<text class="player-count">({{ players.length }})</text>
</view>
<!-- 玩家表格表头在左侧 -->
<view class="players-table-container vertical">
<view class="players-table vertical">
<!-- 表头行 -->
<view class="table-row header-row">
<!-- 固定表头玩家 -->
<view class="header-cell player-column">玩家</view>
<!-- 每个玩家作为一列 -->
<view v-for="(player, index) in displayPlayers" :key="player.id" class="data-cell">
<view class="player-info">
<image :src="player.avatar" mode="aspectFill"></image>
<text>{{ player.name }}</text>
</view>
</view>
</view>
<!-- 总分行 -->
<view class="table-row">
<view class="header-cell score-column">总分</view>
<view v-for="(player, index) in displayPlayers" :key="player.id" class="data-cell score-display" >
{{ player.score }}
</view>
</view>
<!-- 每局分数行 -->
<view v-for="(round, roundIndex) in rounds" :key="round" class="table-row">
<view class="header-cell score-column">{{ round }}</view>
<view @click="editScore(index)" v-for="(player, playerIndex) in displayPlayers" :key="player.id" class="data-cell score-display">
{{ player.roundScores[roundIndex] || 0 }}
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 底部操作按钮 -->
<view class="bottom-buttons">
<button class="start-btn" type="primary" @click="startScoring">开局计分</button>
<button class="settle-btn" type="default" @click="settleRoom">结算房间</button>
</view>
</view>
</template>
<script setup>
import { ref, computed, onMounted, onUnmounted, toRaw } from 'vue'
import { GET,POST,PUT } from '../../../utils/request'
import { BASE_URL } from '../../../utils/CommonValues.js';
const roomuser=ref({
roomId: '',
userId: '',
score:'',
playerType:'0',
nickName:'',
avatar:''
})
const res=ref(null)
const scoreuser=ref({
openId: '',
nickName: '',
avatar: 'scoring\static\avatar14.png'
})
const room=ref({
createUser:'1',
odds:1,
room_status:0,
bossId:'1',
roomName:'',
})
const detailscore=ref({
roomId: '',
userId: '',
score:'',
gameTime:'',
detailType:'0',
})
const getuserinfo =()=>{
// 错误1GET参数后多了多余逗号 → 已删除
GET('/score/info/list',null).then(res=>{
console.log('获取用户信息成功:', res.data)
// 检查API返回数据结构
if(res.data && res.data.code === 200 && Array.isArray(res.data.rows)) {
const userList = res.data.rows;
// 清空现有的玩家列表(保留自己)
const selfPlayer = players.value.find(p => p.id === 'self');
players.value = selfPlayer ? [selfPlayer] : [];
// 将API返回的用户信息添加到玩家列表
userList.forEach(user => {
if(user.nickName) {
// 处理头像URL如果是相对路径则与BASE_URL拼接
let avatarUrl = user.avatars;
if (avatarUrl) {
// 检查是否是相对路径(以/开头)
if (avatarUrl.startsWith('/')) {
avatarUrl = `${BASE_URL}${avatarUrl}`;
} else if (!avatarUrl.startsWith('http://') && !avatarUrl.startsWith('https://')) {
// 不是绝对URL也不是以/开头的相对路径,拼接完整路径
avatarUrl = `${BASE_URL}/${avatarUrl}`;
}
} else {
// 如果没有头像则使用默认头像
avatarUrl = '/static/avatar14.png';
}
const newPlayer = {
id: user.userId, // 使用后端返回的userId作为玩家id
name: user.nickName, // 使用API返回的nickName作为玩家名称
avatar: avatarUrl, // 使用处理后的头像URL
score: 0,
roundScores: [],
roomId: roomId.value // 当前房间ID
};
players.value.push(newPlayer);
}
});
console.log('玩家列表更新完成:', players.value);
uni.showToast({
title: `成功添加${userList.length}位玩家`,
icon: 'success'
});
} else {
console.error('API返回数据结构异常:', res.data);
uni.showToast({
title: '获取玩家信息失败,数据结构异常',
icon: 'none'
});
}
}).catch(err=>{
console.error('获取用户信息失败:', err);
// 使用request.js中提供的友好错误信息
const errorMsg = err.userFriendlyMsg || '获取玩家信息失败';
uni.showToast({
title: errorMsg,
icon: 'none',
duration: 2000
});
})
}
const UserId=ref('')
const postuserinfo = () => {
POST('/score/info', scoreuser.value).then(r => {
console.log('用户信息上传成功:', r);
UserId.value = r.data.data.userId;
console.log('用户ID:', UserId.value);
room.value.createUser = UserId.value;
room.value.bossId = UserId.value;
postroom();
// 如果有临时玩家ID更新到players数组中
if (tempPlayerId.value) {
const playerIndex = players.value.findIndex(p => p.id === tempPlayerId.value)
if (playerIndex !== -1) {
// 更新玩家的id为真实的用户ID
players.value[playerIndex].id = UserId.value
// 清空临时ID
tempPlayerId.value = ''
}
}
uni.showToast({
title: '用户信息上传成功',
icon: 'success'
});
}).catch(err => {
console.error('用户信息上传失败:', err);
uni.showToast({
title: '用户信息上传失败',
icon: 'none'
});
// 清空临时ID避免影响后续操作
tempPlayerId.value = ''
});
}
// 全局房间ID变量从本地存储恢复
const roomId=ref(uni.getStorageSync('roomId') || '')
const postroom = () => {
POST('/score/room', room.value).then(r => {
console.log('房间信息上传成功:', r);
// 检查API响应结构
console.log('API响应数据:', r.data);
const receivedRoomId = r.data.data.roomId || '';
console.log('房间ID:', receivedRoomId);
roomId.value = receivedRoomId;
// 保存roomId到本地存储
uni.setStorageSync('roomId', receivedRoomId);
// 更新所有玩家的roomId
players.value.forEach(player => {
player.roomId = receivedRoomId
})
postroomuser();
// 为每个玩家调用postroomdetail即postdetailscore函数提交玩家与房间的关联信息
players.value.forEach(player => {
// 创建玩家与房间关联的详细数据
const playerRoomDetail = {
roomId: receivedRoomId,
userId: player.id,
userName: player.name,
userAvatar: player.avatar,
score: player.score
};
// 调用postdetailscore函数提交数据
postdetailscore(playerRoomDetail);
console.log('玩家与房间关联信息上传成功:', playerRoomDetail);
console.log('房间ID:', roomId.value);
});
uni.showToast({
title: '房间信息上传成功',
icon: 'success'
});
}).catch(err => {
console.error('房间信息上传失败:', err);
uni.showToast({
title: '房间信息上传失败',
icon: 'none'
});
});
};
const postroomuser = () => {
roomuser.value.roomId = roomId.value;
roomuser.value.userId = UserId.value;
roomuser.value.nickName = scoreuser.value.nickName;
POST('/score/player', roomuser.value).then(r => {
console.log('房间用户信息上传成功:', r);
const roomUserId = r.data.data.roomUserId;
console.log('获取到的roomUserId:', roomUserId);
uni.setStorageSync('roomUserId', roomUserId);
roomuser.value.roomUserId = roomUserId;
// 同时更新putscore对象的roomUserId属性确保数据一致性
putscore.value.roomUserId = Number(roomUserId);
console.log('更新后的putscore对象:', putscore.value);
uni.showToast({
title: '房间用户信息上传成功',
icon: 'success'
});
}).catch(err => {
console.error('房间用户信息上传失败:', err);
uni.showToast({
title: '房间用户信息上传失败',
icon: 'none'
});
});
}
const postdetailscore = (scoreData) => {
const dataToSubmit = scoreData || detailscore.value;
console.log('提交数据:', dataToSubmit);
console.log('房间ID:', roomId.value);
if (!dataToSubmit.roomId) {
dataToSubmit.roomId = roomId.value;
}
POST('/score/roomdetail', dataToSubmit).then(r => {
console.log('房间用户详细信息上传成功:', r);
if (!scoreData || dataToSubmit.userId === UserId.value || dataToSubmit.userId === 'self') {
uni.showToast({
title: '房间用户详细信息上传成功',
icon: 'success'
});
}
}).catch(err => {
console.error('房间用户详细信息上传失败:', err);
if (!scoreData || dataToSubmit.userId === UserId.value || dataToSubmit.userId === 'self') {
uni.showToast({
title: '房间用户详细信息上传失败',
icon: 'none'
});
}
});
}
const putscore=ref({
roomUserId:0,
allscore:0
})
const putroomuser = () => {
console.log('开始执行putroomuser函数');
const roomUserId = uni.getStorageSync('roomUserId');
console.log('从本地存储获取的roomUserId:', roomUserId);
// 从players数组中获取当前用户的总分
console.log('当前UserId.value:', UserId.value);
console.log('players数组内容:', players.value);
const currentPlayer = players.value.find(p => p.id === UserId.value);
if (currentPlayer) {
putscore.value.allscore = currentPlayer.score;
console.log('获取到的当前用户总分:', currentPlayer.score);
} else {
console.error('未找到当前用户的总分数据');
}
// 设置roomUserId并转换为数字类型
putscore.value.roomUserId = Number(roomUserId);
console.log('准备发送的putscore数据:', putscore.value);
PUT('/score/player', putscore.value).then(r => {
console.log('房间用户信息上传成功响应:', r);
console.log('响应数据:', r.data);
uni.showToast({
title: '房间用户信息上传成功',
icon: 'success'
});
}).catch(err => {
console.error('房间用户信息上传失败:', err);
console.error('错误详情:', err.errMsg || err);
uni.showToast({
title: '房间用户信息上传失败',
icon: 'none'
});
});
}
// 状态管理
const voiceBroadcast = ref(false)
const tableMode = ref(false)
// 当前用户信息 - 从本地存储获取
const storedUserInfo = uni.getStorageSync('userInfo');
const currentUser = ref({
id:self,
name: storedUserInfo?.name||'acd' ,
avatar: storedUserInfo?.avatar ||'/static/logo.png' })
// 局数信息
const rounds = ref([]) // 存储每局信息,每个元素是局数编号
// 玩家列表(初始包含自己)
const players = ref([
{
id:self,
name: currentUser.value.name ||'acd',
avatar: currentUser.value.avatar||'/static/logo.png',
score: 0,
roundScores: [], // 存储每局的分数
roomId: '' // 当前房间ID
}
])
// 台板玩家对象
const tablePlayer = {
id: 'table',
name: '台板',
avatar: '/static/robot.png',
score: 0,
roundScores: [] // 存储每局的分数
}
// 计算属性:动态返回包含或不包含台板玩家的列表
const displayPlayers = computed(() => {
if (tableMode.value) {
// 检查台板玩家是否已存在,避免重复添加
const hasTablePlayer = players.value.some(player => player.id === 'table')
if (hasTablePlayer) {
return players.value
}
// 添加台板玩家
return [...players.value, tablePlayer]
} else {
// 移除台板玩家
return players.value.filter(player => player.id !== 'table')
}
})
// 保存当前正在添加的玩家临时ID
const tempPlayerId = ref('')
// 添加玩家
const addPlayer = () => {
console.log('添加玩家')
// 生成默认玩家名
const defaultPlayerName = '请输入玩家名称'
// 弹出对话框让用户输入玩家名称
uni.showModal({
title: '添加玩家',
editable: true,
placeholderText: defaultPlayerName,
// #ifdef MP-WEIXIN
confirmText: '添加',
cancelText: '取消',
autofocus: true, // 微信小程序下自动聚焦输入框
// #endif
success: (res) => {
if (res.confirm) {
// 获取用户输入的名称,如果为空则使用默认名称
const playerName = res.content && res.content.trim() !== '' ? res.content.trim() : defaultPlayerName
// 创建新玩家
const newPlayer = {
id: `player_${Date.now()}`,
name: playerName,
avatar: '/static/avatar14.png',
score: 0,
roundScores: [] // 存储每局的分数
}
scoreuser.value.openId=newPlayer.id
scoreuser.value.nickName=newPlayer.name
// 保存临时ID用于后续更新真实ID
tempPlayerId.value = newPlayer.id
postuserinfo()
// 添加到玩家列表
players.value.push(newPlayer)
// 语音提示或普通提示(如果开启)
if (voiceBroadcast.value) {
uni.showToast({
title: `已添加玩家 ${newPlayer.name}`,
icon: 'none'
})
// 这里可以添加语音播报逻辑
} else {
uni.showToast({
title: `已添加玩家 ${newPlayer.name}`,
icon: 'none'
})
}
}
},
fail: (err) => {
console.error('添加玩家失败:', err)
}
})
}
// 转让计分员
const transferScorer = () => {
console.log('转让计分员')
// 显示提示对话框
uni.showModal({
title: '提示',
content: '房间内暂无扫码或分享加入房间的玩家,无法转让计分员。',
showCancel: false,
// #ifdef MP-WEIXIN
confirmText: '确定',
// #endif
success: (res) => {
if (res.confirm) {
console.log('用户确认提示')
}
},
fail: (err) => {
console.error('显示提示失败:', err)
}
})
}
// 编辑用户信息 - 跳转到change页面
const editUserInfo = () => {
console.log('跳转到用户信息编辑页面')
// 跳转到change页面并传递当前用户信息
uni.navigateTo({
url: `/pages/index/singleplay/change?userData=${encodeURIComponent(JSON.stringify(currentUser.value))}`
})
}
// 更新用户数据 - 从change页面返回时调用
const updateUserData = (updatedUser) => {
console.log('更新用户数据', updatedUser)
// 更新当前用户信息
currentUser.value = { ...updatedUser }
// 更新玩家列表中的用户信息
const selfPlayerIndex = players.value.findIndex(p => p.id === 'self')
if (selfPlayerIndex !== -1) {
players.value[selfPlayerIndex] = {
...players.value[selfPlayerIndex],
name: updatedUser.name,
avatar: updatedUser.avatar
}
}
// 将更新后的用户信息保存到本地存储
uni.setStorageSync('userInfo', updatedUser)
uni.setStorageSync('currentUserInfo', updatedUser)
}
// 编辑分数 - 点击分数时调用
const editScore = (playerIndex) => {
console.log('编辑分数,玩家索引:', playerIndex)
// 获取当前选中的玩家
const selectedPlayer = displayPlayers.value[playerIndex]
// 检查是否有局数记录
if (rounds.value.length === 0) {
uni.showToast({
title: '暂无局数记录,无法编辑分数',
icon: 'none'
})
return
}
// 获取当前局数
const currentRound = rounds.value.length
// 准备要传递的玩家数据
const playersToPass = [...displayPlayers.value]
// 保存到临时存储,用于页面间数据传递
uni.setStorageSync('currentPlayers', JSON.stringify(playersToPass))
// 保存当前局数
uni.setStorageSync('currentRound', currentRound)
// 保存选中的玩家索引
uni.setStorageSync('selectedPlayerIndex', playerIndex)
// 跳转到计分页面进行分数编辑
uni.navigateTo({
url: '/pages/index/singleplay/scoring'
})
}
// 开局计分
const startScoring = () => {
console.log('开局计分')
room.value.roomStatus=1
// 检查是否有足够的玩家至少需要2个玩家才能开始计分
if (displayPlayers.value.length < 2) {
uni.showToast({
title: '玩家数不超过两个人时无法点击进行开始计分',
icon: 'none'
})
return
}
// 新增一局
const newRound = rounds.value.length + 1
rounds.value.push(newRound)
// 准备要传递的玩家数据
const playersToPass = [...displayPlayers.value]
// 保存到临时存储,用于页面间数据传递
uni.setStorageSync('currentPlayers', JSON.stringify(playersToPass))
// 保存当前局数
uni.setStorageSync('currentRound', newRound)
// 跳转到计分页面
uni.navigateTo({
url: '/pages/index/singleplay/scoring'
})
}
// 结算房间
const settleRoom = () => {
console.log('结算房间')
// 显示结算结果使用displayPlayers确保包含台板玩家
let result = '结算结果:\n'
displayPlayers.value.forEach(player => {
result += `${player.name}: ${player.score}\n`
})
uni.showModal({
title: '结算',
content: result,
confirmText: '保存记录',
cancelText: '关闭',
success: (res) => {
if (res.confirm) {
// 这里可以保存记录到数据库
uni.showToast({
title: '记录已保存',
icon: 'success'
})
}
}
})
}
const number = ref('')
// 生命周期
onMounted(() => {
console.log('单人模式页面加载完成')
// 可以在这里初始化数据或加载用户信息
getuserinfo();
// 生成四位数字
number.value = Math.floor(1000 + Math.random() * 9000).toString()
// 动态设置导航栏标题
const pageTitle = `单人 - ${number.value}号房间`;
uni.setNavigationBarTitle({
title: pageTitle
})
room.value.roomName = pageTitle
// 更新当前用户的roomId
const selfPlayer = players.value.find(p => p.id === 'self')
if (selfPlayer) {
selfPlayer.roomId = roomId.value
}
// 监听页面返回事件,用于接收从计分页面传回的更新后的分数
const updateListener = () => {
// 尝试获取更新后的玩家数据
const updatedPlayers = uni.getStorageSync('updatedPlayers')
if (updatedPlayers) {
try {
const parsedPlayers = JSON.parse(updatedPlayers)
console.log('收到更新的玩家数据:', parsedPlayers)
// 获取当前局数
const currentRound = uni.getStorageSync('currentRound') || 1
const roundIndex = currentRound - 1
// 更新玩家列表中的分数
parsedPlayers.forEach(updatedPlayer => {
// 查找对应的玩家
const playerIndex = players.value.findIndex(p => p.id === updatedPlayer.id)
if (playerIndex !== -1) {
if (players.value[playerIndex].roundScores.length <= roundIndex) {
players.value[playerIndex].roundScores.length = roundIndex + 1
}
// 保存当前局的分数
players.value[playerIndex].roundScores[roundIndex] = updatedPlayer.score
// 计算总分:所有局分数的总和
players.value[playerIndex].score = players.value[playerIndex].roundScores.reduce((sum, score) => sum + (score || 0), 0)
// 为每个玩家设置单局分数和局次,并提交到后端
// 创建临时的detailscore对象避免多个玩家数据互相覆盖
const tempDetailScore = {
roomId: players.value[playerIndex].roomId, // 当前玩家的roomId
userId: players.value[playerIndex].id, // 当前玩家ID
score: updatedPlayer.score, // 当前局的分数
gameTime: currentRound, // 当前局次
detailType: '0'
}
postdetailscore(tempDetailScore)
putroomuser()
} else if (updatedPlayer.id === 'table') {
if (tablePlayer.roundScores.length <= roundIndex) {
tablePlayer.roundScores.length = roundIndex + 1
}
tablePlayer.roundScores[roundIndex] = updatedPlayer.score
// 计算总分:所有局分数的总和
tablePlayer.score = tablePlayer.roundScores.reduce((sum, score) => sum + (score || 0), 0)
}
})
// 清除临时存储的数据
uni.removeStorageSync('updatedPlayers')
} catch (error) {
console.error('解析更新的玩家数据失败:', error)
}
}
}
// 监听页面显示事件
uni.$on('updatePlayers', updateListener)
// 监听用户数据更新事件来自change页面
const userDataListener = (updatedUser) => {
updateUserData(updatedUser)
}
uni.$on('userDataUpdated', userDataListener)
// 监听页面显示生命周期
uni.onShow(() => {
updateListener()
// 从本地存储获取最新的用户信息
try {
const storedUserInfo = uni.getStorageSync('userInfo');
if (storedUserInfo) {
console.log('从本地存储加载用户信息:', storedUserInfo);
// 更新当前用户信息
currentUser.value = { ...storedUserInfo };
// 更新玩家列表中的用户信息
const selfPlayerIndex = players.value.findIndex(p => p.id === 'self');
if (selfPlayerIndex !== -1) {
players.value[selfPlayerIndex] = {
...players.value[selfPlayerIndex],
name: storedUserInfo.name,
avatar: storedUserInfo.avatar
};
}
}
} catch (error) {
console.error('从本地存储获取用户信息失败:', error);
}
})
}); // 补充onMounted的闭合括号+花括号 → 原代码此处也缺失
onUnmounted(() => {
// 移除事件监听器
uni.$off('updatePlayers');
uni.$off('userDataUpdated');
});
</script><style lang="less" scoped>
.singleplay {
width: 750rpx;
margin: 0;
padding: 20rpx;
box-sizing: border-box;
background-color: #f8f8f8;
min-height: 100vh;
display: flex;
flex-direction: column;
/* #ifdef MP-WEIXIN */
user-select: none;
/* #endif */
}
/* 顶部功能区 */
.top-functions {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
width: 100%;
//padding: 10rpx 15rpx;
margin-bottom: 20rpx;
box-sizing: border-box;
overflow-x: auto;
.function-btn {
display: flex;
flex-direction: row;
align-items: center;
gap: 5rpx;
padding: 8rpx 8rpx;
background-color: #fff;
border-radius: 10rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
margin: 0 8rpx;
transition: all 0.3s ease;
white-space: nowrap;
flex-shrink: 0;
// 图标样式
.btn-icon {
width: 30rpx;
height: 30rpx;
}
// 文本样式
text {
font-size: 22rpx;
color: #333;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
// 微信小程序特有点击反馈
/* #ifdef MP-WEIXIN */
&:active {
opacity: 0.8;
transform: scale(0.95);
}
/* #endif */
}
.switch-group {
display: flex;
flex-direction: row;
align-items: center;
gap: 6rpx;
margin: 0 8rpx;
white-space: nowrap;
flex-shrink: 0;
text {
font-size: 22rpx;
color: #333;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
switch {
transform: scale(0.75);
}
}
}
/* 对局记录区域 */
.game-record {
background-color: #fff;
border-radius: 20rpx;
padding: 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
margin-bottom: 30rpx;
flex: 1;
overflow-y: auto;
padding-bottom: 20rpx;
}
.record-title {
font-size: 28rpx;
font-weight: bold;
color: #333;
margin-bottom: 10rpx;
.hint {
font-size: 24rpx;
font-weight: normal;
color: #666;
margin-left: 10rpx;
}
}
/* 用户信息区域 */
.user-section {
margin-bottom: 20rpx;
.user-hint {
font-size: 24rpx;
color: #fa5d5d;
margin-bottom: 15rpx;
display: block;
}
.user-info {
display: flex;
flex-direction: row;
align-items: center;
gap: 15rpx;
padding: 10rpx;
border-radius: 10rpx;
background-color: #f0f8ff;
.user-avatar {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
}
.user-details {
.user-name {
font-size: 28rpx;
color: #333;
.self-tag {
background-color: #007aff;
color: #fff;
font-size: 20rpx;
padding: 2rpx 10rpx;
border-radius: 10rpx;
margin-left: 8rpx;
}
}
}
}
}
/* 玩家列表区域 */
.players-section {
.section-header {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-bottom: 15rpx;
.header-title {
font-size: 28rpx;
font-weight: bold;
color: #333;
align-items: center;
justify-content: center;
}
.player-count {
font-size: 24rpx;
color: #666;
margin-left: 10rpx;
}
}
}
/* 玩家表格容器 - 实现横向滚动 */
.players-table-container {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
border-radius: 10rpx;
border: 1rpx solid #e0e0e0;
}
/* 垂直表格容器 - 实现垂直滚动和横向滑动 */
.players-table-container.vertical {
overflow-y: auto;
overflow-x: auto;
max-height: 500rpx;
-webkit-overflow-scrolling: touch; /* 优化移动端滚动体验 */
}
/* 玩家表格 */
.players-table {
min-width: 100%;
overflow: visible;
}
/* 垂直表格样式 */
.players-table.vertical {
.table-row {
display: flex;
flex-direction: row;
border-bottom: 1rpx solid #e0e0e0;
&:last-child {
border-bottom: none;
}
}
.header-row {
background-color: #f5f5f5;
}
.header-cell {
width: 150rpx;
flex-shrink: 0;
padding: 15rpx;
font-size: 26rpx;
font-weight: bold;
color: #333;
border-right: 1rpx solid #e0e0e0;
background-color: #f5f5f5;
display: flex;
align-items: center;
justify-content: center;
}
.player-column {
justify-content: flex-start;
align-items: center;
justify-content: center;
}
.score-column {
text-align: center;
}
.data-cell {
min-width: 120rpx;
flex-shrink: 0;
padding: 15rpx;
border-right: 1rpx solid #e0e0e0;
display: flex;
align-items: center;
justify-content: center;
/* 最后一列不需要右边框 */
&:last-child {
border-right: none;
}
}
.player-info {
display: flex;
flex-direction: column;
align-items: center;
gap: 8rpx;
width: 100%;
image {
width: 60rpx;
height: 60rpx;
border-radius: 50%;
}
text {
font-size: 24rpx;
color: #333;
text-align: center;
word-break: break-word;
}
}
.score-display {
font-size: 28rpx;
font-weight: bold;
color: #007aff;
&:active {
background-color: #f0f8ff;
}
}
}
/* 底部操作按钮 */
.bottom-buttons {
display: flex;
flex-direction: row;
gap: 20rpx;
position: sticky;
bottom: 0;
left: 0;
right: 0;
padding: 20rpx;
background-color: #fff;
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.1);
/* #ifdef MP-WEIXIN */
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
/* #endif */
.start-btn,
.settle-btn {
flex: 1;
height: 90rpx;
font-size: 32rpx;
line-height: 90rpx;
margin: 0;
}
.start-btn {
background-color: #007aff;
}
.settle-btn {
border: 1rpx solid #007aff;
color: #007aff;
}
}
/* 适配不同屏幕尺寸 */
@media screen and (max-width: 375px) {
.singleplay {
padding: 15rpx;
}
.top-functions {
gap: 8rpx;
.function-btn {
padding: 1rpx 1rpx;
text {
font-size: 22rpx;
}
}
}
}
</style>