欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

bootstrap select2插件整合ztree实现树形下拉框

程序员文章站 2024-03-05 15:13:19
...

最近在开发管理后台时,需要用到树形下拉框组件。第一反应当然是从网上找,但是结果让人很失望,所以决定自己动手整合一个。

技术需求点:①样式需要和下拉框相同;②下拉选项区域展示树形结构

以上两点需求,独立的每个点都有现成的插件可以使用,即基于bootstrap的select2和基于jquery的ztree;所以,我要做的事,就是扩展select2来支持ztree,这样就基本达到了我的技术需求;

我们要扩展某个插件,那么,必须要先对该插件有一定的了解,至少需要弄清楚该插件的实现流程。对于本次的扩展来说,我需要了解select2是怎样初始化的,什么时候触发下拉列表,下拉列表选项是何时添加到下拉面板中的,点击某个选项之后又是如何保存选项的。

想要得到以上问题的答案,最有效且快速的方式当然是通过select2官网了解其核心实现逻辑,以及它是否对扩展有较好的支持。在

select2高级功能中有对扩展开发的详细介绍

通过了解,我需要扩展4个对象,即可达到目的:BaseAdapter(进行数据处理:当前选择的,选择,取消选择,查询等)、Observable(用于处理下拉面板的展示等)、BaseSelection(选择操作的处理)

详细实现见如下代码:

​
/**
 * 基于select2和ztree的树形下拉框
 * 
 * @param options
 *            object类型的select2配置参数{...,ztree:{setting:{},zNodes:[]},valueField:值属性名称,textField:文本属性名称}
 * @version: v1.0.0
 */
