主要修改了初始化JSON结构,使菜单层级更清晰。
修改后的代码
// 侧边栏列表
var menu_list = [
{
"name": "首页",
"url": ["/", "/index/index"],
"icon": "mdi mdi-home-variant-outline",
"is_out": 0
},
{
"name": "链接管理",
"url": ["#!"],
"icon": "mdi mdi-cookie-outline",
"is_out": 0,
"children": [
{
"name": "链接列表",
"url": ["/link/list", "/link/data"],
"is_out": 0
},
{
"name": "创建链接",
"url": ["/link/create", "/link/edit"],
"is_out": 0
}
]
},
{
"name": "财务管理",
"url": ["#!"],
"icon": "mdi mdi-credit-card-chip-outline",
"is_out": 0,
"children": [
{
"name": "余额充值",
"url": ["/finance/recharge"],
"is_out": 0
},
{
"name": "账单列表",
"url": ["/finance/bills"],
"is_out": 0
}
]
},
{
"name": "用户管理",
"url": ["/user/list"],
"icon": "mdi mdi-account-outline",
"is_out": 0,
},
{
"name": "系统设置",
"url": ["#!"],
"icon": ["mdi mdi-cog-outline"],
"is_out": 0,
"children": [
{
"name": "个人资料",
"url": ["/system/user"],
"is_out": 0,
},
{
"name": "操作日志",
"url": ["/system/logs"],
"is_out": 0,
"children": [
{
"name": "账单日志",
"url": ["/system/logs/bills"],
"is_out": 0,
},
{
"name": "登录日志",
"url": ["/system/logs/login"],
"is_out": 0,
},
{
"name": "链接日志",
"url": ["/system/logs/link"],
"is_out": 0,
}
]
}
]
},
{
"name": "退出登录",
"url": ["/user/logout"],
"icon": "mdi mdi-location-exit",
"is_out": 0
}
];
setSidebar(menu_list);
/**
* 菜单
* @param data 菜单JSON数据
* name 菜单名称 string
* url 菜单链接地址 array 首个为跳转地址。当路由为其中一个时,激活当前菜单选中状态。
* icon 图标
* is_out 是否外链0否|1是 int 外链a标签没有class='multitabs'
* children 子菜单 array
*/
function setSidebar(data) {
if (data.length == 0) return false;
processMenu(data);
console.log(data);
html = createMenu(data, true);
$('.sidebar-main').append(html);
}
// 创建html数据
function createMenu(data, is_frist) {
var menu_body = is_frist ? '<ul class="nav-drawer">' : '<ul class="nav nav-subnav">';
for (var i = 0; i < data.length; i++) {
iframe_class = data[i].is_out == 1 ? 'target="_blank"' : 'class="multitabs"';
icon_div = data[i].is_root == 1 ? '<i class="' + data[i].icon + '"></i>' : '';
menuName = data[i].is_root == 1 ? '<span>' + data[i].name + '</span>' : data[i].name;
if (data[i].children && data[i].children.length > 0) {
selected = data[i].is_active == 1 ? 'active open' : '';
menu_body += '<li class="nav-item nav-item-has-subnav ' + selected + '"><a href="javascript:void(0)">' + icon_div + menuName + '</a>';
menu_body += createMenu(data[i].children);
} else {
selected = data[i].is_active == 1 ? 'active' : '';
menu_body += '<li class="nav-item ' + selected + '"><a href="' + data[i].url[0] + '" ' + iframe_class + '>' + icon_div + menuName + '</a>';
}
menu_body += '</li>';
}
menu_body += '</ul>';
return menu_body;
};
// 添加 is_active 和 is_root 属性到所有层级
function addProperties(menu) {
menu.forEach(item => {
item.is_active = 0;
item.is_root = 0;
if (item.children && item.children.length > 0) {
addProperties(item.children);
}
});
}
// 标记激活状态和父级路径
function markActivePath(menu, currentPath, parentChain = []) {
for (let i = 0; i < menu.length; i++) {
const item = menu[i];
const newParentChain = [...parentChain, item];
// 检查当前路径是否匹配
const isMatch = item.url.some(url => url === currentPath);
if (isMatch) {
// 标记当前节点为激活
item.is_active = 1;
// 标记整个父链为激活
newParentChain.forEach(node => {
node.is_active = 1;
node.is_root = 1;
});
return true; // 找到匹配路径
}
// 递归检查子节点
if (item.children && item.children.length > 0) {
const foundInChildren = markActivePath(item.children, currentPath, newParentChain);
if (foundInChildren) {
// 标记当前节点为激活(因为子节点已激活)
item.is_active = 1;
item.is_root = 1;
return true;
}
}
}
return false; // 当前分支未找到匹配
}
// 主处理函数
function processMenu(menu) {
// 1. 添加初始属性
addProperties(menu);
// 2. 标记第一层级为根节点
menu.forEach(item => {
item.is_root = 1;
});
// 3. 获取当前页面路径
const currentPath = window.location.pathname;
// 4. 标记激活路径
markActivePath(menu, currentPath);
}
光年模板原代码
var menu_list = [
{
"id": "1",
"name": "后台首页",
"url": "lyear_main_1_v5.html",
"pid": 0,
"icon": "mdi mdi-home",
"is_out": 0,
"is_home": 1
},
{
"id": "2",
"name": "布局示例",
"url": "#!",
"pid": 0,
"icon": "mdi mdi-palette",
"is_out": 0,
"is_home": 0
},
{
"id": "3",
"name": "表单布局示例",
"url": "lyear_layout_form.html",
"pid": 2,
"icon": "",
"is_out": 0,
"is_home": 0
},
{
"id": "4",
"name": "聊天页面示例",
"url": "lyear_layout_chat.html",
"pid": 2,
"icon": "",
"is_out": 0,
"is_home": 0
},
{
"id": "5",
"name": "logo处使用文字",
"url": "lyear_layout_logo_text.html",
"pid": 2,
"icon": "",
"is_out": 1,
"is_home": 0
},
{
"id": "6",
"name": "多级菜单",
"url": "#!",
"pid": 0,
"icon": "mdi mdi-menu",
"is_out": 0,
"is_home": 0
},
{
"id": "7",
"name": "一级菜单",
"url": "#!",
"pid": 6,
"icon": "",
"is_out": 0,
"is_home": 0
},
{
"id": "8",
"name": "一级菜单",
"url": "#!",
"pid": 6,
"icon": "",
"is_out": 0,
"is_home": 0
},
{
"id": "9",
"name": "二级菜单",
"url": "#!",
"pid": 8,
"icon": "",
"is_out": 0,
"is_home": 0
},
{
"id": "10",
"name": "二级菜单",
"url": "#!",
"pid": 8,
"icon": "",
"is_out": 0,
"is_home": 0
},
{
"id": "11",
"name": "三级菜单",
"url": "#!",
"pid": 10,
"icon": "",
"is_out": 0,
"is_home": 0
},
{
"id": "12",
"name": "三级菜单",
"url": "#!",
"pid": 10,
"icon": "",
"is_out": 0,
"is_home": 0
}
];
setSidebar(menu_list);
/**
* 菜单
* @param data 菜单JSON数据
* id 菜单唯一ID
* name 菜单名称
* url 菜单链接地址
* icon 图标
* pid 父级ID
* is_out 是否外链0否|1是,外链a标签没有class='multitabs'
* is_home 是否首页
*/
function setSidebar(data){
if (data.length == 0) return false;
var treeObj = getTrees(data, 0, 'id', 'pid', 'children');
html = createMenu(treeObj, true);
$('.sidebar-main').append(html);
}
function createMenu(data, is_frist) {
var menu_body = is_frist ? '<ul class="nav-drawer">' : '<ul class="nav nav-subnav">';
for(var i = 0; i < data.length; i++){
iframe_class = data[i].is_out == 1 ? 'target="_blank"' : 'class="multitabs"';
icon_div = data[i].pid == 0 ? '<i class="' + data[i].icon + '"></i>' : '';
selected = (data[i].pid == 0) && (data[i].is_home == 1) ? 'active' : '';
menuName = data[i].pid == 0 ? '<span>' + data[i].name + '</span>' : data[i].name;
homeIdName = (data[i].pid == 0) && (data[i].is_home == 1) ? 'id="default-page"' : '';
if (data[i].children && data[i].children.length > 0) {
menu_body += '<li class="nav-item nav-item-has-subnav"><a href="javascript:void(0)">' + icon_div + menuName + '</a>';
menu_body += createMenu(data[i].children);
} else {
menu_body += '<li class="nav-item ' + selected + '"><a href="' + data[i].url + '" '+ iframe_class + homeIdName + '>' + icon_div + menuName + '</a>';
}
menu_body += '</li>';
}
menu_body += '</ul>';
return menu_body;
};
/**
* 树状的算法
* @params list 代转化数组
* @params parentId 起始节点
* @params idName 主键ID名
* @params parentIdName 父级ID名称
* @params childrenName 子级名称
* @author CSDN博主「伤包子」
*/
function getTrees(list, parentId, idName, parentIdName, childrenName) {
let items= {};
// 获取每个节点的直属子节点,*记住是直属,不是所有子节点
for (let i = 0; i < list.length; i++) {
let key = list[i][parentIdName];
if (items[key]) {
items[key].push(list[i]);
} else {
items[key] = [];
items[key].push(list[i]);
}
}
return formatTree(items, parentId, idName, childrenName);
}
/**
* 利用递归格式化每个节点
*/
function formatTree(items, parentId, idName, childrenName) {
let result = [];
if (!items[parentId]) {
return result;
}
for (let t in items[parentId]) {
items[parentId][t][childrenName] = formatTree(items, items[parentId][t][idName], idName, childrenName)
result.push(items[parentId][t]);
}
return result;
}