如题所示,要做一个表格和树形相结合的界面。这个当然就需要用到ColumnTree,而又要在其中包含Checkbox,且当Checkbox被选中时,表格的权限列相应的发生变化。必然可读列的Checkbox被选中时,权限自动由0变为1。
要做带Checkbox的ColumnTree,必须要用到对普通ColumnTreeNodeUI进行扩展的ColumnTreeCheckNodeUI.js,这个js文件如下所示。
/*
* Ext JS Library 2.0
* Copyright(c) 2006-2007, Ext JS, LLC.
* licensing@extjs.com
*
* 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\" οnclick=\"clickCheck(this,0)\" name=\"KD\"/>',QX:'0',uiProvider:'col',cls:'master-task',iconCls:'task-folder',leaf:true,id:'0'}]},"+
"{GNMKMC:'组织机构功能',KD:'<input type=\"checkbox\" οnclick=\"clickCheck(this,1)\" name=\"KD\"/>',QX:'0',uiProvider:'col',cls:'master-task',iconCls:'task-folder',leaf:true,id:'1'},"+
"{GNMKMC:'面板功能',KD:'<input type=\"checkbox\" οnclick=\"clickCheck(this,2)\" name=\"KD\"/>',KX:'<input type=\"checkbox\" name=\"KX\" οnclick=\"clickCheck(this,2)\" />',SH:'<input type=\"checkbox\" name=\"SH\" οnclick=\"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;
});