!function($) {
	'use strict';
	function print(obj) {
		console.dir('------------');
		console.dir(obj);
	}
	// 定义数据适配器
	$.fn.select2.amd.define('select2/data/ztree', [ './base', '../utils', 'jquery' ], function(BaseAdapter, Utils, $) {
		function ZtreeSelectAdapter($element, options) {
			this.$element = $element;
			this.options = options;
			ZtreeSelectAdapter.__super__.constructor.call(this);
		}
		Utils.Extend(ZtreeSelectAdapter, BaseAdapter);
		//当前选中的选项:通过选中的option找到对应的ztreenode,并将znode设置为选中状态
		ZtreeSelectAdapter.prototype.current = function(callback) {
			var data = [];
			var self = this;
			this.$element.find(':selected').each(function() {
				var $option = $(this);
				var option = self.item($option);
				option.tId = $option.data('tId');
				data.push(option);
			});

			// 从ztree获取当前选中的数据
			var $ztree = this.container.results.$ztree;
			for (var i = 0; i < data.length; i++) {
				var node = $ztree.getNodeByTId(data[i].tId);
				if (!node) {
					continue;
				}
				data[i] = node;
				$ztree.selectNode(node, false, false);
			}
			callback(data);
		};

		ZtreeSelectAdapter.prototype.select = function(data) {
			var self = this;
			data.selected = true;
			var val = data.id;
			this.$element.val(val);
			this.$element.trigger('change');
		};

		ZtreeSelectAdapter.prototype.unselect = function(data) {};

		ZtreeSelectAdapter.prototype.bind = function(container, $container) {
			var self = this;
			this.container = container;
			container.on('select', function(params) {
				self.select(params.data);
			});
		};

		ZtreeSelectAdapter.prototype.destroy = function() {
			this.$element.find('*').each(function() {
				Utils.RemoveData(this);
			});
		};

		//搜索
		ZtreeSelectAdapter.prototype.query = function(params, callback) {
			var preText = this.$element.data('ztree-search');
			var text = $.trim(params.term);
			this.$element.data('ztree-search', text);
			//首次搜索,并且本次搜索的内容是空字符,不作处理
			if (!preText && !text) {
				callback({
					results : []
				});
				return;
			}
			
			var data = [];
			var $ztree = this.container.results.$ztree;
			//无ztree对象,不作处理
			if (!$ztree) {
				callback({
					results : []
				});
				return;
			}
			
			//ztree模糊搜索
			data = $ztree.fuzzySearch(text);

			callback({
				results : data
			});
		};
		ZtreeSelectAdapter.prototype.addOptions = function($options) {
			Utils.appendMany(this.$element, $options);
		};
		ZtreeSelectAdapter.prototype.option = function(data) {};

		ZtreeSelectAdapter.prototype.item = function($option) {
			var data = {};
			data = Utils.GetData($option[0], 'data');
			if (data != null) {
				return data;
			}
			if ($option.is('option')) {
				data = {
					id : $option.val(),
					text : $option.text(),
					disabled : $option.prop('disabled'),
					selected : $option.prop('selected'),
					title : $option.prop('title')
				};
			} else if ($option.is('optgroup')) {
				data = {
					text : $option.prop('label'),
					children : [],
					title : $option.prop('title')
				};
				var $children = $option.children('option');
				var children = [];
				for (var c = 0; c < $children.length; c++) {
					var $child = $($children[c]);
					var child = this.item($child);
					children.push(child);
				}
				data.children = children;
			}
			data = this._normalizeItem(data);
			data.element = $option[0];
			Utils.StoreData($option[0], 'data', data);
			return data;
		};

		ZtreeSelectAdapter.prototype._normalizeItem = function(item) {
			if (item !== Object(item)) {
				item = {
					id : item,
					text : item
				};
			}
			item = $.extend({}, {
				text : ''
			}, item);

			var defaults = {
				selected : false,
				disabled : false
			};

			if (item.id != null) {
				item.id = item.id.toString();
			}

			if (item.text != null) {
				item.text = item.text.toString();
			}

			if (item._resultId == null && item.id && this.container != null) {
				item._resultId = this.generateResultId(this.container, item);
			}

			return $.extend({}, defaults, item);
		};

		ZtreeSelectAdapter.prototype.matches = function(params, data) {
			var matcher = this.options.get('matcher');

			return matcher(params, data);
		};

		return ZtreeSelectAdapter;
	});
	// 单选适配器
	$.fn.select2.amd.define('select2/selection/ztreeSingle', [ 'jquery', './base', '../utils' ], function($, BaseSelection, Utils) {
		function ZtreeSingleSelection() {
			ZtreeSingleSelection.__super__.constructor.apply(this, arguments);
		}
		Utils.Extend(ZtreeSingleSelection, BaseSelection);
		ZtreeSingleSelection.prototype.render = function() {
			var $selection = ZtreeSingleSelection.__super__.render.call(this);
			$selection.addClass('select2-selection--single');
			$selection.html('<span class="select2-selection__rendered"></span>' + '<span class="select2-selection__arrow" role="presentation">' + '<b role="presentation"></b>'
					+ '</span>');
			return $selection;
		};
		ZtreeSingleSelection.prototype.bind = function(container, $container) {
			var self = this;
			ZtreeSingleSelection.__super__.bind.apply(this, arguments);
			var id = container.id + '-container';
			this.$selection.find('.select2-selection__rendered').attr('id', id).attr('role', 'textbox').attr('aria-readonly', 'true');
			this.$selection.attr('aria-labelledby', id);
			this.$selection.on('mousedown', function(evt) {
				// 只响应鼠标左键事件
				if (evt.which !== 1) {
					return;
				}
				self.trigger('toggle', {
					originalEvent : evt
				});
			});
			this.$selection.on('focus', function(evt) {

			});
			this.$selection.on('blur', function(evt) {
			});
			container.on('focus', function(evt) {
				if (!container.isOpen()) {
					self.$selection.trigger('focus');
				}
			});
		};
		ZtreeSingleSelection.prototype.clear = function() {
			var $rendered = this.$selection.find('.select2-selection__rendered');
			$rendered.empty();
			$rendered.removeAttr('title'); // clear tooltip on
			// empty
		};
		ZtreeSingleSelection.prototype.display = function(data, container) {
			var template = this.options.get('templateSelection');
			var escapeMarkup = this.options.get('escapeMarkup');
			var str = template(data, container);
			return escapeMarkup(str);
		};

		ZtreeSingleSelection.prototype.selectionContainer = function() {
			return $('<span></span>');
		};

		ZtreeSingleSelection.prototype.update = function(data) {
			if (data.length === 0) {
				this.clear();
				return;
			}
			var selection = data[0];
			var $rendered = this.$selection.find('.select2-selection__rendered');
			var formatted = this.display(selection, $rendered);
			$rendered.empty().append(formatted);
			$rendered.attr('title', selection[this.options.get('textField')]);
		};

		return ZtreeSingleSelection;
	});
	// 定义结果展示适配器
	$.fn.select2.amd.define('select2/ztreeresults', [ 'jquery', './utils' ], function($, Utils) {
		function ZtreeResults($element, options, dataAdapter) {
			this.$element = $element;
			this.data = dataAdapter;
			this.options = options;
			ZtreeResults.__super__.constructor.call(this);
		}
		Utils.Extend(ZtreeResults, Utils.Observable);
		//初始化ztree
		ZtreeResults.prototype.render = function() {
			var $results = $('<ul class="select2-results__options ztree" role="tree"></ul>');
			if (this.options.get('multiple')) {
				$results.attr('aria-multiselectable', 'true');
			}
			this.$results = $results;
			// 初始化ztree和下拉选项,以便通过select元素的option找到对应的znode
			if (!this.$ztree) {
				var config = this.options.get('ztree');
				this.$ztree = $.fn.zTree.init(this.$results, config.setting, config.zNodes);
				var data = this.$ztree.transformToArray(this.$ztree.getNodes());
				// 添加1个空元素
				var valueField = this.options.get('valueField');
				var textField = this.options.get('textField');
				// 利用节点数据生成select-->option
				for (var i = 0; i < data.length; i++) {
					var id = data[i][valueField];
					var $option = $('<option value="' + id + '" data-select2-id="' + id + '">' + data[i][textField] + '</option>');
					$option.data('tId', data[i].tId);
					this.$element.append($option);
				}
			}

			return $results;
		};

		ZtreeResults.prototype.clear = function() {};

		ZtreeResults.prototype.displayMessage = function(params) {
			var escapeMarkup = this.options.get('escapeMarkup');
			this.clear();
			this.hideLoading();
			var $message = $('<li role="treeitem" aria-live="assertive"' + ' class="select2-results__option"></li>');
			var message = this.options.get('translations').get(params.message);
			$message.append(escapeMarkup(message(params.args)));
			$message[0].className += ' select2-results__message';
			this.$results.append($message);
		};
		ZtreeResults.prototype.hideMessages = function() {
			this.$results.find('.select2-results__message').remove();
		};
		// 展示下拉选项
		ZtreeResults.prototype.append = function(data) {
			this.hideLoading();
		};

		ZtreeResults.prototype.position = function($results, $dropdown) {
			var $resultsContainer = $dropdown.find('.select2-results');
			$resultsContainer.append($results);
		};

		ZtreeResults.prototype.sort = function(data) {
			return data;
		};
		//不作处理
		ZtreeResults.prototype.highlightFirstItem = function(){};
		//不作处理
		ZtreeResults.prototype.setClasses = function() {};

		ZtreeResults.prototype.showLoading = function(params) {
			this.hideLoading();
			var loadingMore = this.options.get('translations').get('searching');
			var loading = {
				disabled : true,
				loading : true,
				text : loadingMore(params)
			};
			var $loading = this.option(loading);
			$loading.className += ' loading-results';
			this.$results.prepend($loading);
		};

		ZtreeResults.prototype.hideLoading = function() {
			this.$results.find('.loading-results').remove();
		};

		ZtreeResults.prototype.option = function(data) {
			var option = document.createElement('li');
			option.className = 'select2-results__option';
			var attrs = {
				'role' : 'treeitem',
				'aria-selected' : 'false'
			};
			var matches = window.Element.prototype.matches || window.Element.prototype.msMatchesSelector || window.Element.prototype.webkitMatchesSelector;
			if (data.title) {
				option.title = data.title;
			}
			this.template(data, option);
			Utils.StoreData(option, 'data', data);

			return option;
		};

		ZtreeResults.prototype.bind = function(container, $container) {
			var self = this;
			var id = container.id + '-results';
			this.$results.attr('id', id);
			container.on('results:all', function(params) {
				self.clear();
				self.append(params.data);
			});
			container.on('results:append', function(params) {
				self.append(params.data);
				if (container.isOpen()) {
					self.setClasses();
				}
			});
			container.on('query', function(params) {
				self.hideMessages();
				self.showLoading(params);
			});
			container.on('select', function() {
				if (!container.isOpen()) {
					return;
				}
				self.setClasses();
			});
			container.on('open', function() {
				self.$results.attr('aria-expanded', 'true');
				self.$results.attr('aria-hidden', 'false');
				self.setClasses();
				self.ensureHighlightVisible();
			});
			container.on('close', function() {
				self.$results.attr('aria-expanded', 'false');
				self.$results.attr('aria-hidden', 'true');
				self.$results.removeAttr('aria-activedescendant');
			});
			container.on('results:toggle', function() {
				var $highlighted = self.getHighlightedResults();
				if ($highlighted.length === 0) {
					return;
				}
				$highlighted.trigger('mouseup');
			});
			container.on('results:select', function() {
				var $highlighted = self.getHighlightedResults();
				if ($highlighted.length === 0) {
					return;
				}
				var data = Utils.GetData($highlighted[0], 'data');
				if ($highlighted.attr('aria-selected') == 'true') {
					self.trigger('close', {});
				} else {
					self.trigger('select', {
						data : data
					});
				}
			});
			if ($.fn.mousewheel) {
				this.$results.on('mousewheel', function(e) {
					var top = self.$results.scrollTop();
					var bottom = self.$results.get(0).scrollHeight - top + e.deltaY;
					var isAtTop = e.deltaY > 0 && top - e.deltaY <= 0;
					var isAtBottom = e.deltaY < 0 && bottom <= self.$results.height();
					if (isAtTop) {
						self.$results.scrollTop(0);
						e.preventDefault();
						e.stopPropagation();
					} else if (isAtBottom) {
						self.$results.scrollTop(self.$results.get(0).scrollHeight - self.$results.height());
						e.preventDefault();
						e.stopPropagation();
					}
				});
			}

			this.$results.on('mouseup', '.select2-results__option[aria-selected]', function(evt) {
				var $this = $(this);
				var data = Utils.GetData(this, 'data');
				if ($this.attr('aria-selected') === 'true') {
					if (self.options.get('multiple')) {
						self.trigger('unselect', {
							originalEvent : evt,
							data : data
						});
					} else {
						self.trigger('close', {});
					}
					return;
				}
				self.trigger('select', {
					originalEvent : evt,
					data : data
				});
			});
		};

		ZtreeResults.prototype.getHighlightedResults = function() {
			return [];
		};

		ZtreeResults.prototype.destroy = function() {
			this.$results.remove();
		};
		ZtreeResults.prototype.ensureHighlightVisible = function() {};

		ZtreeResults.prototype.template = function(result, container) {
			var template = this.options.get('templateResult');
			var escapeMarkup = this.options.get('escapeMarkup');
			var content = template(result, container);
			if (content == null) {
				container.style.display = 'none';
			} else if (typeof content === 'string') {
				container.innerHTML = escapeMarkup(content);
			} else {
				$(container).append(content);
			}
		};

		return ZtreeResults;
	});

	// 定义组件
	$.fn.select2.amd.define("jquery.select2ztree", [ 'jquery', 'jquery-mousewheel', './select2/core', './select2/defaults', './select2/utils', "./select2/ztreeresults",
			"select2/selection/ztreeSingle", "select2/data/ztree" ], function($, _, Select2, Defaults, Utils, ZtreeResults,
			ZtreeSingleSelection, ZtreeSelectAdapter) {
		if ($.fn.select2ztree) {
			return Select2;
		}
		var thisMethods = [ 'open', 'close', 'destroy' ];
		// 定义jquery对象名称
		$.fn.select2ztree = function(options) {
			options = options || {};
			if (typeof options === 'object') {
				// 初始化属性
				// 值属性名称
				options.valueField = options.valueField || 'id';
				// 文本属性名称
				options.textField = options.textField || 'text';
				options.resultsAdapter = ZtreeResults;
				if ($(this).attr('multiple')) {
					options.selectionAdapter = ZtreeMultiSelection;
				} else {
					options.selectionAdapter = ZtreeSingleSelection;
				}
				options.dataAdapter = ZtreeSelectAdapter;
				options.templateSelection = function(selection) {
					// 模糊搜索控件会将节点名称的实际值保存在oldname属性中
					if (selection.oldname) {
						return selection.oldname;
					}
					return selection[options.textField];
				};
				if (!options.ztree || !options.ztree.setting) {
					throw new Error('缺少ztree配置: {ztree:{setting:{},zNodes:[]}}');
				}
				if(options.ztree.zNodes){
					//禁止节点进行url跳转
					$.each(options.ztree.zNodes,function(idx,ele){
						ele.url = '';
					});
				}
				//覆盖视图配置
				options.ztree.setting.view={
					selectedMulti : false,
					dblClickExpand : false
				}
				var self = this;
				// 重写ztree点击事件:点击之后用于触发select2相关操作
				options.ztree.setting.callback = {
					onClick : function(event, treeId, treeNode, clickFlag) {
						// 触发select2的选择操作
						var data = [];
						data.push(treeNode[options.valueField]);
						self.select2ztree('trigger', 'select', {
							data : treeNode,
							originalEvent : event
						});
					}
				};
				this.each(function() {
					var instanceOptions = $.extend(true, {}, options);
					new Select2($(this), instanceOptions);
				});
				return this;
			} else if (typeof options === 'string') {
				var ret;
				var args = Array.prototype.slice.call(arguments, 1);
				this.each(function() {
					var instance = Utils.GetData(this, 'select2');
					if (instance == null && window.console && console.error) {
						console.error('select2ztree(\'' + options + '\') method was called on an ' + 'element that is not using Select2.');
					}
					ret = instance[options].apply(instance, args);
				});
				if ($.inArray(options, thisMethods) > -1) {
					return this;
				}
				return ret;
			} else {
				throw new Error('错误的配置参数:' + options);
			}
		};

		$.fn.select2ztree.defaults = Defaults;

		return Select2;
	});
	jQuery.fn.select2.amd.require('jquery.select2ztree');
}(jQuery);

