经过实践验证,这个控件还不是很完善!有bug存在!,虽然bug最后被我修改掉了,但是因为时间太久我记不清是那个版本了!下面网址所发的是一个在用的版本,具体是不是带bug的我忘记了,只能靠大家自己研究一下喽,我做了2天多的劳动成果!奉献一下!希望有空的朋友帮忙写个示例和教程!这个控件毕竟是每个项目都需要的!

//实际项目中已经更改,时间已久,都忘记有多少地方关联的!

 

 

因为很多的项目需要用到这个控件为了方便大家所以就把这个贴出来,有员工的级联选择,只选择员工,等等你所想要的效果都能很方便的实现! 

StaffSelectPanel = function() {

	StaffSelectPanel.superclass.constructor.apply(this, arguments);
	
	this.selectNodes = [];

	if (!Ext.isEmpty(this.initSelectNodes)) {
		if (typeof this.initSelectNodes == "string") {
			this.selectNodes = this.initSelectNodes.split(",");
		} else if (typeof this.initSelectNodes == "array") {
			this.selectNodes = this.initSelectNodes;
		}
	}
	this.loader = new Ext.tree.TreeLoader( {
		dataUrl : this.serviceUrl,
		baseAttrs : {
			uiProvider : Ext.tree.TreeCheckNodeUI
		} // 添加 uiProvider 属性
	});
	
	

};

Ext.extend(StaffSelectPanel, Ext.tree.TreePanel, {
	/* 以上为扩展属性 */
	animate : false,
	rootVisible : true,
	autoScroll : true,
	root : new Ext.tree.AsyncTreeNode( {
		id : '-1',
		text : '所有部门'
	}),
	listeners : {
		"expandnode" : function(node) {
			var selectln = 0;
			var childln = node.childNodes.length;			
			for (var i = 0;i < childln; i++) {
				selectln = this.selectNodes.length;
				if (selectln > 0) {
					var tmpNode = node.childNodes[i];
					for (var j = 0;j < selectln; j++) {
						if (tmpNode.id == this.selectNodes[j]) {
							tmpNode.ui.toggleCheck(true);
							tmpNode.attributes.checked = true;
							this.selectNodes.splice(j,1);
							break;
						};
					}
				}
			}

		},
		"check":function(node,checked){
			this.lastSelectNode=node;
		}
	},
	/*************************************** 下面是对外的接口 *********************/
	
	// 获取选中的节点的文本
	getSelectNodesText : function() {	
		return this.getSelectNodesAttributes("text");
	},
	
	//获取最后一个被选择的节点
	getLastSelectNode:function()
	{
		return  this.lastSelectNode;
	},
	// 获取所有选中的节点的指定的属性的值
	getSelectNodesAttributes : function(attributesName) {
		var tArray = [];
		var selectNodes = this.getChecked();
		var ln =selectNodes.length;
		for(var i=0;i<ln;i++)
		{			
			eval("tArray[tArray.length]=selectNodes[i].attributes."+attributesName);
		}
		return tArray;
	},
	
	// 获取选中的节点的keys
	getSelectNodesKeys : function() {
		var array = [];
		var selectNodes = this.getChecked();
		var ln =selectNodes.length;
		for(var i=0;i<ln;i++)
		{
			array[array.length] = selectNodes[i].id;
		}
		return array;
	},
	
	/*查找子节点 	
	 * Node:开始查找的节点 
	 * key: 节点attributes属性, 
	 * value: key属性所对应的值 
	 * deep: 是否递归
	 */
	findChildrenNode:function(node, key, value, deep) {

		var nodes = node.childNodes;

		for (var i = 0;i < nodes.length; i++) {

			if (nodes[i].attributes[key] == value) {
				return nodes[i];
			} else {
				if (deep && nodes[i].hasChildNodes()) {
					if (node = this.findChildrenNode(nodes[i], key, value, deep)) {
						return node;
					}
				}
			}
		}
		return null;
	},
	
	/*查找节点
	 * key: 节点attributes属性, 
	 * value: key属性所对应的值 
	 * deep: 是否递归
	 */
	findNode:function( key, value, deep)
	{
		return this.findChildrenNode(this.getRootNode(), key, value, deep);
	},
	
	/*
	 * 获取当先选择的所有节点
	 * 返回的是所有被选中的节点的集合 :Ext.util.MixedCollection
	 */
	getSelectNodes:function()
	{
		return this.getChecked();
	},
	

	/*设置选择的节点
	 * nodes:string 或者 array例如:"1,2,3",[1,2,3,4]
	 */ 
	setSelectNodes : function(nodes) {
		var selectNodes=[];
		if (!Ext.isEmpty(nodes)) {
			if (typeof nodes == "string") {
				selectNodes = nodes.split(",");
			} else if (typeof nodes == "array") {
				selectNodes = nodes;
			}			
			var ln= selectNodes.length;
			for(var i=0;i<ln;i++)
			{
				var node = this.findNode('id',selectNodes[i],true);
				if(node)
				{
					node.ui.toggleCheck(true);
					node.attributes.checked = true;
				}
			}
			//this.collapse();//收起来所有的节点,为了展开的时候能够触发展开事件,
		}
	},
	
	/*循环所有选中的节点
	 * fn参数是一个方法:执行的过程中这个方法会收到两个参数(key,node)
	 */
	eachSelectNodes:function(fn)
	{
		if(typeof fn =="undefind")
		{
			return;
		}else if(typeof fn =="function")
		{
			var selectNodes = this.getChecked();
			var ln= selectNodes.length;
			for(var i=0;i<ln;i++)
			{
				fn(selectNodes[i].id,selectNodes[i])			
			}
		}
	}
	
});
/*
 * 
 * var tree = new Ext.tree.TreePanel({ checkModel: 'single', //对树的级联多选
 * onlyLeafCheckable: true,//对树所有结点都可选 animate: false, rootVisible: false,
 * autoScroll:true, loader: new Ext.tree.TreeLoader( { dataUrl :
 * "/govoa/showdepttree/", baseAttrs: { uiProvider: Ext.tree.TreeCheckNodeUI }
 * //添加 uiProvider 属性 }), root: new Ext.tree.AsyncTreeNode({ id:'-1' }) });
 * tree.on("check",function(node,checked){alert(node.text+" = "+checked)});
 * //注册"check"事件
 */

 

