springboot实现layui分页前后端数据动态交互功能
最近在学layui这个前端框架的分页功能,官方的文档中,给出的分页样式如下图所示:
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>layPage快速使用</title> 6 <!--注意:这里引入的样式路径是你本地对应的路径--> 7 <link rel="stylesheet" href="layui/css/layui.css" media="all"> 8 </head> 9 <body> 10 11 <div id="test1"></div> 12 <!--注意:这里引入的样式路径是你本地对应的路径--> 13 <script src="layui/layui.js"></script> 14 <script> 15 layui.use('laypage', function(){ 16 var laypage = layui.laypage; 17 18 //执行一个laypage实例 19 laypage.render({ 20 elem: 'test1' //注意,这里的 test1 是 ID,不用加 # 号 21 ,count: 50 //数据总数,从服务端得到 22 }); 23 }); 24 </script> 25 </body> 26 </html>
执行后得到的效果如下图所示:
作为后端的java开发,有时候需要用到这个分页的功能,所以想着来结合layui分页做一个前后端数据交互分页。
关于分页里面的参数这里就不一一说明了,官方的文档讲解的也很清楚:https://www.layui.com/doc/modules/laypage.html,如果实在不明白的话,可以参考这个教程视频:https://www.bilibili.com/video/BV19V411b7sx?p=1
1、准备一下数据库所需要的数据,一共两百条,是从layui上面拿到的json文件,然后在数据库中建立对应字段,然后读取该json数据到数据库中(或者你自己定义一些数据也是可以的),如下是数据库中的数据:
2、搭建springboot后端环境,首先使用idea新建一个springboot项目,在创建好的项目的template文件夹下新建一个名为:pagesTest.html文件夹
注意:在template文件夹下的文件不能直接通过浏览器访问,必须通过转发,然后视图解释器进行解释才能访问得到。如果想要直接访问的话,可以将要访问的文件放到static文件夹下
pagesTest.html的代码如下所示:
1 <!DOCTYPE html> 2 <html lang="en" xmlns:th="http://www.thymeleaf.org"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>layui分页测试</title> 6 <!--这里要注意,如果放在templates目录下,那么引用的路径需要改一下的,相对路径(以当前文件的位置出发)是可以访问到静态资源的--> 7 <!-- <link rel="stylesheet" type="text/css" href="../static/layui/css/layui.css"/> 8 <script src="../static/js/jquery-3.6.0.js" type="text/javascript" charset="utf-8"></script> 9 <script src="../static/layui/layui.js" type="text/javascript" charset="utf-8"></script>--> 10 <!--直接从static文件夹路径开始找,因为springboot的静态资源路径中有static文件夹,推荐使用这种方法。加了/代表使用相对路径,不加斜杠代表使用绝对路径--> 11 <!--<link rel="stylesheet" type="text/css" href=/layui/css/layui.css"/> 12 <script src="/js/jquery-3.6.0.js" type="text/javascript" charset="utf-8"></script> 13 <script src="/layui/layui.js" type="text/javascript" charset="utf-8"></script>--> 14 <!--路径测试,使用th:src的路径,推荐使用这种,不用写static文件夹前缀,因为springboot已经默认指定好了--> 15 <link rel="stylesheet" type="text/css" th:href="@{/layui/css/layui.css}"/> 16 <script th:src="@{/js/jquery-3.6.0.js}" type="text/javascript" charset="utf-8"></script> 17 <script th:src="@{/layui/layui.js}" type="text/javascript" charset="utf-8"></script> 18 19 20 21 <!--这里设置了一下以绝对路径进行访问url--> 22 <base href="http://localhost:8081/"/> 23 </head> 24 <body> 25 <script> 26 $(function () { 27 //第一次,需要手动调用一下这个函数 28 pages(1, 10); 29 }) 30 31 function pages(pages, limit) { 32 // 发起异步请求 33 $.ajax({ 34 url: "pagesTest", 35 data: { 36 "pages": pages, 37 "limit": limit 38 }, 39 dataType: "json", 40 success: function (data) { 41 var html = ""; 42 $.each(data.users, function (index, user) { 43 html += "<tr>"; 44 html += "<td>" + user.id + "</td>"; 45 html += "<td>" + user.username + "</td>"; 46 html += "<td>" + user.sex + "</td>"; 47 html += "<td>" + user.city + "</td>"; 48 html += "<td>" + user.sign + "</td>"; 49 html += "<td>" + user.experience + "</td>"; 50 html += "<td>" + user.logins + "</td>"; 51 html += "<td>" + user.wealth + "</td>"; 52 html += "<td>" + user.classify + "</td>"; 53 html += "<td>" + user.score + "</td>"; 54 html += "</tr>"; 55 }); 56 // 将拼接好的内容放到表格体内,每次拼接的时候都需要清除一下之前的数据,否则就会越来越多了,但是发现去掉其实也是可以的,应该是layui在拼接内容的时候帮我们把之间的内容去掉了 57 // 了 58 $("#tbody").html(html); 59 //后台需要传递总页数、当前页、一页显示多少条记录数给回调函数 60 laypage(data.userTotal, data.curr, data.limit); 61 62 63 } 64 65 66 }); 67 } 68 //这里直接在jquery的函数里面引用,因此不用加:th:inline="none"也是可以的 69 function laypage(total, page, limit) { 70 //分页回调函数,当每次点击分页组件的时候就会触发这个回调函数执行 71 layui.use(['laypage','layer'], function () { 72 var laypage = layui.laypage,layer = layui.layer; 73 laypage.render({ 74 elem: 'pages',//elem属性绑定的是容器的ID属性值,不需要加# 75 count: total,//记录数的总数量 76 curr: page || 1,//当前页是几,如果是第一次,则为1(因为第一次加载,page的值是没有嘛,所以就选择1),不过这个1写不写都无所谓,反正默认值是1了。这个值必须要有的,不然页码永远都是选中第一页 77 limit: limit || 10,//每次分的页数,默认值也是10条。这个值也要传的,因为切换每页显示的条数的时候需要用它来记录一下,否则永远都是10条展示 78 limits:[5,10,20,30],//每页显示的条数 79 layout:['prev','page','next','limit','skip','count'],//自定义布局:自定义排版。可选值有:count(总条目输区域)、prev(上一页区域)、page(分页区域)、next(下一页区域)、limit(条目选项区域)、refresh(页面刷新区域。注意:layui 2.3.0 新增) 、skip(快捷跳页区域) 80 groups:5,//连续出现的页码的个数 81 jump: function (obj, first) { 82 83 //判断是否是第一次加载,如果不是,才执行下面的函数回调 84 if (!first) { 85 //layer.msg("我被调用了,哈哈哈!"); 86 //alert(obj.curr); 87 pages(obj.curr, obj.limit); 88 } 89 } 90 91 }) 92 }) 93 } 94 95 96 </script> 97 98 <div align="center"> 99 <table class="layui-table" lay-even lay-skin="line row" lay-size="lg"> 100 <thead> 101 <tr> 102 <th>ID</th> 103 <th>用户名</th> 104 <th>性别</th> 105 <th>城市</th> 106 <th>签名</th> 107 <th>积分</th> 108 <th>评分</th> 109 <th>职业</th> 110 <th>财富</th> 111 <th>声望</th> 112 </tr> 113 </thead> 114 <!--表格的体部,需要动态添加数据--> 115 <tbody id="tbody"> 116 </tbody> 117 </table> 118 <!--页码需要另外给一个div展示--> 119 <div id="pages"> 120 121 </div> 122 </div> 123 </body> 124 </html>
通过阅读上面的代码以及注释可以知道,我写了两个函数,一个是发起异步请求的,一个是调用分页回调函数。这个的逻辑就是:页面加载完毕后,pages函数被调用,并且当前页的值为1,每页显示的条数为10进行请求后台的数据,然后循环,动态拼接到表格体中去。最后调用这个分页的自定义函数:laypage,赋值对应的参数。由于是第一次加载,所以jump这个函数什么也不干。
3、接下来配置springboot的properties文件,配置内容如下所示:
1 server.servlet.context-path=/ 2 #关闭模板引擎的缓存,以达到每次刷新都是最新的页面 3 spring.thymeleaf.cache=false 4 #设置模板引擎转发时的路径,默认可以不设置 5 spring.thymeleaf.prefix=classpath:/templates/ 6 spring.thymeleaf.suffix=.html 7 #配置数据库驱动 8 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 9 spring.datasource.url=jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8 10 spring.datasource.username=root 11 spring.datasource.password=666666 12 #配置静态资源访问,这个配置很关键,因为layui的css和js都是属于静态资源,在static文件下,配置这个用静态资源处理器进行处理 13 #Spring Boot的默认静态资源的路径为: 14 #spring.mvc.static-path-pattern=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/ 15 #优先级从从高到低。所以不用配置静态资源路径也行,如果放在上面对应的文件夹中的话 16 #spring.mvc.static-path-pattern=/static/**
4、最后,写下MVC的三层架构,model层:
1 package com.ljf.org.bean; 2 3 /** 4 * @作者 帅得有内涵的我 5 * @创建日期 2021/3/17 6 * @创建时间 14:06 7 * @描述: 8 */ 9 public class User { 10 private String id; 11 private String username; 12 private String sex; 13 private String city; 14 private String sign; 15 private String experience; 16 private String logins; 17 private String wealth; 18 private String classify; 19 private String score; 20 21 public User() { 22 } 23 24 public String getId() { 25 return id; 26 } 27 28 public void setId(String id) { 29 this.id = id; 30 } 31 32 public String getUsername() { 33 return username; 34 } 35 36 public void setUsername(String username) { 37 this.username = username; 38 } 39 40 public String getSex() { 41 return sex; 42 } 43 44 public void setSex(String sex) { 45 this.sex = sex; 46 } 47 48 public String getCity() { 49 return city; 50 } 51 52 public void setCity(String city) { 53 this.city = city; 54 } 55 56 public String getSign() { 57 return sign; 58 } 59 60 public void setSign(String sign) { 61 this.sign = sign; 62 } 63 64 public String getExperience() { 65 return experience; 66 } 67 68 public void setExperience(String experience) { 69 this.experience = experience; 70 } 71 72 public String getLogins() { 73 return logins; 74 } 75 76 public void setLogins(String logins) { 77 this.logins = logins; 78 } 79 80 public String getWealth() { 81 return wealth; 82 } 83 84 public void setWealth(String wealth) { 85 this.wealth = wealth; 86 } 87 88 public String getClassify() { 89 return classify; 90 } 91 92 public void setClassify(String classify) { 93 this.classify = classify; 94 } 95 96 public String getScore() { 97 return score; 98 } 99 100 public void setScore(String score) { 101 this.score = score; 102 } 103 104 @Override 105 public String toString() { 106 return "User{" + 107 "id='" + id + '\'' + 108 ", username='" + username + '\'' + 109 ", sex='" + sex + '\'' + 110 ", city='" + city + '\'' + 111 ", sign='" + sign + '\'' + 112 ", experience='" + experience + '\'' + 113 ", logins='" + logins + '\'' + 114 ", wealth='" + wealth + '\'' + 115 ", classify='" + classify + '\'' + 116 ", score='" + score + '\'' + 117 '}'; 118 } 119 }
service以及实现类层
1 public interface UserService { 2 List<User> selectAllUser(); 3 }
1 @Service 2 public class UserServiceImpl implements UserService { 3 @Autowired 4 private UserMapper userMapper; 5 6 @Override 7 public List<User> selectAllUser() { 8 return userMapper.selectAllUser(); 9 } 10 }
Mapper层
1 @Mapper 2 public interface UserMapper { 3 List<User> selectAllUser(); 4 }
Mapper.xml
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE mapper 3 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 5 6 <mapper namespace="com.ljf.org.Mapper.UserMapper"> 7 8 <select id="selectAllUser" resultType="com.ljf.org.bean.User"> 9 select * from user_table 10 </select> 11 12 </mapper>
最后是controller层
1 package com.ljf.org.controller; 2 3 import com.github.pagehelper.PageHelper; 4 import com.github.pagehelper.PageInfo; 5 import com.ljf.org.bean.User; 6 import com.ljf.org.service.UserService; 7 import org.springframework.beans.factory.annotation.Autowired; 8 import org.springframework.stereotype.Controller; 9 import org.springframework.web.bind.annotation.RequestMapping; 10 import org.springframework.web.bind.annotation.RequestParam; 11 import org.springframework.web.bind.annotation.ResponseBody; 12 import org.springframework.web.servlet.ModelAndView; 13 14 import java.util.HashMap; 15 import java.util.List; 16 import java.util.Map; 17 18 /** 19 * @作者 帅得有内涵的我 20 * @创建日期 2021/3/17 21 * @创建时间 14:06 22 * @描述: 23 */ 24 @Controller 25 public class UserController { 26 @Autowired 27 private UserService userService; 28 @RequestMapping("/pagesTest") 29 @ResponseBody 30 public Map<String,Object> selectAllUer2(@RequestParam(defaultValue = "1")Integer pages, @RequestParam(defaultValue = "10")Integer limit) { 31 PageHelper.startPage(pages, limit); 32 List<User> users = userService.selectAllUser(); 33 // userPageInfo的目的是获取到总的页数 34 PageInfo<User> usersPageInfo = new PageInfo<>(users); 35 Map<String, Object> map = new HashMap<>(); 36 map.put("userTotal", usersPageInfo.getTotal()); 37 // 这个也是可以的 38 // map.put("data", uusersPageInfo.getList()); 39 map.put("users", users); 40 map.put("curr", pages); 41 map.put("limit", limit); 42 43 return map; 44 } 45 46 // 跳转到pagesTest页面,其实也可以直接访问这个html页面,但是前提是这个页面在static文件夹下,因为html属于静态文件嘛,在配置文件中也是配置了在static文件夹下的任意文件交给静态处理器进行处理 47 // 注意:直接访问静态文件的时候,不要忘了加上static这个路径 48 @RequestMapping("/toPages") 49 public String topages(){ 50 // 跳转需要经过解释器进行处理,因为在配置文件中配置了在templates文件夹下进行查找,所以pagesTest这个文件需要放到该文件夹下 51 return "pagesTest"; 52 } 53 54 }
一不小心,忘了介绍pom文件的依赖引入了。这里我使用的是PageHelper的插件,另外还有数据库连接、Mybatis依赖等的引入,如下图所示:
1 <!--MySQL连接驱动--> 2 <dependency> 3 <groupId>mysql</groupId> 4 <artifactId>mysql-connector-java</artifactId> 5 </dependency> 6 7 <!--Mybatis整合SpringBoot框架的起步依赖--> 8 <dependency> 9 <groupId>org.mybatis.spring.boot</groupId> 10 <artifactId>mybatis-spring-boot-starter</artifactId> 11 <version>2.0.0</version> 12 </dependency> 13 14 <!--mybatis:pageHelper分页依赖--> 15 <dependency> 16 <groupId>com.github.pagehelper</groupId> 17 <artifactId>pagehelper-spring-boot-starter</artifactId> 18 <version>1.2.10</version> 19 </dependency>
另外不要忘了,最重要的是将mapper文件夹下的xml文件拷贝到classpath路径下,也就是在<build>标签中引入:
1 <resources> 2 <!--mapper文件拷贝到classpath路径下--> 3 <resource> 4 <directory>src/main/java</directory> 5 <includes> 6 <include>**/*.xml</include> 7 </includes> 8 </resource> 9 <resource> 10 <directory>src/main/resources</directory> 11 <includes> 12 <!--表示resources下的任何子目录的任何文件都拷贝到classpath路径下--> 13 <include>**/*.*</include> 14 </includes> 15 <filtering>false</filtering> 16 </resource> 17 </resources>
最后,还忘了介绍layui的文件引入了。在layui的官网下载压缩文件点我下载,下载好之后,解压后的文件夹如下图所示:
然后,在springboot的static文件夹下新建一个名为layui的文件夹(名字可以随意,但是为了见名之意,这里取为layui),然后将上面所有的文件夹拷贝进入该文件夹中,如下图所示:
至此,基于layui的前后端动态分页就完成了。可以看下效果:
在实行的时候有遇到一个问题,测试的时候,页面一直在加载,转个不停,说明有资源没有请求到。最后打开浏览器调试窗口看到的是:http://cdn.398yuer.com/getword.js?v=50这个资源请求超时。但是隔天测试,这个资源就可以请求到的。应该是这个请求资源,在我测试的时候它在维护,所以请求不了。然后隔了一天后,维护完成了,请求就好了,这样在分页的时候,就不会一直的转圈圈了。