​

css依赖

bootstrap.min.css、select2.min.css、bootstrapStyle.css

bootstrapStyle.css代码如下:

.ztree * {
    padding: 0;
    margin: 0;
    font-size: 14px;
    font-family: Verdana,Arial,Helvetica,AppleGothic,sans-serif
}

.ztree {
    margin: 0;
    padding: 5px;
    color: #333
}

.ztree li {
    padding: 0;
    margin: 0;
    list-style: none;
    line-height: 17px;
    text-align: left;
    white-space: nowrap;
    outline: 0
}

.ztree li ul {
    margin: 0;
    padding: 0 0 0 18px
}

.ztree li ul.line {
    background: url(./img/line_conn.png) 0 0 repeat-y
}

.ztree li a {
    padding-right: 3px;
    margin: 0;
    cursor: pointer;
    height: 21px;
    color: #333;
    background-color: transparent;
    text-decoration: none;
    vertical-align: top;
    display: inline-block
}

.ztree li a:hover {
    text-decoration: underline
}

.ztree li a.curSelectedNode {
    padding-top: 0;
    background-color: #e5e5e5;
    color: #000;
    height: 21px;
    opacity: .8
}

.ztree li a.curSelectedNode_Edit {
    padding-top: 0;
    background-color: #e5e5e5;
    color: #000;
    height: 21px;
    border: 1px #666 solid;
    opacity: .8
}

