220 lines
6.6 KiB
Vue
220 lines
6.6 KiB
Vue
<script setup>
|
||
import { ref, onMounted, onBeforeUnmount } from "vue";
|
||
import { onLoad, onShow, onHide } from "@dcloudio/uni-app";
|
||
import Container from "@/components/Container.vue";
|
||
import BowTarget from "@/components/BowTarget.vue";
|
||
import ShootProgress from "@/components/ShootProgress.vue";
|
||
import BattleHeader from "@/components/BattleHeader.vue";
|
||
import PlayerScore from "@/components/PlayerScore.vue";
|
||
import SButton from "@/components/SButton.vue";
|
||
import Avatar from "@/components/Avatar.vue";
|
||
import ScreenHint from "@/components/ScreenHint.vue";
|
||
import TestDistance from "@/components/TestDistance.vue";
|
||
import audioManager from "@/audioManager";
|
||
import { getBattleAPI, laserCloseAPI } from "@/apis";
|
||
import { MESSAGETYPESV2 } from "@/constants";
|
||
import useStore from "@/store";
|
||
import { storeToRefs } from "pinia";
|
||
const store = useStore();
|
||
const { user } = storeToRefs(store);
|
||
const title = ref("");
|
||
const start = ref(null);
|
||
const battleId = ref("");
|
||
const currentRound = ref(1);
|
||
const tips = ref("即将开始...");
|
||
const players = ref([]);
|
||
const playersSorted = ref([]);
|
||
const playersScores = ref([]);
|
||
const halfTimeTip = ref(false);
|
||
const halfRest = ref(false);
|
||
|
||
const navigateToResult = () => {
|
||
uni.redirectTo({ url: `/pages/battle-result?battleId=${battleId.value}` });
|
||
};
|
||
|
||
function recoverData(battleInfo, { force = false } = {}) {
|
||
if (!battleInfo) return;
|
||
try {
|
||
if (battleInfo.way === 1) title.value = "好友约战 - 大乱斗";
|
||
if (battleInfo.way === 2) title.value = "排位赛 - 大乱斗";
|
||
|
||
// 优先使用接口数据,否则使用缓存
|
||
if (battleInfo.teams?.[0]?.players) {
|
||
players.value = [...battleInfo.teams[0].players];
|
||
} else {
|
||
// 大乱斗可能存的是 players 列表
|
||
// 这里的缓存逻辑根据 AboutToStart 消息结构可能不同,假设也是 teams[0]
|
||
// 如果是从 match-page 过来的,match-page 只存了 teams[1] 和 [2] 给对抗模式
|
||
// 大乱斗的匹配逻辑可能不同,暂时保持原样,只做安全保护
|
||
players.value = [];
|
||
}
|
||
|
||
start.value = battleInfo.status !== 0;
|
||
|
||
if (battleInfo.status === 0) {
|
||
const readyRemain = (Date.now() - (battleInfo.createTime || Date.now())) / 1000;
|
||
if (readyRemain > 0 && readyRemain < 15) {
|
||
setTimeout(() => uni.$emit("update-timer", 15 - readyRemain - 0.2), 200);
|
||
}
|
||
return;
|
||
}
|
||
|
||
tips.value =
|
||
(battleInfo.rounds.length !== 2 ? "上" : "下") + "半场:请先射6箭";
|
||
playersScores.value = battleInfo.rounds.map((r) => ({ ...r.shoots }));
|
||
const totals = {};
|
||
players.value.forEach((p) => {
|
||
const total = playersScores.value.reduce((acc, round) => {
|
||
const arr = round[p.id] || [];
|
||
return acc + arr.length;
|
||
}, 0);
|
||
totals[p.id] = total;
|
||
});
|
||
playersSorted.value = players.value.slice().sort((a, b) => {
|
||
return totals[b.id] - totals[a.id];
|
||
});
|
||
if (battleInfo.status === 3) {
|
||
halfTimeTip.value = true;
|
||
halfRest.value = true;
|
||
tips.value = "准备下半场";
|
||
// 剩余休息时间
|
||
// const remain = (Date.now() - battleInfo.timeoutTime) / 1000;
|
||
setTimeout(() => {
|
||
uni.$emit("update-remain", 0);
|
||
}, 200);
|
||
return;
|
||
}
|
||
if (force) {
|
||
const remain = (Date.now() - (battleInfo.current?.startTime || Date.now())) / 1000;
|
||
console.log(`当前轮已进行${remain}秒`);
|
||
if (remain > 0 && remain < 90) {
|
||
setTimeout(() => {
|
||
uni.$emit("update-remain", 90 - remain - 0.2);
|
||
}, 200);
|
||
}
|
||
}
|
||
} catch (err) {
|
||
console.error("recoverData error:", err);
|
||
}
|
||
}
|
||
|
||
onLoad(async (options) => {
|
||
if (options.battleId) battleId.value = options.battleId;
|
||
// uni.enableAlertBeforeUnload({
|
||
// message: "离开比赛可能导致比赛失败,是否继续?",
|
||
// success: (res) => {
|
||
// console.log("已启用离开提示");
|
||
// },
|
||
// });
|
||
});
|
||
|
||
async function onReceiveMessage(msg) {
|
||
if (Array.isArray(msg)) return;
|
||
if (msg.type === MESSAGETYPESV2.BattleStart) {
|
||
halfTimeTip.value = false;
|
||
halfRest.value = false;
|
||
recoverData(msg);
|
||
} else if (msg.type === MESSAGETYPESV2.ShootResult) {
|
||
recoverData(msg);
|
||
} else if (msg.type === MESSAGETYPESV2.HalfRest) {
|
||
halfTimeTip.value = true;
|
||
halfRest.value = true;
|
||
tips.value = "准备下半场";
|
||
} else if (msg.type === MESSAGETYPESV2.BattleEnd) {
|
||
setTimeout(() => {
|
||
uni.redirectTo({
|
||
url: "/pages/battle-result?battleId=" + msg.matchId,
|
||
});
|
||
}, 1000);
|
||
}
|
||
}
|
||
onMounted(async () => {
|
||
uni.setKeepScreenOn({
|
||
keepScreenOn: true,
|
||
});
|
||
uni.$on("socket-inbox", onReceiveMessage);
|
||
await laserCloseAPI();
|
||
});
|
||
onBeforeUnmount(() => {
|
||
uni.setKeepScreenOn({
|
||
keepScreenOn: false,
|
||
});
|
||
uni.$off("socket-inbox", onReceiveMessage);
|
||
audioManager.stopAll();
|
||
});
|
||
|
||
onShow(async () => {
|
||
if (battleId.value) {
|
||
const result = await getBattleAPI(battleId.value);
|
||
if (!result) return;
|
||
if (result.status === 2) {
|
||
uni.showToast({
|
||
title: "比赛已结束",
|
||
icon: "none",
|
||
});
|
||
uni.navigateBack({
|
||
delta: 2,
|
||
});
|
||
} else {
|
||
recoverData(result, { force: true });
|
||
}
|
||
}
|
||
});
|
||
</script>
|
||
|
||
<template>
|
||
<Container :title="title" :bgType="1">
|
||
<view class="container">
|
||
<BattleHeader v-if="!start" :players="players" />
|
||
<TestDistance v-if="start === false" :guide="false" :isBattle="true" />
|
||
<ShootProgress
|
||
:show="start"
|
||
:start="start && !halfRest"
|
||
:tips="tips"
|
||
:total="90"
|
||
:melee="true"
|
||
:battleId="battleId"
|
||
/>
|
||
<view v-if="start" class="user-row">
|
||
<Avatar :src="user.avatar" :size="35" />
|
||
<BowPower />
|
||
</view>
|
||
<BowTarget
|
||
v-if="start"
|
||
:currentRound="
|
||
playersScores.map((s) => s[user.id].length).reduce((a, b) => a + b, 0)
|
||
"
|
||
:totalRound="12"
|
||
:scores="playersScores.map((r) => r[user.id]).flat()"
|
||
:stop="halfRest"
|
||
/>
|
||
<view :style="{ paddingBottom: '20px' }">
|
||
<PlayerScore
|
||
v-if="start"
|
||
v-for="(player, index) in playersSorted"
|
||
:key="index"
|
||
:player="player"
|
||
:scores="playersScores.map((s) => s[player.id])"
|
||
/>
|
||
</view>
|
||
<ScreenHint
|
||
:show="halfTimeTip"
|
||
mode="small"
|
||
:onClose="() => (halfTimeTip = false)"
|
||
>
|
||
<view class="half-time-tip">
|
||
<text>上半场结束,休息一下吧:)</text>
|
||
<text>20秒后开始下半场</text>
|
||
</view>
|
||
</ScreenHint>
|
||
</view>
|
||
</Container>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.container {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
</style>
|