做久了vue和react框架项目,偶尔也需要做做原生的项目。不可否认vue的双向绑定机制确实很香,但是也是建立在原生js基础上。所以,只有做更多的原生js项目,才能更加了解vue框架的底层原理。在日常开发中,也会不可避免的会遇到原生开发的需求。这里主要介绍下jqgrid和validform这两种插件的用法及二次封装
1. 表格数据处理插件:jqGrid
插件使用api说明:jqGrid APi 详解_小戴BOTAOY演示博客
官方api文档说明:jqGrid demos-jqGrid实例-中文-mn886.net
colModel参数说明:https://www.cnblogs.com/qing123/p/3801691.html
jqgrid是一款基于jq的表单数据处理插件, 在做表格渲染时,可以让我们只关注数据的处理,而不用去注重dom的元素,而且插件自带的增删改查操作,以及自定义事件的方法都可以帮助我们处理各种各样不同的业务需求。多达几十个参数的配置和十几种实例方法的选择。可以说基本上能满足大部分的表格业务需求了。
效果展示:
jqgrid本身的框架样式并不美观,不过好在提供了几种皮肤选择。这里用的bootstrap皮肤,主要是做一下按钮的美化。引入的bootstrap版本是Bootstrap v3.3.7
jqgrid的配置项和方法实在太多了,因此这里做了极致封装。我们先来看下封装后的使用代码:
<div class="jarviswidget jarviswidget-sortable">
<div class="widget-body" id="content">
<div class="table-responsive">
<table id="jqgrid"></table>
</div>
</div>
</div>
<div id="jqgrid_pager"></div>
<script>
//api文档说明 https://www.yii666.com/article/423468.html?action=onAll colModel参数说明:https://www.cnblogs.com/qing123/p/3801691.html
$(document).ready(function() {
// 初始化 jqGrid
var jqgridTable = createJqGridTable();
var obj = [
{"value":"id","title":"ID",editable:false},
{"value":"pname","title":"字典分组"},
{"value":"fieldName","title":"字典类型"},
{"value":"text","title":"字典标签"},
{"value":"realValue","title":"字典键值"},
{"value":"remark","title":"备注"},
]
jqgridTable.createGrid({
caption: "数据列表", //表格标题,可以去掉
showRowNumbers:'true', //是否展示序号列,默认展示true.可以去掉
url: "/manage/system/dictionary/list", // 数据源URL
colData:obj,
});
jqgridTable.setupNavGridEvents({editurl:"/manage/system/dictionary/saveOrUpdate",refresh:true,search:false,edit : true,add : true,del : true}) //导航栏内置操作事件
});
</script>
就是这么简单,不过百行代码就可以完成一个表格数据的显示,增删改操作。导航栏自带的增删改操作传递的参数就是数据列表中的键值。也是比较符合通常情况下前后端列表对接的参数需求的。当然,如果你需要额外自定义也是可以的。下面我们再来看看封装方法。
/***表格封装jqgrid 开始 ***/
function createJqGridTable() {
var jqgridTable = {
createGrid: function (options) {
var defaultOptions = {
// 默认的jqGrid配置项
gridId:'#jqgrid',
contentId:'content',
url: "",
data:"",
datatype: "json",
mtype: "post",//向后台请求数据的ajax的类型。可选post,get
showRowNumbers:false,
colData:[],
colNames: [],
colModel: [],
rowNum: 10,
rowList: [10, 20, 30],
pager: "#jqgrid_pager",
sortname: "", // 默认排序列
sortorder: "desc", // 默认排序方式
viewrecords: true, // 显示总记录数
caption: "", // 表格标题
height: "auto", //自适应高度
autowidth: true, //自适应宽度
multiselect: false, //是否多选
onSelectRow: null,
ondblClickRow: null,
jsonReader : {
id:"id", //设置唯一标识
repeatitems: true, // 指示每个行数据是否是一个包含字段名和值的对象
root: "rows", // 数据列表
page:"page", //当前页
total: "total", // 总页数
records:"records" //总条数
},
cmTemplate: { cellattr: function (rowId, cellValue, rowData, colData) { return 'style="line-height:30px"'; } },
loadComplete: null,
gridComplete:null, //jqGrid 渲染完成触发
beforeProcessing: function (data) { //数据加载之前的操作
if(options.showRowNumbers){
var rows = data.rows;
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
row.rowNumber = i + 1;
}
}
},
beforeRequest: function () { //请求前处理
var postData = $(this).jqGrid('getGridParam', 'postData');
var form = document.getElementById("queryForm");
var formData = serializeForm(form);
var obj = $.extend({}, postData, formData);
console.log("请求前传参:",obj);
$(this).jqGrid('setGridParam', { postData: obj });
},
};
// 合并默认配置和传入的配置项
var gridOptions = $.extend({}, defaultOptions, options);
//表数据处理
if (gridOptions.showRowNumbers) { //是否展示序号列
gridOptions.colNames.push("序号");
gridOptions.colModel.push({
name: "rowNumber",
index: "rowNumber",
width: 50,
align: "center",
sortable: false,
resizable: false,
fixed: true,
});
}
$.each(gridOptions.colData, function(index, col) {
gridOptions.colNames.push(col.title);
var colModelItem = {
name: col.value,
index: col.value,
width: col.width || 150,
align: col.align || "center",
editable: col.editable || true,
};
// 将 col 对象中的属性复制到 colModelItem 对象中
$.extend(colModelItem, col);
gridOptions.colModel.push(colModelItem);
});
// 创建jqGrid实例
jsongrid = $(gridOptions.gridId).jqGrid(gridOptions);
// 监听窗口大小变化事件
$(window).on('resize.jqGrid', function () {
$(gridOptions.gridId).jqGrid('setGridWidth', $("#" + gridOptions.contentId).width());
});
jqGridAfterLoadComplete(); //样式调整-表格数据
},
// 封装导航栏操作事件
setupNavGridEvents: function (options) {
var defaultOptions = {
gridId:'#jqgrid', //导航id
pager:'#jqgrid_pager',
refresh: false,
search: false,
edit: false,
add: false,
del: false,
editurl: "", // 默认为空
};
var navGridOptions = $.extend({}, defaultOptions, options);
$(navGridOptions.gridId).jqGrid('navGrid', navGridOptions.pager, navGridOptions,
{
// 编辑操作的处理函数
closeAfterEdit: true,
beforeShowForm:null, //显示弹窗前方法
onclickSubmit: null, // 请求前
afterComplete: handleResponse
},
{
// 添加操作的处理函数
closeAfterAdd: true,
beforeShowForm:null, //显示弹窗前方法
onclickSubmit: null, // 请求前
beforeSubmit: function (postdata, formid) {
// 在提交之前修改 id 字段的值
postdata.id = '';
return [true, ''];
},
afterComplete: handleResponse
},
{
// 删除操作的处理函数
closeAfterDel: true,
beforeShowForm:null, //显示弹窗前方法
onclickSubmit: null, // 请求前
afterComplete: handleResponse
}
);
// 设置 editurl
if (navGridOptions.editurl) {
$(navGridOptions.gridId).jqGrid('setGridParam', { editurl: navGridOptions.editurl });
}
jqGridAfterLoadComplete(); //样式调整-导航栏
},
}
return jqgridTable;
}
// 处理响应数据
function handleResponse(response, postdata) {
console.log(response, postdata);
var result = response.responseJSON;
showDialog({ msg: result.retMsg });
}
function serializeForm(form) {
var formData = {};
var dataArray = $(form).serializeArray();
$(dataArray).each(function(index, field) {
formData[field.name] = field.value;
});
return formData;
}
/***** 样式调整 *****/
function jqGridAfterLoadComplete() {
console.log("动态加载样式...")
// remove classes
$(".ui-jqgrid").removeClass("ui-widget ui-widget-content");
$(".ui-jqgrid-view").children().removeClass("ui-widget-header ui-state-default");
$(".ui-jqgrid-labels, .ui-search-toolbar").children().removeClass("ui-state-default ui-th-column ui-th-ltr");
$(".ui-jqgrid-pager").removeClass("ui-state-default");
$(".ui-jqgrid").removeClass("ui-widget-content");
// add classes
$(".ui-jqgrid-htable").addClass("table table-bordered table-hover");
$(".ui-jqgrid-btable").addClass("table table-bordered table-striped");
//样式优化
$(".widget-body").css({padding:"0",border: "none"});
$(".ui-pg-div").removeClass().addClass("btn btn-sm btn-primary");
$(".ui-icon.ui-icon-plus").removeClass().addClass("fa fa-plus");
$(".ui-icon.ui-icon-pencil").removeClass().addClass("fa fa-pencil").parent(".btn-primary").removeClass("btn-primary").addClass("btn-success");
$(".ui-icon.ui-icon-trash").removeClass().addClass("fa fa-trash-o").parent(".btn-primary").removeClass("btn-primary").addClass("btn-danger");
$(".ui-icon.ui-icon-search").removeClass().addClass("fa fa-search");
$(".ui-icon.ui-icon-refresh").removeClass().addClass("fa fa-refresh");
$(".ui-icon.ui-icon-disk").removeClass().addClass("fa fa-save").parent(".btn-primary").removeClass("btn-primary").addClass("btn-success");
$(".ui-icon.ui-icon-cancel").removeClass().addClass("fa fa-times").parent(".btn-primary").removeClass("btn-primary").addClass("btn-danger");
$( ".ui-icon.ui-icon-seek-prev" ).wrap( "<div class='btn btn-sm btn-default'></div>" );
$(".ui-icon.ui-icon-seek-prev").removeClass().addClass("fa fa-backward");
$( ".ui-icon.ui-icon-seek-first" ).wrap( "<div class='btn btn-sm btn-default'></div>" );
$(".ui-icon.ui-icon-seek-first").removeClass().addClass("fa fa-fast-backward");
$( ".ui-icon.ui-icon-seek-next" ).wrap( "<div class='btn btn-sm btn-default'></div>" );
$(".ui-icon.ui-icon-seek-next").removeClass().addClass("fa fa-forward");
$( ".ui-icon.ui-icon-seek-end" ).wrap( "<div class='btn btn-sm btn-default'></div>" );
$(".ui-icon.ui-icon-seek-end").removeClass().addClass("fa fa-fast-forward");
}
/***表格封装结束***/
后端返回列表数据格式:
2.表单校验插件:validform
官方使用文档说明:http://www.5imoban.net/view/validform/document.html#getstart
作为表单校验,主要用来校验表单是否必填吗,以及填入的数据格式是否正确。validform这个插件还是比较好用的,封装后会自动将表单的值做序列化处理传给后端。如果想额外加其他参数,也是可以在提交的时候进行插入一个type=hidden的表单值即可。先来看使用代码:
<div class="widget-body no-padding">
<form action="/manage/system/dept/deptSaveOrUpdate" method="post" id="basicForm" class="smart-form" novalidate="novalidate">
<fieldset>
<div class="row">
<section class="col col-4">
<label class="label">分组名</label>
<label class="input">
<input type="text" name="pname" datatype="s5-16" errormsg="昵称至少5个字符,最多16个字符!" />
</label>
</section>
<section class="col col-4">
<label class="label">字段名</label>
<label class="input">
<input type="text" name="fieldName" class="invalid" id="fieldName" datatype="s4-16" errormsg="别名至少4个字符" />
</label>
</section>
</div>
</fieldset>
<footer>
<input type="hidden" name="id" />
<button type="submit" name="submit" id="btn_oper" class="btn btn-primary"> 保存 </button>
<a class="btn btn-primary" href="#/manage/system/dept/deptsList" click="location.go(-1);"> 返回 </a>
</footer>
</form>
</div>
<script type="text/javascript">
pageSetUp();
//表单验证及回调
$(document).ready(function() {
commonValidateForm({
id: "#basicForm",
beforeCheck:function(curform){
var id=123;
curform.find('[name="id"]').val(id); // 将额外参数值设置到隐藏字段
},
callBack:function (data){
console.log("提交成功",data);
//window.history.go(-1)
return true;
},
});
</script>
validform的配置项及回调方法并不多,还是比较好上手的,看一下官方文档基本就会用了。下面是封装代码。
//公共的表单验证. 使用文档说明:http://www.5imoban.net/view/validform/document.html#getstart https://blog.csdn.net/kexiaoling/article/details/49639813
//函数的主要参数是一个包含配置选项的对象 o,其中包括以下属性:
// id:表单的选择器,默认为 #basicForm。
// postonce:是否启用防止重复提交功能,默认为 true。
// btnReset:重置按钮的选择器,默认为 #btn_reset。
// btnSubmit:提交按钮的选择器,默认为 #btn_oper。
// isReset:是否在提交后重置表单,默认为 true。
// isReload:是否在提交后重新加载页面,默认为 false。
// callBack:提交成功后的回调函数,默认为空函数。
// beforeCheck:提交前的回调函数,默认为空函数。
// datatype:自定义验证规则的配置对象,默认为空对象。
// fillModel:与重置事件绑定的回调函数,用于填充表单数据,默认为空对象。
// resetEventCallBack:重置事件的回调函数,默认为一个函数,会调用 fillModel 来填充表单数据。
// 函数返回一个 Validform 实例,通过该实例可以调用 Validform 插件提供的方法。
//
// 函数内部使用了 Validform 的配置选项来初始化表单验证,包括:
//
// showAllError:是否显示所有错误信息,默认为 true。
// ajaxPost:是否使用 Ajax 提交表单,默认为 true。
// tiptype:提示信息的显示方式的回调函数。
// beforeSubmit:提交前的回调函数,用于进行一些准备工作。
// callback:提交后的回调函数,处理服务器返回的数据。
function commonValidateForm(o) {
o = o ? o : {};
var defaults = {
id: "#basicForm",
postonce: true,
btnReset: "#btn_reset",
btnSubmit: "#btn_oper",
isReset: true,
isReload: false,
callBack: function() {},
beforeCheck: function() {},
datatype:{
// "*":"不能为空!",
// "*6-16":"请填写6到16位任意字符!",
// "n":"请填写数字!",
// "n6-16":"请填写6到16位数字!",
// "s":"不能输入特殊字符!",
// "s6-18":"请填写6到18位字符!",
// "p":"请填写邮政编码!",
// "m":"请填写手机号码!",
// "e":"邮箱地址格式不对!",
// "url":"请填写网址!"
},
//此属性和resetEventCallBack事件绑定了.点击reset事件后,会自动回调resetEventCallBack方法,调用fillModel来填充表单数据.
fillModel: {},
resetEventCallBack: function() {
$(o.id).fill(o.fillModel);
}
};
o = $.extend(true, defaults, o);
return validFormPlug = $(o.id).Validform({
showAllError: false,
ajaxPost: true,
tipSweep:true,
postonce: o.postonce,
btnSubmit: o.btnSubmit,
btnReset: o.btnReset,
resetEventCallBack: o.resetEventCallBack,
tiptype:function(msg,o,cssctl){
console.log(msg,o);
if (o.type === 3) {
// 验证失败
if (o.obj.attr("errormsg")) {
cocoMessage.info(msg, 1000);
} else {
cocoMessage.info(msg, 1000);
}
}
},
// tiptype: function(msg,o,cssctl){
// //msg:提示信息;
// //o:{obj:*,type:*,curform:*}, obj指向的是当前验证的表单元素(或表单对象),type指示提示的状态,值为1、2、3、4, 1:正在检测/提交数据,2:通过验证,3:验证失败,4:提示ignore状态, curform为当前form对象;
// //cssctl:内置的提示信息样式控制函数,该函数需传入两个参数:显示提示信息的对象 和 当前提示的状态(既形参o中的type);
// console.log(msg,o);
// if(!o.obj.is("form")&&o.type==3){//验证表单元素时o.obj为该表单元素,全部验证通过提交表单时o.obj为该表单对象;
// console.log("提交表单");
// cocoMessage.info(msg, 1000);
// }else{
// console.log("验证通过");
// }
// },
datatype:o.datatype,
beforeSubmit: function(curform) {
alertLoading("正在提交...", 0);
o.beforeCheck(curform);
},
callback: function(data) {
alertHide();
validFormPlug.resetStatus(); //重置表单提交状态.
//## 表单提交后处理
alertSuccess(data.retMsg, varSaveOrUpdateShowTime);
setTimeout(function() {
var callFlag = o.callBack(data); //回调方法
if(callFlag){ //如果返回 true,则不继续处理
return;
}
//默认是重置表单
if (o.isReset) {
validFormPlug.resetForm()
// commonBaseForm(); //绑定事件
}
//可配置成重新刷新页面
if (o.isReload) {
location.reload();
}
}, varSaveOrUpdateShowTime);
//## End 表单提交后处理
}
});
}