🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
``` /** @Name:layui.atree2.0 树组件 @Author:smallwei @License:MIT */ layui.define('jquery', function (exports) { "use strict"; var $ = layui.$, hint = layui.hint(); //勾选集合 var changeList = []; //变量别名 var props = { name: 'name', id: 'id', children: 'children', checkbox: 'checkbox', spread: 'spread', deleteBtnLabelKey: 'delete', addBtnLabelKey: 'add', //修改 reviseBtnLabelKey: 'revise' }; var enterSkin = 'layui-atree-enter', Atree = function (options) { //缓冲重要变量 this.$vm = this; this.$options = options || {}; this.$el = this.utils.isElementNode(this.$options.elem) ? this.$options.elem : $(this.$options.elem); //模块的属性 this.nodes = this.$options.nodes || []; this.props = this.$options.props || props; //模块的别名 this.nameKey = this.props.name || props.name; this.idKey = this.props.id || props.id; this.childrenKey = this.props.children || props.children; this.checkboxKey = this.props.checkbox || props.checkbox; this.spreadKey = this.props.spread || props.spread; this.addBtnLabelKey = this.props.addBtnLabel || props.addBtnLabel; this.deleteBtnLabelKey = this.props.deleteBtnLabel || props.deleteBtnLabel; this.reviseBtnLabelKey = this.props.reviseBtnLabel || props.reviseBtnLabel; this.move = {}; }; //图标 var icon = { arrow: ['&#xe623;', '&#xe625;'] //箭头 , checkbox: ['&#xe68c;', '&#xe624;'] //复选框 , leaf: '&#xe621;' //叶节点 }; //工具包 Atree.prototype.utils = (function () { return { isElementNode: function (node) { return node.nodeType === 1; } } })() //初始化 Atree.prototype.init = function () { var that = this.$vm, options = this.$options that.$el.addClass('layui-box layui-atree'); //添加tree样式 if (options.skin) { that.$el.addClass('layui-atree-skin-' + options.skin); } that.tree(that.$el, that.nodes); that.on(that.$el); // 补充 第一级 var nodes = that.nodes,len = nodes.length; if(that.drag){ for(var i = 0; i < len; i++){ (function(i,item){ that.$el.children('li').eq(i).children('.layui-atree-node').on('mouseenter', function (){ var othis = $(this) , move = that.move; if (move.from) { move.to = { item: item, elem: othis.parent('li') }; othis.addClass(enterSkin); } }) })(i, nodes[i]); } } }; //树节点解析 Atree.prototype.tree = function (elem, children, parent) { var that = this.$vm, options = this.$options var nodes = children; layui.each(nodes, function (index, item) { var hasChild = item[that.childrenKey] && item[that.childrenKey].length > 0; var dom = that.getDom(item); if (parent) item.$parent = parent; var ul = $(dom.ul(item)); var li = $(that.getNode(item)); //如果被选中加入checkbox集合里 if (item[that.checkboxKey]) { changeList.push(item); } //如果有子节点,则递归继续生成树 if (hasChild) { li.append(ul); that.tree(ul, item[that.childrenKey], item); } //伸展节点 that.spread(li, item); that.bindUlEvent(li, item); elem.append(li); }); }; //节点dom拼接 Atree.prototype.getDom = function (item) { var that = this.$vm, options = this.$options, item = item, hasChild = item[that.childrenKey] && item[that.childrenKey].length > 0; return { spread: function () { return hasChild ? '<i class="layui-icon layui-atree-spread">' + ( item[that.spreadKey] || options.spreadAll ? icon.arrow[1] : icon.arrow[0] ) + '</i>' : ''; }, checkbox: function () { return options.check ? ( '<i class="layui-icon layui-atree-check' + (item[that.checkboxKey] ? ' is-checked' : '') + '">' + ( item[that.checkboxKey] ? icon.checkbox[1] : icon.checkbox[0] ) + '</i>' ) : ''; }, ul: function () { return '<ul class="' + (item[that.spreadKey] || options.spreadAll ? "layui-show" : "") + '"></ul>' }, node: function () { return '<a href="' + (item.href || 'javascript:;') + '" ' + ( options.target && item.href ? 'target=\"' + options.target + '\"' : '' ) + '>' + ('<cite>' + (item[that.nameKey] || '未命名') + '</cite></a>') }, menu: function () { return '<div class="layui-atree-menu">' + '<span class="layui-atree-add" id="atree-add">' + that.addBtnLabelKey + '</span>' + '<span class="layui-atree-delete">' + that.deleteBtnLabelKey + '</span>' + '<span class="layui-atree-revise">' + that.reviseBtnLabelKey + '</span>' + '</div>' } } } //获取树节点 Atree.prototype.getNode = function (item) { var that = this.$vm, options = this.$options var dom = that.getDom(item); var li = ['<li ' + (item[that.spreadKey] || options.spreadAll ? 'data-spread="' + (item[that.spreadKey] || true) + '"' : '') + (item[that.checkboxKey] ? 'data-check="' + item[that.checkboxKey] + '"' : '') + ('data-id=' + item[that.idKey]) + '><div class="layui-atree-node">' //展开箭头 , dom.spread() //复选框 , dom.checkbox() //节点 , dom.node() //菜单 , dom.menu(), '</div></li>' ].join(''); return li; } //父绑定事件 Atree.prototype.bindUlEvent = function (li, item) { var that = this.$vm, options = this.$options //触发点击节点回调 typeof options.click === 'function' && that.click(li, item); //节点选择 typeof options.change === 'function' && options.check === 'checkbox' && that.checkbox(li, item); //新增方法 typeof options.addClick === 'function' && that.add(li, item); //删除方法 typeof options.deleteClick === 'function' && that.delete(li, item); //修改方式 typeof options.reviseClick === 'function' && that.revise(li, item); //拖拽节点 options.drag && that.drag(li, item); } //选中回调函数 Atree.prototype.change = function () { var that = this.$vm, options = this.$options; options.change(changeList); }, //新增方法回调 Atree.prototype.add = function (elem, item) { var that = this.$vm, options = this.$options; var node = elem.children('.layui-atree-node'); var addBtn = node.children('.layui-atree-menu').children('.layui-atree-add') var arrow = node.children('.layui-atree-spread') var ul = elem.children('ul'), a = node.children('a'); var addEvent = function (e) { layui.stope(e); var _addEvent = { add: function (itemAddObj) { if (!item[that.childrenKey]) { item[that.childrenKey] = []; } item[that.childrenKey].push(itemAddObj); var dom = that.getDom(item); if (!ul[0]) { ul = $(dom.ul()) elem.append(ul); } if (!arrow[0]) { arrow = $(dom.spread()); node.prepend(arrow); that.spread(elem, item); } if (!elem.data('spread')) { that.open(elem, ul, arrow) } var li = $(that.getNode(itemAddObj)); that.bindUlEvent(li, itemAddObj); ul.append(li); } } options.addClick(item, elem, _addEvent.add) } addBtn.on('click', addEvent); } //删除方法回调 Atree.prototype.delete = function (elem, item) { //debugger var that = this.$vm, options = this.$options; var node = elem.children('.layui-atree-node'); var deleteBtn = node.children('.layui-atree-menu').children('.layui-atree-delete') var ul = elem.children('ul'), a = elem.children('a'); var deleteEvent = function (e) { layui.stope(e); var _deleteEvent = { done: function () { var parent = elem.parent(); var arrow = parent.parent().children('.layui-atree-spread') if (parent.children('li').length === 1) { arrow.remove(); } elem.remove(); } } options.deleteClick(item, elem, _deleteEvent.done) } deleteBtn.on('click', deleteEvent); } //修改方法回调 Atree.prototype.revise = function (elem, item) { var that = this.$vm, options = this.$options; var node = elem.children('.layui-atree-node'); var reviseBtn = node.children('.layui-atree-menu').children('.layui-atree-revise'); var ul = elem.children('ul'), a = node.children('a'); var reviseEvent = function (e) { layui.stope(e); var _reviseEvent = { done: function () { var htmla = a.prop('outerHTML'); var nodeName = item.name; // a.next('div.layui-atree-menu').hide(); a.replaceWith('<input value="' + nodeName + '">'); elem.find('input').focus().change(function(){ item.name = $(this).val(); htmla = htmla.replace(nodeName, $(this).val()); $(this).blur(); }).blur(function(){ $(this).replaceWith(htmla); a = elem.children('div').children('a'); /* layui.use(['admin','layer'], function () { layui.admin.req({ url: layui.setter.apiUrl.SlopsApi + '/Execute', data: { path: 'admin/AtreeRevise.txt', bidID: layui.data(layui.setter.tableName).bidID, ID: item.id, Title: item.name }, done: function (res) { } }) }); */ }); } } options.reviseClick(item, elem, _reviseEvent.done) } reviseBtn.on('click', reviseEvent); } //点击节点回调 Atree.prototype.click = function (elem, item) { var that = this.$vm, options = this.$options; var node = elem.children('.layui-atree-node'); node.children('a').on('click', function (e) { layui.stope(e); options.click(item) }); }; //节点选择 Atree.prototype.checkbox = function (elem, item) { var that = this.$vm, options = this.$options; var node = elem.children('.layui-atree-node'); var checkbox = node.children('.layui-atree-check') var ul = elem.children('ul'), a = node.children('a'); //递归设置子节点 var setAllChildCheck = function (dom, item, type) { var list = dom.children('.layui-show').find('li'); var children = item ? item.children || [] : []; for (var i = 0; i < list.length; i++) { var li = $(list[i]); setCheck(li, children[i], type); setAllChildCheck(li, children[i], type); } } //递归设置父节点 var setAllPatentCheck = function (dom, item, type) { var parent = dom.parent().parent(); var list = parent.children('.layui-show').find('li'); var isChildrenCheck = true; for (var i = 0; i < list.length; i++) { var li = $(list[i]); if (!li.data('check')) isChildrenCheck = false; } if (item.$parent && (isChildrenCheck || !type)) { setCheck(parent, item.$parent, type); setAllPatentCheck(parent, item.$parent, type); } } //设置节点选中状态 var setCheck = function (elem, item, type) { var checkbox = elem.children('.layui-atree-node').find('.layui-atree-check'); if (type) { elem.data('check', true) elem.attr("data-check", true); checkbox.html(icon.checkbox[1]) checkbox.addClass(' is-checked'); } else { elem.data('check', null); elem.attr("data-check", null); checkbox.removeClass(' is-checked'); checkbox.html(icon.checkbox[0]) } if (item) { var index = layui.findObj(changeList, item[that.idKey], that.idKey); //debugger if (index === -1 && type === true) { changeList.push(item); } else if (type === false) { changeList.splice(index, 1); } } } var check = function () { var checkFlag; if (elem.data('check')) { checkFlag = false; } else { checkFlag = true; } setCheck(elem, item, checkFlag) setAllPatentCheck(elem, item, checkFlag); setAllChildCheck(elem, item, checkFlag); that.change(); } checkbox.on('click', check); }; //伸展节点 Atree.prototype.spread = function (elem, item) { var that = this.$vm, options = this.$options; var node = elem.children('.layui-atree-node'); var arrow = node.children('.layui-atree-spread') var ul = elem.children('ul'), a = node.children('a'); //如果没有子节点,则不执行 if (!ul[0]) return; arrow.on('click', function () { that.open(elem, ul, arrow) }); } //打开节点 Atree.prototype.open = function (elem, ul, arrow) { if (elem.data('spread')) { elem.data('spread', null) elem.attr("data-spread", null); ul.removeClass('layui-show'); arrow.html(icon.arrow[0]); } else { elem.data('spread', true); elem.attr("data-spread", true); ul.addClass('layui-show'); arrow.html(icon.arrow[1]); } }; //通用事件 Atree.prototype.on = function (elem) { var that = this.$vm, options = this.$options; var dragStr = 'layui-atree-drag'; //屏蔽选中文字 elem.find('i').on('selectstart', function (e) { return false }); //拖拽 if (options.drag) { //mousedown:鼠标按钮被按下(左键或者右键)时触发。不能通过键盘触发。 $(document).on('mousemove', function (e) { var move = that.move; // 拖动状态, 增加一个 if (move.from) { //debugger var to = move.to ,dragElem = $('.' + dragStr); e.preventDefault(); if(!dragElem[0]){ dragElem = $('<div class="layui-box ' + dragStr + '"></div>'); $('body').append(dragElem); (dragElem).addClass('layui-show').html(move.from.elem.children('.layui-atree-node').children('a').html()); } dragElem.css({ left: e.pageX + 10, top: e.pageY + 10 }); } //mouseup:鼠标按钮被释放弹起时触发。不能通过键盘触发。 }).on('mouseup', function () { var move = that.move; if (move.from) { that.$el.find('.'+enterSkin).removeClass(enterSkin); that.move = {}; $('.' + dragStr).remove(); if(!move.to) return; move.from.elem.appendTo(move.to.elem); var item = move.from.item, chds = move.from.$parent&&move.from.$parent.children, len = chds&&chds.length; for(var i = 0; i < len; i++){ if(chds[i] === item){ chds.splice(i,1); break; } } move.to.item.children.push(item); options.dragend && options.dragend(move.from,move.to); that.move = {}; } }); } }; //拖拽节点 Atree.prototype.move = {}; Atree.prototype.drag = function (elem, item) { var that = this.$vm, options = this.$options; var a = elem.children('.layui-atree-node').children('a'), mouseenter = function () { var othis = $(this), move = that.move; if (move.from) { move.to = { item: item, elem: elem }; othis.addClass(enterSkin); } }; // 点击后,初始拖动 a.on('mousedown', function () { var move = that.move move.from = { item: item, elem: elem }; }); // 拖动状态经过项目时,增加经过样式,离开取消样式 elem.on('mouseenter', mouseenter) .on('mouseleave', function () { var othis = $(this), move = that.move; if (move.from) { delete move.to; othis.removeClass(enterSkin); } }); }; // 新增 Atree.prototype.addNode = function (node, parent){ var t = $.type(parent); if(t === 'object'){ parent.children.push(node); }else if( t === 'null' || t === 'undefined' ){ this.$options.nodes.push(node); }else{ var parentNode = this.findNode(parent); parentNode && parentNode.children.push(node); } return this; } // 移除节点,根据节点ID Atree.prototype.removeNode = function (id){ var node = this.findNode(id); var chds = node.$parent.children, len = chds.length; for(var i = 0; i < len; i++){ if(chds[i] === node){ chds.splice(i,1); return this; } } return this; } Atree.prototype.findNode = function (id, node){ if(!node){ node = {}; node.children = this.$options.nodes; } if(node.id == id){ return node; } else { var children = node.children, len = children&&children.length; if(len){ for(var i = 0; i < len; i++){ var nd = this.findNode(id, children[i]); if(nd){ return nd; } } } return null; } } Atree.prototype.findNodeByName = function (name, node){ if(!node){ node = {}; node.children = this.$options.nodes; } if(node[this.nameKey]&&node[this.nameKey].indexOf(name) != -1){ return node; } else { var children = node.children, len = children&&children.length; if(len){ for(var i = 0; i < len; i++){ var nd = this.findNodeByName(name, children[i]); if(nd){ return nd; } } } return null; } } //查询到对应的值 Atree.prototype.getNodesByName = function (name, arr, node){ if(!node){ node = {}; node.children = this.$options.nodes; } // 相似记录 到 arr 中 if(node[this.nameKey]&&node[this.nameKey].indexOf(name) != -1){ arr.push(node); } // 检查是否有 子集, 有则遍历子, 无则返回 var children = node.children, len = children&&children.length; if(len){ for(var i = 0; i < len; i++){ this.getNodesByName(name, arr, children[i]); } }else{ return; } } Atree.prototype.updateNode = function (id, attrs){ var node = this.findNode(id); node&&$.extend(node, attrs); return this; } // 重新加载 Atree.prototype.reload = function (){ this.$el.html(''); this.init(this.$options); return this; } //暴露接口 exports('atree', function (options) { //debugger var atree = new Atree(options = options || {}); var elem = $(options.elem); if (!elem[0]) { return hint.error('layui.atree 没有找到' + options.elem + '元素'); } atree.init(elem); return atree; }); }); ```