.ztree li a.tmpTargetNode_inner {
    padding-top: 0;
    background-color: #aaa;
    color: #fff;
    height: 21px;
    border: 1px #666 solid;
    opacity: .8;
    filter: alpha(opacity=80)
}

.ztree li a.tmpTargetNode_prev {
}

.ztree li a.tmpTargetNode_next {
}

.ztree li a input.rename {
    height: 14px;
    width: 80px;
    padding: 0;
    margin: 0;
    font-size: 12px;
    border: 1px #585956 solid;
    *border: 0px
}

.ztree li span {
    line-height: 21px;
    margin-right: 2px
}

.ztree li span.button {
    line-height: 0;
    margin: 0;
    padding: 0;
    width: 21px;
    height: 21px;
    display: inline-block;
    vertical-align: middle;
    border: 0;
    cursor: pointer;
    outline: 0;
    background-color: transparent;
    background-repeat: no-repeat;
    background-attachment: scroll;
    background-image: url(./img/bootstrap.png);
    *background-image: url("./img/bootstrap.gif")
}

.ztree li span.button.chk {
    width: 13px;
    height: 13px;
    margin: 0 2px;
    cursor: auto
}

.ztree li span.button.chk.checkbox_false_full {
    background-position: -5px -5px
}

.ztree li span.button.chk.checkbox_false_full_focus {
    background-position: -5px -26px
}

