這次和大家分享的是自己寫的一個table常用幾種展示格式的js插件取名為( table-shenniu
),樣式使用的是bootstrap.min.css,還需要引用jquery.min.js包,這個插件由來的目的是項目中需要一個table格式的送出資料的頁面,功能有合并單元格,隻能送出選中行資料,全選功能,和一個下拉選功能;這幾種功能感覺朋友們肯定都會遇到,是以幹脆封裝一個插件,釋出出來說不定能幫到有些朋友快速完成任務哈哈,當然最主要的還是希望朋友們能互相交流裡面的代碼,邏輯,謝謝;中秋節就要到了,這裡提前預祝大家節日快樂,吃月餅的時候不要忘記了點個贊。
以上是個人的看法,下面來正式分享今天的文章吧:
. 功能介紹與效果圖(認可的點個贊)
. 需求分析
. 插件主要代碼塊的說明
. 擷取table插件資料,并送出給背景
. 插件源碼及幫助文檔
下面一步一個腳印的來分享:
1.普通效果

如圖所示,這裡的乘客可能會選擇不同的産品,同時選擇的購買份數也可能改變,然後隻能送出最後一列勾選上的資料這是上面的圖展示出來的功能資訊;為了更好的使用者體驗顯然“乘客姓名”這一列需要吧相同名稱的資料合并;價格一列通常在使用者填寫表單中不會有彙總的操作,但是資料統計報表中一般就需要;還有這個産品列隻有名稱,沒有更較長的描述資訊,通常我們需要展示出來友善使用者了解更多的資訊,一般我們直接在産品名稱對應的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元素展示出來後,如果作為使用者送出的界面,還需要能擷取出使用者選擇的資料,并送出給背景,我這裡列舉一個簡單的擷取列子,咋們可以直接在回調函數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的擴充方法大家可以分開使用,朋友們有興趣的也可以看幫助文檔,根據文檔上面的頁面列子下載下傳代碼也行;更多的展示效果可以來這裡看文檔
;源碼如下:
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", //如果是下拉框,這裡需要初始值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 }
View Code
git位址:
https://github.com/shenniubuxing3nuget釋出包:
https://www.nuget.org/profiles/shenniubuxing3