当前位置:   article > 正文

PageHelper实现多条件的分页查询,封装页码按钮的生成逻辑,对接后端接口_pagehelper后接多个查询

pagehelper后接多个查询

效果图

难点分析

PageHelper的引入和使用,这里我就不多说了。有了PageHelper,后端的分页查询就非常简单了。

要实现携带多条件的分页查询,前端要如何跟后端接口对接呢?在开始代码之前,先来依次思考和分析下面的问题,然后再来整理思路,最后看代码。

问题1:前端需要传给后端什么参数?

(1)page:当前页。理应由前端传给后端,但是前端的传参有可能超出实际范围(比如说,一共才有10页,用户传参11,那么后端应该返回第10页的数据,而不应该返回空数据),这就必须交由后端来纠正之后,再查询对应页码的数据。然后后端将纠正后的当前页返回给前端, 以便前端来渲染页码按钮组。如果不传,由后端返回默认值1。

(2)count:每页显示的记录数,由前端传给后端。如果不传,使用后端定义的默认值。

(3)各种条件参数。以上面的效果图为例,需要传给后端 4 个条件参数:① 商品名称关键词:prodName;②商品种类:cate;③最低价格:minPrice;④最高价格:maxPrice。

问题2:条件以什么方式提交给后端?        

  前端访问后端接口有 3 种情况,以效果图为例分析:

(1)第一次打开页面,以Get方式,此时没有携带任何条件

(2)点击 “查询” 按钮,以Post方式提交表单,此时携带多个条件,且有些提交可能为空

(3)点击页码按钮切换页面时,由于是通过a标签跳转,所以是Get方式。跳转时,有可能携带多个条件。

   所以接口需要使用 @RequestMapping 注解

问题3:后端应该返回什么数据给前端?

(1)当前页的列表数据。如:List<Product>

(2)page:被后端纠正之后的当前页。用于生成分页按钮。

(3)total:总记录数。用于生成分页按钮。

(4)count:每一页显示的记录数,也有可能被后端纠正。用于生成分页按钮。

(5)urlParamsStr:由于点击页码按钮时,通过a标签跳转,可能需要携带多个条件,而且只能通过url参数的形式携带过去。所以后端需要将多个条件拼接成字符串,返回给前端,方便前端生成分页按钮。

问题4:生成页码按钮需要什么参数?如何封装页码按钮的逻辑?

(1)page:被后端纠正之后的当前页。用于生成分页按钮。

(2)total:总记录数。用于生成分页按钮。

(3)count:每一页显示的记录数,也有可能被后端纠正。用于生成分页按钮。

要想生成页码按钮,上面3个参数,必不可少!!!至于页码按钮的生成逻辑,参见我的另一篇博客:https://blog.csdn.net/qq_43290318/article/details/111601738

后端代码

