通用后台管理系统UI-AdminLTE:构造动态菜单栏
AdminLTE是一款基于bootstrap的后台管理系统的通用模板UI,它的样式美观且较为符合大多数后台管理系统的需求,典型的上|左右|下的布局形式。并且提供了一整套我们开发的时候可能用到的UI样式,比如表格,表单,图表,日历等。非常适合像我这样对样式编排不太擅长的后端开发者。有了它,我们可以不用自己去写很多复杂的html,css。而把更多时间和精力留在后台的开发上。
话不多说,接下来我简要的介绍一下这款模板UI框架的用法。
该款框架是免费的,可以直接在官网下载,下载下来的文件大概有50多兆,包含了所有的html,css,js,还有很多的demo。可供我们随时查阅学习。
先来看看整体的UI的风格吧,是不是挺炫酷的。

可以说几乎所有的后台需要用到的样式都可以从中寻找得到。
由于全套的UI都是静态数据,所以本篇文章着重介绍一下如何动态构造左侧的菜单栏。这应该也是大家比较关心的问题。
所谓的动态就是指的是从数据库或者文件,或者内存中取到的数据。
本人习惯将菜单的数据写成一个静态的js文件,放在项目的js目录中,这样做的好处是不必每次都去从数据库请求,减少IO操作造成的性能和时间损失,当然你也可以从数据库去请求,甚至把菜单数据放入到redis等内存数据库。各类方法都不影响我们前端代码的编写,因为传递的数据格式都是json.
1.将菜单数据写入一个json文件,代码如下。该文件的路径为webapp/static/json/menu.json。
[{
	  "menuId":"1",
      "name": "基本信息",
      "controller":"#",
      "child": []
  },{
	  "menuId":"2",
      "name": "会员管理",
      "controller":"#",
      "child": [{
		  "menuId":"3",
		  "pMenuId":"2",
          "name": "会员概览",
          "controller":"user/home"
      },{
		  "menuId":"4",
		  "pMenuId":"2",
          "name": "添加会员",
          "controller":"user/add"
      }] 
  },{
      "menuId":"5",
      "name": "销售管理",
      "controller":"#",
      "child": [{
	      "menuId":"5",
		  "pMenuId":"6",
          "name": "销售返佣",
          "controller":"post/home"
      },{
		  "menuId":"5",
		  "pMenuId":"7",
          "name": "销售报表",
          "controller":"post/add"
      }]
  }]
