107 lines
3.4 KiB
Markdown
107 lines
3.4 KiB
Markdown
# 9.6 状态管理(Pinia)
|
||
|
||
:::tip 概述
|
||
前端使用 Pinia 进行全局状态管理,核心 Store 包含用户信息、动态路由、项目配置等。
|
||
:::
|
||
|
||
## Store 清单
|
||
|
||
| Store | 文件 | 说明 |
|
||
|-------|------|------|
|
||
| userStore | `store/modules/user.ts` | Token / 用户信息 / 权限列表 / 登录登出 |
|
||
| asyncRouteStore | `store/modules/asyncRoute.ts` | 动态路由 / keep-alive 缓存 |
|
||
| projectSettingStore | `store/modules/projectSetting.ts` | 项目配置 |
|
||
| ossConfigStore | `store/modules/ossConfig.ts` | 云存储配置 |
|
||
| designSettingStore | `store/modules/designSetting.ts` | 主题设计 |
|
||
| lockscreenStore | `store/modules/lockscreen.ts` | 锁屏 |
|
||
| tabsViewStore | `store/modules/tabsView.ts` | 标签页 |
|
||
|
||
## userStore 核心代码
|
||
|
||
```typescript
|
||
// ui/src/store/modules/user.ts
|
||
import { ACCESS_TOKEN, CURRENT_USER, IS_LOCKSCREEN } from '@/store/mutation-types';
|
||
import { ResultEnum } from '@/enums/httpEnum';
|
||
|
||
export const useUserStore = defineStore({
|
||
id: 'app-user',
|
||
state: (): IUserState => ({
|
||
token: Storage.get(ACCESS_TOKEN, ''),
|
||
username: '',
|
||
avatar: '',
|
||
permissions: [],
|
||
info: Storage.get(CURRENT_USER, {}),
|
||
}),
|
||
actions: {
|
||
// 登录(key/code 为验证码参数;响应字段为 snake_case 的 access_token,见 8.6)
|
||
async login(userInfo) {
|
||
const response = await login(userInfo);
|
||
const { code, data } = response;
|
||
if (code === ResultEnum.SUCCESS) {
|
||
const ex = 7 * 24 * 60 * 60 * 1000;
|
||
storage.set(ACCESS_TOKEN, data.access_token, ex); // 仅存 access_token
|
||
storage.set(IS_LOCKSCREEN, false);
|
||
this.setToken(data.access_token);
|
||
}
|
||
return response;
|
||
},
|
||
|
||
// 获取用户信息(含 roles / permissions / city)
|
||
async GetInfo() {
|
||
const res = await getUserInfo(); // GET /api/index/getUser
|
||
this.setPermissions(res.permissions);
|
||
this.setUserInfo(res);
|
||
this.setAvatar(res.avatar);
|
||
return res;
|
||
},
|
||
|
||
// 登出(后端同时吊销 refresh_token)
|
||
async logout() {
|
||
await logout();
|
||
this.setPermissions([]);
|
||
this.setUserInfo('');
|
||
storage.remove(ACCESS_TOKEN);
|
||
storage.remove(CURRENT_USER);
|
||
storage.clear();
|
||
},
|
||
},
|
||
});
|
||
```
|
||
|
||
:::tip 权限与后端一致
|
||
`GetInfo` 拿到的 `permissions` 与 `Authenticate` 中间件鉴权用的是同一份数据(复用 `JwtService::getUserPermissions` 的 Redis 缓存)——超管为 `['*:*:*']`,普通用户为角色关联的 `menu.permission` 列表。
|
||
:::
|
||
|
||
## 在组件中使用
|
||
|
||
:::tip Store 使用方式
|
||
- 在 `setup` 中:`const userStore = useUserStore()`
|
||
- 在 `setup` 外:`const userStore = useUserStoreWidthOut()`
|
||
:::
|
||
|
||
```typescript
|
||
import { useUserStore } from '@/store/modules/user';
|
||
|
||
const userStore = useUserStore();
|
||
|
||
// 获取用户信息
|
||
const username = userStore.username;
|
||
const permissions = userStore.permissions;
|
||
|
||
// 登录
|
||
await userStore.login({ username: 'admin', password: '123456', key, code });
|
||
|
||
// 登出
|
||
await userStore.logout();
|
||
```
|
||
|
||
## asyncRouteStore(动态路由)
|
||
|
||
| 状态/方法 | 说明 |
|
||
|------|------|
|
||
| `getIsDynamicAddedRoute` | 动态路由是否已加载(守卫据此跳过重复生成) |
|
||
| `generateRoutes(userInfo)` | 调用 `/api/index/getMenus` 生成路由表 |
|
||
| `keepAliveComponents` | 页面级缓存组件名列表(标签页切换保留状态) |
|
||
|
||
(生成细节见 [9.5 路由与动态菜单](/zh/frontend/router.html)。)
|