This commit is contained in:
2025-12-16 09:32:55 +08:00
parent 5420cb599a
commit c20f085384
36 changed files with 2720 additions and 751 deletions

View File

@@ -11,38 +11,80 @@
</view>
</view>
<!-- 玩家信息表头 -->
<view class="player-header">
<view class="header-row">
<view class="header-cell header-label">玩家 ({{ players.length }})</view>
<view class="header-cell" v-for="player in players" :key="player.id">
<view class="player-header-info">
<image class="player-avatar" :src="player.avatar" mode="aspectFit"></image>
<text class="player-name">{{ player.name }}</text>
<!-- 玩家列表容器 - 添加滚动条功能 -->
<view class="player-list-container">
<!-- 水平滚动容器 -->
<scroll-view
class="player-scroll-view"
scroll-x="true"
:scroll-left="scrollLeft"
@scroll="onScroll"
:show-scrollbar="false"
scroll-with-animation
>
<view class="player-list-content">
<!-- 玩家信息表头 -->
<view class="player-header">
<view class="header-row">
<view class="header-cell header-label">玩家 ({{ players.length }})</view>
<view class="player-columns">
<view class="player-column" v-for="player in players" :key="player.id">
<view class="player-header-info">
<!-- 统一头像容器样式 -->
<view class="avatar-container">
<image class="player-avatar" :src="player.avatar" mode="aspectFill"></image>
</view>
<text class="player-name">{{ player.name }}</text>
</view>
</view>
</view>
</view>
</view>
<!-- 对局详情表格 -->
<view class="detail-table">
<!-- 总分行 -->
<view class="table-row">
<view class="row-label">总分</view>
<view class="row-cells">
<view class="score-cell" v-for="player in players" :key="player.id">
{{ formatScore(player.totalScore) }}
</view>
</view>
</view>
<!-- 每局得分行 -->
<view class="table-row" v-for="(round, roundIndex) in gameRounds" :key="roundIndex">
<view class="row-label">{{ roundIndex + 1 }}</view>
<view class="row-cells">
<view class="score-cell" v-for="player in players" :key="player.id">
{{ formatScore(getPlayerRoundScore(roundIndex, player.id)) }}
</view>
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 对局详情表格 -->
<view class="detail-table">
<!-- 总分行 -->
<view class="table-row">
<view class="row-label">总分</view>
<view class="row-cells">
<view class="score-cell" v-for="player in players" :key="player.id">
{{ formatScore(player.totalScore) }}
</view>
</view>
</view>
<!-- 每局得分行 -->
<view class="table-row" v-for="(round, roundIndex) in gameRounds" :key="roundIndex">
<view class="row-label">{{ roundIndex + 1 }}</view>
<view class="row-cells">
<view class="score-cell" v-for="player in players" :key="player.id">
{{ formatScore(getPlayerRoundScore(roundIndex, player.id)) }}
</view>
</scroll-view>
<!-- 自定义可拖动滚动条 -->
<view class="custom-scrollbar" v-if="showScrollIndicator">
<view
class="scroll-track"
@touchstart="onTrackTouchStart"
@touchmove.prevent="onTrackTouchMove"
@touchend="onTrackTouchEnd"
:id="trackId"
>
<view
class="scroll-thumb"
:style="{
left: thumbPosition + '%',
width: thumbWidth + '%'
}"
@touchstart="onThumbTouchStart"
@touchmove.prevent="onThumbTouchMove"
@touchend="onThumbTouchEnd"
></view>
</view>
</view>
</view>
@@ -55,13 +97,45 @@
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { ref, onMounted, computed, nextTick, watch } from 'vue'
import { onShow } from '@dcloudio/uni-app'
// 玩家数据
const players = ref([])
// 对局记录
const gameRounds = ref([])
// 滚动条相关数据
const scrollLeft = ref(0)
const scrollWidth = ref(0)
const scrollViewWidth = ref(0)
const trackId = ref('scroll-track-' + Date.now())
// 滚动条拖动状态
const isDragging = ref(false)
const dragStartX = ref(0)
const dragStartScrollLeft = ref(0)
const trackRect = ref({ left: 0, width: 0 })
// 计算属性:是否显示滚动条指示器
const showScrollIndicator = computed(() => {
return scrollWidth.value > scrollViewWidth.value
})
// 计算属性:滚动条滑块宽度
const thumbWidth = computed(() => {
if (scrollWidth.value === 0) return 100
const widthRatio = (scrollViewWidth.value / scrollWidth.value) * 100
return Math.max(20, Math.min(100, widthRatio)) // 最小宽度20%最大100%
})
// 计算属性:滚动条滑块位置
const thumbPosition = computed(() => {
if (scrollWidth.value === 0 || scrollWidth.value <= scrollViewWidth.value) return 0
const maxScroll = scrollWidth.value - scrollViewWidth.value
return (scrollLeft.value / maxScroll) * (100 - thumbWidth.value)
})
// 格式化分数显示
const formatScore = (score) => {
if (score === 0) return '0'
@@ -128,9 +202,154 @@ onMounted(() => {
]
calculateTotalScores()
}
// 监听玩家数量变化
watch(() => players.value.length, () => {
setTimeout(() => {
updateScrollDimensions()
}, 300)
})
// 更新滚动区域尺寸
setTimeout(() => {
updateScrollDimensions()
}, 1000)
})
onShow(() => {
// 更新滚动区域
setTimeout(() => {
updateScrollDimensions()
getTrackRect()
}, 300)
})
// 滚动事件处理
const onScroll = (e) => {
scrollLeft.value = e.detail.scrollLeft
}
// 更新滚动区域尺寸
const updateScrollDimensions = () => {
console.log('开始更新滚动区域尺寸...')
nextTick(() => {
setTimeout(() => {
const query = uni.createSelectorQuery()
query.select('.player-scroll-view').boundingClientRect()
query.select('.player-list-content').boundingClientRect()
query.exec((res) => {
if (res[0] && res[1]) {
scrollViewWidth.value = res[0].width
scrollWidth.value = res[1].width
console.log('滚动区域计算完成:', {
scrollViewWidth: scrollViewWidth.value,
scrollWidth: scrollWidth.value,
showScrollIndicator: scrollWidth.value > scrollViewWidth.value,
playersCount: players.value.length
})
// 强制更新显示状态
if (scrollWidth.value > scrollViewWidth.value) {
console.log('需要显示滚动条')
}
} else {
console.error('获取滚动区域尺寸失败')
}
})
}, 100)
})
}
// 获取轨道位置信息
const getTrackRect = () => {
return new Promise((resolve) => {
setTimeout(() => {
const query = uni.createSelectorQuery()
query.select('.custom-scrollbar .scroll-track').boundingClientRect()
query.exec((res) => {
if (res[0]) {
trackRect.value = {
left: res[0].left,
width: res[0].width
}
resolve(trackRect.value)
} else {
resolve(null)
}
})
}, 100)
})
}
// 滚动条滑块触摸开始
const onThumbTouchStart = async (e) => {
isDragging.value = true
dragStartX.value = e.touches[0].clientX
dragStartScrollLeft.value = scrollLeft.value
await getTrackRect()
e.stopPropagation()
}
// 滚动条滑块触摸移动
const onThumbTouchMove = (e) => {
if (!isDragging.value) return
const deltaX = e.touches[0].clientX - dragStartX.value
const maxScroll = Math.max(0, scrollWidth.value - scrollViewWidth.value)
if (maxScroll > 0 && trackRect.value.width > 0) {
const scrollPercent = deltaX / trackRect.value.width
const newScrollLeft = dragStartScrollLeft.value + (scrollPercent * maxScroll)
scrollLeft.value = Math.max(0, Math.min(maxScroll, newScrollLeft))
}
e.stopPropagation()
}
// 滚动条滑块触摸结束
const onThumbTouchEnd = (e) => {
isDragging.value = false
e.stopPropagation()
}
// 滚动条轨道触摸开始
const onTrackTouchStart = async (e) => {
const rect = await getTrackRect()
if (!rect) return
const clickX = e.touches[0].clientX - rect.left
const thumbWidthPx = (thumbWidth.value / 100) * rect.width
const thumbCenter = (thumbPosition.value / 100) * rect.width + thumbWidthPx / 2
const maxScroll = Math.max(0, scrollWidth.value - scrollViewWidth.value)
if (maxScroll > 0) {
if (clickX < thumbCenter) {
scrollLeft.value = Math.max(0, scrollLeft.value - scrollViewWidth.value * 0.8)
} else {
scrollLeft.value = Math.min(maxScroll, scrollLeft.value + scrollViewWidth.value * 0.8)
}
}
}
// 滚动条轨道触摸移动
const onTrackTouchMove = (e) => {
e.stopPropagation()
}
// 滚动条轨道触摸结束
const onTrackTouchEnd = (e) => {
e.stopPropagation()
}
// 返回上一页
const goBack = () => {
uni.navigateBack()
}
// 分享对局详情
const shareDetail = () => {
@@ -145,6 +364,7 @@ const shareDetail = () => {
.game-detail-page {
background-color: #fff;
min-height: 100vh;
padding-bottom: 200rpx; /* 给底部按钮留出空间 */
}
.nav-bar {
@@ -169,27 +389,59 @@ const shareDetail = () => {
}
}
/* 玩家列表容器 */
.player-list-container {
padding: 0 30rpx 30rpx;
position: relative;
}
/* 滚动视图样式 */
.player-scroll-view {
width: 100%;
white-space: nowrap;
// 隐藏原生滚动条
::-webkit-scrollbar {
display: none;
}
}
.player-list-content {
display: inline-block;
min-width: 100%;
white-space: nowrap;
}
.player-header {
padding: 20rpx 30rpx;
background-color: #f5f5f5;
border-bottom: 1rpx solid #eee;
.header-row {
display: flex;
align-items: center;
.header-cell {
flex: 1;
padding: 20rpx 0;
.header-label {
width: 200rpx;
flex: none;
font-size: 28rpx;
color: #000;
font-weight: 500;
display: flex;
justify-content: center;
&.header-label {
width: 200rpx;
flex: none;
font-size: 28rpx;
color: #000;
font-weight: 500;
}
align-items: center;
padding-left: 20rpx;
}
.player-columns {
display: flex;
}
.player-column {
display: flex;
flex-direction: column;
align-items: center;
width: 140rpx;
flex-shrink: 0;
padding: 0 10rpx;
}
.player-header-info {
@@ -197,24 +449,41 @@ const shareDetail = () => {
flex-direction: column;
align-items: center;
.player-avatar {
width: 60rpx;
height: 60rpx;
border-radius: 50%;
/* 统一头像容器样式 */
.avatar-container {
position: relative;
width: 110rpx;
height: 110rpx;
margin-bottom: 10rpx;
display: flex;
align-items: center;
justify-content: center;
.player-avatar {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
border: 5rpx solid #fff;
box-shadow: 0 6rpx 20rpx rgba(65, 71, 155, 0.3);
background: linear-gradient(135deg, #41479b 0%, #8b91e2 100%);
transition: all 0.3s ease;
}
}
.player-name {
font-size: 24rpx;
color: #000;
text-align: center;
word-break: break-all;
white-space: normal;
max-width: 120rpx;
font-weight: 500;
}
}
}
}
.detail-table {
padding: 0 30rpx;
.table-row {
display: flex;
padding: 20rpx 0;
@@ -226,30 +495,70 @@ const shareDetail = () => {
.row-label {
width: 200rpx;
flex: none;
font-size: 28rpx;
color: #000;
display: flex;
align-items: center;
font-weight: 500;
padding-left: 20rpx;
}
.row-cells {
display: flex;
flex: 1;
.score-cell {
flex: 1;
width: 140rpx;
flex-shrink: 0;
text-align: center;
font-size: 28rpx;
color: #333;
display: flex;
align-items: center;
justify-content: center;
padding: 0 10rpx;
}
}
}
}
/* 自定义可拖动滚动条样式 - 完全复制单人房间的样式 */
.custom-scrollbar {
margin-top: 20rpx;
padding: 0 20rpx;
.scroll-track {
height: 12rpx;
background-color: #f0f0f0;
border-radius: 6rpx;
position: relative;
// 添加点击区域扩展
&::before {
content: '';
position: absolute;
top: -10rpx;
bottom: -10rpx;
left: 0;
right: 0;
}
}
.scroll-thumb {
position: absolute;
height: 100%;
background-color: #41479b;
border-radius: 6rpx;
transition: all 0.1s ease;
// 添加悬停效果
&:active {
background-color: #33367a;
transform: scale(1.1);
}
}
}
.action-buttons {
position: fixed;
bottom: 0;