[原创]开发一个适合Ajax+JSON+jQuery环境使用的多功能页码栏——jPagerBar-1.1.1


Demo:http://www.senparc.com/WebTools.xhtml/JPagerBar
jPagerBar1.2已发布,升级说明及Demo、源代码下载请见:http://www.cnblogs.com/szw/archive/2009/08/27/1555201.html

事由:由于最近在测试开发的一个ASP.NET MVC的项目需要用到页码栏(并且需要用到AJAX+JSON传输数据),而微软发布的.NET3.5 CTP MVCTOOLKIT里面又没有提供,网上找了下似乎也没有太称心的,于是就自己动手做一个。

由于这个项目用到页码栏的地方大多是后台,所以既然不考虑SEO,本着效率第一的原则,决定全部使用js(jQuery)+Ajax+JSON的模式。我把js开发框架确定在了jQuery上。起先想叫“jPager”,想起来好像JAVA已经有一个JPager了,那就叫jPagerBar

先确定一下这个插件在项目中必须满足的几个要求:

1、页数不确定,根据记录条数和每页显示记录数自动生成

2、样式不确定,必须可以根据页面需要应用不同样式

3、必须可以自动缩略多余的页码,比如总共有100页,当浏览第50页时,可以省略1-4951-100之间的部分页码,并且这个延伸显示的页码数量可以由自己设定

4、提供“上一页”“下一页”按钮,并且可以自己设定对应的值或图形

5、记录为空时,隐藏页栏,显示友好的空记录信息

6、属于附加要求:由于该空间可能用于不提供AJAX/JSON数据的页面或者由于配合SEO需要,同时兼顾POST+AJAX方式和GET直接请求页面(如.aspx)两种方式,并且逻辑和输出尽量分离以便日后需要直接HTML输出时(SEO需要),由js转换为C#语言。

7、同一页面中可以多次重复调用,互不影响,并兼容IE/FF

这里先列出完成以上几点必须提供的API,然后我会把源代码全部发上来:

调用:ShowPageBar([containerId] ,[url] , [attr]);

解释:

参数名称

类型

说明

containerId

字符串