controller层

  1. package easymall.controller;
  2. import java.util.List;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Controller;
  5. import org.springframework.ui.Model;
  6. import org.springframework.util.ObjectUtils;
  7. import org.springframework.web.bind.annotation.ModelAttribute;
  8. import org.springframework.web.bind.annotation.RequestMapping;
  9. import com.github.pagehelper.PageHelper;
  10. import com.github.pagehelper.PageInfo;
  11. import easymall.po.Products;
  12. import easymall.pojo.ProdListReqParamsVo;
  13. import easymall.service.ProductsService;
  14. @Controller("productsController")
  15. public class ProductsController {
  16. @Autowired
  17. private ProductsService productsService;
  18. /**
  19. * 该接口必须支持post和get方式访问
  20. * 因为表单提交是post方式,而第一次打开页面和点击页码切换页面都是get方式
  21. *
  22. * @param page 当前页。分页所需参数,如果前端不传,则默认为1
  23. * @param count 每页显示多少条记录。分页所需参数,如果前端不传,则默认为2
  24. */
  25. @RequestMapping("/prodlist")
  26. public String prodlist(@ModelAttribute("params") ProdListReqParamsVo params,
  27. Integer page, Integer count, Model model) {
  28. // 参数检查和纠正
  29. if (!ObjectUtils.isEmpty(params.getMinPrice()) &&
  30. !ObjectUtils.isEmpty(params.getMaxPrice())) {
  31. // 纠正 minPrice为非负数
  32. if (params.getMinPrice() < 0) {
  33. params.setMinPrice(0d);
  34. }
  35. // 纠正为 minPrice <= maxPrice
  36. if (params.getMinPrice() > params.getMaxPrice()) {
  37. double min = params.getMinPrice();
  38. params.setMinPrice(params.getMaxPrice());
  39. params.setMaxPrice(min);
  40. }
  41. }
  42. // curPage 是否越界,可不需要判断,PageHelper内部会判断并纠正
  43. if (ObjectUtils.isEmpty(page)) {
  44. page = 1;
  45. }
  46. if (ObjectUtils.isEmpty(count) || count <= 0) {
  47. count = 2;
  48. }
  49. // 查询所有分类
  50. List<String> cates = productsService.allcategorys();
  51. // 调用PageHelper进行分页
  52. // 紧跟在这个方法后的第一个MyBatis 查询方法会被进行分页
  53. PageHelper.startPage(page, count);
  54. // 查询数据
  55. List<Products> prodList = productsService.getProdListByConds(params);
  56. // 获取各种分页属性
  57. PageInfo<Products> pageInfo = new PageInfo<>(prodList);
  58. // 将有效的参数拼接成url字符串,用于拼接到url后面。切换页码时携带
  59. String urlParamsStr = params.joinUrlParams();
  60. model.addAttribute("cates", cates); // 分类数据
  61. model.addAttribute("prodList", prodList); // 商品列表数据
  62. model.addAttribute("page", pageInfo.getPageNum()); // 传给前端被修正后的当前页
  63. model.addAttribute("count", count); // 每一页显示多少条记录
  64. model.addAttribute("total", pageInfo.getTotal()); // 总记录数
  65. model.addAttribute("urlParamsStr", urlParamsStr);
  66. return "prod_list";
  67. }
  68. }

vo

  1. package easymall.pojo;
  2. import java.io.UnsupportedEncodingException;
  3. import java.net.URLEncoder;
  4. import org.springframework.util.ObjectUtils;
  5. /**
  6. * 商品列表接口的请求参数
  7. *
  8. * @author passerbyYSQ
  9. * @date 2020-11-30 19:49:08
  10. */
  11. public class ProdListReqParamsVo {
  12. // 商品名称关键词。可以为空
  13. private String prodName;
  14. // 分类名字。可以为空,为空时表示所有分类
  15. private String cate;
  16. // 最低价格。不允许为负数,且 minPrice <= maxPrice
  17. private Double minPrice;
  18. // 最高价格。不允许为负数,且 minPrice <= maxPrice
  19. private Double maxPrice;
  20. /**
  21. * 将有效的参数拼接成url字符串,用于拼接到url后面。切换页码时携带
  22. */
  23. public String joinUrlParams() {
  24. StringBuilder urlParamsStr = new StringBuilder("");
  25. if (prodName != null) { // 可以为空串
  26. urlParamsStr.append("&prodName=").append(prodName);
  27. }
  28. if (cate != null) { // 可以为空串
  29. String cateTmp = cate;
  30. try {
  31. // 对中文进行url编码
  32. cateTmp = URLEncoder.encode(cate, "UTF-8");
  33. } catch (UnsupportedEncodingException e) {
  34. e.printStackTrace();
  35. }
  36. urlParamsStr.append("&cate=").append(cateTmp);
  37. }
  38. if (!ObjectUtils.isEmpty(minPrice)) {
  39. urlParamsStr.append("&minPrice=").append(minPrice);
  40. }
  41. if (!ObjectUtils.isEmpty(maxPrice)) {
  42. urlParamsStr.append("&maxPrice=").append(maxPrice);
  43. }
  44. return urlParamsStr.toString();
  45. }
  46. public String getProdName() {
  47. return prodName;
  48. }
  49. public void setProdName(String prodName) {
  50. this.prodName = prodName;
  51. }
  52. public String getCate() {
  53. return cate;
  54. }
  55. public void setCate(String cate) {
  56. this.cate = cate;
  57. }
  58. public Double getMinPrice() {
  59. return minPrice;
  60. }
  61. public void setMinPrice(Double minPrice) {
  62. this.minPrice = minPrice;
  63. }
  64. public Double getMaxPrice() {
  65. return maxPrice;
  66. }
  67. public void setMaxPrice(Double maxPrice) {
  68. this.maxPrice = maxPrice;
  69. }
  70. @Override
  71. public String toString() {
  72. return "ProdListReqParamsVo [goodsName=" + prodName + ", cate=" + cate + ", minPrice=" + minPrice
  73. + ", maxPrice=" + maxPrice + "]";
  74. }
  75. }

