PageHelper implements multi-condition paging query, encapsulates the generation logic of page number buttons, and docks back-end interfaces

Effect picture

Difficulty analysis

The introduction and use of PageHelper, I won't say much here. With PageHelper, the back-end paging query is very simple.

To realize a paged query with multiple conditions, how does the front-end interface with the back-end interface? Before starting the code, first think about and analyze the following questions in turn, then sort out the ideas, and finally look at the code.

Question 1: What parameters does the front end need to pass to the back end?

(1) page: current page. It should be passed from the front end to the back end, but the parameters of the front end may exceed the actual range (for example, there are only 10 pages in total, and the user passes the parameter 11, then the back end should return the data on page 10 instead of empty data ), which must be corrected by the back end, and then query the data corresponding to the page number. Then the back end returns the corrected current page to the front end, so that the front end can render the page number button group. If not, the backend will return the default value of 1.

(2) Count: The number of records displayed on each page, which is passed from the front end to the back end. If not, use the default value defined by the backend.

(3) Various condition parameters. Taking the above rendering as an example, 4 conditional parameters need to be passed to the backend: ① Commodity name keyword: prodName; ② Commodity type: cate; ③ Lowest price: minPrice; ④ Highest price: maxPrice.

Question 2: In what way is the condition submitted to the backend?        

  There are 3 situations in which the front-end accesses the back-end interface. Take the effect diagram as an example to analyze:

(1) When you open the page for the first time, use the Get method, without any conditions

(2) Click the "Query" button to submit the form by Post. At this time, multiple conditions are carried, and some submissions may be empty

(3) When you click the page number button to switch pages, the method is Get because it is jumped through the a tag. When jumping, it is possible to carry multiple conditions.

   So the interface needs to use @RequestMapping annotation

Question 3: What data should the back end return to the front end?

(1) List data of the current page. Such as: List<Product>

(2) page: the current page after being corrected by the backend. Used to generate paging buttons.

(3) total: total number of records. Used to generate paging buttons.

(4) Count: The number of records displayed on each page may also be corrected by the backend. Used to generate paging buttons.

(5) urlParamsStr: Because when the page number button is clicked, jump through the a tag, it may need to carry multiple conditions, and it can only be carried in the form of url parameters. Therefore, the backend needs to concatenate multiple conditions into strings and return them to the frontend to facilitate the frontend to generate paging buttons.

Question 4: What parameters are needed to generate a page number button? How to encapsulate the logic of the page number button?

(1) page: the current page after being corrected by the backend. Used to generate paging buttons.

(2) total: total number of records. Used to generate paging buttons.

(3) Count: The number of records displayed on each page may also be corrected by the backend. Used to generate paging buttons.

To generate a page number button, the above 3 parameters are essential! ! ! As for the generation logic of the page number button, please refer to my other blog: https://blog.csdn.net/qq_43290318/article/details/111601738 .

Backend code

controller layer

package easymall.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;

import easymall.po.Products;
import easymall.pojo.ProdListReqParamsVo;
import easymall.service.ProductsService;

@Controller("productsController")
public class ProductsController {

	@Autowired
	private ProductsService productsService;
	
	/**
	 * 该接口必须支持post和get方式访问
	 * 因为表单提交是post方式,而第一次打开页面和点击页码切换页面都是get方式
	 * 
	 * @param page	    当前页。分页所需参数,如果前端不传,则默认为1
	 * @param count		每页显示多少条记录。分页所需参数,如果前端不传,则默认为2
	 */
	@RequestMapping("/prodlist") 
	public String prodlist(@ModelAttribute("params") ProdListReqParamsVo params, 
			Integer page, Integer count, Model model) {
		// 参数检查和纠正
		if (!ObjectUtils.isEmpty(params.getMinPrice()) &&
				!ObjectUtils.isEmpty(params.getMaxPrice())) {
			// 纠正 minPrice为非负数 
			if (params.getMinPrice() < 0) {
				params.setMinPrice(0d);
			}
			// 纠正为 minPrice <= maxPrice
			if (params.getMinPrice() > params.getMaxPrice()) {
				double min = params.getMinPrice();
				params.setMinPrice(params.getMaxPrice());
				params.setMaxPrice(min);
			}
		}
		// curPage 是否越界,可不需要判断,PageHelper内部会判断并纠正
		if (ObjectUtils.isEmpty(page)) {
			page = 1;
		}
		if (ObjectUtils.isEmpty(count) || count <= 0) {
			count = 2;
		}
		
		// 查询所有分类
		List<String> cates = productsService.allcategorys();
		
		// 调用PageHelper进行分页
		// 紧跟在这个方法后的第一个MyBatis 查询方法会被进行分页
		PageHelper.startPage(page, count);
		
		// 查询数据
		List<Products> prodList = productsService.getProdListByConds(params);
		
		// 获取各种分页属性
		PageInfo<Products> pageInfo = new PageInfo<>(prodList);
		// 将有效的参数拼接成url字符串,用于拼接到url后面。切换页码时携带
		String urlParamsStr = params.joinUrlParams();
		
		model.addAttribute("cates", cates);  // 分类数据
		model.addAttribute("prodList", prodList); // 商品列表数据
		model.addAttribute("page", pageInfo.getPageNum()); // 传给前端被修正后的当前页
		model.addAttribute("count", count);  // 每一页显示多少条记录
		model.addAttribute("total", pageInfo.getTotal()); // 总记录数
		model.addAttribute("urlParamsStr", urlParamsStr);
		
		return "prod_list";
	}
}

