论坛首页 AJAX版 EXT

Faceye基础版(开源)中对带复选框的树结构的处理

浏览 662 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
最后更新时间:2008-07-08
在Faceye基础版(开源)中,使用了大量的树结构,比如台台管理树,用户RSS订阅及分类树,用户博客分类对,网址导航分类树,开源项目分类树,博客管理树等大量的树形结构,甚至在用户授权,用户分组,资料分类等也都使用了树结构,树结构的处理,在Faceye中相比比较成熟,这些,是基于前期项目中对Struts-menu,Dtree等处理
关于Struts-Menu,Dtree的处理,在我早期的博客中都有描述.

带有复选框的树,在实际的项目中,特别是授权结构中,有大量的使用.我们来看一下Faceye中如何处理这种带复选框的树形结构.

首先,Faceye使用了Ext的树解决方案,所以,所有的数据结构,均转化为Ext中树解决方案的数据结构.在这里,我们可以很方便的把这一结构转化为其它树支持的数据结构,比如Strus-menu,dtreee等.
下面是对树完整的脚本处理代码.
[code]
/**
*RoleController
*  角色访问权限管理
*  管理角色对模块的访问权限
*  www.faceye.com网络支持系统
* 作者:宋海鹏 ecsun@sohu.com/myecsun@hotmail.com
*/
Ext.BLANK_IMAGE_URL = 'scripts/ext/resources/images/vista/s.gif';
com.faceye.compoents.core.security.RoleVisiteModelPermission={
init:function(roleId){
    Ext.QuickTips.init();
    if(Ext.getCmp('tree')){
    Ext.get('tree-viewer').remove(true);
//      Ext.getCmp('tree').destroy(this);
        Ext.getBody().createChild({tag:'div',id:'tree-viewer'});
    }
var tree;
    var root;
//    if(!tree){
    var Tree = Ext.tree;
    tree = new Ext.tree.TreePanel(
    {
        el:'tree-viewer',
        id:'tree',
        autoScroll:true,
        animate:true,
        border:false,
        enableDD:true,
        containerScroll: true,
        rootVisible:false,
        loader: new Ext.tree.TreeLoader({
dataUrl:'/faceye/treeAction.do?method=treeWithCheckBoxForPermission&roleId='+roleId
// uiProviders:{
//                'col': Ext.ux.CheckColumnNodeUI
//            }
})
// columns:[{
//// header:'node',
// dataIndex:'name'
// }]
//
    }
    ); 
    //Defint node click event,when node is clicked,send data to inner 'div' and show data in
    // set the root node
    root = new Tree.AsyncTreeNode({
        text: 'Common Platform',
        draggable:false,
        id:'source',
        checked:true
//        uiProvider: Ext.tree.CheckboxNodeUI
    });
    tree.setRootNode(root);
    /**
    *Open node URL in a target contanier
    */
    //全部展开
    tree.expandAll();
    tree.on('checkchange',function(node){
    if(!node.isLeaf()){
    node.toggle();
    }
        fireCheckChange(node);
//      node.cascade(function(n){
//     n.getUI().toggleCheck();
// });
     
    });
   
    /**
     * 当fire checkchange时执行
     */
     function fireCheckChange(node){
     if(node.getUI().isChecked()){
        checkedChildrenNodes(node);
        checkedParentNodes(node);
        }else{
          //取得当前节点的所有子节点,包括当前节点
          var allChildrenNodes=getAllChildrenNodes(node);
          //如果当前节点的所有子节点中,不存在checked=true的节点,那么将当前节点置为checked=false.
//         
          //如果当前节点有子节点,同时,当前节点checked=false,那么将其所有子节点置为checked=false
          for(var i=0;i<allChildrenNodes.length;i++){
          if(allChildrenNodes[i].getUI().isChecked()){
          allChildrenNodes[i].getUI().toggleCheck();
         
          }
          }
          unCheckedParentNode(node);
        }
     }
   
    /**
     * 当点击父节点时
     * 将其所有子节点选中
     */
    function checkedChildrenNodes(node){
    //取得本节点的所有子节点,子节点中包括其自己
        var allChildrenNodes=getAllChildrenNodes(node);
        if(allChildrenNodes.length>1){
        for(var i=0;i<allChildrenNodes.length;i++){
        if(!allChildrenNodes[i].getUI().isChecked()){
        allChildrenNodes[i].getUI().toggleCheck();
        }
        }
        }
    }
   
    /**
     * 当当前子节点的父节点的所有子节点中
     * 不存在checked=true的子节点时,父节点不被选中
     */
     function unCheckedParentNode(currentChildNode){
     if(currentChildNode.parentNode){
     var parentNode=currentChildNode.parentNode;
     //取得本父节点下所有被选中的子节点
     //包括本父节点本身
     var allCheckedChildrenNodes=getCheckedNodes(parentNode);
     if(allCheckedChildrenNodes.length === 1){
     parentNode.getUI().toggleCheck();
     parentNode.attributes.checked=false;
     }
     if(parentNode.parentNode){
     unCheckedParentNode(parentNode);
     }
     }
     }
    /**
     * 当点击子节点时
     * 将父节点选中
     */
    function checkedParentNodes(node){
    //取得本节点的所有父节点,父节点中包括其自己
        var allParentNodes=getAllParentNodes(node);
        if(allParentNodes.length>1){
        for(var i=0;i<allParentNodes.length;i++){
        if(!allParentNodes[i].getUI().isChecked()){
        allParentNodes[i].getUI().toggleCheck();
        }
        }
        }
    }
    /**
     * 取得所有子节点中checked 为true的节点ID
     * 包括本节点
     */
    function getCheckedNodesId(node){
    var checked = [];
    if( node.getUI().isChecked() || node.attributes.checked ) {
//    alert('dfdf'+node.childNodes.length);
checked.push(node.id);
if( !node.isLeaf() ) {
for(var i = 0; i < node.childNodes.length; i++ ) {
checked = checked.concat( getCheckedNodesId(node.childNodes[i]) );
}
}
}
return checked;
    };
    /**
     * 取得所有子节点中checked为true的节点(TreeNode)
     * 包括本节点
     */
    function getCheckedNodes(node){
    var checked = [];
    if( node.getUI().isChecked() ) {
       checked.push(node);
if( !node.isLeaf() ) {
for(var i = 0; i < node.childNodes.length; i++ ) {
checked = checked.concat( getCheckedNodes(node.childNodes[i]) );
}
}
}
return checked;
    };
   
    /**
     * 取得一个节点的所有子节点
     * 包括本节点
     */
     function getAllChildrenNodes(node){
     var children = [];
     children.push(node);
     if(!node.isLeaf()){
     for(var i=0;i<node.childNodes.length;i++){
     children = children.concat(getAllChildrenNodes(node.childNodes[i]));
     }
     }
     return children;
     };
    /**
     * 取得一个节点的所有父节点
     *
     */
     function getAllParentNodes(node){
     var parentNodes=[];
        parentNodes.push(node);
     if(node.parentNode){
     parentNodes = parentNodes.concat(getAllParentNodes(node.parentNode));
     }
     return parentNodes;
     };
     /**
      * 取得所有checked=true的节点ID
      */
     
      function getAllChecked(){
      return getCheckedNodesId(root);
      }
     tree.on('click', function (node){
      if(node.isLeaf()){
//         Ext.get('content-iframe').dom.src = node.attributes.link+'&node='+node.id;
         //define grid;      
         return true;
     }else{
      /**
       *open node by single click,not double click.
       */
        node.toggle();
     }
    });
   
    tree.on('dblclick',function(node){
    if(node.isLeaf()){
    return true;
    }else{
    node.toggle();
    fireCheckChange(node);
//    this.fireEvent('checkchange', this.node, true);
    }
    });
    // render the tree
//    }
   
    tree.render();
    root.expand();
var win=new Ext.Window({
            layout:'fit',
            //模式窗口
            modal:true,
            width:450,
                        height:300,
                        closeAction:'hide',
                        plain: true,
//                        margins:'5 5 5 5',
                       
                        title:'为角色授权',
                        buttonAlign:'center',
                        buttons: [{
                           text:'确定',
                           scope:com.faceye.compoents.core.security.SelectRoles,
                           type:'submit',
                           disabled:false,
                           handler:function(){
                            var checked=getAllChecked().join(',');
                             Ext.Ajax.request({
                 url:'/faceye/roleAction.do?method=permission',
                 failure:function(){
            Ext.Msg.alert('角色授权','角色授权失败!');
            win.hide();
                },
               success:function(){
            Ext.Msg.alert('角色授权','角色授权成功!');
            win.hide();
                },
               params:{
            treeIds:checked,
            roleId:roleId
                }
                   });
                           
                           }
                              },{
                           text: '放弃',
                           handler: function(){
//                             formItemSelector.getForm().reset();
                              if(win.getComponent('tree')){
//                              Ext.get('tree-viewer').remove();
//                              win.destroy();
//                                win.remove(tree);
                                                
                              }
                              win.hide();
//                              win.disable();
//                              tree.disable();
                               }
                          }
                          ]
            });
           
win.add(tree);
win.show();
}
};