XML

由于Service层没有什么关键代码,这里我直接贴出XML中的SQL语句!

  1. <!-- 根据多条件检索商品 -->
  2. <select id="selectProdsByConds" parameterType="easymall.pojo.ProdListReqParamsVo" resultType="easymall.po.Products">
  3. select * from products
  4. <where>
  5. <if test="prodName!=null and prodName!=''">
  6. <!-- mybatis提供了<bind>标签来解决不同数据库模糊查询的差异,建议使用bind标签 -->
  7. <bind name="prod_name" value="'%' + prodName + '%'" />
  8. and name like #{prod_name}
  9. </if>
  10. <if test="cate!=null and cate!=''">
  11. and category=#{cate}
  12. </if>
  13. <if test="minPrice!=null">
  14. and price&gt;=#{minPrice}
  15. </if>
  16. <if test="maxPrice!=null">
  17. and #{maxPrice} &gt;= price
  18. </if>
  19. </where>
  20. </select>

前端代码

prod_list.jsp

  1. <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
  2. <!DOCTYPE HTML>
  3. <html>
  4. <head>
  5. <meta http-equiv="Content-type" content="text/html; charset=UTF-8" />
  6. <link href="${pageContext.request.contextPath }/css/prodList.css" rel="stylesheet" type="text/css">
  7. <link href="${pageContext.request.contextPath }/css/pageHelper.css" rel="stylesheet" type="text/css">
  8. <style>
  9. .pagehelper {
  10. text-align: center;
  11. }
  12. </style>
  13. </head>
  14. <body>
  15. <%@ include file="_head.jsp" %>
  16. <div id="content">
  17. <div id="search_div">
  18. <form method="post" action="${pageContext.request.contextPath}/prodlist">
  19. <span class="input_span">商品名:<input type="text" name="prodName" value="${params.prodName}"/></span>
  20. <span class="input_span">商品种类:</span>
  21. <select name="cate">
  22. <option value="">不限</option>
  23. <c:forEach items="${cates}" var="cate">
  24. <option value="${cate}" <c:if test="${cate==params.cate}">selected</c:if>>${cate}</option>
  25. </c:forEach>
  26. </select>
  27. <span class="input_span">商品价格区间:</span>
  28. <input type="text" name="minPrice" value="${params.minPrice}"/>
  29. - <input type="text" name="maxPrice" value="${params.maxPrice}"/>
  30. <input type="submit" value="查 询">
  31. </form>
  32. </div>
  33. <!-- 放置分页按钮 -->
  34. <div class="pagehelper">
  35. </div>
  36. <div id="prod_content">
  37. <c:forEach items="${prodList}" var="prod">
  38. <div class="prod_div">
  39. <a href="${pageContext.request.contextPath}/prodInfo?pid=${prod.id}" target="-blank">
  40. <img src="${pageContext.request.contextPath}${prod.imgurl}" border="0"></img>
  41. </a>
  42. <div id="prod_name_div">
  43. <a href="${pageContext.request.contextPath}/prodInfo?pid=${prod.id}" target="-blank">
  44. ${prod.name}
  45. </a>
  46. </div>
  47. <div id="prod_price_div">
  48. ¥${prod.price}元
  49. </div>
  50. <div>
  51. <div id="gotocart_div">
  52. <a href="${ pageContext.request.contextPath }/cart/addCart?pid=${prod.id}&buyNum=1">加入购物车</a>
  53. </div>
  54. <div id="say_div">
  55. 133人评价
  56. </div>
  57. </div>
  58. </div>
  59. </c:forEach>
  60. <div style="clear: both"></div>
  61. </div>
  62. </div>
  63. <%@ include file="_foot.jsp" %>
  64. <script src="${pageContext.request.contextPath }/js/jquery-1.4.2.js"></script>
  65. <script src="${pageContext.request.contextPath }/js/pageHelper.js"></script>
  66. <script>
  67. $(function() {
  68. let API_URL = "${pageContext.request.contextPath}/prodlist";
  69. let curPage = ${page};
  70. let total = ${total};
  71. let count = ${count};
  72. let sideBtnCount = 2;
  73. let urlParamsStr = '${urlParamsStr}';
  74. let btnHtml = pageHelper(API_URL, curPage, total, count, sideBtnCount, urlParamsStr);
  75. $('div.pagehelper').html(btnHtml);
  76. })
  77. </script>
  78. </body>
  79. </html>

