详解MVC如何使用开源分页插件(shenniu.pager.js)


Posted in Javascript onDecember 16, 2016

最近比较忙,前期忙公司手机端接口项目,各种开发+调试+发布现在几乎上线无问题了;虽然公司项目忙不过在期间抽空做了两件个人觉得有意义的事情,一者使用aspnetcore开发了个人线上项目(要说线上其实只能ip访问,没有域名哈哈),其架构组成由:aspnetcore1.0.0+redis+ postgressql+TaskMainForm服务,这个项目在后期会开源出来供大家分享学习,站点地址点这里心声网;一者是目前正在做的后台管理框架一叶子,现目前刚好吧js分页插件shenniu.pager.js写完,个人觉得还是可以的,这也是本章将要和大家分享的内容;那么开始今天的分享内容,希望各位多多扫码支持:

  • 为什么采用js分页及效果图
  • 在view中如何使用及分享个后台方法
  • 开发者视角阅读shenniu.pager.js代码

下面一步一个脚印的来分享:

. 为什么采用js分页及效果图

首先,咋们来了解下市面上mvc两种常用的分页方式:跳转分页和ajax分页;跳转分页意思就是页面重定向到指定的页面并通过传递分页需要的参数,从而获取数据后通过Modal来绑定数据,这个每次都会刷下页面体验上不是很好;ajax分页通过异步js请求某个接口,然后从接口获取到数据后,再赋值到展示的界面上,这种方式是不会刷新页面,从而保证了用户体验;

下面来看下这次分享的js分页插件效果图:

图一:

详解MVC如何使用开源分页插件(shenniu.pager.js)

图二:

详解MVC如何使用开源分页插件(shenniu.pager.js)

图三:

详解MVC如何使用开源分页插件(shenniu.pager.js)

看效果图好像看不出来什么东西,我只能说没办法,以后争取弄个gif动态图片吧,后面代码才是关键

. 在view中如何使用及分享个后台方法

首先,为了页面样式好看我使用了bootstrap+ace样式框架,样式效果就是如上面几张图所示(这里是样式和js文件);由于该插件是采用jquery格式书写的所以需要引用jquery.js,如上面图所示使用到了日期选择框,因为我采用的样式都是基于h5的设计所以这里引用的日期选择插件bootstrap-datepicker.min.js和她的样式bootstrap-datepicker3.min.css;该实例需要的引用文件都好了,下面看下截图:

详解MVC如何使用开源分页插件(shenniu.pager.js)

再来,咋们就开始使用shenniu.pager.js,我们需要在点击“查询”按钮的时候去调用这个插件,然后通过插件去获取后台接口返回的数据,并绑定到页面展示出来,所以有了如下代码:

var snTool = new shenniuTool();
 $("button[id='btnSearch']").on("click", function () {
 var data = {
 txtName: $("input[name='txtName']").val(),
 nStatus: $("select option:selected").val(),
 dOperateTime: $("input[name='dOperateTime']").val()
 };
 snTool.listFun({
 showId: "divShowResult", //内容显示的div的Id
 url: "/Menu/Search",
 data: data,
 pageSize: 2, //每页N条
 headText: [
 { nickName: "全选", name: "Id", colType: "checkbox" },
 { nickName: "名称", name: "Name", colType: "label", isModalHeadText: true },
 { nickName: "链接", name: "Link" },
 { nickName: "状态", name: "EnableDes" },
 { nickName: "操作人", name: "OperatorDes" },
 { nickName: "操作时间", name: "OperateTime", format: "yyyy-MM-dd" },
 { nickName: "操作", name: "Id", colType: "operate" }
 ],
 editeOption: {
 url: "/Menu/Edit",
 title: "编辑",
 height: 500
 },
 viewOption: {
 url: "/Menu/Details",
 title: "查看",
 height: 500
 },
 delOption: {
 url: "/Menu/Delete",
 title: "删除",
 height: 500
 },
 modalExt: modalExt
 });
 });

注意参数url: "/Menu/Search",这个指向的就是后台接口,那么咋们来看下我后台咋们写的:

