添加分享房间链接和对战结束返回房间

This commit is contained in:
kron
2026-01-09 11:50:21 +08:00
parent 4aa14c6a4c
commit 71b25144a4
14 changed files with 204 additions and 209 deletions

View File

@@ -412,9 +412,8 @@ export const cancelOrderListAPI = async (id) => {
return request("POST", "/user/order/cancelOrder", { id }); return request("POST", "/user/order/cancelOrder", { id });
}; };
export const isGamingAPI = async () => { export const getUserGameState = () => {
const result = await request("GET", "/user/isGaming"); return request("GET", "/user/state");
return result.gaming || false;
}; };
export const getCurrentGameAPI = async () => { export const getCurrentGameAPI = async () => {
@@ -546,3 +545,9 @@ export const getMyLikeList = (page = 1, pageSize = 10) => {
`/user/score/sheet/week/shoot/rank/like/list?pageNum=${page}&pageSize=${pageSize}` `/user/score/sheet/week/shoot/rank/like/list?pageNum=${page}&pageSize=${pageSize}`
); );
}; };
export const getReadyAPI = (roomId) => {
return request("POST", `/user/room/ready`, {
roomId,
});
};

View File

@@ -2,12 +2,14 @@
import { ref, watch, onMounted, onBeforeUnmount } from "vue"; import { ref, watch, onMounted, onBeforeUnmount } from "vue";
import { onShow } from "@dcloudio/uni-app"; import { onShow } from "@dcloudio/uni-app";
import { isGamingAPI, getCurrentGameAPI } from "@/apis"; import { getCurrentGameAPI, getUserGameState } from "@/apis";
import { debounce } from "@/util"; import { debounce } from "@/util";
import useStore from "@/store"; import useStore from "@/store";
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
const { user } = storeToRefs(useStore()); const store = useStore();
const { user, game } = storeToRefs(store);
const { updateGame } = store;
const props = defineProps({ const props = defineProps({
signin: { signin: {
@@ -15,13 +17,14 @@ const props = defineProps({
default: () => {}, default: () => {},
}, },
}); });
const show = ref(false);
const loading = ref(false); const loading = ref(false);
onShow(async () => { onShow(async () => {
if (user.value.id) { if (user.value.id) {
const isGaming = await isGamingAPI(); setTimeout(async () => {
show.value = isGaming; const state = await getUserGameState();
updateGame(state.gaming, state.roomId);
}, 1000);
} }
}); });
@@ -29,10 +32,10 @@ watch(
() => user.value, () => user.value,
async (value) => { async (value) => {
if (!value.id) { if (!value.id) {
show.value = false; updateGame(false, "");
} else { } else {
const isGaming = await isGamingAPI(); const state = await getUserGameState();
show.value = isGaming; updateGame(state.gaming, state.roomId);
} }
} }
); );
@@ -40,26 +43,23 @@ watch(
const onClick = debounce(async () => { const onClick = debounce(async () => {
if (loading.value) return; if (loading.value) return;
try { try {
const isGaming = await isGamingAPI(); loading.value = true;
show.value = isGaming; if (game.value.inBattle) {
if (isGaming) {
loading.value = true;
await uni.$checkAudio(); await uni.$checkAudio();
const result = await getCurrentGameAPI(); const result = await getCurrentGameAPI();
loading.value = false; } else if (game.value.roomID) {
} else { uni.navigateTo({
uni.showToast({ url: "/pages/battle-room?roomNumber=" + game.value.roomID,
title: "比赛已结束",
icon: "none",
}); });
} else {
updateGame(false, "");
} }
} finally { } finally {
loading.value = false; loading.value = false;
} }
}); });
const gameOver = () => { const gameOver = () => {
show.value = false; updateGame(false, "");
}; };
onMounted(() => { onMounted(() => {
uni.$on("game-over", gameOver); uni.$on("game-over", gameOver);
@@ -70,10 +70,19 @@ onBeforeUnmount(() => {
</script> </script>
<template> <template>
<view v-if="show" class="back-to-game" @click="onClick"> <view
v-if="game.inBattle || game.roomID"
class="back-to-game"
@click="onClick"
>
<image src="../static/back-to-game-bg.png" mode="widthFix" /> <image src="../static/back-to-game-bg.png" mode="widthFix" />
<image src="../static/pk-icon.png" mode="widthFix" /> <block v-if="game.inBattle">
<text>返回进行中的对局</text> <image src="../static/pk-icon.png" mode="widthFix" />
<text>返回进行中的对局</text>
</block>
<block v-else-if="game.roomID">
<text>返回房间</text>
</block>
<image src="../static/back.png" mode="widthFix" /> <image src="../static/back.png" mode="widthFix" />
</view> </view>
</template> </template>
@@ -93,17 +102,18 @@ onBeforeUnmount(() => {
.back-to-game > image:first-child { .back-to-game > image:first-child {
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100rpx;
} }
.back-to-game > image:nth-child(2) { .back-to-game > image:nth-child(2) {
position: relative; position: relative;
width: 60px; width: 60px;
height: 60px; height: 60px;
} }
.back-to-game > text:nth-child(3) { .back-to-game > text {
position: relative; position: relative;
font-size: 14px; font-size: 14px;
} }
.back-to-game > image:nth-child(4) { .back-to-game > image:last-child {
position: relative; position: relative;
width: 15px; width: 15px;
margin-left: 5px; margin-left: 5px;

View File

@@ -112,21 +112,8 @@ const backToGame = debounce(async () => {
try { try {
isLoading.value = true; isLoading.value = true;
const game = await getCurrentGameAPI();
// 设置请求超时 if (!game || !game.gameId) {
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error("请求超时")), 10000); // 10秒超时
});
const result = await Promise.race([getCurrentGameAPI(), timeoutPromise]);
// 处理返回结果
if (result && result.gameId) {
// 跳转到游戏页面
uni.navigateTo({
url: `/pages/battle-room?gameId=${result.gameId}`,
});
} else {
uni.showToast({ uni.showToast({
title: "没有进行中的对局", title: "没有进行中的对局",
icon: "none", icon: "none",
@@ -136,10 +123,6 @@ const backToGame = debounce(async () => {
showHint.value = false; showHint.value = false;
} catch (error) { } catch (error) {
console.error("获取当前游戏失败:", error); console.error("获取当前游戏失败:", error);
uni.showToast({
title: error.message || "网络请求失败,请重试",
icon: "none",
});
} finally { } finally {
isLoading.value = false; isLoading.value = false;
} }

View File

@@ -1,7 +1,14 @@
<script setup> <script setup>
import { ref } from "vue"; import { ref } from "vue";
import SButton from "@/components/SButton.vue"; import SButton from "@/components/SButton.vue";
import { joinRoomAPI, createRoomAPI, isGamingAPI } from "@/apis";
import { joinRoomAPI, createRoomAPI } from "@/apis";
import { debounce } from "@/util";
import useStore from "@/store";
import { storeToRefs } from "pinia";
const store = useStore();
const { user, game } = storeToRefs(store);
const props = defineProps({ const props = defineProps({
onConfirm: { onConfirm: {
@@ -11,13 +18,11 @@ const props = defineProps({
}); });
const battleMode = ref(1); const battleMode = ref(1);
const step = ref(1);
const loading = ref(false); const loading = ref(false);
const roomNumber = ref(""); const roomNumber = ref("");
const createRoom = async () => { const createRoom = debounce(async () => {
const isGaming = await isGamingAPI(); if (game.value.inBattle) {
if (isGaming) {
uni.$showHint(1); uni.$showHint(1);
return; return;
} }
@@ -31,36 +36,20 @@ const createRoom = async () => {
battleMode.value === 2 ? 2 : 1, battleMode.value === 2 ? 2 : 1,
battleMode.value === 2 ? 10 : size battleMode.value === 2 ? 10 : size
); );
if (result.number) roomNumber.value = result.number; if (result.number) {
step.value = 2; props.onConfirm();
await joinRoomAPI(result.number);
uni.navigateTo({
url: "/pages/battle-room?roomNumber=" + result.number,
});
}
loading.value = false; loading.value = false;
}; });
const enterRoom = async () => {
step.value = 1;
props.onConfirm();
await joinRoomAPI(roomNumber.value);
uni.navigateTo({
url: `/pages/battle-room?roomNumber=${roomNumber.value}`,
});
};
const setClipboardData = () => {
uni.setClipboardData({
data: roomNumber.value,
success() {
uni.showToast({ title: "复制成功" });
},
});
};
</script> </script>
<template> <template>
<view class="container"> <view class="container">
<image <image src="../static/choose-battle-mode.png" mode="widthFix" />
v-if="step === 1" <view class="create-options">
src="../static/choose-battle-mode.png"
mode="widthFix"
/>
<view v-if="step === 1" class="create-options">
<view <view
:class="{ 'battle-btn': true, 'battle-choosen': battleMode === 1 }" :class="{ 'battle-btn': true, 'battle-choosen': battleMode === 1 }"
@click="() => (battleMode = 1)" @click="() => (battleMode = 1)"
@@ -72,14 +61,12 @@ const setClipboardData = () => {
@click="() => (battleMode = 3)" @click="() => (battleMode = 3)"
> >
<text>对抗模式2V2</text> <text>对抗模式2V2</text>
<!-- <text>敬请期待</text> -->
</view> </view>
<view <view
:class="{ 'battle-btn': true, 'battle-choosen': battleMode === 4 }" :class="{ 'battle-btn': true, 'battle-choosen': battleMode === 4 }"
@click="() => (battleMode = 4)" @click="() => (battleMode = 4)"
> >
<text>对抗模式3V3</text> <text>对抗模式3V3</text>
<!-- <text>敬请期待</text> -->
</view> </view>
<view <view
:class="{ 'battle-btn': true, 'battle-choosen': battleMode === 2 }" :class="{ 'battle-btn': true, 'battle-choosen': battleMode === 2 }"
@@ -88,18 +75,7 @@ const setClipboardData = () => {
<text>乱斗模式3-10</text> <text>乱斗模式3-10</text>
</view> </view>
</view> </view>
<SButton v-if="step === 1" :onClick="createRoom">下一步</SButton> <SButton :onClick="createRoom">创建房间</SButton>
<view v-if="step === 2" class="room-info">
<view>
<text>房间号</text>
<text>{{ roomNumber }}</text>
</view>
<view class="copy-room-number" @click="setClipboardData"
>复制房间信息邀请朋友进入</view
>
<SButton width="70vw" :onClick="enterRoom">进入房间</SButton>
<text>30分钟无人进入则房间无效</text>
</view>
</view> </view>
</template> </template>
@@ -142,42 +118,4 @@ const setClipboardData = () => {
border: 4rpx solid #fff3; border: 4rpx solid #fff3;
border-color: #fed847; border-color: #fed847;
} }
/* .battle-close {
background-color: #8889;
color: #b3b3b3;
}
.battle-close > text:last-child {
font-size: 12px;
} */
.room-info {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
padding-top: 40px;
}
.room-info > view:first-child {
font-size: 22px;
color: #fff;
margin-bottom: 20px;
}
.room-info > text {
color: #888686;
font-size: 14px;
margin: 10px 0;
}
.room-info > view:last-child {
color: #287fff;
margin: 20px 0;
font-size: 14px;
}
.copy-room-number {
width: calc(70vw - 20px);
color: #fed847;
border: 1px solid #fed847;
padding: 10px;
text-align: center;
border-radius: 10px;
margin-bottom: 20px;
}
</style> </style>

View File

@@ -97,8 +97,10 @@ const handleLogin = async () => {
const devices = await getMyDevicesAPI(); const devices = await getMyDevicesAPI();
if (devices.bindings && devices.bindings.length) { if (devices.bindings && devices.bindings.length) {
updateDevice(devices.bindings[0].deviceId, devices.bindings[0].deviceName); updateDevice(devices.bindings[0].deviceId, devices.bindings[0].deviceName);
const data = await getDeviceBatteryAPI(); try {
updateOnline(data.online); const data = await getDeviceBatteryAPI();
updateOnline(data.online);
} catch (error) {}
} }
loading.value = false; loading.value = false;
props.onClose(); props.onClose();

View File

@@ -27,15 +27,14 @@ export const MESSAGETYPES = {
Calibration: 4168086625, Calibration: 4168086625,
DeviceOnline: 4168086626, DeviceOnline: 4168086626,
DeviceOffline: 4168086627, DeviceOffline: 4168086627,
SomeoneIsReady: 4168086628,
}; };
export const topThreeColors = ["#FFD947", "#D2D2D2", "#FFA515"]; export const topThreeColors = ["#FFD947", "#D2D2D2", "#FFA515"];
export const getMessageTypeName = (id) => { export const getMessageTypeName = (id) => {
for (let key in MESSAGETYPES) { for (let key in MESSAGETYPES) {
if (MESSAGETYPES[key] === id) { if (MESSAGETYPES[key] === id) return key;
return key;
}
} }
return null; return null;
}; };

View File

@@ -3,6 +3,9 @@
{ {
"path": "pages/index" "path": "pages/index"
}, },
{
"path": "pages/friend-battle"
},
{ {
"path": "pages/point-book" "path": "pages/point-book"
}, },
@@ -90,9 +93,6 @@
{ {
"path": "pages/practise-two" "path": "pages/practise-two"
}, },
{
"path": "pages/friend-battle"
},
{ {
"path": "pages/battle-room" "path": "pages/battle-room"
}, },

View File

@@ -18,7 +18,18 @@ const totalPoints = ref(0);
const rank = ref(0); const rank = ref(0);
function exit() { function exit() {
uni.navigateBack(); const battleInfo = uni.getStorageSync("last-battle");
if (battleInfo && battleInfo.roomId) {
uni.redirectTo({
url: `/pages/battle-room?roomNumber=${battleInfo.roomId}`,
});
} else if (data.value.roomId) {
uni.redirectTo({
url: `/pages/battle-room?roomNumber=${data.value.roomId}`,
});
} else {
uni.navigateBack();
}
} }
onLoad(async (options) => { onLoad(async (options) => {
@@ -310,7 +321,7 @@ const checkBowData = () => {
</text> </text>
<view class="op-btn"> <view class="op-btn">
<view @click="checkBowData">查看成绩</view> <view @click="checkBowData">查看成绩</view>
<view @click="exit">退出</view> <view @click="exit">返回</view>
</view> </view>
<UserUpgrade /> <UserUpgrade />
</view> </view>
@@ -420,6 +431,7 @@ const checkBowData = () => {
border-radius: 20px; border-radius: 20px;
padding: 10px 0; padding: 10px 0;
text-align: center; text-align: center;
color: #000;
} }
.op-btn > view:last-child { .op-btn > view:last-child {
color: #fff; color: #fff;

View File

@@ -1,6 +1,6 @@
<script setup> <script setup>
import { ref, watch, onMounted, onBeforeUnmount } from "vue"; import { ref, watch, onMounted, onBeforeUnmount } from "vue";
import { onLoad, onShow, onHide } from "@dcloudio/uni-app"; import { onLoad, onShareAppMessage } from "@dcloudio/uni-app";
import Container from "@/components/Container.vue"; import Container from "@/components/Container.vue";
import PlayerSeats from "@/components/PlayerSeats.vue"; import PlayerSeats from "@/components/PlayerSeats.vue";
import Guide from "@/components/Guide.vue"; import Guide from "@/components/Guide.vue";
@@ -13,12 +13,15 @@ import {
exitRoomAPI, exitRoomAPI,
startRoomAPI, startRoomAPI,
chooseTeamAPI, chooseTeamAPI,
getReadyAPI,
} from "@/apis"; } from "@/apis";
import { MESSAGETYPES } from "@/constants"; import { MESSAGETYPES } from "@/constants";
import useStore from "@/store"; import useStore from "@/store";
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
const store = useStore(); const store = useStore();
const { user } = storeToRefs(store); const { user } = storeToRefs(store);
const room = ref({}); const room = ref({});
const roomNumber = ref(""); const roomNumber = ref("");
const owner = ref({}); const owner = ref({});
@@ -30,14 +33,27 @@ const redTeam = ref([]);
const showModal = ref(false); const showModal = ref(false);
const battleType = ref(0); const battleType = ref(0);
const refreshRoomTimer = ref(null); const refreshRoomTimer = ref(null);
const ready = ref(false);
const allReady = ref(false);
const timer = ref(null);
async function refreshRoomData() { async function refreshRoomData() {
if (!roomNumber.value) return;
const result = await getRoomAPI(roomNumber.value); const result = await getRoomAPI(roomNumber.value);
if (result.started) return; if (result.started) return;
room.value = result; room.value = result;
battleType.value = result.battleType; battleType.value = result.battleType;
(result.members || []).some((m) => { const members = result.members || [];
if (members.length === result.count) {
allReady.value = members.every((m) => !!m.userInfo.state);
}
members.some((m) => {
if (m.userInfo.id === user.value.id) {
ready.value = !!m.userInfo.state;
return true;
}
return false;
});
members.some((m) => {
if (m.userInfo.id === result.creator) { if (m.userInfo.id === result.creator) {
owner.value = { owner.value = {
id: m.userInfo.id, id: m.userInfo.id,
@@ -77,13 +93,19 @@ async function refreshRoomData() {
players.value = new Array(result.count).fill({}); players.value = new Array(result.count).fill({});
refreshMembers(result.members); refreshMembers(result.members);
} }
if (timer.value) clearInterval(timer.value);
timer.value = setTimeout(refreshRoomData, 1000);
} }
const startGame = async () => { const startGame = async () => {
const result = await startRoomAPI(room.value.number); const result = await startRoomAPI(room.value.number);
}; };
const refreshMembers = (members) => { const getReady = async () => {
await getReadyAPI(roomNumber.value);
};
const refreshMembers = (members = []) => {
blueTeam.value = []; blueTeam.value = [];
redTeam.value = []; redTeam.value = [];
members.forEach((m, index) => { members.forEach((m, index) => {
@@ -138,8 +160,7 @@ async function onReceiveMessage(messages = []) {
}); });
} }
} }
} } else if (msg.constructor === MESSAGETYPES.UserExitRoom) {
if (msg.constructor === MESSAGETYPES.UserExitRoom) {
if (battleType.value === 1) { if (battleType.value === 1) {
if (msg.userId === room.value.creator) { if (msg.userId === room.value.creator) {
owner.value = { owner.value = {
@@ -157,25 +178,22 @@ async function onReceiveMessage(messages = []) {
if (msg.room && msg.room.members) { if (msg.room && msg.room.members) {
refreshMembers(msg.room.members); refreshMembers(msg.room.members);
} }
} } else if (msg.constructor === MESSAGETYPES.TeamUpdate) {
if (msg.constructor === MESSAGETYPES.TeamUpdate) {
if (msg.room && msg.room.members) { if (msg.room && msg.room.members) {
refreshMembers(msg.room.members); refreshMembers(msg.room.members);
} }
} } else if (msg.constructor === MESSAGETYPES.RoomDestroy) {
if (msg.constructor === MESSAGETYPES.RoomDestroy) {
uni.showToast({ uni.showToast({
title: "房间已解散", title: "房间已解散",
icon: "none", icon: "none",
}); });
roomNumber.value = "";
setTimeout(() => { setTimeout(() => {
uni.navigateBack(); uni.navigateBack();
}, 1000); }, 1000);
} else if (msg.constructor === MESSAGETYPES.SomeoneIsReady) {
refreshRoomData();
} }
} } else if (msg.constructor === MESSAGETYPES.WaitForAllReady) {
if (msg.constructor === MESSAGETYPES.WaitForAllReady) {
roomNumber.value = "";
if (msg.groupUserStatus) { if (msg.groupUserStatus) {
uni.setStorageSync("red-team", msg.groupUserStatus.redTeam); uni.setStorageSync("red-team", msg.groupUserStatus.redTeam);
uni.setStorageSync("blue-team", msg.groupUserStatus.blueTeam); uni.setStorageSync("blue-team", msg.groupUserStatus.blueTeam);
@@ -184,6 +202,8 @@ async function onReceiveMessage(messages = []) {
...msg.groupUserStatus.blueTeam, ...msg.groupUserStatus.blueTeam,
]); ]);
uni.removeStorageSync("current-battle"); uni.removeStorageSync("current-battle");
// 避免离开页面,触发退出房间
roomNumber.value = "";
if (msg.groupUserStatus.config.mode == 1) { if (msg.groupUserStatus.config.mode == 1) {
uni.redirectTo({ uni.redirectTo({
url: `/pages/team-battle?battleId=${msg.id}&gameMode=1`, url: `/pages/team-battle?battleId=${msg.id}&gameMode=1`,
@@ -226,15 +246,18 @@ const setClipboardData = () => {
}); });
}; };
const onBack = () => { onShareAppMessage(() => {
showModal.value = true; return {
}; title: "邀请您进入房间对战",
path: "/pages/friend-battle?roomID=" + roomNumber.value,
imageUrl: "",
};
});
onLoad(async (options) => { onLoad(async (options) => {
if (options.roomNumber) { if (options.roomNumber) {
roomNumber.value = options.roomNumber; roomNumber.value = options.roomNumber;
refreshRoomData(); refreshRoomData();
refreshRoomTimer.value = setInterval(refreshRoomData, 2000);
} }
}); });
@@ -251,19 +274,14 @@ onBeforeUnmount(() => {
keepScreenOn: false, keepScreenOn: false,
}); });
uni.$off("socket-inbox", onReceiveMessage); uni.$off("socket-inbox", onReceiveMessage);
if (roomNumber.value && owner.value.id !== user.value.id) { if (roomNumber.value) exitRoomAPI(roomNumber.value);
exitRoomAPI(roomNumber.value); if (timer.value) clearInterval(timer.value);
} timer.value = null;
}); });
onShow(async () => {
refreshRoomData();
});
onHide(() => {});
</script> </script>
<template> <template>
<Container :title="`好友约战 - ${roomNumber}`" :onBack="onBack"> <Container :title="`好友约战 - ${roomNumber}`">
<view class="standby-phase"> <view class="standby-phase">
<Guide> <Guide>
<view class="battle-guide"> <view class="battle-guide">
@@ -274,7 +292,7 @@ onHide(() => {});
}}</text> }}</text>
<text v-if="battleType === 2">大乱斗即将开始! </text> <text v-if="battleType === 2">大乱斗即将开始! </text>
</view> </view>
<view @click="setClipboardData">复制房间号</view> <button hover-class="none" open-type="share">邀请</button>
</view> </view>
</Guide> </Guide>
<view v-if="battleType === 1 && room.count === 2" class="team-mode"> <view v-if="battleType === 1 && room.count === 2" class="team-mode">
@@ -389,7 +407,7 @@ onHide(() => {});
</view> </view>
</block> </block>
<view> <view>
<SButton <!-- <SButton
v-if="user.id === owner.id && battleType === 1 && room.count === 2" v-if="user.id === owner.id && battleType === 1 && room.count === 2"
:disabled="!opponent.id" :disabled="!opponent.id"
:onClick="startGame" :onClick="startGame"
@@ -409,11 +427,14 @@ onHide(() => {});
:onClick="startGame" :onClick="startGame"
>开启对局</SButton >开启对局</SButton
> >
<SButton v-if="user.id !== owner.id" disabled>等待房主开启对战</SButton> <SButton v-if="user.id !== owner.id" disabled>等待房主开启对战</SButton> -->
<text class="tips">创建者点击下一步所有人即可进入游戏</text> <SButton :disabled="ready" :onClick="getReady">{{
allReady.value ? "即将进入对局..." : "我准备好了"
}}</SButton>
<!-- <text class="tips">创建者点击下一步所有人即可进入游戏</text> -->
</view> </view>
</view> </view>
<SModal <!-- <SModal
:show="showModal" :show="showModal"
:onClose="() => (showModal = false)" :onClose="() => (showModal = false)"
height="520rpx" height="520rpx"
@@ -429,7 +450,7 @@ onHide(() => {});
</SButton> </SButton>
</block> </block>
</view> </view>
</SModal> </SModal> -->
</Container> </Container>
</template> </template>
@@ -537,7 +558,7 @@ onHide(() => {});
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
} }
.battle-guide > view:last-child { .battle-guide > button:last-child {
color: #fed847; color: #fed847;
border: 1px solid #fed847; border: 1px solid #fed847;
margin-right: 10px; margin-right: 10px;

View File

@@ -1,52 +1,54 @@
<script setup> <script setup>
import { ref } from "vue"; import { ref } from "vue";
import { onShow } from "@dcloudio/uni-app"; import { onLoad, onShow } from "@dcloudio/uni-app";
import Container from "@/components/Container.vue"; import Container from "@/components/Container.vue";
import Guide from "@/components/Guide.vue"; import Guide from "@/components/Guide.vue";
import SButton from "@/components/SButton.vue"; import SButton from "@/components/SButton.vue";
import SModal from "@/components/SModal.vue"; import SModal from "@/components/SModal.vue";
import Signin from "@/components/Signin.vue";
import CreateRoom from "@/components/CreateRoom.vue"; import CreateRoom from "@/components/CreateRoom.vue";
import Avatar from "@/components/Avatar.vue"; import Avatar from "@/components/Avatar.vue";
import { getRoomAPI, joinRoomAPI, isGamingAPI, getBattleDataAPI } from "@/apis";
import { getRoomAPI, joinRoomAPI, getBattleDataAPI } from "@/apis";
import { debounce, canEenter } from "@/util";
import useStore from "@/store"; import useStore from "@/store";
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
const store = useStore(); const store = useStore();
const { user, device, online } = storeToRefs(store); const { user, device, online, game } = storeToRefs(store);
import { debounce, canEenter } from "@/util";
const showModal = ref(false); const showModal = ref(false);
const showSignin = ref(false);
const warnning = ref(""); const warnning = ref("");
const roomNumber = ref(""); const roomNumber = ref("");
const data = ref({}); const data = ref({});
const roomID = ref("");
const enterRoom = debounce(async () => { const enterRoom = debounce(async (number) => {
if (!canEenter(user.value, device.value, online.value)) return; if (!canEenter(user.value, device.value, online.value)) return;
const isGaming = await isGamingAPI(); if (game.value.inBattle) {
if (isGaming) {
uni.$showHint(1); uni.$showHint(1);
return; return;
} }
if (!roomNumber.value) { if (!number) {
warnning.value = "请输入房间号"; warnning.value = "请输入房间号";
showModal.value = true; showModal.value = true;
} else { } else {
const room = await getRoomAPI(roomNumber.value); const room = await getRoomAPI(number);
if (room.number) { if (room.number) {
const alreadyIn = room.members.find( const alreadyIn = room.members.find(
(item) => item.userInfo.id === user.value.id (item) => item.userInfo.id === user.value.id
); );
if (!alreadyIn) { if (!alreadyIn) {
const result = await joinRoomAPI(roomNumber.value); const result = await joinRoomAPI(number);
if (result.full) { if (result.full) {
warnning.value = "房间已满员"; warnning.value = "房间已满员";
showModal.value = true; showModal.value = true;
return; return;
} }
} }
roomNumber.value = "";
showModal.value = false;
uni.navigateTo({ uni.navigateTo({
url: `/pages/battle-room?roomNumber=${room.number}`, url: "/pages/battle-room?roomNumber=" + number,
}); });
} else { } else {
warnning.value = room.started ? "该房间对战已开始,无法加入" : "查无此房"; warnning.value = room.started ? "该房间对战已开始,无法加入" : "查无此房";
@@ -56,18 +58,24 @@ const enterRoom = debounce(async () => {
}); });
const onCreateRoom = async () => { const onCreateRoom = async () => {
if (!canEenter(user.value, device.value, online.value)) return; if (!canEenter(user.value, device.value, online.value)) return;
const isGaming = await isGamingAPI();
if (isGaming) {
uni.$showHint(1);
return;
}
warnning.value = ""; warnning.value = "";
showModal.value = true; showModal.value = true;
}; };
const onSignin = () => {
showSignin.value = false;
if (roomID.value) enterRoom(roomID.value);
};
onShow(async () => { onShow(async () => {
const result = await getBattleDataAPI(); const result = await getBattleDataAPI();
data.value = result; data.value = result;
}); });
onLoad(async (options) => {
roomID.value = options.roomID || "";
if (options.roomID) {
if (user.value.id) enterRoom(options.roomID);
else showSignin.value = true;
}
});
</script> </script>
<template> <template>
@@ -127,7 +135,7 @@ onShow(async () => {
v-model="roomNumber" v-model="roomNumber"
placeholder-style="color: #ccc" placeholder-style="color: #ccc"
/> />
<view @click="enterRoom">进入房间</view> <view @click="enterRoom(roomNumber)">进入房间</view>
</view> </view>
</view> </view>
<view class="create-room"> <view class="create-room">
@@ -159,6 +167,9 @@ onShow(async () => {
</view> </view>
<CreateRoom v-if="!warnning" :onConfirm="() => (showModal = false)" /> <CreateRoom v-if="!warnning" :onConfirm="() => (showModal = false)" />
</SModal> </SModal>
<SModal :show="showSignin" :onClose="() => (showSignin = false)">
<Signin :onClose="onSignin" />
</SModal>
</view> </view>
</Container> </Container>
</template> </template>

View File

@@ -30,8 +30,8 @@ const {
getLvlName, getLvlName,
updateOnline, updateOnline,
} = store; } = store;
const { user, device, rankData, online, game } = storeToRefs(store);
const { user, device, rankData, online } = storeToRefs(store);
const showModal = ref(false); const showModal = ref(false);
const showGuide = ref(false); const showGuide = ref(false);

View File

@@ -4,12 +4,12 @@ import { onShow } from "@dcloudio/uni-app";
import Container from "@/components/Container.vue"; import Container from "@/components/Container.vue";
import Avatar from "@/components/Avatar.vue"; import Avatar from "@/components/Avatar.vue";
import { topThreeColors } from "@/constants"; import { topThreeColors } from "@/constants";
import { isGamingAPI, getHomeData } from "@/apis"; import { getHomeData } from "@/apis";
import { canEenter } from "@/util"; import { canEenter } from "@/util";
import useStore from "@/store"; import useStore from "@/store";
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
const store = useStore(); const store = useStore();
const { user, device, online } = storeToRefs(store); const { user, device, online, game } = storeToRefs(store);
const { getLvlName } = store; const { getLvlName } = store;
const defaultSeasonData = { const defaultSeasonData = {
@@ -43,8 +43,7 @@ const handleSelect = (index) => {
const toMatchPage = async (gameType, teamSize) => { const toMatchPage = async (gameType, teamSize) => {
if (!canEenter(user.value, device.value, online.value)) return; if (!canEenter(user.value, device.value, online.value)) return;
const isGaming = await isGamingAPI(); if (game.value.inBattle) {
if (isGaming) {
uni.$showHint(1); uni.$showHint(1);
return; return;
} }
@@ -64,14 +63,14 @@ const toRankListPage = () => {
}); });
}; };
const onChangeSeason = async (seasonId, name) => { const onChangeSeason = async (seasonId, name) => {
showSeasonList.value = false;
if (name !== seasonName.value) { if (name !== seasonName.value) {
handleSelect(selectedIndex.value);
const result = await getHomeData(seasonId); const result = await getHomeData(seasonId);
rankData.value = result; rankData.value = result;
seasonName.value = name; seasonName.value = name;
handleSelect(selectedIndex.value);
updateData(); updateData();
} }
showSeasonList.value = false;
}; };
const updateData = () => { const updateData = () => {
const { userGameStats, seasonList } = rankData.value; const { userGameStats, seasonList } = rankData.value;
@@ -502,10 +501,11 @@ onShow(async () => {
} }
.ranking-data > view:first-of-type > view { .ranking-data > view:first-of-type > view {
width: 25%; width: 25%;
padding: 7px 10px;
text-align: center; text-align: center;
border-radius: 20px; border-radius: 20px;
font-size: 30rpx; font-size: 30rpx;
word-break: keep-all;
line-height: 70rpx;
} }
.rank-item { .rank-item {
width: calc(100% - 30px); width: calc(100% - 30px);
@@ -595,13 +595,19 @@ onShow(async () => {
.season-list > view { .season-list > view {
display: flex; display: flex;
align-items: center; align-items: center;
padding: 10px 20px;
word-break: keep-all; word-break: keep-all;
padding: 20rpx 0;
}
.season-list > view > text {
width: 140rpx;
text-align: right;
} }
.season-list > view > image { .season-list > view > image {
width: 12px; width: 24rpx;
height: 12px; height: 24rpx;
margin-left: 10px; min-width: 24rpx;
min-height: 24rpx;
margin-left: 20rpx;
} }
.my-rank-score { .my-rank-score {
position: absolute !important; position: absolute !important;

View File

@@ -2,7 +2,7 @@ import { defineStore } from "pinia";
const defaultUser = { const defaultUser = {
id: "", id: "",
nickName: "游客", nickName: "",
avatar: "../static/user-icon.png", avatar: "../static/user-icon.png",
trio: 0, // 大于1表示完成了新手引导 trio: 0, // 大于1表示完成了新手引导
lvlName: "", lvlName: "",
@@ -50,6 +50,10 @@ export default defineStore("store", {
ringRank: [], ringRank: [],
}, },
online: false, online: false,
game: {
roomID: "",
inBattle: false,
},
}), }),
// 计算属性 // 计算属性
@@ -103,6 +107,10 @@ export default defineStore("store", {
this.config.randInfos this.config.randInfos
); );
}, },
updateGame(inBattle = false, roomID = "") {
this.game.roomID = roomID;
this.game.inBattle = inBattle;
},
}, },
// 开启数据持久化 // 开启数据持久化

View File

@@ -1,4 +1,4 @@
import { isGamingAPI, getGameAPI } from "@/apis"; import { getUserGameState, getGameAPI } from "@/apis";
export const formatTimestamp = (timestamp) => { export const formatTimestamp = (timestamp) => {
const date = new Date(timestamp * 1000); const date = new Date(timestamp * 1000);
@@ -90,8 +90,8 @@ export const wxShare = async (canvasId = "shareCanvas") => {
}; };
export const isGameEnded = async (battleId) => { export const isGameEnded = async (battleId) => {
const isGaming = await isGamingAPI(); const state = await getUserGameState();
if (!isGaming) { if (!state.gaming) {
const result = await getGameAPI(battleId); const result = await getGameAPI(battleId);
if (result.mode) { if (result.mode) {
uni.redirectTo({ uni.redirectTo({
@@ -107,7 +107,7 @@ export const isGameEnded = async (battleId) => {
}, 1000); }, 1000);
} }
} }
return !isGaming; return !state.gaming;
}; };
// 获取元素尺寸和位置信息 // 获取元素尺寸和位置信息