如题所示,要做一个表格和树形相结合的界面。这个当然就需要用到ColumnTree,而又要在其中包含Checkbox,且当Checkbox被选中时,表格的权限列相应的发生变化。必然可读列的Checkbox被选中时,权限自动由0变为1。
要做带Checkbox的ColumnTree,必须要用到对普通ColumnTreeNodeUI进行扩展的ColumnTreeCheckNodeUI.js,这个js文件如下所示。
/* * Ext JS Library 2.0 * Copyright(c) 2006-2007, Ext JS, LLC. * [email protected] * * http://extjs.com/license */ Ext.tree.ColumnTree = Ext.extend(Ext.tree.TreePanel, { //lines:false, borderWidth: Ext.isBorderBox ? 0 : 2, // the combined left/right border for each cell cls:'x-column-tree', scrollOffset : 18, onRender : function(){ Ext.tree.ColumnTree.superclass.onRender.apply(this, arguments); this.headers = this.body.createChild( {cls:'x-tree-headers '},this.body.dom); var cols = this.columns, c; var totalWidth = 0; for(var i = 0, len = cols.length; i < len; i++){ c = cols[i]; totalWidth += c.width; this.headers.createChild({ cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''), cn: { cls:'x-tree-hd-text', html: c.header }, style:'width:'+(c.width-this.borderWidth)+'px;' }); } this.headers.createChild({ cls:'x-tree-hd ', cn: { html: '' }, style:'width:'+this.scrollOffset+'px;' }); totalWidth += this.scrollOffset; this.headers.createChild({cls:'x-clear'}); // prevent floats from wrapping when clipped this.headers.setWidth(totalWidth); totalWidth -= this.scrollOffset; this.innerCt.setWidth(totalWidth); } }); Ext.tree.ColumnTreeNodeUI = Ext.extend(Ext.tree.TreeNodeUI, { focus: Ext.emptyFn, // prevent odd scrolling behavior renderElements : function(n, a, targetNode, bulkRender){ this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : ''; var t = n.getOwnerTree(); var cols = t.columns; var bw = t.borderWidth; var c = cols[0]; var cb = typeof a.checked == 'boolean'; if(typeof this.checkModel != 'undefined'){ cb = (!this.onlyLeafCheckable || n.isLeaf()); } var href = a.href ? a.href : Ext.isGecko ? "" : "#"; var buf = ['<li class="x-tree-node"><div ext:tree-node-id="',n.id,'" class="x-tree-node-el x-tree-node-leaf x-unselectable ', a.cls,'" unselectable="on">', '<div class="x-tree-col" style="width:',c.width-bw,'px;">', '<span class="x-tree-node-indent">',this.indentMarkup,"</span>", '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow">', '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on">', cb ? ('<input class="x-tree-node-cb" style="display:none;" type="checkbox" ' + (a.checked ? 'checked="checked" />' : '/>')) : '', '<a hidefocus="on" class="x-tree-node-anchor" href="',href,'" tabIndex="1" ', a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '>', '<span unselectable="on">', n.text || (a[c.dataIndex]?(c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]):'')," </span></a>", "</div>"]; for(var i = 1, len = cols.length; i < len; i++){ c = cols[i]; buf.push('<div class="x-tree-col ',(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">', '<div class="x-tree-col-text">',(a[c.dataIndex]?(c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]):'')," </div>", "</div>"); } buf.push('<div class="x-clear"></div>', '</div>', '<ul class="x-tree-node-ct" style="display:none;"></ul>', "</li>"); if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){ this.wrap = Ext.DomHelper.insertHtml("beforeBegin",n.nextSibling.ui.getEl(), buf.join("")); }else{ this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf.join("")); } this.elNode = this.wrap.childNodes[0]; this.ctNode = this.wrap.childNodes[1]; var cs = this.elNode.firstChild.childNodes; this.indentNode = cs[0]; this.ecNode = cs[1]; this.iconNode = cs[2]; var index = 3; if(cb){ this.checkbox = cs[3]; index++; } this.anchor = cs[index]; this.textNode = cs[index].firstChild; } }); Ext.ux.ColumnTreeCheckNodeUI = function() { this.checkModel = 'multiple'; //only leaf can checked this.onlyLeafCheckable = false; Ext.ux.ColumnTreeCheckNodeUI.superclass.constructor.apply(this, arguments); }; Ext.extend(Ext.ux.ColumnTreeCheckNodeUI, Ext.tree.ColumnTreeNodeUI, { renderElements : function(n, a, targetNode, bulkRender){ var t = n.getOwnerTree(); this.checkModel = t.checkModel || this.checkModel; this.onlyLeafCheckable = t.onlyLeafCheckable || false; Ext.ux.ColumnTreeCheckNodeUI.superclass.renderElements.apply(this, arguments); var cb = (!this.onlyLeafCheckable || n.isLeaf()); if(cb){ Ext.fly(this.checkbox).on('click', this.check.createDelegate(this,[null])); } }, // private check : function(checked){ var n = this.node; var tree = n.getOwnerTree(); this.checkModel = tree.checkModel || this.checkModel; if( checked === null ) { checked = this.checkbox.checked; } else { this.checkbox.checked = checked; } n.attributes.checked = checked; tree.fireEvent('check', n, checked); if(!this.onlyLeafCheckable){ if(this.checkModel == 'cascade' || this.checkModel == 'parentCascade'){ var parentNode = n.parentNode; if(parentNode !== null) { this.parentCheck(parentNode,checked); } } if(this.checkModel == 'cascade' || this.checkModel == 'childCascade'){ if( !n.expanded && !n.childrenRendered ) { n.expand(false,false,this.childCheck); }else { this.childCheck(n); } } } else if(this.checkModel == 'single'){ var checkedNodes = tree.getChecked(); for(var i=0;i<checkedNodes.length;i++){ var node = checkedNodes[i]; if(node.id != n.id){ node.getUI().checkbox.checked = false; node.attributes.checked = false; tree.fireEvent('check', node, false); } } } }, // private childCheck : function(node){ var a = node.attributes; if(!a.leaf) { var cs = node.childNodes; var csui; for(var i = 0; i < cs.length; i++) { csui = cs[i].getUI(); if(csui.checkbox.checked ^ a.checked) csui.check(a.checked); } } }, // private parentCheck : function(node ,checked){ var checkbox = node.getUI().checkbox; if(typeof checkbox == 'undefined')return ; if(!(checked ^ checkbox.checked))return; if(!checked && this.childHasChecked(node))return; checkbox.checked = checked; node.attributes.checked = checked; node.getOwnerTree().fireEvent('check', node, checked); var parentNode = node.parentNode; if( parentNode !== null){ this.parentCheck(parentNode,checked); } }, // private childHasChecked : function(node){ var childNodes = node.childNodes; if(childNodes || childNodes.length>0){ for(var i=0;i<childNodes.length;i++){ if(childNodes[i].getUI().checkbox.checked) return true; } } return false; }, toggleCheck : function(value){ var cb = this.checkbox; if(cb){ var checked = (value === undefined ? !cb.checked : value); this.check(checked); } } });
将这个代码粘贴到项目新建的ColumnTreeCheckNodeUI.js文件中,并在页面的头部进行引入。
<!-- 引入扩展树 --> <link rel="stylesheet" type="text/css" href="extExtend/ColumnNodeUI.css" /> <link rel="stylesheet" type="text/css" href="extExtend//column-tree.css" /> <script type="text/javascript" src="extExtend/ColumnNodeUI.js"></script> <script type="text/javascript" src="extExtend/ColumnTreeCheckNodeUI.js"></script>
extExtend下的文件下载地址http://download.csdn.net/download/rongyongfeikai2/4481420
然后页面中,这棵ColumnTree的写法为:
tree = new Ext.ux.tree.ColumnTree({ width: Ext.get('myTreeDiv').getWidth(), height: 600, rootVisible:true, autoScroll:true, title:'权限分配', renderTo: 'myTreeDiv', viewConfig:{ forceFit:true }, columns:[{ header:'模块', width:Ext.get('myTreeDiv').getWidth()*0.6, dataIndex:'GNMKMC' },{ header:'可读', width:Ext.get('myTreeDiv').getWidth()*0.1, dataIndex:'KD' },{ header:'可写', width:Ext.get('myTreeDiv').getWidth()*0.1, dataIndex:'KX' },{ header:'审核', width:Ext.get('myTreeDiv').getWidth()*0.1, dataIndex:'SH' },{ header:'权限', width:Ext.get('myTreeDiv').getWidth()*0.1, dataIndex:'QX' }], loader: new Ext.tree.TreeLoader({ dataUrl:'servlet/GnmkColumnTreeServlet', uiProviders:{ 'col': Ext.ux.ColumnTreeCheckNodeUI } }), root: new Ext.tree.AsyncTreeNode({ text:'****人事管理系统权限管理' }) });
那么这棵树的数据从哪里来呢?我们可以看到,TreeLoader的dataUrl调用了一个Servlet。Servlet拼接了一个Json串传给tree。
request.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8"); response.setContentType("text/html"); String testJson = ""; System.out.println("进入了!!!!"); testJson = "[{GNMKMC:'菜单功能',uiProvider:'col',cls:'master-task',iconCls:'task-folder',children:[{GNMKMC:'查询',KD:'<input type=\"checkbox\" onclick=\"clickCheck(this,0)\" name=\"KD\"/>',QX:'0',uiProvider:'col',cls:'master-task',iconCls:'task-folder',leaf:true,id:'0'}]},"+ "{GNMKMC:'组织机构功能',KD:'<input type=\"checkbox\" onclick=\"clickCheck(this,1)\" name=\"KD\"/>',QX:'0',uiProvider:'col',cls:'master-task',iconCls:'task-folder',leaf:true,id:'1'},"+ "{GNMKMC:'面板功能',KD:'<input type=\"checkbox\" onclick=\"clickCheck(this,2)\" name=\"KD\"/>',KX:'<input type=\"checkbox\" name=\"KX\" onclick=\"clickCheck(this,2)\" />',SH:'<input type=\"checkbox\" name=\"SH\" onclick=\"clickCheck(this,2)\" />',QX:'0',uiProvider:'col',cls:'master-task',iconCls:'task-folder',leaf:true,id:'2'}]"; System.out.println(testJson); response.getWriter().write(testJson);
我们可以看到,放的Checkbox在clickCheck中将节点的id传了出来。这样,我们就可以在clickCheck中对节点的列进行修改。
//点击了选择框 function clickCheck(checkbox,param) { var qxValue = 0; var myQx = 0; var oldNode = tree.getNodeById(param); if(checkbox.name == 'KD') { qxValue = 1; }else if(checkbox.name == 'KX') { qxValue = 2; }else if(checkbox.name == 'SH') { qxValue = 4; } //如果选择框被选中 if(checkbox.checked) { myQx = parseInt(oldNode.attributes.QX) + qxValue; }else { myQx = parseInt(oldNode.attributes.QX) - qxValue; } oldNode.attributes.QX = myQx; oldNode.ui.elNode.childNodes[4].firstChild.innerHTML = myQx; judge(myQx); }
如此,就大功告成了!
由于是异步加载数据,故有时不希望树可以被关闭。禁止树被关闭的代码为:
tree.on('beforecollapsenode',function(node,deep,anim){ return false; });