[HttpGet]
 public JsonResult Search()
 {
 var moPageResult = new StageModel.MoPageResult<dynamic>();
 try
 {
 var txtName = Request.Params["txtName"];
 var nStatus = string.IsNullOrWhiteSpace(Request.Params["nStatus"]) ? -1 : Convert.ToInt32(Request.Params["nStatus"]);
 var dOperateTime = string.IsNullOrWhiteSpace(Request.Params["dOperateTime"]) ? Convert.ToDateTime("1991-01-01") : Convert.ToDateTime(Request.Params["dOperateTime"]);
 var data = db.MoMenus.AsQueryable();
 if (!string.IsNullOrWhiteSpace(txtName))
 {
 data = data.Where(b => b.Name.Contains(txtName));
 }
 if (nStatus >= 0)
 {
 data = data.Where(b => b.IsEnable == (nStatus == (int)StageEnumHelper.ComStatus.启用));
 }
 if (dOperateTime > Convert.ToDateTime("1991-01-01"))
 {
 data = data.Where(b => b.OperateTime >= dOperateTime && b.OperateTime < dOperateTime.AddDays(1));
 }
 moPageResult.MoPageContent(
 data,
 b => b.OperateTime,
 b => new
 {
 Id = b.Id,
 Name = b.Name,
 Link = b.Link,
 Des = b.Des,
 IsEnable = b.IsEnable,
 Operator = b.Operator,

 OperatorDes = b.MoUserInfo.NickName,
 EnableDes = b.IsEnable ? "启用" : "禁用",
 OperateTime = b.OperateTime
 });
 }
 catch (Exception ex)
 {
 }
 return Json(moPageResult, JsonRequestBehavior.AllowGet);
 }

后台接口Request.Params获取的几个参数就是从前端

var data = {
 txtName: $("input[name='txtName']").val(),
 nStatus: $("select option:selected").val(),
 dOperateTime: $("input[name='dOperateTime']").val()
 };

传递过来的,分别代码了视图中的名称,状态,操作时间等查询条件;下面来看下,后台这儿没有看到获取类似分页的当前页数和分页记录数的操作,是封装到了MoPageResult类中的MoPageContent()中,来看下MoPageResult类代码如:

#region 分页数据返回
 public class MoPageResult<TResult> where TResult : class, new()
 {
 public MoPageResult()
 {
 }
 public IQueryable<TResult> MoResult;
 /// <summary>
 /// 总页数
 /// </summary>
 public int PageTotal { get; set; }
 /// <summary>
 /// 当前页数
 /// </summary>
 public int CurrentPage { get; set; }
 /// <summary>
 /// 分页记录数
 /// </summary>
 public int PageSize { get; set; }
 /// <summary>
 /// 分页方法
 /// </summary>
 /// <typeparam name="TKey"></typeparam>
 /// <param name="query"></param>
 /// <param name="order_desc"></param>
 public void MoPageContent<T, TKey>(IQueryable<T> query, Expression<Func<T, TKey>> desc, Expression<Func<T, TResult>> selector = null, bool isDesc = true) where T : class,new()
 {
 if (HttpContext.Current == null) { return; }
 var Request = HttpContext.Current.Request;
 this.PageSize = string.IsNullOrWhiteSpace(Request.Params["pageSize"]) ? 15 : Convert.ToInt32(Request.Params["pageSize"]);
 this.CurrentPage = string.IsNullOrWhiteSpace(Request.Params["currentPage"]) ? 1 : Convert.ToInt32(Request.Params["currentPage"]);
 var nTotal = query.Count();
 this.PageTotal = nTotal / this.PageSize + (nTotal % this.PageSize > 0 ? 1 : 0);
 if (selector != null)
 {
 if (isDesc)
 {
 query = query.OrderByDescending(desc);
 }
 else
 {
 query = query.OrderBy(desc);
 }
 this.MoResult = query.
 Skip((this.CurrentPage - 1) * this.PageSize).
 Take(this.PageSize).
 Select(selector);
 }
 }
 }
#endregion

MoPageContent()中默认是获取了pagesize,currentpage参数,这样减少了用户操作性,并且此方法承担了计算总页数和执行分页语句的角色,注意最后查询语句Select(selector),selector是Expression<Func<T, TResult>>类型,这个T有条件约束where T : class,new();我在调用该分页类的使用传递的T是dynamic,因为赖人如我觉得匿名类更方便;唯一遗憾的是select输出暂时无法直接对某个属性直接使用方法;

最后,插件使用还需要注意一个地方,就是时间,如果后台不处理时间直接DateTime的json格式化,那么在插件获取出来的时间格式如:

详解MVC如何使用开源分页插件(shenniu.pager.js)