至于如何写入到文件,我们可以在每次修改菜单以后,先获取菜单的json数据,然后调用如下代码来将菜单的json数据写入文件。
    public void generateMenuJson(String jsonStr) {
        try {
            File f = new File(ServletActionContext.getServletContext()
                    .getRealPath("/static/json") + "/menu.json");
            if (!f.exists()) {
                f.createNewFile();
            }
            // 定义编码
            OutputStreamWriter write = new OutputStreamWriter(
                    new FileOutputStream(f), "UTF-8");
            BufferedWriter writer = new BufferedWriter(write);
            writer.write(jsonStr);
            writer.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
2.前台ajax获取json数据,并且动态构造出html元素及样式。
主要是用了jQuery ajax异步获取数据,还有就是jquery的append()方法,代码没有什么高深的,只是拼接的时候要很认真。一个取巧的方法是去下载下来的html源码中复制一些关键的代码,避免写错。
 $(function () {
     $.ajax({
         type: 'get',
         url:'static/json/menu.json',
         dataType:'json',
         success:function(data){
             var menu=null;
             var html=null;
             var childLen=null;
             var child=null;
             var json=data;
             console.log(json);
             for(var i in json){
                 menu=json[i];
                 //这里默认展开第一个主菜单
                 if(i==0){
                     html=$('<li menu-id="'+i+'" class="active treeview "><li>');
                 }else{
                     html=$('<li menu-id="'+i+'" class="treeview "><li>');
                 }
                 $(".sidebar .sidebar-menu").append(html);
                 html=$('<a href="'+menu.controller+'"><i class="fa fa-dashboard"></i> <span>'+menu.name+'</span><span class="pull-right-container"><i class="fa fa-angle-left pull-right"></i></span></a><ul menuUL-id="'+i+'" class="treeview-menu"></ul>');
                 $('[menu-id="'+i+'"]').append(html);
                 //继续遍历二级菜单
                 childLen=menu.child.length;
                 for(var j in menu.child){
                     child=menu.child[j];
                     //这里默认设置第一个子菜单为选中状态
                     if(j==0){
                             html=$('<li class="active"><a href="'+child.controller+'"><i class="fa fa-circle-o"></i>'+child.name+'</a></li>');
                     }else{
                             html=$('<li><a href="'+child.controller+'"><i class="fa fa-circle-o"></i>'+child.name+'</a></li>');
                     }
                     $('[menuUL-id="'+i+'"]').append(html);
                 }
             }
         }
 });
 });
相信大家对照demo中的html源码,可以很容易的理解上述代码。我们把原来的左侧菜单的静态html注释掉,引入上述的js文件。看看效果。

这样基本实现了菜单显示的效果。但是还有一个缺点,就是菜单不能根据页面动态变换样式,比如我们在会员概览页面,此时展开的是会员管理,选中的是会员概览。而当我们到了销售管理的时候,我们又希望此时菜单能展开销售管理主菜单并选中相应的子菜单。
要实现这个功能,我们需要在上述的js代码中传入当前访问的链接的菜单id,以及其父级菜单id(如果是最上层的菜单,则其父菜单为自己)。然后根据这两个参数来进行菜单是否展开即是否选中的判断。具体步骤如下。
1.菜单实体如下
package com.wonyen.entity;
public class TMenu {
    private int menuId;//菜单编号
    private int pMenuId;//父菜单编号
    private String name;//菜单名称
    private String controller;//菜单对应的controller
    public int getMenuId() {
        return menuId;
    }
    public void setMenuId(int menuId) {
        this.menuId = menuId;
    }
    public int getpMenuId() {
        return pMenuId;
    }
    public void setpMenuId(int pMenuId) {
        this.pMenuId = pMenuId;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getController() {
        return controller;
    }
    public void setController(String controller) {
        this.controller = controller;
    }
}
2.获取当前菜单对象,并传入到前台。以会员概览页面为例,代码如下。第8行获取了当前的菜单对象,第9行将其传入到ModelAndView对象中,我们就可以在转发的jsp页面中拿到这个menu对象了。
     @RequestMapping("partnerHome")
     public ModelAndView partnerHome(ParamModel pm) {
         ModelAndView mv = new ModelAndView("back/partner/PartnerList");
         mv.addObject("pm", pm);
         List<TPartnerLevel> partnerLevelList = partnerLevelService
                 .getPartnerLevelList();
         mv.addObject("partnerLevelList", partnerLevelList);
         TMenu menu=menuService.getMenuByController("partnerHome");//获取当前的菜单信息
         mv.addObject("menu", menu);
         return mv;
     }
3.在每个页面body加入两个属性,menu_id和p_menu_id.如下所示。
<body class="hold-transition skin-red sidebar-mini" menu_id="${menu.menuId}" p_menu_id="${menu.pMenuId}">
4.重新改写原来js代码,让其支持根据当前页面来变换选中的菜单。
$(function () {
    var menu_id=$('body').attr('menu_id');//菜单id
    var p_menu_id=$('body').attr('p_menu_id');//父级菜单id
    $.ajax({
        type: 'get',
        url:'static/json/menu.json',
        dataType:'json',
        success:function(data){
            var menu=null;
            var html=null;
            var childLen=null;
            var child=null;
            var json=data;
            console.log(json);
            for(var i in json){
                menu=json[i];
                //如果父菜单是该菜单,就展开
                if(menu.menuId==p_menu_id){
                    html=$('<li menu-id="'+i+'" class="active treeview "><li>');
                }else{
                    html=$('<li menu-id="'+i+'" class="treeview "><li>');
                }
                $(".sidebar .sidebar-menu").append(html);
                html=$('<a href="'+menu.controller+'"><i class="fa fa-dashboard"></i> <span>'+menu.name+'</span><span class="pull-right-container"><i class="fa fa-angle-left pull-right"></i></span></a><ul menuUL-id="'+i+'" class="treeview-menu"></ul>');
                $('[menu-id="'+i+'"]').append(html);
                //继续遍历二级菜单
                childLen=menu.child.length;
                for(var j in menu.child){
                    child=menu.child[j];
                    //如果子菜单是该菜单,则设为active选中
                    if(child.menuId==menu_id){
                            html=$('<li class="active"><a href="'+child.controller+'"><i class="fa fa-circle-o"></i>'+child.name+'</a></li>');
                    }else{
                            html=$('<li><a href="'+child.controller+'"><i class="fa fa-circle-o"></i>'+child.name+'</a></li>');
                    }
                    $('[menuUL-id="'+i+'"]').append(html);
                }
            }
        }
});
});
经过上述步骤以后,我们就可以得到更为合理的左侧菜单了。来看看效果吧。
--会员概览页

---会员添加页

看,已经实现了我们想要的效果。如果你也正在寻找一个后台的UI,赶紧试试吧。
-----------------------------------------------------------------------------------------------2018年4月2日更新-------------------------------------------------------------------------------------------------------------------------------------------------------------------
最近发现一种新的记录菜单选中情况的方法,就是利用浏览器的缓存,每次点击菜单的时候,将点击的菜单Id记录到浏览器缓存中,然后加载菜单的时候通过缓存中的菜单来比对,匹配成功就将其展开并且设为active.这种方法就不需要在服务端传入当前的菜单了,简单了很多。
 $(function () {
     var firstLvId=getFirstLvMenu();//一级菜单
     var secondLvId=getSecondLvMenu();//二级菜单
     var thirdLvId=getThirdLvMenu();//三级菜单
     $.ajax({
         type: 'get',
         url:'/menu/tree',
         dataType:'json',
         success:function(data){
             var menu=null;
             var html=null;
             var childLen=null;
             var child=null;
             var json=data;
             console.log(json);
             for(var i in json){
                 menu=json[i];
                 //如果父菜单是该菜单,就展开
                 if(menu.menuId==firstLvId){
                     html=$('<li menu-id="'+i+'" class="active treeview "><li>');
                 }else{
                     html=$('<li menu-id="'+i+'" class="treeview "><li>');
                 }
                 $(".sidebar .sidebar-menu").append(html);
                 html=$('<a class="first-menu" href="../'+menu.menuSrc+'" onclick="saveFirstLvMenu('+menu.menuId+')"><i class="fa fa-dashboard"></i> <span>'+menu.menuName+'</span><span class="pull-right-container"><i class="fa fa-angle-left pull-right"></i></span></a><ul menuUL-id="'+i+'" class="treeview-menu"></ul>');
                 $('[menu-id="'+i+'"]').append(html);
                 //继续遍历二级菜单
                 childLen=menu.child.length;
                 for(var j in menu.child){
                     child=menu.child[j];
                     //如果子菜单是该菜单,则设为active选中
                     if(child.menuId==secondLvId){
                             html=$('<li class="active"><a class="second-menu" href="../'+child.menuSrc+'" onclick="saveSecondLvMenu('+child.menuId+')"><i class="fa fa-circle-o"></i>'+child.menuName+'</a></li>');
                     }else{
                             html=$('<li><a class="second-menu" href="../'+child.menuSrc+'" onclick="saveSecondLvMenu('+child.menuId+')"><i class="fa fa-circle-o"></i>'+child.menuName+'</a></li>');
                     }
                     $('[menuUL-id="'+i+'"]').append(html);
                 }
             }
         }
 });
 });
 function saveFirstLvMenu(menuId) {
     var id = JSON.stringify(menuId);
      window.sessionStorage.setItem("firstMenuId", id);
 }
 function saveSecondLvMenu(menuId) {
     var id = JSON.stringify(menuId);
      window.sessionStorage.setItem("secondMenuId", id);
 }
 function saveThirdLvMenu(menuId) {
     var id = JSON.stringify(menuId);
      window.sessionStorage.setItem("thirdMenuId", id);
 }
 function getFirstLvMenu() {
     return JSON.parse(window.sessionStorage.getItem("firstMenuId"));
 }
 function getSecondLvMenu() {
     return JSON.parse(window.sessionStorage.getItem("secondMenuId"));
 }
 function getThirdLvMenu() {
     return JSON.parse(window.sessionStorage.getItem("thirdMenuId"));
 }
通用后台管理系统UI-AdminLTE:构造动态菜单栏的更多相关文章
- 通用后台管理系统UI模板-AdminLTE简介及构造动态菜单栏
		
AdminLTE是一款基于bootstrap的后台管理系统的通用模板UI,它的样式美观且较为符合大多数后台管理系统的需求,典型的上|左右|下的布局形式.并且提供了一整套我们开发的时候可能用到的UI样式 ...
 - ASP.NET MVC5+EF6+LayUI实战教程,通用后台管理系统框架(1)
		
文章转自:http://www.xuboyi.com/298.html 前言 网站运营有一段时间了,记录的内容都是杂七杂八的,思前想后,决定给大家分享一套ASP.Net的系列教程.手把手的做一套通用后 ...
 - 分享基于EF+MVC+Bootstrap的通用后台管理系统及架构
		
基于EF+MVC+Bootstrap构建通用后台管理系统,集成轻量级的缓存模块.日志模块.上传缩略图模块.通用配置及服务调用, 提供了OA.CRM.CMS的原型实例,适合快速构建中小型互联网及行业 ...
 - 分享基于EF+MVC+Bootstrap的通用后台管理系统及架构(转)
		
http://www.cnblogs.com/guozili/p/3496265.html 基于EF+MVC+Bootstrap构建通用后台管理系统,集成轻量级的缓存模块.日志模块.上传缩略图模块.通 ...
 - 基于EF+MVC+Bootstrap的通用后台管理系统及架构
		
分享基于EF+MVC+Bootstrap的通用后台管理系统及架构 基于EF+MVC+Bootstrap构建通用后台管理系统,集成轻量级的缓存模块.日志模块.上传缩略图模块.通用配置及服务调用, 提供了 ...
 - asp.net EF+MVC+Bootstrap 通用后台管理系统
		
需要源码,请加QQ:858-048-581 开发环境: VS2012或以上 数据库: SQL Server 2008R2或以上 基于EF+MVC+Bootstrap构建通用后台管理系统,集成轻量级 ...
 - 通用后台管理系统(ExtJS 4.2 + Spring MVC 3.2 + Hibernate)
		
通用后台管理系统(ExtJS 4.2 +Spring MVC 3.2 + Hibernate) 开发语言JAVA 成品成品 前端技术extjs 数据库mysql,sql server,oracle 系 ...
 - 后台管理系统-使用AdminLTE搭建前端
		
返回总目录<ABP项目实战-后台管理系统-目录> 安装AdminLte 我们通过Nuget包管理器安装AdminLte 引用三方组件 因为AdminLte使用到了很多三方的组件,所以我们需 ...
 - ASP.NET MVC5 + EF6 + LayUI实战教程,通用后台管理系统框架(3)
		
前言 本节将我们自己的CSS样式替换系统自带的 开始搭建 将脚本文件夹删掉,将内容文件夹里的内容删掉,将我们自己的CSS样式文件,全部复制到内容里边 新建家庭控制器 给家庭控制器添加索引视图 指数代码 ...
 
随机推荐
- 剑指Offer面试题39(Java版):二叉树的深度
			
题目:输入一棵二叉树的根节点,求该数的深度. 从根节点到叶结点依次进过的结点(含根,叶结点)形成树的一条路径,最长路径的长度为树的深度. 比如.例如以下图的二叉树的深度为4.由于它从根节点到叶结点的最 ...
 - Linux 高速操作IOport
			
在嵌入式设备中对GPIO的操作是最主要的操作. 一般的做法是写一个单独驱动程序,网上大多数的样例都是这种.事实上linux以下有一个通用的GPIO操作接口.那就是我要介绍的 "/sys/cl ...
 - 漂亮CSS样式用户留言表单
			
基本样式 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF- ...
 - 安装虚拟机后无法SSH远程连接
			
1.安装虚拟机工具 vmware workstation 2.创建一个虚拟机,系统版本是:CentOS-6.8-x86_64-LiveDVD 3.系统安装完成后,选择网络为桥接模式,如图 4.检查主机 ...
 - 《大型网站系统与JAVA中间件实践》【PDF】下载
			
<大型网站系统与JAVA中间件实践>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230062557 内容简介 到底是本什么书,拥有这样 ...
 - IEEE Trans 2009 Stagewise Weak Gradient Pursuits论文学习
			
论文在第二部分先提出了贪婪算法框架,如下截图所示: 接着根据原子选择的方法不同,提出了SWOMP(分段弱正交匹配追踪)算法,以下部分为转载<压缩感知重构算法之分段弱正交匹配追踪(SWOMP)&g ...
 - The Movie db (TMDB)的API申请
			
在共享API TMDB中申请时,一只报错Application summary please elaborate on how you plan to use our API,我是用汉字描述的,开始以 ...
 - [转]winform 自动伸缩控件xpandercontrols 使用说明
			
链接地址:http://blog.sina.com.cn/s/blog_b5b004920101f5h3.html
 - 关于mysql使用命令行时出现Data too long for column的解决方案:
			
方法一: 1,在mysql根目录下找到my.ini文件: 2:将其中sql-mode中的STRICT_TRANS_TABLES这个属性去掉: 3:重启mysql的服务(注意注销电脑不会重启mysql服 ...
 - JavaWeb之数据源连接池(2)---C3P0
			
我们接着<JavaWeb之数据源连接池(1)---DBCP>继续介绍数据源连接池. 首先,在Web项目的WebContent--->WEB-INF--->lib文件夹中添加C3 ...