menu路由构建优化

This commit is contained in:
2025-10-15 17:54:40 +08:00
parent ab22fe1008
commit 5f76c539f4
4 changed files with 189 additions and 57 deletions

View File

@@ -23,6 +23,9 @@ export interface SysMenu extends BaseDTO {
url?: string; url?: string;
/** 菜单组件 */ /** 菜单组件 */
component?: string; component?: string;
/** 菜单布局 */
layout?: string;
/** 菜单布局 */
/** 菜单图标 */ /** 菜单图标 */
icon?: string; icon?: string;
/** 菜单顺序 */ /** 菜单顺序 */

View File

@@ -83,16 +83,21 @@ function generateRouteFromMenu(menu: SysMenu, isTopLevel = true): RouteRecordRaw
// 如果有子菜单,使用布局组件 // 如果有子菜单,使用布局组件
if (menu.children && menu.children.length > 0) { if (menu.children && menu.children.length > 0) {
// 如果是顶层的NAVIGATION类型菜单使用NavigationLayout // 根据layout字段选择布局
if (isTopLevel && menu.type === MenuType.NAVIGATION) { const layout = (menu as any).layout || menu.component;
route.component = getComponent(menu.component || 'NavigationLayout');
} else if (menu.type === MenuType.SIDEBAR) { if (layout) {
// SIDEBAR类型的菜单使用BlankLayout避免嵌套布局 // 如果指定了layout使用指定的布局
// BlankLayout 只是一个纯容器,不会添加额外的导航栏或面包屑 route.component = getComponent(layout);
route.component = getComponent(menu.component || 'BlankLayout');
} else { } else {
// 其他情况使用BasicLayout // 根据菜单类型选择默认布局
route.component = getComponent(menu.component || 'BasicLayout'); if (isTopLevel && menu.type === MenuType.NAVIGATION) {
route.component = getComponent('NavigationLayout');
} else if (menu.type === MenuType.SIDEBAR) {
route.component = getComponent('BlankLayout');
} else {
route.component = getComponent('BasicLayout');
}
} }
} else { } else {
// 没有子菜单,使用具体的页面组件 // 没有子菜单,使用具体的页面组件
@@ -277,6 +282,7 @@ export function buildMenuTree(menus: SysMenu[]): SysMenu[] {
const menuMap = new Map<string, SysMenu>(); const menuMap = new Map<string, SysMenu>();
const rootMenus: SysMenu[] = []; const rootMenus: SysMenu[] = [];
const maxDepth = allMenus.length; // 最多遍历len层
// 创建菜单映射 // 创建菜单映射
allMenus.forEach(menu => { allMenus.forEach(menu => {
@@ -285,24 +291,48 @@ export function buildMenuTree(menus: SysMenu[]): SysMenu[] {
} }
}); });
// 构建树结构 // 循环构建树结构最多遍历maxDepth次
for (let depth = 0; depth < maxDepth; depth++) {
let hasChanges = false;
allMenus.forEach(menu => { allMenus.forEach(menu => {
const menuNode = menuMap.get(menu.menuID!); if (!menu.menuID) return;
const menuNode = menuMap.get(menu.menuID);
if (!menuNode) return; if (!menuNode) return;
if (!menu.parentID || menu.parentID === '0') { // 如果节点已经在树中,跳过
if (isNodeInTree(menuNode, rootMenus)) {
return;
}
if (!menu.parentID || menu.parentID === '0' || menu.parentID === '') {
// 根菜单 // 根菜单
if (!isNodeInTree(menuNode, rootMenus)) {
rootMenus.push(menuNode); rootMenus.push(menuNode);
hasChanges = true;
}
} else { } else {
// 子菜单 // 子菜单
const parent = menuMap.get(menu.parentID); const parent = menuMap.get(menu.parentID);
if (parent) { if (parent && isNodeInTree(parent, rootMenus)) {
parent.children = parent.children || []; if (!parent.children) {
parent.children = [];
}
if (!parent.children.includes(menuNode)) {
parent.children.push(menuNode); parent.children.push(menuNode);
hasChanges = true;
}
} }
} }
}); });
// 如果没有变化,说明树构建完成
if (!hasChanges) {
break;
}
}
// 按orderNum排序 // 按orderNum排序
const sortMenus = (menus: SysMenu[]): SysMenu[] => { const sortMenus = (menus: SysMenu[]): SysMenu[] => {
return menus return menus
@@ -316,6 +346,19 @@ export function buildMenuTree(menus: SysMenu[]): SysMenu[] {
return sortMenus(rootMenus); return sortMenus(rootMenus);
} }
// 检查节点是否已经在树中
function isNodeInTree(node: SysMenu, tree: SysMenu[]): boolean {
for (const treeNode of tree) {
if (treeNode.menuID === node.menuID) {
return true;
}
if (treeNode.children && isNodeInTree(node, treeNode.children)) {
return true;
}
}
return false;
}
/** /**
* 根据权限过滤菜单 * 根据权限过滤菜单
* @param menus 菜单列表 * @param menus 菜单列表

View File

@@ -271,33 +271,63 @@ async function loadDeptList() {
// 将扁平数据转换为树形结构 // 将扁平数据转换为树形结构
function buildTree(flatData: SysDept[]): SysDept[] { function buildTree(flatData: SysDept[]): SysDept[] {
if (!flatData || flatData.length === 0) {
return [];
}
const tree: SysDept[] = []; const tree: SysDept[] = [];
const map: Record<string, SysDept> = {}; const map = new Map<string, SysDept>();
const maxDepth = flatData.length; // 最多遍历len层
// 创建映射表 // 初始化所有节点
flatData.forEach(item => { flatData.forEach(item => {
if (item.deptID) { if (item.deptID) {
map[item.deptID] = { ...item, children: [] }; map.set(item.deptID, { ...item, children: [] });
} }
}); });
// 构建树结构 // 循环构建树结构最多遍历maxDepth次
for (let depth = 0; depth < maxDepth; depth++) {
let hasChanges = false;
flatData.forEach(item => { flatData.forEach(item => {
if (item.deptID) { if (!item.deptID) return;
const node = map[item.deptID];
if (item.parentID && map[item.parentID]) { const node = map.get(item.deptID);
// 有父节点添加到父节点的children中 if (!node) return;
if (!map[item.parentID].children) {
map[item.parentID].children = []; // 如果节点已经在树中,跳过
if (isNodeInTree(node, tree)) {
return;
} }
map[item.parentID].children!.push(node);
} else { if (!item.parentID || item.parentID === '0' || item.parentID === '') {
// 没有父节点或父节点不存在,作为根节点 // 根节点
if (!isNodeInTree(node, tree)) {
tree.push(node); tree.push(node);
hasChanges = true;
}
} else {
// 查找父节点
const parent = map.get(item.parentID);
if (parent && isNodeInTree(parent, tree)) {
if (!parent.children) {
parent.children = [];
}
if (!parent.children.includes(node)) {
parent.children.push(node);
hasChanges = true;
}
} }
} }
}); });
// 如果没有变化,说明树构建完成
if (!hasChanges) {
break;
}
}
// 清理空的children数组 // 清理空的children数组
function cleanEmptyChildren(nodes: SysDept[]) { function cleanEmptyChildren(nodes: SysDept[]) {
nodes.forEach(node => { nodes.forEach(node => {
@@ -313,6 +343,19 @@ function buildTree(flatData: SysDept[]): SysDept[] {
return tree; return tree;
} }
// 检查节点是否已经在树中
function isNodeInTree(node: SysDept, tree: SysDept[]): boolean {
for (const treeNode of tree) {
if (treeNode.deptID === node.deptID) {
return true;
}
if (treeNode.children && isNodeInTree(node, treeNode.children)) {
return true;
}
}
return false;
}
// 新增部门 // 新增部门
function handleAdd() { function handleAdd() {
isEdit.value = false; isEdit.value = false;

View File

@@ -347,33 +347,63 @@ async function loadMenuList() {
// 将扁平数据转换为树形结构 // 将扁平数据转换为树形结构
function buildTree(flatData: SysMenu[]): SysMenu[] { function buildTree(flatData: SysMenu[]): SysMenu[] {
if (!flatData || flatData.length === 0) {
return [];
}
const tree: SysMenu[] = []; const tree: SysMenu[] = [];
const map: Record<string, SysMenu> = {}; const map = new Map<string, SysMenu>();
const maxDepth = flatData.length; // 最多遍历len层
// 创建映射表 // 初始化所有节点
flatData.forEach(item => { flatData.forEach(item => {
if (item.menuID) { if (item.menuID) {
map[item.menuID] = { ...item, children: [] }; map.set(item.menuID, { ...item, children: [] });
} }
}); });
// 构建树结构 // 循环构建树结构最多遍历maxDepth次
for (let depth = 0; depth < maxDepth; depth++) {
let hasChanges = false;
flatData.forEach(item => { flatData.forEach(item => {
if (item.menuID) { if (!item.menuID) return;
const node = map[item.menuID];
if (item.parentID && map[item.parentID]) { const node = map.get(item.menuID);
// 有父节点添加到父节点的children中 if (!node) return;
if (!map[item.parentID].children) {
map[item.parentID].children = []; // 如果节点已经在树中,跳过
if (isNodeInTree(node, tree)) {
return;
} }
map[item.parentID].children!.push(node);
} else { if (!item.parentID || item.parentID === '0' || item.parentID === '') {
// 没有父节点或父节点不存在,作为根节点 // 根节点
if (!isNodeInTree(node, tree)) {
tree.push(node); tree.push(node);
hasChanges = true;
}
} else {
// 查找父节点
const parent = map.get(item.parentID);
if (parent && isNodeInTree(parent, tree)) {
if (!parent.children) {
parent.children = [];
}
if (!parent.children.includes(node)) {
parent.children.push(node);
hasChanges = true;
}
} }
} }
}); });
// 如果没有变化,说明树构建完成
if (!hasChanges) {
break;
}
}
// 清理空的children数组 // 清理空的children数组
function cleanEmptyChildren(nodes: SysMenu[]) { function cleanEmptyChildren(nodes: SysMenu[]) {
nodes.forEach(node => { nodes.forEach(node => {
@@ -389,6 +419,19 @@ function buildTree(flatData: SysMenu[]): SysMenu[] {
return tree; return tree;
} }
// 检查节点是否已经在树中
function isNodeInTree(node: SysMenu, tree: SysMenu[]): boolean {
for (const treeNode of tree) {
if (treeNode.menuID === node.menuID) {
return true;
}
if (treeNode.children && isNodeInTree(node, treeNode.children)) {
return true;
}
}
return false;
}
// 新增菜单 // 新增菜单
function handleAdd() { function handleAdd() {
isEdit.value = false; isEdit.value = false;