Asp.Net MVC 分页、检索、排序整体实现

2020-12-13 02:23

阅读:495

标签:des   cPage   style   blog   class   code   

原文:Asp.Net MVC 分页、检索、排序整体实现

    很多时候需要这样的功能,对表格进行分页、排序和检索。这个有很多实现的方式,有现成的表格控件、用前端的mvvm,用户控件。但很多时候看着很漂亮的东西你想进一步控制的时候却不那么如意。这里自己实现一次,功能不是高大全,但求一个清楚明白,也欢迎园友拍砖。前端是bootstrap3+jPaginate,后台基于membership。没什么难点。

    先上效果图。

    soscw.com,搜素材

 

    分页其实就是处理好 每页项目数、总项目数、总页数、当前页。为了方便复用,就先从仓库开始说起。

一、建立仓库

  1.定义Ipager接口,需要分页的模型仓库继承这个接口

soscw.com,搜素材
namespace Protal.Model.Abstract
{
    /// 
    /// 分页处理
    /// 
   public interface IPager
    {
        /// 
        /// 每页项目数
        /// 
        /// The page item count.
      int  PageItemCount { get; set; }
      /// 
      /// 总页数
      /// 
      /// The totoal page.
       int TotoalPage { get; }
       /// 
       /// 显示的页数
       /// 
       /// The display page.
       int DisplayPage { get; set; }
       /// 
       /// 满足条件的总数目
       /// 
        int TotalItem { get; set; }
    }
}
soscw.com,搜素材

2.定义IUsersRepository,主要处理User 相关的业务逻辑。Find函数是主要的查询方法,order表示顺反排序。

soscw.com,搜素材
 public interface IUsersRepository : IPager
    {
        /// 
        /// Post list
        /// 
        /// Order expression
        /// Filter expression
        /// Records to skip
        /// Records to take
        /// List of users
        IEnumerable Find(int order=0,string filter="", int skip = 0, int take = 10);
        /// 
        /// Get single post
        /// 
        /// User id
        /// User object
        User FindByName(string name);
        /// 
        /// Add new user
        /// 
        /// Blog user
        /// Saved user
        User Add(User user);
        /// 
        /// Update user
        /// 
        /// User to update
        /// True on success
        bool Update(User user);
        /// 
        /// Save user profile
        /// 
        /// Blog user
        /// True on success
        bool SaveProfile(User user);
        /// 
        /// Delete user
        /// 
        /// User ID
        /// True on success
        bool Remove(string userName);
    }
soscw.com,搜素材

 

二、仓库的实现和绑定

  主要方法:Membership的中的User和我们自定义的不一样,所以存在一个转换

