截流自动化的商城平台
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

treeTable.js 59KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494
  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. // 移除loading
  695. if ($tr) {
  696. $tr.removeClass('ew-tree-table-loading');
  697. $tr.children('td').find('.ew-tree-pack').children('.ew-tree-table-arrow').removeClass('layui-anim layui-anim-rotate layui-anim-loop');
  698. } else {
  699. $tbLoading.hide();
  700. $tbLoading.removeClass('ew-loading-float');
  701. // 是否为空
  702. if (!res || res.length == 0) {
  703. $tbEmpty.show();
  704. } else {
  705. $tbEmpty.hide();
  706. }
  707. }
  708. });
  709. };
  710. /**
  711. * 根据数据渲染body
  712. * @param data 数据集合
  713. * @param option 配置项
  714. * @param d 父级数据
  715. * @param $tr 父级dom
  716. */
  717. TreeTable.prototype.renderBodyData = function (data, d, $tr) {
  718. var that = this;
  719. var options = this.options;
  720. var components = this.getComponents();
  721. var $view = components.$view;
  722. var $table = components.$table;
  723. var tbFilter = components.tbFilter;
  724. addPidField(data, options.tree, d); // 补充pid字段
  725. // 更新到数据
  726. if (d == undefined) {
  727. options.data = data;
  728. } else {
  729. d[options.tree.childName] = data;
  730. }
  731. var indent;
  732. if ($tr) {
  733. indent = parseInt($tr.data('indent')) + 1;
  734. }
  735. var htmlStr = this.renderBody(data, indent);
  736. if ($tr) {
  737. // 移除旧dom
  738. $tr.nextAll('tr').each(function () {
  739. if (parseInt($(this).data('indent')) <= (indent - 1)) {
  740. return false;
  741. }
  742. $(this).remove();
  743. });
  744. // 渲染新dom
  745. $tr.after(htmlStr);
  746. $tr.addClass('ew-tree-table-open');
  747. } else {
  748. $table.children('tbody').html(htmlStr);
  749. }
  750. form.render(null, tbFilter); // 渲染表单元素
  751. this.renderNumberCol(); // 渲染序号列
  752. this.checkIndeterminateCB(); // 恢复复选框半选状态
  753. if ($tr) {
  754. // 更新父级复选框状态
  755. this.checkParentCB($tr);
  756. $tr.prevAll('tr').each(function () {
  757. var tInd = parseInt($(this).data('indent'));
  758. if (tInd < (indent - 1)) {
  759. that.checkParentCB($(this));
  760. indent = tInd + 1;
  761. }
  762. });
  763. }
  764. this.checkChooseAllCB(); // 联动全选框
  765. updateFixedTbHead($view);
  766. };
  767. /**
  768. * 联动子级复选框状态
  769. * @param $tr 当前tr的dom
  770. * @param checked
  771. */
  772. TreeTable.prototype.checkSubCB = function ($tr, checked) {
  773. var that = this;
  774. var components = this.getComponents();
  775. var cbFilter = components.checkboxFilter;
  776. var indent = -1, $trList;
  777. if ($tr.is('tbody')) {
  778. $trList = $tr.children('tr');
  779. } else {
  780. indent = parseInt($tr.data('indent'));
  781. $trList = $tr.nextAll('tr')
  782. }
  783. $trList.each(function () {
  784. if (parseInt($(this).data('indent')) <= indent) {
  785. return false;
  786. }
  787. var $cb = $(this).children('td').find('input[name="' + cbFilter + '"]');
  788. $cb.prop('checked', checked);
  789. if (checked) {
  790. $cb.data('indeterminate', 'false');
  791. $cb.next('.layui-form-checkbox').addClass('layui-form-checked');
  792. $cb.next('.layui-form-checkbox').removeClass('ew-form-indeterminate');
  793. } else {
  794. $cb.data('indeterminate', 'false');
  795. $cb.next('.layui-form-checkbox').removeClass('layui-form-checked ew-form-indeterminate');
  796. }
  797. that.update($(this).data('id'), {LAY_CHECKED: checked}); // 同步更新数据
  798. });
  799. };
  800. /**
  801. * 联动父级复选框状态
  802. * @param $tr 父级的dom
  803. */
  804. TreeTable.prototype.checkParentCB = function ($tr) {
  805. var that = this;
  806. var components = this.getComponents();
  807. var cbFilter = components.checkboxFilter;
  808. var indent = parseInt($tr.data('indent'));
  809. var ckNum = 0, unCkNum = 0;
  810. $tr.nextAll('tr').each(function () {
  811. if (parseInt($(this).data('indent')) <= indent) {
  812. return false;
  813. }
  814. var $cb = $(this).children('td').find('input[name="' + cbFilter + '"]');
  815. if ($cb.prop('checked')) {
  816. ckNum++;
  817. } else {
  818. unCkNum++;
  819. }
  820. });
  821. var $cb = $tr.children('td').find('input[name="' + cbFilter + '"]');
  822. if (ckNum > 0 && unCkNum == 0) { // 全选
  823. $cb.prop('checked', true);
  824. $cb.data('indeterminate', 'false');
  825. $cb.next('.layui-form-checkbox').addClass('layui-form-checked');
  826. $cb.next('.layui-form-checkbox').removeClass('ew-form-indeterminate');
  827. that.update($tr.data('id'), {LAY_CHECKED: true}); // 同步更新数据
  828. } else if (ckNum == 0 && unCkNum > 0) { // 全不选
  829. $cb.prop('checked', false);
  830. $cb.data('indeterminate', 'false');
  831. $cb.next('.layui-form-checkbox').removeClass('layui-form-checked ew-form-indeterminate');
  832. that.update($tr.data('id'), {LAY_CHECKED: false}); // 同步更新数据
  833. } else if (ckNum > 0 && unCkNum > 0) { // 半选
  834. $cb.prop('checked', true);
  835. $cb.data('indeterminate', 'true');
  836. $cb.next('.layui-form-checkbox').addClass('layui-form-checked ew-form-indeterminate');
  837. that.update($tr.data('id'), {LAY_CHECKED: true}); // 同步更新数据
  838. }
  839. };
  840. /** 联动全选复选框 */
  841. TreeTable.prototype.checkChooseAllCB = function () {
  842. var components = this.getComponents();
  843. var cbAllFilter = components.cbAllFilter;
  844. var cbFilter = components.checkboxFilter;
  845. var $tbody = components.$table.children('tbody');
  846. var ckNum = 0, unCkNum = 0;
  847. $tbody.children('tr').each(function () {
  848. var $cb = $(this).children('td').find('input[name="' + cbFilter + '"]');
  849. if ($cb.prop('checked')) {
  850. ckNum++;
  851. } else {
  852. unCkNum++;
  853. }
  854. });
  855. var $cb = $('input[lay-filter="' + cbAllFilter + '"]');
  856. if (ckNum > 0 && unCkNum == 0) { // 全选
  857. $cb.prop('checked', true);
  858. $cb.data('indeterminate', 'false');
  859. $cb.next('.layui-form-checkbox').addClass('layui-form-checked');
  860. $cb.next('.layui-form-checkbox').removeClass('ew-form-indeterminate');
  861. } else if ((ckNum == 0 && unCkNum > 0) || (ckNum == 0 && unCkNum == 0)) { // 全不选
  862. $cb.prop('checked', false);
  863. $cb.data('indeterminate', 'false');
  864. $cb.next('.layui-form-checkbox').removeClass('layui-form-checked ew-form-indeterminate');
  865. } else if (ckNum > 0 && unCkNum > 0) { // 半选
  866. $cb.prop('checked', true);
  867. $cb.data('indeterminate', 'true');
  868. $cb.next('.layui-form-checkbox').addClass('layui-form-checked ew-form-indeterminate');
  869. }
  870. };
  871. /** 填充序号列 */
  872. TreeTable.prototype.renderNumberCol = function () {
  873. var components = this.getComponents();
  874. var $tbody = components.$table.children('tbody');
  875. $tbody.children('tr').each(function (index) {
  876. $(this).children('td').find('.ew-tree-table-numbers').text(index + 1);
  877. });
  878. };
  879. /* 解决form.render之后半选框被重置的问题 */
  880. TreeTable.prototype.checkIndeterminateCB = function () {
  881. var components = this.getComponents();
  882. var cbFilter = components.checkboxFilter;
  883. $('input[lay-filter="' + cbFilter + '"]').each(function () {
  884. var $cb = $(this);
  885. if ($cb.data('indeterminate') == 'true' && $cb.prop('checked')) {
  886. $cb.next('.layui-form-checkbox').addClass('ew-form-indeterminate');
  887. }
  888. });
  889. };
  890. /**
  891. * 搜索数据
  892. * @param ids 关键字或数据id集合
  893. */
  894. TreeTable.prototype.filterData = function (ids) {
  895. var components = this.getComponents();
  896. var $trList = components.$table.children('tbody').children('tr');
  897. if (typeof ids == 'string') { // 关键字
  898. var keyword = ids;
  899. ids = [];
  900. $trList.each(function () {
  901. var id = $(this).data('id');
  902. $(this).children('td').each(function () {
  903. if ($(this).text().indexOf(keyword) != -1) {
  904. ids.push(id);
  905. return false;
  906. }
  907. });
  908. });
  909. }
  910. $trList.addClass('ew-tree-table-filter-hide');
  911. for (var i = 0; i < ids.length; i++) {
  912. var $tr = $trList.filter('[data-id="' + ids[i] + '"]');
  913. $tr.removeClass('ew-tree-table-filter-hide');
  914. // 联动父级
  915. var indent = parseInt($tr.data('indent'));
  916. $tr.prevAll('tr').each(function () {
  917. var tInd = parseInt($(this).data('indent'));
  918. if (tInd < indent) {
  919. $(this).removeClass('ew-tree-table-filter-hide'); // 联动父级
  920. if (!$(this).hasClass('ew-tree-table-open')) {
  921. toggleRow($(this));
  922. }
  923. indent = tInd;
  924. }
  925. });
  926. }
  927. };
  928. /** 重置搜索 */
  929. TreeTable.prototype.clearFilter = function () {
  930. var components = this.getComponents();
  931. var $trList = components.$table.children('tbody').children('tr');
  932. $trList.removeClass('ew-tree-table-filter-hide');
  933. };
  934. /** 展开指定行 */
  935. TreeTable.prototype.expand = function (id, cascade) {
  936. var components = this.getComponents();
  937. var $tr = components.$table.children('tbody').children('tr[data-id="' + id + '"]');
  938. if (!$tr.hasClass('ew-tree-table-open')) {
  939. $tr.children('td').find('.ew-tree-pack').trigger('click');
  940. }
  941. if (cascade == false) {
  942. return;
  943. }
  944. // 联动父级
  945. var indent = parseInt($tr.data('indent'));
  946. $tr.prevAll('tr').each(function () {
  947. var tInd = parseInt($(this).data('indent'));
  948. if (tInd < indent) {
  949. if (!$(this).hasClass('ew-tree-table-open')) {
  950. $(this).children('td').find('.ew-tree-pack').trigger('click');
  951. }
  952. indent = tInd;
  953. }
  954. });
  955. };
  956. /** 折叠指定行 */
  957. TreeTable.prototype.fold = function (id, cascade) {
  958. var components = this.getComponents();
  959. var $tr = components.$table.children('tbody').children('tr[data-id="' + id + '"]');
  960. if ($tr.hasClass('ew-tree-table-open')) {
  961. $tr.children('td').find('.ew-tree-pack').trigger('click');
  962. }
  963. if (cascade == false) {
  964. return;
  965. }
  966. // 联动父级
  967. var indent = parseInt($tr.data('indent'));
  968. $tr.prevAll('tr').each(function () {
  969. var tInd = parseInt($(this).data('indent'));
  970. if (tInd < indent) {
  971. if ($(this).hasClass('ew-tree-table-open')) {
  972. $(this).children('td').find('.ew-tree-pack').trigger('click');
  973. }
  974. indent = tInd;
  975. }
  976. });
  977. };
  978. /** 全部展开 */
  979. TreeTable.prototype.expandAll = function () {
  980. var that = this;
  981. var components = this.getComponents();
  982. var $trList = components.$table.children('tbody').children('tr');
  983. $trList.each(function () {
  984. that.expand($(this).data('id'), false);
  985. });
  986. };
  987. /** 全部折叠 */
  988. TreeTable.prototype.foldAll = function () {
  989. var that = this;
  990. var components = this.getComponents();
  991. var $trList = components.$table.children('tbody').children('tr');
  992. $trList.each(function () {
  993. that.fold($(this).data('id'), false);
  994. });
  995. };
  996. /** 获取当前数据 */
  997. TreeTable.prototype.getData = function () {
  998. return this.options.data;
  999. };
  1000. /** 重载表格 */
  1001. TreeTable.prototype.reload = function (opt) {
  1002. treeTb.render($.extend(this.options, opt));
  1003. };
  1004. /** 根据id更新数据 */
  1005. TreeTable.prototype.update = function (id, fields) {
  1006. var data = getDataById(this.getData(), id, this.options.tree);
  1007. $.extend(data, fields);
  1008. };
  1009. /** 根据id删除数据 */
  1010. TreeTable.prototype.del = function (id) {
  1011. delDataById(this.getData(), id, this.options.tree);
  1012. };
  1013. /** 获取当前选中行 */
  1014. TreeTable.prototype.checkStatus = function (needIndeterminate) {
  1015. (needIndeterminate == undefined) && (needIndeterminate = true);
  1016. var that = this;
  1017. var components = this.getComponents();
  1018. var $table = components.$table;
  1019. var checkboxFilter = components.checkboxFilter;
  1020. var radioFilter = components.radioFilter;
  1021. var list = [];
  1022. // 获取单选框选中数据
  1023. var $radio = $table.find('input[name="' + radioFilter + '"]');
  1024. if ($radio.length > 0) {
  1025. var id = $radio.filter(':checked').val();
  1026. var d = getDataById(this.getData(), id, this.options.tree);
  1027. if (d) {
  1028. list.push(d);
  1029. }
  1030. } else { // 获取复选框数据
  1031. $table.find('input[name="' + checkboxFilter + '"]:checked').each(function () {
  1032. var id = $(this).val();
  1033. var isIndeterminate = $(this).next('.layui-form-checkbox').hasClass('ew-form-indeterminate');
  1034. if (needIndeterminate || !isIndeterminate) {
  1035. var d = getDataById(that.getData(), id, that.options.tree);
  1036. if (d) {
  1037. d.isIndeterminate = isIndeterminate;
  1038. list.push(d);
  1039. }
  1040. }
  1041. });
  1042. }
  1043. return list;
  1044. };
  1045. /** 设置复/单选框选中 */
  1046. TreeTable.prototype.setChecked = function (ids) {
  1047. var components = this.getComponents();
  1048. var $table = components.$table;
  1049. var checkboxFilter = components.checkboxFilter;
  1050. var radioFilter = components.radioFilter;
  1051. var $radio = $table.find('input[name="' + radioFilter + '"]');
  1052. if ($radio.length > 0) { // 开启了单选框
  1053. $radio.each(function () {
  1054. if (ids[ids.length - 1] == $(this).val()) {
  1055. $(this).next('.layui-form-radio').trigger('click');
  1056. return false;
  1057. }
  1058. });
  1059. } else { // 开启了复选框
  1060. $table.find('input[name="' + checkboxFilter + '"]').each(function () {
  1061. var $cb = $(this);
  1062. var value = $cb.val();
  1063. var $layCb = $cb.next('.layui-form-checkbox');
  1064. for (var i = 0; i < ids.length; i++) {
  1065. if (value == ids[i]) {
  1066. var checked = $cb.prop('checked');
  1067. var indeterminate = $layCb.hasClass('ew-form-indeterminate');
  1068. if (!checked || indeterminate) {
  1069. $layCb.trigger('click');
  1070. }
  1071. }
  1072. }
  1073. });
  1074. }
  1075. };
  1076. /** 移除全部选中 */
  1077. TreeTable.prototype.removeAllChecked = function () {
  1078. var components = this.getComponents();
  1079. var $table = components.$table;
  1080. var checkboxFilter = components.checkboxFilter;
  1081. this.checkSubCB($table.children('tbody'), false);
  1082. };
  1083. /**
  1084. * 刷新指定父级下的节点
  1085. * @param id 父级id,空则全部刷新
  1086. * @param data 非异步模式替换的数据
  1087. */
  1088. TreeTable.prototype.refresh = function (id, data) {
  1089. if (isClass(id) == 'Array') {
  1090. data = id;
  1091. id = undefined;
  1092. }
  1093. var components = this.getComponents();
  1094. var $table = components.$table;
  1095. var d, $tr;
  1096. if (id != undefined) {
  1097. d = getDataById(this.getData(), id, this.options.tree);
  1098. $tr = $table.children('tbody').children('tr[data-id="' + id + '"]');
  1099. }
  1100. if (data) { // 数据模式
  1101. components.$tbLoading.addClass('ew-loading-float');
  1102. components.$tbLoading.show();
  1103. this.renderBodyData(data, d, $tr);
  1104. components.$tbLoading.hide();
  1105. components.$tbLoading.removeClass('ew-loading-float');
  1106. if (data && data.length > 0) {
  1107. components.$tbEmpty.hide();
  1108. } else {
  1109. components.$tbEmpty.show();
  1110. }
  1111. } else { // 异步模式
  1112. this.renderBodyAsync(d, $tr);
  1113. }
  1114. };
  1115. /** 生成表头 */
  1116. function getThead(options) {
  1117. var htmlStr = '<tr>';
  1118. for (var i = 0; i < options.cols.length; i++) {
  1119. var col = options.cols[i];
  1120. htmlStr += '<td data-index="' + i + '" ';
  1121. col.align && (htmlStr += ' align="' + col.align + '"'); // 对齐方式
  1122. htmlStr += ' >';
  1123. if (col.singleLine && col.type != 'checkbox') { // 单行显示
  1124. 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">';
  1125. }
  1126. // 标题
  1127. if (col.type == 'checkbox') {
  1128. htmlStr += options.getAllChooseBox();
  1129. } else {
  1130. htmlStr += (col.title || '');
  1131. }
  1132. // 列宽拖拽
  1133. if (!col.unresize && 'checkbox' != col.type && 'radio' != col.type && 'numbers' != col.type && 'space' != col.type) {
  1134. htmlStr += '<span class="ew-tb-resize"></span>';
  1135. }
  1136. if (col.singleLine) { // 单行显示
  1137. htmlStr += '</div></div>';
  1138. }
  1139. htmlStr += '</td>';
  1140. }
  1141. htmlStr += '</tr>';
  1142. return htmlStr;
  1143. }
  1144. /** 生成colgroup */
  1145. function getColgroup(options) {
  1146. var htmlStr = '<colgroup>';
  1147. for (var i = 0; i < options.cols.length; i++) {
  1148. var col = options.cols[i];
  1149. htmlStr += '<col ';
  1150. // 设置宽度
  1151. if (col.width) {
  1152. htmlStr += 'width="' + col.width + '"'
  1153. } else if (col.type == 'space') { // 空列
  1154. htmlStr += 'width="15"'
  1155. } else if (col.type == 'numbers') { // 序号列
  1156. htmlStr += 'width="40"'
  1157. } else if (col.type == 'checkbox' || col.type == 'radio') { // 复/单选框列
  1158. htmlStr += 'width="48"'
  1159. }
  1160. htmlStr += ' />';
  1161. }
  1162. htmlStr += '</colgroup>';
  1163. return htmlStr;
  1164. }
  1165. /** 计算table宽度 */
  1166. function getTbWidth(options) {
  1167. var minWidth = 0, setWidth = true;
  1168. for (var i = 0; i < options.cols.length; i++) {
  1169. var col = options.cols[i];
  1170. if (col.type == 'space') { // 空列
  1171. minWidth += 15;
  1172. } else if (col.type == 'numbers') { // 序号列
  1173. minWidth += 40;
  1174. } else if (col.type == 'checkbox' || col.type == 'radio') { // 复/单选框列
  1175. minWidth += 48;
  1176. } else if (!col.width || /\d+%$/.test(col.width)) { // 列未固定宽度
  1177. setWidth = false;
  1178. if (col.minWidth) {
  1179. minWidth += col.minWidth;
  1180. } else if (options.cellMinWidth) {
  1181. minWidth += options.cellMinWidth;
  1182. }
  1183. } else { // 列固定宽度
  1184. minWidth += col.width;
  1185. }
  1186. }
  1187. return {minWidth: minWidth, setWidth: setWidth};
  1188. }
  1189. /** 生成全选按钮 */
  1190. function getAllChooseBox(options) {
  1191. var tbFilter = $(options.elem).next().attr('lay-filter');
  1192. var cbAllFilter = 'ew_tb_choose_all_' + tbFilter;
  1193. return '<input type="checkbox" lay-filter="' + cbAllFilter + '" lay-skin="primary" class="ew-tree-table-checkbox"/>';
  1194. }
  1195. /** 获取列图标 */
  1196. function getIcon(d, treeOption) {
  1197. if (getHaveChild(d, treeOption)) {
  1198. return '<i class="ew-tree-icon layui-icon layui-icon-layer"></i>';
  1199. } else {
  1200. return '<i class="ew-tree-icon layui-icon layui-icon-file"></i>';
  1201. }
  1202. }
  1203. /** 折叠/展开行 */
  1204. function toggleRow($tr) {
  1205. var indent = parseInt($tr.data('indent'));
  1206. var isOpen = $tr.hasClass('ew-tree-table-open');
  1207. if (isOpen) { // 折叠
  1208. $tr.removeClass('ew-tree-table-open');
  1209. $tr.nextAll('tr').each(function () {
  1210. if (parseInt($(this).data('indent')) <= indent) {
  1211. return false;
  1212. }
  1213. $(this).addClass('ew-tree-tb-hide');
  1214. });
  1215. } else { // 展开
  1216. $tr.addClass('ew-tree-table-open');
  1217. var hideInd;
  1218. $tr.nextAll('tr').each(function () {
  1219. var ind = parseInt($(this).data('indent'));
  1220. if (ind <= indent) {
  1221. return false;
  1222. }
  1223. if (hideInd != undefined && ind > hideInd) {
  1224. return true;
  1225. }
  1226. $(this).removeClass('ew-tree-tb-hide');
  1227. if (!$(this).hasClass('ew-tree-table-open')) {
  1228. hideInd = parseInt($(this).data('indent'));
  1229. } else {
  1230. hideInd = undefined;
  1231. }
  1232. });
  1233. }
  1234. updateFixedTbHead($tr.parent().parent().parent().parent().parent());
  1235. }
  1236. /** 固定表头滚动条补丁 */
  1237. function updateFixedTbHead($view) {
  1238. var $group = $view.children('.ew-tree-table-group');
  1239. var $headBox = $group.children('.ew-tree-table-head');
  1240. var $tbBox = $group.children('.ew-tree-table-box');
  1241. var sWidth = $tbBox.width() - $tbBox.prop('clientWidth');
  1242. if (sWidth > 0) {
  1243. $headBox.css('border-right', sWidth + 'px solid #f2f2f2');
  1244. } else {
  1245. $headBox.css('border-right', 'none');
  1246. }
  1247. }
  1248. // 监听窗口大小改变
  1249. $(window).resize(function () {
  1250. $('.ew-tree-table').each(function () {
  1251. updateFixedTbHead($(this));
  1252. var $tbBox = $(this).children('.ew-tree-table-group').children('.ew-tree-table-box');
  1253. var full = $tbBox.attr('ew-tree-full');
  1254. if (full && device.ie && device.ie < 10) {
  1255. $tbBox.css('height', getPageHeight() - full);
  1256. }
  1257. });
  1258. });
  1259. // 表格溢出点击展开功能
  1260. $(document).on('mouseenter', '.ew-tree-table td', function () {
  1261. var $tdSingle = $(this).children('.ew-tree-table-td-single');
  1262. var $content = $tdSingle.children('.ew-tree-tips');
  1263. if ($tdSingle.length > 0 && $content.prop('scrollWidth') > $content.outerWidth()) {
  1264. $(this).append('<div class="layui-table-grid-down"><i class="layui-icon layui-icon-down"></i></div>');
  1265. }
  1266. }).on('mouseleave', '.ew-tree-table td', function () {
  1267. $(this).children('.layui-table-grid-down').remove();
  1268. });
  1269. // 点击箭头展开
  1270. $(document).on('click', '.ew-tree-table td>.layui-table-grid-down', function (e) {
  1271. hideAllTdTips();
  1272. var $tdSingle = $(this).parent().children('.ew-tree-table-td-single');
  1273. $tdSingle.addClass('ew-tree-tips-open');
  1274. var $box = $tdSingle.parents().filter('.ew-tree-table-box');
  1275. if ($box.length <= 0) {
  1276. $box = $tdSingle.parents().filter('.ew-tree-table-head');
  1277. }
  1278. if (($tdSingle.outerWidth() + $tdSingle.parent().offset().left) > $box.offset().left + $box.outerWidth()) {
  1279. $tdSingle.addClass('ew-show-left');
  1280. }
  1281. if (($tdSingle.outerHeight() + $tdSingle.parent().offset().top) > $box.offset().top + $box.outerHeight()) {
  1282. $tdSingle.addClass('ew-show-bottom');
  1283. }
  1284. e.stopPropagation();
  1285. });
  1286. // 点击关闭按钮关闭
  1287. $(document).on('click', '.ew-tree-table .ew-tree-tips-c', function (e) {
  1288. hideAllTdTips();
  1289. });
  1290. // 点击空白部分关闭
  1291. $(document).on('click', function () {
  1292. hideAllTdTips();
  1293. });
  1294. $(document).on('click', '.ew-tree-table-td-single.ew-tree-tips-open', function (e) {
  1295. e.stopPropagation();
  1296. });
  1297. /* 关闭所有单元格溢出提示框 */
  1298. function hideAllTdTips() {
  1299. var $single = $('.ew-tree-table-td-single');
  1300. $single.removeClass('ew-tree-tips-open');
  1301. $single.removeClass('ew-show-left');
  1302. }
  1303. /** 判断是否还有子节点 */
  1304. function getHaveChild(d, treeOption) {
  1305. var haveChild = false;
  1306. if (d[treeOption.haveChildName] != undefined) {
  1307. haveChild = d[treeOption.haveChildName];
  1308. haveChild = haveChild == true || haveChild == 'true';
  1309. } else if (d[treeOption.childName]) {
  1310. haveChild = d[treeOption.childName].length > 0;
  1311. }
  1312. return haveChild;
  1313. }
  1314. /** 补充pid字段 */
  1315. function addPidField(data, treeOption, parent) {
  1316. for (var i = 0; i < data.length; i++) {
  1317. if (parent) {
  1318. data[i][treeOption.pidName] = parent[treeOption.idName];
  1319. }
  1320. if (data[i][treeOption.childName] && data[i][treeOption.childName].length > 0) {
  1321. addPidField(data[i][treeOption.childName], treeOption, data[i]);
  1322. }
  1323. }
  1324. }
  1325. /** 根据id获取数据 */
  1326. function getDataById(data, id, treeOption) {
  1327. for (var i = 0; i < data.length; i++) {
  1328. if (data[i][treeOption.idName] == id) {
  1329. return data[i];
  1330. }
  1331. if (data[i][treeOption.childName] && data[i][treeOption.childName].length > 0) {
  1332. var d = getDataById(data[i][treeOption.childName], id, treeOption);
  1333. if (d != undefined) {
  1334. return d;
  1335. }
  1336. }
  1337. }
  1338. }
  1339. /** 根据id删除数据 */
  1340. function delDataById(data, id, treeOption) {
  1341. for (var i = 0; i < data.length; i++) {
  1342. if (data[i][treeOption.idName] == id) {
  1343. data.splice(i, 1);
  1344. return true;
  1345. }
  1346. if (data[i][treeOption.childName] && data[i][treeOption.childName].length > 0) {
  1347. var rs = delDataById(data[i][treeOption.childName], id, treeOption);
  1348. if (rs) {
  1349. return true;
  1350. }
  1351. }
  1352. }
  1353. }
  1354. /** 获取顶级的pId */
  1355. function getPids(list, idName, pidName) {
  1356. var pids = [];
  1357. for (var i = 0; i < list.length; i++) {
  1358. var hasPid = false;
  1359. for (var j = 0; j < list.length; j++) {
  1360. if (i != j && list[j][idName] == list[i][pidName]) {
  1361. hasPid = true;
  1362. }
  1363. }
  1364. if (!hasPid) {
  1365. pids.push(list[i][pidName]);
  1366. }
  1367. }
  1368. return pids;
  1369. }
  1370. /** 判断pId是否相等 */
  1371. function pidEquals(pId, pIds) {
  1372. if (isClass(pIds) == 'Array') {
  1373. for (var i = 0; i < pIds.length; i++) {
  1374. if (pId == pIds[i]) {
  1375. return true;
  1376. }
  1377. }
  1378. } else {
  1379. return pId == pIds;
  1380. }
  1381. return false;
  1382. }
  1383. /** 获取变量类型 */
  1384. function isClass(o) {
  1385. if (o === null)
  1386. return 'Null';
  1387. if (o === undefined)
  1388. return 'Undefined';
  1389. return Object.prototype.toString.call(o).slice(8, -1);
  1390. }
  1391. /* 获取浏览器高度 */
  1392. function getPageHeight() {
  1393. return document.documentElement.clientHeight || document.body.clientHeight;
  1394. }
  1395. /* 获取浏览器宽度 */
  1396. function getPageWidth() {
  1397. return document.documentElement.clientWidth || document.body.clientWidth;
  1398. }
  1399. /** 对外提供的方法 */
  1400. var treeTb = {
  1401. /* 渲染 */
  1402. render: function (options) {
  1403. return new TreeTable(options);
  1404. },
  1405. /* 事件监听 */
  1406. on: function (events, callback) {
  1407. return layui.onevent.call(this, MOD_NAME, events, callback);
  1408. },
  1409. /* pid转children形式 */
  1410. pidToChildren: function (data, idName, pidName, childName, pId) {
  1411. childName || (childName = 'children');
  1412. var newList = [];
  1413. for (var i = 0; i < data.length; i++) {
  1414. (pId == undefined) && (pId = getPids(data, idName, pidName));
  1415. if (pidEquals(data[i][pidName], pId)) {
  1416. var children = this.pidToChildren(data, idName, pidName, childName, data[i][idName]);
  1417. (children.length > 0) && (data[i][childName] = children);
  1418. newList.push(data[i]);
  1419. }
  1420. }
  1421. return newList;
  1422. }
  1423. };
  1424. exports('treeTable', treeTb);
  1425. });