创建几个常用table展示方式插件

这次和大家分享的是自己写的一个table常用几种展示格式的js插件取名为(table-shenniu),样式使用的是bootstrap.min.css,还需要引用jquery.min.js包,这个插件由来的目的是项目中需要一个table格式的提交数据的页面,功能有合并单元格,只能提交选中行数据,全选功能,和一个下拉选功能;这几种功能感觉朋友们肯定都会遇到,所以干脆封装一个插件,发布出来说不定能帮到有些朋友快速完成任务哈哈,当然最主要的还是希望朋友们能相互交流里面的代码,逻辑,谢谢;中秋节就要到了,这里提前预祝大家节日快乐,吃月饼的时候不要忘记了点个赞。

以上是个人的看法,下面来正式分享今天的文章吧:

. 功能介绍与效果图(认可的点个赞)

. 需求分析

. 插件主要代码块的说明

. 获取table插件数据,并提交给后台

. 插件源码及帮助文档

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

. 功能介绍与效果图(认可的点个赞)

1.普通效果

创建几个常用table展示方式插件

2.合并表格

创建几个常用table展示方式插件

3.汇总

创建几个常用table展示方式插件

4.合并+汇总

创建几个常用table展示方式插件

5.展示选中行明细

创建几个常用table展示方式插件

. 需求分析

首先,咋们来看张图:

创建几个常用table展示方式插件

如图所示,这里的乘客可能会选择不同的产品,同时选择的购买份数也可能改变,然后只能提交最后一列勾选上的数据这是上面的图展示出来的功能信息;为了更好的用户体验显然“乘客姓名”这一列需要吧相同名称的数据合并;价格一列通常在用户填写表单中不会有汇总的操作,但是数据统计报表中一般就需要;还有这个产品列只有名称,没有更详细描述信息,通常我们需要展示出来方便用户了解更多的信息,一般我们直接在产品名称对应的html元素使用title展示,这样不友好,所以又有了点击产品名称,查看明细的需求;下面我们来整合下需求:

1. table中每行需要checkbox选择框和对应的份数下拉框select及全选按钮

2. 重复数据合并单元格

3. 汇总金额数字列

4. 明细描述

5. 还需要一个能隐藏产品Id或者乘客Id的元素(插件这里定位一个隐藏列)

这个就是table列表需要满足的需求,也是大众化的需求吧

. 插件主要代码块的说明

首先,咋们来说下插件需要的固定格式的数据:列表头json数据,列表行json集合数据格式;

列表头json数据:

 //测试用例-列表头
var header = [ {
"title": "产品名称",
"name": "product",
"type": "label"
},
{
"title": "销售价",
"name": "sale",
"type": "label",
},
{
"title": "份数",
"name": "num",
"type": "select", //如果是下拉框,这里需要初始值val
"val": [
{
"text": ,
"val":
},
{
"text": ,
"val":
},
{
"text": ,
"val":
}
]
},
{
"title": "总额",
"name": "amount",
"type": "label"
},
{
"title": "<input type='checkbox' name='cbAll' style='border: 0px; width: 20px; height: 20px;'/>", //如果是全选选择框,这里直接写html元素
"name": "cb",
"type": "checkbox"
},
{
"title": "保险Id",
"name": "productid",
"type": "hidden"
}
];

列表头数据注意点在于:

1. 如果"type": "select"类型,需要初始值val属性的值,这样每一行中才会出现一个下拉框

2. "type": "checkbox",如果列是复选框,那么头部单元格一般是全选的复选框,我们直接在头部json定义

<input type='checkbox'  name='cbAll' style='border: 0px; width: 20px; height: 20px;'/>

就行了,如果不需要全选框,直接定义成文字就行

3. 插件列支持的type类型有type:有label(文本),select(下拉框,如果是下拉框需要初始化下拉数据“val”),checkbox(选择框,如果需要全选框直接写html元素,默认自带),hidden(隐藏域,保存每行唯一的值)

列表行json集合数据格式:

 //测试用例-没行数据