.ztree li span.button.chk.checkbox_false_part {
    background-position: -5px -48px
}

.ztree li span.button.chk.checkbox_false_part_focus {
    background-position: -5px -68px
}

.ztree li span.button.chk.checkbox_false_disable {
    background-position: -5px -89px
}

.ztree li span.button.chk.checkbox_true_full {
    background-position: -26px -5px
}

.ztree li span.button.chk.checkbox_true_full_focus {
    background-position: -26px -26px
}

.ztree li span.button.chk.checkbox_true_part {
    background-position: -26px -48px
}

.ztree li span.button.chk.checkbox_true_part_focus {
    background-position: -26px -68px
}

.ztree li span.button.chk.checkbox_true_disable {
    background-position: -26px -89px
}

.ztree li span.button.chk.radio_false_full {
    background-position: -47px -5px
}

.ztree li span.button.chk.radio_false_full_focus {
    background-position: -47px -26px
}

.ztree li span.button.chk.radio_false_part {
    background-position: -47px -47px
}

.ztree li span.button.chk.radio_false_part_focus {
    background-position: -47px -68px
}

.ztree li span.button.chk.radio_false_disable {
    background-position: -47px -89px
}

.ztree li span.button.chk.radio_true_full {
    background-position: -68px -5px
}

.ztree li span.button.chk.radio_true_full_focus {
    background-position: -68px -26px
}

