很多時候需要這樣的功能,對表格進行分頁、排序和檢索。這個有很多實現的方式,有現成的表格控件、用前端的mvvm,用戶控件。但很多時候看著很漂亮的東西你想進一步控制的時候卻不那麼如意。這裡自己實現一次,功能不是高大全,但求一個清楚明白,也歡迎園友拍磚。前端是bootstrap3+jPaginate,後台基於membership。沒什麼難點。
先上效果圖。
分頁其實就是處理好 每頁項目數、總項目數、總頁數、當前頁。為了方便復用,就先從倉庫開始說起。
一、建立倉庫
1.定義Ipager接口,需要分頁的模型倉庫繼承這個接口
namespace Protal.Model.Abstract { /// <summary> /// 分頁處理 /// </summary> public interface IPager { /// <summary> /// 每頁項目數 /// </summary> /// <value>The page item count.</value> int PageItemCount { get; set; } /// <summary> /// 總頁數 /// </summary> /// <value>The totoal page.</value> int TotoalPage { get; } /// <summary> /// 顯示的頁數 /// </summary> /// <value>The display page.</value> int DisplayPage { get; set; } /// <summary> /// 滿足條件的總數目 /// </summary> int TotalItem { get; set; } } }
2.定義IUsersRepository,主要處理User 相關的業務邏輯。Find函數是主要的查詢方法,order表示順反排序。
public interface IUsersRepository : IPager { /// <summary> /// Post list /// </summary> /// <param name="order">Order expression</param> /// <param name="filter">Filter expression</param> /// <param name="skip">Records to skip</param> /// <param name="take">Records to take</param> /// <returns>List of users</returns> IEnumerable<User> Find(int order=0,string filter="", int skip = 0, int take = 10); /// <summary> /// Get single post /// </summary> /// <param name="name">User id</param> /// <returns>User object</returns> User FindByName(string name); /// <summary> /// Add new user /// </summary> /// <param name="user">Blog user</param> /// <returns>Saved user</returns> User Add(User user); /// <summary> /// Update user /// </summary> /// <param name="user">User to update</param> /// <returns>True on success</returns> bool Update(User user); /// <summary> /// Save user profile /// </summary> /// <param name="user">Blog user</param> /// <returns>True on success</returns> bool SaveProfile(User user); /// <summary> /// Delete user /// </summary> /// <param name="userName">User ID</param> /// <returns>True on success</returns> bool Remove(string userName); }
二、倉庫的實現和綁定
主要方法:Membership的中的User和我們自定義的不一樣,所以存在一個轉換
public class UsersRepository : IUsersRepository { /// <summary> /// The _user list /// </summary> private List<User> _userList = new List<User>(); /// <summary> /// The _page item count /// </summary> private int _pageItemCount; /// <summary> /// The _display page /// </summary> private int _displayPage; /// <summary> /// The _usercount /// </summary> private int _usercount; /// <summary> /// The _total item /// </summary> private int _totalItem; /// <summary> /// 標記是否有查詢條件 沒有的話則返回全部數目 /// </summary> private Func<User, bool> _func; /// <summary> /// Gets or sets the users. /// </summary> /// <value>The users.</value> public List<User> Users { get { int count; var usercollection = Membership.GetAllUsers(0, 999, out count); if (count == _usercount) return _userList; _usercount = count; var members = usercollection.Cast<MembershipUser>().ToList(); foreach (var membershipUser in members)//這裡存在一個轉換 { _userList.Add(new User { Email = membershipUser.Email, UserName = membershipUser.UserName, //roles password }); } return _userList; } set { _userList = value; } } //查詢 public IEnumerable<User> Find(int order = 0, string filter = "", int skip = 0, int take = 10) { if (take == 0) take = Users.Count; //過濾 _func = string.IsNullOrEmpty(filter) ? (Func<User, bool>) (n => n.UserName != "") : (n => n.UserName.Contains(filter)); var users = Users.Where(_func).ToList(); //更新總數目 _totalItem = users.Count; users = order == 0 ? users.OrderBy(n => n.UserName).ToList() : users.OrderByDescending(n => n.UserName).ToList(); return users.Skip(skip).Take(take); } /// <summary> /// 每頁項目數 /// </summary> /// <value>The page item count.</value> public int PageItemCount { get { if (_pageItemCount == 0) { _pageItemCount = ProtalConfig.UserPageItemCount; } return _pageItemCount; } set { _pageItemCount = value; } } /// <summary> /// 總頁數 /// </summary> /// <value>The totoal page.</value> public int TotoalPage { get { var page = (int) Math.Ceiling((double) TotalItem/PageItemCount); return page==0?1:page; } } /// <summary> /// 顯示的頁數 /// </summary> /// <value>The display page.</value> public int DisplayPage { get { if (_displayPage == 0) { _displayPage = ProtalConfig.UserDisplayPage; } return _displayPage; } set { _displayPage = value; } } /// <summary> /// 滿足條件的總數目 保持更新 /// </summary> /// <value>The total item.</value> public int TotalItem { get { if (_func == null) _totalItem = Users.Count; return _totalItem; } set { _totalItem = value; } } }
ProtalConfig.UserDisplayPage 這裡是通過配置實現一個默認頁數,讓用戶可以再webconfig中更改行列的數目。
public static int UserPageItemCount { get { if (_userPageItemCount == 0) { _userPageItemCount = WebConfigurationManager.AppSettings["UserPageItemCount"] != null ? Convert.ToInt16(WebConfigurationManager.AppSettings["UserPageItemCount"]) : 5; } return _userPageItemCount; } set { _userPageItemCount = value; } }
再進行綁定:
_kernel.Bind<IUsersRepository>().To<UsersRepository>();
三、控制器部分
我們需要兩個頁面,一個主頁面Index,一個負責局部刷新的部分視圖 UserTable
下面是主要的方法,主要邏輯都在在倉庫中處理了。
[Authorize] public class UserManagerController : Controller { /// <summary> /// The _repository /// </summary> private readonly IUsersRepository _repository; /// <summary> /// Initializes a new instance of the <see cref="UserManagerController"/> class. /// </summary> /// <param name="iRepository">The i repository.</param> public UserManagerController(IUsersRepository iRepository) { _repository = iRepository; } /// <summary> /// Indexes the specified page index. /// </summary> /// <param name="pageIndex">Index of the page.</param> /// <returns>ActionResult.</returns> public ActionResult Index(int pageIndex=1) { ViewBag.DisplayPage = _repository.DisplayPage; pageIndex = HandlePageindex(pageIndex); //支持地址欄直接分頁 ViewBag.CurrentPage = pageIndex; return View(); } /// <summary> /// Users table. 分頁模塊 /// </summary> /// <param name="pageIndex">Index of the page.</param> /// <param name="order">The order.</param> /// <param name="filter">The filter str.</param> /// <returns>ActionResult.</returns> public ActionResult UserTable(int pageIndex = 1, int order = 0, string filter = "") { pageIndex = HandlePageindex(pageIndex); var skip = (pageIndex - 1) * _repository.PageItemCount; var users = _repository.Find(order,filter, skip, _repository.PageItemCount); //總用戶數 ViewBag.TotalUser = _repository.TotalItem; //總頁數 ViewBag.TotalPageCount = _repository.TotoalPage; ; return PartialView(users); } /// <summary> /// 處理頁數 防止過大或過小 /// </summary> /// <param name="index"></param> /// <returns></returns> private int HandlePageindex(int index) { var totoalpage = _repository.TotoalPage; if (index == 0) return 1; return index > totoalpage ? totoalpage : index; } }
四、視圖部分Html jquery
1.Index.cshtml
<script src="~/Scripts/form.js"></script> <div class="container"> <h4 class="bottomline">管理用戶</h4> <p> <button data-target="#adduser" id="adduserbt" data-toggle="modal" class="btn btn-info btn-hover">新增用戶</button> <button class="btn btn-danger" id="deluser">刪除</button> <span class="errorinfo"></span> <input type="search" class="pull-right" id="usersearch" placeholder="搜索"/> </p> <div id="userpart"> @Html.Action("UserTable",new{pageIndex=ViewBag.CurrentPage}) </div> <div id="userpager"></div> <input type="hidden" id="dispalypage" value="@ViewBag.DisplayPage"/> <input type="hidden" id="page" value="@ViewBag.CurrentPage"/> <input type="hidden" id="currentpage" value="@ViewBag.CurrentPage"/> </div> <div class="modal fade adduserbox"id="adduser" tabindex="1" role="dialog" aria-hidden="true"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true" >×</button> <h4 class="modal-title">Add new User</h4> </div> <div class="modal-body"> @{ Html.RenderAction("Create","UserManager"); } </div> </div> </div> @section Scripts { @Scripts.Render("~/bundles/jqueryval") }
2.UserTable.cshtml,角色部分還未處理,這個表格更新之後,也會更新滿足條件的用戶數和新的總頁數,觸發Jpaginate重新分頁一次。
@model IEnumerable<Protal.Model.Data.User.User> <table id="usertable" class="table table-striped table-condensed table-hover table-bordered"> <tr> <th><input type="checkbox" id="allcheck" /><label for="allcheck">全選</label></th> <th><a href="#" id="usersort" data-order="0" class="glyphicon-sort">名稱</a></th> <th>角色</th> <th>E-mail</th> </tr> <tbody> @foreach (var item in Model) { <tr> <td> <input type="checkbox" data-id="@item.UserName" /></td> <td> <a>@item.UserName</a> </td> <td> @Html.Raw(item.Role) </td> <td> @item.Email</td> </tr> }</tbody> <tfoot> <tr> <td colspan="4"> <span>@Html.Raw("共"+ViewBag.TotalUser+"人")</span> @*<span>@ViewBag.TotalPageCount</span>*@ </td> </tr> </tfoot> </table> <input type="hidden" id="totoalpage" value="@ViewBag.TotalPageCount"/>
3.腳本
其中用到的像checkall,infoShow 都是自己擴展的一些簡單的方法,用於全選和提示。
$(function () { var options = { dataType: 'json', success: processJson }; pageagin($("#totoalpage").val()); //分頁 function pageagin(totalcount) { $("#userpager").paginate({ count: totalcount, start: $("#page").val(), dispaly: $("#dispalypage").val(), boder: false, border_color: '#fff',//自己調整樣式。 text_color: 'black', background_color: 'none', border_hover_color: '#ccc', text_hover_color: '#000', background_hover_color: '#fff', images: false, mouse: 'press', onChange: function (page) { //翻頁 paging(page); $("#currentpage").val(page); } }); } //分頁更新 function paging(page) { $.post("/Users/UserTable", { pageIndex: page, order: $("#userpart").attr("data-order"), filter: $.trim($("#usersearch").val()) }, function (data) { $("#userpart").html(data); }); } //排序 $("#usersort").live("click",function () { $("#userpart").triggerdataOrder(); paging( $("#currentpage").val()); }); //搜索 $("#usersearch").keyup(function() { paging($("#currentpage").val()); pageagin($("#totoalpage").val()); }); //處理form $("#userForm").submit(function () { $(this).ajaxSubmit(options); return false; }); function processJson(data) { if (data == 1) { location.reload(); } else { alert("添加失敗"); } } //高亮 $("#unav li:eq(0)").addClass("active"); $("#adnav li:eq(2)").addClass("active"); //全選/全不選 $("#allcheck").checkall($("#usertable tbody input[type='checkbox']")); //刪除用戶 $("#deluser").click(function () { var checks = $("#usertable tbody input[type='checkbox']:checked"); var lens = checks.length; if (lens == 0) { $.infoShow("未選擇刪除對象",0); return false; } if (confirm("確定要刪除所選中用戶?")) { for (var i = 0; i < lens; i++) { var $chek = checks.eq(i); var id = $chek.attr("data-id"); var tr = $chek.parent().parent(); $.post("Users/DeleteUser", { id: id }, function (data) { if (data == 1) { tr.fadeOut(); $.infoShow("刪除成功", 1); } else { $.infoShow("刪除失敗", 0); } }); } } return true; }); // 增加用戶 $("#adduserbt").click(function() { $(".modal-header").show(); }); })
到這裡就是全部的代碼,供大家和自己參考。
再給大家看兩個效果圖,一個是kendoui的grid,一個是Angular做的分頁。後面有機會給大家介紹。
Kendo- Grid
Kendo和MVC框架融合度比較高,它的核心代碼如下:
@model IEnumerable<Kendo.Mvc.Examples.Models.ProductViewModel> @(Html.Kendo().Grid(Model) .Name("Grid") .Columns(columns => { columns.Bound(p => p.ProductID).Groupable(false); columns.Bound(p => p.ProductName); columns.Bound(p => p.UnitPrice); columns.Bound(p => p.UnitsInStock); }) .Pageable() .Sortable() .Scrollable() .Filterable() .DataSource(dataSource => dataSource .Ajax() .ServerOperation(false) ) )
AngularJs 核心還是調用封裝好的API函數,相當於上面的倉庫中的方法,然後通過模型綁定。
總結一下:自己實現代碼量比較多,功能不全,有重復造輪子的感覺,但可以較好的控制,基本夠用;kendo的方式感覺高大全,用熟了開發速度快。就是多一些引用,且需要擔心kendoui和其他的ui框架會有沖突。前端MVVM的方式我了解還不夠深,感覺前端腳本的代碼量也蠻多,效果不錯。但生成的html代碼很少。上面這個表格。chrome F12或者右鍵查看源碼都是下面這樣子的:
主要的就一個div
<div data-ng-app="blogAdmin" data-ng-view="" id="ng-view"></div>
自我保護倒是蠻好,也就是SEO可能有問題。應該還有更好的方式,猿友們指點指點。
<html> <head> <title>Name of the blog (Admin)</title> <link rel="shortcut icon" href="/pics/blogengine.ico" type="image/x-icon" /> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" /> <meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-status-bar-style" content="black" /> <meta name="format-detection" content="telephone=no" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <link href="/Content/bootstrap/bootstrap.css" rel="stylesheet"/> <link href="/Content/bootstrap/bootstrap-theme.css" rel="stylesheet"/> <link href="/Content/toastr.css" rel="stylesheet"/> <link href="/Content/font-awesome.css" rel="stylesheet"/> <link href="/Content/editor.css" rel="stylesheet"/> <link href="/Content/app.css" rel="stylesheet"/> <script type="text/javascript"> if (navigator.userAgent.match(/IEMobile\/10\.0/)) { var msViewportStyle = document.createElement("style"); var mq = "@-ms-viewport{width:auto!important}"; msViewportStyle.appendChild(document.createTextNode(mq)); document.getElementsByTagName("head")[0].appendChild(msViewportStyle); } </script> </head> <body> <script type="text/javascript"> var SiteVars = { ApplicationRelativeWebRoot: '/', RelativeWebRoot: '/', BlogInstanceId: '96d5b379-7e1d-4dac-a6ba-1e50db561b04', UserName: 'admin', UserRights: ['ViewDetailedErrorMessages', 'AccessAdminPages', 'AccessAdminSettingsPages', 'ManageWidgets', 'ViewPublicComments', 'ViewUnmoderatedComments', 'CreateComments', 'ModerateComments', 'ViewPublicPosts', 'ViewUnpublishedPosts', 'CreateNewPosts', 'EditOwnPosts', 'EditOtherUsersPosts', 'DeleteOwnPosts', 'DeleteOtherUsersPosts', 'PublishOwnPosts', 'PublishOtherUsersPosts', 'ViewPublicPages', 'ViewUnpublishedPages', 'CreateNewPages', 'EditOwnPages', 'ViewRatingsOnPosts', 'SubmitRatingsOnPosts', 'ViewRoles', 'CreateNewRoles', 'EditRoles', 'DeleteRoles', 'EditOwnRoles', 'EditOtherUsersRoles', 'CreateNewUsers', 'DeleteUserSelf', 'DeleteUsersOtherThanSelf', 'EditOwnUser', 'EditOtherUsers'], AbsoluteWebRoot: 'http://localhost:53265/', Version: 'BlogEngine.NET ' + '2.9.1.0', IsPrimary: 'True', IsAdmin: 'True', AppRoot: function (url) { window.location = '/' + url; return false; }, BlogRoot: function (url) { window.location = '/' + url; } }; </script> <script type="text/javascript" src="admin.res.axd"></script> <div id="container" class="app-wrapper ltr"> <div data-ng-app="blogAdmin" data-ng-view="" id="ng-view"></div> </div> <script src="/scripts/jquery-2.0.3.js"></script> <script src="/scripts/jquery.validate.js"></script> <script src="/scripts/jquery.form.js"></script> <script src="/scripts/toastr.js"></script> <script src="/Scripts/angular.min.js"></script> <script src="/Scripts/angular-route.min.js"></script> <script src="/Scripts/angular-animate.min.js"></script> <script src="/Scripts/angular-sanitize.min.js"></script> <script src="/admin/be-grid.js"></script> <script src="/admin/app.js"></script> <script src="/admin/controllers/dashboard.js"></script> <script src="/admin/controllers/blogs.js"></script> <script src="/admin/controllers/posts.js"></script> <script src="/admin/controllers/pages.js"></script> <script src="/admin/controllers/tags.js"></script> <script src="/admin/controllers/categories.js"></script> <script src="/admin/controllers/comments.js"></script> <script src="/admin/controllers/users.js"></script> <script src="/admin/controllers/roles.js"></script> <script src="/admin/controllers/profile.js"></script> <script src="/admin/controllers/settings.js"></script> <script src="/admin/controllers/packages.js"></script> <script src="/admin/controllers/common.js"></script> <script src="/admin/services.js"></script> <script src="/scripts/bootstrap.js"></script> <script src="/scripts/moment.js"></script> </body> </html>
PS:這個東西沒什麼難度,邏輯都在倉庫中,要源碼的同學我後續分離出來了再貼出來。當然這個又很多方式,我也不是要秀什麼框架,但我目前項目的需求是要這麼分開的。一個控制器是可用解決所有問題,但我其他模型也要分頁又要便於測試難道我都寫在控制器中嗎?
Demo 下載請戳這裡
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持幫客之家。