上节课我们实现了展示后台页面的功能,如下图所示。可以看到,目前页面是没有数据的,我们本节课便一起学习下如何展示商品列表。
我们知道,EasyUI的最大特点便是局部刷新,所有展示都是分模块展示的,不像我们一般页面采用全部刷新。查询商品是index.jsp中的一个模块展示而已,我们下面来看下index.jsp页面代码,如下图所示。可以看到,当我们点击查询商品的时候实际上是去访问item-list这么一个逻辑视图。
我们再来看下item-list.jsp页面代码,如下图所示,我们可以看到这个页面代码只是有个table和一个div,并没有文件头和尾,因此这不是一个完整的页面,而只是一个片段而已(它是index.jsp的一个代码片段而已,而EasyUI的特点便是对这样的代码块进行刷新,而不会刷新其它模块)。
下面我们再具体看下item-list.jsp这个页面的代码,首先看下<table>头信息,如下所示,可以看到,table的class设置成了"easyui-datagrid",设置了它之后便默认把表格渲染成我们在最上方那张图所看到的表格样式。虽然不太好看,但是对于后台来说能用即可,美观并不是多么重要。data-options中singleSelect:false表示可以多选,如果想要实现只能选择一条记录的效果,可以把singleSelect的值设置为true。
- <table class="easyui-datagrid" id="itemList" title="商品列表"
- data-options="singleSelect:false,collapsible:true,pagination:true,url:'/item/list',method:'get',pageSize:30,toolbar:toolbar">
如果把collapsible的值设置为false,那么这个按钮将不再显示,如下图所示(注:修改页面后不用重新启动,直接刷新页面即可)。
pagination:true代表的意思是要显示分页,如果不想分页就把该值设置为false。设置成false后界面如下图所示,可以发现,与上面的图相比,没有了分页的信息。
url:'/item/list'的意思是我们初始化商品列表请求的url。method:'get'表示请求方式是GET。pageSize:30表示每页显示30条记录,我们从有分页信息的图中可以看到"30"的信息,就是这里设置的值。toolbar:toolbar指定工具栏,如上图的"新增"、"编辑"、"删除"、"下架"、"上架"这些工具。toolbar是由js定义的,代码就在item-list页面下方的js部分,如下所示。
- var toolbar = [{
- text:'新增',
- iconCls:'icon-add',
- handler:function(){
- $(".tree-title:contains('新增商品')").parent().click();
- }
- },{
- text:'编辑',
- iconCls:'icon-edit',
- handler:function(){
- var ids = getSelectionsIds();
- if(ids.length == 0){
- $.messager.alert('提示','必须选择一个商品才能编辑!');
- return ;
- }
- if(ids.indexOf(',') > 0){
- $.messager.alert('提示','只能选择一个商品!');
- return ;
- }
- $("#itemEditWindow").window({
- onLoad :function(){
- //回显数据
- var data = $("#itemList").datagrid("getSelections")[0];
- data.priceView = TAOTAO.formatPrice(data.price);
- $("#itemeEditForm").form("load",data);
- // 加载商品描述
- $.getJSON('/rest/item/query/item/desc/'+data.id,function(_data){
- if(_data.status == 200){
- //UM.getEditor('itemeEditDescEditor').setContent(_data.data.itemDesc, false);
- itemEditEditor.html(_data.data.itemDesc);
- }
- });
- //加载商品规格
- $.getJSON('/rest/item/param/item/query/'+data.id,function(_data){
- if(_data && _data.status == 200 && _data.data && _data.data.paramData){
- $("#itemeEditForm .params").show();
- $("#itemeEditForm [name=itemParams]").val(_data.data.paramData);
- $("#itemeEditForm [name=itemParamId]").val(_data.data.id);
- //回显商品规格
- var paramData = JSON.parse(_data.data.paramData);
- var html = "<ul>";
- for(var i in paramData){
- var pd = paramData[i];
- html+="<li><table>";
- html+="<tr><td colspan=\"2\" class=\"group\">"+pd.group+"</td></tr>";
- for(var j in pd.params){
- var ps = pd.params[j];
- html+="<tr><td class=\"param\"><span>"+ps.k+"</span>: </td><td><input autocomplete=\"off\" type=\"text\" value='"+ps.v+"'/></td></tr>";
- }
- html+="</li></table>";
- }
- html+= "</ul>";
- $("#itemeEditForm .params td").eq(1).html(html);
- }
- });
- TAOTAO.init({
- "pics" : data.image,
- "cid" : data.cid,
- fun:function(node){
- TAOTAO.changeItemParam(node, "itemeEditForm");
- }
- });
- }
- }).window("open");
- }
- },{
- text:'删除',
- iconCls:'icon-cancel',
- handler:function(){
- var ids = getSelectionsIds();
- if(ids.length == 0){
- $.messager.alert('提示','未选中商品!');
- return ;
- }
- $.messager.confirm('确认','确定删除ID为 '+ids+' 的商品吗?',function(r){
- if (r){
- var params = {"ids":ids};
- $.post("/rest/item/delete",params, function(data){
- if(data.status == 200){
- $.messager.alert('提示','删除商品成功!',undefined,function(){
- $("#itemList").datagrid("reload");
- });
- }
- });
- }
- });
- }
- },'-',{
- text:'下架',
- iconCls:'icon-remove',
- handler:function(){
- var ids = getSelectionsIds();
- if(ids.length == 0){
- $.messager.alert('提示','未选中商品!');
- return ;
- }
- $.messager.confirm('确认','确定下架ID为 '+ids+' 的商品吗?',function(r){
- if (r){
- var params = {"ids":ids};
- $.post("/rest/item/instock",params, function(data){
- if(data.status == 200){
- $.messager.alert('提示','下架商品成功!',undefined,function(){
- $("#itemList").datagrid("reload");
- });
- }
- });
- }
- });
- }
- },{
- text:'上架',
- iconCls:'icon-remove',
- handler:function(){
- var ids = getSelectionsIds();
- if(ids.length == 0){
- $.messager.alert('提示','未选中商品!');
- return ;
- }
- $.messager.confirm('确认','确定上架ID为 '+ids+' 的商品吗?',function(r){
- if (r){
- var params = {"ids":ids};
- $.post("/rest/item/reshelf",params, function(data){
- if(data.status == 200){
- $.messager.alert('提示','上架商品成功!',undefined,function(){
- $("#itemList").datagrid("reload");
- });
- }
- });
- }
- });
- }
- }];
- <thead>
- <tr>
- <th data-options="field:'ck',checkbox:true"></th>
- <th data-options="field:'id',width:60">商品ID</th>
- <th data-options="field:'title',width:200">商品标题</th>
- <th data-options="field:'cid',width:100">叶子类目</th>
- <th data-options="field:'sellPoint',width:100">卖点</th>
- <th data-options="field:'price',width:70,align:'right',formatter:TAOTAO.formatPrice">价格</th>
- <th data-options="field:'num',width:70,align:'right'">库存数量</th>
- <th data-options="field:'barcode',width:100">条形码</th>
- <th data-options="field:'status',width:60,align:'center',formatter:TAOTAO.formatItemStatus">状态</th>
- <th data-options="field:'created',width:130,align:'center',formatter:TAOTAO.formatDateTime">创建日期</th>
- <th data-options="field:'updated',width:130,align:'center',formatter:TAOTAO.formatDateTime">更新日期</th>
- </tr>
- </thead>
当我们从地址访问item-list时会报一个400错误,代表请求有问题,我们应该让请求转向item-list.jsp页面并且从数据库查询出数据,转换成json返回给客户端。让datagrid进行局部刷新。由于要分页,因此还传递了page和rows,分别表示当前页码(从1开始)和每页显示多少条记录。
那么,我们服务端响应的数据格式应该是什么样子的呢?EasyUI中datagrid控件要求的数据格式为:{total:"2",rows:[{"id":"1","name":"张三"},{"id":"2","name":"李四"}]}
返回过滤数据显示,该函数带一个参数"data"用来指向源数据(即:获取的数据源,比如json对象)。您可以改变源数据的标准数据格式,但是这个函数必须返回包含total和rows属性的标准数据对象。还要就是rows当中的字段名称必须和<table>表格里面定义的字段名称一致。我们一般采用用一个pojo类来返回前台所需要的数据,而我们rows当中的json数据则一般是由Java对象转换而来,多个对象的话就把一个集合转换为json串。
下面我们来新建一个pojo类EasyUIDataGridResult,由于这个pojo类有可能被多个服务所调用,因此我们把它放到taotao-common工程下,我们新建一个com.taotao.common.pojo包并在该包下新建一个EasyUIDataGridResult类,该类的代码如下图右侧所示,必须注意的是,属性的名称必须叫toal和rows,另外由于rows集合可能是各种不同的对象,因此我们便不再使用泛型,直接用List表示类型,它可以装任意类型的对象。由于pojo要在服务端和客户端进行传输,因此一定要实现序列化接口。
下面我们再说下分页,我们使用逆向工程生成的mapper文件(比如TbItemMapper.xml)中是没有分页功能的,那么我们怎么实现分页功能?
第一,大家可以自己手工修改mapper文件,使其可以分页,这种方式比较麻烦,因为当生成的mapper文件很多时,一个一个的改又是一个耗时的工程,而且还容易出错。
第二,就是不修改mapper文件,使用mybatis提供的一个分页插件pagehelper来实现分页,这种方法当然是我们的首选了。
PageHelper分页插件目前支持:Oracle、Mysql、MariaDB(其实只是Mysql的一个分支而已)、SQLite、Hsqldb、PostgreSQL六种数据库。
我们使用的PageHelper分页插件版本是3.4.2-fix,这个版本从中央仓库是下载不到的,这是经过高人修改过的版本,mybatis官方提供的分页插件在无条件分页查询的时候没问题,但是当有条件进行分页查询的时候会抛异常,因此高人做了修改,命名为了3.4.2-fix版本。我们目前只需要会使用就可以了,等我们学会熟练使用了,能够很好的阅读源码了,便可以看看哪些做了修改。
我们怎么使用这个修改后的分页插件呢?方法有两种,第一种便是从:http://download.csdn.net/detail/u012453843/9797063这个地址下载pagehelper修改过后的maven工程,解压后,导入到eclipse。如下图所示,如果我们使用的jdk版本是1.8的话,我们需要修改pom.xml文件中build定义的jdk的版本为1.8。
我们对该工程进行maven打包,打包到本地maven仓库中,方法是在pagehelper工程上右键-------->Run As--------->Maven Install。
第二种使用方法便是到http://download.csdn.net/detail/u012453843/9786164这个地址进行下载。下载完之后,我们解压它并把它放在我们本地maven仓库的相应位置下,如下图所示。这样便可以使用修改后的pagehelper插件了。
接下来我们开始在mybatis的配置文件中配置分页插件,如下图所示,这段配置的作用是根据不同的数据库采用不同的分页方法。
为方便大家复制,把代码放置如下:
- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE configuration
- PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
- "http://mybatis.org/dtd/mybatis-3-config.dtd">
- <configuration>
- <!-- 配置分页插件 -->
- <plugins>
- <plugin interceptor="com.github.pagehelper.PageHelper">
- <property name="dialect" value="mysql"/>
- </plugin>
- </plugins>
- </configuration>
为方便大家复制代码,这里把测试类的代码粘贴出来,如下所示。
- package com.taotao.pagehelper;
- import java.util.List;
- import org.junit.Test;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- import com.github.pagehelper.PageHelper;
- import com.github.pagehelper.PageInfo;
- import com.taotao.mapper.TbItemMapper;
- import com.taotao.pojo.TbItem;
- import com.taotao.pojo.TbItemExample;
- import com.taotao.pojo.TbItemExample.Criteria;
- public class TestPageHelper {
- @Test
- public void testPageHelper() throws Exception{
- //1.在Mybatis配置文件中配置分页插件,这一步我刚才已经做过了。
- //2.在执行查询之前配置分页条件,使用pagehelper静态方法
- PageHelper.startPage(1, 10);
- //3.执行查询
- ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-dao.xml");
- TbItemMapper tbItemMapper = applicationContext.getBean(TbItemMapper.class);
- TbItemExample tbItemExample = new TbItemExample();
- //如果要使用条件查询,则先创建Criteria,然后使用它来拼接查询条件,这里我们不按条件查询,我们查询全部。
- // Criteria criteria = tbItemExample.createCriteria();
- // criteria.andIdEqualTo(1L);
- //pagehelper的Page类是继承ArrayList的,Page里面有分页结果
- List<TbItem> list = tbItemMapper.selectByExample(tbItemExample);
- //4.取分页信息,使用PageInfo对象获取,我们使用PageInfo的目的便是把List强转成Page对象,从而得到分页结果
- PageInfo<TbItem> pageInfo = new PageInfo<>(list);
- System.out.println("总记录数:"+pageInfo.getTotal());
- System.out.println("总页数:"+pageInfo.getPages());
- System.out.println("返回的记录数:"+pageInfo.getSize());
- }
- }
- /*
- The MIT License (MIT)
- Copyright (c) 2014 abel533@gmail.com
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- */
- package com.github.pagehelper;
- import org.apache.ibatis.session.RowBounds;
- import java.util.ArrayList;
- import java.util.List;
- /**
- * Mybatis - 分页对象
- *
- * @author liuzh/abel533/isea533
- * @version 3.3.0
- * 项目地址 : http://git.oschina.net/free/Mybatis_PageHelper
- */
- public class Page<E> extends ArrayList<E> {
- private static final long serialVersionUID = 1L;
- /**不进行count查询*/
- private static final int NO_SQL_COUNT = -1;
- /**进行count查询*/
- private static final int SQL_COUNT = 0;
- /**页码,从1开始*/
- private int pageNum;
- /**页面大小*/
- private int pageSize;
- /**起始行*/
- private int startRow;
- /**末行*/
- private int endRow;
- /**总数*/
- private long total;
- /**总页数*/
- private int pages;
- /**分页合理化*/
- private boolean reasonable;
- public Page(){
- super();
- }
- public Page(int pageNum, int pageSize) {
- this(pageNum, pageSize, SQL_COUNT);
- }
- public Page(int pageNum, int pageSize, boolean count) {
- this(pageNum, pageSize, count ? Page.SQL_COUNT : Page.NO_SQL_COUNT);
- }
- public Page(int pageNum, int pageSize, int total) {
- super(pageSize > -1 ? pageSize : 0);
- this.pageNum = pageNum;
- this.pageSize = pageSize;
- this.total = total;
- calculateStartAndEndRow();
- }
- public Page(RowBounds rowBounds, boolean count) {
- this(rowBounds, count ? Page.SQL_COUNT : Page.NO_SQL_COUNT);
- }
- public Page(RowBounds rowBounds, int total) {
- super(rowBounds.getLimit() > -1 ? rowBounds.getLimit() : 0);
- this.pageSize = rowBounds.getLimit();
- this.startRow = rowBounds.getOffset();
- //RowBounds方式默认不求count总数,如果想求count,可以修改这里为SQL_COUNT
- this.total = total;
- this.endRow = this.startRow + this.pageSize;
- }
- public List<E> getResult() {
- return this;
- }
- public int getPages() {
- return pages;
- }
- public int getEndRow() {
- return endRow;
- }
- public int getPageNum() {
- return pageNum;
- }
- public void setPageNum(int pageNum) {
- //分页合理化,针对不合理的页码自动处理
- this.pageNum = (reasonable && pageNum <= 0) ? 1 : pageNum;
- }
- public int getPageSize() {
- return pageSize;
- }
- public void setPageSize(int pageSize) {
- this.pageSize = pageSize;
- }
- public int getStartRow() {
- return startRow;
- }
- public long getTotal() {
- return total;
- }
- public void setTotal(long total) {
- this.total = total;
- if (pageSize > 0) {
- pages = (int) (total / pageSize + ((total % pageSize == 0) ? 0 : 1));
- } else {
- pages = 0;
- }
- //分页合理化,针对不合理的页码自动处理
- if (reasonable && pageNum > pages) {
- pageNum = pages;
- calculateStartAndEndRow();
- }
- }
- public void setReasonable(boolean reasonable) {
- this.reasonable = reasonable;
- //分页合理化,针对不合理的页码自动处理
- if (this.reasonable && this.pageNum <= 0) {
- this.pageNum = 1;
- calculateStartAndEndRow();
- }
- }
- /**
- * 计算起止行号
- */
- private void calculateStartAndEndRow() {
- this.startRow = this.pageNum > 0 ? (this.pageNum - 1) * this.pageSize : 0;
- this.endRow = this.startRow + this.pageSize * (this.pageNum > 0 ? 1 : 0);
- }
- public boolean isCount() {
- return this.total > NO_SQL_COUNT;
- }
- @Override
- public String toString() {
- return "Page{" +
- "pageNum=" + pageNum +
- ", pageSize=" + pageSize +
- ", startRow=" + startRow +
- ", endRow=" + endRow +
- ", total=" + total +
- ", pages=" + pages +
- '}';
- }
- }
接下来我们需要在taotao-manager-service的实现类中实现该接口,如下图所示。
下面我们来写Controller代码,如下图所示,我们在ItemController当中添加一个获取商品列表的接口,返回值是EasyUIDataGridResult类。
写完了代码,由于我们在taotao-common当中添加了一个pojo,在taotao-manager-interface添加一个接口,因此这两个工程都需要打下包到本地仓库。打包的方法如下图所示(taotao-manager-interface打包方式与此相同)。
下面我们重新启动taotao-manager和taotao-manager-web工程,然后访问http://localhost:8081/并点击查询商品,可以看到如下图所示界面,说明我们成功进行分页查询了。
最后来说下debug,我们需要关联源码,否则debug将难以进行,关联方法如下图所示。
弹出的对话框中,选择taotao-manager工程,点击"Source"页签,去除掉"Default"。
由于taotao-manager工程是个聚合工程,它是由四个子工程组成的,因此我们把它们加进来。添加之后记得点击下图的"Apply"按钮。
添加之后如下图所示,同理,我们把taotao-manager-web工程的Default也去掉并把taotao-manager-web工程添加进来(记得点击"Apply"按钮)。debug启动可以点击下图的"Debug"按钮。