soscw.com,搜素材
 public class UsersRepository : IUsersRepository
   {
       /// 
       /// The _user list
       /// 
        private List _userList = new List();
       /// 
       /// The _page item count
       /// 
        private int _pageItemCount;
        /// 
        /// The _display page
        /// 
        private int _displayPage;
        /// 
        /// The _usercount
        /// 
        private int _usercount;
        /// 
        /// The _total item
        /// 
        private int _totalItem;
        /// 
        /// 标记是否有查询条件 没有的话则返回全部数目
        /// 
        private Funcbool> _func;

        /// 
        /// Gets or sets the users.
        /// 
        /// The users.
        public List Users
       {
           get
           {
               int count;
               var usercollection = Membership.GetAllUsers(0, 999, out count);
               if (count == _usercount) return _userList;
               _usercount = count;
               var members = usercollection.Cast().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 Find(int order = 0, string filter = "", int skip = 0, int take = 10) { if (take == 0) take = Users.Count;
//过滤 _func
= string.IsNullOrEmpty(filter) ? (Funcbool>) (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); } /// /// 每页项目数 /// /// The page item count. public int PageItemCount { get { if (_pageItemCount == 0) { _pageItemCount = ProtalConfig.UserPageItemCount; } return _pageItemCount; } set { _pageItemCount = value; } } /// /// 总页数 /// /// The totoal page. public int TotoalPage { get { var page = (int) Math.Ceiling((double) TotalItem/PageItemCount); return page==0?1:page; } } /// /// 显示的页数 /// /// The display page. public int DisplayPage { get { if (_displayPage == 0) { _displayPage = ProtalConfig.UserDisplayPage; } return _displayPage; } set { _displayPage = value; } } /// /// 满足条件的总数目 保持更新 /// /// The total item. public int TotalItem { get { if (_func == null) _totalItem = Users.Count; return _totalItem; } set { _totalItem = value; } } }
soscw.com,搜素材
ProtalConfig.UserDisplayPage 这里是通过配置实现一个默认页数,让用户可以再webconfig中更改行列的数目。
soscw.com,搜素材soscw.com,搜素材
 public static int UserPageItemCount
                {
                    get
                    {
                        if (_userPageItemCount == 0)
                        {
                            _userPageItemCount = WebConfigurationManager.AppSettings["UserPageItemCount"] != null ?
                                Convert.ToInt16(WebConfigurationManager.AppSettings["UserPageItemCount"]) : 5;
                        }
                        return _userPageItemCount;
                    }
                    set
                    {
                        _userPageItemCount = value;
                    }
                }
View Code

 再进行绑定:

    _kernel.Bind().To();

 

三、控制器部分

  我们需要两个页面,一个主页面Index,一个负责局部刷新的部分视图 UserTable

 下面是主要的方法,主要逻辑都在在仓库中处理了。

soscw.com,搜素材
   [Authorize]
    public class UserManagerController : Controller
    {
        /// 
        /// The _repository
        /// 
        private readonly IUsersRepository _repository;

        /// 
        /// Initializes a new instance of the  class.
        /// 
        /// The i repository.
        public UserManagerController(IUsersRepository iRepository)
        {
            _repository = iRepository; 
        }

        /// 
        /// Indexes the specified page index.
        /// 
        /// Index of the page.
        /// ActionResult.
        public ActionResult Index(int pageIndex=1)
        {
            ViewBag.DisplayPage = _repository.DisplayPage;
            pageIndex = HandlePageindex(pageIndex);
         
            //支持地址栏直接分页
            ViewBag.CurrentPage = pageIndex;
            return View();
        }


        /// 
        /// Users table. 分页模块
        /// 
        /// Index of the page.
        /// The order.
        /// The filter str.
        /// ActionResult.
        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);
        }

        /// 
        /// 处理页数 防止过大或过小
        /// 
        /// 
        /// 
        private int HandlePageindex(int index)
        {
            var totoalpage = _repository.TotoalPage;
            if (index == 0) return 1;
            return index > totoalpage ? totoalpage : index;
        }
}
soscw.com,搜素材

 

四、视图部分Html jquery

 1.Index.cshtml

soscw.com,搜素材
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 Userh4>
        div>
        div class="modal-body">
           @{
               Html.RenderAction("Create","UserManager");
            }
        div>
    div>
div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}
soscw.com,搜素材

2.UserTable.cshtml,角色部分还未处理,这个表格更新之后,也会更新满足条件的用户数和新的总页数,触发Jpaginate重新分页一次。

soscw.com,搜素材
@model IEnumerableProtal.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-mailth>
        tr>
        tbody>
            @foreach (var item in Model) {
                tr>
                    td> input type="checkbox" data-id="@item.UserName" />td>
                    td> a>@item.UserNamea> td>
                    td> @Html.Raw(item.Role) td>
                    td> @item.Emailtd>
                tr>
            }tbody>
     tfoot>
         tr>
             td colspan="4">
                  span>@Html.Raw("共"+ViewBag.TotalUser+"人")span> @*span>@ViewBag.TotalPageCountspan>*@
             td>
         tr>
     tfoot>
    table>
  input type="hidden" id="totoalpage" value="@ViewBag.TotalPageCount"/>
soscw.com,搜素材

3.脚本

其中用到的像checkall,infoShow 都是自己扩展的一些简单的方法,用于全选和提示。

soscw.com,搜素材soscw.com,搜素材
  $(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 ) {
                    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();
        });
    })
View Code

到这里就是全部的代码,供大家和自己参考。

再给大家看两个效果图,一个是kendoui的grid,一个是Angular做的分页。后面有机会给大家介绍。

Kendo- Grid

soscw.com,搜素材

 Kendo和MVC框架融合度比较高,它的核心代码如下:

soscw.com,搜素材soscw.com,搜素材
@model IEnumerable

@(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)        
     )
)
View Code

AngularJs 核心还是调用封装好的API函数,相当于上面的仓库中的方法,然后通过模型绑定。

soscw.com,搜素材

 总结一下:自己实现代码量比较多,功能不全,有重复造轮子的感觉,但可以较好的控制,基本够用;kendo的方式感觉高大全,用熟了开发速度快。就是多一些引用,且需要担心kendoui和其他的ui框架会有冲突。前端MVVM的方式我了解还不够深,感觉前端脚本的代码量也蛮多,效果不错。但生成的html代码很少。上面这个表格。chrome F12或者右键查看源码都是下面这样子的:

  主要的就一个div 

 div data-ng-app="blogAdmin" data-ng-view="" id="ng-view">div>

自我保护倒是蛮好,也就是SEO可能有问题。应该还有更好的方式,猿友们指点指点。

soscw.com,搜素材soscw.com,搜素材
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


评论


亲,登录后才可以留言!