这个控件需要用到下面的一个扩展控件,不是我写的是一位高手所写,具体blog我忘记了,先道个歉,没写上你的大名!

/** * @class Ext.tree.TreeCheckNodeUI * @extends Ext.tree.TreeNodeUI * * 对 Ext.tree.TreeNodeUI 进行checkbox功能的扩展,后台返回的结点信息不用非要包含checked属性 * * 扩展的功能点有: * 一、支持只对树的叶子进行选择 * 只有当返回的树结点属性leaf = true 时,结点才有checkbox可选 * 使用时,只需在声明树时,加上属性 onlyLeafCheckable: true 既可,默认是false * * 二、支持对树的单选 * 只允许选择一个结点 * 使用时,只需在声明树时,加上属性 checkModel: "single" 既可 * * 二、支持对树的级联多选 * 当选择结点时,自动选择该结点下的所有子结点,以及该结点的所有父结点(根结点除外),特别是支持异步,当子结点还没显示时,会从后台取得子结点,然后将其选中/取消选中 * 使用时,只需在声明树时,加上属性 checkModel: "cascade" 既可 * * 三、添加"check"事件 * 该事件会在树结点的checkbox发生改变时触发 * 使用时,只需给树注册事件,如: * tree.on("check",function(node,checked){...}); * * 默认情况下,checkModel为'multiple',也就是多选,onlyLeafCheckable为false,所有结点都可选 * * 使用方法:在loader里加上 baseAttrs:{uiProvider:Ext.tree.TreeCheckNodeUI} 既可. * 例如: * var tree = new Ext.tree.TreePanel({ * el:'tree-ct', * width:568, * height:300, * checkModel: 'cascade', //对树的级联多选 * onlyLeafCheckable: false,//对树所有结点都可选 * animate: false, * rootVisible: false, * autoScroll:true, * loader: new Ext.tree.DWRTreeLoader({ * dwrCall:Tmplt.getTmpltTree, * baseAttrs: { uiProvider: Ext.tree.TreeCheckNodeUI } //添加 uiProvider 属性 * }), * root: new Ext.tree.AsyncTreeNode({ id:'0' }) * }); * tree.on("check",function(node,checked){alert(node.text+" = "+checked)}); //注册"check"事件 * tree.render(); * */ Ext.tree.TreeCheckNodeUI = function() { //'multiple':多选; 'single':单选; 'cascade':级联多选 this.checkModel = 'multiple'; //only leaf can checked this.onlyLeafCheckable = false; Ext.tree.TreeCheckNodeUI.superclass.constructor.apply(this, arguments); }; Ext.extend(Ext.tree.TreeCheckNodeUI, Ext.tree.TreeNodeUI, { renderElements : function(n, a, targetNode, bulkRender){ var tree = n.getOwnerTree(); this.checkModel = tree.checkModel || this.checkModel; this.onlyLeafCheckable = tree.onlyLeafCheckable || false; // add some indent caching, this helps performance when rendering a large tree this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : ''; var cb = (!this.onlyLeafCheckable || a.leaf); 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">', '<span class="x-tree-node-indent">',this.indentMarkup,"</span>", '<img src="', this.emptyIcon, '" mce_src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow" />', '<img src="', a.icon || this.emptyIcon, '" mce_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" type="checkbox" ' + (a.checked ? 'checked="checked" />' : '/>')) : '', '<a hidefocus="on" class="x-tree-node-anchor" href="',href,'" mce_href="',href,'" tabIndex="1" ', a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '><span unselectable="on">',n.text,"</span></a></div>", '<ul class="x-tree-node-ct" style="display:none;" mce_style="display:none;"></ul>', "</li>"].join(''); var nel; if(bulkRender !== true && n.nextSibling && (nel = n.nextSibling.ui.getEl())){ this.wrap = Ext.DomHelper.insertHtml("beforeBegin", nel, buf); }else{ this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf); } this.elNode = this.wrap.childNodes[0]; this.ctNode = this.wrap.childNodes[1]; var cs = this.elNode.childNodes; this.indentNode = cs[0]; this.ecNode = cs[1]; this.iconNode = cs[2]; var index = 3; if(cb){ this.checkbox = cs[3]; Ext.fly(this.checkbox).on('click', this.check.createDelegate(this,[null])); index++; } this.anchor = cs[index]; this.textNode = cs[index].firstChild; }, // 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 && this.checkModel == 'cascade'){ var parentNode = n.parentNode; if(parentNode !== null) { this.parentCheck(parentNode,checked); } 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); } } });

/**   
 * @class Ext.tree.TreeCheckNodeUI   
 * @extends Ext.tree.TreeNodeUI   
 *    
 * 对 Ext.tree.TreeNodeUI 进行checkbox功能的扩展,后台返回的结点信息不用非要包含checked属性   
 *    
 * 扩展的功能点有:   
 * 一、支持只对树的叶子进行选择   
 *    只有当返回的树结点属性leaf = true 时,结点才有checkbox可选   
 *    使用时,只需在声明树时,加上属性 onlyLeafCheckable: true 既可,默认是false   
 *    
 * 二、支持对树的单选   
 *    只允许选择一个结点   
 *    使用时,只需在声明树时,加上属性 checkModel: "single" 既可   
 *    
 * 二、支持对树的级联多选    
 *    当选择结点时,自动选择该结点下的所有子结点,以及该结点的所有父结点(根结点除外),特别是支持异步,当子结点还没显示时,会从后台取得子结点,然后将其选中/取消选中   
 *    使用时,只需在声明树时,加上属性 checkModel: "cascade" 既可   
 *    
 * 三、添加"check"事件   
 *    该事件会在树结点的checkbox发生改变时触发   
 *    使用时,只需给树注册事件,如:   
 *    tree.on("check",function(node,checked){...});   
 *    
 * 默认情况下,checkModel为'multiple',也就是多选,onlyLeafCheckable为false,所有结点都可选   
 *    
 * 使用方法:在loader里加上 baseAttrs:{uiProvider:Ext.tree.TreeCheckNodeUI} 既可.   
 * 例如:   
 *   var tree = new Ext.tree.TreePanel({   
 *      el:'tree-ct',   
 *      width:568,   
 *      height:300,   
 *      checkModel: 'cascade',   //对树的级联多选   
 *      onlyLeafCheckable: false,//对树所有结点都可选   
 *      animate: false,   
 *      rootVisible: false,   
 *      autoScroll:true,   
 *      loader: new Ext.tree.DWRTreeLoader({   
 *          dwrCall:Tmplt.getTmpltTree,   
 *          baseAttrs: { uiProvider: Ext.tree.TreeCheckNodeUI } //添加 uiProvider 属性   
 *      }),   
 *      root: new Ext.tree.AsyncTreeNode({ id:'0' })   
 *  });   
 *  tree.on("check",function(node,checked){alert(node.text+" = "+checked)}); //注册"check"事件   
 *  tree.render();   
 *    
 */   
   
Ext.tree.TreeCheckNodeUI = function() {    
    //'multiple':多选; 'single':单选; 'cascade':级联多选    
    this.checkModel = 'multiple';    
        
    //only leaf can checked    
    this.onlyLeafCheckable = false;    
        
    Ext.tree.TreeCheckNodeUI.superclass.constructor.apply(this, arguments);    
};    
   
Ext.extend(Ext.tree.TreeCheckNodeUI, Ext.tree.TreeNodeUI, {    
   
    renderElements : function(n, a, targetNode, bulkRender){    
        var tree = n.getOwnerTree();    
        this.checkModel = tree.checkModel || this.checkModel;    
        this.onlyLeafCheckable = tree.onlyLeafCheckable || false;    
            
        // add some indent caching, this helps performance when rendering a large tree    
        this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';    
   
    var cb = (!this.onlyLeafCheckable || a.leaf);    
        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">',    
            '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",    
            '<img src="', this.emptyIcon, '" mce_src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow" />',    
            '<img src="', a.icon || this.emptyIcon, '" mce_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" type="checkbox" ' + (a.checked ? 'checked="checked" />' : '/>')) : '',    
            '<a hidefocus="on" class="x-tree-node-anchor" href="',href,'" mce_href="',href,'" tabIndex="1" ',    
             a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '><span unselectable="on">',n.text,"</span></a></div>",    
            '<ul class="x-tree-node-ct" style="display:none;" mce_style="display:none;"></ul>',    
            "</li>"].join('');    
   
        var nel;    
        if(bulkRender !== true && n.nextSibling && (nel = n.nextSibling.ui.getEl())){    
            this.wrap = Ext.DomHelper.insertHtml("beforeBegin", nel, buf);    
        }else{    
            this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf);    
        }    
            
        this.elNode = this.wrap.childNodes[0];    
        this.ctNode = this.wrap.childNodes[1];    
        var cs = this.elNode.childNodes;    
        this.indentNode = cs[0];    
        this.ecNode = cs[1];    
        this.iconNode = cs[2];    
        var index = 3;    
        if(cb){    
            this.checkbox = cs[3];    
            Ext.fly(this.checkbox).on('click', this.check.createDelegate(this,[null]));    
            index++;    
        }    
        this.anchor = cs[index];    
        this.textNode = cs[index].firstChild;    
    },    
        
    // 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 && this.checkModel == 'cascade'){    
            var parentNode = n.parentNode;    
            if(parentNode !== null) {    
                this.parentCheck(parentNode,checked);    
            }    
            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);    
        }    
    }    
});  

