本節,我將經過一個Demo,演示Datatables 和ASP.NET MVC的完美結合,能夠這麼說,若是這樣的界面都能作出來,後臺系統90%的界面功能均可以開發出來了。javascript
用jquery Datatables 來開發確實是件比較蛋疼的事情(和Jquery EasyUI、MiniUI、ExtJs相比),用其它的第三方UI框架來實現相同的功能真是很是很是的簡單,但是使用Datatables倒是那麼的吃力,至少我這麼以爲,多是由於我對這個控件使用得還不夠純熟。在官網,datatables默認使用的是bootstraps的樣式,這裏我已經把樣式重寫了一部分。css
看見公司原有的系統,一樣是使用ASP.NET MVC作的,在頁面隨便點擊個東東,整個界面就刷新了,刷得我本身都受不了,更別期望固定表頭啊什麼什麼的了,徹底不存在用戶體驗啊!因而我就本身寫了UI框架(也能夠說是組裝,可是我重寫了許多東西)。html
技術點:一、服務器端分頁。二、查詢(模糊查詢)三、界面操做刷新後依舊保留當前分頁 四、固定表頭、表尾 五、動態控制列的隱藏和顯示 六、全選、反選(數據行中複選框所有選中時,全選按鈕自動選中,我發現不少程序員這個功能一直沒作,但是說是bug麼?) 七、服務器排序(功能我已經開發出來了,可是這裏我沒有寫上去,這個網上我尚未看到實現的Demo) 八、特殊字段標紅顯示 九、滑動變色 十、單擊行選中變色 ....java
先看下效果:jquery
點擊圖片,摺疊或展開列程序員
新建Reconciliation控制器ajax
using Core.CostFlow; using Core.Filters; using Core.Reconciliation; using Data.Reconciliation; using ProjectBase.Utils; using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using ProjectBase.Utils.Entities; namespace Site.Controllers { public class ReconciliationController : Controller { //運單對帳 public ActionResult WayBill() { return View(); } [HttpPost] public JsonResult WayBillList(WayBillReconciliationFilter filter) { DataTablesRequest parm = new DataTablesRequest(this.Request); //處理對象 int pageIndex = parm.iDisplayStart / parm.iDisplayLength; filter.PageIndex = pageIndex; //頁索引 filter.PageSize = parm.iDisplayLength; //頁行數 var DataSource = WayBillReconciliation.GetByFilter(filter) as WRPageOfList<WayBillReconciliation>; int i = parm.iDisplayLength * pageIndex; List<WayBillReconciliation> queryData = DataSource.ToList(); var data = queryData.Select(u => new { Index = ++i, //行號 ID = u.ID, CusName = u.CusName, //客戶簡稱 PostingTime =u.PostingTime==null?string.Empty: u.PostingTime.Value.ToStringDate(),//收寄日期 ExpressNo = u.ExpressNo, //郵件號 BatchNO = u.LoadBillNum, //提單號 Weight = u.Weight==null ? 0m : u.Weight / 100, //重量 WayBillFee = u.WayBillFee, //郵資 ProcessingFee = u.ProcessingFee, //郵政郵件處理費 InComeWayBillFee = u.ExpressFee, //客戶運費 InComeOprateFee = u.OperateFee, //客戶操做費 WayBillMargins = u.WayBillProfit, //運費毛利 TotalMargins = u.ExpressFee + u.OperateFee + u.InComeOtherFee-(u.WayBillFee + u.ProcessingFee + u.CostOtherFee), //總毛利 Margin = (u.ExpressFee + u.OperateFee + u.InComeOtherFee == 0 ? 0m : (u.ExpressFee + u.OperateFee + u.InComeOtherFee - (u.WayBillFee + u.ProcessingFee + u.CostOtherFee)) / (u.ExpressFee + u.OperateFee + u.InComeOtherFee) * 100) + "%", //毛利率 毛利率=(總收入-總的支出的成本)/總收入*100% ReconcileDate=DateTime.Now.ToString("yyyy-MM"), //對帳日期 CostOtherFee = u.CostOtherFee, //成本 其餘費用 CostTotalFee = u.WayBillFee + u.ProcessingFee+u.CostOtherFee, //成本 總費用 CostStatus = u.CostStatus.ToChinese(), //成本 狀態 InComeOtherFee = u.InComeOtherFee, //收入 其餘費用 InComeTotalFee = u.ExpressFee + u.OperateFee+u.InComeOtherFee, //收入 總費用 InComeStatus = u.InComeStatus.ToChinese(), //收入 狀態 TotalStatus="" }); decimal totalProfit = 0m; //總毛利求和 //構形成Json的格式傳遞 var result = new { iTotalRecords = DataSource.Count, iTotalDisplayRecords = DataSource.RecordTotal, data = data, TotalWeight = DataSource.StatModelBy.TotalWeight/100, TotalWayBillFee = DataSource.StatModelBy.TotalWayBillFee, TotalProcessingFee = DataSource.StatModelBy.TotalProcessingFee, TotalExpressFee = DataSource.StatModelBy.TotalExpressFee, TotalOperateFee = DataSource.StatModelBy.TotalOperateFee, SumWayBillProfit = DataSource.StatModelBy.TotalWayBillProfit, SumTotalProfit = totalProfit }; return Json(result, JsonRequestBehavior.AllowGet); } /// <summary> /// 提單對帳 /// </summary> /// <returns></returns> public ActionResult LoadBill() { return View(); } public JsonResult LoadBillList() { return Json(null, JsonRequestBehavior.AllowGet); } } }
新建WayBill視圖json
@{ ViewBag.Title = "運費對帳"; } <style type="text/css"> .numberColor { color:red; } </style> <link href="~/libs/DataTables-1.10.6/media/css/jquery.dataTablesNew.css" rel="stylesheet" /> <script src="~/libs/DataTables-1.10.6/media/js/jquery.dataTables.min.js"></script> <script src="~/Scripts/DataTablesExt.js"></script> <script src="~/libs/My97DatePicker/WdatePicker.js"></script> <script type="text/javascript"> $(function () { var h = $(document).height() - 312; var table = $("#table_local").dataTable({ bProcessing: true, "scrollY": h, "scrollCollapse": "true", "dom": 'tr<"bottom"lip><"clear">', "bServerSide": true, //指定從服務器端獲取數據 sServerMethod: "POST", showRowNumber:true, sAjaxSource: "@Url.Action("WayBillList", "Reconciliation")", "initComplete": function (data, args) { //getTotal(args); var arr = new Array(7,8,9,12,13,14); controlColumnShow(table, arr,false); }, "fnServerParams": function (aoData) { //查詢條件 aoData.push( { "name": "CusShortName", "value": $("#CusShortName").val() }, { "name": "LoadBillNum", "value": $("#LoadBillNum").val() }, { "name": "ExpressNo", "value": $("#ExpressNo").val() }, { "name": "PostingTime", "value": $("#PostingTime").val() }, { "name": "PostingTimeTo", "value": $("#PostingTimeTo").val() } // ,{ "name": "PostingTimeTo", "value": $("#sltMargin").val() } ); }, //跟數組下標同樣,第一列從0開始,這裏表格初始化時,第四列默認降序 "order": [[ 2, "asc" ]], columns: [ { "data": "ID", orderable: false, "render": function (data, type, row, meta) { return " <input id='cbx" + data + "' type='checkbox' onclick='controlSelectAll(" + data + ")' class='cbx' value='" + data + "'/> " + row.Index; } }, { "data": "ReconcileDate",visible:false},//對帳日期 { "data": "CusName" }, //客戶名稱 { "data": "PostingTime"},//收寄日期 { "data": "ExpressNo", orderable: false }, //郵件號 { "data": "BatchNO"},//提單號 { "data": "Weight"},//重量 { "data": "WayBillFee"},//郵政郵資 { "data": "ProcessingFee" },//郵政郵件處理費 { "data": "CostOtherFee"},//其它費用 { "data": "CostTotalFee" },//總成本 { "data": "CostStatus", orderable: false },//狀態 { "data": "InComeWayBillFee" },//客戶運費 { "data": "InComeOprateFee"},//客戶操做費 { "data": "InComeOtherFee"},//其它費用 { "data": "InComeTotalFee" },//總收入 { "data": "InComeStatus", orderable: false },//狀態 { "data": "WayBillMargins", orderable: false, "render": function (data, type, row, meta) { //運費毛利 var css = ""; if (data < 0) { css=" class='numberColor'"; } var re = "<div"+css+">"+data+"</div>"; return re; } }, { "data": "TotalMargins", orderable: false, "render": function (data, type, row, meta) { //總毛利 var css = ""; if (data < 0) { css = " class='numberColor'"; } var re = "<div" + css + ">" + data + "</div>"; return re; } }, { "data": "Margin", orderable: false },//毛利率 { "data": "TotalStatus", orderable: false }, { "data": "ID", orderable: false, width: "80", "render": function (data, type, row, meta) { //操做 var re = "<div style='text-align:center'><a style='visibility:visible' onclick='openDetail(" + data + ")'>詳情</a> "; return re; } } ], paging: true,//分頁 ordering: true,//是否啓用排序 searching: true,//搜索 language: { "sProcessing": "處理中...", lengthMenu: '每頁顯示:<select class="form-control input-xsmall">' + '<option value="10">10</option>' + '<option value="20">20</option>' + '<option value="30">30</option>' + '<option value="50">50</option>' + '<option value="100">100</option>' + '<option value="150">150</option>' + '<option value="200">200</option>' + '<option value="250">250</option>',//左上角的分頁大小顯示。 search: '<span class="label label-success">搜索:</span>',//右上角的搜索文本,能夠寫html標籤 paginate: {//分頁的樣式內容。 previous: "上一頁", next: "下一頁", first: "", last: "" }, zeroRecords: "暫無記錄",//table tbody內容爲空時,tbody的內容。 //下面三者構成了整體的左下角的內容。 info: "總共 <span class='pagesStyle'>(_PAGES_) </span>頁,顯示 _START_ -- _END_ ,共<span class='recordsStyle'> (_TOTAL_)</span> 條",//左下角的信息顯示,大寫的詞爲關鍵字。初始_MAX_ 條 infoEmpty: "0條記錄",//篩選爲空時左下角的顯示。 infoFiltered: ""//篩選以後的左下角篩選提示, }, pagingType: "full_numbers"//分頁樣式的類型 }); //設置選中行樣式 $('#table_local tbody').on('click', 'tr', function () { if ($(this).hasClass('selected')) { $(this).removeClass('selected'); } else { table.$('tr.selected').removeClass('selected'); $(this).addClass('selected'); } }); //展開摺疊列 $("#imgIncome").click(function () { var url = $("#imgIncome").attr("src"); var arr = new Array(7, 8, 9); if (url == "/images/icon_9.png") { controlColumnShow(table, arr, true); $("#imgIncome").attr("src", "/images/icon_10.png"); } else { controlColumnShow(table, arr, false); $("#imgIncome").attr("src", "/images/icon_9.png"); } }); //收入展開摺疊 $("#imgCost").click(function () { var url = $("#imgCost").attr("src"); var arr = new Array(12, 13, 14); if (url == "/images/icon_9.png") { controlColumnShow(table, arr, true); $("#imgCost").attr("src", "/images/icon_10.png"); } else { controlColumnShow(table, arr, false); $("#imgCost").attr("src", "/images/icon_9.png"); } }); }); function reloadList() { var tables = $('#table_local').dataTable().api();//獲取DataTables的Api,詳見 http://www.datatables.net/reference/api/ tables.ajax.reload(function () { var json = tables.context[0].json; getTotal(json); }, false); } //統計 function getTotal(json) { if (json) { if (json.TotalWeight) { $("#spnTotalWeight").html(json.TotalWeight); $("#spnTotalWayBillFee").html(json.TotalWayBillFee); $("#spnTotalProcessingFee").html(json.TotalProcessingFee); $("#spnTotalExpressFee").html(json.TotalExpressFee); $("#spnTotalOperateFee").html(json.TotalOperateFee); $("#spnSumWayBillProfit").html(json.SumWayBillProfit); $("#spnSumTotalProfit").html(json.SumTotalProfit); } } } //控制指定定列的隱藏和顯示(table,列索引數組,隱藏or顯示:true,false) function controlColumnShow(table, arr,tag) { for (var i = 0; i < arr.length; i++) { table.fnSetColumnVis(arr[i],tag); } } </script> <div class="areabx clear"> @using (Html.BeginForm("List", null, FormMethod.Get, new { @clase = "form-inline", @role = "form" })) { <div class="areabx_header">@ViewBag.Title</div> <ul class="formod mgt10"> <li><span>客戶簡稱:</span>@Html.TextBox("CusShortName","",new { @class = "trade-time wid153" })</li> <li><span>提單號:</span>@Html.TextBox("LoadBillNum","", new { @class = "trade-time" })</li> </ul> <ul class="formod mgt10"> <li><span>運單號:</span>@Html.TextBox("ExpressNo","", new { @class = "trade-time wid153" })</li> <li><span>收寄日期:</span>@Html.TextBox("PostingTime", "", new { @class = "trade-time wid153", @onClick = "WdatePicker({maxDate:'#F{$dp.$D(\\'PostingTimeTo\\')}'})" })</li> <li><span style="text-align:left;width:25px;margin-left:-20px;">—</span> @Html.TextBox("PostingTimeTo", "", new { @class = "trade-time wid153", @onClick = "WdatePicker({minDate:'#F{$dp.$D(\\'PostingTime\\')}'})" })</li> <li><span>毛利:</span><select class="trade-time" id="sltMargin"><option value="" selected="selected">所有</option><option value="+">+</option><option value="-">-</option></select></li> </ul> <div class="botbtbx pdb0"> <input type="button" value="查詢" id="btnSearch" onclick="reloadList();" class="btn btn-primary" /> </div> } <div class="tob_box mgt15"> <table id="table_local" class="display" cellspacing="0" cellpadding="0" border="0" style="width: 100%"> <thead> <tr> <th rowspan="2"> <input type='checkbox' id='chkAllColl' onclick='selectAll()' />序號</th> <th rowspan="2">對帳日期</th> <th rowspan="2">客戶簡稱</th> <th rowspan="2">收寄日期</th> <th rowspan="2">郵件號</th> <th rowspan="2">提單號</th> <th rowspan="2">重量(kg)</th> <th colspan="5"><span>成本</span><span class="divIncome1"><img id="imgIncome" src="/images/icon_9.png" alt="收起/展開"/></span></th> <th colspan="5"><span>收入</span><span class="divIncome1"><img id="imgCost" src="/images/icon_9.png" alt="收起/展開"/></span></th> <th colspan="3">毛利</th> <th rowspan="2">狀態</th> <th rowspan="2">操做</th> </tr> <tr> <th>郵政郵資</th> <th>郵政郵件處理費</th> <th>其它費用</th> <th>總成本</th> <th>狀態</th> <th>客戶運費</th> <th>客戶操做費</th> <th>其它費用</th> <th>總收入</th> <th>狀態</th> <th>運費毛利</th> <th>總毛利</th> <th>毛利率</th> </tr> </thead> <tfoot> <tr> <td>總計</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td><span id="spnTotalWeight"></span></td> <td><span id="spnTotalWayBillFee"></span></td> <td><span id="spnTotalProcessingFee"></span></td> <td></td> <td></td> <td></td> <td><span id="spnTotalExpressFee"></span></td> <td><span id="spnTotalOperateFee"></span></td> <td></td> <td></td> <td></td> <td><span id="spnSumWayBillProfit"></span></td> <td><span id="spnSumTotalProfit"></span></td> <td></td> <td></td> <td></td> </tr> </tfoot> </table> </div> </div>
這裏面 table.fnSetColumnVis(arr[i], tag);這行代碼控制列動態隱藏和展現的時候,會從新加載數據,能夠在後面加一個false參數,取消刷新。 如: table.fnSetColumnVis(arr[i], tag,false);bootstrap
請求參數封裝類DataTablesRequest,這個類是從冠軍的博客下載的,它主要用於解析datatables的請求參數,因爲datatables支持多列排序,因此比較複雜。下載的這個類有點問題,那就是獲取的排序方式一直是asc,因而我進行了修改,修改後的代碼以下:api
/* ============================================================================== * 功能描述:DataTablesRequest * 創 建 者:Zouqj * 建立日期:2015/4/21 17:47:35 ==============================================================================*/ using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace ProjectBase.Utils { // 排序的方向 public enum SortDirection { Asc, // 升序 Desc // 降序 } // 排序列的定義 public class SortColumn { public int Index { get; set; } // 列序號 public SortDirection Direction { get; set; } // 列的排序方向 } // 列定義 public class Column { public string Name { get; set; } // 列名 public bool Sortable { get; set; } // 是否可排序 public bool Searchable { get; set; } // 是否可搜索 public string Search { get; set; } // 搜索串 public bool EscapeRegex { get; set; } // 是否正則 } public class DataTablesRequest { private HttpRequestBase request; // 內部使用的 Request 對象 public DataTablesRequest(System.Web.HttpRequestBase request) // 用於 MVC 模式下的構造函數 { this.request = request; this.echo = this.ParseStringParameter(sEchoParameter); this.displayStart = this.ParseIntParameter(iDisplayStartParameter); this.displayLength = this.ParseIntParameter(iDisplayLengthParameter); this.sortingCols = this.ParseIntParameter(iSortingColsParameter); this.search = this.ParseStringParameter(sSearchParameter); this.regex = this.ParseStringParameter(bRegexParameter) == "true"; // 排序的列 int count = this.iSortingCols; this.sortColumns = new SortColumn[count]; for (int i = 0; i < count; i++) { SortColumn col = new SortColumn(); col.Index = this.ParseIntParameter(string.Format("iSortCol_{0}", i)); if (this.ParseStringParameter(string.Format("sSortDir_{0}", i)) == "desc") { col.Direction = SortDirection.Desc; } else { col.Direction = SortDirection.Asc; } this.sortColumns[i] = col; } this.ColumnCount = this.ParseIntParameter(iColumnsParameter); count = this.ColumnCount; this.columns = new Column[count]; if(this.ParseStringParameter(sColumnsParameter)==null||!this.ParseStringParameter(sColumnsParameter).Contains(',')) { return; } string[] names = this.ParseStringParameter(sColumnsParameter).Split(','); for (int i = 0; i < count; i++) { Column col = new Column(); col.Name = names[i]; col.Sortable = this.ParseStringParameter(string.Format("bSortable_{0}", i)) == "true"; col.Searchable = this.ParseStringParameter(string.Format("bSearchable_{0}", i)) == "true"; col.Search = this.ParseStringParameter(string.Format("sSearch_{0}", i)); col.EscapeRegex = this.ParseStringParameter(string.Format("bRegex_{0}", i)) == "true"; columns[i] = col; } } public DataTablesRequest(HttpRequest httpRequest) // 標準的 WinForm 方式下的構造函數 : this(new HttpRequestWrapper(httpRequest)) { } #region private const string sEchoParameter = "sEcho"; // 起始索引和長度 private const string iDisplayStartParameter = "iDisplayStart"; private const string iDisplayLengthParameter = "iDisplayLength"; // 列數 private const string iColumnsParameter = "iColumns"; private const string sColumnsParameter = "sColumns"; // 參與排序列數 private const string iSortingColsParameter = "iSortingCols"; private const string iSortColPrefixParameter = "iSortCol_"; // 排序列的索引 private const string sSortDirPrefixParameter = "sSortDir_"; // 排序的方向 asc, desc // 每一列的可排序性 private const string bSortablePrefixParameter = "bSortable_"; // 全局搜索 private const string sSearchParameter = "sSearch"; private const string bRegexParameter = "bRegex"; // 每一列的搜索 private const string bSearchablePrefixParameter = "bSearchable_"; private const string sSearchPrefixParameter = "sSearch_"; private const string bEscapeRegexPrefixParameter = "bRegex_"; #endregion private readonly string echo; public string sEcho { get { return echo; } } private readonly int displayStart; public int iDisplayStart { get { return this.displayStart; } } private readonly int displayLength; public int iDisplayLength { get { return this.displayLength; } } // 參與排序的列 private readonly int sortingCols; public int iSortingCols { get { return this.sortingCols; } } // 排序列 private readonly SortColumn[] sortColumns; public SortColumn[] SortColumns { get { return sortColumns; } } private readonly int ColumnCount; public int iColumns { get { return this.ColumnCount; } } private readonly Column[] columns; public Column[] Columns { get { return this.columns; } } private readonly string search; public string Search { get { return this.search; } } private readonly bool regex; public bool Regex { get { return this.regex; } } #region 經常使用的幾個解析方法 private int ParseIntParameter(string name) // 解析爲整數 { int result = 0; string parameter = this.request[name]; if (!string.IsNullOrEmpty(parameter)) { int.TryParse(parameter, out result); } return result; } private string ParseStringParameter(string name) // 解析爲字符串 { return this.request[name]; } private bool ParseBooleanParameter(string name) // 解析爲布爾類型 { bool result = false; string parameter = this.request[name]; if (!string.IsNullOrEmpty(parameter)) { bool.TryParse(parameter, out result); } return result; } #endregion } }
本篇我不想作過多的說明,我寫了很是詳實的註釋,並且代碼很是通俗易懂,界面的功能仍是很是強大的,我相信,從這些犀利的代碼中,你必定會獲益良多。