vo

package easymall.pojo;


import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

import org.springframework.util.ObjectUtils;

/**
 * 商品列表接口的请求参数
 * 
 * @author	passerbyYSQ
 * @date	2020-11-30 19:49:08
 */
public class ProdListReqParamsVo {
	
	// 商品名称关键词。可以为空
	private String prodName;
	
	// 分类名字。可以为空,为空时表示所有分类
	private String cate;
	
	// 最低价格。不允许为负数,且 minPrice <= maxPrice 
	private Double minPrice;
	
	// 最高价格。不允许为负数,且 minPrice <= maxPrice 
	private Double maxPrice;
	
	/**
	 * 将有效的参数拼接成url字符串,用于拼接到url后面。切换页码时携带
	 */
	public String joinUrlParams() {
		StringBuilder urlParamsStr = new StringBuilder("");
		if (prodName != null) { // 可以为空串
			urlParamsStr.append("&prodName=").append(prodName);
		}
		if (cate != null) { // 可以为空串
			String cateTmp = cate;
			try {
				// 对中文进行url编码
				cateTmp = URLEncoder.encode(cate, "UTF-8");
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
			urlParamsStr.append("&cate=").append(cateTmp);
		}
		if (!ObjectUtils.isEmpty(minPrice)) {
			urlParamsStr.append("&minPrice=").append(minPrice);
		}
		if (!ObjectUtils.isEmpty(maxPrice)) {
			urlParamsStr.append("&maxPrice=").append(maxPrice);
		}
		
		return urlParamsStr.toString();
	}

	public String getProdName() {
		return prodName;
	}
	
	public void setProdName(String prodName) {
		this.prodName = prodName;
	}

	public String getCate() {
		return cate;
	}

	public void setCate(String cate) {
		this.cate = cate;
	}

	public Double getMinPrice() {
		return minPrice;
	}

	public void setMinPrice(Double minPrice) {
		this.minPrice = minPrice;
	}

	public Double getMaxPrice() {
		return maxPrice;
	}

	public void setMaxPrice(Double maxPrice) {
		this.maxPrice = maxPrice;
	}

	@Override
	public String toString() {
		return "ProdListReqParamsVo [goodsName=" + prodName + ", cate=" + cate + ", minPrice=" + minPrice
				+ ", maxPrice=" + maxPrice + "]";
	}
	
}

XML

Since there is no key code in the Service layer, here I directly post the SQL statement in the XML!

    <!-- 根据多条件检索商品 -->
   <select id="selectProdsByConds" parameterType="easymall.pojo.ProdListReqParamsVo" resultType="easymall.po.Products">

  	 select * from products
  	 <where>
  	 	<if test="prodName!=null and prodName!=''">
  	 		<!-- mybatis提供了<bind>标签来解决不同数据库模糊查询的差异,建议使用bind标签 -->
  	 		<bind name="prod_name" value="'%' + prodName + '%'" />
  	 		and name like #{prod_name}
  	 	</if>
  	 	<if test="cate!=null and cate!=''">
  	 		and category=#{cate}
  	 	</if>
  	 	<if test="minPrice!=null">
  	 		and price&gt;=#{minPrice}
  	 	</if>
  	 	<if test="maxPrice!=null">
  	 		and #{maxPrice} &gt;= price
  	 	</if>
  	 </where>
  		
  </select>

Front-end code

prod_list.jsp

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<!DOCTYPE HTML>
<html>
<head>
	<meta http-equiv="Content-type" content="text/html; charset=UTF-8" />
	<link href="${pageContext.request.contextPath }/css/prodList.css" rel="stylesheet" type="text/css">
	<link href="${pageContext.request.contextPath }/css/pageHelper.css" rel="stylesheet" type="text/css">
	
	<style>
		.pagehelper {
			text-align: center;
		}
	</style>
	
</head>
<body>

	<%@ include file="_head.jsp" %>
	
