新建项目
This commit is contained in:
98
pages/result/result.js
Normal file
98
pages/result/result.js
Normal file
@@ -0,0 +1,98 @@
|
||||
const service = require("../../services/game-service");
|
||||
const { formatCountdown } = require("../../utils/format");
|
||||
|
||||
Page({
|
||||
data: {
|
||||
code: "",
|
||||
room: null,
|
||||
winnerName: "",
|
||||
winnerCopy: "",
|
||||
ranking: [],
|
||||
elapsedText: "00:00",
|
||||
eventCount: 0,
|
||||
errorMessage: "",
|
||||
replaying: false
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
const code = options.code || "";
|
||||
const room = service.getRoom(code);
|
||||
if (!room) {
|
||||
this.setData({ errorMessage: "战报不存在或本地数据已被清理" });
|
||||
return;
|
||||
}
|
||||
const hidersWin = room.winner === "hiders";
|
||||
const snapshot = room.resultSnapshot;
|
||||
const sourcePlayers = snapshot
|
||||
? snapshot.players
|
||||
: room.players.map((player) => ({
|
||||
...player,
|
||||
initialRole: player.initialRole || player.role,
|
||||
finalRole: player.role,
|
||||
survivedSeconds: 0
|
||||
}));
|
||||
const ranking = [...sourcePlayers]
|
||||
.sort((a, b) => {
|
||||
if (b.captures !== a.captures) return b.captures - a.captures;
|
||||
if (b.survivedSeconds !== a.survivedSeconds) {
|
||||
return b.survivedSeconds - a.survivedSeconds;
|
||||
}
|
||||
if (a.joinedAt !== b.joinedAt) return a.joinedAt - b.joinedAt;
|
||||
return a.id.localeCompare(b.id);
|
||||
})
|
||||
.map((player, index) => ({
|
||||
...player,
|
||||
rank: index + 1,
|
||||
roleText: player.initialRole === "hider" && player.finalRole === "seeker"
|
||||
? "躲藏者→寻找者"
|
||||
: player.initialRole === "seeker" ? "寻找者" : "躲藏者",
|
||||
resultText:
|
||||
player.captures > 0
|
||||
? `完成 ${player.captures} 次抓捕`
|
||||
: player.caughtAt
|
||||
? "勇敢参与追逃"
|
||||
: player.initialRole === "hider" ? "坚持到游戏结束" : "持续追踪目标"
|
||||
}));
|
||||
this.setData({
|
||||
code,
|
||||
room,
|
||||
errorMessage: "",
|
||||
winnerName: hidersWin ? "躲藏者胜利" : "寻找者胜利",
|
||||
winnerCopy: hidersWin
|
||||
? "有人成功坚持到了倒计时结束。"
|
||||
: "所有躲藏者都已被找到。",
|
||||
ranking,
|
||||
elapsedText: formatCountdown(snapshot ? snapshot.elapsedSeconds : 0),
|
||||
eventCount: snapshot ? snapshot.eventCount : room.events.length
|
||||
});
|
||||
},
|
||||
|
||||
replay() {
|
||||
if (this.data.replaying) return;
|
||||
this.setData({ replaying: true });
|
||||
try {
|
||||
service.resetRoom(this.data.code);
|
||||
wx.redirectTo({
|
||||
url: `/pages/lobby/lobby?code=${this.data.code}`
|
||||
});
|
||||
} catch (error) {
|
||||
this.setData({ replaying: false });
|
||||
wx.showToast({ title: error.message, icon: "none" });
|
||||
}
|
||||
},
|
||||
|
||||
backHome() {
|
||||
service.clearCurrentRoom();
|
||||
wx.reLaunch({ url: "/pages/home/home" });
|
||||
},
|
||||
|
||||
onShareAppMessage() {
|
||||
if (!this.data.room) {
|
||||
return { title: "藏猫猫计划", path: "/pages/home/home" };
|
||||
}
|
||||
return {
|
||||
title: `${this.data.room.name}:${this.data.winnerName}`,
|
||||
path: "/pages/home/home"
|
||||
};
|
||||
}
|
||||
});
|
||||
3
pages/result/result.json
Normal file
3
pages/result/result.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"navigationBarTitleText": "本局战报"
|
||||
}
|
||||
65
pages/result/result.wxml
Normal file
65
pages/result/result.wxml
Normal file
@@ -0,0 +1,65 @@
|
||||
<view class="page result-page" wx:if="{{room}}">
|
||||
<view class="result-hero">
|
||||
<view class="confetti confetti-a"></view>
|
||||
<view class="confetti confetti-b"></view>
|
||||
<view class="trophy">★</view>
|
||||
<view class="result-eyebrow">GAME COMPLETE</view>
|
||||
<view class="winner">{{winnerName}}</view>
|
||||
<view class="winner-copy">{{winnerCopy}}</view>
|
||||
<view class="result-meta">{{room.name}} · 房间 {{room.code}}</view>
|
||||
</view>
|
||||
|
||||
<view class="highlight-grid">
|
||||
<view class="highlight">
|
||||
<view class="highlight-value">{{room.players.length}}</view>
|
||||
<view class="highlight-label">参与玩家</view>
|
||||
</view>
|
||||
<view class="highlight">
|
||||
<view class="highlight-value">{{elapsedText}}</view>
|
||||
<view class="highlight-label">实际用时</view>
|
||||
</view>
|
||||
<view class="highlight">
|
||||
<view class="highlight-value">{{eventCount}}</view>
|
||||
<view class="highlight-label">关键事件</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section-label">本局表现</view>
|
||||
<view class="ranking-list">
|
||||
<view wx:for="{{ranking}}" wx:key="id" class="ranking-row">
|
||||
<view class="rank {{item.rank <= 3 ? 'top' : ''}}">{{item.rank}}</view>
|
||||
<view class="rank-avatar">{{item.avatar}}</view>
|
||||
<view class="rank-main">
|
||||
<view class="rank-name">{{item.name}}</view>
|
||||
<view class="rank-copy">{{item.resultText}}</view>
|
||||
</view>
|
||||
<view class="rank-role">{{item.roleText}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="badge-card">
|
||||
<view class="badge-icon">↗</view>
|
||||
<view>
|
||||
<view class="badge-title">本局称号 · 城市追踪者</view>
|
||||
<view class="badge-copy">完成了第一场真实世界追逃。下一局试试更大的场地吧。</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="button-stack">
|
||||
<button class="secondary-button" open-type="share">分享本局战报</button>
|
||||
<button
|
||||
class="primary-button"
|
||||
loading="{{replaying}}"
|
||||
disabled="{{replaying}}"
|
||||
bindtap="replay"
|
||||
>按原规则再来一局</button>
|
||||
<button class="ghost-button" bindtap="backHome">返回首页</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>
|
||||
200
pages/result/result.wxss
Normal file
200
pages/result/result.wxss
Normal file
@@ -0,0 +1,200 @@
|
||||
.result-hero {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: 50rpx 30rpx;
|
||||
background: #183f31;
|
||||
border-radius: 36rpx;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.trophy {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
margin: 0 auto 24rpx;
|
||||
background: #d9ef72;
|
||||
border-radius: 32rpx;
|
||||
color: #17231d;
|
||||
font-size: 54rpx;
|
||||
}
|
||||
|
||||
.result-eyebrow {
|
||||
color: rgba(255, 255, 255, 0.58);
|
||||
font-size: 20rpx;
|
||||
font-weight: 800;
|
||||
letter-spacing: 4rpx;
|
||||
}
|
||||
|
||||
.winner {
|
||||
margin-top: 12rpx;
|
||||
font-size: 52rpx;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.winner-copy {
|
||||
margin-top: 12rpx;
|
||||
color: rgba(255, 255, 255, 0.72);
|
||||
font-size: 25rpx;
|
||||
}
|
||||
|
||||
.result-meta {
|
||||
margin-top: 32rpx;
|
||||
padding-top: 24rpx;
|
||||
border-top: 2rpx solid rgba(255, 255, 255, 0.12);
|
||||
color: #d9ef72;
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.confetti {
|
||||
position: absolute;
|
||||
width: 20rpx;
|
||||
height: 70rpx;
|
||||
background: #ef8f4a;
|
||||
transform: rotate(28deg);
|
||||
}
|
||||
|
||||
.confetti-a {
|
||||
top: 70rpx;
|
||||
left: 45rpx;
|
||||
}
|
||||
|
||||
.confetti-b {
|
||||
top: 120rpx;
|
||||
right: 50rpx;
|
||||
height: 48rpx;
|
||||
background: #d9ef72;
|
||||
transform: rotate(-35deg);
|
||||
}
|
||||
|
||||
.highlight-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 12rpx;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
padding: 24rpx 10rpx;
|
||||
background: #fffdf7;
|
||||
border-radius: 22rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.highlight-value {
|
||||
font-size: 38rpx;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.highlight-label {
|
||||
margin-top: 5rpx;
|
||||
color: #7c8580;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
|
||||
.ranking-list {
|
||||
overflow: hidden;
|
||||
background: #fffdf7;
|
||||
border-radius: 28rpx;
|
||||
}
|
||||
|
||||
.ranking-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
padding: 22rpx 20rpx;
|
||||
border-bottom: 2rpx solid rgba(23, 35, 29, 0.07);
|
||||
}
|
||||
|
||||
.ranking-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.rank {
|
||||
width: 32rpx;
|
||||
color: #8b938e;
|
||||
font-size: 23rpx;
|
||||
font-weight: 900;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.rank.top {
|
||||
color: #ef8f4a;
|
||||
}
|
||||
|
||||
.rank-avatar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 62rpx;
|
||||
height: 62rpx;
|
||||
background: #e7eee8;
|
||||
border-radius: 20rpx;
|
||||
font-size: 23rpx;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.rank-main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.rank-name {
|
||||
overflow: hidden;
|
||||
font-size: 26rpx;
|
||||
font-weight: 800;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.rank-copy {
|
||||
margin-top: 5rpx;
|
||||
color: #7c8580;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
|
||||
.rank-role {
|
||||
flex: 0 0 auto;
|
||||
max-width: 150rpx;
|
||||
color: #1d6b4f;
|
||||
font-size: 21rpx;
|
||||
font-weight: 700;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.badge-card {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
margin-top: 26rpx;
|
||||
padding: 26rpx;
|
||||
background: #fff0df;
|
||||
border-radius: 26rpx;
|
||||
}
|
||||
|
||||
.badge-icon {
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 70rpx;
|
||||
height: 70rpx;
|
||||
background: #ef8f4a;
|
||||
border-radius: 22rpx;
|
||||
color: #fff;
|
||||
font-size: 36rpx;
|
||||
}
|
||||
|
||||
.badge-title {
|
||||
color: #813f16;
|
||||
font-size: 26rpx;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.badge-copy {
|
||||
margin-top: 8rpx;
|
||||
color: #9c531e;
|
||||
font-size: 21rpx;
|
||||
line-height: 1.5;
|
||||
}
|
||||
Reference in New Issue
Block a user