V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
javahih
V2EX  ›  Java

SpringMVC+Ztree 实现权限菜单配置

  •  
  •   javahih · 2017-12-24 13:03:34 +08:00 · 3010 次点击
    这是一个创建于 2531 天前的主题,其中的信息可能已经有所发展或是发生改变。

    计划在开源项目里加入权限配置的功能,打算加入 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(收藏)

    1 条回复    2017-12-24 15:26:10 +08:00
    miao1007
        1
    miao1007  
       2017-12-24 15:26:10 +08:00
    嗯...DOM 操作看的好累
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3224 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 12:53 · PVG 20:53 · LAX 04:53 · JFK 07:53
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.