1 重构Web.Areas.Admin.Models.Customers.CustomerSearchModel
using Framework.Models;
using Microsoft.AspNetCore.Mvc.Rendering;
using System.ComponentModel.DataAnnotations;
namespace Web.Areas.Admin.Models.Customers
{
/// <summary>
/// 【用户过滤筛选模型--纪录】
/// <remarks>
/// 摘要:
/// 为用户表的过滤筛选操作提供数据支撑后,把这些数据对JQuery DataTables插件表格进行渲染显示。
/// </remarks>
/// </summary>
public record CustomerSearchModel : BaseSearchModel
{
#region 拷贝构造方法
/// <summary>
/// 【拷贝构造方法】
/// <remarks>
/// 摘要:
/// 实例化该纪录时,为列表接口类型的属性成员实例分配内存空间。
/// </remarks>
/// </summary
public CustomerSearchModel()
{
DeletedSelectList = new List<SelectListItem> {
new SelectListItem { Value = null, Text = "全部" },
new SelectListItem { Value = "False", Text = "可用" },
new SelectListItem { Value = "True", Text = "已被删除" },
};
}
#endregion
#region 属性
/// <summary>
/// 【用户名】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定的用户名(账户、昵称)。
/// </remarks>
/// </summary>
[Display(Name = "用户名")]
public string Username { get; set; }
/// <summary>
/// 【电子邮箱】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定用户所对应的电子邮箱。
/// </remarks>
/// </summary>
[Display(Name = "邮箱")]
[EmailAddress(ErrorMessage = "输入的电子邮件格式错误。")]
public string Email { get; set; }
/// <summary>
/// 【手机号】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定用户所对应的手机号。
/// </remarks>
/// </summary>
[Display(Name = "手机号")]
[RegularExpression(@"^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\d{8}$", ErrorMessage = "输入的手机号格式错误。")]
public string Phone { get; set; }
/// <summary>
/// 【逻辑删除?】
/// <remarks>
/// 摘要:
/// 获取/设置1个值false(可用)/true(已经被逻辑删除),该值指示用户实体的1个指定实例是否已经处于逻辑删除状态。
/// </remarks>
/// </summary>
[Display(Name = "删除")]
public bool? Deleted { get; set; }
/// <summary>
/// 【班级选择列表实例】
/// <remarks>
/// 摘要:
/// 获取/设置把班级实体的所有下拉列表实例,存储到列表接口实例中。
/// </remarks>
/// </summary>
public IList<SelectListItem> DeletedSelectList { get; set; }
/// <summary>
/// 【注册时间从】
/// <remarks>
/// 摘要:
/// 获取/设置用户实体1个指定实例第1次被持久化到用户表中的时间。
/// </remarks>
/// </summary>
[Display(Name = "注册日期从")]
public DateTime? CreatedDateFrom { get; set; }
/// <summary>
/// 【注册时间到】
/// <remarks>
/// 摘要:
/// 获取/设置用户实体1个指定实例第1次被持久化到用户表中的时间。
/// </remarks>
/// </summary>
[Display(Name = "注册日期到")]
public DateTime? CreatedDateTo { get; set; }
#endregion
}
}
2 Web.Areas.Admin.Factories.CustomerModelFactory.GetAbsoluteAvatarUrl
/// <param name="virtualAvatarUrl">1个指定头像图片网络格式的相对路径。</param>
/// <summary>
/// 【获取头像图片网络格式的绝对路径】
/// <remarks>
/// 摘要:
/// 获取1个指定头像图片网络格式的绝对路径。
/// </remarks>
/// <returns>
/// 1个指定头像图片网络格式的绝对路径。
/// </returns>
/// </summary>
private string GetAbsoluteAvatarUrl(string virtualAvatarUrl)
{
string absoluteAvatarUrl = string.Empty;
if (string.IsNullOrEmpty(virtualAvatarUrl))
{
string _path = _nopFileProvider.Combine(_nopFileProvider.WebRootPath, @"\images\Avatar\Default.jpg");
if (_nopFileProvider.FileExists(_path))
{
string _absoluteAvatarUrl = _nopFileProvider.GetVirtualPath(@"\images\Avatar\Default.jpg");
//去URL格式路径字符中的第一个字符:“~/”。
_absoluteAvatarUrl = _absoluteAvatarUrl.Replace("~/", string.Empty);
absoluteAvatarUrl = _webHelper.GetStoreHost() + _absoluteAvatarUrl;
}
}
else
{
absoluteAvatarUrl = _webHelper.GetStoreHost().TrimEnd('/') + virtualAvatarUrl;
}
return absoluteAvatarUrl;
}
3 重构Web.Areas.Admin.Factories.CustomerModelFactory.PrepareCustomerListModelAsync
/// <param name="searchModel">用户过滤筛选模型纪录的1个指定实例。</param>
/// <summary>
/// 【异步预处理用户分页列表模型纪录】
/// <remarks>
/// 摘要:
/// 获取用户分页列表模型纪录的1个指定实例,为Jquery DataTable插件当前页渲染显示提供基本且必须的数据支撑。
/// </remarks>
/// <returns>
/// 用户分页列表模型纪录的1个指定实例。
/// </returns>
/// </summary>
public virtual async Task<CustomerListModel> PrepareCustomerListModelAsync(CustomerSearchModel searchModel)
{
var createdTo = !searchModel.CreatedDateTo.HasValue ? null
: searchModel.CreatedDateTo?.AddDays(1);
//根据前端传递的用户过滤筛选模型纪录的1个指定实例,从用户表中获取(1逻辑页中的)相应行数的数据,并把这些数据存储到列表实例中。
var customers = await _customerService.GetAllCustomersAsync(
deleted: searchModel.Deleted,
email: searchModel.Email,
username: searchModel.Username,
phone: searchModel.Phone,
createdDateFrom: searchModel.CreatedDateFrom,
createdDateTo: createdTo,
orderByFiled: searchModel.OrderBy,
orderByType: searchModel.OrderDir.ToString(),
pageIndex: searchModel.Page - 1,
pageSize: searchModel.PageSize);
//实例化当前用户分页列表模型纪录(“物理页”),为Jquery DataTable插件当前页渲染显示提供基本且必须的数据支撑。
var model = new CustomerListModel().PrepareToGrid(searchModel, customers, () =>
{
return customers.Select(customer =>
{
var customerModel = customer.ToModel<CustomerModel>();
customerModel.Avatar = GetAbsoluteAvatarUrl(customerModel.Avatar);
return customerModel;
});
});
return model;
}
4 重构Web\Areas\Admin\Views\Customer\Index.cshtml
@model CustomerSearchModel
<div class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<div class="fs-3">用户列表</div>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-end">
<li class="breadcrumb-item">
@*resetRoleList:用于控制对弹出框的关闭和角色列表的刷新渲染*@
<button type="button" id="resetRoleList" class="btn btn-secondary me-3 d-none">
<i class="fa-solid fa-rotate-right"></i>
重置
</button>
<button type="button" onclick="OpenWindow('@(Url.Action("Create", "Role", new {btnId = "resetRoleList"}))', 500, 500, true); return false;" class="btn btn-success float-end">
<i class="fas fa-plus"></i>
添加
</button>
</li>
</ol>
</div>
</div>
</div>
</div>
<!-- Main content -->
<div class="content">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card card-primary card-outline">
<form id="customerSearchModelForm" asp-action="Index" class="form-horizontal" method="post">
<div class="card-body">
<div class="row mb-3">
<div class=" col-sm-6">
<div class="form-group row mb-3">
<label asp-for="Username" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="Username" class="form-control" />
<span asp-validation-for="Username" class="text-danger"></span>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="form-group row mb-3">
<label asp-for="Email" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
</div>
</div>
</div>
<div class="row mb-3">
<div class=" col-sm-6">
<div class="form-group row mb-3">
<label asp-for="Phone" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="Phone" class="form-control" />
<span asp-validation-for="Phone" class="text-danger"></span>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="form-group row mb-3">
<label asp-for="Deleted" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<select asp-for="Deleted" class="form-select" asp-items="Model.DeletedSelectList"></select>
</div>
</div>
</div>
</div>
<div class="row mb-3">
<div class=" col-sm-6">
<div class="form-group row mb-3">
<label asp-for="CreatedDateFrom" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="CreatedDateFrom" class="form-control" />
<span asp-validation-for="CreatedDateFrom" class="text-danger"></span>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="form-group row mb-3">
<label asp-for="CreatedDateTo" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="CreatedDateTo" class="form-control" />
<span asp-validation-for="CreatedDateTo" class="text-danger"></span>
</div>
</div>
</div>
</div>
</div>
<div class="card-footer text-center">
<button type="submit" class="btn btn-primary btn-search">
<i class="fas fa-search"></i>
查询
</button>
<button type="submit" class="btn btn-default float-right">Cancel</button>
</div>
</form>
</div>
<div class="card">
<div class="card-body">
<table id="example" class="table table-striped table-bordered nowrap projects" width="100%" cellspacing="0">
</table>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
</div>
<!-- /.row -->
</div><!-- /.container-fluid -->
</div>
@section Scripts {
@{
await Html.RenderPartialAsync("_ValidationScriptsPartial");
}
<script type="text/javascript">
var tableModel;
$(document).ready(function () {
kendo.culture("zh-CN");//对所有的“kendo”控件时进行全局性汉化。
$("#CreatedDateFrom").kendoDatePicker({
format: "yyyy-MM-dd", //设定显示在input标签的日期格式
max: new Date(Date.now()),设定最大日期为当前日期。
});
$("#CreatedDateTo").kendoDatePicker({
format: "yyyy-MM-dd", //设定显示在input标签的日期格式
max: new Date(Date.now()),设定最大日期为当前日期。
});
tableModel = $("#example").DataTable({
language: {
url: '../../lib/DataTables/DataTables-1.13.4/language/zh.json' //DataTables语言配置选项
},
processing: true, //是否显示“处理中...”(排序的时候,数据很多耗费时间长的话,也会显示这个)
serverSide: true, //是否开启服务器模式
paging: true, //是否允许翻页
lengthMenu: [10, 20, 50, 100], //Jquery DataTable插件下拉框控件中的数量选择内容。
pageLength: 10, // 每页的行数的默认值:10。
searching: false, //是否在Jquery DataTable插件中显示搜索框控件。
//autoWidth: true, //自适应宽度
bSort: true, //显示排序按钮
pagingType: "numbers",//使用该类型,为在“表中数据为空”时,禁止在Jquery DataTable插件中,显示“上页”、“下页”按钮。
//当处理大数据时,延迟渲染数据,有效提高Datatables处理能力
deferRender: true,
order: [[8, "desc"]],//默认设置第8列以“倒序”方式对Jquery DataTable插件进行排序显示。
ajax: {
url: "/Admin/Customer/CustomerList",
type: "POST",
datatype: "JSON",
cache: false, //禁用缓存
data: function (customerSearchModel) { //传递参数
customerSearchModel.Username = $("#Username").val();
customerSearchModel.Email = $("#Email").val();
customerSearchModel.Phone = $("#Phone").val();
customerSearchModel.Deleted = $("#Deleted").val();
customerSearchModel.CreatedDateFrom = $("#CreatedDateFrom").val();
customerSearchModel.CreatedDateTo = $("#CreatedDateTo").val();
},
},
columnDefs: [{ //设置列定义初始化属性
defaultContent: "", //为列设置默认的静态内容
targets: "_all" //指定所有列
}],
//注意:"data"对应的字段(实体属性)的第1个字母必是小写,否则实体实例的值将不会被浏览器渲染出来。
columns: [
{
data: "id", "title": "<input type='checkbox' id='checkAll' class='form-check-input checkBox20' />",
width: "25px",
orderable: false,//禁止当前列渲染显示排序按钮。
render: function (data, type, row, meta) {
return "<input type='checkbox' name='checkItem' value=" + data + " class='form-check-input checkBox20' />";
},
},
{ data: "id", title: "编号", width: "100px" },
{ data: "username", title: "用户名", autoWidth: true },
{
data: null,
title: "头像",
width: "65px",
orderable: false,//禁止当前列渲染显示排序按钮。
render: function (data, type, full, meta) {
return "<img src='" + data.avatar + "'class='img-size-64'/>";
},
},
{ data: "email", title: "邮箱", autoWidth: true },
{ data: "phone", title: "手机", width: "100px", orderable: false },
{
data: "active",
title: "可用",
width: "50px",
orderable: false,
//禁止当前列渲染显示排序按钮。
render: function (data, type, row, full, meta) {
//console.log(data);
if (data) {
return "<span class='badge bg-success'>激活</span>";
}
else {
return "<span class='badge bg-danger'>禁用</span>";
}
},
},
{
data: "deleted",
title: "删除",
width: "50px",
orderable: false,//禁止当前列渲染显示排序按钮。
render: function (data, type, row, full, meta) {
//console.log(data);
if (data) {
return "<span class='badge bg-danger'>已经删除</span>";
}
else {
return "<span class='badge bg-success' style='letter-spacing: 8px; text-indent: 8px; '>可用</span>";
}
},
},
{
data: "createdDate",
title: "创建日期",
width: "100px",
render: function (data, type, full, meta) {
//console.log(data);
//console.log(type);
//console.log(full);
//console.log(meta);
//时间格式化
return moment(data).format("YYYY-MM-DD HH:mm:ss");
}
},
{
data: null,
width: "100px",
orderable: false,//禁止当前列渲染显示排序按钮。
render: function (data, type, row, full, meta) {
return "<a href='#' class='btn btn-sm btn-info me-3' >编辑</a>"
+ "<a href='#' class='btn btn-sm btn-danger' οnclick=deleteData('" + data.id + "'); >删除</a>";
},
},
],
});
});
//查询表单必须使用该事件来执行筛选操作,否则查询表单中的输入控件将被清空。
$("#customerSearchModelForm").on("submit", function (e) {
//该方法将通知Web浏览器不要执行与事件关联的默认动作(如果存在这样的动作),
// 例如:例如,当你点击一个超链接时,浏览器会自动跳转到超链接指向的页面,这就是浏览器的默认行为。
// 如果你希望在点击超链接时不跳转,可以在超链接的事件处理函数中使用e.preventDefault()来阻止浏览器的默认行为。
e.preventDefault();
tableModel.ajax.reload();
});
</script>
}
对以上功能更为具体实现和注释见230601_019ShopRazor(查询表单与JQuery DataTables级联渲染显示)。