最近在搞的系统需要一个完整的用户、角色、权限对应关系的设置,而权限都是根据系统目录结构的菜单来设定的,所以在角色中选权限时,把权限做成一个树结构直接在树的节点上用多选框来勾选,用户体验会好很多。其实树结构还可能会用在很多地方,比如:公司的组织机构、省市镇行政等层级分明的地方。
在比较过几个树结构控件之后,我发现dhtmlxtree比较符合我的需求:一方面是它提供我需要的多选框,一方面是它提供丰富的UI可供选择,还有一方面是它支持右击子节点进行操作,虽然这个功能我还没有用到,但是为以后的扩展提供了方便。
一、采用dhtmlxtree将树结构显示到页面上
首先我们来看一下我的效果图:
在上图中就是根据我自定义的XML所生成的树结构,右边三个按钮分别是选中当前节点及其子节点、取消选中当前节点及其子节点、获取所选节点id。
下面我们来看一下这个显示的jsp页面:
- <%@ page language="java" contentType="text/html; charset=UTF-8"
- pageEncoding="UTF-8"%>
- <%@taglib prefix="s" uri="/struts-tags"%>
- <%
- String base = request.getScheme() + "://" + request.getServerName()
- + ":" + request.getServerPort() + request.getContextPath()
- + "/";
- %>
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <title>树结构</title>
- <link href="<%=base%>page/Public/p_w_picpaths/main.css" rel="stylesheet"
- type="text/css" />
- <script src="<%=base%>js/jquery-1.7.2.js" type="text/javascript"></script>
- <link rel="STYLESHEET" type="text/css" href="<%=base%>tree/codebase/dhtmlxtree.css">
- <script src="<%=base%>tree/codebase/dhtmlxcommon.js"></script>
- <script src="<%=base%>tree/codebase/dhtmlxtree.js"></script>
- </head>
- <body>
- <table>
- <tr>
- <td valign="top">
- <div id="treeboxbox_tree2" style="width:250px; height:218px;background-color:#f5f5f5;border :1px solid Silver;; overflow:auto;"></div>
- </td>
- <td style="padding-left:25" valign="top">
- Three state checkboxes<br>
- <br>
- <a href="javascript:void(0);" onClick="tree2.setCheck(tree2.getSelectedItemId(),true);">Check item</a><br><br>
- <a href="javascript:void(0);" onClick="tree2.setCheck(tree2.getSelectedItemId(),false);">UnCheck item</a><br><br>
- <a href="javascript:void(0);" onClick="alert(tree2.getAllChecked());">Get list of checked</a><br><br>
- </td>
- </tr>
- </table>
- <script>
- //新建一个dhtmlXTreeObject对象,将树呈现在id为treeboxbox_tree2的div中
- tree2 = new dhtmlXTreeObject("treeboxbox_tree2", "100%", "100%", 0);
- //设置树的皮肤
- tree2.setSkin('dhx_skyblue');
- //设置树的UI图片存放地址
- tree2.setImagePath("<%=base%>tree/codebase/imgs/csh_bluebooks/");
- //子节点前是否需要多选框
- tree2.enableCheckBoxes(1);
- tree2.enableThreeStateCheckboxes(true);
- //这一步是最重要的,设定加载那个XML下的树结构
- tree2.loadXML("<%=base%>tree/sample/common/right.xml");
- </script>
- </body>
- </html>
下面我们来看一下自定义生成的right.xml:
- <?xml version="1.0" encoding="UTF-8"?>
- <tree id="0">
- <item call="1" id="21" im0="tombs.gif" im1="tombs.gif" im2="iconSafe.gif" open="1" select="1" text="系统维护">
- <item id="23" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="流程模块">
- <item id="30" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="添加流程"/>
- <item id="31" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="编辑流程"/>
- <item id="32" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="删除流程"/>
- </item>
- <item id="24" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="权限管理">
- <item id="33" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="添加权限"/>
- <item id="34" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="编辑权限"/>
- <item id="35" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="删除权限"/>
- </item>
- <item id="25" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="角色管理">
- <item id="36" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="添加角色"/>
- <item id="37" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="编辑角色"/>
- <item id="38" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="删除角色"/>
- </item>
- <item id="26" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="用户模块">
- <item id="39" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="添加用户"/>
- <item id="40" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="编辑用户"/>
- <item id="41" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="删除用户"/>
- </item>
- </item>
- <item call="1" id="22" im0="tombs.gif" im1="tombs.gif" im2="iconSafe.gif" open="1" select="1" text="物料数据库">
- <item id="27" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="供应商管理">
- <item id="42" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="添加供应商"/>
- <item id="43" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="编辑供应商"/>
- <item id="44" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="删除供应商"/>
- <item id="45" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="供应商查询"/>
- <item id="46" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="供应商变更"/>
- </item>
- <item id="28" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="物料管理">
- <item id="47" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="添加物料"/>
- <item id="48" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="编辑物料"/>
- <item id="49" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="删除物料"/>
- <item id="50" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="物料查询"/>
- <item id="51" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="物料申请"/>
- <item id="52" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="物料变更"/>
- </item>
- <item id="29" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="BOM管理">
- <item id="53" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="添加BOM"/>
- <item id="54" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="编辑BOM"/>
- <item id="55" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="删除BOM"/>
- <item id="56" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="BOM变更"/>
- <item id="57" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="BOM查询"/>
- <item id="58" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="BOM导入"/>
- </item>
- </item>
- <item call="1" id="59" im0="tombs.gif" im1="tombs.gif" im2="iconSafe.gif" open="1" select="1" text="生产管理"/>
- <item call="1" id="60" im0="tombs.gif" im1="tombs.gif" im2="iconSafe.gif" open="1" select="1" text="维修管理"/>
- <item call="1" id="61" im0="tombs.gif" im1="tombs.gif" im2="iconSafe.gif" open="1" select="1" text="字典管理"/>
- </tree>
到这里我们就知道了页面显示一棵树就这么简单,只要在页面导入dhtmlxtree.css、dhtmlxcommon.js、dhtmlxtree.js三个文件,然后在写一段设置的js
剩下的最关键的就是怎么生成这个用于存储树节点的XML了
二、dhtmlxtree树结构XML的生成(权限树生成)
我的需求是要根据权限的添加来增加树的子节点,而权限其实就是我系统的目录,而目录的层次不定,所以在设计数据库时需要设计为多层结构。
首先让我们来看一下权限的数据库构造
id | ishaveson | parentid | rightid | rightname | memo |
20 | 0 | 0 | root | root | |
21 | 0 | 0 | 1000 | 系统维护 | 系统维护 一级模块权限 |
22 | 0 | 0 | 2000 | 物料数据库 | 物料数据库模块 一级模块权限 |
23 | 0 | 1000 | 1100 | 流程模块 | 流程模块 二级模块权限 |
24 | 0 | 1000 | 1200 | 权限管理 | 权限管理 二级权限模块 |
25 | 0 | 1000 | 1300 | 角色管理 | 角色管理 二级权限模块 |
以上是从数据库中取出的几条数据,其中第一行为字段名,其中比较重要的两个字段是rightid和parentid,分别代表权限编码和父节点编码,ishaveson是是否允许有子节点。
要生成一棵多层的树的原理:
1、采用遍历循环,首先是第一条数据根节点,rightid为0,写入XML,作为根节点
2、然后我们就查询所有parentid为0的权限数据,然后遍历,设此级数据为二级菜单
3、遍历每一条二级菜单时,先将该条数据写入XML,
4、然后查询出所有parentid为该数据的rightid时,然后遍历三级菜单,
5、无限循环,知道该二级菜单的每一个子孙菜单都写入XML后,遍历下一个二级菜单
6、整个遍历完之后就得到了我们的right.xml
下面是循环遍历写入XML的方法,其中RightXML为自定义读写XML方法。
- /**
- *
- * @brief 根据数据库中的right重建xml
- *
- */
- public void createXML(){
- try {
- String basepath = ServletActionContext.getServletContext().getRealPath("/");
- System.out.println("basepath:"+basepath);
- RightXML myxml = new RightXML(basepath+"/"+"tree/sample/common/right.xml");
- Element root = myxml.writeRoot();
- Right pright = new Right();
- pright.setParentid("0");
- //获取父节点为0的子节点
- List<Right> rlist = entityService.get(pright);
- if(rlist != null && rlist.size()>0 ){
- for(Right right1:rlist){
- //写入第一层的权限
- Element son = myxml.writeSon(root, right1);
- Right pright1 = new Right();
- pright1.setParentid(right1.getRightid());
- //查询第二层的权限列表
- List<Right> rlist2 = entityService.get(pright1);
- if(rlist2 != null && rlist2.size()>0 ){
- //循环添加直至到树叶
- addChild(rlist2,myxml,son);
- }
- }
- }
- //保存XML
- myxml.toSave();
- } catch (ParserConfigurationException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- /**
- *
- * @brief 循环遍历读入子节点
- *
- * @param rlist 某节点的子节点list
- * @param myxml 自定义读写XMl方法
- * @param parent 父节点
- */
- public void addChild(List<Right> rlist,RightXML myxml,Element parent){
- //如果rlist不为空则一直循环下去
- if(rlist != null && rlist.size()>0 ){
- //遍历循环子节点
- for(Right right:rlist){
- Right pright = new Right();
- pright.setParentid(right.getRightid());
- //获取该子节点的所有子节点
- List<Right> rlist1 = entityService.get(pright);
- //继续遍历循环
- addChild(rlist1,myxml,myxml.writeSon2(parent, right));
- }
- }
- }
三、角色选择权限树的处理
在角色选择权限并添加成功后,我们需要显示已选的权限,这时,我是这样处理的,我将right.xml拷贝一份,然后遍历那些选中的权限的id,然后将XML中id为此id的节点添加 select="1" 属性,然后重新生成一个名为此角色名的xml,这样需要显示时只需要在js中将要显示树的xml设置为此角色名的xml。
具体代码我就不在这里贴了,附件附上简单树显示的代码。
下面是官方网站的地址及API地址:
dhtmlxtree官方网站地址 http://dhtmlx.com/docs/products/dhtmlxTree/
dhtmlxtree官网API http://docs.dhtmlx.com/doku.php?id=dhtmlxtree:api_toc_alpha