最近在搞的系统需要一个完整的用户、角色、权限对应关系的设置,而权限都是根据系统目录结构的菜单来设定的,所以在角色中选权限时,把权限做成一个树结构直接在树的节点上用多选框来勾选,用户体验会好很多。其实树结构还可能会用在很多地方,比如:公司的组织机构、省市镇行政等层级分明的地方。
    在比较过几个树结构控件之后,我发现dhtmlxtree比较符合我的需求:一方面是它提供我需要的多选框,一方面是它提供丰富的UI可供选择,还有一方面是它支持右击子节点进行操作,虽然这个功能我还没有用到,但是为以后的扩展提供了方便。

一、采用dhtmlxtree将树结构显示到页面上

首先我们来看一下我的效果图:
 
在上图中就是根据我自定义的XML所生成的树结构,右边三个按钮分别是选中当前节点及其子节点、取消选中当前节点及其子节点、获取所选节点id。

下面我们来看一下这个显示的jsp页面:

 
  
  1. <%@ page language="java" contentType="text/html; charset=UTF-8" 
  2.     pageEncoding="UTF-8"%> 
  3. <%@taglib prefix="s" uri="/struts-tags"%> 
  4. <%  
  5.     String base = request.getScheme() + "://" + request.getServerName()  
  6.             + ":" + request.getServerPort() + request.getContextPath()  
  7.             + "/";  
  8. %> 
  9. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 
  10. <html> 
  11. <head> 
  12. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
  13. <title>树结构</title> 
  14. <link href="<%=base%>page/Public/p_w_picpaths/main.css" rel="stylesheet" 
  15.     type="text/css" /> 
  16. <script src="<%=base%>js/jquery-1.7.2.js" type="text/javascript"></script> 
  17. <link rel="STYLESHEET" type="text/css" href="<%=base%>tree/codebase/dhtmlxtree.css"> 
  18. <script  src="<%=base%>tree/codebase/dhtmlxcommon.js"></script> 
  19. <script  src="<%=base%>tree/codebase/dhtmlxtree.js"></script> 
  20. </head> 
  21. <body> 
  22. <table> 
  23.     <tr> 
  24.         <td valign="top"> 
  25.             <div id="treeboxbox_tree2" style="width:250px; height:218px;background-color:#f5f5f5;border :1px solid Silver;; overflow:auto;"></div> 
  26.         </td> 
  27.         <td  style="padding-left:25" valign="top"> 
  28.             Three state checkboxes<br> 
  29.             <br> 
  30.         <a href="javascript:void(0);" onClick="tree2.setCheck(tree2.getSelectedItemId(),true);">Check item</a><br><br> 
  31.         <a href="javascript:void(0);" onClick="tree2.setCheck(tree2.getSelectedItemId(),false);">UnCheck item</a><br><br> 
  32.         <a href="javascript:void(0);" onClick="alert(tree2.getAllChecked());">Get list of checked</a><br><br> 
  33.         </td> 
  34.     </tr> 
  35. </table> 
  36. <script> 
  37. //新建一个dhtmlXTreeObject对象,将树呈现在id为treeboxbox_tree2的div中  
  38. tree2 = new dhtmlXTreeObject("treeboxbox_tree2", "100%", "100%", 0);  
  39. //设置树的皮肤  
  40. tree2.setSkin('dhx_skyblue');  
  41. //设置树的UI图片存放地址  
  42. tree2.setImagePath("<%=base%>tree/codebase/imgs/csh_bluebooks/");  
  43. //子节点前是否需要多选框  
  44. tree2.enableCheckBoxes(1);  
  45. tree2.enableThreeStateCheckboxes(true);  
  46. //这一步是最重要的,设定加载那个XML下的树结构  
  47. tree2.loadXML("<%=base%>tree/sample/common/right.xml");  
  48. </script> 
  49. </body> 
  50. </html> 

下面我们来看一下自定义生成的right.xml:

 
  
  1. <?xml version="1.0" encoding="UTF-8"?> 
  2. <tree id="0"> 
  3. <item call="1" id="21" im0="tombs.gif" im1="tombs.gif" im2="iconSafe.gif" open="1" select="1" text="系统维护"> 
  4. <item id="23" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="流程模块"> 
  5. <item id="30" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="添加流程"/> 
  6. <item id="31" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="编辑流程"/> 
  7. <item id="32" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="删除流程"/> 
  8. </item> 
  9. <item id="24" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="权限管理"> 
  10. <item id="33" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="添加权限"/> 
  11. <item id="34" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="编辑权限"/> 
  12. <item id="35" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="删除权限"/> 
  13. </item> 
  14. <item id="25" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="角色管理"> 
  15. <item id="36" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="添加角色"/> 
  16. <item id="37" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="编辑角色"/> 
  17. <item id="38" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="删除角色"/> 
  18. </item> 
  19. <item id="26" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="用户模块"> 
  20. <item id="39" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="添加用户"/> 
  21. <item id="40" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="编辑用户"/> 
  22. <item id="41" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="删除用户"/> 
  23. </item> 
  24. </item> 
  25. <item call="1" id="22" im0="tombs.gif" im1="tombs.gif" im2="iconSafe.gif" open="1" select="1" text="物料数据库"> 
  26. <item id="27" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="供应商管理"> 
  27. <item id="42" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="添加供应商"/> 
  28. <item id="43" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="编辑供应商"/> 
  29. <item id="44" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="删除供应商"/> 
  30. <item id="45" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="供应商查询"/> 
  31. <item id="46" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="供应商变更"/> 
  32. </item> 
  33. <item id="28" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="物料管理"> 
  34. <item id="47" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="添加物料"/> 
  35. <item id="48" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="编辑物料"/> 
  36. <item id="49" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="删除物料"/> 
  37. <item id="50" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="物料查询"/> 
  38. <item id="51" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="物料申请"/> 
  39. <item id="52" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="物料变更"/> 
  40. </item> 
  41. <item id="29" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="BOM管理"> 
  42. <item id="53" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="添加BOM"/> 
  43. <item id="54" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="编辑BOM"/> 
  44. <item id="55" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="删除BOM"/> 
  45. <item id="56" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="BOM变更"/> 
  46. <item id="57" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="BOM查询"/> 
  47. <item id="58" im0="book_titel.gif" im1="book.gif" im2="book_titel.gif" text="BOM导入"/> 
  48. </item> 
  49. </item> 
  50. <item call="1" id="59" im0="tombs.gif" im1="tombs.gif" im2="iconSafe.gif" open="1" select="1" text="生产管理"/> 
  51. <item call="1" id="60" im0="tombs.gif" im1="tombs.gif" im2="iconSafe.gif" open="1" select="1" text="维修管理"/> 
  52. <item call="1" id="61" im0="tombs.gif" im1="tombs.gif" im2="iconSafe.gif" open="1" select="1" text="字典管理"/> 
  53. </tree> 

