tree.js 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125
  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. //模块名
  11. ,MOD_NAME = 'tree'
  12. //外部接口
  13. ,tree = {
  14. config: {}
  15. ,index: layui[MOD_NAME] ? (layui[MOD_NAME].index + 10000) : 0
  16. //设置全局项
  17. ,set: function(options){
  18. var that = this;
  19. that.config = $.extend({}, that.config, options);
  20. return that;
  21. }
  22. //事件监听
  23. ,on: function(events, callback){
  24. return layui.onevent.call(this, MOD_NAME, events, callback);
  25. }
  26. }
  27. //操作当前实例
  28. ,thisModule = function(){
  29. var that = this
  30. ,options = that.config
  31. ,id = options.id || that.index;
  32. thisModule.that[id] = that; //记录当前实例对象
  33. thisModule.config[id] = options; //记录当前实例配置项
  34. return {
  35. config: options
  36. //重置实例
  37. ,reload: function(options){
  38. that.reload.call(that, options);
  39. }
  40. ,getChecked: function(){
  41. return that.getChecked.call(that);
  42. }
  43. ,setChecked: function(id){//设置值
  44. return that.setChecked.call(that, id);
  45. }
  46. }
  47. }
  48. //获取当前实例配置项
  49. ,getThisModuleConfig = function(id){
  50. var config = thisModule.config[id];
  51. if(!config) hint.error('The ID option was not found in the '+ MOD_NAME +' instance');
  52. return config || null;
  53. }
  54. //字符常量
  55. ,SHOW = 'layui-show', HIDE = 'layui-hide', NONE = 'layui-none', DISABLED = 'layui-disabled'
  56. ,ELEM_VIEW = 'layui-tree', ELEM_SET = 'layui-tree-set', ICON_CLICK = 'layui-tree-iconClick'
  57. ,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'
  58. ,ELEM_LINE_SHORT = 'layui-tree-setLineShort', ELEM_SHOW = 'layui-tree-showLine', ELEM_EXTEND = 'layui-tree-lineExtend'
  59. //构造器
  60. ,Class = function(options){
  61. var that = this;
  62. that.index = ++tree.index;
  63. that.config = $.extend({}, that.config, tree.config, options);
  64. that.render();
  65. };
  66. //默认配置
  67. Class.prototype.config = {
  68. data: [] //数据
  69. ,showCheckbox: false //是否显示复选框
  70. ,showLine: true //是否开启连接线
  71. ,accordion: false //是否开启手风琴模式
  72. ,onlyIconControl: false //是否仅允许节点左侧图标控制展开收缩
  73. ,isJump: false //是否允许点击节点时弹出新窗口跳转
  74. ,edit: false //是否开启节点的操作图标
  75. //,showSearch: false //是否打开节点过滤
  76. //,drag: false //是否开启节点拖拽
  77. ,text: {
  78. defaultNodeName: '未命名' //节点默认名称
  79. ,none: '无数据' //数据为空时的文本提示
  80. }
  81. };
  82. //重载实例
  83. Class.prototype.reload = function(options){
  84. var that = this;
  85. layui.each(options, function(key, item){
  86. if(item.constructor === Array) delete that.config[key];
  87. });
  88. that.config = $.extend(true, {}, that.config, options);
  89. that.render();
  90. };
  91. //主体渲染
  92. Class.prototype.render = function(){
  93. var that = this
  94. ,options = that.config;
  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. if(options.showSearch){
  100. temp.prepend('<input type="text" class="layui-input layui-tree-search" placeholder="请输入关键字进行过滤">');
  101. };
  102. //索引
  103. that.key = options.id || that.index;
  104. //插入组件结构
  105. that.elem = temp;
  106. that.elemNone = $('<div class="layui-tree-emptyText">'+ options.text.none +'</div>');
  107. othis.html(that.elem);
  108. if(that.elem.find('.layui-tree-set').length == 0){
  109. return that.elem.append(that.elemNone);
  110. };
  111. //拖拽事件
  112. options.drag && that.drag();
  113. //复选框渲染
  114. if(options.showCheckbox){
  115. that.renderForm('checkbox');
  116. };
  117. that.elem.find('.layui-tree-set').each(function(){
  118. var othis = $(this);
  119. //最外层
  120. if(!othis.parent('.layui-tree-pack')[0]){
  121. othis.addClass('layui-tree-setHide');
  122. };
  123. //没有下一个节点 上一层父级有延伸线
  124. if(!othis.next()[0] && othis.parents('.layui-tree-pack').eq(1).hasClass('layui-tree-lineExtend')){
  125. othis.addClass(ELEM_LINE_SHORT);
  126. };
  127. //没有下一个节点 外层最后一个
  128. if(!othis.next()[0] && !othis.parents('.layui-tree-set').eq(0).next()[0]){
  129. othis.addClass(ELEM_LINE_SHORT);
  130. };
  131. });
  132. that.events();
  133. };
  134. //渲染表单
  135. Class.prototype.renderForm = function(type){
  136. form.render(type, 'LAY-tree-'+ this.index);
  137. };
  138. //节点解析
  139. Class.prototype.tree = function(elem, children){
  140. var that = this
  141. ,options = that.config
  142. ,data = children || options.data;
  143. //遍历数据
  144. layui.each(data, function(index, item){
  145. var hasChild = item.children && item.children.length > 0
  146. ,packDiv = $('<div class="layui-tree-pack" '+ (item.spread ? 'style="display: block;"' : '') +'"></div>')
  147. ,entryDiv = $(['<div data-id="'+ item.id +'" class="layui-tree-set'+ (item.spread ? " layui-tree-spread" : "") + (item.checked ? " layui-tree-checkedFirst" : "") +'">'
  148. ,'<div '+ (options.drag && !item.fixed ? 'draggable="true"' : '') +' class="layui-tree-entry">'
  149. ,'<div class="layui-tree-main">'
  150. //箭头
  151. ,function(){
  152. if(options.showLine){
  153. if(hasChild){
  154. return '<span class="layui-tree-iconClick layui-tree-icon"><i class="layui-icon '+ (item.spread ? "layui-icon-subtraction" : "layui-icon-addition") +'"></i></span>';
  155. }else{
  156. return '<span class="layui-tree-iconClick"><i class="layui-icon layui-icon-file"></i></span>';
  157. };
  158. }else{
  159. return '<span class="layui-tree-iconClick"><i class="layui-tree-iconArrow '+ (hasChild ? "": HIDE) +'"></i></span>';
  160. };
  161. }()
  162. //复选框
  163. ,function(){
  164. return options.showCheckbox ? '<input type="checkbox" name="layuiTreeCheck" lay-skin="primary" '+ (item.disabled ? "disabled" : "") +' value="'+ item.id +'">' : '';
  165. }()
  166. //节点
  167. ,function(){
  168. if(options.isJump && item.href){
  169. return '<a href="'+ item.href +'" target="_blank" class="'+ ELEM_TEXT +'">'+ (item.title || item.label || options.text.defaultNodeName) +'</a>';
  170. }else{
  171. return '<span class="'+ ELEM_TEXT + (item.disabled ? ' '+ DISABLED : '') +'">'+ (item.title || item.label || options.text.defaultNodeName) +'</span>';
  172. }
  173. }()
  174. ,'</div>'
  175. //节点操作图标
  176. ,function(){
  177. if(!options.edit) return '';
  178. var editIcon = {
  179. add: '<i class="layui-icon layui-icon-add-1" data-type="add"></i>'
  180. ,update: '<i class="layui-icon layui-icon-edit" data-type="update"></i>'
  181. ,del: '<i class="layui-icon layui-icon-delete" data-type="del"></i>'
  182. }, arr = ['<div class="layui-btn-group layui-tree-btnGroup">'];
  183. if(options.edit === true){
  184. options.edit = ['update', 'del']
  185. }
  186. if(typeof options.edit === 'object'){
  187. layui.each(options.edit, function(i, val){
  188. arr.push(editIcon[val] || '')
  189. });
  190. return arr.join('') + '</div>';
  191. }
  192. }()
  193. ,'</div></div>'].join(''));
  194. //如果有子节点,则递归继续生成树
  195. if(hasChild){
  196. entryDiv.append(packDiv);
  197. that.tree(packDiv, item.children);
  198. };
  199. elem.append(entryDiv);
  200. //若有前置节点,前置节点加连接线
  201. if(entryDiv.prev('.'+ELEM_SET)[0]){
  202. entryDiv.prev().children('.layui-tree-pack').addClass('layui-tree-showLine');
  203. };
  204. //若无子节点,则父节点加延伸线
  205. if(!hasChild){
  206. entryDiv.parent('.layui-tree-pack').addClass('layui-tree-lineExtend');
  207. };
  208. //展开节点操作
  209. that.spread(entryDiv, item);
  210. //选择框
  211. if(options.showCheckbox){
  212. that.checkClick(entryDiv, item);
  213. }
  214. //操作节点
  215. options.edit && that.operate(entryDiv, item);
  216. });
  217. };
  218. //展开节点
  219. Class.prototype.spread = function(elem, item){
  220. var that = this
  221. ,options = that.config
  222. ,entry = elem.children('.'+ELEM_ENTRY)
  223. ,elemMain = entry.children('.'+ ELEM_MAIN)
  224. ,elemIcon = entry.find('.'+ ICON_CLICK)
  225. ,elemText = entry.find('.'+ ELEM_TEXT)
  226. ,touchOpen = options.onlyIconControl ? elemIcon : elemMain //判断展开通过节点还是箭头图标
  227. ,state = '';
  228. //展开收缩
  229. touchOpen.on('click', function(e){
  230. var packCont = elem.children('.'+ELEM_PACK)
  231. ,iconClick = touchOpen.children('.layui-icon')[0] ? touchOpen.children('.layui-icon') : touchOpen.find('.layui-tree-icon').children('.layui-icon');
  232. //若没有子节点
  233. if(!packCont[0]){
  234. state = 'normal';
  235. }else{
  236. if(elem.hasClass(ELEM_SPREAD)){
  237. elem.removeClass(ELEM_SPREAD);
  238. packCont.slideUp(200);
  239. iconClick.removeClass(ICON_SUB).addClass(ICON_ADD);
  240. }else{
  241. elem.addClass(ELEM_SPREAD);
  242. packCont.slideDown(200);
  243. iconClick.addClass(ICON_SUB).removeClass(ICON_ADD);
  244. //是否手风琴
  245. if(options.accordion){
  246. var sibls = elem.siblings('.'+ELEM_SET);
  247. sibls.removeClass(ELEM_SPREAD);
  248. sibls.children('.'+ELEM_PACK).slideUp(200);
  249. sibls.find('.layui-tree-icon').children('.layui-icon').removeClass(ICON_SUB).addClass(ICON_ADD);
  250. };
  251. };
  252. };
  253. });
  254. //点击回调
  255. elemText.on('click', function(){
  256. var othis = $(this);
  257. //判断是否禁用状态
  258. if(othis.hasClass(DISABLED)) return;
  259. //判断展开收缩状态
  260. if(elem.hasClass(ELEM_SPREAD)){
  261. state = options.onlyIconControl ? 'open' : 'close';
  262. } else {
  263. state = options.onlyIconControl ? 'close' : 'open';
  264. }
  265. //点击产生的回调
  266. options.click && options.click({
  267. elem: elem
  268. ,state: state
  269. ,data: item
  270. });
  271. });
  272. };
  273. //计算复选框选中状态
  274. Class.prototype.setCheckbox = function(elem, item, elemCheckbox){
  275. var that = this
  276. ,options = that.config
  277. ,checked = elemCheckbox.prop('checked');
  278. //同步子节点选中状态
  279. if(typeof item.children === 'object' || elem.find('.'+ELEM_PACK)[0]){
  280. var childs = elem.find('.'+ ELEM_PACK).find('input[name="layuiTreeCheck"]');
  281. childs.each(function(){
  282. if(this.disabled) return; //不可点击则跳过
  283. this.checked = checked;
  284. });
  285. };
  286. //同步父选中状态
  287. var setParentsChecked = function(thisNodeElem){
  288. //若无父节点,则终止递归
  289. if(!thisNodeElem.parents('.'+ ELEM_SET)[0]) return;
  290. var state
  291. ,parentPack = thisNodeElem.parent('.'+ ELEM_PACK)
  292. ,parentNodeElem = parentPack.parent()
  293. ,parentCheckbox = parentPack.prev().find('input[name="layuiTreeCheck"]');
  294. //如果子节点有任意一条选中,则父节点为选中状态
  295. if(checked){
  296. parentCheckbox.prop('checked', checked);
  297. } else { //如果当前节点取消选中,则根据计算“兄弟和子孙”节点选中状态,来同步父节点选中状态
  298. parentPack.find('input[name="layuiTreeCheck"]').each(function(){
  299. if(this.checked){
  300. state = true;
  301. }
  302. });
  303. //如果兄弟子孙节点全部未选中,则父节点也应为非选中状态
  304. state || parentCheckbox.prop('checked', false);
  305. }
  306. //向父节点递归
  307. setParentsChecked(parentNodeElem);
  308. };
  309. setParentsChecked(elem);
  310. that.renderForm('checkbox');
  311. };
  312. //复选框选择
  313. Class.prototype.checkClick = function(elem, item){
  314. var that = this
  315. ,options = that.config
  316. ,entry = elem.children('.'+ ELEM_ENTRY)
  317. ,elemMain = entry.children('.'+ ELEM_MAIN);
  318. elemMain.on('click', 'input[name="layuiTreeCheck"]+', function(e){
  319. layui.stope(e); //阻止点击节点事件
  320. var elemCheckbox = $(this).prev()
  321. ,checked = elemCheckbox.prop('checked');
  322. if(elemCheckbox.prop('disabled')) return;
  323. that.setCheckbox(elem, item, elemCheckbox);
  324. //复选框点击产生的回调
  325. options.oncheck && options.oncheck({
  326. elem: elem
  327. ,checked: checked
  328. ,data: item
  329. });
  330. });
  331. };
  332. //节点操作
  333. Class.prototype.operate = function(elem, item){
  334. var that = this
  335. ,options = that.config
  336. ,entry = elem.children('.'+ ELEM_ENTRY)
  337. ,elemMain = entry.children('.'+ ELEM_MAIN);
  338. entry.children('.layui-tree-btnGroup').on('click', '.layui-icon', function(e){
  339. layui.stope(e); //阻止节点操作
  340. var type = $(this).data("type")
  341. ,packCont = elem.children('.'+ELEM_PACK)
  342. ,returnObj = {
  343. data: item
  344. ,type: type
  345. ,elem:elem
  346. };
  347. //增加
  348. if(type == 'add'){
  349. //若节点本身无子节点
  350. if(!packCont[0]){
  351. //若开启连接线,更改图标样式
  352. if(options.showLine){
  353. elemMain.find('.'+ICON_CLICK).addClass('layui-tree-icon');
  354. elemMain.find('.'+ICON_CLICK).children('.layui-icon').addClass(ICON_ADD).removeClass('layui-icon-file');
  355. //若未开启连接线,显示箭头
  356. }else{
  357. elemMain.find('.layui-tree-iconArrow').removeClass(HIDE);
  358. };
  359. //节点添加子节点容器
  360. elem.append('<div class="layui-tree-pack"></div>');
  361. };
  362. //新增节点
  363. var key = options.operate && options.operate(returnObj)
  364. ,obj = {};
  365. obj.title = options.text.defaultNodeName;
  366. obj.id = key;
  367. that.tree(elem.children('.'+ELEM_PACK), [obj]);
  368. //放在新增后面,因为要对元素进行操作
  369. if(options.showLine){
  370. //节点本身无子节点
  371. if(!packCont[0]){
  372. //遍历兄弟节点,判断兄弟节点是否有子节点
  373. var siblings = elem.siblings('.'+ELEM_SET), num = 1
  374. ,parentPack = elem.parent('.'+ELEM_PACK);
  375. layui.each(siblings, function(index, i){
  376. if(!$(i).children('.'+ELEM_PACK)[0]){
  377. num = 0;
  378. };
  379. });
  380. //若兄弟节点都有子节点
  381. if(num == 1){
  382. //兄弟节点添加连接线
  383. siblings.children('.'+ELEM_PACK).addClass(ELEM_SHOW);
  384. siblings.children('.'+ELEM_PACK).children('.'+ELEM_SET).removeClass(ELEM_LINE_SHORT);
  385. elem.children('.'+ELEM_PACK).addClass(ELEM_SHOW);
  386. //父级移除延伸线
  387. parentPack.removeClass(ELEM_EXTEND);
  388. //同层节点最后一个更改线的状态
  389. parentPack.children('.'+ELEM_SET).last().children('.'+ELEM_PACK).children('.'+ELEM_SET).last().addClass(ELEM_LINE_SHORT);
  390. }else{
  391. elem.children('.'+ELEM_PACK).children('.'+ELEM_SET).addClass(ELEM_LINE_SHORT);
  392. };
  393. }else{
  394. //添加延伸线
  395. if(!packCont.hasClass(ELEM_EXTEND)){
  396. packCont.addClass(ELEM_EXTEND);
  397. };
  398. //子节点添加延伸线
  399. elem.find('.'+ELEM_PACK).each(function(){
  400. $(this).children('.'+ELEM_SET).last().addClass(ELEM_LINE_SHORT);
  401. });
  402. //如果前一个节点有延伸线
  403. if(packCont.children('.'+ELEM_SET).last().prev().hasClass(ELEM_LINE_SHORT)){
  404. packCont.children('.'+ELEM_SET).last().prev().removeClass(ELEM_LINE_SHORT);
  405. }else{
  406. //若之前的没有,说明处于连接状态
  407. packCont.children('.'+ELEM_SET).last().removeClass(ELEM_LINE_SHORT);
  408. };
  409. //若是最外层,要始终保持相连的状态
  410. if(!elem.parent('.'+ELEM_PACK)[0] && elem.next()[0]){
  411. packCont.children('.'+ELEM_SET).last().removeClass(ELEM_LINE_SHORT);
  412. };
  413. };
  414. };
  415. if(!options.showCheckbox) return;
  416. //若开启复选框,同步新增节点状态
  417. if(elemMain.find('input[name="layuiTreeCheck"]')[0].checked){
  418. var packLast = elem.children('.'+ELEM_PACK).children('.'+ELEM_SET).last();
  419. packLast.find('input[name="layuiTreeCheck"]')[0].checked = true;
  420. };
  421. that.renderForm('checkbox');
  422. //修改
  423. }else if(type == 'update'){
  424. var text = elemMain.children('.'+ ELEM_TEXT).html();
  425. elemMain.children('.'+ ELEM_TEXT).html('');
  426. //添加输入框,覆盖在文字上方
  427. elemMain.append('<input type="text" class="layui-tree-editInput">');
  428. //获取焦点
  429. elemMain.children('.layui-tree-editInput').val(text).focus();
  430. //嵌入文字移除输入框
  431. var getVal = function(input){
  432. var textNew = input.val().trim();
  433. textNew = textNew ? textNew : options.text.defaultNodeName;
  434. input.remove();
  435. elemMain.children('.'+ ELEM_TEXT).html(textNew);
  436. //同步数据
  437. returnObj.data.title = textNew;
  438. //节点修改的回调
  439. options.operate && options.operate(returnObj);
  440. };
  441. //失去焦点
  442. elemMain.children('.layui-tree-editInput').blur(function(){
  443. getVal($(this));
  444. });
  445. //回车
  446. elemMain.children('.layui-tree-editInput').on('keydown', function(e){
  447. if(e.keyCode === 13){
  448. e.preventDefault();
  449. getVal($(this));
  450. };
  451. });
  452. //删除
  453. }else{
  454. options.operate && options.operate(returnObj); //节点删除的回调
  455. returnObj.status = 'remove'; //标注节点删除
  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[name="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[name="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. Class.prototype.drag = function(){
  564. var that = this
  565. ,options = that.config;
  566. that.elem.on('dragstart', '.'+ELEM_ENTRY, function(){
  567. var parent = $(this).parent('.'+ELEM_SET)
  568. ,pares = parent.parents('.'+ELEM_SET)[0] ? parent.parents('.'+ELEM_SET).eq(0) : '未找到父节点';
  569. //开始拖拽触发的回调
  570. options.dragstart && options.dragstart(parent, pares);
  571. });
  572. that.elem.on('dragend', '.'+ELEM_ENTRY, function(e){
  573. var e = e || event
  574. ,disY = e.clientY
  575. ,olds = $(this)
  576. ,setParent = olds.parent('.'+ELEM_SET)
  577. ,setHeight = setParent.height()
  578. ,setTop = setParent.offset().top
  579. ,elemSet = that.elem.find('.'+ELEM_SET)
  580. ,elemHeight = that.elem.height()
  581. ,elemTop = that.elem.offset().top
  582. ,maxTop = elemHeight + elemTop - 13;
  583. //原父节点
  584. var isTree = setParent.parents('.'+ELEM_SET)[0]
  585. ,nextOld = setParent.next()[0];
  586. if(isTree){
  587. var parentPack = setParent.parent('.'+ELEM_PACK)
  588. ,parentSet = setParent.parents('.'+ELEM_SET).eq(0)
  589. ,warpPack = parentSet.parent('.'+ELEM_PACK)
  590. ,parentTop = parentSet.offset().top
  591. ,siblingOld = setParent.siblings()
  592. ,num = parentSet.children('.'+ELEM_PACK).children('.'+ELEM_SET).length;
  593. };
  594. //原节点操作
  595. var setDel = function(parentSet){
  596. //若为最后一个节点操作
  597. if(!isTree && !nextOld){
  598. that.elem.children('.'+ELEM_SET).last().children('.'+ELEM_PACK).children('.'+ELEM_SET).last().addClass(ELEM_LINE_SHORT);
  599. };
  600. //若为最外层节点,不做下列操作
  601. if(!isTree){
  602. setParent.removeClass('layui-tree-setHide');
  603. return;
  604. };
  605. //若为唯一子节点
  606. if(num == 1){
  607. if(options.showLine){
  608. parentSet.find('.'+ICON_CLICK).removeClass('layui-tree-icon');
  609. parentSet.find('.'+ICON_CLICK).children('.layui-icon').removeClass(ICON_SUB).addClass('layui-icon-file');
  610. warpPack.addClass(ELEM_EXTEND);
  611. warpPack.children('.'+ELEM_SET).children('.'+ELEM_PACK).each(function(){
  612. $(this).children('.'+ELEM_SET).last().addClass(ELEM_LINE_SHORT);
  613. });
  614. }else{
  615. parentSet.find('.layui-tree-iconArrow').addClass(HIDE);
  616. };
  617. parentSet.children('.'+ELEM_PACK).remove();
  618. parentSet.removeClass(ELEM_SPREAD);
  619. }else{
  620. //若开启连接线
  621. if(options.showLine){
  622. //遍历兄弟节点,判断兄弟节点是否有子节点
  623. var number = 1;
  624. layui.each(siblingOld, function(index, i){
  625. if(!$(i).children('.'+ELEM_PACK)[0]){
  626. number = 0;
  627. };
  628. });
  629. //若兄弟节点都有子节点
  630. if(number == 1){
  631. //若节点本身无子节点
  632. if(!setParent.children('.'+ELEM_PACK)[0]){
  633. //父级去除延伸线,因为此时子节点里没有空节点
  634. parentPack.removeClass(ELEM_EXTEND);
  635. siblingOld.children('.'+ELEM_PACK).addClass(ELEM_SHOW);
  636. siblingOld.children('.'+ELEM_PACK).children('.'+ELEM_SET).removeClass(ELEM_LINE_SHORT);
  637. };
  638. //若为最后一个节点
  639. parentPack.children('.'+ELEM_SET).last().children('.'+ELEM_PACK).children('.'+ELEM_SET).last().addClass(ELEM_LINE_SHORT);
  640. //若为最外层最后一个节点,去除前一个结点的连接线
  641. if(!nextOld && !parentSet.parents('.'+ELEM_SET)[0] && !parentSet.next()[0]){
  642. parentPack.children('.'+ELEM_SET).last().addClass(ELEM_LINE_SHORT);
  643. };
  644. }else{
  645. //若为最后一个节点且有延伸线
  646. if(!nextOld && setParent.hasClass(ELEM_LINE_SHORT)){
  647. parentPack.children('.'+ELEM_SET).last().addClass(ELEM_LINE_SHORT);
  648. };
  649. };
  650. };
  651. //若开启复选框
  652. if(options.showCheckbox){
  653. //若开启复选框,进行下步操作
  654. var elemRemove = function(elem){
  655. //若无父结点,则不执行
  656. if(elem){
  657. if(!elem.parents('.'+ELEM_SET)[0]) return;
  658. }else{
  659. if(!parentSet[0]) return;
  660. };
  661. var siblingTree = elem ? elem.siblings().children('.'+ELEM_ENTRY) : siblingOld.children('.'+ELEM_ENTRY)
  662. ,parentTree = elem ? elem.parent('.'+ELEM_PACK).prev() : parentPack.prev()
  663. ,checkState = parentTree.find('input[name="layuiTreeCheck"]')[0]
  664. ,state = 1, ndig = 0;
  665. //若父节点未勾选
  666. if(checkState.checked == false){
  667. //遍历兄弟节点
  668. siblingTree.each(function(i, item1){
  669. var input = $(item1).find('input[name="layuiTreeCheck"]')[0];
  670. if(input.checked == false && !input.disabled){ state = 0 };
  671. //判断是否全为不可勾选框
  672. if(!input.disabled){ ndig = 1 };
  673. });
  674. //若有可勾选选择框并且已勾选
  675. if(state == 1 && ndig == 1){
  676. //勾选父节点
  677. checkState.checked = true;
  678. that.renderForm('checkbox');
  679. //向上遍历祖先节点
  680. elemRemove(parentTree.parent('.'+ELEM_SET) || parentSet);
  681. };
  682. };
  683. };
  684. elemRemove();
  685. };
  686. };
  687. };
  688. //查找
  689. elemSet.each(function(){
  690. //筛选可插入位置
  691. if($(this).height() != 0){
  692. //若在本身位置
  693. if((disY > setTop && disY < setTop + setHeight)){
  694. options.dragend && options.dragend('drag error');
  695. return;
  696. };
  697. //若仅有一个子元素
  698. if(num == 1 && disY > parentTop && disY < setTop + setHeight){
  699. options.dragend && options.dragend('drag error');
  700. return;
  701. };
  702. var thisTop = $(this).offset().top;
  703. //若位于元素上
  704. if((disY > thisTop) && (disY < thisTop + 15)){
  705. //若元素无子节点
  706. if(!$(this).children('.'+ELEM_PACK)[0]){
  707. if(options.showLine){
  708. $(this).find('.'+ICON_CLICK).eq(0).addClass('layui-tree-icon');
  709. $(this).find('.'+ICON_CLICK).eq(0).children('.layui-icon').addClass(ICON_ADD).removeClass('layui-icon-file');
  710. }else{
  711. $(this).find(".layui-tree-iconArrow").removeClass(HIDE);
  712. };
  713. $(this).append('<div class="layui-tree-pack"></div>');
  714. };
  715. //插入元素
  716. $(this).children('.'+ELEM_PACK).append(setParent);
  717. setDel(parentSet);
  718. //若开启连接线,更改线状态
  719. if(options.showLine){
  720. var children = $(this).children('.'+ELEM_PACK).children('.'+ELEM_SET);
  721. setParent.children('.'+ELEM_PACK).children('.'+ELEM_SET).last().addClass(ELEM_LINE_SHORT);
  722. if(children.length == 1){
  723. //遍历兄弟节点,判断兄弟节点是否有子节点
  724. var siblings = $(this).siblings('.'+ELEM_SET), ss = 1
  725. ,parentPack = $(this).parent('.'+ELEM_PACK);
  726. layui.each(siblings, function(index, i){
  727. if(!$(i).children('.'+ELEM_PACK)[0]){
  728. ss = 0;
  729. };
  730. });
  731. //若兄弟节点都有子节点
  732. if(ss == 1){
  733. //兄弟节点添加连接线
  734. siblings.children('.'+ELEM_PACK).addClass(ELEM_SHOW);
  735. siblings.children('.'+ELEM_PACK).children('.'+ELEM_SET).removeClass(ELEM_LINE_SHORT);
  736. $(this).children('.'+ELEM_PACK).addClass(ELEM_SHOW);
  737. //父级移除延伸线
  738. parentPack.removeClass(ELEM_EXTEND);
  739. //同层节点最后一个去除连接线
  740. parentPack.children('.'+ELEM_SET).last().children('.'+ELEM_PACK).children('.'+ELEM_SET).last().addClass(ELEM_LINE_SHORT).removeClass('layui-tree-setHide');
  741. }else{
  742. $(this).children('.'+ELEM_PACK).children('.'+ELEM_SET).addClass(ELEM_LINE_SHORT).removeClass('layui-tree-setHide');
  743. };
  744. }else{
  745. //若原子节点含有延伸线
  746. if(setParent.prev('.'+ELEM_SET).hasClass(ELEM_LINE_SHORT)){
  747. setParent.prev('.'+ELEM_SET).removeClass(ELEM_LINE_SHORT);
  748. setParent.addClass(ELEM_LINE_SHORT);
  749. }else{
  750. //清除之前状态
  751. setParent.removeClass('layui-tree-setLineShort layui-tree-setHide');
  752. //若添加节点无子节点
  753. if(!setParent.children('.'+ELEM_PACK)[0]){
  754. //兄弟节点子节点添加延伸线
  755. setParent.siblings('.'+ELEM_SET).find('.'+ELEM_PACK).each(function(){
  756. $(this).children('.'+ELEM_SET).last().addClass(ELEM_LINE_SHORT);
  757. });
  758. }else{
  759. setParent.prev('.'+ELEM_SET).children('.'+ELEM_PACK).children('.'+ELEM_SET).last().removeClass(ELEM_LINE_SHORT);
  760. };
  761. };
  762. //若无下兄弟节点
  763. if(!$(this).next()[0]){
  764. setParent.addClass(ELEM_LINE_SHORT);
  765. };
  766. };
  767. };
  768. //若开启复选框,同步新增节点状态
  769. if(options.showCheckbox){
  770. if($(this).children('.'+ELEM_ENTRY).find('input[name="layuiTreeCheck"]')[0].checked){
  771. var packLast = setParent.children('.'+ELEM_ENTRY);
  772. packLast.find('input[name="layuiTreeCheck"]+').click();
  773. };
  774. };
  775. options.dragend && options.dragend('drag success', setParent, $(this));
  776. return false;
  777. //若位于元素上方
  778. }else if(disY < thisTop){
  779. $(this).before(setParent);
  780. setDel(parentSet);
  781. //若开启连接线,更改线状态
  782. if(options.showLine){
  783. var packCont = setParent.children('.'+ELEM_PACK)
  784. ,setFirst = $(this).parents('.'+ELEM_SET).eq(0)
  785. ,setPackLast = setFirst.children('.'+ELEM_PACK).children('.'+ELEM_SET).last();
  786. if(packCont[0]){
  787. setParent.removeClass(ELEM_LINE_SHORT);
  788. packCont.children('.'+ELEM_SET).last().removeClass(ELEM_LINE_SHORT);
  789. //遍历兄弟节点,判断兄弟节点是否有子节点
  790. var siblings = setParent.siblings('.'+ELEM_SET), ss = 1;
  791. layui.each(siblings, function(index, i){
  792. if(!$(i).children('.'+ELEM_PACK)[0]){
  793. ss = 0;
  794. };
  795. });
  796. //若兄弟节点都有子节点
  797. if(ss == 1){
  798. if(setFirst[0]){
  799. //兄弟节点添加连接线
  800. siblings.children('.'+ELEM_PACK).addClass(ELEM_SHOW);
  801. siblings.children('.'+ELEM_PACK).children('.'+ELEM_SET).removeClass(ELEM_LINE_SHORT);
  802. //同层节点最后一个添加延伸线
  803. setPackLast.children('.'+ELEM_PACK).children('.'+ELEM_SET).last().addClass(ELEM_LINE_SHORT).removeClass(ELEM_SHOW);
  804. };
  805. }else{
  806. setParent.children('.'+ELEM_PACK).children('.'+ELEM_SET).last().addClass(ELEM_LINE_SHORT);
  807. };
  808. //若是最外层,要始终保持相连的状态
  809. if(!setFirst.parent('.'+ELEM_PACK)[0] && setFirst.next()[0]){
  810. setPackLast.removeClass(ELEM_LINE_SHORT);
  811. };
  812. }else{
  813. if(!setFirst.hasClass(ELEM_EXTEND)){
  814. setFirst.addClass(ELEM_EXTEND);
  815. };
  816. //子节点添加延伸线
  817. setFirst.find('.'+ELEM_PACK).each(function(){
  818. $(this).children('.'+ELEM_SET).last().addClass(ELEM_LINE_SHORT);
  819. });
  820. //若是最外层,要始终保持相连的状态
  821. // if(!setFirst.parent('.'+ELEM_PACK)[0] && setFirst.next()[0]){
  822. // //setFirst.children('.'+ELEM_PACK).children('.'+ELEM_SET).last().removeClass(ELEM_LINE_SHORT);
  823. // };
  824. };
  825. //若移到最外层
  826. if(!setFirst[0]){
  827. //隐藏前置连接线
  828. setParent.addClass('layui-tree-setHide');
  829. setParent.children('.'+ELEM_PACK).children('.'+ELEM_SET).last().removeClass(ELEM_LINE_SHORT);
  830. };
  831. };
  832. //开启复选框且有父节点,同步新增节点状态
  833. if(setFirst[0] && options.showCheckbox){
  834. if(setFirst.children('.'+ELEM_ENTRY).find('input[name="layuiTreeCheck"]')[0].checked){
  835. var packLast = setParent.children('.'+ELEM_ENTRY);
  836. packLast.find('input[name="layuiTreeCheck"]+').click();
  837. };
  838. };
  839. options.dragend && options.dragend('拖拽成功,插入目标节点上方', setParent, $(this));
  840. return false;
  841. //若位于最下方
  842. }else if(disY > maxTop){
  843. that.elem.children('.'+ELEM_SET).last().children('.'+ELEM_PACK).addClass(ELEM_SHOW);
  844. that.elem.append(setParent);
  845. setDel(parentSet);
  846. //最外层保持连接
  847. setParent.prev().children('.'+ELEM_PACK).children('.'+ELEM_SET).last().removeClass(ELEM_LINE_SHORT);
  848. //隐藏前置连接线
  849. setParent.addClass('layui-tree-setHide');
  850. //最后一个子节点加延伸
  851. setParent.children('.'+ELEM_PACK).children('.'+ELEM_SET).last().addClass(ELEM_LINE_SHORT);
  852. options.dragend && options.dragend('拖拽成功,插入最外层节点', setParent, that.elem);
  853. return false;
  854. };
  855. };
  856. });
  857. });
  858. };
  859. //部分事件
  860. Class.prototype.events = function(){
  861. var that = this
  862. ,options = that.config
  863. ,checkWarp = that.elem.find('.layui-tree-checkedFirst');
  864. //初始选中
  865. layui.each(checkWarp, function(i, item){
  866. $(item).children('.'+ELEM_ENTRY).find('input[name="layuiTreeCheck"]+').trigger('click');
  867. });
  868. //搜索
  869. that.elem.find('.layui-tree-search').on('keyup', function(){
  870. var input = $(this)
  871. ,val = input.val()
  872. ,pack = input.nextAll()
  873. ,arr = [];
  874. //遍历所有的值
  875. pack.find('.'+ ELEM_TEXT).each(function(){
  876. var entry = $(this).parents('.'+ELEM_ENTRY);
  877. //若值匹配,加一个类以作标识
  878. if($(this).html().indexOf(val) != -1){
  879. arr.push($(this).parent());
  880. var select = function(div){
  881. div.addClass('layui-tree-searchShow');
  882. //向上父节点渲染
  883. if(div.parent('.'+ELEM_PACK)[0]){
  884. select(div.parent('.'+ELEM_PACK).parent('.'+ELEM_SET));
  885. };
  886. };
  887. select(entry.parent('.'+ELEM_SET));
  888. };
  889. });
  890. //根据标志剔除
  891. pack.find('.'+ELEM_ENTRY).each(function(){
  892. var parent = $(this).parent('.'+ELEM_SET);
  893. if(!parent.hasClass('layui-tree-searchShow')){
  894. parent.addClass(HIDE);
  895. };
  896. });
  897. if(pack.find('.layui-tree-searchShow').length == 0){
  898. that.elem.append(that.elemNone);
  899. };
  900. //节点过滤的回调
  901. options.onsearch && options.onsearch({
  902. elem: arr
  903. });
  904. });
  905. //还原搜索初始状态
  906. that.elem.find('.layui-tree-search').on('keydown', function(){
  907. $(this).nextAll().find('.'+ELEM_ENTRY).each(function(){
  908. var parent = $(this).parent('.'+ELEM_SET);
  909. parent.removeClass('layui-tree-searchShow '+ HIDE);
  910. });
  911. if($('.layui-tree-emptyText')[0]) $('.layui-tree-emptyText').remove();
  912. });
  913. };
  914. //得到选中节点
  915. Class.prototype.getChecked = function(){
  916. var that = this
  917. ,options = that.config
  918. ,checkId = []
  919. ,checkData = [];
  920. //遍历节点找到选中索引
  921. that.elem.find('.layui-form-checked').each(function(){
  922. checkId.push($(this).prev()[0].value);
  923. });
  924. //遍历节点
  925. var eachNodes = function(data, checkNode){
  926. layui.each(data, function(index, item){
  927. layui.each(checkId, function(index2, item2){
  928. if(item.id == item2){
  929. var cloneItem = $.extend({}, item);
  930. delete cloneItem.children;
  931. checkNode.push(cloneItem);
  932. if(item.children){
  933. cloneItem.children = [];
  934. eachNodes(item.children, cloneItem.children);
  935. }
  936. return true
  937. }
  938. });
  939. });
  940. };
  941. eachNodes($.extend({}, options.data), checkData);
  942. return checkData;
  943. };
  944. //设置选中节点
  945. Class.prototype.setChecked = function(checkedId){
  946. var that = this
  947. ,options = that.config;
  948. //初始选中
  949. that.elem.find('.'+ELEM_SET).each(function(i, item){
  950. var thisId = $(this).data('id')
  951. ,input = $(item).children('.'+ELEM_ENTRY).find('input[name="layuiTreeCheck"]')
  952. ,reInput = input.next();
  953. //若返回数字
  954. if(typeof checkedId === 'number'){
  955. if(thisId == checkedId){
  956. if(!input[0].checked){
  957. reInput.click();
  958. };
  959. return false;
  960. };
  961. }else{
  962. //若返回数组
  963. if($.inArray(thisId, checkedId) != -1){
  964. if(!input[0].checked){
  965. reInput.click();
  966. };
  967. };
  968. };
  969. });
  970. };
  971. //记录所有实例
  972. thisModule.that = {}; //记录所有实例对象
  973. thisModule.config = {}; //记录所有实例配置项
  974. //重载实例
  975. tree.reload = function(id, options){
  976. var that = thisModule.that[id];
  977. that.reload(options);
  978. return thisModule.call(that);
  979. };
  980. //获得选中的节点数据
  981. tree.getChecked = function(id){
  982. var that = thisModule.that[id];
  983. return that.getChecked();
  984. };
  985. //设置选中节点
  986. tree.setChecked = function(id, checkedId){
  987. var that = thisModule.that[id];
  988. return that.setChecked(checkedId);
  989. };
  990. //核心入口
  991. tree.render = function(options){
  992. var inst = new Class(options);
  993. return thisModule.call(inst);
  994. };
  995. exports(MOD_NAME, tree);
  996. })