diff --git a/CHANGELOG.md b/CHANGELOG.md index abde392..365fb51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ ## 更新日志 +### 1.0.10 + +*2019-10-20* + +#### 新增 + +- 新增`content`配置, 可自定义下拉框HTML, 具体见 [下拉自定义](https://maplemei.gitee.io/xm-select/#/example-plugin/ZP01) +- 方法`setValue`新增参数`listenOn`, 可以设置是否通过`on`监听 + +#### Bug fixes + +- 修复初始化渲染也会被`on`监听的bug +- 修复分组模式下, 搜索后分组显示错误 +- 调整分组模式下也可以使用分页, 选项控制 + + ### 1.0.9 *2019-10-17* diff --git a/dist/static/2.js b/dist/static/2.js index ea50068..9ce5a4a 100644 --- a/dist/static/2.js +++ b/dist/static/2.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[2],{361:function(e,t,n){e.exports=n.p+"static/wx.f391ad4.jpg"},363:function(e,t,n){"use strict";n.r(t);var a=function(){var e=this.$createElement,t=this._self._c||e;return t("section",{staticClass:"content element-doc"},[this._m(0),this._m(1),t("demo-block",[t("div",[t("p",[this._v("事实证明分页是好使的 ^_^")])]),t("template",{slot:"source"},[t("element-demo0")],1),t("template",{slot:"highlight"},[t("pre",{pre:!0},[t("code",{pre:!0,attrs:{class:"html"}},[this._v('
\n\n\n\n
\n\n
 ```
 :::
diff --git a/docs/mds/XM21.md b/docs/mds/XM21.md
index 0349f9d..9e5032c 100644
--- a/docs/mds/XM21.md
+++ b/docs/mds/XM21.md
@@ -1,12 +1,6 @@
 ## 分组
 
 
-
-:::tip
-使用分组时, 不建议开启分页, 也不建议开启选项数量控制!!!
-:::
-
-
 ### optgroup模式
 
 :::demo 指定选项中的`optgroup`为`true`
@@ -134,3 +128,37 @@ var demo4 = xmSelect.render({
 
 ```
 :::
