WebEnh

.net7 mvc jquery bootstrap json 学习中 第一次学PHP,正在研究中。自学进行时... ... 我的博客 https://enhweb.github.io/ 不错的皮肤:darkgreentrip,iMetro_HD
  首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

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

Posted on 2016-12-20 23:36  WebEnh  阅读(6095)  评论(0编辑  收藏  举报

这次和大家分享的是自己写的一个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",  //如果是下拉框,这里需要初始值val
18                         "val": [
19                                     {
20                                         "text": 1,
21                                         "val": 1
22                                     },
23                                     {
24                                         "text": 2,
25                                         "val": 2
26                                     },
27                                     {
28                                         "text": 3,
29                                         "val": 3
30                                     }
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='");
 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",  //如果是下拉框,这里需要初始值val
164                         "val": [
165                                     {
166                                         "text": 1,
167                                         "val": 1
168                                     },
169                                     {
170                                         "text": 2,
171                                         "val": 2
172                                     },
173                                     {
174                                         "text": 3,
175                                         "val": 3
176                                     }
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",  //全选框Name
217         cbName: "cb",        //每行选框Name
218         tableId: "table" + new Date().getTime(),   //tableId    默认时间格式
219 
220         isTotal: false, //是否汇总                              (默认不汇总)
221         outTotalCols: "",  //不汇总列                            多个格式如:1,2
222         isCombine: false, //是否合并单元格                      (默认不合并)
223         outCombineCols: "", //不合并单元格列                     多个格式如:1,2
224         back: function () {
225 
226             console.log("这里是回调");
227         }   //回调函数
228     };
229 
230     $.extend(defOption, option);
231 
232     var tbArr = [];
233     tbArr.push('<table id="{0}" class="table table-hover table-bordered text-center ">'.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 class="table table-bordered">');
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 }
复制代码
一箱一包行天下,付尽狂名十五年