1 分页操作
///<typeparam name="T">泛型类型实例(1个指定类的类型实例)。</typeparam>
/// <summary>
/// 【分页列表--接口】
/// <remarks>
/// 摘要:
/// 继承于该接口的具体实现类及其属性成员实例,为1个指定类的数据源执行分页操作提供数据支撑。
/// </remarks>
public interface IPagedList<T> : IList<T>
{
#region 属性
/// <summary>
/// 【页数索引】
/// <remarks>
/// 摘要:
/// 获取分页操作中当前页的页数值。
/// </remarks>
/// </summary>
int PageIndex { get; }
/// <summary>
/// 【页数大小】
/// <remarks>
/// 摘要:
/// 获取分页操作中每页最多显示实例的项(行)数值。
/// </remarks>
/// </summary>
int PageSize { get; }
/// <summary>
/// 【项数总计】
/// <remarks>
/// 摘要:
/// 获取分页操作中数据源实例的项(行)数总计值。
/// </remarks>
/// </summary>
int TotalCount { get; }
/// <summary>
/// 【页数总计】
/// <remarks>
/// 摘要:
/// 获取分页操作中页数总计值。
/// </remarks>
/// </summary>
int TotalPages { get; }
/// <summary>
/// 【有上1页?】
/// <remarks>
/// 摘要:
/// 获取1个值false(没有)/true(有),该值指示在分页操作中当前页是否有上1页(第1页之前没有上1页)。
/// </remarks>
/// </summary>
bool HasPreviousPage { get; }
/// <summary>
/// 【有下1页?】
/// <remarks>
/// 摘要:
/// 获取1个值false(没有)/true(有),该值指示在分页操作中当前页是否有下1页(最后1页之后没有下1页)。
/// </remarks>
/// </summary>
bool HasNextPage { get; }
#endregion
}
///<typeparam name="T">泛型类型实例(1个指定类的类型实例)。</typeparam>
/// <summary>
/// 【分页列表--类】
/// <remarks>
/// 摘要:
/// 通过该类及其属性成员实例,为1个指定类的数据源执行分页操作提供数据支撑。
/// </remarks>
[Serializable]
public class PagedList<T> : List<T>, IPagedList<T>
{
#region 拷贝构造方法
/// <param name="source">以列表接口实例进行存储的1个指定类型的数据源(枚举数接口实例=长串型,包含:数组、列表、字典等)。</param>
/// <param name="pageIndex">分页操作中当前页的页数值。</param>
/// <param name="pageSize">分页操作中每页最多显示实例的项(行)数值。 </param>
/// <param name="totalCount">分页操作中实例的项(行)数总计值。</param>
/// <summary>
/// 【拷贝构造方法】
/// <remarks>
/// 摘要:
/// 通过拷贝构造方法为该类中的属性成员赋值实例化初始值。
/// </remarks>
/// </summary
public PagedList(IList<T> source, int pageIndex, int pageSize, int? totalCount = null)
{
//分页操作中每页最多显示实例的项(行)数值,“Math.Max”方法保证该参数实例的最小值必须>=1。
pageSize = Math.Max(pageSize, 1);
TotalCount = totalCount ?? source.Count;
TotalPages = TotalCount / pageSize;
if (TotalCount % pageSize > 0)
TotalPages++;
PageSize = pageSize;
PageIndex = pageIndex;
//该方法主要功能是只从数据源中加载1页数据,以最的小内存开销,为页面的渲染显示提供相应数据支撑,在当前类中基本没有起到应用的作用;真正起到应有作用的是定义在“PagedListExtension(AsyncIQueryableExtensions).ToPagedList”方法中的该方法。
AddRange(totalCount != null ? source : source.Skip(pageIndex * pageSize).Take(pageSize));
}
#endregion
#region 属性
/// <summary>
/// 【页数索引】
/// <remarks>
/// 摘要:
/// 获取分页操作中当前页的页数值。
/// </remarks>
/// </summary>
public int PageIndex { get; }
/// <summary>
/// 【页数大小】
/// <remarks>
/// 摘要:
/// 获取分页操作中每页最多显示实例的项(行)数值。
/// </remarks>
/// </summary>
public int PageSize { get; }
/// <summary>
/// 【项数总计】
/// <remarks>
/// 摘要:
/// 获取分页操作中数据源实例的项(行)数总计值。
/// </remarks>
/// </summary>
public int TotalCount { get; }
/// <summary>
/// 【页数总计】
/// <remarks>
/// 摘要:
/// 获取分页操作中页数总计值。
/// </remarks>
/// </summary>
public int TotalPages { get; }
/// <summary>
/// 【有上1页?】
/// <remarks>
/// 摘要:
/// 获取1个值false(没有)/true(有),该值指示在分页操作中当前页是否有上1页(第1页之前没有上1页)。
/// </remarks>
/// </summary>
public bool HasPreviousPage => PageIndex > 0;
/// <summary>
/// 【有下1页?】
/// <remarks>
/// 摘要:
/// 获取1个值false(没有)/true(有),该值指示在分页操作中当前页是否有下1页(最后1页之后没有下1页)。
/// </remarks>
/// </summary>
public bool HasNextPage => PageIndex + 1 < TotalPages;
#endregion
}
public static class PagedListExtension
{
/// <param name="source">以可查询类型接口实例进行存储的1个指定类型的数据源(枚举数接口实例=长串型,包含:数组、列表、字典等)。</param>
/// <param name="pageIndex">分页操作中当前页的页数值。</param>
/// <param name="pageSize">分页操作中每页最多显示实例的项(行)数值。 </param>
/// <param name="getOnlyTotalCount">指示是否只从数据源中加载1页数据,以最的小内存开销,为页面的渲染显示提供相应数据支撑,默认值:false,即只从数据源中加载1页数据</param>
/// <summary>
/// 【到分页列表】
/// <remarks>
/// 摘要:
/// 该方法默认只从数据源中加载1页数据,存储到“PagedList”类的实例中,从而实现“PagedList”类实例以最的小内存开销,为页面的渲染显示提供相应数据支撑。
/// </remarks>
/// </summary
public static IPagedList<T> ToPagedList<T>(this IQueryable<T> source, int pageIndex, int pageSize, bool getOnlyTotalCount = false)
{
if (source == null)
return new PagedList<T>(new List<T>(), pageIndex, pageSize);
//分页操作中每页最多显示实例的项(行)数值,“Math.Max”方法保证该参数实例的最小值必须>=1。
pageSize = Math.Max(pageSize, 1);
var count = source.Count();
var data = new List<T>();
//通过参数实例的默认值,“AddRange”方法主要功能是只从数据源中加载1页数据,以最的小内存开销,为页面的渲染显示提供相应数据支撑,在当方法中“AddRange”方法起到了自己应用的作用。
if (!getOnlyTotalCount)
data.AddRange(source.Skip(pageIndex * pageSize).Take(pageSize).ToList());
return new PagedList<T>(data, pageIndex, pageSize, count);
}
}
2 模型定义
/// <summary>
/// 【基本查询模型--纪录】
/// <remarks>
/// 摘要:
/// 通过该类及其属性成员实例实现当前程序与前台Jquery DataTabes插件的数据交互操作及其控件和渲染(显示)。
/// 注意:
/// 该纪录是抽象纪录,所以该纪录只能被其它纪录所继承,并由继承纪录所实例化。
/// </remarks>
/// </summary>
public abstract record BaseSearchModel
{
#region 拷贝构造方法
/// <summary>
/// 【拷贝构造方法】
/// <remarks>
/// 摘要:
/// 通过拷贝构造方法为该类中的属性成员赋值实例化初始值。
/// </remarks>
/// </summary
protected BaseSearchModel()
{
//设置Jquery DataTable插件每页最多的行数(默认值):15,Length>=1。
Length = 10;
}
#endregion
#region 属性
/// <summary>
/// 【页数】
/// <remarks>
/// 摘要:
/// 获取query DataTable插件当前显示页面的页数值(Jquery DataTable插件默认开始页为:0;而该属性实例则把该默认开始页设置为:1,如果使用默认Jquery DataTable插件,则插件最后页会现“没有匹配结果”异常,即Jquery DataTable插件会多出1页)。
/// </remarks>
/// </summary>
public int Page => (Start / Length) + 1;
/// <summary>
/// 【页面大小】
/// <remarks>
/// 摘要:
/// 获取Jquery DataTable插件每页最多的行数值:(默认值)15,PageSize>=1。
/// 注意:
/// 该属性为只读属性,该属性的实例值由该类中“Length”属性成员所决定。
/// </remarks>
/// </summary>
public int PageSize => Length;
/// <summary>
/// 【有效页面大小】
/// <remarks>
/// 摘要:
/// 获取/设置Jquery DataTable插件以供选择的每页最多显示的行数值,PageSize或Length:(弹出窗口默认值)7, (默认值)15, 20, 50, 100。
/// </remarks>
/// </summary>
public string AvailablePageSizes { get; set; }
/// <summary>
/// 【绘制】
/// <remarks>
/// 摘要:
/// 获取/设置的Jquery DataTable插件被操作的次数值。
/// </remarks>
/// </summary>
public string Draw { get; set; }
/// <summary>
/// 【开始】
/// <remarks>
/// 摘要:
/// 获取/设置Jquery DataTable插件当前显示页面,需要跳过的行数值:=(页数-1)*PageSize(或Length:(弹出窗口默认值)7, (默认值)15, 20, 50, 100),Start>=0。
/// </remarks>
/// </summary>
public int Start { get; set; }
/// <summary>
/// 【长度】
/// <remarks>
/// 摘要:
/// 获取/设置Jquery DataTable插件每页最多的行数值:(默认值)15,Length>=1。
/// </remarks>
/// </summary>
public int Length { get; set; }
#endregion
#region 方法
/// <summary>
/// 【设置网格页大小】
/// <remarks>
/// 摘要:
/// 通过以供选择的每页最多显示的行数值,动态设置Jquery DataTable插件每页最多显示行数。
/// </remarks>
/// </summary>
public void SetGridPageSize()
{
SetGridPageSize(15, "7, 15, 20, 50, 100");
}
/// <summary>
/// 【设置弹出网格页大小】
/// <remarks>
/// 摘要:
/// 通过以供选择的每页最多显示的行数值,动态设置Jquery DataTable插件每页最多显示行数。
/// </remarks>
/// </summary>
public void SetPopupGridPageSize()
{
SetGridPageSize(7, "7, 15, 20, 50, 100");
}
///<param name="pageSize">Jquery DataTable插件每页最多显示行数值。</param>
///<param name="availablePageSizes">A以供选择的每页最多显示的行数值。</param>
/// <summary>
/// 【设置网格页大小】
/// <remarks>
/// 摘要:
/// 通过以供选择的每页最多显示的行数值,动态设置Jquery DataTable插件每页最多显示行数。
/// </remarks>
/// </summary>
public void SetGridPageSize(int pageSize, string availablePageSizes = null)
{
if (availablePageSizes == null)
availablePageSizes = "7, 15, 20, 50, 100";
Start = 0;
Length = pageSize;
AvailablePageSizes = availablePageSizes;
}
#endregion
}
public record StudentSearchModel : BaseSearchModel
{
#region 属性
/// <summary>
/// 【姓名】
/// <remarks>
/// 摘要:
/// 获取/设置查询模型类1个指定实例的学生姓名。
/// </remarks>
/// </summary>
[Display(Name = "姓名")]
public string StudnetName { get; set; }
#endregion
}
///<typeparam name="T">泛型类型实例(1个指定类的类型实例)。</typeparam>
/// <summary>
/// 【基本分页列表--纪录】
/// <remarks>
/// 摘要:
/// 该类及其属性成员实例,为Jquery DataTable插件渲染显示提供基本且必须的数据支撑。
/// 注意:
/// 该纪录是抽象纪录,所以该纪录只能被其它纪录所继承,并由继承纪录所实例化。
/// </remarks>
public abstract record BasePagedListModel<T>
{
#region 属性--Jquery DataTable插件
/// <summary>
/// 【数据】
/// <remarks>
/// 摘要:
/// 获取/设置以枚举数接口进行存储的学生实体的所有实例。
/// 注意:
/// 为了与Jquery DataTable插件能够在浏览器中渲染出这些数据,该属性必须被命名为“Data”,也只能被命名为“Data”,否则浏览器中将不会渲染出这些数据。
/// </remarks>
/// </summary>
public IEnumerable<T> Data { get; set; }
/// <summary>
/// 【绘制】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定的Jquery DataTable插件被操作的次数值。
/// 注意:
/// C#中的属性命名通过使用“骆驼命名方式”,而JSON编码格式中的键(key)命名的第1个字母必须是小写,否则浏览器中将不会通过Jquery DataTable插件渲染出这些数据。
/// 由于上述原因就必须使用“[JsonProperty(PropertyName = "xxx")]”标记把C#中的属性名序列化为符合JSON编码格式中的键(key)命名。
/// </remarks>
/// </summary>
[JsonProperty(PropertyName = "draw")]
public string Draw { get; set; }
/// <summary>
/// 【过滤总计】
/// <remarks>
/// 摘要:
/// 获取/设置符合指定过滤条件的1个指定实体所有实例的总计值。
/// 注意:
/// C#中的属性命名通过使用“骆驼命名方式”,而JSON编码格式中的键(key)命名的第1个字母必须是小写,否则浏览器中将不会通过Jquery DataTable插件渲染出这些数据。
/// 由于上述原因就必须使用“[JsonProperty(PropertyName = "xxx")]”标记把C#中的属性名序列化为符合JSON编码格式中的键(key)命名。
/// </remarks>
/// </summary>
[JsonProperty(PropertyName = "recordsFiltered")]
public int RecordsFiltered { get; set; }
/// <summary>
/// 【总计】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定实体所有实例的总计值。
/// 注意:
/// C#中的属性命名通过使用“骆驼命名方式”,而JSON编码格式中的键(key)命名的第1个字母必须是小写,否则浏览器中将不会通过Jquery DataTable插件渲染出这些数据。
/// 由于上述原因就必须使用“[JsonProperty(PropertyName = "xxx")]”标记把C#中的属性名序列化为符合JSON编码格式中的键(key)命名。
/// </remarks>
/// </summary>
[JsonProperty(PropertyName = "recordsTotal")]
public int RecordsTotal { get; set; }
#endregion
}
/// <summary>
/// 【学生分页列表--纪录】
/// <remarks>
/// 摘要:
/// 该类及其属性成员实例,加载学生实体的所有实例,为Jquery DataTable插件渲染显示提供必须的数据支撑。
/// </remarks>
public record StudentPagedListModel : BasePagedListModel<Student>
{
}
3 JqueryDataTable.cshtml
@model JsonTable.Nop.Models.StudentSearchModel
@{
ViewData["Title"] = "JqueryDataTable";
}
@section styles
{
<link rel="stylesheet" type="text/css" href="~/lib/dataTables-1.12.1/css/dataTables.bootstrap5.css" />
}
<div class="row">
<div class="col-md-12 mt-5">
<form asp-controller="Nop" asp-action="JqueryDataTable" method="post" class="row gx-3 gy-2 align-items-center" id="JqueryDataTableForm">
<div asp-validation-summary="ModelOnly"></div>
<div class="col-sm-3">
<label asp-for="StudnetName" class="visually-hidden"></label>
<input asp-for="StudnetName" class="form-control" />
<span asp-validation-for="StudnetName" class="text-danger"></span>
</div>
<div class="col-sm-9">
<button type="button" id="searchStudent" class="btn btn-primary me-3">
<i class="fas fa-search"></i>
查询
</button>
</div>
</form>
</div>
</div>
<div class="row">
<div class="col-md-12 mt-5">
<table id="example" class="table table-striped table-bordered dt-responsive nowrap" width="100%" cellspacing="0">
</table>
</div>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script type="text/javascript" src="~/lib/dataTables-1.12.1/js/jquery.dataTables.js"></script>
<script type="text/javascript" src="~/lib/dataTables-1.12.1/js/dataTables.bootstrap5.js"></script>
<script type="text/javascript">
var tableModel;
$(document).ready(function () {
tableModel = $("#example").DataTable({
language: {
url: '../../lib/dataTables-1.12.1/language/zh.json' //DataTables语言配置选项
},
processing: true, //是否显示“处理中...”(排序的时候,数据很多耗费时间长的话,也会显示这个)
serverSide: true, //是否开启服务器模式
paging: true, //是否允许翻页
//"rowId": "userId", //指定用于行ID的属性名 默认是:DT_RowId
lengthMenu: [7, 15, 20, 50, 100], //DataTableJquery插件下拉框控件中的数量选择内容。
pageLength: 15, // 每页的行数的默认值:10。
searching: true, //是否在DataTableJquery插件中显示搜索框控件。
autoWidth: true, //自适应宽度
//bSort : false, //禁止排序
pagingType: "numbers",//使用该类型,为在“表中数据为空”时,禁止在DataTableJquery插件中,显示“上页”、“下页”按钮。
order: [[1, "desc"]],//默认设置第二列以“倒序”方式对DataTableJquery插件进行排序显示。
ajax: {
url: "/Nop/PrepareJquerySearch",
type: "POST",
datatype: "JSON",
cache: false, //禁用缓存
data: function (studentSearchModel) { //传递参数
studentSearchModel.studnetName = $("#StudnetName").val();
},
},
columnDefs: [{ //设置列定义初始化属性
"defaultContent": "", //为列设置默认的静态内容
"targets": "_all" //指定所有列
}],
//注意:"data"对应的字段(实体属性)的第1个字母必是小写,否则实体实例的值将不会被浏览器渲染出来。
"columns": [
{
"orderable": false,//禁用DataTableJquery插件第1列中的“排序”按钮。
"data": "id", "title": "<input type='checkbox' id='checkAll' class='form-check-input checkBox25' />",
"render": function (data, type, row, meta) {
return "<input type='checkbox' name='checkItem' value=" + data + " class='form-check-input checkBox25' />";
},
"sWidth": "25px",
},
{ "data": "id", "name": "id", "title": "编号", "autoWidth": true },//“name”属性及其对应值对DataTableJquery插件进行排序操作。
{ "data": "code", "name": "code", "title": "学号", "autoWidth": true },//“name”属性及其对应值对DataTableJquery插件进行排序操作。
{ "data": "studnetName", "name": "studnetName", "title": "姓名", "autoWidth": true },//“name”属性及其对应值对DataTableJquery插件进行排序操作。
{ "data": "specialty", "name": "specialty", "title": "专业", "autoWidth": true },//“name”属性及其对应值对DataTableJquery插件进行排序操作。
{ "data": "grade", "name": "grade", "title": "年级", "autoWidth": true },//“name”属性及其对应值对DataTableJquery插件进行排序操作。
{ "data": "category", "name": "category", "title": "班级", "autoWidth": true },//“name”属性及其对应值对DataTableJquery插件进行排序操作。
{
"orderable": false,//禁用DataTableJquery插件第8列中的“排序”按钮。
"render": function (data, type, full, meta) {
//注意:“,500,500,true”参数实例中的“,”之间不能存在空格,否则“编辑”按钮点击无效,即“编辑”弹出窗口将不会出现。
return "<a href='#' class='btn btn-info' >编辑</a>";
},
"sWidth": "30px",
},
{
"orderable": false,//禁用DataTableJquery插件第9列中的“排序”按钮。
data: null,
render: function (data, type, row) {
return "<a href='#' class='btn btn-danger'>删除</a>";
},
"sWidth": "30px",
},
],
});
// init:初始化和数据都加载完成,和 initComplete配置等价
tableModel.on("draw", function () {
$("#checkAll").prop("checked", false);//每次翻页;或跳转到上一页;或跳转到指定,DataTableJquery插件中已经处于全选状态的全选复选框控件,处于不选中状态。
//全选
$("#checkAll").click(function () {
$("[name=checkItem]:checkbox").prop("checked", this.checked);
});
//复选框组的联动效果
$("[name=checkItem]:checkbox").click(function () {
var flag = true;
$("[name=checkItem]:checkbox").each(function (index) {
if (!this.checked) {
flag = false;
}
});
$("#checkAll").prop("checked", flag);
});
});
});
//查询列表
$("#searchStudent").on("click", function () {
//重新加载DataTable插件,并返回第1页。
$("#example").DataTable().ajax.reload();
});
</script>
}
对以上功能更为具体实现和注释见:22-08-19-064_JsonTable(Nop后台重构定义Jquery DataTables)