菜单布局等初步完成

This commit is contained in:
2025-10-08 14:11:54 +08:00
parent d9ea2e842b
commit 4bc587ecf5
29 changed files with 4472 additions and 11977 deletions

View File

@@ -8,6 +8,7 @@ import { Module } from 'vuex';
import { LoginDomain, SysMenu, SysPermission } from '@/types';
import { authApi } from '@/apis/auth';
import router from '@/router';
import { getFirstAccessibleMenuUrl, buildMenuTree } from '@/utils/route-generator';
// State接口定义
export interface AuthState {
@@ -23,17 +24,48 @@ export interface AuthState {
routesLoaded: boolean;
}
// 从localStorage恢复状态的辅助函数
function getStoredState(): Partial<AuthState> {
try {
const token = localStorage.getItem('token');
const loginDomainStr = localStorage.getItem('loginDomain');
const menusStr = localStorage.getItem('menus');
const permissionsStr = localStorage.getItem('permissions');
return {
token: token || null,
loginDomain: loginDomainStr ? JSON.parse(loginDomainStr) : null,
menus: menusStr ? JSON.parse(menusStr) : [],
permissions: permissionsStr ? JSON.parse(permissionsStr) : [],
routesLoaded: false, // 路由始终需要重新加载
};
} catch (error) {
console.error('从localStorage恢复状态失败:', error);
return {
token: null,
loginDomain: null,
menus: [],
permissions: [],
routesLoaded: false,
};
}
}
// 认证模块
const authModule: Module<AuthState, any> = {
namespaced: true,
state: (): AuthState => ({
loginDomain: null,
token: localStorage.getItem('token') || null,
menus: [],
permissions: [],
routesLoaded: false,
}),
state: (): AuthState => {
// 从localStorage恢复状态
const storedState = getStoredState();
return {
loginDomain: storedState.loginDomain || null,
token: storedState.token || null,
menus: storedState.menus || [],
permissions: storedState.permissions || [],
routesLoaded: false,
};
},
getters: {
// 是否已登录
@@ -85,10 +117,19 @@ const authModule: Module<AuthState, any> = {
state.menus = loginDomain.menus || [];
state.permissions = loginDomain.permissions || [];
// 存储token到localStorage
// 持久化到localStorage
if (state.token) {
localStorage.setItem('token', state.token);
}
if (loginDomain) {
localStorage.setItem('loginDomain', JSON.stringify(loginDomain));
}
if (state.menus.length > 0) {
localStorage.setItem('menus', JSON.stringify(state.menus));
}
if (state.permissions.length > 0) {
localStorage.setItem('permissions', JSON.stringify(state.permissions));
}
},
// 设置Token
@@ -104,11 +145,17 @@ const authModule: Module<AuthState, any> = {
// 设置菜单
SET_MENUS(state, menus: SysMenu[]) {
state.menus = menus;
if (menus.length > 0) {
localStorage.setItem('menus', JSON.stringify(menus));
}
},
// 设置权限
SET_PERMISSIONS(state, permissions: SysPermission[]) {
state.permissions = permissions;
if (permissions.length > 0) {
localStorage.setItem('permissions', JSON.stringify(permissions));
}
},
// 设置路由加载状态
@@ -123,13 +170,18 @@ const authModule: Module<AuthState, any> = {
state.menus = [];
state.permissions = [];
state.routesLoaded = false;
// 清除localStorage
localStorage.removeItem('token');
localStorage.removeItem('loginDomain');
localStorage.removeItem('menus');
localStorage.removeItem('permissions');
}
},
actions: {
// 登录
async login({ commit, dispatch }, loginParam) {
async login({ commit, dispatch, state }, loginParam) {
try {
const loginDomain = await authApi.login(loginParam);
@@ -138,8 +190,14 @@ const authModule: Module<AuthState, any> = {
// 生成动态路由
await dispatch('generateRoutes');
console.log(router.getRoutes())
// 获取第一个可访问的菜单URL用于登录后跳转
const firstMenuUrl = getFirstAccessibleMenuUrl(state.menus);
return Promise.resolve(loginDomain);
return Promise.resolve({
loginDomain,
redirectUrl: firstMenuUrl || '/home' // 如果没有菜单,跳转到首页
});
} catch (error) {
return Promise.reject(error);
}
@@ -163,11 +221,49 @@ const authModule: Module<AuthState, any> = {
}
},
// 恢复登录状态(页面刷新时使用)
async restoreAuth({ state, commit, dispatch }) {
try {
// 如果没有token无法恢复
if (!state.token) {
return false;
}
// 如果已经有完整的登录信息,直接生成路由
if (state.loginDomain && state.menus.length > 0) {
console.log('从localStorage恢复登录状态');
await dispatch('generateRoutes');
return true;
}
// 如果只有token需要从后端重新获取用户信息
// console.log('Token存在重新获取用户信息');
// const loginDomain = await authApi.getUserInfo(); // 需要后端提供这个接口
// commit('SET_LOGIN_DOMAIN', loginDomain);
// await dispatch('generateRoutes');
return true;
} catch (error) {
console.error('恢复登录状态失败:', error);
// 恢复失败,清除认证信息
commit('CLEAR_AUTH');
return false;
}
},
// 生成动态路由
async generateRoutes({ state, commit }) {
try {
// 如果路由已经加载,避免重复生成
if (state.routesLoaded) {
console.log('路由已加载,跳过生成');
return;
}
if (!state.menus || state.menus.length === 0) {
console.warn('用户菜单为空,无法生成路由');
commit('SET_ROUTES_LOADED', true); // 标记为已加载,避免重复尝试
return;
}
@@ -206,56 +302,6 @@ const authModule: Module<AuthState, any> = {
}
};
/**
* 构建菜单树结构
* @param menus 菜单列表
* @returns 菜单树
*/
function buildMenuTree(menus: SysMenu[]): SysMenu[] {
if (!menus || menus.length === 0) {
return [];
}
const menuMap = new Map<string, SysMenu>();
const rootMenus: SysMenu[] = [];
// 创建菜单映射
menus.forEach(menu => {
if (menu.menuID) {
menuMap.set(menu.menuID, { ...menu, children: [] });
}
});
// 构建树结构
menus.forEach(menu => {
const menuNode = menuMap.get(menu.menuID!);
if (!menuNode) return;
if (!menu.parentID || menu.parentID === '0') {
// 根菜单
rootMenus.push(menuNode);
} else {
// 子菜单
const parent = menuMap.get(menu.parentID);
if (parent) {
parent.children = parent.children || [];
parent.children.push(menuNode);
}
}
});
// 按orderNum排序
const sortMenus = (menus: SysMenu[]): SysMenu[] => {
return menus
.sort((a, b) => (a.orderNum || 0) - (b.orderNum || 0))
.map(menu => ({
...menu,
children: menu.children ? sortMenus(menu.children) : []
}));
};
return sortMenus(rootMenus);
}
/**
* 重置路由