tree.js 27 KB

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