treeTable.js 59 KB

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