update:新增房间重连逻辑,优化tententen语音,修复mvp斩获环

This commit is contained in:
2026-07-16 16:40:32 +08:00
parent 621397e25d
commit f64a6b5c95
13 changed files with 328 additions and 255 deletions
+169 -10
View File
@@ -1,6 +1,6 @@
<script setup>
import { ref, onMounted, computed, onBeforeUnmount } from "vue";
import { onShow, onLoad, onShareAppMessage } from "@dcloudio/uni-app";
import { onShow, onHide, onLoad, onShareAppMessage } from "@dcloudio/uni-app";
import Container from "@/components/Container.vue";
import PlayerSeats from "@/components/PlayerSeats.vue";
import GuideTwo from "@/components/GuideTwo.vue";
@@ -14,12 +14,14 @@ import {
chooseTeamAPI,
getReadyAPI,
kickPlayerAPI,
getBattleAPI,
} from "@/apis";
import { debounce, isLimitError } from "@/util";
import { MESSAGETYPES, MESSAGETYPESV2 } from "@/constants";
import useStore from "@/store";
import { storeToRefs } from "pinia";
import audioManager from "@/audioManager";
import { returnToBattle } from "@/utils/matchReturn";
const store = useStore();
const { user } = storeToRefs(store);
@@ -74,17 +76,151 @@ const timer = ref(null);
const goBattle = ref(false);
const showLimitModal = ref(false);
const readySubmitting = ref(false);
const navigationPending = ref(false);
let pageMounted = false;
let pageVisible = false;
let foregroundGeneration = 0;
const MATCH_INFO_RETRY_DELAY_MS = 500;
/** 从结算页返回时为 true,跳过进场靶纸语音 */
const skipTargetAudio = ref(false);
const wait = (delay) =>
new Promise((resolve) => {
setTimeout(resolve, delay);
});
const isCurrentForeground = (generation) =>
pageMounted &&
pageVisible &&
generation === foregroundGeneration &&
!navigationPending.value;
const redirectOnce = (url) =>
new Promise((resolve, reject) => {
if (navigationPending.value) {
resolve(false);
return;
}
navigationPending.value = true;
uni.redirectTo({
url,
success: resolve,
fail: (error) => {
navigationPending.value = false;
goBattle.value = false;
reject(error);
},
});
});
async function queryCurrentBattle(generation) {
try {
return await getBattleAPI();
} catch (error) {
await wait(MATCH_INFO_RETRY_DELAY_MS);
if (!isCurrentForeground(generation)) throw error;
return getBattleAPI();
}
}
async function returnToCurrentBattle(battle) {
if (!battle?.matchId || navigationPending.value) return false;
goBattle.value = true;
try {
const returned = await returnToBattle(battle, redirectOnce);
if (!returned) goBattle.value = false;
return returned;
} catch (error) {
goBattle.value = false;
throw error;
}
}
async function reconcileBattleRoom(generation) {
let battle = null;
try {
battle = await queryCurrentBattle(generation);
} catch (error) {
if (!isCurrentForeground(generation)) return;
console.log("resume room match info error", error);
uni.showToast({
title: "比赛状态查询失败,请重试",
icon: "none",
});
return;
}
if (!isCurrentForeground(generation)) return;
if (battle?.matchId) {
try {
await returnToCurrentBattle(battle);
} catch (error) {
console.log("return to room battle error", error);
uni.showToast({
title: "进入比赛失败,请重试",
icon: "none",
});
}
return;
}
let roomStarted = false;
try {
roomStarted = await refreshRoomData();
} catch (error) {
if (isCurrentForeground(generation)) {
console.log("refresh room data error", error);
}
return;
}
if (!isCurrentForeground(generation) || !roomStarted) return;
await wait(MATCH_INFO_RETRY_DELAY_MS);
if (!isCurrentForeground(generation)) return;
try {
battle = await queryCurrentBattle(generation);
} catch (error) {
if (!isCurrentForeground(generation)) return;
console.log("retry room match info error", error);
uni.showToast({
title: "比赛状态查询失败,请重试",
icon: "none",
});
return;
}
if (!isCurrentForeground(generation)) return;
if (battle?.matchId) {
try {
await returnToCurrentBattle(battle);
} catch (error) {
console.log("return to room battle error", error);
uni.showToast({
title: "进入比赛失败,请重试",
icon: "none",
});
}
return;
}
uni.showToast({
title: "比赛正在创建,请稍候",
icon: "none",
});
}
/**
* 从服务端刷新当前房间数据,更新成员列表、准备状态等信息
* 仅在 roomNumber 有效且房间未开始时执行
*/
async function refreshRoomData() {
if (!roomNumber.value) return;
if (!roomNumber.value) return false;
const result = await getRoomAPI(roomNumber.value);
if (result.started) return;
if (result.started) return true;
room.value = result;
// 加入者通过 API 返回的 targetType 字段同步靶纸尺寸,并持久化到本地缓存
if (result.targetType) {
@@ -158,6 +294,7 @@ async function refreshRoomData() {
}
if (timer.value) clearInterval(timer.value);
// timer.value = setTimeout(refreshRoomData, 2000);
return false;
}
const getReady = debounce(async () => {
@@ -239,17 +376,22 @@ async function onReceiveMessage(message) {
}
});
} else if (message.type === MESSAGETYPESV2.AboutToStart) {
if (navigationPending.value) return;
const matchId = message.matchId || message.id;
if (!matchId) return;
goBattle.value = true;
let url = "";
if (message.mode <= 3) {
uni.setStorageSync("blue-team", message.teams[1].players || []);
uni.setStorageSync("red-team", message.teams[2].players || []);
uni.redirectTo({
url: "/pages/team-battle/index?battleId=" + message.matchId,
});
url = "/pages/team-battle/index?battleId=" + matchId;
} else {
uni.redirectTo({
url: "/pages/melee-battle?battleId=" + message.matchId,
});
url = "/pages/melee-battle?battleId=" + matchId;
}
try {
await redirectOnce(url);
} catch (error) {
console.log("redirect to room battle error", error);
}
}
}
@@ -344,7 +486,15 @@ onShareAppMessage(() => {
*/
onShow(() => {
goBattle.value = false;
refreshRoomData();
navigationPending.value = false;
pageVisible = true;
const generation = ++foregroundGeneration;
if (pageMounted) void reconcileBattleRoom(generation);
});
onHide(() => {
pageVisible = false;
foregroundGeneration += 1;
});
/**
@@ -368,6 +518,10 @@ onLoad(async (options) => {
const stored = uni.getStorageSync(`targetSize_${roomNumber.value}`);
if (stored) targetSize.value = stored;
}
if (pageMounted && pageVisible) {
const generation = ++foregroundGeneration;
void reconcileBattleRoom(generation);
}
});
/**
@@ -379,6 +533,8 @@ onMounted(() => {
keepScreenOn: true,
});
uni.$on("socket-inbox", onReceiveMessage);
pageMounted = true;
if (pageVisible) void reconcileBattleRoom(foregroundGeneration);
// 页面加载完成 1 秒后根据靶纸尺寸播报对应语音;从结算页返回时跳过
setTimeout(() => {
if (!skipTargetAudio.value) {
@@ -389,6 +545,9 @@ onMounted(() => {
});
onBeforeUnmount(() => {
pageMounted = false;
pageVisible = false;
foregroundGeneration += 1;
uni.setKeepScreenOn({
keepScreenOn: false,
});