完成创建房间相关接口

This commit is contained in:
kron
2025-05-30 16:14:17 +08:00
parent 01a327e40e
commit b7d64396ef
9 changed files with 364 additions and 45 deletions

9
shoot.http Normal file
View File

@@ -0,0 +1,9 @@
@serverUrl = http://120.79.241.5:8000/api/shoot
### 模拟射箭
POST {{serverUrl}}/index/arrow
Content-Type: application/json
{
"device_id": "iXAx67Ce"
}

View File

@@ -109,9 +109,31 @@ export const bindDeviceAPI = (device) => {
}, },
success: (res) => { success: (res) => {
const { code, data } = res.data; const { code, data } = res.data;
if (code === 0) { if (code === 0) resolve(data);
resolve(data); },
} fail: (err) => {
reject(err);
uni.showToast({
title: "获取数据失败",
icon: "none",
});
},
});
});
};
export const unbindDeviceAPI = (deviceId) => {
return new Promise((resolve, reject) => {
uni.request({
url: `${BASE_URL}/user/device/unbindDevice`,
method: "POST",
header: getAuthHeader(),
data: {
deviceId,
},
success: (res) => {
const { code, data } = res.data;
if (code === 0) resolve(data);
}, },
fail: (err) => { fail: (err) => {
reject(err); reject(err);
@@ -156,9 +178,122 @@ export const createPractiseAPI = (arrows) => {
}, },
success: (res) => { success: (res) => {
const { code, data } = res.data; const { code, data } = res.data;
if (code === 0) { if (code === 0) resolve(data);
resolve(data); },
} fail: (err) => {
reject(err);
uni.showToast({
title: "获取数据失败",
icon: "none",
});
},
});
});
};
export const createRoomAPI = (gameType, teamSize) => {
return new Promise((resolve, reject) => {
uni.request({
url: `${BASE_URL}/user/createroom`,
method: "POST",
header: getAuthHeader(),
data: {
gameType,
teamSize,
},
success: (res) => {
const { code, data } = res.data;
if (code === 0) resolve(data);
},
fail: (err) => {
reject(err);
uni.showToast({
title: "获取数据失败",
icon: "none",
});
},
});
});
};
export const getRoomAPI = (number) => {
return new Promise((resolve, reject) => {
uni.request({
url: `${BASE_URL}/user/room?number=${number}`,
method: "GET",
header: getAuthHeader(),
success: (res) => {
const { code, data } = res.data;
if (code === 0) resolve(data);
},
fail: (err) => {
reject(err);
uni.showToast({
title: "获取数据失败",
icon: "none",
});
},
});
});
};
export const destroyRoomAPI = (roomNumber) => {
return new Promise((resolve, reject) => {
uni.request({
url: `${BASE_URL}/user/room/destroyRoom`,
method: "POST",
header: getAuthHeader(),
data: {
roomNumber,
},
success: (res) => {
const { code, data } = res.data;
if (code === 0) resolve(data);
},
fail: (err) => {
reject(err);
uni.showToast({
title: "获取数据失败",
icon: "none",
});
},
});
});
};
export const exitRoomAPI = (number) => {
return new Promise((resolve, reject) => {
uni.request({
url: `${BASE_URL}/user/room/exitRoom`,
method: "POST",
header: getAuthHeader(),
data: {
number,
},
success: (res) => {
const { code, data } = res.data;
if (code === 0) resolve(data);
},
fail: (err) => {
reject(err);
uni.showToast({
title: "获取数据失败",
icon: "none",
});
},
});
});
};
export const startRoomAPI = (number) => {
return new Promise((resolve, reject) => {
uni.request({
url: `${BASE_URL}/user/room/start?number=${number}`,
method: "GET",
header: getAuthHeader(),
success: (res) => {
const { code, data } = res.data;
if (code === 0) resolve(data);
}, },
fail: (err) => { fail: (err) => {
reject(err); reject(err);

View File

@@ -1,4 +1,9 @@
<script setup> <script setup>
import useStore from "@/store";
import { storeToRefs } from "pinia";
const store = useStore();
const { user } = storeToRefs(store);
const tabs = [ const tabs = [
{ image: "../static/tab-vip.png" }, { image: "../static/tab-vip.png" },
{ image: "../static/tab-grow.png" }, { image: "../static/tab-grow.png" },
@@ -6,6 +11,12 @@ const tabs = [
]; ];
function handleTabClick(index) { function handleTabClick(index) {
if (!user.value.id) {
return uni.showToast({
title: "还未登录",
icon: "none",
});
}
if (index === 1) { if (index === 1) {
uni.navigateTo({ uni.navigateTo({
url: "/pages/my-growth", url: "/pages/my-growth",

View File

@@ -1,14 +1,44 @@
<script setup> <script setup>
import { ref } from "vue"; import { ref } from "vue";
import SButton from "@/components/SButton.vue"; import SButton from "@/components/SButton.vue";
const props = defineProps({}); import { createRoomAPI } from "@/apis";
const props = defineProps({
onConfirm: {
type: Function,
default: () => {},
},
});
const battleMode = ref(1); const battleMode = ref(1);
const step = ref(1); const step = ref(1);
const loading = ref(false);
const roomNumber = ref("");
const toBattleRoom = () => { const createRoom = async () => {
if (loading.value === true) return;
loading.value = true;
const result = await createRoomAPI(
battleMode.value,
battleMode.value === 1 ? 2 : 10
);
if (result.number) roomNumber.value = result.number;
step.value = 2;
loading.value = false;
};
const enterRoom = () => {
props.onConfirm();
uni.navigateTo({ uni.navigateTo({
url: "/pages/battle-room", url: `/pages/battle-room?roomNumber=${roomNumber.value}`,
});
};
const setClipboardData = () => {
uni.setClipboardData({
data: roomNumber.value,
success() {
uni.showToast({ title: "复制成功" });
},
}); });
}; };
</script> </script>
@@ -41,15 +71,15 @@ const toBattleRoom = () => {
<text>敬请期待</text> <text>敬请期待</text>
</view> </view>
</view> </view>
<SButton v-if="step === 1" :onClick="() => (step = 2)">下一步</SButton> <SButton v-if="step === 1" :onClick="createRoom">下一步</SButton>
<view v-if="step === 2" class="room-info"> <view v-if="step === 2" class="room-info">
<view> <view>
<text>房间号</text> <text>房间号</text>
<text>3245</text> <text>{{ roomNumber }}</text>
</view> </view>
<SButton width="60vw" :onClick="toBattleRoom">进入房间</SButton> <SButton width="60vw" :onClick="enterRoom">进入房间</SButton>
<text>30分钟无人进入则房间无效</text> <text>30分钟无人进入则房间无效</text>
<view>复制房间信息邀请朋友进入</view> <view @click="setClipboardData">复制房间信息邀请朋友进入</view>
</view> </view>
</view> </view>
</template> </template>

View File

@@ -33,7 +33,7 @@ defineProps({
display: flex; display: flex;
align-items: center; align-items: center;
padding: 0 15px; padding: 0 15px;
width: 100vw; width: 100%;
} }
.container > image { .container > image {
width: 20%; width: 20%;

View File

@@ -1,5 +1,7 @@
<script setup> <script setup>
import { ref } from "vue"; import { ref, onUnmounted } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import Container from "@/components/Container.vue";
import AppBackground from "@/components/AppBackground.vue"; import AppBackground from "@/components/AppBackground.vue";
import Header from "@/components/Header.vue"; import Header from "@/components/Header.vue";
import Guide from "@/components/Guide.vue"; import Guide from "@/components/Guide.vue";
@@ -10,24 +12,75 @@ import BattleFooter from "@/components/BattleFooter.vue";
import BowPower from "@/components/BowPower.vue"; import BowPower from "@/components/BowPower.vue";
import ShootProgress from "@/components/ShootProgress.vue"; import ShootProgress from "@/components/ShootProgress.vue";
import PlayersRow from "@/components/PlayersRow.vue"; import PlayersRow from "@/components/PlayersRow.vue";
import { getRoomAPI, destroyRoomAPI, exitRoomAPI, startRoomAPI } from "@/apis";
import { MESSAGETYPES } from "@/constants";
import websocket from "@/websocket";
import useStore from "@/store";
import { storeToRefs } from "pinia";
const store = useStore();
const { user } = storeToRefs(store);
const step = ref(1); const step = ref(1);
const room = ref({});
const roomNumber = ref("");
const seats = new Array(10).fill(1); const seats = new Array(10).fill(1);
const players = new Array(7).fill(1); const players = new Array(7).fill(1);
const teams = [{ name: "选手1", avatar: "../static/avatar.png" }]; const teams = [{ name: "选手1", avatar: "../static/avatar.png" }];
onLoad(async (options) => {
if (options.roomNumber) {
roomNumber.value = options.roomNumber;
const result = await getRoomAPI(options.roomNumber);
room.value = result;
}
});
onUnmounted(() => {
websocket.closeWebSocket();
if (user.value.id === room.value.creator) {
destroyRoomAPI(room.value.id);
} else {
exitRoomAPI(room.value.id);
}
});
const startGame = async () => {
const result = await startRoomAPI(room.value.number);
console.log(result);
step.value = 2;
const token = uni.getStorageSync("token");
websocket.createWebSocket(token, (content) => {
const messages = JSON.parse(content).data.updates || [];
messages.forEach((msg) => {
if (msg.constructor === MESSAGETYPES.ShootSyncMeArrowID) {
scores.value.push(msg.target);
if (scores.value.length === total) {
showScore.value = true;
websocket.closeWebSocket();
}
}
});
});
};
</script> </script>
<template> <template>
<view class="container"> <Container title="对战">
<AppBackground /> <view class="standby-phase" v-if="step === 1">
<Header title="对战" />
<view v-if="step === 1">
<Guide> <Guide>
<view :style="{ display: 'flex', flexDirection: 'column' }"> <view :style="{ display: 'flex', flexDirection: 'column' }">
<text :style="{ color: '#fed847' }">人都到齐了吗</text> <text :style="{ color: '#fed847' }">人都到齐了吗</text>
<text>天赋异禀的弓箭手们比赛即将开始</text> <text>天赋异禀的弓箭手们比赛即将开始</text>
</view> </view>
</Guide> </Guide>
<view class="players"> <view v-if="room.battleType === 1 && room.count === 2" class="one-on-one">
<image src="../static/1v1-bg.png" mode="widthFix" />
<view>
<image :src="user.avatarUrl" mode="widthFix" />
<image src="../static/versus.png" mode="widthFix" />
<view>
<image src="../static/question-mark.png" mode="widthFix" />
</view>
</view>
</view>
<view v-if="room.battleType === 2 && room.count === 10" class="players">
<view v-for="(_, index) in seats" :key="index"> <view v-for="(_, index) in seats" :key="index">
<image src="../static/player-bg.png" mode="widthFix" /> <image src="../static/player-bg.png" mode="widthFix" />
<image <image
@@ -48,9 +101,20 @@ const teams = [{ name: "选手1", avatar: "../static/avatar.png" }];
/> />
</view> </view>
</view> </view>
<SButton :onClick="() => (step = 2)">进入大乱斗</SButton> <view>
<SButton
v-if="room.battleType === 1 && room.count === 2"
:onClick="startGame"
>进入对战</SButton
>
<SButton
v-if="room.battleType === 2 && room.count === 10"
:onClick="startGame"
>进入大乱斗</SButton
>
<text class="tips">创建者点击下一步所有人即可进入游戏</text> <text class="tips">创建者点击下一步所有人即可进入游戏</text>
</view> </view>
</view>
<view v-if="step === 2"> <view v-if="step === 2">
<BattleHeader /> <BattleHeader />
<Guide noBg> <Guide noBg>
@@ -71,12 +135,14 @@ const teams = [{ name: "选手1", avatar: "../static/avatar.png" }];
<BowTarget :power="45" currentRound="1" totalRound="3" debug /> <BowTarget :power="45" currentRound="1" totalRound="3" debug />
<BattleFooter :blueTeam="[6, 2, 3]" :redTeam="[4, 5, 2]" /> <BattleFooter :blueTeam="[6, 2, 3]" :redTeam="[4, 5, 2]" />
</view> </view>
</view> </Container>
</template> </template>
<style scoped> <style scoped>
.container { .standby-phase {
width: 100vw; width: 100%;
height: calc(100% - 40px);
overflow-x: hidden;
} }
.players { .players {
display: flex; display: flex;
@@ -144,4 +210,43 @@ const teams = [{ name: "选手1", avatar: "../static/avatar.png" }];
.player-unknow > image { .player-unknow > image {
width: 40%; width: 40%;
} }
.one-on-one {
width: calc(100vw - 30px);
height: 120vw;
margin: 15px;
}
.one-on-one > image:first-child {
position: absolute;
width: calc(100vw - 30px);
z-index: -1;
}
.one-on-one > view {
display: flex;
justify-content: center;
align-items: center;
height: 95%;
}
.one-on-one > view > image:first-child {
width: 70px;
transform: translateY(-45px);
border-radius: 50%;
}
.one-on-one > view > image:nth-child(2) {
width: 120px;
}
.one-on-one > view > view:nth-child(3) {
width: 70px;
height: 70px;
border-radius: 50%;
background-color: #ccc;
display: flex;
justify-content: center;
align-items: center;
transform: translateY(45px);
}
.one-on-one > view > view:nth-child(3) > image {
width: 25px;
height: 25px;
margin-right: 2px;
}
</style> </style>

View File

@@ -6,18 +6,28 @@ 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 CreateRoom from "@/components/CreateRoom.vue"; import CreateRoom from "@/components/CreateRoom.vue";
import { getRoomAPI } from "@/apis";
const showModal = ref(false); const showModal = ref(false);
const warnning = ref(""); const warnning = ref("");
const roomNumber = ref(""); const roomNumber = ref("");
const enterRoom = () => { const enterRoom = async () => {
if (!roomNumber.value) { if (!roomNumber.value) {
warnning.value = "请输入房间号"; warnning.value = "请输入房间号";
showModal.value = true;
} else {
const room = await getRoomAPI(roomNumber.value);
if (room.number) {
roomNumber.value = "";
uni.navigateTo({
url: `/pages/battle-room?roomNumber=${roomNumber.value}`,
});
} else { } else {
warnning.value = "查无此房"; warnning.value = "查无此房";
}
showModal.value = true; showModal.value = true;
}
}
}; };
const createRoom = () => { const createRoom = () => {
warnning.value = ""; warnning.value = "";
@@ -61,7 +71,7 @@ const createRoom = () => {
<view v-if="warnning" class="warnning"> <view v-if="warnning" class="warnning">
{{ warnning }} {{ warnning }}
</view> </view>
<CreateRoom v-if="!warnning" /> <CreateRoom v-if="!warnning" :onConfirm="() => (showModal = false)" />
</SModal> </SModal>
</view> </view>
</template> </template>

View File

@@ -11,15 +11,26 @@ const store = useStore();
// 使用storeToRefs用于UI里显示保持响应性 // 使用storeToRefs用于UI里显示保持响应性
const { user } = storeToRefs(store); const { user } = storeToRefs(store);
const isLogin = () => {
if (!user.value.id) {
uni.showToast({
title: "还未登录",
icon: "none",
});
}
return !!user.value.id;
};
const toLoginPage = () => { const toLoginPage = () => {
uni.navigateTo({ uni.navigateTo({
url: "/pages/login", url: "/pages/login",
}); });
}; };
const toFristTryPage = () => { const toFristTryPage = () => {
if (isLogin()) {
uni.navigateTo({ uni.navigateTo({
url: "/pages/first-try", url: "/pages/first-try",
}); });
}
}; };
const toRankingPage = () => { const toRankingPage = () => {
uni.navigateTo({ uni.navigateTo({
@@ -27,27 +38,35 @@ const toRankingPage = () => {
}); });
}; };
const toFriendBattlePage = () => { const toFriendBattlePage = () => {
if (isLogin()) {
uni.navigateTo({ uni.navigateTo({
url: "/pages/friend-battle", url: "/pages/friend-battle",
}); });
}
}; };
const toPractisePage = () => { const toPractisePage = () => {
if (isLogin()) {
uni.navigateTo({ uni.navigateTo({
url: "/pages/practise", url: "/pages/practise",
}); });
}
}; };
const toQquipmentPage = () => { const toQquipmentPage = () => {
if (isLogin()) {
uni.navigateTo({ uni.navigateTo({
url: "/pages/equipment-debug", url: "/pages/equipment-debug",
}); });
}
}; };
const toAddDevicePage = () => { const toAddDevicePage = () => {
if (isLogin()) {
uni.navigateTo({ uni.navigateTo({
url: "/pages/add-device", url: "/pages/add-device",
}); });
}
}; };
// 获取全局配置 // 获取全局配置

BIN
src/static/1v1-bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 KiB