diff --git a/HARD b/HARD new file mode 100644 index 00000000..e69de29b diff --git a/doc/atree.md b/doc/atree.md new file mode 100644 index 00000000..00d2304f --- /dev/null +++ b/doc/atree.md @@ -0,0 +1,24 @@ +# atree2.0.0属性含义 +* spreadAll 设置checkbox全部选中 +* check勾选风格,不写没有勾选框 +* props 设置key属性别名 + - addBtnLabel:新增按钮标题 + - deleteBtnLabel:删除按钮标题 + - name:树显示的标题 + - id:主键对应的字段名 + - children:子类对应的字段名 + - checkbox:选中对应的字段名 + - spread:是否展开对应的字段名 +* change选中回调函数 + - val:选中的对象数组 +* click点击标题回调函数 + - item:当前点击的对象 +* addClick:新增回调函数 + - item:当前父节点的对象 + - elem:当前节点的dom对象 + - done:添加到dom节点的方法 +* deleteClick:删除回调函数 + - item:当前父节点的对象 + - elem:当前节点的dom对象 + - done:删除dom节点的方法 + \ No newline at end of file diff --git a/examples/atree.html b/examples/atree.html new file mode 100644 index 00000000..85d8a542 --- /dev/null +++ b/examples/atree.html @@ -0,0 +1,151 @@ + + + + + + + 树模块 - layui + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/css/layui.css b/src/css/layui.css index c62c75e7..bca21b9d 100644 --- a/src/css/layui.css +++ b/src/css/layui.css @@ -912,6 +912,27 @@ body .layui-table-tips .layui-layer-content{background: none; padding: 0; box-sh .layui-tree-drag{display: none; position: absolute; left: -666px; top: -666px; background-color: #f2f2f2; padding: 5px 10px; border: 1px dotted #000; white-space: nowrap} .layui-tree-drag i{padding-right: 5px;} +/** atree **/ +.layui-atree{line-height: 26px;} +.layui-atree li{position:relative;text-overflow: ellipsis; overflow:hidden; white-space: nowrap;} +.layui-atree li a, +.layui-atree li .layui-atree-spread{display: inline-block; vertical-align: top;font-size: 12px; height: 26px; *display: inline; *zoom:1; cursor: pointer;color: #c0c4cc} +.layui-atree li a{font-size: 0;} +.layui-atree li a i{font-size: 16px;} +.layui-atree li a cite{padding: 0 6px; font-size: 14px; font-style: normal;color: #606266;} +.layui-atree li i{padding-left: 6px; -moz-user-select: none;} +.layui-atree li .layui-atree-check{font-size: 16px;color: #dcdfe6} +.layui-atree li .layui-atree-check:hover{color: #409eff;} +.layui-atree li .layui-atree-check.is-checked{color: #409eff;} +.layui-atree li ul{ display:none;margin-left: 40px;} +.layui-atree li .layui-atree-enter{line-height: 24px; border: 1px dotted #000;} +.layui-atree-drag{display: none; position: absolute; left: -666px; top: -666px; background-color: #f2f2f2; padding: 5px 10px; border: 1px dotted #000; white-space: nowrap} +.layui-atree-drag i{padding-right: 5px;} +.layui-atree-node{position: relative;} +.layui-atree-node:hover { background-color:#eee;} +.layui-atree-menu{position: absolute;top:0;right: 0;z-index:1024;} +.layui-atree-menu span{margin-left: 10px;} + /** 导航菜单 **/ .layui-nav{position: relative; padding: 0 20px; background-color: #393D49; color: #fff; border-radius: 2px; font-size: 0; box-sizing: border-box;} .layui-nav *{font-size: 14px;} diff --git a/src/font/iconfont.eot b/src/font/iconfont.eot index 51c4f895..1524ef0c 100644 Binary files a/src/font/iconfont.eot and b/src/font/iconfont.eot differ diff --git a/src/font/iconfont.svg b/src/font/iconfont.svg index e05b1895..88235dff 100644 --- a/src/font/iconfont.svg +++ b/src/font/iconfont.svg @@ -9,9 +9,9 @@ Created by iconfont - + - + - + - + @@ -75,7 +75,7 @@ t9.5 -10.5t21.5 -4h37h67h81h80h64h36q23 0 34 12t2 38q-5 13 -9.5 30.5t-9.5 34.5q- - + @@ -84,10 +84,10 @@ t9.5 -10.5t21.5 -4h37h67h81h80h64h36q23 0 34 12t2 38q-5 13 -9.5 30.5t-9.5 34.5q- - + - + @@ -111,7 +111,7 @@ t9.5 -10.5t21.5 -4h37h67h81h80h64h36q23 0 34 12t2 38q-5 13 -9.5 30.5t-9.5 34.5q- - + @@ -156,7 +156,7 @@ t9.5 -10.5t21.5 -4h37h67h81h80h64h36q23 0 34 12t2 38q-5 13 -9.5 30.5t-9.5 34.5q- - + @@ -171,7 +171,7 @@ t9.5 -10.5t21.5 -4h37h67h81h80h64h36q23 0 34 12t2 38q-5 13 -9.5 30.5t-9.5 34.5q- - + @@ -195,7 +195,7 @@ t9.5 -10.5t21.5 -4h37h67h81h80h64h36q23 0 34 12t2 38q-5 13 -9.5 30.5t-9.5 34.5q- - + @@ -204,13 +204,13 @@ t9.5 -10.5t21.5 -4h37h67h81h80h64h36q23 0 34 12t2 38q-5 13 -9.5 30.5t-9.5 34.5q- - + - + @@ -222,7 +222,7 @@ t9.5 -10.5t21.5 -4h37h67h81h80h64h36q23 0 34 12t2 38q-5 13 -9.5 30.5t-9.5 34.5q- - + @@ -231,7 +231,7 @@ t9.5 -10.5t21.5 -4h37h67h81h80h64h36q23 0 34 12t2 38q-5 13 -9.5 30.5t-9.5 34.5q- - + @@ -327,6 +327,9 @@ t9.5 -10.5t21.5 -4h37h67h81h80h64h36q23 0 34 12t2 38q-5 13 -9.5 30.5t-9.5 34.5q- + + + @@ -339,6 +342,9 @@ t9.5 -10.5t21.5 -4h37h67h81h80h64h36q23 0 34 12t2 38q-5 13 -9.5 30.5t-9.5 34.5q- + + + diff --git a/src/font/iconfont.ttf b/src/font/iconfont.ttf index 43a31613..f6c86825 100644 Binary files a/src/font/iconfont.ttf and b/src/font/iconfont.ttf differ diff --git a/src/font/iconfont.woff b/src/font/iconfont.woff index 7fd98e35..409d2a22 100644 Binary files a/src/font/iconfont.woff and b/src/font/iconfont.woff differ diff --git a/src/lay/modules/atree.js b/src/lay/modules/atree.js new file mode 100644 index 00000000..98671179 --- /dev/null +++ b/src/lay/modules/atree.js @@ -0,0 +1,420 @@ +/** + + @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', + }; + + var enterSkin = 'layui-atree-enter', + Atree = function(options) { + this.options = options; + this.nodes = 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; + }; + //图标 + var icon = { + arrow: ['', ''] //箭头 + , + checkbox: ['', ''] //复选框 + , + leaf: '' //叶节点 + }; + + //初始化 + Atree.prototype.init = function(elem) { + var that = this; + elem.addClass('layui-box layui-atree'); //添加tree样式 + if(that.options.skin) { + elem.addClass('layui-atree-skin-' + that.options.skin); + } + that.tree(elem); + that.on(elem); + }; + + //树节点解析 + Atree.prototype.tree = function(elem, children) { + var that = this, + options = that.options + var nodes = children || options.nodes; + layui.each(nodes, function(index, item) { + var hasChild = item[that.childrenKey] && item[that.childrenKey].length > 0; + var dom = that.getDom(item); + 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]); + } + //伸展节点 + that.spread(li, item); + that.bindUlEvent(li, item); + elem.append(li); + + }); + }; + + //节点dom拼接 + Atree.prototype.getDom = function(item) { + var that = this, + options = that.options, + item = item, + hasChild = item[that.childrenKey] && item[that.childrenKey].length > 0; + return { + spread: function() { + return hasChild ? '' + ( + item[that.spreadKey] || options.spreadAll ? icon.arrow[1] : icon.arrow[0] + ) + '' : ''; + }, + checkbox: function() { + return options.check ? ( + '' + ( + item[that.checkboxKey] ? icon.checkbox[1] : icon.checkbox[0] + ) + '' + ) : ''; + }, + ul: function() { + return '
    ' + }, + node: function() { + return '' + + ('' + (item[that.nameKey] || '未命名') + '') + }, + menu: function() { + return '
    ' + + '' + that.addBtnLabelKey + '' + + '' + that.deleteBtnLabelKey + '' + + '
    ' + } + } + + } + //获取树节点 + Atree.prototype.getNode = function(item) { + var that = this, + options = that.options + var dom = that.getDom(item); + var li = ['
  • ' + //展开箭头 + , + dom.spread() + + //复选框 + , + dom.checkbox() + + //节点 + , + dom.node() + //菜单 + , + dom.menu(), + '
  • ' + ].join(''); + return li; + } + + //父绑定事件 + Atree.prototype.bindUlEvent = function(li, item) { + var that = this, + options = that.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); + + //拖拽节点 + options.drag && that.drag(li, item); + } + + //选中回调函数 + Atree.prototype.change = function() { + var that = this, + options = that.options; + options.change(changeList); + }, + + //新增方法回调 + Atree.prototype.add = function(elem, item) { + var that = this, + options = that.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) { + var that = this, + options = that.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.click = function(elem, item) { + var that = this, + options = that.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, + options = that.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 whileAllCheck = 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); + whileAllCheck(li, children[i], type); + } + } + var setCheck = function(elem, item, type) { + var checkbox = elem.children('.layui-atree-node').find('.layui-atree-check'); + if(type) { + elem.data('check', true) + checkbox.html(icon.checkbox[1]) + checkbox.addClass(' is-checked'); + } else { + elem.data('check', null); + checkbox.removeClass(' is-checked'); + checkbox.html(icon.checkbox[0]) + } + if(item) { + var index = layui.findObj(changeList, item[that.idKey], that.idKey); + 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) + whileAllCheck(elem, item, checkFlag); + that.change(); + } + checkbox.on('click', check); + + }; + + //伸展节点 + Atree.prototype.spread = function(elem, item) { + var that = this, + options = that.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) + ul.removeClass('layui-show'); + arrow.html(icon.arrow[0]); + } else { + elem.data('spread', true); + ul.addClass('layui-show'); + arrow.html(icon.arrow[1]); + } + }; + //通用事件 + Atree.prototype.on = function(elem) { + var that = this, + options = that.options; + var dragStr = 'layui-atree-drag'; + + //屏蔽选中文字 + elem.find('i').on('selectstart', function(e) { + return false + }); + + //拖拽 + if(options.drag) { + $(document).on('mousemove', function(e) { + var move = that.move; + if(move.from) { + var to = move.to, + treeMove = $('
    '); + e.preventDefault(); + $('.' + dragStr)[0] || $('body').append(treeMove); + var dragElem = $('.' + dragStr)[0] ? $('.' + dragStr) : treeMove; + (dragElem).addClass('layui-show').html(move.from.elem.children('a').html()); + dragElem.css({ + left: e.pageX + 10, + top: e.pageY + 10 + }) + } + }).on('mouseup', function() { + var move = that.move; + if(move.from) { + move.from.elem.children('a').removeClass(enterSkin); + move.to && move.to.elem.children('a').removeClass(enterSkin); + that.move = {}; + $('.' + dragStr).remove(); + } + }); + } + }; + + //拖拽节点 + Atree.prototype.move = {}; + Atree.prototype.drag = function(elem, item) { + var that = this, + options = that.options; + var a = elem.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 + }; + }); + a.on('mouseenter', mouseenter).on('mousemove', mouseenter) + .on('mouseleave', function() { + var othis = $(this), + move = that.move; + if(move.from) { + delete move.to; + othis.removeClass(enterSkin); + } + }); + }; + + //暴露接口 + exports('atree', function(options) { + var atree = new Atree(options = options || {}); + var elem = $(options.elem); + if(!elem[0]) { + return hint.error('layui.atree 没有找到' + options.elem + '元素'); + } + atree.init(elem); + }); +}); \ No newline at end of file diff --git a/src/layui.js b/src/layui.js index 351a6975..90e0d180 100644 --- a/src/layui.js +++ b/src/layui.js @@ -56,6 +56,7 @@ ,layedit: 'modules/layedit' //富文本编辑器 ,form: 'modules/form' //表单集 ,upload: 'modules/upload' //上传 + ,atree: 'modules/atree' //新树结构 ,tree: 'modules/tree' //树结构 ,table: 'modules/table' //表格 ,element: 'modules/element' //常用元素操作 @@ -430,7 +431,20 @@ error: error } }; + //判断对象是否相等 + Layui.prototype.isEqualObj = function(obj1,obj2){ + return JSON.stringify(obj1)===JSON.stringify(obj2) + } + //寻找对象是否存在数组中 + Layui.prototype.findObj = function(list,value,key) { + var that = this, + result = -1; + that.each(list, function(index, item) { + if(item[key] == value)result = index; + }) + return result; + } //遍历 Layui.prototype.each = function(obj, fn){ var key