🎈 perf: 完善五子棋主框架
This commit is contained in:
parent
24ff08605b
commit
b9b1063d5b
2
.gitignore
vendored
2
.gitignore
vendored
@ -31,3 +31,5 @@ coverage
|
||||
|
||||
*.lock
|
||||
*.lockb
|
||||
|
||||
components.d.ts
|
||||
|
5
components.d.ts
vendored
5
components.d.ts
vendored
@ -7,9 +7,7 @@ export {}
|
||||
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
'菜单': typeof import('./src/assets/icons/菜单.svg')['default']
|
||||
AppHeaderMenu: typeof import('./src/components/app/AppHeaderMenu.vue')['default']
|
||||
AppHeaderUser: typeof import('./src/components/app/AppHeaderUser.vue')['default']
|
||||
'Components.d': typeof import('./components.d.ts')['default']
|
||||
ElAvatar: typeof import('element-plus/es')['ElAvatar']
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElContainer: typeof import('element-plus/es')['ElContainer']
|
||||
@ -37,7 +35,6 @@ declare module 'vue' {
|
||||
IconCsValidate: typeof import('~icons/cs/validate')['default']
|
||||
IconEpLoading: typeof import('~icons/ep/loading')['default']
|
||||
IconEpUserFilled: typeof import('~icons/ep/user-filled')['default']
|
||||
Menu: typeof import('./src/assets/icons/Menu.svg')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
}
|
||||
|
20
src/App.vue
20
src/App.vue
@ -9,7 +9,7 @@
|
||||
<h3 v-show="!smallMobileScreen" class="app__title" @click="navigate">社团展示系统</h3>
|
||||
</router-link>
|
||||
<app-header-menu v-show="!mobileScreen" mode="horizontal" />
|
||||
<el-dropdown v-if="!mobileScreen">
|
||||
<el-dropdown v-if="!mobileScreen && userStore.logined">
|
||||
<app-header-user
|
||||
@login="showLoginRegisterDialog = true"
|
||||
@logout="logout"
|
||||
@ -51,6 +51,7 @@
|
||||
@login="showLoginRegisterDialog = true"
|
||||
@click="mobileScreen && (showVerticalHeaderMenu = true)"
|
||||
/>
|
||||
<div v-else class="app-vertical-drawer__not-login-title flex center">尚未登录</div>
|
||||
<el-button
|
||||
v-if="userStore.logined"
|
||||
class="app-vertical-drawer__logout"
|
||||
@ -71,9 +72,12 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.app-header-user {
|
||||
> :first-child:not(.app-header-menu) {
|
||||
margin-top: 10px;
|
||||
}
|
||||
&__not-login-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
&__logout {
|
||||
margin: 0 10px;
|
||||
}
|
||||
@ -110,6 +114,7 @@
|
||||
<script setup lang="ts">
|
||||
import axiosInstance, { type RawResp } from '@/api/index';
|
||||
import AppHeaderMenu from '@/components/app/AppHeaderMenu.vue';
|
||||
import AppHeaderUser from '@/components/app/AppHeaderUser.vue';
|
||||
import LoginRegisterDialog, {
|
||||
loginImplKey,
|
||||
registerImplKey,
|
||||
@ -162,13 +167,10 @@ async function logout() {
|
||||
await userStore.updateSelfUserInfo(true);
|
||||
}
|
||||
const showVerticalHeaderMenu = ref(false);
|
||||
watch(
|
||||
() => mobileScreen,
|
||||
(v) => {
|
||||
if (v) return;
|
||||
showVerticalHeaderMenu.value = false;
|
||||
},
|
||||
);
|
||||
watch(mobileScreen, (v) => {
|
||||
if (v) return;
|
||||
showVerticalHeaderMenu.value = false;
|
||||
});
|
||||
// onMounted(() => {
|
||||
// userStore.token = null;
|
||||
// userStore.updateSelfUserInfo(true);
|
||||
|
@ -20,6 +20,9 @@ body {
|
||||
.flex-column {
|
||||
flex-direction: column;
|
||||
}
|
||||
.flex-stretch {
|
||||
flex: 1;
|
||||
}
|
||||
.align-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import { useUserStore } from '@/stores/user';
|
||||
import { PageErrorType, type PageErrorReason } from '@/views/ErrorPage.vue';
|
||||
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router';
|
||||
import { RoutePermissionId } from './permissions';
|
||||
import { timeout } from '@/utils';
|
||||
|
||||
declare module 'vue-router' {
|
||||
interface RouteMeta {
|
||||
|
@ -28,8 +28,8 @@ function createResponseSchema<T extends z.ZodTypeAny>(data: T) {
|
||||
]);
|
||||
}
|
||||
|
||||
export type SucceedResponse<T> = Extract<T, { type: 'success' }>;
|
||||
export type ErrorResponse<T> = Exclude<SucceedResponse<T>, T>;
|
||||
export type SucceedResponseOf<T> = Extract<T, { type: 'success' }>;
|
||||
export type ErrorResponseOf<T> = Exclude<SucceedResponseOf<T>, T>;
|
||||
export const ordinarySchema = createResponseSchema(z.literal(true));
|
||||
export const loginResponseSchema = ordinarySchema;
|
||||
export const registerResponseSchema = ordinarySchema;
|
||||
@ -53,7 +53,7 @@ export const userInfoResponseSchema = createResponseSchema(
|
||||
),
|
||||
}),
|
||||
);
|
||||
export type SucceedUserInfoResponse = SucceedResponse<z.infer<typeof userInfoResponseSchema>>;
|
||||
export type SucceedUserInfoResponse = SucceedResponseOf<z.infer<typeof userInfoResponseSchema>>;
|
||||
export type UserInfo = SucceedUserInfoResponse['data'];
|
||||
export const verifyResponseSchema = createResponseSchema(
|
||||
z
|
||||
|
@ -1,11 +1,20 @@
|
||||
<template>
|
||||
<el-main class="gobang-page__wrapper">
|
||||
<el-container>
|
||||
<el-main class="gobang-page__wrapper flex">
|
||||
<el-container class="flex-stretch">
|
||||
<el-header class="gobang-page-header flex align-center">
|
||||
<div class="gobang-page-header__title">五子棋</div>
|
||||
<el-button type="success">创建房间</el-button>
|
||||
<el-button type="primary" :loading="loading" @click="refresh">刷新</el-button>
|
||||
<el-button type="primary">单人游戏</el-button>
|
||||
</el-header>
|
||||
<el-main class="gobang-page-main"></el-main>
|
||||
<el-main
|
||||
v-loading="loading"
|
||||
class="gobang-page-main flex-column"
|
||||
:class="{ flex: !rooms.length, center: !rooms.length }"
|
||||
>
|
||||
<div class="gobang-page-main__no-rooms-title">没有房间</div>
|
||||
<el-button type="primary" :loading="loading" @click="refresh">刷新</el-button>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-main>
|
||||
</template>
|
||||
@ -24,13 +33,41 @@
|
||||
gap: 10px;
|
||||
border-bottom: 1px solid var(--el-border-color);
|
||||
}
|
||||
.gobang-page-main {
|
||||
gap: 10px;
|
||||
&__no-rooms-title {
|
||||
font-size: 25px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
interface Room {}
|
||||
import type { SucceedUserInfoResponse } from '@/schemas';
|
||||
import { useUserStore } from '@/stores/user';
|
||||
import { useWebSocket } from '@vueuse/core';
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
const userStore = useUserStore();
|
||||
enum RoomState {
|
||||
CREATED,
|
||||
WAITING,
|
||||
GAMING,
|
||||
}
|
||||
interface Room {
|
||||
/** 房间UUID */
|
||||
id: string;
|
||||
state: RoomState;
|
||||
pieces: unknown;
|
||||
isWhiteAcceptRestart: boolean;
|
||||
isBlackAcceptRestart: boolean;
|
||||
canWhiteDown: boolean;
|
||||
}
|
||||
type GobangResp =
|
||||
| {
|
||||
name: 'RoomInfo';
|
||||
name: 'UserInfo';
|
||||
payload: SucceedUserInfoResponse;
|
||||
}
|
||||
| {
|
||||
name: 'RoomList';
|
||||
payload: {
|
||||
rooms: Room[];
|
||||
};
|
||||
@ -43,16 +80,34 @@ type GobangResp =
|
||||
};
|
||||
type RespOf<T extends GobangResp['name']> = Extract<GobangResp, { name: T }>;
|
||||
type RespHandlerMap = {
|
||||
[P in GobangResp['name']]: (payload: RespOf<P>['payload']) => void;
|
||||
[P in GobangResp['name']]?: (payload: RespOf<P>['payload']) => void;
|
||||
};
|
||||
const respHandlers: RespHandlerMap = {
|
||||
RoomInfo(payload) {
|
||||
RoomList(payload) {
|
||||
rooms.value = payload.rooms;
|
||||
loading.value = false;
|
||||
},
|
||||
PlayerJoin: (payload) => {
|
||||
rooms.value = payload.rooms;
|
||||
},
|
||||
};
|
||||
const rooms = ref<Room[]>();
|
||||
// const { data } = useWebSocket<GobangResp>(`ws://127.0.0.1:58080/chess/${token}`);
|
||||
const rooms = ref<Room[]>([]);
|
||||
const loading = ref(false);
|
||||
watch(loading, (loading) => {
|
||||
if (!loading) return;
|
||||
send(JSON.stringify({ name: 'RoomList' }));
|
||||
console.log(1);
|
||||
});
|
||||
const { send } = useWebSocket<string>(`ws://wzpmc.cn:18080/chess/${userStore.token}`, {
|
||||
onMessage(_, { data }) {
|
||||
const resp: GobangResp = JSON.parse(data);
|
||||
respHandlers[resp.name]?.(resp.payload as any);
|
||||
},
|
||||
});
|
||||
function refresh() {
|
||||
loading.value = true;
|
||||
}
|
||||
onMounted(() => {
|
||||
refresh();
|
||||
});
|
||||
</script>
|
||||
|
Loading…
x
Reference in New Issue
Block a user