307 lines
9.4 KiB
Vue
307 lines
9.4 KiB
Vue
<script setup>
|
||
import {ref, onMounted, onBeforeUnmount, nextTick} from "vue";
|
||
import {onLoad, onShow, onHide} from "@dcloudio/uni-app";
|
||
import Container from "@/components/Container.vue";
|
||
import BattleHeader from "@/components/BattleHeader.vue";
|
||
import BowTarget from "@/components/BowTarget.vue";
|
||
import BattleFooter from "@/components/BattleFooter.vue";
|
||
import ScreenHint from "@/components/ScreenHint.vue";
|
||
import SButton from "@/components/SButton.vue";
|
||
import RoundEndTip from "@/components/RoundEndTip.vue";
|
||
import TestDistance from "@/components/TestDistance.vue";
|
||
import TeamAvatars from "@/components/TeamAvatars.vue";
|
||
import ShootProgress2 from "@/components/ShootProgress2.vue";
|
||
import {laserCloseAPI, getBattleAPI} from "@/apis";
|
||
import {MESSAGETYPESV2} from "@/constants";
|
||
import audioManager from "@/audioManager";
|
||
import useStore from "@/store";
|
||
import {storeToRefs} from "pinia";
|
||
|
||
const store = useStore();
|
||
const {user} = storeToRefs(store);
|
||
const start = ref(null);
|
||
const tips = ref("");
|
||
const battleId = ref("");
|
||
const currentRound = ref(0);
|
||
const goldenRound = ref(0);
|
||
const currentRedPoint = ref(0);
|
||
const currentBluePoint = ref(0);
|
||
const scores = ref([]);
|
||
const blueScores = ref([]);
|
||
const redTeam = ref([]);
|
||
const blueTeam = ref([]);
|
||
const currentShooterId = ref(0);
|
||
const roundResults = ref([]);
|
||
const redPoints = ref(0);
|
||
const bluePoints = ref(0);
|
||
const showRoundTip = ref(false);
|
||
const isFinalShoot = ref(false);
|
||
const matchStatus = ref(undefined);
|
||
const updateRemainSecond = ref(0);
|
||
/** 对战来源类型(1=好友约战,2=匹配对战),用于结算页分流 */
|
||
const battleWay = ref(0);
|
||
|
||
const recoverData = (battleInfo, {force = false, arrowOnly = false} = {}) => {
|
||
try {
|
||
battleId.value = battleInfo.matchId;
|
||
// 存储对战来源,供结算跳转分流使用
|
||
if (battleInfo.way !== undefined) battleWay.value = battleInfo.way;
|
||
|
||
// 优先使用接口返回的队伍数据,如果没有则尝试从缓存读取(应对匹配刚完成接口未就绪的情况)
|
||
const t1 = battleInfo.teams?.[1] || {};
|
||
const t2 = battleInfo.teams?.[2] || {};
|
||
|
||
if (t1.players) blueTeam.value = [...t1.players];
|
||
else {
|
||
const cached = uni.getStorageSync("blue-team");
|
||
if (cached && cached.length) blueTeam.value = cached;
|
||
}
|
||
|
||
if (t2.players) redTeam.value = [...t2.players];
|
||
else {
|
||
const cached = uni.getStorageSync("red-team");
|
||
if (cached && cached.length) redTeam.value = cached;
|
||
}
|
||
|
||
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;
|
||
}
|
||
if (!arrowOnly) {
|
||
currentShooterId.value = battleInfo.current.playerId;
|
||
const redPlayer = battleInfo.teams[2].players.find(
|
||
(item) => item.id === battleInfo.current.playerId
|
||
);
|
||
let nextTips = redPlayer ? "请红队射箭" : "请蓝队射箭";
|
||
if (force) nextTips += "重回";
|
||
if (
|
||
battleInfo.current.playerId === user.value.id &&
|
||
redTeam.value.length > 1
|
||
) {
|
||
nextTips += "你";
|
||
}
|
||
tips.value = nextTips;
|
||
uni.$emit("update-tips", nextTips);
|
||
uni.$emit("update-remain", {reset: true, value: 15, team: redTeam?'red':'blue'});
|
||
if (force) {
|
||
const remain = (Date.now() - battleInfo.current.startTime) / 1000;
|
||
console.log(`当前轮已进行${remain}秒`);
|
||
if (remain > 0 && remain < 15) {
|
||
updateRemainSecond.value = 15 - remain - 0.2
|
||
}
|
||
} else {
|
||
updateRemainSecond.value = battleInfo.readyTime
|
||
}
|
||
} else {
|
||
currentRound.value = battleInfo.current.round || 1;
|
||
const latestRound = battleInfo.rounds[currentRound.value - 1];
|
||
if (latestRound) {
|
||
blueScores.value = latestRound.shoots[1];
|
||
scores.value = latestRound.shoots[2];
|
||
}
|
||
}
|
||
roundResults.value = battleInfo.rounds || [];
|
||
isFinalShoot.value = battleInfo.current.goldRound;
|
||
bluePoints.value = battleInfo.teams[1].score;
|
||
redPoints.value = battleInfo.teams[2].score;
|
||
} catch (err) {
|
||
console.log(err);
|
||
}
|
||
};
|
||
|
||
function onAudioEnded(s) {
|
||
if (s.indexOf('请红方射箭') >= 0 || s.indexOf('请蓝方射箭') >= 0) {
|
||
let team = s.indexOf('请红方射箭') >= 0 ? 'red' : 'blue';
|
||
uni.$emit("update-remain", {stop: false, value: updateRemainSecond.value, team: team});
|
||
}
|
||
if (s.indexOf("比赛结束") >= 0) {
|
||
console.log("比赛结束");
|
||
onBattleEnd()
|
||
}
|
||
}
|
||
|
||
function onBattleEnd() {
|
||
if (matchStatus.value === 2) {
|
||
// 好友约战(way===1)跳转专属结算页,其他模式保持跳转旧结算页
|
||
// 后期如需新增更多模式的专属页面,在此扩展 if/else 分支即可
|
||
if (battleWay.value === 1) {
|
||
uni.redirectTo({
|
||
url: `/pages/friend-battle-result?battleId=${battleId.value}`,
|
||
});
|
||
} else {
|
||
uni.redirectTo({
|
||
url: `/pages/battle-result?battleId=${battleId.value}`,
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
function onNewRound(msg) {
|
||
showRoundTip.value = true;
|
||
isFinalShoot.value = msg.current.goldRound;
|
||
const latestRound = msg.rounds[currentRound.value - 1];
|
||
if (latestRound) {
|
||
if (isFinalShoot.value) {
|
||
currentBluePoint.value = msg.teams[1].score;
|
||
currentRedPoint.value = msg.teams[2].score;
|
||
} else {
|
||
currentBluePoint.value = latestRound.scores[1].score;
|
||
currentRedPoint.value = latestRound.scores[2].score;
|
||
}
|
||
}
|
||
}
|
||
|
||
async function onReceiveMessage(msg) {
|
||
if (Array.isArray(msg)) return;
|
||
if (msg.type === MESSAGETYPESV2.BattleStart) {
|
||
start.value = true;
|
||
} else if (msg.type === MESSAGETYPESV2.ToSomeoneShoot) {
|
||
recoverData(msg);
|
||
} else if (msg.type === MESSAGETYPESV2.ShootResult) {
|
||
uni.$emit("update-remain", {stop: true})
|
||
showRoundTip.value = false;
|
||
recoverData(msg, {arrowOnly: true});
|
||
} else if (msg.type === MESSAGETYPESV2.NewRound) {
|
||
setTimeout(() => {
|
||
onNewRound(msg)
|
||
}, 800)
|
||
} else if (msg.type === MESSAGETYPESV2.BattleEnd) {
|
||
matchStatus.value = msg.status;
|
||
if (msg.status === 4) {
|
||
showRoundTip.value = true;
|
||
currentBluePoint.value = 0;
|
||
currentRedPoint.value = 0;
|
||
setTimeout(() => {
|
||
uni.navigateBack();
|
||
}, 2000);
|
||
}
|
||
}
|
||
}
|
||
|
||
onLoad(async (options) => {
|
||
if (options.battleId) battleId.value = options.battleId;
|
||
// uni.enableAlertBeforeUnload({
|
||
// message: "离开比赛可能导致比赛失败,是否继续?",
|
||
// success: (res) => {
|
||
// console.log("已启用离开提示");
|
||
// },
|
||
// });
|
||
});
|
||
onMounted(async () => {
|
||
uni.setKeepScreenOn({
|
||
keepScreenOn: true,
|
||
});
|
||
uni.$on("socket-inbox", onReceiveMessage);
|
||
uni.$on("audioEnded", onAudioEnded);
|
||
await laserCloseAPI();
|
||
});
|
||
onBeforeUnmount(() => {
|
||
uni.setKeepScreenOn({
|
||
keepScreenOn: false,
|
||
});
|
||
uni.$off("audioEnded", onAudioEnded);
|
||
audioManager.stopAll();
|
||
});
|
||
const refreshTimer = ref(null);
|
||
onShow(async () => {
|
||
if (battleId.value) {
|
||
const result = await getBattleAPI(battleId.value);
|
||
if (!result) return;
|
||
if (result.status === 2) {
|
||
// 比赛已结束(如切后台再回来):跳结算页,按 way 分流
|
||
// 与 onBattleEnd 保持一致,避免返回首页
|
||
if (result.way === 1) {
|
||
uni.redirectTo({
|
||
url: `/pages/friend-battle-result?battleId=${result.matchId}`,
|
||
});
|
||
} else {
|
||
uni.redirectTo({
|
||
url: `/pages/battle-result?battleId=${result.matchId}`,
|
||
});
|
||
}
|
||
} else {
|
||
recoverData(result, {force: true});
|
||
}
|
||
}
|
||
});
|
||
</script>
|
||
|
||
<template>
|
||
<Container :bgType="start ? 3 : 1">
|
||
<view class="container">
|
||
<BattleHeader
|
||
v-if="start === false"
|
||
:redTeam="redTeam"
|
||
:blueTeam="blueTeam"
|
||
/>
|
||
<TestDistance v-if="start === false" :guide="false" :isBattle="true"/>
|
||
<view v-if="start" class="players-row">
|
||
<TeamAvatars
|
||
:team="blueTeam"
|
||
:isRed="false"
|
||
:currentShooterId="currentShooterId"
|
||
/>
|
||
<ShootProgress2
|
||
:tips="tips"
|
||
:currentRound="
|
||
goldenRound > 0 ? 'gold' + goldenRound : 'round' + currentRound
|
||
"
|
||
/>
|
||
<TeamAvatars :team="redTeam" :currentShooterId="currentShooterId"/>
|
||
</view>
|
||
<BowTarget
|
||
v-if="start"
|
||
mode="team"
|
||
:scores="scores"
|
||
:blueScores="blueScores"
|
||
/>
|
||
<BattleFooter
|
||
v-if="start"
|
||
:roundResults="roundResults"
|
||
:redPoints="redPoints"
|
||
:bluePoints="bluePoints"
|
||
:goldenRound="goldenRound"
|
||
/>
|
||
<ScreenHint
|
||
:show="showRoundTip"
|
||
:onClose="() => (showRoundTip = false)"
|
||
:mode="isFinalShoot ? 'tall' : 'normal'"
|
||
>
|
||
<RoundEndTip
|
||
v-if="showRoundTip"
|
||
:isFinal="isFinalShoot"
|
||
:round="currentRound"
|
||
:bluePoint="currentBluePoint"
|
||
:redPoint="currentRedPoint"
|
||
:roundData="
|
||
roundResults[currentRound - 1] ? roundResults[currentRound - 1] : []
|
||
"
|
||
:onAutoClose="()=>{ showRoundTip = false}"
|
||
/>
|
||
</ScreenHint>
|
||
</view>
|
||
</Container>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.container {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.players-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-top: -2%;
|
||
margin-bottom: 6%;
|
||
}
|
||
</style>
|