var data = [
{ "product": "神雕侠侣电影票", "sale": 30.00, "num": , "amount": 30.00, "cb": false, "productid": },
{ "product": "摇摇乐门票", "sale": 30.00, "num": , "amount": 60.00, "cb": false, "productid": },
{ "product": "神雕侠侣电影票", "sale": 30.00, "num": , "amount": 30.00, "cb": true, "productid": },
{ "product": "摇摇乐门票", "sale": 30.00, "num": , "amount": 90.00, "cb": false, "productid": }
]; data = [
{ "product": { "产品名称": "神雕侠侣电影票", "描述": "神雕侠侣电影票,中秋节大放送,情侣们快来啊,只需一块钱", "销售价 ": 30.00 }, "sale": 30.00, "num": , "amount": 30.00, "cb": false, "productid": },
{ "product": { "产品名称": "摇摇乐门票", "描述": "摇摇乐门票,中秋节大放送,情侣们快来啊,只需一块钱", "销售价 ": 30.00 }, "sale": 30.00, "num": , "amount": 60.00, "cb": false, "productid": },
{ "product": { "产品名称": "杨过和小龙女电影票", "描述": "杨过和小龙女电影票,中秋节大放送,情侣们快来啊,只需一块钱", "销售价 ": 30.00 }, "sale": 30.00, "num": , "amount": 30.00, "cb": true, "productid": },
{ "product": { "产品名称": "四川一日行旅游券", "描述": "小编忙,没时间维护" }, "sale": 30.00, "num": , "amount": 90.00, "cb": false, "productid": },
{ "product": { "产品名称": "四川一日行旅游券", "描述": "小编忙,没时间维护" }, "sale": 30.00, "num": , "amount": 30.00, "cb": false, "productid": }
];

这里需要注意的是这两种数据结构,如果想要展示明细,那么没个单元格对应的值是一个{}对象,数据格式如:{"产品名称":"四川一日行旅游券","描述":"小编忙,没时间维护"},这里的“产品名称“将作为明细列名称;如果只需要展示名称不要明细,直接赋值成值就行了

其次,来看下可配置的参数说明,如下代码:

var defOption = {
id: "", //要显示插件内容的div的Id (必需)
header: header, //json格式列表头 (必需)
data: data, //json格式数据 (必需)
cbAllName: "cbAll", //全选框Name
cbName: "cb", //每行选框Name
tableId: "table" + new Date().getTime(), //tableId 默认时间格式 isTotal: false, //是否汇总 (默认不汇总)
outTotalCols: "", //不汇总列 多个格式如:1,2
isCombine: false, //是否合并单元格 (默认不合并)
outCombineCols: "", //不合并单元格列 多个格式如:1,2
back: function () { console.log("这里是回调");
} //回调函数
};

然后,代码逻辑步奏为:初始化列表头->初始化每行内容(每行加载的时候需要根据列表头部的type来判断应该加载什么html元素控件并且可以绑定初始化状态值)->全选绑定事件->绑定某个需要展示明细的单元格,动态加载明细内容数据->执行汇总->执行合并单元格(此处需要注意的是,不需要先汇总在合并单元格,如果顺序发生变化,汇总的格式将异常)->执行回调响应函数;这个就是主要的代码逻辑;

. 获取table插件数据,并提交给后台

首先,table元素展示出来后,如果作为用户提交的界面,还需要能获取出用户选择的数据,并提交给后台,我这里列举一个简单的获取列子,咋们可以直接在回调函数back中写提交表单的代码如:

 var tbChoice = new tb_choice({
id: "divShow",
data: data,
header: header,
outCols: "0,4,6",
isTotal: false,
back: function () { $("#btnSave").click(function () { var cbs = $("input[name='cb'][type='checkbox']:checked");
if (cbs.length <= ) { alert("请选择保险"); return; } var param = [];
$.each(cbs, function (index, item) {
var tr = $(this).parent().parent();
var id = $(tr).find("[name='id']").val();
var num = $(tr).find("[name='num'] option:selected").val();
var productId = $(tr).find("[name='productid']").val(); param.push({ id: id, num: num, productId: productId });
}); //请求后台
$.post("Add.aspx", { op: orderId, t: "save", param: JSON.stringify(param) }, function (result01) { var resultJson = JSON.parse(result01);
console.log(resultJson);
alert(resultJson.msg);
if (resultJson.status) { location.reload(true);
}
});
});
}
});