[/code]

在带有复选框的树形结构处理中,主要需要解决这么几个问题:
1.我们的树,是无限级别的
2.当选中父节点时,其所有子节点,包括直接子节点和间接子节点,一并选中
3.当一个节点的所有子节点被取消选中状态时,其父节点要处于未被选中的状态.

因为树向来是子子孙孙无穷尽的一个东西,所以在这里,需要用到比较多的递归处理,不管是在脚本方面,还是在后台代码里,都需要用到一些递归处理,比如我们所提供的方法中:
[code]
/**
     * 取得一个节点的所有父节点
     *
     */
     function getAllParentNodes(node){
     var parentNodes=[];
        parentNodes.push(node);
     if(node.parentNode){
     parentNodes = parentNodes.concat(getAllParentNodes(node.parentNode));
     }
     return parentNodes;
     };
[/code]
在这里,提供了这样一样功能,既:取得当前点击(选中)节点的所有父节点,这些父节点中,包括直接父节点和间接父节点,也就是说,在这里,会取得当前选中节点的父亲节点,父亲节点的父亲节点....一直到根节点为止.

类似的方法有:
[code]
/**
     * 取得一个节点的所有子节点
     * 包括本节点
     */
     function getAllChildrenNodes(node){
     var children = [];
     children.push(node);
     if(!node.isLeaf()){
     for(var i=0;i<node.childNodes.length;i++){
     children = children.concat(getAllChildrenNodes(node.childNodes[i]));
     }
     }
     return children;
     };
