欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-07-04 20:39:58
很多时候需要这样的功能,对表格进行分页、排序和检索。这个有很多实现的方式,有现成的表格控件、用前端的mvvm,用户控件。但很多时候看着很漂亮的东西你想进一步控制的时候却不那...

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

先上效果图。

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

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

一、建立仓库

  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

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

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函数,相当于上面的仓库中的方法,然后通过模型绑定。

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

总结一下:自己实现代码量比较多,功能不全,有重复造*的感觉,但可以较好的控制,基本够用;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 下载请戳这里

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。