这几天研究多级树的设计,学到了点东西,分享一下心得:

表设计:

分组表(MasGroup)

  1. class MasGroup {  
  2.  
  3.     String groupName  
  4.     boolean root = false 
  5.  
  6.     static hasMany = [users: MasUser]  
  7.  
  8.     static constraints = {  
  9.     }  

这里面特意增加了root属性,如果当前组是根元素的话,设置为true,主要是方便定位树根。

用户表(MasUser)

  1. class MasUser {  
  2.  
  3.     String userName  
  4.     String mobileNumber  
  5.  
  6.     static belongsTo = MasGroup  
  7.  
  8.     static hasMany = [groups: MasGroup]      
  9.  
  10.     static constraints = {  
  11.     }  

OK,由于每个组可以有多个子组,独立设计了树根组(GroupTree)

  1. class GroupTree {  
  2.  
  3.     MasGroup currentGroup  
  4.     MasGroup childGroup  
  5.  
  6.     static constraints = {  
  7.     }  
  8.  

让Grails自动建表,分组信息会独立存放到group-tree表中。

下面是创建目录树的算法,之前琢磨了好久,用到节点深度、节点兄弟等好几种,刚刚开窍,思路是这样:

定位一个节点,先查找有没有子节点,有则创建ul树枝,然后递归循环孙子节点,没有则直接输出节点。

  1. class GroupTreeService {  
  2.  
  3.     static transactional = true 
  4.  
  5.     List<GroupTree> tree  
  6.  
  7.     public String buildTreeMenu() {  
  8.  
  9.         tree = GroupTree.findAll()  
  10.  
  11.         if (tree == null || tree.isEmpty()) {  
  12.             System.out.println("uninitialized or no group tree defined")  
  13.         }  
  14.  
  15.         // 查找根节点  
  16.         List<MasGroup> rootGroups = MasGroup.findAllByRoot(true)  
  17.  
  18.         StringBuilder sb = new StringBuilder()  
  19.  
  20.         sb.append("<ul id='navigation'>")  
  21.         rootGroups.each {  
  22.             sb.append(listTree(it))  
  23.         }  
  24.         sb.append("</ul>")  
  25.  
  26.         return sb.toString()  
  27.     }  
  28.  
  29.     /**  
  30.      * 列表树结构  
  31.      * @param mg 当前节点组  
  32.      * @return 该节点组的树结构  
  33.      */ 
  34.     private String listTree(MasGroup mg) {  
  35.  
  36.         // 寻找子节点  
  37.         List<MasGroup> children = this.findChildren(mg)  
  38.  
  39.         StringBuilder sb = new StringBuilder()  
  40.  
  41.         // 当前节点的子节点非空,循环子节点  
  42.         if (!children.isEmpty()) {  
  43.  
  44.             // 先输出节点信息的树结构  
  45.             sb.append("<li><a>").append(mg.groupName).append("</a><ul>")  
  46.  
  47.             // 循环节点  
  48.             children.each {  
  49.  
  50.                 // 节点还有子节点时,递归循环  
  51.                 if (!this.findChildren(it).isEmpty())  
  52.                     sb.append(this.listTree(it))  
  53.                 else 
  54.  
  55.                 // 没有子节点,即末尾节点,输出节点信息  
  56.                     sb.append("<li><a>").append(it.groupName).append("</a></li>")  
  57.             }  
  58.  
  59.             // 结束节点树结构  
  60.             sb.append("</ul></li>")  
  61.  
  62.         } else 
  63.  
  64.             // 当前节点没有子节点,输出节点信息  
  65.             sb.append("<li><a>").append(mg.groupName).append("</a></li>")  
  66.  
  67.  
  68.         return sb.toString()  
  69.     }  
  70.  
  71.     /**  
  72.      * 查找当前节点的子节点  
  73.      *  
  74.      * @param currentGroup 当前节点组  
  75.      * @return 子节点,如果没有,则返回空列表的集合(列表可能为空间,但集合一定非空)  
  76.      */ 
  77.     private List<MasGroup> findChildren(MasGroup currentGroup) {  
  78.         List<MasGroup> children = new ArrayList<MasGroup>()  
  79.         tree.each {  
  80.             if (currentGroup.id == it.currentGroup.id)  
  81.                 children.add(it.childGroup)  
  82.         }  
  83.         return children  
  84.     }  
  85.  
  86.  

 

这个跑起来之后,添加一些测试数据,能得到期望的结果,我使用了JQuery-treeview插件生成树目录,截图如下:

Grails树形菜单(1)_grails 

接下来做前台。