很多时候需要这样的功能,对表格进行分页、排序和检索。这个有很多实现的方式,有现成的表格控件、用前端的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 下载请戳这里
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。