新建项目
This commit is contained in:
116
pages/lobby/lobby.js
Normal file
116
pages/lobby/lobby.js
Normal file
@@ -0,0 +1,116 @@
|
||||
const service = require("../../services/game-service");
|
||||
const { GAME_MODES } = require("../../utils/constants");
|
||||
|
||||
Page({
|
||||
data: {
|
||||
code: "",
|
||||
room: null,
|
||||
modeName: "",
|
||||
isHost: false,
|
||||
showStartButton: false,
|
||||
meReady: false,
|
||||
canStart: false,
|
||||
startHint: "",
|
||||
errorMessage: "",
|
||||
starting: false
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
this.setData({ code: options.code || "" });
|
||||
this.loadRoom(true);
|
||||
},
|
||||
|
||||
onShow() {
|
||||
if (this.data.code) this.loadRoom(false);
|
||||
},
|
||||
|
||||
onPullDownRefresh() {
|
||||
this.loadRoom(false);
|
||||
wx.stopPullDownRefresh();
|
||||
},
|
||||
|
||||
loadRoom(addBots) {
|
||||
let room = service.getRoom(this.data.code);
|
||||
if (!room) {
|
||||
this.setData({ room: null, errorMessage: "房间不存在或本地数据已被清理" });
|
||||
return;
|
||||
}
|
||||
if (addBots && room.players.length < Math.min(6, room.settings.maxPlayers)) {
|
||||
room = service.addDemoPlayers(
|
||||
room.code,
|
||||
Math.min(6, room.settings.maxPlayers)
|
||||
);
|
||||
}
|
||||
if (room.status !== "waiting") {
|
||||
const target = room.status === "finished" ? "result" : "game";
|
||||
wx.redirectTo({
|
||||
url: `/pages/${target}/${target}?code=${room.code}`
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const me = room.players.find((player) => player.id === service.USER_ID);
|
||||
const isHost = room.hostId === service.USER_ID;
|
||||
const canOperate = isHost || room.code === "731204";
|
||||
const enoughPlayers = room.players.length >= 4;
|
||||
const seekersValid = room.settings.seekerCount < room.players.length;
|
||||
const everyoneReady = room.players.every((player) => player.ready);
|
||||
let startHint = "分配身份并开始";
|
||||
if (!enoughPlayers) startHint = "至少需要 4 人";
|
||||
else if (!seekersValid) startHint = "寻找者人数过多";
|
||||
else if (!everyoneReady) startHint = "等待所有玩家准备";
|
||||
this.setData({
|
||||
room,
|
||||
errorMessage: "",
|
||||
modeName: GAME_MODES[room.settings.mode].name,
|
||||
isHost,
|
||||
showStartButton: canOperate,
|
||||
meReady: me ? me.ready : false,
|
||||
canStart: canOperate && enoughPlayers && seekersValid && everyoneReady,
|
||||
startHint
|
||||
});
|
||||
},
|
||||
|
||||
toggleReady() {
|
||||
try {
|
||||
service.toggleReady(this.data.code);
|
||||
wx.vibrateShort({ type: "light" });
|
||||
this.loadRoom(false);
|
||||
} catch (error) {
|
||||
wx.showToast({ title: error.message, icon: "none" });
|
||||
}
|
||||
},
|
||||
|
||||
startGame() {
|
||||
if (this.data.starting) return;
|
||||
this.setData({ starting: true });
|
||||
try {
|
||||
service.startGame(this.data.code);
|
||||
wx.redirectTo({
|
||||
url: `/pages/game/game?code=${this.data.code}`
|
||||
});
|
||||
} catch (error) {
|
||||
this.setData({ starting: false });
|
||||
wx.showToast({ title: error.message, icon: "none" });
|
||||
}
|
||||
},
|
||||
|
||||
copyCode() {
|
||||
wx.setClipboardData({
|
||||
data: this.data.code,
|
||||
success: () => wx.showToast({ title: "房间码已复制", icon: "success" })
|
||||
});
|
||||
},
|
||||
|
||||
backHome() {
|
||||
service.clearCurrentRoom();
|
||||
wx.reLaunch({ url: "/pages/home/home" });
|
||||
},
|
||||
|
||||
onShareAppMessage() {
|
||||
return {
|
||||
title: `来加入「${this.data.room.name}」`,
|
||||
path: `/pages/join/join?code=${this.data.code}`
|
||||
};
|
||||
}
|
||||
});
|
||||
4
pages/lobby/lobby.json
Normal file
4
pages/lobby/lobby.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"navigationBarTitleText": "游戏候场",
|
||||
"enablePullDownRefresh": true
|
||||
}
|
||||
89
pages/lobby/lobby.wxml
Normal file
89
pages/lobby/lobby.wxml
Normal file
@@ -0,0 +1,89 @@
|
||||
<view class="page lobby-page" wx:if="{{room}}">
|
||||
<view class="room-head">
|
||||
<view class="row between">
|
||||
<view class="pill">{{modeName}}</view>
|
||||
<view class="room-status"><view class="pulse"></view>等待加入</view>
|
||||
</view>
|
||||
<view class="room-name">{{room.name}}</view>
|
||||
<view class="code-wrap" bindtap="copyCode">
|
||||
<view>
|
||||
<view class="code-label">房间码 · 点击复制</view>
|
||||
<view class="room-code">{{room.code}}</view>
|
||||
</view>
|
||||
<button class="share-mini" open-type="share">邀请好友</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="rule-strip">
|
||||
<view>
|
||||
<text class="rule-number">{{room.settings.durationMinutes}}</text>
|
||||
<text class="rule-unit">分钟</text>
|
||||
<view class="rule-name">游戏时间</view>
|
||||
</view>
|
||||
<view>
|
||||
<text class="rule-number">{{room.settings.radiusMeters}}</text>
|
||||
<text class="rule-unit">米</text>
|
||||
<view class="rule-name">活动半径</view>
|
||||
</view>
|
||||
<view>
|
||||
<text class="rule-number">{{room.settings.seekerCount}}</text>
|
||||
<text class="rule-unit">人</text>
|
||||
<view class="rule-name">寻找者</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="row between player-heading">
|
||||
<view class="section-label">玩家 {{room.players.length}} / {{room.settings.maxPlayers}}</view>
|
||||
<view class="muted demo-note">已加入模拟队友</view>
|
||||
</view>
|
||||
<view class="player-list">
|
||||
<view wx:for="{{room.players}}" wx:key="id" class="player-row">
|
||||
<view class="player-avatar {{item.isHost ? 'host' : ''}}">{{item.avatar}}</view>
|
||||
<view class="player-main">
|
||||
<view class="player-name">
|
||||
{{item.name}}
|
||||
<text wx:if="{{item.isHost}}" class="host-tag">房主</text>
|
||||
<text wx:if="{{item.isBot}}" class="bot-tag">模拟</text>
|
||||
</view>
|
||||
<view class="player-state">{{item.ready ? '已准备好' : '正在确认装备'}}</view>
|
||||
</view>
|
||||
<view class="ready-dot {{item.ready ? 'ready' : ''}}">
|
||||
{{item.ready ? '✓' : '…'}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="tip-card">
|
||||
<view class="tip-icon">!</view>
|
||||
<view>
|
||||
<view class="tip-title">开局前安全确认</view>
|
||||
<view class="tip-copy">确保所有人定位可用、电量充足,并约定紧急集合点。抓捕只用口令确认,禁止身体接触。</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="button-stack">
|
||||
<button
|
||||
wx:if="{{showStartButton}}"
|
||||
class="primary-button"
|
||||
loading="{{starting}}"
|
||||
disabled="{{starting || !canStart}}"
|
||||
bindtap="startGame"
|
||||
>
|
||||
{{startHint}}
|
||||
</button>
|
||||
<button
|
||||
wx:else
|
||||
class="{{meReady ? 'ghost-button' : 'secondary-button'}}"
|
||||
bindtap="toggleReady"
|
||||
>
|
||||
{{meReady ? '取消准备' : '我准备好了'}}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="state-page" wx:elif="{{errorMessage}}">
|
||||
<view class="state-icon">!</view>
|
||||
<view class="state-title">无法进入候场</view>
|
||||
<view class="state-copy">{{errorMessage}}</view>
|
||||
<button class="primary-button state-action" bindtap="backHome">返回首页</button>
|
||||
</view>
|
||||
221
pages/lobby/lobby.wxss
Normal file
221
pages/lobby/lobby.wxss
Normal file
@@ -0,0 +1,221 @@
|
||||
.room-head {
|
||||
padding: 30rpx;
|
||||
background: #183f31;
|
||||
border-radius: 34rpx;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.room-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
color: rgba(255, 255, 255, 0.72);
|
||||
font-size: 23rpx;
|
||||
}
|
||||
|
||||
.pulse {
|
||||
width: 14rpx;
|
||||
height: 14rpx;
|
||||
background: #d9ef72;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 0 8rpx rgba(217, 239, 114, 0.12);
|
||||
}
|
||||
|
||||
.room-name {
|
||||
margin-top: 28rpx;
|
||||
font-size: 43rpx;
|
||||
font-weight: 900;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.code-wrap {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
margin-top: 34rpx;
|
||||
padding-top: 26rpx;
|
||||
border-top: 2rpx solid rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
.code-label {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
font-size: 21rpx;
|
||||
}
|
||||
|
||||
.room-code {
|
||||
margin-top: 5rpx;
|
||||
color: #d9ef72;
|
||||
font-size: 50rpx;
|
||||
font-weight: 900;
|
||||
letter-spacing: 7rpx;
|
||||
}
|
||||
|
||||
.share-mini {
|
||||
width: 180rpx;
|
||||
height: 70rpx;
|
||||
margin: 0;
|
||||
background: #d9ef72;
|
||||
color: #17231d;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.rule-strip {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
margin-top: 22rpx;
|
||||
padding: 26rpx 10rpx;
|
||||
background: #fffdf7;
|
||||
border-radius: 26rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.rule-number {
|
||||
font-size: 36rpx;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.rule-unit {
|
||||
margin-left: 4rpx;
|
||||
color: #68726c;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
|
||||
.rule-name {
|
||||
margin-top: 6rpx;
|
||||
color: #7c8580;
|
||||
font-size: 21rpx;
|
||||
}
|
||||
|
||||
.player-heading .section-label {
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.demo-note {
|
||||
margin-top: 26rpx;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
|
||||
.player-list {
|
||||
overflow: hidden;
|
||||
background: #fffdf7;
|
||||
border-radius: 28rpx;
|
||||
}
|
||||
|
||||
.player-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
padding: 22rpx 26rpx;
|
||||
border-bottom: 2rpx solid rgba(23, 35, 29, 0.07);
|
||||
}
|
||||
|
||||
.player-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.player-avatar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 66rpx;
|
||||
height: 66rpx;
|
||||
background: #e7eee8;
|
||||
border-radius: 22rpx;
|
||||
font-size: 25rpx;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.player-avatar.host {
|
||||
background: #d9ef72;
|
||||
}
|
||||
|
||||
.player-main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.player-name {
|
||||
overflow: hidden;
|
||||
font-size: 27rpx;
|
||||
font-weight: 800;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.host-tag,
|
||||
.bot-tag {
|
||||
margin-left: 10rpx;
|
||||
padding: 3rpx 10rpx;
|
||||
background: #e7eee8;
|
||||
border-radius: 99rpx;
|
||||
color: #1d6b4f;
|
||||
font-size: 18rpx;
|
||||
}
|
||||
|
||||
.bot-tag {
|
||||
background: #fff0df;
|
||||
color: #9c531e;
|
||||
}
|
||||
|
||||
.player-state {
|
||||
margin-top: 6rpx;
|
||||
color: #89918c;
|
||||
font-size: 21rpx;
|
||||
}
|
||||
|
||||
.ready-dot {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 42rpx;
|
||||
height: 42rpx;
|
||||
background: #eef0ed;
|
||||
border-radius: 50%;
|
||||
color: #8b938e;
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.ready-dot.ready {
|
||||
background: #1d6b4f;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.tip-card {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
margin-top: 26rpx;
|
||||
padding: 24rpx;
|
||||
background: #fff0df;
|
||||
border-radius: 24rpx;
|
||||
}
|
||||
|
||||
.tip-icon {
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 46rpx;
|
||||
height: 46rpx;
|
||||
background: #ef8f4a;
|
||||
border-radius: 50%;
|
||||
color: #fff;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.tip-title {
|
||||
color: #813f16;
|
||||
font-size: 25rpx;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.tip-copy {
|
||||
margin-top: 8rpx;
|
||||
color: #9c531e;
|
||||
font-size: 22rpx;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
button[disabled] {
|
||||
background: #c8ceca;
|
||||
color: #fff;
|
||||
}
|
||||
Reference in New Issue
Block a user