table.js 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012
  1. /**
  2. @Name:layui.table 表格操作
  3. @Author:贤心
  4. @License:MIT
  5. */
  6. layui.define(['laytpl', 'laypage', 'layer', 'form'], function(exports){
  7. "use strict";
  8. var $ = layui.$
  9. ,laytpl = layui.laytpl
  10. ,laypage = layui.laypage
  11. ,layer = layui.layer
  12. ,form = layui.form
  13. ,hint = layui.hint()
  14. ,device = layui.device()
  15. //外部接口
  16. ,table = {
  17. config: {
  18. checkName: 'LAY_CHECKED' //是否选中状态的字段名
  19. ,indexName: 'LAY_TABLE_INDEX' //下标索引名
  20. } //全局配置项
  21. ,cache: {} //数据缓存
  22. ,index: layui.table ? (layui.table.index + 10000) : 0
  23. //设置全局项
  24. ,set: function(options){
  25. var that = this;
  26. that.config = $.extend({}, that.config, options);
  27. return that;
  28. }
  29. //事件监听
  30. ,on: function(events, callback){
  31. return layui.onevent.call(this, MOD_NAME, events, callback);
  32. }
  33. }
  34. //操作当前实例
  35. ,thisTable = function(){
  36. var that = this
  37. ,options = that.config
  38. ,id = options.id;
  39. id && (thisTable.config[id] = options);
  40. return {
  41. reload: function(options){
  42. that.reload.call(that, options);
  43. }
  44. ,config: options
  45. }
  46. }
  47. //字符常量
  48. ,MOD_NAME = 'table', ELEM = '.layui-table', THIS = 'layui-this', SHOW = 'layui-show', HIDE = 'layui-hide', DISABLED = 'layui-disabled'
  49. ,ELEM_VIEW = 'layui-table-view', ELEM_HEADER = '.layui-table-header', ELEM_BODY = '.layui-table-body', ELEM_MAIN = '.layui-table-main', ELEM_FIXED = '.layui-table-fixed', ELEM_FIXL = '.layui-table-fixed-l', ELEM_FIXR = '.layui-table-fixed-r', ELEM_TOOL = '.layui-table-tool', ELEM_SORT = '.layui-table-sort', ELEM_EDIT = 'layui-table-edit', ELEM_HOVER = 'layui-table-hover'
  50. //thead区域模板
  51. ,TPL_HEADER = function(options){
  52. options = options || {};
  53. return ['<table cellspacing="0" cellpadding="0" border="0" class="layui-table" '
  54. ,'{{# if(d.data.skin){ }}lay-skin="{{d.data.skin}}"{{# } }} {{# if(d.data.size){ }}lay-size="{{d.data.size}}"{{# } }} {{# if(d.data.even){ }}lay-even{{# } }}>'
  55. ,'<thead>'
  56. ,'{{# layui.each(d.data.cols, function(i1, item1){ }}'
  57. ,'<tr>'
  58. ,'{{# layui.each(item1, function(i2, item2){ }}'
  59. ,'{{# if(item2.fixed && item2.fixed !== "right"){ left = true; } }}'
  60. ,'{{# if(item2.fixed === "right"){ right = true; } }}'
  61. ,function(){
  62. if(options.fixed && options.fixed !== 'right'){
  63. return '{{# if(item2.fixed && item2.fixed !== "right"){ }}';
  64. }
  65. if(options.fixed === 'right'){
  66. return '{{# if(item2.fixed === "right"){ }}';
  67. }
  68. return '';
  69. }()
  70. ,'{{# if(item2.checkbox){ }}'
  71. ,'<th data-field="{{ item2.field||i2 }}" data-type="checkbox" {{#if(item2.colspan){}} colspan="{{item2.colspan}}"{{#} if(item2.rowspan){}} rowspan="{{item2.rowspan}}"{{#}}} unresize="true"><div class="layui-table-cell laytable-cell-checkbox"><input type="checkbox" name="layTableCheckbox" lay-skin="primary" lay-filter="layTableAllChoose" {{# if(item2[d.data.checkName]){ }}checked{{# }; }}></div></th>'
  72. ,'{{# } else if(item2.space){ }}'
  73. ,'<th data-field="{{ item2.field||i2 }}" {{#if(item2.colspan){}} colspan="{{item2.colspan}}"{{#} if(item2.rowspan){}} rowspan="{{item2.rowspan}}"{{#}}} unresize="true"><div class="layui-table-cell laytable-cell-space"></div></th>'
  74. ,'{{# } else { }}'
  75. ,'<th data-field="{{ item2.field||i2 }}" {{#if(item2.colspan){}} colspan="{{item2.colspan}}"{{#} if(item2.rowspan){}} rowspan="{{item2.rowspan}}"{{#}}} {{# if(item2.unresize){ }}unresize="true"{{# } }}>'
  76. ,'{{# if(item2.colspan > 1){ }}'
  77. ,'<div class="layui-table-cell laytable-cell-group" {{#if(item2.align){}}align="{{item2.align}}"{{#}}}>'
  78. ,'<span>{{item2.title||""}}</span>'
  79. ,'</div>'
  80. ,'{{# } else { }}'
  81. ,'<div class="layui-table-cell laytable-cell-{{d.index}}-{{item2.field||i2}}" {{#if(item2.align){}}align="{{item2.align}}"{{#}}}>'
  82. ,'<span>{{item2.title||""}}</span>'
  83. ,'{{# if(item2.sort){ }}'
  84. ,'<span class="layui-table-sort layui-inline"><i class="layui-edge layui-table-sort-asc"></i><i class="layui-edge layui-table-sort-desc"></i></span>'
  85. ,'{{# } }}'
  86. ,'</div>'
  87. ,'{{# } }}'
  88. ,'</th>'
  89. ,'{{# }; }}'
  90. ,(options.fixed ? '{{# }; }}' : '')
  91. ,'{{# }); }}'
  92. ,'</tr>'
  93. ,'{{# }); }}'
  94. ,'</thead>'
  95. ,'</table>'].join('');
  96. }
  97. //tbody区域模板
  98. ,TPL_BODY = ['<table cellspacing="0" cellpadding="0" border="0" class="layui-table" '
  99. ,'{{# if(d.data.skin){ }}lay-skin="{{d.data.skin}}"{{# } }} {{# if(d.data.size){ }}lay-size="{{d.data.size}}"{{# } }} {{# if(d.data.even){ }}lay-even{{# } }}>'
  100. ,'<tbody></tbody>'
  101. ,'</table>'].join('')
  102. //主模板
  103. ,TPL_MAIN = ['<div class="layui-form layui-border-box {{d.VIEW_CLASS}}" lay-filter="LAY-table-{{d.index}}" style="{{# if(d.data.width){ }}width:{{d.data.width}}px;{{# } }} {{# if(d.data.height){ }}height:{{d.data.height}}px;{{# } }}">'
  104. ,'{{# var left, right; }}'
  105. ,'<div class="layui-table-header">'
  106. ,TPL_HEADER()
  107. ,'</div>'
  108. ,'<div class="layui-table-body layui-table-main">'
  109. ,TPL_BODY
  110. ,'</div>'
  111. ,'{{# if(left){ }}'
  112. ,'<div class="layui-table-fixed layui-table-fixed-l">'
  113. ,'<div class="layui-table-header">'
  114. ,TPL_HEADER({fixed: true})
  115. ,'</div>'
  116. ,'<div class="layui-table-body">'
  117. ,TPL_BODY
  118. ,'</div>'
  119. ,'</div>'
  120. ,'{{# }; }}'
  121. ,'{{# if(right){ }}'
  122. ,'<div class="layui-table-fixed layui-table-fixed-r">'
  123. ,'<div class="layui-table-header">'
  124. ,TPL_HEADER({fixed: 'right'})
  125. ,'<div class="layui-table-mend"></div>'
  126. ,'</div>'
  127. ,'<div class="layui-table-body">'
  128. ,TPL_BODY
  129. ,'</div>'
  130. ,'</div>'
  131. ,'{{# }; }}'
  132. ,'{{# if(d.data.page){ }}'
  133. ,'<div class="layui-table-tool">'
  134. ,'<div class="layui-inline layui-table-page" id="layui-table-page{{d.index}}"></div>'
  135. ,'</div>'
  136. ,'{{# } }}'
  137. ,'<style>'
  138. ,'{{# layui.each(d.data.cols, function(i1, item1){'
  139. ,'layui.each(item1, function(i2, item2){ }}'
  140. ,'.laytable-cell-{{d.index}}-{{item2.field||i2}}{ width:{{item2.width||50}}px }'
  141. ,'{{# });'
  142. ,'}); }}'
  143. ,'</style>'
  144. ,'</div>'].join('')
  145. ,_WIN = $(window)
  146. ,_DOC = $(document)
  147. //构造器
  148. ,Class = function(options){
  149. var that = this;
  150. that.index = ++table.index;
  151. that.config = $.extend({}, that.config, table.config, options);
  152. that.render();
  153. };
  154. //默认配置
  155. Class.prototype.config = {
  156. limit: 30 //每页显示的数量
  157. ,loading: true //请求数据时,是否显示loading
  158. };
  159. //表格渲染
  160. Class.prototype.render = function(sets){
  161. var that = this, options;
  162. if(sets) that.config = sets;
  163. options = that.config;
  164. options.elem = $(options.elem);
  165. options.where = options.where || {};
  166. //请求参数的自定义格式
  167. options.request = $.extend({
  168. pageName: 'page'
  169. ,limitName: 'limit'
  170. }, options.request)
  171. //响应数据的自定义格式
  172. options.response = $.extend({
  173. statusName: 'code'
  174. ,statusCode: 0
  175. ,msgName: 'msg'
  176. ,dataName: 'data'
  177. ,countName: 'count'
  178. }, options.response)
  179. if(!options.elem[0]) return that;
  180. var othis = options.elem
  181. ,hasRender = othis.next('.' + ELEM_VIEW);
  182. if(options.height && /^full-\d+$/.test(options.height)){ //full-差距值
  183. that.fullHeightGap = options.height.split('-')[1];
  184. options.height = _WIN.height() - that.fullHeightGap;
  185. }
  186. //替代元素
  187. var reElem = that.elem = $(laytpl(TPL_MAIN).render({
  188. VIEW_CLASS: ELEM_VIEW
  189. ,data: options
  190. ,index: that.index //索引
  191. }));
  192. options.index = that.index;
  193. //生成替代元素
  194. hasRender[0] && hasRender.remove(); //如果已经渲染,则Rerender
  195. othis.after(reElem);
  196. //各级容器
  197. that.layHeader = reElem.find(ELEM_HEADER);
  198. that.layMain = reElem.find(ELEM_MAIN);
  199. that.layBody = reElem.find(ELEM_BODY);
  200. that.layFixed = reElem.find(ELEM_FIXED);
  201. that.layFixLeft = reElem.find(ELEM_FIXL);
  202. that.layFixRight = reElem.find(ELEM_FIXR);
  203. that.layTool = reElem.find(ELEM_TOOL);
  204. //设置body区域高度
  205. if(options.height){
  206. that.fullSize();
  207. }
  208. //如果多级表头,则填补表头高度
  209. if(options.cols.length > 1){
  210. var th = that.layFixed.find(ELEM_HEADER).find('th');
  211. th.height(that.layHeader.height() - 1 - parseFloat(th.css('padding-top')) - parseFloat(th.css('padding-bottom')));
  212. }
  213. that.pullData(1);
  214. that.events();
  215. };
  216. //表格重载
  217. Class.prototype.reload = function(options){
  218. var that = this;
  219. that.config = $.extend({}, that.config, options);
  220. that.render();
  221. };
  222. //获得数据
  223. Class.prototype.pullData = function(curr, loadIndex){
  224. var that = this
  225. ,options = that.config
  226. ,request = options.request
  227. ,response = options.response
  228. ,sort = function(){
  229. if(typeof options.initSort === 'object'){
  230. that.sort(options.initSort.field, options.initSort.type);
  231. }
  232. };
  233. if(options.url){ //Ajax请求
  234. var params = {};
  235. params[request.pageName] = curr;
  236. params[request.limitName] = options.limit;
  237. $.ajax({
  238. type: options.method || 'get'
  239. ,url: options.url
  240. ,data: $.extend(params, options.where)
  241. ,dataType: 'json'
  242. ,success: function(res){
  243. if(res[response.statusName] != response.statusCode){
  244. that.renderForm();
  245. return that.layMain.html('<div class="layui-none">'+ (res[response.msgName] || '返回的数据状态异常') +'</div>');
  246. }
  247. that.renderData(res, curr, res[response.countName]), sort();
  248. loadIndex && layer.close(loadIndex);
  249. typeof options.done === 'function' && options.done(res, curr, res[response.countName]);
  250. }
  251. ,error: function(e, m){
  252. that.layMain.html('<div class="layui-none">数据接口请求异常</div>');
  253. that.renderForm();
  254. loadIndex && layer.close(loadIndex);
  255. }
  256. });
  257. } else if(options.data && options.data.constructor === Array){ //已知数据
  258. var res = {}
  259. ,startLimit = curr*options.limit - options.limit
  260. res[response.dataName] = options.data.concat().splice(startLimit, options.limit);
  261. res[response.countName] = options.data.length;
  262. that.renderData(res, curr, options.data.length), sort();
  263. typeof options.done === 'function' && options.done(res, curr, res[response.countName]);
  264. }
  265. };
  266. //页码
  267. Class.prototype.page = 1;
  268. //遍历表头
  269. Class.prototype.eachCols = function(callback){
  270. layui.each(this.config.cols, function(i1, item1){
  271. layui.each(item1, function(i2, item3){
  272. callback(i2, item3, [i1, item1]);
  273. });
  274. });
  275. };
  276. //数据渲染
  277. Class.prototype.renderData = function(res, curr, count, sort){
  278. var that = this
  279. ,options = that.config
  280. ,data = res[options.response.dataName] || []
  281. ,trs = []
  282. ,trs_fixed = []
  283. ,trs_fixed_r = []
  284. //渲染视图
  285. ,render = function(){
  286. if(!sort && that.sortKey){
  287. return that.sort(that.sortKey.field, that.sortKey.sort, true);
  288. }
  289. layui.each(data, function(i1, item1){
  290. var tds = [], tds_fixed = [], tds_fixed_r = [];
  291. if(item1.length === 0) return;
  292. if(!sort){
  293. item1[table.config.indexName] = i1;
  294. }
  295. that.eachCols(function(i3, item3){
  296. var content = item1[item3.field||i3];
  297. if(content === undefined || content === null) content = '';
  298. if(item3.colspan > 1) return;
  299. var td = ['<td data-field="'+ (item3.field||i3) +'"'+ function(){
  300. var attr = [];
  301. if(item3.edit) attr.push(' data-edit="true"'); //是否允许单元格编辑
  302. if(item3.align) attr.push(' align="'+ item3.align +'"'); //对齐方式
  303. if(item3.templet) attr.push(' data-content="'+ content +'"'); //自定义模板
  304. if(item3.toolbar) attr.push(' data-off="true"'); //自定义模板
  305. if(item3.event) attr.push(' lay-event="'+ item3.event +'"'); //自定义事件
  306. if(item3.style) attr.push(' style="'+ item3.style +'"'); //自定义样式
  307. return attr.join('');
  308. }() +'>'
  309. ,'<div class="layui-table-cell laytable-cell-'+ function(){
  310. if(item3.checkbox) return 'checkbox';
  311. if(item3.space) return 'space'; //间距
  312. return options.index + '-' + (item3.field||i3);
  313. }() +'">' + function(){
  314. if(item3.checkbox){
  315. return '<input type="checkbox" name="layTableCheckbox" lay-skin="primary" '+ function(){
  316. var checkName = table.config.checkName;
  317. if(item3[checkName]){
  318. item1[checkName] = item3[checkName];
  319. return item3[checkName] ? 'checked' : '';
  320. }
  321. return item1[checkName] ? 'checked' : '';
  322. }() +'>';
  323. }
  324. if(item3.toolbar){
  325. return laytpl($(item3.toolbar).html()||'').render(item1);
  326. }
  327. return item3.templet ? laytpl($(item3.templet).html() || String(content)).render(item1) : content;
  328. }()
  329. ,'</div></td>'].join('');
  330. tds.push(td);
  331. if(item3.fixed && item3.fixed !== 'right') tds_fixed.push(td);
  332. if(item3.fixed === 'right') tds_fixed_r.push(td);
  333. });
  334. trs.push('<tr data-index="'+ i1 +'">'+ tds.join('') + '</tr>');
  335. trs_fixed.push('<tr data-index="'+ i1 +'">'+ tds_fixed.join('') + '</tr>');
  336. trs_fixed_r.push('<tr data-index="'+ i1 +'">'+ tds_fixed_r.join('') + '</tr>');
  337. });
  338. that.layBody.scrollTop(0);
  339. that.layMain.find('tbody').html(trs.join(''));
  340. that.layFixLeft.find('tbody').html(trs_fixed.join(''));
  341. that.layFixRight.find('tbody').html(trs_fixed_r.join(''));
  342. that.renderForm();
  343. that.syncCheckAll();
  344. that.haveInit ? that.scrollPatch() : setTimeout(function(){
  345. that.scrollPatch();
  346. }, 50);
  347. that.haveInit = true;
  348. layer.close(that.tipsIndex);
  349. };
  350. that.key = options.id || options.index;
  351. table.cache[that.key] = data; //记录数据
  352. //排序
  353. if(sort){
  354. return render();
  355. }
  356. if(data.length === 0){
  357. that.renderForm();
  358. that.layFixed.remove();
  359. return that.layMain.html('<div class="layui-none">无数据</div>');
  360. }
  361. render();
  362. //分页
  363. if(options.page){
  364. that.page = curr;
  365. that.count = count;
  366. laypage.render({
  367. elem: 'layui-table-page' + options.index
  368. ,count: count
  369. ,groups: 3
  370. ,limits: options.limits || [10,20,30,40,50,60,70,80,90]
  371. ,limit: options.limit
  372. ,curr: curr
  373. ,layout: ['prev', 'page', 'next', 'skip', 'count', 'limit']
  374. ,prev: '<i class="layui-icon">&#xe603;</i>'
  375. ,next: '<i class="layui-icon">&#xe602;</i>'
  376. ,jump: function(obj, first){
  377. if(!first){
  378. that.page = obj.curr;
  379. options.limit = obj.limit;
  380. that.pullData(obj.curr, that.loading());
  381. }
  382. }
  383. });
  384. that.layTool.find('.layui-table-count span').html(count)
  385. }
  386. };
  387. //渲染表单
  388. Class.prototype.renderForm = function(type){
  389. form.render((type || 'checkbox'), 'LAY-table-'+ this.index);
  390. }
  391. //数据排序
  392. Class.prototype.sort = function(th, type, pull, formEvent){
  393. var that = this
  394. ,field
  395. ,config = that.config
  396. ,filter = config.elem.attr('lay-filter')
  397. ,data = table.cache[that.key], thisData;
  398. //字段匹配
  399. if(typeof th === 'string'){
  400. that.layHeader.find('th').each(function(i, item){
  401. var othis = $(this)
  402. ,_field = othis.data('field');
  403. if(_field === th){
  404. th = othis;
  405. field = _field;
  406. return false;
  407. }
  408. });
  409. }
  410. try {
  411. var field = field || th.data('field');
  412. //如果欲执行的排序已在状态中,则不执行渲染
  413. if(that.sortKey && !pull){
  414. if(field === that.sortKey.field && type === that.sortKey.sort){
  415. return;
  416. }
  417. }
  418. var elemSort = that.layHeader.find('th .laytable-cell-'+ config.index +'-'+ field).find(ELEM_SORT);
  419. that.layHeader.find('th').find(ELEM_SORT).removeAttr('lay-sort'); //清除其它标题排序状态
  420. elemSort.attr('lay-sort', type || null);
  421. that.layFixed.find('th')
  422. } catch(e){
  423. return hint.error('Table modules: Did not match to field');
  424. }
  425. //记录排序索引和类型
  426. that.sortKey = {
  427. field: field
  428. ,sort: type
  429. };
  430. if(type === 'asc'){ //升序
  431. thisData = layui.sort(data, field);
  432. } else if(type === 'desc'){ //降序
  433. thisData = layui.sort(data, field, true);
  434. } else { //清除排序
  435. thisData = layui.sort(data, table.config.indexName);
  436. delete that.sortKey;
  437. }
  438. that.renderData({
  439. data: thisData
  440. }, that.page, that.count, true);
  441. layer.close(that.tipsIndex);
  442. if(formEvent){
  443. layui.event.call(th, MOD_NAME, 'sort('+ filter +')', {
  444. field: field
  445. ,type: type
  446. });
  447. }
  448. };
  449. //请求loading
  450. Class.prototype.loading = function(){
  451. var that = this
  452. ,config = that.config;
  453. if(config.loading && config.url){
  454. return layer.msg('数据请求中', {
  455. icon: 16
  456. ,offset: [
  457. that.elem.offset().top + that.elem.height()/2 - 35 - _WIN.scrollTop() + 'px'
  458. ,that.elem.offset().left + that.elem.width()/2 - 90 - _WIN.scrollLeft() + 'px'
  459. ]
  460. ,anim: -1
  461. ,fixed: false
  462. });
  463. }
  464. };
  465. //同步选中值状态
  466. Class.prototype.setCheckData = function(index, checked){
  467. var that = this
  468. ,config = that.config
  469. ,thisData = table.cache[that.key];
  470. if(!thisData[index]) return;
  471. thisData[index][config.checkName] = checked;
  472. };
  473. //同步全选按钮状态
  474. Class.prototype.syncCheckAll = function(){
  475. var that = this
  476. ,config = that.config
  477. ,checkAllElem = that.layHeader.find('input[name="layTableCheckbox"]')
  478. ,syncColsCheck = function(checked){
  479. that.eachCols(function(i, item){
  480. if(item.checkbox){
  481. item[config.checkName] = checked;
  482. }
  483. });
  484. return checked;
  485. };
  486. if(!checkAllElem[0]) return
  487. if(table.checkStatus(that.key).isAll){
  488. if(!checkAllElem[0].checked){
  489. checkAllElem.prop('checked', true);
  490. that.renderForm();
  491. }
  492. syncColsCheck(true);
  493. } else {
  494. if(checkAllElem[0].checked){
  495. checkAllElem.prop('checked', false);
  496. that.renderForm();
  497. }
  498. syncColsCheck(false);
  499. }
  500. };
  501. //获取cssRule
  502. Class.prototype.getCssRule = function(field, callback){
  503. var that = this
  504. ,style = that.elem.find('style')[0]
  505. ,sheet = style.sheet || style.styleSheet
  506. ,rules = sheet.cssRules || sheet.rules;
  507. layui.each(rules, function(i, item){
  508. if(item.selectorText === ('.laytable-cell-'+ that.index +'-'+ field)){
  509. return callback(item), true;
  510. }
  511. });
  512. };
  513. //尺寸始终铺满
  514. Class.prototype.fullSize = function(){
  515. var that = this
  516. ,options = that.config
  517. ,height = options.height, bodyHeight;
  518. if(that.fullHeightGap){
  519. height = _WIN.height() - that.fullHeightGap;
  520. if(height < 135) height = 135;
  521. that.elem.css('height', height);
  522. }
  523. //tbody区域高度
  524. bodyHeight = parseFloat(height) - parseFloat(that.layHeader.height()) - 1;
  525. if(options.page){
  526. bodyHeight = bodyHeight - parseFloat(that.layTool.outerHeight() + 1);
  527. }
  528. that.layBody.css('height', bodyHeight);
  529. };
  530. //滚动条补丁
  531. Class.prototype.scrollPatch = function(){
  532. var that = this
  533. ,layMainTable = that.layMain.children('table')
  534. ,scollWidth = that.layMain.width() - that.layMain.prop('clientWidth') //纵向滚动条宽度
  535. ,scollHeight = that.layMain.height() - that.layMain.prop('clientHeight'); //横向滚动条高度
  536. if(scollWidth && scollHeight){
  537. if(!that.elem.find('.layui-table-patch')[0]){
  538. var patchElem = $('<th class="layui-table-patch"><div class="layui-table-cell"></div></th>'); //补丁元素
  539. patchElem.find('div').css({
  540. width: scollWidth
  541. });
  542. that.layHeader.eq(0).find('thead tr').append(patchElem)
  543. }
  544. } else {
  545. that.layHeader.eq(0).find('.layui-table-patch').remove();
  546. }
  547. that.layFixed.find(ELEM_BODY).css('height', that.layMain.height() - scollHeight); //固定列区域高度
  548. that.layFixRight[layMainTable.width() > that.layMain.width() ? 'removeClass' : 'addClass'](HIDE); //表格宽度小于容器宽度时,隐藏固定列
  549. that.layFixRight.css('right', scollWidth - 1); //操作栏
  550. };
  551. //事件处理
  552. Class.prototype.events = function(){
  553. var that = this
  554. ,config = that.config
  555. ,_BODY = $('body')
  556. ,dict = {}
  557. ,th = that.layHeader.find('th')
  558. ,resizing
  559. ,ELEM_CELL = '.layui-table-cell'
  560. ,filter = config.elem.attr('lay-filter');
  561. //拖拽调整宽度
  562. th.on('mousemove', function(e){
  563. var othis = $(this)
  564. ,oLeft = othis.offset().left
  565. ,pLeft = e.clientX - oLeft;
  566. if(othis.attr('colspan') > 1 || othis.attr('unresize') || dict.resizeStart){
  567. return;
  568. }
  569. dict.allowResize = othis.width() - pLeft <= 10; //是否处于拖拽允许区域
  570. _BODY.css('cursor', (dict.allowResize ? 'col-resize' : ''));
  571. }).on('mouseleave', function(){
  572. var othis = $(this);
  573. if(dict.resizeStart) return;
  574. _BODY.css('cursor', '');
  575. }).on('mousedown', function(e){
  576. if(dict.allowResize){
  577. var field = $(this).data('field');
  578. e.preventDefault();
  579. dict.resizeStart = true; //开始拖拽
  580. dict.offset = [e.clientX, e.clientY]; //记录初始坐标
  581. that.getCssRule(field, function(item){
  582. dict.rule = item;
  583. dict.ruleWidth = parseFloat(item.style.width);
  584. });
  585. }
  586. });
  587. //拖拽中
  588. _DOC.on('mousemove', function(e){
  589. if(dict.resizeStart){
  590. e.preventDefault();
  591. if(dict.rule){
  592. var setWidth = dict.ruleWidth + e.clientX - dict.offset[0];
  593. dict.rule.style.width = setWidth + 'px';
  594. layer.close(that.tipsIndex);
  595. }
  596. resizing = 1
  597. }
  598. }).on('mouseup', function(e){
  599. if(dict.resizeStart){
  600. dict = {};
  601. _BODY.css('cursor', '');
  602. that.scrollPatch();
  603. }
  604. if(resizing === 2){
  605. resizing = null;
  606. }
  607. });
  608. //排序
  609. th.on('click', function(){
  610. var othis = $(this)
  611. ,elemSort = othis.find(ELEM_SORT)
  612. ,nowType = elemSort.attr('lay-sort')
  613. ,type;
  614. if(!elemSort[0] || resizing === 1) return resizing = 2;
  615. if(nowType === 'asc'){
  616. type = 'desc';
  617. } else if(nowType === 'desc'){
  618. type = null;
  619. } else {
  620. type = 'asc';
  621. }
  622. that.sort(othis, type, null, true);
  623. }).find(ELEM_SORT+' .layui-edge ').on('click', function(e){
  624. var othis = $(this)
  625. ,index = othis.index()
  626. ,field = othis.parents('th').eq(0).data('field')
  627. layui.stope(e);
  628. if(index === 0){
  629. that.sort(field, 'asc', null, true);
  630. } else {
  631. that.sort(field, 'desc', null, true);
  632. }
  633. });
  634. //复选框选择
  635. that.elem.on('click', 'input[name="layTableCheckbox"]+', function(){
  636. var checkbox = $(this).prev()
  637. ,childs = that.layBody.find('input[name="layTableCheckbox"]')
  638. ,index = checkbox.parents('tr').eq(0).data('index')
  639. ,checked = checkbox[0].checked
  640. ,isAll = checkbox.attr('lay-filter') === 'layTableAllChoose';
  641. //全选
  642. if(isAll){
  643. childs.each(function(i, item){
  644. item.checked = checked;
  645. that.setCheckData(i, checked);
  646. });
  647. that.syncCheckAll();
  648. that.renderForm();
  649. } else {
  650. that.setCheckData(index, checked);
  651. that.syncCheckAll();
  652. }
  653. layui.event.call(this, MOD_NAME, 'checkbox('+ filter +')', {
  654. checked: checked
  655. ,data: table.cache[that.key][index]
  656. ,type: isAll ? 'all' : 'one'
  657. });
  658. });
  659. //行事件
  660. that.layBody.on('mouseenter', 'tr', function(){
  661. var othis = $(this)
  662. ,index = othis.index();
  663. that.layBody.find('tr:eq('+ index +')').addClass(ELEM_HOVER)
  664. }).on('mouseleave', 'tr', function(){
  665. var othis = $(this)
  666. ,index = othis.index();
  667. that.layBody.find('tr:eq('+ index +')').removeClass(ELEM_HOVER)
  668. });
  669. //单元格编辑
  670. that.layBody.on('change', '.'+ELEM_EDIT, function(){
  671. var othis = $(this)
  672. ,value = this.value
  673. ,field = othis.parent().data('field')
  674. ,index = othis.parents('tr').eq(0).data('index')
  675. ,data = table.cache[that.key][index];
  676. data[field] = value; //更新缓存中的值
  677. layui.event.call(this, MOD_NAME, 'edit('+ filter +')', {
  678. value: value
  679. ,data: data
  680. ,field: field
  681. });
  682. }).on('blur', '.'+ELEM_EDIT, function(){
  683. var templet
  684. ,othis = $(this)
  685. ,field = othis.parent().data('field')
  686. ,index = othis.parents('tr').eq(0).data('index')
  687. ,data = table.cache[that.key][index];
  688. that.eachCols(function(i, item){
  689. if(item.field == field && item.templet){
  690. templet = item.templet;
  691. }
  692. });
  693. othis.siblings(ELEM_CELL).html(
  694. templet ? laytpl($(templet).html() || this.value).render(data) : this.value
  695. );
  696. othis.parent().data('content', this.value);
  697. othis.remove();
  698. });
  699. //单元格事件
  700. that.layBody.on('click', 'td', function(){
  701. var othis = $(this)
  702. ,field = othis.data('field')
  703. ,elemCell = othis.children(ELEM_CELL);
  704. if(othis.data('off')) return;
  705. //显示编辑框
  706. if(othis.data('edit')){
  707. var input = $('<input class="'+ ELEM_EDIT +'">');
  708. input[0].value = othis.data('content') || elemCell.text();
  709. othis.find('.'+ELEM_EDIT)[0] || othis.append(input);
  710. return input.focus();
  711. }
  712. //如果出现省略,则可查看更多
  713. if(elemCell.prop('scrollWidth') > elemCell.outerWidth()){
  714. that.tipsIndex = layer.tips([
  715. '<div class="layui-table-tips-main" style="margin-top: -'+ (elemCell.height() + 16) +'px;'+ function(){
  716. if(config.size === 'sm'){
  717. return 'padding: 4px 15px; font-size: 12px;';
  718. }
  719. if(config.size === 'lg'){
  720. return 'padding: 14px 15px;';
  721. }
  722. return '';
  723. }() +'">'
  724. ,elemCell.html()
  725. ,'</div>'
  726. ,'<i class="layui-icon layui-table-tips-c">&#x1006;</i>'
  727. ].join(''), elemCell[0], {
  728. tips: [3, '']
  729. ,time: -1
  730. ,anim: -1
  731. ,maxWidth: (device.ios || device.android) ? 300 : 600
  732. ,isOutAnim: false
  733. ,skin: 'layui-table-tips'
  734. ,success: function(layero, index){
  735. layero.find('.layui-table-tips-c').on('click', function(){
  736. layer.close(index);
  737. });
  738. }
  739. });
  740. }
  741. });
  742. //工具条操作事件
  743. that.layBody.on('click', '*[lay-event]', function(){
  744. var othis = $(this)
  745. ,index = othis.parents('tr').eq(0).data('index')
  746. ,tr = that.layBody.find('tr[data-index="'+ index +'"]')
  747. ,ELEM_CLICK = 'layui-table-click'
  748. ,data = table.cache[that.key][index];
  749. layui.event.call(this, MOD_NAME, 'tool('+ filter +')', {
  750. data: table.clearCacheKey(data)
  751. ,event: othis.attr('lay-event')
  752. ,tr: tr
  753. ,del: function(){
  754. table.cache[that.key][index] = [];
  755. tr.remove();
  756. that.scrollPatch();
  757. }
  758. ,update: function(fields){
  759. fields = fields || {};
  760. layui.each(fields, function(key, value){
  761. if(key in data){
  762. var templet;
  763. data[key] = value;
  764. that.eachCols(function(i, item2){
  765. if(item2.field == key && item2.templet){
  766. templet = item2.templet;
  767. }
  768. });
  769. tr.children('td[data-field="'+ key +'"]').children(ELEM_CELL).html(
  770. templet ? laytpl($(templet).html() || value).render(data) : value
  771. );
  772. }
  773. });
  774. }
  775. });
  776. tr.addClass(ELEM_CLICK).siblings('tr').removeClass(ELEM_CLICK);
  777. });
  778. //同步滚动条
  779. that.layMain.on('scroll', function(){
  780. var othis = $(this)
  781. ,scrollLeft = othis.scrollLeft()
  782. ,scrollTop = othis.scrollTop();
  783. that.layHeader.scrollLeft(scrollLeft);
  784. that.layFixed.find(ELEM_BODY).scrollTop(scrollTop);
  785. layer.close(that.tipsIndex);
  786. });
  787. _WIN.on('resize', function(){ //自适应
  788. that.fullSize();
  789. that.scrollPatch();
  790. });
  791. };
  792. //初始化
  793. table.init = function(filter, settings){
  794. settings = settings || {};
  795. var that = this
  796. ,elemTable = filter ? $('table[lay-filter="'+ filter +'"]') : $(ELEM + '[lay-data]')
  797. ,errorTips = 'Table element property lay-data configuration item has a syntax error: ';
  798. //遍历数据表格
  799. elemTable.each(function(){
  800. var othis = $(this), tableData = othis.attr('lay-data');
  801. try{
  802. tableData = new Function('return '+ tableData)();
  803. } catch(e){
  804. hint.error(errorTips + tableData)
  805. }
  806. var cols = [], options = $.extend({
  807. elem: this
  808. ,cols: []
  809. ,data: []
  810. ,skin: othis.attr('lay-skin') //风格
  811. ,size: othis.attr('lay-size') //尺寸
  812. ,even: typeof othis.attr('lay-even') === 'string' //偶数行背景
  813. }, table.config, settings, tableData);
  814. filter && othis.hide();
  815. //获取表头数据
  816. othis.find('thead>tr').each(function(i){
  817. options.cols[i] = [];
  818. $(this).children().each(function(ii){
  819. var th = $(this), itemData = th.attr('lay-data');
  820. try{
  821. itemData = new Function('return '+ itemData)();
  822. } catch(e){
  823. return hint.error(errorTips + itemData)
  824. }
  825. var row = $.extend({
  826. title: th.text()
  827. ,colspan: th.attr('colspan') //列单元格
  828. ,rowspan: th.attr('rowspan') //行单元格
  829. }, itemData);
  830. cols.push(row)
  831. options.cols[i].push(row);
  832. });
  833. });
  834. //获取表体数据
  835. othis.find('tbody>tr').each(function(i1){
  836. var tr = $(this), row = {};
  837. tr.children('td').each(function(i2, item2){
  838. var td = $(this)
  839. ,field = td.data('field');
  840. if(field){
  841. return row[field] = td.html();
  842. }
  843. });
  844. layui.each(cols, function(i3, item3){
  845. var td = tr.children('td').eq(i3);
  846. row[item3.field] = td.html();
  847. });
  848. options.data[i1] = row;
  849. });
  850. table.render(options);
  851. });
  852. return that;
  853. };
  854. //表格选中状态
  855. table.checkStatus = function(id){
  856. var nums = 0
  857. ,arr = []
  858. ,data = table.cache[id];
  859. if(!data) return {};
  860. //计算全选个数
  861. layui.each(data, function(i, item){
  862. if(item[table.config.checkName]){
  863. nums++;
  864. arr.push(table.clearCacheKey(item));
  865. }
  866. });
  867. return {
  868. data: arr //选中的数据
  869. ,isAll: nums === data.length //是否全选
  870. };
  871. };
  872. //表格重载
  873. thisTable.config = {};
  874. table.reload = function(id, options){
  875. var config = thisTable.config[id];
  876. if(!config) return hint.error('The ID option was not found in the table instance');
  877. return table.render($.extend({}, config, options));
  878. };
  879. //核心入口
  880. table.render = function(options){
  881. var inst = new Class(options);
  882. return thisTable.call(inst);
  883. };
  884. //清除临时Key
  885. table.clearCacheKey = function(data){
  886. data = $.extend({}, data);
  887. delete data[table.config.checkName];
  888. delete data[table.config.indexName];
  889. return data;
  890. };
  891. //自动完成渲染
  892. table.init();
  893. exports(MOD_NAME, table);
  894. });