fix: fix router cannot change bug
This commit is contained in:
parent
57e7825f74
commit
dc2f094b6f
71
components.d.ts
vendored
71
components.d.ts
vendored
@ -7,39 +7,42 @@ export {}
|
||||
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
BackgroundComp: (typeof import('./src/components/BackgroundComp.vue'))['default'];
|
||||
ElAvatar: (typeof import('element-plus/es'))['ElAvatar'];
|
||||
ElButton: (typeof import('element-plus/es'))['ElButton'];
|
||||
ElContainer: (typeof import('element-plus/es'))['ElContainer'];
|
||||
ElDialog: (typeof import('element-plus/es'))['ElDialog'];
|
||||
ElDrawer: (typeof import('element-plus/es'))['ElDrawer'];
|
||||
ElDropdown: (typeof import('element-plus/es'))['ElDropdown'];
|
||||
ElDropdownItem: (typeof import('element-plus/es'))['ElDropdownItem'];
|
||||
ElDropdownMenu: (typeof import('element-plus/es'))['ElDropdownMenu'];
|
||||
ElForm: (typeof import('element-plus/es'))['ElForm'];
|
||||
ElFormItem: (typeof import('element-plus/es'))['ElFormItem'];
|
||||
ElHeader: (typeof import('element-plus/es'))['ElHeader'];
|
||||
ElIcon: (typeof import('element-plus/es'))['ElIcon'];
|
||||
ElImage: (typeof import('element-plus/es'))['ElImage'];
|
||||
ElInput: (typeof import('element-plus/es'))['ElInput'];
|
||||
ElInputNumber: (typeof import('element-plus/es'))['ElInputNumber'];
|
||||
ElMain: (typeof import('element-plus/es'))['ElMain'];
|
||||
ElMenu: (typeof import('element-plus/es'))['ElMenu'];
|
||||
ElMenuItem: (typeof import('element-plus/es'))['ElMenuItem'];
|
||||
ElPopover: (typeof import('element-plus/es'))['ElPopover'];
|
||||
ElTabPane: (typeof import('element-plus/es'))['ElTabPane'];
|
||||
ElTabs: (typeof import('element-plus/es'))['ElTabs'];
|
||||
Game2048: (typeof import('./src/components/Game2048.vue'))['default'];
|
||||
Game2048Button: (typeof import('./src/components/Game2048Button.vue'))['default'];
|
||||
Game2048Score: (typeof import('./src/components/Game2048Score.vue'))['default'];
|
||||
IconCsLoading: (typeof import('~icons/cs/loading'))['default'];
|
||||
IconCsLock: (typeof import('~icons/cs/lock'))['default'];
|
||||
IconCsUser: (typeof import('~icons/cs/user'))['default'];
|
||||
IconCsValidate: (typeof import('~icons/cs/validate'))['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'];
|
||||
VerifyInput: (typeof import('./src/components/VerifyInput.vue'))['default'];
|
||||
BackgroundComp: typeof import('./src/components/BackgroundComp.vue')['default']
|
||||
ElAvatar: typeof import('element-plus/es')['ElAvatar']
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElContainer: typeof import('element-plus/es')['ElContainer']
|
||||
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||
ElDrawer: (typeof import('element-plus/es'))['ElDrawer']
|
||||
ElDropdown: typeof import('element-plus/es')['ElDropdown']
|
||||
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
|
||||
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
|
||||
ElForm: typeof import('element-plus/es')['ElForm']
|
||||
ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
||||
ElHeader: typeof import('element-plus/es')['ElHeader']
|
||||
ElIcon: typeof import('element-plus/es')['ElIcon']
|
||||
ElImage: (typeof import('element-plus/es'))['ElImage']
|
||||
ElInput: typeof import('element-plus/es')['ElInput']
|
||||
ElInputNumber: (typeof import('element-plus/es'))['ElInputNumber']
|
||||
ElMain: typeof import('element-plus/es')['ElMain']
|
||||
ElMenu: typeof import('element-plus/es')['ElMenu']
|
||||
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
|
||||
ElPopover: typeof import('element-plus/es')['ElPopover']
|
||||
ElTabPane: typeof import('element-plus/es')['ElTabPane']
|
||||
ElTabs: typeof import('element-plus/es')['ElTabs']
|
||||
Game2048: typeof import('./src/components/Game2048.vue')['default']
|
||||
Game2048Button: typeof import('./src/components/Game2048Button.vue')['default']
|
||||
Game2048Score: typeof import('./src/components/Game2048Score.vue')['default']
|
||||
IconCsLoading: typeof import('~icons/cs/loading')['default']
|
||||
IconCsLock: typeof import('~icons/cs/lock')['default']
|
||||
IconCsUser: typeof import('~icons/cs/user')['default']
|
||||
IconCsValidate: typeof import('~icons/cs/validate')['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']
|
||||
VerifyInput: typeof import('./src/components/VerifyInput.vue')['default']
|
||||
}
|
||||
export interface ComponentCustomProperties {
|
||||
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
|
||||
}
|
||||
}
|
||||
|
42
package.json
42
package.json
@ -14,40 +14,42 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"@vueuse/core": "^10.9.0",
|
||||
"axios": "^1.6.8",
|
||||
"@vueuse/core": "^10.11.1",
|
||||
"axios": "^1.7.9",
|
||||
"crypto-js": "^4.2.0",
|
||||
"element-plus": "^2.7.0",
|
||||
"element-plus": "^2.9.0",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"pinia": "^2.1.7",
|
||||
"mitt": "^3.0.1",
|
||||
"pinia": "^2.3.0",
|
||||
"vfonts": "^0.0.3",
|
||||
"vue": "^3.4.21",
|
||||
"vue-router": "^4.3.0",
|
||||
"zod": "^3.22.4"
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.0",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify-json/ep": "^1.1.15",
|
||||
"@rushstack/eslint-patch": "^1.10.2",
|
||||
"@iconify-json/ep": "^1.2.1",
|
||||
"@rushstack/eslint-patch": "^1.10.4",
|
||||
"@tsconfig/node20": "^20.1.4",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/node": "^20.12.7",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"@types/node": "^20.17.9",
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
||||
"@vue/eslint-config-prettier": "^8.0.0",
|
||||
"@vue/eslint-config-typescript": "^12.0.0",
|
||||
"@vue/tsconfig": "^0.5.1",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-vue": "^9.24.1",
|
||||
"npm-run-all2": "^6.1.2",
|
||||
"prettier": "^3.2.5",
|
||||
"sass": "^1.75.0",
|
||||
"typescript": "^5.5.3",
|
||||
"unplugin-auto-import": "^0.17.5",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-plugin-vue": "^9.32.0",
|
||||
"npm-run-all2": "^6.2.6",
|
||||
"prettier": "^3.4.2",
|
||||
"sass": "^1.82.0",
|
||||
"typescript": "^5.7.2",
|
||||
"unplugin-auto-import": "^0.17.8",
|
||||
"unplugin-icons": "^0.18.5",
|
||||
"unplugin-vue-components": "^0.26.0",
|
||||
"vite": "^5.2.8",
|
||||
"vite": "^5.4.11",
|
||||
"vue-tsc": "^1.8.27"
|
||||
}
|
||||
},
|
||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||
}
|
||||
|
48
src/App.vue
48
src/App.vue
@ -19,7 +19,7 @@
|
||||
<img alt="2048" class="header-menu-image" src="@/assets/2048.png" />
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
<template v-if="userStore.userInfo !== null">
|
||||
<template v-if="userStore.userInfo && userStore.userInfo.id !== -1">
|
||||
<div class="username">
|
||||
{{ userStore.userInfo.name }}
|
||||
</div>
|
||||
@ -27,11 +27,11 @@
|
||||
<el-avatar :icon="userStore.userInfo.avatar || Avatar" :size="40"></el-avatar>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<router-link v-slot="{ navigate }" custom to="/user">
|
||||
<!-- <router-link v-slot="{ navigate }" custom to="/user">
|
||||
<el-dropdown-item :icon="UserFilled" @click="navigate()">
|
||||
个人主页
|
||||
</el-dropdown-item>
|
||||
</router-link>
|
||||
</router-link> -->
|
||||
<el-dropdown-item :icon="CloseBold" @click="logout">
|
||||
退出登录
|
||||
</el-dropdown-item>
|
||||
@ -57,14 +57,14 @@
|
||||
</transition>
|
||||
</div>
|
||||
</el-header>
|
||||
<el-main>
|
||||
<el-main v-loading="!!pageStore.pageLoadingCount">
|
||||
<router-view v-slot="{ Component: comp }">
|
||||
<transition appear mode="out-in" name="page-root">
|
||||
<component :is="comp" />
|
||||
</transition>
|
||||
</router-view>
|
||||
<transition name="page-root"></transition>
|
||||
<div
|
||||
<!-- <div
|
||||
:style="
|
||||
pageStore.pageLoadingCount
|
||||
? {
|
||||
@ -85,7 +85,7 @@
|
||||
<icon-cs-loading />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
</el-main>
|
||||
</el-container>
|
||||
<el-dialog
|
||||
@ -358,7 +358,7 @@
|
||||
}
|
||||
</style>
|
||||
<script lang="ts" setup>
|
||||
import axiosInstance from '@/api';
|
||||
import axiosInstance, { type RawResp } from '@/api';
|
||||
import { type VerifyImagePath } from '@/components/VerifyInput.vue';
|
||||
import { loginResponseSchema, registerResponseSchema } from '@/schemas';
|
||||
import { useUserStore } from '@/stores';
|
||||
@ -453,24 +453,21 @@ interface LoginParams {
|
||||
code: string;
|
||||
}
|
||||
|
||||
async function login(params: LoginParams) {
|
||||
try {
|
||||
const loginResp = loginResponseSchema.parse(
|
||||
(await axiosInstance.post('/api/user/login', params)).data
|
||||
);
|
||||
if (loginResp.type === 'error') {
|
||||
ElMessage.error(loginErrorMessage(loginResp.code, loginResp.msg));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (e) {
|
||||
if (e instanceof AxiosError) {
|
||||
async function login(params: LoginParams): Promise<boolean> {
|
||||
const loginRespRaw = await axiosInstance
|
||||
.post<RawResp>('/api/user/login', params)
|
||||
.then((r) => r.data)
|
||||
.catch((e: AxiosError) => {
|
||||
ElMessage.error(loginErrorMessage(e.code, e.message));
|
||||
} else {
|
||||
ElMessage.error('error');
|
||||
}
|
||||
});
|
||||
if (!loginRespRaw) return false;
|
||||
const loginResp = loginResponseSchema.parse(loginRespRaw);
|
||||
if (loginResp.type === 'error') {
|
||||
ElMessage.error(loginErrorMessage(loginResp.code, loginResp.msg));
|
||||
return false;
|
||||
}
|
||||
await userStore.updateSelfUserInfo(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
async function submitLoginForm() {
|
||||
@ -492,7 +489,6 @@ async function submitLoginForm() {
|
||||
verifyImage: { key },
|
||||
verifyCode: code
|
||||
} = loginFormData;
|
||||
console.log(loginFormData);
|
||||
succeed = await login({
|
||||
username,
|
||||
password,
|
||||
@ -501,7 +497,7 @@ async function submitLoginForm() {
|
||||
});
|
||||
} finally {
|
||||
if (succeed) {
|
||||
router.push({ path: route.fullPath, force: true });
|
||||
showLoginRegisterDialog.value = false;
|
||||
} else {
|
||||
loginFormData.verifyImage = 'none';
|
||||
loginFormRef.value?.resetFields('verifyCode');
|
||||
@ -560,7 +556,7 @@ async function submitRegisterForm() {
|
||||
succeed = await register({ username, password, key, code, auth: 1 });
|
||||
} finally {
|
||||
if (succeed) {
|
||||
userStore.userInfoStatus = 'unInitialized';
|
||||
userStore.userInfoStatus = 'uninitialized';
|
||||
router.push({ path: route.fullPath, force: true });
|
||||
} else {
|
||||
registerFormData.verifyImage = 'none';
|
||||
@ -572,6 +568,6 @@ async function submitRegisterForm() {
|
||||
|
||||
async function logout() {
|
||||
userStore.$reset();
|
||||
router.push({ path: route.fullPath, force: true });
|
||||
await userStore.updateSelfUserInfo(true);
|
||||
}
|
||||
</script>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useUserStore } from '@/stores';
|
||||
import axios from 'axios';
|
||||
|
||||
const baseURL = 'http://wzpmc.cn:18080/';
|
||||
const baseURL = 'https://wzpmc.cn:18080/';
|
||||
const axiosInstance = axios.create({
|
||||
baseURL
|
||||
});
|
||||
@ -15,9 +15,10 @@ axiosInstance.interceptors.request.use((config) => {
|
||||
axiosInstance.interceptors.response.use((response) => {
|
||||
const userStore = useUserStore();
|
||||
const authorization = response.headers['set-authorization'] as string | undefined;
|
||||
if (authorization !== undefined) {
|
||||
if (authorization) {
|
||||
userStore.token = authorization;
|
||||
}
|
||||
return response;
|
||||
});
|
||||
export default axiosInstance;
|
||||
export type RawResp = Record<string, unknown>;
|
||||
|
7
src/bus.ts
Normal file
7
src/bus.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import mitt from 'mitt';
|
||||
|
||||
type Events = {
|
||||
userDataUpdateSucceed: void;
|
||||
userDataUpdateFailed: void;
|
||||
};
|
||||
export const bus = mitt<Events>();
|
@ -1,11 +1,7 @@
|
||||
import axiosInstance from '@/api';
|
||||
import { type SucceedUserInfoResponse, userInfoResponseSchema } from '@/schemas';
|
||||
import { useUserStore } from '@/stores';
|
||||
import { type PageErrorReason, usePageStore } from '@/stores/page';
|
||||
import { errorMessage, IdPool, waitRef } from '@/utils';
|
||||
import { IdPool, waitRef } from '@/utils';
|
||||
import { useThrottleFn } from '@vueuse/core';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { toRef } from 'vue';
|
||||
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router';
|
||||
import { RoutePermissionId } from './permissions';
|
||||
@ -13,7 +9,6 @@ import { RoutePermissionId } from './permissions';
|
||||
export * from './permissions';
|
||||
|
||||
declare module 'vue-router' {
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
interface RouteMeta {
|
||||
errorRouteName?: string;
|
||||
routeId?: number;
|
||||
@ -69,69 +64,41 @@ const router = createRouter({
|
||||
routes: routes
|
||||
});
|
||||
|
||||
async function getUserInfoResponse(
|
||||
showErrorMessage: boolean
|
||||
): Promise<SucceedUserInfoResponse | undefined> {
|
||||
try {
|
||||
const userInfoResponse = userInfoResponseSchema.parse(
|
||||
(await axiosInstance.get('/api/user/info')).data
|
||||
);
|
||||
if (userInfoResponse.type === 'error') {
|
||||
if (showErrorMessage) {
|
||||
ElMessage.error(
|
||||
errorMessage('获取用户信息失败', userInfoResponse.code, userInfoResponse.msg)
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
return userInfoResponse;
|
||||
} catch (e) {
|
||||
if (showErrorMessage && e instanceof AxiosError) {
|
||||
ElMessage.error(errorMessage('获取用户信息失败', e.code, e.message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
router.beforeEach(async (...[to, , next]) => {
|
||||
router.beforeEach(async (to, from) => {
|
||||
const userStore = useUserStore();
|
||||
const pageStore = usePageStore();
|
||||
const { permissionId } = to.meta;
|
||||
pageStore.setNewRouteId(to);
|
||||
switch (userStore.userInfoStatus) {
|
||||
case 'unInitialized':
|
||||
case 'failed': {
|
||||
if (permissionId === undefined) {
|
||||
next();
|
||||
}
|
||||
userStore.userInfoStatus = 'initializing';
|
||||
const userInfoResponse = await getUserInfoResponse(true);
|
||||
if (userInfoResponse === undefined) {
|
||||
userStore.userInfoStatus = 'failed';
|
||||
if (permissionId === undefined) {
|
||||
return;
|
||||
try {
|
||||
switch (userStore.userInfoStatus) {
|
||||
case 'uninitialized': {
|
||||
const succeed = await userStore.updateSelfUserInfo(true);
|
||||
if (!succeed) {
|
||||
if (permissionId === undefined) {
|
||||
return;
|
||||
}
|
||||
return pageStore.createTempErrorRoute(
|
||||
{
|
||||
type: 'networkError',
|
||||
originalPath: to.fullPath
|
||||
},
|
||||
to
|
||||
);
|
||||
}
|
||||
pageStore.removeRouteId(to);
|
||||
return pageStore.createTempErrorRoute(
|
||||
{
|
||||
type: 'networkError',
|
||||
originalPath: to.fullPath
|
||||
},
|
||||
to
|
||||
);
|
||||
break;
|
||||
}
|
||||
userStore.updateUserInfo(userInfoResponse.data);
|
||||
userStore.userInfoStatus = 'initialized';
|
||||
break;
|
||||
case 'initializing':
|
||||
return false;
|
||||
}
|
||||
case 'initializing':
|
||||
return false;
|
||||
}
|
||||
if (permissionId !== undefined) {
|
||||
if (userStore.hasPermission(permissionId)) {
|
||||
return true;
|
||||
} else {
|
||||
return pageStore.createTempErrorRoute({ type: 'noPermission' });
|
||||
if (permissionId !== undefined) {
|
||||
if (userStore.hasPermission(permissionId)) {
|
||||
return true;
|
||||
} else {
|
||||
return pageStore.createTempErrorRoute({ type: 'noPermission' }, from);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
pageStore.removeRouteId(to);
|
||||
}
|
||||
});
|
||||
router.beforeEach((to) => {
|
||||
@ -144,28 +111,24 @@ router.beforeEach((to) => {
|
||||
pageStore.pageErrorReason = undefined;
|
||||
}
|
||||
});
|
||||
router.afterEach(
|
||||
useThrottleFn(
|
||||
async (to) => {
|
||||
const userStore = useUserStore();
|
||||
const pageStore = usePageStore();
|
||||
if (['initialized', 'failed'].includes(userStore.userInfoStatus)) {
|
||||
await waitRef(toRef(userStore, 'userInfoStatus'), 'initialized', 'failed');
|
||||
}
|
||||
const userInfoResponse = await getUserInfoResponse(false);
|
||||
if (userInfoResponse === undefined) {
|
||||
return;
|
||||
}
|
||||
userStore.updateUserInfo(userInfoResponse.data);
|
||||
const { permissionId } = to.meta;
|
||||
if (permissionId !== undefined && !userStore.hasPermission(permissionId)) {
|
||||
router.push(pageStore.createTempErrorRoute({ type: 'noPermission' }));
|
||||
}
|
||||
},
|
||||
10000,
|
||||
true
|
||||
)
|
||||
);
|
||||
// router.afterEach(
|
||||
// useThrottleFn(
|
||||
// async (to) => {
|
||||
// const userStore = useUserStore();
|
||||
// const pageStore = usePageStore();
|
||||
// if (['initialized', 'failed'].includes(userStore.userInfoStatus)) {
|
||||
// await waitRef(toRef(userStore, 'userInfoStatus'), 'initialized');
|
||||
// }
|
||||
// await userStore.updateSelfUserInfo(false, true);
|
||||
// const { permissionId } = to.meta;
|
||||
// if (permissionId !== undefined && !userStore.hasPermission(permissionId)) {
|
||||
// router.push(pageStore.createTempErrorRoute({ type: 'noPermission' }));
|
||||
// }
|
||||
// },
|
||||
// 10000,
|
||||
// true
|
||||
// )
|
||||
// );
|
||||
router.afterEach((to) => {
|
||||
const pageStore = usePageStore();
|
||||
pageStore.removeRouteId(to);
|
||||
|
@ -46,7 +46,7 @@ export const usePageStore = defineStore('page-store', () => {
|
||||
}
|
||||
| undefined
|
||||
>,
|
||||
currentRoute: RouteLocationNormalized = useRoute(),
|
||||
currentRoute: RouteLocationNormalized | undefined = useRoute(),
|
||||
root: boolean = false
|
||||
): RouteLocationRaw {
|
||||
pageErrorReason.value = reason;
|
||||
|
@ -1,8 +1,12 @@
|
||||
import axiosInstance, { type RawResp } from '@/api';
|
||||
import type { UserInfo } from '@/schemas';
|
||||
import { type idAndNameSchema } from '@/schemas';
|
||||
import { userInfoResponseSchema, type idAndNameSchema } from '@/schemas';
|
||||
import { errorMessage } from '@/utils';
|
||||
import { StorageSerializers, useLocalStorage } from '@vueuse/core';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { defineStore } from 'pinia';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { z } from 'zod';
|
||||
|
||||
type Permission = z.infer<typeof idAndNameSchema>;
|
||||
@ -13,32 +17,45 @@ export const useUserStore = defineStore('user', () => {
|
||||
const userInfo = useLocalStorage<UserInfo | null>('user-info', null, {
|
||||
serializer: StorageSerializers.object
|
||||
});
|
||||
const permissions = reactive<Permission[]>([]);
|
||||
const userInfoStatus = ref<'unInitialized' | 'initializing' | 'initialized' | 'failed'>(
|
||||
'unInitialized'
|
||||
const permissions = computed<Permission[]>(() => userInfo.value?.auth.permissions ?? []);
|
||||
const userInfoStatus = ref<'uninitialized' | 'initializing' | 'initialized'>('uninitialized');
|
||||
watch(
|
||||
userInfo,
|
||||
(info) => {
|
||||
userInfoStatus.value = info ? 'initialized' : 'uninitialized';
|
||||
},
|
||||
{ flush: 'sync' }
|
||||
);
|
||||
|
||||
/**
|
||||
* 在更新用户信息.
|
||||
* @param info 用户信息
|
||||
*/
|
||||
function updateUserInfo(info: UserInfo) {
|
||||
if (token.value !== null) {
|
||||
userInfo.value = info;
|
||||
async function updateSelfUserInfo(
|
||||
showErrorMessage: boolean,
|
||||
silent: boolean = false
|
||||
): Promise<boolean> {
|
||||
if (!silent) userInfoStatus.value = 'initializing';
|
||||
const raw = await axiosInstance
|
||||
.get<RawResp>('/api/user/info')
|
||||
.then((r) => r.data)
|
||||
.catch((e: AxiosError) => {
|
||||
if (!showErrorMessage) return;
|
||||
ElMessage.error(errorMessage('获取用户信息失败', e.code, e.message));
|
||||
});
|
||||
const resp = userInfoResponseSchema.parse(raw);
|
||||
if (resp.type === 'error') {
|
||||
if (showErrorMessage) {
|
||||
ElMessage.error(errorMessage('获取用户信息失败', resp.code, resp.msg));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
permissions.splice(0, permissions.length, ...info.auth.permissions);
|
||||
console.log(permissions);
|
||||
userInfo.value = resp.data;
|
||||
return true;
|
||||
}
|
||||
|
||||
function hasPermission(permissionId: number) {
|
||||
return permissions.find((permission) => permission.id === permissionId);
|
||||
return permissions.value.find((permission) => permission.id === permissionId);
|
||||
}
|
||||
|
||||
function $reset() {
|
||||
token.value = null;
|
||||
userInfo.value = null;
|
||||
permissions.splice(0);
|
||||
userInfoStatus.value = 'unInitialized';
|
||||
userInfoStatus.value = 'uninitialized';
|
||||
}
|
||||
|
||||
return {
|
||||
@ -46,7 +63,7 @@ export const useUserStore = defineStore('user', () => {
|
||||
userInfo,
|
||||
permissions,
|
||||
userInfoStatus,
|
||||
updateUserInfo,
|
||||
updateSelfUserInfo,
|
||||
hasPermission,
|
||||
$reset
|
||||
};
|
||||
|
@ -1,5 +1,14 @@
|
||||
<template>
|
||||
<div class="page-root">这是主页</div>
|
||||
<div class="page-root">
|
||||
<div class="page" v-if="userStore.userInfo">
|
||||
<div>用户名:{{ userStore.userInfo.name }}</div>
|
||||
<div>id:{{ userStore.userInfo.id }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss" scoped></style>
|
||||
<script lang="ts" setup></script>
|
||||
<script lang="ts" setup>
|
||||
import { useUserStore } from '@/stores/user.js';
|
||||
|
||||
const userStore = useUserStore();
|
||||
</script>
|
||||
|
@ -52,7 +52,7 @@
|
||||
import axiosInstance from '@/api';
|
||||
import { type UserInfo, userInfoResponseSchema } from '@/schemas';
|
||||
import { useUserStore } from '@/stores';
|
||||
import { computed, onBeforeMount, ref } from 'vue';
|
||||
import { computed, onBeforeMount, ref, watch } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
const userStore = useUserStore();
|
||||
@ -68,7 +68,12 @@ const avatar = ref<string>();
|
||||
async function getAvatar(sha1: string) {
|
||||
const avatarResp = await axiosInstance.get(`/api/user/avatar${sha1}`);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => userStore.userInfo,
|
||||
(info) => {
|
||||
userInfo.value = info ?? undefined;
|
||||
}
|
||||
);
|
||||
onBeforeMount(async () => {
|
||||
if (id == undefined) {
|
||||
userInfo.value = userStore.userInfo!;
|
||||
|
@ -45,5 +45,9 @@ export default defineConfig({
|
||||
'@': resolve('./src')
|
||||
// vue: 'vue/dist/vue.esm-bundler.js'
|
||||
}
|
||||
},
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
port: 18081
|
||||
}
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user