到这里我们就知道了页面显示一棵树就这么简单,只要在页面导入dhtmlxtree.css、dhtmlxcommon.js、dhtmlxtree.js三个文件,然后在写一段设置的js
剩下的最关键的就是怎么生成这个用于存储树节点的XML

二、dhtmlxtree树结构XML的生成(权限树生成)

    我的需求是要根据权限的添加来增加树的子节点,而权限其实就是我系统的目录,而目录的层次不定,所以在设计数据库时需要设计为多层结构。
    首先让我们来看一下权限的数据库构造

idishavesonparentidrightidrightnamememo
200 0rootroot
21001000系统维护系统维护 一级模块权限
22002000物料数据库物料数据库模块 一级模块权限
23010001100流程模块流程模块 二级模块权限
24010001200权限管理权限管理 二级权限模块
25010001300角色管理角色管理 二级权限模块

 

以上是从数据库中取出的几条数据,其中第一行为字段名,其中比较重要的两个字段是rightid和parentid,分别代表权限编码和父节点编码,ishaveson是是否允许有子节点。

要生成一棵多层的树的原理:
1、采用遍历循环,首先是第一条数据根节点,rightid为0,写入XML,作为根节点
2、然后我们就查询所有parentid为0的权限数据,然后遍历,设此级数据为二级菜单
3、遍历每一条二级菜单时,先将该条数据写入XML,
4、然后查询出所有parentid为该数据的rightid时,然后遍历三级菜单,
5、无限循环,知道该二级菜单的每一个子孙菜单都写入XML后,遍历下一个二级菜单
6、整个遍历完之后就得到了我们的right.xml

下面是循环遍历写入XML的方法,其中RightXML为自定义读写XML方法。

 
  
  1. /**  
  2.  *   
  3.  * @brief 根据数据库中的right重建xml  
  4.  *  
  5.  */ 
  6. public void createXML(){  
  7.     try {  
  8.         String basepath = ServletActionContext.getServletContext().getRealPath("/");  
  9.         System.out.println("basepath:"+basepath);  
  10.         RightXML myxml = new RightXML(basepath+"/"+"tree/sample/common/right.xml");  
  11.         Element root = myxml.writeRoot();  
  12.           
  13.         Right pright = new Right();  
  14.         pright.setParentid("0");  
  15.         //获取父节点为0的子节点  
  16.         List<Right> rlist = entityService.get(pright);  
  17.         if(rlist != null && rlist.size()>0 ){  
  18.             for(Right right1:rlist){  
  19.                 //写入第一层的权限  
  20.                 Element son = myxml.writeSon(root, right1);  
  21.                 Right pright1 = new Right();  
  22.                 pright1.setParentid(right1.getRightid());  
  23.                 //查询第二层的权限列表  
  24.                 List<Right> rlist2 = entityService.get(pright1);  
  25.                 if(rlist2 != null && rlist2.size()>0 ){  
  26.                 //循环添加直至到树叶  
  27.                 addChild(rlist2,myxml,son);  
  28.                 }  
  29.             }  
  30.         }  
  31.         //保存XML  
  32.         myxml.toSave();  
  33.     } catch (ParserConfigurationException e) {  
  34.         // TODO Auto-generated catch block  
  35.         e.printStackTrace();  
  36.     }  
  37. }  
  38.  
  39. /**  
  40.  *   
  41.  * @brief 循环遍历读入子节点  
  42.  *  
  43.  * @param rlist 某节点的子节点list  
  44.  * @param myxml 自定义读写XMl方法  
  45.  * @param parent 父节点  
  46.  */ 
  47. public void addChild(List<Right> rlist,RightXML myxml,Element parent){  
  48.     //如果rlist不为空则一直循环下去  
  49.     if(rlist != null && rlist.size()>0 ){  
  50.         //遍历循环子节点  
  51.         for(Right right:rlist){  
  52.             Right pright = new Right();  
  53.             pright.setParentid(right.getRightid());  
  54.             //获取该子节点的所有子节点  
  55.             List<Right> rlist1 = entityService.get(pright);  
  56.             //继续遍历循环  
  57.             addChild(rlist1,myxml,myxml.writeSon2(parent, right));  
  58.         }  
  59.     }  

三、角色选择权限树的处理

在角色选择权限并添加成功后,我们需要显示已选的权限,这时,我是这样处理的,我将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