pageHelper.js

封装页码的生成逻辑

  1. /**
  2. * 生成分页按钮的html代码
  3. * @param curPage 当前页。理应由前端传给后端,但是前端的传参有可能超出实际范围,这就必须交由
  4. * 后端来纠正之后,再查询对应页码的数据。然后后端将纠正后的当前页返回给前端,
  5. * 以便前端来渲染页码按钮组。如果不传,由后端返回默认值1
  6. * @param total 总记录数。实际上,后端可以直接返回总页数就可以了,只不过有一定局限性:假如
  7. * 前端还需要显示总记录数,凭借总页数和每页记录数,是无法计算出总记录数的。而返
  8. * 回总记录数,前端可以自行计算总页数,同时还可以额外显示总记录数
  9. * @param count 每页显示的记录数,由前端传给后端。如果不传,使用后端定义的默认值
  10. * @param sideBtnCount 当前页按钮的左边有多少个按钮,不需要传给后端
  11. * @param urlParamsStr 点击页码切换页面时,携带的条件参数的字符串,拼接在url后面。由后端定义并传给
  12. * 前端。后端接口并负责接收,按照自己定义的规则进行解析,拆解参数。
  13. * 例子:&name=ysq&age=21。前面的&不能少
  14. */
  15. function pageHelper(API_URL, curPage, total, count, sideBtnCount, urlParamsStr) {
  16. // 计算总页数
  17. let pageCount = Math.ceil(total / count);
  18. let leftPage, rightPage;
  19. if (pageCount <= 2 * sideBtnCount + 1) {
  20. leftPage = 1;
  21. rightPage = pageCount;
  22. } else {
  23. // 计算按钮组最左端和最右端的页码
  24. // 将[1, pageCount]分为3个区间:
  25. // [1, sideBtnCount],[sideBtnCount+1, pageCount-sideBtnCount],[pageCount-sideBtnCount+1, pageCount]
  26. if (curPage > sideBtnCount && curPage <= pageCount - sideBtnCount) {
  27. // [sideBtnCount+1, pageCount-sideBtnCount]
  28. leftPage = curPage - sideBtnCount;
  29. rightPage = curPage + sideBtnCount;
  30. } else if (curPage <= sideBtnCount) {
  31. // [1, sideBtnCount]
  32. leftPage = 1;
  33. rightPage = 2 * sideBtnCount + 1;
  34. // 越界时,修正当前页
  35. if (curPage < 1) {
  36. curPage = 1;
  37. }
  38. } else if (curPage > pageCount - sideBtnCount) {
  39. // [pageCount-sideBtnCount+1, pageCount]
  40. leftPage = pageCount - 2 * sideBtnCount;
  41. rightPage = pageCount;
  42. // 越界时,修正当前页
  43. if (curPage > pageCount) {
  44. curPage = pageCount;
  45. }
  46. }
  47. }
  48. return "<div class='pagination'>" +
  49. firstBtn('First') +
  50. preBtn('Pre') +
  51. numBtn(leftPage, rightPage) +
  52. nextBtn('Next') +
  53. lastBtn('Last') +
  54. "</div>";
  55. /**
  56. * 返回一个可点击的按钮的html代码
  57. * @param contentHtml 按钮中的内容
  58. */
  59. function clickableBtn(contentHtml, num) {
  60. //return `<a href='${API_URL}?page=${num}${urlParamsStr}'>${contentHtml}</a>`;
  61. return "<a href='" + API_URL + "?page=" + num + urlParamsStr + "'>" + contentHtml + "</a>";
  62. }
  63. /**
  64. * 返回一个当前页按钮的html代码
  65. * @param contentHtml
  66. */
  67. function currentBtn(contentHtml) {
  68. //return `<span>${contentHtml}</span>`;
  69. return "<span>" + contentHtml + "</span>";
  70. }
  71. /**
  72. * 返回上一页按钮的html代码
  73. * @param contentHtml
  74. */
  75. function preBtn(contentHtml) {
  76. if (curPage <= 1) {
  77. return ''; // 我这里直接返回空,你也可以根据你的喜好,返回禁用点击的按钮
  78. }
  79. return clickableBtn(contentHtml, curPage - 1);
  80. }
  81. /**
  82. * 返回下一页按钮的html代码
  83. * @param contentHtml
  84. */
  85. function nextBtn(contentHtml) {
  86. if (curPage >= pageCount) {
  87. return '';
  88. }
  89. return clickableBtn(contentHtml, curPage + 1);
  90. }
  91. /**
  92. * 返回首页按钮的html代码
  93. * @param contentHtml
  94. */
  95. function firstBtn(contentHtml) {
  96. if (leftPage <= 1) {
  97. // 如果首页(1)已经显示在了按钮组(>=leftPage)当中,则不需要首页按钮,这里我直接返回空
  98. return '';
  99. }
  100. return clickableBtn(contentHtml, 1);
  101. }
  102. /**
  103. * 返回末页按钮的html代码
  104. * @param contentHtml
  105. */
  106. function lastBtn(contentHtml) {
  107. if (pageCount <= rightPage) {
  108. // 如果末页(pageCount)已经显示在了按钮组(<=rightPage)当中,则不需要首页按钮,这里我直接返回空
  109. return '';
  110. }
  111. return clickableBtn(contentHtml, pageCount);
  112. }
  113. /**
  114. * 生成[left, right]区间的按钮的html代码
  115. * @param left
  116. * @param right
  117. */
  118. function numBtn(left, right) {
  119. let btnHtml = '';
  120. for (let i = left; i <= right; i++) {
  121. if (i === curPage) { // 当前页
  122. btnHtml += currentBtn(i);
  123. } else {
  124. btnHtml += clickableBtn(i, i);
  125. }
  126. }
  127. return btnHtml;
  128. }
  129. }
  130. // 获取指定的路径参数,获取不到返回空串
  131. function getUrlParam(key) {
  132. // ? 后面的
  133. let searchStr = window.location.search.substring(1);
  134. console.log(searchStr);
  135. let paramMap = new Array();
  136. let paramEntrys = searchStr.split('&');
  137. for(let i=0; i<paramEntrys.length; i++) {
  138. let entry = paramEntrys[i].split('=');
  139. paramMap[ entry[0] ] = entry[1];
  140. }
  141. console.log(paramMap);
  142. return paramMap[key];
  143. }

