1 分页功能开发
首先集成分页功能需要先在 Maven 中添加 pagehelper 依赖,然后在 ChapterService 中添加 pagehelper 的设置,接着试一下分页功能是否生效。后面添加分页功能的具体细节,第一修改成后端接收分页要求,第二修改前端分页布局,第三修改前端请求。
1 添加依赖
首先在 course 的 pom.xml 文件中添加依赖和版本
<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>1.2.10</version></dependency>
然后在 service 模块中的 pom.xml 添加
<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId></dependency>
2 添加设置
在 ChapterService 的 list 类第一句添加PageHelper.startPage(1,1);
即可,访问 http://127.0.0.1:9002/business/admin/chapter/list 即可看到只返回了一条记录。
1 添加后端 pageDto
package com.course.server.dto;import java.util.List;public class PageDto<T> {//当前页码protected int page;//每页条数protected int size;//总条数protected long total;protected List<T> list;public long getTotal() {return total;}@Overridepublic String toString() {return "PageDto{" +"page=" + page +", size=" + size +", total=" + total +", list=" + list +'}';}public List<T> getList() {return list;}public void setList(List<T> list) {this.list = list;}public void setTotal(long total) {this.total = total;}public int getPage() {return page;}public void setPage(int page) {this.page = page;}public int getSize() {return size;}public void setSize(int size) {this.size = size;}
}
2 调整后端 Controller Service 层
这里调用后端查询的方法是,前端先传入分页的参数 page 和 size;接着后端先查询出结果,然后将结果传给 PageInfo 实例,实例对结果进行分页处理后再赋给 PageDto 返回前端。
修改 ChapterController 层代码如下:
package com.course.business.controller.admin;import com.course.server.dto.PageDto;
import com.course.server.service.ChapterService;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@RestController
@RequestMapping("/admin/chapter")
public class ChapterController {@Resourceprivate ChapterService chapterService;@RequestMapping("/list")public PageDto list(@RequestBody PageDto pageDto){chapterService.list(pageDto);return pageDto;}
}
这里如果在 list 的传入函数中不加 @RequestBody 是接收不到传入的数据的。
ChapterService 代码如下:
package com.course.server.service;import com.course.server.domain.Chapter;
import com.course.server.domain.ChapterExample;
import com.course.server.dto.ChapterDto;
import com.course.server.dto.PageDto;
import com.course.server.mapper.ChapterMapper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import com.github.pagehelper.PageHelper;@Service
public class ChapterService {@Resourceprivate ChapterMapper chapterMapper;public void list(PageDto pageDto){PageHelper.startPage(pageDto.getPage(),pageDto.getSize());ChapterExample chapterExample = new ChapterExample();List<Chapter> chapterList = chapterMapper.selectByExample(chapterExample);PageInfo<Chapter> pageInfo = new PageInfo<>(chapterList);pageDto.setTotal(pageInfo.getTotal());List<ChapterDto> chapterDtosList = new ArrayList<>();for (int i = 0; i < chapterList.size(); i++) {Chapter chapter = chapterList.get(i);ChapterDto chapterDto = new ChapterDto();BeanUtils.copyProperties(chapter,chapterDto);chapterDtosList.add(chapterDto);}pageDto.setList(chapterDtosList);}
}
3 调整前端 chapter.vue
之前的 chapter 点击查询会直接返回所有结果,并不需要传给后端参数,所以直接用 get 请求。现在前端需要传递给后端 page 和 size 分页参数,所以需要用 post 请求,修改 method 中的 list 方法如下:
methods: {list(page) {let _this = this;console.log(page, _this.$refs.pagination.size),_this.$ajax.post('http://127.0.0.1:9000/business/admin/chapter/list',{page: 1,size: 1}).then((response)=>{console.log(response);_this.chapters = response.data.list;})}}
这时刷新会显示一条记录。
3 调整前端布局
这里添加了刷新按钮,和页面选择按钮。然后将 page 属性和选择的页面按钮绑定在一起,默认为1;将 size 属性和选择每页显示的数据条数绑定在一起,默认为10。
在 component 包中添加 pagination.vue 文件,代码如下:
<template><div class="pagination" role="group" aria-label="分页"><button type="button" class="btn btn-default btn-white btn-round"v-bind:disabled="page === 1"v-on:click="selectPage(1)">1</button><button type="button" class="btn btn-default btn-white btn-round"v-bind:disabled="page === 1"v-on:click="selectPage(page - 1)">上一页</button><button v-for="p in pages" v-bind:id="'page-' + p"type="button" class="btn btn-default btn-white btn-round"v-bind:class="{'btn-primary active':page == p}"v-on:click="selectPage(p)">{{p}}</button><button type="button" class="btn btn-default btn-white btn-round"v-bind:disabled="page === pageTotal"v-on:click="selectPage(page + 1)">下一页</button><button type="button" class="btn btn-default btn-white btn-round"v-bind:disabled="page === pageTotal"v-on:click="selectPage(pageTotal)">{{pageTotal||1}}</button> <span class="m--padding-10">每页<select v-model="size"><option value="1">1</option><option value="5">5</option><option value="10">10</option><option value="20">20</option><option value="50">50</option><option value="100">100</option></select>条,共【{{total}}】条</span></div>
</template><script>export default {name: 'pagination',props: {list: {type: Function,default: null},itemCount: Number // 显示的页码数,比如总共有100页,只显示10页,其它用省略号表示},data: function () {return {total: 0, // 总行数size: 10, // 每页条数page: 0, // 当前页码pageTotal: 0, // 总页数pages: [], // 显示的页码数组}},methods: {/*** 渲染分页组件* @param page* @param total*/render(page, total) {let _this = this;_this.page = page;_this.total = total;_this.pageTotal = Math.ceil(total / _this.size);_this.pages = _this.getPageItems(_this.pageTotal, page, _this.itemCount || 5);},/*** 查询某一页* @param page*/selectPage(page) {let _this = this;if (page < 1) {page = 1;}if (page > _this.pageTotal) {page = _this.pageTotal;}if (this.page !== page) {_this.page = page;if (_this.list) {_this.list(page);}}},/*** 当前要显示在页面上的页码* @param total* @param current* @param length* @returns {Array}*/getPageItems(total, current, length) {let items = [];if (length >= total) {for (let i = 1; i <= total; i++) {items.push(i);}} else {let base = 0;// 前移if (current - 0 > Math.floor((length - 1) / 2)) {// 后移base = Math.min(total, current - 0 + Math.ceil((length - 1) / 2)) - length;}for (let i = 1; i <= length; i++) {items.push(base + i);}}return items;}}}
</script><style scoped>.pagination {vertical-align: middle !important;font-size: 16px;margin-top: 0;margin-bottom: 10px;}.pagination button {margin-right: 5px;}.btn-primary.active {background-color: #2f7bba !important;border-color: #27689d !important;color: white !important;font-weight: 600;}/*.pagination select {*//*vertical-align: middle !important;*//*font-size: 16px;*//*margin-top: 0;*//*}*/
</style>
同时修改 chapter.vue 中 login 函数,添加 pagination 组件并显示,修改后代码如下:
<template><div><p><button v-on:click="list(1)" class="btn btn-white btn-default btn-round"><i class="ace-icon fa fa-refresh red2"></i>refresh</button></p><pagination ref="pagination" v-bind:list="list"></pagination><table id="simple-table" class="table table-bordered table-hover"...></div></template><script>import Pagination from "../../components/pagination";export default {components:{Pagination},name: "chapter",data: function(){return{chapters:[]}},mounted: function(){let _this = this;_this.list(1);},methods: {list(page) {let _this = this;console.log(page, _this.$refs.pagination.size),_this.$ajax.post('http://127.0.0.1:9000/business/admin/chapter/list',{page: page,size: _this.$refs.pagination.size}).then((response)=>{console.log(response);_this.chapters = response.data.list;_this.$refs.pagination.render(page, response.data.total)})}}}
</script>
这时可以选择页数刷新,成功显示出数据。
2 增加章节
下面我们增加一个增加章节的功能。首先需要在前端增加添加的页面,这里选择 bootstrap 中的表单和填出层,进行一些修改;然后在 chapter.vue 中增加 save 函数请求后端保存章节;最后在后端增加保存功能。
1 添加增加组件
有些组件可以直接去全局 CSS 样式复制了修改,这里用了模态框和表单的例子来修改。
表单修改后代码如下:
<div id="form-modal" class="modal fade" tabindex="-1" role="dialog"><div class="modal-dialog" role="document"><div class="modal-content"><div class="modal-header"><button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button><h4 class="modal-title">表单</h4></div><div class="modal-body"><form class="form-horizontal"><div class="form-group"><label class="col-sm-2 control-label">名称</label><div class="col-sm-10"><input v-model="chapter.name" class="form-control" placeholder="名称"></div></div><div class="form-group"><label class="col-sm-2 control-label">课程ID</label><div class="col-sm-10"><input v-model="chapter.courseId" class="form-control" placeholder="课程ID"></div></div></form></div><div class="modal-footer"><button type="button" class="btn btn-default" data-dismiss="modal">Close</button><button type="button" v-on:click="save()" class="btn btn-primary">Save changes</button></div></div><!-- /.modal-content --></div><!-- /.modal-dialog --></div><!-- /.modal -->
同时在刷新按钮旁边新增一个 add 按钮。代码如下:
<button v-on:click="add()" class="btn btn-white btn-default btn-round"><i class="ace-icon fa fa-edit red2"></i>add</button>
2 前端
点击 add 按钮时,模态框显示;点击 save 按钮后将表单数据传给后端,如果成功保存,则将页面关闭并显示第一页。
add() {$("#form-modal").modal("show");},save(page) {let _this = this;console.log(page);_this.$ajax.post('http://127.0.0.1:9000/business/admin/chapter/save',_this.chapter).then((response)=>{console.log("保存大章结果",response);let res = response.data;if(res.success){$("#form-modal").modal("hide");_this.list(1);}})}
3 后端添加
在 Controller 层和 Service 层添加 save 功能的对应代码。
Controller 层代码如下:
@RequestMapping("/save")public ChapterDto save(@RequestBody ChapterDto chapterDto){chapterDto.setId(UuidUtil.getShortUuid());chapterService.save(chapterDto);return chapterDto;}
Service 层代码如下:
public void save(ChapterDto chapterDto){Chapter chapter = new Chapter();BeanUtils.copyProperties(chapterDto,chapter);chapterMapper.insert(chapter);}
其中后端增加了工具类 CopyUtil 和 UuidUtil 类。CopyUtil 功能是复制实例内容到新的实例中,一般用在 domain 类和 dto 类转换。UuidUtil 功能是生成 8位 id,这里在 save 方法中用到了。
CopyUtil
package com.course.server.util;import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;import java.util.ArrayList;
import java.util.List;public class CopyUtil {public static <T> List<T> copyList(List source, Class<T> clazz) {List<T> target = new ArrayList<>();if (!CollectionUtils.isEmpty(source)){if (!CollectionUtils.isEmpty(source)){for (Object c: source) {T obj = copy(c, clazz);target.add(obj);}}}return target;}public static <T> T copy(Object source, Class<T> clazz) {if (source == null) {return null;}T obj = null;try {obj = clazz.newInstance();} catch (Exception e) {e.printStackTrace();}BeanUtils.copyProperties(source, obj);return obj;}
}
UuidUtil
package com.course.server.util;import java.util.UUID;public class UuidUtil {public static String[] chars = new String[] { "a", "b", "c", "d", "e", "f","g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s","t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5","6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I","J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V","W", "X", "Y", "Z" };/*** 获取短UUID* @return*/public static String getShortUuid() {StringBuffer shortBuffer = new StringBuffer();String uuid = UuidUtil.getUuid();for (int i = 0; i < 8; i++) {String str = uuid.substring(i * 4, i * 4 + 4);int x = Integer.parseInt(str, 16);shortBuffer.append(chars[x % 0x3E]); // 对62取余}return shortBuffer.toString();}/*** 获得32位UUID*/public static String getUuid(){String uuid = UUID.randomUUID().toString();//去掉“-”符号return uuid.replaceAll("-", "");}public static void main(String[] args) {System.out.println(getUuid());}
}
同时,为了前后端数据交互数据的统一,新增了一个 responsedto 类。
package com.course.server.dto;public class ResponseDto<T> {/*** 业务上的成功或失败*/private boolean success = true;/*** 返回码*/private String code;/*** 返回信息*/private String message;/*** 返回泛型数据,自定义类型*/private T content;public String getCode() {return code;}public void setCode(String code) {this.code = code;}public boolean getSuccess() {return success;}public void setSuccess(boolean success) {this.success = success;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}public T getContent() {return content;}public void setContent(T content) {this.content = content;}@Overridepublic String toString() {final StringBuffer sb = new StringBuffer("ResponseDto{");sb.append("success=").append(success);sb.append(", code='").append(code).append('\'');sb.append(", message='").append(message).append('\'');sb.append(", content=").append(content);sb.append('}');return sb.toString();}
}
这里将 Controller 层的返回统一改为 ResponseDto 类型,代码如下:
package com.course.business.controller.admin;import com.course.server.dto.ChapterDto;
import com.course.server.dto.PageDto;
import com.course.server.dto.ResponseDto;
import com.course.server.service.ChapterService;
import com.course.server.util.UuidUtil;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@RestController
@RequestMapping("/admin/chapter")
public class ChapterController {@Resourceprivate ChapterService chapterService;@RequestMapping("/list")public ResponseDto list(@RequestBody PageDto pageDto){chapterService.list(pageDto);ResponseDto responseDto = new ResponseDto();responseDto.setContent(pageDto);return responseDto;}@RequestMapping("/save")public ResponseDto save(@RequestBody ChapterDto chapterDto){chapterDto.setId(UuidUtil.getShortUuid());chapterService.save(chapterDto);ResponseDto responseDto = new ResponseDto();responseDto.setContent(chapterDto);return responseDto;}
}
总结
以上就完成了分页功能和增加章节功能的增加,并且将工具类封装在一起,还统一了后端返回给前端的数据类型。