1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180 |
- /**
- * 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;
- }
- /**
- * 错误处理
- */
- function handleError(e1, e2) {
- if (!window.console || !window.console.trace) {
- return;
- }
- console.trace(e1);
- if (e2) {
- console.trace(e2);
- }
- }
- /**
- * 获取当前 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;
- }
- 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();
- }
- $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;
- }
- 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
- };
- }
- // 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 {
- dmcss['width'] = 'auto';
- }*/
- $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;
- }
- bg = options.inputBgColor;
- warnbg = options.inputWarnColor;
- var curVal = $input.val();
- var preVal = $input.data("pre-val");
- if (setOrGetDataId($input) || !curVal) {
- $input.css("background", bg || "");
- if (!curVal && preVal) {
- $input.trigger("onUnsetSelectValue").data("pre-val", "");
- }
- 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);
- }
- 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);
- }
- /**
- * 验证 $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;
- }
- /**
- * 数据格式检测
- * 检测 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;
- }
- /**
- * 判断字段名是否在 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 : []]);
- }
- }
- /**
- * 隐藏下拉列表
- */
- 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 : []]);
- }
- }
- /**
- * 下拉列表刷新
- * 作为 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 (
- options._lastData &&
- JSON.stringify(options._lastData) === JSON.stringify(data) &&
- $dropdownMenu.find("tr").length === len
- ) {
- showDropMenu($input, options);
- return adjustDropMenuPos($input, $dropdownMenu, options);
- }
- 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>");
- }
- 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];
- }
- if (isUndefined(idValue) && options.indexId === index) {
- idValue = dataI[field];
- }
- index++;
- }
- $.each(columns, function(index, field) {
- // 列表中只显示有效的字段
- if (inEffectiveFields(field, options)) {
- tds.push('<td data-name="', field, '">', dataI[field], "</td>");
- }
- });
- 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();
- }
- var ajaxParam = {
- type: "GET",
- dataType: options.jsonp ? "jsonp" : "json",
- timeout: 5000
- };
- // jsonp
- if (options.jsonp) {
- ajaxParam.jsonp = options.jsonp;
- }
- // 自定义 ajax 请求参数生成方法
- var adjustAjaxParam,
- fnAdjustAjaxParam = options.fnAdjustAjaxParam;
- if ($.isFunction(fnAdjustAjaxParam)) {
- adjustAjaxParam = fnAdjustAjaxParam(keyword, options);
- // options.fnAdjustAjaxParam 返回false,则终止 ajax 请求
- if (FALSE === adjustAjaxParam) {
- return;
- }
- $.extend(ajaxParam, adjustAjaxParam);
- }
- // 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();
- }
- 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);
- }
- // 给了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;
- }
- }
- }
- } 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());
- }
- 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;
- }
- // 是否显示 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.bs", 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.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);
- }
- // 如果弹起的键是回车、向上或向下方向键则返回
- 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.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();
- 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();
- });
- }
- });
- },
- 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);
- }
- };
- });
|