UNPKG

nuijs

Version:

nui框架

1,379 lines (1,324 loc) 59 kB
/** * @author Aniu[2017-12-23 16:50] * @update Aniu[2018-01-31 11:54] * @version 1.0.3 * @description 搜索 */ Nui.define(function(require){ this.imports('../assets/components/search/index'); var component = require('../core/component'); var util = require('../core/util'); var request = require('../core/request'); var Search = this.extend(component, { _static:{ _init:function(){ var self = this, timer = null; Nui.win.resize(function(){ clearTimeout(timer); timer = setTimeout(function(){ Nui.each(self.__instances, function(obj){ if(obj._showed){ obj.resize() } }) }, 100) }) }, /** * @func 将tag数据转为html模版 * @param data <Object> * { * text:'', * fields:{ * field1:value1, * field2:value2, * ... * } * } * @param data <Array> * [{ * text:'', * fields:{ * field1:value1, * field2:value2, * ... * } * } * @param option <Object> * { * title:true, * close:'×', * type:'gray' * } */ data2html:function(data, option){ if(data && typeof data === 'object'){ if(!option){ option = {} } var _exports = Search.exports || Search; return _exports._tpl2html.call(_exports, 'tags', { data:[].concat(data), title:option.title === undefined ? true : option.title, close:option.close === undefined ? '×' : option.close, type:option.type ? [].concat(option.type) : false }) } return '' }, active:function(){ return this._active } }, _options:{ /** * @func 请求url * @type <String> */ url:'', /** * @func 查询参数 * @type <String> * @type <Function> * @param self <Object> 组件实例对象 * @param value <String> 文本框输入值 * @return <Object> 查询参数对象集合 */ query:'keyword', /** * @func 设置层级 * @type <Number> * @desc 若style中已经设置,则不使用该值 */ zIndex:19920604, /** * @func 定义列表模版 * @type <String> * @type <Function> * @param self <Object> 组件实例对象 * @return <String> 返回列表模版 * @desc 模版中可以使用<%$data%>获取当前行数据,<%$index%>获取当前行索引 * @desc 配合选项参数selected方法在模版中类名中加入<%selected($data)%>可以设置当前行是否选中 * @desc 模版中有效列表类名必须包含con-search-item以及data-index="<%$index%>"属性 */ item:'', /** * @func 定义空数据模版 * @type <String> * @type <Function> * @param self <Object> 组件实例对象 * @return <String> 返回空数据时模版 * @desc 模版中可以使用<%value%>获取当前输入值 */ empty:'', /** * @func 定义输入时有结果返回提示模版 * @type <String> * @type <Function> * @param self <Object> 组件实例对象 * @return <String> 返回提示模版 */ prompt:'', /** * @func 定义顶部模版 * @type <String> * @type <Function> * @param self <Object> 组件实例对象 * @return <String> 返回顶部模版 */ head:'', /** * @func 定义底部模版 * @type <String> * @type <Function> * @param self <Object> 组件实例对象 * @return <String> 返回底部模版 */ foot:'', /** * @func 设置列表展示内容字段名 * @type <String> * @desc 在展示列表时如果未使用item参数,将会展示该值内容 */ field:'', /** * @func 是否在文本获取焦点时展示下拉 * @type <Boolean> * @desc 设置true后组件内部会绑定focus事件,因此不建议手动绑定focus事件调用组件的同时将该参数设置为true,那样会导致事件重复绑定 */ focus:false, /** * @func 是否允许文本框内容为空时展示下拉 * @type <Boolean> */ nullable:false, /** * @func 是否默认选中第一个,当值为null时,仅仅输入框为空时才选中第一个 * @type <Boolean, Null> */ activeFirst:false, /** * @func 是否在选择后让输入框获得焦点 * @type <Boolean> */ selectFocus:true, /** * @func 是否允许单选时重复匹配 * @type <Boolean> * @desc 当列表有重复的值时,如果设为false,那么只会匹配第一条 */ repeat:true, /** * @func 是否等到中文输入完成再执行查询 * @type <Boolean> */ complete:false, /** * @func 搜索时是否缓存数据 * @type <Boolean> * @desc 设置为true后,如果输入之前已经查询过的数据,那么不再执行查询,直接返回该数据 */ cache:false, /** * @func 展示内容显示容器 * @type <String> 字符串选择器 * @type <Object> dom、jQuery对象 * @type <Function> * @param self <Object> 组件实例对象 * @return <String, Object> 返回选择器或者元素对象 * @desc 如果设置为body,创建的元素会基于搜索输入框进行定位,如果不是则基于该值定义的元素进行定位 */ container:'body', /** * @func 下拉列表的数量,超过出现滚动条 * @type <Number> */ limit:6, /** * @func 为展示元素增加高宽 * @type <Object> */ size:null, /** * @func 设置展示元素的偏移值 * @type <Object> */ offset:null, /** * @func 设置展示元素样式 * @type <Object> */ style:null, /** * @func jQuery ajax配置 * @type <Object> */ ajax:null, /** * @func 自定义列表数据 * @type <Array> * @type <Function> * @param self <Object> 组件实例对象 * @return <Array> 返回自定义数据 */ data:null, /** * @func 展示标签配置项 * @type <Object> */ tag:{ /** * @func 标签类型 * @type <String> * @type <Array> * @desc 设置后会增加ui-tag-{type}类名 */ type:'', /** * @func 关闭按钮内容 * @type <String> */ close:'×', /** * @func 是否多选 * @type <Boolean> */ multiple:false, /** * @func 是否可以用退格键删除标签 * @type <Boolean> */ backspace:false, /** * @func 点击叉号删除标签时是否使输入框获取焦点 * @type <Boolean> */ focus:false, /** * @func 选择完是否清空输入框 * @type <Boolean> */ clear:false, /** * @func 鼠标悬停在标签上是否有提示 * @type <Boolean> */ title:true, /** * @func 标签填充容器 * @type <DOM, jQuery Object, Selector> */ container:null, /** * @func 设置滚动容器 * @type <DOM, jQuery Object, Selector> * @desc 如果未设置将取container作为滚动容器 */ scroll:null, /** * @func 设置标签数据 * @type <Function> * @param self <Object> 组件实例对象 * @param elem <jQuery Object> 标签元素对象 * @return 返回数据对象 */ getData:null, /** * @func 检测是否可以删除标签 * @type <Function> * @param self <Object> 组件实例对象 * @param data <Object> 需要添加为标签的数据对象 * @param tag <Object> 已存在的标签对象 * @return <Boolean> 返回true表示需要添加的数据已存在与标签中,需删除; 返回true或者null表示不会被添加为标签 */ deleteMatch:null }, /** * @func 设置多菜单 * @type <Array> * [{ * title:'', * content:'', * active:true, * onShow:function(){ * * } * }] */ tabs:null, /** * @func * @type <Object> * { * field:'name', * like:/^\d/ * } * @type <Array> * [{ * field:'name', * like:/^\d/, * like:'^{value}', * like:function(data, value){ * return data.indexOf(value) !== -1 * } * }] */ match:null, /** * @func 定义搜索列表选中数据 * @type <Function> * @param self <Object> 组件实例对象 * @param data <Object> 当前选中数据 * @return <String> 返回自定义填充值 * @desc 未设置该值时,默认取field中的数据 */ setValue:null, /** * @func 列表渲染时判断某一行是否被选中 * @type <Function> * @param self <Object> 组件实例对象 * @param data <Object> 列表选中数据 * @return <Boolean> */ selected:null, /** * @func 过滤数据 * @type <Function> * @param self <Object> 组件实例对象 * @param data <Object> 列表数据 * @param value <String> 输入框中的值 * @return <Array> 返回过滤后的数组 */ filter:null, /** * @func 输入框失去焦点时校验内容是否存在 * @type <Boolean> 为true时默认取field参数对应的字段 * @type <Function> * @param self <Object> 组件实例对象 * @param data <Object> 遍历的一行数据 * @param value <String> 输入框中的值 * @return <Boolean> 返回true则表示数据匹配 * @desc 仅在单选功能才会启用 */ exist:null, /** * @func 请求返回数据时触发回调 * @type <Function> * @param self <Object> 组件实例对象 * @param data <Anything> 接口返回数据 * @return <Array> 返回列表数据 */ onResponse:null, /** * @func 选中列表前触发回调 * @type <Function> * @param self <Object> 组件实例对象 * @param data <Object> 选中项数据 * @param event <Object> 事件对象 * @param elem <jQuery Object> 选中项对象 * @return <Boolean> 返回false则不会触发setValue以及onSelect */ onSelectBefore:null, /** * @func 选中列表后触发回调 * @type <Function> * @param self <Object> 组件实例对象 * @param data <Object> 选中项数据 * @param event <Object> 事件对象 * @param elem <jQuery Object> 选中项对象 */ onSelect:null, /** * @func 输入框失焦后触发 * @type <Function> * @param self <Object> 组件实例对象 * @param data <Object> exist参数启用时该参数才存在,表示已选中数据 */ onBlur:null, /** * @func 改变选中值后触发回调 * @type <Function> * @param self <Object> 组件实例对象 * @param event <Event Object> 当删除标签时才会有该参数 */ onChange:null }, _template:{ wrap: '<div class="<% className %>"<%if style%> style="<%include \'style\'%>"<%/if%>>'+ '<%include "head"%>'+ '<div class="con-search-body<%if tabs.length > 1%> con-search-body-tab<%/if%>">'+ '<%include "tabs"%>'+ '<div class="con-search-inner">'+ '<%each tabs%>'+ '<div class="con-search-content<%if $index === 0%> con-search-result<%/if%>" style="display:none;"></div>'+ '<%/each%>'+ '</div>'+ '</div>'+ '<%include "foot"%>'+ '</div>', result: '<%var count = 0%>'+ '<%if data && (count = data.length)%>'+ '<%if prompt%>'+ '<%include "prompt"%>'+ '<%/if%>'+ '<%include "list"%>'+ '<%elseif value%>'+ '<%include "empty"%>'+ '<%/if%>', list: '<ul class="con-search-list">'+ '<%each data $data $index%>'+ '<%include "item"%>'+ '<%/each%>'+ '</ul>', tabs: '<%if tabs.length > 1%>'+ '<div class="con-search-tab">'+ '<%each tabs tab%>'+ '<span class="con-search-tab-nav"<%if typeof tab.style === "object" && tab.style%> style="<%each tab.style%><%$index%>:<%$value%>;<%/each%>"<%/if%>>'+ '<%tab.title%>'+ '</span>'+ '<%/each%>'+ '</div>'+ '<%/if%>', tags: '<%each data $data k%>'+ '<%if $data && $data.text%>'+ '<span class="ui-tag<%if type%><%each type v i%> ui-tag-<%v%><%/each%><%/if%>">'+ '<em class="con-tag-text"<%if title%> title="<%$data.text%>"<%/if%>><%$data.text%></em>'+ '<%if close%>'+ '<b class="con-tag-close"><%close%></b>'+ '<%/if%>'+ '<%if $data.fields?? && $data.fields%>'+ '<%each $data.fields%>'+ '<%if $value !== undefined%>'+ '<input type="hidden" name="<%$index%>" value="<%$value%>">'+ '<%/if%>'+ '<%/each%>'+ '<%/if%>'+ '</span>'+ '<%/if%>'+ '<%/each%>' }, _events:{ 'mouseenter':'_mouseover', 'mouseleave':'_mouseout', 'mouseenter .con-search-result .con-search-item':'_mouseover _itemActive', 'mouseleave .con-search-result .con-search-item':'_itemCancelActive', 'click .con-search-result .con-search-item':'_setActive _select', 'click .con-search-tab-nav':'_toggle' }, _toggle:function(e, elem){ var self = this, opts = self._options, index = elem.index(); if(index < 0){ index = 0 } var data = self._elemData[index]; var container = data.$container; if(!container.is(':visible')){ if(index !== 0){ if(container.is(':empty') && data.content){ var content = ''; if(typeof data.content === 'function'){ content = data.content.call(opts, self, elem, container) } else{ content = data.content } if(content === false){ return } else if(typeof content === 'string'){ container.html(content) } } self._selectTab = data; } elem.addClass('s-crt'); container.show(); Nui.each(self._elemData, function(v, i){ if(i !== index){ v.$elem.removeClass('s-crt') v.$container.hide() } }) self.activeTab = data; if(typeof data.onShow === 'function'){ data.onShow.call(opts, self, elem, container) } } }, _mouseover:function(e, elem){ this._hover = true }, _mouseout:function(e, elem){ delete this._hover; }, _setActive:function(e, elem){ this._activeItem = elem; this._activeIndex = this._activeItem.data('index'); }, _itemActive:function(e, elem){ if(!e && this._activeItem){ this._activeItem.removeClass('s-hover'); delete this._activeItem; } elem.addClass('s-hover'); }, _itemCancelActive:function(e, elem){ elem.removeClass('s-hover') }, _select:function(e){ var self = this, opts = self._options, data = self.queryData[self._activeIndex], elem = this._activeItem, value = ''; if(data === undefined || !elem){ return } self._selectData = data; if(self._callback('SelectBefore', [data, e, elem]) === false){ return } if(typeof opts.setValue === 'function'){ value = opts.setValue.call(opts, self, data) } else if(opts.field){ value = data[opts.field] } self.value(value); self.hide(); self._callback('Select', [data, e, elem]); }, _match:function(data){ var self = this, opts = self._options, match = false; Nui.each(self._matchs, function(val){ var field = val.field, like = val.like, fieldValue; if(field && like && (fieldValue = data[field])){ if(Nui.type(like, 'RegExp') && fieldValue.match(like)){ match = true } else if(typeof like === 'string'){ like = like.replace(/\{value\}/g, self.val); if(fieldValue.match(new RegExp(like))){ match = true } } else if(typeof like === 'function'){ match = !!like(fieldValue, self.val) } if(match){ return false } } }) return match }, _storage:function(data, value){ var self = this, opts = self._options; if(!Nui.type(data, 'Array')){ data = [] } if(opts.cache === true){ self.cacheData[value !== undefined ? value : self.val] = data } self.queryData = data; self._show(true) }, _exist:function(){ var self = this, opts = self._options, exist = false, data = self.queryData; if(data.length && self.val){ Nui.each(data, function(val){ if(self._existCheck.call(opts, val, self.val) === true){ exist = val; return false } }) } return exist }, _filter:function(){ var self = this, opts = self._options, data = [], _data = self._setData(); if(typeof opts.filter === 'function'){ data = opts.filter.call(opts, self, _data, self.val); } else if(_data.length && self._matchs && self._matchs.length){ Nui.each(_data, function(val){ if(self._match(val)){ data.push(val) } }) } else{ data = _data } self._storage(data) }, _clear:function(){ clearTimeout(this._timer); if(this._ajax){ this._timer = null; this._ajax.abort() } }, _request:function(){ var self = this, opts = self._options, data = {}, value = self.val; if(opts.query && typeof opts.query === 'string'){ data[opts.query] = value } else if(typeof opts.query === 'function'){ var ret = opts.query.call(opts, self, value); if(ret){ if(typeof ret === 'object'){ data = ret } else if(typeof ret === 'string'){ data[ret] = value } } else{ return } } var success; if(typeof opts.ajax === 'function'){ success = opts.ajax.success; delete opts.ajax.success } self._clear() self._timer = setTimeout(function(){ self._ajax = request(jQuery.extend(true, { url:opts.url, data:data, type:'get', dataType:'json', cache:false, async:true, success:function(res, status, xhr){ var _data = res; if(typeof success === 'function'){ success.call(this, res, status, xhr) } if(typeof opts.onResponse === 'function'){ _data = opts.onResponse.call(opts, self, res) } self._storage(_data, value); } }, opts.ajax||{}), null) }, 50) }, _setActiveItem:function(code){ if(this._items){ var $elem = this._items[this._activeIndex]; if($elem){ this._itemActive(null, $elem); this._activeItem = $elem; var stop = this['_scroll' + code]($elem.position().top); if(stop !== undefined){ this.$list.animate({'scrollTop':stop}, 200) } } } }, _setDefault:function(){ var self = this, opts = self._options; if(opts.nullable === true){ if(!opts.url){ self.queryData = self._setData() } else{ self.queryData = [] } } }, //向下滚动 _scroll38:function(top){ var $list = this.$list, height = this._listHeight, stop; if(top > height){ stop = top } else if(top < 0){ stop = '-=' + height } return stop }, //向上滚动 _scroll40:function(top){ var $list = this.$list, height = this._listHeight, stop; if(top < 0){ stop = 0 } else if(top >= height){ stop = '+=' + top } return stop }, //按上 _code38:function(){ var len = this.queryData.length; if(len){ if(this._activeIndex === undefined || this._activeIndex <= 0){ this._activeIndex = len } this._activeIndex -= 1; this._setActiveItem(38); } }, //按下 _code40:function(){ var len = this.queryData.length; if(len){ if(this._activeIndex === undefined || this._activeIndex === len - 1){ this._activeIndex = -1 } this._activeIndex += 1; this._setActiveItem(40); } }, //回车 _code13:function(e){ this._select(e) }, _allow:function(condition){ if(this.activeTab !== this._selectTab && condition){ return true } }, _bindEvent:function(){ var self = this, opts = self._options, complete = true, timer = null, bsie = false; var inputCallback = function(elem){ clearTimeout(timer) //因为input事件会在compositionend之前触发,因此加个定时器延迟执行 timer = setTimeout(function(){ //bsie是为了防止IE8-事件执行多次 if(self._focus && !bsie && complete){ bsie = true if(self.val = Nui.trim(elem.val())){ var cache; if(opts.cache === true && (cache = self.cacheData[self.val])){ self._storage(cache); } else if(opts.url){ self.queryData = [] self._request() } else{ self._filter() } } else{ self._show(true, true) } bsie = false } }) } var show = function(callback){ self._show(); self._hover = true; //添加定时器是因为部分浏览器不会获得焦点 setTimeout(function(){ self.target.focus(); callback && callback() }) } self._on('click', self.target, function(e, elem){ if(!self._showed){ elem.focus() } }) self._on('focus', self.target, function(e, elem){ self._focus = true; if(opts.focus === true){ self._show() } }) self._on('blur', self.target, function(e, elem){ delete self._focus; if(!self._hover){ var data, args; if(self._existCheck){ args = [self._selectData]; if((data = self._exist()) === false){ self.value(null) } else if(self._showed && data !== self._selectData){ args = [data]; self._code40(); self._select(e); } } self._clear() self._callback('Blur', [].concat(args, e, elem)) self.hide() } else if(opts.selectFocus){ show() } }) self._on('keydown', self.target, function(e, elem){ var code = e.keyCode; if(self._allow(code === 13)){ self['_code'+code](e) } else if(code === 8){ var focusIndex = util.getFocusIndex(e.target); //光标位置在输入框起始处时删除末尾的标签 if(!focusIndex && self._tag.backspace === true){ var tagSize = 0; if(self.tagData && (tagSize = self.tagData.length)){ self.tagData[tagSize - 1].$elem.remove(); self._change(e) } } //IE9退格按键无法触发input事件 else if(Nui.browser.msie && Nui.browser.version == '9.0'){ inputCallback(elem) } } }) self._on('keyup', self.target, function(e, elem){ var code = e.keyCode; if(self._allow(code === 40 || code === 38)){ if(self._showed === true){ self['_code'+code](e) } else{ self._show() } } }) if(opts.complete){ self._on('compositionstart', self.target, function(e, elem){ complete = false }) self._on('compositionend', self.target, function(e, elem){ //中文输入完毕 complete = true }) } self._on('input propertychange', self.target, function(e, elem){ inputCallback(elem) }) var $tagContainer = self.$tagContainer; var $tagScroll = self.$tagScroll; var tag = opts.tag; if($tagContainer){ self._on('click', $tagContainer, '.ui-tag > .con-tag-close', function(e, elem){ if(self._disabled()){ return } elem.closest('.ui-tag').remove(); delete self._hover; if(tag.focus === true){ show(function(){ self._change(e) }) } else{ self._change(e); } }) if($tagScroll && $tagScroll.find(self.target).length){ //关闭标签时隐藏组件 if(tag.focus !== true){ self._on('mouseenter', $tagContainer, '.ui-tag', function(){ delete self._hover }) } self._on('mouseenter', $tagScroll , function(){ self._mouseover() }) self._on('mouseleave', $tagScroll , function(){ self._mouseout() }) self._on('click', $tagContainer, '.ui-tag', function(){ self._tag_event = true; }) self._on('click', $tagScroll , function(e){ if(!self._tag_event && !self._showed){ delete self._hover; show(function(){ self.resize() }) } else{ delete self._tag_event } }) } else if(tag.focus === true){ self._on('mouseenter', $tagContainer, '.ui-tag', function(){ self._mouseover() }) self._on('mouseleave', $tagContainer, '.ui-tag', function(){ self._mouseout() }) } } }, _create:function(){ var self = this, data = self._tplData(), opts = self._options; data.style = opts.style || {}; data.style.display = 'none'; if(data.style['z-index'] === undefined){ data.style['z-index'] = opts.zIndex || 0 } self._elemData = [{ title:'结果', style:{ display:'none' } }]; if(Nui.isArray(opts.tabs) && opts.tabs.length){ self._isTab = true; self._elemData = self._elemData.concat(opts.tabs); } data.tabs = self._elemData; self.element = $(self._tpl2html('wrap', data)).appendTo(self.container); self._setElemData(); self.$body = self.element.children('.con-search-body'); self.$foot = self.element.children('.con-search-foot'); self.$inner = self.$body.children('.con-search-inner'); self.$result = self.$inner.children('.con-search-result'); self._elemData[0].$elem = $(); self._elemData[0].$container = self.$result; if(self._isTab){ var tabs = self.$body.children('.con-search-tab').children(); var containers = self.$inner.children(); Nui.each(self._elemData, function(v, i){ v.$elem = tabs.eq(i); v.$container = containers.eq(i); if(!self._defaultTab && v.active === true){ self._defaultTab = v } }) //没有设置默认显示标签则取第一个 if(!self._defaultTab){ self._defaultTab = self._elemData[1] } } self._event() }, _initTemplate:function(name){ var self = this, opts = self._options, content = opts[name]; if(typeof content === 'function'){ content = content.call(opts, self) } if(!content || typeof content !== 'string'){ if(name === 'item' && opts.field){ content = '<li class="con-search-item<%selected($data)%>" data-index="<%$index%>"><%$data["'+ opts.field +'"]??%></li>' } else{ content = '' } } else if(name === 'item' && content && !/^\s*\<li\s+/i.test(content)){ content = '<li class="con-search-item<%selected($data)%>" data-index="<%$index%>">'+ content +'</li>' } if(name !== 'item' && content){ content = '<div class="con-search-'+ name +'">'+ content +'</div>' } self._template[name] = content; }, _setData:function(){ var self = this, opts = self._options, data = opts.data; if(typeof opts.data === 'function'){ data = opts.data.call(opts, self) } if(!Nui.type(data, 'Array')){ data = [] } return self.data = data }, _setElemData:function(){ var self = this, elem = self.element, _class = self.constructor; self._elementData = { btbWidth:_class._getSize(elem, 'tb', 'border'), blrWidth:_class._getSize(elem, 'lr', 'border'), ptbWidth:_class._getSize(elem, 'tb', 'padding'), plrWidth:_class._getSize(elem, 'lr', 'padding') } }, _setTargetData:function(){ var self = this, target = self.target, _class = self.constructor; if(self.container[0].nodeName !== 'BODY'){ self._container_body = false; target = self.container; var pos = self.container.css('position'); if('absolute relative fixed'.indexOf(pos) === -1){ self.container.css('position', 'relative') } } else{ self._container_body = true } self._targetData = { blWidth:_class._getSize(target, 'l', 'border'), brWidth:_class._getSize(target, 'r', 'border'), btWidth:_class._getSize(target, 't', 'border'), bbWidth:_class._getSize(target, 'b', 'border') } }, _getTagData:function($elem){ var self = this, opts = self._options, data = {}; if(typeof self._tag.getData === 'function'){ data = self._tag.getData.call(opts, self, $elem) || {} } else{ data.text = $elem.children('.con-tag-text').text(); } data.$elem = $elem; return data }, _setTagsData:function(){ var self = this, opts = self._options; self.$tags = self.$tagContainer.children('.ui-tag'); self.tagData = []; self.$tags.each(function(){ self.tagData.push(self._getTagData($(this))) }) }, _initTag:function(){ var self = this, opts = self._options; self._tag = opts.tag; if(typeof self._tag !== 'object'){ self._tag = {} } self.$tagContainer = self._jquery(self._tag.container); self.$tagScroll = self._jquery(self._tag.scroll); if(!self.$tagScroll){ self.$tagScroll = self.$tagContainer } }, _initData:function(){ var self = this, opts = self._options, data = opts.data, match = opts.match, target = self.target; self.cacheData = {}; self.queryData = []; self.data = []; self._size = opts.size || {}; self._offset = opts.offset || {}; self._isTab = false; self._defaultTab = null; self._listHeight = 0; self._multiple = target.prop('multiple') || opts.tag.multiple; self._setTargetData(); self._initTag(); Nui.each(['empty', 'head', 'foot'], function(name){ self._initTemplate(name); }) if(match && Nui.type(match, 'Object')){ match = [match] } if(Nui.type(match, 'Array')){ self._matchs = match } if(self._multiple !== true){ if(opts.exist === true && opts.field){ self._existCheck = function(val){ return val[opts.field] === self.val } } else if(typeof opts.exist === 'function'){ self._existCheck = opts.exist } } }, _getSelected:function(){ var self = this, opts = self._options; delete self._selected var selected = function(){ return '' } if(typeof opts.selected === 'function'){ selected = function(data){ if(!opts.repeat && self._selected && !opts.tag.multiple){ return '' } if(opts.selected.call(opts, self, data) === true){ self._selected = true return ' s-crt' } return '' } } else if(opts.field){ selected = function(data){ var cls = ''; if(!opts.repeat && self._selected && !opts.tag.multiple){ return cls } if(self.tagData){ Nui.each(self.tagData, function(v){ if(data[opts.field] === v.text){ self._selected = true cls = ' s-crt' return false } }) } else if(self.val && data[opts.field] === self.val){ self._selected = true cls = ' s-crt' } return cls } } return selected }, _setHeight:function(){ var self = this, opts = self._options, len = self.queryData.length; if(len > 0 && opts.limit > 0){ var height = 0; delete self._activeIndex; self.$list = self.$result.children('.con-search-list'); self._items = []; self.$list.children('.con-search-item').each(function(i){ var $elem = $(this); self._items.push($elem); if(!self._isTab && self._activeIndex === undefined && $elem.hasClass('s-crt')){ self._selectData = self.queryData[i]; self._activeIndex = i; self._activeItem = $elem; } }) if(!self._itemHeight){ if(self._items.length){ self._itemHeight = height = self._items[0].outerHeight() } } else{ height = self._itemHeight } if(height){ if(len > opts.limit){ height *= opts.limit } else{ height *= len } self.$list.height(self._listHeight = height); } } else{ delete self.$list; delete self._items; } }, _scrollto:function(){ if(this._activeIndex !== undefined && !this._isTab && this.$list){ this.$list.scrollTop(this._activeIndex * this._itemHeight) } else if(this._activeIndex === undefined){ if(!this.val && this._options.activeFirst === null || !!this._options.activeFirst){ this._code40() } } }, _render:function(){ var self = this, _class = self.constructor, result = self._elemData[0], inputting = self.inputting; if(!this.element){ return } result.$elem.hide(); result.$container.hide(); _class._active = self; //输入的时候才会显示 if((self._isTab && self.val && inputting) || !self._isTab){ if(!inputting || !self.val || (inputting && !self._initTpl)){ Nui.each(['item', 'prompt'], function(name){ self._initTemplate(name); }) if(inputting){ self._initTpl = true } } self.$result.html(self._tpl2html('result', { data:self.queryData, value:self.val, selected:self._getSelected(), prompt:!!this._template.prompt })); result.$elem.show(); if(self._defaultTab){ self._defaultTab.$elem.hide() } self._toggle(null, result.$elem); self._setHeight(); } else if(self._selectTab){ self._defaultTab.$elem.show(); self._toggle(null, self._selectTab.$elem) } else if(self._defaultTab){ self._toggle(null, self._defaultTab.$elem.show()) } self.element.show(); self._showed = true; self.resize(); self._scrollto(); }, _change:function(e){ var self = this; self._setTagsData(); if(self.element){ self.resize() self._callback('Change', [e]) } }, _data2html:function(data){ return this.constructor.data2html(data, this._tag) }, _exec:function(){ var self = this, opts = self._options; if(self._getTarget() && (self.container = self._jquery(opts.container))){ self._initData(); self._bindEvent(); self._callback('Init') } }, _reset:function(){ component.exports._reset.call(this); this.hide(); delete this._selectData; }, /** * @param inputting <Boolean> 正在输入操作 */ _show:function(inputting, isDefatlt){ var self = this, opts = self._options, _class = self.constructor; self.inputting = !!inputting; self.val = Nui.trim(this.target.val()); if(self._callback('ShowBefore') === false){ return } if(self._disabled() || (self._hover && !self.inputting)){ return } //页面中只能存在一个显示的组件 if(_class._active && _class._active !== self){ _class._active.hide() } //输入框值为空不允许展示下拉 else if(opts.nullable !== true && !self.val){ self.hide() } else{ if(!self.element){ self._create() } if(!self._showed && self.$tagContainer){ self._setTagsData() } //不论输入框是否有值,获得焦点时显示完整列表 if(!self.inputting || isDefatlt){ self._setDefault() } if(self.queryData.length || opts.empty || self._isTab){ self._render(); this._callback('Show'); } else{ self.hide() } } }, resize:function(){ if(!this.element){ return } var self = this, target = self.target, elem = self.element, targetData = self._targetData, elemData = self._elementData, oHeight = elem.outerHeight(), offset = target.offset(), otop = offset.top, oleft = offset.left, wWidth = Nui.win.width(), wHeight = Nui.win.height(), notbody = !self._container_body, containeOffset = self.container.offset(), stop = Nui.win.scrollTop(), sleft = Nui.win.scrollLeft(); if(notbody){ containeOffset.top -= self.container.scrollTop(); containeOffset.left -= self.container.scrollLeft(); otop = offset.top - containeOffset.top; oleft = offset.left - containeOffset.left; } else{ containeOffset.top = 0 containeOffset.left = 0 } targetData.oWidth = target.outerWidth() targetData.oHeight = target.outerHeight() var top = otop + targetData.oHeight - targetData.bbWidth + (self._offset.top||0); var left = oleft + (self._offset.left||0); var width = targetData.oWidth - elemData.blrWidth - elemData.plrWidth + (self._size.width || 0); self.element.css({ top:top, left:left, width:width }).removeClass('nui-search-position-top'); var initOffset = self.element.offset(); //内容在可视区域底部显示不全,则在输入框上方显示 var diff = wHeight - (initOffset.top - stop) - oHeight; if(diff < 0){ var _top = offset.top - oHeight - (self._offset.top||0) + targetData.btWidth; //上方区域足够 if(_top >= 0){ top = _top - containeOffset.top self.element.addClass('nui-search-position-top') } } //内容在可视区域右侧显示不全,