Skip to content

Commit

Permalink
feat(all) : 新增下拉树和单选树
Browse files Browse the repository at this point in the history
新增了下拉树和单选树,以及部分配置
  • Loading branch information
wangjie committed Nov 19, 2018
1 parent b7a4bab commit 0d077d9
Show file tree
Hide file tree
Showing 33 changed files with 446 additions and 217 deletions.
131 changes: 101 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,24 @@ layui自身提供一个tree树形菜单,但是并不适用于权限控制中

![功能演示](https://raw.githubusercontent.com/wangerzi/layui-authtree/master/screenGIF.gif)

##### 在线演示:

[http://authtree.wj2015.com/](http://authtree.wj2015.com/)

## 期望收集

- [x] 全选-全不选功能
- [x] 显示到某层的功能(展开全部/收起全部)
- [x] 最新添加/最新取消节点
- [ ] 样例添加搜索功能
- [x] 普通列表转权限树功能
- [ ] 权限树以适合单选框的形式转义(半完成)
- [x] 权限树以适合单选框的形式转义
- [x] 配置新增“取消级联选中”功能
- [x] 树节点双击打开/关闭
- [x] 支持对勾选的进行展开,而不是整个树
- [ ] 新增toolbar,用于扩展权限控制的操作
- [ ] 部分选择权限的时候,他的父级前面不打对号,换个其他符号
- [x] 可以配置单选/多选

## BUG收集
- [ ] 插件BUG - 数据量达到150+会比较卡
Expand Down Expand Up @@ -111,41 +116,46 @@ layui.use(['jquery', 'authtree', 'form', 'layer'], function(){

## 函数列表

| 函数名 | 描述 |
| --------------------------- | ------------------------------------------------------------ |
| **render(dst, trees, opt)** | 初始化一棵权限树 |
| **listConvert(list, opt)** | 将普通列表无限递归转换为树(opt参数可配置项,请参考下方参数配置) |
| getMaxDept(dst) | 获取树的最大深度 |
| **checkAll(dst)** | 全选所有节点 |
| **uncheckAll(dst)** | 取消选中所有节点 |
| showAll(dst) | 显示整颗树 |
| closeAll(dst) | 关闭整颗树 |
| toggleAll(dst) | 树的显示/关闭切换 |
| showDept(dst, dept) | 显示此树到第 dept 层 |
| closeDept(dst, dept) | 第 dept 层之后全部关闭 |
| getLeaf(dst) | 获取叶子节点(form.on()需延迟获取) |
| getAll(dst) | 获取所有节点数据(form.on()需延迟获取) |
| getLastChecked(dst) | 最新选中节点数据(之前未选-现在选中)(form.on()需延迟获取) |
| getChecked(dst) | 获取所有选中节点的数据(form.on()需延迟获取) |
| getLastNotChecked(dst) | 最新取消(之前选中-现在未选)(form.on()需延迟获取) |
| getNotChecked(dst) | 获取未选中数据(form.on()需延迟获取) |
| 函数名 | 描述 |
| ---------------------------- | ------------------------------------------------------------ |
| **render(dst, trees, opt)** | 初始化一棵权限树 |
| **listConvert(list, opt)** | 将普通列表无限递归转换为树(opt参数可配置项,请参考下方参数配置) |
| treeConvertSelect(tree, opt) | 将树转为单选树,如果后台返回列表数据,可以先 listConvert() 转换为树 |
| getMaxDept(dst) | 获取树的最大深度 |
| **checkAll(dst)** | 全选所有节点 |
| **uncheckAll(dst)** | 取消选中所有节点 |
| showAll(dst) | 显示整颗树 |
| closeAll(dst) | 关闭整颗树 |
| toggleAll(dst) | 树的显示/关闭切换 |
| showDept(dst, dept) | 显示此树到第 dept 层 |
| closeDept(dst, dept) | 第 dept 层之后全部关闭 |
| getLeaf(dst) | 获取叶子节点(form.on()需延迟获取) |
| getAll(dst) | 获取所有节点数据(form.on()需延迟获取) |
| getLastChecked(dst) | 最新选中节点数据(之前未选-现在选中)(form.on()需延迟获取) |
| getChecked(dst) | 获取所有选中节点的数据(form.on()需延迟获取) |
| getLastNotChecked(dst) | 最新取消(之前选中-现在未选)(form.on()需延迟获取) |
| getNotChecked(dst) | 获取未选中数据(form.on()需延迟获取) |

## 参数配置

##### render 参数配置

`render()` 函数是本插件的核心方法,调用 `render(dst, trees, opt)` 函数时,opt 中可以传递的参数如下

| 参数名 | 描述 | 默认 |
| --------------- | ------------------------------------------------------------ | --------- |
| **inputname** | 上传上去的 input 表单的name | menuids[] |
| openchecked | 自动显示选中的节点 | false |
| layfilter | input 元素的 layfilter,可通过 authtree.on('change(layfilter)') 监听 | checkauth |
| openall | 是否初始化显示全部 | false |
| **dblshow** | 双击展开节点 | false |
| dbltimeout | 双击展开节点延迟(最好不要超过300,不然单击延迟会比较高) | 180 |
| **autochecked** | 选中节点后,是否自动选中直属父级并且选中所有子节点 | true |
| **autoclose** | 取消节点选中后,是否自动取消父级选中(当兄弟节点均为选中时) | true |
| 参数名 | 描述 | 默认 |
| ---------------- | ------------------------------------------------------------ | --------- |
| **inputname** | 上传上去的 input 表单的name | menuids[] |
| openchecked | 自动显示选中的节点 | false |
| layfilter | input 元素的 layfilter,可通过 authtree.on('change(layfilter)') 监听 | checkauth |
| openall | 是否初始化显示全部 | false |
| **dblshow** | 双击展开节点 | false |
| dbltimeout | 双击展开节点延迟(最好不要超过300,不然单击延迟会比较高) | 180 |
| **autochecked** | 选中节点后,是否自动选中直属父级并且选中所有子节点 | true |
| **autoclose** | 取消节点选中后,是否自动取消父级选中(当兄弟节点均为选中时) | true |
| checkType | 选择表单类型,checkbox: 多选,radio: 单选 | checkbox |
| openIconContent | 展开的前显字符配置(默认是方向向下的三角形,就像这样▼) | &\#xe625; |
| closeIconContent | 折叠的前显字符配置(默认是方向向右的三角形,就像这样▶) | &\#xe623; |
| prefixChildStr | 有子节点的前显字符配置 | ├─ |

> **自动选中父级节点和自动取消父级选中 用法描述:**
>
Expand All @@ -157,6 +167,64 @@ layui.use(['jquery', 'authtree', 'form', 'layer'], function(){
>
> 两种状态一般同时开启,或者同时关闭,不然可能体验有点奇怪
##### treeConvertSelect参数配置

`treeConverSelect` 是用于树转换为『单选树』的方法,其中 opt 参数如下

| 参数名 | 描述 | 默认值 |
| ---------------- | ------------------------------------------------------------ | ------ |
| childKey | 子节点列表的Key | list |
| prefixChildStr | 有子节点的名称显示前缀 | ├─ |
| prefixNoChildStr | 没有子节点的名称显示前缀 ||
| prefixDeptStr | 树的深度影响的子节点显示前缀 | |
| prefixFirstEmpty | 如果第一列就存在没有子节点的情况,加的特殊前缀(占位用,正常使用不需要修改) | |

**调用样例**

```html
<script type="text/javascript">
$.ajax({
url: 'tree.json',
dataType: 'json',
success: function(res){
// 更多传入参数及其具体意义请查看文档
var selectList = authtree.treeConvertSelect(res.data.trees, {
childKey: 'list',
});
console.log(selectList);
// 渲染单选框
var string = laytpl($('#LAY-auth-tree-convert-select').html()).render({
list: selectList,
});
$('#LAY-auth-tree-convert-select-dom').html(string);
form.render('select');
// TODO::form.on('select(LAY-FILTER)')监听选中
},
error: function(xml, errstr, err) {
layer.alert(errstr+',获取样例数据失败,请检查是否部署在本地服务器中!');
}
});
</script>
<!- 单选树的模板->
<script type="text/html" id="LAY-auth-tree-convert-select">
<fieldset class="layui-elem-field layui-field-title">
<legend>树转下拉树</legend>
</fieldset>
<form class="layui-form">
<div class="layui-form-item">
<label class="layui-form-label">转换结果</label>
<div class="layui-input-block">
<select name="authid" class="layui-input">
{{# layui.each(d.list, function(index, item) { }}
<option vlaue="{{item.value}}">{{item.name}}</option>
{{# });}}
</select>
</div>
</div>
</form>
</script>
```

##### listConvert 参数配置

`listConvert` 是用于转换列表和树结构的方法,参数比较灵活,调用 `listConvert(list, opt)` 时,opt可传入参数如下
Expand Down Expand Up @@ -257,6 +325,7 @@ var trees = authtree.listConvert(res.data.list, {
11. 支持自动展开所有选中节点(v1.1 支持)
12. 支持列表转树(v1.1 支持)
13. 支持双击展开子节点(v1.1 支持)
14. 支持配置渲染参数、单选树、下拉树(v1.2 支持)

## 使用方法:

Expand Down Expand Up @@ -397,6 +466,8 @@ layui/ 官网下载的layui

## 更新记录:

2018-11-19 v1.2 支持单选树、下拉树、展开/收起图标配置

2018-09-23 v1.1 新增自动展开所有选中节点,列表转树,支持双击展开子节点等方法,消除BUG

2018-09-23 v1.0 正式版,方法效率优化以及新增监听事件,消除各种BUG
Expand All @@ -421,4 +492,4 @@ layui/ 官网下载的layui

##### github:

感谢 [adminpass](https://github.com/adminpass)[MrZhouL](https://github.com/MrZhouL)[lzjlxebr](https://github.com/lzjlxebr)[lintiansheng](https://github.com/lintiansheng) 等人提供的反馈和建议
感谢 [adminpass](https://github.com/adminpass)[MrZhouL](https://github.com/MrZhouL)[lzjlxebr](https://github.com/lzjlxebr)[lintiansheng](https://github.com/lintiansheng) 等人提供的反馈和建议
89 changes: 75 additions & 14 deletions extends/authtree.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,14 @@ layui.define(['jquery', 'form'], function(exports){

obj = {
// 渲染 + 绑定事件
openIconContent: '&#xe625;',
closeIconContent: '&#xe623;',
openIconContent: '',
closeIconContent: '',
// 表单类型 checkbox: 多选,radio:单选
checkType: 'checkbox',
// 选中、半选中、未选中
checkedIconContent: '',
halfCheckedIconContent: '',
notCheckedIconContent: '',
// 保存节点数据
checkedNode : {},
notCheckedNode : {},
Expand All @@ -35,7 +41,7 @@ layui.define(['jquery', 'form'], function(exports){
* @param {[type]} openall [是否展开全部]
* @return {[type]} [description]
*/
render: function(dst, trees, opt){
render: function(dst, trees, opt) {
var inputname = opt.inputname ? opt.inputname : 'menuids[]';
opt.inputname = inputname;
var layfilter = opt.layfilter ? opt.layfilter : 'checkauth';
Expand All @@ -52,6 +58,23 @@ layui.define(['jquery', 'form'], function(exports){
opt.autoclose = autoclose;
var autochecked = typeof opt.autochecked !== 'undefined' ? opt.autochecked : true;
opt.autochecked = autochecked;
// 有子节点的前显字符配置
opt.prefixChildStr = opt.prefixChildStr ? opt.prefixChildStr : '├─';
// 单选、多选配置
opt.checkType = opt.checkType ? opt.checkType : 'checkbox';
this.checkType = opt.checkType;
// 展开、折叠节点的前显字符配置
opt.openIconContent = opt.openIconContent ? opt.openIconContent : '&#xe625;';
this.openIconContent = opt.openIconContent;
opt.closeIconContent = opt.closeIconContent ? opt.closeIconContent : '&#xe623;';
this.closeIconContent = opt.closeIconContent;
// 选中、半选中、未选中节点的图标配置
opt.checkedIconContent = opt.checkedIconContent ? opt.checkedIconContent : '&#xe605;';
this.checkedIconContent = opt.checkedIconContent;
opt.halfCheckedIconContent = opt.halfCheckedIconContent ? opt.halfCheckedIconContent : '&#xe605;';
this.halfCheckedIconContent = opt.halfCheckedIconContent;
opt.notCheckedIconContent = opt.notCheckedIconContent ? opt.notCheckedIconContent : '&#xe605;';
this.notCheckedIconContent = opt.notCheckedIconContent;

// 不启用双击展开,单击不用延迟
if (!dblshow) {
Expand All @@ -61,7 +84,14 @@ layui.define(['jquery', 'form'], function(exports){
// 记录渲染过的树
obj.renderedTrees[dst] = {trees: trees, opt: opt};

$(dst).html(obj.renderAuth(trees, 0, {inputname: inputname, layfilter: layfilter, openall: openall, openchecked: openchecked}));
$(dst).html(obj.renderAuth(trees, 0, {
inputname: inputname,
layfilter: layfilter,
openall: openall,
openchecked: openchecked,
checkType: this.checkType,
prefixChildStr: opt.prefixChildStr,
}));
form.render();
// 变动则存一下临时状态
obj._saveNodeStatus(dst);
Expand Down Expand Up @@ -175,8 +205,8 @@ layui.define(['jquery', 'form'], function(exports){
// '+new Array(dept * 4).join('&nbsp;')+'
str += '<div><div class="auth-status" style="display: flex;flex-direction: row;align-items: flex-end;"> '+
(hasChild?'<i class="layui-icon auth-icon '+(openstatus?'active':'')+'" style="cursor:pointer;">'+(openstatus?obj.openIconContent:obj.closeIconContent)+'</i>':'<i class="layui-icon auth-leaf" style="opacity:0;color: transparent;">&#xe626;</i>')+
(dept > 0 ? '<span>├─ </span>':'')+
'<input type="checkbox" name="'+inputname+'" title="'+item.name+'" value="'+item.value+'" lay-skin="primary" lay-filter="'+layfilter+'" '+
(dept > 0 ? ('<span>'+opt.prefixChildStr+' </span>'):'')+
'<input type="'+opt.checkType+'" name="'+inputname+'" title="'+item.name+'" value="'+item.value+'" lay-skin="primary" lay-filter="'+layfilter+'" '+
(item.checked?'checked="checked"':'')+'> </div>'+
' <div class="auth-child" style="'+(openstatus ?'':'display:none;')+'padding-left:40px;"> '+append+'</div></div>'
});
Expand Down Expand Up @@ -250,23 +280,54 @@ layui.define(['jquery', 'form'], function(exports){
// 子节点列表的Key
opt.childKey = opt.childKey ? opt.childKey : 'list';
// 有子节点的前缀
opt.prefixHasChildStr = opt.prefixStr ? opt.prefixStr : '├─';
opt.prefixChildStr = opt.prefixChildStr ? opt.prefixChildStr : '├─ ';
// 没有子节点的前缀
opt.prefixHasChildStr = opt.prefixStr ? opt.prefixStr : '|';
opt.prefixNoChildStr = opt.prefixNoChildStr ? opt.prefixNoChildStr : '';
// 树的深度影响的子节点数据
opt.prefixDeptStr = opt.prefixDeptStr ? opt.prefixDeptStr : ' ';
// 如果第一列就存在没有子节点的情况,加的特殊前缀
opt.prefixFirstEmpty = opt.prefixFirstEmpty ? opt.prefixFirstEmpty : '  '

return this._treeToSelect(list, opt.currentDept, opt);
return this._treeToSelect(tree, opt.currentDept, opt);
},
// 实际处理递归的函数
_treeToSelect: function(list, currentDept, opt) {
_treeToSelect: function(tree, currentDept, opt) {
var ansList = [];
var prefix = '';

for (index in list) {
var item = list[index];
item['name'] =
ansList.push(item);
for (var i = 0;i < currentDept; i++) {
prefix += opt.prefixDeptStr;
}

for (index in tree) {
var child_flag = 0;
var item = tree[index];
if (opt.childKey in item && item[opt.childKey] && item[opt.childKey].length > 0) {
child_flag = 1;
}
name = item.name;
if (child_flag) {
name = opt.prefixChildStr + name;
} else {
if (currentDept > 1) {
name = opt.prefixNoChildStr + name;
} else {
name = opt.prefixFirstEmpty + name;
}
}
ansList.push({
name: prefix+name,
value: item.value,
checked: item.checked,
});
// 添加子节点
if (child_flag) {
var child = this._treeToSelect(item[opt.childKey], currentDept + 1, opt);
// apply 的骚操作,使用第二个参数可以用于合并两个数组
ansList.push.apply(ansList, child);
}
}
return ansList;
},
// 自动调整宽度以解决 form.render()生成元素兼容性问题,如果用户手动调用 form.render() 之后也需要调用此方法
autoWidth: function(dst) {
Expand Down
Loading

0 comments on commit 0d077d9

Please sign in to comment.