🎈 perf: 暂存

This commit is contained in:
Litrix 2024-12-16 14:58:00 +08:00
parent 5624d1ccd4
commit c0e0199056
4 changed files with 118 additions and 66 deletions

View File

@ -23,7 +23,6 @@
.app-header-menu--horizontal {
flex: 1;
margin-right: 10px;
border-bottom: none;
}
.app-header-menu :is(.el-menu-item, .el-sub-menu__title) {
user-select: none;

View File

@ -1,17 +1,27 @@
<template>
<div class="gobang-header__content size-stretch flex align-center">
<el-header class="gobang-header flex align-center">
<router-link custom :to="{ name: 'GobangList' }" v-slot="{ navigate }">
<div class="gobang-header__content-left flex align-center" @click="navigate">
<div class="gobang-header__left flex align-center" @click="navigate">
<el-icon :size="30"><icon-cs-gobang /></el-icon>
<h2 v-show="!smLess" class="gobang-header__title">五子棋</h2>
</div>
</router-link>
<slot></slot>
</div>
</el-header>
</template>
<style lang="scss" scoped>
.gobang-header__content-left {
gap: 10px;
.gobang-header {
--el-header-height: var(--header-height);
background-color: white;
border-bottom: 1px solid var(--el-border-color);
position: fixed;
top: var(--header-height);
width: 100%;
z-index: 100;
user-select: none;
&__left {
gap: 10px;
}
}
</style>
<script setup lang="ts">

View File

@ -1,15 +1,13 @@
<template>
<el-main class="gobang-list-page__wrapper flex">
<el-container direction="vertical">
<el-header class="gobang-header">
<gobang-header-content class="justify-between">
<div class="flex align-center">
<el-button type="primary" :loading="loading" @click="refresh">刷新</el-button>
<el-button @click="showDialog = true" type="success">创建房间</el-button>
<el-button type="primary" disabled>单人游戏</el-button>
</div>
</gobang-header-content>
</el-header>
<gobang-header class="justify-between">
<div class="flex align-center">
<el-button type="primary" :loading="loading" @click="refresh">刷新</el-button>
<el-button @click="showDialog = true" type="success">创建房间</el-button>
<el-button type="primary" disabled>单人游戏</el-button>
</div>
</gobang-header>
<el-main
v-loading="loading"
:element-loading-text="loadingText"
@ -39,7 +37,6 @@
</el-main>
</template>
<style lang="scss" scoped>
@import url('@/assets/gobang-header.scss');
.gobang-list-page__wrapper {
background-color: white;
}
@ -85,46 +82,93 @@ export function toRoomRender(room: Room): RoomRender {
briefId: room.id.slice(0, 8),
});
}
export type Request =
| SimplePart<'RoomList'>
| SimplePart<'CreateRoom'>
| Part<'PlayerJoin', { roomId: RoomId }>
| SimplePart<'ResetRoom'>;
export type RequestOf<T extends Request['name']> = Extract<Request, { name: T }>;
export interface SimplePart<N extends string> {
name: N;
}
export interface Part<N extends string, P extends Record<string, unknown>> extends SimplePart<N> {
type Payload = Record<string, unknown>;
export interface PayloadPart<N extends string, P extends Payload> extends SimplePart<N> {
payload: P;
}
export type RespErrorPart = Part<'Error', { reason: string }>;
export type Request =
| SimplePart<'RoomList'>
| SimplePart<'CreateRoom'>
| PayloadPart<'PlayerJoin', { roomId: RoomId }>
| SimplePart<'ResetRoom'>;
export type RequestOf<T extends Request['name']> = Extract<Request, { name: T }>;
export const relations = {
RoomList: 'RoomList',
RoomCreated: 'CreateRoom',
PlayerSideAllocation: 'PlayerJoin',
};
const reversedRelations: Record<string, string[]> = {};
for (const [k, v] of Object.entries(relations)) {
(reversedRelations[v] ??= []).push(k);
}
export interface RelatedPart<N extends keyof typeof relations, P extends Payload>
extends PayloadPart<N, P> {
originalPacketName?: (typeof relations)[N];
}
export interface RespErrorPart extends PayloadPart<'Error', { reason: string }> {
originalPacketName: Request['name'];
}
export type Resp =
| RespErrorPart
| Part<'UserInfo', SucceedUserInfoResponse>
// RoomList
| Part<'RoomList', { rooms: Room[] }>
// CreateRoom
| Part<'RoomCreated', { roomId: RoomId }>
// PlayerJoin
| Part<'PlayerSideAllocation', { isWhite: boolean }>;
| PayloadPart<'UserInfo', SucceedUserInfoResponse>
| RelatedPart<'RoomList', { rooms: Room[] }>
| RelatedPart<'RoomCreated', { roomId: RoomId }>
| RelatedPart<'PlayerSideAllocation', { isWhite: boolean }>;
export type RespOf<T extends Resp['name']> = Extract<Resp, { name: T }>;
export type RespHandlerMap = {
[P in Resp['name']]?: (payload: RespOf<P>['payload']) => void;
};
export function useGobangSocket(respHandlers: RespHandlerMap) {
interface UseGobangSocketOptions {
succeed: {
[P in Resp['name']]?: (payload: RespOf<P>['payload']) => void;
};
error: {
[P in Resp['name']]?: (reason: string) => void;
};
finally: object;
}
export function useGobangSocket(options: UseGobangSocketOptions) {
const { succeed: handlers } = options;
const userStore = useUserStore();
return useWebSocket<string>(`ws://wzpmc.cn:18080/chess/${userStore.token}`, {
onMessage(_, { data }) {
const resp: Resp = JSON.parse(data);
console.log(resp);
respHandlers[resp.name]?.(resp.payload as any);
const map = new Map<string, Set<string>>();
const { send: _send } = useWebSocket<string>(
`ws://172.16.114.84:58080/chess/${userStore.token}`,
{
onMessage(_, { data }) {
const resp: Resp = JSON.parse(data);
const { name } = resp;
let excFinally = true;
if ('originalPacketName' in resp) {
const set = map.get(name);
if (set) {
set.delete(name);
if (!set.size) {
map.delete(name);
} else {
excFinally = false;
}
}
}
if (excFinally) {
options.finally;
}
console.log(resp);
handlers[name]?.(resp.payload as any);
},
},
});
);
return {
send(data: Request) {
const respNames = reversedRelations[data.name];
map.set(data.name, respNames && new Set(respNames));
_send(JSON.stringify(data));
},
};
}
</script>
<script lang="ts" setup>
import CreateGobangRoomDialog from '@/components/gobang/CreateGobangRoomDialog.vue';
import GobangHeaderContent from '@/components/gobang/GobangHeaderContent.vue';
import GobangHeader from '@/components/gobang/GobangHeader.vue';
import router from '@/router';
import type { SucceedUserInfoResponse } from '@/schemas';
import { useUserStore } from '@/stores/user';
@ -136,17 +180,19 @@ const rooms = ref<RoomRender[]>([]);
const loading = ref(false);
const loadingText = ref<string>();
const { send } = useGobangSocket({
Error(p) {
ElMessage.error(p.reason);
loading.value = false;
},
RoomList(p) {
rooms.value = p.rooms.map(toRoomRender);
console.log(toRaw(rooms.value));
loading.value = false;
},
RoomCreated(p) {
play(p.roomId);
succeed: {
Error(p) {
ElMessage.error(p.reason);
loading.value = false;
},
RoomList(p) {
rooms.value = p.rooms.map(toRoomRender);
console.log(toRaw(rooms.value));
loading.value = false;
},
RoomCreated(p) {
play(p.roomId);
},
},
});
function refresh() {

View File

@ -4,17 +4,15 @@
class="gobang-play-page__wrapper flex"
>
<el-container v-if="state !== 'firstLoading'">
<el-header class="gobang-header">
<gobang-header-content class="justify-between">
<div class="flex align-center">
<template v-if="roomId">
<h2 v-show="!smLess">蓝色基因对战平台</h2>
<h3 class="gobang-play-page__brief-id flex center">#{{ roomId.slice(0, 8) }}</h3>
</template>
<h2 v-else>单人游戏</h2>
</div>
</gobang-header-content>
</el-header>
<gobang-header class="justify-between">
<div class="flex align-center">
<template v-if="roomId">
<h2 v-show="!smLess">蓝色基因对战平台</h2>
<h3 class="gobang-play-page__brief-id flex center">#{{ roomId.slice(0, 8) }}</h3>
</template>
<h2 v-else>单人游戏</h2>
</div>
</gobang-header>
<el-main class="gobang-play-page-main center flex">
<div class="gobang-chessboard" :style="{}">
<canvas class="gobang-chessboard__background" ref="canvas"></canvas>
@ -41,7 +39,6 @@
</el-main>
</template>
<style lang="scss" scoped>
@import url('@/assets/gobang-header.scss');
:deep(.gobang-header__content-left) {
position: relative;
z-index: 1;
@ -91,7 +88,7 @@
}
</style>
<script setup lang="ts">
import GobangHeaderContent from '@/components/gobang/GobangHeaderContent.vue';
import GobangHeader from '@/components/gobang/GobangHeader.vue';
import { useMediaStore } from '@/stores/media';
import { create2DArray } from '@/utils';
import { type RequestOf, type RoomId, useGobangSocket } from '@/views/GobangListPage.vue';