BookSystem/frontend/src/modules/admin/stores/auth.ts
jayhgq 12a577f9a3 feat: 实现动态菜单功能,替换硬编码菜单
1.  后端新增menu字段接口和route权限字段,重构权限接口返回菜单树
2.  前端替换硬编码侧边菜单为动态渲染,新增菜单store和图标映射逻辑
3.  调整权限路由元信息和初始化权限数据
2026-06-09 15:57:23 +08:00

112 lines
2.4 KiB
TypeScript

import { defineStore } from "pinia";
import { computed, ref } from "vue";
import { http } from "@/modules/admin/api/http";
export type UserProfile = {
id: number;
username: string;
nickname: string;
email?: string | null;
phone?: string | null;
role_id: number;
};
export type Permission = {
id: number;
name: string;
code: string;
parentid: number;
description?: string | null;
};
export type MenuItem = {
id: number;
name: string;
code: string;
parentid: number;
route: string;
description?: string;
children: MenuItem[];
};
export type RolePermissionResponse = {
role_id: number;
role_name: string;
permissions: Permission[];
menu: MenuItem[];
};
export const useAuthStore = defineStore("auth", () => {
const token = ref<string>(localStorage.getItem("access_token") || "");
const user = ref<UserProfile | null>(null);
const permissions = ref<Permission[]>([]);
const menu = ref<MenuItem[]>([]);
const isAuthenticated = computed(() => Boolean(token.value));
const permissionCodes = computed(() => permissions.value.map(p => p.code));
function setToken(t: string) {
token.value = t;
if (t) {
localStorage.setItem("access_token", t);
} else {
localStorage.removeItem("access_token");
}
}
async function fetchProfile() {
const { data } = await http.get<UserProfile>("/users/me");
user.value = data;
return data;
}
async function fetchPermissions() {
const { data } = await http.get<RolePermissionResponse>("/permissions/user/me");
permissions.value = data.permissions || [];
menu.value = data.menu || [];
return { permissions: permissions.value, menu: menu.value };
}
async function loginAndFetchData() {
await fetchProfile();
await fetchPermissions();
}
function hasPermission(code: string): boolean {
return permissionCodes.value.includes(code);
}
function hasAnyPermission(codes: string[]): boolean {
return codes.some(code => permissionCodes.value.includes(code));
}
async function logout() {
try {
await http.post("/users/logout");
} catch {
/* ignore */
}
user.value = null;
permissions.value = [];
menu.value = [];
setToken("");
}
return {
token,
user,
permissions,
menu,
permissionCodes,
isAuthenticated,
setToken,
fetchProfile,
fetchPermissions,
loginAndFetchData,
hasPermission,
hasAnyPermission,
logout,
};
});