乐趣区

关于springboot:后台管理系统菜单管理模块

1 菜单治理页面设计

1.1 业务设计

菜单治理又称为资源管理,是系统资源对外的表现形式。本模块次要是实现对菜单进行增加、批改、查问、删除等操作。


CREATE TABLE `sys_menus` (`id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) DEFAULT NULL COMMENT '资源名称',
  `url` varchar(200) DEFAULT NULL COMMENT '资源 URL',
  `type` int(11) DEFAULT NULL COMMENT '类型     1:菜单   2:按钮',
  `sort` int(11) DEFAULT NULL COMMENT '排序',
  `note` varchar(100) DEFAULT NULL COMMENT '备注',
  `parentId` int(11) DEFAULT NULL COMMENT '父菜单 ID,一级菜单为 0',
  `permission` varchar(500) DEFAULT NULL COMMENT '受权(如:sys:user:create)',
  `createdTime` datetime DEFAULT NULL COMMENT '创立工夫',
  `modifiedTime` datetime DEFAULT NULL COMMENT '批改工夫',
  `createdUser` varchar(20) DEFAULT NULL COMMENT '创立用户',
  `modifiedUser` varchar(20) DEFAULT NULL COMMENT '批改用户',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='资源管理';

菜单表与角色表是多对多的关系,在表设计时,多对多关系通常由两头表 (关系表) 进行保护

基于角色菜单表的设计,其角色和菜单对应的关系数据要存储到关系表中,其具体存储模式

菜单与角色的关系表脚本设计如下:

CREATE TABLE `sys_role_menus` (`id` int(11) NOT NULL AUTO_INCREMENT,
  `role_id` int(11) DEFAULT NULL COMMENT '角色 ID',
  `menu_id` int(11) DEFAULT NULL COMMENT 'ID',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='角色与菜单对应关系';

1.2 原型设计

当在主页左侧菜单栏,点击菜单治理时,在主页内容出现区,出现菜单列表页面

当在菜单列表页面点击增加按钮时,异步加载菜单编辑页面,并在列表内容出现区,出现菜单编辑页面, 如图 - 4 所示。

在菜单编辑页面抉择上级菜单时,异步加载菜单信息,并以树结构的模式出现上级菜单

1.3 API 设计

菜单治理业务后盾 API 分层架构及调用关系如图

2 菜单治理页面展现

2.1 首页菜单事件处理

▪ 业务形容与设计实现

首先筹备菜单列表页面(/templates/pages/sys/menu_list.html),而后在 starter.html 页面中点击菜单治理时异步加载菜单列表页面。

▪ 要害代码设计与实现

找到我的项目中的 starter.html 页面,页面加载实现当前,注册菜单治理项的点击事件,当点击菜单治理时,执行事件处理函数。要害代码如下:

$(function(){
 …
 doLoadUI("load-menu-id","menu/menu_list")
})

阐明: 对于 doLoadUI 函数, 如果在 starter.html 中曾经定义, 则无需再次定义.

function doLoadUI(id,url){$("#"+id).click(function(){$("#mainContentId").load(url);
 });
}

其中,load 函数为 jquery 中的 ajax 异步申请函数。

2.2 菜单列表页面

▪ 业务形容与设计实现

本页面出现菜单信息时要以树结构模式进行出现。此树结构会借助 jquery 中的 treeGrid 插件进行实现,所以在菜单列表页面须要引入 treeGrid 相干 JS。然而,具体的 treeGrid 怎么用可自行在网上进行查问(已比拟成熟)。

▪ 要害代码设计与实现:

要害 JS 引入(menu_list.html),代码如下:

<script type="text/javascript" src="bower_components/treegrid/jquery.treegrid.extension.js"></script>
<script type="text/javascript" src="bowe
r_components/treegrid/jquery.treegrid.min.js"></script>
<script type="text/javascript" src="bower_components/treegrid/tree.table.js"></script>

3 菜单治理页面代码实现

3.1 菜单列表数据实现

3.1.1 数据加载时序

页面数据展现
菜单列表页面加载实现,启动菜单数据异步加载操作,本次菜单列表页面要出现菜单以及上级菜单信息, 其数据查问时,数据的封装及传递过程

页面菜单数据删除展现
基于用户在列表页面上抉择的的菜单记录 ID,执行删除操作,本次删除业务实现中,首先要基于 id 判断以后菜单是否有子菜单,如果有子菜单则不容许删除,没有则先删除菜单角色关系数据,而后再删除菜单本身信息。

上级菜单页面展现
在菜单编辑页面上,点击上级菜单时,其数据加载时序剖析

菜单数据增加实现
用户在菜单编辑页面输出数据,而后异步提交到服务端,其繁难数据传递根本架构

用户在菜单增加页面中填写好菜单数据,而后点击保留按钮,将用户填写的数据增加到数据库。

菜单数据更新展现
当点击编辑页面更新按钮时

3.1.2 代码实现

第一步:创立 SysMenuDao 层接口

package com.cy.pj.sys.dao;
import com.cy.pj.common.pojo.Node;
import com.cy.pj.sys.pojo.SysMenu;
import com.cy.pj.sys.pojo.SysUserMenu;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
import java.util.Map;
@Mapper
public interface SysMenuDao {List<SysUserMenu> findMenusByIds(List<Integer> menuIds);
    // 基于菜单 ID 获取受权示意
    List<String> findPermissions(List<Integer> menuIds);
    int updateObject(SysMenu entity);
    @Insert("insert into sys_menus" +
            "(name,url,type,sort,note,parentId,permission,createdTime,modifiedTime,createdUser,modifiedUser)" +
            "values" +
            "(#{name},#{url},#{type},#{sort},#{note},#{parentId},#{permission},now(),now(),#{createdUser},#{modifiedUser})")
    int insertObject(SysMenu entity);
    @Select("select id,name,parentId from sys_menus")
    List<Node> findZtreeMenuNodes();
    
    @Select("select count(*) from sys_menus where parentId=#{id}")
    int getChildCount(Integer id);
    @Delete("delete from sys_menus where id=#{id}")
    int deleteObject(Integer id);
    // 查问所有菜单信息
    List<Map<String,Object>> findObjects();}

第二步:创立 SysMenuMapper.xml 映射文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cy.pj.sys.dao.SysMenuDao">
    <resultMap type="com.cy.pj.sys.pojo.SysUserMenu" id="sysUserMenu">
        <!-- 一级菜单映射 -->
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="url" column="url"/>
        <!-- 二级菜单映射 -->
        <collection property="childs" ofType="com.cy.pj.sys.pojo.SysUserMenu">
            <id property="id" column="cid"/>
            <result property="name" column="cname"/>
            <result property="url" column="curl"/>
        </collection>
    </resultMap>
    <select id="findMenusByIds"
        resultMap="sysUserMenu">
        select p.id,p.name,p.url,c.id cid,c.name cname,c.url curl
        from sys_menus p join sys_menus c
        on c.parentId=p.id
        <where>
            <foreach collection="menuIds"
                 open="("
                 close=")"
                 separator="or"
                 item="menuId">
                c.id=#{menuId}
            </foreach>
            and p.parentId is null
        </where>
    </select>
    <select id="findPermissions"
        resultType="string">
        select permission <!-- sys:user:update -->
        from sys_menus
        where id in
        <foreach collection="menuIds"
             open="("
             close=")"
             separator=","
             item="item">
            #{item}
        </foreach>
    </select>
    <!-- 查问所有菜单以及菜单对应的上级菜单,当没有上级菜单时,以后菜单的上级菜单显示为 null-->
    <select id="findObjects" resultType="map">
    /* 办法 1(左外关联查问)*/
        /*select c.*,p.name parentName
        from sys_menus c left join sys_menus p
        on c.parentId=p.id*/
    /* 办法 2(嵌套查问)*/
        select c.*,(select p.name
                    from sys_menus p
 where c.parentId=p.id) parentName
 from sys_menus c
 </select>
    <update id="updateObject"
        parameterType="com.cy.pj.sys.pojo.SysMenu">
         update sys_menus
         set
           name=#{name},
           type=#{type},
           sort=#{sort},
           url=#{url},
           parentId=#{parentId},
           permission=#{permission},
           modifiedUser=#{modifiedUser},
           modifiedTime=now()
        where id=#{id}
    </update>
</mapper>

第三步:创立 SysMenuService 接口

在菜单查问中,业务层对象次要是借助数据层对象实现菜单数据的查问。后续还能够基于 AOP 对数据进行缓存,记录拜访日志等。

package com.cy.pj.sys.servive;
import com.cy.pj.common.pojo.Node;
import com.cy.pj.sys.pojo.SysMenu;
import com.cy.pj.sys.pojo.SysUserMenu;
import java.util.List;
import java.util.Map;
public interface SysMenuService {List<SysUserMenu> findUserMenusByUserId(Integer id);
    int updateObject(SysMenu entity);
    int saveObject(SysMenu entity);
    List<Node> findZtreeMenuNodes();
    int deleteObject(Integer id);
    List<Map<String,Object>> findObjects();}

第四步:创立 SysMenuServiceImpl 实现类

package com.cy.pj.sys.servive.impl;
import com.cy.pj.common.exception.ServiceException;
import com.cy.pj.common.pojo.Node;
import com.cy.pj.sys.dao.SysUserRoleDao;
import com.cy.pj.sys.pojo.SysMenu;
import com.cy.pj.sys.dao.SysMenuDao;
import com.cy.pj.sys.dao.SysRoleMenuDao;
import com.cy.pj.sys.pojo.SysUserMenu;
import com.cy.pj.sys.servive.SysMenuService;
import io.micrometer.core.instrument.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
@Service
public class SysMenuServiceImpl implements SysMenuService {
    @Autowired
    private SysMenuDao sysMenuDao;
    @Autowired
    private SysRoleMenuDao sysRoleMenuDao;
    @Autowired
    private SysUserRoleDao sysUserRoleDao;
    @Override
    public List<SysUserMenu> findUserMenusByUserId(Integer id) {
        //1. 对用户 id 进行判断
        //2. 基于用户 id 查找用户对应的角色 id
        List<Integer> roleIds=
                sysUserRoleDao.findRoleIdsByUserId(id);
        //3. 基于角色 id 获取角色对应的菜单信息, 并进行封装.
        List<Integer> menuIds=
                sysRoleMenuDao.findMenuIdsByRoleIds(roleIds);
        //4. 基于菜单 id 获取用户对应的菜单信息并返回
        return sysMenuDao.findMenusByIds(menuIds);
    }
    @Override
    public int updateObject(SysMenu entity) {
        //1. 非法验证
        if(entity==null)
            throw new ServiceException("保留对象不能为空");
        if(StringUtils.isEmpty(entity.getName()))
            throw new ServiceException("菜单名不能为空");
        //2. 更新数据
        int rows=sysMenuDao.updateObject(entity);
        if(rows==0)
            throw new ServiceException("记录可能曾经不存在");
        //3. 返回数据
        return rows;
    }
    @Override
    public int saveObject(SysMenu entity) {
        //1. 非法验证
        if(entity==null)
            throw new IllegalArgumentException("保留对象不能为空");
        if(StringUtils.isEmpty(entity.getName()))
            throw new IllegalArgumentException("菜单名不能为空");
        //2. 保留数据
        int rows=sysMenuDao.insertObject(entity);
        //3. 返回数据
        return rows;
    }
    @Override
    public List<Node> findZtreeMenuNodes() {return sysMenuDao.findZtreeMenuNodes();
    }
    @Override
    public int deleteObject(Integer id) {
        //1, 参数校验
        if(id==null||id<1)
            throw new IllegalArgumentException("id 值有效");
        //2,断定菜单是否有子菜单,有则不容许删除
        int childCount=sysMenuDao.getChildCount(id);
        if(childCount>0)
            throw new ServiceException("请先删除子菜单");
        //3,删除关系数据
        sysRoleMenuDao.deleteObjectsByMenuId(id);
        int rows=sysMenuDao.deleteObject(id);
        //4,删除本身信息
        if(rows==0)
            throw new ServiceException("记录可能曾经不存在");
        return rows;
    }
    @Override
    public List<Map<String, Object>> findObjects() {return sysMenuDao.findObjects();
    }
}

第五步:创立 SysMenuController 管制层

管制层对象次要负责申请和响应数据的解决,例如,本模块通过业务层对象执行业务逻辑,再通过 VO 对象封装响应后果(次要对业务层数据增加状态信息),最初将响应后果转换为 JSON 格局的字符串响应到客户端。

package com.cy.pj.sys.controller;
import com.cy.pj.common.pojo.JsonResult;
import com.cy.pj.sys.pojo.SysMenu;
import com.cy.pj.sys.servive.SysMenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class SysMenuController {
    @Autowired
    private SysMenuService sysMenuService;
    @RequestMapping("/menu/doUpdateObject")
    public JsonResult doUpdateObject(SysMenu entity){sysMenuService.updateObject(entity);
        return new JsonResult("update ok");
    }
    @RequestMapping("/menu/doSaveObject")
    public JsonResult doSaveObject(SysMenu entity){sysMenuService.saveObject(entity);
        return new JsonResult("save ok");
    }
    @RequestMapping("/menu/doFindZtreeMenuNodes")
    public JsonResult doFindZtreeMenuNodes(){
        return new JsonResult(sysMenuService.findZtreeMenuNodes());
    }
    @RequestMapping("/menu/doDeleteObject")
    public JsonResult doDeleteObject(Integer id){sysMenuService.deleteObject(id);
        return new JsonResult("delete ok");
    }
    @GetMapping("menu/doFindObjects")
    public JsonResult doFindObjects(){return new JsonResult(sysMenuService.findObjects());
    }
}

第六步:创立 SysRoleMenuDao 接口

package com.cy.pj.sys.dao;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface SysRoleMenuDao {
    List<Integer> findMenuIdsByRoleIds(@Param("roleIds")List<Integer> roleIds);
    int insertObjects(@Param("roleId")Integer roleId,
            @Param("menuIds")Integer[] menuIds);
    @Delete("delete from sys_role_menus where role_id=#{roleId}")
    int deleteObjectsByRoleId(Integer roleId);
    // 基于菜单 id 执行关系
 @Delete("delete from sys_role_menus where menu_id=#{menuId}")
    int deleteObjectsByMenuId(Integer menuId);
}

第七步:创立 SysRoleMenuMapper.xml 文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cy.pj.sys.dao.SysRoleMenuDao">
    <select id="findMenuIdsByRoleIds"
 resultType="int">
        select menu_id
        from sys_role_menus
        where role_id in
        <foreach collection="roleIds"
 open="("
 close=")"
 separator=","
 item="item">
            #{item}
        </foreach>
    </select>
    <select id="findMenuIdsByRoleId"
 resultType="int">
            select menu_id
            from sys_role_menus
            where role_id=#{id}
    </select>
    <insert id="insertObjects">
        insert into sys_role_menus
        (role_id,menu_id)
        values
        <foreach collection="menuIds"
 separator=","
 item="menuId">
            (#{roleId},#{menuId})
        </foreach>
    </insert>
</mapper>

第八步:创立 Node 类
定义值对象封装查问到的上级菜单 id,name,parentId 信息。

package com.cy.pj.common.pojo;
import lombok.Data;
import java.io.Serializable;
@Data
public class Node implements Serializable {
    private static final long serialVersionUID = -7022202313802285223L;
    private Integer id;
    private String name;
    private Integer parentId;
}

第九步:创立 SysMenu 类
定义长久化对象,封装客户端申请数据,并将数据传递到数据层进行长久化。

package com.cy.pj.sys.pojo;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
public class SysMenu implements Serializable {
    private static final long serialVersionUID = -1086340530112865421L;
    private Integer id;
    /** 菜单名称 */
 private String name;
    /** 菜单 url: log/doFindPageObjects*/
 private String url;
    /** 菜单类型(两种: 按钮, 一般菜单)*/
 private Integer type=1;
    /** 排序(序号)*/
 private Integer sort;
    /** 备注 */
 private String note;
    /** 上级菜单 id*/
 private Integer parentId;
    /** 菜单对应的权限标识(sys:log:delete)*/
 private String permission;
    /** 创立用户 */
 private String createdUser;
    /** 批改用户 */
 private String modifiedUser;
    private Date createdTime;
    private Date modifiedTime;
}

3.2 客户端页面出现

menu_list.html

<div class="row">
   <div class="col-xs-12">
      <div class="box">
         <div class="box-header">
            <h3 class="box-title">
            
            菜单治理 </h3>
            <div class="box-tools">
               <div class="input-group input-group-sm" style="width: 100px;">
                  <div class="input-group-btn">
                  
                     <button type="button" 
class="btn btn-success btn-delete">
                     删除 </button>
                     <button type="button" class="btn btn-default btn-add"> 增加 </button>
                     <button type="button" class="btn btn-default btn-update"> 批改 </button>
                  </div>
               </div>
            </div>
         </div>
         <!-- /.box-header -->
 <div class="box-body table-responsive no-padding">
          <table id="menuTable" class="table table-hover">
           <thead>
           <tr>
            <th data-field="selectItem" data-checkbox="true"></th>
           </tr>
          </thead>
          
         </table>
        </div>
      </div>
      <!-- /.box -->
 </div>
</div>
<script type="text/javascript" src="bower_components/treegrid/jquery.treegrid.extension.js"></script>
<script type="text/javascript" src="bower_components/treegrid/jquery.treegrid.min.js"></script>
<script type="text/javascript" src="bower_components/treegrid/tree.table.js"></script>
<script type="text/javascript">
/**
 * 初始化表格的列
 */
var columns = [
{
   field : 'selectItem',
   radio : true
},
{
   title : '菜单 ID',
   field : 'id',
   align : 'center',
   valign : 'middle',
   width : '80px'
},
{
   title : '菜单名称',
   field : 'name',
   align : 'center',
   valign : 'middle',
   width : '130px'
},
{
   title : '上级菜单',
   field : 'parentName',
   align : 'center',
   valign : 'middle',
   sortable : true,
   width : '100px'
},
{
   title : '类型',
   field : 'type',
   align : 'center',
   valign : 'middle',
   width : '70px',
   formatter : function(item, index) {if (item.type == 1) {return '<span class="label label-success"> 菜单 </span>';}
      if (item.type == 2) {return '<span class="label label-warning"> 按钮 </span>';}
   }
}, 
{
   title : '排序号',
   field : 'sort',
   align : 'center',
   valign : 'middle',
   sortable : true,
   width : '70px'
}, 
{
   title : '菜单 URL',
   field : 'url',
   align : 'center',
   valign : 'middle',
   width : '160px'
}, 
{
   title : '受权标识',// 要显示的题目名称
 field : 'permission',//json 串中的 key
 align : 'center',// 程度居中
 valign : 'middle',// 垂直居中
 sortable : false // 是否排序
} ];// 格局来自官网 demos -->treeGrid(jquery 扩大的一个网格树插件)
$(function(){doGetObjects();
   $(".input-group-btn")
   .on("click",".btn-delete",doDeleteObject)
    .on("click",".btn-add,.btn-update",doLoadEditUI);
})
function doLoadEditUI(){
   var title;
   // 基于点击对象的 class 属性值, 批改题目
 if($(this).hasClass("btn-add")){title="增加菜单";}else{
      title="批改菜单";
      var item=doGetCheckedItem();
      if(!item){alert("请先抉择");
         return;
      }
      $("#mainContentId")
      .data("rowData",item);
   }
   // 异步加载编辑页面
 var url="menu/menu_edit";
   $("#mainContentId").load(url,function(){$(".box-title").html(title);
   })
}
function doGetCheckedItem(){return $("tbody input[type='radio']:checked")
   .parents("tr").data("rowData");
}
function doGetCheckedId(){
   // 办法 1:
 //var radio=$("tbody input[type='radio']:checked"); //if(radio)return radio.val(); // 办法 2:
 //1. 获取选中的记录
 var selections=$("#menuTable")
   //bootstrapTreeTable 是 treeGrid 插件外部定义的 jquery 扩大函数
 //getSelections 为扩大函数外部要调用的一个办法
 .bootstrapTreeTable("getSelections");
   //2. 对记录进行断定
 if(selections.length==1)
   return selections[0].id;
}
function doDeleteObject(){
  //1. 获取选中记录的 id 值
 var id=doGetCheckedId();
  console.log("id="+id);
  if(!id){alert("请先选中");
     return;
  }
  //2. 给出提醒是否确认删除
 if(!confirm("确认删除吗"))return;
  //3. 发送异步申请执行删除操作
 //3.1 定义申请参数
 var params={"id":id};
  //3.2 定义申请 url
 var url="menu/doDeleteObject";
  //3.3 发送异步申请
 $.post(url,params,function(result){if(result.state==1){alert(result.message);
        $("tbody input[type='radio']:checked")
        .parents("tr").remove();}else{alert(result.message);
     }
  })
}
function doGetObjects(){
   // 移除 mainContentId 地位的数据
 $("#mainContentId").removeData();
   var treeTable=new TreeTable(
            "menuTable",//tableId
 "menu/doFindObjects",//url
 columns);// 表中列的配置
 //treeTable.setExpandColumn(2);
 // 做表格初始化
 treeTable.init();  // 发动 ajax 申请(借助 ajax 函数)
  }

菜单列表页面(/templates/pages/sys/menu_edit.html)

<!-- Horizontal Form -->
 <div class="box box-info">
      <div class="box-header with-border">
        <h3 class="box-title"> 增加菜单 </h3>
      </div>
      <!-- /.box-header -->
 <!-- form start --> <form class="form-horizontal">
        <div class="box-body">
          <div class="form-group">
            <label for="nameId" class="col-sm-2 control-label"> 类型 </label>
            <div class="col-sm-10 typeRadio">
               <label class="radio-inline">
            <input type="radio" name="typeId" value="1" checked> 菜单 </label>
            <label class="radio-inline">
            <input type="radio" name="typeId" value="2"> 按钮 </label>
            </div>
          </div>
          <div class="form-group">
            <label for="nameId" class="col-sm-2 control-label"> 菜单名称 </label>
            <div class="col-sm-10">
              <input type="text" class="form-control" id="nameId" placeholder="名称">
            </div>
          </div>
          <div class="form-group">
            <label for="parentId" class="col-sm-2 control-label"> 上级菜单 </label>
            <div class="col-sm-10">
              <input type="text" class="form-control load-sys-menu" readonly="readonly" id="parentId" placeholder="上级菜单">
            </div>
          </div>
          <div class="form-group">
            <label for="urlId" class="col-sm-2 control-label"> 菜单 URL</label>
            <div class="col-sm-10">
              <input type="text" class="form-control" id="urlId" placeholder="url">
            </div>
          </div>
          <div class="form-group">
         <label for="permissionId" class="col-sm-2 control-label"> 受权标识:</label>
         <div class="col-sm-10">
            <input type="text" id="permissionId"
 placeholder="多个用逗号分隔,如:user:list,user:create"
 class="form-control">
         </div>
        </div>
        <div class="form-group">
            <label for="sortId" class="col-sm-2 control-label"> 排序号:</label>
            <div class="col-sm-10">
               <input type="text" id="sortId" placeholder="排序号"
 class="form-control">
            </div>
       </div>
        </div>
        <!-- /.box-body -->
 <div class="box-footer">
          <button type="button" class="btn btn-default btn-cancel">Cancel</button>
          <button type="button" class="btn btn-info pull-right btn-save">Save</button>
        </div>
        <!-- /.box-footer -->
 </form>
      <!-- zTree 对应的 div -->
 <div class="layui-layer layui-layer-page layui-layer-molv layer-anim" id="menuLayer" type="page" times="2" showtime="0" contype="object"
 style="z-index:59891016; width: 300px; height: 450px; top: 100px; left: 500px; display:none">
      <div class="layui-layer-title" style="cursor: move;"> 抉择菜单 </div>
      <div class="layui-layer-content" style="height: 358px;">
         <div style="padding: 10px;" class="layui-layer-wrap">
            <ul id="menuTree" class="ztree"></ul>    <!-- 动静加载树 -->
 </div>
      </div>
      <span class="layui-layer-setwin"> <a class="layui-layer-ico layui-layer-close layui-layer-close1 btn-cancel" ></a></span>
      <div class="layui-layer-btn layui-layer-btn-">
         <a class="layui-layer-btn0 btn-confirm"> 确定 </a>
         <a class="layui-layer-btn1 btn-cancel"> 勾销 </a>
        </div>
      </div>
  </div>
  <script type="text/javascript" src="bower_components/ztree/jquery.ztree.all.min.js"></script>
  <script type="text/javascript" src="bower_components/layer/layer.js">
  </script>
  <script type="text/javascript">
  
  var zTree; //zTree 是第三方扩大的一个 Jquery 插件
 // 初始化 zTree 时会用到
 var setting = {
   data : {
      simpleData : {
         enable : true,// 示意应用简略数据模式
 idKey : "id",  // 节点数据中保留惟一标识的属性名称
 pIdKey : "parentId",  // 节点数据中保留其父节点惟一标识的属性名称
 rootPId : null // 根节点 id
 }//json 格局 javascript 对象
 }
  }//json 格局的 javascript 对象
 $(function(){$(".form-horizontal")// 事件不能注册到 $("#mainContentId")对象上
 .on("click",".load-sys-menu",doLoadZtreeNodes);
     
     $("#menuLayer")
      .on("click",".btn-confirm",doSetSelectNode)
      .on("click",".btn-cancel",doHideTree);
     
     $(".box-footer")
     .on("click",".btn-cancel",doCancel)
     .on("click",".btn-save",doSaveOrUpdate)
     
     var data=$("#mainContentId").data("rowData");
     if(data)doInitEditFormData(data);
  })
  function doInitEditFormData(data){/*   $("input[type='radio']").each(function(){if($(this).val()==data.type){$(this).prop("checked",true); } }) */ $(".typeRadio input[value='"+data.type+"']").prop("checked",true);
        $("#nameId").val(data.name);
        $("#sortId").val(data.sort);
        $("#urlId").val(data.url);
        $("#permissionId").val(data.permission);
        $("#parentId").val(data.parentName);
        $("#parentId").data("parentId",data.parentId);
  }
  // 获取表单数据
 function doGetEditFormData(){
     var params={type:$("form input[name='typeId']:checked").val(),
      name:$("#nameId").val(),
      url:$("#urlId").val(),
      sort:$("#sortId").val(),
      permission:$("#permissionId").val(),
      parentId:$("#parentId").data("parentId")
     }
     return params;
  }
  function doSaveOrUpdate(){
     //1. 获取表单数据
 var params=doGetEditFormData();
      var rowData=
      $("#mainContentId").data("rowData");
     //2. 异步提交表单数据(post)
 var insertUrl="menu/doSaveObject";
      var updateUrl="menu/doUpdateObject";
      var url=rowData?updateUrl:insertUrl;
      if(rowData)params.id=rowData.id;
      $.post(url,params,function(result){if(result.state==1){alert(result.message);
              doCancel();}else{alert(result.message);
           }
     });
  }
  // 编辑页面 cancel 事件处理
 function doCancel(){
     //1. 定义 url
 var url="menu/menu_list";
     //2. 异步加载列表页面
 $("#mainContentId").load(url);
  }
  
  //zTree 勾销按钮事件处理函数
 function doHideTree(){$("#menuLayer").css("display","none");
  }
  //zTree 确定按钮事件处理函数
 function doSetSelectNode(){
     //1. 获取选中的节点对象
 var nodes=zTree.getSelectedNodes();
     if(nodes.length==1){var selectedNode=nodes[0];
       console.log("selectNode",selectedNode);
       var rowData=$("#mainContentId").data("rowData");
       if(rowData){// 批改时做如下解决
 // 断定以后选中的上级菜单节点是否为以后要批改节点的子节点.
 var flag=isChild(rowData.id,selectedNode);
         if(flag){alert("不能抉择以后节点以及对应子节点");
            return;
         }
       }
     //2. 将对象中内容, 填充到表单
 $("#parentId").data("parentId",selectedNode.id);
      $("#parentId").val(selectedNode.name);
      }
     //3. 暗藏树对象
 doHideTree();}
  // 断定以后选中节点是否是以后节点的子节点
 function isChild(currentNodeId,selectNode){if(selectNode.id==currentNodeId)return true;
    var node=selectNode.getParentNode();
    if(!node)return false;
    return isChild(currentNodeId,node);
  }
  function doLoadZtreeNodes(){
     var url="menu/doFindZtreeMenuNodes";
     // 异步加载数据, 并初始化数据
 $.getJSON(url,function(result){if(result.state==1){
           // 应用 init 函数须要先引入 ztree 对应的 js 文件
 zTree=$.fn.zTree.init($("#menuTree"),
                 setting,
                 result.data);//id,name,parentId
 //doRemoveNodeFromZtree();// 批改时, 可思考此计划
 // 显示 zTree 对应的 div
 $("#menuLayer").css("display","block");
        }else{alert(result.message);
        }
     })
  }
  function doRemoveNodeFromZtree(){
     // 断定是否是批改, 如果是批改, 从 zTree 中移除以后菜单以及对应子菜单
 var rowData=$("#mainContentId").data("rowData");
      if(rowData){//rowData 有值, 示意是批改.
 console.log("zTree",zTree);
        //1. 获取以后的菜单对象
 var node=zTree.getNodeByParam("id",rowData.id,null);
        console.log("node",node);
        //2. 移除以后菜单.
 zTree.removeNode(node);
      }
  }
 </script>
退出移动版