SpringMVC+ZTree实现树形菜单权限配置

计划在开源项目里加入权限配置的功能,打算加入zTree实现树形结构。

Team的Github开源项目链接:https://github.com/u014427391/jeeplatform
欢迎star(收藏)

zTree 是一个依靠 jQuery 实现的多功能 “树插件”。优异的性能、灵活的配置、多种功能的组合是 zTree 最大优点。

zTree下载链接:http://www.treejs.cn/v3/main.php#_zTreeInfo

这里写图片描述

角色信息实体类:

package org.muses.jeeplatform.core.entity.admin;

import javax.persistence.*;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

/**
 * @description 角色信息实体类
 * @author Nicky
 * @date 2017年3月16日
 */
@Table(name="sys_role")
@Entity
public class Role implements Serializable{

    /** 角色Id**/
    private int roleId;

    /** 角色描述**/
    private String roleDesc;

    /** 角色名称**/
    private String roleName;

    /** 角色标志**/
    private String role;

    private Set<Permission> permissions = new HashSet<Permission>();

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    public int getRoleId() {
        return roleId;
    }

    public void setRoleId(int roleId) {
        this.roleId = roleId;
    }

    @Column(length=100)
    public String getRoleDesc() {
        return roleDesc;
    }

    public void setRoleDesc(String roleDesc) {
        this.roleDesc = roleDesc;
    }

    @Column(length=100)
    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    @Column(length=100)
    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }

    //修改cascade策略为级联关系
    @OneToMany(targetEntity=Permission.class,cascade=CascadeType.MERGE,fetch=FetchType.EAGER)
    @JoinTable(name="sys_role_permission", joinColumns=@JoinColumn(name="roleId",referencedColumnName="roleId"), inverseJoinColumns=@JoinColumn(name="permissionId",referencedColumnName="id",unique=true))
    public Set<Permission> getPermissions() {
        return permissions;
    }

    public void setPermissions(Set<Permission> permissions) {
        this.permissions = permissions;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Role) {
            Role role = (Role) obj;
            return this.roleId==(role.getRoleId())
                    && this.roleName.equals(role.getRoleName())
                    && this.roleDesc.equals(role.getRoleDesc())
                    && this.role.equals(role.getRole());
        }
        return super.equals(obj);
    }
}

权限信息实体类:

package org.muses.jeeplatform.core.entity.admin;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;

/**
 * @description 权限操作的Vo类
 * @author Nicky
 * @date 2017年3月6日
 */
@Table(name="sys_permission")
@Entity
public class Permission implements Serializable {

    private int id;
    private String pdesc;
    private String name;
    private static final long serialVersionUID = 1L;

    private Menu menu;

    private Set<Operation> operations = new HashSet<Operation>();

    public Permission() {
        super();
    }

    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Id
    public int getId() {
        return this.id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Column(length=100)
    public String getPdesc() {
        return this.pdesc;
    }

    public void setPdesc(String pdesc) {
        this.pdesc = pdesc;
    }

    @Column(length=100)
    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @OneToOne(targetEntity=Menu.class,cascade=CascadeType.REFRESH,fetch=FetchType.EAGER)
    @JoinColumn(name="menuId",referencedColumnName="menuId")
    public Menu getMenu() {
        return menu;
    }

    public void setMenu(Menu menu) {
        this.menu = menu;
    }

    @ManyToMany(targetEntity=Operation.class,cascade=CascadeType.MERGE,fetch=FetchType.EAGER)
    @JoinTable(name="sys_permission_operation",joinColumns=@JoinColumn(name="permissionId",referencedColumnName="id"),inverseJoinColumns=@JoinColumn(name="operationId",referencedColumnName="id"))
    public Set<Operation> getOperations() {
        return operations;
    }

    public void setOperations(Set<Operation> operations) {
        this.operations = operations;
    }
}

实现菜单信息实体类,用JPA来实现

package org.muses.jeeplatform.core.entity.admin;

import javax.persistence.*;
import java.io.Serializable;
import java.util.List;

/**
 * @description 菜单信息实体
 * @author Nicky
 * @date 2017年3月17日
 */
@Table(name="sys_menu")
@Entity
public class Menu implements Serializable {

    /** 菜单Id**/
    private int menuId;
    
    /** 上级Id**/
    private int parentId;
    
    /** 菜单名称**/
    private String menuName;
    
    /** 菜单图标**/
    private String menuIcon;
    
    /** 菜单URL**/
    private String menuUrl;
    
    /** 菜单类型**/
    private String menuType;
    
    /** 菜单排序**/
    private String menuOrder;

    /**菜单状态**/
    private String menuStatus;

    private List<Menu> subMenu;

    private String target;

    private boolean hasSubMenu = false;

    public Menu() {
        super();
    }   
    
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    public int getMenuId() {
        return this.menuId;
    }

    public void setMenuId(int menuId) {
        this.menuId = menuId;
    }

    @Column(length=100)
    public int getParentId() {
        return parentId;
    }

