2024-04-08 18:00:04 +08:00
|
|
|
<template>
|
2024-12-12 18:03:40 +08:00
|
|
|
<el-container class="app__container">
|
2024-12-20 22:00:55 +08:00
|
|
|
<el-header class="app-header flex items-center">
|
2024-12-15 14:20:54 +08:00
|
|
|
<el-icon v-show="mdLess" size="30" @click="showVerticalHeaderMenu = true">
|
2024-12-13 21:54:17 +08:00
|
|
|
<icon-cs-menu />
|
|
|
|
</el-icon>
|
2024-12-15 14:20:54 +08:00
|
|
|
<router-link custom to="/" v-slot="{ navigate }">
|
2024-12-12 18:03:40 +08:00
|
|
|
<el-icon size="50" @click="navigate"><icon-cs-club /></el-icon>
|
2024-12-15 21:12:21 +08:00
|
|
|
<h3 v-show="showAppTitle" class="app__title" @click="navigate">社团展示系统</h3>
|
2024-12-12 18:03:40 +08:00
|
|
|
</router-link>
|
2024-12-15 14:20:54 +08:00
|
|
|
<header-menu v-show="!mdLess" mode="horizontal" />
|
|
|
|
<el-dropdown v-if="!mdLess && userStore.logined">
|
2024-12-15 10:02:18 +08:00
|
|
|
<header-user @login="onLoginButtonClick" @logout="logout" />
|
2024-12-13 21:54:17 +08:00
|
|
|
<template #dropdown>
|
|
|
|
<el-dropdown-menu>
|
2024-12-21 22:17:07 +08:00
|
|
|
<el-dropdown-item :icon="UserFilled" @click="jumpToUserPage">个人主页</el-dropdown-item>
|
2024-12-13 21:54:17 +08:00
|
|
|
<el-dropdown-item :icon="CloseBold" @click="logout">退出登录</el-dropdown-item>
|
|
|
|
</el-dropdown-menu>
|
2024-12-12 18:03:40 +08:00
|
|
|
</template>
|
2024-12-13 21:54:17 +08:00
|
|
|
</el-dropdown>
|
2024-12-21 22:17:07 +08:00
|
|
|
<header-user
|
|
|
|
v-else
|
|
|
|
@login="onLoginButtonClick"
|
|
|
|
@logout="logout"
|
|
|
|
@click="showVerticalHeaderMenu = true"
|
|
|
|
/>
|
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">
|
2024-12-20 22:00:55 +08:00
|
|
|
<el-container>
|
2024-12-12 18:03:40 +08:00
|
|
|
<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>
|
2024-07-11 22:33:48 +08:00
|
|
|
</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" />
|
2024-12-13 21:54:17 +08:00
|
|
|
<el-drawer
|
|
|
|
class="app-vertical-drawer"
|
|
|
|
v-model="showVerticalHeaderMenu"
|
|
|
|
direction="ltr"
|
|
|
|
size="250"
|
|
|
|
:with-header="false"
|
|
|
|
>
|
2024-12-15 21:12:21 +08:00
|
|
|
<h3 v-show="!showAppTitle" class="app__title text-center">社团展示系统</h3>
|
2024-12-21 22:17:07 +08:00
|
|
|
<template v-if="userStore.logined">
|
|
|
|
<header-user @login="showLoginRegisterDialog = true" />
|
|
|
|
<el-button :icon="UserFilled" @click="jumpToUserPage">个人主页</el-button>
|
|
|
|
<el-button type="danger" :icon="CloseBold" @click="logout">退出登录</el-button>
|
|
|
|
</template>
|
2024-12-18 20:15:00 +08:00
|
|
|
<div v-else class="app-vertical-drawer__not-login-title flex justify-center items-center">
|
|
|
|
尚未登录
|
|
|
|
</div>
|
2024-12-15 10:02:18 +08:00
|
|
|
<header-menu mode="vertical" @select="showVerticalHeaderMenu = false" />
|
2024-12-13 21:54:17 +08:00
|
|
|
</el-drawer>
|
2024-04-08 18:00:04 +08:00
|
|
|
</template>
|
2024-12-13 21:54:17 +08:00
|
|
|
<style lang="scss">
|
|
|
|
.app-vertical-drawer {
|
|
|
|
--el-drawer-padding-primary: 0;
|
|
|
|
.el-drawer__body {
|
|
|
|
gap: 10px;
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
}
|
2024-12-13 23:02:39 +08:00
|
|
|
> :first-child:not(.app-header-menu) {
|
2024-12-13 21:54:17 +08:00
|
|
|
margin-top: 10px;
|
|
|
|
}
|
2024-12-13 23:02:39 +08:00
|
|
|
&__not-login-title {
|
|
|
|
font-weight: bold;
|
|
|
|
}
|
2024-12-21 22:17:07 +08:00
|
|
|
.el-button {
|
2024-12-13 21:54:17 +08:00
|
|
|
margin: 0 10px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</style>
|
2024-07-08 21:31:47 +08:00
|
|
|
<style lang="scss" scoped>
|
2024-12-20 22:00:55 +08:00
|
|
|
@use '@/assets/mixins' as m;
|
2024-12-12 18:03:40 +08:00
|
|
|
.app__container {
|
2024-07-11 22:33:48 +08:00
|
|
|
min-height: 100vh;
|
|
|
|
}
|
|
|
|
.app-header {
|
2024-12-20 22:00:55 +08:00
|
|
|
@include m.header(0);
|
2024-12-12 18:03:40 +08:00
|
|
|
--el-loading-spinner-size: 30px;
|
2024-12-15 21:52:30 +08:00
|
|
|
user-select: none;
|
2024-12-15 10:02:18 +08:00
|
|
|
// 侧边栏需要居中
|
2024-12-13 21:54:17 +08:00
|
|
|
.app-header-user {
|
|
|
|
margin-left: auto;
|
|
|
|
}
|
2024-07-11 22:33:48 +08:00
|
|
|
}
|
2024-12-12 18:03:40 +08:00
|
|
|
.app-main {
|
2024-12-15 21:12:21 +08:00
|
|
|
--el-main-padding: 0 0 0 0;
|
|
|
|
margin-top: var(--header-height);
|
2024-07-11 22:33:48 +08:00
|
|
|
display: flex;
|
|
|
|
}
|
2024-12-15 21:12:21 +08:00
|
|
|
.app-router-component {
|
|
|
|
--el-main-padding: 0;
|
|
|
|
}
|
2024-12-12 18:03:40 +08:00
|
|
|
.app-router-view-enter-active,
|
|
|
|
.app-router-view-leave-active {
|
2024-12-21 22:17:07 +08:00
|
|
|
transition: opacity 0.25s;
|
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
|
|
|
}
|
|
|
|
</style>
|
2024-12-12 18:03:40 +08:00
|
|
|
<script setup lang="ts">
|
2024-12-13 18:54:48 +08:00
|
|
|
import axiosInstance, { type RawResp } from '@/api/index';
|
2024-12-15 10:02:18 +08:00
|
|
|
import HeaderMenu from '@/components/app/HeaderMenu.vue';
|
|
|
|
import HeaderUser from '@/components/app/HeaderUser.vue';
|
2024-12-13 18:54:48 +08:00
|
|
|
import LoginRegisterDialog, {
|
|
|
|
loginImplKey,
|
|
|
|
registerImplKey,
|
2024-12-15 10:02:18 +08:00
|
|
|
} from '@/components/app/LoginRegisterDialog.vue';
|
|
|
|
import { useMediaStore } from '@/stores/media';
|
2024-12-13 21:54:17 +08:00
|
|
|
import { usePageStore } from '@/stores/page.js';
|
|
|
|
import { useUserStore } from '@/stores/user';
|
2024-12-21 22:17:07 +08:00
|
|
|
import { CloseBold, UserFilled } from '@element-plus/icons-vue';
|
2024-12-18 20:15:00 +08:00
|
|
|
import { useMediaQuery } from '@vueuse/core';
|
2024-12-12 18:03:40 +08:00
|
|
|
import type { AxiosError } from 'axios';
|
|
|
|
import { ElMessage } from 'element-plus';
|
2024-12-13 13:00:56 +08:00
|
|
|
import { storeToRefs } from 'pinia';
|
2024-12-13 21:54:17 +08:00
|
|
|
import { provide, ref, watch } from 'vue';
|
2024-12-12 18:03:40 +08:00
|
|
|
import { loginResponseSchema, registerResponseSchema } from './schemas/response';
|
2024-12-21 22:17:07 +08:00
|
|
|
import router from '@/router';
|
2024-04-14 14:40:34 +08:00
|
|
|
const userStore = useUserStore();
|
2024-07-11 22:33:48 +08:00
|
|
|
const pageStore = usePageStore();
|
2024-12-18 20:15:00 +08:00
|
|
|
const { mdLess } = storeToRefs(useMediaStore());
|
2024-12-15 21:12:21 +08:00
|
|
|
const showAppTitle = useMediaQuery('(width >= 390px)');
|
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-13 21:54:17 +08:00
|
|
|
showVerticalHeaderMenu.value = false;
|
2024-12-21 12:06:42 +08:00
|
|
|
await userStore.logout();
|
2024-04-14 14:40:34 +08:00
|
|
|
}
|
2024-12-13 21:54:17 +08:00
|
|
|
const showVerticalHeaderMenu = ref(false);
|
2024-12-15 14:20:54 +08:00
|
|
|
watch(mdLess, (v) => {
|
2024-12-13 23:02:39 +08:00
|
|
|
if (v) return;
|
|
|
|
showVerticalHeaderMenu.value = false;
|
|
|
|
});
|
2024-12-15 10:02:18 +08:00
|
|
|
function onLoginButtonClick() {
|
|
|
|
showLoginRegisterDialog.value = true;
|
|
|
|
}
|
2024-12-21 22:17:07 +08:00
|
|
|
function jumpToUserPage() {
|
|
|
|
showVerticalHeaderMenu.value = false;
|
|
|
|
router.push({
|
|
|
|
name: 'User',
|
|
|
|
params: {
|
|
|
|
id: userStore.userInfo!.id,
|
|
|
|
},
|
|
|
|
});
|
2024-12-15 10:02:18 +08:00
|
|
|
}
|
2024-12-13 13:00:56 +08:00
|
|
|
// onMounted(() => {
|
|
|
|
// userStore.token = null;
|
|
|
|
// userStore.updateSelfUserInfo(true);
|
|
|
|
// });
|
2024-04-08 18:00:04 +08:00
|
|
|
</script>
|