详解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 相关文章推荐
JS图片切换的具体方法(带缩略图版)
Nov 12 Javascript
轻松学习jQuery插件EasyUI EasyUI实现拖动基本操作
Nov 30 Javascript
jquery if条件语句的写法
May 19 Javascript
Jquery $when done then的用法详解
May 20 Javascript
关于function类中定义变量this的简单说明
May 28 Javascript
jQuery实现倒计时重新发送短信验证码功能示例
Jan 12 Javascript
JS使用正则表达式验证身份证号码
Jun 23 Javascript
jQuery remove()过滤被删除的元素(推荐)
Jul 18 jQuery
Angular7.2.7路由使用初体验
Mar 01 Javascript
JS回调函数简单易懂的入门实例分析
Sep 29 Javascript
JavaScript中遍历的十种方法总结
Dec 15 Javascript
vue 在服务器端直接修改请求的接口地址
Dec 19 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
smarty模板引擎从配置文件中获取数据的方法
2015/01/22 PHP
PHP导出带样式的Excel示例代码
2016/08/28 PHP
jquery 插件学习(五)
2012/08/06 Javascript
javascript当中的代码嗅探扩展原生对象和原型(prototype)
2013/01/11 Javascript
在服务端(Page.Write)调用自定义的JS方法详解
2013/08/09 Javascript
JavaScript中Cookies的相关使用教程
2015/06/04 Javascript
JQuery实现左右滚动菜单特效
2015/09/28 Javascript
封装获取dom元素的简单实例
2016/07/08 Javascript
浅谈MVC+EF easyui dataGrid 动态加载分页表格
2016/11/10 Javascript
薪资那么高的Web前端必看书单
2017/10/13 Javascript
ReactNative实现Toast的示例
2017/12/31 Javascript
JS处理一些简单计算题
2018/02/24 Javascript
vue-router启用history模式下的开发及非根目录部署方法
2018/12/23 Javascript
vue路由对不同界面进行传参及跳转的总结
2019/04/20 Javascript
解析vue、angular深度作用选择器
2019/09/11 Javascript
Vue 实现复制功能,不需要任何结构内容直接复制方式
2019/11/09 Javascript
[02:55]2018DOTA2国际邀请赛勇士令状不朽珍藏Ⅲ饰品一览
2018/08/01 DOTA
Python实现的监测服务器硬盘使用率脚本分享
2014/11/07 Python
深入解析Python的Tornado框架中内置的模板引擎
2016/07/11 Python
Python深入06——python的内存管理详解
2016/12/07 Python
python能做什么 python的含义
2019/10/12 Python
python 串行执行和并行执行实例
2020/04/30 Python
美国浴缸、水槽和水龙头购物网站:Vintage Tub & Bath
2019/11/05 全球购物
德国高尔夫商店:Par71.de
2020/11/29 全球购物
日本AOKI官方商城:AOKI西装
2020/06/11 全球购物
汽车销售求职自荐信
2013/10/01 职场文书
大学生应聘推荐信范文
2013/11/19 职场文书
初中政治教学反思
2014/01/17 职场文书
军训学生自我鉴定
2014/02/12 职场文书
商场消防演习方案
2014/02/12 职场文书
幼儿园六一活动总结
2014/08/27 职场文书
党员个人剖析材料
2014/09/30 职场文书
承诺书范本
2015/01/21 职场文书
公务员处分决定书
2015/06/25 职场文书
2016党校学习心得体会
2016/01/07 职场文书
导游词之阆中古城
2019/12/23 职场文书