Spring boot Mybatis Thymeleaf Bootstrap页面布局和表格分页

本文主要给出了一个用Bootstrap实现表格分页的方案,也实现了基本的页面布局例子。

软件开发中分页是常用的,写一个分页控件虽然不难但是也比较麻烦。分页的基本原理是将数据库表中的数据以分页的形式显示在页面表格中,做法是以当前页码为基准点的一个固定长度的滑动窗口来显示若干页面,滑动窗口以外的页面用省略号(....)表示,见图1。图1中显示的是导航控件,控件中每个小格子装的是一个<a></a>标签,标签里面显示一个页面编号,当点击该标签的时候这个页面编号会传到后台controller里,controller里面得到该页编号后会从数据库取得该页的记录,将其传到页面并更新表格,以此达到分页浏览效果。当滑动窗口接近首页或者末页时,左右两端会超出页面范围,分别见图2和图3。难点是显示和控制导航控件,而在导航空间中如何生成滑动窗口内的页码,也是实现该控件核心的地方。通常有两中方法实现导航控件:(1)在html页面前端实现页码显示逻辑;(2)在后端用java代码实现页码逻辑,然后传到html前端来显示。由于前端实现会设计html标签,容易出错、移植性也差,因此推荐使用方法(2)来实现,下面给出实现代码。