提供装载页码栏的容器标签的客户端ID(用于定位,如div

url

字符串

如果使用GET方法请求页面(最普通的页码栏换页方式),提供需要请求的URL(如果为POST,此url可为空值,有页面指定,稍后源代码中可见)

attr

hashtable

页码栏参数,为hash数据具体包括如下表“表二”

表二 attr hash参数说明:

[attr]参数名称

类型

说明

style

字符串

css样式,默认”technorati”等,可自己设置(我的示例中使用了网上提供的一套Web2.0的样式,大家也可以自己定制)

totalCount

整数

总记录条数

pageCount

整数

没页显示记录条数,默认为20

showPageNumber

整数

缩略显示页码的阀值(如页码为50,则相邻只显示50 - showPageNumber50 +showPageNumber的页码),默认为3

currentPageIndex

整数

当前页码

onclick

字符串

页码单击事件(如果采用单击事件,并且包含“return false”字段,则url将被忽略),插件会把“{pageindex}”自动替换为当前页码

barMark

字符串

数据显示区域的Mark标签(<a name=[ barMark]></a>),备用

noRecordTip

字符串

如果没有记录,显示的友好提示

preWord

字符造

“上一页”按钮显示的字样

nextWord

字符串

“下一页”按钮显示的字样

jQuery源代码如下(PageBar-1.1.1.js):
/*
插件名称:jPagerBar
主要功能:配合AJAX/JSON等响应方式及数据格式,自动生成页码栏。可以在同一网页中重复使用,互不影响。
API包括:
页码栏容器ID、GET请求URL
以及:页码栏样式、记录总数、每页显示记录数、显示相邻页码数量阀值、当前页、onclick事件、
页码栏定位标签、无记录提示、“上一页”“下一页”按钮的表现文字(或HTML)。
当前版本号:1.1.1
发布日期:2008/2/22

作者:TNT2(SZWoncnblogs-szw.cnblogs.com)QQ:63408537(加位好友请说明来意)Email/MSN:szw2003@163.com
http://www.Senparc.com

版权及相关说明:
1、作者对此插件保留所有权利。本插件本着开源、交流、共同进步的宗旨,以免费形式为大家无偿提供。修改、引用请保留以上说明信息,否则将视同为主动盗用本插件。
2、为保证本插件的完整性、安全性和版本统一性,谢绝任何单位和个人将此插件代码修改后以个人名义或“jPagerBar”及类似名称发布,一旦发现,作者将不遗余进行力地进行追查、打击、曝光
3、作者对此插件保留最终解释权。


如有任何问题或意见、建议,欢迎与作者取得联系!让我们共同进步!
======================================================================================================================
*/


functionShowPageBar(containerId,url,attr)
{

varstyle=(attr["style"]==null)?"technorati":attr["style"];//class样式
vartotalCount=(attr["totalCount"]==null||attr["totalCount"]==0)?0:attr["totalCount"];;//parseInt()//总记录条数
varpageCount=(attr["pageCount"]==null||attr["pageCount"]==0)?20:attr["pageCount"];//attr["pageCount"];//每页记录数
varshowPageNumber=(attr["showPageNumber"]==null||attr["showPageNumber"]==0)?20:attr["showPageNumber"];//attr["showPageNumber"];//显示页码数量
varcurrentPageIndex=attr["currentPageIndex"];//当前页
varonclick=attr["onclick"];//onclick参数,如果包含“returnfalse”,则连接转为跳到barMark(暂留接口,其实returnfalse后一般情况下href将失效。)
varbarMark=attr["mark"];//onclick后跳转到的<aname="barMark"></a>标签
varnoRecordTip=attr["noRecordTip"];//没有记录提示(支持HTML)
varpreWord=(attr["preWord"]==null)?"<":attr["preWord"];//上一条记录文字,默认为“<”
varnextWord=(attr["nextWord"]==null)?">":attr["nextWord"];//下一条记录文字,默认为“>”

//输出设置
varbarID=containerId+"_pageBar";
//varbarDiv=$("#"+barID);
//添加PageBar层
$("#"+containerId).html("<divid=\""+barID+"\"class=\""+style+"\"></div>");
//输出设置结束

//如果没有记录,返回空记录提示
if(totalCount==0)
{
$(
"#"+barID).html(noRecordTip);
returnfalse;
}


pageCount
=(pageCount==null||pageCount==0)?20:pageCount;//每页显示记录数
vartotalPage=parseInt((totalCount-1)/pageCount)+1;//总页数

showPageNumber
=(showPageNumber==null||showPageNumber==0)?3:showPageNumber;
currentPageIndex
=(currentPageIndex==null||currentPageIndex<=0||currentPageIndex>totalPage)?1:currentPageIndex;

varbackPageStyle=(currentPageIndex<=1)?"disabled":"";
varnextPageStyle=(currentPageIndex>=totalPage)?"disabled":"";

varfirstDisplayPageEnd=0;//从第1页显示到xx页
varbodyDisplayPageStart=0;//当前页临近最左页码
varbodyDisplayPageEnd=0;//当前页临近最右页码
varendDisplayPageStart=0;//从第xx页显示到最后一页

//设定bodyDisplayPageStart
bodyDisplayPageStart=(currentPageIndex-showPageNumber<=1)?1:currentPageIndex-showPageNumber;//(ViewData.pageIndex-ViewData.showPageNumber<=ViewData.showPageNumber)?ViewData.showPageNumber+1:ViewData.pageIndex-ViewData.showPageNumber;

//设定bodyDisplayPageEnd
bodyDisplayPageEnd=(currentPageIndex+showPageNumber>=totalPage)?totalPage:currentPageIndex+showPageNumber;


//设定firstDisplayPageEnd
if(bodyDisplayPageStart>1)
{
if(bodyDisplayPageStart-showPageNumber<=1)
firstDisplayPageEnd
=bodyDisplayPageStart-1;
else
firstDisplayPageEnd
=showPageNumber;
}

else
{
firstDisplayPageEnd
=0;
}


//设定endDisplayPageStart
if(bodyDisplayPageEnd<totalPage)
{
if(bodyDisplayPageEnd+showPageNumber>=totalPage)
endDisplayPageStart
=bodyDisplayPageEnd+1;
else
endDisplayPageStart
=totalPage-showPageNumber+1;
}

else
{
endDisplayPageStart
=totalPage+1;
}



/********备用算法Start********/

////设定firstDisplayPageEnd
//
if(currentPageIndex-showPageNumber>0&&bodyDisplayPageStart>currentPageIndex-showPageNumber)
//
firstDisplayPageEnd=(showPageNumber>=totalPage)?0:showPageNumber;
//
else
//
firstDisplayPageEnd=0;

////设定endDisplayPageStart
//
if(bodyDisplayPageEnd<totalPage)
//
endDisplayPageStart=(bodyDisplayPageEnd+showPageNumber<totalPage)?totalPage-showPageNumber+1:totalPage+1;
//
else
//
endDisplayPageStart=totalPage+1;
//

//
//alert(bodyDisplayPageEnd+"<"+totalCount+"-"+showPageNumber);
//
////设定补充首尾
//
if(bodyDisplayPageStart>1&&firstDisplayPageEnd==0)
//
firstDisplayPageEnd=(bodyDisplayPageStart>showPageNumber)?showPageNumber:bodyDisplayPageStart-1;
//
if(bodyDisplayPageEnd<totalPage&&endDisplayPageStart>totalPage)
//
endDisplayPageStart=(bodyDisplayPageEnd<totalPage-showPageNumber)?totalCount-showPageNumber+1:bodyDisplayPageEnd+1;//MS第一个判断有点多余TNT2
/********备用算法End********/

//页面参数设定结束

//开始输出
//alert($("#"+barID).html());

//上一条
if(currentPageIndex<=1)
$(
"<spanclass=\""+backPageStyle+"\">"+preWord+"</span>").appendTo($("#"+barID));
else
$(GetPageLink(currentPageIndex
-1,currentPageIndex,preWord,onclick,url,barMark)).appendTo($("#"+barID));


//first
for(vari=1;i<=firstDisplayPageEnd;i++)
$(GetPageLink(i,currentPageIndex,i,onclick,url,barMark)).appendTo($(
"#"+barID));

//省略号
if(firstDisplayPageEnd+1<bodyDisplayPageStart)
$(
"<span></span>").appendTo($("#"+barID));

//body
for(vari=bodyDisplayPageStart;i<=bodyDisplayPageEnd;i++)
$(GetPageLink(i,currentPageIndex,i,onclick,url,barMark)).appendTo($(
"#"+barID));

//省略号
if(bodyDisplayPageEnd+1<endDisplayPageStart)
$(
"<span></span>").appendTo($("#"+barID));

//end
for(vari=endDisplayPageStart;i<=totalPage;i++)
$(GetPageLink(i,currentPageIndex,i,onclick,url,barMark)).appendTo($(
"#"+barID));

//>
if(currentPageIndex>=totalPage)
$(
"<spanclass=\""+nextPageStyle+"\">"+nextWord+"</span>").appendTo($("#"+barID));
else
$(GetPageLink(currentPageIndex
+1,currentPageIndex,nextWord,onclick,url,barMark)).appendTo($("#"+barID));

//alert($("#"+barID).html());
}


//页码标签链接
functionGetPageLink(linkPageIndex,currentPageIndex,text,onclick,url,barMark)
{
varpageData="?page=";//string.Format("{0}page=",(Request.QueryString.Count==0)?"?":"&")+"{0}";//页码参数

onclick
=(onclick!=null)?"onclick=\""+onclick+"\"":"";
onclick
=onclick.replace("{pageindex}",linkPageIndex);
href
=(onclick!=null&&onclick.indexOf("returnfalse")!=-1)?"href=\"#"+barMark+"\"":"href=\""+url+pageData+linkPageIndex+"\"";

varlinkHTML="";

if(linkPageIndex==currentPageIndex)
linkHTML
="<spanclass=\"current\">"+text+"</span>";
else
linkHTML
="<a"+href+onclick+">"+text+"</a>";

returnlinkHTML;
}

我们在HTML中这样调用:

var pageCount = 15;//每页计数
var totalRecord = 0;
var pagerStyle = "flickr";//jPagerBar样式

ShowPageBar("pageDataList1_Pager",//[containerId]
"
<%=Request.Url.AbsolutePath%>",//[url]
{style:pagerStyle,mark:"pageDataList1Mark",
totalCount:msg.totalCount,showPageNumber:3,pageCount:pageCount,currentPageIndex:pageIndex,noRecordTip:"没有记录",preWord:"上一页",nextWord:"下一页",
onclick:"TurnToPage({pageindex});returnfalse;"}//[attr]
);

上面代码中的msg是我最后获取的JSON数据,msg.totalCount是一个总记录的计数,在这里您可以从msg.data(JSON数据列表)的计数获取,我在此多加这个totalCount只是为了防止测试过程中数据“失真”而引发的bug的掩盖,特此说明。

一般我们还需要一个Handler来获取JSON数据(Handler1.ashx)已升级

namespacejPagerBar.Handler
{
///<summary>
///$codebehindclassname$的摘要说明
///</summary>

[WebService(Namespace="http://tempuri.org/")]
[WebServiceBinding(ConformsTo
=WsiProfiles.BasicProfile1_1)]
publicclassHandler1:IHttpHandler
{

publicvoidProcessRequest(HttpContextcontext)
{

//获取数据

if(context.Request.RequestType=="POST")
{
//设置行为参数
stringaction=context.Request.Form["action"].ToString();//动作
stringkind=(action.StartsWith("g_"))?action.Replace("g_",string.Empty):string.Empty;//分类查找前缀
stringorderString=(context.Request.Form["orderby"].ToString());//排序
stringorder="ascending";//排序:升序
stringorderBy=(!string.IsNullOrEmpty(orderString))?orderString.Substring(0,orderString.Length-2):"ID";//要排序的字段,如果为空,默认为"ID"
if(orderString.EndsWith("_d"))
{
order
="descending";//排序:降序
}


intpageCount=int.Parse(context.Request.Form["pageCount"].ToString());//每页显示记录数
intpageIndex=int.Parse(context.Request.Form["pageIndex"].ToString());//当前页
intskipRecord=(pageIndex-1)*pageCount;//跳过记录数


XElementdsXML
=CreateDataSorce();//创建并获取模拟数据源

//获取数据
vardsLinq=
(order
=="ascending")?
(fromx
indsXML.Descendants("DataTemp")
where((!string.IsNullOrEmpty(kind))?(x.Element("Group").Value==kind):(x.Element("Group").Value!=null))
orderbyx.Element(orderBy).Valueascending
select
newDataSourceModel()
{
ID
=x.Element("ID").Value,
Group
=x.Element("Group").Value,
Colum1
=x.Element("Colum1").Value,
Colum2
=x.Element("Colum2").Value,
Colum3
=x.Element("Colum3").Value
}
)
:
(fromx
indsXML.Descendants("DataTemp")
where((!string.IsNullOrEmpty(kind))?(x.Element("Group").Value==kind):(x.Element("Group").Value!=null))
orderbyx.Element(orderBy).Valuedescending
select
newDataSourceModel()
{
ID
=x.Element("ID").Value,
Group
=x.Element("Group").Value,
Colum1
=x.Element("Colum1").Value,
Colum2
=x.Element("Colum2").Value,
Colum3
=x.Element("Colum3").Value
}
);

inttotalCount=dsLinq.Count();//记录总数,在此只起到示范作用
//vardsFinalList=(order=="a")?
//dsLinq.OrderBy(x=>x.).Skip(skipRecord).Take(pageCount).ToList():
//dsLinq.OrderByDescending(orderby).Skip(skipRecord).Take(pageCount).ToList();

System.Text.StringBuildersb
=newSystem.Text.StringBuilder();
sb.Append(
"{");
sb.Append(
"totalCount:");
sb.Append(totalCount.ToString());
sb.Append(
",data:");
sb.Append(ToJSON(dsLinq.Skip(skipRecord).Take(pageCount).ToList()));
sb.Append(
"}");

context.Response.ContentType
="text/plain";
context.Response.Write(sb.ToString());
context.Response.End();
}


}



//创建数据源(用XML模拟)
privateXElementCreateDataSorce()
{
stringdataSourceFilePath=HttpContext.Current.Server.MapPath("~/App_Data/DataSource.xml");

if(!File.Exists(dataSourceFilePath))
{
//设置模拟数据源
XElementdsInfos=newXElement("jPagerBar");
Randomrdn
=newRandom();
for(inti=1;i<=300;i++)
{
//分类数据(模拟)
//string[]groups=newstring[]{"G1","G2","G3","G4"};
stringguid=Guid.NewGuid().ToString().Replace("-","");//用Guid产生随机代码
//生成模拟数据
XElementdsInfo=newXElement("DataTemp",
newXElement("ID",i.ToString("000")),
newXElement("Group","Group"+rdn.Next(0,5).ToString()),
newXElement("Colum1",guid.Substring(0,3)+"_C1in"+i.ToString()),
newXElement("Colum2",guid.Substring(3,3)+rdn.Next(100).ToString()+"_C2in"+i.ToString()),
newXElement("Colum3",guid.Substring(6,3)+rdn.Next(100).ToString()+"_C3in"+i.ToString())
);
dsInfos.Add(dsInfo);
//填充数据
}


//保存
dsInfos.Save(dataSourceFilePath);
}


returnXElement.Load(dataSourceFilePath);
}


publicstaticstringToJSON(objectobj)
{
JavaScriptSerializerserializer
=newJavaScriptSerializer();
returnserializer.Serialize(obj);
}


//privatestaticvoidGetJSON(XElementdsXML)
//{
//vardsLinq=(fromxindsXML.Descendants("DataTemp")
//selectnewDataSourceModel()
//{
//ID=x.Element("ID").Value,
//Group=x.Element("Group").Value,
//Colum1=x.Element("Colum1").Value,
//Colum2=x.Element("Colum2").Value,
//Colum3=x.Element("Colum3").Value
//});
//inttotalCount=dsLinq.Count();//记录总数,在此只起到示范作用,客户端的记录总数可以从JSON数据的data计数获取
//System.Text.StringBuildersb=newSystem.Text.StringBuilder();
//sb.Append("{");
//sb.Append("totalCount:");
//sb.Append(totalCount.ToString());
//sb.Append(",data:");
//sb.Append(ToJSON(dsLinq.ToList()));
//sb.Append("}");
//}



publicboolIsReusable
{
get
{
returnfalse;
}

}

}


//数据源结构
publicclassDataSourceModel
{
publicstringID{get;set;}
publicstringGroup{get;set;}
publicstringColum1{get;set;}
publicstringColum2{get;set;}
publicstringColum3{get;set;}
}

}



上面我用XML模拟了一个数据源,正好顺便尝试了一下Linq to XML:)如果您是使用.NET2.0的话,也可改成XMLDocument的方式或者从自己的数据源获取。

