当前位置:   article > 正文

Vue+ElementUI+SpringMVC实现分页

springmvc vue elementui 三级菜单

Vue + ElementUI + SpringMVC实现分页

这一段时间写项目用到了Vue+ElementUI,这里记录一下使用ElementUI内置分页插件结合后端SSM框架的实现思路和实现过程。

其中遇到了很多坑,我会尽量把见到的坑都记录下来,希望对你有所帮助。

本案例对应的开源项目地址请看我的GitHub仓库:

首先 让我们看一下最终效果:

起步

本博文的主要讲一下Vue+ElementUI结合后端SpringMVC实现分页的实现思路,基本的elementUI用法请自行百度;

Vue的常用语法可以看我的 博文

关于SSM的整合教程可以看我的这篇 博文GitHub

<br/>

介绍

本案例中设计到的技术栈:

准备

1、SSM框架的整合教程可以参考我的这篇博文:手摸手带你整合SSM框架; GitHub

2、在后端项目中导入PageHelper.jar的依赖

  1. <dependency>
  2. <groupId>com.github.pagehelper</groupId>
  3. <artifactId>pagehelper</artifactId>
  4. <version>4.0.0</version>
  5. </dependency>

***注意 使用PageHelper分页插件除了要导入依赖,还需要在Mybatis配置文件中进行相关配置,并交给Spring进行管理。如下配置即可:

  1. <plugins>
  2. <!-- com.github.pagehelper 为 PageHelper 类所在包名 -->
  3. <plugin interceptor="com.github.pagehelper.PageHelper">
  4. <!-- 设置数据库类型 Oracle,Mysql,MariaDB,SQLite,Hsqldb,PostgreSQL 六种数据库-->
  5. <property name="dialect" value="mysql"/>
  6. </plugin>
  7. </plugins>

这里还要注意的是PageHelper5.X版本和PageHelper4.X版本PageHelper类所在的包名是不同的。 在Spring配置文件中扫描此配置文件即可:

3、在HTML中导入vue.js and element-ui

好的,至此,我们把基本的环境已经讲过了,下面看下相关前端代码:

  1. <!-- 列表 -->
  2. <el-table
  3. ref="user"
  4. :data="user"
  5. tooltip-effect="dark"
  6. style="width: 100%">
  7. <el-table-column
  8. prop="id"
  9. sortable
  10. label="编号"
  11. width="80">
  12. </el-table-column>
  13. <el-table-column
  14. prop="username"
  15. sortable
  16. label="联系人"
  17. width="120">
  18. </el-table-column>
  19. <el-table-column
  20. prop="phone"
  21. sortable
  22. label="联系电话"
  23. width="120">
  24. </el-table-column>
  25. <el-table-column
  26. prop="mailbox"
  27. label="电子邮箱"
  28. width="150">
  29. </el-table-column>
  30. <el-table-column
  31. prop="postalCode"
  32. sortable
  33. label="邮政编码"
  34. width="120">
  35. </el-table-column>
  36. <el-table-column
  37. prop="date"
  38. sortable
  39. label="注册时间"
  40. width="200">
  41. </el-table-column>
  42. <el-table-column
  43. prop="address"
  44. label="通讯地址"
  45. width="200"
  46. show-overflow-tooltip>
  47. </el-table-column>
  48. </el-table>
  49. <!-- 分页 -->
  50. <div class="pagination">
  51. <el-pagination
  52. background
  53. @size-change="handleSizeChange"
  54. @current-change="handleCurrentChange"
  55. :current-page="pageConf.pageCode"
  56. :page-sizes="pageConf.pageOption"
  57. :page-size="pageConf.pageSize"
  58. layout="total, sizes, prev, pager, next, jumper"
  59. :total="pageConf.totalPage">
  60. </el-pagination>
  61. </div>

前端

注意我们上面前端HTML样式用使用Vue绑定的数据:

1、列表数据

  1. //注意这部分代码是在Vue实例中的data属性中定义的
  2. data() {
  3. //用户信息
  4. //element-ui的table需要的参数必须是Array类型的
  5. user: [{
  6. username: '',
  7. phone: '',
  8. mailbox: '',
  9. postalCode: '',
  10. date: '',
  11. address: ''
  12. }],
  13. }

上面ElementUI表格中<el-table>中用Vue绑定的:data="user"就是这个数据,注意:这里的user对象中的数据需要是Array类型的,不要问为什么,请去看ElementUI源码;

2、分页数据

  1. //注意这部分代码是在Vue实例中的data属性中定义的
  2. data() {
  3. //定义分页Config
  4. pageConf: {
  5. //设置一些初始值(会被覆盖)
  6. pageCode: 1, //当前页
  7. pageSize: 4, //每页显示的记录数
  8. totalPage: 12, //总记录数
  9. pageOption: [4, 10, 20], //分页选项
  10. handleCurrentChange: function () {
  11. console.log("页码改变了");
  12. }
  13. },
  14. }
  15. methods: {
  16. //pageSize改变时触发的函数
  17. handleSizeChange(val) {},
  18. //当前页改变时触发的函数
  19. handleCurrentChange(val) {},
  20. }

上面<el-pagination>中绑定的数据就来自这个对象:pageConf,那么下面你需要关注<el-pagination>中的几个配置参数(方法通过Vue的@绑定,数据通过Vue的:绑定):

  • @size-change: 表示每页记录的个数发生变化时触发的函数,如:原来是每页/3条,变为每页/6条;handleSizeChange中包含一个参数表示当前是每页显示几条记录。

  • @current-change: 表示当前页发生变化时触发的函数,如:点击下一页;handleCurrentChange中包含一个参数表示当前是第几页。

  • :current-page: 当前页,即我们命名的pageCode,表示当前页面上展示的第几页。

  • :page-sizes: 分页选项,即页面提供一个列表让你选择每页显示多少条记录,注意这个参数的第一个值表示当前页是每页/记录,你写上即生效。

  • :page-size: 表示每页显示的记录数,即我们命名的pageSize

  • :total: 表示总记录数,即我们这个表格中一共要显示多少条数据。

<br/>

注意:

  • 以上代码可能与截图中样式不符,因为我把这篇博文中不涉及的都删除了。

  • 表格中的数据来自:data这个绑定的对象数组中,即我们再Vue实例data中定义的user: [{}],前提是你在每一个<el-table-column>中都定义了prop并标识了user:[{}]中定义的变量,不然element-ui不知道你想在表格的这一行显示什么,当然这已经比我们常用的表格渲染数据方便很多了。

  • element-ui自带的分页插件需要提供数据才能正常显示分页信息,这些数据都应该是动态的,所以我们绑定在pageConf对象中;因为这些数据应该是后端读取出来的,即通过得到后端传来的分页数据,我们才知道这里的分页信息应该怎样定义。

  • 在data中定义的pageConf是初始化参数,最后会被覆盖掉,但是要注意pageOption这个参数,一定要和初始的pageSize配合服用。

  • 以上涉及两个函数handleSizeChangehandleCurrentChange,我们要在其触发时自动改变对应的pageOption参数。

<br/>

会遇到的坑

1、<el-table>中需要渲染的数据仅需要传入:data="user"即可,但是这个数据user必须是一个对象数组,一定是数组

2、想要<el-table>正确渲染你user中定义的数据,你必须为每个<el-table-column>定义prop属性,绑定对应你想展示的数据,不然ElementUI不知道你想展示什么。

3、pageOption分页选项一定要注意,要配合pageSize的默认值,不要乱定义,比如:pageSize: 2, pageOption: [10,20,30],这样你就会发现页码根本不能正确显示,因为你设置pageSize:2表示你想每页展示2条数据,但是你又定义pageOption: [10,20,30]第一个参数即是默认被选中的,即你又想每页显示10条数据,那么ElementUI就蒙蔽了,不知道你到底想每页显示几条数据。

3、根据上面的参数,以及handleSizeChangehandleCurrentChange这两个函数的参数你就应该想到分页的实现其实是pageCode(当前页)和pageSize(每页显示的记录数)和后端进行数据交换的。在前端你需要关心的怎样把pageSizepageCode传给后端进行分页查询;在后端你需要关心的是怎样调用pageHelper插件将分页的记录数据(包括totalPageuser数据等)return 给前端。

<br/>

后端

定义请求映射路径:findByPage

  1. @RequestMapping("/findByPage")
  2. public PageBean findByPage(@RequestParam("pageCode") int pageCode, @RequestParam("pageSize") int pageSize) {
  3. System.out.println("分页的数据:" + userService.findByPage(pageCode, pageSize));
  4. return userService.findByPage(pageCode, pageSize);
  5. }

注意

如上是我们在Controller中定义的请求映射路径,其中需要接收两个参数:pageCodepageSize分别表示当前页、每页显示的记录数;即前端请求这个方法时只需要将pageCodepageSize传进来就行,后端使用pageHelper分页插件将查询到的数据进行分页,并将结果返回给前端。

对于请求映射中包含多个参数的,应该使用@RequestParam()进行标记,不然可能报错400等。

<br/>

逻辑思路

后端

首先我们需要定义分页实体类:PageBean.java

  1. public class PageBean() implements Serialization {
  2. //当前页
  3. private long total;
  4. //当前页记录
  5. private List rows;
  6. }

因为我们使用了mybatis的分页插件:PageHelper,所以PageHelper最终为我们封装在PageBean的数据应该是这个样子的:

**注意:**需要返回JSON格式数据。可以看到里面主要包含两个参数:totalrows

  • total表示当前数据的分页得到的总页数,相当于我们前端定义的pageCode
  • rows表示当前查询到数据的集合体。

即后端的逻辑比较简单,因为最麻烦的分页逻辑,PageHelper已经帮我们完成了,我们需要做的:

1、在Controller中定义请求映射方法:PageBean findByPage(@RequestParam("pageCode")int pageCode, @RequestParam("pageSize")int pageSize){}

2、Controller调用Service,通过PageHelper分页插件获取到这两个参数pageCode,pageSize,自动进行分页计算。

3、Service调用Dao,指定对应的SQLSELECT * FROM user,可以看到这个SQL仅仅需要查询所有数据即可,返回的数据类型是com.github.pagehelper.Page

4、Controller需要返回给前端的数据类型是:PageBean(我们自定义的),其中有两个参数:com.github.pagehelper.Page.getTotal()com.github.pagehelper.Page.getResult()

5、综上,我们基本已经获取到了数据,然后通过SpringMVC提供的注解:@RsponseBody(局部标识方法)或@RestController(全局标识类),自动将返回的数据转换为JSON格式,然后再发送给前端。

<br/>

前端

前端逻辑相对复杂一些,我们主要需要关注两点:

1.进入页面触发的事件方法、以及点击分页相关的按钮怎样和后端交互? 2.如何将后端交互返回的数据赋值给表格中的绑定的数据、以及分页组件中绑定的数据,并实现HTML页面的渲染?

第一点

进入页面触发的事件方法、以及点击分页相关的按钮怎样和后端交互?

1.有哪些可能被触发的事件和方法?

  • findByPage(pageCode,pageSize) 这个是分页的核心方法,会被多次触发。又因为进入页面就应该理解渲染表格中的数据,所以分页方法应在渲染页面时就执行,所以需要在created声明周期函数中调用findByPage(this.pageConf.pageCode,this.pageConf.pageSize)(传入默认的值)。对应的HTML代码:
findByPage(pageCode, pageSize) {},
  • handleSizeChange(val) 这个函数是当pageSize(每页显示的记录数)改变时被触发,通过HTML中的@size-change属性绑定。比如:原来4条/每页改变为6条/每页,就将触发这个函数;其中的参数val表示当前页每页显示几条记录pageSize = val。对应的HTML代码:
  1. handleSizeChange(val) {
  2. this.findByPage(this.pageConf.pageCode, val);
  3. },

每当pageSize改变就需要重新调用findByPage(this.pageConf.pageCode, val)函数重新计算页面需要渲染的数据。

  • handleCurrentChange(val) 这个函数是当pageCode(当前页)改变时触发的函数,通过HTML中的@current-change属性绑定。比如:点击下一页、上一页,就会触发这个函数;其中的参数val表示当前是第几页pageCode = val。对应的HTML代码:
  1. handleCurrentChange(val) {
  2. this.findByPage(val, this.pageConf.pageSize);
  3. },

每当pageCode改变时就需要重新调用findByPage(val, this.pageConf.pageSize)函数从新计算页面需要渲染的数据。

2.分页相关按钮是什么鬼?

在传统没有每页插件的时候,我们通常会手写分页逻辑,那么就需要为每一个页面绑定一个触发方法,而使用了element-ui提供的分页插件,大大简化了分页逻辑,其中点击的下一页、上一页、点击每页显示记录选项、去第几页等这些功能都是ElementUI自动帮我们绑定了事件。

3.怎样和后端交互?

