从零开始写项目第三篇【在线聊天和个人收藏夹】

Spring Boot 结合 WebSocket 实现在线聊天 今天的文章可谓是积蓄了我这几年来的应聘和面试经历总结出来的经验,干货满满呀!如果你能够一直坚持看到这儿,那么首先我还是十分佩服你的毅力的。所以看完之后,还是多多行动起来吧!可以非常负责地说,如果你能够坚持把我上面列举的内容都一个不拉地看完并且全部消化为自己的知识的话,那么你就至少已经达到了中级开发工程师以上的水平,进入大厂技术这块是基本没有什么问题的了。所以看完之后,还是多多行动起来吧! 阅读详情

在线聊天

在浏览网页的时候无意发现了弹幕这个玩意,于是我们简单去探究了一下弹幕其实是怎么产生的。

后来就接触到了“推送”这么一个概念,然后发现了goEasy这个服务商。

http://goeasy.io/cn/started

goEasy能够将我们的数据实时进行推送,使用起来也是很方便的。

使用了goEasy以后,我们就可以实现实时推送了。那么就剩下弹幕是怎么弄的了。后来又去找到了JS的组件:

http://yaseng.org/jquery.barrager.js/

这个组件可以帮我们很方便地生成弹幕…

<script type="text/javascript">    /*使用goEasy的推送功能*/    var goEasy = new GoEasy({        appkey: 'xxxxx'    });    /*按回车的时候会推送*/    $("#inputText").keydown(function () {        if (event.keyCode == 13) {            goEasy.publish({                channel: 'demo_channel',                message: $("#inputText").val()            });            //推送完清空输入框            $("#inputText").val("");        }    });    /*单击按钮的时候会推送*/    $("#btn").click(function () {        goEasy.publish({            channel: 'demo_channel',            message: $("#inputText").val()        });    });    /*接收数据、数据形式是弹幕*/    goEasy.subscribe({        channel: 'demo_channel',        onMessage: function (message) {            var txt = message.content;            var item = {                img: '${request.contextPath}/imgs/favicon.ico', //图片                info: $("#userNickname").val() +":"+ txt, //文字                href: 'www.baidu.com', //链接                close: true, //显示关闭按钮                speed: 8, //延迟,单位秒,默认8                bottom: 500, //距离底部高度,单位px,默认随机                color: '#fff', //字体颜色颜色,默认白色                old_ie_color: '#fff' //ie低版兼容色,不能与网页背景相同,默认黑色            };            $('body').barrager(item);        }    });</script>
   /*使用goEasy的推送功能*/
   var goEasy = new GoEasy({
       appkey: 'xxxxx'
   });

   /*按回车的时候会推送*/
   $("#inputText").keydown(function () {
       if (event.keyCode == 13) {
           goEasy.publish({
               channel: 'demo_channel',
               message: $("#inputText").val()
           });
           //推送完清空输入框
           $("#inputText").val("");
       }
   });

   /*单击按钮的时候会推送*/
   $("#btn").click(function () {
       goEasy.publish({
           channel: 'demo_channel',
           message: $("#inputText").val()
       });
   });

   /*接收数据、数据形式是弹幕*/
   goEasy.subscribe({
       channel: 'demo_channel',
       onMessage: function (message) {
           var txt = message.content;
           var item = {
               img: '${request.contextPath}/imgs/favicon.ico', //图片
               info: $("#userNickname").val() +":"+ txt, //文字
               href: 'www.baidu.com', //链接
               close: true, //显示关闭按钮
               speed: 8, //延迟,单位秒,默认8
               bottom: 500, //距离底部高度,单位px,默认随机
               color: '#fff', //字体颜色颜色,默认白色
               old_ie_color: '#fff' //ie低版兼容色,不能与网页背景相同,默认黑色
           };
           $('body').barrager(item);
       }
   });
</script>

实现效果:

640?wx_fmt=png

该功能主要是使用到了两个组件,现在的功能实现非常简单,后面有需要再补充进去吧。

个人收藏夹

由于经常用到的网址越来越多,经常需要找好一阵子才能找到我想要去的网站。即使浏览器有收藏夹这么一个功能…

640?wx_fmt=png

还是需要去找找找找的,因此我想自己建一个以关键搜索来跳转页面的功能。因为我的桌面版就是使用Rolan这么一个软件,输入关键字,就可以帮我打开软件了,很方便。

于是想起我学过Lucene这么一个全文搜索工具的,后来又发现了Elasticsearch是Lucene的子代,功能更强大也是一种比较新的技术了。后来我就去花了几天去学习Elasticsearch,找到了不少的资料。

学习Elasticsearch的过程已经在相关的博文中写过了,这里就不一一赘述了。

值得注意一点的是:当创建Elasticsearch的Client的时候,一定要加入嗅探这么一个配置:

.put("client.transport.sniff", true)true)

否则Elasticsearch在使用的时候会非常非常卡,简直令人怀疑人生。

自动补齐

经常上baidu、google的时候,当我们输入部分关键字的时候,那么会在下拉框中自动提示信息的。这个是怎么实现的呢???

Elasticsearch有suggest这么一个自动提示的功能,参考博文:

http://blog.youkuaiyun.com/gumpeng/article/details/50346631

http://www.tcao.net/article/86.html

http://blog.youkuaiyun.com/liyantianmin/article/details/60778273

具体的操作:

    //自动补全属性--------    .startObject("suggestName")    .field("type", "completion")    .field("analyzer", "standard")    .field("payloads", "true")    .endObject()    //自动补全属性结束--------
   .startObject("suggestName")
   .field("type", "completion")
   .field("analyzer", "standard")
   .field("payloads", "true")
   .endObject()
   //自动补全属性结束--------

当用户加入新的索引记录的时候,自动补齐字段就是我们的关键字段

    public static String String2JSON(String... strings) throws IOException {        String suggestName = strings[2];        if (suggestName.length() > 0) {        }        XContentBuilder builder = jsonBuilder()                .startObject()                .field("userId", strings[0])                .field("webSiteAddr", strings[1])                .field("webSiteName", strings[2])                //自动补全字段                .field("suggestName", strings[2])                .endObject();        return builder.string();    }

       String suggestName = strings[2];
       if (suggestName.length() > 0) {

       }
       XContentBuilder builder = jsonBuilder()
               .startObject()
               .field("userId", strings[0])
               .field("webSiteAddr", strings[1])
               .field("webSiteName", strings[2])
               //自动补全字段
               .field("suggestName", strings[2])
               .endObject();

       return builder.string();
   }

那么在查询的时候就非常好办了,首先查询Elasticserach所有的记录,再与提示字段来进行匹配,查询出来的数据就可以与关键字段相关了。

    /**     * 根据“命名”查询出数据(自动补全)     *     * @param client     * @param indexName     * @param condition 查询条件     */    public static List<String> queryIndexByConditionCompletion(TransportClient client, String indexName, String condition) {        String field = "suggestName";        //先查询所有结果再与提示字段的数据进行匹配        SearchRequestBuilder req = client.prepareSearch(indexName);        req.setQuery(QueryBuilders.matchAllQuery());        CompletionSuggestionBuilder csfb = new CompletionSuggestionBuilder(field).field(field).text(condition).size(100);        req.addSuggestion(csfb);        SearchResponse suggestResponse = req.execute().actionGet();        //遍历返回的结果、装载到集合中        List<String> lists = new ArrayList<String>();        List<? extends Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option>> results = suggestResponse.getSuggest().getSuggestion(field).getEntries();        for (Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option> op : results) {            List<? extends Suggest.Suggestion.Entry.Option> options = op.getOptions();            for (Suggest.Suggestion.Entry.Option pp : options) {                lists.add(pp.getText().toString());            }        }        return lists;    }
   public static List<String> queryIndexByConditionCompletion(TransportClient client, String indexName, String condition) {


       String field = "suggestName";

       //先查询所有结果再与提示字段的数据进行匹配
       SearchRequestBuilder req = client.prepareSearch(indexName);
       req.setQuery(QueryBuilders.matchAllQuery());
       CompletionSuggestionBuilder csfb = new CompletionSuggestionBuilder(field).field(field).text(condition).size(100);
       req.addSuggestion(csfb);
       SearchResponse suggestResponse = req.execute().actionGet();


       //遍历返回的结果、装载到集合中
       List<String> lists = new ArrayList<String>();
       List<? extends Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option>> results = suggestResponse.getSuggest().getSuggestion(field).getEntries();
       for (Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option> op : results) {

           List<? extends Suggest.Suggestion.Entry.Option> options = op.getOptions();
           for (Suggest.Suggestion.Entry.Option pp : options) {

               lists.add(pp.getText().toString());

           }
       }
       return lists;
   }

对于下拉框提示的是通过JS+CSS来进行展示出来的,我是参考了一篇博文,对其进行修改的:

那篇博文现在找不着了…我就贴出我的代码吧:

    <!--下拉框提示、请求后台-->    <script type="text/javascript">        $(function () {            //实现搜索输入框的输入提示js类            function oSearchSuggest(searchFuc) {                var input = $('#condition');                var suggestWrap = $('#gov_search_suggest');                var key = "";                var init = function () {                    input.bind('keyup', sendKeyWord);                    input.bind('blur', function () {                        setTimeout(hideSuggest, 100);                    })                }                var hideSuggest = function () {                    suggestWrap.hide();                }                //发送请求,根据关键字到后台查询                var sendKeyWord = function (event) {                    //键盘选择下拉项                    if (suggestWrap.css('display') == 'block' && event.keyCode == 38 || event.keyCode == 40) {                        var current = suggestWrap.find('li.hover');                        if (event.keyCode == 38) {                            if (current.length > 0) {                                var prevLi = current.removeClass('hover').prev();                                if (prevLi.length > 0) {                                    prevLi.addClass('hover');                                    input.val(prevLi.html());                                }                            } else {                                var last = suggestWrap.find('li:last');                                last.addClass('hover');                                input.val(last.html());                            }                        } else if (event.keyCode == 40) {                            if (current.length > 0) {                                var nextLi = current.removeClass('hover').next();                                if (nextLi.length > 0) {                                    nextLi.addClass('hover');                                    input.val(nextLi.html());                                }                            } else {                                var first = suggestWrap.find('li:first');                                first.addClass('hover');                                input.val(first.html());                            }                        }                        //输入字符                    } else {                        var valText = $.trim(input.val());                        if (valText == '' || valText == key) {                            return;                        }                        searchFuc(valText);                        key = valText;                    }                }                //请求返回后,执行数据展示                this.dataDisplay = function (data) {                    if (data.length <= 0) {                        suggestWrap.hide();                        return;                    }                    //往搜索框下拉建议显示栏中添加条目并显示                    var li;                    var tmpFrag = document.createDocumentFragment();                    suggestWrap.find('ul').html('');                    for (var i = 0; i < data.length; i++) {                        li = document.createElement('LI');                        li.innerHTML = data[i];                        tmpFrag.appendChild(li);                    }                    suggestWrap.find('ul').append(tmpFrag);                    suggestWrap.show();                    //为下拉选项绑定鼠标事件                    suggestWrap.find('li').hover(function () {                        suggestWrap.find('li').removeClass('hover');                        $(this).addClass('hover');                    }, function () {                        $(this).removeClass('hover');                    }).bind('click', function () {                        input.val(this.innerHTML);                        suggestWrap.hide();                    });                }                init();            }            //实例化输入提示的JS,参数为进行查询操作时要调用的函数名            var searchSuggest = new oSearchSuggest(sendKeyWordToBack);            //这是一个模似函数,实现向后台发送ajax查询请求,并返回一个查询结果数据,传递给前台的JS,再由前台JS来展示数据。本函数由程序员进行修改实现查询的请求            //参数为一个字符串,是搜索输入框中当前的内容            <!--搜索数据-->            function sendKeyWordToBack(keyword) {                var obj = {                    "condition": $("#condition").val()                };                $.ajax({                    type: "POST",                    url: path + "/favorites/querySiteCompletion.do",                    data: obj,                    dataType: "json",                    success: function (data) {                        console.log(data);                        var aData = [];                        for (var i = 0; i < data.length; i++) {                            //以下为根据输入返回搜索结果的模拟效果代码,实际数据由后台返回                            if (data[i] != "") {                                aData.push(data[i]);                            }                        }                        //将返回的数据传递给实现搜索输入框的输入提示js类                        searchSuggest.dataDisplay(aData);                    }                });            }        });    </script><script type="text/javascript">
       $(function () {
           //实现搜索输入框的输入提示js类
           function oSearchSuggest(searchFuc) {
               var input = $('#condition');
               var suggestWrap = $('#gov_search_suggest');
               var key = "";
               var init = function () {
                   input.bind('keyup', sendKeyWord);
                   input.bind('blur', function () {
                       setTimeout(hideSuggest, 100);
                   })
               }
               var hideSuggest = function () {
                   suggestWrap.hide();
               }
               //发送请求,根据关键字到后台查询
               var sendKeyWord = function (event) {
                   //键盘选择下拉项
                   if (suggestWrap.css('display') == 'block' && event.keyCode == 38 || event.keyCode == 40) {
                       var current = suggestWrap.find('li.hover');
                       if (event.keyCode == 38) {
                           if (current.length > 0) {
                               var prevLi = current.removeClass('hover').prev();
                               if (prevLi.length > 0) {
                                   prevLi.addClass('hover');
                                   input.val(prevLi.html());
                               }
                           } else {
                               var last = suggestWrap.find('li:last');
                               last.addClass('hover');
                               input.val(last.html());
                           }
                       } else if (event.keyCode == 40) {
                           if (current.length > 0) {
                               var nextLi = current.removeClass('hover').next();
                               if (nextLi.length > 0) {
                                   nextLi.addClass('hover');
                                   input.val(nextLi.html());
                               }
                           } else {
                               var first = suggestWrap.find('li:first');
                               first.addClass('hover');
                               input.val(first.html());
                           }
                       }
                       //输入字符
                   } else {
                       var valText = $.trim(input.val());
                       if (valText == '' || valText == key) {
                           return;
                       }
                       searchFuc(valText);
                       key = valText;
                   }
               }
               //请求返回后,执行数据展示
               this.dataDisplay = function (data) {
                   if (data.length <= 0) {
                       suggestWrap.hide();
                       return;
                   }
                   //往搜索框下拉建议显示栏中添加条目并显示
                   var li;
                   var tmpFrag = document.createDocumentFragment();
                   suggestWrap.find('ul').html('');
                   for (var i = 0; i < data.length; i++) {
                       li = document.createElement('LI');
                       li.innerHTML = data[i];
                       tmpFrag.appendChild(li);
                   }
                   suggestWrap.find('ul').append(tmpFrag);
                   suggestWrap.show();
                   //为下拉选项绑定鼠标事件
                   suggestWrap.find('li').hover(function () {
                       suggestWrap.find('li').removeClass('hover');
                       $(this).addClass('hover');
                   }, function () {
                       $(this).removeClass('hover');
                   }).bind('click', function () {
                       input.val(this.innerHTML);
                       suggestWrap.hide();
                   });
               }
               init();
           }
           //实例化输入提示的JS,参数为进行查询操作时要调用的函数名
           var searchSuggest = new oSearchSuggest(sendKeyWordToBack);
           //这是一个模似函数,实现向后台发送ajax查询请求,并返回一个查询结果数据,传递给前台的JS,再由前台JS来展示数据。本函数由程序员进行修改实现查询的请求
           //参数为一个字符串,是搜索输入框中当前的内容
           <!--搜索数据-->
           function sendKeyWordToBack(keyword) {
               var obj = {
                   "condition": $("#condition").val()
               };
               $.ajax({
                   type: "POST",
                   url: path + "/favorites/querySiteCompletion.do",
                   data: obj,
                   dataType: "json",
                   success: function (data) {
                       console.log(data);
                       var aData = [];
                       for (var i = 0; i < data.length; i++) {
                           //以下为根据输入返回搜索结果的模拟效果代码,实际数据由后台返回
                           if (data[i] != "") {
                               aData.push(data[i]);
                           }
                       }
                       //将返回的数据传递给实现搜索输入框的输入提示js类
                       searchSuggest.dataDisplay(aData);
                   }
               });
           }
       });
   
</script>

HTML和CSS,我用得是Bootstrap的。

    <!--搜索网站-->    <div class="row " style="position: absolute; margin:0px auto;text-align:center; ">        <div class="col-lg-6 gover_search">            <div class="input-group gover_search_form clearfix">                <input class="form-control input-lg " type="text" placeholder="请输入您自定义的网站名..." id="condition">                <span class="input-group-btn">                    <button class="btn btn-primary btn-lg" type="button" id="search">搜索</button>                </span>                <div class="search_suggest" id="gov_search_suggest">                    <ul>                    </ul>                </div>            </div>        </div>    </div>
   <div class="row " style="position: absolute; margin:0px auto;
text-align:center; "
>

       <div class="col-lg-6 gover_search">
           <div class="input-group gover_search_form clearfix">

               <input class="form-control input-lg " type="text" placeholder="请输入您自定义的网站名..." id="condition">
               <span class="input-group-btn">
                   <button class="btn btn-primary btn-lg" type="button" id="search">搜索</button>
               </span>
               <div class="search_suggest" id="gov_search_suggest">
                   <ul>

                   </ul>
               </div>
           </div>
       </div>
   </div>
    <!--搜索网站-->    <div class="row " style="position: absolute; margin:0px auto;text-align:center; ">        <div class="col-lg-6 gover_search">            <div class="input-group gover_search_form clearfix">                <input class="form-control input-lg " type="text" placeholder="请输入您自定义的网站名..." id="condition">                <span class="input-group-btn">                    <button class="btn btn-primary btn-lg" type="button" id="search">搜索</button>                </span>                <div class="search_suggest" id="gov_search_suggest">                    <ul>                    </ul>                </div>            </div>        </div>    </div>-->
   <div class="row " style="position: absolute; margin:0px auto;
text-align:center; ">
       <div class="col-lg-6 gover_search">
           <div class="input-group gover_search_form clearfix">

               <input class="form-control input-lg " type="text" placeholder="请输入您自定义的网站名..." id="condition">
               <span class="input-group-btn">
                   <button class="btn btn-primary btn-lg" type="button" id="search">搜索</button>
               </span>
               <div class="search_suggest" id="gov_search_suggest">
                   <ul>

                   </ul>
               </div>
           </div>
       </div>
   </div>

实现效果:

640?wx_fmt=png

增加网站

在增加网站的时候,我们希望以单一的名字来进行添加,因为我们是通过命名来进行搜索对应的网站的。

所以在添加网站的时候需要先判断有没有该命名才能继续添加、如果存在命名了,就不让它添加了。

代码如下:

    /**     * 插入数据进索引库中,在插入之前先判断该“命名”索引是否存在     *     * @param client     * @param indexName     * @param type     * @param json     * @param webSiteName     * @return     */    public static String insertIndexData(TransportClient client, String indexName, String type, String json, String webSiteName) {        //在插入之前、查看一下有没有该数据、如果有就不允许插入了【本站只允许命名是唯一的】        List<String> list = queryIndexByCondition(client, indexName, type, webSiteName);        if (list != null && list.size() > 0) {            return "hasWebSiteName";        } else {            client.prepareIndex(indexName, type).setSource(json).get();            return "success";        }    }
   public static String insertIndexData(TransportClient client, String indexName, String type, String json, String webSiteName) {


       //在插入之前、查看一下有没有该数据、如果有就不允许插入了【本站只允许命名是唯一的】
       List<String> list = queryIndexByCondition(client, indexName, type, webSiteName);

       if (list != null && list.size() > 0) {
           return "hasWebSiteName";
       } else {
           client.prepareIndex(indexName, type).setSource(json).get();
           return "success";
       }


   }

删除与查询网站

添加完网站之后,我们可以查询当前用户所有的网站,并可以删除

查询用户所有的网站我还使用到了Elasticsearch的分页功能:

    /**     * 根据用户id查询出索引记录、并对其进行分页     *     * @param client     * @param indexName     * @param type     * @param userId     * @param currentPage     * @return     */    public static Map<String, String> queryIndexById(TransportClient client, String indexName, String type, String userId, Integer currentPage) {        QueryBuilder query = QueryBuilders.matchQuery("userId", userId);        //接收返回的数据        Map<String, String> map = new HashMap();        //计算出开始取的页数        int startIndex = (currentPage - 1) * EsUtilsPro.PAGE_SIZE;        //查询出总记录数        Long totalRecordCount = ElasticsearchUtils.queryTotalCount(client, indexName, type, userId);        //查询出总页数        Long totalPageCount = totalRecordCount % EsUtilsPro.PAGE_SIZE == 0 ? totalRecordCount / EsUtilsPro.PAGE_SIZE : totalRecordCount / EsUtilsPro.PAGE_SIZE + 1;        SearchResponse response = client.prepareSearch(indexName).setTypes(type).setQuery(query).setFrom(startIndex).setSize(EsUtilsPro.PAGE_SIZE).execute()                .actionGet();        SearchHits hits = response.getHits();        //返回一个最高匹配那个对象就好了。        if (hits.totalHits() > 0) {            for (SearchHit hit : hits) {                map.put(hit.getId(), hit.getSourceAsString());            }        }        map.put("currentPage", String.valueOf(currentPage));        map.put("totalRecordCount", String.valueOf(totalRecordCount));        map.put("totalPageCount", String.valueOf(totalPageCount));        return map;    }
   public static Map<String, String> queryIndexById(TransportClient client, String indexName, String type, String userId, Integer currentPage) {

       QueryBuilder query = QueryBuilders.matchQuery("userId", userId);

       //接收返回的数据
       Map<String, String> map = new HashMap();

       //计算出开始取的页数
       int startIndex = (currentPage - 1) * EsUtilsPro.PAGE_SIZE;

       //查询出总记录数
       Long totalRecordCount = ElasticsearchUtils.queryTotalCount(client, indexName, type, userId);

       //查询出总页数
       Long totalPageCount = totalRecordCount % EsUtilsPro.PAGE_SIZE == 0 ? totalRecordCount / EsUtilsPro.PAGE_SIZE : totalRecordCount / EsUtilsPro.PAGE_SIZE + 1;

       SearchResponse response = client.prepareSearch(indexName).setTypes(type).setQuery(query).setFrom(startIndex).setSize(EsUtilsPro.PAGE_SIZE).execute()
               .actionGet();
       SearchHits hits = response.getHits();


       //返回一个最高匹配那个对象就好了。
       if (hits.totalHits() > 0) {
           for (SearchHit hit : hits) {
               map.put(hit.getId(), hit.getSourceAsString());
           }
       }

       map.put("currentPage", String.valueOf(currentPage));
       map.put("totalRecordCount", String.valueOf(totalRecordCount));
       map.put("totalPageCount", String.valueOf(totalPageCount));

       return map;

   }

查询总记录数

    /**     * 根据用户id查询该用户拥有的总记录数     *     * @param client     * @param indexName     * @param type     * @param userId     * @return     */    public static Long queryTotalCount(TransportClient client, String indexName, String type, String userId) {        QueryBuilder query = QueryBuilders.matchQuery("userId", userId);        SearchResponse response = client.prepareSearch(indexName).setTypes(type).setQuery(query).execute().actionGet();        return response.getHits().totalHits();    }
   public static Long queryTotalCount(TransportClient client, String indexName, String type, String userId) {

       QueryBuilder query = QueryBuilders.matchQuery("userId", userId);

       SearchResponse response = client.prepareSearch(indexName).setTypes(type).setQuery(query).execute().actionGet();


       return response.getHits().totalHits();

   }

使用Map集合的原因是因为我们要在查询的基础上实现删除的功能,要想删除就必须知道当前记录的id,因此就使用到了Map集合了。

   <!--根据Id查询索引记录-->    <script>        $(function () {            $("#queryById").click(function () {                var currentPage = $("#currentPage").val();                if (currentPage == null || currentPage == "") {                    currentPage = 1;                }                $.ajax({                    url: path + "/favorites/querySiteById.do",                    type: "post",                    data: {userId: $("#userId").val(), currentPage: currentPage},                    success: function (responseText) {                        //返回的是一个Map集合,我们遍历Map集合,                        for (var index in responseText) {                            //获取Map的value,index就代表Map的key                            var jsonObj = responseText[index]                            if (index.length > 16) {                                //获取JSON对象                                var data = eval("(" + jsonObj + ")");                                $("#manageSiteContent").before("<tr><td><input type='text' value=" + data.webSiteAddr + " ></input></td><td><input type='text' value=" + data.webSiteName + " ></input></td><td><a href='${request.contextPath}/favorites/deleteSiteById.do?indexId=" + index + "'>删除</a></td></tr>");                            } else {                                if (index == "currentPage") {                                    $("#currentPage").val(jsonObj);                                } else if (index == "totalPageCount") {                                    $("#totalPageCount").val(jsonObj);                                } else {                                    $("#totalRecordCount").val(jsonObj);                                }                            }                        }                        createPage($("#currentPage").val(), $("#totalPageCount").val(), $("#totalRecordCount").val());                    },                    error: function () {                        sweetAlert("系统错误了");                    }                });            });        });    </script><script>
       $(function () {
           $("#queryById").click(function () {
               var currentPage = $("#currentPage").val();
               if (currentPage == null || currentPage == "") {
                   currentPage = 1;
               }
               $.ajax({
                   url: path + "/favorites/querySiteById.do",
                   type: "post",
                   data: {userId: $("#userId").val(), currentPage: currentPage},
                   success: function (responseText) {
                       //返回的是一个Map集合,我们遍历Map集合,
                       for (var index in responseText) {
                           //获取Map的value,index就代表Map的key
                           var jsonObj = responseText[index]
                           if (index.length > 16) {
                               //获取JSON对象
                               var data = eval("(" + jsonObj + ")");
                               $("#manageSiteContent").before("<tr><td><input type='text' value=" + data.webSiteAddr + " ></input></td><td><input type='text' value=" + data.webSiteName + " ></input></td><td><a href='${request.contextPath}/favorites/deleteSiteById.do?indexId=" + index + "'>删除</a></td></tr>");
                           } else {
                               if (index == "currentPage") {
                                   $("#currentPage").val(jsonObj);
                               } else if (index == "totalPageCount") {
                                   $("#totalPageCount").val(jsonObj);
                               } else {
                                   $("#totalRecordCount").val(jsonObj);
                               }
                           }
                       }
                       createPage($("#currentPage").val(), $("#totalPageCount").val(), $("#totalRecordCount").val());
                   },
                   error: function () {
                       sweetAlert("系统错误了");
                   }
               });
           });
       });
   
</script>

生成分页的样式、并指定当前页数(i就作为当前的页数)

    <!--生成分页样式-->    <script>        function createPage(totalPageCount) {            for (var i = 1; i <= totalPageCount; i++) {                $(".pagination").append("<li><a href='#' onclick='queryPage(" + i + ")'>" + i + "</a></li>");            }        }    </script><script>
       function createPage(totalPageCount) {
           for (var i = 1; i <= totalPageCount; i++) {
               $(".pagination").append("<li><a href='#' onclick='queryPage(" + i + ")'>" + i + "</a></li>");
           }
       }
   
</script>

异步查询分页数据、在查询之前先把原有的记录删除掉。

    <!--异步查询分页数据-->    <script>        function queryPage(currentPage) {            //把原有的数据删除掉            $("#manageSiteTr").siblings().empty();            $.ajax({                url: path + "/favorites/querySiteById.do",                type: "post",                data: {userId: $("#userId").val(), currentPage: currentPage},                success: function (responseText) {                    //返回的是一个Map集合,我们遍历Map集合,                    for (var index in responseText) {                        //获取Map的value,index就代表Map的key                        var jsonObj = responseText[index]                        if (index.length > 16) {                            //获取JSON对象                            var data = eval("(" + jsonObj + ")");                            $("#manageSiteContent").before("<tr><td><input type='text' value=" + data.webSiteAddr + " ></input></td><td><input type='text' value=" + data.webSiteName + " ></input></td><td><a href='${request.contextPath}/favorites/deleteSiteById.do?indexId=" + index + "'>删除</a></td></tr>");                        } else {                            if (index == "currentPage") {                                $("#currentPage").val(jsonObj);                            } else if (index == "totalPageCount") {                                $("#totalPageCount").val(jsonObj);                            } else {                                $("#totalRecordCount").val(jsonObj);                            }                        }                    }                },                error: function () {                    sweetAlert("系统错误了");                }            });        }    </script><script>
       function queryPage(currentPage) {
           //把原有的数据删除掉
           $("#manageSiteTr").siblings().empty();
           $.ajax({
               url: path + "/favorites/querySiteById.do",
               type: "post",
               data: {userId: $("#userId").val(), currentPage: currentPage},
               success: function (responseText) {
                   //返回的是一个Map集合,我们遍历Map集合,
                   for (var index in responseText) {
                       //获取Map的value,index就代表Map的key
                       var jsonObj = responseText[index]
                       if (index.length > 16) {
                           //获取JSON对象
                           var data = eval("(" + jsonObj + ")");
                           $("#manageSiteContent").before("<tr><td><input type='text' value=" + data.webSiteAddr + " ></input></td><td><input type='text' value=" + data.webSiteName + " ></input></td><td><a href='${request.contextPath}/favorites/deleteSiteById.do?indexId=" + index + "'>删除</a></td></tr>");
                       } else {
                           if (index == "currentPage") {
                               $("#currentPage").val(jsonObj);
                           } else if (index == "totalPageCount") {
                               $("#totalPageCount").val(jsonObj);
                           } else {
                               $("#totalRecordCount").val(jsonObj);
                           }
                       }
                   }
               },
               error: function () {
                   sweetAlert("系统错误了");
               }
           });
       }
   
</script>

最终的效果如下:

640?wx_fmt=png
640?wx_fmt=png

如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:Java3y


网络收藏夹 小弟初学ASP-现在不用了-的时候,写的一个在线网络收藏夹,用是没什么用,不过对想学习ASP等简单语言的盆友来说,也许大概有点启发作用吧!文件打包完整体上传,供弟兄们参考。 立即下载

相关推荐

Mellanox ASAP2技术揭秘:如何通过硬件卸载提升OVS性能?

本文深入解析Mellanox ASAP2技术如何通过硬件卸载显著提升Open vSwitch(OVS)性能。通过将数据平面操作转移到ConnectX智能网卡处理,ASAP2实现了10倍吞吐量提升和90%延迟降低,同时大幅减少CPU占用。文章包含技术原理剖析、实战配置指南及性能对比数据,为网络虚拟化优化提供关键解决方案。

weixin_30335575的博客 373

从零开始写项目【总结】

从零开发项目概述最近这一直在复习数据结构和算法,也就是前面发出去的排序算法八大基础排序总结,Java实现单向链表,栈和队列就是这么简单,十道简单算法题等等…被虐得不要不要...

3y 1134

Allegro老鸟的20年效率秘籍:用YepEda Skill脚本实现PCB设计全流程自动化

本文分享了Allegro资深工程师20年效率秘籍,通过YepEda Skill脚本实现PCB设计全流程自动化。从封装建库到规则配置,再到DFM审核,YepEda Skill大幅提升设计效率,每天节省至少4小时重复劳动。文章详细解析了自动化工具链的核心功能,并提供了实战技巧和量化对比数据。

weixin_42546392的博客 239

【Java EE】从零开始写项目【总结】

从零开发项目概述 最近这一直在复习数据结构和算法,也就是前面发出去的排序算法八大基础排序总结,Java实现单向链表,栈和队列就是这么简单,十道简单算法题等等… 被虐得不要不要的,即使是非常简单有时候绕半天,想不明白。 本来这篇博文是想留到我最后Java基础复习完再写出来的,鉴于公众号有的同学想看看邮箱登陆的案例,想看看Java开发后台是怎么的,于是我就提前写了这篇博文 这个项目是我自己手写...

3y 1009

从零开始学习信号完整性(SIPI)--1 简介

信号完整性就是研究如何让驱动芯片发出的信号经过传输通道被接收芯片正确接收的学问。 信号完整性包含了三个要素: 信号:是被传输的主体,我们首先要了解、熟悉信号的特性。 芯片:分为驱动芯片和接收芯片,信号完整性工程师就是要实现信号在驱动芯片和接收芯片之间的正确传输。 传输通道:是传输信号的介质,是信号所走的“路”。如何实现信号的正确传输呢?那就要信号完整性工程师为信号“修路”、“搭桥”。

qq_29476769的博客 4万+

数字蓝牙功放

数字蓝牙功放(TPA3110+BK8000L),基于D类数字功放芯片TPA3116蓝牙功放,HIFI级带滤波15Wx2蓝牙数字功放,支持有线和蓝牙两种音源输入

runweipa的博客 1419

多语言国际版在线聊天室/匿名在线聊天室/语音聊天室/网页APP聊天室

私人聊天消息/一对一聊天 私人或一对一聊天功能可供用户无缝聊天。他们可以使用此功能私下分享任何他们想要的东西。管理员可以通过管理仪表板轻松管理私人聊天。 多语言支持 QuChat带有一个内置的语言和翻译管理器。您可以有效地添加自己的语言和翻译。QuChat可以翻译成任何语言,包括RTL 语言。它支持任何 Unicode 字符集。 密码保护的聊天室 用户可以在他们的聊天室中添加图钉。有密码的人可以进入聊天室。这样,您就可以拥有受密码保护的聊天室。 访客记录 管理员可以允许访客访问 QuChat。您可以为单个

qq_34237088的博客 4219

DBeaver连接IoTDB避坑指南:手动配置JDBC驱动的完整流程(附问题排查)

本文提供了在DBeaver中手动配置Apache IoTDB JDBC驱动的完整流程与避坑指南。详细解析了从驱动创建、库文件添加(关键是指向整个lib目录)到连接测试的每一步,并重点针对常见的ClassNotFoundException、连接拒绝等错误提供了深度排查方案,帮助用户高效建立图形化管理连接。

weixin_28327051的博客 396

Gmail聊天

    今天Gmail终于可以实现Gtalk的功能了,以后就不必再下载客户端,只要有一个Gmail帐号就可以在线聊天。也不必再担心被网管抓了,哈哈。,这是不是意味着Gtalk没有存在的必要了呢?    前段时间GOOGLE还推出了新的Toolbar,可以邮件提醒,还有一个不错的功能就是在线的Bookmark,不用担心电脑坏了,收藏夹的内容丢失了,走到哪都可以用。    当然如果还能加上一个在线的日

jxyuhua的地盘 1676

4、I3C协议详解

I3C最初的设计目的是为移动设备创建一个能够使用多个传感器的单一接口。随着现代化移动设备对于传感器数量的增加以及对性能的提高,I2C和SPI已经达到了他们所能支持的临界点,而I3C的出现就是为了解决这一问题,I3C可以在同一根主线上支持更多的传感器设备,而且不会因为要支持中断或睡眠模式而增加额外的逻辑信号。I3C可以在更低功耗的情况下提供更快的传输速率,该特性的提升不止是对移动设备,对其他的嵌入式设备也都是很大的帮助。例如穿戴设备,需要在有限的物理空间中使用多个传感器并且要求更小的耗电。 一、简述..

迟子涵925的博客 2万+

Tomcat启动过程中cmd窗口(控制台)中文乱码的问题

tomcat启动时,启动过程日志中文乱码的处理方法。

寻码启示 1944

从零开始写项目第二篇【登陆注册、聊天、收藏夹模块】

登陆模块目标我要将其弄成类似的登陆,功能是要比较完善的。本来我是想做一步写一步的,但是发现这样文章就会太乱,因为要改的地方太多了。前面写过的,后边就被修改了。这样看起来太...

3y 670

含分布式电源的配电网三段式电流保护影响仿真研究

文章摘要 本文研究了分布式电源(DG)接入对配电网三段式电流保护的影响。通过在Matlab/Simulink平台搭建含同步发电机型DG的10kV配电网模型,设计了基于傅里叶算法的三段式电流保护模块(I段瞬时速断、II段限时速断、III段定时限过流)。研究分析了不同DG接入位置、容量及故障点对保护系统的影响机理。仿真结果表明,DG接入会改变故障电流特性,可能导致三种典型问题:保护范围扩大引起误动、保护灵敏度下降导致拒动,以及保护选择性丧失。该研究为含DG配电网的保护配置与整定提供了理论依据和仿真参考,对保障分

huanghm88的专栏 268

批量导入html收藏夹谷歌,怎么在电脑谷歌浏览器内完成书签和收藏夹的导入操作...

怎么在电脑谷歌浏览器内完成书签和收藏夹的导入操作当我们在使用电脑的时候,可以下载谷歌浏览器来进行网页的浏览,如果想要将其他浏览器内的书签与收藏夹导入至电脑谷歌浏览器内,应如何操作呢?接下来就由小编来告诉大家。具体如下:1. 第一步,打开电脑并在桌面找到谷歌浏览器图标,双击打开应用并进入主页后,点击右上角的三点图标。2. 第二步,点击下拉菜单中的设置选项。3. 第三步,在打卡的设置页面内,点击下方的...

weixin_39936792的博客 2172

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

如何在iPhone上将联系人添加到收藏夹

The Phone app on your iPhone has a convenient “Favorites” section where you can find your most important contacts. Here’s how to choose which contacts appear here. iPhone上的“电话”应用程序具有方便的“收藏夹”部分,您可以在其中...

966

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

【免费下载】 POETIZE个人博客系统源码 最美博客

POETIZE个人博客系统源码 最美博客 【下载地址】POETIZE个人博客系统源码最美博客 POETIZE个人博客系统源码 最美博客是一个基于SpringBoot、Vue2和Vue3开发的综合性个人博客系统。该系统不仅支持移动端自适应,还提供了完备的前台和后台管理功能。系统分为两个主要模块:博客系统和聊天室系统 ...

gitblog_09752的博客 1710

微信聊天机器人搭建 教程/开发

现在并没有长期免费的微信群机器人,很多都是前期免费试用,后期进行收费,或者核心功能需要付费使用的。这时如果需要群机器人帮助我们管理群聊,建议大家使有条件的可以自己开发微信管理系统。了解微信群机器人的朋友都知道,微信管理有很多功能尚未开发出来,很多软件功能其实并不齐全,那么自主开发的微信群机器人可以有哪些功能呢?个人微信:自动接收好友请求,并自动回复;设置聊天机器人;根据关键词自动回复图片、文章、文件、视频、甚至是群邀请链接等等;微信群:群聊欢迎语;自动移除发放广告成员;关键词回复(淘宝常用)

2503_90618378的博客 374

个人开源项目总结

本项目为Qt实现企业微信界面项目,纯界面逻辑,不包含真实业务逻辑。主要用于学习QWidget的使用,包含绘制,布局,信号,事件,重写,多线程,qss等技术使用。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uPNBf3gw-1664262417192)(https://gitee.com/hudejie/wecom-copy/badge/star.svg?项目地址:https://gitee.com/hudejie/wecom-copy本项目为Qt实现笔记本软件。

qq_25549309的博客 1863

功能全面的二手交易系统源码,在线聊天、支付、社交一应俱全

该系统是一款基于ThinkPHP和Uniapp的二手交易平台源码,集成商品发布、智能搜索、实时聊天、安全支付及社交功能。支持多图上传、在线询价、订单管理,同时提供高价回收服务和社区互动板块。采用开源架构,允许二次开发,包含详细技术文档,确保平台稳定运行和自定义扩展。支付流程加密保障安全,社交功能增强用户粘性,适合各类二手交易场景快速部署。

春哥开源研究所的博客 373

21 从“收藏吃灰“到高效知识管理:用腾讯ima打造个人知识库

从"收藏吃灰"到高效知识管理:用腾讯ima打造个人知识库

PeNg_Dr1988 2231

微信个人号API接口,个人号开发

所有个人号模块分析: 登录模块 登录微控平台 member/login 获取微信二维码 user/login 执行微信登录 getIPadLoginInfo 获取联系人列表(群、好友) getAllContact 二次登录(退出微信号 需要再次登录 调用此接口即可) secondLogin 退出微信 logout 消息接收模块 设置消息接收地址 setHttpCallbackUrl 取消消息接收 cancelHttpCallbackUrl 消息发送模块 发送文本消息 sendText 发送图片消息

seeseeyoua的博客 3793

SpringBoot + Vue2/3 混合前端 POETIZE 个人博客系统:100% 可用、功能超全,带独立聊天室,助你免踩坑轻松毕业!

【毕设推荐】SpringBoot+Vue2/3混合开发POETIZE个人博客系统,集博客与聊天室于一体,助你轻松完成毕业设计!项目采用主流技术栈(SpringBoot/Vue/MySQL),功能齐全(文章管理、多媒体展示、表白墙、独立聊天室),100%可用无报错,支持移动端适配。亮点包括:①混合前端技术(Vue2+Vue3)②完善后台管理系统③WebSocket实时通讯④开箱即用配置。系统架构清晰,涵盖用户认证、文章服务、媒体管理等功能模块,适合Java全栈开发学习与实践。获取完整源码请留言"求源

dragon 1141

[特殊字符] GitHub 2025年7月月度精选项目 Top5

项目关键词背后趋势ChatDev多智能体、多角色AIAI自动化开发框架Linkwarden信息管理、本地部署私有云工具+知识效率LobeChat多模型Chat界面AI产品化+开源商业MonaspaceUI/UX、字体体验开发者体验优化AI-RealtorRAG+行业AI助手AI垂直落地探索如果你:想找些能跑起来的 AI 项目练手想做技术自媒体搬运+演示变现或是单纯对开源好奇建议你每月关注 GitHub Trending,这里不仅有技术,还有灵感和机会。

2403_87082399的博客 2162
上一篇: 从零开始写项目第四篇【搭建Linux环境】
下一篇: 从零开始写项目第五篇【评论功能、备忘录】
Java3y
Java3y 领域专家: 后端开发技术领域 领域专家: 后端开发技术领域
博客等级 码龄9年 1万+粉丝 509原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值