创建 matching 匹配页面及完成到 playing 页面的跳转

This commit is contained in:
2023-02-27 16:04:58 +08:00
parent 51fda496d1
commit e6e6bc15e9
14 changed files with 478 additions and 11 deletions
+118
View File
@@ -0,0 +1,118 @@
// 定义匹配页面
<template>
<div class="matchground">
<div class="row">
<!-- 左边 6 ,右边 6 -->
<div class="col-6">
<!-- 用户自己的信息 -->
<div class="user-photo">
<img :src="$store.state.user.photo" alt="">
</div>
<div class="user-username">{{ $store.state.user.username }}</div>
</div>
<div class="col-6">
<!-- 对手的信息 -->
<div class="opponent-photo">
<img :src="$store.state.pk.opponent_photo" alt="">
</div>
<div class="opponent-username">{{ $store.state.pk.opponent_username }}</div>
</div>
<div class="col-12" style="text-align:center">
<button type="button" class="btn btn-warning btn-lg" v-on:click="click_match_btn">{{ match_btn_info
}}</button>
</div>
</div>
</div>
</template>
<script>
import { useStore } from 'vuex';
import { ref } from 'vue';
export default {
setup: () => {
const store = useStore();
let match_btn_info = ref("开始匹配")
const click_match_btn = () => {
if (match_btn_info.value === "开始匹配") {
match_btn_info.value = "取消";
// 向后端发请求,使用 JSON.stringify() 将一个 JSON 封装成一字符串
store.state.pk.socket.send(JSON.stringify({
// 传一个 event 域
event:"start-matching",
}));
} else {
match_btn_info.value = "开始匹配";
store.state.pk.socket.send(JSON.stringify({
event:"stop-matching",
}))
}
}
return {
match_btn_info,
click_match_btn,
}
}
};
</script>
<style scoped>
.matchground {
/* 60% 浏览器宽度, 70% 浏览器高度 */
width: 60vw;
height: 70vh;
/* 背景色: rgba 加上透明通道 */
background: rgba(70, 70, 70, 0.5);
/* 距上边距40px,左右居中 */
margin: 40px auto;
}
.user-photo {
/* 居中 */
text-align: center;
padding-top: 10vh;
}
.user-photo>img {
/* 头像圆形 */
border-radius: 50%;
width: 20vh;
}
.user-username {
text-align: center;
font-size: 24px;
/* 字体加粗 */
font-weight: 600;
color: rgba(255, 255, 255, 0.9);
margin-top: 3vh;
}
.opponent-photo {
/* 居中 */
text-align: center;
padding-top: 10vh;
}
.opponent-photo>img {
/* 头像圆形 */
border-radius: 50%;
width: 20vh;
}
.opponent-username {
text-align: center;
font-size: 24px;
/* 字体加粗 */
font-weight: 600;
color: rgba(255, 255, 255, 0.9);
margin-top: 3vh;
}
button {
margin-top: 15vh;
width: 15%;
}
</style>
+2 -2
View File
@@ -99,9 +99,9 @@ export default {
let route_name = computed(() => route.name);
const logout = () => {
console.log("退出前:" + store.state.user.token);
// console.log("退出前:" + store.state.user.token);
store.dispatch("logout");
console.log("退出后:" + store.state.user.token);
// console.log("退出后:" + store.state.user.token);
};
return {
+1
View File
@@ -1,6 +1,7 @@
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
// 全局挂载 store , 后面写的 vue 页面都可以通过 $store 来调用
import store from './store'
createApp(App).use(store).use(router).mount('#app')
+2
View File
@@ -1,6 +1,7 @@
import { createStore } from "vuex";
import ModuleUser from "./user";
import ModuleBot from './bot';
import ModulePk from './pk';
export default createStore({
state: {},
@@ -10,5 +11,6 @@ export default createStore({
modules: {
user: ModuleUser,
bot: ModuleBot,
pk: ModulePk,
},
});
+29
View File
@@ -0,0 +1,29 @@
export default {
state: {
// 当前状态:用于判断时匹配中还是已经匹配完成: matching 表示匹配中(匹配界面), playing 表示匹配完成(对战界面)
status: "matching",
// socket 信息
socket: null,
// 对手信息
opponent_username: "",
opponent_photo: "",
},
getters: {},
mutations: {
// 更新 socket 信息
updateSocket(state, socket) {
state.socket = socket;
},
// 更新对手信息
updateOpponent(state, opponent) {
state.opponent_username = opponent.username;
state.opponent_photo = opponent.photo;
},
// 更新匹配状态信息
updateStatus(state, status) {
state.status = status;
},
},
actions: {},
module: {},
};
+71 -1
View File
@@ -1,14 +1,84 @@
<template>
<PlayGround>对战</PlayGround>
<PlayGround v-if="$store.state.pk.status === 'playing'" />
<MatchGround v-else-if="$store.state.pk.status === 'matching'" />
</template>
<script>
import PlayGround from "../../components/PlayGround.vue";
import MatchGround from "../../components/MatchGround.vue"
// onMounted 用于组件被挂载时, onUnmounted 用于组件被解除挂载时
import { onMounted, onUnmounted } from "vue";
// 取出全局变量
import { useStore } from "vuex";
export default {
components: {
PlayGround,
MatchGround
},
// 创建前后端连接:当当前页面组件被加载时,建立连接,故使用 onMounted 函数
setup() {
const store = useStore();
// 定义后端连接的 url, 使用 ws 协议
const socketUrl = `ws://127.0.0.1:3000/websocket/${store.state.user.token}/`;
let socket = null;
// 当前组件被挂载的时候,需要创建后端连接,并且需要将连接信息存储到 store 全局变量里
onMounted(() => {
// 定义默认匹配页面对手信息(匹配等待过程中使用)
store.commit("updateOpponent", {
username: "等待对手",
photo: "https://typoraflykhan.oss-cn-beijing.aliyuncs.com/202302251825860.png",
})
// 新建 WebSocket
socket = new WebSocket(socketUrl);
store.commit("updateSocket", socket);
// 调用 socket 函数: onopen 对应后端的 WebSocketServer 的 onOpen 方法
socket.onopen = () => {
console.log("frontend: connected!");
// 将 socket 存到全局变量里面
store.commit("updateSocket", socket);
}
socket.onmessage = (msg) => {
const data = JSON.parse(msg.data);
// console.log(data);
// data.event 在后端定义
if (data.event === "start-matching") {
// 匹配成功,更新对手信息
store.commit("updateOpponent", {
username: data.opponent_username,
photo: data.opponent_photo,
})
// 匹配成功,延迟两秒后:更改匹配状态 matching -> playing
setTimeout(() => {
store.commit("updateStatus", "playing");
}, 2000);
}
}
socket.onclose = () => {
console.log("frontend: disconnected");
}
});
// 离开 pk 页面时,调用 onUnmounted函数
onUnmounted(() => {
// 断开连接
socket.close();
// 离开 pk 页面时,将匹配状态改回 matching
store.commit("updateStatus", "matching");
});
}
};
</script>