Skip to content

Store 状态管理

说明

前端使用 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);
}

温馨提示

Token 通过 Storage 工具类管理,支持自动过期(默认 7 天)。退出登录时 storage.clear() 会清除所有 localStorage 数据。权限列表 permissionsGetInfo 时从后端获取,存储在 user store 中供 v-perm 指令使用。

总结

Pinia 状态管理包含 user(登录与权限)、asyncRoute(动态路由与菜单)、projectSetting(项目配置)等核心模块。user store 管理 Token 和权限列表,asyncRoute store 管理动态路由生成和组件缓存。Store 之间通过路由守卫协调工作,完成登录 → 获取信息 → 生成路由 → 挂载应用的完整流程。

小蚂蚁云团队 · 提供技术支持