在弄完ztree后,这里没有急着去做博客列表的相关功能,因为涉及要说的插件太多。这里单独的说明下如何集成datatables,并详细解释在当前系统中用到的属性。这里使用一个非常简单的表作为例子(公告列表)。
1、数据库表的设计
这是一个完整的查询列表页面,使用datatables的时候,只要先在页面设计好表头等就可以了。
datatables真心很好用,作为jquery的页面元组,个人觉得还是很专业的。这里说点题外话,自己在刚入行(javaweb)的那一两年,对css+div有种爱恨交加的感受,爱是它们的语法很简单,恨的是自己实在没有UI设计的文艺细胞。面对一个小小的后台项目,想在已有的模板上面加点自己的东西,都举步维艰。那时候就有一个想法,有没有一套完整的js系统,有自己的css和图片以及icon等,按照全局的样式,封装成一个一个的表单组件(table、from等),使用的时候,只要拿对应的组件就可以了,并不需要考虑全局的布局和样式问题,自己安心的做接口就行。所以就主动的搜寻到了extjs、easyui等。无奈,要么收费,要么试用的组件不全,要么就是像extjs那样太大了......,最后就一直在jquery的组件中东拼西凑着,虽然没有所谓的统一,但还是用得舒心,也许只是习惯了吧。随着移动互联网的兴起,mvvm模式的成熟,尤其是web前端开发的独立,web工程师终于可以安静的去关心框架和数据库了。扯淡到此为止,这里还是上代码,具体解释在注释中:
写到这里,大家可能会有疑问,分页、总记录数等这些页面信息怎么体现在页面,或者说后台查询出来的分页数据,怎么给datatables?这个呢,就要从datatables的参数说起了。我们在浏览器后台打印下它的请求参数,如图:
上图中的数据就是:
后台怎么获取datatables的翻页数据解决了。那么datatables需要的当前页,总数量等这些页面信息怎么从后台获取呢?它又怎么和数据内容区分呢?这个我们可以到datatables的学习论坛中去查找就是了,这里不卖官子,直接告诉大家:
5、封装一个DataTablesUtil.php
如果只有一个系统只有这个页面用到datatables,那么我想谁都不会想着要封装一个工具类,这就是实际开发与普通练习的区别。我们通过第4小节的描述,有很清楚的查询思路。但是当大家仔细观察aoData参数结构的时候,我们可以发现,还可以抽取出一个排序的思路。这里还是说下细节:"mDataProp_xx"封装了每列的名称;"bSortable_xx"封装了当前xx列是否需要排序;"sSortDir_xx"打家看到它的值就知道是存放asc和desc这两个排序方式的;"iSortingCols"就是当前查询具体是哪一列在排序(和数组下标一样从0开始)。所以,我们可以做一个大胆的逻辑,根据这些参数获取到客户在使用的时候点了那列,升序还是降序。下面先上DataTablesUtil.php的完整代码:
6、后台代码查询
有了封装好的DataTablesUtil.php,那么我们的功能代码就清爽多了。
1、数据库表的设计
CREATE TABLE `mq_notice` (
`id` int(10) NOT NULL AUTO_INCREMENT COMMENT '逻辑主键',
`title` varchar(100) CHARACTER SET utf8 NOT NULL COMMENT '标题',
`content` text CHARACTER SET utf8 COMMENT '内容',
`delete_flag` char(1) NOT NULL DEFAULT '0' COMMENT '删除标志(0:未删除;1:已删除)',
`create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
2、页面结构
这是一个完整的查询列表页面,使用datatables的时候,只要先在页面设计好表头等就可以了。
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
{include file="../application/admin/view/header.html" /}
{include file="../application/admin/view/footer.html" /}
<title>公告管理</title>
</head>
<body>
<nav class="breadcrumb"><i class="Hui-iconfont"></i> 首页 <span class="c-gray en">></span> 综合管理 <span class="c-gray en">></span> 公告管理 <a class="btn btn-success radius r" style="line-height:1.6em;margin-top:3px" href="javascript:location.replace(location.href);" title="刷新" ><i class="Hui-iconfont"></i></a></nav>
<div class="page-container">
<div class="text-c">
标题:<input type="text" class="input-text" style="width:250px" placeholder="输入标题" id="title" name="" />
精确查找<input id="accurateFlg" name="accurateFlg" type="checkBox" value="1" />
<button type="button" class="btn btn-success radius" id="search" name="search"><i class="Hui-iconfont"></i> 搜公告</button>
</div>
<div class="cl pd-5 bg-1 bk-gray mt-20">
<span class="l">
<a href="javascript:;" οnclick="delete_batch()" class="btn btn-danger radius"><i class="Hui-iconfont"></i> 批量删除</a>
<a href="javascript:;" οnclick="notice_add('添加公告','__URL__/notice/toNoticeAdd','','')" class="btn btn-primary radius"><i class="Hui-iconfont"></i> 添加公告</a>
</span>
</div>
<div class="mt-20">
<table class="table table-border table-bordered table-hover table-bg table-sort">
<thead>
<tr class="text-c">
<th width="25"><input type="checkbox" id="chkId" name="chkIdAll" value=""></th>
<th width="40">ID</th>
<th width="200">公告标题</th>
<th width="200">发布时间</th>
<th width="100">操作</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</body>
3、datatables的详细说明
datatables真心很好用,作为jquery的页面元组,个人觉得还是很专业的。这里说点题外话,自己在刚入行(javaweb)的那一两年,对css+div有种爱恨交加的感受,爱是它们的语法很简单,恨的是自己实在没有UI设计的文艺细胞。面对一个小小的后台项目,想在已有的模板上面加点自己的东西,都举步维艰。那时候就有一个想法,有没有一套完整的js系统,有自己的css和图片以及icon等,按照全局的样式,封装成一个一个的表单组件(table、from等),使用的时候,只要拿对应的组件就可以了,并不需要考虑全局的布局和样式问题,自己安心的做接口就行。所以就主动的搜寻到了extjs、easyui等。无奈,要么收费,要么试用的组件不全,要么就是像extjs那样太大了......,最后就一直在jquery的组件中东拼西凑着,虽然没有所谓的统一,但还是用得舒心,也许只是习惯了吧。随着移动互联网的兴起,mvvm模式的成熟,尤其是web前端开发的独立,web工程师终于可以安静的去关心框架和数据库了。扯淡到此为止,这里还是上代码,具体解释在注释中:
<script type="text/javascript" src="__ADMIN__/lib/datatables/1.10.0/jquery.dataTables.min.js"></script>
<script type="text/javascript">
var table;
$(function(){
table = $('.table-sort').dataTable({
"bStateSave": false,//状态保存
"bServerSide" : true, //是否启动服务器端数据导入
"bFilter": false, //去掉默认自带的搜索框
"aLengthMenu" : [20, 40, 60],//每页显示行数
"sAjaxSource" : "__URL__/notice/noticeDatas",//请求后台的url
/**
* fnServerParams函数:
* 顾名思义,就是请求后台的参数设置
* 大家可以在开发的时候可以用console.log(JSON.stringify(aoData))
* 查看下参数结构,我们可以将自己页面的查询条件放入aoData中,如例子所示
*/
"fnServerParams" : function(aoData) {
aoData.push({
"name" : "criteria",
"value" : {
"title" : $("#title").val(),
"accurateFlg" : $("#accurateFlg").prop("checked") ? "1" : ""
}
});
},
/**
* aoColumns 对应于表头和属性赋值等
*
* "mData" : 对应的属性名
* "bSortable" : 是否需要排序,默认是true(就是每列表头上的的上、下两个箭头)
* "sTitle" : 列名
* "sDefaultContent" : 默认值
* "sClass" : 相当于css中的text-align,有text-l、text-c、text-r(左中右)这三个选择
*/
"aoColumns" : [{
"mData" : "id"
},{
"mData" : "id",
"sTitle" : "ID",
"sWidth": "40px",
"sDefaultContent" : "",
"sClass" : "text-c"
},{
"mData" : "title",
"sTitle" : "标题",
"sDefaultContent" : "",
"sClass" : "text-c"
},{
"mData" : "create_time",
"sTitle" : "发布时间",
"sDefaultContent" : "",
"sClass" : "text-c"
},{
"mData" : "",
"sTitle" : "操作",
"sDefaultContent" : "",
"sClass" : "text-c"
}],
//aoColumnDefs用来设置列的属性,可以指定任何列,我们这里用来设置第一列[0]的复选框
"aoColumnDefs":[{
"sClass": "text-c",
"mRender" : function(data,type,full) {
return '<input type="checkbox" id="chkId_' + data
+ '" name="chkId" value="' + data + '" style="border:1px solid red"/>';
},
"bSortable": false,
"aTargets": [0]
}],
//columnDefs和oColumnDefs差不多,它们的区别问百度吧
"columnDefs": [{ "orderable": false, "targets": 1 }],
/**
* fnServerData函数
* 这个函数很重要,我们可以通过里面的参数类重构页面与服务器的交互方式
* 总之,里面的ajax由我们配置,大家可以打印参数和回调测试测试
*/
"fnServerData" : function(sSource, aoData, fnCallback) {
$.ajax({
type : 'POST',
url : sSource,
async : false,
data : {
"aoData" : JSON.stringify(aoData)
},
success : fnCallback,
error : function(XMLHttpRequest, textStatus, errorThrown) {
alert(XMLHttpRequest.status + "," + textStatus);
}
});
},
/**
* 大家试想一下这样一个业务场景,我们数据库中存放了性别这一列,数据库中的值
* 1代表男;0代表女;假设我们没有在php中处理,那么页面显示的时候就会显示1或者是0
* 这显然是不友好的。
* fnRowCallback就可以直接在页面完美的解决这场景
* 说白了,就是获取完行数据以后,我们在页面显示之前,对数据预处理下
* 例子中只是增加操作列的查看、修改和删除图标与事件
*/
"fnRowCallback" : function(nRow, aData, iDisplayIndex) {
var cstr = "<a title='查看' href='javascript:void(0);' οnclick='notice_edit("+aData.id+",0)' class='ml-5' style='text-decoration:none'><i class='Hui-iconfont'></i></a>" +
"<a title='编辑' href='javascript:void(0);' οnclick='notice_edit("+aData.id+",1)' class='ml-5' style='text-decoration:none'><i class='Hui-iconfont'></i></a>" +
"<a title='删除' href='javascript:void(0);' οnclick='notice_del(this,"+ aData.id +")' class='ml-5' style='text-decoration:none'><i class='Hui-iconfont'></i></a>";
$('td:eq(4)', nRow).html(cstr);
return nRow;
}
});
//注册查询按钮事件
$("#search").bind("click", function () {
//这里可以做一些js的格式验证
table.fnDraw();//每次查询,调用fnDraw重绘
});
});
</script>
4、datatables分页说明
写到这里,大家可能会有疑问,分页、总记录数等这些页面信息怎么体现在页面,或者说后台查询出来的分页数据,怎么给datatables?这个呢,就要从datatables的参数说起了。我们在浏览器后台打印下它的请求参数,如图:
上图中的数据就是:
[
{"name":"sEcho","value":1},
{"name":"iColumns","value":5},
{"name":"sColumns","value":",,,,"},
{"name":"iDisplayStart","value":0},
{"name":"iDisplayLength","value":20},
{"name":"mDataProp_0","value":"id"},
{"name":"bSortable_0","value":false},
{"name":"mDataProp_1","value":"id"},
{"name":"bSortable_1","value":true},
{"name":"mDataProp_2","value":"title"},
{"name":"bSortable_2","value":true},
{"name":"mDataProp_3","value":"create_time"},
{"name":"bSortable_3","value":true},
{"name":"mDataProp_4","value":""},
{"name":"bSortable_4","value":true},
{"name":"iSortCol_0","value":0},
{"name":"sSortDir_0","value":"asc"},
{"name":"iSortingCols","value":1},
{"name":"criteria","value":{"title":"","accurateFlg":""}}
]
我们可以很清楚的看到iDisplayStart、iDisplayLength这两个属性,里面的值就是mysql分页时候limit 0,20的值。所以后台可以获取aoData中的这两个值来进行翻页处理。
后台怎么获取datatables的翻页数据解决了。那么datatables需要的当前页,总数量等这些页面信息怎么从后台获取呢?它又怎么和数据内容区分呢?这个我们可以到datatables的学习论坛中去查找就是了,这里不卖官子,直接告诉大家:
{
"sEcho":1,//这个参数有意思,下小节封装DataTablesUtil.php会详细说明
"iTotalRecords":100,//实际的总数量
"iTotalDisplayRecords":100,//过滤之后的数量,我们不用datatables的假翻页的话,它和iTotalRecords等价
"aaData":'.....',//当前页的所有结果集
...
}
值得说明的是,不同的datatables的版本,参数名是不一样的,我这边用的是H-ui自带的1.10.0,其他的版本,可以上官网或者百度询问,这里不一一列举。到了这一步,我们书写后台的查询语句就有思路了。
5、封装一个DataTablesUtil.php
如果只有一个系统只有这个页面用到datatables,那么我想谁都不会想着要封装一个工具类,这就是实际开发与普通练习的区别。我们通过第4小节的描述,有很清楚的查询思路。但是当大家仔细观察aoData参数结构的时候,我们可以发现,还可以抽取出一个排序的思路。这里还是说下细节:"mDataProp_xx"封装了每列的名称;"bSortable_xx"封装了当前xx列是否需要排序;"sSortDir_xx"打家看到它的值就知道是存放asc和desc这两个排序方式的;"iSortingCols"就是当前查询具体是哪一列在排序(和数组下标一样从0开始)。所以,我们可以做一个大胆的逻辑,根据这些参数获取到客户在使用的时候点了那列,升序还是降序。下面先上DataTablesUtil.php的完整代码:
<?php
namespace app\common\utils;
/**
*
* @author tim
*
*/
class DataTablesUtil
{
//datatable参数下标常量定义
const ID_DISPLAY_START = 'iDisplayStart'; //开始行
const ID_DISPLAY_LENGTH = 'iDisplayLength'; //每页显示行数
const S_ECHO = 'sEcho'; //每页的唯一标识,必须在结果集中传回
const ORDER_PREFIX = 'mDataProp_'; //列名前缀
const ISORTCOL_0 = 'iSortCol_0'; //排列下标
const SSORTDIR_0 = 'sSortDir_0'; //排列方式
//自定义查询条件key
const CRITERIA = 'criteria';//和页面中aoData中的查询条件key要一致
//封装查询条件下标常量定义
const ORDER = 'order';
const LIMIT = 'limit';
/**
* 根据$aoData参数,封装查询条件(筛选_criteria、排序_order、分页_limit)
* */
public static function getQueryPageProperty($aoData){
//json参数转化为数组
$arr = json_decode($aoData,true);
//将数据整理为key=>value的关联模式数组
$dataArr = array();
for ($i=0;$i<count($arr);$i++){
$dataArr[$arr[$i]['name']] = $arr[$i]['value'];
}
//设置返回的数据
$iSortCol_0 = $dataArr[DataTablesUtil::ISORTCOL_0];
$sSortDir_0 = $dataArr[DataTablesUtil::SSORTDIR_0];
$orderClumn = $dataArr[DataTablesUtil::ORDER_PREFIX.$iSortCol_0];
$pageArr[DataTablesUtil::S_ECHO] = $dataArr[DataTablesUtil::S_ECHO];
if(empty($orderClumn)){
$pageArr[DataTablesUtil::ORDER] = '';
}else{
$pageArr[DataTablesUtil::ORDER] = $orderClumn.' '.$sSortDir_0.' ';
}
$pageArr[DataTablesUtil::LIMIT] = $dataArr[DataTablesUtil::ID_DISPLAY_START].','.$dataArr[DataTablesUtil::ID_DISPLAY_LENGTH];
$pageArr[DataTablesUtil::CRITERIA] = $dataArr[DataTablesUtil::CRITERIA];
return $pageArr;
}
/**
* 返回dataTable所需要的json格式数据
* @param $sEcho
* @param $count
* @param $data 查询结果集(数组)
* */
public static function getJsonPage($sEcho,$count,array $data){
$json = '{"sEcho":'.$sEcho.',"iTotalRecords":'.$count
.',"iTotalDisplayRecords":'.$count
.',"aaData":'.json_encode($data).'}';
return $json;
}
}
可能是用惯了java,这里封装也完全是按java的开发思维去封装的,在常量上面有些冗余的感觉(因为自己是使用eclipse开发的,代码追踪起来很方便。对于用文本编辑器开发的小伙伴看起来就有些麻烦)。这里说明下,其中getQueryPageProperty函数的作用就是将aoData参数封装成一个类似于以下格式的数组:
{
'sEcho'=>1,
'order'=>'id asc',
'limit'=>'10,20',
'criteria'=>{'title':'xxx','accurateFlg':1}//就是你在fnServerParams中为aoData参数push的查询条件
}
而getJsonPage函数就不多解释了,返回一个datatables的结果集(包含翻页)
6、后台代码查询
有了封装好的DataTablesUtil.php,那么我们的功能代码就清爽多了。
/**
* 异步查询数据,返回json格式
* */
public function noticeDatas(Request $request){
$aoData = $request->param('aoData');
$queryArr = DataTablesUtil::getQueryPageProperty($aoData);
$criteria = $queryArr[DataTablesUtil::CRITERIA];
//查询条件
$condition = array();
$condition['delete_flag'] = '0';
if(!empty($criteria['title'])){
if(!empty($criteria['accurateFlg']) && $criteria['accurateFlg'] == 1){
$condition['title'] = $criteria['title'];
}else{
$condition['title'] = ['like','%'.$criteria['title'].'%'];
}
}
//查询当前条件的总数
$count = Notice::where($condition)->count();
//查询当前条件的结果集,并分页和排序
$list = Notice::where($condition)
->order($queryArr[DataTablesUtil::ORDER])
->limit($queryArr[DataTablesUtil::LIMIT])
->select();
$json = DataTablesUtil::getJsonPage($queryArr[DataTablesUtil::S_ECHO],$count,$list);
return json_decode($json);
}
最后上一张图,看看效果: