这一段时间写项目用到了Vue+ElementUI,这里记录一下使用ElementUI内置分页插件结合后端SSM框架的实现思路和实现过程。javascript
其中遇到了不少坑,我会尽可能把见到的坑都记录下来,但愿对你有所帮助。html
本案例对应的开源项目地址请看个人GitHub仓库:前端
首先 让咱们看一下最终效果:mysql
本博文的主要讲一下Vue+ElementUI结合后端SpringMVC实现分页的实现思路,基本的elementUI用法请自行百度;git
Vue的经常使用语法能够看个人 博文 。github
关于SSM的整合教程能够看个人这篇 博文; GitHub。redis
<br/>spring
介绍
本案例中设计到的技术栈:
PageHelper: Mybatis的分页插件
一、SSM框架的整合教程能够参考个人这篇博文:手摸手带你整合SSM框架; GitHub。
二、在后端项目中导入PageHelper.jar
的依赖
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>4.0.0</version> </dependency>
***注意 使用PageHelper分页插件除了要导入依赖,还须要在Mybatis配置文件中进行相关配置,并交给Spring进行管理。以下配置便可:
<plugins> <!-- com.github.pagehelper 为 PageHelper 类所在包名 --> <plugin interceptor="com.github.pagehelper.PageHelper"> <!-- 设置数据库类型 Oracle,Mysql,MariaDB,SQLite,Hsqldb,PostgreSQL 六种数据库--> <property name="dialect" value="mysql"/> </plugin> </plugins>
这里还要注意的是PageHelper5.X版本和PageHelper4.X版本PageHelper类所在的包名是不一样的。 在Spring配置文件中扫描此配置文件便可:
三、在HTML中导入vue.js
and element-ui
。
好的,至此,咱们把基本的环境已经讲过了,下面看下相关前端代码:
<!-- 列表 --> <el-table ref="user" :data="user" tooltip-effect="dark" style="width: 100%"> <el-table-column prop="id" sortable label="编号" width="80"> </el-table-column> <el-table-column prop="username" sortable label="联系人" width="120"> </el-table-column> <el-table-column prop="phone" sortable label="联系电话" width="120"> </el-table-column> <el-table-column prop="mailbox" label="电子邮箱" width="150"> </el-table-column> <el-table-column prop="postalCode" sortable label="邮政编码" width="120"> </el-table-column> <el-table-column prop="date" sortable label="注册时间" width="200"> </el-table-column> <el-table-column prop="address" label="通信地址" width="200" show-overflow-tooltip> </el-table-column> </el-table> <!-- 分页 --> <div class="pagination"> <el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="pageConf.pageCode" :page-sizes="pageConf.pageOption" :page-size="pageConf.pageSize" layout="total, sizes, prev, pager, next, jumper" :total="pageConf.totalPage"> </el-pagination> </div>
注意咱们上面前端HTML样式用使用Vue绑定的数据:
一、列表数据
//注意这部分代码是在Vue实例中的data属性中定义的 data() { //用户信息 //element-ui的table须要的参数必须是Array类型的 user: [{ username: '', phone: '', mailbox: '', postalCode: '', date: '', address: '' }], }
上面ElementUI表格中<el-table>
中用Vue绑定的:data="user"
就是这个数据,注意:这里的user对象中的数据须要是Array类型的,不要问为何,请去看ElementUI源码;
二、分页数据
//注意这部分代码是在Vue实例中的data属性中定义的 data() { //定义分页Config pageConf: { //设置一些初始值(会被覆盖) pageCode: 1, //当前页 pageSize: 4, //每页显示的记录数 totalPage: 12, //总记录数 pageOption: [4, 10, 20], //分页选项 handleCurrentChange: function () { console.log("页码改变了"); } }, } methods: { //pageSize改变时触发的函数 handleSizeChange(val) {}, //当前页改变时触发的函数 handleCurrentChange(val) {}, }
上面<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
配合服用。
以上涉及两个函数handleSizeChange
、handleCurrentChange
,咱们要在其触发时自动改变对应的pageOption
参数。
<br/>
一、<el-table>
中须要渲染的数据仅须要传入:data="user"
便可,可是这个数据user
必须是一个对象数组,必定是数组
二、想要<el-table>
正确渲染你user
中定义的数据,你必须为每一个<el-table-column>
定义prop
属性,绑定对应你想展现的数据,否则ElementUI不知道你想展现什么。
三、pageOption
分页选项必定要注意,要配合pageSize
的默认值,不要乱定义,好比:pageSize: 2, pageOption: [10,20,30]
,这样你就会发现页码根本不能正确显示,由于你设置pageSize:2
表示你想每页展现2条数据,可是你又定义pageOption: [10,20,30]
第一个参数便是默认被选中的,即你又想每页显示10条数据,那么ElementUI就蒙蔽了,不知道你到底想每页显示几条数据。
三、根据上面的参数,以及handleSizeChange
、handleCurrentChange
这两个函数的参数你就应该想到分页的实现实际上是pageCode
(当前页)和pageSize
(每页显示的记录数)和后端进行数据交换的。在前端你须要关心的怎样把pageSize
和pageCode
传给后端进行分页查询;在后端你须要关心的是怎样调用pageHelper
插件将分页的记录数据(包括totalPage
、user
数据等)return 给前端。
<br/>
定义请求映射路径:findByPage
@RequestMapping("/findByPage") public PageBean findByPage(@RequestParam("pageCode") int pageCode, @RequestParam("pageSize") int pageSize) { System.out.println("分页的数据:" + userService.findByPage(pageCode, pageSize)); return userService.findByPage(pageCode, pageSize); }
如上是咱们在Controller中定义的请求映射路径,其中须要接收两个参数:pageCode
和pageSize
分别表示当前页、每页显示的记录数;即前端请求这个方法时只须要将pageCode
和pageSize
传进来就行,后端使用pageHelper
分页插件将查询到的数据进行分页,并将结果返回给前端。
对于请求映射中包含多个参数的,应该使用@RequestParam()
进行标记,否则可能报错400等。
<br/>
首先咱们须要定义分页实体类:PageBean.java
public class PageBean() implements Serialization { //当前页 private long total; //当前页记录 private List rows; }
由于咱们使用了mybatis的分页插件:PageHelper
,因此PageHelper
最终为咱们封装在PageBean
的数据应该是这个样子的:
**注意:**须要返回JSON格式数据。能够看到里面主要包含两个参数:total
、rows
total
表示当前数据的分页获得的总页数,至关于咱们前端定义的pageCode
。rows
表示当前查询到数据的集合体。即后端的逻辑比较简单,由于最麻烦的分页逻辑,PageHelper
已经帮咱们完成了,咱们须要作的:
一、在Controller中定义请求映射方法:PageBean findByPage(@RequestParam("pageCode")int pageCode, @RequestParam("pageSize")int pageSize){}
二、Controller调用Service,经过PageHelper
分页插件获取到这两个参数pageCode,pageSize,自动进行分页计算。
三、Service调用Dao,指定对应的SQLSELECT * FROM user
,能够看到这个SQL仅仅须要查询全部数据便可,返回的数据类型是com.github.pagehelper.Page
四、Controller须要返回给前端的数据类型是:PageBean
(咱们自定义的),其中有两个参数:com.github.pagehelper.Page.getTotal()
和com.github.pagehelper.Page.getResult()
。
五、综上,咱们基本已经获取到了数据,而后经过SpringMVC提供的注解:@RsponseBody
(局部标识方法)或@RestController
(全局标识类),自动将返回的数据转换为JSON格式,而后再发送给前端。
<br/>
前端逻辑相对复杂一些,咱们主要须要关注两点:
1.进入页面触发的事件方法、以及点击分页相关的按钮怎样和后端交互? 2.如何将后端交互返回的数据赋值给表格中的绑定的数据、以及分页组件中绑定的数据,并实现HTML页面的渲染?
进入页面触发的事件方法、以及点击分页相关的按钮怎样和后端交互?
1.有哪些可能被触发的事件和方法?
created
声明周期函数中调用findByPage(this.pageConf.pageCode,this.pageConf.pageSize)
(传入默认的值)。对应的HTML代码:findByPage(pageCode, pageSize) {},
@size-change
属性绑定。好比:原来4条/每页改变为6条/每页,就将触发这个函数;其中的参数val
表示当前页每页显示几条记录pageSize = val
。对应的HTML代码:handleSizeChange(val) { this.findByPage(this.pageConf.pageCode, val); },
每当pageSize改变就须要从新调用findByPage(this.pageConf.pageCode, val)
函数从新计算页面须要渲染的数据。
@current-change
属性绑定。好比:点击下一页、上一页,就会触发这个函数;其中的参数val
表示当前是第几页pageCode = val
。对应的HTML代码:handleCurrentChange(val) { this.findByPage(val, this.pageConf.pageSize); },
每当pageCode改变时就须要从新调用findByPage(val, this.pageConf.pageSize)
函数重新计算页面须要渲染的数据。
2.分页相关按钮是什么鬼?
在传统没有每页插件的时候,咱们一般会手写分页逻辑,那么就须要为每个页面绑定一个触发方法,而使用了element-ui提供的分页插件,大大简化了分页逻辑,其中点击的下一页、上一页、点击每页显示记录选项、去第几页等这些功能都是ElementUI自动帮咱们绑定了事件。
3.怎样和后端交互?
和后端实现交互的方法主要是findByPage()
这个核心方法,其相关JS代码:
findByPage(pageCode, pageSize) { this.$http.post('/user/findByPage.do', {pageCode: pageCode, pageSize: pageSize}).then(result => { this.pageConf.totalPage = result.body.total; this.user = result.body.rows; }); },
如上,findByPage()是咱们定义的分页的核心方法,全部其余分页中触发的方法都会调用这个方法从新和后端交互,获取到最新的数据并返回给页面。其中你须要注意:
findByPage()中包含两个参数:pageCode、pageSize。
调用vue-resource提供的post请求方法,其中传入两个参数pageCode、pageSize;在then()
回调函数中可获取请求返回的数据。
注意Controller返回的数据就在result
这个参数中,可是实际的数据是在result.body
中的,因此你直接result.total
是获取不到数据的。
前面已经看到了,后端主要返回两个封装了数据的参数:total
(总页数)、rows
(核心数据)
findByPage方法请求后端获得了total
和rows
,就应该分别赋值给this.pageConf.totalPage
和this.user
;根据Vue双向绑定的功能,页面新的数据会直接渲染出来。
如何将后端交互返回的数据赋值给表格中的绑定的数据、以及分页组件中绑定的数据,并实现HTML页面的渲染?
其实第一点中咱们已经讲到了,由于Vue有一个双向绑定的功能,即咱们请求后端将数据赋值给data:{}
中的对象后,HTML页面会当即渲染新的data
数据。
如何将后端返回的数据赋值给页面须要展现的数据?
首先是<el-table>
中要渲染的数据,其来自:data="user"
绑定的user对象,咱们须要将后端返回的数据赋值给这个user
根据双向绑定思想即会更新表格中的数据。
其次就是<el-pagination>
中定义的分页参数,因为element-ui分页插件已经帮咱们完成了不少逻辑计算,咱们须要交互改变的参数只有三个:pageCode
当前页、pageSize
每页显示的记录数、totalPage
总记录条数,然后端返回的数据咱们也看过,综上:咱们只须要将后端返回的总页数total
赋值给user
对象中的属性totalPage
便可。
主要JavaScript代码
findByPage(pageCode, pageSize) { this.$http.post('/user/findByPage.do', {pageCode: pageCode, pageSize: pageSize}).then(result => { this.pageConf.totalPage = result.body.total; this.user = result.body.rows; }); },
<br/>
通过上面的分析,其实不少代码已经展现出来了,下面咱们看看完整的代码:
实体类
public class PageBean implements Serializable { //当前页 private long total; //当前页记录 private List rows; ... } public class User implements Serializable { private Long id; //用户编号 private String username; //用户名 private String password; //密码 private String phone; //联系电话 private String mailbox; //邮箱 private String address; //地址 private String postalCode; //邮政编码 private String date; //注册日期 ... }
Controller
@ResponseBody @RequestMapping("/findByPage") public PageBean findByPage(@RequestParam("pageCode") int pageCode, @RequestParam("pageSize") int pageSize) { return userService.findByPage(pageCode, pageSize); }
Service
import com.github.pagehelper.Page; import com.github.pagehelper.PageHelper; import com.instrument.dao.UserDao; import com.instrument.entity.PageBean; import com.instrument.entity.User; ... public PageBean findByPage(int pageCode, int pageSize) { //使用Mybatis分页插件 PageHelper.startPage(pageCode,pageSize); //调用分页查询方法,其实就是查询全部数据,mybatis自动帮咱们进行分页计算 Page<User> page = userDao.findByPage(); return new PageBean(page.getTotal(),page.getResult()); }
这里dao层调用的findByPage()
对应的SQL仅仅是SELECT * FROM 表
。而分页是调用的startPage()
和Page
函数二者共同完成的分页逻辑计算,其返回的数据主要是在total
和rows
中封装着。
mapper.xml
<!-- 分页查询 --> <select id="findByPage" resultType="com.instrument.entity.User"> SELECT * FROM user </select>
<div id="#app"> <el-table ref="user" :data="user" tooltip-effect="dark" style="width: 100%"> <el-table-column prop="id" sortable label="编号" width="80"> </el-table-column> <el-table-column prop="username" sortable label="联系人" width="120"> </el-table-column> <el-table-column prop="phone" sortable label="联系电话" width="120"> </el-table-column> <el-table-column prop="mailbox" label="电子邮箱" width="150"> </el-table-column> <el-table-column prop="postalCode" sortable label="邮政编码" width="120"> </el-table-column> <el-table-column prop="date" sortable label="注册时间" width="200"> </el-table-column> <el-table-column prop="address" label="通信地址" width="200" show-overflow-tooltip> </el-table-column> </el-table> <!-- 分页 --> <div class="pagination"> <el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="pageConf.pageCode" :page-sizes="pageConf.pageOption" :page-size="pageConf.pageSize" layout="total, sizes, prev, pager, next, jumper" :total="pageConf.totalPage"> </el-pagination> </div> </div> <script type="text/javascript" src="../vue.js"></script> <script type="text/javascript"> new Vue({ el: '#app' data(){ //用户信息 //element-ui的table须要的参数必须是Array类型的 user: [{ username: '', phone: '', mailbox: '', postalCode: '', date: '', address: '' }], //定义分页Config pageConf: { //设置一些初始值(会被覆盖) pageCode: 1, //当前页 pageSize: 4, //每页显示的记录数 totalPage: 12, //总记录数 pageOption: [4, 10, 20], //分页选项 handleCurrentChange: function () { console.log("页码改变了"); } }, }, methods:{ findByPage(pageCode, pageSize) { this.$http.post('/user/findByPage.do', {pageCode: pageCode, pageSize: pageSize}).then(result => { this.pageConf.totalPage = result.body.total; this.user = result.body.rows; }); }, //pageSize改变时触发的函数 handleSizeChange(val) { this.findByPage(this.pageConf.pageCode, val); }, //当前页改变时触发的函数 handleCurrentChange(val) { this.findByPage(val, this.pageConf.pageSize); }, // 获取全部数据 findAll() { this.$http.post('/user/findAll.do').then(result => { this.user = result.body; }); } }, created(){ this.findAll(); this.findByPage(this.pageConf.pageCode, this.pageConf.pageSize); } }); </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.