首先给出数据库表和一些测试数据,sql脚本如下:

                                                 user.sql

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `Id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`Id`)
) ENGINE=MyISAM AUTO_INCREMENT=76 DEFAULT CHARSET=utf8;

#
#测试数据
#
INSERT INTO `user` VALUES (3,'张三',15),(4,'李四',25),(5,'李四',25),(6,'ff',22),
(7,'gg',66),(8,'rr',66),(9,'gg',22),(10,'rr',33),(11,'ee',55),(12,'ww',44),(13,'tq',22),
(14,'t2',66),(15,'r6',55),(16,'王五',20),(17,'王五',20),(18,'王五',20),(19,'王五',20),
(20,'王五',20),(21,'王五',20),(22,'王五',20),(23,'王五',20),(24,'王五',20),(25,'王五',20),
(26,'王五',20),(27,'王五',20),(28,'王五',20),(29,'王五',20),(30,'王五',20),(31,'王五',20),
(32,'王五',20),(33,'王五',20),(34,'王五',20),(35,'王五',20),(36,'赵六',18),(37,'赵六',18),
(38,'赵六',18),(39,'赵六',18),(40,'赵六',18),(41,'赵六',18),(42,'赵六',18),(43,'赵六',18),
(44,'赵六',18),(45,'赵六',18),(46,'赵六',18),(47,'赵六',18),(48,'赵六',18),(49,'赵六',18),
(50,'赵六',18),(51,'赵六',18),(52,'赵六',18),(53,'赵六',18),(54,'赵六',18),(55,'赵六',18),
(56,'赵六',18),(57,'赵六',18),(58,'赵六',18),(59,'赵六',18),(60,'赵六',18),(61,'赵六',18),
(62,'赵六',18),(63,'赵六',18),(64,'赵六',18),(65,'赵六',18),(66,'赵六',18),(67,'赵六',18),
(68,'赵六',18),(69,'赵六',18),(70,'赵六',18),(71,'赵六',18),(72,'赵六',18),(73,'赵六',18),
(74,'赵六',18),(75,'赵六',18);

 

Page.java,实现分页的java类,会传递到页面前端,里面有详细的注释说明。

                                                                    Page.java

import java.util.List;
public class Page<T> {   
    private int totalRows;	//总行数
    private int current;    //当前页	
    private int totalPages; //总页数
    private int pageSize;     // 每页行数
    private List<T> rows;     // 结果集,储存一个页面的数据记录
    private int[] navigatepageNums;	//所有导航页号
    private int navigatePages; //导航页码数,即滑动窗口中格子个数

    //导航条上的第一页
    private int navigateFirstPage;
    //导航条上的最后一页
    private int navigateLastPage;	    
    //是否为第一页
    private boolean isFirstPage = false;  
	//是否为最后一页
    private boolean isLastPage = false;
    
    //构造函数
    public Page(int totalRows,int pageSize,int navigatePages, int current, List<T> rows) {
       //设置5个属性
    	this.totalRows=totalRows;
    	this.pageSize=pageSize;
    	this. navigatePages=navigatePages;
    	this.current=current;
    	this.rows=rows;  
       // 设置6个属性
        this.calcProperties();  	
    }
    //计算滑动窗口中的页号,不止一种生成策略,这里采用
    //http://git.oschina.net/free/Mybatis_PageHelper提供的方案来实现
//总页码编号为1...totalPages。基本原理是:
//以当前页面为基点,在页码序列中各取导航页码数的一半来填充数组navigatepageNums。
   private int[] calcNavigatepageNums() {
        //当总页数小于或等于导航页码数时
        if (totalPages <= navigatePages) {
            navigatepageNums = new int[totalPages];
            for (int i = 0; i < totalPages; i++) {
                navigatepageNums[i] = i + 1;
            }
        } else { //当总页数大于导航页码数时
            navigatepageNums = new int[navigatePages];
            int startNum = current - navigatePages / 2;
            int endNum = current + navigatePages / 2;

            if (startNum < 1) {
                startNum = 1;
                //前端超页时,从页码1开始填充数组navigatepageNums。
                for (int i = 0; i < navigatePages; i++) {
                    navigatepageNums[i] = startNum++;
                }
            } else if (endNum > totalPages) {
                endNum = totalPages;
                //后端超页时,从最大页码开始逆序填充数组navigatepageNums。
                for (int i = navigatePages - 1; i >= 0; i--) {
                    navigatepageNums[i] = endNum--;
                }
            } else {
                //前后都不超页时。
                for (int i = 0; i < navigatePages; i++) {
                    navigatepageNums[i] = startNum++;
                }
            }
        }
        return navigatepageNums;
    }
	
	//设置6个属性
    private void calcProperties() {
    	this.totalPages=(int) Math.ceil((double)totalRows/pageSize);
        this.isFirstPage = current == 1;
        this.isLastPage = current == totalPages || totalPages == 0;
        this.navigatepageNums=calcNavigatepageNums();
        //计算滑动窗口第一格的页号、最后一格页号
        if (navigatepageNums != null && navigatepageNums.length > 0) {
            this.navigateFirstPage = navigatepageNums[0];
            this.navigateLastPage = navigatepageNums[navigatepageNums.length - 1];   
        }
    ...
    // 省略了getters setters


}

注意对于boolean类型的变量isFirstPage和isLastPage,如果用eclipse自动生成的getters和setters方法可能会不正确,这两个变量正确的代码如下: 

	public boolean getIsFirstPage() {
		return isFirstPage;
	}
	public void setIsFirstPage(boolean isFirstPage) {
		this.isFirstPage = isFirstPage;
	}
	public boolean getIsLastPage() {
		return isLastPage;
	}
	public void setIsLastPage(boolean isLastPage) {
		this.isLastPage = isLastPage;
	}

 从上代码可以看出:总页码编号为1...totalPages,以当前页码(current)为基点,在页码序列中各取导航页码数的一半来填充数组navigatepageNums;当前端超页时,从页码1开始填充数组navigatepageNums;当后端超页时,从最大页码开始逆序填充数组navigatepageNums。navigatePages为偶数时,在navigatepageNums中的页码中,current前面比面的页码数要比后面的页码数多1(见图4);navigatePages为奇数时,current前后页码数相同(见图5)。

页面布局与分页控件实现,Bootstrap和jquery的css、js等静态文件放置在src/main/resources/static下面,整个静态文件结构如图6所示

分页前端控件pager.html是thymeleaf片段,主要是为了方便移植,里面用了bootstrap的分页css样式。

                                                                       pager.html

<!-- 
page.current 当前页
page.totalPages  总页数
page. navigatepageNums  显示的所有导航页号
url 请求的url
urlPara 请求参数名称
 -->
<th:block th:fragment="pageHtml(page, url, urlPara)">
	<meta charset="UTF-8" />
	<nav class="pagination" role="navigation" aria-label="pagination">
		<ul class="pagination">
			<!-- 如果是首页,disable上一页和首页-->
			<li th:class="${page.isFirstPage}==true? 'disabled'">
			    <a th:href="${page.isFirstPage}==true?'#': @{${url}(${urlPara}=1)}">首页</a>
			    <a th:href="${page.isFirstPage}==true?'#': @{${url} + '?' + ${urlPara} + '=' + ${page.current - 1}}">上一页</a>
			</li>				
		
			<!-- 如果前面页码没有显示完则显示省略号 -->
			<li th:if="${page.navigateFirstPage > 1}"><span>&hellip;</span></li>
			<!-- 显示导航页码 -->
			<th:block th:each="i: ${page.navigatepageNums}">
				<li th:class="${page.current == i} ? 'active'"><a
					th:href="@{${url} + '?' + ${urlPara} + '=' + ${i}}" th:text="${i}"></a></li>
			</th:block>			
			<!-- 如果后面页码没有显示完则显示省略号 -->
			<li th:if="${page.navigateLastPage < page.totalPages}"><span>&hellip;</span></li>			
            <!-- 如果是末页,disable下一页和末页-->
			<li th:class="${page.isLastPage}==true? 'disabled'">				   
				 <a th:href="${page.isLastPage}==true? '#': @{${url} + '?' + ${urlPara} + '=' + ${page.current + 1}}">下一页 </a>
				 <a	th:href="${page.isLastPage}==true? '#': @{${url}(${urlPara}=${page.totalPages})}">末页</a>
			</li>

		</ul>
	</nav>
</th:block>

使用控件

建立一个html的thyemeleaf页面,userlist.html,里面用了bootstrap布局和分页样式。可以在网站http://www.ibootstrap.cn/上生成页面布局等样式然后拷贝下来修改。

                                                               userlist.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
	xmlns="http://www.w3.org/1999/xhtml"
	xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<!-- <meta charset="UTF-8"> -->
<link rel="stylesheet" href="../css/bootstrap.min.css">
<script src="../js/jquery-1.11.3.min.js"></script>
<script src="../js/bootstrap.min.js"></script>
<title></title>
</head>
<body style="margin-right: 20px; margin-left: 20px">

	<!-- <p>姓名: <span th:text="${session.ssUser.name}" ></span>. </p> -->
	<div class="page-header" style="color: blue">
		<h1>
			我的管理信息系统 <small>用户列表与信息</small>
		</h1>
	</div>
	</div>

	<div class="container">
		<div class="row clearfix">
			<div class="col-md-12 column">
				<nav class="navbar navbar-default" role="navigation">
					<div class="navbar-header">
						<button class="navbar-toggle" type="button" data-toggle="collapse"
							data-target="#bs-example-navbar-collapse-1">
							<span class="sr-only">Toggle navigation</span><span
								class="icon-bar"></span><span class="icon-bar"></span><span
								class="icon-bar"></span>
						</button>
						<a class="navbar-brand" href="#">Brand</a>
					</div>

					<div class="collapse navbar-collapse"
						id="bs-example-navbar-collapse-1">
						<ul class="nav navbar-nav">
							<li class="active"><a href="#">Link</a></li>
							<li><a href="#">Link</a></li>
							<li class="dropdown"><a class="dropdown-toggle" href="#"
								data-toggle="dropdown">Dropdown<strong class="caret"></strong></a>
								<ul class="dropdown-menu">
									<li><a href="#">Action</a></li>
									<li><a href="#">Another action</a></li>
									<li><a href="#">Something else here</a></li>
									<li class="divider"></li>
									<li><a href="#">Separated link</a></li>
									<li class="divider"></li>
									<li><a href="#">One more separated link</a></li>
								</ul></li>
						</ul>
						<form class="navbar-form navbar-left" role="search">
							<div class="form-group">
								<input class="form-control" type="text" />
							</div>
							<button class="btn btn-default" type="submit">Submit</button>
						</form>
						<ul class="nav navbar-nav navbar-right">
							<li><a href="#">Link</a></li>
							<li class="dropdown"><a class="dropdown-toggle" href="#"
								data-toggle="dropdown">Dropdown<strong class="caret"></strong></a>
								<ul class="dropdown-menu">
									<li><a href="#">Action</a></li>
									<li><a href="#">Another action</a></li>
									<li><a href="#">Something else here</a></li>
									<li class="divider"></li>
									<li><a href="#">Separated link</a></li>
								</ul></li>
						</ul>
					</div>

				</nav>
	<table class="table">
		<thead>
			<tr>
				<th>编号</th>
				<th>用户名</th>
				<th>年龄</th>
				<th>状态</th>
			</tr>
		</thead>
		<tbody>
			<tr th:each="user,state:${page.rows}"
				th:class="${state.odd ? 'success':'info'}">
				<td th:text="${user.id}"></td>
				<td th:text="${user.name}">TB - Monthly</td>
				<td th:text="${user.age}">01/04/2012</td>
				<td>Default</td>
			</tr>

		</tbody>
	</table>
	<div class="row clearfix">
		<div class="col-md-12 column">
			<!--page是后台传递过来的对象,  '/user/showall'是调用controller,
			    cpage是参数表示当前页码
			-->
			<div
				th:replace="~{pager.html :: pageHtml(${page},'/user/showall', 'cpage')}">
			</div>
		</div>
	</div>
			</div>
		</div>

	</div>

	<div class="row clearfix">
		<div class="col-md-4 column">
			<h2>Heading</h2>
			<p>
			<div th:object="${session.ssUser}">
				<span>th:text不能够写出 th: text 中间不能用空格</span>
				<p>
					姓名: <span th:text="*{name}">Sebastian</span>.
				</p>
				<p>
					年龄: <span th:text="*{age}">Pepper</span>.
				</p>
			</div>
			</p>
			<p>
				<a class="btn" href="#">View details »</a>
			</p>
		</div>
		<div class="col-md-4 column">
			<h2>Heading</h2>
			<p>Donec id elit non mi porta gravida at eget metus. Fusce
				dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh,
				ut fermentum massa justo sit amet risus. Etiam porta sem malesuada
				magna mollis euismod. Donec sed odio dui.</p>
			<p>
				<a class="btn" href="#">View details »</a>
			</p>
		</div>
		<div class="col-md-4 column">
			<h2>Heading</h2>
			<p>Donec id elit non mi porta gravida at eget metus. Fusce
				dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh,
				ut fermentum massa justo sit amet risus. Etiam porta sem malesuada
				magna mollis euismod. Donec sed odio dui.</p>
			<p>
				<a class="btn" href="#">View details »</a>
			</p>
		</div>
	</div>
	</div>

</body>
</html>

controller部分代码:

                                                                UserController.java   

@Controller
@RequestMapping("user")
public class UserController {
	@Autowired  //注入,xml方式
	private UserMapper userMapper;
	
	@GetMapping("showall")
	public String queryAll(Model model, HttpSession session,Integer cpage) {
		//cpage当前页号
		if (cpage==null) {			
			cpage=1;
		}
		int pageSize=5;   //页面显示行数,自行设置
		int navigatePages=6;//滑动窗口中格子个数,自行设置
		
		int startrow=(cpage-1)*pageSize;
                //从startrow行开始,查询pageSize条记录		
		List<User> rows=userMapper.selectusersPage(startrow, pageSize);		
		int total=userMapper.getTotal();
		//根据页面属性生成页面对象
		Page<User> page =new Page<User>(total, pageSize, navigatePages, cpage, rows);
		//传递到前台页面
		model.addAttribute("page", page);
        return "userlist";
}

Mybatis的UserMapper.xml,其中分页显示主要用到了selectusersPage和getTotal两个查询方法。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.demo.dao.UserMapper">
	<select id="queryAllUser" resultType="User">
		select * from user
	</select>
	<select id="findnameandage" resultType="User"
		parameterType="string">
		select * from user where name=#{name} and age=#{age}
	</select>
	<select id="findnameandageMap" resultType="User" parameterType="map">
		select * from user where name=#{name} and age=#{age}
	</select>
	<select id="selectusersPage" resultType="User">
		select * from user order by id asc limit #{startrow},#{num}
	</select>
        <select id="getTotal"  resultType="int">
		select count(id) from user
	</select>
</mapper>

UserMapper接口,代码如下:

 

package com.demo.dao;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Mapper;
import com.entity.User;
@Mapper
public interface UserMapper {
	List<User> queryAllUser();
	List<User> findnameandage(String name, int age);
	List<User> findnameandageMap(Map map);
	List<User> selectusersPage(int startrow,int num);
	int getTotal();
}

User实体类比较简单,代码如下:

                                                           User.java 

public class User {
    private int id; 
    private String name;
    private int age;
    ...
    // 省略了getter setter
}

 

 

 

 下面是显示效果图:

源码链接地址:https://pan.baidu.com/s/1uMTybwLH8Liq2tnkBHumOQ 提取码: nrru

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值