.ztree li span.button.chk.radio_true_part {
    background-position: -68px -47px
}

.ztree li span.button.chk.radio_true_part_focus {
    background-position: -68px -68px
}

.ztree li span.button.chk.radio_true_disable {
    background-position: -68px -89px
}

.ztree li span.button.switch {
    width: 21px;
    height: 21px
}

.ztree li span.button.root_open {
    background-position: -105px -63px
}

.ztree li span.button.root_close {
    background-position: -126px -63px
}

.ztree li span.button.roots_open {
    background-position: -105px 0
}

.ztree li span.button.roots_close {
    background-position: -126px 0
}

.ztree li span.button.center_open {
    background-position: -105px -21px
}

.ztree li span.button.center_close {
    background-position: -126px -21px
}

.ztree li span.button.bottom_open {
    background-position: -105px -42px
}

.ztree li span.button.bottom_close {
    background-position: -126px -42px
}

.ztree li span.button.noline_open {
    background-position: -105px -84px
}

.ztree li span.button.noline_close {
    background-position: -126px -84px
}

.ztree li span.button.root_docu {
    background: 0 0
}

.ztree li span.button.roots_docu {
    background-position: -84px 0
}

.ztree li span.button.center_docu {
    background-position: -84px -21px
}

.ztree li span.button.bottom_docu {
    background-position: -84px -42px
}

.ztree li span.button.noline_docu {
    background: 0 0
}

.ztree li span.button.ico_open {
    margin-right: 2px;
    background-position: -147px -21px;
    vertical-align: top;
    *vertical-align: middle
}

.ztree li span.button.ico_close {
    margin-right: 2px;
    margin-right: 2px;
    background-position: -147px 0;
    vertical-align: top;
    *vertical-align: middle
}