    public void setParentId(int parentId) {
        this.parentId = parentId;
    }

    @Column(length=100)
    public String getMenuName() {
        return this.menuName;
    }

    public void setMenuName(String menuName) {
        this.menuName = menuName;
    }   
    
    @Column(length=30)
    public String getMenuIcon() {
        return this.menuIcon;
    }

    public void setMenuIcon(String menuIcon) {
        this.menuIcon = menuIcon;
    }   
    
    @Column(length=100)
    public String getMenuUrl() {
        return this.menuUrl;
    }

    public void setMenuUrl(String menuUrl) {
        this.menuUrl = menuUrl;
    }   
    
    @Column(length=100)
    public String getMenuType() {
        return this.menuType;
    }

    public void setMenuType(String menuType) {
        this.menuType = menuType;
    }

    @Column(length=10)
    public String getMenuOrder() {
        return menuOrder;
    }

    public void setMenuOrder(String menuOrder) {
        this.menuOrder = menuOrder;
    }

    @Column(length=10)
    public String getMenuStatus(){
        return menuStatus;
    }

    public void setMenuStatus(String menuStatus){
        this.menuStatus = menuStatus;
    }

    @Transient
    public List<Menu> getSubMenu() {
        return subMenu;
    }

    public void setSubMenu(List<Menu> subMenu) {
        this.subMenu = subMenu;
    }

    public void setTarget(String target){
        this.target = target;
    }

    @Transient
    public String getTarget(){
        return target;
    }

    public void setHasSubMenu(boolean hasSubMenu){
        this.hasSubMenu = hasSubMenu;
    }

    @Transient
    public boolean getHasSubMenu(){
        return hasSubMenu;
    }

}

实现JpaRepository接口

package org.muses.jeeplatform.core.dao.repository.admin;

import org.muses.jeeplatform.core.entity.admin.Role;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * Created by Nicky on 2017/12/2.
 */
public interface RoleRepository extends JpaRepository<Role,Integer> {

}

实现JpaRepository接口

package org.muses.jeeplatform.core.dao.repository.admin;

import org.muses.jeeplatform.core.entity.admin.Menu;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * Created by Nicky on 2017/6/17.
 */
public interface MenuTreeRepository extends JpaRepository<Menu,Integer>{

}

角色Service类:

package org.muses.jeeplatform.service;


import com.google.common.collect.Lists;
import org.muses.jeeplatform.core.dao.repository.admin.RolePageRepository;
import org.muses.jeeplatform.core.entity.admin.Role;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * Created by Nicky on 2017/7/30.
 */
@Service
public class RolePageService {

    @Autowired
    RolePageRepository roleRepository;

    /**
     * 构建PageRequest对象
     * @param num
     * @param size
     * @param asc
     * @param string
     * @return
     */
    private PageRequest buildPageRequest(int num, int size, Sort.Direction asc,
                                         String string) {
        return new PageRequest(num-1, size,null,string);
    }

    /**
     * 获取所有的菜单信息并分页显示
     * @param pageNo
     *          当前页面数
     * @param pageSize
     *          每一页面的页数
     * @return
     */
    public Page<Role> findAll(int pageNo, int pageSize, Sort.Direction dir, String str){
        PageRequest pageRequest = buildPageRequest(pageNo, pageSize, dir, str);
        Page<Role> roles = roleRepository.findAll(pageRequest);
        return roles;
    }

    public List<Role> findAllRole(){
        Iterable<Role> roles = roleRepository.findAll();
        List<Role> myList = Lists.newArrayList(roles);
        return myList;
    }

    /**
     * 根据角色id查找角色信息
     * @param roleId
     * @return
     */
    public Role findByRoleId(String roleId){
        return roleRepository.findOne(Integer.parseInt(roleId));
    }

    /**
     * 保存角色信息
     * @param role
     */
    public void doSave(Role role){
        roleRepository.save(role);
    }




}

菜单Service类:

package org.muses.jeeplatform.service;

import org.muses.jeeplatform.annotation.RedisCache;
import org.muses.jeeplatform.common.RedisCacheNamespace;
import org.muses.jeeplatform.core.dao.repository.admin.MenuTreeRepository;
import org.muses.jeeplatform.core.entity.admin.Menu;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * Created by Nicky on 2017/6/17.
 */
@Service
public class MenuTreeService {

    @Autowired
    MenuTreeRepository menuTreeRepository;

