tree.js 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843
  1. /**
  2. @Name:layui.tree 树
  3. @Author:star1029
  4. @License:MIT
  5. */
  6. layui.define('form', function(exports) {
  7. "use strict";
  8. var $ = layui.$,
  9. form = layui.form,
  10. layer = layui.layer
  11. //模块名
  12. , MOD_NAME = 'tree'
  13. //外部接口
  14. , tree = {
  15. config: {},
  16. index: layui[MOD_NAME] ? (layui[MOD_NAME].index + 10000) : 0
  17. //设置全局项
  18. ,
  19. set: function(options) {
  20. var that = this;
  21. that.config = $.extend({}, that.config, options);
  22. return that;
  23. }
  24. //事件监听
  25. ,
  26. on: function(events, callback) {
  27. return layui.onevent.call(this, MOD_NAME, events, callback);
  28. }
  29. }
  30. //操作当前实例
  31. , thisModule = function() {
  32. var that = this,
  33. options = that.config,
  34. id = options.id || that.index;
  35. thisModule.that[id] = that; //记录当前实例对象
  36. thisModule.config[id] = options; //记录当前实例配置项
  37. return {
  38. config: options
  39. //重置实例
  40. ,
  41. reload: function(options) {
  42. that.reload.call(that, options);
  43. },
  44. getChecked: function() {
  45. return that.getChecked.call(that);
  46. },
  47. setChecked: function(id) { //设置值
  48. return that.setChecked.call(that, id);
  49. }
  50. }
  51. }
  52. //获取当前实例配置项
  53. , getThisModuleConfig = function(id) {
  54. var config = thisModule.config[id];
  55. if (!config) hint.error('The ID option was not found in the ' + MOD_NAME + ' instance');
  56. return config || null;
  57. }
  58. //字符常量
  59. , SHOW = 'layui-show', HIDE = 'layui-hide', NONE = 'layui-none', DISABLED = 'layui-disabled'
  60. , ELEM_VIEW = 'layui-tree', ELEM_SET = 'layui-tree-set', ICON_CLICK = 'layui-tree-iconClick', ICON_ADD = 'layui-icon-addition', ICON_SUB = 'layui-icon-subtraction', ELEM_ENTRY = 'layui-tree-entry', ELEM_MAIN = 'layui-tree-main', ELEM_TEXT = 'layui-tree-txt', ELEM_PACK = 'layui-tree-pack', ELEM_SPREAD = 'layui-tree-spread', ELEM_LINE_SHORT = 'layui-tree-setLineShort', ELEM_SHOW = 'layui-tree-showLine', ELEM_EXTEND = 'layui-tree-lineExtend'
  61. //构造器
  62. , Class = function(options) {
  63. var that = this;
  64. that.index = ++tree.index;
  65. that.config = $.extend({}, that.config, tree.config, options);
  66. that.render();
  67. };
  68. //默认配置
  69. Class.prototype.config = {
  70. data: [] //数据
  71. ,
  72. showCheckbox: false //是否显示复选框
  73. ,
  74. showLine: true //是否开启连接线
  75. ,
  76. accordion: false //是否开启手风琴模式
  77. ,
  78. onlyIconControl: false //是否仅允许节点左侧图标控制展开收缩
  79. ,
  80. isJump: false //是否允许点击节点时弹出新窗口跳转
  81. ,
  82. edit: false //是否开启节点的操作图标
  83. ,
  84. checkChild: true //选中父级时 是否选选择所有子级
  85. ,
  86. text: {
  87. defaultNodeName: '未命名' //节点默认名称
  88. ,
  89. none: '无数据' //数据为空时的文本提示
  90. }
  91. };
  92. //重载实例
  93. Class.prototype.reload = function(options) {
  94. var that = this;
  95. layui.each(options, function(key, item) {
  96. if (item.constructor === Array) delete that.config[key];
  97. });
  98. that.config = $.extend(true, {}, that.config, options);
  99. that.render();
  100. };
  101. //主体渲染
  102. Class.prototype.render = function() {
  103. var that = this,
  104. options = that.config;
  105. that.checkids = [];
  106. var temp = $('<div class="layui-tree' + (options.showCheckbox ? " layui-form" : "") + (options.showLine ? " layui-tree-line" : "") + '" lay-filter="LAY-tree-' + that.index + '"></div>');
  107. that.tree(temp);
  108. var othis = options.elem = $(options.elem);
  109. if (!othis[0]) return;
  110. //索引
  111. that.key = options.id || that.index;
  112. //插入组件结构
  113. that.elem = temp;
  114. that.elemNone = $('<div class="layui-tree-emptyText">' + options.text.none + '</div>');
  115. othis.html(that.elem);
  116. if (that.elem.find('.layui-tree-set').length == 0) {
  117. return that.elem.append(that.elemNone);
  118. };
  119. //复选框渲染
  120. if (options.showCheckbox) {
  121. that.renderForm('checkbox');
  122. };
  123. that.elem.find('.layui-tree-set').each(function() {
  124. var othis = $(this);
  125. //最外层
  126. if (!othis.parent('.layui-tree-pack')[0]) {
  127. othis.addClass('layui-tree-setHide');
  128. };
  129. //没有下一个节点 上一层父级有延伸线
  130. if (!othis.next()[0] && othis.parents('.layui-tree-pack').eq(1).hasClass('layui-tree-lineExtend')) {
  131. othis.addClass(ELEM_LINE_SHORT);
  132. };
  133. //没有下一个节点 外层最后一个
  134. if (!othis.next()[0] && !othis.parents('.layui-tree-set').eq(0).next()[0]) {
  135. othis.addClass(ELEM_LINE_SHORT);
  136. };
  137. });
  138. that.events();
  139. };
  140. //渲染表单
  141. Class.prototype.renderForm = function(type) {
  142. form.render(type, 'LAY-tree-' + this.index);
  143. };
  144. //节点解析
  145. Class.prototype.tree = function(elem, children) {
  146. var that = this,
  147. options = that.config,
  148. data = children || options.data;
  149. //遍历数据
  150. layui.each(data, function(index, item) {
  151. var hasChild = item.children && item.children.length > 0,
  152. packDiv = $('<div class="layui-tree-pack" ' + (item.spread ? 'style="display: block;"' : '') + '></div>'),
  153. entryDiv = $(['<div data-id="' + item.id + '" class="layui-tree-set' + (item.spread ? " layui-tree-spread" : "") + (item.checked ? " layui-tree-checkedFirst" : "") + '">', '<div class="layui-tree-entry">', '<div class="layui-tree-main">'
  154. //箭头
  155. ,
  156. function() {
  157. if (options.showLine) {
  158. if (hasChild) {
  159. return '<span class="layui-tree-iconClick layui-tree-icon"><i class="layui-icon ' + (item.spread ? "layui-icon-subtraction" : "layui-icon-addition") + '"></i></span>';
  160. } else {
  161. return '<span class="layui-tree-iconClick"><i class="layui-icon layui-icon-file"></i></span>';
  162. };
  163. } else {
  164. return '<span class="layui-tree-iconClick"><i class="layui-tree-iconArrow ' + (hasChild ? "" : HIDE) + '"></i></span>';
  165. };
  166. }()
  167. //复选框
  168. ,
  169. function() {
  170. return options.showCheckbox ? '<input type="checkbox" name="' + (item.field || ('layuiTreeCheck_' + item.id)) + '" same="layuiTreeCheck" lay-skin="primary" ' + (item.disabled ? "disabled" : "") + ' value="' + item.id + '">' : '';
  171. }()
  172. //节点
  173. ,
  174. function() {
  175. if (options.isJump && item.href) {
  176. return '<a href="' + item.href + '" target="_blank" class="' + ELEM_TEXT + '">' + (item.title || item.label || options.text.defaultNodeName) + '</a>';
  177. } else {
  178. return '<span class="' + ELEM_TEXT + (item.disabled ? ' ' + DISABLED : '') + '">' + (item.title || item.label || options.text.defaultNodeName) + '</span>';
  179. }
  180. }(), '</div>'
  181. //节点操作图标
  182. ,
  183. function() {
  184. if (!options.edit) return '';
  185. var editIcon = {
  186. add: '<i class="layui-icon layui-icon-add-1" data-type="add"></i>',
  187. update: '<i class="layui-icon layui-icon-edit" data-type="update"></i>',
  188. del: '<i class="layui-icon layui-icon-delete" data-type="del"></i>'
  189. },
  190. arr = ['<div class="layui-btn-group layui-tree-btnGroup">'];
  191. if (options.edit === true) {
  192. options.edit = ['update', 'del']
  193. }
  194. if (typeof options.edit === 'object') {
  195. layui.each(options.edit, function(i, val) {
  196. arr.push(editIcon[val] || '')
  197. });
  198. return arr.join('') + '</div>';
  199. }
  200. }(), '</div></div>'
  201. ].join(''));
  202. //如果有子节点,则递归继续生成树
  203. if (hasChild) {
  204. entryDiv.append(packDiv);
  205. that.tree(packDiv, item.children);
  206. };
  207. elem.append(entryDiv);
  208. //若有前置节点,前置节点加连接线
  209. if (entryDiv.prev('.' + ELEM_SET)[0]) {
  210. entryDiv.prev().children('.layui-tree-pack').addClass('layui-tree-showLine');
  211. };
  212. //若无子节点,则父节点加延伸线
  213. if (!hasChild) {
  214. entryDiv.parent('.layui-tree-pack').addClass('layui-tree-lineExtend');
  215. };
  216. //展开节点操作
  217. that.spread(entryDiv, item);
  218. //选择框
  219. if (options.showCheckbox) {
  220. item.checked && that.checkids.push(item.id);
  221. that.checkClick(entryDiv, item);
  222. }
  223. //操作节点
  224. options.edit && that.operate(entryDiv, item);
  225. });
  226. };
  227. //展开节点
  228. Class.prototype.spread = function(elem, item) {
  229. var that = this,
  230. options = that.config,
  231. entry = elem.children('.' + ELEM_ENTRY),
  232. elemMain = entry.children('.' + ELEM_MAIN),
  233. elemIcon = entry.find('.' + ICON_CLICK),
  234. elemText = entry.find('.' + ELEM_TEXT),
  235. touchOpen = options.onlyIconControl ? elemIcon : elemMain //判断展开通过节点还是箭头图标
  236. ,
  237. state = '';
  238. //展开收缩
  239. touchOpen.on('click', function(e) {
  240. var packCont = elem.children('.' + ELEM_PACK),
  241. iconClick = touchOpen.children('.layui-icon')[0] ? touchOpen.children('.layui-icon') : touchOpen.find('.layui-tree-icon').children('.layui-icon');
  242. //若没有子节点
  243. if (!packCont[0]) {
  244. state = 'normal';
  245. } else {
  246. if (elem.hasClass(ELEM_SPREAD)) {
  247. elem.removeClass(ELEM_SPREAD);
  248. packCont.slideUp(200);
  249. iconClick.removeClass(ICON_SUB).addClass(ICON_ADD);
  250. } else {
  251. elem.addClass(ELEM_SPREAD);
  252. packCont.slideDown(200);
  253. iconClick.addClass(ICON_SUB).removeClass(ICON_ADD);
  254. //是否手风琴
  255. if (options.accordion) {
  256. var sibls = elem.siblings('.' + ELEM_SET);
  257. sibls.removeClass(ELEM_SPREAD);
  258. sibls.children('.' + ELEM_PACK).slideUp(200);
  259. sibls.find('.layui-tree-icon').children('.layui-icon').removeClass(ICON_SUB).addClass(ICON_ADD);
  260. };
  261. };
  262. };
  263. });
  264. //点击回调
  265. elemText.on('click', function() {
  266. var othis = $(this);
  267. //判断是否禁用状态
  268. if (othis.hasClass(DISABLED)) return;
  269. //判断展开收缩状态
  270. if (elem.hasClass(ELEM_SPREAD)) {
  271. state = options.onlyIconControl ? 'open' : 'close';
  272. } else {
  273. state = options.onlyIconControl ? 'close' : 'open';
  274. }
  275. //点击产生的回调
  276. options.click && options.click({
  277. elem: elem,
  278. state: state,
  279. data: item
  280. });
  281. });
  282. };
  283. //计算复选框选中状态
  284. Class.prototype.setCheckbox = function(elem, item, elemCheckbox) {
  285. var that = this,
  286. options = that.config,
  287. checked = elemCheckbox.prop('checked');
  288. if (elemCheckbox.prop('disabled')) return;
  289. //同步子节点选中状态
  290. if (typeof item.children === 'object' || elem.find('.' + ELEM_PACK)[0]) {
  291. //同步子节点选中状态
  292. if (options.checkChild) {
  293. var childs = elem.find('.' + ELEM_PACK).find('input[same="layuiTreeCheck"]');
  294. childs.each(function() {
  295. if (this.disabled) return; //不可点击则跳过
  296. this.checked = checked;
  297. });
  298. } else {
  299. if (!checked) {
  300. var childs = elem.find('.' + ELEM_PACK).find('input[same="layuiTreeCheck"]');
  301. childs.each(function() {
  302. if (this.disabled) return; //不可点击则跳过
  303. this.checked = checked;
  304. });
  305. }
  306. }
  307. }
  308. //同步父节点选中状态
  309. var setParentsChecked = function(thisNodeElem) {
  310. //若无父节点,则终止递归
  311. if (!thisNodeElem.parents('.' + ELEM_SET)[0]) return;
  312. var state, parentPack = thisNodeElem.parent('.' + ELEM_PACK),
  313. parentNodeElem = parentPack.parent(),
  314. parentCheckbox = parentPack.prev().find('input[same="layuiTreeCheck"]');
  315. //如果子节点有任意一条选中,则父节点为选中状态
  316. if (checked) {
  317. parentCheckbox.prop('checked', checked);
  318. } else { //如果当前节点取消选中,则根据计算“兄弟和子孙”节点选中状态,来同步父节点选中状态
  319. parentPack.find('input[same="layuiTreeCheck"]').each(function() {
  320. if (this.checked) {
  321. state = true;
  322. }
  323. });
  324. //如果兄弟子孙节点全部未选中,则父节点也应为非选中状态
  325. state || parentCheckbox.prop('checked', false);
  326. }
  327. //向父节点递归
  328. setParentsChecked(parentNodeElem);
  329. };
  330. setParentsChecked(elem);
  331. that.renderForm('checkbox');
  332. };
  333. //复选框选择
  334. Class.prototype.checkClick = function(elem, item) {
  335. var that = this,
  336. options = that.config,
  337. entry = elem.children('.' + ELEM_ENTRY),
  338. elemMain = entry.children('.' + ELEM_MAIN);
  339. //点击复选框
  340. elemMain.on('click', 'input[same="layuiTreeCheck"]+', function(e) {
  341. layui.stope(e); //阻止点击节点事件
  342. var elemCheckbox = $(this).prev(),
  343. checked = elemCheckbox.prop('checked');
  344. if (elemCheckbox.prop('disabled')) return;
  345. that.setCheckbox(elem, item, elemCheckbox);
  346. //复选框点击产生的回调
  347. options.oncheck && options.oncheck({
  348. elem: elem,
  349. checked: checked,
  350. data: item
  351. });
  352. });
  353. };
  354. //节点操作
  355. Class.prototype.operate = function(elem, item) {
  356. var that = this,
  357. options = that.config,
  358. entry = elem.children('.' + ELEM_ENTRY),
  359. elemMain = entry.children('.' + ELEM_MAIN);
  360. entry.children('.layui-tree-btnGroup').on('click', '.layui-icon', function(e) {
  361. layui.stope(e); //阻止节点操作
  362. var type = $(this).data("type"),
  363. packCont = elem.children('.' + ELEM_PACK),
  364. returnObj = {
  365. data: item,
  366. type: type,
  367. elem: elem
  368. };
  369. //增加
  370. if (type == 'add') {
  371. //若节点本身无子节点
  372. if (!packCont[0]) {
  373. //若开启连接线,更改图标样式
  374. if (options.showLine) {
  375. elemMain.find('.' + ICON_CLICK).addClass('layui-tree-icon');
  376. elemMain.find('.' + ICON_CLICK).children('.layui-icon').addClass(ICON_ADD).removeClass('layui-icon-file');
  377. //若未开启连接线,显示箭头
  378. } else {
  379. elemMain.find('.layui-tree-iconArrow').removeClass(HIDE);
  380. };
  381. //节点添加子节点容器
  382. elem.append('<div class="layui-tree-pack"></div>');
  383. };
  384. //新增节点
  385. var key = options.operate && options.operate(returnObj),
  386. obj = {};
  387. obj.title = options.text.defaultNodeName;
  388. obj.id = key;
  389. that.tree(elem.children('.' + ELEM_PACK), [obj]);
  390. //放在新增后面,因为要对元素进行操作
  391. if (options.showLine) {
  392. //节点本身无子节点
  393. if (!packCont[0]) {
  394. //遍历兄弟节点,判断兄弟节点是否有子节点
  395. var siblings = elem.siblings('.' + ELEM_SET),
  396. num = 1,
  397. parentPack = elem.parent('.' + ELEM_PACK);
  398. layui.each(siblings, function(index, i) {
  399. if (!$(i).children('.' + ELEM_PACK)[0]) {
  400. num = 0;
  401. };
  402. });
  403. //若兄弟节点都有子节点
  404. if (num == 1) {
  405. //兄弟节点添加连接线
  406. siblings.children('.' + ELEM_PACK).addClass(ELEM_SHOW);
  407. siblings.children('.' + ELEM_PACK).children('.' + ELEM_SET).removeClass(ELEM_LINE_SHORT);
  408. elem.children('.' + ELEM_PACK).addClass(ELEM_SHOW);
  409. //父级移除延伸线
  410. parentPack.removeClass(ELEM_EXTEND);
  411. //同层节点最后一个更改线的状态
  412. parentPack.children('.' + ELEM_SET).last().children('.' + ELEM_PACK).children('.' + ELEM_SET).last().addClass(ELEM_LINE_SHORT);
  413. } else {
  414. elem.children('.' + ELEM_PACK).children('.' + ELEM_SET).addClass(ELEM_LINE_SHORT);
  415. };
  416. } else {
  417. //添加延伸线
  418. if (!packCont.hasClass(ELEM_EXTEND)) {
  419. packCont.addClass(ELEM_EXTEND);
  420. };
  421. //子节点添加延伸线
  422. elem.find('.' + ELEM_PACK).each(function() {
  423. $(this).children('.' + ELEM_SET).last().addClass(ELEM_LINE_SHORT);
  424. });
  425. //如果前一个节点有延伸线
  426. if (packCont.children('.' + ELEM_SET).last().prev().hasClass(ELEM_LINE_SHORT)) {
  427. packCont.children('.' + ELEM_SET).last().prev().removeClass(ELEM_LINE_SHORT);
  428. } else {
  429. //若之前的没有,说明处于连接状态
  430. packCont.children('.' + ELEM_SET).last().removeClass(ELEM_LINE_SHORT);
  431. };
  432. //若是最外层,要始终保持相连的状态
  433. if (!elem.parent('.' + ELEM_PACK)[0] && elem.next()[0]) {
  434. packCont.children('.' + ELEM_SET).last().removeClass(ELEM_LINE_SHORT);
  435. };
  436. };
  437. };
  438. if (!options.showCheckbox) return;
  439. //若开启复选框,同步新增节点状态
  440. if (elemMain.find('input[same="layuiTreeCheck"]')[0].checked) {
  441. var packLast = elem.children('.' + ELEM_PACK).children('.' + ELEM_SET).last();
  442. packLast.find('input[same="layuiTreeCheck"]')[0].checked = true;
  443. };
  444. that.renderForm('checkbox');
  445. //修改
  446. } else if (type == 'update') {
  447. var text = elemMain.children('.' + ELEM_TEXT).html();
  448. elemMain.children('.' + ELEM_TEXT).html('');
  449. //添加输入框,覆盖在文字上方
  450. elemMain.append('<input type="text" class="layui-tree-editInput">');
  451. //获取焦点
  452. elemMain.children('.layui-tree-editInput').val(text).focus();
  453. //嵌入文字移除输入框
  454. var getVal = function(input) {
  455. var textNew = input.val().trim();
  456. textNew = textNew ? textNew : options.text.defaultNodeName;
  457. input.remove();
  458. elemMain.children('.' + ELEM_TEXT).html(textNew);
  459. //同步数据
  460. returnObj.data.title = textNew;
  461. //节点修改的回调
  462. options.operate && options.operate(returnObj);
  463. };
  464. //失去焦点
  465. elemMain.children('.layui-tree-editInput').blur(function() {
  466. getVal($(this));
  467. });
  468. //回车
  469. elemMain.children('.layui-tree-editInput').on('keydown', function(e) {
  470. if (e.keyCode === 13) {
  471. e.preventDefault();
  472. getVal($(this));
  473. };
  474. });
  475. //删除
  476. } else {
  477. layer.confirm('确认删除该节点 "<span style="color: #999;">' + (item.title || '') + '</span>" 吗?', function(index) {
  478. options.operate && options.operate(returnObj); //节点删除的回调
  479. returnObj.status = 'remove'; //标注节点删除
  480. layer.close(index);
  481. //若删除最后一个,显示空数据提示
  482. if (!elem.prev('.' + ELEM_SET)[0] && !elem.next('.' + ELEM_SET)[0] && !elem.parent('.' + ELEM_PACK)[0]) {
  483. elem.remove();
  484. that.elem.append(that.elemNone);
  485. return;
  486. };
  487. //若有兄弟节点
  488. if (elem.siblings('.' + ELEM_SET).children('.' + ELEM_ENTRY)[0]) {
  489. //若开启复选框
  490. if (options.showCheckbox) {
  491. //若开启复选框,进行下步操作
  492. var elemDel = function(elem) {
  493. //若无父结点,则不执行
  494. if (!elem.parents('.' + ELEM_SET)[0]) return;
  495. var siblingTree = elem.siblings('.' + ELEM_SET).children('.' + ELEM_ENTRY),
  496. parentTree = elem.parent('.' + ELEM_PACK).prev(),
  497. checkState = parentTree.find('input[same="layuiTreeCheck"]')[0],
  498. state = 1,
  499. num = 0;
  500. //若父节点未勾选
  501. if (checkState.checked == false) {
  502. //遍历兄弟节点
  503. siblingTree.each(function(i, item1) {
  504. var input = $(item1).find('input[same="layuiTreeCheck"]')[0]
  505. if (input.checked == false && !input.disabled) {
  506. state = 0;
  507. };
  508. //判断是否全为不可勾选框
  509. if (!input.disabled) {
  510. num = 1;
  511. };
  512. });
  513. //若有可勾选选择框并且已勾选
  514. if (state == 1 && num == 1) {
  515. //勾选父节点
  516. checkState.checked = true;
  517. that.renderForm('checkbox');
  518. //向上遍历祖先节点
  519. elemDel(parentTree.parent('.' + ELEM_SET));
  520. };
  521. };
  522. };
  523. elemDel(elem);
  524. };
  525. //若开启连接线
  526. if (options.showLine) {
  527. //遍历兄弟节点,判断兄弟节点是否有子节点
  528. var siblings = elem.siblings('.' + ELEM_SET),
  529. num = 1,
  530. parentPack = elem.parent('.' + ELEM_PACK);
  531. layui.each(siblings, function(index, i) {
  532. if (!$(i).children('.' + ELEM_PACK)[0]) {
  533. num = 0;
  534. };
  535. });
  536. //若兄弟节点都有子节点
  537. if (num == 1) {
  538. //若节点本身无子节点
  539. if (!packCont[0]) {
  540. //父级去除延伸线,因为此时子节点里没有空节点
  541. parentPack.removeClass(ELEM_EXTEND);
  542. siblings.children('.' + ELEM_PACK).addClass(ELEM_SHOW);
  543. siblings.children('.' + ELEM_PACK).children('.' + ELEM_SET).removeClass(ELEM_LINE_SHORT);
  544. };
  545. //若为最后一个节点
  546. if (!elem.next()[0]) {
  547. elem.prev().children('.' + ELEM_PACK).children('.' + ELEM_SET).last().addClass(ELEM_LINE_SHORT);
  548. } else {
  549. parentPack.children('.' + ELEM_SET).last().children('.' + ELEM_PACK).children('.' + ELEM_SET).last().addClass(ELEM_LINE_SHORT);
  550. };
  551. //若为最外层最后一个节点,去除前一个结点的连接线
  552. if (!elem.next()[0] && !elem.parents('.' + ELEM_SET)[1] && !elem.parents('.' + ELEM_SET).eq(0).next()[0]) {
  553. elem.prev('.' + ELEM_SET).addClass(ELEM_LINE_SHORT);
  554. };
  555. } else {
  556. //若为最后一个节点且有延伸线
  557. if (!elem.next()[0] && elem.hasClass(ELEM_LINE_SHORT)) {
  558. elem.prev().addClass(ELEM_LINE_SHORT);
  559. };
  560. };
  561. };
  562. } else {
  563. //若无兄弟节点
  564. var prevDiv = elem.parent('.' + ELEM_PACK).prev();
  565. //若开启了连接线
  566. if (options.showLine) {
  567. prevDiv.find('.' + ICON_CLICK).removeClass('layui-tree-icon');
  568. prevDiv.find('.' + ICON_CLICK).children('.layui-icon').removeClass(ICON_SUB).addClass('layui-icon-file');
  569. //父节点所在层添加延伸线
  570. var pare = prevDiv.parents('.' + ELEM_PACK).eq(0);
  571. pare.addClass(ELEM_EXTEND);
  572. //兄弟节点最后子节点添加延伸线
  573. pare.children('.' + ELEM_SET).each(function() {
  574. $(this).children('.' + ELEM_PACK).children('.' + ELEM_SET).last().addClass(ELEM_LINE_SHORT);
  575. });
  576. } else {
  577. //父节点隐藏箭头
  578. prevDiv.find('.layui-tree-iconArrow').addClass(HIDE);
  579. };
  580. //移除展开属性
  581. elem.parents('.' + ELEM_SET).eq(0).removeClass(ELEM_SPREAD);
  582. //移除节点容器
  583. elem.parent('.' + ELEM_PACK).remove();
  584. };
  585. elem.remove();
  586. });
  587. };
  588. });
  589. };
  590. //部分事件
  591. Class.prototype.events = function() {
  592. var that = this,
  593. options = that.config,
  594. checkWarp = that.elem.find('.layui-tree-checkedFirst');
  595. //初始选中
  596. that.setChecked(that.checkids);
  597. //搜索
  598. that.elem.find('.layui-tree-search').on('keyup', function() {
  599. var input = $(this),
  600. val = input.val(),
  601. pack = input.nextAll(),
  602. arr = [];
  603. //遍历所有的值
  604. pack.find('.' + ELEM_TEXT).each(function() {
  605. var entry = $(this).parents('.' + ELEM_ENTRY);
  606. //若值匹配,加一个类以作标识
  607. if ($(this).html().indexOf(val) != -1) {
  608. arr.push($(this).parent());
  609. var select = function(div) {
  610. div.addClass('layui-tree-searchShow');
  611. //向上父节点渲染
  612. if (div.parent('.' + ELEM_PACK)[0]) {
  613. select(div.parent('.' + ELEM_PACK).parent('.' + ELEM_SET));
  614. };
  615. };
  616. select(entry.parent('.' + ELEM_SET));
  617. };
  618. });
  619. //根据标志剔除
  620. pack.find('.' + ELEM_ENTRY).each(function() {
  621. var parent = $(this).parent('.' + ELEM_SET);
  622. if (!parent.hasClass('layui-tree-searchShow')) {
  623. parent.addClass(HIDE);
  624. };
  625. });
  626. if (pack.find('.layui-tree-searchShow').length == 0) {
  627. that.elem.append(that.elemNone);
  628. };
  629. //节点过滤的回调
  630. options.onsearch && options.onsearch({
  631. elem: arr
  632. });
  633. });
  634. //还原搜索初始状态
  635. that.elem.find('.layui-tree-search').on('keydown', function() {
  636. $(this).nextAll().find('.' + ELEM_ENTRY).each(function() {
  637. var parent = $(this).parent('.' + ELEM_SET);
  638. parent.removeClass('layui-tree-searchShow ' + HIDE);
  639. });
  640. if ($('.layui-tree-emptyText')[0]) $('.layui-tree-emptyText').remove();
  641. });
  642. };
  643. //得到选中节点
  644. Class.prototype.getChecked = function() {
  645. var that = this,
  646. options = that.config,
  647. checkId = [],
  648. checkData = [];
  649. //遍历节点找到选中索引
  650. that.elem.find('.layui-form-checked').each(function() {
  651. checkId.push($(this).prev()[0].value);
  652. });
  653. //遍历节点
  654. var eachNodes = function(data, checkNode) {
  655. layui.each(data, function(index, item) {
  656. layui.each(checkId, function(index2, item2) {
  657. if (item.id == item2) {
  658. var cloneItem = $.extend({}, item);
  659. delete cloneItem.children;
  660. checkNode.push(cloneItem);
  661. if (item.children) {
  662. cloneItem.children = [];
  663. eachNodes(item.children, cloneItem.children);
  664. }
  665. return true
  666. }
  667. });
  668. });
  669. };
  670. eachNodes($.extend({}, options.data), checkData);
  671. return checkData;
  672. };
  673. //设置选中节点
  674. Class.prototype.setChecked = function(checkedId) {
  675. var that = this,
  676. options = that.config;
  677. //初始选中
  678. that.elem.find('.' + ELEM_SET).each(function(i, item) {
  679. var thisId = $(this).data('id'),
  680. input = $(item).children('.' + ELEM_ENTRY).find('input[same="layuiTreeCheck"]'),
  681. reInput = input.next();
  682. //若返回数字
  683. if (typeof checkedId === 'number') {
  684. if (thisId == checkedId) {
  685. if (!input[0].checked) {
  686. reInput.click();
  687. };
  688. return false;
  689. };
  690. }
  691. //若返回数组
  692. else if (typeof checkedId === 'object') {
  693. layui.each(checkedId, function(index, value) {
  694. if (value == thisId && !input[0].checked) {
  695. reInput.click();
  696. return true;
  697. }
  698. });
  699. };
  700. });
  701. };
  702. //记录所有实例
  703. thisModule.that = {}; //记录所有实例对象
  704. thisModule.config = {}; //记录所有实例配置项
  705. //重载实例
  706. tree.reload = function(id, options) {
  707. var that = thisModule.that[id];
  708. that.reload(options);
  709. return thisModule.call(that);
  710. };
  711. //获得选中的节点数据
  712. tree.getChecked = function(id) {
  713. var that = thisModule.that[id];
  714. return that.getChecked();
  715. };
  716. //设置选中节点
  717. tree.setChecked = function(id, checkedId) {
  718. var that = thisModule.that[id];
  719. return that.setChecked(checkedId);
  720. };
  721. //核心入口
  722. tree.render = function(options) {
  723. var inst = new Class(options);
  724. return thisModule.call(inst);
  725. };
  726. exports(MOD_NAME, tree);
  727. })