很多时候需要这样的功能,对表格进行分页、排序和检索。这个有很多实现的方式,有现成的表格控件、用前端的mvvm,用户控件。但很多时候看着很漂亮的东西你想进一步控制的时候却不那么如意。这里自己实现一次,功能不是高大全,但求一个清楚明白,也欢迎园友拍砖。前端是bootstrap3+jPaginate,后台基于membership。没什么难点。
先上效果图。
分页其实就是处理好 每页项目数、总项目数、总页数、当前页。为了方便复用,就先从仓库开始说起。
一、建立仓库
1.定义Ipager接口,需要分页的模型仓库继承这个接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
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表示顺反排序。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
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和我们自定义的不一样,所以存在一个转换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
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中更改行列的数目。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public static int UserPageItemCount { get { if (_userPageItemCount == 0) { _userPageItemCount = WebConfigurationManager.AppSettings[ "UserPageItemCount" ] != null ? Convert.ToInt16(WebConfigurationManager.AppSettings[ "UserPageItemCount" ]) : 5; } return _userPageItemCount; } set { _userPageItemCount = value; } } |
再进行绑定:
1
|
_kernel.Bind<IUsersRepository>().To<UsersRepository>(); |
三、控制器部分
我们需要两个页面,一个主页面Index,一个负责局部刷新的部分视图 UserTable
下面是主要的方法,主要逻辑都在在仓库中处理了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
[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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
<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重新分页一次。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
@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 都是自己扩展的一些简单的方法,用于全选和提示。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
|
$( 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框架融合度比较高,它的核心代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
@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
1
|
< div data-ng-app = "blogAdmin" data-ng-view = "" id = "ng-view" ></ div > |
自我保护倒是蛮好,也就是SEO可能有问题。应该还有更好的方式,猿友们指点指点。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
<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' ], 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:这个东西没什么难度,逻辑都在仓库中,要源码的同学我后续分离出来了再贴出来。当然这个又很多方式,我也不是要秀什么框架,但我目前项目的需求是要这么分开的。一个控制器是可用解决所有问题,但我其他模型也要分页又要便于测试难道我都写在控制器中吗?
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:http://www.cnblogs.com/stoneniqiu/p/3713114.html