.ztree li span.button.ico_docu {
    margin-right: 2px;
    background-position: -147px -43px;
    vertical-align: top;
    *vertical-align: middle
}

.ztree li span.button.edit {
    margin-left: 2px;
    margin-right: -1px;
    background-position: -189px -21px;
    vertical-align: top;
    *vertical-align: middle
}

.ztree li span.button.edit:hover {
    background-position: -168px -21px
}

.ztree li span.button.remove {
    margin-left: 2px;
    margin-right: -1px;
    background-position: -189px -42px;
    vertical-align: top;
    *vertical-align: middle
}

.ztree li span.button.remove:hover {
    background-position: -168px -42px
}

.ztree li span.button.add {
    margin-left: 2px;
    margin-right: -1px;
    background-position: -189px 0;
    vertical-align: top;
    *vertical-align: middle
}

.ztree li span.button.add:hover {
    background-position: -168px 0
}

.ztree li span.button.ico_loading {
    margin-right: 2px;
    background: url(./img/loading.gif) no-repeat scroll 0 0 transparent;
    vertical-align: top;
    *vertical-align: middle
}

ul.tmpTargetzTree {
    background-color: #ffe6b0;
    opacity: .8;
    filter: alpha(opacity=80)
}

span.tmpzTreeMove_arrow {
    width: 16px;
    height: 21px;
    display: inline-block;
    padding: 0;
    margin: 2px 0 0 1px;
    border: 0;
    position: absolute;
    background-color: transparent;
    background-repeat: no-repeat;
    background-attachment: scroll;
    background-position: -168px -84px;
    background-image: url(./img/bootstrap.png);
    *background-image: url("./img/bootstrap.gif")
}

ul.ztree.zTreeDragUL {
    margin: 0;
    padding: 0;
    position: absolute;
    width: auto;
    height: auto;
    overflow: hidden;
    background-color: #cfcfcf;
    border: 1px #00b83f dotted;
    opacity: .8;
    filter: alpha(opacity=80)
}

.ztreeMask {
    z-index: 10000;
    background-color: #cfcfcf;
    opacity: 0;
    filter: alpha(opacity=0);
    position: absolute
}

 bootstrapStyle.css需要的图片,请下载后放到与bootstrapStyle.css相同目录下的img:bootstrap.png和line_conn.png;line_conn.png这张图就是一个小黑点,大家注意一下

bootstrap select2插件整合ztree实现树形下拉框bootstrap select2插件整合ztree实现树形下拉框

js依赖:

jquery.min.js(3.3.1)、bootstrap.min.js(3.4.1)、jquery.ztree.all.min.js(3.5.40)、jquery.ztree.exhide.js、select2.js(4.0.8)、以及ztree模糊搜索扩展

/**
 * ztree模糊搜索
 * 
 * @version: v1.0.0
 */
