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

表设计:

分组表(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 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 rootGroups = MasGroup.findAllByRoot(true)  
  17.  
  18.         StringBuilder sb = new StringBuilder()  
  19.  
  20.         sb.append("")  
  21.         rootGroups.each {  
  22.             sb.append(listTree(it))  
  23.         }  
  24.         sb.append("")  
  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 children = this.findChildren(mg)  
  38.  
  39.         StringBuilder sb = new StringBuilder()  
  40.  
  41.         // 当前节点的子节点非空,循环子节点  
  42.         if (!children.isEmpty()) {  
  43.  
  44.             // 先输出节点信息的树结构  
  45.             sb.append("
  46. ").append(mg.groupName).append("
      ")  
    •  
    •             // 循环节点  
    •             children.each {  
    •  
    •                 // 节点还有子节点时,递归循环  
    •                 if (!this.findChildren(it).isEmpty())  
    •                     sb.append(this.listTree(it))  
    •                 else 
    •  
    •                 // 没有子节点,即末尾节点,输出节点信息  
    •                     sb.append("
    • ").append(it.groupName).append("
    • ")  
    •             }  
    •  
    •             // 结束节点树结构  
    •             sb.append("
  47. ")  
  48.  
  49.         } else 
  50.  
  51.             // 当前节点没有子节点,输出节点信息  
  52.             sb.append("
  53. ").append(mg.groupName).append("
  54. ")  
  55.  
  56.  
  57.         return sb.toString()  
  58.     }  
  59.  
  60.     /**  
  61.      * 查找当前节点的子节点  
  62.      *  
  63.      * @param currentGroup 当前节点组  
  64.      * @return 子节点,如果没有,则返回空列表的集合(列表可能为空间,但集合一定非空)  
  65.      */ 
  66.     private List findChildren(MasGroup currentGroup) {  
  67.         List children = new ArrayList()  
  68.         tree.each {  
  69.             if (currentGroup.id == it.currentGroup.id)  
  70.                 children.add(it.childGroup)  
  71.         }  
  72.         return children  
  73.     }  
  74.  
  75.  

 

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

Grails树形菜单(1)_第1张图片 

接下来做前台。