320 lines
7.2 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="page">
<view class="bg-decoration"></view>
<view class="header">
<text class="title">实时对战信号</text>
<view class="refresh-text-btn" :class="{ loading: loading }" @tap="loadRooms">
{{ loading ? '同步中...' : '刷新信号' }}
</view>
</view>
<scroll-view scroll-y class="content" @refresherrefresh="loadRooms" :refresher-enabled="true" :refresher-triggered="isRefreshing">
<view v-if="rooms.length > 0" class="room-list">
<view v-for="room in rooms" :key="room.match_id" class="room-card glass-card fadeInUp">
<view class="room-main">
<view class="room-info">
<view class="room-header">
<text class="room-id">房间 #{{ room.match_id.split('.')[0].substring(0, 6) }}</text>
<view class="status-badge" :class="room.started ? 'started' : 'waiting'">
{{ room.started ? '进行中' : '等待中' }}
</view>
</view>
<view class="room-stats">
<view class="stat-item">
<text class="stat-icon">👥</text>
<text class="stat-text">{{ room.player_count }}/{{ room.max_players }} 玩家</text>
</view>
<view class="stat-item">
<text class="stat-icon">📡</text>
<text class="stat-text">延迟: {{ Math.floor(Math.random() * 50) + 20 }}ms</text>
</view>
</view>
</view>
<view class="room-actions">
<view v-if="!room.started && room.player_count < room.max_players" class="btn-action join" @tap="joinRoom(room)">
<text class="action-text">加入</text>
</view>
<view class="btn-action watch" @tap="watchRoom(room)">
<text class="action-text">围观</text>
</view>
</view>
</view>
</view>
</view>
<view v-else-if="!loading" class="empty-box">
<view class="empty-icon">🛰</view>
<text class="empty-text">未监测到活跃战局</text>
<view class="btn-primary start-new" @tap="goBack">去发起匹配</view>
</view>
</scroll-view>
</view>
</template>
<script>
import { nakamaManager } from '../../../utils/nakamaManager.js';
import { authRequest } from '../../../utils/request.js';
export default {
data() {
return {
rooms: [],
loading: false,
isRefreshing: false,
gameToken: '',
nakamaServer: '',
nakamaKey: ''
}
},
onLoad(options) {
this.gameToken = options.game_token;
this.nakamaServer = decodeURIComponent(options.nakama_server || '');
this.nakamaKey = decodeURIComponent(options.nakama_key || '');
this.initAndLoad();
},
methods: {
async initAndLoad() {
this.loading = true;
try {
if (!nakamaManager.isConnected) {
nakamaManager.initClient(this.nakamaServer || 'wss://game.1024tool.vip', this.nakamaKey || 'defaultkey');
await nakamaManager.authenticateWithGameToken(this.gameToken);
}
await this.loadRooms();
} catch (e) {
uni.showToast({ title: '连接通讯中心失败', icon: 'none' });
} finally {
this.loading = false;
}
},
async loadRooms() {
this.isRefreshing = true;
try {
const res = await nakamaManager.rpc('list_matches', {});
this.rooms = res || [];
} catch (e) {
console.error('Failed to load rooms', e);
} finally {
this.isRefreshing = false;
this.loading = false;
}
},
goBack() {
uni.navigateBack();
},
joinRoom(room) {
// 通过 MatchID 传参给 play.vue
uni.navigateTo({
url: `/pages-game/game/minesweeper/play?match_id=${room.match_id}&game_token=${encodeURIComponent(this.gameToken)}&nakama_server=${encodeURIComponent(this.nakamaServer)}&nakama_key=${encodeURIComponent(this.nakamaKey)}`
});
},
watchRoom(room) {
uni.navigateTo({
url: `/pages-game/game/minesweeper/play?match_id=${room.match_id}&is_spectator=1&game_token=${encodeURIComponent(this.gameToken)}&nakama_server=${encodeURIComponent(this.nakamaServer)}&nakama_key=${encodeURIComponent(this.nakamaKey)}`
});
}
}
}
</script>
<style lang="scss" scoped>
@import '@/uni.scss';
.page {
min-height: 100vh;
background-color: #0f172a;
color: #f8fafc;
display: flex;
flex-direction: column;
}
.bg-decoration {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 400rpx;
background: radial-gradient(circle at 50% 0%, rgba(59, 130, 246, 0.15) 0%, transparent 70%);
z-index: 0;
}
.header {
position: relative;
z-index: 10;
padding: 100rpx 40rpx 40rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
.title {
font-size: 38rpx;
font-weight: 800;
letter-spacing: 2rpx;
color: #f8fafc;
}
.refresh-text-btn {
padding: 12rpx 24rpx;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12rpx;
font-size: 24rpx;
color: #94a3b8;
transition: all 0.2s;
&.loading {
opacity: 0.6;
pointer-events: none;
}
&:active {
background: rgba(255, 255, 255, 0.1);
transform: scale(0.95);
}
}
.content {
flex: 1;
padding: 0 30rpx;
box-sizing: border-box;
}
.room-list {
padding-bottom: 60rpx;
}
.room-card {
margin-bottom: 24rpx;
padding: 32rpx;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.room-main {
display: flex;
justify-content: space-between;
align-items: center;
}
.room-header {
display: flex;
align-items: center;
margin-bottom: 16rpx;
}
.room-id {
font-size: 28rpx;
font-weight: 600;
color: #94a3b8;
margin-right: 16rpx;
}
.status-badge {
padding: 4rpx 16rpx;
border-radius: 20rpx;
font-size: 20rpx;
font-weight: 700;
&.waiting {
background: rgba(34, 197, 94, 0.2);
color: #4ade80;
}
&.started {
background: rgba(59, 130, 246, 0.2);
color: #60a5fa;
}
}
.room-stats {
display: flex;
gap: 24rpx;
}
.stat-item {
display: flex;
align-items: center;
}
.stat-icon {
font-size: 24rpx;
margin-right: 8rpx;
}
.stat-text {
font-size: 24rpx;
color: #cbd5e1;
}
.room-actions {
display: flex;
gap: 16rpx;
}
.btn-action {
padding: 16rpx 32rpx;
border-radius: 12rpx;
font-size: 24rpx;
font-weight: 700;
transition: all 0.2s;
&.join {
background: #3b82f6;
color: white;
box-shadow: 0 4rpx 12rpx rgba(59, 130, 246, 0.3);
}
&.watch {
background: rgba(255, 255, 255, 0.1);
color: #f8fafc;
border: 1px solid rgba(255, 255, 255, 0.2);
}
&:active {
transform: scale(0.95);
}
}
.empty-box {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-top: 200rpx;
}
.empty-icon {
font-size: 120rpx;
margin-bottom: 40rpx;
opacity: 0.5;
}
.empty-text {
font-size: 32rpx;
color: #64748b;
margin-bottom: 60rpx;
}
.start-new {
width: 320rpx;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fadeInUp {
animation: fadeInUp 0.4s ease-out both;
}
</style>