这个时候就需要在使用shenniu.pager.js插件时候在属性headText中,指定时间列的格式如:

{ nickName: "操作时间", name: "OperateTime", format: "yyyy-MM-dd" }

使用format格式化时间格式,这个插件兼容的给有:yyyy,MM,dd,HH,mm,ss,相信满足大家需要了;

. 开发者视角阅读shenniu.pager.js代码

首先,我们从上而下,映入眼帘的是插件属性:

var defOption = {
 showId: "divShowResult", //内容显示的div
 url: "", //ajax数据来源地址
 headText: [
 { nickName: "A", colType: "checkbox", name: "Id" },
 { nickName: "B", colType: "label", name: "Name", isModalHeadText: true } //isModalHeadText:是否是模式窗体头部显示的信息
 ],
 data: {}, //查询条件
 editeOption: {
 url: "",
 title: "编辑",
 width: 500,
 height: 500
 }, //编辑地址,不包括id
 viewOption: {
 url: "",
 title: "查看",
 width: 500,
 height: 500
 }, //查看地址
 delOption: {
 url: "",
 title: "删除",
 width: 500,
 height: 500
 }, //删除地址
 currentPage: 1, //当前页数
 pageSize: 15, //分页记录数
 showPageTab: 6, //展示6个页数
 modalExt: null, //模式窗体对象
 //可忽略
 callback: function () { }, //回调函数
 tabId: "tab001",
 loading: "努力加载中,等会吧...", //可以直接写出<img src=''/>
 sucFun: function (data) { },
 befFun: function () { },
 errFun: function () { },
 comFun: function () { },
 timeout: 60000 //超时60S
 };
$.extend(defOption, option);

里面已经包括了注释说明,看起来应该不是问题; $.extend(defOption, option); 这段代码意思是吧用户传递进来的参数和插件里面默认的参数合并,用户大于插件直接可以覆盖相同属性的值;

再来,看请求后台的方法:

//请求后台
 function ajaxFun(option) {
 if (option) {
 $.extend(defOption, option);
 }
 //获取分页参数
 var hidPageSize = defOption.pageSize;
 var hidCurrentPage = defOption.currentPage;
 if ($("form input[name='pageSize']").val()) {
 hidPageSize = $("form input[name='pageSize']").val();
 }
 if ($("form input[name='currentPage']").val()) {
 hidCurrentPage = $("form input[name='currentPage']").val();
 }
 //合并用户查询条件和分页参数条件
 var searchData = {
 pageSize: hidPageSize,
 currentPage: hidCurrentPage
 };
 $.extend(searchData, defOption.data);
 //请求后台数据
 $.ajax({
 url: defOption.url,
 type: "get",
 data: searchData,
 dataType: "json",
 timeout: defOption.timeout,
 async: true,
 beforeSend: defOption.befFun,
 success: defOption.sucFun,
 });
 }

这个方法就是请求接口获取数据的方法,里面默认获取了页面中的pageSize,currentPage两个分页所需要的参数,这里采用的是get方式来请求,当然可以写成post,不过需要后台支持post就行了;

我们再看查询列表方法:

