Vben Admin 源码学习:状态管理-登录用户信息
0x00 前言
本文将对 Vue-Vben-Admin 的状态管理之登录用户信息源码实现进行分析解读,耐心读完,相信您一定会有所收获!
0x01 user.ts 登录用户信息
文件 src\store\modules\user.ts
声明导出一个store实例 useUserStore
、一个方法 useUserStoreWithOut()
用于没有使用 setup
组件时使用。
export const useUserStore = defineStore({
id: 'app-user',
state: {},
getters: {}
actions:{}
});
export function useUserStoreWithOut() {
return useUserStore(store);
}
State
状态对象定义了登录用户的用户信息、token、角色列表、登录是否超期失效、用户信息最后更新时间。
state: (): UserState => ({
// 用户信息
userInfo: null,
// token
token: undefined,
// 角色列表
roleList: [],
// 登录是否超期失效
sessionTimeout: false,
// 用户信息最后更新时间
lastUpdateTime: 0,
}),
接口 UserInfo
定义了 用户id、用户名称、真实名称、头像、描述、用户角色等。 RoleEnum
定义了前端角色类型值。
export interface UserInfo {
// 用户id
userId: string | number;
// 用户名称
username: string;
// 真实名称
realName: string;
// 头像
avatar?: string;
// 描述
desc?: string;
// 用户指定不同的后台首页
homePath?: string;
// 用户角色信息
roles: RoleInfo[];
}
export interface RoleInfo {
roleName: string;
value: string;
}
export enum RoleEnum {
// 超级管理员
SUPER = 'super',
// tester
TEST = 'test',
}
以上接口/类型定义需根据实际业务/服务调整。
src\utils\auth 权限缓存方法
接下会使用权限缓存方法 getAuthCache
、setAuthCache
,用于获取和设置权限缓存。默认存放于localStorage
。
// src\utils\auth\index.ts
const { permissionCacheType } = projectSetting;
const isLocal = permissionCacheType === CacheTypeEnum.LOCAL;
export function getAuthCache<T>(key: BasicKeys) {
const fn = isLocal ? Persistent.getLocal : Persistent.getSession;
return fn(key) as T;
}
export function setAuthCache(key: BasicKeys, value) {
const fn = isLocal ? Persistent.setLocal : Persistent.setSession;
return fn(key, value, true);
}
缓存方式支持 localStorage
和 sessionStorage
,在 src/settings/projectSetting.ts
进行设置, 改动后需要清空浏览器缓存后生效。
// src/settings/projectSetting.ts
const setting: ProjectConfig = {
// 权限缓存存放位置。默认存放于 localStorage
permissionCacheType: CacheTypeEnum.LOCAL,
}
// src\enums\cacheEnum.ts
export enum CacheTypeEnum {
SESSION,
LOCAL,
}
Getter
从状态中获取用户信息、token、角色列表、登录是否超期失效、最后更新时间,若用户信息、token、角色列表为空,则从缓存中获取值。
getters: {
getUserInfo(): UserInfo {
return this.userInfo || getAuthCache<UserInfo>(USER_INFO_KEY) || {};
},
getToken(): string {
return this.token || getAuthCache<string>(TOKEN_KEY);
},
getRoleList(): RoleEnum[] {
return this.roleList.length > 0 ? this.roleList : getAuthCache<RoleEnum[]>(ROLES_KEY);
},
getSessionTimeout(): boolean {
return !!this.sessionTimeout;
},
getLastUpdateTime(): number {
return this.lastUpdateTime;
},
},
Actions
更新用户信息、token、角色列表等,同时使用 setAuthCache
进行缓存。
setToken(info: string | undefined) {
this.token = info ? info : '';
setAuthCache(TOKEN_KEY, info);
},
setRoleList(roleList: RoleEnum[]) {
this.roleList = roleList;
setAuthCache(ROLES_KEY, roleList);
},
setUserInfo(info: UserInfo | null) {
this.userInfo = info;
this.lastUpdateTime = new Date().getTime();
setAuthCache(USER_INFO_KEY, info);
},
setSessionTimeout
方法设置用户登录状态。
setSessionTimeout(flag: boolean) {
this.sessionTimeout = flag;
},
resetState
方法清空重置用户登录状态。
resetState() {
this.userInfo = null;
this.token = '';
this.roleList = [];
this.sessionTimeout = false;
},
用户登录
login()
方法用于用户登录后获取用户信息,返回一个 Promise
对象。
- 调用了
loginApi
,此服务接口为数据mock&联调,根据项目自行替换真实服务。 - 获取
token
后调用setToken
更新状态。 - 调用
afterLoginAction
方法进行登录后预处理操作。
async login(
params: LoginParams & {
goHome?: boolean;
mode?: ErrorMessageMode;
},
): Promise<GetUserInfoModel | null> {
try {
const { goHome = true, mode, ...loginParams } = params;
const data = await loginApi(loginParams, mode);
const { token } = data;
// save token
this.setToken(token);
return this.afterLoginAction(goHome);
} catch (error) {
return Promise.reject(error);
}
},
afterLoginAction
方法用于用户登录后,进行角色、权限、菜单、路由等配置操作。
- 调用
getUserInfoAction
获取登录用户信息,- 调用 mock 服务
getUserInfo()
, - 根据返回用户登录信息,设置角色列表,
- 更新状态对象值。
- 调用 mock 服务
- 调用权限存储构建路由列表(详细逻辑稍后文章会详细讲解)。
- 若是路由还没态添加过,调用
buildRoutesAction()
方法, 根据不同的权限处理方式构建路由列表 - 根据用户指定不同的后台首页进行跳转。
- 若是路由还没态添加过,调用
async afterLoginAction(goHome?: boolean): Promise<GetUserInfoModel | null> {
if (!this.getToken) return null;
// get user info
const userInfo = await this.getUserInfoAction();
...
const permissionStore = usePermissionStore();
if (!permissionStore.isDynamicAddedRoute) {
const routes = await permissionStore.buildRoutesAction();
routes.forEach((route) => {
router.addRoute(route as unknown as RouteRecordRaw);
});
router.addRoute(PAGE_NOT_FOUND_ROUTE as unknown as RouteRecordRaw);
permissionStore.setDynamicAddedRoute(true);
}
goHome && (await router.replace(userInfo?.homePath || PageEnum.BASE_HOME));
...
return userInfo;
},
async getUserInfoAction(): Promise<UserInfo | null> {
if (!this.getToken) return null;
const userInfo = await getUserInfo();
const { roles = [] } = userInfo;
if (isArray(roles)) {
const roleList = roles.map((item) => item.value) as RoleEnum[];
this.setRoleList(roleList);
} else {
userInfo.roles = [];
this.setRoleList([]);
}
this.setUserInfo(userInfo);
return userInfo;
},
登录页面中调用 userStore.login()
方法进行系统登录操作。
// src\views\sys\login\LoginForm.vue
const userInfo = await userStore.login({
password: data.password,
username: data.account,
mode: 'none', //不要默认的错误提示
});
if (userInfo) {
notification.success({
message: t('sys.login.loginSuccessTitle'),
description: `${t('sys.login.loginSuccessDesc')}: ${userInfo.realName}`,
duration: 3,
});
}
用户注销
logout
注销方法调用 mock 服务 doLogout()
,同时清空 token
和用户信息,设置用户登录状态失效,然后跳转系统登录界面。
// 用户注销
async logout(goLogin = false) {
if (this.getToken) {
try {
await doLogout();
} catch {
console.log('注销Token失败');
}
}
this.setToken(undefined);
this.setSessionTimeout(false);
this.setUserInfo(null);
goLogin && router.push(PageEnum.BASE_LOGIN);
},
confirmLoginOut()
会弹出系统确认框,确认后调用 logout
方法。
// 退出系统确认框
confirmLoginOut() {
const { createConfirm } = useMessage();
const { t } = useI18n();
createConfirm({
iconType: 'warning',
title: () => h('span', t('sys.app.logoutTip')),
content: () => h('span', t('sys.app.logoutMessage')),
onOk: async () => {
await this.logout(true);
},
});
},
0x02 📚参考
0x03 关注专栏
此文章已收录到专栏中 👇,可以直接关注。
转载自:https://juejin.cn/post/7103913895194001421