剩下的就是应用了!

 

	var selectUserWindow=null;
	function showSelectUserPanel()
	{
		if(selectUserWindow==null)
		{	
		var sp= new StaffSelectPanel({
			serviceUrl:'/govoa/showdepttree/',			
			checkModel: 'cascade',   // 'multiple':多选; 'single':单选; 'cascade':级联多选  
    		onlyLeafCheckable: false//只允许选择叶子(员工)  
			//initSelectNodes:'10,12,13'//初始化被选中的员工;
			//setSelectNodes(stirng||array)//设置被选中的员工
		});
	 			  
		selectUserWindow = new Ext.Window(	{
			xtype:"panel",
			title:"选择用户",
			layout:'fit',
			height:400,
			closeAction:'hide',
			width:400,	
			staffSelectPanel:sp,
			buttons:[{
				text:"确定",
				handler:function(){
					
					
					var textArray=[];
					var idArray=[];
					sp.eachSelectNodes(function(key,node){
						if(node.attributes.leaf)
						{
							textArray[textArray.length]=node.attributes.text;
							idArray[idArray.length]=node.attributes.id;
						}
					});
					var txts= textArray.join(",");
					var ids=idArray.join(",");
					setSelectUsers(txts,ids);
					selectUserWindow.hide();
				}
			}/*
			,
			         {
			text:"设值",
			handler:function(){
				sp.setSelectNodes("10,12,13");
			}},{
			text:"自定义循环显示选中的节点",
			handler:function(){
				sp.eachSelectNodes(function(key,node){
					alert(key+"_"+ node.attributes.text);
				});
			}},
			{
			text:"取图标的样式",
			handler:function(){
				// 	获取所有选中的节点的指定的属性的值
				alert(sp.getSelectNodesAttributes('iconCls'));
				
			}}*/],
			items:[sp]
		});
		selectUserWindow.show();
		}
		else
		{
			selectUserWindow.staffSelectPanel.eachSelectNodes(function(key,node){
					node.ui.toggleCheck(false);
					node.attributes.checked = false;
				});
			selectUserWindow.show();
		}
	}
	


	function setSelectUsers(text,value)
	{
		document.getElementById("jieshouren").value = text;
		document.getElementById("jieshourenid").value = value;
		//alert(text);
	}


	function chkInput()
	{
		if(document.getElementById("jieshouren").value == "")
		{
			alert("请输入收件人姓名");
			return false;
			}
		
		return true;
	}
<a href="javascript:showSelectUserPanel()" mce_href="javascript:showSelectUserPanel()">选择</a>