和后端实现交互的方法主要是findByPage()这个核心方法,其相关JS代码:

  1. findByPage(pageCode, pageSize) {
  2. this.$http.post('/user/findByPage.do', {pageCode: pageCode, pageSize: pageSize}).then(result => {
  3. this.pageConf.totalPage = result.body.total;
  4. this.user = result.body.rows;
  5. });
  6. },

如上,findByPage()是我们定义的分页的核心方法,所有其他分页中触发的方法都会调用这个方法重新和后端交互,获取到最新的数据并返回给页面。其中你需要注意:

  • findByPage()中包含两个参数:pageCode、pageSize。

  • 调用vue-resource提供的post请求方法,其中传入两个参数pageCode、pageSize;在then()回调函数中可获取请求返回的数据。

  • 注意Controller返回的数据就在result这个参数中,但是实际的数据是在result.body中的,所以你直接result.total是获取不到数据的。

  • 前面已经看到了,后端主要返回两个封装了数据的参数:total(总页数)、rows(核心数据)

  • findByPage方法请求后端得到了totalrows,就应该分别赋值给this.pageConf.totalPagethis.user;根据Vue双向绑定的功能,页面新的数据会直接渲染出来。

第二点

如何将后端交互返回的数据赋值给表格中的绑定的数据、以及分页组件中绑定的数据,并实现HTML页面的渲染?

其实第一点中我们已经讲到了,因为Vue有一个双向绑定的功能,即我们请求后端将数据赋值给data:{}中的对象后,HTML页面会立即渲染新的data数据。

如何将后端返回的数据赋值给页面需要展示的数据?

首先是<el-table>中要渲染的数据,其来自:data="user"绑定的user对象,我们需要将后端返回的数据赋值给这个user根据双向绑定思想即会更新表格中的数据。

其次就是<el-pagination>中定义的分页参数,由于element-ui分页插件已经帮我们完成了很多逻辑计算,我们需要交互改变的参数只有三个:pageCode当前页、pageSize每页显示的记录数、totalPage总记录条数,而后端返回的数据我们也看过,综上:我们只需要将后端返回的总页数total赋值给user对象中的属性totalPage即可。

主要JavaScript代码

  1. findByPage(pageCode, pageSize) {
  2. this.$http.post('/user/findByPage.do', {pageCode: pageCode, pageSize: pageSize}).then(result => {
  3. this.pageConf.totalPage = result.body.total;
  4. this.user = result.body.rows;
  5. });
  6. },

<br/>

代码编写

经过上面的分析,其实很多代码已经展示出来了,下面我们看看完整的代码:

后端

实体类

  1. public class PageBean implements Serializable {
  2. //当前页
  3. private long total;
  4. //当前页记录
  5. private List rows;
  6. ...
  7. }
  8. public class User implements Serializable {
  9. private Long id; //用户编号
  10. private String username; //用户名
  11. private String password; //密码
  12. private String phone; //联系电话
  13. private String mailbox; //邮箱
  14. private String address; //地址
  15. private String postalCode; //邮政编码
  16. private String date; //注册日期
  17. ...
  18. }

Controller

  1. @ResponseBody
  2. @RequestMapping("/findByPage")
  3. public PageBean findByPage(@RequestParam("pageCode") int pageCode, @RequestParam("pageSize") int pageSize) {
  4. return userService.findByPage(pageCode, pageSize);
  5. }

Service

  1. import com.github.pagehelper.Page;
  2. import com.github.pagehelper.PageHelper;
  3. import com.instrument.dao.UserDao;
  4. import com.instrument.entity.PageBean;
  5. import com.instrument.entity.User;
  6. ...
  7. public PageBean findByPage(int pageCode, int pageSize) {
  8. //使用Mybatis分页插件
  9. PageHelper.startPage(pageCode,pageSize);
  10. //调用分页查询方法,其实就是查询所有数据,mybatis自动帮我们进行分页计算
  11. Page<User> page = userDao.findByPage();
  12. return new PageBean(page.getTotal(),page.getResult());
  13. }

这里dao层调用的findByPage()对应的SQL仅仅是SELECT * FROM 表。而分页是调用的startPage()Page函数两者共同完成的分页逻辑计算,其返回的数据主要是在totalrows中封装着。

mapper.xml

  1. <!-- 分页查询 -->
  2. <select id="findByPage" resultType="com.instrument.entity.User">
  3. SELECT * FROM user
  4. </select>