	<div id="content">
		<div id="search_div">
			<form method="post" action="${pageContext.request.contextPath}/prodlist">
				<span class="input_span">商品名:<input type="text" name="prodName" value="${params.prodName}"/></span>
				<span class="input_span">商品种类:</span>
				<select name="cate">
					<option value="">不限</option>
					<c:forEach items="${cates}" var="cate">
						<option value="${cate}" <c:if test="${cate==params.cate}">selected</c:if>>${cate}</option>
					</c:forEach>

				</select>
				<span class="input_span">商品价格区间:</span>
				<input type="text" name="minPrice" value="${params.minPrice}"/>
				- <input type="text" name="maxPrice" value="${params.maxPrice}"/>
				<input type="submit" value="查 询">
			</form>
		</div>
		
		<!-- 放置分页按钮 -->
		<div class="pagehelper">
			
		</div>
		
		<div id="prod_content">
		<c:forEach items="${prodList}" var="prod">
			<div class="prod_div">
				<a href="${pageContext.request.contextPath}/prodInfo?pid=${prod.id}" target="-blank">
				<img src="${pageContext.request.contextPath}${prod.imgurl}" border="0"></img>
				</a>
				<div id="prod_name_div">
					<a href="${pageContext.request.contextPath}/prodInfo?pid=${prod.id}" target="-blank">
						${prod.name}
					</a>
				</div>
				<div id="prod_price_div">
					¥${prod.price}元
				</div>
				<div>
					<div id="gotocart_div">
						<a href="${ pageContext.request.contextPath }/cart/addCart?pid=${prod.id}&buyNum=1">加入购物车</a>
					</div>					
					<div id="say_div">
						133人评价
					</div>					
				</div>
			</div>
			</c:forEach>
			<div style="clear: both"></div>
		</div>
	</div>
	<%@ include file="_foot.jsp" %>
	
	<script src="${pageContext.request.contextPath }/js/jquery-1.4.2.js"></script>
	<script src="${pageContext.request.contextPath }/js/pageHelper.js"></script>
	
