diff --git a/ruoyi-admin/src/main/resources/static/ajax/libs/suggest/bootstrap-suggest.js b/ruoyi-admin/src/main/resources/static/ajax/libs/suggest/bootstrap-suggest.js index c2bb75c9c..ade51b85d 100644 --- a/ruoyi-admin/src/main/resources/static/ajax/libs/suggest/bootstrap-suggest.js +++ b/ruoyi-admin/src/main/resources/static/ajax/libs/suggest/bootstrap-suggest.js @@ -1,1042 +1,1180 @@ /** - * Bootstrap Search Suggest - * @desc 这是一个基于 bootstrap 按钮式下拉菜单组件的搜索建议插件,必须使用于按钮式下拉菜单组件上。 - * @author renxia - * @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; // 用于中文输入法输入时锁定搜索 + 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]; + // 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; } - 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); + 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; } - /** - * 错误处理 - */ - 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); - var separator = options.separator || ',', - inputValList, - inputIdList, - dataId = setOrGetDataId($input); + if (options && options.multiWord) { + inputValList = $input.val().split(separator); + inputValList[inputValList.length - 1] = keywords.key; - 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); + } - //多关键字检索支持设置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(); + } - setOrGetDataId($input, inputIdList.join(separator)) - .val(inputValList.join(separator)) - .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 { - 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(); + // 列表对齐方式 + 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 + }; + } - if (options.autoDropup) { - setTimeout(function() { - var offsetTop = $input.offset().top; - var winScrollTop = $window.scrollTop(); - var menuHeight = $dropdownMenu.height(); + // 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 ( // 自动判断菜单向上展开 - ($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 { + // 是否自动最小宽度 + if (!options.autoMinWidth) { + dmcss.minWidth = parentWidth; + } + /* else { dmcss['width'] = 'auto'; }*/ - $dropdownMenu.css(dmcss); + $dropdownMenu.css(dmcss); - return $input; + 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', ''); - } + 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; + 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); - } + inputbg = $input + .css("backgroundColor") + .replace(/ /g, "") + .split(",", 3) + .join(","); + // 自由输入的内容,设置背景色 + if (!~warnbg.indexOf(inputbg)) { + $input + .trigger("onUnsetSelectValue") // 触发取消data-id事件 + .data("pre-val", "") + .css("background", warnbg); } - /** - * 解除所有列表 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 $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('
' + tip + "
") + .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 = [ + '' + ], + 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; }); - return TRUE; - } - /** - * 数据格式检测 - * 检测 ajax 返回成功数据或 data 参数数据是否有效 - * data 格式:{"value": [{}, {}...]} - */ - function checkData(data) { - var isEmpty = TRUE, o; + // 生成表头 + if (options.showHeader) { + html.push(""); + $.each(columns, function(index, field) { + if (!inEffectiveFields(field, options)) return; - for (o in data) { - if (o === 'value') { - isEmpty = FALSE; + html.push( + "" + ); + + index++; + }); + html.push(""); + } + html.push(""); + + // 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('"); + } + }); + + html.push( + '', + tds.join(""), + "" + ); + } + html.push("
", + options.effectiveFieldsAlias[field] || field, + index === 0 ? "(" + len + ")" : "", // 表头第一列记录总数 + "
', dataI[field], "
"); + + $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; } - if (isEmpty) { - handleError('返回数据格式错误!'); - return FALSE; - } - if (!data.value.length) { - // handleError('返回数据为空!'); - return FALSE; - } + } - return 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 = $( + '' + ).prependTo($input.parent()); } - /** - * 判断字段名是否在 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('
' + tip + '
').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 = [''], - idValue, keyValue; // 作为输入框 data-id 和内容的字段值 - var dataList = data.value; + 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 设置(推荐) - if (!data || !(len = dataList.length)) { - if (options.emptyTip) { - showTip(options.emptyTip, $input, $dropdownMenu, options); - } else { - $dropdownMenu.empty(); - hideDropMenu($input, options); - } - return $input; + /* 搜索相关 */ + 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( + '" + ); + } + + 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; } - // 相同数据,不用继续渲染了 - if ( - options._lastData && - JSON.stringify(options._lastData) === JSON.stringify(data) && - $dropdownMenu.find('tr').length === len - ) { - showDropMenu($input, options); - return adjustDropMenuPos($input, $dropdownMenu, options); + // 是否显示 button 按钮 + if (!options.showBtn) { + $input.css("borderRadius", 4); + $parent + .css("width", "100%") + .find(".btn:eq(0)") + .hide(); } - options._lastData = data; - // 生成表头 - if (options.showHeader) { - html.push(''); - for (field in dataList[0]) { - if (!inEffectiveFields(field, options)) { - continue; - } + // 移除 disabled 类,并禁用自动完成 + $input + .removeClass(DISABLED) + .prop(DISABLED, FALSE) + .attr("autocomplete", "off"); + // dropdown-menu 增加修饰 + $dropdownMenu.css(options.listStyle); - html.push(''); - - index++; - } - html.push(''); + // 默认背景色 + if (!options.inputBgColor) { + options.inputBgColor = $input.css("backgroundColor"); } - html.push(''); - // 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]; + // 开始事件处理 + $input + .on("keydown.bs", function(event) { + var currentList, tipsKeyword; // 提示列表上被选中的关键字 - 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++; - - // 列表中只显示有效的字段 - if (inEffectiveFields(field, options)) { - tds.push(''); - } + // 当提示层显示时才对键盘事件处理 + if (!$dropdownMenu.is(":visible")) { + setOrGetDataId($input, ""); + return; } - html.push('', - tds.join(''), ''); - } - html.push('
', (options.effectiveFieldsAlias[field] || field), - index === 0 ? ('(' + len + ')') : '' , // 表头第一列记录总数 - '
', dataI[field], '
'); + currentList = $dropdownMenu.find("." + options.listHoverCSS); + tipsKeyword = ""; // 提示列表上被选中的关键字 - $dropdownMenu.html(html.join('')); - showDropMenu($input, options); - //.show(); + unHoverAll($dropdownMenu, options); - // scrollbar 存在时,延时到动画结束时调整 padding - setTimeout(function() { - if (notNeedCalcPadding) { + 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, ""); } - var $table = $dropdownMenu.find('table:eq(0)'), - pdr = 0, - mgb = 0; + // 设置值 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 ( - $dropdownMenu.height() < $table.height() && - +$dropdownMenu.css('minWidth').replace('px', '') < $dropdownMenu.width() + $.trim(word) && + word === setOrGetAlt($input) && + $dropdownMenu.find("table tr").length ) { - pdr = 18; - mgb = 20; + return showDropMenu($input, options); } - $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; + if ($dropdownMenu.is(":visible")) { + return; } - $.extend(ajaxParam, adjustAjaxParam); - } - - // url 调整 - ajaxParam.url = function() { - if (!keyword || ajaxParam.data) { - return ajaxParam.url || options.url; + if (options.multiWord) { + word = word.split(options.separator).reverse()[0]; } - var type = '?'; - if (/=$/.test(options.url)) { - type = ''; - } else if (/\?/.test(options.url)) { - type = '&'; + // 是否允许空数据查询 + if (!word.length && !options.allowNoKeyword) { + return; } - return options.url + type + encodeURIComponent(keyword); - }(); + // console.log('word', word); + options.fnGetData($.trim(word), $input, refreshDropMenu, options); + }); - 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; + // 下拉按钮点击时 + $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); } - callback($input, filterData, options); - } // else - } - /** - * 数据处理 - * url 获取数据时,对数据的处理,作为 fnGetData 之后的回调处理 - */ - function processData(data) { - return checkData(data); - } - /** - * 取得 clearable 清除按钮 - */ - function getIClear($input, options) { - var $iClear = $input.prev('i.clearable'); + return FALSE; + }); - // 是否可清除已输入的内容(添加清除按钮) - if (options.clearable && !$iClear.length) { - $iClear = $('') - .prependTo($input.parent()); + // 列表中滑动时,输入框失去焦点 + $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(); + }); } - - 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(); + }); + }, + 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; } - /** - * 默认的配置选项 - * @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 失败时回调方法 - }; - - 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(''); - } - - 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(); - }); - } - - }); - }, - 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; + } - $.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); - } + // 如果是方法,则参数第一个为函数名,从第二个开始为函数参数 + return methods[options].apply(this, [].slice.call(arguments, 1)); + } else { + // 调用初始化方法 + return methods.init.apply(this, arguments); } -}); + }; +}); \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ajax/libs/suggest/bootstrap-suggest.min.js b/ruoyi-admin/src/main/resources/static/ajax/libs/suggest/bootstrap-suggest.min.js index 67061a4c6..5397c5e30 100644 --- a/ruoyi-admin/src/main/resources/static/ajax/libs/suggest/bootstrap-suggest.min.js +++ b/ruoyi-admin/src/main/resources/static/ajax/libs/suggest/bootstrap-suggest.min.js @@ -1,9 +1,9 @@ /** - * bootstrap-suggest-plugin - v0.1.27 + * bootstrap-suggest-plugin - v0.1.29 * @description 这是一个基于 bootstrap 按钮式下拉菜单组件的搜索建议插件,必须使用于按钮式下拉菜单组件上。 * @author lzwme - https://lzw.me * @GitHub https://github.com/lzwme/bootstrap-suggest-plugin.git - * @since 2019-03-27 16:20:36 + * @since 2019-11-18 09:30:06 */ -!function(e){if("function"==typeof define&&define.amd)define(["jquery"],e);else if("object"==typeof exports&&"object"==typeof module)e(require("jquery"));else{if(!window.jQuery)throw new Error("Not found jQuery.");e(window.jQuery)}}(function(c){var p,l=c(window),s="ActiveXObject"in window,e=navigator.userAgent.match(/Chrome\/(\d+)/);e&&(e=+e[1]);var h=s||51r?i-=r:i=0,t.scrollTop(i))}function F(e,t){e.find("tr."+t.listHoverCSS).removeClass(t.listHoverCSS)}function f(e){var t,n=m;for(t in e)if("value"===t){n=w;break}return n?(window.console&&window.console.trace,w):e.value.length?e:w}function T(e,t){var n=t.effectiveFields;return!("__index"===e||n.length&&!~c.inArray(e,n))}function H(e,t,n,i){n.html('
'+e+"
").show(),C(t,n,i)}function q(e,t){var n=e.parent().find("ul:eq(0)");n.is(":visible")||(n.show(),e.trigger("onShowDropdown",[t?t.data.value:[]]))}function W(e,t){var n=e.parent().find("ul:eq(0)");n.is(":visible")&&(n.hide(),e.trigger("onHideDropdown",[t?t.data.value:[]]))}function B(e,t,n){var i,r,o,a,l,s,u,d=e.parent().find("ul:eq(0)"),f=0,c=[''],p=t.value;if(!t||!(i=p.length))return n.emptyTip?H(n.emptyTip,e,d,n):(d.empty(),W(e,n)),e;if(n._lastData&&JSON.stringify(n._lastData)===JSON.stringify(t)&&d.find("tr").length===i)return q(e,n),C(e,d,n);if(n._lastData=t,n.showHeader){for(o in c.push(""),p[0])T(o,n)&&(c.push(""),f++);c.push("")}for(c.push(""),r=0;r',u[o],"");c.push('',a.join(""),"")}return c.push("
",n.effectiveFieldsAlias[o]||o,0===f?"("+i+")":"","
"),d.html(c.join("")),q(e,n),setTimeout(function(){if(!h){var e=d.find("table:eq(0)"),t=0,n=0;d.height().'+f.listHoverCSS+"{"+f.listHoverStyle+"}"),t.each(function(){var e,n,t,i,r,o,a,l=c(this),s=l.parent(),u=(i=f,r=(t=l).prev("i.clearable"),i.clearable&&!r.length&&(r=c('').prependTo(t.parent())),r.css({position:"absolute",top:"calc(50% - 6px)",transform:"rotate(45deg)",zIndex:4,cursor:"pointer",width:"14px",lineHeight:"14px",textAlign:"center",fontSize:12}).hide()),d=s.find("ul:eq(0)");(d.parent().css("position","relative"),o=l,a=f,!d.length||o.data(v)?w:(o.data(v,{options:a}),m))&&(f.showBtn||(l.css("borderRadius",4),s.css("width","100%").find(".btn:eq(0)").hide()),l.removeClass(y).prop(y,w).attr("autocomplete","off"),d.css(f.listStyle),f.inputBgColor||(f.inputBgColor=l.css("backgroundColor")),l.on("keydown",function(e){var t,n;if(d.is(":visible")){if(t=d.find("."+f.listHoverCSS),n="",F(d,f),e.keyCode===f.keyDown){if(t.length?t.next().length?n=x(t.next().mouseover()):f.autoSelect&&S(l,"").val(k(l)):n=x(d.find("tbody tr:first").mouseover()),A(l,d,f),!f.autoSelect)return}else if(e.keyCode===f.keyUp){if(t.length?t.prev().length?n=x(t.prev().mouseover()):f.autoSelect&&S(l,"").val(k(l)):n=x(d.find("tbody tr:last").mouseover()),A(l,d,f),!f.autoSelect)return}else e.keyCode===f.keyEnter?(n=x(t),W(l,f)):S(l,"");j(l,n,f)}else S(l,"")}).on("compositionstart",function(e){p=m}).on("compositionend",function(e){p=w}).on("keyup input paste",function(e){var t;e.keyCode&&D(l,f),~c.inArray(e.keyCode,[f.keyDown,f.keyUp,f.keyEnter])?l.val(l.val()):(clearTimeout(n),n=setTimeout(function(){p||(t=l.val(),c.trim(t)&&t===k(l)||(k(l,t),f.multiWord&&(t=t.split(f.separator).reverse()[0]),(t.length||f.allowNoKeyword)&&f.fnGetData(c.trim(t),l,B,f)))},f.delay||300))}).on("focus",function(){C(l,d,f)}).on("blur",function(){e||W(l,f)}).on("click",function(){var e=l.val();if(c.trim(e)&&e===k(l)&&d.find("table tr").length)return q(l,f);d.is(":visible")||(f.multiWord&&(e=e.split(f.separator).reverse()[0]),(e.length||f.allowNoKeyword)&&f.fnGetData(c.trim(e),l,B,f))}),s.find(".btn:eq(0)").attr("data-toggle","").click(function(){if(d.is(":visible"))W(l,f);else{if(f.url){if(l.click().focus(),!d.find("tr").length)return w}else B(l,f.data,f);q(l,f)}return w}),d.mouseenter(function(){e=1,l.blur()}).mouseleave(function(){e=0,l.focus()}).on("mouseenter","tbody tr",function(){return F(d,f),c(this).addClass(f.listHoverCSS),w}).on("mousedown","tbody tr",function(){var e=x(c(this));j(l,e,f),k(l,e.key),D(l,f),f.hideOnSelect&&W(l,f)}),u.length&&(u.click(function(){S(l,"").val(""),D(l,f)}),s.mouseenter(function(){l.prop(y)||u.css("right",f.showBtn?Math.max(l.next().width(),33)+2:12).show()}).mouseleave(function(){u.hide()})))})},show:function(){return this.each(function(){c(this).click()})},hide:function(){return this.each(function(){W(c(this))})},disable:function(){return this.each(function(){c(this).attr(y,m).parent().find(".btn:eq(0)").prop(y,m)})},enable:function(){return this.each(function(){c(this).attr(y,w).parent().find(".btn:eq(0)").prop(y,w)})},destroy:function(){return this.each(function(){c(this).off().removeData(v).removeAttr("style").parent().find(".btn:eq(0)").off().show().attr("data-toggle","dropdown").prop(y,w).next().css("display","").off()})},version:function(){return"0.1.27"}};c.fn[v]=function(e){if("string"==typeof e&&i[e]){var t=m;return this.each(function(){if(!c(this).data(v))return t=w}),t||"init"===e||"version"===e?i[e].apply(this,[].slice.call(arguments,1)):this}return i.init.apply(this,arguments)}}); +!function(e){if("function"==typeof define&&define.amd)define(["jquery"],e);else if("object"==typeof exports&&"object"==typeof module)e(require("jquery"));else{if(!window.jQuery)throw new Error("Not found jQuery.");e(window.jQuery)}}(function(g){var c,s=g(window),l="ActiveXObject"in window,e=navigator.userAgent.match(/Chrome\/(\d+)/);e&&(e=+e[1]);var y=l||51o?i-=o:i=0,t.scrollTop(i))}function F(e,t){e.find("tr."+t.listHoverCSS).removeClass(t.listHoverCSS)}function f(e){var t,n=b;for(t in e)if("value"===t){n=m;break}return n?(window.console&&window.console.trace,m):e.value.length?e:m}function T(e,t){var n=t.effectiveFields;return!("__index"===e||n.length&&!~g.inArray(e,n))}function H(e,t,n,i){n.html('
'+e+"
").show(),j(t,n,i)}function q(e,t){var n=e.parent().find("ul:eq(0)");n.is(":visible")||(n.show(),e.trigger("onShowDropdown",[t?t.data.value:[]]))}function W(e,t){var n=e.parent().find("ul:eq(0)");n.is(":visible")&&(n.hide(),e.trigger("onHideDropdown",[t?t.data.value:[]]))}function B(e,t,n){var i,o,a,r,s,l,u=e.parent().find("ul:eq(0)"),d=0,f=[''],c=t.value;if(!t||!(i=c.length))return n.emptyTip?H(n.emptyTip,e,u,n):(u.empty(),W(e,n)),e;if(n._lastData&&JSON.stringify(n._lastData)===JSON.stringify(t)&&u.find("tr").length===i)return q(e,n),j(e,u,n);n._lastData=t;var p,h=n.effectiveFields.length?n.effectiveFields:g.map(c[0],function(e,t){return t});n.showHeader&&(f.push(""),g.each(h,function(e,t){T(t,n)&&(f.push(""),e++)}),f.push("")),f.push("");var v=Math.min(n.maxOptionCount,i);for(o=0;o',p[t],"")}),f.push('',r.join(""),"")}return f.push("
",n.effectiveFieldsAlias[t]||t,0===e?"("+i+")":"","
"),u.html(f.join("")),q(e,n),setTimeout(function(){if(!y){var e=u.find("table:eq(0)"),t=0,n=0;u.height().'+f.listHoverCSS+"{"+f.listHoverStyle+"}"),t.each(function(){var e,n,t,i,o,a,r,s=g(this),l=s.parent(),u=(i=f,o=(t=s).prev("i.clearable"),i.clearable&&!o.length&&(o=g('').prependTo(t.parent())),o.css({position:"absolute",top:"calc(50% - 6px)",transform:"rotate(45deg)",zIndex:4,cursor:"pointer",width:"14px",lineHeight:"14px",textAlign:"center",fontSize:12}).hide()),d=l.find("ul:eq(0)");(d.parent().css("position","relative"),a=s,r=f,!d.length||a.data(p)?m:(a.data(p,{options:r}),b))&&(f.showBtn||(s.css("borderRadius",4),l.css("width","100%").find(".btn:eq(0)").hide()),s.removeClass(v).prop(v,m).attr("autocomplete","off"),d.css(f.listStyle),f.inputBgColor||(f.inputBgColor=s.css("backgroundColor")),s.on("keydown.bs",function(e){var t,n;if(d.is(":visible")){if(t=d.find("."+f.listHoverCSS),n="",F(d,f),e.keyCode===f.keyDown){if(t.length?t.next().length?n=x(t.next().mouseover()):f.autoSelect&&S(s,"").val(k(s)):n=x(d.find("tbody tr:first").mouseover()),A(s,d,f),!f.autoSelect)return}else if(e.keyCode===f.keyUp){if(t.length?t.prev().length?n=x(t.prev().mouseover()):f.autoSelect&&S(s,"").val(k(s)):n=x(d.find("tbody tr:last").mouseover()),A(s,d,f),!f.autoSelect)return}else e.keyCode===f.keyEnter?(n=x(t),W(s,f)):S(s,"");C(s,n,f)}else S(s,"")}).on("compositionstart.bs",function(e){c=b}).on("compositionend.bs",function(e){c=m}).on("keyup.bs input.bs paste.bs",function(e){var t;e.keyCode&&D(s,f),~g.inArray(e.keyCode,[f.keyDown,f.keyUp,f.keyEnter])?s.val(s.val()):(clearTimeout(n),n=setTimeout(function(){c||(t=s.val(),g.trim(t)&&t===k(s)||(k(s,t),f.multiWord&&(t=t.split(f.separator).reverse()[0]),(t.length||f.allowNoKeyword)&&f.fnGetData(g.trim(t),s,B,f)))},f.delay||300))}).on("focus.bs",function(){j(s,d,f)}).on("blur.bs",function(){e||(W(s,f),c=!0,setTimeout(function(){c=m}))}).on("click.bs",function(){var e=s.val();if(g.trim(e)&&e===k(s)&&d.find("table tr").length)return q(s,f);d.is(":visible")||(f.multiWord&&(e=e.split(f.separator).reverse()[0]),(e.length||f.allowNoKeyword)&&f.fnGetData(g.trim(e),s,B,f))}),l.find(".btn:eq(0)").attr("data-toggle","").click(function(){if(d.is(":visible"))W(s,f);else{if(f.url){if(s.click().focus(),!d.find("tr").length)return m}else B(s,f.data,f);q(s,f)}return m}),d.mouseenter(function(){e=1,s.blur()}).mouseleave(function(){e=0,s.focus()}).on("mouseenter","tbody tr",function(){return F(d,f),g(this).addClass(f.listHoverCSS),m}).on("mousedown","tbody tr",function(){var e=x(g(this));C(s,e,f),k(s,e.key),D(s,f),f.hideOnSelect&&W(s,f)}),u.length&&(u.click(function(){S(s,"").val(""),D(s,f)}),l.mouseenter(function(){s.prop(v)||u.css("right",f.showBtn?Math.max(s.next().width(),33)+2:12).show()}).mouseleave(function(){u.hide()})))})},show:function(){return this.each(function(){g(this).click()})},hide:function(){return this.each(function(){W(g(this))})},disable:function(){return this.each(function(){g(this).attr(v,b).parent().find(".btn:eq(0)").prop(v,b)})},enable:function(){return this.each(function(){g(this).attr(v,m).parent().find(".btn:eq(0)").prop(v,m)})},destroy:function(){return this.each(function(){g(this).off("click.bs keydown.bs compositionstart.bs compositionend.bs keyup.bs input.bs paste.bs focus.bs click.bs").removeData(p).removeAttr("style").parent().find(".btn:eq(0)").off().show().attr("data-toggle","dropdown").prop(v,m).next().css("display","").off()})},version:function(){return"0.1.29"}};g.fn[p]=function(e){if("string"==typeof e&&i[e]){var t=b;return this.each(function(){if(!g(this).data(p))return t=m}),t||"init"===e||"version"===e?i[e].apply(this,[].slice.call(arguments,1)):this}return i.init.apply(this,arguments)}}); diff --git a/ruoyi-admin/src/main/resources/templates/demo/form/autocomplete.html b/ruoyi-admin/src/main/resources/templates/demo/form/autocomplete.html index 571732404..b54881e4d 100644 --- a/ruoyi-admin/src/main/resources/templates/demo/form/autocomplete.html +++ b/ruoyi-admin/src/main/resources/templates/demo/form/autocomplete.html @@ -18,7 +18,7 @@
-