159 lines
5.1 KiB
Vue
Raw Normal View History

<template>
2024-12-12 18:03:40 +08:00
<el-container class="app__container">
<el-header class="app-header flex align-center">
<router-link :custom="true" to="/" v-slot="{ navigate }">
<el-icon size="50" @click="navigate"><icon-cs-club /></el-icon>
<h3 class="app__title" @click="navigate">社团展示系统</h3>
</router-link>
<el-menu
class="app-header-menu justify-end"
mode="horizontal"
:default-active="$route.fullPath"
:router="true"
>
<el-menu-item index="/">首页</el-menu-item>
<el-sub-menu v-if="userStore.logined" class="festival-menu" index="festival">
<template #title>社团文化节</template>
<festival-menu-item src="/2048.png" title="2048" to="/2048" />
<festival-menu-item src="/gobang.svg" title="五子棋" to="/gobang" />
</el-sub-menu>
</el-menu>
<div class="app-header-user flex center" v-loading="userStore.initializing">
<template v-if="!userStore.initializing">
<template v-if="userInfo && userInfo.id !== -1">
<el-dropdown>
<div class="app-header-user__inner flex align-center">
<el-avatar :icon="userInfo.avatar ?? undefined"></el-avatar>
<div class="app-header-user__username">
{{ userInfo.name }}
</div>
2024-12-12 18:03:40 +08:00
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :icon="CloseBold" @click="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
2024-12-12 18:03:40 +08:00
</el-dropdown>
</template>
2024-12-12 18:03:40 +08:00
<el-button type="primary" v-else @click="showLoginRegisterDialog = true">登录</el-button>
</template>
</div>
2024-04-09 17:57:51 +08:00
</el-header>
2024-12-12 18:03:40 +08:00
<el-main class="app-main" v-loading="!!pageStore.pageLoadingCount">
<el-container class="app-router-component__wrapper">
<router-view v-slot="{ Component: comp }">
<transition appear mode="out-in" name="app-router-view">
<component class="app-router-component" :is="comp" />
</transition>
</router-view>
</el-container>
</el-main>
2024-04-09 17:57:51 +08:00
</el-container>
2024-12-12 18:03:40 +08:00
<login-register-dialog v-model="showLoginRegisterDialog" />
</template>
<style lang="scss" scoped>
2024-12-12 18:03:40 +08:00
.app__container {
min-height: 100vh;
}
.app-header {
2024-12-12 18:03:40 +08:00
--header-height: 50px;
--el-header-height: var(--header-height);
--el-loading-spinner-size: 30px;
background-color: white;
2024-12-12 18:03:40 +08:00
border-bottom: 1px solid var(--el-border-color);
}
2024-12-12 18:03:40 +08:00
.app-header-menu__wrapper {
flex: 1;
2024-04-14 14:40:34 +08:00
}
2024-12-12 18:03:40 +08:00
.app-header-menu {
flex: 1;
2024-12-12 18:03:40 +08:00
--el-menu-horizontal-height: var(--header-height);
2024-06-05 18:02:15 +08:00
margin-right: 10px;
}
2024-12-12 18:03:40 +08:00
.app-header-menu :is(.el-menu-item, .el-sub-menu__title) {
user-select: none;
}
2024-12-12 18:03:40 +08:00
.app-main {
padding: 0;
2024-12-12 18:03:40 +08:00
flex: 1;
display: flex;
}
2024-12-12 18:03:40 +08:00
.app-header-user {
min-width: 50px;
gap: 10px;
2024-06-05 18:02:15 +08:00
}
2024-12-12 18:03:40 +08:00
.app-header-user__inner {
gap: 10px;
2024-06-05 18:02:15 +08:00
}
2024-12-12 18:03:40 +08:00
.app-router-view-enter-active,
.app-router-view-leave-active {
transition: opacity 0.4s ease;
2024-06-05 18:02:15 +08:00
}
2024-12-12 18:03:40 +08:00
.app-router-view-enter-from,
.app-router-view-leave-to {
opacity: 0;
2024-04-09 17:57:51 +08:00
}
2024-12-12 18:03:40 +08:00
.app-router-component__wrapper {
flex: 1;
2024-04-14 14:40:34 +08:00
}
2024-04-09 17:57:51 +08:00
</style>
2024-12-12 18:03:40 +08:00
<script setup lang="ts">
import { CloseBold } from '@element-plus/icons-vue';
import type { AxiosError } from 'axios';
import { ElMessage } from 'element-plus';
import { onMounted, provide, ref } from 'vue';
import axiosInstance, { type RawResp } from './api/index';
import FestivalMenuItem from './components/FestivalMenuItem.vue';
import LoginRegisterDialog, {
loginImplKey,
registerImplKey,
} from './components/LoginRegisterDialog.vue';
import { loginResponseSchema, registerResponseSchema } from './schemas/response';
import { usePageStore } from './stores/page.js';
import { useUserStore } from './stores/user';
import { storeToRefs } from 'pinia';
2024-04-14 14:40:34 +08:00
const userStore = useUserStore();
2024-12-12 18:03:40 +08:00
const { userInfo } = storeToRefs(userStore);
const pageStore = usePageStore();
2024-12-10 20:04:14 +08:00
const showLoginRegisterDialog = ref(false);
2024-12-12 18:03:40 +08:00
provide(loginImplKey, async (params) => {
2024-12-09 20:43:08 +08:00
const loginRespRaw = await axiosInstance
.post<RawResp>('/api/user/login', params)
.then((r) => r.data)
2024-12-12 18:03:40 +08:00
.catch((err: AxiosError) => {
ElMessage.error([err.code, err.message].join(':'));
2024-12-09 20:43:08 +08:00
});
if (!loginRespRaw) return false;
2024-12-12 18:03:40 +08:00
const resp = loginResponseSchema.parse(loginRespRaw);
if (resp.type === 'error') {
ElMessage.error([resp.code, resp.msg].join(':'));
2024-04-14 14:40:34 +08:00
return false;
}
2024-12-10 20:04:14 +08:00
return userStore.updateSelfUserInfo(true);
2024-12-12 18:03:40 +08:00
});
provide(registerImplKey, async (params) => {
2024-12-10 20:04:14 +08:00
const raw = await axiosInstance
.put<RawResp>('/api/user/create', params)
.then((r) => r.data)
.catch((err: AxiosError) => {
2024-12-12 18:03:40 +08:00
ElMessage.error([err.code, err.message].join(':'));
2024-12-10 20:04:14 +08:00
});
if (!raw) return false;
const resp = registerResponseSchema.parse(raw);
if (resp.type === 'error') {
2024-12-12 18:03:40 +08:00
ElMessage.error([resp.code, resp.msg].join(':'));
2024-04-14 14:40:34 +08:00
return false;
}
2024-12-10 20:04:14 +08:00
return userStore.updateSelfUserInfo(true);
2024-12-12 18:03:40 +08:00
});
2024-04-14 14:40:34 +08:00
async function logout() {
2024-12-10 20:04:14 +08:00
userStore.token = null;
2024-12-09 20:43:08 +08:00
await userStore.updateSelfUserInfo(true);
2024-04-14 14:40:34 +08:00
}
2024-12-12 12:41:24 +08:00
onMounted(() => {
2024-12-12 18:03:40 +08:00
userStore.token = null;
userStore.updateSelfUserInfo(true);
2024-12-12 12:41:24 +08:00
});
</script>