treeTable.js 55 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388
  1. /**
  2. * 树形表格 2.x
  3. * date:2019-11-08 License By http://easyweb.vip
  4. */
  5. layui.define(['layer', 'laytpl', 'form'], function (exports) {
  6. var $ = layui.jquery;
  7. var layer = layui.layer;
  8. var laytpl = layui.laytpl;
  9. var form = layui.form;
  10. var device = layui.device();
  11. var MOD_NAME = 'treeTable'; // 绑定事件的模块名
  12. layui.link(layui.cache.base + 'treeTable/treeTable.css');
  13. /** TreeTable类构造方法 */
  14. var TreeTable = function (options) {
  15. // 表格默认参数
  16. var defaultOption = {
  17. elem: undefined, // table容器
  18. data: [], // 数据
  19. cols: [], // 列配置
  20. reqData: undefined, // 异步加载数据的方法
  21. width: undefined, // 容器宽度
  22. height: undefined, // 容器高度
  23. cellMinWidth: 100, // 单元格最小宽度
  24. skin: undefined, // 表格风格
  25. size: undefined, // 表格尺寸
  26. even: undefined, // 是否开启隔行变色
  27. style: undefined, // 容器样式
  28. getThead: function () { // 获取表头
  29. return getThead(this);
  30. },
  31. getAllChooseBox: function () { // 获取全选按钮
  32. return getAllChooseBox(this);
  33. },
  34. getColgroup: function () { // 获取colgroup
  35. return getColgroup(this);
  36. },
  37. getTbWidth: function () { // 计算table的宽度
  38. return getTbWidth(this);
  39. },
  40. tree: {},
  41. text: {}
  42. };
  43. // 默认tree参数
  44. var treeDefaultOption = {
  45. idName: 'id', // id的字段名
  46. pidName: 'pid', // pid的字段名
  47. childName: 'children', // children的字段名
  48. haveChildName: 'haveChild', // 是否有children标识的字段名
  49. openName: 'open', // 是否默认展开的字段名
  50. isPidData: false, // 是否是pid形式的数据
  51. iconIndex: 0, // 图标列的索引
  52. arrowType: undefined, // 箭头类型
  53. onlyIconControl: false, // 仅允许点击图标折叠
  54. getIcon: function (d) { // 自定义图标
  55. return getIcon(d, this);
  56. }
  57. };
  58. // 默认提示文本
  59. var textDefaultOption = {
  60. none: '<div style="padding: 15px 0;">暂无数据</div>' // 空文本提示文字
  61. };
  62. this.options = $.extend(defaultOption, options);
  63. this.options.tree = $.extend(treeDefaultOption, options.tree);
  64. this.options.text = $.extend(textDefaultOption, options.text);
  65. for (var i = 0; i < options.cols.length; i++) {
  66. // 列默认参数
  67. var colDefaultOption = {
  68. field: undefined, // 字段名
  69. title: undefined, // 标题
  70. align: undefined, // 对齐方式
  71. templet: undefined, // 自定义模板
  72. toolbar: undefined, // 工具列
  73. width: undefined, // 宽度
  74. type: undefined, // 列类型
  75. style: undefined, // 单元格样式
  76. fixed: undefined, // 固定列
  77. unresize: false // 关闭拖拽列宽
  78. };
  79. this.options.cols[i] = $.extend(colDefaultOption, options.cols[i]);
  80. }
  81. this.init(); // 初始化表格
  82. this.bindEvents(); // 绑定事件
  83. };
  84. /** 初始化表格 */
  85. TreeTable.prototype.init = function () {
  86. var options = this.options;
  87. var tbFilter = options.elem.substring(1); // 树表格的filter
  88. var $elem = $(options.elem); // 原始表格
  89. // 生成树表格dom
  90. $elem.removeAttr('lay-filter');
  91. $elem.next('.ew-tree-table').remove();
  92. var viewHtml = '<div class="layui-form ew-tree-table" style="' + (options.style || '') + '">';
  93. viewHtml += ' <div class="ew-tree-table-group">';
  94. viewHtml += ' <div class="ew-tree-table-head">';
  95. viewHtml += ' <div class="ew-tree-table-border bottom"></div>';
  96. viewHtml += ' <table class="layui-table"></table>';
  97. viewHtml += ' </div>';
  98. viewHtml += ' <div class="ew-tree-table-box">';
  99. viewHtml += ' <table class="layui-table"></table>';
  100. viewHtml += ' <div class="ew-tree-table-loading"><i class="layui-icon layui-anim layui-anim-rotate layui-anim-loop">&#xe63d;</i></div>';
  101. viewHtml += ' <div class="ew-tree-table-empty" style="display: none;">' + (options.text.none || '') + '</div>';
  102. viewHtml += ' </div>';
  103. viewHtml += ' </div>';
  104. viewHtml += ' <div class="ew-tree-table-border top"></div>';
  105. viewHtml += ' <div class="ew-tree-table-border left"></div>';
  106. viewHtml += ' <div class="ew-tree-table-border right"></div>';
  107. viewHtml += ' <div class="ew-tree-table-border bottom"></div>';
  108. viewHtml += ' </div>';
  109. $elem.after(viewHtml);
  110. // 获取各个组件
  111. var components = this.getComponents();
  112. var $view = components.$view; // 容器
  113. $view.attr('lay-filter', tbFilter);
  114. var $group = components.$group; // 表格容器
  115. var $tbBox = components.$tbBox; // 表格主体部分容器
  116. var $table = components.$table; // 主体表格
  117. var $headTb = components.$headTb; // 表头表格
  118. var $tbEmpty = components.$tbEmpty; // 空视图
  119. var $tbLoading = components.$tbLoading; // 空视图
  120. // 基础参数设置
  121. options.width && $view.css('width', options.width);
  122. options.skin && $table.attr('lay-skin', options.skin);
  123. options.size && $table.attr('lay-size', options.size);
  124. options.even && $table.attr('lay-even', options.even);
  125. // 容器边框调整
  126. if (device.ie) {
  127. $view.find('.ew-tree-table-border.bottom').css('height', '1px');
  128. $view.find('.ew-tree-table-border.right').css('width', '1px');
  129. }
  130. // 计算表格宽度
  131. var tbWidth = options.getTbWidth();
  132. $tbBox.css('min-width', tbWidth.minWidth);
  133. $headTb.parent().css('min-width', tbWidth.minWidth);
  134. if (tbWidth.setWidth) {
  135. $tbBox.css('width', tbWidth.width);
  136. $headTb.parent().css('width', tbWidth.width);
  137. }
  138. // 渲染表结构及表头
  139. var colgroupHtmlStr = options.getColgroup();
  140. var headHtmlStr = colgroupHtmlStr + '<thead>' + options.getThead() + '</thead>';
  141. if (options.height) { // 固定表头
  142. $table.html(colgroupHtmlStr + '<tbody></tbody>');
  143. $headTb.html(headHtmlStr);
  144. $table.css('margin-top', '-1px');
  145. if (options.height.indexOf('full-') == 0) { // 差值高度
  146. var h = parseFloat(options.height.substring(5));
  147. var cssStr = '<style>.ew-tree-table > .ew-tree-table-group > .ew-tree-table-box {';
  148. cssStr += ' height: ' + (getPageHeight() - h) + 'px;';
  149. cssStr += ' height: -moz-calc(100vh - ' + h + 'px);';
  150. cssStr += ' height: -webkit-calc(100vh - ' + h + 'px);';
  151. cssStr += ' height: calc(100vh - ' + h + 'px);';
  152. cssStr += ' }</style>';
  153. $tbBox.after(cssStr);
  154. $tbBox.attr('ew-tree-full', h);
  155. } else { // 固定高度
  156. $tbBox.css('height', options.height);
  157. }
  158. } else {
  159. $table.html(headHtmlStr + '<tbody></tbody>');
  160. }
  161. form.render('checkbox', tbFilter); // 渲染表头的表单元素
  162. // 渲染数据
  163. if (options.reqData) { // 异步加载
  164. this.renderBodyAsync();
  165. } else { // 一次性渲染
  166. if (options.data && options.data.length > 0) {
  167. // 处理数据
  168. if (options.tree.isPidData) { // pid形式数据
  169. treeTb.pidToChildren(options.data, options.tree.idName, options.tree.pidName, options.tree.childName);
  170. } else { // children形式数据
  171. addPidField(options.data, options.tree);
  172. }
  173. $table.children('tbody').html(this.renderBody(options.data));
  174. $tbLoading.hide();
  175. this.renderNumberCol(); // 渲染序号列
  176. form.render(null, tbFilter); // 渲染表单元素
  177. this.checkChooseAllCB(); // 联动全选框
  178. updateFixedTbHead($view);
  179. } else {
  180. $tbLoading.hide();
  181. $tbEmpty.show();
  182. }
  183. }
  184. };
  185. /** 绑定各项事件 */
  186. TreeTable.prototype.bindEvents = function () {
  187. var that = this;
  188. var options = this.options;
  189. var components = this.getComponents();
  190. var $view = components.$view;
  191. var $table = components.$table;
  192. var $tbEmpty = components.$tbEmpty;
  193. var tbFilter = components.tbFilter;
  194. var checkboxFilter = components.checkboxFilter;
  195. var radioFilter = components.radioFilter;
  196. var cbAllFilter = components.cbAllFilter;
  197. var $tbody = $table.children('tbody');
  198. /** 行事件公共返回对象 */
  199. var commonMember = function (ext) {
  200. var $tr = $(this);
  201. if (!$tr.is('tr')) {
  202. $tr = $tr.parentsUntil('tr[data-id]').parent();
  203. }
  204. var id = $tr.data('id');
  205. var data = getDataById(options.data, id, options.tree);
  206. var obj = {
  207. tr: $tr, // 当前行
  208. data: data, //当前行数据
  209. del: function () { // 删除行
  210. var indent = parseInt(this.tr.data('indent'));
  211. this.tr.nextAll('tr').each(function () {
  212. if (parseInt($(this).data('indent')) <= indent) {
  213. return false;
  214. }
  215. $(this).remove();
  216. });
  217. var $parentTr = this.tr.prevAll('tr');
  218. this.tr.remove();
  219. delDataById(options.data, id, options.tree);
  220. if (!options.data || options.data.length <= 0) {
  221. $tbEmpty.show();
  222. }
  223. that.renderNumberCol(); // 渲染序号列
  224. // 联动父级
  225. $parentTr.each(function () {
  226. var tInd = parseInt($(this).data('indent'));
  227. if (tInd < indent) {
  228. that.checkParentCB($(this));
  229. indent = tInd;
  230. }
  231. });
  232. that.checkChooseAllCB(); // 联动全选框
  233. },
  234. update: function (fields) { // 修改行
  235. data = $.extend(data, fields);
  236. var indent = parseInt(this.tr.data('indent'));
  237. that.renderBodyTr(data, indent, undefined, this.tr);
  238. form.render(null, tbFilter); // 渲染表单元素
  239. that.checkIndeterminateCB(); // 恢复半选框状态
  240. that.checkChooseAllCB(); // 联动全选框
  241. }
  242. };
  243. return $.extend(obj, ext);
  244. };
  245. // 绑定折叠展开事件
  246. $tbody.off('click.fold').on('click.fold', '.ew-tree-pack', function (e) {
  247. layui.stope(e);
  248. var $tr = $(this).parent().parent();
  249. if ($tr.hasClass('ew-tree-table-loading')) { // 已是加载中
  250. return;
  251. }
  252. var haveChild = $tr.data('have-child');
  253. if (haveChild != true && haveChild != 'true') { // 子节点
  254. return;
  255. }
  256. var id = $tr.data('id');
  257. var isOpen = $tr.hasClass('ew-tree-table-open');
  258. var data = getDataById(options.data, id, options.tree);
  259. if (!isOpen && (!data[options.tree.childName] || data[options.tree.childName].length <= 0)) {
  260. that.renderBodyAsync(data, $tr);
  261. } else {
  262. toggleRow($tr);
  263. }
  264. });
  265. // 绑定lay-event事件
  266. $tbody.off('click.tool').on('click.tool', '*[lay-event]', function (e) {
  267. layui.stope(e);
  268. var $this = $(this);
  269. layui.event.call(this, MOD_NAME, 'tool(' + tbFilter + ')', commonMember.call(this, {
  270. event: $this.attr('lay-event')
  271. }));
  272. });
  273. // 绑定单选框事件
  274. form.on('radio(' + radioFilter + ')', function (data) {
  275. var d = getDataById(options.data, data.value, options.tree);
  276. that.removeAllChecked();
  277. d.LAY_CHECKED = true; // 同时更新数据
  278. layui.event.call(this, MOD_NAME, 'checkbox(' + tbFilter + ')', {checked: true, data: d, type: 'one'});
  279. });
  280. // 绑定复选框事件
  281. form.on('checkbox(' + checkboxFilter + ')', function (data) {
  282. var checked = data.elem.checked;
  283. var $cb = $(data.elem);
  284. var $layCb = $cb.next('.layui-form-checkbox');
  285. // 如果是半选状态,点击全选
  286. if (!checked && $layCb.hasClass('ew-form-indeterminate')) {
  287. checked = true;
  288. $cb.prop('checked', checked);
  289. $cb.data('indeterminate', 'false');
  290. $layCb.addClass('layui-form-checked');
  291. $layCb.removeClass('ew-form-indeterminate');
  292. }
  293. var d = getDataById(options.data, data.value, options.tree);
  294. d.LAY_CHECKED = checked; // 同时更新数据
  295. // 联动操作
  296. var $tr = $cb.parentsUntil('tr').parent();
  297. if (d[options.tree.childName] && d[options.tree.childName].length > 0) {
  298. that.checkSubCB($tr, checked); // 联动子级
  299. }
  300. var indent = parseInt($tr.data('indent'));
  301. $tr.prevAll('tr').each(function () {
  302. var tInd = parseInt($(this).data('indent'));
  303. if (tInd < indent) {
  304. that.checkParentCB($(this)); // 联动父级
  305. indent = tInd;
  306. }
  307. });
  308. that.checkChooseAllCB(); // 联动全选框
  309. // 回调事件
  310. layui.event.call(this, MOD_NAME, 'checkbox(' + tbFilter + ')', {
  311. checked: checked,
  312. data: d,
  313. type: 'one'
  314. });
  315. });
  316. // 绑定全选复选框事件
  317. form.on('checkbox(' + cbAllFilter + ')', function (data) {
  318. var checked = data.elem.checked;
  319. var $cb = $(data.elem);
  320. var $layCb = $cb.next('.layui-form-checkbox');
  321. if (!options.data || options.data.length <= 0) { // 如果数据为空
  322. $cb.prop('checked', false);
  323. $cb.data('indeterminate', 'false');
  324. $layCb.removeClass('layui-form-checked ew-form-indeterminate');
  325. return;
  326. }
  327. // 如果是半选状态,点击全选
  328. if (!checked && $layCb.hasClass('ew-form-indeterminate')) {
  329. checked = true;
  330. $cb.prop('checked', checked);
  331. $cb.data('indeterminate', 'false');
  332. $layCb.addClass('layui-form-checked');
  333. $layCb.removeClass('ew-form-indeterminate');
  334. }
  335. layui.event.call(this, MOD_NAME, 'checkbox(' + tbFilter + ')', {
  336. checked: checked,
  337. data: undefined,
  338. type: 'all'
  339. });
  340. that.checkSubCB($table.children('tbody'), checked); // 联动操作
  341. });
  342. // 绑定行单击事件
  343. $tbody.off('click.row').on('click.row', 'tr', function () {
  344. layui.event.call(this, MOD_NAME, 'row(' + tbFilter + ')', commonMember.call(this, {}));
  345. });
  346. // 绑定行双击事件
  347. $tbody.off('dblclick.rowDouble').on('dblclick.rowDouble', 'tr', function () {
  348. layui.event.call(this, MOD_NAME, 'rowDouble(' + tbFilter + ')', commonMember.call(this, {}));
  349. });
  350. // 绑定单元格点击事件
  351. $tbody.off('click.cell').on('click.cell', 'td', function (e) {
  352. var $td = $(this);
  353. var type = $td.data('type');
  354. // 判断是否是复选框、单选框列
  355. if (type == 'checkbox' || type == 'radio') {
  356. layui.stope(e);
  357. return;
  358. }
  359. var edit = $td.data('edit');
  360. var field = $td.data('field');
  361. if (edit) { // 开启了单元格编辑
  362. layui.stope(e);
  363. if ($tbody.find('.ew-tree-table-edit').length > 0) {
  364. return;
  365. }
  366. var index = $td.data('index');
  367. var indentSize = $td.children('.ew-tree-table-indent').length;
  368. var id = $td.parent().data('id');
  369. var d = getDataById(options.data, id, options.tree);
  370. if ('text' == edit || 'number' == edit) { // 文本框
  371. var $input = $('<input type="' + edit + '" class="layui-input ew-tree-table-edit"/>');
  372. $input[0].value = d[field];
  373. $td.append($input);
  374. $input.focus();
  375. $input.blur(function () {
  376. var value = $(this).val();
  377. if (value == d[field]) {
  378. $(this).remove();
  379. return;
  380. }
  381. var rs = layui.event.call(this, MOD_NAME, 'edit(' + tbFilter + ')', commonMember.call(this, {
  382. value: value,
  383. field: field
  384. }));
  385. if (rs == false) {
  386. $(this).addClass('layui-form-danger');
  387. $(this).focus();
  388. } else {
  389. d[field] = value; // 同步更新数据
  390. that.renderBodyTd(d, indentSize, index, $td); // 更新单元格
  391. }
  392. });
  393. } else {
  394. console.error('不支持的单元格编辑类型:' + edit);
  395. }
  396. } else { // 回调单元格点击事件
  397. var rs = layui.event.call(this, MOD_NAME, 'cell(' + tbFilter + ')', commonMember.call(this, {
  398. td: $td,
  399. field: field
  400. }));
  401. if (rs == false) {
  402. layui.stope(e);
  403. }
  404. }
  405. });
  406. // 绑定单元格双击事件
  407. $tbody.off('dblclick.cellDouble').on('dblclick.cellDouble', 'td', function (e) {
  408. var $td = $(this);
  409. var type = $td.data('type');
  410. // 判断是否是复选框、单选框列
  411. if (type == 'checkbox' || type == 'radio') {
  412. layui.stope(e);
  413. return;
  414. }
  415. var edit = $td.data('edit');
  416. var field = $td.data('field');
  417. if (edit) { // 开启了单元格编辑
  418. layui.stope(e);
  419. } else { // 回调单元格双击事件
  420. var rs = layui.event.call(this, MOD_NAME, 'cellDouble(' + tbFilter + ')', commonMember.call(this, {
  421. td: $td,
  422. field: field
  423. }));
  424. if (rs == false) {
  425. layui.stope(e);
  426. }
  427. }
  428. });
  429. // 列宽拖拽调整
  430. /*$view.off('mousedown.resize').on('mousedown.resize', '.ew-tb-resize', function (e) {
  431. layui.stope(e);
  432. var index = $(this).parent().data('index');
  433. $(this).data('move', 'true');
  434. $(this).data('x', e.clientX);
  435. var w = $(this).parent().parent().parent().parent().children('colgroup').children('col').eq(index).attr('width');
  436. $(this).data('width', w);
  437. });
  438. $view.off('mousemove.resize').on('mousemove.resize', '.ew-tb-resize', function (e) {
  439. layui.stope(e);
  440. var move = $(this).data('move');
  441. if ('true' == move) {
  442. var x = $(this).data('x');
  443. var w = $(this).data('width');
  444. var index = $(this).parent().data('index');
  445. var nw = parseFloat(w) + e.clientX - parseFloat(x);
  446. $(this).parent().parent().parent().parent().children('colgroup').children('col').eq(index).attr('width', nw);
  447. }
  448. });
  449. $view.off('mouseup.resize').on('mouseup.resize', '.ew-tb-resize', function (e) {
  450. layui.stope(e);
  451. $(this).data('move', 'false');
  452. });
  453. $view.off('mouseleave.resize').on('mouseleave.resize', '.ew-tb-resize', function (e) {
  454. layui.stope(e);
  455. $(this).data('move', 'false');
  456. });*/
  457. };
  458. /** 获取各个组件 */
  459. TreeTable.prototype.getComponents = function () {
  460. var $view = $(this.options.elem).next(); // 容器
  461. var $group = $view.children('.ew-tree-table-group'); // 表格容器
  462. var $tbBox = $group.children('.ew-tree-table-box'); // 表格主体部分容器
  463. var $table = $tbBox.children('.layui-table'); // 主体表格
  464. var $headTb = $group.children('.ew-tree-table-head').children('.layui-table'); // 表头表格
  465. var $tbEmpty = $tbBox.children('.ew-tree-table-empty'); // 空视图
  466. var $tbLoading = $tbBox.children('.ew-tree-table-loading'); // 加载视图
  467. var tbFilter = $view.attr('lay-filter'); // 容器filter
  468. var checkboxFilter = 'ew_tb_checkbox_' + tbFilter; // 复选框filter
  469. var radioFilter = 'ew_tb_radio_' + tbFilter; // 单选框filter
  470. var cbAllFilter = 'ew_tb_choose_all_' + tbFilter; // 全选按钮filter
  471. return {
  472. $view: $view,
  473. $group: $group,
  474. $tbBox: $tbBox,
  475. $table: $table,
  476. $headTb: $headTb,
  477. $tbEmpty: $tbEmpty,
  478. $tbLoading: $tbLoading,
  479. tbFilter: tbFilter,
  480. checkboxFilter: checkboxFilter,
  481. radioFilter: radioFilter,
  482. cbAllFilter: cbAllFilter
  483. };
  484. };
  485. /**
  486. * 递归渲染表格主体部分
  487. * @param data 数据列表
  488. * @param indentSize 缩进大小
  489. * @param isHide 是否默认隐藏
  490. * @returns {string}
  491. */
  492. TreeTable.prototype.renderBody = function (data, indentSize, isHide) {
  493. var options = this.options;
  494. var treeOption = options.tree;
  495. indentSize || (indentSize = 0);
  496. var htmlStr = '';
  497. for (var i = 0; i < data.length; i++) {
  498. var d = data[i];
  499. htmlStr += this.renderBodyTr(d, indentSize, isHide);
  500. // 递归渲染子集
  501. var children = d[treeOption.childName];
  502. if (children && children.length > 0) {
  503. htmlStr += this.renderBody(children, indentSize + 1, !d[treeOption.openName]);
  504. }
  505. }
  506. return htmlStr;
  507. };
  508. /**
  509. * 渲染一行数据
  510. * @param d 行数据
  511. * @param option 配置
  512. * @param indentSize 缩进大小
  513. * @param isHide 是否隐藏
  514. * @param $tr
  515. * @returns {string}
  516. */
  517. TreeTable.prototype.renderBodyTr = function (d, indentSize, isHide, $tr) {
  518. var options = this.options;
  519. var cols = options.cols;
  520. var treeOption = options.tree;
  521. indentSize || (indentSize = 0);
  522. var htmlStr = '';
  523. var haveChild = getHaveChild(d, treeOption);
  524. if ($tr) {
  525. $tr.data('pid', d[treeOption.pidName] || '');
  526. $tr.data('have-child', haveChild);
  527. $tr.data('indent', indentSize);
  528. $tr.removeClass('ew-tree-table-loading');
  529. } else {
  530. var classNames = '';
  531. if (haveChild && d[treeOption.openName]) {
  532. classNames += 'ew-tree-table-open';
  533. }
  534. if (isHide) {
  535. classNames += 'ew-tree-tb-hide';
  536. }
  537. htmlStr += '<tr class="' + classNames + '" data-id="' + d[treeOption.idName] + '"';
  538. htmlStr += ' data-pid="' + (d[treeOption.pidName] || '') + '" data-have-child="' + haveChild + '"';
  539. htmlStr += ' data-indent="' + indentSize + '">';
  540. }
  541. for (var j = 0; j < cols.length; j++) {
  542. var $td;
  543. if ($tr) {
  544. $td = $tr.children('td').eq(j);
  545. }
  546. htmlStr += this.renderBodyTd(d, indentSize, j, $td);
  547. }
  548. htmlStr += '</tr>';
  549. return htmlStr;
  550. };
  551. /**
  552. * 渲染每一个单元格数据
  553. * @param d 行数据
  554. * @param indentSize 缩进大小
  555. * @param index 第几列
  556. * @param $td
  557. * @returns {string}
  558. */
  559. TreeTable.prototype.renderBodyTd = function (d, indentSize, index, $td) {
  560. var options = this.options;
  561. var col = options.cols[index];
  562. var treeOption = options.tree;
  563. var components = this.getComponents();
  564. var checkboxFilter = components.checkboxFilter;
  565. var radioFilter = components.radioFilter;
  566. indentSize || (indentSize = 0);
  567. // 内容填充
  568. var fieldStr = '';
  569. if (col.type == 'numbers') { // 序号列
  570. fieldStr += '<span class="ew-tree-table-numbers"></span>';
  571. } else if (col.type == 'checkbox') { // 复选框列
  572. var attrStr = 'name="' + checkboxFilter + '" lay-filter="' + checkboxFilter + '" value="' + d[treeOption.idName] + '"';
  573. attrStr += d.LAY_CHECKED ? ' checked="checked"' : '';
  574. fieldStr += '<input type="checkbox" lay-skin="primary" ' + attrStr + ' class="ew-tree-table-checkbox" />';
  575. } else if (col.type == 'radio') { // 单选框列
  576. var attrStr = 'name="' + radioFilter + '" lay-filter="' + radioFilter + '" value="' + d[treeOption.idName] + '"';
  577. attrStr += d.LAY_CHECKED ? ' checked="checked"' : '';
  578. fieldStr += '<input type="radio" ' + attrStr + ' class="ew-tree-table-radio" />';
  579. } else if (col.templet) { // 自定义模板
  580. if (typeof col.templet == 'function') {
  581. fieldStr += col.templet(d);
  582. } else if (typeof col.templet == 'string') {
  583. laytpl($(col.templet).html()).render(d, function (html) {
  584. fieldStr += html;
  585. });
  586. }
  587. } else if (col.toolbar) { // 工具列
  588. laytpl($(col.toolbar).html()).render(d, function (html) {
  589. fieldStr += html;
  590. });
  591. } else if (col.field && d[col.field] != undefined && d[col.field] != null) { // 普通字段
  592. fieldStr += d[col.field];
  593. }
  594. var tdStr = '';
  595. // 图标列处理
  596. if (index == treeOption.iconIndex) {
  597. // 缩进
  598. for (var k = 0; k < indentSize; k++) {
  599. tdStr += '<span class="ew-tree-table-indent"></span>';
  600. }
  601. tdStr += '<span class="ew-tree-pack">';
  602. // 加箭头
  603. var haveChild = getHaveChild(d, treeOption);
  604. tdStr += ('<i class="layui-icon ew-tree-table-arrow ' + (haveChild ? '' : 'ew-tree-table-arrow-hide') + ' ' + (options.tree.arrowType || '') + '"></i>');
  605. // 加图标
  606. tdStr += treeOption.getIcon(d);
  607. if (options.tree.onlyIconControl) {
  608. tdStr += '</span>';
  609. tdStr += ('<span>' + fieldStr + '</span>');
  610. } else {
  611. tdStr += ('<span>' + fieldStr + '</span>');
  612. tdStr += '</span>';
  613. }
  614. } else {
  615. tdStr += fieldStr;
  616. }
  617. if ($td && col.type != 'numbers') {
  618. $td.html(tdStr);
  619. }
  620. var htmlStr = '<td data-index="' + index + '" ';
  621. col.field && (htmlStr += (' data-field="' + col.field + '"'));
  622. col.edit && (htmlStr += (' data-edit="' + col.edit + '"'));
  623. col.type && (htmlStr += (' data-type="' + col.type + '"'));
  624. col.align && (htmlStr += (' align="' + col.align + '"')); // 对齐方式
  625. col.style && (htmlStr += (' style="' + col.style + '"')); // 单元格样式
  626. htmlStr += '>';
  627. htmlStr += (tdStr + '</td>');
  628. return htmlStr;
  629. };
  630. /**
  631. * 异步加载渲染
  632. * @param data 父级数据
  633. * @param $tr 父级dom
  634. */
  635. TreeTable.prototype.renderBodyAsync = function (d, $tr) {
  636. var that = this;
  637. var options = this.options;
  638. var components = this.getComponents();
  639. var $tbEmpty = components.$tbEmpty;
  640. var $tbLoading = components.$tbLoading;
  641. // 显示loading
  642. if ($tr) {
  643. $tr.addClass('ew-tree-table-loading');
  644. $tr.children('td').children('.ew-tree-pack').children('.ew-tree-table-arrow').addClass('layui-anim layui-anim-rotate layui-anim-loop');
  645. } else {
  646. if (options.data && options.data.length > 0) {
  647. $tbLoading.addClass('ew-loading-float');
  648. }
  649. $tbLoading.show();
  650. }
  651. // 请求数据
  652. options.reqData(d, function (res) {
  653. if (options.tree.isPidData) {
  654. res = treeTb.pidToChildren(res, options.tree.idName, options.tree.pidName, options.tree.childName);
  655. }
  656. that.renderBodyData(res, d, $tr); // 渲染内容
  657. // 移除loading
  658. if ($tr) {
  659. $tr.removeClass('ew-tree-table-loading');
  660. $tr.children('td').children('.ew-tree-pack').children('.ew-tree-table-arrow').removeClass('layui-anim layui-anim-rotate layui-anim-loop');
  661. } else {
  662. $tbLoading.hide();
  663. $tbLoading.removeClass('ew-loading-float');
  664. }
  665. // 是否为空
  666. if ((!res || res.length == 0) && !$tr) {
  667. $tbEmpty.show();
  668. }
  669. });
  670. };
  671. /**
  672. * 根据数据渲染body
  673. * @param data 数据集合
  674. * @param option 配置项
  675. * @param d 父级数据
  676. * @param $tr 父级dom
  677. */
  678. TreeTable.prototype.renderBodyData = function (data, d, $tr) {
  679. var that = this;
  680. var options = this.options;
  681. var components = this.getComponents();
  682. var $view = components.$view;
  683. var $table = components.$table;
  684. var tbFilter = components.tbFilter;
  685. addPidField(data, options.tree, d); // 补充pid字段
  686. // 更新到数据
  687. if (d == undefined) {
  688. options.data = data;
  689. } else {
  690. d[options.tree.childName] = data;
  691. }
  692. var indent;
  693. if ($tr) {
  694. indent = parseInt($tr.data('indent')) + 1;
  695. }
  696. var htmlStr = this.renderBody(data, indent);
  697. if ($tr) {
  698. // 移除旧dom
  699. $tr.nextAll('tr').each(function () {
  700. if (parseInt($(this).data('indent')) <= (indent - 1)) {
  701. return false;
  702. }
  703. $(this).remove();
  704. });
  705. // 渲染新dom
  706. $tr.after(htmlStr);
  707. $tr.addClass('ew-tree-table-open');
  708. } else {
  709. $table.children('tbody').html(htmlStr);
  710. }
  711. form.render(null, tbFilter); // 渲染表单元素
  712. this.renderNumberCol(); // 渲染序号列
  713. this.checkIndeterminateCB(); // 恢复复选框半选状态
  714. if ($tr) {
  715. // 更新父级复选框状态
  716. this.checkParentCB($tr);
  717. $tr.prevAll('tr').each(function () {
  718. var tInd = parseInt($(this).data('indent'));
  719. if (tInd < (indent - 1)) {
  720. that.checkParentCB($(this));
  721. indent = tInd + 1;
  722. }
  723. });
  724. }
  725. this.checkChooseAllCB(); // 联动全选框
  726. updateFixedTbHead($view);
  727. };
  728. /**
  729. * 联动子级复选框状态
  730. * @param $tr 当前tr的dom
  731. * @param checked
  732. */
  733. TreeTable.prototype.checkSubCB = function ($tr, checked) {
  734. var that = this;
  735. var components = this.getComponents();
  736. var cbFilter = components.checkboxFilter;
  737. var indent = -1, $trList;
  738. if ($tr.is('tbody')) {
  739. $trList = $tr.children('tr');
  740. } else {
  741. indent = parseInt($tr.data('indent'));
  742. $trList = $tr.nextAll('tr')
  743. }
  744. $trList.each(function () {
  745. if (parseInt($(this).data('indent')) <= indent) {
  746. return false;
  747. }
  748. var $cb = $(this).children('td').children('input[name="' + cbFilter + '"]');
  749. $cb.prop('checked', checked);
  750. if (checked) {
  751. $cb.data('indeterminate', 'false');
  752. $cb.next('.layui-form-checkbox').addClass('layui-form-checked');
  753. $cb.next('.layui-form-checkbox').removeClass('ew-form-indeterminate');
  754. } else {
  755. $cb.data('indeterminate', 'false');
  756. $cb.next('.layui-form-checkbox').removeClass('layui-form-checked ew-form-indeterminate');
  757. }
  758. that.update($(this).data('id'), {LAY_CHECKED: checked}); // 同步更新数据
  759. });
  760. };
  761. /**
  762. * 联动父级复选框状态
  763. * @param $tr 父级的dom
  764. */
  765. TreeTable.prototype.checkParentCB = function ($tr) {
  766. var that = this;
  767. var components = this.getComponents();
  768. var cbFilter = components.checkboxFilter;
  769. var indent = parseInt($tr.data('indent'));
  770. var ckNum = 0, unCkNum = 0;
  771. $tr.nextAll('tr').each(function () {
  772. if (parseInt($(this).data('indent')) <= indent) {
  773. return false;
  774. }
  775. var $cb = $(this).children('td').children('input[name="' + cbFilter + '"]');
  776. if ($cb.prop('checked')) {
  777. ckNum++;
  778. } else {
  779. unCkNum++;
  780. }
  781. });
  782. var $cb = $tr.children('td').children('input[name="' + cbFilter + '"]');
  783. if (ckNum > 0 && unCkNum == 0) { // 全选
  784. $cb.prop('checked', true);
  785. $cb.data('indeterminate', 'false');
  786. $cb.next('.layui-form-checkbox').addClass('layui-form-checked');
  787. $cb.next('.layui-form-checkbox').removeClass('ew-form-indeterminate');
  788. that.update($tr.data('id'), {LAY_CHECKED: true}); // 同步更新数据
  789. } else if (ckNum == 0 && unCkNum > 0) { // 全不选
  790. $cb.prop('checked', false);
  791. $cb.data('indeterminate', 'false');
  792. $cb.next('.layui-form-checkbox').removeClass('layui-form-checked ew-form-indeterminate');
  793. that.update($tr.data('id'), {LAY_CHECKED: false}); // 同步更新数据
  794. } else if (ckNum > 0 && unCkNum > 0) { // 半选
  795. $cb.prop('checked', true);
  796. $cb.data('indeterminate', 'true');
  797. $cb.next('.layui-form-checkbox').addClass('layui-form-checked ew-form-indeterminate');
  798. that.update($tr.data('id'), {LAY_CHECKED: true}); // 同步更新数据
  799. }
  800. };
  801. /** 联动全选复选框 */
  802. TreeTable.prototype.checkChooseAllCB = function () {
  803. var components = this.getComponents();
  804. var cbAllFilter = components.cbAllFilter;
  805. var cbFilter = components.checkboxFilter;
  806. var $tbody = components.$table.children('tbody');
  807. var ckNum = 0, unCkNum = 0;
  808. $tbody.children('tr').each(function () {
  809. var $cb = $(this).children('td').children('input[name="' + cbFilter + '"]');
  810. if ($cb.prop('checked')) {
  811. ckNum++;
  812. } else {
  813. unCkNum++;
  814. }
  815. });
  816. var $cb = $('input[lay-filter="' + cbAllFilter + '"]');
  817. if (ckNum > 0 && unCkNum == 0) { // 全选
  818. $cb.prop('checked', true);
  819. $cb.data('indeterminate', 'false');
  820. $cb.next('.layui-form-checkbox').addClass('layui-form-checked');
  821. $cb.next('.layui-form-checkbox').removeClass('ew-form-indeterminate');
  822. } else if ((ckNum == 0 && unCkNum > 0) || (ckNum == 0 && unCkNum == 0)) { // 全不选
  823. $cb.prop('checked', false);
  824. $cb.data('indeterminate', 'false');
  825. $cb.next('.layui-form-checkbox').removeClass('layui-form-checked ew-form-indeterminate');
  826. } else if (ckNum > 0 && unCkNum > 0) { // 半选
  827. $cb.prop('checked', true);
  828. $cb.data('indeterminate', 'true');
  829. $cb.next('.layui-form-checkbox').addClass('layui-form-checked ew-form-indeterminate');
  830. }
  831. };
  832. /** 填充序号列 */
  833. TreeTable.prototype.renderNumberCol = function () {
  834. var components = this.getComponents();
  835. var $tbody = components.$table.children('tbody');
  836. $tbody.children('tr').each(function (index) {
  837. $(this).children('td').children('.ew-tree-table-numbers').text(index + 1);
  838. });
  839. };
  840. /* 解决form.render之后半选框被重置的问题 */
  841. TreeTable.prototype.checkIndeterminateCB = function () {
  842. var components = this.getComponents();
  843. var cbFilter = components.checkboxFilter;
  844. $('input[lay-filter="' + cbFilter + '"]').each(function () {
  845. var $cb = $(this);
  846. if ($cb.data('indeterminate') == 'true' && $cb.prop('checked')) {
  847. $cb.next('.layui-form-checkbox').addClass('ew-form-indeterminate');
  848. }
  849. });
  850. };
  851. /**
  852. * 搜索数据
  853. * @param ids 关键字或数据id集合
  854. */
  855. TreeTable.prototype.filterData = function (ids) {
  856. var components = this.getComponents();
  857. var $trList = components.$table.children('tbody').children('tr');
  858. if (typeof ids == 'string') { // 关键字
  859. var keyword = ids;
  860. ids = [];
  861. $trList.each(function () {
  862. var id = $(this).data('id');
  863. $(this).children('td').each(function () {
  864. if ($(this).text().indexOf(keyword) != -1) {
  865. ids.push(id);
  866. return false;
  867. }
  868. });
  869. });
  870. }
  871. $trList.addClass('ew-tree-table-filter-hide');
  872. for (var i = 0; i < ids.length; i++) {
  873. var $tr = $trList.filter('[data-id="' + ids[i] + '"]');
  874. $tr.removeClass('ew-tree-table-filter-hide');
  875. // 联动父级
  876. var indent = parseInt($tr.data('indent'));
  877. $tr.prevAll('tr').each(function () {
  878. var tInd = parseInt($(this).data('indent'));
  879. if (tInd < indent) {
  880. $(this).removeClass('ew-tree-table-filter-hide'); // 联动父级
  881. if (!$(this).hasClass('ew-tree-table-open')) {
  882. toggleRow($(this));
  883. }
  884. indent = tInd;
  885. }
  886. });
  887. }
  888. };
  889. /** 重置搜索 */
  890. TreeTable.prototype.clearFilter = function () {
  891. var components = this.getComponents();
  892. var $trList = components.$table.children('tbody').children('tr');
  893. $trList.removeClass('ew-tree-table-filter-hide');
  894. };
  895. /** 展开指定行 */
  896. TreeTable.prototype.expand = function (id, cascade) {
  897. var components = this.getComponents();
  898. var $tr = components.$table.children('tbody').children('tr[data-id="' + id + '"]');
  899. if (!$tr.hasClass('ew-tree-table-open')) {
  900. $tr.children('td').children('.ew-tree-pack').trigger('click');
  901. }
  902. if (cascade == false) {
  903. return;
  904. }
  905. // 联动父级
  906. var indent = parseInt($tr.data('indent'));
  907. $tr.prevAll('tr').each(function () {
  908. var tInd = parseInt($(this).data('indent'));
  909. if (tInd < indent) {
  910. if (!$(this).hasClass('ew-tree-table-open')) {
  911. $(this).children('td').children('.ew-tree-pack').trigger('click');
  912. }
  913. indent = tInd;
  914. }
  915. });
  916. };
  917. /** 折叠指定行 */
  918. TreeTable.prototype.fold = function (id, cascade) {
  919. var components = this.getComponents();
  920. var $tr = components.$table.children('tbody').children('tr[data-id="' + id + '"]');
  921. if ($tr.hasClass('ew-tree-table-open')) {
  922. $tr.children('td').children('.ew-tree-pack').trigger('click');
  923. }
  924. if (cascade == false) {
  925. return;
  926. }
  927. // 联动父级
  928. var indent = parseInt($tr.data('indent'));
  929. $tr.prevAll('tr').each(function () {
  930. var tInd = parseInt($(this).data('indent'));
  931. if (tInd < indent) {
  932. if ($(this).hasClass('ew-tree-table-open')) {
  933. $(this).children('td').children('.ew-tree-pack').trigger('click');
  934. }
  935. indent = tInd;
  936. }
  937. });
  938. };
  939. /** 全部展开 */
  940. TreeTable.prototype.expandAll = function () {
  941. var that = this;
  942. var components = this.getComponents();
  943. var $trList = components.$table.children('tbody').children('tr');
  944. $trList.each(function () {
  945. that.expand($(this).data('id'), false);
  946. });
  947. };
  948. /** 全部折叠 */
  949. TreeTable.prototype.foldAll = function () {
  950. var that = this;
  951. var components = this.getComponents();
  952. var $trList = components.$table.children('tbody').children('tr');
  953. $trList.each(function () {
  954. that.fold($(this).data('id'), false);
  955. });
  956. };
  957. /** 获取当前数据 */
  958. TreeTable.prototype.getData = function () {
  959. return this.options.data;
  960. };
  961. /** 重载表格 */
  962. TreeTable.prototype.reload = function (opt) {
  963. treeTb.render($.extend(this.options, opt));
  964. };
  965. /** 根据id更新数据 */
  966. TreeTable.prototype.update = function (id, fields) {
  967. var data = getDataById(this.getData(), id, this.options.tree);
  968. $.extend(data, fields);
  969. };
  970. /** 根据id删除数据 */
  971. TreeTable.prototype.del = function (id) {
  972. delDataById(this.getData(), id, this.options.tree);
  973. };
  974. /** 获取当前选中行 */
  975. TreeTable.prototype.checkStatus = function () {
  976. var that = this;
  977. var components = this.getComponents();
  978. var $table = components.$table;
  979. var checkboxFilter = components.checkboxFilter;
  980. var radioFilter = components.radioFilter;
  981. var list = [];
  982. // 获取单选框选中数据
  983. var $radio = $table.find('input[name="' + radioFilter + '"]');
  984. if ($radio.length > 0) {
  985. var id = $radio.filter(':checked').val();
  986. var d = getDataById(this.getData(), id, this.options.tree);
  987. if (d) {
  988. list.push(d);
  989. }
  990. } else { // 获取复选框数据
  991. $table.find('input[name="' + checkboxFilter + '"]:checked').each(function () {
  992. var id = $(this).val();
  993. var d = getDataById(that.getData(), id, that.options.tree);
  994. if (d) {
  995. list.push(d);
  996. }
  997. });
  998. }
  999. return list;
  1000. };
  1001. /** 设置复/单选框选中 */
  1002. TreeTable.prototype.setChecked = function (ids) {
  1003. var components = this.getComponents();
  1004. var $table = components.$table;
  1005. var checkboxFilter = components.checkboxFilter;
  1006. var radioFilter = components.radioFilter;
  1007. var $radio = $table.find('input[name="' + radioFilter + '"]');
  1008. if ($radio.length > 0) { // 开启了单选框
  1009. $radio.each(function () {
  1010. if (ids[ids.length - 1] == $(this).val()) {
  1011. $(this).next('.layui-form-radio').trigger('click');
  1012. return false;
  1013. }
  1014. });
  1015. } else { // 开启了复选框
  1016. $table.find('input[name="' + checkboxFilter + '"]').each(function () {
  1017. var $cb = $(this);
  1018. var value = $cb.val();
  1019. var $layCb = $cb.next('.layui-form-checkbox');
  1020. for (var i = 0; i < ids.length; i++) {
  1021. if (value == ids[i]) {
  1022. var checked = $cb.prop('checked');
  1023. var indeterminate = $layCb.hasClass('ew-form-indeterminate');
  1024. if (!checked || indeterminate) {
  1025. $layCb.trigger('click');
  1026. }
  1027. }
  1028. }
  1029. });
  1030. }
  1031. };
  1032. /** 移除全部选中 */
  1033. TreeTable.prototype.removeAllChecked = function () {
  1034. var components = this.getComponents();
  1035. var $table = components.$table;
  1036. var checkboxFilter = components.checkboxFilter;
  1037. this.checkSubCB($table.children('tbody'), false);
  1038. };
  1039. /**
  1040. * 刷新指定父级下的节点
  1041. * @param id 父级id,空则全部刷新
  1042. * @param data 非异步模式替换的数据
  1043. */
  1044. TreeTable.prototype.refresh = function (id, data) {
  1045. var components = this.getComponents().$table;
  1046. var $table = components.$table;
  1047. var d, $tr;
  1048. if (id != undefined) {
  1049. d = getDataById(this.getData(), id, this.options.tree);
  1050. $tr = $table.children('tbody').children('tr[data-id="' + id + '"]');
  1051. }
  1052. if (data) { // 数据模式
  1053. components.$tbLoading.addClass('ew-loading-float');
  1054. components.$tbLoading.show();
  1055. this.renderBodyData(data, d, $tr);
  1056. components.$tbLoading.hide();
  1057. components.$tbLoading.removeClass('ew-loading-float');
  1058. } else { // 异步模式
  1059. this.renderBodyAsync(d, $tr);
  1060. }
  1061. };
  1062. /** 生成表头 */
  1063. function getThead(options) {
  1064. var htmlStr = '<tr>';
  1065. for (var i = 0; i < options.cols.length; i++) {
  1066. var col = options.cols[i];
  1067. htmlStr += '<td data-index="' + i + '" ';
  1068. col.align && (htmlStr += ' align="' + col.align + '"'); // 对齐方式
  1069. htmlStr += ' >';
  1070. // 标题
  1071. if (col.type == 'checkbox') {
  1072. htmlStr += options.getAllChooseBox();
  1073. } else {
  1074. htmlStr += (col.title || '');
  1075. }
  1076. // 列宽拖拽
  1077. if (!col.unresize && 'checkbox' != col.type && 'radio' != col.type && 'numbers' != col.type && 'space' != col.type) {
  1078. htmlStr += '<span class="ew-tb-resize"></span>';
  1079. }
  1080. htmlStr += '</td>';
  1081. }
  1082. htmlStr += '</tr>';
  1083. return htmlStr;
  1084. }
  1085. /** 生成colgroup */
  1086. function getColgroup(options) {
  1087. var htmlStr = '<colgroup>';
  1088. for (var i = 0; i < options.cols.length; i++) {
  1089. var col = options.cols[i];
  1090. htmlStr += '<col ';
  1091. // 设置宽度
  1092. if (col.width) {
  1093. htmlStr += 'width="' + col.width + '"'
  1094. } else if (col.type == 'space') { // 空列
  1095. htmlStr += 'width="15"'
  1096. } else if (col.type == 'numbers') { // 序号列
  1097. htmlStr += 'width="40"'
  1098. } else if (col.type == 'checkbox' || col.type == 'radio') { // 复/单选框列
  1099. htmlStr += 'width="48"'
  1100. }
  1101. htmlStr += ' />';
  1102. }
  1103. htmlStr += '</colgroup>';
  1104. return htmlStr;
  1105. }
  1106. /** 计算table宽度 */
  1107. function getTbWidth(options) {
  1108. var minWidth = 0, width = 0, setWidth = true;
  1109. for (var i = 0; i < options.cols.length; i++) {
  1110. var col = options.cols[i];
  1111. if (col.type == 'space') { // 空列
  1112. minWidth += 15;
  1113. width += 15;
  1114. } else if (col.type == 'numbers') { // 序号列
  1115. minWidth += 40;
  1116. width += 40;
  1117. } else if (col.type == 'checkbox' || col.type == 'radio') { // 复/单选框列
  1118. minWidth += 48;
  1119. width += 48;
  1120. } else if (!col.width || /\d+%$/.test(col.width)) { // 列未固定宽度
  1121. setWidth = false;
  1122. if (this.cellMinWidth != undefined) {
  1123. minWidth += options.cellMinWidth;
  1124. width += options.cellMinWidth;
  1125. }
  1126. } else { // 列固定宽度
  1127. minWidth += col.width;
  1128. width += col.width;
  1129. }
  1130. }
  1131. return {minWidth: minWidth, width: width, setWidth: setWidth};
  1132. }
  1133. /** 生成全选按钮 */
  1134. function getAllChooseBox(options) {
  1135. var tbFilter = $(options.elem).next().attr('lay-filter');
  1136. var cbAllFilter = 'ew_tb_choose_all_' + tbFilter;
  1137. return '<input type="checkbox" lay-filter="' + cbAllFilter + '" lay-skin="primary" class="ew-tree-table-checkbox"/>';
  1138. }
  1139. /** 获取列图标 */
  1140. function getIcon(d, treeOption) {
  1141. if (getHaveChild(d, treeOption)) {
  1142. return '<i class="ew-tree-icon layui-icon layui-icon-layer"></i>';
  1143. } else {
  1144. return '<i class="ew-tree-icon layui-icon layui-icon-file"></i>';
  1145. }
  1146. }
  1147. /** 折叠/展开行 */
  1148. function toggleRow($tr) {
  1149. var indent = parseInt($tr.data('indent'));
  1150. var isOpen = $tr.hasClass('ew-tree-table-open');
  1151. if (isOpen) { // 折叠
  1152. $tr.removeClass('ew-tree-table-open');
  1153. $tr.nextAll('tr').each(function () {
  1154. if (parseInt($(this).data('indent')) <= indent) {
  1155. return false;
  1156. }
  1157. $(this).addClass('ew-tree-tb-hide');
  1158. });
  1159. } else { // 展开
  1160. $tr.addClass('ew-tree-table-open');
  1161. var hideInd;
  1162. $tr.nextAll('tr').each(function () {
  1163. var ind = parseInt($(this).data('indent'));
  1164. if (ind <= indent) {
  1165. return false;
  1166. }
  1167. if (hideInd != undefined && ind > hideInd) {
  1168. return true;
  1169. }
  1170. $(this).removeClass('ew-tree-tb-hide');
  1171. if (!$(this).hasClass('ew-tree-table-open')) {
  1172. hideInd = parseInt($(this).data('indent'));
  1173. } else {
  1174. hideInd = undefined;
  1175. }
  1176. });
  1177. }
  1178. updateFixedTbHead($tr.parent().parent().parent().parent().parent());
  1179. }
  1180. /** 固定表头减去滚动条 */
  1181. function updateFixedTbHead($view) {
  1182. var $group = $view.children('.ew-tree-table-group');
  1183. var $tbBox = $group.children('.ew-tree-table-box');
  1184. var sWidth = $tbBox.width() - $tbBox.prop('clientWidth');
  1185. if (sWidth > 0) {
  1186. if (!(device.ie && device.ie < 9)) {
  1187. sWidth = sWidth - 0.48;
  1188. }
  1189. $group.children('.ew-tree-table-head').css('padding-right', sWidth);
  1190. } else {
  1191. $group.children('.ew-tree-table-head').css('padding-right', 0);
  1192. }
  1193. }
  1194. // 监听窗口大小改变
  1195. $(window).resize(function () {
  1196. $('.ew-tree-table').each(function () {
  1197. updateFixedTbHead($(this));
  1198. var $tbBox = $(this).children('.ew-tree-table-group').children('.ew-tree-table-box');
  1199. var full = $tbBox.attr('ew-tree-full');
  1200. if (full && device.ie && device.ie < 10) {
  1201. $tbBox.css('height', getPageHeight() - full);
  1202. }
  1203. });
  1204. });
  1205. /** 判断是否还有子节点 */
  1206. function getHaveChild(d, treeOption) {
  1207. var haveChild = false;
  1208. if (d[treeOption.haveChildName] != undefined) {
  1209. haveChild = d[treeOption.haveChildName];
  1210. haveChild = haveChild == true || haveChild == 'true';
  1211. } else if (d[treeOption.childName]) {
  1212. haveChild = d[treeOption.childName].length > 0;
  1213. }
  1214. return haveChild;
  1215. }
  1216. /** 补充pid字段 */
  1217. function addPidField(data, treeOption, parent) {
  1218. for (var i = 0; i < data.length; i++) {
  1219. if (parent) {
  1220. data[i][treeOption.pidName] = parent[treeOption.idName];
  1221. }
  1222. if (data[i][treeOption.childName] && data[i][treeOption.childName].length > 0) {
  1223. addPidField(data[i][treeOption.childName], treeOption, data[i]);
  1224. }
  1225. }
  1226. }
  1227. /** 根据id获取数据 */
  1228. function getDataById(data, id, treeOption) {
  1229. for (var i = 0; i < data.length; i++) {
  1230. if (data[i][treeOption.idName] == id) {
  1231. return data[i];
  1232. }
  1233. if (data[i][treeOption.childName] && data[i][treeOption.childName].length > 0) {
  1234. var d = getDataById(data[i][treeOption.childName], id, treeOption);
  1235. if (d != undefined) {
  1236. return d;
  1237. }
  1238. }
  1239. }
  1240. }
  1241. /** 根据id删除数据 */
  1242. function delDataById(data, id, treeOption) {
  1243. for (var i = 0; i < data.length; i++) {
  1244. if (data[i][treeOption.idName] == id) {
  1245. data.splice(i, 1);
  1246. return true;
  1247. }
  1248. if (data[i][treeOption.childName] && data[i][treeOption.childName].length > 0) {
  1249. var rs = delDataById(data[i][treeOption.childName], id, treeOption);
  1250. if (rs) {
  1251. return true;
  1252. }
  1253. }
  1254. }
  1255. }
  1256. /** 获取顶级的pId */
  1257. function getPids(list, idName, pidName) {
  1258. var pids = [];
  1259. for (var i = 0; i < list.length; i++) {
  1260. var hasPid = false;
  1261. for (var j = 0; j < list.length; j++) {
  1262. if (i != j && list[j][idName] == list[i][pidName]) {
  1263. hasPid = true;
  1264. }
  1265. }
  1266. if (!hasPid) {
  1267. pids.push(list[i][pidName]);
  1268. }
  1269. }
  1270. return pids;
  1271. }
  1272. /** 判断pId是否相等 */
  1273. function pidEquals(pId, pIds) {
  1274. if (isClass(pIds) == 'Array') {
  1275. for (var i = 0; i < pIds.length; i++) {
  1276. if (pId == pIds[i]) {
  1277. return true;
  1278. }
  1279. }
  1280. } else {
  1281. return pId == pIds;
  1282. }
  1283. return false;
  1284. }
  1285. /** 获取变量类型 */
  1286. function isClass(o) {
  1287. if (o === null)
  1288. return 'Null';
  1289. if (o === undefined)
  1290. return 'Undefined';
  1291. return Object.prototype.toString.call(o).slice(8, -1);
  1292. }
  1293. /* 获取浏览器高度 */
  1294. function getPageHeight() {
  1295. return document.documentElement.clientHeight || document.body.clientHeight;
  1296. }
  1297. /** 对外提供的方法 */
  1298. var treeTb = {
  1299. /* 渲染 */
  1300. render: function (options) {
  1301. return new TreeTable(options);
  1302. },
  1303. /* 事件监听 */
  1304. on: function (events, callback) {
  1305. return layui.onevent.call(this, MOD_NAME, events, callback);
  1306. },
  1307. /* pid转children形式 */
  1308. pidToChildren: function (data, idName, pidName, childName, pId) {
  1309. childName || (childName = 'children');
  1310. var newList = [];
  1311. for (var i = 0; i < data.length; i++) {
  1312. (pId == undefined) && (pId = getPids(data, idName, pidName));
  1313. if (pidEquals(data[i][pidName], pId)) {
  1314. var children = this.pidToChildren(data, idName, pidName, childName, data[i][idName]);
  1315. (children.length > 0) && (data[i][childName] = children);
  1316. newList.push(data[i]);
  1317. }
  1318. }
  1319. return newList;
  1320. }
  1321. };
  1322. exports('treeTable', treeTb);
  1323. });