[/code]

这是取得一个节点所有子节点的方法.处理跟上面类似.

通过以上这些处理,我们就可以很方便的处理带复选框的树形结构.
而在这里,我们最终真正关心的,是那些被我们选中的节点,于是,我们需要取得所有被选中的节点,在这里我们需要使用另外一个方法:
[code]
/**
     * 取得所有子节点中checked为true的节点(TreeNode)
     * 包括本节点
     */
    function getCheckedNodes(node){
    var checked = [];
    if( node.getUI().isChecked() ) {
       checked.push(node);
if( !node.isLeaf() ) {
for(var i = 0; i < node.childNodes.length; i++ ) {
checked = checked.concat( getCheckedNodes(node.childNodes[i]) );
}
}
}
return checked;
    };
[/code]
在这里,我们同样使用到了递归.

同时,我们可以看到,在生成树的时候,我们使用了这样一个loader:
[code]
loader: new Ext.tree.TreeLoader({
dataUrl:'/faceye/treeAction.do?method=treeWithCheckBoxForPermission&roleId='+roleId
// uiProviders:{
//                'col': Ext.ux.CheckColumnNodeUI
//            }
})
[/code]
这是我们从后台取数据的方法,同时,要在前台加入check column
要加入check column,我们需要看以下代码:
在方法:treeWithCheckBoxForPermission中,我们做了以下操作:
[code]
/**
* 构造带有checkbox的tree,为角色进行节点授权做准备
* @param mapping
* @param form
* @param request
* @param response
* @return
*/
public ActionForward treeWithCheckBoxForPermission(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
String json = "";
// JSONArray ja=new JSONArray();
    String roleId=this.getHttp().getParameter(request, "roleId");
if (super.getHttp().getParameter(request, "node").equals("source")) {
json = this.getTreeService().treeJSONWithCheckBox(roleId);
} else {
String currentNode = super.getHttp().getParameter(request, "node");
json = this.getTreeService().treeJSONWithCheckBox(
this.getTreeService().getTransedTrees(),
currentNode,roleId);
}
super.jsonPrint(response, json);
// return mapping.findForward("system.admin.face");
return null;
}
[/code]
在这里,我们主要对原始的树形结构进行了处理,在每个节点上面,打上了checkbox标识:
[code]
public String treeJSONWithCheckBox(Serializable roleId) {
// TODO Auto-generated method stub
try {
List trees = this.getTransedTrees();
String result = this.treeJSONWithCheckBox(trees, roleId);
return result;
} catch (Exception e) {
log.info(">>>>faceye error in method:treeJSON() is" + e.toString());
return null;
}

}
[/code]

