bootstrap select2插件整合ztree实现树形下拉框
最近在开发管理后台时,需要用到树形下拉框组件。第一反应当然是从网上找,但是结果让人很失望,所以决定自己动手整合一个。
技术需求点:①样式需要和下拉框相同;②下拉选项区域展示树形结构
以上两点需求,独立的每个点都有现成的插件可以使用,即基于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这张图就是一个小黑点,大家注意一下
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]);
最终实现的效果如下图