我这里直接在回调back函数里面写ajax提交给后台数据,这里使用遍历table的每行需要的数据组合成json格式发送给后台,用起来还是挺简单的呢,因为获取的使用通过name属性来获取元素的值,而name对应的都是最开始列表头json数据的name,大家可以对比下

. 插件源码及帮助文档

首先,插件的源码待会在结尾全部发布,里面分别有汇总方法,合并列方法,和一个String的扩展方法大家可以分开使用,朋友们有兴趣的也可以看帮助文档,根据文档上面的页面列子下载代码也行;更多的展示效果可以来这里看文档table-shenniu;源码如下:

 /// <reference path="../jquery-1.10.2.min.js" />

 var tb_total = function () {

     return {

         //汇总
//tableId:table元素的Id
//outCols:排除不合并的列,多个使用‘,’隔开
sumTab: function (tableId, outCols) { if (!outCols) { outCols = ""; } var rows = $("#" + tableId + " tr"); var colLen = $(rows).eq().find("td[class!='hide']").length;
var totalVal = [];
for (var i = ; i < colLen; i++) {
totalVal.push("");
}
$.each(rows, function (i, item) { var tds = $(item).find("td[class!='hide']");
var crossVal = "";
$(tds).each(function (i_td, item_td) { var hVal = $(item_td).html(); if (isNaN(hVal)) {
//非数字
totalVal[i_td] = "";
} else {
if (totalVal[i_td].length > ) { //数字
totalVal[i_td] = (parseFloat(totalVal[i_td]) + parseFloat(hVal)).toFixed();
} else { crossVal = hVal; }
}
});
}); var totalHtml = [];
totalHtml.push("<tr style='background-color:#EBF0EE'>");
for (var i in totalVal) { totalHtml.push("<td align=\"center\">"); if (i == ) {
totalHtml.push("<font style='color:red'>合计</font>");
totalHtml.push("</td>");
continue;
} else if (outCols.length > ) { //表示存在排除列
if (("," + outCols + ",").indexOf("," + i + ",") > -) {
totalHtml.push("");
totalHtml.push("</td>");
continue;
}
}
totalHtml.push(totalVal[i]);
totalHtml.push("</td>");
}
totalHtml.push("</tr>"); $("#" + tableId).append(totalHtml.join(''));
}, //合并列
//tableId:table元素的Id
//outCols:排除不合并的列,多个使用‘,’隔开
uniteTab: function (tableId, col, outCols) { if (!outCols) { outCols = ""; }
//col-- 需要合并单元格的列 1开始
var colLength = col;
var tb = document.getElementById(tableId);
if (!tb) { return; }
tb.style.display = '';
var i = ;
var j = ;
var rowCount = tb.rows.length; // 行数
var colCount = tb.rows[].cells.length; // 列数
var obj1 = null;
var obj2 = null;
//为每个单元格命名
for (i = ; i < rowCount; i++) {
for (j = ; j < colCount; j++) {
if (!tb.rows[i].cells[j]) { continue; }
tb.rows[i].cells[j].id = tableId + "tb__" + i.toString() + "_" + j.toString();
}
}
//合并行
for (i = ; i < colCount; i++) {
if (i == colLength) break;
//排除不合并列
if (("," + outCols + ",").indexOf("," + i + ",") > -) { continue; } obj1 = document.getElementById(tableId + "tb__0_" + i.toString())
for (j = ; j < rowCount; j++) {
obj2 = document.getElementById(tableId + "tb__" + j.toString() + "_" + i.toString());
if (obj1.innerText == obj2.innerText) {
obj1.rowSpan++;
obj2.parentNode.removeChild(obj2);
} else {
obj1 = document.getElementById(tableId + "tb__" + j.toString() + "_" + i.toString());
}
}
} //合并列
for (i = ; i < rowCount; i++) {
if (tb.rows[i] != null) {
colCount = tb.rows[i].cells.length;
obj1 = document.getElementById(tb.rows[i].cells[].id);
if (obj1 != null) {
for (j = ; j < colCount; j++) {
if (j >= colLength) break;
if (obj1.colSpan >= colLength) break; if (tb.rows[i].cells[j]) {
obj2 = document.getElementById(tb.rows[i].cells[j].id); if (obj1.innerText == obj2.innerText) {
obj1.colSpan++;
obj2.parentNode.removeChild(obj2);
j = j - ;
}
else {
obj1 = obj2;
j = j + obj1.rowSpan;
}
}
}
}
}
} }
}
} //table 表插件,要求bootstrap.min.css样式
var tb_choice = function (option) { //测试用例-列表头
var header = [ {
"title": "产品名称",
"name": "product",
"type": "label"
},
{
"title": "销售价",
"name": "sale",
"type": "label",
},
{
"title": "份数",
"name": "num",
"type": "select", //如果是下拉框,这里需要初始值val
"val": [
{
"text": ,
"val":
},
{
"text": ,
"val":
},
{
"text": ,
"val":
}
]
},
{
"title": "总额",
"name": "amount",
"type": "label"
},
{
"title": "<input type='checkbox' name='cbAll' style='border: 0px; width: 20px; height: 20px;'/>", //如果是全选选择框,这里直接写html元素
"name": "cb",
"type": "checkbox"
},
{
"title": "保险Id",
"name": "productid",
"type": "hidden"
}
]; //测试用例-没行数据
var data = [
{ "product": "神雕侠侣电影票", "sale": 30.00, "num": , "amount": 30.00, "cb": false, "productid": },
{ "product": "摇摇乐门票", "sale": 30.00, "num": , "amount": 60.00, "cb": false, "productid": },
{ "product": "神雕侠侣电影票", "sale": 30.00, "num": , "amount": 30.00, "cb": true, "productid": },
{ "product": "摇摇乐门票", "sale": 30.00, "num": , "amount": 90.00, "cb": false, "productid": }
]; data = [
{ "product": { "产品名称": "神雕侠侣电影票", "描述": "神雕侠侣电影票,中秋节大放送,情侣们快来啊,只需一块钱", "销售价 ": 30.00 }, "sale": 30.00, "num": , "amount": 30.00, "cb": false, "productid": },
{ "product": { "产品名称": "摇摇乐门票", "描述": "摇摇乐门票,中秋节大放送,情侣们快来啊,只需一块钱", "销售价 ": 30.00 }, "sale": 30.00, "num": , "amount": 60.00, "cb": false, "productid": },
{ "product": { "产品名称": "杨过和小龙女电影票", "描述": "杨过和小龙女电影票,中秋节大放送,情侣们快来啊,只需一块钱", "销售价 ": 30.00 }, "sale": 30.00, "num": , "amount": 30.00, "cb": true, "productid": },
{ "product": { "产品名称": "四川一日行旅游券", "描述": "小编忙,没时间维护" }, "sale": 30.00, "num": , "amount": 90.00, "cb": false, "productid": },
{ "product": { "产品名称": "四川一日行旅游券", "描述": "小编忙,没时间维护" }, "sale": 30.00, "num": , "amount": 30.00, "cb": false, "productid": }
]; var defOption = {
id: "", //要显示插件内容的div的Id (必需)
header: header, //json格式列表头 (必需)
data: data, //json格式数据 (必需)
cbAllName: "cbAll", //全选框Name
cbName: "cb", //每行选框Name
tableId: "table" + new Date().getTime(), //tableId 默认时间格式 isTotal: false, //是否汇总 (默认不汇总)
outTotalCols: "", //不汇总列 多个格式如:1,2
isCombine: false, //是否合并单元格 (默认不合并)
outCombineCols: "", //不合并单元格列 多个格式如:1,2
back: function () { console.log("这里是回调");
} //回调函数
}; $.extend(defOption, option); var tbArr = [];
tbArr.push('<table id="{0}" class="table table-hover table-bordered text-center ">'.format([defOption.tableId])); //头部
tbArr.push('<thead><tr>');
$.each(defOption.header, function (i, item) {
tbArr.push("<th {1}>{0}</th>".format([item.title, (item.type == "hidden" ? "class='text-center hide'" : "class='text-center'")]));
});
tbArr.push('</tr></thead>'); //内容
$.each(defOption.data, function (i, item) { tbArr.push("<tr >");
$.each(item, function (key, val) { var htype = [];
$.each(defOption.header, function (_, headItem) { if (headItem.name == key) { switch (headItem.type) {
case "label":
if (typeof (val) == "object") { //只循环一次
$.each(val, function (valKey, valItem) {
htype.push("<label data-item='{1}' style=' cursor:pointer'>{0}</label>".format([valItem, JSON.stringify(val)]));
return false;
});
} else {
htype.push(val);
}
break;
case "select":
htype.push("<select name='{0}'>".format([key]));
$.each(headItem.val, function (s_i, s_item) {
htype.push("<option value='{1}' {2}>{0}</option>".format([s_item.text, s_item.val, (s_item.val == val ? "selected='selected'" : "")]));
});
htype.push("</select>");
break;
case "text":
htype.push("<input name='{1}' type='text' class='form-control' value='{0}'/>".format([val, key]));
break;
case "checkbox":
htype.push("<input type='checkbox' name='{1}' {2} value='{0}' style='border: 0px; width: 20px; height: 20px;'/>".format([val, key, val ? "checked='checked'" : ""]));
break;
case "hidden":
htype.push("<input type='hidden' name='{1}' value='{0}'/>".format([val, key]));
break;
}
tbArr.push("<td style='font-weight: bold;' {1}>{0}</td>".format([htype.join(''), (headItem.type == "hidden" ? "class='hide'" : "class='text-center-vertical'")]));
return;
}
});
});
tbArr.push("</tr>");
});
tbArr.push('</table>'); $("#" + option.id).html(tbArr.join('')); //全选绑定
$("input[type='checkbox'][name='" + defOption.cbAllName + "']").click(function () { var status = $(this).prop("checked");
if (status) {
$("input[type='checkbox'][name='" + defOption.cbName + "']").prop({ "checked": true });
} else {
$("input[type='checkbox'][name='" + defOption.cbName + "']").prop({ "checked": false });
}
});
//label明细展示绑定
$("label[data-item]").click(function () {
var dataItem = $(this).attr("data-item");
if (dataItem) {
var tr = $(this).parent().parent();
var nextTr = tr.next();
var nextName = nextTr.attr("data-item");
if (!nextName) { var data = JSON.parse(dataItem);
var childTab = [];
childTab.push('<table class="table table-bordered">');
var childHeader = "";
var childContent = "";
$.each(data, function (key, item) {
childHeader += "<td>{0}</td>".format([key]);
childContent += "<td>{0}</td>".format([item]);
});
childTab.push("<tr>{0}</tr>".format([childHeader]));
childTab.push("<tr>{0}</tr>".format([childContent]));
childTab.push("</table>"); var colsLen = tr.find("td[class!='hide']").length;
var appendDes = $("<tr data-item='{2}'><td colspan='{1}' class='text-left'>{0}</td></tr>".format([childTab.join(''), colsLen, dataItem]));
tr.after(appendDes);
} else {
nextTr.remove();
}
}
}); //单元格操作
var tbTotal = new tb_total();
//汇总
if (defOption.isTotal) { tbTotal.sumTab(defOption.tableId, defOption.outTotalCols); }
//合并单元格
if (defOption.isCombine) { tbTotal.uniteTab(defOption.tableId, , defOption.outCombineCols); } //执行回调响应函数
defOption.back();
} String.prototype.format = function (arrVal) { if (!Array.isArray(arrVal)) { return this; } var str = this;
for (var i = ; i < arrVal.length; i++) { str = str.replace("{" + i + "}", arrVal[i]);
}
return str;
}
上一篇:Java线程实现与安全


下一篇:Jenkins 安装的HTML Publisher Plugin 插件无法展示ant生成的JunitReport报告