!function($) {
	/**
	 * @param searchText
	 *            模糊搜索文本内容
	 * @param isHighLight
	 *            是否高亮,默认高亮,传入false禁用
	 * @param isExpand
	 *            是否展开,默认合拢,传入true展开
	 * 
	 * @returns
	 */
	var _init = $.fn.zTree.init;
	$.fn.zTree.init = function(obj, zSetting, zNodes) {
		var zTreeTools = _init(obj, zSetting, zNodes);
		// 为ztree对象添加模糊搜索方法
		zTreeTools.fuzzySearch = function(searchText, isHighLight, isExpand) {
			var zTreeObj = this;
			var nameKey = zTreeObj.setting.data.key.name;
			isHighLight = isHighLight === false ? false : true;
			isExpand = isExpand ? true : false;
			zTreeObj.setting.view.nameIsHTML = isHighLight;
			var metaChar = '[\\[\\]\\\\\^\\$\\.\\|\\?\\*\\+\\(\\)]';
			var rexMeta = new RegExp(metaChar, 'gi');
			function ztreeFilter(zTreeObj, _keywords, callBackFunc) {
				if (!_keywords) {
					_keywords = '';
				}
				function filterFunc(node) {
					if (node && node.oldname && node.oldname.length > 0) {
						node[nameKey] = node.oldname;
					}
					zTreeObj.updateNode(node);
					if (_keywords.length == 0) {
						zTreeObj.showNode(node);
						zTreeObj.expandNode(node, isExpand);
						return true;
					}
					if (node[nameKey] && node[nameKey].toLowerCase().indexOf(_keywords.toLowerCase()) != -1) {
						if (isHighLight) {
							var newKeywords = _keywords.replace(rexMeta, function(matchStr) {
								return '\\' + matchStr;
							});
							node.oldname = node[nameKey];
							var rexGlobal = new RegExp(newKeywords, 'gi');
							node[nameKey] = node.oldname.replace(rexGlobal, function(originalText) {
								var highLightText = '<span style="color: whitesmoke;background-color: darkred;">' + originalText + '</span>';
								return highLightText;
							});
							zTreeObj.updateNode(node);
						}
						zTreeObj.showNode(node);
						return true;
					}

					zTreeObj.hideNode(node);
					return false;
				}

				var nodesShow = zTreeObj.getNodesByFilter(filterFunc);
				processShowNodes(nodesShow, _keywords);
				return nodesShow;
			}

			/**
			 * 展示搜索结果前处理节点
			 */
			function processShowNodes(nodesShow, _keywords) {
				if (nodesShow && nodesShow.length > 0) {
					if (_keywords.length > 0) {
						$.each(nodesShow, function(n, obj) {
							var pathOfOne = obj.getPath();
							if (pathOfOne && pathOfOne.length > 0) {
								for (var i = 0; i < pathOfOne.length - 1; i++) {
									zTreeObj.showNode(pathOfOne[i]);
									zTreeObj.expandNode(pathOfOne[i], true);
								}
							}
						});
					} else {
						var rootNodes = zTreeObj.getNodesByParam('level', '0');
						$.each(rootNodes, function(n, obj) {
							zTreeObj.expandNode(obj, true);
						});
					}
				}
			}
			return ztreeFilter(zTreeObj, searchText);
		}
		return zTreeTools;
	};
}(jQuery);

 

组件初始化代码如下:

var setting = {
				view : {
					dblClickExpand : false
				},
				data : {
					simpleData : {
						enable : true
					}
				}
			};

			var zNodes = [ {
				id : 1,
				pId : 0,
				name : "北京"
			}, {
				id : 2,
				pId : 0,
				name : "天津"
			}, {
				id : 3,
				pId : 0,
				name : "上海"
			}, {
				id : 6,
				pId : 0,
				name : "重庆"
			}, {
				id : 4,
				pId : 0,
				name : "河北省",
				open : true
			}, {
				id : 41,
				pId : 4,
				name : "石家庄"
			}, {
				id : 42,
				pId : 4,
				name : "保定"
			}, {
				id : 43,
				pId : 4,
				name : "邯郸"
			}, {
				id : 44,
				pId : 4,
				name : "承德"
			}, {
				id : 5,
				pId : 0,
				name : "广东省",
				open : true
			}, {
				id : 51,
				pId : 5,
				name : "广州"
			}, {
				id : 52,
				pId : 5,
				name : "深圳"
			}, {
				id : 53,
				pId : 5,
				name : "东莞"
			}, {
				id : 54,
				pId : 5,
				name : "佛山"
			}, {
				id : 6,
				pId : 0,
				name : "福建省",
				open : true
			}, {
				id : 61,
				pId : 6,
				name : "福州"
			}, {
				id : 62,
				pId : 6,
				name : "厦门"
			}, {
				id : 63,
				pId : 6,
				name : "泉州"
			}, {
				id : 64,
				pId : 6,
				name : "三明"
			} ];
			//初始化树形下拉框
			var select2ztree = $('#select2').select2ztree({
				textField : 'name',
				titleField : 'name',
				ztree : {
					setting : setting,
					zNodes : zNodes
				}
			});
            //注意,select2的赋值方式与jquery常规的val赋值不一样,必须使用下面的方式
            $('#select2').select2ztree('val',[4]);

最终实现的效果如下图

bootstrap select2插件整合ztree实现树形下拉框