+
+
+### 带有分页的分组
+
+:::demo 
+```html
+
+ + +``` +::: diff --git a/docs/mds/ZP01.md b/docs/mds/ZP01.md new file mode 100644 index 0000000..e79986d --- /dev/null +++ b/docs/mds/ZP01.md @@ -0,0 +1,53 @@ +## 下拉自定义 + + + +### 下拉表格 + +至于能干什么, 就看你们的想象了~~ + +:::demo +```html + +
+ + +``` +::: diff --git a/docs/mds/ZP02.md b/docs/mds/ZP02.md new file mode 100644 index 0000000..23948ee --- /dev/null +++ b/docs/mds/ZP02.md @@ -0,0 +1,88 @@ +## 下拉树 + +### eleTree + +结合 `layui` 插件中心的 `eleTree`, 传送门 + + +:::demo +```html + +
+ + +``` +::: diff --git a/docs/mds/ZTEST.md b/docs/mds/ZTEST.md index 8a02bf2..8052d48 100644 --- a/docs/mds/ZTEST.md +++ b/docs/mds/ZTEST.md @@ -7,28 +7,29 @@
``` ::: diff --git a/docs/mds/options.md b/docs/mds/options.md index 5bd851d..1ea71da 100644 --- a/docs/mds/options.md +++ b/docs/mds/options.md @@ -8,6 +8,7 @@ | el | 渲染对象, css选择器 | string | - | - | | language | 语言选择 | string | zn / en | zn | | data | 显示的数据 | array | - | [ ] | +| content | 自定义下拉框html | string | - | - | | initValue | 初始化选中的数据, 需要在data中存在 | array | - | null | | tips | 默认提示, 类似于placeholder | string | - | 请选择 | | empty | 空数据提示 | string | - | 暂无数据 | @@ -38,7 +39,7 @@ | name | 表单提交时的name | string | - | select | | toolbar | 工具条, 具体看下表 | object | - | - | | showCount | 展示在下拉框中的最多选项数量 | int | - | 0 | -| autoRow | 是否开启自动换行(选项过多时) | boolean | - | false | +| autoRow | 是否开启自动换行(选项过多时) | boolean | true / false | false | | size | 尺寸 | string | large / medium / small / mini | medium | @@ -171,7 +172,7 @@ xmSelect.render()后会返回一个xmSelect对象, 可以进行方法调用 | 事件名 | 说明 | 参数 | | ------ | ------------------ | -------- | | getValue | 获取当前选中的数据 | (type: 类型), 可选值: name, nameStr, value, valueStr | -| setValue | 动态设置数据 | (array: 选中的数据, show: 是否展开下拉, 不传默认当前显示状态, 取值: true/false) | +| setValue | 动态设置数据 | (array: 选中的数据, show: 是否展开下拉,不传默认当前显示状态,取值: true/false, listenOn: 是否触发on的监听, 默认false) | | append | 追加赋值 | (array: 追加的数据) | | delete | 删除赋值 | (array: 删除的数据) | | opened | 主动展开下拉 | - | diff --git a/docs/mds/question.md b/docs/mds/question.md index a2ca485..d85f760 100644 --- a/docs/mds/question.md +++ b/docs/mds/question.md @@ -22,3 +22,8 @@ - 打开控制台查看是否报错 - 加群: 660408068, 询问 + + +### 4.占位标签为什么是div + +演示中使用的是div, 不限制标签, 但是不建议使用`select`, 因为`layui`会渲染`select`标签 \ No newline at end of file diff --git a/docs/plugins/eleTree/eleTree.css b/docs/plugins/eleTree/eleTree.css new file mode 100755 index 0000000..ee5598c --- /dev/null +++ b/docs/plugins/eleTree/eleTree.css @@ -0,0 +1,168 @@ +/* #region tree */ +.eleTree{ + position: relative; +} +.eleTree-hide, +.eleTree-search-hide{ + display: none; +} +.eleTree-loadData{ + width: 100%; + height: 100%; + position: absolute; + z-index: 1; + top: 0px; +} +.eleTree-loadData .layui-icon{ + position: absolute; + left: 50%; + top: 50%; + transform: translateX(-50%) translateY(-50%); +} +.eleTree-node-content{ + cursor: pointer; + height: 26px; + line-height: 1.3; + white-space: nowrap; +} +.eleTree-node-content:hover, +.eleTree-node-content.eleTree-node-content-active{ + background-color: #eee; +} +.eleTree-node-content-icon .layui-icon{ + padding: 6px 3px; + color: #c0c4cc; + font-size: 12px; + display: inline-block; + transform: rotate(0deg); + transition: transform .3s ease-in-out; +} +.eleTree-node-content-icon .layui-icon.icon-rotate{ + transform: rotate(90deg); +} +.eleTree-node-content .layui-form-checkbox[lay-skin=primary] i{ + width: 13px; + height: 14px; + line-height: 1.3; +} +.eleTree-node-content-label{ + padding-left: 5px; +} +.eleTree-node-content-input{ + width: 80px; + border: 1px solid #e6e6e6; + outline: 0; + padding: 3px 5px; + font-size: 12px; +} + +/* 线条样式 */ +.eleTree-node{ + position: relative; +} +.eleTree-node .eleTree-node-verticalline{ + position: absolute; + width: 0; + height: 100%; + border: 1px dotted #ccc; + z-index: 1; +} +.eleTree-node .eleTree-node-horizontalline{ + position: absolute; + height: 0; + top: 13px; + border: 1px dotted #ccc; + z-index: 1; +} + +/* checkbox第三种状态 */ +input.eleTree-hideen[type=checkbox]{ + display: none; +} +.eleTree-checkbox { + height: auto!important; + line-height: normal!important; + min-height: 12px; + border: none!important; + margin-right: 0; + padding-left: 18px; + position: relative; + display: inline-block; +} +.eleTree-checkbox i { + left: 0; + border: 1px solid #d2d2d2; + font-size: 12px; + border-radius: 2px; + background-color: #fff; + -webkit-transition: .1s linear; + transition: .1s linear; + position: absolute; + top: 0; + color: #fff; + cursor: pointer; + text-align: center; + width: 13px; + height: 14px; + line-height: 1.3; +} +.eleTree-checkbox i:hover { + border-color: #5FB878; +} +.eleTree-checkbox-checked i { + border-color: #5FB878; + background-color: #5FB878; + color: #fff; +} +.eleTree-checkbox-line:after{ + content: ""; + position: relative; + width: 8px; + height: 1px; + background-color: #fff; + display: inline-block; + top: -4px; +} + +.eleTree-checkbox.eleTree-checkbox-disabled i{ + cursor: not-allowed; + background-color: #f2f6fc; + border-color: #dcdfe6; + color: #c2c2c2; +} +.eleTree-checkbox.eleTree-checkbox-disabled i.eleTree-checkbox-line:after{ + background-color: #c2c2c2; +} +.eleTree-checkbox.eleTree-checkbox-disabled i:hover{ + border-color: #dcdfe6; +} + +#tree-menu{ + margin: 0; + padding: 2px; + position: absolute; + background: #f5f5f5; + border: 1px solid #979797; + box-shadow: 2px 2px 2px #999; + display: none; + z-index: 20181205; +} +#tree-menu li>a{ + display: block; + padding: 0 1em; + text-decoration: none; + width: auto; + color: #000; + white-space: nowrap; + line-height: 2.4em; + text-shadow: 1px 1px 0 #fff; + border-radius: 1px; +} +#tree-menu li>a:hover{ + background-color: #e8eff7; + box-shadow: 0 0 2px #0a6aa1; +} +.tree-menu-bg{ + background-color: #ccc; +} +/* #endregion */ \ No newline at end of file diff --git a/docs/plugins/eleTree/eleTree.js b/docs/plugins/eleTree/eleTree.js new file mode 100755 index 0000000..dba9a9c --- /dev/null +++ b/docs/plugins/eleTree/eleTree.js @@ -0,0 +1,1554 @@ +/** + * @Name: 基于layui的tree重写 + * @Author: 李祥 + * @License:MIT + * 最近修改时间: 2019/09/24 + */ + +layui.define(["jquery","laytpl"], function (exports) { + var $ = layui.jquery; + var laytpl = layui.laytpl; + var hint = layui.hint(); + + var MOD_NAME="eleTree"; + + //外部接口 + var eleTree={ + //事件监听 + on: function(events, callback){ + return layui.onevent.call(this, MOD_NAME, events, callback); + }, + render: function(options) { + var inst = new Class(options); + return thisTree.call(inst); + } + } + + var thisTree=function() { + var _self=this; + var options = _self.config; + + // 暴漏外面的方法 + return { + // 接收两个参数,1. 节点 key 2. 节点数据的数组 + updateKeyChildren: function(key,data) { + if(options.data.length===0) return; + return _self.updateKeyChildren.call(_self,key,data); + }, + updateKeySelf: function(key,data) { + if(options.data.length===0) return; + return _self.updateKeySelf.call(_self,key,data); + }, + remove: function(key) { + if(options.data.length===0) return; + return _self.remove.call(_self,key); + }, + append: function(key,data) { + if(options.data.length===0) return; + return _self.append.call(_self,key,data); + }, + insertBefore: function(key,data) { + if(options.data.length===0) return; + return _self.insertBefore.call(_self,key,data); + }, + insertAfter: function(key,data) { + if(options.data.length===0) return; + return _self.insertAfter.call(_self,key,data); + }, + // 接收两个 boolean 类型的参数,1. 是否只是叶子节点,默认值为 false 2. 是否包含半选节点,默认值为 false + getChecked: function(leafOnly, includeHalfChecked) { + if(options.data.length===0) return; + return _self.getChecked.call(_self,leafOnly, includeHalfChecked); + }, + // 接收勾选节点数据的数组 + setChecked: function(data,isReset) { + if(options.data.length===0) return; + return _self.setChecked.call(_self,data,isReset); + }, + // 取消选中 + unCheckNodes: function() { + if(options.data.length===0) return; + return _self.unCheckNodes.call(_self); + }, + unCheckArrNodes: function(data) { + if(options.data.length===0) return; + return _self.unCheckArrNodes.call(_self,data); + }, + expandAll: function() { + if(options.data.length===0) return; + return _self.expandAll.call(_self); + }, + expandNode: function(key) { + if(options.data.length===0) return; + return _self.expandNode.call(_self,key); + }, + unExpandNode: function(key) { + if(options.data.length===0) return; + return _self.unExpandNode.call(_self,key); + }, + toggleExpandNode: function(key) { + if(options.data.length===0) return; + return _self.toggleExpandNode.call(_self,key); + }, + unExpandAll: function() { + if(options.data.length===0) return; + return _self.unExpandAll.call(_self); + }, + reload: function(options) { + return _self.reload.call(_self,options); + }, + search: function(value) { + return _self.search.call(_self,value); + }, + getAllNodeData: function() { + return _self.getAllNodeData.call(_self); + } + } + } + + // 模板渲染 + var TPL_ELEM=function(options,floor,parentStatus) { + return [ + '{{# for(var i=0;i', + function() { + // 是否显示连线 + if(!options.showLine) return ''; + if(floor!==0){ + var s=''+ + ''; + return s; + }else{ + var s=''+ + ''; + return s; + } + }(), + '
', + '', + '', + '{{# }else{ }}', + 'leaf-icon" style="color: transparent;" >', + '{{# } }}' + ].join(""); + return str; + } + return ['{{# if(!d[i]["'+options.request.children+'"] || d[i]["'+options.request.children+'"].length===0){ }}', + 'leaf-icon" style="color: transparent;"', + '{{# } }}', + '">' + ].join(""); + }(), + '', + function() { + if(options.showCheckbox){ + var status=""; + if(options.checkStrictly){ + status='"0"'; + }else if(parentStatus==="1"){ + status='"1" checked'; + }else if(parentStatus==="2"){ + status='"2"'; + }else{ + status='"0"'; + } + return [ + '{{# if(d[i]["'+options.request.checked+'"]) { }}', + '' + ].join(""); + } + return '' + }(), + '', + '
', + '', + '', + '{{# } }}' + ].join(""); + } + + var TPL_NoText=function() { + return '

{{d.emptText}}

'; + } + + var Class=function(options) { + options.response=$.extend({}, this.config.response, options.response); + options.request=$.extend({}, this.config.request, options.request); + this.config = $.extend({}, this.config, options); + this.config.customKey=this.customKeyInit(); + this.prevClickEle=null; + this.nameIndex=1; + this.isRenderAllDom=false; + this.render(); + }; + + Class.prototype={ + constructor: Class, + config: { + elem: "", + data: [], + emptText: "暂无数据", // 内容为空的时候展示的文本 + renderAfterExpand: true, // 是否在第一次展开某个树节点后才渲染其子节点 + highlightCurrent: false, // 是否高亮当前选中节点,默认值是 false。 + defaultExpandAll: false, // 是否默认展开所有节点 + expandOnClickNode: true, // 是否在点击节点的时候展开或者收缩节点, 默认值为 true,如果为 false,则只有点箭头图标的时候才会展开或者收缩节点。 + checkOnClickNode: false, // 是否在点击节点的时候选中节点,默认值为 false,即只有在点击复选框时才会选中节点。 + defaultExpandedKeys: [], // 默认展开的节点的 key 的数组 + autoExpandParent: true, // 展开子节点的时候是否自动展开父节点 + showCheckbox: false, // 节点是否可被选择 + checkStrictly: false, // 在显示复选框的情况下,是否严格的遵循父子不互相关联的做法,默认为 false + defaultCheckedKeys: [], // 默认勾选的节点的 key 的数组 + accordion: false, // 是否每次只打开一个同级树节点展开(手风琴效果) + indent: 16, // 相邻级节点间的水平缩进,单位为像素 + lazy: false, // 是否懒加载子节点,需与 load 方法结合使用 + load: function() {}, // 加载子树数据的方法,仅当 lazy 属性为true 时生效 + draggable: false, // 是否开启拖拽节点功能 + contextmenuList: [], // 启用右键菜单,支持的操作有:"copy","add","edit","remove" + searchNodeMethod: null, // 对树节点进行筛选时执行的方法,返回 true 表示这个节点可以显示,返回 false 则表示这个节点会被隐藏 + showLine: false, // 是否显示连线,默认false + + method: "get", + url: "", + contentType: "", + headers: {}, + done: null, + + response: { + statusName: "code", + statusCode: 0, + dataName: "data" + }, + request: { + name: "label", + key: "id", + children: "children", + disabled: "disabled", + checked: "checked", + isLeaf: "isLeaf" + }, + customKey: "id", // 自定义key转义,即appKey=>app-id + }, + render: function() { + if(this.config.indent>30){ + this.config.indent=30; + }else if(this.config.indent<10){ + this.config.indent=10; + } + var options=this.config; + options.where=options.where || {}; + if(!options.elem) return hint.error("缺少elem参数"); + options.elem=typeof options.elem === "string" ? $(options.elem) : options.elem; + this.filter=options.elem.attr("lay-filter"); + // load加载框 + options.elem.append('
') + + // 判断加载方式 + if(options.data.length===0){ + this.ajaxGetData(); + }else{ + this.renderData(); + } + }, + renderData: function() { + var options=this.config; + $(this.config.elem).off(); // 取消事件绑定,防止多次绑定事件 + // 渲染第一层 + laytpl(TPL_ELEM(options,0)).render(options.data, function(string){ + options.elem.html(string); + }); + // 懒加载 > 展开所有 > 初始展开项 > 初始渲染所有子节点 > 初始选中项 > 每次点击只渲染当前层(默认) + // 判断所有dom是否全部加载 + if(!options.lazy){ + if(!options.renderAfterExpand || options.defaultExpandAll || options.defaultExpandedKeys.length>0 || options.defaultCheckedKeys.length>0){ + this.initialExpandAll(options.data,[],1); + } + } + + this.eleTreeEvent(); + this.checkboxRender(); + this.checkboxEvent(); + this.defaultChecked(); + this.nodeEvent(); + this.rightClickMenu(); + if(!options.checkStrictly){ + this.checkboxInit(); + } + }, + ajaxGetData: function() { + var options=this.config; + var _self=this; + if(!options.url) { + laytpl(TPL_NoText()).render(options, function(string){ + options.elem.html(string); + }); + return; + } + var data = $.extend({}, options.where); + if(options.contentType && options.contentType.indexOf("application/json") == 0){ //提交 json 格式 + data = JSON.stringify(data); + } + + $.ajax({ + type: options.method || 'get' + ,url: options.url + ,contentType: options.contentType + ,data: data + ,dataType: 'json' + ,headers: options.headers || {} + ,success: function(res){ + if(res[options.response.statusName] != options.response.statusCode || !res[options.response.dataName]){ + hint.error("请检查数据格式是否符合规范"); + typeof options.done === 'function' && options.done(res); + return; + } + options.data=res[options.response.dataName]; + _self.renderData(); + typeof options.done === 'function' && options.done(res); + } + }); + }, + reload: function(options) { + var _self=this; + if(this.config.data && this.config.data.constructor === Array) this.config.data=[]; + this.config = $.extend({}, this.config, options); + // $(this.config.elem).off(); // 取消事件绑定,防止多次绑定事件 + // reload记录选中的数据 + // this.getChecked().forEach(function(val) { + // if($.inArray(val.key,this.config.defaultCheckedKeys)===-1){ + // this.config.defaultCheckedKeys.push(val.key); + // } + // },this); + return eleTree.render(this.config) + }, + // 自定义data属性修改,即 appId=>app-id,解决key包含大写的问题 + customKeyInit: function() { + var options=this.config; + var key=""; + for(var i=0;i.eleTree-node-content-icon"; + options.elem.on("click",expandOnClickNode,function(e) { + e.stopPropagation(); + var eleTreeNodeContent=$(this).parent(".eleTree-node").length===0?$(this).parent(".eleTree-node-content"):$(this); + var eleNode=eleTreeNodeContent.parent(".eleTree-node"); + var sibNode=eleTreeNodeContent.siblings(".eleTree-node-group"); + var el=eleTreeNodeContent.children(".eleTree-node-content-icon").children(".layui-icon"); + + if(el.hasClass("icon-rotate")){ + // 合并 + sibNode.hide("fast"); + el.removeClass("icon-rotate"); + return; + } + + if(sibNode.children(".eleTree-node").length===0){ + var floor=Number(eleNode.attr("eletree-floor"))+1; + + // 选择祖父 + var selectParentsFn=function() { + if(!options.checkStrictly){ + var eleNode1=sibNode.children(".eleTree-node").eq(0); + if(eleNode1.length!==0){ + var siblingNode1=eleNode1.siblings(".eleTree-node"); + var item1=eleNode1.children(".eleTree-node-content").children(".eleTree-hideen").get(0); + _self.selectParents(item1,eleNode1,siblingNode1); + } + } + } + + var data=_self.reInitData(eleNode); + var d=data.currentData; + // 是否懒加载 + if(options.lazy && el.hasClass("lazy-icon")){ + el.removeClass("layui-icon-triangle-r").addClass("layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop"); + options.load(d,function(getData) { + // 如果原来有数据则合并,没有则赋值 + if(d[options.request.children]){ + d[options.request.children]=d[options.request.children].concat(getData); + }else{ + d[options.request.children]=getData; + } + var eletreeStatus=eleTreeNodeContent.children("input.eleTree-hideen").attr("eletree-status"); + if(d[options.request.children] && d[options.request.children].length>0){ + // 只渲染获取到的数据 + laytpl(TPL_ELEM(options,floor,eletreeStatus)).render(getData, function(string){ + sibNode.append(string).show("fast"); + }); + }else{ + el.css("color","transparent").addClass("leaf-icon"); + } + el.removeClass("lazy-icon layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop").addClass("layui-icon-triangle-r icon-rotate"); + + // 懒加载子元素选择祖父 + selectParentsFn(); + _self.checkboxRender(); + }) + }else{ + var eletreeStatus=eleTreeNodeContent.children("input.eleTree-hideen").attr("eletree-status"); + if(d[options.request.children] && d[options.request.children].length>0){ + laytpl(TPL_ELEM(options,floor,eletreeStatus)).render(d[options.request.children], function(string){ + sibNode.append(string).show("fast"); + el.addClass("icon-rotate"); + }); + // 选择祖父 + selectParentsFn(); + _self.checkboxRender(); + } + } + }else{ + // 有子节点则展开子节点 + sibNode.show("fast"); + el.addClass("icon-rotate"); + } + + // 手风琴效果 + if(options.accordion){ + var node=eleTreeNodeContent.parent(".eleTree-node").siblings(".eleTree-node"); + node.children(".eleTree-node-group").hide("fast"); + node.children(".eleTree-node-content").children(".eleTree-node-content-icon").children(".layui-icon").removeClass("icon-rotate"); + } + }) + }, + // checkbox选中 + checkboxEvent: function() { + var options=this.config; + var _self=this; + var checkOnClickNode=options.checkOnClickNode?".eleTree-node-content":".eleTree-checkbox"; + // input添加属性eleTree-status:即input的三种状态,"0":未选中,"1":选中,"2":子孙部分选中 + options.elem.on("click",checkOnClickNode,function(e) { + e.stopPropagation(); + var eleTreeNodeContent=$(this).parent(".eleTree-node").length===0?$(this).parent(".eleTree-node-content"):$(this); + var checkbox=eleTreeNodeContent.children(".eleTree-checkbox"); + if(checkbox.hasClass("eleTree-checkbox-disabled")) return; + // 获取点击所在数据 + var node=eleTreeNodeContent.parent(".eleTree-node"); + // var d=_self.reInitData(node).currentData; + // 实际的input + var inp=checkbox.siblings(".eleTree-hideen").get(0); + var childNode=eleTreeNodeContent.siblings(".eleTree-node-group").find("input[name='eleTree-node']"); + + // 添加active背景 + if(_self.prevClickEle) _self.prevClickEle.removeClass("eleTree-node-content-active"); + if(options.highlightCurrent) eleTreeNodeContent.addClass("eleTree-node-content-active"); + _self.prevClickEle=eleTreeNodeContent; + + if(!inp) return; + + if(inp.checked){ + // 反选自身 + $(inp).prop("checked",false).attr("eleTree-status","0").removeAttr("data-checked"); + // 点击祖父层选中子孙层 + if(!options.checkStrictly){ + childNode.prop("checked",false).attr("eleTree-status","0").removeAttr("data-checked"); + } + + }else{ + // 反选自身 + $(inp).prop("checked",true).attr("eleTree-status","1"); + // 点击祖父层选中子孙层 + if(!options.checkStrictly){ + childNode.prop("checked",true).attr("eleTree-status","1"); + } + } + + var eleNode=eleTreeNodeContent.parent(".eleTree-node"); + // 点击子孙层选中祖父层(递归) + if(!options.checkStrictly){ + var siblingNode=eleNode.siblings(".eleTree-node"); + // 点击子孙层选中祖父层(递归) + _self.selectParents(inp,eleNode,siblingNode); + } + + _self.checkboxRender(); + + layui.event.call(inp, MOD_NAME, 'nodeChecked('+ _self.filter +')', { + node: eleNode, + data: _self.reInitData(eleNode), + isChecked: inp.checked + }); + }) + }, + // 对后台数据有 checked:true 的默认选中项渲染父子层 + checkboxInit: function() { + var options=this.config; + var _self=this; + options.elem.find("input[data-checked]").each(function(index,item) { + var checkboxEl=$(item).siblings(".eleTree-checkbox"); + var childNode=checkboxEl.parent(".eleTree-node-content").siblings(".eleTree-node-group").find("input[name='eleTree-node']"); + // 选择当前 + $(item).prop("checked","checked").attr("eleTree-status","1"); + checkboxEl.addClass("eleTree-checkbox-checked"); + checkboxEl.children("i").addClass("layui-icon-ok").removeClass("eleTree-checkbox-line"); + if(options.checkStrictly) return; + // 选择子孙 + childNode.prop("checked","checked").attr("eleTree-status","1"); + childNode.siblings(".eleTree-checkbox").addClass("eleTree-checkbox-checked"); + childNode.siblings(".eleTree-checkbox").children("i").addClass("layui-icon-ok").removeClass("eleTree-checkbox-line"); + + // 选择祖父 + var eleNode=checkboxEl.parent(".eleTree-node-content").parent(".eleTree-node"); + var siblingNode=eleNode.siblings(".eleTree-node"); + _self.selectParents(item,eleNode,siblingNode); + }) + _self.checkboxRender(); + }, + // 通过子元素选中祖父元素 + selectParents: function(inp,eleNode,siblingNode) { + // inp: 实际input(dom元素) + // eleNode: input父层类(.eleTree-node) + // siblingNode: 父层同级兄弟 + while (Number(eleNode.attr("eletree-floor"))!==0) { + // 同级input状态存入数组 + var arr=[]; + arr.push($(inp).attr("eleTree-status")); + siblingNode.each(function(index,item) { + var siblingIsChecked=$(item).children(".eleTree-node-content").children("input[name='eleTree-node']").attr("eleTree-status"); + arr.push(siblingIsChecked); + }) + // 父元素的实际input + var parentInput=eleNode.parent(".eleTree-node-group").siblings(".eleTree-node-content").children("input[name='eleTree-node']"); + // 父元素的checkbox替代 + var parentCheckbox=parentInput.siblings(".eleTree-checkbox"); + // 子都选中则选中父 + if(arr.every(function(val) { + return val==="1"; + })){ + parentInput.prop("checked",true).attr("eleTree-status","1"); + } + // 子有一个未选中则checkbox第三种状态 + if(arr.some(function(val) { + return val==="0" || val==="2"; + })){ + parentInput.attr("eleTree-status","2"); + } + // 子全部未选中则取消父选中(并且取消第三种状态) + if(arr.every(function(val) { + return val==="0"; + })){ + parentInput.prop("checked",false); + parentInput.attr("eleTree-status","0"); + } + + var parentNode=eleNode.parents("[eletree-floor='"+(Number(eleNode.attr("eletree-floor"))-1)+"']"); + var parentCheckbox=parentNode.children(".eleTree-node-content").children("input[name='eleTree-node']").get(0); + var parentSiblingNode=parentNode.siblings(".eleTree-node"); + eleNode=parentNode; + inp=parentCheckbox; + siblingNode=parentSiblingNode; + } + }, + // 初始展开所有 + initialExpandAll: function(data,arr,floor,isMethodsExpandAll) { + var options=this.config; + var _self=this; + this.isRenderAllDom=true; + data.forEach(function(val,index) { + arr.push(index); + if(val[options.request.children] && val[options.request.children].length>0){ + var el=options.elem.children(".eleTree-node").eq(arr[0]).children(".eleTree-node-group"); + for(var i=1;i0) { + // 继续展开祖父层 + var f=function(eleP) { + if(options.autoExpandParent){ + eleP.parents(".eleTree-node").each(function(i,item) { + if($(item).data(options.request.key)){ + $(item).children(".eleTree-node-group").show().siblings(".eleTree-node-content").children(".eleTree-node-content-icon").children(".layui-icon").addClass("icon-rotate"); + } + }) + } + } + // 展开指定id项 + var id=el.parent(".eleTree-node").data(options.request.key); + if($.inArray(id,options.defaultExpandedKeys)!==-1){ + // 直接展开子节点 + el.show().siblings(".eleTree-node-content").children(".eleTree-node-content-icon").children(".layui-icon").addClass("icon-rotate"); + // 展开子项是否继续展开祖父项 + f(el.parent(".eleTree-node[data-"+options.customKey+"]")); + }else{ + // 如当前节点的子节点有展开项,则展开当前子节点的祖父层 + el.children(".eleTree-node").each(function(index, item) { + var id=$(item).data(options.request.key); + if($.inArray(id,options.defaultExpandedKeys)!==-1){ + f($(item)); + return false; + } + }) + } + } + }); + floor++; + _self.initialExpandAll(val[options.request.children],arr,floor,isMethodsExpandAll); + floor--; + } + // 重置数组索引 + arr.pop(); + }) + }, + // 选中单个节点 + checkedOneNode: function(nodeContent){ + var options=this.config; + var inp=nodeContent.children("input.eleTree-hideen").get(0); + $(inp).prop("checked",true).attr("eleTree-status","1"); + + if(options.checkStrictly) return; + + // 点击祖父层选中子孙层 + var childNode=nodeContent.siblings(".eleTree-node-group").find("input[name='eleTree-node']"); + childNode.prop("checked",true).attr("eleTree-status","1"); + + var eleNode=nodeContent.parent(".eleTree-node"); + var siblingNode=eleNode.siblings(".eleTree-node"); + // 点击子孙层选中祖父层(递归) + this.selectParents(inp,eleNode,siblingNode); + }, + // 初始默认选中 + defaultChecked: function(dataChecked) { + var options=this.config; + var _self=this; + var arr=dataChecked || options.defaultCheckedKeys; + if(arr.length===0){ + return false; + } + arr.forEach(function(val,index) { + var nodeContent=options.elem.find("[data-"+options.customKey+"='"+val+"']").children(".eleTree-node-content"); + nodeContent.length>0 && _self.checkedOneNode(nodeContent); + }) + this.checkboxInit(); + }, + // 自定义checkbox解析 + checkboxRender: function() { + var options=this.config; + options.elem.find(".eleTree-checkbox").remove(); + options.elem.find("input.eleTree-hideen[type=checkbox]").each(function(index,item){ + if($(item).hasClass("eleTree-disabled")){ + $(item).after('
'); + }else{ + $(item).after('
'); + } + + var checkbox=$(item).siblings(".eleTree-checkbox"); + if($(item).attr("eletree-status")==="1"){ + checkbox.addClass("eleTree-checkbox-checked"); + checkbox.children("i").addClass("layui-icon-ok").removeClass("eleTree-checkbox-line"); + }else if($(item).attr("eletree-status")==="0"){ + checkbox.removeClass("eleTree-checkbox-checked"); + checkbox.children("i").removeClass("layui-icon-ok eleTree-checkbox-line"); + }else if($(item).attr("eletree-status")==="2"){ + checkbox.addClass("eleTree-checkbox-checked"); + checkbox.children("i").removeClass("layui-icon-ok").addClass("eleTree-checkbox-line"); + } + + }) + }, + // 通过dom节点找对应数据 + reInitData: function(node) { + var options=this.config; + var i=node.index(); + var floor=Number(node.attr("eletree-floor")); + var arr=[]; // 节点对应的index + while (floor>=0) { + arr.push(i); + floor=floor-1; + node=node.parents("[eletree-floor='"+floor+"']"); + i=node.index(); + } + arr=arr.reverse(); + var oData=this.config.data; + // 当前节点的父节点数据 + var parentData=oData[arr[0]]; + // 当前节点的data数据 + var d = oData[arr[0]]; + for(var j = 1; j0){ + fn(data[obj.i][options.request.children]); + } + }else{ + callback(data,obj); + } + } + } + fn(options.data); + }, + updateKeyChildren: function(key,data) { + var options=this.config; + var node=options.elem.find("[data-"+options.customKey+"='"+key+"']"); + var floor=Number(node.attr("eletree-floor"))+1; + var _self=this; + + this.keySearchToOpera(key,function(d,obj) { + // 数据更新 + d[obj.i][options.request.children]=data; + // dom更新 + node.length!==0 && laytpl(TPL_ELEM(options,floor)).render(data, function(string){ + $(node).children(".eleTree-node-group").empty().append(string); + options.defaultExpandAll && $(node).children(".eleTree-node-group").show(); + }); + _self.unCheckNodes(true); + _self.defaultChecked(); + }); + }, + updateKeySelf: function(key,data) { + var options=this.config; + var node=options.elem.find("[data-"+options.customKey+"='"+key+"']").children(".eleTree-node-content"); + var floor=Number(node.attr("eletree-floor"))+1; + data[options.request.name] && node.children(".eleTree-node-content-label").text(data[options.request.name]); + data[options.request.disabled] && node.children(".eleTree-hideen").addClass("eleTree-disabled") + .siblings(".eleTree-checkbox").addClass("eleTree-checkbox-disabled"); + // 数据更新 + var getData=this.keySearchToOpera(key,function(d,obj) { + data[options.request.key]=d[obj.i][options.request.key]; + data[options.request.children]=d[obj.i][options.request.children]; + d[obj.i]=$.extend({},d[obj.i],data); + console.log(options.data); + }); + }, + remove: function(key) { + var options=this.config; + var node=options.elem.find("[data-"+options.customKey+"='"+key+"']"); + var pElem=node.parent(".eleTree-node-group"); + // 数据删除 + this.keySearchToOpera(key,function(data,obj) { + data.splice(obj.i,1); + obj.i--; + obj.len--; + + node.length!==0 && options.elem.find("[data-"+options.customKey+"='"+key+"']").remove(); + if(pElem.children(".eleTree-node").length===0){ + pElem.siblings(".eleTree-node-content").children(".eleTree-node-content-icon").children(".layui-icon").css("color", "transparent"); + } + }); + this.unCheckNodes(true); + this.defaultChecked(); + this.checkboxInit(); + }, + append: function(key,data) { + var options=this.config; + // 如果不传key,则直接添加到根节点 + if(typeof key==="object" && key!==null){ + data=key; + key=null; + } + if(key===null || key===""){ + options.data.push(data); + laytpl(TPL_ELEM(options,0,"0")).render([data], function(string){ + $(options.elem).append(string); + $(options.elem).children(".eleTree-node:last").show(); + }); + this.checkboxRender(); + return; + } + // 传key则添加到子节点 + var node=options.elem.find("[data-"+options.customKey+"='"+key+"']"); + var floor=Number(node.attr("eletree-floor"))+1; + // 数据更新 + this.keySearchToOpera(key,function(d,obj) { + if(d[obj.i][options.request.children]){ + d[obj.i][options.request.children].push(data); + }else{ + d[obj.i][options.request.children]=[data]; + } + var arr=d[obj.i][options.request.children]; + var icon=node.children(".eleTree-node-content").find(".eleTree-node-content-icon .layui-icon"); + // 添加之后长度为1,则原来没有三角,添加三角 + if(arr.length===1){ + icon.removeAttr("style"); + } + // 判断原来是否没有展开 + if(!icon.hasClass("icon-rotate")){ + var expandOnClickNode=options.expandOnClickNode?node.children(".eleTree-node-content"):node.children(".eleTree-node-content").children(".eleTree-node-content-icon"); + expandOnClickNode.trigger("click"); + } + // 判断节点是否已存在 + var isExist=false; + node.children(".eleTree-node-group").children(".eleTree-node").each(function(index,item){ + if(data[options.request.key]==$(item).data(options.request.key)){ + isExist=true; + } + }) + if(!isExist){ + var len=arr.length; + var eletreeStatus=node.children(".eleTree-node-content").children("input.eleTree-hideen").attr("eletree-status"); + eletreeStatus=eletreeStatus==="2" ? "0" : eletreeStatus; + node.length!==0 && laytpl(TPL_ELEM(options,floor,eletreeStatus)).render([arr[len-1]], function(string){ + node.children(".eleTree-node-group").append(string).show(); + }); + } + + }); + this.checkboxRender(); + }, + insertBefore: function(key,data) { + var options=this.config; + var node=options.elem.find("[data-"+options.customKey+"='"+key+"']"); + var floor=Number(node.attr("eletree-floor")); + // 数据更新 + this.keySearchToOpera(key,function(d,obj) { + d.splice(obj.i,0,data); + obj.i++; + obj.len++; + var eletreeStatus=node.parent(".eleTree-node-group").length===0 ? "0" : node.parent(".eleTree-node-group").parent(".eleTree-node") + .children(".eleTree-node-content").children("input.eleTree-hideen").attr("eletree-status"); + eletreeStatus=eletreeStatus==="2" ? "0" : eletreeStatus; + node.length!==0 && laytpl(TPL_ELEM(options,floor,eletreeStatus)).render([data], function(string){ + node.before(string).prev(".eleTree-node"); + }); + }); + this.checkboxRender(); + }, + insertAfter: function(key,data) { + var options=this.config; + var node=options.elem.find("[data-"+options.customKey+"='"+key+"']"); + var floor=Number(node.attr("eletree-floor")); + // 数据更新 + this.keySearchToOpera(key,function(d,obj) { + d.splice(obj.i+1,0,data); + obj.i++; + obj.len++; + var eletreeStatus=node.parent(".eleTree-node-group").length===0 ? "0" : node.parent(".eleTree-node-group").parent(".eleTree-node") + .children(".eleTree-node-content").children("input.eleTree-hideen").attr("eletree-status"); + eletreeStatus=eletreeStatus==="2" ? "0" : eletreeStatus; + node.length!==0 && laytpl(TPL_ELEM(options,floor,eletreeStatus)).render([data], function(string){ + $(node).after(string).next(".eleTree-node"); + }); + }); + this.checkboxRender(); + // if(!options.lazy){ + // if(!options.renderAfterExpand || options.defaultExpandAll || options.defaultExpandedKeys.length>0){ + // this.initialExpandAll(options.data,[],1); + // } + // } + }, + getChecked: function(leafOnly, includeHalfChecked) { + var options=this.config + ,el + ,arr=[]; + leafOnly=leafOnly || false; + includeHalfChecked=includeHalfChecked || false; + if(leafOnly){ + el=options.elem.find(".layui-icon.leaf-icon").parent(".eleTree-node-content-icon") + .siblings("input.eleTree-hideen[eletree-status='1']"); + }else if(includeHalfChecked){ + el=options.elem.find("input.eleTree-hideen[eletree-status='1'],input.eleTree-hideen[eletree-status='2']"); + }else{ + el=options.elem.find("input.eleTree-hideen[eletree-status='1']"); + } + el.each(function(index,item) { + var obj={}; + var id=$(item).parent(".eleTree-node-content").parent(".eleTree-node").data(options.request.key); + var label=$(item).siblings(".eleTree-node-content-label").text(); + obj[options.request.key]=id; + obj[options.request.name]=label; + obj.elem=item; + obj.othis=$(item).siblings(".eleTree-checkbox").get(0) + arr.push(obj); + }) + return arr; + }, + setChecked: function(arr,isReset) { + var options=this.config; + isReset=isReset || false; + if(isReset){ + this.unCheckNodes(); + options.defaultCheckedKeys=$.extend([],arr); + }else{ + this.unCheckNodes(true); + arr.forEach(function(val) { + if($.inArray(val,options.defaultCheckedKeys)===-1){ + options.defaultCheckedKeys.push(val); + } + }) + } + this.defaultChecked(); + }, + unCheckNodes: function(_internal) { + _internal=_internal || false; // _internal: 是否内部调用 + var options=this.config; + options.elem.find("input.eleTree-hideen[eletree-status='1'],input.eleTree-hideen[eletree-status='2']").each(function(index,item) { + $(item).attr("eletree-status","0").prop("checked",false); + // 如果外部的取消选中,则所有的记录全部取消 + if(!_internal){ + $(item).removeAttr("data-checked"); + } + }); + this.checkboxRender(); + }, + unCheckArrNodes: function(arr) { + var options=this.config; + var dataChecked=[]; + options.elem.find(".eleTree-hideen[eletree-status='1']").each(function(index,item) { + var id=$(item).parent(".eleTree-node-content").parent(".eleTree-node").data(options.request.key); + // 获取所有被选中项,并去除arr中包含的数据 + if(arr.some(function(val) { + return val==id; + })){ + // 如果id在arr数组中,则清除dom上面的checked数据 + $(item).removeAttr("data-checked"); + return; + } + dataChecked.push(id); + }) + + // 更新defaultCheckedKeys数据 + for(var j=0;j.layui-icon").addClass("icon-rotate") + .parent(".eleTree-node-content-icon").parent(".eleTree-node-content") + .siblings(".eleTree-node-group").show(); + } + options.elem.children(".eleTree-node").children(".eleTree-node-group").empty(); + this.initialExpandAll(options.data,[],1,true); + this.unCheckNodes(true); + this.defaultChecked(); + this.checkboxInit(); + }, + // 展开某节点的所有子节点 + expandNode: function(key) { + var options=this.config; + var parentsEl=options.elem.find("[data-"+options.customKey+"='"+key+"']"); + var isExpand=parentsEl.children(".eleTree-node-content").find(".eleTree-node-content-icon>.layui-icon.layui-icon-triangle-r").hasClass("icon-rotate"); + var parentGroup=parentsEl.find(".eleTree-node-group"); + // 判断是否已经展开 + if(isExpand) return false; + // 判断子节点是否已经渲染(目前只判断所有子节点是否已经全部渲染,而不是当前子节点是否全部渲染) + if(this.isRenderAllDom){ + parentGroup.show("fast"); + parentsEl.find(".layui-icon.layui-icon-triangle-r").addClass("icon-rotate"); + return false; + } + if(options.lazy) return hint.error("展开所有子节点方法暂不支持懒加载"); + + var data=this.reInitData(parentsEl); + var d=data.currentData; + var floor=Number(parentsEl.attr("eletree-floor"))+1; + var fn=function(data,arr,floor) { + data.forEach(function(val,index) { + arr.push(index); + if(val[options.request.children] && val[options.request.children].length>0){ + var el=parentsEl.children(".eleTree-node-group"); + for(var i=1;i.layui-icon.layui-icon-triangle-r").hasClass("icon-rotate"); + // 判断是否已经合并 + if(!isExpand) return false; + parentsEl.find(".eleTree-node-group").hide("fast"); + parentsEl.find(".layui-icon.layui-icon-triangle-r").removeClass("icon-rotate"); + }, + // 切换某节点的所有子节点的展开合并状态 + toggleExpandNode: function(key) { + var options=this.config; + var parentsEl=options.elem.find("[data-"+options.customKey+"='"+key+"']"); + var isExpand=parentsEl.children(".eleTree-node-content").find(".eleTree-node-content-icon>.layui-icon.layui-icon-triangle-r").hasClass("icon-rotate"); + if(isExpand){ + this.unExpandNode(key); + }else{ + this.expandNode(key); + } + }, + // 节点事件 + nodeEvent: function() { + var _self=this; + var options=this.config; + // 节点被点击的回调事件 + options.elem.on("click",".eleTree-node-content",function(e) { + var eleNode=$(this).parent(".eleTree-node"); + var eleTreeNodeContent=eleNode.children(".eleTree-node-content"); + // 添加active背景 + if(_self.prevClickEle) _self.prevClickEle.removeClass("eleTree-node-content-active"); + if(options.highlightCurrent) eleTreeNodeContent.addClass("eleTree-node-content-active"); + _self.prevClickEle=eleTreeNodeContent; + + $("#tree-menu").hide().remove(); + layui.event.call(eleNode, MOD_NAME, 'nodeClick('+ _self.filter +')', { + node: eleNode, + data: _self.reInitData(eleNode), + event: e + }); + }) + // 节点右键的回调事件 + options.elem.on("contextmenu",".eleTree-node-content",function(e) { + var eleNode=$(this).parent(".eleTree-node"); + layui.event.call(eleNode, MOD_NAME, 'nodeContextmenu('+ _self.filter +')', { + node: eleNode, + data: _self.reInitData(eleNode), + event: e + }); + }) + // 节点被拖拽的回调事件 + options.draggable && options.elem.on("mousedown",".eleTree-node-content",function(e) { + var time=0; + var eleNode=$(this).parent(".eleTree-node"); + var eleFloor=Number(eleNode.attr("eletree-floor")); + var groupNode=eleNode.parent(".eleTree-node-group"); + + e.stopPropagation(); + options.elem.css("user-select","none"); + var cloneNode=eleNode.clone(true); + var temNode=eleNode.clone(true); + + var x=e.clientX-options.elem.offset().left; + var y=e.clientY-options.elem.offset().top; + options.elem.append(cloneNode); + cloneNode.css({ + "display": "none", + "opacity": 0.7, + "position": "absolute", + "background-color": "#f5f5f5", + "width": "100%" + }) + + var currentData=_self.reInitData(eleNode); + + var isStop=false; + + $(document).on("mousemove",function(e) { + // t为了区别click事件 + time++; + if(time>2){ + var xx=e.clientX-options.elem.offset().left+10; + var yy=e.clientY-options.elem.offset().top+$(document).scrollTop()-5; // 加上浏览器滚动高度 + + cloneNode.css({ + display: "block", + left: xx+"px", + top: yy+"px" + }) + } + }).on("mouseup",function(e) { + $(document).off("mousemove").off("mouseup"); + var target=$(e.target).parents(".eleTree-node").eq(0); + cloneNode.remove(); + options.elem.css("user-select","auto"); + + + // 当前点击的是否时最外层 + var isCurrentOuterMost=eleNode.parent().get(0).isEqualNode(options.elem.get(0)) + // 目标是否时最外层 + var isTargetOuterMost=$(e.target).get(0).isEqualNode(options.elem.get(0)) + if(isTargetOuterMost){ + target=options.elem; + } + // 判断是否超出边界 + if(target.parents(options.elem).length===0 && !isTargetOuterMost){ + return; + } + // 判断初始与结束是否是同一个节点 + if(target.get(0).isEqualNode(eleNode.get(0))){ + return; + } + // 判断是否是父节点放到子节点 + var tFloor=target.attr("eletree-floor"); + var isInChild=false; + eleNode.find("[eletree-floor='"+tFloor+"']").each(function() { + if(this.isEqualNode(target.get(0))){ + isInChild=true; + } + }) + if(isInChild){ + return; + } + + var targetData=_self.reInitData(target); + layui.event.call(target, MOD_NAME, 'nodeDrag('+ _self.filter +')', { + current: { + node: eleNode, + data: currentData + }, + target: { + node: target, + data: targetData + }, + stop: function() { + isStop=true; + } + }); + // 拖拽是否取消 + if(isStop){ + return false; + } + + // 数据更改 + var currList=currentData.parentData.data[options.request.children] + var currIndex=currentData.parentData.childIndex + var currData=currentData.currentData; + var tarData=targetData.currentData; + // 当前是否是最外层 + isCurrentOuterMost ? options.data.splice(currIndex,1) : currList.splice(currIndex,1) + // 目标是否是最外层 + isTargetOuterMost ? options.data.push(currData) : (function() { + !tarData[options.request.children] ? tarData[options.request.children]=[] : ""; + tarData[options.request.children].push(currData); + })() + + // dom互换 + eleNode.remove(); + var floor=null; + // 最外层判断 + if(isTargetOuterMost){ + target.append(temNode); + floor=0; + }else{ + target.children(".eleTree-node-group").append(temNode); + floor=Number(target.attr("eletree-floor"))+1; + } + // 加floor和padding + temNode.attr("eletree-floor",String(floor)); + temNode.children(".eleTree-node-content").css("padding-left",floor*options.indent+"px"); + // 计算线条的left + if(options.showLine){ + // 判断目标是否是最外层,是的话隐藏线条 + if(floor===0){ + temNode.children(".eleTree-node-verticalline,.eleTree-node-horizontalline").hide(); + }else{ + temNode.children(".eleTree-node-verticalline,.eleTree-node-horizontalline").css("left",options.indent*(floor-1)+9+"px").show(); + } + } + // 通过floor差值计算子元素的floor + var countFloor=eleFloor-floor; + temNode.find(".eleTree-node").each(function(index,item) { + var f=Number($(item).attr("eletree-floor"))-countFloor; + $(item).attr("eletree-floor",String(f)); + $(item).children(".eleTree-node-content").css("padding-left",f*options.indent+"px"); + options.showLine && $(item).children(".eleTree-node-verticalline,.eleTree-node-horizontalline").css("left",options.indent*(f-1)+9+"px").show(); + }) + // 原dom去三角 + var leaf=groupNode.children(".eleTree-node").length===0; + leaf && groupNode.siblings(".eleTree-node-content") + .children(".eleTree-node-content-icon").children(".layui-icon") + .removeClass("icon-rotate").css("color","transparent"); + // 当前的增加三角 + var cLeaf=target.children(".eleTree-node-group").children(".eleTree-node").length===1; + cLeaf && target.children(".eleTree-node-content") + .children(".eleTree-node-content-icon").children(".layui-icon") + .addClass("icon-rotate").removeAttr("style"); + // 判断当前是否需要显示 + var isShowNode=target.children(".eleTree-node-content").find(".layui-icon").hasClass("icon-rotate"); + (isTargetOuterMost || isShowNode) && target.children(".eleTree-node-group").show(); + + _self.unCheckNodes(true); + _self.defaultChecked(); + _self.checkboxInit(); + }) + }) + }, + rightClickMenu: function() { + var _self=this; + var options=this.config; + if(options.contextmenuList.length<=0){ + return; + } + $(document).on("click",function() { + $("#tree-menu").hide().remove(); + }); + + var customizeMenu=[]; // 用户自定义的 + var internalMenu=["copy","add","add.async","insertBefore","insertAfter","append","edit","edit.async","remove","remove.async"]; // 系统自带的 + var customizeStr=''; + options.contextmenuList.forEach(function(val) { + if($.inArray(val,internalMenu)===-1){ + customizeMenu.push(val); + customizeStr+='
  • '+(val.text || val)+'
  • '; + } + }) + var menuStr=['
      ' + ,$.inArray("copy",options.contextmenuList)!==-1?'
    • 复制
    • ':'' + ,($.inArray("add",options.contextmenuList)!==-1 || $.inArray("add.async",options.contextmenuList)!==-1)?'
    • 新增
    • '+ + '
    • 插入节点前
    • '+ + '
    • 插入节点后
    • '+ + '
    • 插入子节点
    • ' : "" + ,($.inArray("edit",options.contextmenuList)!==-1 || $.inArray("edit.async",options.contextmenuList)!==-1)?'
    • 修改
    • ':'' + ,($.inArray("remove",options.contextmenuList)!==-1 || $.inArray("remove.async",options.contextmenuList)!==-1)?'
    • 删除
    • ':'' + ,customizeStr + ,'
    '].join(""); + this.treeMenu=$(menuStr); + options.elem.off("contextmenu").on("contextmenu",".eleTree-node-content",function(e) { + var that=this; + e.stopPropagation(); + e.preventDefault(); + // 添加active背景 + if(_self.prevClickEle) _self.prevClickEle.removeClass("eleTree-node-content-active"); + $(this).addClass("eleTree-node-content-active"); + var eleNode=$(this).parent(".eleTree-node"); + var nodeData=_self.reInitData(eleNode); + + // 菜单位置 + $(document.body).after(_self.treeMenu); + $("#tree-menu").find("li.append,li.insertAfter,li.insertBefore").hide(); + $("#tree-menu").find(":not(li.append,li.insertAfter,li.insertBefore)").show(); + $("#tree-menu").css({ + left: e.clientX+$(document).scrollLeft(), + top: e.clientY+$(document).scrollTop() + }).show(); + // 复制 + $("#tree-menu li.copy").off().on("click",function() { + var el = $(that).children(".eleTree-node-content-label").get(0); + var selection = window.getSelection(); + var range = document.createRange(); + range.selectNodeContents(el); + selection.removeAllRanges(); + selection.addRange(range); + document.execCommand('Copy', 'false', null); + selection.removeAllRanges(); + }); + // 新增 + $("#tree-menu li.add").off().on("click",function(e) { + e.stopPropagation(); + $(this).hide().siblings("li:not(.append,.insertAfter,.insertBefore)").hide(); + $(this).siblings(".append,li.insertAfter,li.insertBefore").show(); + }) + // 添加的默认数据 + var obj={}; + obj[options.request.key]=Date.now(); + obj[options.request.name]="未命名"+_self.nameIndex; + if(options.lazy){ + obj[options.request.isLeaf]=true; + } + + var arr=["Append","InsertBefore","InsertAfter"]; + arr.forEach(function(val) { + var s=val[0].toLocaleLowerCase()+val.slice(1,val.length); + $("#tree-menu li."+s).off().on("click",function(e) { + var node=$(that).parent(".eleTree-node"); + var key=node.data(options.request.key); + var isStop=false; + var s=val[0].toLocaleLowerCase()+val.slice(1,val.length); + // 每次只能添加一条数据,不可以批量添加 + _self[s](key,obj); + var nodeArr=[]; + node.children(".eleTree-node-group").children(".eleTree-node").each(function(i,itemNode) { + nodeArr.push(itemNode); + }) + node.siblings(".eleTree-node").each(function(i,itemNode) { + nodeArr.push(itemNode); + }) + $.each(nodeArr, function(i,itemNode) { + if(obj[options.request.key]===$(itemNode).data(options.request.key)){ + var label=$(itemNode).children(".eleTree-node-content").children(".eleTree-node-content-label").hide(); + var text=label.text(); + var inp=""; + label.after(inp); + + label.siblings(".eleTree-node-content-input").focus().select().off().on("blur",function() { + var v=$(this).val(); + obj[options.request.name]=v; + var inpThis=this; + + layui.event.call(node, MOD_NAME, 'node'+val+'('+ _self.filter +')', { + node: node, + data: nodeData.currentData, + newData: obj, + // 重新设置数据 + setData: function(o) { + // obj[options.request.key]=Date.now(); + obj[options.request.name]=v; + if(options.lazy){ + obj[options.request.isLeaf]=true; + } + var newObj=$.extend({},obj,o); + this.newData=newObj; + // 修改数据 + var d=_self.reInitData($(itemNode)).currentData; + d[options.request.name]=newObj[options.request.name]; + d[options.request.key]=newObj[options.request.key]; + // 修改dom + $(inpThis).siblings(".eleTree-node-content-label").text(newObj[options.request.name]).show(); + $(itemNode).attr("data-"+options.customKey,newObj[options.request.key]); // 改变页面上面的显示的key,之后可以获取dom + $(itemNode).data(options.request.key,newObj[options.request.key]); // 改变data数据,之后可以通过data获取key + $(inpThis).remove(); + + _self.nameIndex++; + isStop=true; + }, + // 停止添加 + stop: function() { + isStop=true; + this.newData={}; + _self.remove(obj[options.request.key]); + } + }); + + // 不是异步添加 + if($.inArray("add.async",options.contextmenuList)===-1){ + if(isStop) return; + // 修改数据 + _self.reInitData($(itemNode)).currentData[options.request.name]=v; + // 修改dom + $(this).siblings(".eleTree-node-content-label").text(v).show(); + $(this).remove(); + + _self.nameIndex++; + } + }).on("mousedown",function(e) { + // 防止input拖拽 + e.stopPropagation(); + }).on("click",function(e) { + e.stopPropagation(); + }) + } + }) + }) + }) + + // 编辑 + $("#tree-menu li.edit").off().on("click",function(e) { + e.stopPropagation(); + $("#tree-menu").hide().remove(); + var node=$(that).parent(".eleTree-node"); + var key=node.data(options.request.key); + var label=$(that).children(".eleTree-node-content-label").hide(); + var text=label.text(); + var inp=""; + label.after(inp); + label.siblings(".eleTree-node-content-input").focus().select().off().on("blur",function() { + var val=$(this).val(); + var isStop=false; + var inpThis=this; + layui.event.call(node, MOD_NAME, 'nodeEdit('+ _self.filter +')', { + node: node, + value: val, + data: nodeData.currentData, + // 停止添加 + stop: function() { + isStop=true; + $(inpThis).siblings(".eleTree-node-content-label").show(); + $(inpThis).remove(); + }, + async: function() { + if(isStop) return; + // 修改数据 + _self.reInitData(eleNode).currentData[options.request.name]=val; + // 修改dom + $(inpThis).siblings(".eleTree-node-content-label").text(val).show(); + $(inpThis).remove(); + } + }); + // 不是异步 + if($.inArray("edit.async",options.contextmenuList)===-1){ + if(isStop) return; + // 修改数据 + _self.reInitData(eleNode).currentData[options.request.name]=val; + // 修改dom + $(this).siblings(".eleTree-node-content-label").text(val).show(); + $(this).remove(); + } + + }).on("mousedown",function(e) { + // 防止input拖拽 + e.stopPropagation(); + }) + }) + // 删除 + $("#tree-menu li.remove").off().on("click",function(e) { + var node=$(that).parent(".eleTree-node"); + var key=node.data(options.request.key); + var isStop=false; + layui.event.call(node, MOD_NAME, 'nodeRemove('+ _self.filter +')', { + node: node, + data: nodeData.currentData, + // 停止添加 + stop: function() { + isStop=true; + return this; + }, + async: function() { + if(isStop) return; + _self.remove(key); + return this; + } + }); + // 不是异步 + if($.inArray("remove.async",options.contextmenuList)===-1){ + if(isStop) return; + _self.remove(key); + } + + }) + + // 自定义菜单回调 + customizeMenu.forEach(function(val) { + var text=val.eventName || val; + $("#tree-menu li."+text).off().on("click",function() { + var node=$(that).parent(".eleTree-node"); + var isStop=false; + layui.event.call(node, MOD_NAME, 'node'+text.replace(text.charAt(0),text.charAt(0).toUpperCase())+'('+ _self.filter +')', { + node: node, + data: nodeData.currentData, + }); + }); + }) + + _self.prevClickEle=$(this); + }) + }, + search: function(value) { + var options=this.config; + if(!options.searchNodeMethod || typeof options.searchNodeMethod !== "function"){ + return; + } + var data=options.data; + // 数据递归 + var traverse=function(data) { + data.forEach(function(val,index) { + // 所有查找到的节点增加属性 + val.visible=options.searchNodeMethod(value,val); + if(val[options.request.children] && val[options.request.children].length>0){ + traverse(val[options.request.children]); + } + //如果当前节点属性为隐藏,判断其子节点是否有显示的,如果有,则当前节点改为显示 + if(!val.visible){ + var childSomeShow = false; + if(val[options.request.children] && val[options.request.children].length>0){ + childSomeShow=val[options.request.children].some(function(v,i) { + return v.visible; + }) + } + val.visible = childSomeShow; + } + // 通过节点的属性,显示隐藏各个节点,并添加删除搜索类 + var el=options.elem.find("[data-"+options.customKey+"='"+val[options.request.key]+"']"); + if(val.visible){ + el.removeClass("eleTree-search-hide"); + // 判断父节点是否展开,如果父节点没有展开,则子节点也不要显示 + var parentEl=el.parent(".eleTree-node-group").parent(".eleTree-node"); + var isParentOpen=parentEl.children(".eleTree-node-content").children(".eleTree-node-content-icon").children(".layui-icon.layui-icon-triangle-r").hasClass("icon-rotate") + if((parentEl.length>0 && isParentOpen) || parentEl.length===0){ + el.show(); + } + }else{ + el.hide().addClass("eleTree-search-hide"); + } + // 删除子层属性 + // if(val[options.request.children] && val[options.request.children].length>0){ + // val[options.request.children].forEach(function(v,i) { + // delete v.visible; + // }) + // } + }) + } + traverse(data); + // 删除最外层属性 + var arr=data.map(function (val) { + var v=val.visible; + // delete val.visible; + return v; + }); + var isNotext=options.elem.children(".eleTree-noText"); + // 如果第一层的所有的都隐藏,则显示文本 + if(arr.every(function(v) { + return v===false; + })){ + if(isNotext.length===0){ + laytpl(TPL_NoText()).render(options, function(string){ + options.elem.append(string); + }); + } + }else{ + isNotext.remove(); + } + }, + getAllNodeData: function() { + var options=this.config; + return options.data; + } + } + + exports(MOD_NAME,eleTree); +}) diff --git a/docs/plugins/index.js b/docs/plugins/index.js new file mode 100644 index 0000000..75f3757 --- /dev/null +++ b/docs/plugins/index.js @@ -0,0 +1,2 @@ +import './eleTree/eleTree.js' +import './eleTree/eleTree.css' \ No newline at end of file diff --git a/docs/router.js b/docs/router.js index 395a0e3..2b00aa6 100644 --- a/docs/router.js +++ b/docs/router.js @@ -1,4 +1,5 @@ import Component from './components/component.vue'; +import { version } from '../package.json' function importVue(path) { return r => require.ensure([], () => r(require(`./pages${path}.vue`))); @@ -20,7 +21,7 @@ export default [{ redirect: '/component', }, { path: '/changelog', - name: '更新日志', + name: '更新日志 v' + version, component: importVue('/changelog'), }, { path: '/add', @@ -84,12 +85,25 @@ export default [{ { path: '/example-custom/ZM04', name: '远程搜索', component: importMd('/ZM04') }, { path: '/example-custom/ZM05', name: '动态数据', component: importMd('/ZM05') }, { path: '/example-custom/ZM06', name: '弹框中的多选', component: importMd('/ZM06') }, - // { path: '/example-custom/ZTEST', name: '测试', component: importMd('/ZTEST') }, + ] + }, { + path: '/example-plugin', + name: '拓展中心', + redirect: '/example-plugin/ZP01', + component: Component, + children: [ + { path: '/example-plugin/ZP01', name: '下拉自定义', component: importMd('/ZP01') }, + { path: '/example-plugin/ZP02', name: '下拉树', component: importMd('/ZP02') }, ] }, { path: '/question', name: '常见问题', component: importMd('/question'), - }, + }, { + path: '/test', + name: '测试', + hidden: true, + component: importMd('/ZTEST'), + }, ]; diff --git a/package.json b/package.json index 644d109..2f2733c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xm-select", - "version": "1.0.9", + "version": "1.0.10", "description": "始于Layui的select多选解决方案", "main": "index.js", "scripts": { diff --git a/src/components/common/util.js b/src/components/common/util.js index 2707ad0..d24736a 100644 --- a/src/components/common/util.js +++ b/src/components/common/util.js @@ -146,50 +146,27 @@ export function IEVersion() { } } -export function filterGroupOption(arr, data, prop){ - const { children, optgroup } = prop; - data.filter(item => !item[optgroup]).forEach(item => { - let child = item[children]; - if(isArray(child)){ - filterGroupOption(arr, child, children, optgroup); - }else{ - arr.push(item); - } - }); -} - -export function findSelected(arr, data, prop){ - const { selected, children, optgroup } = prop; - data.filter(item => !item[optgroup]).forEach(item => { - let child = item[children]; - if(isArray(child)){ - findSelected(arr, child, prop); - }else{ - item[selected] && (arr.push(item)); - } - }); -} - -export function addGroupLabel(arr, prop){ - const { disabled, children, optgroup, value } = prop; - let group; - for(let i = 0; i < arr.length; i++){ - let item = arr[i]; - if(item[optgroup]){ - group = item; - group.__value = []; - continue; - } - let child = item[children]; - if(child && child.length > 0){ - group = null; - item.__value = child.filter(c => !c[disabled]).map(c => c[value]); - continue; - } - if(!group || item[disabled]){ - continue; - } - group.__value.push(item[value]); - } - return arr; +export function exchangeOptionsData(arr, { prop }){ + let { disabled, children, optgroup, value } = prop; + let newArr = [], group; + for(let i = 0; i < arr.length; i++){ + let item = arr[i]; + newArr.push(item); + if(item[optgroup]){ + group = item; + item[children] = []; + continue; + } + let child = item[children]; + if(isArray(child)){ + group = null; + item[optgroup] = true; + child.forEach(c => newArr.push(c)); + continue; + } + if(group){ + group[children].push(item); + } + } + return newArr; } diff --git a/src/components/config/options.js b/src/components/config/options.js index 9a32943..661e8e3 100644 --- a/src/components/config/options.js +++ b/src/components/config/options.js @@ -9,6 +9,8 @@ export default function (lan = 'zn') { return { //多选数据 data: [], + //自定义数据 + content: '', //表单提交的name name: 'select', //尺寸 diff --git a/src/components/core/index.js b/src/components/core/index.js index 3375927..4f0b4b9 100644 --- a/src/components/core/index.js +++ b/src/components/core/index.js @@ -1,6 +1,6 @@ import { h, Component, render } from '@/components/preact' import Framework from '@/components/element/framework' -import { selector, warn, listenerClose, watch, safety, isArray, deepMerge } from '@/components/common/util' +import { selector, warn, listenerClose, isArray, deepMerge, exchangeOptionsData } from '@/components/common/util' import defaultOptions from '@/components/config/options' @@ -46,6 +46,18 @@ class xmOptions { warn(`没有找到渲染对象: ${options.el}, 请检查`) return ; } + //判断data的数据类型 + let optionsData = this.options.data || []; + if(typeof(optionsData) === 'function'){ + optionsData = optionsData(); + this.options.data = optionsData; + } + if(!isArray(optionsData)){ + warn(`data数据必须为数组类型, 不能是${ typeof(data) }类型`) + return ; + } + //调整数据结构 + this.options.data = exchangeOptionsData(optionsData, this.options); const onRef = (ref) => childs[this.options.el] = ref; @@ -93,7 +105,7 @@ class xmOptions { * 获取多选选中的数据 */ getValue(type){ - let arr = safety(childs[this.options.el].state.sels); + let arr = deepMerge([], childs[this.options.el].state.sels); if(type === 'name'){ return arr.map(item => item[this.options.prop.name]); @@ -114,12 +126,12 @@ class xmOptions { /** * 设置多选数据 */ - setValue(sels, show){ + setValue(sels, show, listenOn = false){ if(!isArray(sels)){ warn('请传入数组结构...') return ; } - childs[this.options.el].value(sels, show); + childs[this.options.el].value(sels, show, listenOn); return this; } @@ -134,7 +146,7 @@ class xmOptions { childs[this.options.el].append(sels); return this; } - + /** * 删除赋值 */ @@ -146,7 +158,7 @@ class xmOptions { childs[this.options.el].del(sels); return this; } - + /** * 闪烁警告边框 */ diff --git a/src/components/element/framework.js b/src/components/element/framework.js index 03463d6..5a7abe4 100644 --- a/src/components/element/framework.js +++ b/src/components/element/framework.js @@ -1,10 +1,11 @@ import { h, Component, render } from '@/components/preact' -import { checkUserAgent, isFunction, toNum, filterGroupOption, findSelected, mergeArr } from '@/components/common/util' +import { checkUserAgent, isFunction, toNum, mergeArr } from '@/components/common/util' //渲染类 import Tips from './tips'; import Label from './label'; import General from './model/general'; +import Custom from './model/custom'; /** * 框架渲染类, 渲染基础的外边框 + 属性变化监听 @@ -28,14 +29,13 @@ class Framework extends Component{ } findValue(data){ - let list = []; - findSelected(list, data, this.props.prop); - return list; + const { selected } = this.props.prop; + return data.filter(item => item[selected] === true); } - resetSelectValue(sels = [], change = [], isAdd){ + resetSelectValue(sels = [], change = [], isAdd, listenOn = true){ let on = this.props.on; - if(isFunction(on)){ + if(isFunction(on) && this.prepare && listenOn){ on({ arr: sels, change, isAdd }); } this.setState({ sels }); @@ -45,20 +45,19 @@ class Framework extends Component{ this.setState({ data }); } - value(sels, show){ + value(sels, show, listenOn){ if(show !== false && show !== true){ show = this.state.show; } let changeData = this.exchangeValue(sels); - this.resetSelectValue(changeData, changeData, true); + this.resetSelectValue(changeData, changeData, true, listenOn); this.setState({ show }) } exchangeValue(sels){ + const { optgroup, value } = this.props.prop; let data = this.state.data; - let value = this.props.prop.value; - let list = []; - filterGroupOption(list, data, this.props.prop); + let list = data.filter(item => !item[optgroup]); return sels.map(sel => typeof sel === 'object' ? sel[value] : sel).map(val => list.find(item => item[value] == val)).filter(a => a); } @@ -82,7 +81,7 @@ class Framework extends Component{ auto(arr){ let value = this.props.prop.value; - let sels = arr.filter(v => this.state.sels.findIndex(item => item[value] === v) != -1); + let sels = arr.filter(v => this.state.sels.findIndex(item => item[value] === v[value]) != -1); sels.length == arr.length ? this.del(arr) : this.append(arr); } @@ -174,6 +173,10 @@ class Framework extends Component{ } } + componentDidMount(){ + this.prepare = true; + } + render(config, { sels, show }) { const { tips, theme, prop, style, radio, repeat, clickClose, on, max, maxMethod } = config; const borderStyle = { borderColor: theme.color }; @@ -194,7 +197,7 @@ class Framework extends Component{ this.updateBorderColor('') }, 300); } - + //右边下拉箭头的变化class const iconClass = show ? 'xm-icon xm-icon-expand' : 'xm-icon'; //提示信息的属性 @@ -256,7 +259,11 @@ class Framework extends Component{