Repository navigation
Expand file tree
/
Copy pathbootstrap.js
More file actions
3576 lines (3165 loc) · 153 KB
/
Copy pathbootstrap.js
File metadata and controls
3576 lines (3165 loc) · 153 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// @author 文志新 http://jsfiddle.net/wenyi/47nz7ez9/3/
$("#table").bootstrapTable({
url: options.url,
method: options.method || 'get', //请求方式(*)
//toolbar: '#toolbar', //工具按钮用哪个容器
striped: true, //是否显示行间隔色
cache: false, //是否使用缓存,默认为true,所以一般情况下需要设置一下这个属性(*)
pagination: true, //是否显示分页(*)
sortable: false, //是否启用排序
sortOrder: "asc", //排序方式
//: queryParams,//传递参数(*)
pageNumber: 1, //初始化加载第一页,默认第一页
pageSize: 20, //每页的记录行数(*)
pageList: [10, 20, 50, 100], //可供选择的每页的行数(*)
//search: true, //是否显示表格搜索,此搜索是客户端搜索,不会进服务端
//strictSearch: true,
//showColumns: true, //是否显示所有的列
//showRefresh: true, //是否显示刷新按钮
minimumCountColumns: 2, //最少允许的列数
clickToSelect: true, //是否启用点击选中行
uniqueId: "id", //每一行的唯一标识,一般为主键列
showToggle: false, //是否显示详细视图和列表视图的切换按钮
sidePagination: "server", //服务端处理分页
columns: [
{ title: 'id', field: 'id', visible: false, align: 'center', valign: 'middle' },
{ title: '名称', field: 'name', align: 'center', valign: 'middle' }, {
title: '操作',
field: '#',
align: 'center',
formatter: function(value, row, index) {
var e = '<a href="#" mce_href="#" onclick="onEditClick(\'' + row.id + '\',\'' + row.flowProcessId + '\',\'' + '\')">编辑</a> ';
var f = '<a href="#" mce_href="#" onclick="onCopyClick(\'' + row.id + '\')">复制</a> ';
//var d = '<a href="#" mce_href="#" onclick="onDeleteClick(\''+ row.id +'\')">删除</a> ';
return e + f;
}
}
];
});
/**关于插件的通用构造
*
* 构造函数PluginName($trigger,options)传入触发元素和配置项,以便在构造函数中使用触发元素
* 写在$.fn.pluginName外围,通过实例化$ele.data('bootstrap.table',(dat=new BootstrapTable(this,options)));
* 实例写在触发元素的data属性中,以便调用该实例,避免多次创造实例;
* $.fn.pluginName(option)参数是对象,执行构造函数PluginName,产生新的实例;
* 是字符串,调用data对象中存储的实例执行方法,提供PluginName.prototype的方法接口;
* 脱离在构造函数PluginName及其原型中的函数,通过$.fn.pluginName.util暴露接口;
* 构造函数PluginName使用$ele.trigger提供事件接口;
* 配置项可以是html页面data数据配置、或者$.fn.pluginName参数option配置;
* 在html中的配置可以方便使用模板引擎传值实现初始化,后台java使用content.put方法;
* 默认配置项利用js中一切都是对象的特点附着在PluginName.DEFAULTS;
*
* 构造函数渲染页面时,先填充页面内容,再绑定事件;
* 绑定事件通过$ele.off(eventType).on(eventType,function(){})先解绑;
*
* 书写有分支具体情况到主干通用情况,类似先做能力检测,再做浏览器嗅探
* 应用场景,if(){return};抽离分支具体情况,后续代码对主干通用情况进行处理
* 优先检测具体的配置项,其次对自定义配置项、默认配置项作混合处理
* 预先配置子条目的显示情况、数据信息,再次配置父条目的显示情况、配置信息
*
* if语句传递设置的详情变量时,先顺序设置头部,再针对一般情况进行处理,其次设置尾部
* 类似插件作者对分页切换页面按钮的处理,
* 借form、to设置起始页展示情况,再是两串...,最后是尾页的显示情况
*/
/**关于jquery
*
* data:
* $(ele).data()以对象形式返回;
* $(ele).data({url:"www.baidu.com"})以对象形式设置data属性;
* $(ele).data(url,{param:"www.baidu.com"})以对象形式设置某个data属性;
*
* event:
* $.proxy(fn,content)调用的函数fn,传入的上下文content;
* $.Event(eventType,arguments)转化成事件,并传入参数,只可使用一次;
*
* type:
* $.isNumeric()判断变量是否数值型或数字值字符串,包含十六进制等;
* $.isFunction()判断是否为函数;
* $.isEmptyObject()是否为空对象;
* $.isPlainObject()是否为对象字面量;
* $.isArray()是否数组;
* $.type()判断类型;
*
* util:
* $.grep([]/{},function(item,index))过滤出返回值为真的数组;
* $.map([]/{},function(item,index))过滤出以返回值为数组元素的数组;
* $.inArray()返回元素在数组中的位置;
*/
/**bootstrap
* 使用caret样式实现三角形;
*/
/**技巧
*
* 使用数组拼接字符串,这样做方便使用三目运算符处理可变的内容;
*
* i<0?[]:{} 三目运算符后跟数组或对象;
*
* 三目运算符以[]包裹,获取对象的属性;
*
* "paramName".split(/(?=[A-Z])/) 返回结果为["param","Name"];
*
* str.replace(/%s/g, function(){}) 对每个匹配值应用函数;
*
* [{i:1},{j:2}].sort(function(a,b){}) a、b为数组元素;
*
* 非字符串数组对象使用toString()方法转换后,再调用localeCompare进行比较;
*
* ~~利用按位非转化成整数型;
*
* Object.getOwnPropertyNames以数组形式返回对象的属性;
*
* scrollWidth包含超过可视区域的长度;
* clientWidth可视区域不包括滚动条的宽度;
* offsetWidth可视区域包括滚动条的宽度;
* outerWidth包括内边距、边框的宽度;
* outerWidth(true)包括内边距、边框、外边距的宽度;
* innerWidth包括内边距、不包括边框的宽度;
*
* scrollLeft()获取元素的水平偏移,有水平滚动条的情况下;
* scrollTop()获取元素的垂直偏移,有垂直滚动条的情况下;
*
* 通过hide()方法隐藏的元素可以用:hidden伪类获取;
*/
/**关于本插件
*
* html页面配置项写在触发元素的data属性,在$.prototype.bootstrapTable方法将其和默认配置项合并;
* 触发元素还包含表格内容如表头信息,在构造函数Bootstrap中获取该信息,与配置项对应内容合并;
* js配置的优先级高于html页面配置项,initTable获取页面配置thead>tr>th、tbody>tr>td信息;
* 表体渲染通过this.data实现,搜索、排序以及其他方法更新this.data数据,实现数据和显示分离;
*/
! function($) {
'use strict';
/**工具函数
*
* sprintf(str,str1,str2):
* sprintf("<span style='%s'>%s</span>","text-align:center"."hello");
* 顺序替换文本,无替换文本返回空;
*
* getPropertyFromOther(list,from,to,value):
* getPropertyFromOther(this.columns,'field','title',value);
* 获取field属性为value的数组元素的title属性值this.columns[i].title;
* 应用场景:卡片式显示时需要获取标题栏的显示文案信息;
*
* getFieldIndex(columns,field):
* getFieldIndex(this.columns,field);
* 获取field属性为field的数组元素的列坐标this.columns[i].filedIndex;
*
* setFieldIndex(this.options.columns):
* setFieldIndex([[
{colspan:4,title:'Group1'},// 跨几列
{rowspan:3,title:'ID'},// 跨几行
],[
{rowspan:2,title:'Name1'},
{colspan:3,title:'Group2'},
],[
{title:'Name2'},
{title:'Name3'},
{title:'Name4'},
]])
* 为每条标题栏数据设置filedIndex:所在列/列坐标;
* 无field主键的标题栏数据以列坐标作为field属性的值;
* 意义:借用this.columns[fieldIndex]=column构建this.columns;
* this.column的数据格式为{[column]},列坐标相同的标题栏取靠后的子标题;
* 问题:用户配置options.columns时需要顺序写就标题栏,以子标题不跨列为前提,函数的应用场景变得有限;
*
* getScrollBarWidth获取滚动条的宽度;
*
* calculateObjectValue(self,name,args,defaultValue):
* 当name为字符串时,获取window对象的方法并执行,参数为args,没有该方法时调用sprintf替换name文本;
* 当name为函数时,传入args执行该函数;
* 以上情况都不是返回defaultValue;
* 问题:原型中的方法可以直接调用,window对象的方法使用户配置变得繁琐,sprintfy也可以预先作判断后调用;
*
* compareObjects(objectA,objectB,compareLength):
* compareObjects(this.options, options, true);
* 当compareLength为真时,先比较两个对象的长度是否相同,其次比较同名元素是否等值;
* 当compareLength为否时,只比较两个对象的同名元素是否等值;
* 问题:在插件中的使用,依赖this.options和options长度相等,且没有不同名的属性;
*
* escapeHTML转义;
*
* getRealHeight($el):
* 以子元素的最大高度作为父元素的高度;
*
* getRealDataAttr($(this).data()):
* 将paramName的data属性转换成param-name修改完成后输出;
*
* getItemField(item,field,escape):
* 当field为函数时,获取item[field]并进行转义(escape为真的情况下);
* 当field为字符串时,以最末一个点号后的字符获取item中的属性并进行转义(escape为真的情况下);
* 问题:实际应用场景field多是字符串,且没有点号,可以限制用户配置使调用函数变得方便;
*
* isIEBrowser是否IE浏览器或者Trident呈现引擎;
*/
var cachedWidth = null;
// 顺序替换文本,无替换文本返回空;
var sprintf = function(str) {
var args = arguments,
flag = true,
i = 1;
str = str.replace(/%s/g, function() {
var arg = args[i++]; // 对每个匹配值应用函数,i自加1,直到无匹配值;
if (typeof arg === 'undefined') {
flag = false;
return '';
}
return arg;
});
return flag ? str : '';
};
// 获取from属性为value的数组元素的to属性值this.columns[i].to;
// 应用场景,卡片式显示时需要获取标题栏的显示文案信息;
var getPropertyFromOther = function(list, from, to, value) {
var result = '';
$.each(list, function(i, item) {
if (item[from] === value) {
result = item[to];
return false;
}
return true;
});
return result;
};
// 获取field属性为field的数组元素的列坐标thi s.columns[i].filedIndex;
var getFieldIndex = function(columns, field) {
var index = -1;
$.each(columns, function(i, column) {
if (column.field === field) {
index = i;
return false;
}
return true;
});
return index;
};
// 为每条标题栏数据设置filedIndex:所在列/列坐标;
/**
* 策略:
* 表头分割成columns.length(总行数)* totalCol(总列数)的矩阵,以flag矩阵标记为false;
* 各标题栏为columns[i][j].rowspan(行数)* columns[i][j].colspan(列数)的小矩阵;
* 从左自右、从上而下遍历标题栏小矩阵,将标题栏对应的flag首行、首列标记为true;
* 因标题栏只能跨列或跨行,不能既跨行又跨列,函数的实现依赖用户输入正确的options.columns格式,
*/
var setFieldIndex = function(columns) {
var i, j, k,
totalCol = 0,
flag = [];
for (i = 0; i < columns[0].length; i++) {
totalCol += columns[0][i].colspan || 1; // 表格总列数;
}
for (i = 0; i < columns.length; i++) { // columns.length表格总行数;
flag[i] = [];
for (j = 0; j < totalCol; j++) {
flag[i][j] = false;
}
}
for (i = 0; i < columns.length; i++) {
for (j = 0; j < columns[i].length; j++) { // columns[i].length某行共有多少元素;
var r = columns[i][j],
rowspan = r.rowspan || 1,
colspan = r.colspan || 1,
// i为标题栏小矩阵在flag大矩阵的行位置;
// index记录标题栏小矩阵在flag大矩阵的列位置;
index = $.inArray(false, flag[i]);
if (colspan === 1) {
// fieldIndex记录每个标题栏的列坐标信息;
r.fieldIndex = index;
if (typeof r.field === 'undefined') {
r.field = index;
}
}
// 目的是让$.inArray(false, flag[i])正确获取标题栏的列坐标;
for (k = 0; k < rowspan; k++) {
flag[i + k][index] = true;
}
for (k = 0; k < colspan; k++) {
flag[i][index + k] = true;
}
}
}
};
// 获取滚动条的宽度;
var getScrollBarWidth = function() {
if (cachedWidth === null) {
var inner = $('<p/>').addClass('fixed-table-scroll-inner'),
outer = $('<div/>').addClass('fixed-table-scroll-outer'),
w1, w2;
outer.append(inner);
$('body').append(outer);
w1 = inner[0].offsetWidth;
outer.css('overflow', 'scroll');
w2 = inner[0].offsetWidth;
if (w1 === w2) {
w2 = outer[0].clientWidth;
}
outer.remove();
cachedWidth = w1 - w2;
}
return cachedWidth;
};
// 当name为字符串时,获取window对象的方法并执行,参数为args,没有该方法时调用sprintf替换name文本;
// 当name为函数时,传入args执行该函数;以上情况都不是返回defaultValue;
var calculateObjectValue = function(self, name, args, defaultValue) {
var func = name;
if (typeof name === 'string') {
// support obj.func1.func2
var names = name.split('.');
if (names.length > 1) {
func = window;
$.each(names, function(i, f) {
func = func[f];
});
} else {
func = window[name];
}
}
if (typeof func === 'object') {
return func;
}
if (typeof func === 'function') {
return func.apply(self, args);
}
if (!func && typeof name === 'string' && sprintf.apply(this, [name].concat(args))) {
return sprintf.apply(this, [name].concat(args));
}
return defaultValue;
};
// 当compareLength为真时,先比较两个对象的长度是否相同,其次比较同名元素是否等值;
// 当compareLength为否时,只比较两个对象的同名元素是否等值;
var compareObjects = function(objectA, objectB, compareLength) {
var objectAProperties = Object.getOwnPropertyNames(objectA),
objectBProperties = Object.getOwnPropertyNames(objectB),
propName = '';
if (compareLength) {
if (objectAProperties.length !== objectBProperties.length) {
return false;
}
}
for (var i = 0; i < objectAProperties.length; i++) {
propName = objectAProperties[i];
if ($.inArray(propName, objectBProperties) > -1) {
if (objectA[propName] !== objectB[propName]) {
return false;
}
}
}
return true;
};
// 转义;
var escapeHTML = function(text) {
if (typeof text === 'string') {
return text
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/`/g, '`');
}
return text;
};
// 以子元素的最大高度作为父元素的高度;
var getRealHeight = function($el) {
var height = 0;
$el.children().each(function() {
if (height < $(this).outerHeight(true)) {
height = $(this).outerHeight(true);
}
});
return height;
};
// 将paramName的data属性转换成param-name修改完成后输出;
var getRealDataAttr = function(dataAttr) {
for (var attr in dataAttr) {
// "paramName".split(/(?=[A-Z])/) 返回结果为["param","Name"];
var auxAttr = attr.split(/(?=[A-Z])/).join('-').toLowerCase();
if (auxAttr !== attr) {
dataAttr[auxAttr] = dataAttr[attr];
delete dataAttr[attr];
}
}
return dataAttr;
};
// 当field为函数时,获取item[field]并进行转义(escape为真的情况下);
// 当field为字符串时,以最末一个点号后的字符获取item中的属性并进行转义(escape为真的情况下);
var getItemField = function(item, field, escape) {
var value = item;
if (typeof field !== 'string' || item.hasOwnProperty(field)) {
return escape ? escapeHTML(item[field]) : item[field];
}
var props = field.split('.');
for (var p in props) {
value = value && value[props[p]];
}
return escape ? escapeHTML(value) : value;
};
// 浏览器嗅探,匹配IE浏览器或呈现引擎Trident的返回为真;
var isIEBrowser = function() {
return !!(navigator.userAgent.indexOf("MSIE ") > 0 || !!navigator.userAgent.match(/Trident.*rv\:11\./));
};
/**BootstrapTable构造函数
*
* this.$el带table标签的触发元素,置入this.$tableBody中;
* this.$container最外层包裹元素.bootstrap-table;
* this.$toolbar即是.bootstrap-table下工具栏元素.fixed-table-toolbar;
* this.$pagination即是.bootstrap-table下分页元素.fixed-table-pagination;
* this.$tableContainer即是.bootstrap-table下标题内容元素.fixed-table-container;
* this.$tableHeader即是.fixed-table-container下表头元素.fixed-table-header;
* this.$tableBody即是.fixed-table-container下表体元素.fixed-table-body;
* this.$tableFooter即是.fixed-table-container下表尾元素.fixed-table-footer;
* this.$tableLoading即是.fixed-table-container下加载中提示元素.fixed-table-loading;
* this.$header即是this.$el下的thead元素;
* this.$header_即是对this.$header的克隆,置入this.$tableHeader中,目的是实现表体的滚动;
* this.options.toolbar用户自定义按钮组,置入this.$toolbar中;
*
*
*
* this.options.column:
* 混合js和html表头标题栏的配置项,包括thead>tr的class/title/html/rowspan/colspan/data;
* this.columns:
* 以this.columns[fieldIndex]的数组形式[columns]记录表体相应标题栏的数据内容;
* this.options.data:
* 有js配置,获取js配置,没有js配置,获取html配置;
* 包含tbody>tr的id/class/data,tbody>tr>td的html/id/class/rowspan/title/data;
* this.header.fields:
* 以this.header.fields[column.fieldIndex]数组形式[field]记录表体每列数据的主键;
* 应用场景:渲染表体td时由该主键通过getFieldIndex获取this.columns的序号;
* 问题:不能直接通过tr的序号获取this.columns数据,或者VisibleColumns数据???
* this.header.fields剔除了this.columns中visible为false的内容;
* this.header.styles、this.header.classes:
* 数组形式存储经过处理的每列数据的样式、类;
* this.header.formatters、this.header.cellStyles、this.header.sorters:
* 数组形式存储格式化函数、设置单元格样式函数、排序函数,或者window方法名;
* this.header.events、this.header.sortNames、this.header.searchables:
* 数组形式存储表体相应标题栏配置的事件events、上传排序的字段sortName、可搜索searchable;
* 应用场景:由主键名在this.header.fields的位置锁定对应的事件、排序字段、可搜索;
* this.header.stateField:
* 复选框内容上传时的字段名;
* this.data:
* initSearch搜索前以及执行filterBy方法前与this.options.data等值;
* 搜索后this.data为过滤后数据;
* data的数据格式:
对象形式
[{ // 行tr中的属性设置
_data:{
index:1,// 后续会自动赋值为数据在当前页的所在行号
field:field
},
_id:id,
_class:class,
field1:value1,field2:value2, //填充在单元格中的显示内容,从this.header.fields数组中获取属性名field1等
_field_id
}] // 每一行特定的样式、属性根据this.options.rowStyle、this.options.rowAttributes获取
数组形式
[["field","class"]] // 数组形式不能对每一行添加特定的data属性、id以及class
// 特定的的样式、属性仍可由this.options.rowStyle、this.options.rowAttributes获取
// 以及data-index添加行号、data-uniqueId每行的唯一标识,根据options.uniqueId从data中获取
* this.options.data:
* initSearch搜索前以及执行filterBy方法前与this.data等值;
* 搜索后this.data为未过滤数据;
* 应用场景:搜索未使用前后台数据交互,通过this.options.data获取新的过滤数据this.data;
* this.searchText:
* this.text:
* 搜索文本;
* this.filterColumns:
* options.data和this.filterColumns相符则在this.data中保留,不相符则在this.data中删除;
* prototype.filterBy方法使用;
*
*
*
* init():
* 初始化函数;
* initLocale():
* 将语言包添加进配置项this.options中,数据格式是{formatLoadingMessage:fn};
* initContainer():
* 创建包裹元素this.container及其子元素this.$toolbar、this.$pagination等,为表格添加样式;
* initTable():
* 获取页面thead>th、tbody>tr>td的配置内容更新options.columns、options.data配置信息;
* this.columns记录表体每列数据相应标题栏options.column的数据内容;
* initHeader():
* 由options.columns渲染表头,th的data-field记为column[field],data数据记为column;
* 更新this.header记录主键、样式、事件、格式化函数等,以及stateField复选框上传字段;
* 绑定事件,包括点击排序、确认键排序、全选,更新上传数据、页面状态等;
* initFooter():
* 显示隐藏表尾;
* initData(data,type):
* 页面初始化时this.data赋值为this.options.data;
* ajax交互时this.data赋值为后台返回的数据,options.data也同样更新为后台返回的数据;
* type为append时,表格尾部插入数据,this.data、options.data尾部插入相应数据;
* type为prepend时,表格顶部插入数据,this.data、options.data头部插入相应数据;
* 前台分页的情况下调用this.initSort进行排序,后台分页以返回数据为准;
* initSort():
* onSort方法设置点击排序按钮时,更新options.sortName为相应标题栏column.field值;
* initSort由column.field值获得相应的sortName;
* 调用option.sorter函数或window[option.sorter]方法进行比较排序;
* 排序通过更新this.data数据实现,this.data渲染到页面使用initBody方法;
* onSort():
* 点击排序时更新options.sortName为被排序列数据对应column[field]值,以便initSort获取sortName;
* 更新options.sortOrder为被排序列数据对应column[order]值,或保持原有值;
* 触发sort事件,调用initServer获取后台数据,调用initSort排序、initBody渲染表体;
* initToolbar():
* 创建工具栏内的用户自定义按钮组、分页显示隐藏切换按钮、刷新按钮、筛选条目按钮;
* 以及卡片表格显示切换按钮、搜索框,并绑定相应事件;筛选条目时触发column-switch事件;
* onSearch():
* this.text,this.options.text设为搜索框去除两端空白的值,pageNumber设为1;
* 调用initSearch将筛选本地数据;调用updatePagination完成远程交互或单渲染本地数据;
* 触发search事件;
* initSearch():
* 前台分页的情况下,先通过this.filterColumns通过this.option.data过滤this.data数据;
* 再通过搜索文本过滤this.data数据;
* 输入文本和经this.header.formatters或window[this.header.formatters]函数格式化匹配比对;
* initPagination():
* 初始化设置分页相关页面信息显示情况,包括当前页显示第几条到第几天数据,共几条数据,每页显示几条数据;
* 以及每页显示数据量切换按钮,选择页面的分页栏显示情况,再绑定事件;
* 通过options.pageNumber获取this.pageFrom、this.pageTo,以便前台分页时initBody获取相应数据;
* updatePagination():
* 分页点选按钮有disabled属性则返回,无则重新布局分页显示面板;
* maintainSelected为否时,调用resetRows重置复选框为不选中,调用initServer交互或initbody渲染页面;
* initSever由后台返回数据;
* initPagination通过options.pageNumber取得options.pageFrom、options.pageTo;
* initBody由options.pageFrom、options.pageTo获取需要渲染的数据data;
* 触发page-change事件;
* onPageListChange():
* 每页显示数据量下拉列表选中时触发,替换每页显示数据量按钮文本;
* 更新options.pageSize,通过updatePagination调用initPagination设置this.pageFrom、this.pageTo;
* updatePagination再调用initServer完成交互,或initBody渲染本地数据;
* onPageFirst():
* onPagePre():
* onPageNext():
* onPageLast():
* onPageNumber():
* 更新options.pageNumber,通过updatePagination调用initPagination设置this.pageFrom、this.pageTo;
* updatePagination再调用initServer完成交互,或initBody渲染本地数据;
* 跳转分页首页、上一页、下一页、尾页,由页码跳转;
* initBody():
* fixedScroll为否的情况下,表体移动到顶部;
* 通过this.pageFrom、this.pageTo截取this.getData()数据row渲染表体,主要是tbody>tr以及tbody>tr>td;
* tbody>tr的data-index设为行号,class、id为row._class、row._id,样式、data数据通过调用函数获得;
* tbody>tr>td区分为卡片显示和表格显示两类,显示值取经this.header.formatters[i]处理后的row[field];
* tbody>tr>td其他class、id、title、data取row[_field_class]等;
* 绑定展开卡片详情按钮点击事件、复选框点击事件、this.header.events中的事件;
* 触发post-body、pre-body事件;
* initServer():
* 触发ajax事件(可以是用户自定义的方法options.ajax),上传数据格式为:
* {
* searchText:this.searchText,
* sortName:this.options.sortName,
* sortOrder:this.options.sortOrder,
* pageSize/limit:...,
* pageNumber/offset:...,
* [filter]:...,
* query:...
* }
* 上传成功调用this.load方法,触发load-success事件、上传失败load-error事件;
* initSearchText():
* 初始化若设置搜索文本,开启搜索,不支持本地数据搜索;
* getCaret():
* getCaret改变排序点击箭头的上下方向;
* updateSelected():
* 全选以及全不选时改变全选复选框的勾选状态,以及tr行添加或删除selected类;
* updateRows():
* 复选框选中或撤销选中后更新this.data数据中的[this.header.stateField]属性,以便上传复选框的状态;
* resetRows():
* 重置,撤销复选框选中,以及data[i][that.header.stateField]赋值为false;
* trigger():
* 执行this.options[BootstrapTable.EVENTS[name]]函数(类似浏览器默认事件),并触发事件;
* resetHeader():
* 一定时间内触发this.fitHeader函数调整表头的水平偏移,垂直滚动不影响表头;
* fitHeader():
* 克隆this.$el中的表头,置入this.$tableHeader中,出使垂直滚动时,表头信息不变;
* 调整表体水平偏移量和表体水平一致;
* resetFooter():
* 创建表尾,利用footerFormatter填充表尾内容,调用fitFooter调整表尾偏移;
* fitFooter():
* 调整表尾水平偏移,设置表尾fnt-cell的宽度;
* toggleColumn():
* toggleColumn筛选条目下拉菜单点选时更新columns[i].visible信息;
* this.columns信息的改变,重新调用表头、表体、搜索框、分页初始化函数;
* 当勾选的显示条目小于this.options.minimumCountColumns时,勾选的条目设置disabled属性;
* toggleRow():
* 从表体中找到data-index=index或data-uniqueid=uniqueId那一行,根据visible显示或隐藏;
* getVisibleFields():
* 从this.column中剔除visible为false的项,返回field数组;
*/
var BootstrapTable = function(el, options) {
this.options = options;
this.$el = $(el);
this.$el_ = this.$el.clone(); // 记录触发元素的初始值,destroy销毁重置的时候使用
this.timeoutId_ = 0; // 超时计时器,有滚动条的情况下调整表头的水平偏移量
this.timeoutFooter_ = 0; // 超时计时器,有滚动条的情况下调整表尾的水平偏移量
this.init();
};
BootstrapTable.DEFAULTS = {
classes: 'table table-hover', // 触发元素table加入的样式;
locale: undefined, // 设置语言包;
height: undefined, // 包括分页和工具栏的高度;
undefinedText: '-', // 无显示文本时默认显示内容;
sortName: undefined,
// 初始化排序字段名,须是column.filed主键名;
// 点击排序后更新options.sortName为被排序列的主键名columns[i].field;
// 通过主键名在this.header.fields中的序号获取sortName,比较后排序;
sortOrder: 'asc', // 升序或降序,并且上传
striped: false, // 触发元素table是否显示间行色
columns: [
[]
],
data: [],
dataField: 'rows', // 后台传回data数据的字段名
method: 'get', // ajax发送类型
url: undefined, // ajax发送地址
ajax: undefined,
// 用户自定义ajax,或通过字符串调用window下的方法,对插件内配置有较多使用,调用其实不便
cache: true, // ajax设置
contentType: 'application/json',
dataType: 'json',
ajaxOptions: {},
queryParams: function(params) {
return params;
},
queryParamsType: 'limit', // undefined
responseHandler: function(res) {
return res;
}, // ajax处理相应的函数
pagination: false, // 分页是否显示
onlyInfoPagination: false, // 分页文案提示只显示有多少条数据
sidePagination: 'client', // client or server 前台分页或后台分页
totalRows: 0, // server side need to set
pageNumber: 1, // 初始化显示第几页
pageSize: 10, // 初始化页面多少条数据
pageList: [10, 25, 50, 100], // 每页显示数据量切换配置项
paginationHAlign: 'right', //right, left 分页浮动设置
paginationVAlign: 'bottom', //bottom, top, both
// 分页显示位置,上、下或上下均显示,默认底部显示
// 设置一页显示多少条数据按钮向上下拉,还是向下下拉,默认向上下拉
// 设置为both时,分页在上下位置均显示,设置一页显示多少条数据按钮向上下拉
paginationDetailHAlign: 'left', //right, left 每页几行数据显示区浮动情况
paginationPreText: '‹', // 分页上一页按钮
paginationNextText: '›', // 分页下一页按钮
search: false, // 是否支持搜索
searchOnEnterKey: false, // enter键开启搜索
strictSearch: false, // 搜索框输入内容是否须和条目显示数据严格匹配,false时只要键入部分值就可获得结果
searchAlign: 'right', // 搜索框对齐方式
selectItemName: 'btSelectItem', // 复选框默认上传name值
showHeader: true, // 显示表头,包含标题栏内容
showFooter: false, // 默认隐藏表尾
showColumns: false, // 显示筛选条目按钮
showPaginationSwitch: false, // 显示分页隐藏展示按钮
showRefresh: false, // 显示刷新按钮
showToggle: false, // 显示切换表格显示或卡片式详情显示按钮
buttonsAlign: 'right', // 显示隐藏分页、刷新、切换表格或卡片显示模式、筛选显示条目、下载按钮浮动设置
smartDisplay: true,
// smartDisplay为真,当总页数为1时,屏蔽分页,以及下拉列表仅有一条时,屏蔽切换条目数按钮下拉功能
escape: false, // 是否转义
minimumCountColumns: 1, // 最小显示条目数
idField: undefined, // data中存储复选框value的键值
uniqueId: undefined, // 从data中获取每行数据唯一标识的属性名
cardView: false, // 是否显示卡片式详情(移动端),直接罗列每行信息,不以表格形式显现
detailView: false, // 表格显示数据时,每行数据前是否有点击按钮,显示这行数据的卡片式详情
detailFormatter: function(index, row) {
return '';
}, // 折叠式卡片详情渲染文本,包含html标签、Dom元素
trimOnSearch: true, // 搜索框键入值执行搜索时,清除两端空格填入该搜索框
clickToSelect: false,
singleSelect: false, // 复选框只能单选,需设置column.radio属性为真
toolbar: undefined, // 用户自定义按钮组,区别于搜索框、分页切换按钮等
toolbarAlign: 'left', // 用户自定义按钮组浮动情况
checkboxHeader: true, // 为否隐藏全选按钮
sortable: true, // 可排序全局设置
silentSort: true, // ajax交互的时候是否显示loadding加载信息
maintainSelected: false, // 为真且翻页时,保持前一页各条row[that.header.stateField]为翻页前的值
searchTimeOut: 500,
// 搜索框键入值敲enter后隔多少时间执行搜索,使用clearTimeout避免间隔时间内多次键入反复搜索
searchText: '', // 初始化搜索内容
iconSize: undefined, // 按钮、搜索输入框通用大小,使用bootstrap的sm、md、lg等
iconsPrefix: 'glyphicon', // 按钮通用样式
icons: {
paginationSwitchDown: 'glyphicon-collapse-down icon-chevron-down', // 显示分页按钮
paginationSwitchUp: 'glyphicon-collapse-up icon-chevron-up', // 隐藏分页按钮
refresh: 'glyphicon-refresh icon-refresh', // 刷新按钮
toggle: 'glyphicon-list-alt icon-list-alt', // 切换表格详情显示、卡片式显示按钮
columns: 'glyphicon-th icon-th', // 筛选条目按钮
detailOpen: 'glyphicon-plus icon-plus', // 卡片式详情展开按钮
detailClose: 'glyphicon-minus icon-minus' // 卡片式详情折叠按钮
}, // 工具栏按钮具体样式
rowStyle: function(row, index) {
return {};
}, // 传递给每一行的css设置,row为这一行的data数据
rowAttributes: function(row, index) {
return {};
}, // 传递给每一行的属性设置
onAll: function(name, args) {
return false;
},
onClickCell: function(field, value, row, $element) {
return false;
},
onDblClickCell: function(field, value, row, $element) {
return false;
},
onClickRow: function(item, $element) {
return false;
},
onDblClickRow: function(item, $element) {
return false;
},
onSort: function(name, order) {
return false;
},
onCheck: function(row) {
return false;
},
onUncheck: function(row) {
return false;
},
onCheckAll: function(rows) {
return false;
},
onUncheckAll: function(rows) {
return false;
},
onCheckSome: function(rows) {
return false;
},
onUncheckSome: function(rows) {
return false;
},
onLoadSuccess: function(data) {
return false;
},
onLoadError: function(status) {
return false;
},
onColumnSwitch: function(field, checked) {
return false;
},
onPageChange: function(number, size) {
return false;
},
onSearch: function(text) {
return false;
},
onToggle: function(cardView) {
return false;
},
onPreBody: function(data) {
return false;
},
onPostBody: function() {
return false;
},
onPostHeader: function() {
return false;
},
onExpandRow: function(index, row, $detail) {
return false;
},
onCollapseRow: function(index, row) {
return false;
},
onRefreshOptions: function(options) {
return false;
},
onResetView: function() {
return false;
}
};
BootstrapTable.LOCALES = [];
BootstrapTable.LOCALES['en-US'] = BootstrapTable.LOCALES['en'] = {
formatLoadingMessage: function() {
return 'Loading, please wait...';
},
formatRecordsPerPage: function(pageNumber) {
return sprintf('%s records per page', pageNumber);
}, // 每页显示条目数提示文案
formatShowingRows: function(pageFrom, pageTo, totalRows) {
return sprintf('Showing %s to %s of %s rows', pageFrom, pageTo, totalRows);
}, // 分页提示当前页从第pageFrom到pageTo,总totalRows条
formatDetailPagination: function(totalRows) {
return sprintf('Showing %s rows', totalRows); // 分页提示供多少条数据
},
formatSearch: function() {
return 'Search';
}, // 设置搜索框placeholder属性
formatNoMatches: function() {
return 'No matching records found';
}, // 没有数据时提示文案
formatPaginationSwitch: function() {
return 'Hide/Show pagination';
}, // 显示隐藏分页按钮title属性提示文案
formatRefresh: function() {
return 'Refresh';
}, // 刷新按钮title属性提示文案
formatToggle: function() {
return 'Toggle';
}, // 切换表格、卡片显示模式按钮title属性提示文案
formatColumns: function() {
return 'Columns';
}, // 筛选显示条目按钮title属性提示文案
formatAllRows: function() {
return 'All';
}
};
$.extend(BootstrapTable.DEFAULTS, BootstrapTable.LOCALES['en-US']);
BootstrapTable.COLUMN_DEFAULTS = {
radio: false, // 有否radio,有则options.singleSelect设为真
checkbox: false, // 有否checkbox,options.singleSelect设为真,checkbox单选
checkboxEnabled: true, // 复选框是否可选
field: undefined, // 后台数据的id号
title: undefined, // 内容文案
titleTooltip: undefined, // title属性文案
'class': undefined, // 样式
align: undefined, // tbody、thead、tfoot文本对齐情况
halign: undefined, // thead文本对齐情况
falign: undefined, // tfoot文本对齐情况
valign: undefined, // 垂直对齐情况
width: undefined, // 宽度,字符串或数值输入,均转化为"36px"或"10%"形式
sortable: false, // 是否可排序,options.sortable设置为真的时候可用
order: 'asc', // asc, desc
visible: true, // 可见性
switchable: true, // 该条目可以通过筛选条目按钮切换显示状态
clickToSelect: true,
formatter: undefined,
// 以function(field,row,index){}格式化数据,field后台字段,row行数据,index对应row的序号值
// 无配置时以title显示,有配置时以返回值显示
footerFormatter: undefined, // 填充表尾内容
events: undefined, // 数据格式为[{"click element":functionName}],回调中传入(value,row,index)
sorter: undefined, // 调用sorter函数或window[sorter]函数进行排序,高优先级
sortName: undefined, // 进行排序的字段名,用以获取options.data中的数据
cellStyle: undefined,
// 调用cellStyle函数或window[cellStyle]函数添加样式以及类;
// 以function(field,row,index){}设置单元格样式以及样式类,返回数据格式为{classes:"class1 class2",css:{key:value}}
searchable: true, // 设置哪一列的数据元素可搜索
searchFormatter: true,
cardVisible: true // 设为否时,卡片式显示时该列数据不显示
};
BootstrapTable.EVENTS = {
'all.bs.table': 'onAll',
'click-cell.bs.table': 'onClickCell',
'dbl-click-cell.bs.table': 'onDblClickCell',
'click-row.bs.table': 'onClickRow',
'dbl-click-row.bs.table': 'onDblClickRow',
'sort.bs.table': 'onSort',
'check.bs.table': 'onCheck',
'uncheck.bs.table': 'onUncheck',
'check-all.bs.table': 'onCheckAll',
'uncheck-all.bs.table': 'onUncheckAll',
'check-some.bs.table': 'onCheckSome',
'uncheck-some.bs.table': 'onUncheckSome',
'load-success.bs.table': 'onLoadSuccess',
'load-error.bs.table': 'onLoadError',
'column-switch.bs.table': 'onColumnSwitch',
'page-change.bs.table': 'onPageChange',
'search.bs.table': 'onSearch',
'toggle.bs.table': 'onToggle',
'pre-body.bs.table': 'onPreBody',
'post-body.bs.table': 'onPostBody',
'post-header.bs.table': 'onPostHeader',
'expand-row.bs.table': 'onExpandRow',
'collapse-row.bs.table': 'onCollapseRow',
'refresh-options.bs.table': 'onRefreshOptions',
'reset-view.bs.table': 'onResetView'
};
BootstrapTable.prototype.init = function() {
this.initLocale();
// 将语言包添加进配置项this.options中,数据格式是{formatLoadingMessage:fn};
this.initContainer();
// 创建包裹元素this.container及其子元素this.$toolbar、this.$pagination等,为表格添加样式;
this.initTable();
// 获取页面配置更新options.columns、options.data,this.columns记录表体对应标题栏的数据;
this.initHeader();
// 渲染表头,设置this.header数据记录主键等,绑定表头事件,卡片式显示时隐藏表头;
this.initData();
// 页面初始化、后台传值、表头表尾插入数据,更新this.data、options.data,前台分页则排序;
this.initFooter();
// 显示隐藏表尾;
this.initToolbar();
// 创建工具栏按钮组,并绑定事件;
this.initPagination();
// 创建分页相关内容,并绑定事件;
this.initBody();
// 渲染表体,区分卡片显示和表格显示两类,绑定相关事件;
this.initSearchText();
// 初始化若设置搜索文本,开启搜索,不支持本地数据搜索;
this.initServer();
// 上传数据,成功时调用load方法渲染表格;
};
// 将语言包添加进配置项this.options中,数据格式是{formatLoadingMessage:fn};
BootstrapTable.prototype.initLocale = function() {
if (this.options.locale) {
var parts = this.options.locale.split(/-|_/);
parts[0].toLowerCase();
parts[1] && parts[1].toUpperCase();
if ($.fn.bootstrapTable.locales[this.options.locale]) {
$.extend(this.options, $.fn.bootstrapTable.locales[this.options.locale]);
} else if ($.fn.bootstrapTable.locales[parts.join('-')]) {
$.extend(this.options, $.fn.bootstrapTable.locales[parts.join('-')]);
} else if ($.fn.bootstrapTable.locales[parts[0]]) {
$.extend(this.options, $.fn.bootstrapTable.locales[parts[0]]);
}
}
};
// 创建包裹元素this.container及其子元素this.$toolbar、this.$pagination等,为表格添加样式;
BootstrapTable.prototype.initContainer = function() {
this.$container = $([
'<div class="bootstrap-table">',
'<div class="fixed-table-toolbar"></div>',
this.options.paginationVAlign === 'top' || this.options.paginationVAlign === 'both' ?
'<div class="fixed-table-pagination" style="clear: both;"></div>' :
'',
'<div class="fixed-table-container">',
'<div class="fixed-table-header"><table></table></div>',
'<div class="fixed-table-body">',
'<div class="fixed-table-loading">',
this.options.formatLoadingMessage(),
'</div>',
'</div>',
'<div class="fixed-table-footer"><table><tr></tr></table></div>',
this.options.paginationVAlign === 'bottom' || this.options.paginationVAlign === 'both' ?
'<div class="fixed-table-pagination"></div>' :
'',
'</div>',
'</div>'
].join(''));
this.$container.insertAfter(this.$el);
this.$tableContainer = this.$container.find('.fixed-table-container');
this.$tableHeader = this.$container.find('.fixed-table-header');
this.$tableBody = this.$container.find('.fixed-table-body');
this.$tableLoading = this.$container.find('.fixed-table-loading');
this.$tableFooter = this.$container.find('.fixed-table-footer');
this.$toolbar = this.$container.find('.fixed-table-toolbar');
this.$pagination = this.$container.find('.fixed-table-pagination');
this.$tableBody.append(this.$el);
this.$container.after('<div class="clearfix"></div>');
this.$el.addClass(this.options.classes);
if (this.options.striped) {
this.$el.addClass('table-striped');
}
if ($.inArray('table-no-bordered', this.options.classes.split(' ')) !== -1) {
this.$tableContainer.addClass('table-no-bordered');
}
};
// 当html中包含thead>tr>th以及tbody>tr>td时,获取页面的配置项更新options.columns、options.data;
// js配置的优先级高于html页面配置;
// this.columns以this.columns[fieldIndex]的数组形式[columns]记录表体相应标题栏的数据内容;
BootstrapTable.prototype.initTable = function() {
var that = this,