你的位置:首页 > Java教程

[Java教程]创建几个常用table展示方式插件


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

 

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

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

. 需求分析

. 插件主要代码块的说明

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

. 插件源码及帮助文档

 

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

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

1.普通效果

2.合并表格

3.汇总

4.合并+汇总

5.展示选中行明细

 

. 需求分析

首先,咋们来看张图:

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

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

2. 重复数据合并单元格

3. 汇总金额数字列

4. 明细描述

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

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

 

. 插件主要代码块的说明

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

列表头json数据:

 1 //测试用例-列表头 2   var header = [ 3  4           { 5             "title": "产品名称", 6             "name": "product", 7             "type": "label" 8           }, 9           {10             "title": "销售价",11             "name": "sale",12             "type": "label",13           },14           {15             "title": "份数",16             "name": "num",17             "type": "select", //如果是下拉框,这里需要初始值val18             "val": [19                   {20                     "text": 1,21                     "val": 122                   },23                   {24                     "text": 2,25                     "val": 226                   },27                   {28                     "text": 3,29                     "val": 330                   }31             ]32           },33           {34             "title": "总额",35             "name": "amount",36             "type": "label"37           },38           {39             "title": "<input type='checkbox' name='cbAll' style='border: 0px; width: 20px; height: 20px;'/>",  //如果是全选选择框,这里直接写html元素40             "name": "cb",41             "type": "checkbox"42           },43           {44             "title": "保险Id",45             "name": "productid",46             "type": "hidden"47           }48   ];