	<script>
		$(function() {
			let API_URL = "${pageContext.request.contextPath}/prodlist";
			let curPage = ${page};  
			let total = ${total};
			let count = ${count}; 
			let sideBtnCount = 2;
			let urlParamsStr = '${urlParamsStr}';
			
			let btnHtml = pageHelper(API_URL, curPage, total, count, sideBtnCount, urlParamsStr);
			$('div.pagehelper').html(btnHtml);
		})
	</script>
</body>
</html>

pageHelper.js

Packaging page number generation logic

/**
 * 生成分页按钮的html代码
 * @param curPage       当前页。理应由前端传给后端,但是前端的传参有可能超出实际范围,这就必须交由
 *                      后端来纠正之后,再查询对应页码的数据。然后后端将纠正后的当前页返回给前端,
 *                      以便前端来渲染页码按钮组。如果不传,由后端返回默认值1
 * @param total         总记录数。实际上,后端可以直接返回总页数就可以了,只不过有一定局限性:假如
 *                      前端还需要显示总记录数,凭借总页数和每页记录数,是无法计算出总记录数的。而返
 *                      回总记录数,前端可以自行计算总页数,同时还可以额外显示总记录数
 * @param count         每页显示的记录数,由前端传给后端。如果不传,使用后端定义的默认值
 * @param sideBtnCount  当前页按钮的左边有多少个按钮,不需要传给后端
 * @param urlParamsStr	点击页码切换页面时,携带的条件参数的字符串,拼接在url后面。由后端定义并传给
 *			            前端。后端接口并负责接收,按照自己定义的规则进行解析,拆解参数。
 *			            例子:&name=ysq&age=21。前面的&不能少
 */
function pageHelper(API_URL, curPage, total, count, sideBtnCount, urlParamsStr) {
    // 计算总页数
    let pageCount = Math.ceil(total / count);
	
 	let leftPage, rightPage;

	if (pageCount <= 2 * sideBtnCount + 1) {
		leftPage = 1;
		rightPage = pageCount; 
	} else {
		// 计算按钮组最左端和最右端的页码
	    // 将[1, pageCount]分为3个区间:
	    // [1, sideBtnCount],[sideBtnCount+1, pageCount-sideBtnCount],[pageCount-sideBtnCount+1, pageCount]
	   	if (curPage > sideBtnCount && curPage <= pageCount - sideBtnCount) {
	        // [sideBtnCount+1, pageCount-sideBtnCount]
	        leftPage = curPage - sideBtnCount;
	        rightPage = curPage + sideBtnCount;
	
	    } else if (curPage <= sideBtnCount) {
	        // [1, sideBtnCount]
	        leftPage = 1;
	        rightPage = 2 * sideBtnCount + 1;
	        // 越界时,修正当前页
	        if (curPage < 1) {
	            curPage = 1;
	        }
	
	    } else if (curPage > pageCount - sideBtnCount) {
	        // [pageCount-sideBtnCount+1, pageCount]
	        leftPage = pageCount - 2 * sideBtnCount;
	        rightPage = pageCount;
	        // 越界时,修正当前页
	        if (curPage > pageCount) {
	            curPage = pageCount;
	        }
	    }
	}

    return "<div class='pagination'>" +
            firstBtn('First') +
            preBtn('Pre') +
            numBtn(leftPage, rightPage) +
            nextBtn('Next') +
            lastBtn('Last') +
            "</div>";

    /**
     * 返回一个可点击的按钮的html代码
     * @param contentHtml   按钮中的内容
     */
    function clickableBtn(contentHtml, num) {
        //return  `<a href='${API_URL}?page=${num}${urlParamsStr}'>${contentHtml}</a>`;
		return  "<a href='" + API_URL + "?page=" + num + urlParamsStr + "'>" + contentHtml + "</a>";
    }

    /**
     * 返回一个当前页按钮的html代码
     * @param contentHtml
     */
    function currentBtn(contentHtml) {
        //return  `<span>${contentHtml}</span>`;
		return  "<span>" + contentHtml + "</span>";
    }

    /**
     * 返回上一页按钮的html代码
     * @param contentHtml
     */
    function preBtn(contentHtml) {
        if (curPage <= 1) {
            return ''; // 我这里直接返回空,你也可以根据你的喜好,返回禁用点击的按钮
        }
        return clickableBtn(contentHtml, curPage - 1);
    }

    /**
     * 返回下一页按钮的html代码
     * @param contentHtml
     */
    function nextBtn(contentHtml) {
        if (curPage >= pageCount) {
            return '';
        }
        return clickableBtn(contentHtml, curPage + 1);
    }

    /**
     * 返回首页按钮的html代码
     * @param contentHtml
     */
    function firstBtn(contentHtml) {
        if (leftPage <= 1) {
            // 如果首页(1)已经显示在了按钮组(>=leftPage)当中,则不需要首页按钮,这里我直接返回空
            return '';
        }
        return clickableBtn(contentHtml, 1);
    }

    /**
     * 返回末页按钮的html代码
     * @param contentHtml
     */
    function lastBtn(contentHtml) {
        if (pageCount <= rightPage) {
            // 如果末页(pageCount)已经显示在了按钮组(<=rightPage)当中,则不需要首页按钮,这里我直接返回空
            return '';
        }
        return clickableBtn(contentHtml, pageCount);
    }

    /**
     * 生成[left, right]区间的按钮的html代码
     * @param left
     * @param right
     */
    function numBtn(left, right) {
        let btnHtml = '';
        for (let i = left; i <= right; i++) {
            if (i === curPage) {  // 当前页
                btnHtml += currentBtn(i);
            } else {
                btnHtml += clickableBtn(i, i);
            }
        }
        return btnHtml;
    }
}

// 获取指定的路径参数,获取不到返回空串
function getUrlParam(key) {
    // ? 后面的
    let searchStr = window.location.search.substring(1);
    console.log(searchStr);

    let paramMap = new Array();

    let paramEntrys = searchStr.split('&');
    for(let i=0; i<paramEntrys.length; i++) {
        let entry = paramEntrys[i].split('=');
        paramMap[ entry[0] ] = entry[1];
    }

    console.log(paramMap);

    return paramMap[key];
}

pageHelper.css

Customize the css style of the page number button

.pagination {
	margin: 12px;
}
.pagination a {
    background:url(../img/core_bg.png) #f8f8f8;
    border-color: #c9c9c9 #bdbdbd #b0b0b0;
    border-image: none;
    border-radius: 3px;
    border-style: solid;
    border-width: 1px;
    color: #666666;
    display: inline-block;
    line-height: 13px;
    margin-right: 3px;
    padding: 6px 10px;
    text-decoration: none;
    vertical-align: top;
}
.pagination a:hover {
	background-color: #f8f8f8;
    border-color:#c9c9c9 #bdbdbd #b0b0b0;
    border-image:none;
    border-radius:3px;
    border-style:solid;
    border-width:1px;
    color:#666666;
    display:inline-block;
    line-height:13px;
    margin-right:3px;
    padding:6px 10px;
    text-decoration:none;
    background:#488fcf;
    border-color: #2470b5 #488fcf #488fcf;
    color:#fff;
}
.pagination span {
    background-color: #f8f8f8;
    border-color:#c9c9c9 #bdbdbd #b0b0b0;
    border-image:none;
    border-radius:3px;
    border-style:solid;
    border-width:1px;
    color:#666666;
    display:inline-block;
    line-height:13px;
    margin-right:3px;
    padding:6px 10px;
    text-decoration:none;
    background:#488fcf;
    border-color: #2470b5 #488fcf #488fcf;
    color:#fff;
}

Background image quoted by css: core_bg.png

Guess you like

Origin blog.csdn.net/qq_43290318/article/details/111828676