本文中,让我们尝试构建自定义 HTML Hepler以在 .NET Core MVC 应用程序中提供分页。首先对不熟悉的人简单介绍一下,什么是HTML Helper(助手):
在Web应用程序中,如果要显示大量记录,则需要提供分页。在本文中,我们通过创建自定义 HTML Helper 在 .NET Core MVC 应用程序中实现分页。为了简单起见,我们只能使用数字来表示数据。
假设我们需要在多页中显示 55 条记录,每页有 10 个项目,如上所示。
打开 Visual Studio 2019 > 创建 .NET Core MVC 应用程序,如下所示。
项目命名为 HTMLHelpersApp。
选择 .NET 框架版本。
创建所需的模型和帮助文件。
在 Number.cs 中添加代码。该模型捕获用户输入。它只有一个属性:“InputNumber”。
using System;
using System.ComponentModel.DataAnnotations;
namespace HTMLHelpersApp.Models
{
public class Number
{
//validation for required, only numbers, allowed range-1 to 500
[Required(ErrorMessage = "Value is Required!. Please enter value between 1 and 500.")]
[RegularExpression(@"^\d+$", ErrorMessage = "Only numbers are allowed. Please enter value between 1 and 500.")]
[Range(1, 500, ErrorMessage = "Please enter value between 1 and 500.")]
public int InputNumber = 1;
}
}
现在让我们添加一个公共类 PageInfo.cs。创建新文件夹 Common 并添加 PageInfo.cs 类。
在 PageInfo.cs 中添加代码:
根据总项目数和每页项目数,计算页面的总页数、第一个项目和最后一个项目。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace HTMLHelpersApp.Common
{
public class PageInfo
{
public int TotalItems { get; set; }
public int ItemsPerPage { get; set; }
public int CurrentPage { get; set; }
public PageInfo()
{
CurrentPage = 1;
}
//starting item number in the page
public int PageStart
{
get { return ((CurrentPage - 1) * ItemsPerPage + 1); }
}
//last item number in the page
public int PageEnd
{
get
{
int currentTotal = (CurrentPage - 1) * ItemsPerPage + ItemsPerPage;
return (currentTotal < TotalItems ? currentTotal : TotalItems);
}
}
public int LastPage
{
get { return (int)Math.Ceiling((decimal)TotalItems / ItemsPerPage); }
}
}
}
现在我们来到最重要的部分:创建自定义 HTML 助手。
public static IHtmlContent PageLinks(this IHtmlHelper htmlHelper, PageInfo pageInfo, Func<int, string> PageUrl)
5.取2个参数
使用标签构建器创建锚标签。
TagBuilder tag = new TagBuilder("a");
Add attributes
tag.MergeAttribute("href", hrefValue);
tag.InnerHtml.Append(" "+ innerHtml + " ");
样式也可以用作属性。
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;
using System;
using System.Text;
namespace HTMLHelpersApp.Common
{
public static class PagingHtmlHelpers
{
public static IHtmlContent PageLinks(this IHtmlHelper htmlHelper, PageInfo pageInfo, Func<int, string> PageUrl)
{
StringBuilder pagingTags = new StringBuilder();
//Prev Page
if (pageInfo.CurrentPage > 1)
{
pagingTags.Append(GetTagString("Prev", PageUrl(pageInfo.CurrentPage - 1)));
}
//Page Numbers
for (int i = 1; i <= pageInfo.LastPage; i++)
{
pagingTags.Append(GetTagString(i.ToString(), PageUrl(i)));
}
//Next Page
if (pageInfo.CurrentPage < pageInfo.LastPage)
{
pagingTags.Append(GetTagString("Next", PageUrl(pageInfo.CurrentPage + 1)));
}
//paging tags
return new HtmlString(pagingTags.ToString());
}
private static string GetTagString(string innerHtml, string hrefValue)
{
TagBuilder tag = new TagBuilder("a"); // Construct an <a> tag
tag.MergeAttribute("class","anchorstyle");
tag.MergeAttribute("href", hrefValue);
tag.InnerHtml.Append(" "+ innerHtml + " ");
using (var sw = new System.IO.StringWriter())
{
tag.WriteTo(sw, System.Text.Encodings.Web.HtmlEncoder.Default);
return sw.ToString();
}
}
}
}
在“Models”文件夹中添加一个新类“ShowPaging.cs”。
using HTMLHelpersApp.Common;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace HTMLHelpersApp.Models
{
public class ShowPaging
{
//validation for required, only numbers, allowed range-1 to 500
[Required(ErrorMessage = "Value is Required!. Please enter value between 1 and 500.")]
[RegularExpression(@"^\d+$", ErrorMessage = "Only positive numbers are allowed. Please enter value between 1 and 500.")]
[Range(1, 500, ErrorMessage = "Please enter value between 1 and 500.")]
public int InputNumber { get; set; }
public List<string> DisplayResult { get; set; }
public PageInfo PageInfo;
}
}
添加一个新控制器:“HTMLHelperController”
右键单击控制器文件夹并在上下文菜单中选择控制器。
选择“MVCController-Empty”。
在“HTMLHelperController”中添加代码。
using HTMLHelpersApp.Common;
using HTMLHelpersApp.Models;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
namespace HTMLHelpersApp.Controllers
{
public class HTMLHelperController : Controller
{
private const int PAGE_SIZE = 10;
public IActionResult Number()
{
return View();
}
public IActionResult ShowPaging(ShowPaging model, int page = 1, int inputNumber = 1)
{
if (ModelState.IsValid)
{
var displayResult = new List<string>();
string message;
//set model.pageinfo
model.PageInfo = new PageInfo();
model.PageInfo.CurrentPage = page;
model.PageInfo.ItemsPerPage = PAGE_SIZE;
model.PageInfo.TotalItems = inputNumber;
//Set model.displayresult - numbers list
for (int count = model.PageInfo.PageStart; count <= model.PageInfo.PageEnd; count++)
{
message = count.ToString();
displayResult.Add(message.Trim());
}
model.DisplayResult = displayResult;
}
//return view model
return View(model);
}
}
}
在 Views 文件夹中创建一个新文件夹“HTMLHelper”,并创建一个新视图“Number.cshtml”。
在“Number.cshtml”中添加代码。
@model HTMLHelpersApp.Models.Number
<h4>Number</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="ShowPaging" method="get">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<input asp-for="InputNumber" class="form-control"/>
</div>
<div class="form-group">
<input type="submit" value="Submit" class="btn btn-primary" />
</div>
</form>
</div>
</div>
同样,创建一个新视图“ShowPaging.cshtml”。
@model HTMLHelpersApp.Models.ShowPaging
@using HTMLHelpersApp.Common
<link rel="stylesheet" href ="~/css/anchorstyles.css"/>
<form>
<h4>Show Paging</h4>
<hr />
<div asp-validation-summary="All" class="text-danger"></div>
<dl class="row">
<dt class="col-sm-2">
<b>Number: </b> @Html.DisplayFor(model => model.InputNumber)
</dt>
<dd>
<a asp-action="Number">Change Number</a>
</dd>
</dl>
<div>
@if (Model != null && Model.DisplayResult != null)
{
<ul>
@foreach (var item in Model.DisplayResult)
{
<li>@Html.Raw(item)</li>
}
</ul>
<div>
@Html.PageLinks(@Model.PageInfo, x => Url.Action("ShowPaging",
new { page = x.ToString(), inputNumber = @Model.InputNumber }))
</div>
}
</div>
</form>
解决方案资源管理器如下所示:
在“startup.cs”中配置默认控制器和操作。
编译并运行应用程序,输入数字 35。
点击提交。
你会在底部看到分页,每页显示10个数字,一共4页,且每页都一个链接。
qPaginator是一个简洁、高度自定义的jQuery分页组件 !而且自带BootStrap的样式!当然你入股不喜欢BootStrap的样式,完全可以自定义!
GitHub地址: https://github.com/DangJin/jqPaginator!
好了我们接下来介绍下这么优美的东西该如何使用吧!
引入Js文件
这里需要说明的是因为内置BootStrap样式,所以需要引入BootStrap的Css样式哦!
配置参数
参数 | 默认值 | 说明 |
---|---|---|
totalPages | 0 | 设置分页的总页数 |
totalCounts | 0 | 设置分页的总条目数 |
pageSize | 0 | 设置每一页的条目数注意:要么设置totalPages,要么设置totalCounts + pageSize,否则报错;设置了totalCounts和pageSize后,会自动计算出totalPages。 |
currentPage | 1 | 设置当前的页码 |
visiblePages | 7 | 设置最多显示的页码数(例如有100也,当前第1页,则显示1 - 7页) |
disableClass | 'disabled' | 设置首页,上一页,下一页,末页的“禁用状态”样式 |
activeClass | 'active' | 设置当前页码样式 |
first | bootstrap风格 | 设置“首页”的Html结构 |
prev | bootstrap风格 | 设置“上一页”的Html结构 |
next | bootstrap风格 | 设置“下一页”的Html结构 |
last | bootstrap风格 | 设置“末页”的Html结构 |
page | bootstrap风格 | 设置页码的Html结构,其中可以使用{{page}}代表当前页,{{totalPages}}代表总页数,{{totalCounts}}代表总条目数(例如:上面的“极简风格”的Demo,就是使用了{{占位符}},并将visiblePages设为1实现的。)注意:first、prev、next、last。page只要设置一个,其余未设置的会变为空。 |
wrapper | (无) | 分页结构的Html包裹,例如:<div class="your class"></div>,一般不会用到 |
onPageChange | (无) | 回调函数,当换页时触发(包括初始化第一页的时候),会传入两个参数:1、“目标页"的页码,Number类型2、触发类型,可能的值:“init”(初始化),“change”(点击分页) |
如果不是自定义样式,用默认的记得一定要加这个属性 wrapper 如果不加,会出现这种奇葩的情况
BootStrap默认wrapper 是<nav></nav>
当报表展示的数据量比较多的时候,有两种办法可以有效提升预览速度。
一种情况是sql查询速度比较快,页面展示比较慢。此时直接使用帆软内置的分页预览方式就可以大幅提升预览速度。
另一种情况是,sql查询本身就比较慢。这种就需要通过sql分页来提升预览速度。
帆软本身提供行式引擎来解决这种问题,不过使用行式引擎有其自身的局限性,具体详情可以查看帆软帮助文档:启用行式引擎执行层式报表- FineReport帮助文档 - 全面的报表使用教程和学习资料
自定义帆软的工具栏,使用js来实现对应的翻页功能,并且提供两个参数来供sql调用,能满足所有数据库sql分页的使用场景。最终效果如下:
1、创建分页sql
sql语句如下:
select * from 销量 order by 销量
${if(len(pageno)=0,"","limit "+((pageno-1)*pagesize)+","+pagesize)}
同时创建一个计算总行数和总页数的sql,需要注意,数据集名称命名为pagetool,总行数重新名为totalcnt,总页数重命名为totalpage,后面js中调用会用到。
select count(1) as totalcnt,
ceiling(count(1)*1.0/${pagesize}) as totalpage
from 销量
2、报表设计
报表设计没有限制,根据实际项目需求设计。这里的设计如下:
3、引入js文件
点击工具栏进行分页的时候,需要向页面传递参数,并刷新页面。这里采用动态参数传参的方式,因为多处自定义事件均使用到传参,因此将它定义成一个函数,以实现复用。代码如下:
var reload=function(param){
FR.ajax({
url:'/webroot/decision/view/report?op=fr_dialog&cmd=parameters_dynamic',
type:'POST',
data:param,
headers: {sessionID:FR.SessionMgr.getSessionID()},
complete:function(res, status){
if(window.FR && FR.Chart && FR.Chart.WebUtils){
FR.Chart.WebUtils.clearCharts(); }
if(window.FR && FR.destroyDialog) {
FR.destroyDialog(); }
_g().loadContentPane();
}
})
}
将其保存为reload.js文件,并在模板>模板web属性>引用JavaScript处将其导入,如下所示:
5、自定义工具栏
考虑到实现效果后可以自定义每页显示的条数,导致报表展示的页面大小无法估计。因此使用填报预览的方式来实现。
1)打开模板>模板web属性>填报页面设置,选择为该模板单独设置,将默认的工具栏按钮全部清除掉,然后依次添加两个自定义按钮,一个邮件按钮,四个自定义按钮,一个PDF按钮。如下所示:
2)为自定义按钮重命名并添加自定义事件
如图所示,六个自定义的重命名分别为
①重命名:首页,自定义事件如下:
reload({"pageno":1})
②重命名:上一页,自定义事件如下:
var pageno = FR.remoteEvaluate("=$pageno");
reload({"pageno":pageno-1});
③重命名:下一页,自定义事件如下:
var pageno = FR.remoteEvaluate("=$pageno");
reload({"pageno":pageno*1+1})
④重命名:末页,自定义事件如下:
var totalpage = FR.remoteEvaluate("=value('pagetool',2,1)");
reload({"pageno":totalpage});
⑤重命名:导出Excel,自定义事件如下:
var url = "${servletURL}?viewlet=${reportName}&format=excel&pageno=";
window.location=encodeURI(encodeURI(url));
⑥重命名:数据总量,无自定义事件。
5、添加加载结束事件
在模板>模板web属性>填报页面设置>为该模板单独设置中,添加加载结束事件,如下图所示
其中代码如下:
setTimeout(function(){
// 定义变量获取总行数、总页数、页码、每页显示行数
var totalcnt = FR.remoteEvaluate("=value('pagetool',1,1)");
var totalpage = FR.remoteEvaluate("=value('pagetool',2,1)");
var pageno = FR.remoteEvaluate("=$pageno");
var pagesize = FR.remoteEvaluate("=$pagesize");
//定义当前页和每页显示行数的html代码
var currentpageStr = "<input id='currentpage' min='1' type='number' style='width:50px;'/><label id='totalpage' style='font:400 13.3333px Arial;'></label>";
var pagesizeStr = "<label style='font:400 13.3333px Arial;'>每页显示条数:</label><input id='pagesize' min='10' max='100' type='number' style='width:50px'/>";
//将email按钮和pdf按钮替换成当前页和每页显示行数,并设置按钮属性
$("div[widgetname=Email]").replaceWith(currentpageStr);
$("div[widgetname=PDF]").replaceWith(pagesizeStr);
$("#currentpage").val(pageno);
$("#currentpage").attr("max",totalpage);
$("#totalpage").text(" / "+totalpage);
$("#pagesize").val(pagesize);
_g().toolbar.options.items[6].setValue("数据总量:"+totalcnt);
//当前页的值有变化时触发的操作
$("#currentpage").change(function(){
var val = $(this).val();
if(val <1 || val>totalpage){
$(this).val(pageno);
}else{
reload({"pageno":val})
}
})
//每页显示行数的值有变化时触发的操作
$("#pagesize").change(function(){
var val = $(this).val();
if(val <10 || val>100){
$(this).val(pagesize);
}else{
reload({"pagesize":val})
}
})
//当页码为1时,首页和上一页不可用
if(pageno == 1){
_g().toolbar.options.items[0].setEnable(false);
_g().toolbar.options.items[1].setEnable(false);
}
//当页码等于最后一页时,下一页和末页不可用
if(pageno == totalpage){
_g().toolbar.options.items[3].setEnable(false);
_g().toolbar.options.items[4].setEnable(false);
}
},10)
11.0.5(Build#persist-2022.06.15.17.11.21.865)
*请认真填写需求信息,我们会在24小时内与您取得联系。