列表头数据注意点在于:

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集合数据格式:

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

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

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

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中写提交表单的代码如:

 1 var tbChoice = new tb_choice({ 2         id: "divShow", 3         data: data, 4         header: header, 5         outCols: "0,4,6", 6         isTotal: false, 7         back: function () { 8  9           $("#btnSave").click(function () {10 11             var cbs = $("input[name='cb'][type='checkbox']:checked");12             if (cbs.length <= 0) { alert("请选择保险"); return; }13 14             var param = [];15             $.each(cbs, function (index, item) {16               var tr = $(this).parent().parent();17               var id = $(tr).find("[name='id']").val();18               var num = $(tr).find("[name='num'] option:selected").val();19               var productId = $(tr).find("[name='productid']").val();20 21               param.push({ id: id, num: num, productId: productId });22             });23 24             //请求后台25             $.post("Add.aspx", { op: orderId, t: "save", param: JSON.stringify(param) }, function (result01) {26 27               var resultJson = JSON.parse(result01);28               console.log(resultJson);29               alert(resultJson.msg);30               if (resultJson.status) {31 32                 location.reload(true);33               }34             });35           });36         }37       });

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

 

. 插件源码及帮助文档

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

 1 /// <reference path="../jquery-1.10.2.min.js" /> 2  3  4 var tb_total = function () { 5  6   return { 7  8     //汇总 9     //tableId:table元素的Id 10     //outCols:排除不合并的列,多个使用‘,’隔开 11     sumTab: function (tableId, outCols) { 12  13       if (!outCols) { outCols = ""; } 14  15       var rows = $("#" + tableId + " tr"); 16  17       var colLen = $(rows).eq(1).find("td[class!='hide']").length; 18       var totalVal = []; 19       for (var i = 0; i < colLen; i++) { 20         totalVal.push("0"); 21       } 22       $.each(rows, function (i, item) { 23  24         var tds = $(item).find("td[class!='hide']"); 25         var crossVal = ""; 26         $(tds).each(function (i_td, item_td) { 27  28           var hVal = $(item_td).html(); 29  30           if (isNaN(hVal)) { 31             //非数字 32             totalVal[i_td] = ""; 33           } else { 34             if (totalVal[i_td].length > 0) { 35  36               //数字 37               totalVal[i_td] = (parseFloat(totalVal[i_td]) + parseFloat(hVal)).toFixed(2); 38             } else { crossVal = hVal; } 39           } 40         }); 41       }); 42  43       var totalHtml = []; 44       totalHtml.push("<tr style='background-color:#EBF0EE'>"); 45       for (var i in totalVal) { 46  47         totalHtml.push("<td align=\"center\">"); 48  49         if (i == 0) { 50           totalHtml.push("<font style='color:red'>合计</font>"); 51           totalHtml.push("</td>"); 52           continue; 53         } else if (outCols.length > 0) { 54  55           //表示存在排除列 56           if (("," + outCols + ",").indexOf("," + i + ",") > -1) { 57             totalHtml.push(""); 58             totalHtml.push("</td>"); 59             continue; 60           } 61         } 62         totalHtml.push(totalVal[i]); 63         totalHtml.push("</td>"); 64       } 65       totalHtml.push("</tr>"); 66  67       $("#" + tableId).append(totalHtml.join('')); 68     }, 69  70     //合并列 71     //tableId:table元素的Id 72     //outCols:排除不合并的列,多个使用‘,’隔开 73     uniteTab: function (tableId, col, outCols) { 74  75       if (!outCols) { outCols = ""; } 76       //col-- 需要合并单元格的列 1开始 77       var colLength = col; 78       var tb = document.getElementById(tableId); 79       if (!tb) { return; } 80       tb.style.display = ''; 81       var i = 0; 82       var j = 0; 83       var rowCount = tb.rows.length; //  行数  84       var colCount = tb.rows[0].cells.length; //  列数  85       var obj1 = null; 86       var obj2 = null; 87       //为每个单元格命名  88       for (i = 0; i < rowCount; i++) { 89         for (j = 0; j < colCount; j++) { 90           if (!tb.rows[i].cells[j]) { continue; } 91           tb.rows[i].cells[j].id = tableId + "tb__" + i.toString() + "_" + j.toString(); 92         } 93       } 94       //合并行  95       for (i = 0; i < colCount; i++) { 96         if (i == colLength) break; 97         //排除不合并列 98         if (("," + outCols + ",").indexOf("," + i + ",") > -1) { continue; } 99 100         obj1 = document.getElementById(tableId + "tb__0_" + i.toString())101         for (j = 1; j < rowCount; j++) {102           obj2 = document.getElementById(tableId + "tb__" + j.toString() + "_" + i.toString());103           if (obj1.innerText == obj2.innerText) {104             obj1.rowSpan++;105             obj2.parentNode.removeChild(obj2);106           } else {107             obj1 = document.getElementById(tableId + "tb__" + j.toString() + "_" + i.toString());108           }109         }110       }111 112       //合并列113       for (i = 0; i < rowCount; i++) {114         if (tb.rows[i] != null) {115           colCount = tb.rows[i].cells.length;116           obj1 = document.getElementById(tb.rows[i].cells[0].id);117           if (obj1 != null) {118             for (j = 1; j < colCount; j++) {119               if (j >= colLength) break;120               if (obj1.colSpan >= colLength) break;121 122               if (tb.rows[i].cells[j]) {123                 obj2 = document.getElementById(tb.rows[i].cells[j].id);124 125                 if (obj1.innerText == obj2.innerText) {126                   obj1.colSpan++;127                   obj2.parentNode.removeChild(obj2);128                   j = j - 1;129                 }130                 else {131                   obj1 = obj2;132                   j = j + obj1.rowSpan;133                 }134               }135             }136           }137         }138       }139 140     }141   }142 }143 144 //table 表插件,要求bootstrap.min.css样式145 var tb_choice = function (option) {146 147   //测试用例-列表头148   var header = [149 150           {151             "title": "产品名称",152             "name": "product",153             "type": "label"154           },155           {156             "title": "销售价",157             "name": "sale",158             "type": "label",159           },160           {161             "title": "份数",162             "name": "num",163             "type": "select", //如果是下拉框,这里需要初始值val164             "val": [165                   {166                     "text": 1,167                     "val": 1168                   },169                   {170                     "text": 2,171                     "val": 2172                   },173                   {174                     "text": 3,175                     "val": 3176                   }177             ]178           },179           {180             "title": "总额",181             "name": "amount",182             "type": "label"183           },184           {185             "title": "<input type='checkbox' name='cbAll' style='border: 0px; width: 20px; height: 20px;'/>",  //如果是全选选择框,这里直接写html元素186             "name": "cb",187             "type": "checkbox"188           },189           {190             "title": "保险Id",191             "name": "productid",192             "type": "hidden"193           }194   ];195 196   //测试用例-没行数据197   var data = [198      { "product": "神雕侠侣电影票", "sale": 30.00, "num": 1, "amount": 30.00, "cb": false, "productid": 2 },199      { "product": "摇摇乐门票", "sale": 30.00, "num": 2, "amount": 60.00, "cb": false, "productid": 1 },200      { "product": "神雕侠侣电影票", "sale": 30.00, "num": 1, "amount": 30.00, "cb": true, "productid": 2 },201      { "product": "摇摇乐门票", "sale": 30.00, "num": 3, "amount": 90.00, "cb": false, "productid": 1 }202   ];203 204   data = [205    { "product": { "产品名称": "神雕侠侣电影票", "描述": "神雕侠侣电影票,中秋节大放送,情侣们快来啊,只需一块钱", "销售价  ": 30.00 }, "sale": 30.00, "num": 1, "amount": 30.00, "cb": false, "productid": 1 },206    { "product": { "产品名称": "摇摇乐门票", "描述": "摇摇乐门票,中秋节大放送,情侣们快来啊,只需一块钱", "销售价  ": 30.00 }, "sale": 30.00, "num": 2, "amount": 60.00, "cb": false, "productid": 2 },207    { "product": { "产品名称": "杨过和小龙女电影票", "描述": "杨过和小龙女电影票,中秋节大放送,情侣们快来啊,只需一块钱", "销售价  ": 30.00 }, "sale": 30.00, "num": 1, "amount": 30.00, "cb": true, "productid": 3 },208    { "product": { "产品名称": "四川一日行旅游券", "描述": "小编忙,没时间维护" }, "sale": 30.00, "num": 3, "amount": 90.00, "cb": false, "productid": 4 },209    { "product": { "产品名称": "四川一日行旅游券", "描述": "小编忙,没时间维护" }, "sale": 30.00, "num": 1, "amount": 30.00, "cb": false, "productid": 5 }210   ];211 212   var defOption = {213     id: "",       //要显示插件内容的div的Id      (必需)214     header: header,   //json格式列表头           (必需)215     data: data,     //json格式数据            (必需)216     cbAllName: "cbAll", //全选框Name217     cbName: "cb",    //每行选框Name218     tableId: "table" + new Date().getTime(),  //tableId  默认时间格式219 220     isTotal: false, //是否汇总               (默认不汇总)221     outTotalCols: "", //不汇总列              多个格式如:1,2222     isCombine: false, //是否合并单元格           (默认不合并)223     outCombineCols: "", //不合并单元格列           多个格式如:1,2224     back: function () {225 226       console.log("这里是回调");227     }  //回调函数228   };229 230   $.extend(defOption, option);231 232   var tbArr = [];233   tbArr.push('<table id="{0}" >'.format([defOption.tableId]));234 235   //头部236   tbArr.push('<thead><tr>');237   $.each(defOption.header, function (i, item) {238     tbArr.push("<th {1}>{0}</th>".format([item.title, (item.type == "hidden" ? "class='text-center hide'" : "class='text-center'")]));239   });240   tbArr.push('</tr></thead>');241 242   //内容243   $.each(defOption.data, function (i, item) {244 245     tbArr.push("<tr >");246     $.each(item, function (key, val) {247 248       var htype = [];249       $.each(defOption.header, function (_, headItem) {250 251         if (headItem.name == key) {252 253           switch (headItem.type) {254             case "label":255               if (typeof (val) == "object") {256 257                 //只循环一次258                 $.each(val, function (valKey, valItem) {259                   htype.push("<label data-item='{1}' style=' cursor:pointer'>{0}</label>".format([valItem, JSON.stringify(val)]));260                   return false;261                 });262               } else {263                 htype.push(val);264               }265               break;266             case "select":267               htype.push("<select name='{0}'>".format([key]));268               $.each(headItem.val, function (s_i, s_item) {269                 htype.push("<option value='{1}' {2}>{0}</option>".format([s_item.text, s_item.val, (s_item.val == val ? "selected='selected'" : "")]));270               });271               htype.push("</select>");272               break;273             case "text":274               htype.push("<input name='{1}' type='text' class='form-control' value='{0}'/>".format([val, key]));275               break;276             case "checkbox":277               htype.push("<input type='checkbox' name='{1}' {2} value='{0}' style='border: 0px; width: 20px; height: 20px;'/>".format([val, key, val ? "checked='checked'" : ""]));278               break;279             case "hidden":280               htype.push("<input type='hidden' name='{1}' value='{0}'/>".format([val, key]));281               break;282           }283           tbArr.push("<td style='font-weight: bold;' {1}>{0}</td>".format([htype.join(''), (headItem.type == "hidden" ? "class='hide'" : "class='text-center-vertical'")]));284           return;285         }286       });287     });288     tbArr.push("</tr>");289   });290   tbArr.push('</table>');291 292   $("#" + option.id).html(tbArr.join(''));293 294   //全选绑定295   $("input[type='checkbox'][name='" + defOption.cbAllName + "']").click(function () {296 297     var status = $(this).prop("checked");298     if (status) {299       $("input[type='checkbox'][name='" + defOption.cbName + "']").prop({ "checked": true });300     } else {301       $("input[type='checkbox'][name='" + defOption.cbName + "']").prop({ "checked": false });302     }303   });304   //label明细展示绑定305   $("label[data-item]").click(function () {306     var dataItem = $(this).attr("data-item");307     if (dataItem) {308       var tr = $(this).parent().parent();309       var nextTr = tr.next();310       var nextName = nextTr.attr("data-item");311       if (!nextName) {312 313         var data = JSON.parse(dataItem);314         var childTab = [];315         childTab.push('<table >');316         var childHeader = "";317         var childContent = "";318         $.each(data, function (key, item) {319           childHeader += "<td>{0}</td>".format([key]);320           childContent += "<td>{0}</td>".format([item]);321         });322         childTab.push("<tr>{0}</tr>".format([childHeader]));323         childTab.push("<tr>{0}</tr>".format([childContent]));324         childTab.push("</table>");325 326         var colsLen = tr.find("td[class!='hide']").length;327         var appendDes = $("<tr data-item='{2}'><td colspan='{1}' class='text-left'>{0}</td></tr>".format([childTab.join(''), colsLen, dataItem]));328         tr.after(appendDes);329       } else {330         nextTr.remove();331       }332     }333   });334 335   //单元格操作336   var tbTotal = new tb_total();337   //汇总338   if (defOption.isTotal) { tbTotal.sumTab(defOption.tableId, defOption.outTotalCols); }339   //合并单元格340   if (defOption.isCombine) { tbTotal.uniteTab(defOption.tableId, 2, defOption.outCombineCols); }341 342   //执行回调响应函数343   defOption.back();344 }345 346 String.prototype.format = function (arrVal) {347 348   if (!Array.isArray(arrVal)) { return this; }349 350   var str = this;351   for (var i = 0; i < arrVal.length; i++) {352 353     str = str.replace("{" + i + "}", arrVal[i]);354   }355   return str;356 }

View Code