//查询列表
 listFun: function (option) {
 if (option) {
 $.extend(defOption, option);
 }
 //默认格式
 var tab = [];
 tab.push('<table id="' + defOption.tabId + '" class="table table-bordered table-hover">');
 tab.push('<thead><tr role="row">');
 for (var i in defOption.headText) {
 var head = defOption.headText[i];
 if (head.colType == "label") {
 tab.push('<th class="center" tabindex="0" rowspan="1" colspan="1">' + head.nickName + '</th>');
 } else if (head.colType == "checkbox") {
 tab.push('<th class="center " rowspan="1" colspan="1" aria-label="">');
 tab.push(' <label class="pos-rel">');
 tab.push(' <input type="checkbox" name="cbAll" class="ace">');
 tab.push(' <span class="lbl">' + head.nickName + '</span>');
 tab.push(' </label>');
 tab.push('</th>');
 } else {
 tab.push('<th class="center" tabindex="0" rowspan="1" colspan="1">' + head.nickName + '</th>');
 }
 }
 tab.push('</tr></thead>');
 tab.push('<tbody><tr><td class="text-center" colspan="' + defOption.headText.length + '">' + defOption.loading + '</td></tr></tbody>');
 tab.push('</table>');
 tab.push('<div id="divPager" class="text-center"></div>');
 $("#" + defOption.showId).html(tab.join(''));
 //全选事件
 $("input[type='checkbox'][name='cbAll']").on("click", function () {
 var cbStatus = $(this).is(":checked");
 if (cbStatus) {
 $("input[name='cb']:checkbox").prop("checked", true);
 } else {
 $("input[name='cb']:checkbox").prop("checked", false);
 }
 });
 //数据返回成功处理
 defOption.sucFun = function (data) {
 var head = $("table[id='" + defOption.tabId + "'] tbody");
 if (data) {
 if (data.MoResult) {
 //遍历table展示的数据
 var rows = [];
 $.each(data.MoResult, function (i, item) {
 rows.push('<tr>');
 var modalHeadText = "";
 for (var h_i in defOption.headText) {
 var head = defOption.headText[h_i];
 var item_val = item[head.name];
 if (item_val && typeof (item_val) != "undefined") { } else { item_val = ""; }
 //时间格式化
 if (head.format && item_val.length > 0) {
 console.log(item_val);
 var date = new Date(parseInt(item_val.replace("/Date(", "").replace(")/", ""), 10));
 item_val = head.format.
 replace("yyyy", date.getFullYear()).
 replace("MM", date.getMonth() + 1).
 replace("dd", date.getDate()).
 replace("HH", date.getHours()).
 replace("mm", date.getMinutes()).
 replace("ss", date.getMilliseconds());
 }
 //获取模式窗体头部信息
 if (modalHeadText.length <= 0) { modalHeadText = head.isModalHeadText ? item_val : "" };
 if (head.colType == "label") {
 rows.push('<td class="center">' + item_val + '</td>');
 } else if (head.colType == "checkbox") {
 rows.push('<td class="center">');
 rows.push(' <label class="pos-rel">');
 rows.push(' <input type="checkbox" name="cb" value="' + item_val + '" class="ace">');
 rows.push(' <span class="lbl"></span>');
 rows.push(' </label>');
 rows.push('</td>');
 } else if (head.colType == "operate") {
 rows.push('<td class="center"><div class="hidden-sm hidden-xs action-buttons">');
 if (defOption.editeOption.url.length > 0) {
 var editOption = $.extend({}, defOption.editeOption);
 editOption.url += "/" + item_val;
 editOption.title += modalHeadText.length > 0 ? "-" + modalHeadText : "";
 var op = JSON.stringify(editOption);
 rows.push('<a class="blue" data-item=\'' + op + '\' href="javascript:;"><i class="ace-icon fa fa-pencil bigger-130"></i></a>');
 }
 if (defOption.viewOption.url.length > 0) {
 var viewOption = $.extend({}, defOption.viewOption);
 viewOption.url += "/" + item_val;
 viewOption.title += modalHeadText.length > 0 ? "-" + modalHeadText : "";
 var op = JSON.stringify(viewOption);
 rows.push('<a class="blue" data-item=\'' + op + '\' href="javascript:;"><i class="ace-icon fa fa-search-plus bigger-130"></i></a>');
 }
 if (defOption.delOption.url.length > 0) {
 var delOption = $.extend({}, defOption.delOption);
 delOption.url += "/" + item_val;
 delOption.title += modalHeadText.length > 0 ? "-" + modalHeadText : "";
 var op = JSON.stringify(delOption);
 rows.push('<a class="blue" data-item=\'' + op + '\' href="javascript:;"><i class="ace-icon fa fa-trash-o bigger-130"></i></a>');
 }
 rows.push('</div></td>');
 }
 else {
 rows.push('<td class="center">' + item_val + '</td>');
 }
 }
 rows.push('</tr>');
 });
 //页数展示
 if (data.MoResult.length > 0) {
 var pager = [];
 pager.push('<div class="text-center">');
 pager.push(' <ul class="pagination" style="margin-top:0px">');
 var nPager = defOption.showPageTab;//每次展示6个分页
 //上一页
 var nprev = (data.CurrentPage - 1 >= 1 ? data.CurrentPage - 1 : 1);
 pager.push(' <li class="paginate_button previous" aria-controls="dynamic-table" tabindex="0" id="dynamic-table_previous">');
 pager.push(' <a href="javascript:;" name="npager" data-page="' + nprev + '">上一页</a>');
 pager.push(' </li>');
 //当前页之前页码
 var preTotal = data.CurrentPage - nPager >= 1 ? data.CurrentPage - nPager : 1;
 for (var i = preTotal; i < data.CurrentPage ; i++) {
 pager.push(' <li class="paginate_button ' + (i == data.CurrentPage ? "active disabled" : "") + '" aria-controls="dynamic-table">');
 pager.push(' <a href="javascript:;" name="npager" data-page="' + i + '">' + i + '</a>');
 pager.push(' </li>');
 }
 //当前页
 pager.push(' <li class="paginate_button active disabled" aria-controls="dynamic-table">');
 pager.push(' <a href="javascript:;" name="npager" data-page="' + data.CurrentPage + '">' + data.CurrentPage + '</a>');
 pager.push(' </li>');
 //当前页以后页码
 var nextTotal = data.CurrentPage + nPager > data.PageTotal ? data.PageTotal : data.CurrentPage + nPager;
 for (var i = data.CurrentPage + 1; i <= nextTotal; i++) {
 pager.push(' <li class="paginate_button ' + (i == data.CurrentPage ? "active disabled" : "") + '" aria-controls="dynamic-table">');
 pager.push(' <a href="javascript:;" name="npager" data-page="' + i + '">' + i + '</a>');
 pager.push(' </li>');
 }
 //下一页
 var nnext = (data.PageTotal < data.CurrentPage + 1 ? data.PageTotal : data.CurrentPage + 1);
 pager.push(' <li class="paginate_button next" aria-controls="dynamic-table" tabindex="0" id="dynamic-table_next">');
 pager.push(' <a href="javascript:;" name="npager" data-page="' + nnext + '">下一页</a>');
 pager.push(' </li>');
 pager.push(' </ul>');
 //分页查询条件
 pager.push('<div style="display:none">');
 pager.push(' <form>');
 pager.push(' <input type="hidden" name="pageSize" value="' + defOption.pageSize + '"/>');
 pager.push(' <input type="hidden" name="currentPage" value="' + defOption.currentPage + '"/>');
 pager.push(' </form>');
 pager.push('</div>');
 pager.push('</div>');
 //移除加载中
 //head.html("");
 //添加结果
 head.html(rows.join(''));
 $("div[id='divPager']").html(pager.join(''));
 //操作按钮事件
 $("a[data-item]").on("click", function () {
 var data_Item = $(this).attr("data-item");
 if (data_Item) {
 var data_Item_Obj = JSON.parse(data_Item);
 defOption.modalExt.modalFun({
 width: data_Item_Obj.width,
 height: data_Item_Obj.height,
 url: data_Item_Obj.url,
 title: data_Item_Obj.title,
 callback: defOption.callback
 });
 }
 });
 //绑定分页按钮事件
 $("a[name='npager']").on("click", function () {
 var nPager = $(this).attr("data-page");
 if (nPager.length <= 0) { return; }
 $("form input[name='currentPage']").val(nPager);
 //执行请求后台
 ajaxFun(defOption);
 });
 } else {
 head.html("<tr><td class=\"text-center\" colspan=\"" + defOption.headText.length + "\">未能查询到数据!</td></tr>");
 $("div[id='divPager']").html("");
 }
 } else {
 head.html("<tr><td class=\"text-center\" colspan=\"" + defOption.headText.length + "\">未能查询到数据。</td></tr>");
 $("div[id='divPager']").html("");
 }
 } else {
 head.html("<tr><td class=\"text-center\" colspan=\"" + defOption.headText.length + "\">未能查询到数据</td></tr>");
 $("div[id='divPager']").html("");
 }
 };
 if (option) {
 $.extend(defOption, option);
 }
 //执行请求后台
 ajaxFun(defOption);
 }

