treeTable.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. layui.define(['jquery'], function(exports) {
  2. var MOD_NAME = 'treeTable',
  3. o = layui.jquery,
  4. tree = function() {};
  5. tree.prototype.config = function() {
  6. return {
  7. top_value: 0,
  8. primary_key: 'id',
  9. parent_key: 'pid',
  10. hide_class: 'layui-hide',
  11. icon: {
  12. open: 'layui-icon layui-icon-triangle-d',
  13. close: 'layui-icon layui-icon-triangle-r',
  14. left: 16,
  15. },
  16. cols: [],
  17. checked: {},
  18. is_click_icon: false,
  19. is_checkbox: false,
  20. is_cache: true,
  21. };
  22. }
  23. tree.prototype.template = function(e) {
  24. var t = this,
  25. level = [],
  26. tbody = '',
  27. is_table = o('table' + e.elem).length || !(e.is_click_icon = true),
  28. checkbox = e.is_checkbox ? '<div class="layui-unselect layui-form-checkbox cbx" lay-skin="primary"><i class="layui-icon layui-icon-ok"></i></div>' : '',
  29. checked = checkbox ? checkbox.replace('cbx', 'cbx layui-form-checked') : '',
  30. thead = checkbox && '<th style="width:28px;">' + (o.inArray(e.top_value, e.checked.data) > -1 ? checked : checkbox) + '</th>';
  31. o.each(t.data(e, e.data), function(idx, item) {
  32. var tr = '',
  33. is_checked = false,
  34. hide_class = (item[e.parent_key] == e.top_value) || (item[e.parent_key] == t.cache(e, item[e.parent_key])) ? '' : e.hide_class;
  35. // 设置每行数据层级
  36. item.level = level[item[e.primary_key]] = item[e.parent_key] != e.top_value ? (level[item[e.parent_key]] + 1) : 0;
  37. // 设置是否为最后一级
  38. item.is_end = !e.childs[item[e.primary_key]];
  39. o.each(e.cols, function(index, obj) {
  40. var style = '';
  41. obj.width && (style += 'width:' + obj.width + ';'), obj.align && (style += 'text-align:' + obj.align + ';'), style && (style = 'style="' + style + '"');
  42. // 标记设置行checkbox选中
  43. if(e.is_checkbox && e.checked && o.inArray(item[e.checked.key], e.checked.data) > -1) {
  44. is_checked = true;
  45. }
  46. // 第一次遍历头部的时候拼接表格头部
  47. idx || (thead += '<th ' + style + '>' + obj.title + '</th>');
  48. // 指定列加入开启、关闭小图标
  49. var icon = (obj.key == e.icon_key && !item.is_end) ? '<i class="' + (t.cache(e, item[e.primary_key]) ? e.icon.open : e.icon.close) + '"></i>' : '<span></span>';
  50. // 指定列小图标按照层级向后位移
  51. var left = (obj.key == e.icon_key ? level[item[e.primary_key]] * e.icon.left + 'px' : '');
  52. icon = icon.replace('>', ' style="margin-left:' + left + ';">');
  53. // 拼接行
  54. tr += '<td ' + style + (left ? 'data-down' : '') + '>' + icon + (is_table ? '' : (is_checked ? checked : checkbox)) + (obj.template ? obj.template(item) : item[obj.key]) + '</td>';
  55. });
  56. var box = is_table ? o(is_checked ? checked : checkbox).wrap('<td style="width:28px;">').parent().prop('outerHTML') : '';
  57. tbody += '<tr class="' + hide_class + '" data-id="' + item[e.primary_key] + '" data-pid="' + item[e.parent_key] + '">' + box + tr + '</tr>';
  58. });
  59. // 处理表树和树的赋值模板
  60. var table = is_table ? '<thead><tr data-id="' + e.top_value + '">' + thead + '</tr></thead><tbody>' + tbody + '</tbody>' : tbody.replace(/<tr/g, '<ul').replace(/tr>/g, 'ul>').replace(/<td/g, '<li').replace(/td>/g, 'li>');
  61. // 确认点击图标或点击列触发展开关闭
  62. var click_btn = e.is_click_icon ? '[data-down] i:not(.layui-icon-ok)' : '[data-down]';
  63. // 模板渲染并处理点击展开收起等功能
  64. o(e.elem).html(table).off('click', click_btn).on('click', click_btn, function() {
  65. var tr = o(this).parents('[data-id]'),
  66. td = tr.find('[data-down]'),
  67. id = tr.data('id'),
  68. pid = tr.data('pid'),
  69. is_open = (td.find('i:not(.layui-icon-ok)').attr('class') == e.icon.close);
  70. if(is_open) {
  71. // 展开子级(子级出现、更改图标)
  72. td.find('i:not(.layui-icon-ok)').attr('class', e.icon.open);
  73. td.parents(e.elem).find('[data-pid=' + id + ']').removeClass(e.hide_class);
  74. t.cache(e, id, true);
  75. } else {
  76. // 关闭子级(更改图标、隐藏所有子孙级)
  77. td.find('i:not(.layui-icon-ok)').attr('class', e.icon.close);
  78. t.childs_hide(e, id);
  79. }
  80. // 设置监听展开关闭
  81. layui.event.call(this, MOD_NAME, 'tree(flex)', {
  82. elem: this,
  83. item: e.childs[pid][id],
  84. table: e.elem,
  85. is_open: is_open,
  86. })
  87. }).off('click', '.cbx').on('click', '.cbx', function() {
  88. var is_checked = o(this).toggleClass('layui-form-checked').hasClass('layui-form-checked'),
  89. tr = o(this).parents('[data-id]'),
  90. id = tr.data('id'),
  91. pid = tr.data('pid');
  92. t.childs_checkbox(e, id, is_checked);
  93. t.parents_checkbox(e, pid);
  94. // 设置监听checkbox选择
  95. layui.event.call(this, MOD_NAME, 'tree(box)', {
  96. elem: this,
  97. item: pid === undefined ? {} : e.childs[pid][id],
  98. table: e.elem,
  99. is_checked: is_checked,
  100. })
  101. }).off('click', '[lay-filter]').on('click', '[lay-filter]', function() {
  102. var tr = o(this).parents('[data-id]'),
  103. id = tr.data('id'),
  104. pid = tr.data('pid'),
  105. filter = o(this).attr("lay-filter");
  106. return layui.event.call(this, MOD_NAME, 'tree(' + filter + ')', {
  107. elem: this,
  108. item: e.childs[pid][id],
  109. })
  110. });
  111. e.end && e.end(e);
  112. };
  113. // 同级全部选中父级选中/同级全部取消取消父级
  114. tree.prototype.parents_checkbox = function(e, pid) {
  115. var po = o(e.elem).find('[data-pid=' + pid + ']'),
  116. co = o(e.elem).find('[data-id=' + pid + ']'),
  117. len = o(e.elem).find('[data-pid=' + pid + '] .cbx.layui-form-checked').length;
  118. if(po.length == len || len == 0) {
  119. var pid = co.data('pid');
  120. len ? co.find('.cbx').addClass('layui-form-checked') : co.find('.cbx').removeClass('layui-form-checked');
  121. pid === undefined || this.parents_checkbox(e, pid);
  122. }
  123. };
  124. // 子级反选
  125. tree.prototype.childs_checkbox = function(e, id, is_checked) {
  126. var t = this;
  127. o(e.elem).find('[data-pid=' + id + ']').each(function() {
  128. var checkbox = o(this).find('.cbx');
  129. is_checked ? checkbox.addClass('layui-form-checked') : checkbox.removeClass('layui-form-checked');
  130. t.childs_checkbox(e, o(this).data('id'), is_checked);
  131. })
  132. };
  133. // 点击收起循环隐藏子级元素
  134. tree.prototype.childs_hide = function(e, id) {
  135. var t = this;
  136. t.cache(e, id, false);
  137. o(e.elem).find('[data-pid=' + id + ']:not(.' + e.hide_class + ')').each(function() {
  138. var td = o(this).find('[data-down]'),
  139. i = td.find('i:not(.layui-icon-ok)');
  140. // 关闭更换小图标
  141. i.length && i.attr('class', e.icon.close);
  142. // 隐藏子级
  143. td.parents(e.elem).find('[data-pid=' + id + ']').addClass(e.hide_class);
  144. t.childs_hide(e, o(this).data('id'))
  145. });
  146. };
  147. // 重新组合数据,父子级关系跟随
  148. tree.prototype.data = function(e) {
  149. var lists = [],
  150. childs = [];
  151. o.each(e.data, function(idx, item) {
  152. lists[item[e.primary_key]] = item;
  153. if(!childs[item[e.parent_key]]) {
  154. childs[item[e.parent_key]] = [];
  155. }
  156. childs[item[e.parent_key]][item[e.primary_key]] = item;
  157. });
  158. e.childs = childs;
  159. return this.tree_data(e, lists, e.top_value, []);
  160. };
  161. tree.prototype.tree_data = function(e, lists, pid, data) {
  162. var t = this;
  163. if(lists[pid]) {
  164. data.push(lists[pid]);
  165. delete lists[pid]
  166. }
  167. o.each(e.data, function(index, item) {
  168. if(item[e.parent_key] == pid) {
  169. data.concat(t.tree_data(e, lists, item[e.primary_key], data))
  170. }
  171. });
  172. return data;
  173. };
  174. tree.prototype.render = function(e) {
  175. var t = this;
  176. e = o.extend(t.config(), e);
  177. if(e.url) {
  178. o.get(e.url, function(res) {
  179. e.data = res;
  180. t.template(e);
  181. })
  182. } else {
  183. t.template(e);
  184. }
  185. return e;
  186. };
  187. // 获取已选值集合
  188. tree.prototype.checked = function(e) {
  189. var ids = [];
  190. o(e.elem).find('.cbx.layui-form-checked').each(function() {
  191. var id = o(this).parents('[data-id]').data('id');
  192. ids.push(id);
  193. })
  194. return ids;
  195. };
  196. // 全部展开
  197. tree.prototype.openAll = function(e) {
  198. var t = this;
  199. o.each(e.data, function(idx, item) {
  200. item[e.primary_key] && t.cache(e, item[e.primary_key], true);
  201. })
  202. t.render(e);
  203. }
  204. // 全部关闭
  205. tree.prototype.closeAll = function(e) {
  206. localStorage.setItem(e.elem.substr(1), '');
  207. this.render(e);
  208. }
  209. tree.prototype.on = function(events, callback) {
  210. return layui.onevent.call(this, MOD_NAME, events, callback)
  211. };
  212. // 存储折叠状态
  213. tree.prototype.cache = function(e, val, option) {
  214. if(!e.is_cache) {
  215. return false;
  216. }
  217. var t = this,
  218. name = e.elem.substr(1),
  219. val = val.toString(),
  220. cache = localStorage.getItem(name) ? localStorage.getItem(name).split(',') : [],
  221. index = o.inArray(val, cache);
  222. if(option === undefined) {
  223. return index == -1 ? false : val
  224. }
  225. if(option && index == -1) {
  226. cache.push(val)
  227. }
  228. if(!option && index > -1) {
  229. cache.splice(index, 1)
  230. }
  231. localStorage.setItem(name, cache.join(','));
  232. };
  233. var tree = new tree();
  234. exports(MOD_NAME, tree)
  235. });