zoukankan      html  css  js  c++  java
  • iBase4j前端01_bootstrap-suggest json-server模拟后台数据、bootstrap-suggest环境搭建、开启bootstrap-suggest的post和put请求

    1 准备

      1.1 模拟的json数据

    {
        "info": [
            {
            "message": "信息",
            "value": [
                {
                    "userName": "淳芸",
                    "shortAccount": "chunyun",
                    "userId": 20001
                }, {
                    "userName": "orion-01",
                    "userName": "orion-01",
                    "shortAccount": "orion-01",
                    "userId": 20000
                }, {
                    "userName": "唐宏禹14",
                    "shortAccount": "TANGHONGYU",
                    "userId": 20011
                }, {
                    "userName": "唐宏禹13",
                    "shortAccount": "tanghongyu",
                    "userId": 20011
                }, {
                    "userName": "穆晓晨",
                    "shortAccount": "mUXIAOCHEN",
                    "userId": 20002
                }, {
                    "userName": "张欢引",
                    "shortAccount": "zhanghuanyin",
                    "userId": 20003
                }, {
                    "userName": "吴琼",
                    "shortAccount": "wuqiong",
                    "userId": 20004
                }, {
                    "userName": "吴东鹏",
                    "shortAccount": "wudongpeng",
                    "userId": 20005
                }, {
                    "userName": "黄少铅",
                    "shortAccount": "huangshaoqian",
                    "userId": 20006
                }, {
                    "userName": "胡运燕",
                    "shortAccount": "yunyan",
                    "userId": 20007
                }, {
                    "userName": "刘幸",
                    "shortAccount": "liuxing",
                    "userId": 20008
                }, {
                    "userName": "陈媛媛",
                    "shortAccount": "CHENYUANYUAN",
                    "userId": 20009
                }, {
                    "userName": "李大鹏",
                    "shortAccount": "dapeng",
                    "userId": 20010
                }, {
                    "userName": "唐宏禹",
                    "shortAccount": "tanghongyu",
                    "userId": 20011
                }, {
                    "userName": "旷东林",
                    "shortAccount": "kuangdonglin",
                    "userId": 20010
                }, {
                    "userName": "唐宏禹15",
                    "shortAccount": "TANGhongyu",
                    "userId": 20011
                }, {
                    "userName": "唐宏禹12",
                    "shortAccount": "tanghongyu",
                    "userId": 20011
                }, {
                    "userName": "唐宏禹11",
                    "shortAccount": "TangHongYu",
                    "userId": 20011
                }, {
                    "userName": "旷东林",
                    "shortAccount": "kuangdonglin",
                    "userId": 20010
                }, {
                    "userName": "唐宏禹10",
                    "shortAccount": "tanghongyu",
                    "userId": 20011
                }, {
                    "userName": "唐宏禹",
                    "shortAccount": "tanghongyu",
                    "userId": 20011
                }, {
                    "userName": "唐宏禹9",
                    "shortAccount": "tanghongyu",
                    "userId": 20011
                }, {
                    "userName": "唐宏禹8",
                    "shortAccount": "tanghongyu",
                    "userId": 20011
                }, {
                    "userName": "唐宏禹7",
                    "shortAccount": "tanghongyu",
                    "userId": 20011
                }, {
                    "userName": "旷东林",
                    "shortAccount": "kuangdonglin",
                    "userId": 20010
                }, {
                    "userName": "唐宏禹6",
                    "shortAccount": "tanghongyu",
                    "userId": 20011
                }, {
                    "userName": "唐宏禹5",
                    "shortAccount": "tanghongyu",
                    "userId": 20011
                }, {
                    "userName": "唐宏禹4",
                    "shortAccount": "tanghongyu",
                    "userId": 20011
                }, {
                    "userName": "唐宏禹3",
                    "shortAccount": "tanghongyu",
                    "userId": 20011
                }, {
                    "userName": "唐宏禹2",
                    "shortAccount": "tanghongyu",
                    "userId": 20011
                }, {
                    "userName": "唐宏禹1",
                    "shortAccount": "tanghongyu",
                    "userId": 20011
                }
            ],
            "code": 200,
            "redirect": ""
        }]
    }
    data.json

      1.2 模拟后台服务

        利用json-server模拟后台,json-server使用详解

        进入到模拟数据所在文件夹,执行下面的语句

    json-server 模拟数据文件 -p 启动端口

          例如:json-server data.json -p 8888

          启动效果如下:

        利用浏览器访问得到的响应如下:

    2 下载相关依赖包

      2.1 jquery

        https://jquery.com/download/

     

      2.2 bootstrap

        https://v3.bootcss.com/

      2.3 bootstrap-suggest

        https://github.com/lzwme/bootstrap-suggest-plugin

    3 编程步骤

      3.1 引入相关文件

        引入jquery、boostrap的css和js、引入bootstrap-suggest

      3.2 编写HTML代码

        技巧01:编写一个input和ul即可

        坑01:CSS类照着写,因为bootstrap-suggest依赖需要用到bootstrap相关的样式

      3.3 编写JS代码

      3.4 测试效果

      3.5 代码汇总

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Suggest使用案例</title>
    <meta name="description" content="">
    <meta name="keywords" content="">
    <link href="" rel="stylesheet">
    
    <link rel="stylesheet" type="text/css" href="../css/common.css">
    
    <!-- <link rel="stylesheet" type="text/css" href="../tool/bootstrap-3.3.7/dist/css/bootstrap-theme.min.css"> -->
    <link rel="stylesheet" type="text/css" href="../tool/bootstrap-3.3.7/dist/css/bootstrap.min.css">
    <link rel="stylesheet" type="text/css" href="../tool/bootstrap-3.3.7/dist/css/bootstrap.css">
    
    <script type="text/javascript" src="../tool/jquery/jquery-3.3.1.js"></script>
    <script type="text/javascript" src="../tool/bootstrap-3.3.7/dist/js/bootstrap.min.js"></script>
    <script type="text/javascript" src="../tool/bootstrap-suggest-plugin/dist/bootstrap-suggest.min.js"></script>
    
    <script type="text/javascript" src="../js/demo01_suggest.js"></script>
    
    </head>
    <body>
        <div class="panel panel-primary">
            <div class="panel-heading">测试</div>
            <div class="panel-body">
                <div class="input-group">
                    <input type="text" class="form-control" id="modalTest_input" autocomplete="off">
                    <div class="input-group-btn">
                        <button type="button" class="btn btn-default dropdown-toggle" data-toggle="">
                            <span class="caret"></span>
                        </button>
                        <ul class="dropdown-menu dropdown-menu-right" role="menu"></ul>
                    </div>
                </div>
            </div>
            <div class="panel-footer">
                <span id="Timer"></span>
            </div>
        </div>
        <hr class="hr">
    
    </body>
    </html>
    suggest.html
    $().ready(function() {
    
        $("#modalTest_input").bsSuggest({
                url: " http://localhost:8888/info",
                allowNoKeyword: true,   // 是否允许非关键字搜索【默认true】
                showHeader: true, // 是否显示表头【默认true】
                clearable: true, // 是否开启清空数据【默认false】
                showBtn: true, // 是否显示下拉按钮【默认false】
                multiWord: true, //以分隔符号分割的多关键字支持 
                separator: ",", //多关键字支持时的分隔符 
                getDataMethod: 'url', // 请求url
                effectiveFields: ["userName", "userId", "shortAccount"], // 显示的字段
                effectiveFieldsAlias:{userName: "姓名", userId: "用户ID", shortAccount: "账号"}, // 字段标题
                idField: "userId", // id字段
                keyField: "userName", // input元素中的显示字段
                fnPreprocessKeyword: function(keyword) { //请求数据前,对输入关键字作进一步处理方法。注意,应返回字符串
                    // alert("搜索关键字:" + keyword);
                    return keyword;
                },
                fnProcessData: function (result) {    // url 获取数据时,对数据的处理,作为 fnGetData 的回调函数
                var index, len, data = {value: []};
                var json = result[0];
    
                // 判断是否有响应数据
                if (!json || !json.value || json.value.length === 0) {
                    console.log("没有数据");
                    return false;
                }
                len = json.value.length;
                // 将获取到的数据放到data.value中
                for (index = 0; index < len; index++) {
                    data.value.push(json.value[index]);
                }
                //字符串转化为 js 对象 【只要获取到数据】
                return data;
            }
            }).on('onDataRequestSuccess', function (e, result) {
                console.log('onDataRequestSuccess: ', result);
            }).on('onSetSelectValue', function (e, keyword, data) {
                console.log("选择一个过后:");
                console.log('onSetSelectValue: ', keyword, data);
            }).on('onUnsetSelectValue', function () {
                console.log('onUnsetSelectValue');
            }).on('onShowDropdown', function (e, data) {
                console.log('onShowDropdown', e.target.value, data);
            }).on('onHideDropdown', function (e, data) {
                console.log('onHideDropdown', e.target.value, data);
            });
    
        // 每间隔一秒刷新时间
        setInterval("gettime()", 1000);
    
        //获取时间并设置格式
        gettime = function GetTime() {
            var mon, day, now, hour, min, ampm, time, str, tz, end, beg, sec;
            /*
            mon = new Array("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
                    "Sep", "Oct", "Nov", "Dec");
            */
            mon = new Array("一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月",
                    "九月", "十月", "十一月", "十二月");
            /*
            day = new Array("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat");
            */
            day = new Array("周日", "周一", "周二", "周三", "周四", "周五", "周六");
            now = new Date();
            hour = now.getHours();
            min = now.getMinutes();
            sec = now.getSeconds();
            if (hour < 10) {
                hour = "0" + hour;
            }
            if (min < 10) {
                min = "0" + min;
            }
            if (sec < 10) {
                sec = "0" + sec;
            }
            $("#Timer").html(
                now.getFullYear() + "年" + (now.getMonth() + 1) + "月" + now.getDate() + "日" + "  " + hour + ":" + min + ":" + sec
                );
        }
        
    })
    suggest.js

    4 bootstrap-suggest进阶

      4.1 原有bug

        原来的bootstrap-suggest框架仅仅支持get请求

      4.2 需求

        需要进行put或者post请求,而且还需要向后台传值

      4.3 修改bootstrap-suggeest的源代码

        技巧01:bootstrap-suggest中有一个ajax方法,该方法接收两个参数

          参数一 -> options -> 就是我们在自定义js文件对bootstrap-suggest的一些配置信息

          参数二 -> keyword -> 就是input标签中输入的值

    /**
     * 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-2016 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.');
        }
    })(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 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 val !== undefined ? $input.attr('alt', val) : $input.attr('alt');
        }
        /**
         * 设置或获取输入框的 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);
        }
        /**
         * 下拉列表刷新
         * 作为 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 || !dataList || !(len = dataList.length)) {
                if (options.emptyTip) {
                    showTip(options.emptyTip, $input, $dropdownMenu, options);
                } else {
                    $dropdownMenu.empty().hide();
                }
                return $input;
            }
    
            // 相同数据,不用继续渲染了
            if (
                options._lastData &&
                JSON.stringify(options._lastData) === JSON.stringify(data) &&
                $dropdownMenu.find('tr').length === len
            ) {
                $dropdownMenu.show();
                return adjustDropMenuPos($input, $dropdownMenu, options);
                // return $input;
            }
            options._lastData = data;
    
            // 生成表头
            if (options.showHeader) {
                html.push('<thead><tr>');
                for (field in dataList[0]) {
                    if (!inEffectiveFields(field, options)) {
                        continue;
                    }
    
                    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;
            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 (!keyValue && options.indexKey === index) {
                        keyValue = dataI[field];
                    }
                    if (!idValue && options.indexId === index) {
                        idValue = dataI[field];
                    }
    
                    index++;
    
                    // 列表中只显示有效的字段
                    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('')).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 就是自定义js文件的一些配置参数 keyword 就是搜索关键字母
         * @return {Object}   
         */
        function ajax(options, keyword) {
            keyword = keyword || '';
            // console.log(options); // 打印信息
            var preAjax = options._preAjax;
    
            if (preAjax && preAjax.abort && preAjax.readyState !== 4) {
                // console.log('abort pre ajax');
                preAjax.abort();
            }
            
            // 指定了非GET请求方式
            var type = options.type;
            if (type && type !='GET' && type != 'get') {
                if(options.param.keyword){
                    options.param.keyword = keyword;
                }else{
                    options.param['keyword'] = keyword;
                }
                
                var ajaxParam = {
                    type: type,
                    dataType: options.jsonp ? 'jsonp' : 'json',
                    timeout: 5000,
                    data: JSON.stringify(options.param)
                };
            } else {
                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"></i>')
                        .prependTo($input.parent());
            }
    
            return $iClear.css({
                position: 'absolute',
                top: 12,
                // right: options.showBtn ? Math.max($input.next('.input-group-btn').width(), 33) + 2 : 12,
                zIndex: 4,
                cursor: 'pointer',
                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 搜索时显示的提示内容,当搜索时间较长时给出正在搜索的提示
    
            /* 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'
            },                              // 列表的样式控制
            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 (undefined === 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)');
    
                    // 验证输入框对象是否符合条件
                    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);
                            $dropdownMenu.hide(); // .empty();
                        } 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) { // 不是进入下拉列表状态,则隐藏列表
                            $dropdownMenu.css('display', '');
                        }
                    }).on('click', function() {
                        // console.log('input click');
                        var word = $input.val();
    
                        if (
                            $.trim(word) &&
                            word === setOrGetAlt($input) &&
                            $dropdownMenu.find('table tr').length
                        ) {
                            return $dropdownMenu.show();
                        }
    
                        // if ($dropdownMenu.css('display') !== 'none') {
                        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() {
                        var display = 'none';
    
                        // if ($dropdownMenu.is(':visible')) {
                        if ($dropdownMenu.css('display') === display) {
                            display = 'block';
                            if (options.url) {
                                $input.click().focus();
                                if (!$dropdownMenu.find('tr').length) {
                                    display = 'none';
                                }
                            } else {
                                // 不以 keyword 作为过滤,展示所有的数据
                                refreshDropMenu($input, options.data, options);
                            }
                        }
    
                        $dropdownMenu.css('display', display);
                        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);
                            $dropdownMenu.hide();
                        });
    
                    // 存在清空按钮
                    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('.input-group-btn').width(), 33) + 2 : 12)
                                    .show();
                            }
                        }).mouseleave(function() {
                            $iClear.hide();
                        });
                    }
    
                });
            },
            show: function() {
                return this.each(function() {
                    $(this).click();
                });
            },
            hide: function() {
                return this.each(function() {
                    $(this).parent().find('ul:eq(0)').css('display', '');
                });
            },
            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);
            }
        }
    });
    bootstrap-suggest.js

      4.4 进行get请求

        技巧01:获取get请求的方式和原来的一样

      4.5 获取post、put请求

        技巧01:在bootstrap中的ajax方法中打印options来进行观察配置信息

        4.5.1 配置type

          技巧01:如果配置了type就必须配置param,而且里面params中不用包含一个keyword作为键的KV键值对,因为在bootstrap-suggest中会将input中输入的值作为keyword赋值给param的keyword属性【PS: 在js中为某个对象添加属性和值是直接写就行啦,不用提前声明;param.keyword=xxx,就相当于给param新增了一个keyword属性,而且将xxx赋值给这个属性】

          技巧02:第一次点击input框时,控制台打印出来的信息中没有keyword的信息,因为此时input中还没有任何数据,在input中输入数据后就有数据啦

          技巧03:在input中输入信息后就会自动向后台发送post请求,并将相关数据通过请求体发送到后台;但是这时候控制会报一个500错误,因为我们是使用json-server模拟后台数据,所以会报错;在实际应用中连接正确的后台就不会报错啦

        4.5.2 配置jsonp

          一样的配方,照着套路来就行啦

          技巧01:如果需要配置jsonp,就配置成 jsonp: "jsop" ;否则就不要做jsonp配置啦

          技巧02:控制台同样会报一个错,因为我们使用的是json-server模拟服务器

      4.6 获取选中信息

        选择一条记录后,input标签会出现关键字字段对应的值

        技巧01:可以在onSetSelectValue进行一些选中一条记录后的逻辑操作,例如,向后台发送请求等等

        技巧02:可以在onSetSelectValue方法中获取到配置的ID字段和关键字字段对应的值以及选中后的整条记录的值,例如

    5 利用SpringBoot搭建真实的后台

      待更新...2018年8月15日22:28:59

    6 参考信息

      本博文源码:点击前往

      https://www.npmjs.com/package/bootstrap-suggest-plugin

     

  • 相关阅读:
    中断类型表
    ComboBox中Datasource、 DisplayMember和ValueMember 三个属性问题 初学者
    ComboBox中的 DataSourc、 DisplayMember 、 valueMember 属性顺序设置问题 初学者
    皮尔逊相关度评价(Pearson correlation Score)
    欧几里得距离评价(Euclidean Distance Score)
    Leonardo da Vince
    维基F#译文: 值与函数(中)
    维基F#译文: 值与函数(上)
    perl5的引用(perlreftut)
    维基F#译文:入门
  • 原文地址:https://www.cnblogs.com/NeverCtrl-C/p/9484480.html
Copyright © 2011-2022 走看看