浏览 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开发人员或用户提供,请尊重原著):
****************************************************************** 声明:JavaEye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
|
|
| 返回顶楼 | |
|
最后更新时间:2008-06-24
请问楼主,faceye里面的很多代码好象是自动生成的,我对这比较感兴趣,是如何自动生成的?
谢谢 |
|
| 返回顶楼 | |
|
最后更新时间:2008-06-25
使用ant,加模板。
|
|
| 返回顶楼 | |


![ecsun的博客: [海鹏Blog]--{FaceYe开源} 用户头像](http://www.javaeye.com/upload/logo/user/36668/bcfaff38-8200-4288-88e6-f588c3138e36.gif?1196653519)

:
