Files
shoot-miniprograms/src/pages/team-battle.vue

307 lines
9.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>