pageHelper.css

自定义页码按钮的css样式

  1. .pagination {
  2. margin: 12px;
  3. }
  4. .pagination a {
  5. background:url(../img/core_bg.png) #f8f8f8;
  6. border-color: #c9c9c9 #bdbdbd #b0b0b0;
  7. border-image: none;
  8. border-radius: 3px;
  9. border-style: solid;
  10. border-width: 1px;
  11. color: #666666;
  12. display: inline-block;
  13. line-height: 13px;
  14. margin-right: 3px;
  15. padding: 6px 10px;
  16. text-decoration: none;
  17. vertical-align: top;
  18. }
  19. .pagination a:hover {
  20. background-color: #f8f8f8;
  21. border-color:#c9c9c9 #bdbdbd #b0b0b0;
  22. border-image:none;
  23. border-radius:3px;
  24. border-style:solid;
  25. border-width:1px;
  26. color:#666666;
  27. display:inline-block;
  28. line-height:13px;
  29. margin-right:3px;
  30. padding:6px 10px;
  31. text-decoration:none;
  32. background:#488fcf;
  33. border-color: #2470b5 #488fcf #488fcf;
  34. color:#fff;
  35. }
  36. .pagination span {
  37. background-color: #f8f8f8;
  38. border-color:#c9c9c9 #bdbdbd #b0b0b0;
  39. border-image:none;
  40. border-radius:3px;
  41. border-style:solid;
  42. border-width:1px;
  43. color:#666666;
  44. display:inline-block;
  45. line-height:13px;
  46. margin-right:3px;
  47. padding:6px 10px;
  48. text-decoration:none;
  49. background:#488fcf;
  50. border-color: #2470b5 #488fcf #488fcf;
  51. color:#fff;
  52. }

css引用的背景图:core_bg.png

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/157242
推荐阅读
相关标签
  

闽ICP备14008679号