这里最主要的一个方法是:
[code]
public String treeJSONWithCheckBox(List source, Serializable roleId) {
// TODO Auto-generated method stub
if (source == null || source.isEmpty()) {
return null;
}
List roots = this.getRoots(source);
Iterator it = roots.iterator();
StringBuffer json = new StringBuffer();
json.append("[");
while (it.hasNext()) {
Map item = (Map) it.next();
json.append(this.transTree2JSONWithCheckBox(source, item, roleId));
json.append(",");

}
json.deleteCharAt(json.lastIndexOf(","));
json.append("]");
return json.toString();
}
[/code]

在这里,我们调用了最终方法:
[code]
transTree2JSONWithCheckBox
[/code]
对树结构进行最终处理
[code]
private String transTree2JSONWithCheckBox(List source, Map tree,
Serializable roleId) {
StringBuffer json = new StringBuffer();
json.append("{");
json.append("\"text\":");
json.append("\"");
json.append(tree.get(StringPool.TREE_NAME).toString());
json.append("\"");
json.append(",");
json.append("\"id\":");
json.append("\"");
json.append(tree.get(StringPool.TREE_ID).toString());
json.append("\"");
json.append(",");
json.append("\"leaf\":");
if (this
.isHaveChildren(source, tree.get(StringPool.TREE_ID).toString())) {
json.append("false");
} else {
json.append("true");
}
json.append(",");
json.append("\"cls\":");
json.append("\"file\"");
if (tree.containsKey(StringPool.TREE_ACTION)
&& null != tree.get(StringPool.TREE_ACTION)) {
json.append(",");
json.append("\"link\":");
json.append("\"");
json.append(tree.get(StringPool.TREE_ACTION).toString());
json.append("\"");
} else {
json.append(",");
json.append("\"link\":");
json.append("\"");
json.append("#");
json.append("\"");
}

json.append(",");
json.append("\"checked\":");
// json.append("true");
json.append(this.isNodeChecked(tree.get(StringPool.TREE_ID).toString(),
roleId));

json.append("}");
return json.toString();
}
[/code]
可以看到,我们在这里,加入了:[code]checked:true[/code]这样的属性.
至此,可以看到完整的带有复选框的树形结构的处理流程,完整代码,请从 http://code.google.com/p/faceye下载.

******************************************************************
关于FaceYe开源portal的其它更多内容包括:

FaceYe用户及开发人员提供文档(以下内容为FaceYe开发人员或用户提供,请尊重原著):

******************************************************************
  • 0dad9c2b-3358-37a0-a116-69106e79dfb3-thumb
  • 描述: 本部分相关页面预览图.对角色授于模块权限.
  • 大小: 162.5 KB
   
最后更新时间:2008-06-24
请问楼主,faceye里面的很多代码好象是自动生成的,我对这比较感兴趣,是如何自动生成的?
谢谢
   
0 请登录后投票
最后更新时间:2008-06-25
使用ant,加模板。
   
0 请登录后投票
论坛首页 AJAX版 EXT

跳转论坛:
JavaEye推荐