这个方法体挺长的,主要操作是:

默认格式展示列表头部并呈现出加载中的提示=》绑定复选框全选事件=》创建数据返回成功函数sucFun()=》调用请求后台方法ajaxFun();

再来看函数sucFun()等到数据返回后执行的操作是:

遍历json返回数据展示到table中(其中包括了时间格式化的处理,复选框,label及操作按钮类型operate的初始化)=》页数展示及事件绑定(目前只有上一页,当前页之前页码,当前页,当前页以后页码,下一页的效果展示,分页查询条件(生成pagesize和currentPage隐藏控件),绑定分页按钮事件)

以上就是shenniu.pager.js整个插件内容,不多且清晰,感觉分享给大家挺高兴,下面贴出示例中用到的js文件和css文件可以在这里下载:http://xiazai.3water.com/201612/yuanma/shenniu.pager_3water.rar

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持三水点靠木!

Javascript 相关文章推荐
ie下jquery.getJSON的缓存问题的处理方法
Mar 29 Javascript
你未必知道的JavaScript和CSS交互的5种方法
Apr 02 Javascript
js添加千分位的实现代码(超简单)
Aug 01 Javascript
JS简单实现点击复制链接的方法
Aug 03 Javascript
深入理解JS DOM事件机制
Aug 06 Javascript
数组Array的排序sort方法
Feb 17 Javascript
input 标签实现输入框带提示文字效果(两种方法)
Oct 09 Javascript
AngularJS基于http请求实现下载php生成的excel文件功能示例
Jan 23 Javascript
jQuery实现简单复制json对象和json对象集合操作示例
Jul 09 jQuery
解决Layui中templet中a的onclick参数传递的问题
Sep 20 Javascript
JavaScript如何实现防止重复的网络请求的示例
Jan 28 Javascript
vue报错function () { [native code] },无法出现我们想要的内容 Unknown custom element
Apr 11 Vue.js
js继承实现方法详解
Dec 16 #Javascript
详解jQuery简单的表格应用
Dec 16 #Javascript
JS中parseInt()和map()用法分析
Dec 16 #Javascript
HTML5canvas 绘制一个圆环形的进度表示实例
Dec 16 #Javascript
JS数字千分位格式化实现方法总结
Dec 16 #Javascript
jquery插件锦集【推荐】
Dec 16 #Javascript
jQuery Easyui 下拉树组件combotree
Dec 16 #Javascript
You might like
php中使用parse_url()对网址进行解析的实现代码(parse_url详解)
2012/01/03 PHP
php线性表顺序存储实现代码(增删查改)
2012/02/16 PHP
thinkPHP模板中for循环与switch语句用法示例
2016/11/30 PHP
解决laravel上传图片之后,目录有图片,但是访问不到(404)的问题
2019/10/14 PHP
基于jQuery实现点击同时更改两个iframe的网址
2010/07/01 Javascript
jquery(live)中File input的change方法只起一次作用的解决办法
2011/10/21 Javascript
jQuery事件用法实例汇总
2014/08/29 Javascript
JavaScript中的console.assert()函数介绍
2014/12/29 Javascript
jQuery使用removeClass方法删除元素指定Class的方法
2015/03/26 Javascript
JS+CSS实现带有碰撞缓冲效果的竖向导航条代码
2015/09/15 Javascript
使用Chart.js图表库制作漂亮的响应式表单
2015/10/28 Javascript
Bootstrap入门书籍之(三)栅格系统
2016/02/17 Javascript
全面解析Angular中$Apply()及$Digest()的区别
2016/08/04 Javascript
浅述节点的创建及常见功能的实现
2016/12/15 Javascript
js实现常见的工具条效果
2017/03/02 Javascript
bootstrap响应式表格实例详解
2017/05/15 Javascript
AngularJS中的promise用法分析
2017/05/19 Javascript
vue实现图片滚动的示例代码(类似走马灯效果)
2018/03/03 Javascript
Vue 实现手动刷新组件的方法
2019/02/19 Javascript
jQuery实现轮播图效果
2019/11/26 jQuery
Jquery如何使用animation动画效果改变背景色的代码
2020/07/20 jQuery
详细讲解用Python发送SMTP邮件的教程
2015/04/29 Python
python 随机数使用方法,推导以及字符串,双色球小程序实例
2017/09/12 Python
python3中获取文件当前绝对路径的两种方法
2018/04/26 Python
python基于pdfminer库提取pdf文字代码实例
2019/08/15 Python
HTML5 微格式和相关的属性名称
2010/02/10 HTML / CSS
AmazeUI 图标的示例代码
2020/08/13 HTML / CSS
Nike澳大利亚官网:Nike.com (AU)
2019/06/03 全球购物
Crocs欧洲官网:Crocs Europe
2020/01/14 全球购物
五一服装活动方案
2014/01/11 职场文书
能源工程专业应届生求职信
2014/03/01 职场文书
2014庆六一活动方案
2014/03/02 职场文书
高中军训第一天感言
2014/03/06 职场文书
第二课堂活动总结
2014/05/07 职场文书
大学生党员个人对照检查材料范文
2014/09/25 职场文书
会计试用期自我评价
2015/03/10 职场文书