前端

  1. <div id="#app">
  2. <el-table
  3. ref="user"
  4. :data="user"
  5. tooltip-effect="dark"
  6. style="width: 100%">
  7. <el-table-column
  8. prop="id"
  9. sortable
  10. label="编号"
  11. width="80">
  12. </el-table-column>
  13. <el-table-column
  14. prop="username"
  15. sortable
  16. label="联系人"
  17. width="120">
  18. </el-table-column>
  19. <el-table-column
  20. prop="phone"
  21. sortable
  22. label="联系电话"
  23. width="120">
  24. </el-table-column>
  25. <el-table-column
  26. prop="mailbox"
  27. label="电子邮箱"
  28. width="150">
  29. </el-table-column>
  30. <el-table-column
  31. prop="postalCode"
  32. sortable
  33. label="邮政编码"
  34. width="120">
  35. </el-table-column>
  36. <el-table-column
  37. prop="date"
  38. sortable
  39. label="注册时间"
  40. width="200">
  41. </el-table-column>
  42. <el-table-column
  43. prop="address"
  44. label="通讯地址"
  45. width="200"
  46. show-overflow-tooltip>
  47. </el-table-column>
  48. </el-table>
  49. <!-- 分页 -->
  50. <div class="pagination">
  51. <el-pagination
  52. background
  53. @size-change="handleSizeChange"
  54. @current-change="handleCurrentChange"
  55. :current-page="pageConf.pageCode"
  56. :page-sizes="pageConf.pageOption"
  57. :page-size="pageConf.pageSize"
  58. layout="total, sizes, prev, pager, next, jumper"
  59. :total="pageConf.totalPage">
  60. </el-pagination>
  61. </div>
  62. </div>
  63. <script type="text/javascript" src="../vue.js"></script>
  64. <script type="text/javascript">
  65. new Vue({
  66. el: '#app'
  67. data(){
  68. //用户信息
  69. //element-ui的table需要的参数必须是Array类型的
  70. user: [{
  71. username: '',
  72. phone: '',
  73. mailbox: '',
  74. postalCode: '',
  75. date: '',
  76. address: ''
  77. }],
  78. //定义分页Config
  79. pageConf: {
  80. //设置一些初始值(会被覆盖)
  81. pageCode: 1, //当前页
  82. pageSize: 4, //每页显示的记录数
  83. totalPage: 12, //总记录数
  84. pageOption: [4, 10, 20], //分页选项
  85. handleCurrentChange: function () {
  86. console.log("页码改变了");
  87. }
  88. },
  89. },
  90. methods:{
  91. findByPage(pageCode, pageSize) {
  92. this.$http.post('/user/findByPage.do', {pageCode: pageCode, pageSize: pageSize}).then(result => {
  93. this.pageConf.totalPage = result.body.total;
  94. this.user = result.body.rows;
  95. });
  96. },
  97. //pageSize改变时触发的函数
  98. handleSizeChange(val) {
  99. this.findByPage(this.pageConf.pageCode, val);
  100. },
  101. //当前页改变时触发的函数
  102. handleCurrentChange(val) {
  103. this.findByPage(val, this.pageConf.pageSize);
  104. },
  105. // 获取所有数据
  106. findAll() {
  107. this.$http.post('/user/findAll.do').then(result => {
  108. this.user = result.body;
  109. });
  110. }
  111. },
  112. created(){
  113. this.findAll();
  114. this.findByPage(this.pageConf.pageCode, this.pageConf.pageSize);
  115. }
  116. });
  117. </script>

以上代码我们基本已经解释过了,唯一没有提到的就是findAll()这个方法,要知道,进入到页面后,首先就是展示所有数据(即使有没有分页);那么就需要在生命周期函数created中执行findAll()获取所有数据直接渲染到页面上this.user=result.body即可。其次又因为我们使用了分页查询功能,进入页面后展示的数据应该是分页查询后的数据(因为我们设置有默认的分页参数值)。

<br/>

交流

如果大家有兴趣,欢迎大家加入我的Java交流技术群:671017003 ,一起交流学习Java技术。博主目前一直在自学JAVA中,技术有限,如果可以,会尽力给大家提供一些帮助,或是一些学习方法,当然群里的大佬都会积极给新手答疑的。所以,别犹豫,快来加入我们吧!

<br/>

联系

If you have some questions after you see this article, you can contact me or you can find some info by clicking these links.

转载于:https://my.oschina.net/u/3955926/blog/1936297

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/579928
推荐阅读
相关标签
  

闽ICP备14008679号