|
@@ -1,1042 +1,1180 @@
|
|
|
/**
|
|
|
- * Bootstrap Search Suggest
|
|
|
- * @desc 这是一个基于 bootstrap 按钮式下拉菜单组件的搜索建议插件,必须使用于按钮式下拉菜单组件上。
|
|
|
- * @author renxia <lzwy0820#qq.com>
|
|
|
- * @github https://github.com/lzwme/bootstrap-suggest-plugin.git
|
|
|
- * @since 2014-10-09
|
|
|
- *===============================================================================
|
|
|
- * (c) Copyright 2014-2019 http://lzw.me All Rights Reserved.
|
|
|
- ********************************************************************************/
|
|
|
-(function (factory) {
|
|
|
- if (typeof define === "function" && define.amd) {
|
|
|
- define(['jquery'], factory);
|
|
|
- } else if (typeof exports === 'object' && typeof module === 'object') {
|
|
|
- factory(require('jquery'));
|
|
|
- } else if (window.jQuery) {
|
|
|
- factory(window.jQuery);
|
|
|
- } else {
|
|
|
- throw new Error('Not found jQuery.');
|
|
|
- }
|
|
|
+ * bootstrap-suggest-plugin - v0.1.29
|
|
|
+ * @description 这是一个基于 bootstrap 按钮式下拉菜单组件的搜索建议插件,必须使用于按钮式下拉菜单组件上。
|
|
|
+ * @author lzwme - https://lzw.me
|
|
|
+ * @GitHub https://github.com/lzwme/bootstrap-suggest-plugin.git
|
|
|
+ * @since 2019-11-18 09:30:06
|
|
|
+ */
|
|
|
+(function(factory) {
|
|
|
+ if (typeof define === "function" && define.amd) {
|
|
|
+ define(["jquery"], factory);
|
|
|
+ } else if (typeof exports === "object" && typeof module === "object") {
|
|
|
+ factory(require("jquery"));
|
|
|
+ } else if (window.jQuery) {
|
|
|
+ factory(window.jQuery);
|
|
|
+ } else {
|
|
|
+ throw new Error("Not found jQuery.");
|
|
|
+ }
|
|
|
})(function($) {
|
|
|
- var VERSION = 'VERSION_PLACEHOLDER';
|
|
|
- var $window = $(window);
|
|
|
- var isIe = 'ActiveXObject' in window; // 用于对 IE 的兼容判断
|
|
|
- var inputLock; // 用于中文输入法输入时锁定搜索
|
|
|
-
|
|
|
- // ie 下和 chrome 51 以上浏览器版本,出现滚动条时不计算 padding
|
|
|
- var chromeVer = navigator.userAgent.match(/Chrome\/(\d+)/);
|
|
|
- if (chromeVer) {
|
|
|
- chromeVer = +chromeVer[1];
|
|
|
- }
|
|
|
- var notNeedCalcPadding = isIe || chromeVer > 51;
|
|
|
-
|
|
|
- // 一些常量
|
|
|
- var BSSUGGEST = 'bsSuggest';
|
|
|
- var onDataRequestSuccess = 'onDataRequestSuccess';
|
|
|
- var DISABLED = 'disabled';
|
|
|
- var TRUE = true;
|
|
|
- var FALSE = false;
|
|
|
-
|
|
|
- function isUndefined(val) {
|
|
|
- return val === void(0);
|
|
|
+ var VERSION = "VERSION_PLACEHOLDER";
|
|
|
+ var $window = $(window);
|
|
|
+ var isIe = "ActiveXObject" in window; // 用于对 IE 的兼容判断
|
|
|
+ var inputLock; // 用于中文输入法输入时锁定搜索
|
|
|
+
|
|
|
+ // ie 下和 chrome 51 以上浏览器版本,出现滚动条时不计算 padding
|
|
|
+ var chromeVer = navigator.userAgent.match(/Chrome\/(\d+)/);
|
|
|
+ if (chromeVer) {
|
|
|
+ chromeVer = +chromeVer[1];
|
|
|
+ }
|
|
|
+ var notNeedCalcPadding = isIe || chromeVer > 51;
|
|
|
+
|
|
|
+ // 一些常量
|
|
|
+ var BSSUGGEST = "bsSuggest";
|
|
|
+ var onDataRequestSuccess = "onDataRequestSuccess";
|
|
|
+ var DISABLED = "disabled";
|
|
|
+ var TRUE = true;
|
|
|
+ var FALSE = false;
|
|
|
+
|
|
|
+ function isUndefined(val) {
|
|
|
+ return val === void 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 错误处理
|
|
|
+ */
|
|
|
+ function handleError(e1, e2) {
|
|
|
+ if (!window.console || !window.console.trace) {
|
|
|
+ return;
|
|
|
}
|
|
|
-
|
|
|
- /**
|
|
|
- * 错误处理
|
|
|
- */
|
|
|
- function handleError(e1, e2) {
|
|
|
- if (!window.console || !window.console.trace) {
|
|
|
- return;
|
|
|
- }
|
|
|
- console.trace(e1);
|
|
|
- if (e2) {
|
|
|
- console.trace(e2);
|
|
|
- }
|
|
|
+ console.trace(e1);
|
|
|
+ if (e2) {
|
|
|
+ console.trace(e2);
|
|
|
}
|
|
|
- /**
|
|
|
- * 获取当前 tr 列的关键字数据
|
|
|
- */
|
|
|
- function getPointKeyword($list) {
|
|
|
- return $list.data();
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 获取当前 tr 列的关键字数据
|
|
|
+ */
|
|
|
+ function getPointKeyword($list) {
|
|
|
+ return $list.data();
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 设置或获取输入框的 alt 值
|
|
|
+ */
|
|
|
+ function setOrGetAlt($input, val) {
|
|
|
+ return isUndefined(val) ? $input.attr("alt") : $input.attr("alt", val);
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 设置或获取输入框的 data-id 值
|
|
|
+ */
|
|
|
+ function setOrGetDataId($input, val) {
|
|
|
+ return val !== void 0
|
|
|
+ ? $input.attr("data-id", val)
|
|
|
+ : $input.attr("data-id");
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 设置选中的值
|
|
|
+ */
|
|
|
+ function setValue($input, keywords, options) {
|
|
|
+ if (!keywords || !keywords.key) {
|
|
|
+ return;
|
|
|
}
|
|
|
- /**
|
|
|
- * 设置或获取输入框的 alt 值
|
|
|
- */
|
|
|
- function setOrGetAlt($input, val) {
|
|
|
- return isUndefined(val) ? $input.attr('alt') : $input.attr('alt', val);
|
|
|
- }
|
|
|
- /**
|
|
|
- * 设置或获取输入框的 data-id 值
|
|
|
- */
|
|
|
- function setOrGetDataId($input, val) {
|
|
|
- return val !== (void 0) ? $input.attr('data-id', val) : $input.attr('data-id');
|
|
|
+
|
|
|
+ var separator = options.separator || ",",
|
|
|
+ inputValList,
|
|
|
+ inputIdList,
|
|
|
+ dataId = setOrGetDataId($input);
|
|
|
+
|
|
|
+ if (options && options.multiWord) {
|
|
|
+ inputValList = $input.val().split(separator);
|
|
|
+ inputValList[inputValList.length - 1] = keywords.key;
|
|
|
+
|
|
|
+ //多关键字检索支持设置id --- 存在 bug,不建议使用
|
|
|
+ if (!dataId) {
|
|
|
+ inputIdList = [keywords.id];
|
|
|
+ } else {
|
|
|
+ inputIdList = dataId.split(separator);
|
|
|
+ inputIdList.push(keywords.id);
|
|
|
+ }
|
|
|
+
|
|
|
+ setOrGetDataId($input, inputIdList.join(separator))
|
|
|
+ .val(inputValList.join(separator))
|
|
|
+ .focus();
|
|
|
+ } else {
|
|
|
+ setOrGetDataId($input, keywords.id || "")
|
|
|
+ .val(keywords.key)
|
|
|
+ .focus();
|
|
|
}
|
|
|
- /**
|
|
|
- * 设置选中的值
|
|
|
- */
|
|
|
- function setValue($input, keywords, options) {
|
|
|
- if (!keywords || !keywords.key) {
|
|
|
- return;
|
|
|
- }
|
|
|
|
|
|
- var separator = options.separator || ',',
|
|
|
- inputValList,
|
|
|
- inputIdList,
|
|
|
- dataId = setOrGetDataId($input);
|
|
|
+ $input
|
|
|
+ .data("pre-val", $input.val())
|
|
|
+ .trigger("onSetSelectValue", [
|
|
|
+ keywords,
|
|
|
+ (options.data.value || options._lastData.value)[keywords.index]
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 调整选择菜单位置
|
|
|
+ * @param {Object} $input
|
|
|
+ * @param {Object} $dropdownMenu
|
|
|
+ * @param {Object} options
|
|
|
+ */
|
|
|
+ function adjustDropMenuPos($input, $dropdownMenu, options) {
|
|
|
+ if (!$dropdownMenu.is(":visible")) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- if (options && options.multiWord) {
|
|
|
- inputValList = $input.val().split(separator);
|
|
|
- inputValList[inputValList.length - 1] = keywords.key;
|
|
|
+ var $parent = $input.parent();
|
|
|
+ var parentHeight = $parent.height();
|
|
|
+ var parentWidth = $parent.width();
|
|
|
|
|
|
- //多关键字检索支持设置id --- 存在 bug,不建议使用
|
|
|
- if (!dataId) {
|
|
|
- inputIdList = [keywords.id];
|
|
|
- } else {
|
|
|
- inputIdList = dataId.split(separator);
|
|
|
- inputIdList.push(keywords.id);
|
|
|
- }
|
|
|
+ if (options.autoDropup) {
|
|
|
+ setTimeout(function() {
|
|
|
+ var offsetTop = $input.offset().top;
|
|
|
+ var winScrollTop = $window.scrollTop();
|
|
|
+ var menuHeight = $dropdownMenu.height();
|
|
|
|
|
|
- setOrGetDataId($input, inputIdList.join(separator))
|
|
|
- .val(inputValList.join(separator))
|
|
|
- .focus();
|
|
|
+ if (
|
|
|
+ // 自动判断菜单向上展开
|
|
|
+ $window.height() + winScrollTop - offsetTop < menuHeight && // 假如向下会撑长页面
|
|
|
+ offsetTop > menuHeight + winScrollTop // 而且向上不会撑到顶部
|
|
|
+ ) {
|
|
|
+ $parent.addClass("dropup");
|
|
|
} else {
|
|
|
- setOrGetDataId($input, keywords.id || '').val(keywords.key).focus();
|
|
|
+ $parent.removeClass("dropup");
|
|
|
}
|
|
|
-
|
|
|
- $input.data('pre-val', $input.val())
|
|
|
- .trigger('onSetSelectValue', [keywords, (options.data.value || options._lastData.value)[keywords.index]]);
|
|
|
+ }, 10);
|
|
|
}
|
|
|
- /**
|
|
|
- * 调整选择菜单位置
|
|
|
- * @param {Object} $input
|
|
|
- * @param {Object} $dropdownMenu
|
|
|
- * @param {Object} options
|
|
|
- */
|
|
|
- function adjustDropMenuPos($input, $dropdownMenu, options) {
|
|
|
- if (!$dropdownMenu.is(':visible')) {
|
|
|
- return;
|
|
|
- }
|
|
|
|
|
|
- var $parent = $input.parent();
|
|
|
- var parentHeight = $parent.height();
|
|
|
- var parentWidth = $parent.width();
|
|
|
-
|
|
|
- if (options.autoDropup) {
|
|
|
- setTimeout(function() {
|
|
|
- var offsetTop = $input.offset().top;
|
|
|
- var winScrollTop = $window.scrollTop();
|
|
|
- var menuHeight = $dropdownMenu.height();
|
|
|
-
|
|
|
- if ( // 自动判断菜单向上展开
|
|
|
- ($window.height() + winScrollTop - offsetTop) < menuHeight && // 假如向下会撑长页面
|
|
|
- offsetTop > (menuHeight + winScrollTop) // 而且向上不会撑到顶部
|
|
|
- ) {
|
|
|
- $parent.addClass('dropup');
|
|
|
- } else {
|
|
|
- $parent.removeClass('dropup');
|
|
|
- }
|
|
|
- }, 10);
|
|
|
- }
|
|
|
-
|
|
|
- // 列表对齐方式
|
|
|
- var dmcss = {};
|
|
|
- if (options.listAlign === 'left') {
|
|
|
- dmcss = {
|
|
|
- 'left': $input.siblings('div').width() - parentWidth,
|
|
|
- 'right': 'auto'
|
|
|
- };
|
|
|
- } else if (options.listAlign === 'right') {
|
|
|
- dmcss = {
|
|
|
- 'left': 'auto',
|
|
|
- 'right': 0
|
|
|
- };
|
|
|
- }
|
|
|
+ // 列表对齐方式
|
|
|
+ var dmcss = {};
|
|
|
+ if (options.listAlign === "left") {
|
|
|
+ dmcss = {
|
|
|
+ left: $input.siblings("div").width() - parentWidth,
|
|
|
+ right: "auto"
|
|
|
+ };
|
|
|
+ } else if (options.listAlign === "right") {
|
|
|
+ dmcss = {
|
|
|
+ left: "auto",
|
|
|
+ right: 0
|
|
|
+ };
|
|
|
+ }
|
|
|
|
|
|
- // ie 下,不显示按钮时的 top/bottom
|
|
|
- if (isIe && !options.showBtn) {
|
|
|
- if (!$parent.hasClass('dropup')) {
|
|
|
- dmcss.top = parentHeight;
|
|
|
- dmcss.bottom = 'auto';
|
|
|
- } else {
|
|
|
- dmcss.top = 'auto';
|
|
|
- dmcss.bottom = parentHeight;
|
|
|
- }
|
|
|
- }
|
|
|
+ // ie 下,不显示按钮时的 top/bottom
|
|
|
+ if (isIe && !options.showBtn) {
|
|
|
+ if (!$parent.hasClass("dropup")) {
|
|
|
+ dmcss.top = parentHeight;
|
|
|
+ dmcss.bottom = "auto";
|
|
|
+ } else {
|
|
|
+ dmcss.top = "auto";
|
|
|
+ dmcss.bottom = parentHeight;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- // 是否自动最小宽度
|
|
|
- if (!options.autoMinWidth) {
|
|
|
- dmcss.minWidth = parentWidth;
|
|
|
- }
|
|
|
- /* else {
|
|
|
+ // 是否自动最小宽度
|
|
|
+ if (!options.autoMinWidth) {
|
|
|
+ dmcss.minWidth = parentWidth;
|
|
|
+ }
|
|
|
+ /* else {
|
|
|
dmcss['width'] = 'auto';
|
|
|
}*/
|
|
|
|
|
|
- $dropdownMenu.css(dmcss);
|
|
|
-
|
|
|
- return $input;
|
|
|
+ $dropdownMenu.css(dmcss);
|
|
|
+
|
|
|
+ return $input;
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 设置输入框背景色
|
|
|
+ * 当设置了 indexId,而输入框的 data-id 为空时,输入框加载警告色
|
|
|
+ */
|
|
|
+ function setBackground($input, options) {
|
|
|
+ var inputbg, bg, warnbg;
|
|
|
+ if ((options.indexId === -1 && !options.idField) || options.multiWord) {
|
|
|
+ return $input;
|
|
|
}
|
|
|
- /**
|
|
|
- * 设置输入框背景色
|
|
|
- * 当设置了 indexId,而输入框的 data-id 为空时,输入框加载警告色
|
|
|
- */
|
|
|
- function setBackground($input, options) {
|
|
|
- var inputbg, bg, warnbg;
|
|
|
- if ((options.indexId === -1 && !options.idField) || options.multiWord) {
|
|
|
- return $input;
|
|
|
- }
|
|
|
|
|
|
- bg = options.inputBgColor;
|
|
|
- warnbg = options.inputWarnColor;
|
|
|
+ bg = options.inputBgColor;
|
|
|
+ warnbg = options.inputWarnColor;
|
|
|
|
|
|
- var curVal = $input.val();
|
|
|
- var preVal = $input.data('pre-val');
|
|
|
+ var curVal = $input.val();
|
|
|
+ var preVal = $input.data("pre-val");
|
|
|
|
|
|
- if (setOrGetDataId($input) || !curVal) {
|
|
|
- $input.css('background', bg || '');
|
|
|
+ if (setOrGetDataId($input) || !curVal) {
|
|
|
+ $input.css("background", bg || "");
|
|
|
|
|
|
- if (!curVal && preVal) {
|
|
|
- $input.trigger('onUnsetSelectValue').data('pre-val', '');
|
|
|
- }
|
|
|
-
|
|
|
- return $input;
|
|
|
- }
|
|
|
+ if (!curVal && preVal) {
|
|
|
+ $input.trigger("onUnsetSelectValue").data("pre-val", "");
|
|
|
+ }
|
|
|
|
|
|
- inputbg = $input.css('backgroundColor').replace(/ /g, '').split(',', 3).join(',');
|
|
|
- // 自由输入的内容,设置背景色
|
|
|
- if (!~warnbg.indexOf(inputbg)) {
|
|
|
- $input.trigger('onUnsetSelectValue') // 触发取消data-id事件
|
|
|
- .data('pre-val', '')
|
|
|
- .css('background', warnbg);
|
|
|
- }
|
|
|
+ return $input;
|
|
|
+ }
|
|
|
|
|
|
- return $input;
|
|
|
+ inputbg = $input
|
|
|
+ .css("backgroundColor")
|
|
|
+ .replace(/ /g, "")
|
|
|
+ .split(",", 3)
|
|
|
+ .join(",");
|
|
|
+ // 自由输入的内容,设置背景色
|
|
|
+ if (!~warnbg.indexOf(inputbg)) {
|
|
|
+ $input
|
|
|
+ .trigger("onUnsetSelectValue") // 触发取消data-id事件
|
|
|
+ .data("pre-val", "")
|
|
|
+ .css("background", warnbg);
|
|
|
}
|
|
|
- /**
|
|
|
- * 调整滑动条
|
|
|
- */
|
|
|
- function adjustScroll($input, $dropdownMenu, options) {
|
|
|
- // 控制滑动条
|
|
|
- var $hover = $input.parent().find('tbody tr.' + options.listHoverCSS),
|
|
|
- pos, maxHeight;
|
|
|
-
|
|
|
- if ($hover.length) {
|
|
|
- pos = ($hover.index() + 3) * $hover.height();
|
|
|
- maxHeight = +$dropdownMenu.css('maxHeight').replace('px', '');
|
|
|
-
|
|
|
- if (pos > maxHeight || $dropdownMenu.scrollTop() > maxHeight) {
|
|
|
- pos = pos - maxHeight;
|
|
|
- } else {
|
|
|
- pos = 0;
|
|
|
- }
|
|
|
|
|
|
- $dropdownMenu.scrollTop(pos);
|
|
|
- }
|
|
|
+ return $input;
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 调整滑动条
|
|
|
+ */
|
|
|
+ function adjustScroll($input, $dropdownMenu, options) {
|
|
|
+ // 控制滑动条
|
|
|
+ var $hover = $input.parent().find("tbody tr." + options.listHoverCSS),
|
|
|
+ pos,
|
|
|
+ maxHeight;
|
|
|
+
|
|
|
+ if ($hover.length) {
|
|
|
+ pos = ($hover.index() + 3) * $hover.height();
|
|
|
+ maxHeight = +$dropdownMenu.css("maxHeight").replace("px", "");
|
|
|
+
|
|
|
+ if (pos > maxHeight || $dropdownMenu.scrollTop() > maxHeight) {
|
|
|
+ pos = pos - maxHeight;
|
|
|
+ } else {
|
|
|
+ pos = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ $dropdownMenu.scrollTop(pos);
|
|
|
}
|
|
|
- /**
|
|
|
- * 解除所有列表 hover 样式
|
|
|
- */
|
|
|
- function unHoverAll($dropdownMenu, options) {
|
|
|
- $dropdownMenu.find('tr.' + options.listHoverCSS).removeClass(options.listHoverCSS);
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 解除所有列表 hover 样式
|
|
|
+ */
|
|
|
+ function unHoverAll($dropdownMenu, options) {
|
|
|
+ $dropdownMenu
|
|
|
+ .find("tr." + options.listHoverCSS)
|
|
|
+ .removeClass(options.listHoverCSS);
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 验证 $input 对象是否符合条件
|
|
|
+ * 1. 必须为 bootstrap 下拉式菜单
|
|
|
+ * 2. 必须未初始化过
|
|
|
+ */
|
|
|
+ function checkInput($input, $dropdownMenu, options) {
|
|
|
+ if (
|
|
|
+ !$dropdownMenu.length || // 过滤非 bootstrap 下拉式菜单对象
|
|
|
+ $input.data(BSSUGGEST) // 是否已经初始化的检测
|
|
|
+ ) {
|
|
|
+ return FALSE;
|
|
|
}
|
|
|
- /**
|
|
|
- * 验证 $input 对象是否符合条件
|
|
|
- * 1. 必须为 bootstrap 下拉式菜单
|
|
|
- * 2. 必须未初始化过
|
|
|
- */
|
|
|
- function checkInput($input, $dropdownMenu, options) {
|
|
|
- if (
|
|
|
- !$dropdownMenu.length || // 过滤非 bootstrap 下拉式菜单对象
|
|
|
- $input.data(BSSUGGEST) // 是否已经初始化的检测
|
|
|
- ) {
|
|
|
- return FALSE;
|
|
|
- }
|
|
|
|
|
|
- $input.data(BSSUGGEST, {
|
|
|
- options: options
|
|
|
- });
|
|
|
-
|
|
|
- return TRUE;
|
|
|
+ $input.data(BSSUGGEST, {
|
|
|
+ options: options
|
|
|
+ });
|
|
|
+
|
|
|
+ return TRUE;
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 数据格式检测
|
|
|
+ * 检测 ajax 返回成功数据或 data 参数数据是否有效
|
|
|
+ * data 格式:{"value": [{}, {}...]}
|
|
|
+ */
|
|
|
+ function checkData(data) {
|
|
|
+ var isEmpty = TRUE,
|
|
|
+ o;
|
|
|
+
|
|
|
+ for (o in data) {
|
|
|
+ if (o === "value") {
|
|
|
+ isEmpty = FALSE;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (isEmpty) {
|
|
|
+ handleError("返回数据格式错误!");
|
|
|
+ return FALSE;
|
|
|
+ }
|
|
|
+ if (!data.value.length) {
|
|
|
+ // handleError('返回数据为空!');
|
|
|
+ return FALSE;
|
|
|
}
|
|
|
- /**
|
|
|
- * 数据格式检测
|
|
|
- * 检测 ajax 返回成功数据或 data 参数数据是否有效
|
|
|
- * data 格式:{"value": [{}, {}...]}
|
|
|
- */
|
|
|
- function checkData(data) {
|
|
|
- var isEmpty = TRUE, o;
|
|
|
-
|
|
|
- for (o in data) {
|
|
|
- if (o === 'value') {
|
|
|
- isEmpty = FALSE;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- if (isEmpty) {
|
|
|
- handleError('返回数据格式错误!');
|
|
|
- return FALSE;
|
|
|
- }
|
|
|
- if (!data.value.length) {
|
|
|
- // handleError('返回数据为空!');
|
|
|
- return FALSE;
|
|
|
- }
|
|
|
|
|
|
- return data;
|
|
|
+ return data;
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 判断字段名是否在 options.effectiveFields 配置项中
|
|
|
+ * @param {String} field 要判断的字段名
|
|
|
+ * @param {Object} options
|
|
|
+ * @return {Boolean} effectiveFields 为空时始终返回 true
|
|
|
+ */
|
|
|
+ function inEffectiveFields(field, options) {
|
|
|
+ var effectiveFields = options.effectiveFields;
|
|
|
+
|
|
|
+ return !(
|
|
|
+ field === "__index" ||
|
|
|
+ (effectiveFields.length && !~$.inArray(field, effectiveFields))
|
|
|
+ );
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 判断字段名是否在 options.searchFields 搜索字段配置中
|
|
|
+ */
|
|
|
+ function inSearchFields(field, options) {
|
|
|
+ return ~$.inArray(field, options.searchFields);
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 通过下拉菜单显示提示文案
|
|
|
+ */
|
|
|
+ function showTip(tip, $input, $dropdownMenu, options) {
|
|
|
+ $dropdownMenu
|
|
|
+ .html('<div style="padding:10px 5px 5px">' + tip + "</div>")
|
|
|
+ .show();
|
|
|
+ adjustDropMenuPos($input, $dropdownMenu, options);
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 显示下拉列表
|
|
|
+ */
|
|
|
+ function showDropMenu($input, options) {
|
|
|
+ var $dropdownMenu = $input.parent().find("ul:eq(0)");
|
|
|
+ if (!$dropdownMenu.is(":visible")) {
|
|
|
+ // $dropdownMenu.css('display', 'block');
|
|
|
+ $dropdownMenu.show();
|
|
|
+ $input.trigger("onShowDropdown", [options ? options.data.value : []]);
|
|
|
}
|
|
|
- /**
|
|
|
- * 判断字段名是否在 options.effectiveFields 配置项中
|
|
|
- * @param {String} field 要判断的字段名
|
|
|
- * @param {Object} options
|
|
|
- * @return {Boolean} effectiveFields 为空时始终返回 true
|
|
|
- */
|
|
|
- function inEffectiveFields(field, options) {
|
|
|
- var effectiveFields = options.effectiveFields;
|
|
|
-
|
|
|
- return !(field === '__index' ||
|
|
|
- effectiveFields.length &&
|
|
|
- !~$.inArray(field, effectiveFields));
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 隐藏下拉列表
|
|
|
+ */
|
|
|
+ function hideDropMenu($input, options) {
|
|
|
+ var $dropdownMenu = $input.parent().find("ul:eq(0)");
|
|
|
+ if ($dropdownMenu.is(":visible")) {
|
|
|
+ // $dropdownMenu.css('display', '');
|
|
|
+ $dropdownMenu.hide();
|
|
|
+ $input.trigger("onHideDropdown", [options ? options.data.value : []]);
|
|
|
}
|
|
|
- /**
|
|
|
- * 判断字段名是否在 options.searchFields 搜索字段配置中
|
|
|
- */
|
|
|
- function inSearchFields(field, options) {
|
|
|
- return ~$.inArray(field, options.searchFields);
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 下拉列表刷新
|
|
|
+ * 作为 fnGetData 的 callback 函数调用
|
|
|
+ */
|
|
|
+ function refreshDropMenu($input, data, options) {
|
|
|
+ var $dropdownMenu = $input.parent().find("ul:eq(0)"),
|
|
|
+ len,
|
|
|
+ i,
|
|
|
+ field,
|
|
|
+ index = 0,
|
|
|
+ tds,
|
|
|
+ html = [
|
|
|
+ '<table class="table table-condensed table-sm" style="margin:0">'
|
|
|
+ ],
|
|
|
+ idValue,
|
|
|
+ keyValue; // 作为输入框 data-id 和内容的字段值
|
|
|
+ var dataList = data.value;
|
|
|
+
|
|
|
+ if (!data || !(len = dataList.length)) {
|
|
|
+ if (options.emptyTip) {
|
|
|
+ showTip(options.emptyTip, $input, $dropdownMenu, options);
|
|
|
+ } else {
|
|
|
+ $dropdownMenu.empty();
|
|
|
+ hideDropMenu($input, options);
|
|
|
+ }
|
|
|
+ return $input;
|
|
|
}
|
|
|
- /**
|
|
|
- * 通过下拉菜单显示提示文案
|
|
|
- */
|
|
|
- function showTip(tip, $input, $dropdownMenu, options) {
|
|
|
- $dropdownMenu.html('<div style="padding:10px 5px 5px">' + tip + '</div>').show();
|
|
|
- adjustDropMenuPos($input, $dropdownMenu, options);
|
|
|
+
|
|
|
+ // 相同数据,不用继续渲染了
|
|
|
+ if (
|
|
|
+ options._lastData &&
|
|
|
+ JSON.stringify(options._lastData) === JSON.stringify(data) &&
|
|
|
+ $dropdownMenu.find("tr").length === len
|
|
|
+ ) {
|
|
|
+ showDropMenu($input, options);
|
|
|
+ return adjustDropMenuPos($input, $dropdownMenu, options);
|
|
|
}
|
|
|
- /**
|
|
|
- * 显示下拉列表
|
|
|
- */
|
|
|
- function showDropMenu($input, options) {
|
|
|
- var $dropdownMenu = $input.parent().find('ul:eq(0)');
|
|
|
- if (!$dropdownMenu.is(':visible')) {
|
|
|
- // $dropdownMenu.css('display', 'block');
|
|
|
- $dropdownMenu.show();
|
|
|
- $input.trigger('onShowDropdown', [options ? options.data.value : []]);
|
|
|
- }
|
|
|
+ options._lastData = data;
|
|
|
+
|
|
|
+ /** 显示于列表中的字段 */
|
|
|
+ var columns = options.effectiveFields.length
|
|
|
+ ? options.effectiveFields
|
|
|
+ : $.map(dataList[0], function(val, key) {
|
|
|
+ return key;
|
|
|
+ });
|
|
|
+
|
|
|
+ // 生成表头
|
|
|
+ if (options.showHeader) {
|
|
|
+ html.push("<thead><tr>");
|
|
|
+ $.each(columns, function(index, field) {
|
|
|
+ if (!inEffectiveFields(field, options)) return;
|
|
|
+
|
|
|
+ html.push(
|
|
|
+ "<th>",
|
|
|
+ options.effectiveFieldsAlias[field] || field,
|
|
|
+ index === 0 ? "(" + len + ")" : "", // 表头第一列记录总数
|
|
|
+ "</th>"
|
|
|
+ );
|
|
|
+
|
|
|
+ index++;
|
|
|
+ });
|
|
|
+ html.push("</tr></thead>");
|
|
|
}
|
|
|
- /**
|
|
|
- * 隐藏下拉列表
|
|
|
- */
|
|
|
- function hideDropMenu($input, options) {
|
|
|
- var $dropdownMenu = $input.parent().find('ul:eq(0)');
|
|
|
- if ($dropdownMenu.is(':visible')) {
|
|
|
- // $dropdownMenu.css('display', '');
|
|
|
- $dropdownMenu.hide();
|
|
|
- $input.trigger('onHideDropdown', [options ? options.data.value : []]);
|
|
|
+ html.push("<tbody>");
|
|
|
+
|
|
|
+ // console.log(data, len);
|
|
|
+ // 按列加数据
|
|
|
+ var dataI;
|
|
|
+ var maxOptionCount = Math.min(options.maxOptionCount, len);
|
|
|
+ for (i = 0; i < maxOptionCount; i++) {
|
|
|
+ index = 0;
|
|
|
+ tds = [];
|
|
|
+ dataI = dataList[i];
|
|
|
+ idValue = dataI[options.idField];
|
|
|
+ keyValue = dataI[options.keyField];
|
|
|
+
|
|
|
+ for (field in dataI) {
|
|
|
+ // 标记作为 value 和 作为 id 的值
|
|
|
+ if (isUndefined(keyValue) && options.indexKey === index) {
|
|
|
+ keyValue = dataI[field];
|
|
|
}
|
|
|
- }
|
|
|
- /**
|
|
|
- * 下拉列表刷新
|
|
|
- * 作为 fnGetData 的 callback 函数调用
|
|
|
- */
|
|
|
- function refreshDropMenu($input, data, options) {
|
|
|
- var $dropdownMenu = $input.parent().find('ul:eq(0)'),
|
|
|
- len, i, field, index = 0,
|
|
|
- tds,
|
|
|
- html = ['<table class="table table-condensed table-sm" style="margin:0">'],
|
|
|
- idValue, keyValue; // 作为输入框 data-id 和内容的字段值
|
|
|
- var dataList = data.value;
|
|
|
-
|
|
|
- if (!data || !(len = dataList.length)) {
|
|
|
- if (options.emptyTip) {
|
|
|
- showTip(options.emptyTip, $input, $dropdownMenu, options);
|
|
|
- } else {
|
|
|
- $dropdownMenu.empty();
|
|
|
- hideDropMenu($input, options);
|
|
|
- }
|
|
|
- return $input;
|
|
|
+ if (isUndefined(idValue) && options.indexId === index) {
|
|
|
+ idValue = dataI[field];
|
|
|
}
|
|
|
+ index++;
|
|
|
+ }
|
|
|
|
|
|
- // 相同数据,不用继续渲染了
|
|
|
- if (
|
|
|
- options._lastData &&
|
|
|
- JSON.stringify(options._lastData) === JSON.stringify(data) &&
|
|
|
- $dropdownMenu.find('tr').length === len
|
|
|
- ) {
|
|
|
- showDropMenu($input, options);
|
|
|
- return adjustDropMenuPos($input, $dropdownMenu, options);
|
|
|
+ $.each(columns, function(index, field) {
|
|
|
+ // 列表中只显示有效的字段
|
|
|
+ if (inEffectiveFields(field, options)) {
|
|
|
+ tds.push('<td data-name="', field, '">', dataI[field], "</td>");
|
|
|
}
|
|
|
- options._lastData = data;
|
|
|
-
|
|
|
- // 生成表头
|
|
|
- if (options.showHeader) {
|
|
|
- html.push('<thead><tr>');
|
|
|
- for (field in dataList[0]) {
|
|
|
- if (!inEffectiveFields(field, options)) {
|
|
|
- continue;
|
|
|
- }
|
|
|
+ });
|
|
|
+
|
|
|
+ html.push(
|
|
|
+ '<tr data-index="',
|
|
|
+ dataI.__index || i,
|
|
|
+ '" data-id="',
|
|
|
+ idValue,
|
|
|
+ '" data-key="',
|
|
|
+ keyValue,
|
|
|
+ '">',
|
|
|
+ tds.join(""),
|
|
|
+ "</tr>"
|
|
|
+ );
|
|
|
+ }
|
|
|
+ html.push("</tbody></table>");
|
|
|
+
|
|
|
+ $dropdownMenu.html(html.join(""));
|
|
|
+ showDropMenu($input, options);
|
|
|
+ //.show();
|
|
|
+
|
|
|
+ // scrollbar 存在时,延时到动画结束时调整 padding
|
|
|
+ setTimeout(function() {
|
|
|
+ if (notNeedCalcPadding) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ var $table = $dropdownMenu.find("table:eq(0)"),
|
|
|
+ pdr = 0,
|
|
|
+ mgb = 0;
|
|
|
+
|
|
|
+ if (
|
|
|
+ $dropdownMenu.height() < $table.height() &&
|
|
|
+ +$dropdownMenu.css("minWidth").replace("px", "") < $dropdownMenu.width()
|
|
|
+ ) {
|
|
|
+ pdr = 18;
|
|
|
+ mgb = 20;
|
|
|
+ }
|
|
|
+
|
|
|
+ $dropdownMenu.css("paddingRight", pdr);
|
|
|
+ $table.css("marginBottom", mgb);
|
|
|
+ }, 301);
|
|
|
+
|
|
|
+ adjustDropMenuPos($input, $dropdownMenu, options);
|
|
|
+
|
|
|
+ return $input;
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * ajax 获取数据
|
|
|
+ * @param {Object} options
|
|
|
+ * @return {Object} $.Deferred
|
|
|
+ */
|
|
|
+ function ajax(options, keyword) {
|
|
|
+ keyword = keyword || "";
|
|
|
+
|
|
|
+ var preAjax = options._preAjax;
|
|
|
+
|
|
|
+ if (preAjax && preAjax.abort && preAjax.readyState !== 4) {
|
|
|
+ // console.log('abort pre ajax');
|
|
|
+ preAjax.abort();
|
|
|
+ }
|
|
|
|
|
|
- html.push('<th>', (options.effectiveFieldsAlias[field] || field),
|
|
|
- index === 0 ? ('(' + len + ')') : '' , // 表头第一列记录总数
|
|
|
- '</th>');
|
|
|
+ var ajaxParam = {
|
|
|
+ type: "GET",
|
|
|
+ dataType: options.jsonp ? "jsonp" : "json",
|
|
|
+ timeout: 5000
|
|
|
+ };
|
|
|
|
|
|
- index++;
|
|
|
- }
|
|
|
- html.push('</tr></thead>');
|
|
|
- }
|
|
|
- html.push('<tbody>');
|
|
|
-
|
|
|
- // console.log(data, len);
|
|
|
- // 按列加数据
|
|
|
- var dataI;
|
|
|
- for (i = 0; i < len; i++) {
|
|
|
- index = 0;
|
|
|
- tds = [];
|
|
|
- dataI = dataList[i];
|
|
|
- idValue = dataI[options.idField];
|
|
|
- keyValue = dataI[options.keyField];
|
|
|
-
|
|
|
- for (field in dataI) {
|
|
|
- // 标记作为 value 和 作为 id 的值
|
|
|
- if (isUndefined(keyValue) && options.indexKey === index) {
|
|
|
- keyValue = dataI[field];
|
|
|
- }
|
|
|
- if (isUndefined(idValue) && options.indexId === index) {
|
|
|
- idValue = dataI[field];
|
|
|
- }
|
|
|
+ // jsonp
|
|
|
+ if (options.jsonp) {
|
|
|
+ ajaxParam.jsonp = options.jsonp;
|
|
|
+ }
|
|
|
|
|
|
- index++;
|
|
|
+ // 自定义 ajax 请求参数生成方法
|
|
|
+ var adjustAjaxParam,
|
|
|
+ fnAdjustAjaxParam = options.fnAdjustAjaxParam;
|
|
|
|
|
|
- // 列表中只显示有效的字段
|
|
|
- if (inEffectiveFields(field, options)) {
|
|
|
- tds.push('<td data-name="', field, '">', dataI[field], '</td>');
|
|
|
- }
|
|
|
- }
|
|
|
+ if ($.isFunction(fnAdjustAjaxParam)) {
|
|
|
+ adjustAjaxParam = fnAdjustAjaxParam(keyword, options);
|
|
|
|
|
|
- html.push('<tr data-index="', (dataI.__index || i),
|
|
|
- '" data-id="', idValue,
|
|
|
- '" data-key="', keyValue, '">',
|
|
|
- tds.join(''), '</tr>');
|
|
|
- }
|
|
|
- html.push('</tbody></table>');
|
|
|
+ // options.fnAdjustAjaxParam 返回false,则终止 ajax 请求
|
|
|
+ if (FALSE === adjustAjaxParam) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- $dropdownMenu.html(html.join(''));
|
|
|
- showDropMenu($input, options);
|
|
|
- //.show();
|
|
|
+ $.extend(ajaxParam, adjustAjaxParam);
|
|
|
+ }
|
|
|
|
|
|
- // scrollbar 存在时,延时到动画结束时调整 padding
|
|
|
- setTimeout(function() {
|
|
|
- if (notNeedCalcPadding) {
|
|
|
- return;
|
|
|
- }
|
|
|
+ // url 调整
|
|
|
+ ajaxParam.url = (function() {
|
|
|
+ if (!keyword || ajaxParam.data) {
|
|
|
+ return ajaxParam.url || options.url;
|
|
|
+ }
|
|
|
+
|
|
|
+ var type = "?";
|
|
|
+ if (/=$/.test(options.url)) {
|
|
|
+ type = "";
|
|
|
+ } else if (/\?/.test(options.url)) {
|
|
|
+ type = "&";
|
|
|
+ }
|
|
|
+
|
|
|
+ return options.url + type + encodeURIComponent(keyword);
|
|
|
+ })();
|
|
|
+
|
|
|
+ return (options._preAjax = $.ajax(ajaxParam)
|
|
|
+ .done(function(result) {
|
|
|
+ options.data = options.fnProcessData(result);
|
|
|
+ })
|
|
|
+ .fail(function(err) {
|
|
|
+ if (options.fnAjaxFail) {
|
|
|
+ options.fnAjaxFail(err, options);
|
|
|
+ }
|
|
|
+ }));
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 检测 keyword 与 value 是否存在互相包含
|
|
|
+ * @param {String} keyword 用户输入的关键字
|
|
|
+ * @param {String} key 匹配字段的 key
|
|
|
+ * @param {String} value key 字段对应的值
|
|
|
+ * @param {Object} options
|
|
|
+ * @return {Boolean} 包含/不包含
|
|
|
+ */
|
|
|
+ function isInWord(keyword, key, value, options) {
|
|
|
+ value = $.trim(value);
|
|
|
+
|
|
|
+ if (options.ignorecase) {
|
|
|
+ keyword = keyword.toLocaleLowerCase();
|
|
|
+ value = value.toLocaleLowerCase();
|
|
|
+ }
|
|
|
|
|
|
- var $table = $dropdownMenu.find('table:eq(0)'),
|
|
|
- pdr = 0,
|
|
|
- mgb = 0;
|
|
|
+ return (
|
|
|
+ value &&
|
|
|
+ (inEffectiveFields(key, options) || inSearchFields(key, options)) && // 必须在有效的搜索字段中
|
|
|
+ (~value.indexOf(keyword) || // 匹配值包含关键字
|
|
|
+ (options.twoWayMatch && ~keyword.indexOf(value))) // 关键字包含匹配值
|
|
|
+ );
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 通过 ajax 或 json 参数获取数据
|
|
|
+ */
|
|
|
+ function getData(keyword, $input, callback, options) {
|
|
|
+ var data,
|
|
|
+ validData,
|
|
|
+ filterData = {
|
|
|
+ value: []
|
|
|
+ },
|
|
|
+ i,
|
|
|
+ key,
|
|
|
+ len,
|
|
|
+ fnPreprocessKeyword = options.fnPreprocessKeyword;
|
|
|
+
|
|
|
+ keyword = keyword || "";
|
|
|
+ // 获取数据前对关键字预处理方法
|
|
|
+ if ($.isFunction(fnPreprocessKeyword)) {
|
|
|
+ keyword = fnPreprocessKeyword(keyword, options);
|
|
|
+ }
|
|
|
|
|
|
- if (
|
|
|
- $dropdownMenu.height() < $table.height() &&
|
|
|
- +$dropdownMenu.css('minWidth').replace('px', '') < $dropdownMenu.width()
|
|
|
- ) {
|
|
|
- pdr = 18;
|
|
|
- mgb = 20;
|
|
|
+ // 给了url参数,则从服务器 ajax 请求
|
|
|
+ // console.log(options.url + keyword);
|
|
|
+ if (options.url) {
|
|
|
+ var timer;
|
|
|
+ if (options.searchingTip) {
|
|
|
+ timer = setTimeout(function() {
|
|
|
+ showTip(
|
|
|
+ options.searchingTip,
|
|
|
+ $input,
|
|
|
+ $input.parent().find("ul"),
|
|
|
+ options
|
|
|
+ );
|
|
|
+ }, 600);
|
|
|
+ }
|
|
|
+
|
|
|
+ ajax(options, keyword)
|
|
|
+ .done(function(result) {
|
|
|
+ callback($input, options.data, options); // 为 refreshDropMenu
|
|
|
+ $input.trigger(onDataRequestSuccess, result);
|
|
|
+ if (options.getDataMethod === "firstByUrl") {
|
|
|
+ options.url = null;
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .always(function() {
|
|
|
+ timer && clearTimeout(timer);
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ // 没有给出 url 参数,则从 data 参数获取
|
|
|
+ data = options.data;
|
|
|
+ validData = checkData(data);
|
|
|
+ // 本地的 data 数据,则在本地过滤
|
|
|
+ if (validData) {
|
|
|
+ if (keyword) {
|
|
|
+ // 输入不为空时则进行匹配
|
|
|
+ len = data.value.length;
|
|
|
+ for (i = 0; i < len; i++) {
|
|
|
+ for (key in data.value[i]) {
|
|
|
+ if (
|
|
|
+ data.value[i][key] &&
|
|
|
+ isInWord(keyword, key, data.value[i][key] + "", options)
|
|
|
+ ) {
|
|
|
+ filterData.value.push(data.value[i]);
|
|
|
+ filterData.value[filterData.value.length - 1].__index = i;
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
- $dropdownMenu.css('paddingRight', pdr);
|
|
|
- $table.css('marginBottom', mgb);
|
|
|
- }, 301);
|
|
|
-
|
|
|
- adjustDropMenuPos($input, $dropdownMenu, options);
|
|
|
-
|
|
|
- return $input;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ filterData = data;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ callback($input, filterData, options);
|
|
|
+ } // else
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 数据处理
|
|
|
+ * url 获取数据时,对数据的处理,作为 fnGetData 之后的回调处理
|
|
|
+ */
|
|
|
+ function processData(data) {
|
|
|
+ return checkData(data);
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 取得 clearable 清除按钮
|
|
|
+ */
|
|
|
+ function getIClear($input, options) {
|
|
|
+ var $iClear = $input.prev("i.clearable");
|
|
|
+
|
|
|
+ // 是否可清除已输入的内容(添加清除按钮)
|
|
|
+ if (options.clearable && !$iClear.length) {
|
|
|
+ $iClear = $(
|
|
|
+ '<i class="clearable glyphicon glyphicon-remove fa fa-plus"></i>'
|
|
|
+ ).prependTo($input.parent());
|
|
|
}
|
|
|
- /**
|
|
|
- * ajax 获取数据
|
|
|
- * @param {Object} options
|
|
|
- * @return {Object} $.Deferred
|
|
|
- */
|
|
|
- function ajax(options, keyword) {
|
|
|
- keyword = keyword || '';
|
|
|
-
|
|
|
- var preAjax = options._preAjax;
|
|
|
-
|
|
|
- if (preAjax && preAjax.abort && preAjax.readyState !== 4) {
|
|
|
- // console.log('abort pre ajax');
|
|
|
- preAjax.abort();
|
|
|
+
|
|
|
+ return $iClear
|
|
|
+ .css({
|
|
|
+ position: "absolute",
|
|
|
+ top: "calc(50% - 6px)",
|
|
|
+ transform: "rotate(45deg)",
|
|
|
+ // right: options.showBtn ? Math.max($input.next('.input-group-btn').width(), 33) + 2 : 12,
|
|
|
+ zIndex: 4,
|
|
|
+ cursor: "pointer",
|
|
|
+ width: "14px",
|
|
|
+ lineHeight: "14px",
|
|
|
+ textAlign: "center",
|
|
|
+ fontSize: 12
|
|
|
+ })
|
|
|
+ .hide();
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 默认的配置选项
|
|
|
+ * @type {Object}
|
|
|
+ */
|
|
|
+ var defaultOptions = {
|
|
|
+ url: null, // 请求数据的 URL 地址
|
|
|
+ jsonp: null, // 设置此参数名,将开启jsonp功能,否则使用json数据结构
|
|
|
+ data: {
|
|
|
+ value: []
|
|
|
+ }, // 提示所用的数据,注意格式
|
|
|
+ indexId: 0, // 每组数据的第几个数据,作为input输入框的 data-id,设为 -1 且 idField 为空则不设置此值
|
|
|
+ indexKey: 0, // 每组数据的第几个数据,作为input输入框的内容
|
|
|
+ idField: "", // 每组数据的哪个字段作为 data-id,优先级高于 indexId 设置(推荐)
|
|
|
+ keyField: "", // 每组数据的哪个字段作为输入框内容,优先级高于 indexKey 设置(推荐)
|
|
|
+
|
|
|
+ /* 搜索相关 */
|
|
|
+ autoSelect: TRUE, // 键盘向上/下方向键时,是否自动选择值
|
|
|
+ allowNoKeyword: TRUE, // 是否允许无关键字时请求数据
|
|
|
+ getDataMethod: "firstByUrl", // 获取数据的方式,url:一直从url请求;data:从 options.data 获取;firstByUrl:第一次从Url获取全部数据,之后从options.data获取
|
|
|
+ delayUntilKeyup: FALSE, // 获取数据的方式 为 firstByUrl 时,是否延迟到有输入时才请求数据
|
|
|
+ ignorecase: FALSE, // 前端搜索匹配时,是否忽略大小写
|
|
|
+ effectiveFields: [], // 有效显示于列表中的字段,非有效字段都会过滤,默认全部有效。
|
|
|
+ effectiveFieldsAlias: {}, // 有效字段的别名对象,用于 header 的显示
|
|
|
+ searchFields: [], // 有效搜索字段,从前端搜索过滤数据时使用,但不一定显示在列表中。effectiveFields 配置字段也会用于搜索过滤
|
|
|
+ twoWayMatch: TRUE, // 是否双向匹配搜索。为 true 即输入关键字包含或包含于匹配字段均认为匹配成功,为 false 则输入关键字包含于匹配字段认为匹配成功
|
|
|
+ multiWord: FALSE, // 以分隔符号分割的多关键字支持
|
|
|
+ separator: ",", // 多关键字支持时的分隔符,默认为半角逗号
|
|
|
+ delay: 300, // 搜索触发的延时时间间隔,单位毫秒
|
|
|
+ emptyTip: "", // 查询为空时显示的内容,可为 html
|
|
|
+ searchingTip: "搜索中...", // ajax 搜索时显示的提示内容,当搜索时间较长时给出正在搜索的提示
|
|
|
+ hideOnSelect: FALSE, // 鼠标从列表单击选择了值时,是否隐藏选择列表
|
|
|
+ maxOptionCount: 200, // 选择列表最多显示的可选项数量,默认为 200
|
|
|
+
|
|
|
+ /* UI */
|
|
|
+ autoDropup: FALSE, // 选择菜单是否自动判断向上展开。设为 true,则当下拉菜单高度超过窗体,且向上方向不会被窗体覆盖,则选择菜单向上弹出
|
|
|
+ autoMinWidth: FALSE, // 是否自动最小宽度,设为 false 则最小宽度不小于输入框宽度
|
|
|
+ showHeader: FALSE, // 是否显示选择列表的 header。为 true 时,有效字段大于一列则显示表头
|
|
|
+ showBtn: TRUE, // 是否显示下拉按钮
|
|
|
+ inputBgColor: "", // 输入框背景色,当与容器背景色不同时,可能需要该项的配置
|
|
|
+ inputWarnColor: "rgba(255,0,0,.1)", // 输入框内容不是下拉列表选择时的警告色
|
|
|
+ listStyle: {
|
|
|
+ "padding-top": 0,
|
|
|
+ "max-height": "375px",
|
|
|
+ "max-width": "800px",
|
|
|
+ overflow: "auto",
|
|
|
+ width: "auto",
|
|
|
+ transition: "0.3s",
|
|
|
+ "-webkit-transition": "0.3s",
|
|
|
+ "-moz-transition": "0.3s",
|
|
|
+ "-o-transition": "0.3s",
|
|
|
+ "word-break": "keep-all",
|
|
|
+ "white-space": "nowrap"
|
|
|
+ }, // 列表的样式控制
|
|
|
+ listAlign: "left", // 提示列表对齐位置,left/right/auto
|
|
|
+ listHoverStyle: "background: #07d; color:#fff", // 提示框列表鼠标悬浮的样式
|
|
|
+ listHoverCSS: "jhover", // 提示框列表鼠标悬浮的样式名称
|
|
|
+ clearable: FALSE, // 是否可清除已输入的内容
|
|
|
+
|
|
|
+ /* key */
|
|
|
+ keyLeft: 37, // 向左方向键,不同的操作系统可能会有差别,则自行定义
|
|
|
+ keyUp: 38, // 向上方向键
|
|
|
+ keyRight: 39, // 向右方向键
|
|
|
+ keyDown: 40, // 向下方向键
|
|
|
+ keyEnter: 13, // 回车键
|
|
|
+
|
|
|
+ /* methods */
|
|
|
+ fnProcessData: processData, // 格式化数据的方法,返回数据格式参考 data 参数
|
|
|
+ fnGetData: getData, // 获取数据的方法,无特殊需求一般不作设置
|
|
|
+ fnAdjustAjaxParam: null, // 调整 ajax 请求参数方法,用于更多的请求配置需求。如对请求关键字作进一步处理、修改超时时间等
|
|
|
+ fnPreprocessKeyword: null, // 搜索过滤数据前,对输入关键字作进一步处理方法。注意,应返回字符串
|
|
|
+ fnAjaxFail: null // ajax 失败时回调方法
|
|
|
+ };
|
|
|
+
|
|
|
+ var methods = {
|
|
|
+ init: function(options) {
|
|
|
+ // 参数设置
|
|
|
+ var self = this;
|
|
|
+ options = options || {};
|
|
|
+
|
|
|
+ // 默认配置有效显示字段多于一个,则显示列表表头,否则不显示
|
|
|
+ if (
|
|
|
+ isUndefined(options.showHeader) &&
|
|
|
+ options.effectiveFields &&
|
|
|
+ options.effectiveFields.length > 1
|
|
|
+ ) {
|
|
|
+ options.showHeader = TRUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ options = $.extend(TRUE, {}, defaultOptions, options);
|
|
|
+
|
|
|
+ // 旧的方法兼容
|
|
|
+ if (options.processData) {
|
|
|
+ options.fnProcessData = options.processData;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (options.getData) {
|
|
|
+ options.fnGetData = options.getData;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (
|
|
|
+ options.getDataMethod === "firstByUrl" &&
|
|
|
+ options.url &&
|
|
|
+ !options.delayUntilKeyup
|
|
|
+ ) {
|
|
|
+ ajax(options).done(function(result) {
|
|
|
+ options.url = null;
|
|
|
+ self.trigger(onDataRequestSuccess, result);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 鼠标滑动到条目样式
|
|
|
+ if (!$("#" + BSSUGGEST).length) {
|
|
|
+ $("head:eq(0)").append(
|
|
|
+ '<style id="' +
|
|
|
+ BSSUGGEST +
|
|
|
+ '">.' +
|
|
|
+ options.listHoverCSS +
|
|
|
+ "{" +
|
|
|
+ options.listHoverStyle +
|
|
|
+ "}</style>"
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ return self.each(function() {
|
|
|
+ var $input = $(this),
|
|
|
+ $parent = $input.parent(),
|
|
|
+ $iClear = getIClear($input, options),
|
|
|
+ isMouseenterMenu,
|
|
|
+ keyupTimer, // keyup 与 input 事件延时定时器
|
|
|
+ $dropdownMenu = $parent.find("ul:eq(0)");
|
|
|
+
|
|
|
+ // 兼容 bs4
|
|
|
+ $dropdownMenu.parent().css("position", "relative");
|
|
|
+
|
|
|
+ // 验证输入框对象是否符合条件
|
|
|
+ if (!checkInput($input, $dropdownMenu, options)) {
|
|
|
+ console.warn(
|
|
|
+ "不是一个标准的 bootstrap 下拉式菜单或已初始化:",
|
|
|
+ $input
|
|
|
+ );
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- var ajaxParam = {
|
|
|
- type: 'GET',
|
|
|
- dataType: options.jsonp ? 'jsonp' : 'json',
|
|
|
- timeout: 5000,
|
|
|
- };
|
|
|
+ // 是否显示 button 按钮
|
|
|
+ if (!options.showBtn) {
|
|
|
+ $input.css("borderRadius", 4);
|
|
|
+ $parent
|
|
|
+ .css("width", "100%")
|
|
|
+ .find(".btn:eq(0)")
|
|
|
+ .hide();
|
|
|
+ }
|
|
|
|
|
|
- // jsonp
|
|
|
- if (options.jsonp) {
|
|
|
- ajaxParam.jsonp = options.jsonp;
|
|
|
+ // 移除 disabled 类,并禁用自动完成
|
|
|
+ $input
|
|
|
+ .removeClass(DISABLED)
|
|
|
+ .prop(DISABLED, FALSE)
|
|
|
+ .attr("autocomplete", "off");
|
|
|
+ // dropdown-menu 增加修饰
|
|
|
+ $dropdownMenu.css(options.listStyle);
|
|
|
+
|
|
|
+ // 默认背景色
|
|
|
+ if (!options.inputBgColor) {
|
|
|
+ options.inputBgColor = $input.css("backgroundColor");
|
|
|
}
|
|
|
|
|
|
- // 自定义 ajax 请求参数生成方法
|
|
|
- var adjustAjaxParam,
|
|
|
- fnAdjustAjaxParam = options.fnAdjustAjaxParam;
|
|
|
+ // 开始事件处理
|
|
|
+ $input
|
|
|
+ .on("keydown.bs", function(event) {
|
|
|
+ var currentList, tipsKeyword; // 提示列表上被选中的关键字
|
|
|
|
|
|
- if ($.isFunction(fnAdjustAjaxParam)) {
|
|
|
- adjustAjaxParam = fnAdjustAjaxParam(keyword, options);
|
|
|
+ // 当提示层显示时才对键盘事件处理
|
|
|
+ if (!$dropdownMenu.is(":visible")) {
|
|
|
+ setOrGetDataId($input, "");
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- // options.fnAdjustAjaxParam 返回false,则终止 ajax 请求
|
|
|
- if (FALSE === adjustAjaxParam) {
|
|
|
+ currentList = $dropdownMenu.find("." + options.listHoverCSS);
|
|
|
+ tipsKeyword = ""; // 提示列表上被选中的关键字
|
|
|
+
|
|
|
+ unHoverAll($dropdownMenu, options);
|
|
|
+
|
|
|
+ if (event.keyCode === options.keyDown) {
|
|
|
+ // 如果按的是向下方向键
|
|
|
+ if (!currentList.length) {
|
|
|
+ // 如果提示列表没有一个被选中,则将列表第一个选中
|
|
|
+ tipsKeyword = getPointKeyword(
|
|
|
+ $dropdownMenu.find("tbody tr:first").mouseover()
|
|
|
+ );
|
|
|
+ } else if (!currentList.next().length) {
|
|
|
+ // 如果是最后一个被选中,则取消选中,即可认为是输入框被选中,并恢复输入的值
|
|
|
+ if (options.autoSelect) {
|
|
|
+ setOrGetDataId($input, "").val(setOrGetAlt($input));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 选中下一行
|
|
|
+ tipsKeyword = getPointKeyword(currentList.next().mouseover());
|
|
|
+ }
|
|
|
+ // 控制滑动条
|
|
|
+ adjustScroll($input, $dropdownMenu, options);
|
|
|
+
|
|
|
+ if (!options.autoSelect) {
|
|
|
return;
|
|
|
- }
|
|
|
+ }
|
|
|
+ } else if (event.keyCode === options.keyUp) {
|
|
|
+ // 如果按的是向上方向键
|
|
|
+ if (!currentList.length) {
|
|
|
+ tipsKeyword = getPointKeyword(
|
|
|
+ $dropdownMenu.find("tbody tr:last").mouseover()
|
|
|
+ );
|
|
|
+ } else if (!currentList.prev().length) {
|
|
|
+ if (options.autoSelect) {
|
|
|
+ setOrGetDataId($input, "").val(setOrGetAlt($input));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 选中前一行
|
|
|
+ tipsKeyword = getPointKeyword(currentList.prev().mouseover());
|
|
|
+ }
|
|
|
|
|
|
- $.extend(ajaxParam, adjustAjaxParam);
|
|
|
- }
|
|
|
+ // 控制滑动条
|
|
|
+ adjustScroll($input, $dropdownMenu, options);
|
|
|
|
|
|
- // url 调整
|
|
|
- ajaxParam.url = function() {
|
|
|
- if (!keyword || ajaxParam.data) {
|
|
|
- return ajaxParam.url || options.url;
|
|
|
+ if (!options.autoSelect) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ } else if (event.keyCode === options.keyEnter) {
|
|
|
+ tipsKeyword = getPointKeyword(currentList);
|
|
|
+ hideDropMenu($input, options);
|
|
|
+ } else {
|
|
|
+ setOrGetDataId($input, "");
|
|
|
}
|
|
|
|
|
|
- var type = '?';
|
|
|
- if (/=$/.test(options.url)) {
|
|
|
- type = '';
|
|
|
- } else if (/\?/.test(options.url)) {
|
|
|
- type = '&';
|
|
|
+ // 设置值 tipsKeyword
|
|
|
+ // console.log(tipsKeyword);
|
|
|
+ setValue($input, tipsKeyword, options);
|
|
|
+ })
|
|
|
+ .on("compositionstart.bs", function(event) {
|
|
|
+ // 中文输入开始,锁定
|
|
|
+ // console.log('compositionstart');
|
|
|
+ inputLock = TRUE;
|
|
|
+ })
|
|
|
+ .on("compositionend.bs", function(event) {
|
|
|
+ // 中文输入结束,解除锁定
|
|
|
+ // console.log('compositionend');
|
|
|
+ inputLock = FALSE;
|
|
|
+ })
|
|
|
+ .on("keyup.bs input.bs paste.bs", function(event) {
|
|
|
+ var word;
|
|
|
+
|
|
|
+ if (event.keyCode) {
|
|
|
+ setBackground($input, options);
|
|
|
}
|
|
|
|
|
|
- return options.url + type + encodeURIComponent(keyword);
|
|
|
- }();
|
|
|
-
|
|
|
- return options._preAjax = $.ajax(ajaxParam).done(function(result) {
|
|
|
- options.data = options.fnProcessData(result);
|
|
|
- }).fail(function(err) {
|
|
|
- if (options.fnAjaxFail) {
|
|
|
- options.fnAjaxFail(err, options);
|
|
|
+ // 如果弹起的键是回车、向上或向下方向键则返回
|
|
|
+ if (
|
|
|
+ ~$.inArray(event.keyCode, [
|
|
|
+ options.keyDown,
|
|
|
+ options.keyUp,
|
|
|
+ options.keyEnter
|
|
|
+ ])
|
|
|
+ ) {
|
|
|
+ $input.val($input.val()); // 让鼠标输入跳到最后
|
|
|
+ return;
|
|
|
}
|
|
|
- });
|
|
|
- }
|
|
|
- /**
|
|
|
- * 检测 keyword 与 value 是否存在互相包含
|
|
|
- * @param {String} keyword 用户输入的关键字
|
|
|
- * @param {String} key 匹配字段的 key
|
|
|
- * @param {String} value key 字段对应的值
|
|
|
- * @param {Object} options
|
|
|
- * @return {Boolean} 包含/不包含
|
|
|
- */
|
|
|
- function isInWord(keyword, key, value, options) {
|
|
|
- value = $.trim(value);
|
|
|
-
|
|
|
- if (options.ignorecase) {
|
|
|
- keyword = keyword.toLocaleLowerCase();
|
|
|
- value = value.toLocaleLowerCase();
|
|
|
- }
|
|
|
|
|
|
- return value &&
|
|
|
- (inEffectiveFields(key, options) || inSearchFields(key, options)) && // 必须在有效的搜索字段中
|
|
|
- (
|
|
|
- ~value.indexOf(keyword) || // 匹配值包含关键字
|
|
|
- options.twoWayMatch && ~keyword.indexOf(value) // 关键字包含匹配值
|
|
|
- );
|
|
|
- }
|
|
|
- /**
|
|
|
- * 通过 ajax 或 json 参数获取数据
|
|
|
- */
|
|
|
- function getData(keyword, $input, callback, options) {
|
|
|
- var data, validData, filterData = {
|
|
|
- value: []
|
|
|
- },
|
|
|
- i, key, len,
|
|
|
- fnPreprocessKeyword = options.fnPreprocessKeyword;
|
|
|
-
|
|
|
- keyword = keyword || '';
|
|
|
- // 获取数据前对关键字预处理方法
|
|
|
- if ($.isFunction(fnPreprocessKeyword)) {
|
|
|
- keyword = fnPreprocessKeyword(keyword, options);
|
|
|
- }
|
|
|
+ clearTimeout(keyupTimer);
|
|
|
+ keyupTimer = setTimeout(function() {
|
|
|
+ // console.log('input keyup', event);
|
|
|
|
|
|
- // 给了url参数,则从服务器 ajax 请求
|
|
|
- // console.log(options.url + keyword);
|
|
|
- if (options.url) {
|
|
|
- var timer;
|
|
|
- if (options.searchingTip) {
|
|
|
- timer = setTimeout(function() {
|
|
|
- showTip(options.searchingTip, $input, $input.parent().find('ul'), options);
|
|
|
- }, 600);
|
|
|
- }
|
|
|
+ // 锁定状态,返回
|
|
|
+ if (inputLock) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- ajax(options, keyword).done(function(result) {
|
|
|
- callback($input, options.data, options); // 为 refreshDropMenu
|
|
|
- $input.trigger(onDataRequestSuccess, result);
|
|
|
- if (options.getDataMethod === 'firstByUrl') {
|
|
|
- options.url = null;
|
|
|
- }
|
|
|
- }).always(function() {
|
|
|
- timer && clearTimeout(timer);
|
|
|
- });
|
|
|
- } else {
|
|
|
- // 没有给出 url 参数,则从 data 参数获取
|
|
|
- data = options.data;
|
|
|
- validData = checkData(data);
|
|
|
- // 本地的 data 数据,则在本地过滤
|
|
|
- if (validData) {
|
|
|
- if (keyword) {
|
|
|
- // 输入不为空时则进行匹配
|
|
|
- len = data.value.length;
|
|
|
- for (i = 0; i < len; i++) {
|
|
|
- for (key in data.value[i]) {
|
|
|
- if (
|
|
|
- data.value[i][key] &&
|
|
|
- isInWord(keyword, key, data.value[i][key] + '', options)
|
|
|
- ) {
|
|
|
- filterData.value.push(data.value[i]);
|
|
|
- filterData.value[filterData.value.length - 1].__index = i;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- filterData = data;
|
|
|
- }
|
|
|
- }
|
|
|
+ word = $input.val();
|
|
|
|
|
|
- callback($input, filterData, options);
|
|
|
- } // else
|
|
|
- }
|
|
|
- /**
|
|
|
- * 数据处理
|
|
|
- * url 获取数据时,对数据的处理,作为 fnGetData 之后的回调处理
|
|
|
- */
|
|
|
- function processData(data) {
|
|
|
- return checkData(data);
|
|
|
- }
|
|
|
- /**
|
|
|
- * 取得 clearable 清除按钮
|
|
|
- */
|
|
|
- function getIClear($input, options) {
|
|
|
- var $iClear = $input.prev('i.clearable');
|
|
|
-
|
|
|
- // 是否可清除已输入的内容(添加清除按钮)
|
|
|
- if (options.clearable && !$iClear.length) {
|
|
|
- $iClear = $('<i class="clearable glyphicon glyphicon-remove fa fa-plus"></i>')
|
|
|
- .prependTo($input.parent());
|
|
|
- }
|
|
|
+ // 若输入框值没有改变则返回
|
|
|
+ if ($.trim(word) && word === setOrGetAlt($input)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- return $iClear.css({
|
|
|
- position: 'absolute',
|
|
|
- top: 'calc(50% - 6px)',
|
|
|
- transform: 'rotate(45deg)',
|
|
|
- // right: options.showBtn ? Math.max($input.next('.input-group-btn').width(), 33) + 2 : 12,
|
|
|
- zIndex: 4,
|
|
|
- cursor: 'pointer',
|
|
|
- width: '14px',
|
|
|
- lineHeight: '14px',
|
|
|
- textAlign: 'center',
|
|
|
- fontSize: 12
|
|
|
- }).hide();
|
|
|
- }
|
|
|
- /**
|
|
|
- * 默认的配置选项
|
|
|
- * @type {Object}
|
|
|
- */
|
|
|
- var defaultOptions = {
|
|
|
- url: null, // 请求数据的 URL 地址
|
|
|
- jsonp: null, // 设置此参数名,将开启jsonp功能,否则使用json数据结构
|
|
|
- data: {
|
|
|
- value: []
|
|
|
- }, // 提示所用的数据,注意格式
|
|
|
- indexId: 0, // 每组数据的第几个数据,作为input输入框的 data-id,设为 -1 且 idField 为空则不设置此值
|
|
|
- indexKey: 0, // 每组数据的第几个数据,作为input输入框的内容
|
|
|
- idField: '', // 每组数据的哪个字段作为 data-id,优先级高于 indexId 设置(推荐)
|
|
|
- keyField: '', // 每组数据的哪个字段作为输入框内容,优先级高于 indexKey 设置(推荐)
|
|
|
-
|
|
|
- /* 搜索相关 */
|
|
|
- autoSelect: TRUE, // 键盘向上/下方向键时,是否自动选择值
|
|
|
- allowNoKeyword: TRUE, // 是否允许无关键字时请求数据
|
|
|
- getDataMethod: 'firstByUrl', // 获取数据的方式,url:一直从url请求;data:从 options.data 获取;firstByUrl:第一次从Url获取全部数据,之后从options.data获取
|
|
|
- delayUntilKeyup: FALSE, // 获取数据的方式 为 firstByUrl 时,是否延迟到有输入时才请求数据
|
|
|
- ignorecase: FALSE, // 前端搜索匹配时,是否忽略大小写
|
|
|
- effectiveFields: [], // 有效显示于列表中的字段,非有效字段都会过滤,默认全部有效。
|
|
|
- effectiveFieldsAlias: {}, // 有效字段的别名对象,用于 header 的显示
|
|
|
- searchFields: [], // 有效搜索字段,从前端搜索过滤数据时使用,但不一定显示在列表中。effectiveFields 配置字段也会用于搜索过滤
|
|
|
- twoWayMatch: TRUE, // 是否双向匹配搜索。为 true 即输入关键字包含或包含于匹配字段均认为匹配成功,为 false 则输入关键字包含于匹配字段认为匹配成功
|
|
|
- multiWord: FALSE, // 以分隔符号分割的多关键字支持
|
|
|
- separator: ',', // 多关键字支持时的分隔符,默认为半角逗号
|
|
|
- delay: 300, // 搜索触发的延时时间间隔,单位毫秒
|
|
|
- emptyTip: '', // 查询为空时显示的内容,可为 html
|
|
|
- searchingTip: '搜索中...', // ajax 搜索时显示的提示内容,当搜索时间较长时给出正在搜索的提示
|
|
|
- hideOnSelect: FALSE, // 鼠标从列表单击选择了值时,是否隐藏选择列表
|
|
|
-
|
|
|
- /* UI */
|
|
|
- autoDropup: FALSE, // 选择菜单是否自动判断向上展开。设为 true,则当下拉菜单高度超过窗体,且向上方向不会被窗体覆盖,则选择菜单向上弹出
|
|
|
- autoMinWidth: FALSE, // 是否自动最小宽度,设为 false 则最小宽度不小于输入框宽度
|
|
|
- showHeader: FALSE, // 是否显示选择列表的 header。为 true 时,有效字段大于一列则显示表头
|
|
|
- showBtn: TRUE, // 是否显示下拉按钮
|
|
|
- inputBgColor: '', // 输入框背景色,当与容器背景色不同时,可能需要该项的配置
|
|
|
- inputWarnColor: 'rgba(255,0,0,.1)', // 输入框内容不是下拉列表选择时的警告色
|
|
|
- listStyle: {
|
|
|
- 'padding-top': 0,
|
|
|
- 'max-height': '375px',
|
|
|
- 'max-width': '800px',
|
|
|
- 'overflow': 'auto',
|
|
|
- 'width': 'auto',
|
|
|
- 'transition': '0.3s',
|
|
|
- '-webkit-transition': '0.3s',
|
|
|
- '-moz-transition': '0.3s',
|
|
|
- '-o-transition': '0.3s',
|
|
|
- 'word-break': 'keep-all',
|
|
|
- 'white-space': 'nowrap'
|
|
|
- }, // 列表的样式控制
|
|
|
- listAlign: 'left', // 提示列表对齐位置,left/right/auto
|
|
|
- listHoverStyle: 'background: #07d; color:#fff', // 提示框列表鼠标悬浮的样式
|
|
|
- listHoverCSS: 'jhover', // 提示框列表鼠标悬浮的样式名称
|
|
|
- clearable: FALSE, // 是否可清除已输入的内容
|
|
|
-
|
|
|
- /* key */
|
|
|
- keyLeft: 37, // 向左方向键,不同的操作系统可能会有差别,则自行定义
|
|
|
- keyUp: 38, // 向上方向键
|
|
|
- keyRight: 39, // 向右方向键
|
|
|
- keyDown: 40, // 向下方向键
|
|
|
- keyEnter: 13, // 回车键
|
|
|
-
|
|
|
- /* methods */
|
|
|
- fnProcessData: processData, // 格式化数据的方法,返回数据格式参考 data 参数
|
|
|
- fnGetData: getData, // 获取数据的方法,无特殊需求一般不作设置
|
|
|
- fnAdjustAjaxParam: null, // 调整 ajax 请求参数方法,用于更多的请求配置需求。如对请求关键字作进一步处理、修改超时时间等
|
|
|
- fnPreprocessKeyword: null, // 搜索过滤数据前,对输入关键字作进一步处理方法。注意,应返回字符串
|
|
|
- fnAjaxFail: null, // ajax 失败时回调方法
|
|
|
- };
|
|
|
+ // 当按下键之前记录输入框值,以方便查看键弹起时值有没有变
|
|
|
+ setOrGetAlt($input, word);
|
|
|
|
|
|
- var methods = {
|
|
|
- init: function(options) {
|
|
|
- // 参数设置
|
|
|
- var self = this;
|
|
|
- options = options || {};
|
|
|
+ if (options.multiWord) {
|
|
|
+ word = word.split(options.separator).reverse()[0];
|
|
|
+ }
|
|
|
|
|
|
- // 默认配置有效显示字段多于一个,则显示列表表头,否则不显示
|
|
|
- if (isUndefined(options.showHeader) && options.effectiveFields && options.effectiveFields.length > 1) {
|
|
|
- options.showHeader = TRUE;
|
|
|
+ // 是否允许空数据查询
|
|
|
+ if (!word.length && !options.allowNoKeyword) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ options.fnGetData($.trim(word), $input, refreshDropMenu, options);
|
|
|
+ }, options.delay || 300);
|
|
|
+ })
|
|
|
+ .on("focus.bs", function() {
|
|
|
+ // console.log('input focus');
|
|
|
+ adjustDropMenuPos($input, $dropdownMenu, options);
|
|
|
+ })
|
|
|
+ .on("blur.bs", function() {
|
|
|
+ if (!isMouseenterMenu) {
|
|
|
+ // 不是进入下拉列表状态,则隐藏列表
|
|
|
+ hideDropMenu($input, options);
|
|
|
+ inputLock = true;
|
|
|
+ setTimeout(function() {
|
|
|
+ inputLock = FALSE;
|
|
|
+ });
|
|
|
}
|
|
|
+ })
|
|
|
+ .on("click.bs", function() {
|
|
|
+ // console.log('input click');
|
|
|
+ var word = $input.val();
|
|
|
|
|
|
- options = $.extend(TRUE, {}, defaultOptions, options);
|
|
|
-
|
|
|
- // 旧的方法兼容
|
|
|
- if (options.processData) {
|
|
|
- options.fnProcessData = options.processData;
|
|
|
+ if (
|
|
|
+ $.trim(word) &&
|
|
|
+ word === setOrGetAlt($input) &&
|
|
|
+ $dropdownMenu.find("table tr").length
|
|
|
+ ) {
|
|
|
+ return showDropMenu($input, options);
|
|
|
}
|
|
|
|
|
|
- if (options.getData) {
|
|
|
- options.fnGetData = options.getData;
|
|
|
+ if ($dropdownMenu.is(":visible")) {
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- if (options.getDataMethod === 'firstByUrl' && options.url && !options.delayUntilKeyup) {
|
|
|
- ajax(options).done(function(result) {
|
|
|
- options.url = null;
|
|
|
- self.trigger(onDataRequestSuccess, result);
|
|
|
- });
|
|
|
+ if (options.multiWord) {
|
|
|
+ word = word.split(options.separator).reverse()[0];
|
|
|
}
|
|
|
|
|
|
- // 鼠标滑动到条目样式
|
|
|
- if (!$('#' + BSSUGGEST).length) {
|
|
|
- $('head:eq(0)').append('<style id="' + BSSUGGEST + '">.' + options.listHoverCSS + '{' + options.listHoverStyle + '}</style>');
|
|
|
+ // 是否允许空数据查询
|
|
|
+ if (!word.length && !options.allowNoKeyword) {
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- return self.each(function() {
|
|
|
- var $input = $(this),
|
|
|
- $parent = $input.parent(),
|
|
|
- $iClear = getIClear($input, options),
|
|
|
- isMouseenterMenu,
|
|
|
- keyupTimer, // keyup 与 input 事件延时定时器
|
|
|
- $dropdownMenu = $parent.find('ul:eq(0)');
|
|
|
-
|
|
|
- // 兼容 bs4
|
|
|
- $dropdownMenu.parent().css('position', 'relative');
|
|
|
-
|
|
|
- // 验证输入框对象是否符合条件
|
|
|
- if (!checkInput($input, $dropdownMenu, options)) {
|
|
|
- console.warn('不是一个标准的 bootstrap 下拉式菜单或已初始化:', $input);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // 是否显示 button 按钮
|
|
|
- if (!options.showBtn) {
|
|
|
- $input.css('borderRadius', 4);
|
|
|
- $parent.css('width', '100%')
|
|
|
- .find('.btn:eq(0)').hide();
|
|
|
- }
|
|
|
-
|
|
|
- // 移除 disabled 类,并禁用自动完成
|
|
|
- $input.removeClass(DISABLED).prop(DISABLED, FALSE).attr('autocomplete', 'off');
|
|
|
- // dropdown-menu 增加修饰
|
|
|
- $dropdownMenu.css(options.listStyle);
|
|
|
-
|
|
|
- // 默认背景色
|
|
|
- if (!options.inputBgColor) {
|
|
|
- options.inputBgColor = $input.css('backgroundColor');
|
|
|
- }
|
|
|
-
|
|
|
- // 开始事件处理
|
|
|
- $input.on('keydown', function(event) {
|
|
|
- var currentList, tipsKeyword; // 提示列表上被选中的关键字
|
|
|
-
|
|
|
- // 当提示层显示时才对键盘事件处理
|
|
|
- if (!$dropdownMenu.is(':visible')) {
|
|
|
- setOrGetDataId($input, '');
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- currentList = $dropdownMenu.find('.' + options.listHoverCSS);
|
|
|
- tipsKeyword = ''; // 提示列表上被选中的关键字
|
|
|
-
|
|
|
- unHoverAll($dropdownMenu, options);
|
|
|
-
|
|
|
- if (event.keyCode === options.keyDown) { // 如果按的是向下方向键
|
|
|
- if (!currentList.length) {
|
|
|
- // 如果提示列表没有一个被选中,则将列表第一个选中
|
|
|
- tipsKeyword = getPointKeyword($dropdownMenu.find('tbody tr:first').mouseover());
|
|
|
- } else if (!currentList.next().length) {
|
|
|
- // 如果是最后一个被选中,则取消选中,即可认为是输入框被选中,并恢复输入的值
|
|
|
- if (options.autoSelect) {
|
|
|
- setOrGetDataId($input, '').val(setOrGetAlt($input));
|
|
|
- }
|
|
|
- } else {
|
|
|
- // 选中下一行
|
|
|
- tipsKeyword = getPointKeyword(currentList.next().mouseover());
|
|
|
- }
|
|
|
- // 控制滑动条
|
|
|
- adjustScroll($input, $dropdownMenu, options);
|
|
|
-
|
|
|
- if (!options.autoSelect) {
|
|
|
- return;
|
|
|
- }
|
|
|
- } else if (event.keyCode === options.keyUp) { // 如果按的是向上方向键
|
|
|
- if (!currentList.length) {
|
|
|
- tipsKeyword = getPointKeyword($dropdownMenu.find('tbody tr:last').mouseover());
|
|
|
- } else if (!currentList.prev().length) {
|
|
|
- if (options.autoSelect) {
|
|
|
- setOrGetDataId($input, '').val(setOrGetAlt($input));
|
|
|
- }
|
|
|
- } else {
|
|
|
- // 选中前一行
|
|
|
- tipsKeyword = getPointKeyword(currentList.prev().mouseover());
|
|
|
- }
|
|
|
-
|
|
|
- // 控制滑动条
|
|
|
- adjustScroll($input, $dropdownMenu, options);
|
|
|
-
|
|
|
- if (!options.autoSelect) {
|
|
|
- return;
|
|
|
- }
|
|
|
- } else if (event.keyCode === options.keyEnter) {
|
|
|
- tipsKeyword = getPointKeyword(currentList);
|
|
|
- hideDropMenu($input, options);
|
|
|
- } else {
|
|
|
- setOrGetDataId($input, '');
|
|
|
- }
|
|
|
-
|
|
|
- // 设置值 tipsKeyword
|
|
|
- // console.log(tipsKeyword);
|
|
|
- setValue($input, tipsKeyword, options);
|
|
|
- }).on('compositionstart', function(event) {
|
|
|
- // 中文输入开始,锁定
|
|
|
- // console.log('compositionstart');
|
|
|
- inputLock = TRUE;
|
|
|
- }).on('compositionend', function(event) {
|
|
|
- // 中文输入结束,解除锁定
|
|
|
- // console.log('compositionend');
|
|
|
- inputLock = FALSE;
|
|
|
- }).on('keyup input paste', function(event) {
|
|
|
- var word;
|
|
|
-
|
|
|
- if (event.keyCode) {
|
|
|
- setBackground($input, options);
|
|
|
- }
|
|
|
-
|
|
|
- // 如果弹起的键是回车、向上或向下方向键则返回
|
|
|
- if (~$.inArray(event.keyCode, [options.keyDown, options.keyUp, options.keyEnter])) {
|
|
|
- $input.val($input.val()); // 让鼠标输入跳到最后
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- clearTimeout(keyupTimer);
|
|
|
- keyupTimer = setTimeout(function() {
|
|
|
- // console.log('input keyup', event);
|
|
|
-
|
|
|
- // 锁定状态,返回
|
|
|
- if (inputLock) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- word = $input.val();
|
|
|
-
|
|
|
- // 若输入框值没有改变则返回
|
|
|
- if ($.trim(word) && word === setOrGetAlt($input)) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // 当按下键之前记录输入框值,以方便查看键弹起时值有没有变
|
|
|
- setOrGetAlt($input, word);
|
|
|
-
|
|
|
- if (options.multiWord) {
|
|
|
- word = word.split(options.separator).reverse()[0];
|
|
|
- }
|
|
|
-
|
|
|
- // 是否允许空数据查询
|
|
|
- if (!word.length && !options.allowNoKeyword) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- options.fnGetData($.trim(word), $input, refreshDropMenu, options);
|
|
|
- }, options.delay || 300);
|
|
|
- }).on('focus', function() {
|
|
|
- // console.log('input focus');
|
|
|
- adjustDropMenuPos($input, $dropdownMenu, options);
|
|
|
- }).on('blur', function() {
|
|
|
- if (!isMouseenterMenu) { // 不是进入下拉列表状态,则隐藏列表
|
|
|
- hideDropMenu($input, options);
|
|
|
- }
|
|
|
- }).on('click', function() {
|
|
|
- // console.log('input click');
|
|
|
- var word = $input.val();
|
|
|
-
|
|
|
- if (
|
|
|
- $.trim(word) &&
|
|
|
- word === setOrGetAlt($input) &&
|
|
|
- $dropdownMenu.find('table tr').length
|
|
|
- ) {
|
|
|
- return showDropMenu($input, options);
|
|
|
- }
|
|
|
-
|
|
|
- if ($dropdownMenu.is(':visible')) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (options.multiWord) {
|
|
|
- word = word.split(options.separator).reverse()[0];
|
|
|
- }
|
|
|
-
|
|
|
- // 是否允许空数据查询
|
|
|
- if (!word.length && !options.allowNoKeyword) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // console.log('word', word);
|
|
|
- options.fnGetData($.trim(word), $input, refreshDropMenu, options);
|
|
|
- });
|
|
|
-
|
|
|
- // 下拉按钮点击时
|
|
|
- $parent.find('.btn:eq(0)').attr('data-toggle', '').click(function() {
|
|
|
- if (!$dropdownMenu.is(':visible')) {
|
|
|
- if (options.url) {
|
|
|
- $input.click().focus();
|
|
|
- if (!$dropdownMenu.find('tr').length) {
|
|
|
- return FALSE;
|
|
|
- }
|
|
|
- } else {
|
|
|
- // 不以 keyword 作为过滤,展示所有的数据
|
|
|
- refreshDropMenu($input, options.data, options);
|
|
|
- }
|
|
|
- showDropMenu($input, options);
|
|
|
- } else {
|
|
|
- hideDropMenu($input, options);
|
|
|
- }
|
|
|
-
|
|
|
- return FALSE;
|
|
|
- });
|
|
|
-
|
|
|
- // 列表中滑动时,输入框失去焦点
|
|
|
- $dropdownMenu.mouseenter(function() {
|
|
|
- // console.log('mouseenter')
|
|
|
- isMouseenterMenu = 1;
|
|
|
- $input.blur();
|
|
|
- }).mouseleave(function() {
|
|
|
- // console.log('mouseleave')
|
|
|
- isMouseenterMenu = 0;
|
|
|
- $input.focus();
|
|
|
- }).on('mouseenter', 'tbody tr', function() {
|
|
|
- // 行上的移动事件
|
|
|
- unHoverAll($dropdownMenu, options);
|
|
|
- $(this).addClass(options.listHoverCSS);
|
|
|
-
|
|
|
- return FALSE; // 阻止冒泡
|
|
|
- })
|
|
|
- .on('mousedown', 'tbody tr', function() {
|
|
|
- var keywords = getPointKeyword($(this));
|
|
|
- setValue($input, keywords, options);
|
|
|
- setOrGetAlt($input, keywords.key);
|
|
|
- setBackground($input, options);
|
|
|
-
|
|
|
- if (options.hideOnSelect) {
|
|
|
- hideDropMenu($input, options);
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- // 存在清空按钮
|
|
|
- if ($iClear.length) {
|
|
|
- $iClear.click(function () {
|
|
|
- setOrGetDataId($input, '').val('');
|
|
|
- setBackground($input, options);
|
|
|
- });
|
|
|
-
|
|
|
- $parent.mouseenter(function() {
|
|
|
- if (!$input.prop(DISABLED)) {
|
|
|
- $iClear.css('right', options.showBtn ? Math.max($input.next().width(), 33) + 2 : 12)
|
|
|
- .show();
|
|
|
- }
|
|
|
- }).mouseleave(function() {
|
|
|
- $iClear.hide();
|
|
|
- });
|
|
|
+ // console.log('word', word);
|
|
|
+ options.fnGetData($.trim(word), $input, refreshDropMenu, options);
|
|
|
+ });
|
|
|
+
|
|
|
+ // 下拉按钮点击时
|
|
|
+ $parent
|
|
|
+ .find(".btn:eq(0)")
|
|
|
+ .attr("data-toggle", "")
|
|
|
+ .click(function() {
|
|
|
+ if (!$dropdownMenu.is(":visible")) {
|
|
|
+ if (options.url) {
|
|
|
+ $input.click().focus();
|
|
|
+ if (!$dropdownMenu.find("tr").length) {
|
|
|
+ return FALSE;
|
|
|
}
|
|
|
+ } else {
|
|
|
+ // 不以 keyword 作为过滤,展示所有的数据
|
|
|
+ refreshDropMenu($input, options.data, options);
|
|
|
+ }
|
|
|
+ showDropMenu($input, options);
|
|
|
+ } else {
|
|
|
+ hideDropMenu($input, options);
|
|
|
+ }
|
|
|
|
|
|
+ return FALSE;
|
|
|
+ });
|
|
|
+
|
|
|
+ // 列表中滑动时,输入框失去焦点
|
|
|
+ $dropdownMenu
|
|
|
+ .mouseenter(function() {
|
|
|
+ // console.log('mouseenter')
|
|
|
+ isMouseenterMenu = 1;
|
|
|
+ $input.blur();
|
|
|
+ })
|
|
|
+ .mouseleave(function() {
|
|
|
+ // console.log('mouseleave')
|
|
|
+ isMouseenterMenu = 0;
|
|
|
+ $input.focus();
|
|
|
+ })
|
|
|
+ .on("mouseenter", "tbody tr", function() {
|
|
|
+ // 行上的移动事件
|
|
|
+ unHoverAll($dropdownMenu, options);
|
|
|
+ $(this).addClass(options.listHoverCSS);
|
|
|
+
|
|
|
+ return FALSE; // 阻止冒泡
|
|
|
+ })
|
|
|
+ .on("mousedown", "tbody tr", function() {
|
|
|
+ var keywords = getPointKeyword($(this));
|
|
|
+ setValue($input, keywords, options);
|
|
|
+ setOrGetAlt($input, keywords.key);
|
|
|
+ setBackground($input, options);
|
|
|
+
|
|
|
+ if (options.hideOnSelect) {
|
|
|
+ hideDropMenu($input, options);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 存在清空按钮
|
|
|
+ if ($iClear.length) {
|
|
|
+ $iClear.click(function() {
|
|
|
+ setOrGetDataId($input, "").val("");
|
|
|
+ setBackground($input, options);
|
|
|
+ });
|
|
|
+
|
|
|
+ $parent
|
|
|
+ .mouseenter(function() {
|
|
|
+ if (!$input.prop(DISABLED)) {
|
|
|
+ $iClear
|
|
|
+ .css(
|
|
|
+ "right",
|
|
|
+ options.showBtn
|
|
|
+ ? Math.max($input.next().width(), 33) + 2
|
|
|
+ : 12
|
|
|
+ )
|
|
|
+ .show();
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .mouseleave(function() {
|
|
|
+ $iClear.hide();
|
|
|
});
|
|
|
- },
|
|
|
- show: function() {
|
|
|
- return this.each(function() {
|
|
|
- $(this).click();
|
|
|
- });
|
|
|
- },
|
|
|
- hide: function() {
|
|
|
- return this.each(function() {
|
|
|
- hideDropMenu($(this));
|
|
|
- });
|
|
|
- },
|
|
|
- disable: function() {
|
|
|
- return this.each(function() {
|
|
|
- $(this).attr(DISABLED, TRUE)
|
|
|
- .parent().find('.btn:eq(0)').prop(DISABLED, TRUE);
|
|
|
- });
|
|
|
- },
|
|
|
- enable: function() {
|
|
|
- return this.each(function() {
|
|
|
- $(this).attr(DISABLED, FALSE)
|
|
|
- .parent().find('.btn:eq(0)').prop(DISABLED, FALSE);
|
|
|
- });
|
|
|
- },
|
|
|
- destroy: function() {
|
|
|
- return this.each(function() {
|
|
|
- $(this).off().removeData(BSSUGGEST).removeAttr('style')
|
|
|
- .parent().find('.btn:eq(0)').off().show().attr('data-toggle', 'dropdown').prop(DISABLED, FALSE) // .addClass(DISABLED);
|
|
|
- .next().css('display', '').off();
|
|
|
- });
|
|
|
- },
|
|
|
- version: function() {
|
|
|
- return VERSION;
|
|
|
}
|
|
|
- };
|
|
|
-
|
|
|
- $.fn[BSSUGGEST] = function(options) {
|
|
|
- // 方法判断
|
|
|
- if (typeof options === 'string' && methods[options]) {
|
|
|
- var inited = TRUE;
|
|
|
- this.each(function() {
|
|
|
- if (!$(this).data(BSSUGGEST)) {
|
|
|
- return inited = FALSE;
|
|
|
- }
|
|
|
- });
|
|
|
- // 只要有一个未初始化,则全部都不执行方法,除非是 init 或 version
|
|
|
- if (!inited && 'init' !== options && 'version' !== options) {
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- // 如果是方法,则参数第一个为函数名,从第二个开始为函数参数
|
|
|
- return methods[options].apply(this, [].slice.call(arguments, 1));
|
|
|
- } else {
|
|
|
- // 调用初始化方法
|
|
|
- return methods.init.apply(this, arguments);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ show: function() {
|
|
|
+ return this.each(function() {
|
|
|
+ $(this).click();
|
|
|
+ });
|
|
|
+ },
|
|
|
+ hide: function() {
|
|
|
+ return this.each(function() {
|
|
|
+ hideDropMenu($(this));
|
|
|
+ });
|
|
|
+ },
|
|
|
+ disable: function() {
|
|
|
+ return this.each(function() {
|
|
|
+ $(this)
|
|
|
+ .attr(DISABLED, TRUE)
|
|
|
+ .parent()
|
|
|
+ .find(".btn:eq(0)")
|
|
|
+ .prop(DISABLED, TRUE);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ enable: function() {
|
|
|
+ return this.each(function() {
|
|
|
+ $(this)
|
|
|
+ .attr(DISABLED, FALSE)
|
|
|
+ .parent()
|
|
|
+ .find(".btn:eq(0)")
|
|
|
+ .prop(DISABLED, FALSE);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ destroy: function() {
|
|
|
+ return this.each(function() {
|
|
|
+ var evNameList =
|
|
|
+ "click.bs keydown.bs compositionstart.bs compositionend.bs keyup.bs input.bs paste.bs focus.bs click.bs";
|
|
|
+ $(this)
|
|
|
+ .off(evNameList)
|
|
|
+ .removeData(BSSUGGEST)
|
|
|
+ .removeAttr("style")
|
|
|
+ .parent()
|
|
|
+ .find(".btn:eq(0)")
|
|
|
+ .off()
|
|
|
+ .show()
|
|
|
+ .attr("data-toggle", "dropdown")
|
|
|
+ .prop(DISABLED, FALSE) // .addClass(DISABLED);
|
|
|
+ .next()
|
|
|
+ .css("display", "")
|
|
|
+ .off();
|
|
|
+ });
|
|
|
+ },
|
|
|
+ version: function() {
|
|
|
+ return VERSION;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ $.fn[BSSUGGEST] = function(options) {
|
|
|
+ // 方法判断
|
|
|
+ if (typeof options === "string" && methods[options]) {
|
|
|
+ var inited = TRUE;
|
|
|
+ this.each(function() {
|
|
|
+ if (!$(this).data(BSSUGGEST)) {
|
|
|
+ return (inited = FALSE);
|
|
|
}
|
|
|
+ });
|
|
|
+ // 只要有一个未初始化,则全部都不执行方法,除非是 init 或 version
|
|
|
+ if (!inited && "init" !== options && "version" !== options) {
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果是方法,则参数第一个为函数名,从第二个开始为函数参数
|
|
|
+ return methods[options].apply(this, [].slice.call(arguments, 1));
|
|
|
+ } else {
|
|
|
+ // 调用初始化方法
|
|
|
+ return methods.init.apply(this, arguments);
|
|
|
}
|
|
|
-});
|
|
|
+ };
|
|
|
+});
|