• ABP之展现层(Datatables分页)


     在前面的随笔中,已经介绍了ABP的增删改查的操作,但是对于查询的数据并没有进行分页,只是进行粗糙的展示,今天的随笔中将摸索进行分页展示。这里打算使用的分页插件是DataTables,这是一款比较强大的表格插件。

    在以前我们后台手动分页的时候,需要前台传入两个重要的分页参数:PageIndex和PageSize(显示第几页的数据和每页显示的数量),这是必须的量的参数。分页作为一个页面展示的基础功能,ABP框架已经对分页功能进行了一些方便性的操作,为我们提供了一些有助于分页的接口和Dto,Dto是什么?这个在前面的随笔中已经研究过了,这里就不再重复。

    一 .ABP中的分页接口

    在ABP中总共为我们提供了三个分页的接口:IPagedResultRequest、ISortedResultRequest、ILimitedResultRequest三个接口

     

    从上面的三个接口中我们看到了三个重要的变量,这就是我们分页和排序中经常用到的量。

    二. 实现分页的Dto

    在我们免费的ABP中模板中,也就是只能找到这么三个接口,对我们分页来说确实并没有提供了多大的方便,但是在ABP Zero中已经对三个接口进行了相应的实现,只是zero是收费的。在这里我们可以模仿zero在我们ABP模板中添加上分页的Dto,并且为DataTables这个插件定制分页Dto。

    详细的代码

     1 1.PagedInputDto
     2  public class PagedInputDto : IPagedResultRequest
     3     {
     4         /// <summary>
     5         /// 每页显示的行数
     6         /// </summary>
     7         [Range(1, AppConsts.MaxPageSize)]
     8         public int MaxResultCount { get; set; }
     9         /// <summary>
    10         /// 跳过数量=MaxResultCount*页数
    11         /// </summary>
    12         [Range(0, int.MaxValue)]
    13         public int SkipCount { get; set; }
    14 
    15         public PagedInputDto()
    16         {
    17             MaxResultCount = AppConsts.DefaultPageSize;
    18         }
    19     }
    20 2. PagedAndFilteredInputDto
    21     public class PagedAndFilteredInputDto : IPagedResultRequest
    22     {
    23         [Range(1, AppConsts.MaxPageSize)]
    24         public int MaxResultCount { get; set; }
    25 
    26         [Range(0, int.MaxValue)]
    27         public int SkipCount { get; set; }
    28 
    29         public string Filter { get; set; }
    30 
    31         public PagedAndFilteredInputDto()
    32         {
    33             MaxResultCount = AppConsts.DefaultPageSize;
    34         }
    35     }
    36 3. PageAndSortedInputDto
    37     public class PagedAndSortedInputDto : PagedInputDto, ISortedResultRequest
    38     {
    39         public string Sorting { get; set; }
    40 
    41         public PagedAndSortedInputDto()
    42         {
    43             MaxResultCount = AppConsts.DefaultPageSize;
    44         }
    45     }
    46 4.PagedSortedAndFilteredInputDto
    47     public class PagedSortedAndFilteredInputDto : PagedAndSortedInputDto
    48     {
    49         public string Filter { get; set; }
    50         //接收DataTables的参数
    51         public int Draw { get; set; }
    52         public int Length
    53         {
    54             get
    55             {
    56                 return this.MaxResultCount;
    57             }
    58 
    59             set
    60             {
    61                 this.MaxResultCount = value;
    62             }
    63         }
    64         public int Start
    65         {
    66             get
    67             {
    68                 return this.SkipCount;
    69             }
    70 
    71             set
    72             {
    73                 this.SkipCount = value;
    74             }
    75         }
    76     }
    77 5.DataTablesPageOutPutDto
    78    [Serializable]
    79     public class DataTablesPagedOutputDto<T>:PagedResultDto<T>
    80     {
    81         public int Draw { get; set; }
    82 
    83         /// <summary>
    84         /// 过滤后的记录数(没有就是全部),这个是必须的参数
    85         /// </summary>
    86         public int RecordsFiltered { get; set; }
    87 
    88         public int RecordsTotal { get { return this.TotalCount; } }
    89       
    90         public DataTablesPagedOutputDto(int totalCount, IReadOnlyList<T> items)
    91           : base(totalCount, items)
    92         {
    93             this.RecordsFiltered = totalCount;
    94         }
    95     }

    其中PagedSortedAndFilteredInputDto和DataTablesPageOutPutDto分别是为了适应DataTables的需求定制的两个类,Input的类中Start、Length、Draw、Filter都是为了接收DataTables传递过来的参数,在OutPut类中定义了RecordsFiltered和recordsTotal和Draw这些都是DataTables需要的参数。说了这么多,还是先看一下DataTables这插件再说。

    三.DataTables分页

    在这里我们使用的服务端分页,详细的内容可查看官网的具体介绍:http://www.datatables.club/manual/server-side.html

    (1)Dto的请求参数

    当然参数还有许多,但是主要的参数也就是上面的那几个,尤其是已经圈出来的这三个,就可以完成分页功能了,如果需要进行排序或者添加按照字段的搜索的功能,那么就需要用到下面的字段了,我们这里只是使用分页功能。

    (2)Dto的返回参数

     通过了上面DataTables官网的介绍,我们已经清楚了我们Dto中定义的参数的作用了,不知道大家有没有一点困惑,就是Draw参数到底是干什么的???哈哈哈,我们在DataTables中已经找到了答案,他是防止跨站脚本攻击的,关于他的赋值,只要给他赋值一个整数就可以了。

    三.在ABP中使用DataTables实现分页

    View

    Index的具体代码

    @using Abp.Authorization.Users
    @using StudyABPProject.Web.Startup
    @model IList<StudyABPProject.Movie.Dto.MovieTicketDto>
    @{
        ViewBag.CurrentPageName = PageNames.Movies; // The menu item will be active for this page.
    }
    @section scripts
        {
        <script src="~/lib/jquery-daterangepicker/daterangepicker.js" asp-append-version="true"></script>
        <script src="~/view-resources/Views/Movie/Index.js" asp-append-version="true"></script>
    
        <link href="~/lib/jquery-daterangepicker/daterangepicker.css" rel="stylesheet" />
        <link href="~/lib/datatables/jquery.dataTables.min.css" rel="stylesheet" />
        <script src="~/lib/datatables/jquery.dataTables.min.js"></script>
    }
    
    <div class="row clearfix">
        <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
            <div class="card">
                <div class="header">
                    <h2>
                        @L("Movie")
                    </h2>
                    <ul class="header-dropdown m-r--5">
                        <li class="dropdown">
                            <a href="javascript:void(0);" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
                                <i class="material-icons">more_vert</i>
                            </a>
                            <ul class="dropdown-menu pull-right">
                                <li><a id="RefreshButton" href="javascript:void(0);" class="waves-effect waves-block"><i class="material-icons">refresh</i>Refresh</a></li>
                            </ul>
                        </li>
                    </ul>
                </div>
                <div class="body table-responsive">
                    <button type="button" class="btn btn-primary  waves-effect  waves-float pull-right" data-toggle="modal" data-target="#MovieTicketCreateModal">
                        <i class="material-icons">添加</i>
                    </button>
                    <table id="MovieTable" name="MovieTable"></table>
                </div>
            </div>
        </div>
    </div>
    
    <div class="modal fade" id="MovieTicketCreateModal" tabindex="-1" role="dialog" aria-labelledby="UserCreateModalLabel" data-backdrop="static">
        <div class="modal-dialog" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <h4 class="modal-title">
                        <span>@L("CreateMovieTicket")</span>
                    </h4>
                </div>
                <div class="modal-body">
                    <form name="movieCreateForm" role="form" novalidate class="form-validation">
                        <div class="tab-content">
                            <div role="tabpanel" class="tab-pane animated fadeIn active" id="create-user-details">
                                <div class="row clearfix" style="margin-top:10px;">
                                    <div class="col-sm-12">
                                        <div class="form-group form-float">
                                            <div class="form-line">
                                                <input class="form-control" type="text" name="MovieName" required maxlength="256" minlength="2">
                                                <label class="form-label">@L("MovieName")</label>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                                <div class="row clearfix">
                                    <div class="col-sm-12">
                                        <div class="form-group form-float">
                                            <div class="form-line">
                                                <input type="text" name="MovieActor" class="form-control" required maxlength="256">
                                                <label class="form-label">@L("MovieActor")</label>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                                <div class="row clearfix">
                                    <div class="col-sm-12">
                                        <div class="form-group form-float">
                                            <div class="form-line">
                                                <input type="datetime" name="StartTime" class="form-control" required>
                                                @*<label class="form-label">@L("StartTime")</label>*@
                                            </div>
                                        </div>
                                    </div>
                                </div>
                                <div class="row clearfix">
                                    <div class="col-sm-12">
                                        <div class="form-group form-float">
                                            <div class="form-line">
                                                <input type="datetime" id="EndTime" name="EndTime" class="form-control">
                                                @*<label class="form-label">@L("EndTime")</label>*@
                                            </div>
                                        </div>
                                    </div>
                                </div>
                                <div class="row clearfix">
                                    <div class="col-sm-12">
                                        <div class="form-group form-float">
                                            <div class="form-line">
                                                <input type="number" id="Money" name="Money" class="form-control">
                                                <label class="form-label">@L("Money")</label>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div class="modal-footer">
                            <button type="button" class="btn btn-default waves-effect" data-dismiss="modal">取消</button>
                            <button type="submit" id="btnSave" class="btn btn-primary waves-effect">保存</button>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
    <div class="modal fade" id="MovieTicketEditModal" tabindex="-1" role="dialog" data-backdrop="static">
        <div class="modal-dialog" role="document">
            <div class="modal-content">
            </div>
        </div>
    </div>

    Js

    关于js代码的位置我也按照框架中的位置放在了view-resources,话说Js代码离View有点远~~~

    Index.Js中的主要代码

    (function () {
        $(function () {
    
            var _movieService = abp.services.app.movieTicket;
            var _$modal = $('#MovieTicketCreateModal');
            var _$form = _$modal.find('form[name="movieCreateForm"]');
    
            _$form.validate({
                rules: {
                    MovieName:
                    {
                        required:true
                    },
                    StartTime: "required",
                    EndtTime:
                    {
                        required: true
                    },
                    MovieActor: "required"
                    ,Money:"required"
                },
                messages: {
                    MovieName: {
                        required:"电影名称不能为空"
                    },
                    MovieActor: {
                        required: "演员名称不能为空"
                    },
                    StartTime: {
                        required: "开始时间不能为空"
                    },
                    EndTime: {
                        required: "结束时间不能为空"
                    },
                    Money: {
                        required: "票价不能为空"
                    }
                }
            });
    
            var dateOption = {
                locale: {
    
                    format: 'YYYY-MM-DD HH:mm:ss',
                    applyLabel: '确认',
                    cancelLabel: '取消'
    
                },
                singleDatePicker: true,
                startDate: moment().format("YYYY-MM-DD HH:mm:ss"),
                timePicker24Hour: true,
                timePicker: true,
                autoApply: true,
                autoUpdateInput: true
            };
            $('input[name=StartTime]').daterangepicker(dateOption);
            $('input[name=EndTime]').daterangepicker(dateOption);
            $('#RefreshButton').click(function () {
                refreshUserList();
            });
          
            $('.delete-movie').click(function () {
                var movieId = $(this).attr("data-movie-id");
                var movieName = $(this).attr("data-movie-name");
                abp.message.confirm(
                    "删除电影 '" + movieName + "'?",
                    function (isConfirmed) {
                        if (isConfirmed) {
                            _movieService.deleteMovie({
                                "id": movieId, "movieName": movieName,
                            }).done(function () {
                                refreshMovieList();
                            });
                        }
                    }
                );
            });
    
            $('.edit-movie').click(function (e) {
                var movieId = $(this).attr("data-movie-id");
    
                e.preventDefault();
                $.ajax({
                    url: abp.appPath + 'MovieTicket/EditMovieTicketModal?movieId=' + movieId,
                    type: 'POST',
                    contentType: 'application/html',
                    success: function (content) {
                        $('#MovieTicketEditModal div.modal-content').html(content);
                    },
                    error: function (e) { }
                });
            });
    
            _$form.find('button[type="submit"]').click(function (e) {
                e.preventDefault();
    
                if (!_$form.valid()) {
                    return;
                }
    
                var movie = _$form.serializeFormToObject(); 
                abp.ui.setBusy(_$modal);
                _movieService.createMovie(movie).done(function (response) {
                    if (response == "No") {
                        abp.message.error("创建失败");
                    }
                    else {
                        _$modal.modal('hide');
                        location.reload(true); 
                    }
                   
                }).always(function () {
                    abp.ui.clearBusy(_$modal);
                });
            });
    
            _$modal.on('shown.bs.modal', function () {
                _$modal.find('input:not([type=hidden]):first').focus();
            });
    
            function refreshMovieList() {
                location.reload(true); //reload page to see new user!
            }
    
            function deleteUser(userId, userName) {
               
            }
            var CONSTANT = {
                DATA_TABLES: {
                    DEFAULT_OPTION: { //DataTables初始化选项  
                        language: {
                            "sProcessing": "处理中...",
                            "sLengthMenu": "每页 _MENU_ 项",
                            "sZeroRecords": "没有匹配结果",
                            "sInfo": "当前显示第 _START_ 至 _END_ 项,共 _TOTAL_ 项。",
                            "sInfoEmpty": "当前显示第 0 至 0 项,共 0 项",
                            "sInfoFiltered": "(由 _MAX_ 项结果过滤)",
                            "sInfoPostFix": "",
                            "sSearch": "搜索:",
                            "sUrl": "",
                            "sEmptyTable": "表中数据为空",
                            "sLoadingRecords": "载入中...",
                            "sInfoThousands": ",",
                            "oPaginate": {
                                "sFirst": "首页",
                                "sPrevious": "上页",
                                "sNext": "下页",
                                "sLast": "末页",
                                "sJump": "跳转"
                            },
                            "oAria": {
                                "sSortAscending": ": 以升序排列此列",
                                "sSortDescending": ": 以降序排列此列"
                            }
                        },
                        autoWidth: false,   //禁用自动调整列宽  
                        stripeClasses: ["odd", "even"],//为奇偶行加上样式,兼容不支持CSS伪类的场合  
                        order: [],          //取消默认排序查询,否则复选框一列会出现小箭头  
                        processing: false,  //隐藏加载提示,自行处理  
                        serverSide: true,   //启用服务器端分页  
                        searching: false    //禁用原生搜索  
                    },
                    COLUMN: {
                        CHECKBOX: { //复选框单元格  
                            className: "td-checkbox",
                            orderable: false,
                             "30px",
                            data: null,
                            render: function (data, type, row, meta) {
                                return '<input type="checkbox" class="iCheck">';
                            }
                        }
                    },
                    RENDER: {   //常用render可以抽取出来,如日期时间、头像等  
                        ELLIPSIS: function (data, type, row, meta) {
                            data = data || "";
                            return '<span title="' + data + '">' + data + '</span>';
                        }
                    }
                }
            };  
            var getQueryCondition=function(data) {  
                var param = {};  
                //组装排序参数  
                if(data.order&&data.order.length && data.order[0]) {
                //组装分页参数  
                param.start = data.start;
                param.length = data.length;  
                param.draw = data.draw;  
                return param;  
            }
            var page = {
                $table: $("#MovieTable"),
                $dataTable: null,
            
                initDataPicker: function () {
                    var dataOption = {
                        startDate: moment().startOf("month"),
                        "maxDate": null,
                        singleDatePicker: true
                    };
                    var dataOption1 = {
                        startDate: moment().endOf("month"),
                        "maxDate": null,
                        singleDatePicker: true
                    };
                    $("#StartTime").WIMIDaterangepicker(dataOption);
                    $("#EndTime").WIMIDaterangepicker(dataOption1);
                },
                initTable: function () {
                    if (!$.fn.DataTable.isDataTable("#MovieTable")) {
                        page.$datatable = page.$table.DataTable($.extend(true, {}, CONSTANT.DATA_TABLES.DEFAULT_OPTION, {
                            ajax: function (data, callback, settings) {
                                //封装请求参数  
                                var param = getQueryCondition(data);
    
                                $.ajax({
                                    type: "GET",
                                    url: "/api/services/app/" + "movieTicket/getAllMovieTicketPage",
                                    cache: false,  //禁用缓存  
                                    data: param,    //传入已封装的参数  
                                    dataType: "json",
                                    success: function (response) {
                                        //封装返回数据  
                                        var returnData = {};
                                        returnData.draw = response.result.draw;//这里直接自行返回了draw计数器,应该由后台返回  
                                        returnData.recordsTotal = response.result.recordsFiltered;//总记录数  
                                        returnData.recordsFiltered = response.result.recordsFiltered;//后台不实现过滤功能,每次查询均视作全部结果  
                                        returnData.data = response.result.items;
                                        //调用DataTables提供的callback方法,代表数据已封装完成并传回DataTables进行渲染  
                                        //此时的数据需确保正确无误,异常判断应在执行此回调前自行处理完毕  
                                        callback(returnData);
                                    },
                                    error: function (XMLHttpRequest, textStatus, errorThrown) {
                                        alert("查询失败");
                                    }
                                });
                            },
                            "paging": true,
                            //绑定数据  
                            "columns": [
                                {
                                    "defaultContent": "",
                                    "title": "操作",
                                    "orderable": false,
                                    "width": "150px",
                                    "className": "text-center not-mobile",
                                    "createdCell": function (td, cellData, rowData, row, col) {
                                        var $actionContent = $("<div class='action-content'>");
                                        $('<button class="btn  btn-xs">修改</button>')
                                            .appendTo($actionContent)
                                            .click(function () {
                                                alert(rowData.startTime);
                                                console.log(rowData);
                                            });
                                    $('<button class="btn  btn-xs"> 删除 </button>')
                                            .appendTo($actionContent)
                                        .click(function () {
                                            alert(rowData.id);
                                        });
                                        $(td).append($actionContent);
                                   }
                                },
                                {
                                    "data": "movieName",
                                    "title": "电影名称"
                                },
                                {
                                    "data": "movieActor",
                                    "title": "演员名称",
                                    "width": "120px",
                                },
                                {
                                    "data": "startTime",
                                    "title": "开始时间",
                                    "render": function (data, type, full, meta) {
                                        return moment(data).format("YYYY-MM-DD HH:mm:ss");
                                    }
                                },
                                {
                                    "data": "endTime",
                                    "title": "结束时间",
                                    "render": function (data, type, full, meta) {
                                        return moment(data).format("YYYY-MM-DD HH:mm:ss");
                                    }

                                },
                                {
                                    "data": "money",
                                    "title": "票价",
                                }
                            ], }));
    //此处需调用api()方法,否则返回的是JQuery对象而不是DataTables的API对象 } else { page.$datatable.ajax.reload(); } }, init: function () { page.initTable(); } } page.init(); }); })();

     在这里需要感谢https://blog.csdn.net/u011072139/article/details/54312414?locationnum=10&fps=1,从这篇博客类借鉴了一些Jscript代码

    注意的问题:

    (1)在使用DataTables的时候,经常出现一个错误,错误的提示:没有“length”,其实出现这个错误的原因是没有为Data赋值,DataTables需要返回Data,然后它会自动计算length,所以只要将Data赋值并返回即可。

    (2)returnData.data = response.result.items;从这行代码中可以看出,我们返回的数据并不是一个简单的对象,不能直接访问我们在后台传出的属性,多封装了一层。

    上面的代码中只是实现了分页的功能,关于删除和修改并没有重新实现。需要注意的是在DataTables尽心后台访问的时候的请求路径url,url: "/api/services/app/" + "movieTicket/getAllMovieTicketPage",这的路径并不是具体的控制器中的方法,而是直接访问的Application层的服务方法,这里就涉及了ABP中动态的Js代理,还一个需要重点关注的是type必须是Get类型,否则是无法找到访问路径的,这是因为在ABP中动态Js代理默认的请求方式是get。关于动态的Js代理在ABP中的应用这个将会在后面的随笔去研究。

    后台主要的代码

      public async Task<PagedResultDto<MovieTicketDto>> GetAllMovieTicketPage(MovieInputDto input)
            {
                var query = movieTicketRepository.GetAll() ;
                var totalCount =await query.CountAsync();
                var models =await query.OrderBy(input.Sorting).AsNoTracking().PageBy(input).ToListAsync();
                if (models.Count()==0)
                {
                    return new DataTablesPagedOutputDto<MovieTicketDto>(0, new List<MovieTicketDto>());
                }
                var items = models.MapTo<List<MovieTicketDto>>();
                return new DataTablesPagedOutputDto<MovieTicketDto>(totalCount,items); 
            }

    到此为止,基本的分页功能已经实现,下面看一下运行的效果吧

    请求的数据:

    返回的数据:

  • 相关阅读:
    android模拟器速度问题
    input.nextLine() 问题出错!
    android中的“visible ”、“invisible”、“gone”的区别(转载)
    为什么匿名内部类参数必须为final类型(转载)
    转载------------------关于android的一些技巧
    关于数据库的数据类型
    关于几个新的快捷键
    目标

    巨大bug
  • 原文地址:https://www.cnblogs.com/XZhao/p/8647535.html
Copyright © 2020-2023  润新知