签入版本
This commit is contained in:
@@ -0,0 +1,247 @@
|
||||
# Store 状态管理
|
||||
|
||||
<!-- Copyright (c) 2021-2026 小蚂蚁云团队 版权所有 -->
|
||||
|
||||
:::tip 说明
|
||||
前端使用 Pinia 进行状态管理,核心模块包括 `user`(用户信息与权限)、`asyncRoute`(动态路由与菜单)、`projectSetting`(项目配置)、`designSetting`(主题设计)等。
|
||||
:::
|
||||
|
||||
## Pinia 配置
|
||||
|
||||
```typescript
|
||||
// src/store/index.ts
|
||||
import { createPinia } from 'pinia';
|
||||
const pinia = createPinia();
|
||||
export default pinia;
|
||||
```
|
||||
|
||||
Store 模块位于 `src/store/modules/` 目录:
|
||||
|
||||
```
|
||||
src/store/modules/
|
||||
├── user.ts # 用户登录状态、Token、用户信息、权限列表
|
||||
├── asyncRoute.ts # 动态路由生成、菜单管理、组件缓存
|
||||
├── projectSetting.ts # 项目配置(权限模式、水印、动画等)
|
||||
├── designSetting.ts # 主题色、深色模式
|
||||
├── lockscreen.ts # 锁屏状态
|
||||
├── tabsView.ts # 标签页管理
|
||||
└── ossConfig.ts # OSS 配置
|
||||
```
|
||||
|
||||
## User Store
|
||||
|
||||
管理用户登录状态、Token、用户信息和权限列表,是核心 Store:
|
||||
|
||||
```typescript
|
||||
// src/store/modules/user.ts
|
||||
|
||||
import { defineStore } from 'pinia';
|
||||
import { createStorage } from '@/utils/Storage';
|
||||
import { ACCESS_TOKEN, CURRENT_USER } from '@/store/mutation-types';
|
||||
import { getUserInfo, login, logout } from '@/api/common/user';
|
||||
|
||||
const Storage = createStorage({ storage: localStorage });
|
||||
|
||||
export interface IUserState {
|
||||
token: string;
|
||||
username: string;
|
||||
welcome: string;
|
||||
avatar: string;
|
||||
permissions: any[];
|
||||
info: any;
|
||||
}
|
||||
|
||||
export const useUserStore = defineStore({
|
||||
id: 'app-user',
|
||||
state: (): IUserState => ({
|
||||
token: Storage.get(ACCESS_TOKEN, ''), // 从 localStorage 恢复 token
|
||||
username: '',
|
||||
welcome: '',
|
||||
avatar: '',
|
||||
permissions: [],
|
||||
info: Storage.get(CURRENT_USER, {}), // 从 localStorage 恢复用户信息
|
||||
}),
|
||||
getters: {
|
||||
getToken(): string { return this.token; },
|
||||
getPermissions(): [any][] { return this.permissions; },
|
||||
getUserInfo(): object { return this.info; },
|
||||
},
|
||||
actions: {
|
||||
// 登录
|
||||
async login(userInfo) {
|
||||
const response = await login(userInfo);
|
||||
const { code, data } = response;
|
||||
if (code === ResultEnum.SUCCESS) {
|
||||
const ex = 7 * 24 * 60 * 60 * 1000; // 7 天过期
|
||||
storage.set(ACCESS_TOKEN, data.access_token, ex);
|
||||
this.setToken(data.access_token);
|
||||
}
|
||||
return Promise.resolve(response);
|
||||
},
|
||||
|
||||
// 获取用户信息
|
||||
GetInfo() {
|
||||
return new Promise((resolve, reject) => {
|
||||
getUserInfo().then((res) => {
|
||||
if (res.permissions && res.permissions.length) {
|
||||
this.setPermissions(res.permissions);
|
||||
}
|
||||
this.setUserInfo(res);
|
||||
this.setAvatar(res.avatar);
|
||||
resolve(res);
|
||||
}).catch(reject);
|
||||
});
|
||||
},
|
||||
|
||||
// 登出
|
||||
async logout() {
|
||||
return new Promise((resolve, reject) => {
|
||||
logout().then(() => {
|
||||
this.setPermissions([]);
|
||||
this.setUserInfo('');
|
||||
storage.remove(ACCESS_TOKEN);
|
||||
storage.remove(CURRENT_USER);
|
||||
storage.clear();
|
||||
resolve('');
|
||||
}).catch(reject);
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Token 存储机制
|
||||
|
||||
Token 通过 `Storage` 工具类存储在 `localStorage` 中,键名为 `ACCESS-TOKEN`,自动大写转换,支持 7 天过期时间:
|
||||
|
||||
```typescript
|
||||
// src/utils/Storage.ts
|
||||
export const createStorage = ({ prefixKey = '', storage = localStorage } = {}) => {
|
||||
const Storage = class {
|
||||
set(key: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) {
|
||||
const stringData = JSON.stringify({
|
||||
value,
|
||||
expire: expire !== null ? Date.now() + expire : null,
|
||||
});
|
||||
this.storage.setItem(this.getKey(key), stringData);
|
||||
}
|
||||
|
||||
get(key: string, def: any = null) {
|
||||
const item = this.storage.getItem(this.getKey(key));
|
||||
if (item) {
|
||||
const data = JSON.parse(item);
|
||||
const { value, expire } = data;
|
||||
if (expire === null || expire >= Date.now()) return value;
|
||||
this.remove(key); // 过期则删除
|
||||
}
|
||||
return def;
|
||||
}
|
||||
};
|
||||
return new Storage();
|
||||
};
|
||||
export const storage = createStorage();
|
||||
```
|
||||
|
||||
## AsyncRoute Store
|
||||
|
||||
管理动态路由和菜单,是路由系统的核心:
|
||||
|
||||
```typescript
|
||||
// src/store/modules/asyncRoute.ts
|
||||
export const useAsyncRouteStore = defineStore({
|
||||
id: 'app-async-route',
|
||||
state: (): IAsyncRouteState => ({
|
||||
menus: [], // 菜单列表
|
||||
routers: constantRouter, // 静态路由
|
||||
addRouters: [], // 动态路由
|
||||
keepAliveComponents: [], // 需要缓存的组件名
|
||||
isDynamicAddedRoute: false, // 路由是否已添加
|
||||
}),
|
||||
actions: {
|
||||
async generateRoutes(data) {
|
||||
const { getPermissionMode } = useProjectSetting();
|
||||
const permissionMode = unref(getPermissionMode);
|
||||
let accessedRouters;
|
||||
|
||||
if (permissionMode === 'BACK') {
|
||||
// 后端控制模式:从后端获取菜单生成路由
|
||||
accessedRouters = await generatorDynamicRouter();
|
||||
} else {
|
||||
// 前端控制模式:根据权限过滤静态路由表
|
||||
accessedRouters = filter(asyncRoutes, routeFilter);
|
||||
}
|
||||
|
||||
this.setRouters(accessedRouters);
|
||||
this.setMenus(accessedRouters);
|
||||
return toRaw(accessedRouters);
|
||||
},
|
||||
|
||||
setRouters(routers) {
|
||||
this.addRouters = routers;
|
||||
this.routers = constantRouter.concat(routers);
|
||||
},
|
||||
setMenus(menus) { this.menus = menus; },
|
||||
setDynamicAddedRoute(added: boolean) { this.isDynamicAddedRoute = added; },
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Store 间协作
|
||||
|
||||
登录流程中 Store 协作:
|
||||
|
||||
```typescript
|
||||
// 路由守卫中的调用(router-guards.ts)
|
||||
const userStore = useUserStoreWidthOut();
|
||||
const asyncRouteStore = useAsyncRouteStoreWidthOut();
|
||||
|
||||
// 1. 获取用户信息(设置 permissions)
|
||||
const userInfo = await userStore.GetInfo();
|
||||
// 2. 根据用户信息生成动态路由
|
||||
const routes = await asyncRouteStore.generateRoutes(userInfo);
|
||||
// 3. 动态添加路由
|
||||
routes.forEach((item) => router.addRoute(item));
|
||||
```
|
||||
|
||||
## 使用方式
|
||||
|
||||
在组件中使用 store:
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
||||
// 获取用户信息
|
||||
const userInfo = computed(() => userStore.getUserInfo);
|
||||
|
||||
// 获取权限列表
|
||||
const permissions = computed(() => userStore.getPermissions);
|
||||
|
||||
// 登出
|
||||
const handleLogout = async () => {
|
||||
await userStore.logout();
|
||||
router.push('/login');
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
在 setup 外使用 store(需要传入 pinia 实例):
|
||||
|
||||
```typescript
|
||||
import { store } from '@/store';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
export function useUserStoreWidthOut() {
|
||||
return useUserStore(store);
|
||||
}
|
||||
```
|
||||
|
||||
:::tip 温馨提示
|
||||
Token 通过 `Storage` 工具类管理,支持自动过期(默认 7 天)。退出登录时 `storage.clear()` 会清除所有 localStorage 数据。权限列表 `permissions` 在 `GetInfo` 时从后端获取,存储在 user store 中供 `v-perm` 指令使用。
|
||||
:::
|
||||
|
||||
## 总结
|
||||
|
||||
Pinia 状态管理包含 user(登录与权限)、asyncRoute(动态路由与菜单)、projectSetting(项目配置)等核心模块。user store 管理 Token 和权限列表,asyncRoute store 管理动态路由生成和组件缓存。Store 之间通过路由守卫协调工作,完成登录 → 获取信息 → 生成路由 → 挂载应用的完整流程。
|
||||
Reference in New Issue
Block a user