http://blog.csdn.net/yerenyuan_pku/article/details/72774381
上文我们实现了展示后台页面的功能,而本文我们实现的主要功能是展示商品列表,大家要是实现了该功能,点击查询商品
超链接,就能看到如下所示结果:
下面我就来教大家如何实现展示商品列表这个功能。
我们知道,EasyUI的最大特点便是局部刷新,所有展示都是分模块展示的,不像我们一般页面采用全部刷新。查询商品是index.jsp中的一个模块展示而已,我们下面来看下index.jsp页面代码,如下图所示。可以看到,当我们点击查询商品这个链接的时候实际上是去访问item-list这么一个逻辑视图。
然后我们再来看下item-list.jsp页面代码,如下图所示,我们可以看到这个页面代码只是有个table和一个div,并没有文件头和尾,因此这不是一个完整的页面,而只是一个片段而已(它是index.jsp的一个代码片段而已,而EasyUI的特点便是对这样的代码块进行刷新,而不会刷新其它模块)。
从上面可看出item_list.jsp页面并不是一个完整的html,如果是一个完整的html,它应该是以<html>
开头,以</html>
结尾,除了index.jsp是一个完整的html之外,你会发现其他的jsp页面都是一个html的片段,因为EasyUI其实就是一个单页面功能,EasyUI这里面所有的动作都是在一个html中完成的,它加载的这些jsp页面,都只是加载一个片段,然后把这个片段再添加到当前页面中。这就是我们通常所说的页面优化,大家是不是又学到一个技能点?
下面我们再具体看下item-list.jsp这个页面的代码,首先看下<table>
头信息,如下所示,可以看到,table的class设置成了”easyui-datagrid”,设置了它之后便默认把表格渲染成我们在最上方那张图所看到的表格样式。虽然不太好看,但是对于后台来说能用即可,美观并不是多么重要。
<table class="easyui-datagrid" id="itemList" title="商品列表"
data-options="singleSelect:false,collapsible:true,pagination:true,url:'/item/list',method:'get',pageSize:30,toolbar:toolbar">
- data-options中
singleSelect:false
表示可以多选,如果想要实现只能选择一条记录的效果,可以把singleSelect的值设置为true。 collapsible:true
所代表的意思是是否显示折叠按钮,如下图最右边红色框圈住的按钮。
如果把collapsible的值设置为false,那么这个按钮将不再显示。pagination:true
代表的意思是要显示分页,如果不想分页就把该值设置为false。url:'/item/list'
的意思是我们初始化商品列表请求的url。method:'get'
表示请求方式是GET。pageSize:30
表示每页显示30条记录,我们从有分页信息的图中可以看到”30”的信息,就是这里设置的值。-
toolbar:toolbar
指定工具栏,如上图的”新增”、”编辑”、”删除”、”下架”、”上架”这些菜单。toolbar是由js定义的,代码就在item-list.jsp页面下方的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"); }); } }); } }); } }];
看完了<table>
里面的属性,下面我们来看下表头的代码,其中<thead>
表示表头,<tr>
表示一行,<th>
表示一列。我们可以看到第一列是复选框,字段名称为”ck”,商品ID的字段名称是”id”,商品标题的字段名称为’title”等等。
<thead>
<tr>
<th data-options="field:'ck',checkbox:true"></th>
<th data-options="field:'id',60">商品ID</th>
<th data-options="field:'title',200">商品标题</th>
<th data-options="field:'cid',100">叶子类目</th>
<th data-options="field:'sellPoint',100">卖点</th>
<th data-options="field:'price',70,align:'right',formatter:TAOTAO.formatPrice">价格</th>
<th data-options="field:'num',70,align:'right'">库存数量</th>
<th data-options="field:'barcode',100">条形码</th>
<th data-options="field:'status',60,align:'center',formatter:TAOTAO.formatItemStatus">状态</th>
<th data-options="field:'created',130,align:'center',formatter:TAOTAO.formatDateTime">创建日期</th>
<th data-options="field:'updated',130,align:'center',formatter:TAOTAO.formatDateTime">更新日期</th>
</tr>
</thead>
- 1
我们再看看商品表各个字段,发现与我们的表头里面定义的字段完全一致,这样才能自动将从数据库查出来的数据赋值给相应的列进行显示。
现在我们试着思考这样一个问题,当我们在浏览器地址栏中输入http://localhost:8080/
url地址,回车就能看到首页——index.jsp,这时我们点击查询商品
超链接,势必会发送一个请求,我们应该让请求转向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要在服务端和客户端之间进行传输,因此一定要实现序列化接口。
上面分析完了之后,下面我来讲讲MyBatis分页插件——PageHelper。
MyBatis分页插件——PageHelper
在实现商品列表展示这个功能的过程中,我们肯定是需要进行商品分页查询的。很不幸的是,我们使用逆向工程生成的mapper文件(比如TbItemMapper.xml)中是是不支持分页处理的,那么我们怎么实现分页功能呢?
- 大家可以自己手工修改mapper文件,使其可以分页,这种方式比较麻烦,因为当生成的mapper文件很多时,一个一个的改又是一个耗时的工程,而且还容易出错。
- 就是不修改mapper文件,使用MyBatis提供的一个分页插件——PageHelper来实现分页,这种方法当然是我们的首选了。
如果你也在用MyBatis,建议尝试该分页插件,这个一定是最方便使用的分页插件。PageHelper分页插件目前支持:Oracle、MySQL、MariaDB(其实只是MySQL的一个分支而已)、SQLite、Hsqldb、PostgreSQL六种数据库。
我们使用的PageHelper分页插件版本是3.4.2-fix,这个版本从中央仓库是下载不到的,这是经过好事者修改过的版本,MyBatis官方提供的分页插件在无条件分页查询的时候没问题,但是当有条件进行分页查询的时候会抛异常(即官方提供的分页插件对逆向工程支持得并不好),因此好事者做了修改,命名为了3.4.2-fix版本,我们目前只需要会使用就可以了,等我们学会熟练使用了,能够很好的阅读源码了,便可以看看哪些做了修改。
我们怎么使用这个修改后的分页插件呢?导入如下修改过后的maven工程——pagehelper到Eclipse中。
由于我们使用的jdk版本是1.7,所以我们并不需要修改pom.xml文件中build定义的jdk版本。当然了,要是你使用得jdk版本更高,比如是jdk1.8,那你就要修改了。
接下来我们就要对该工程进行maven打包了,要打包到本地maven仓库中,如下:
接下来我们开始在mybatis的配置文件中配置分页插件,如下图所示,这段配置的作用是根据不同的数据库采用不同的分页方法。
为方便大家复制,把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>
<!-- com.github.pagehelper为PageHelper类所在包名 -->
<plugin interceptor="com.github.pagehelper.PageHelper">
<!-- 设置数据库类型 Oracle,Mysql,MariaDB,SQLite,Hsqldb,PostgreSQL六种数据库-->
<property name="dialect" value="mysql"/>
</plugin>
</plugins>
</configuration>
- 1
下面新建一个测试类来测试一下分页是否好使,如下图所示。发现是可以正常进行分页查询的。
为方便大家复制代码,这里把测试类的代码粘贴出来,如下所示。
public class TestPageHelper {
@Test
public void testPageHelper() {
// 初始化Spring容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-dao.xml");
// 从容器中获得mapper代理对象
TbItemMapper itemMapper = applicationContext.getBean(TbItemMapper.class);
// 执行查询
TbItemExample example = new TbItemExample();
// 查询之前进行分页处理
PageHelper.startPage(1, 30); // 注意,分页插件仅对最近的这一次查询有效,下一次查询就无效了。怎么实现的呢?使用了LocalThread。
List<TbItem> list = itemMapper.selectByExample(example);
// 取分页信息
System.out.println("结果集中的记录数:" + list.size());
PageInfo<TbItem> pageInfo = new PageInfo<>(list);
System.out.println("总记录数:" + pageInfo.getTotal());
System.out.println("总页数:" + pageInfo.getPages());
}
}
- 1
这里,我还没资格为大家讲解PageHelper的源码,如果同学有兴趣,可以自己尝试着去查阅源码,这就很考大家的能力了。下面我们来实现商品列表查询功能。
商品列表查询
首先我们需要在taotao-manager-interface的接口中添加一个方法,如下图所示。
接下来我们需要在taotao-manager-service的实现类中实现该接口,如下图所示。
下面我们来写Controller类的代码,如下图所示,我们在ItemController当中添加一个获取商品列表的方法,返回值是EasyUIDataGridResult类。
写完了代码,由于我们在taotao-common当中添加了一个pojo,在taotao-manager-interface添加一个接口,因此这两个工程都需要打包到本地仓库。
最后,我们要启动工程了,记住一个原则,即要先启动服务端,再启动客户端。这是为什么呢?因为你服务端没启动,客户端去注册中心查找服务,是不能找到的,找不到服务就会抛异常。所以,我们应该先启动taotao-manager工程,再启动taotao-manager-web工程,然后访问http://localhost:8081
并点击查询商品
,可以看到如下图所示界面,说明我们成功进行分页查询了。
这时我们会发现Eclipse控制台有如下警告:
这个警告我们不用管它,商品列表能分页查询就行。至于出现这个警告的原因就是我们客户端这边没有服务端的类——com.github.pagehelper.Page。