这里我们看一下基本效果:
基本状态:



如果超过相邻页数地阀值的页码用...替代:

下面我们升级一下,可以自定义没页显示得条数和跳至的页码:


光这样当然是不够的,如果用户输入了无效的值,会引发无效的服务器响应,我们需要在客户端就过滤:

一共300条数据,每页10条,只能显示30页,要31页当然就错了。当然这里我的验证过程只是提供了一条思路,还可以有更多的丰富。(咦?光影魔术手不注册还会有水印?)

刚才我说的需要能自定义样式,当然也不能少:


或者这样:

或者这样:



========以下为新增功能

新增筛选、排序功能




这个项目是开源的,大家可以在这里下载(Webforms):/Files/szw/jPagerBar.rar

友情提醒一下:
1、这个Demo默认为.net3.5,您也可体提取到别的.net版本中运行。
2、这个项目的开发背景是ASP.NET MVC,为了比较直观,我用WebForms创建了这个Demo,如果有什么疏漏,请大家多多包涵,并且不吝指出其中的错误或者可以改进的地方。
3、因为MVC和WebForms的输出方式有所不同,大家在这里可能看不到太多和.NET有关的东西,对应的
.ascx文件我会在jPagerBar最终版本发布后,发布上来,当然大家也可以做成UI。

这里再发布一个ASP.NET MVC V1.0环境的Demo(2009-8-11更新):/Files/szw/jPagerBar-MVCv1.0.rar
jPagerBar in MVC Demo:http://www.senparc.com/WebTools.xhtml/JPagerBar

做这个demo有点匆忙,希望对大家有用,十分期待大家的建议,谢谢!

优质内容筛选与推荐>>
1、二、ConfigMap
2、Java中NumberUtil工具类(持续更新中)
3、多线程
4、centos ifconfig命令无法使用问题
5、【vue】---vue中使用async+await出现的问题及解决方案


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号





    联系我们

    欢迎来到TinyMind。

    关于TinyMind的内容或商务合作、网站建议,举报不良信息等均可联系我们。

    TinyMind客服邮箱:support@tinymind.net.cn