import layui from '../core.js'; import $ from 'jquery'; import { form } from './form.js'; import { util } from './util.js'; /** * tree 树组件 */ // 模块名 var MOD_NAME = 'tree'; // 外部接口 var tree = { config: { customName: { // 自定义 data 字段名 id: 'id', title: 'title', children: 'children' } }, index: layui[MOD_NAME] ? (layui[MOD_NAME].index + 10000) : 0, // 设置全局项 set: function(options){ var that = this; that.config = $.extend({}, that.config, options); return that; }, // 事件 on: function(events, callback){ return layui.onevent.call(this, MOD_NAME, events, callback); } }; // 操作当前实例 var thisModule = function(){ var that = this; var options = that.config; var id = options.id || that.index; thisModule.that[id] = that; // 记录当前实例对象 thisModule.config[id] = options; // 记录当前实例配置项 return { config: options, // 重置实例 reload: function(options){ that.reload.call(that, options); }, getChecked: function(){ return that.getChecked.call(that); }, setChecked: function(id){// 设置值 return that.setChecked.call(that, id); } } }; var HIDE = 'layui-hide'; var DISABLED = 'layui-disabled'; var ELEM_SET = 'layui-tree-set'; var ICON_CLICK = 'layui-tree-iconClick'; var ICON_ADD = 'layui-icon-addition'; var ICON_SUB = 'layui-icon-subtraction'; var ELEM_ENTRY = 'layui-tree-entry'; var ELEM_MAIN = 'layui-tree-main'; var ELEM_TEXT = 'layui-tree-txt'; var ELEM_PACK = 'layui-tree-pack'; var ELEM_SPREAD = 'layui-tree-spread'; var ELEM_LINE_SHORT = 'layui-tree-setLineShort'; var ELEM_SHOW = 'layui-tree-showLine'; var ELEM_EXTEND = 'layui-tree-lineExtend'; // 构造器 var Class = function(options){ var that = this; that.index = ++tree.index; that.config = $.extend({}, that.config, tree.config, options); that.render(); }; // 默认配置 Class.prototype.config = { data: [], // 数据 showCheckbox: false, // 是否显示复选框 showLine: true, // 是否开启连接线 accordion: false, // 是否开启手风琴模式 onlyIconControl: false, // 是否仅允许节点左侧图标控制展开收缩 isJump: false, // 是否允许点击节点时弹出新窗口跳转 edit: false, // 是否开启节点的操作图标 text: { defaultNodeName: '未命名', // 节点默认名称 none: '无数据' // 数据为空时的文本提示 } }; // 重载实例 Class.prototype.reload = function(options){ var that = this; layui.each(options, function(key, item){ if(layui.type(item) === 'array') delete that.config[key]; }); that.config = $.extend(true, {}, that.config, options); that.render(); }; // 主体渲染 Class.prototype.render = function(){ var that = this; var options = that.config; // 初始化自定义字段名 options.customName = $.extend({}, tree.config.customName, options.customName); that.checkids = []; var temp = $('
'); that.tree(temp); var othis = options.elem = $(options.elem); if(!othis[0]) return; // 索引 that.key = options.id || that.index; // 插入组件结构 that.elem = temp; that.elemNone = $('
'+ options.text.none +'
'); othis.html(that.elem); if(that.elem.find('.layui-tree-set').length == 0){ return that.elem.append(that.elemNone); } // 复选框渲染 if(options.showCheckbox){ that.renderForm('checkbox'); } that.elem.find('.layui-tree-set').each(function(){ var othis = $(this); // 最外层 if(!othis.parent('.layui-tree-pack')[0]){ othis.addClass('layui-tree-setHide'); } // 没有下一个节点 上一层父级有延伸线 if(!othis.next()[0] && othis.parents('.layui-tree-pack').eq(1).hasClass('layui-tree-lineExtend')){ othis.addClass(ELEM_LINE_SHORT); } // 没有下一个节点 外层最后一个 if(!othis.next()[0] && !othis.parents('.layui-tree-set').eq(0).next()[0]){ othis.addClass(ELEM_LINE_SHORT); } }); that.events(); }; // 渲染表单 Class.prototype.renderForm = function(type){ form.render(type, 'LAY-tree-'+ this.index); }; // 节点解析 Class.prototype.tree = function(elem, children){ var that = this; var options = that.config; var customName = options.customName; var data = children || options.data; // 遍历数据 layui.each(data, function(index, item){ var hasChild = item[customName.children] && item[customName.children].length > 0; var packDiv = $('
'); var entryDiv = $(['
' ,'
' ,'
' // 箭头 ,function(){ if(options.showLine){ if(hasChild){ return ''; }else { return ''; } }else { return ''; } }() // 复选框 ,function(){ return options.showCheckbox ? '' : ''; }() // 节点 ,function(){ if(options.isJump && item.href){ return ''+ (item[customName.title] || item.label || options.text.defaultNodeName) +''; }else { return ''+ (item[customName.title] || item.label || options.text.defaultNodeName) +''; } }() ,'
' // 节点操作图标 ,function(){ if(!options.edit) return ''; var editIcon = { add: '' ,update: '' ,del: '' }, arr = ['
']; if(options.edit === true){ options.edit = ['update', 'del']; } if(typeof options.edit === 'object'){ layui.each(options.edit, function(i, val){ arr.push(editIcon[val] || ''); }); return arr.join('') + '
'; } }() ,'
'].join('')); // 如果有子节点,则递归继续生成树 if(hasChild){ entryDiv.append(packDiv); that.tree(packDiv, item[customName.children]); } elem.append(entryDiv); // 若有前置节点,前置节点加连接线 if(entryDiv.prev('.'+ELEM_SET)[0]){ entryDiv.prev().children('.layui-tree-pack').addClass('layui-tree-showLine'); } // 若无子节点,则父节点加延伸线 if(!hasChild){ entryDiv.parent('.layui-tree-pack').addClass('layui-tree-lineExtend'); } // 展开节点操作 that.spread(entryDiv, item); // 选择框 if(options.showCheckbox){ item.checked && that.checkids.push(item[customName.id]); that.checkClick(entryDiv, item); } // 操作节点 options.edit && that.operate(entryDiv, item); }); }; // 展开节点 Class.prototype.spread = function(elem, item){ var that = this; var options = that.config; var entry = elem.children('.'+ELEM_ENTRY); var elemMain = entry.children('.'+ ELEM_MAIN); var elemCheckbox = elemMain.find('input[same="layuiTreeCheck"]'); var elemIcon = entry.find('.'+ ICON_CLICK); var elemText = entry.find('.'+ ELEM_TEXT); var touchOpen = options.onlyIconControl ? elemIcon : elemMain; // 判断展开通过节点还是箭头图标 var state = ''; // 展开收缩 touchOpen.on('click', function(e){ var packCont = elem.children('.'+ELEM_PACK) ,iconClick = touchOpen.children('.layui-icon')[0] ? touchOpen.children('.layui-icon') : touchOpen.find('.layui-tree-icon').children('.layui-icon'); // 若没有子节点 if(!packCont[0]){ state = 'normal'; }else { if(elem.hasClass(ELEM_SPREAD)){ elem.removeClass(ELEM_SPREAD); packCont.slideUp(200); iconClick.removeClass(ICON_SUB).addClass(ICON_ADD); that.updateFieldValue(item, 'spread', false); }else { elem.addClass(ELEM_SPREAD); packCont.slideDown(200); iconClick.addClass(ICON_SUB).removeClass(ICON_ADD); that.updateFieldValue(item, 'spread', true); // 是否手风琴 if(options.accordion){ var sibls = elem.siblings('.'+ELEM_SET); sibls.removeClass(ELEM_SPREAD); sibls.children('.'+ELEM_PACK).slideUp(200); sibls.find('.layui-tree-icon').children('.layui-icon').removeClass(ICON_SUB).addClass(ICON_ADD); } } } }); // 点击回调 elemText.on('click', function(){ var othis = $(this); // 判断是否禁用状态 if(othis.hasClass(DISABLED)) return; // 判断展开收缩状态 if(elem.hasClass(ELEM_SPREAD)){ state = options.onlyIconControl ? 'open' : 'close'; } else { state = options.onlyIconControl ? 'close' : 'open'; } // 获取选中状态 if(elemCheckbox[0]){ that.updateFieldValue(item, 'checked', elemCheckbox.prop('checked')); } // 点击产生的回调 options.click && options.click({ elem: elem, state: state, data: item }); }); }; // 更新数据源 checked,spread 字段值 Class.prototype.updateFieldValue = function(obj, field, value){ if(field in obj) obj[field] = value; }; // 计算复选框选中状态 Class.prototype.setCheckbox = function(elem, item, elemCheckbox){ var that = this; var options = that.config; var customName = options.customName; var checked = elemCheckbox.prop('checked'); if(elemCheckbox.prop('disabled')) return; // 同步子节点选中状态 if(typeof item[customName.children] === 'object' || elem.find('.'+ELEM_PACK)[0]){ var elemCheckboxs = elem.find('.'+ ELEM_PACK).find('input[same="layuiTreeCheck"]'); elemCheckboxs.each(function(index){ if(this.disabled) return; // 不可点击则跳过 var children = item[customName.children][index]; if(children) that.updateFieldValue(children, 'checked', checked); that.updateFieldValue(this, 'checked', checked); }); } // 同步父节点选中状态 var setParentsChecked = function(thisNodeElem){ // 若无父节点,则终止递归 if(!thisNodeElem.parents('.'+ ELEM_SET)[0]) return; var state; var parentPack = thisNodeElem.parent('.'+ ELEM_PACK); var parentNodeElem = parentPack.parent(); var parentCheckbox = parentPack.prev().find('input[same="layuiTreeCheck"]'); // 如果子节点有任意一条选中,则父节点为选中状态 if(checked){ parentCheckbox.prop('checked', checked); } else { // 如果当前节点取消选中,则根据计算“兄弟和子孙”节点选中状态,来同步父节点选中状态 parentPack.find('input[same="layuiTreeCheck"]').each(function(){ if(this.checked){ state = true; } }); // 如果兄弟子孙节点全部未选中,则父节点也应为非选中状态 state || parentCheckbox.prop('checked', false); } // 向父节点递归 setParentsChecked(parentNodeElem); }; setParentsChecked(elem); that.renderForm('checkbox'); }; // 复选框选择 Class.prototype.checkClick = function(elem, item){ var that = this; var options = that.config; var entry = elem.children('.'+ ELEM_ENTRY); var elemMain = entry.children('.'+ ELEM_MAIN); // 点击复选框 elemMain.on('click', 'input[same="layuiTreeCheck"]+', function(e){ layui.stope(e); // 阻止点击节点事件 var elemCheckbox = $(this).prev(); var checked = elemCheckbox.prop('checked'); if(elemCheckbox.prop('disabled')) return; that.setCheckbox(elem, item, elemCheckbox); that.updateFieldValue(item, 'checked', checked); // 复选框点击产生的回调 options.oncheck && options.oncheck({ elem: elem, checked: checked, data: item }); }); }; // 节点操作 Class.prototype.operate = function(elem, item){ var that = this; var options = that.config; var customName = options.customName; var entry = elem.children('.'+ ELEM_ENTRY); var elemMain = entry.children('.'+ ELEM_MAIN); entry.children('.layui-tree-btnGroup').on('click', '.layui-icon', function(e){ layui.stope(e); // 阻止节点操作 var type = $(this).data("type"); var packCont = elem.children('.'+ELEM_PACK); var returnObj = { data: item, type: type, elem:elem }; // 增加 if(type == 'add'){ // 若节点本身无子节点 if(!packCont[0]){ // 若开启连接线,更改图标样式 if(options.showLine){ elemMain.find('.'+ICON_CLICK).addClass('layui-tree-icon'); elemMain.find('.'+ICON_CLICK).children('.layui-icon').addClass(ICON_ADD).removeClass('layui-icon-file'); // 若未开启连接线,显示箭头 } else { elemMain.find('.layui-tree-iconArrow').removeClass(HIDE); } // 节点添加子节点容器 elem.append('
'); } // 新增节点 var key = options.operate && options.operate(returnObj); var obj = {}; obj[customName.title] = options.text.defaultNodeName; obj[customName.id] = key; that.tree(elem.children('.'+ELEM_PACK), [obj]); // 放在新增后面,因为要对元素进行操作 if(options.showLine){ // 节点本身无子节点 if(!packCont[0]){ // 遍历兄弟节点,判断兄弟节点是否有子节点 var siblings = elem.siblings('.'+ELEM_SET); var num = 1; var parentPack = elem.parent('.'+ELEM_PACK); layui.each(siblings, function(index, i){ if(!$(i).children('.'+ELEM_PACK)[0]){ num = 0; } }); // 若兄弟节点都有子节点 if(num == 1){ // 兄弟节点添加连接线 siblings.children('.'+ELEM_PACK).addClass(ELEM_SHOW); siblings.children('.'+ELEM_PACK).children('.'+ELEM_SET).removeClass(ELEM_LINE_SHORT); elem.children('.'+ELEM_PACK).addClass(ELEM_SHOW); // 父级移除延伸线 parentPack.removeClass(ELEM_EXTEND); // 同层节点最后一个更改线的状态 parentPack.children('.'+ELEM_SET).last().children('.'+ELEM_PACK).children('.'+ELEM_SET).last().addClass(ELEM_LINE_SHORT); } else { elem.children('.'+ELEM_PACK).children('.'+ELEM_SET).addClass(ELEM_LINE_SHORT); } } else { // 添加延伸线 if(!packCont.hasClass(ELEM_EXTEND)){ packCont.addClass(ELEM_EXTEND); } // 子节点添加延伸线 elem.find('.'+ELEM_PACK).each(function(){ $(this).children('.'+ELEM_SET).last().addClass(ELEM_LINE_SHORT); }); // 如果前一个节点有延伸线 if(packCont.children('.'+ELEM_SET).last().prev().hasClass(ELEM_LINE_SHORT)){ packCont.children('.'+ELEM_SET).last().prev().removeClass(ELEM_LINE_SHORT); }else { // 若之前的没有,说明处于连接状态 packCont.children('.'+ELEM_SET).last().removeClass(ELEM_LINE_SHORT); } // 若是最外层,要始终保持相连的状态 if(!elem.parent('.'+ELEM_PACK)[0] && elem.next()[0]){ packCont.children('.'+ELEM_SET).last().removeClass(ELEM_LINE_SHORT); } } } if(!options.showCheckbox) return; // 若开启复选框,同步新增节点状态 if(elemMain.find('input[same="layuiTreeCheck"]')[0].checked){ var packLast = elem.children('.'+ELEM_PACK).children('.'+ELEM_SET).last(); packLast.find('input[same="layuiTreeCheck"]')[0].checked = true; } that.renderForm('checkbox'); // 修改 } else if(type == 'update') { var text = elemMain.children('.'+ ELEM_TEXT).html(); elemMain.children('.'+ ELEM_TEXT).html(''); // 添加输入框,覆盖在文字上方 elemMain.append(''); // 获取焦点 elemMain.children('.layui-tree-editInput').val(util.unescape(text)).focus(); // 嵌入文字移除输入框 var getVal = function(input){ var textNew = util.escape(input.val().trim()); textNew = textNew ? textNew : options.text.defaultNodeName; input.remove(); elemMain.children('.'+ ELEM_TEXT).html(textNew); // 同步数据 returnObj.data[customName.title] = textNew; // 节点修改的回调 options.operate && options.operate(returnObj); }; // 失去焦点 elemMain.children('.layui-tree-editInput').blur(function(){ getVal($(this)); }); // 回车 elemMain.children('.layui-tree-editInput').on('keydown', function(e){ if(e.keyCode === 13){ e.preventDefault(); getVal($(this)); } }); // 删除 } else { layer.confirm('确认删除该节点 "'+ (item[customName.title] || '') +'" 吗?', function(index){ options.operate && options.operate(returnObj); // 节点删除的回调 returnObj.status = 'remove'; // 标注节点删除 layer.close(index); // 若删除最后一个,显示空数据提示 if(!elem.prev('.'+ELEM_SET)[0] && !elem.next('.'+ELEM_SET)[0] && !elem.parent('.'+ELEM_PACK)[0]){ elem.remove(); that.elem.append(that.elemNone); return; } // 若有兄弟节点 if(elem.siblings('.'+ELEM_SET).children('.'+ELEM_ENTRY)[0]){ // 若开启复选框 if(options.showCheckbox){ // 若开启复选框,进行下步操作 var elemDel = function(elem){ // 若无父结点,则不执行 if(!elem.parents('.'+ELEM_SET)[0]) return; var siblingTree = elem.siblings('.'+ELEM_SET).children('.'+ELEM_ENTRY); var parentTree = elem.parent('.'+ELEM_PACK).prev(); var checkState = parentTree.find('input[same="layuiTreeCheck"]')[0]; var state = 1; var num = 0; // 若父节点未勾选 if(checkState.checked == false){ // 遍历兄弟节点 siblingTree.each(function(i, item1){ var input = $(item1).find('input[same="layuiTreeCheck"]')[0]; if(input.checked == false && !input.disabled){ state = 0; } // 判断是否全为不可勾选框 if(!input.disabled){ num = 1; } }); // 若有可勾选选择框并且已勾选 if(state == 1 && num == 1){ // 勾选父节点 checkState.checked = true; that.renderForm('checkbox'); // 向上遍历祖先节点 elemDel(parentTree.parent('.'+ELEM_SET)); } } }; elemDel(elem); } // 若开启连接线 if(options.showLine){ // 遍历兄弟节点,判断兄弟节点是否有子节点 var siblings = elem.siblings('.'+ELEM_SET); var num = 1; var parentPack = elem.parent('.'+ELEM_PACK); layui.each(siblings, function(index, i){ if(!$(i).children('.'+ELEM_PACK)[0]){ num = 0; } }); // 若兄弟节点都有子节点 if(num == 1){ // 若节点本身无子节点 if(!packCont[0]){ // 父级去除延伸线,因为此时子节点里没有空节点 parentPack.removeClass(ELEM_EXTEND); siblings.children('.'+ELEM_PACK).addClass(ELEM_SHOW); siblings.children('.'+ELEM_PACK).children('.'+ELEM_SET).removeClass(ELEM_LINE_SHORT); } // 若为最后一个节点 if(!elem.next()[0]){ elem.prev().children('.'+ELEM_PACK).children('.'+ELEM_SET).last().addClass(ELEM_LINE_SHORT); }else { parentPack.children('.'+ELEM_SET).last().children('.'+ELEM_PACK).children('.'+ELEM_SET).last().addClass(ELEM_LINE_SHORT); } // 若为最外层最后一个节点,去除前一个结点的连接线 if(!elem.next()[0] && !elem.parents('.'+ELEM_SET)[1] && !elem.parents('.'+ELEM_SET).eq(0).next()[0]){ elem.prev('.'+ELEM_SET).addClass(ELEM_LINE_SHORT); } }else { // 若为最后一个节点且有延伸线 if(!elem.next()[0] && elem.hasClass(ELEM_LINE_SHORT)){ elem.prev().addClass(ELEM_LINE_SHORT); } } } } else { // 若无兄弟节点 var prevDiv = elem.parent('.'+ELEM_PACK).prev(); // 若开启了连接线 if(options.showLine){ prevDiv.find('.'+ICON_CLICK).removeClass('layui-tree-icon'); prevDiv.find('.'+ICON_CLICK).children('.layui-icon').removeClass(ICON_SUB).addClass('layui-icon-file'); // 父节点所在层添加延伸线 var pare = prevDiv.parents('.'+ELEM_PACK).eq(0); pare.addClass(ELEM_EXTEND); // 兄弟节点最后子节点添加延伸线 pare.children('.'+ELEM_SET).each(function(){ $(this).children('.'+ELEM_PACK).children('.'+ELEM_SET).last().addClass(ELEM_LINE_SHORT); }); }else { // 父节点隐藏箭头 prevDiv.find('.layui-tree-iconArrow').addClass(HIDE); } // 移除展开属性 elem.parents('.'+ELEM_SET).eq(0).removeClass(ELEM_SPREAD); // 移除节点容器 elem.parent('.'+ELEM_PACK).remove(); } elem.remove(); }); } }); }; // 部分事件 Class.prototype.events = function(){ var that = this; var options = that.config; that.elem.find('.layui-tree-checkedFirst'); // 初始选中 that.setChecked(that.checkids); // 搜索 that.elem.find('.layui-tree-search').on('keyup', function(){ var input = $(this); var val = input.val(); var pack = input.nextAll(); var arr = []; // 遍历所有的值 pack.find('.'+ ELEM_TEXT).each(function(){ var entry = $(this).parents('.'+ELEM_ENTRY); // 若值匹配,加一个类以作标识 if($(this).html().indexOf(val) != -1){ arr.push($(this).parent()); var select = function(div){ div.addClass('layui-tree-searchShow'); // 向上父节点渲染 if(div.parent('.'+ELEM_PACK)[0]){ select(div.parent('.'+ELEM_PACK).parent('.'+ELEM_SET)); } }; select(entry.parent('.'+ELEM_SET)); } }); // 根据标志剔除 pack.find('.'+ELEM_ENTRY).each(function(){ var parent = $(this).parent('.'+ELEM_SET); if(!parent.hasClass('layui-tree-searchShow')){ parent.addClass(HIDE); } }); if(pack.find('.layui-tree-searchShow').length == 0){ that.elem.append(that.elemNone); } // 节点过滤的回调 options.onsearch && options.onsearch({ elem: arr }); }); // 还原搜索初始状态 that.elem.find('.layui-tree-search').on('keydown', function(){ $(this).nextAll().find('.'+ELEM_ENTRY).each(function(){ var parent = $(this).parent('.'+ELEM_SET); parent.removeClass('layui-tree-searchShow '+ HIDE); }); if($('.layui-tree-emptyText')[0]) $('.layui-tree-emptyText').remove(); }); }; // 得到选中节点 Class.prototype.getChecked = function(){ var that = this; var options = that.config; var customName = options.customName; var checkId = []; var checkData = []; // 遍历节点找到选中索引 that.elem.find('.layui-form-checked').each(function(){ checkId.push($(this).prev()[0].value); }); // 遍历节点 var eachNodes = function(data, checkNode){ layui.each(data, function(index, item){ layui.each(checkId, function(index2, item2){ if(item[customName.id] == item2){ that.updateFieldValue(item, 'checked', true); var cloneItem = $.extend({}, item); delete cloneItem[customName.children]; checkNode.push(cloneItem); if(item[customName.children]){ cloneItem[customName.children] = []; eachNodes(item[customName.children], cloneItem[customName.children]); } return true } }); }); }; eachNodes($.extend({}, options.data), checkData); return checkData; }; // 设置选中节点 Class.prototype.setChecked = function(checkedId){ var that = this; that.config; // 初始选中 that.elem.find('.'+ELEM_SET).each(function(i, item){ var thisId = $(this).data('id'); var input = $(item).children('.'+ELEM_ENTRY).find('input[same="layuiTreeCheck"]'); var reInput = input.next(); // 若返回数字 if(typeof checkedId === 'number'){ if(thisId.toString() == checkedId.toString()){ if(!input[0].checked){ reInput.click(); } return false; } } // 若返回数组 else if(typeof checkedId === 'object'){ layui.each(checkedId, function(index, value){ if(value.toString() == thisId.toString() && !input[0].checked){ reInput.click(); return true; } }); } }); }; // 记录所有实例 thisModule.that = {}; // 记录所有实例对象 thisModule.config = {}; // 记录所有实例配置项 // 重载实例 tree.reload = function(id, options){ var that = thisModule.that[id]; that.reload(options); return thisModule.call(that); }; // 获得选中的节点数据 tree.getChecked = function(id){ var that = thisModule.that[id]; return that.getChecked(); }; // 设置选中节点 tree.setChecked = function(id, checkedId){ var that = thisModule.that[id]; return that.setChecked(checkedId); }; // 核心入口 tree.render = function(options){ var inst = new Class(options); return thisModule.call(inst); }; export { tree as default, tree }; //# sourceMappingURL=tree.js.map