feat: 添加用户信息加载动画

This commit is contained in:
Litrix2 2024-04-14 19:44:24 +08:00
parent 233af2b1e3
commit dbe6a3d632
4 changed files with 50 additions and 43 deletions

2
components.d.ts vendored
View File

@ -24,7 +24,7 @@ declare module 'vue' {
ElTabs: typeof import('element-plus/es')['ElTabs']
Game2048: typeof import('./src/components/Game2048.vue')['default']
IconCsLock: typeof import('~icons/cs/lock')['default']
IconEpAvatar: typeof import('~icons/ep/avatar')['default']
IconEpLoading: typeof import('~icons/ep/loading')['default']
IconEpUserFilled: typeof import('~icons/ep/user-filled')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']

View File

@ -3,25 +3,28 @@
<el-header height="50px">
<div class="title-container">社团展示系统</div>
<div class="content">
<div v-if="userStore.userInfo !== null" class="username">
{{ userStore.userInfo.name }}
</div>
<el-dropdown v-if="userStore.userInfo !== null">
<el-avatar :icon="userStore.userInfo.avatar || Avatar" :size="40"></el-avatar>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :icon="CloseBold" @click="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-avatar
v-else
style="color: black; user-select: none; cursor: pointer"
:size="40"
@click="showLoginRegisterDialog = true"
>
登录
</el-avatar>
<template v-if="userStore.initialized">
<div v-if="userStore.userInfo !== null" class="username">
{{ userStore.userInfo.name }}
</div>
<el-dropdown v-if="userStore.userInfo !== null">
<el-avatar :icon="userStore.userInfo.avatar || Avatar" :size="40"></el-avatar>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :icon="CloseBold" @click="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-avatar
v-else
style="color: black; user-select: none; cursor: pointer"
:size="40"
@click="showLoginRegisterDialog = true"
>
登录
</el-avatar>
</template>
<el-icon v-else class="is-loading"><icon-ep-loading /></el-icon>
</div>
</el-header>
</el-container>
@ -149,7 +152,7 @@ import { Avatar, CloseBold } from '@element-plus/icons-vue';
import { AxiosError } from 'axios';
import { type FormInstance, type FormRules, ElMessage } from 'element-plus';
import { partial, pick } from 'lodash-es';
import { onMounted, reactive, ref, watch } from 'vue';
import { onMounted, reactive, ref, watch, onBeforeMount } from 'vue';
import { RouterView, useRoute, useRouter } from 'vue-router';
import { z } from 'zod';
const router = useRouter();
@ -244,7 +247,6 @@ async function submitLoginForm() {
succeed = await login();
} finally {
if (succeed) {
// await timeout(1000);
window.location.reload();
} else {
logining.value = false;

View File

@ -1,10 +1,10 @@
import axiosInstance from '@/api';
import { userInfoResponseSchema, type Route } from '@/schemas';
import { useUserStore } from '@/stores';
import { errorMessage } from '@/utils';
import { errorMessage, timeout } from '@/utils';
import { ElMessage } from 'element-plus';
import { type Component } from 'vue';
import { createRouter, createWebHashHistory, type RouteRecordRaw } from 'vue-router';
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router';
const routes: RouteRecordRaw[] = [
{
path: '/',
@ -16,7 +16,7 @@ const routes: RouteRecordRaw[] = [
}
];
const router = createRouter({
history: createWebHashHistory(),
history: createWebHistory(),
routes: routes
});
const componentMap: Record<string, () => Promise<Component>> = {
@ -43,24 +43,29 @@ function getAvailableRoutes(routeResponse: Route[]) {
}
router.beforeEach(async (to) => {
const userStore = useUserStore();
console.log(userStore.userInfo);
console.log(2, userStore.userInfo);
// await timeout(1000);
if (!userStore.isInitialized) {
const userInfoResponse = userInfoResponseSchema.parse(
(await axiosInstance.get('/api/user/info')).data
);
console.log(userInfoResponse);
if (userInfoResponse.type === 'error') {
ElMessage.error(
errorMessage('获取用户信息失败', userInfoResponse.code, userInfoResponse.msg)
console.log(userStore.initialized);
if (!userStore.initialized) {
try {
const userInfoResponse = userInfoResponseSchema.parse(
(await axiosInstance.get('/api/user/info')).data
);
return false;
console.log(userInfoResponse);
if (userInfoResponse.type === 'error') {
ElMessage.error(
errorMessage('获取用户信息失败', userInfoResponse.code, userInfoResponse.msg)
);
return false;
}
// 判断是否已登录.
if (userStore.token !== null) {
userStore.updateUserInfo(userInfoResponse);
}
getAvailableRoutes(userInfoResponse.data.auth.permissions[0].routers);
} finally {
userStore.initialized = true;
}
if (userStore.token !== null) {
userStore.updateUserInfo(userInfoResponse);
}
getAvailableRoutes(userInfoResponse.data.auth.permissions[0].routers);
userStore.isInitialized = true;
}
if (userStore.addedRouteSet.has(to.fullPath) || to.fullPath === '/404') {
return true;

View File

@ -8,10 +8,10 @@ import { z } from 'zod';
type SucceedUserInfoResponse = SucceedResponse<z.infer<typeof userInfoResponseSchema>>;
type UserInfo = Pick<SucceedUserInfoResponse['data'], 'id' | 'name' | 'avatar'>;
export const useUserStore = defineStore('user', () => {
const token = useLocalStorage<string>('token', null, {
const token = useLocalStorage<string | null>('token', null, {
serializer: StorageSerializers.string
});
const userInfo = useLocalStorage<UserInfo>('user-info', null, {
const userInfo = useLocalStorage<UserInfo | null>('user-info', null, {
serializer: StorageSerializers.object
});
const routeMap = reactive(new Map<string, RouteRecordRaw>());
@ -27,5 +27,5 @@ export const useUserStore = defineStore('user', () => {
addedRouteSet.clear();
isInitialized.value = false;
}
return { token, userInfo, routeMap, addedRouteSet, isInitialized, updateUserInfo, $reset };
return { token, userInfo, routeMap, addedRouteSet, initialized: isInitialized, updateUserInfo, $reset };
});