    /**
     * 查询所有的菜单
     * @return
     */
    @Transactional
    //@RedisCache
    public List<Menu> findAll(){
        return menuTreeRepository.findAll();
    }

}

在Controller类里通过角色id获取该角色可以查看的菜单:

/**
     * 跳转到角色授权页面
     * @param roleId
     * @param model
     * @return
     */
    @RequestMapping(value = "/goAuthorise" )
    public String goAuth(@RequestParam String roleId, Model model){

        List<Menu> menuList = menuTreeService.findAll();

        Role role = roleService.findByRoleId(roleId);

        Set<Permission> hasPermissions = null;

        if(role != null){
            hasPermissions = role.getPermissions();
        }

        for (Menu m : menuList) {
            for(Permission p : hasPermissions){
                if(p.getMenu().getMenuId()==m.getMenuId()){
                    m.setHasSubMenu(true);
                }
            }
        }

        model.addAttribute("roleId" , roleId);

        JSONArray jsonArray = JSONArray.fromObject(menuList);
        String json = jsonArray.toString();

        json = json.replaceAll("menuId","id").replaceAll("parentId","pId").
                replaceAll("menuName","name").replaceAll("hasSubMenu","checked");

        model.addAttribute("menus",json);

        return "admin/role/role_auth";
    }

在前端通过zTree实现树形菜单展示,通过勾选然后实现角色授权:

<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <base href="<%=basePath %>">
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Insert title here</title>
    <!-- 引入JQuery库 start -->
    <script type="text/javascript" src="${basePath}static/js/jquery-1.8.3.js"></script>
    <!-- 引入JQuery库 end -->
    <script type="text/javascript" src="<%=basePath%>plugins/zDialog/zDialog.js"></script>
    <script type="text/javascript" src="<%=basePath%>plugins/zDialog/zDrag.js"></script>
    <script type="text/javascript" src="<%=basePath%>plugins/zDialog/zProgress.js"></script>
    <link rel="stylesheet" href="<%=basePath%>plugins/zTree/3.5/zTreeStyle.css" type="text/css">
    <script type="text/javascript" src="<%=basePath%>plugins/zTree/3.5/jquery-1.4.4.min.js"></script>
    <script type="text/javascript" src="<%=basePath%>plugins/zTree/3.5/jquery.ztree.core.js"></script>
    <script type="text/javascript" src="<%=basePath%>plugins/zTree/3.5/jquery.ztree.excheck.js"></script>
    <script type="text/javascript">
        <!--
        var setting = {
            check: {
                enable: true
            },
            data: {
                simpleData: {
                    enable: true
                }
            },
            callback:{
                onClick: {

                }
            }
        };


        /*[
         { id:1, pId:0, name:"随意勾选 1", open:true},
         { id:11, pId:1, name:"随意勾选 1-1", open:true},
         { id:12, pId:1, name:"随意勾选 1-2", open:true}
         ];*/

        var json = ${menus};
        var zNodes = eval(json);

        var code;

        function setCheck() {
            var zTree = $.fn.zTree.getZTreeObj("treeDemo"),
                py = $("#py").attr("checked")? "p":"",
                sy = $("#sy").attr("checked")? "s":"",
                pn = $("#pn").attr("checked")? "p":"",
                sn = $("#sn").attr("checked")? "s":"",
                type = { "Y":py + sy, "N":pn + sn};
            zTree.setting.check.chkboxType = type;
            showCode('setting.check.chkboxType = { "Y" : "' + type.Y + '", "N" : "' + type.N + '" };');
        }
        function showCode(str) {
            if (!code) code = $("#code");
            code.empty();
            code.append("<li>"+str+"</li>");
        }

        $(document).ready(function(){
            $.fn.zTree.init($("#treeDemo"), setting, zNodes);
            setCheck();
            $("#py").bind("change", setCheck);
            $("#sy").bind("change", setCheck);
            $("#pn").bind("change", setCheck);
            $("#sn").bind("change", setCheck);
        });
        //-->

        function dialogClose()
        {
            parentDialog.close();
        }

        function doSave() {
            var zTree = $.fn.zTree.getZTreeObj("treeDemo");
            var nodes = zTree.getCheckedNodes();
            var tmpNode;
            var ids = "";
            for(var i=0; i<nodes.length; i++){
                tmpNode = nodes[i];
                if(i!=nodes.length-1){
                    ids += tmpNode.id+",";
                }else{
                    ids += tmpNode.id;
                }
            }
            var roleId = ${roleId};
            var params = roleId +";"+ids;
            alert(ids);
            $.ajax({
                type: "POST",
                url: 'role/authorise.do',
                data: {params:params,tm:new Date().getTime()},
                dataType:'json',
                cache: false,
                success: function(data){
                    if("success" == data.result){
                        alert('授权成功!请重新登录!');
                        parent.location.reload();
                        doDialogClose();
                    }else{
                        alert("授权失败!");
                    }
                }
            });
        }

    </script>
</head>
<body >
<div class="content_wrap">
    <div class="zTreeDemoBackground left">
        <ul id="treeDemo" class="ztree"></ul>
    </div>
</div>
&nbsp;&nbsp;
<input type="button" onClick="doSave()" value="保存" class="buttonStyle" />
<input onClick="dialogClose();" class="buttonStyle" type="button" value="关闭" />
</body>
</html>
这里写图片描述

Team的Github开源项目链接:https://github.com/u014427391/jeeplatform
欢迎star(收藏)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容