Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
auto-code-ui.static.js.layui.extend.authtree.js Maven / Gradle / Ivy
/*
* @Author: Jeffrey Wang
* @Date: 2018-03-16 18:24:47
* @Version: v1.2.2
* @Last Modified by: Jeffrey Wang
* @Last Modified time: 2019-01-09 17:48:22
*/
// 节点树
layui.define(['jquery', 'form'], function(exports){
$ = layui.jquery;
form = layui.form;
var MOD_NANE = 'authtree';
var obj = {
// 渲染 + 绑定事件
openIconContent: '',
closeIconContent: '',
// 表单类型 checkbox: 多选,radio:单选
checkType: 'checkbox',
// 选中、半选中、未选中
checkedIconContent: '',
halfCheckedIconContent: '',
notCheckedIconContent: '',
// 保存节点数据
checkedNode : {},
notCheckedNode : {},
// 临时保存最新操作影响的节点
lastCheckedNode : {},
lastNotCheckedNode : {},
// 已经渲染过的树,可用来获取配置,{ dst: {trees: '树的节点数据', opt: '配置'} }
renderedTrees: {},
// 使用 layui 的监听事件
on: function(events, callback) {
return layui.onevent.call(this, MOD_NANE, events, callback);
},
/**
* 渲染DOM并绑定事件
* @param {[type]} dst [目标ID,如:#test1]
* @param {[type]} trees [数据,格式:{}]
* @param {[type]} inputname [上传表单名]
* @param {[type]} layfilter [lay-filter的值]
* @param {[type]} openall [是否展开全部]
* @return {[type]} [description]
*/
render: function(dst, trees, opt) {
// 表单名称配置
var inputname = opt.inputname ? opt.inputname : 'menuids[]';
opt.inputname = inputname;
// lay-filter 配置
var layfilter = opt.layfilter ? opt.layfilter : 'checkauth';
opt.layfilter = layfilter;
// 默认展开全部 配置
var openall = opt.openall ? opt.openall : false;
opt.openall = openall;
// 双击展开此层配置
var dblshow = opt.dblshow ? opt.dblshow : false;
opt.dblshow = dblshow;
// 双击时间差 - 不能设置过长,否则单击延迟很感人
var dbltimeout = opt.dbltimeout ? opt.dbltimeout : 120;
opt.dbltimeout = dbltimeout;
// 默认展开有选中数据的层
var openchecked = typeof opt.openchecked !== 'undefined' ? opt.openchecked : true;
opt.openchecked = openchecked;
// 自动取消选中
var autoclose = typeof opt.autoclose !== 'undefined' ? opt.autoclose : true;
opt.autoclose = autoclose;
// 自动选择直属父级节点
var autochecked = typeof opt.autochecked !== 'undefined' ? opt.autochecked : true;
opt.autochecked = autochecked;
// 是否隐藏左侧 单选/多选的选框 -- 特殊需求,一般用于单选树并且不用
var hidechoose = typeof opt.hidechoose !== 'undefined' ? opt.hidechoose : false;
opt.hidechoose = hidechoose;
// 有子节点的前显字符配置
opt.prefixChildStr = opt.prefixChildStr ? opt.prefixChildStr : '├─';
// 单选、多选配置
opt.checkType = opt.checkType ? opt.checkType : 'checkbox';
this.checkType = opt.checkType;
// 皮肤可选择
opt.checkSkin = opt.checkSkin ? opt.checkSkin : 'primary';
// 展开、折叠节点的前显字符配置
opt.openIconContent = opt.openIconContent ? opt.openIconContent : '';
this.openIconContent = opt.openIconContent;
opt.closeIconContent = opt.closeIconContent ? opt.closeIconContent : '';
this.closeIconContent = opt.closeIconContent;
// 选中、半选中、未选中节点的图标配置
opt.checkedIconContent = opt.checkedIconContent ? opt.checkedIconContent : '';
this.checkedIconContent = opt.checkedIconContent;
opt.halfCheckedIconContent = opt.halfCheckedIconContent ? opt.halfCheckedIconContent : '';
this.halfCheckedIconContent = opt.halfCheckedIconContent;
opt.notCheckedIconContent = opt.notCheckedIconContent ? opt.notCheckedIconContent : '';
this.notCheckedIconContent = opt.notCheckedIconContent;
// 不启用双击展开,单击不用延迟
var dblisten = true;
if (dblshow) {
// 开启双击展开,双击事件默认为120s
} else {
// 未开启双击展开且 dbltimeout <= 0,则说明不用监听双击事件
if (opt.dbltimeout <= 0) {
dblisten = false;
}
dbltimeout = 0;
// opt.dbltimeout = dbltimeout;
}
// 记录渲染过的树
obj.renderedTrees[dst] = {trees: trees, opt: opt};
$(dst).html(obj.renderAuth(trees, 0, {
inputname: inputname,
layfilter: layfilter,
openall: openall,
openchecked: openchecked,
checkType: this.checkType,
prefixChildStr: opt.prefixChildStr,
}));
obj.showChecked(dst);
form.render();
// 变动则存一下临时状态
obj._saveNodeStatus(dst);
// 开启自动宽度优化
obj.autoWidth(dst);
// 备注:如果使用form.on('checkbox()'),外部就无法使用form.on()监听同样的元素了(LAYUI不支持重复监听了)。
// form.on('checkbox('+layfilter+')', function(data){
// /*属下所有权限状态跟随,如果选中,往上走全部选中*/
// var childs = $(data.elem).parent().next().find('input[type="checkbox"]').prop('checked', data.elem.checked);
// if(data.elem.checked){
// /*查找child的前边一个元素,并将里边的checkbox选中状态改为true。*/
// $(data.elem).parents('.auth-child').prev().find('input[type="checkbox"]').prop('checked', true);
// }
// /*console.log(childs);*/
// form.render('checkbox');
// });
// 解决单击和双击冲突问题的 timer 变量
var timer = 0;
$(dst).find('.auth-single:first').unbind('click').on('click', '.layui-form-checkbox,.layui-form-radio', function(event){
// window.event? window.event.cancelBubble = true : event.stopPropagation();
var that = this;
clearTimeout(timer);
// 双击判断需要的延迟处理
timer = setTimeout(function(){
var elem = $(that).prev();
var checked = elem.is(':checked');
if (autochecked) {
if (checked) {
/*查找child的前边一个元素,并将里边的checkbox选中状态改为true。*/
elem.parents('.auth-child').prev().find('.authtree-checkitem:not(:disabled)[type="checkbox"]').prop('checked', true);
}
elem.parent().next().find('.authtree-checkitem:not(:disabled)[type="checkbox"]').prop('checked', checked);
}
if (autoclose) {
if (checked) {
// pass
} else {
// 自动关闭父级选中节点
obj._autoclose($(that).parent());
}
}
form.render('checkbox');
form.render('radio');
// 变动则存一下临时状态
obj._saveNodeStatus(dst);
// 触发 change 事件
obj._triggerEvent(dst, 'change', {
othis: $(that),
oinput: elem,
value: elem.val(),
});
obj.autoWidth(dst);
}, dbltimeout);
return false;
});
/*动态绑定展开事件*/
$(dst).unbind('click').on('click', '.auth-icon', function(){
obj.iconToggle(dst, this);
});
/*双击展开*/
$(dst).find('.auth-single:first').unbind('dblclick').on('dblclick', '.layui-form-checkbox,.layui-form-radio', function(e){
// 触发时间 > 0,才触发双击事件
// opt.dbltimeout 是用户真实设定的超时时间,与 dbltimeout 不一样
// if (opt.dbltimeout > 0) {
obj._triggerEvent(dst, 'dblclick', {
othis: $(this),
elem: $(this).prev(),
value: $(this).prev().val(),
});
// }
if (dblshow) {
clearTimeout(timer);
obj.iconToggle(dst, $(this).prevAll('.auth-icon:first'));
}
}).on('selectstart', function(){
// 屏蔽双击选中文字
return false;
});
},
// 自动关闭 - 如果兄弟节点均没选中,递归取消上级元素选中状态,传入的是 .auth-status 节点,递归 .auth-status 上级节点
_autoclose: function(obj) {
var single = $(obj).parent().parent();
var authStatus = single.parent().prev();
if (!authStatus.hasClass('auth-status')) {
return false;
}
// 仅一层
if (single.find('div>.auth-status>input.authtree-checkitem:not(:disabled)[type="checkbox"]:checked').length === 0) {
authStatus.find('.authtree-checkitem:not(:disabled)[type="checkbox"]').prop('checked', false);
this._autoclose(authStatus);
}
},
// 以 icon 的维度,切换显示下级空间
iconToggle: function(dst, iconobj){
var origin = $(iconobj);
var child = origin.parent().parent().find('.auth-child:first');
if(origin.is('.active')){
/*收起*/
origin.removeClass('active').html(obj.closeIconContent);
child.slideUp('fast');
} else {
/*展开*/
origin.addClass('active').html(obj.openIconContent);
child.slideDown('fast');
}
obj._triggerEvent(dst, 'deptChange');
return false;
},
// 递归创建格式
renderAuth: function(tree, dept, opt){
var inputname = opt.inputname;
var layfilter = opt.layfilter;
var openall = opt.openall;
var str = '';
layui.each(tree, function(index, item){
var hasChild = (item.list&&(item.list.length || !$.isEmptyObject(item.list.length))) ? 1 : 0;
// 注意:递归调用时,this的环境会改变!
var append = hasChild ? obj.renderAuth(item.list, dept+1, opt) : '';
var openstatus = openall || (opt.openchecked && item.checked);
// '+new Array(dept * 4).join(' ')+'
str += '
'+
(hasChild?''+(openstatus?obj.openIconContent:obj.closeIconContent)+' ':' ')+
(dept > 0 ? (''+opt.prefixChildStr+' '):'')+
'
'+
'
'+append+'
'
});
str += '
';
return str;
},
/**
* 显示到已选中的最高层级
* @param {[type]} dst [description]
* @return {[type]} [description]
*/
showChecked: function(dst) {
$(dst).find('.authtree-checkitem:checked').parents('.auth-child').show();
},
/**
* 将普通列表无限递归转换为树
* @param {[type]} list [普通的列表,必须包括 opt.primaryKey 指定的键和 opt.parentKey 指定的键]
* @param {[type]} opt [配置参数,支持 primaryKey(主键 默认id) parentKey(父级id对应键 默认pid) nameKey(节点标题对应的key 默认name) valueKey(节点值对应的key 默认id) checkedKey、disabledKey(节点是否选中的字段 默认checked,传入数组则判断主键是否在此数组中) startPid(第一层扫描的PID 默认0) currentDept(当前层 默认0) maxDept(最大递归层 默认100) childKey(递归完成后子节点对应键 默认list) deptPrefix(根据层级重复的前缀 默认'')]
* @return {[type]} [description]
*/
listConvert: function(list, opt) {
opt.primaryKey = opt.primaryKey ? opt.primaryKey : 'id';
opt.parentKey = opt.parentKey ? opt.parentKey : 'pid';
opt.startPid = opt.startPid ? opt.startPid : 0;
opt.currentDept = opt.currentDept ? opt.currentDept : 0;
opt.maxDept = opt.maxDept ? opt.maxDept : 100;
opt.childKey = opt.childKey ? opt.childKey : 'list';
opt.checkedKey = opt.checkedKey ? opt.checkedKey : 'checked';
opt.disabledKey = opt.disabledKey ? opt.disabledKey : 'disabled';
opt.nameKey = opt.nameKey ? opt.nameKey : 'name';
opt.valueKey = opt.valueKey ? opt.valueKey : 'id';
return this._listToTree(list, opt.startPid, opt.currentDept, opt);
},
// 实际的递归函数,将会变化的参数抽取出来
_listToTree: function(list, startPid, currentDept, opt) {
if (opt.maxDept < currentDept) {
return [];
}
var child = [];
for (index in list) {
// 筛查符合条件的数据(主键 = startPid)
var item = list[index];
if (typeof item[opt.parentKey] !== 'undefined' && item[opt.parentKey] === startPid) {
// 满足条件则递归
var nextChild = this._listToTree(list, item[opt.primaryKey], currentDept+1, opt);
// 节点信息保存
var node = {};
if (nextChild.length > 0) {
node[opt.childKey] = nextChild;
}
node['name'] = item[opt.nameKey];
node['value'] = item[opt.valueKey];
// 已选中节点的两种渲染方式
if (typeof opt.checkedKey === "string" || typeof opt.checkedKey === 'number') {
node['checked'] = item[opt.checkedKey];
} else if(typeof opt.checkedKey === 'object') {
if ($.inArray(item[opt.valueKey], opt.checkedKey) != -1) {
node['checked'] = true;
} else {
node['checked'] = false;
}
} else {
node['checked'] = false;
}
// 禁用节点的两种渲染方式
if (typeof opt.disabledKey === "string" || typeof opt.disabledKey === 'number') {
node['disabled'] = item[opt.disabledKey];
} else if(typeof opt.disabledKey === 'object') {
if ($.inArray(item[opt.valueKey], opt.disabledKey) != -1) {
node['disabled'] = true;
} else {
node['disabled'] = false;
}
} else {
node['disabled'] = false;
}
child.push(node);
}
}
return child;
},
/**
* 将树转为单选可用的 select,如果后台返回列表数据,可以先转换为 tree
* @param {[type]} tree [description]
* @param {[type]} opt [description]
* @return {[type]} [description]
*/
treeConvertSelect: function(tree, opt) {
if (typeof tree.length !== 'number' || tree.length <= 0) {
return [];
}
// 初始化层级
opt.currentDept = opt.currentDept ? opt.currentDept : 0;
// 子节点列表的Key
opt.childKey = opt.childKey ? opt.childKey : 'list';
// 值的key
opt.valueKey = opt.valueKey ? opt.valueKey : 'value';
// 选中的key - 仅支持字符串
opt.checkedKey = opt.checkedKey ? opt.checkedKey : 'checked';
// 禁用的key
opt.disabledKey = opt.disabledKey ? opt.disabledKey : 'disabled';
// 有子节点的前缀
opt.prefixChildStr = opt.prefixChildStr ? opt.prefixChildStr : '├─ ';
// 没有子节点的前缀
opt.prefixNoChildStr = opt.prefixNoChildStr ? opt.prefixNoChildStr : '● ';
// 树的深度影响的子节点数据
opt.prefixDeptStr = opt.prefixDeptStr ? opt.prefixDeptStr : ' ';
// 如果第一列就存在没有子节点的情况,加的特殊前缀
opt.prefixFirstEmpty = opt.prefixFirstEmpty ? opt.prefixFirstEmpty : ' '
return this._treeToSelect(tree, opt.currentDept, opt);
},
// 实际处理递归的函数
_treeToSelect: function(tree, currentDept, opt) {
var ansList = [];
var prefix = '';
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;
}
var 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[opt.valueKey],
checked: item[opt.checkedKey],
disabled: item[opt.disabledKey],
});
// 添加子节点
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) {
var tree = this.getRenderedInfo(dst);
var opt = tree.opt;
$(dst).css({
'whiteSpace': 'nowrap',
'maxWidth' : '100%',
});
$(dst).find('.layui-form-checkbox,.layui-form-radio,.layui-form-audio').each(function(index, item){
if ($(this).is(':hidden')) {
// 比较奇葩的获取隐藏元素宽度的手法,请见谅
$('body').append(''+$(this).html()+'
');
$width = $('#layui-authtree-get-width').find('span').width() + $('#layui-authtree-get-width').find('i').width() + 29;
$('#layui-authtree-get-width').remove();
} else {
$width = $(this).find('span').width() + $(this).find('i').width() + 25;
}
$(this).width($width);
// 隐藏 单选/多选的左侧选框隐藏
if (opt.hidechoose) {
$(this).prevAll('i').css({
zIndex: 2,
});
$(this).css({
position: 'relative'
,left: function () {
return '-'+$(this).css('padding-left');// 避免点击抖动的骚操作
}
}).find('i').hide();
}
});
},
// 触发自定义事件
_triggerEvent: function(dst, events, other) {
var tree = this.getRenderedInfo(dst);
var origin = $(dst);
if (tree) {
var opt = tree.opt;
var data = {
opt: opt,
dst: dst,
othis: origin,
};
if (other && typeof other === 'object') {
data = $.extend(data, other);
}
// 支持 dst 和 用户的配置的 layfilter 监听
layui.event.call(origin, MOD_NANE, events+'('+dst+')', data);
layui.event.call(origin, MOD_NANE, events+'('+opt.layfilter+')', data);
} else {
return false;
}
},
// 获取渲染过的信息
getRenderedInfo: function(dst) {
return this.renderedTrees[dst];
},
// 动态获取最大深度
getMaxDept: function(dst){
var next = $(dst);
var dept = 0;
while(next.length && dept < 100000) {
next = this._getNext(next);
if (next.length) {
dept++;
} else {
break;
}
}
return dept;
},
// 全选
checkAll: function(dst){
var origin = $(dst);
origin.find('.authtree-checkitem:not(:disabled):not(:checked)').prop('checked', true);
form.render('checkbox');
form.render('radio');
obj.autoWidth(dst);
// 变动则存一下临时状态
obj._saveNodeStatus(dst);
obj._triggerEvent(dst, 'change');
obj._triggerEvent(dst, 'checkAll');
},
// 全不选
uncheckAll: function(dst){
var origin = $(dst);
origin.find('.authtree-checkitem:not(:disabled):checked').prop('checked', false);
form.render('checkbox');
form.render('radio');
obj.autoWidth(dst);
// 变动则存一下临时状态
obj._saveNodeStatus(dst);
obj._triggerEvent(dst, 'change');
obj._triggerEvent(dst, 'uncheckAll');
},
// 显示整个树
showAll: function(dst) {
this.showDept(dst, this.getMaxDept(dst));
},
// 关闭整颗树
closeAll: function(dst) {
this.closeDept(dst, 1);
},
// 切换整颗树的显示/关闭
toggleAll: function(dst) {
if (this._shownDept(2)) {
this.closeDept(dst);
} else {
this.showAll(dst);
}
},
// 显示到第 dept 层
showDept: function(dst, dept) {
var next = $(dst);
for(var i = 1; i < dept; i++) {
next = this._getNext(next);
if (next.length) {
this._showSingle(next);
} else {
break;
}
}
obj._triggerEvent(dst, 'deptChange', {dept: dept});
},
// 第 dept 层之后全部关闭
closeDept: function(dst, dept) {
var next = $(dst);
for(var i = 0; i < dept; i++){
next = this._getNext(next);
}
while(next.length) {
this._closeSingle(next);
next = this._getNext(next);
}
obj._triggerEvent(dst, 'deptChange', {dept: dept});
},
// 临时保存所有节点信息状态
_saveNodeStatus: function(dst){
var currentChecked = this.getChecked(dst);
var currentNotChecked = this.getNotChecked(dst);
// 保存新信息前,最新选择的信息
this.lastCheckedNode[dst] = this._getLastChecked(dst, currentChecked, currentNotChecked);
this.lastNotCheckedNode[dst] = this._getLastNotChecked(dst, currentChecked, currentNotChecked);
this.checkedNode[dst] = currentChecked;
this.notCheckedNode[dst] = currentNotChecked;
// console.log('保存节点信息', this.checkedNode[dst], this.notCheckedNode[dst], this.lastCheckedNode[dst], this.lastNotCheckedNode[dst]);
},
// 判断某一层是否显示
_shownDept: function(dst, dept) {
var next = $(dst);
for(var i = 0; i < dept; i++){
next = this._getNext(next);
}
return !next.is(':hidden');
},
// 获取
_getNext: function(dst) {
return $(dst).find('.auth-single:first>div>.auth-child');
},
// 显示某层 single
_showSingle: function(dst) {
layui.each(dst, function(index, item) {
var origin = $(item).find('.auth-single:first');
var parentChild = origin.parent();
var parentStatus = parentChild.prev();
if (!parentStatus.find('.auth-icon').hasClass('active')) {
parentChild.show();
// 显示上级的 .auth-child节点,并修改.auth-status的折叠状态
parentStatus.find('.auth-icon').addClass('active').html(obj.openIconContent);
}
});
},
// 关闭某层 single
_closeSingle: function(dst) {
var origin = $(dst).find('.auth-single:first');
var parentChild = origin.parent();
var parentStatus = parentChild.prev();
if (parentStatus.find('.auth-icon').hasClass('active')) {
parentChild.hide();
// 显示上级的 .auth-child节点,并修改.auth-status的折叠状态
parentStatus.find('.auth-icon').removeClass('active').html(obj.closeIconContent);
}
},
// 获取选中叶子结点
getLeaf: function(dst){
var leafs = $(dst).find('.auth-leaf').parent().find('.authtree-checkitem:checked');
var data = [];
leafs.each(function(index, item){
// console.log(item);
data.push(item.value);
});
// console.log(data);
return data;
},
// 获取所有节点数据
getAll: function(dst){
var inputs = $(dst).find('.authtree-checkitem');
var data = [];
inputs.each(function(index, item){
data.push(item.value);
});
// console.log(data);
return data;
},
// 获取最新选中(之前取消-现在选中)
getLastChecked: function(dst) {
return this.lastCheckedNode[dst] || [];
},
// (逻辑)最新选中(之前取消-现在选中)
_getLastChecked: function(dst, currentChecked, currentNotChecked) {
var lastCheckedNode = currentChecked;
var data = [];
for (i in lastCheckedNode) {
if ($.inArray(lastCheckedNode[i], this.notCheckedNode[dst]) != -1) {
data.push(lastCheckedNode[i]);
}
}
return data;
},
// 获取所有选中的数据
getChecked: function(dst){
var inputs = $(dst).find('.authtree-checkitem:checked');
var data = [];
inputs.each(function(index, item){
data.push(item.value);
});
return data;
},
// 获取最新取消(之前取消-现在选中)
getLastNotChecked: function(dst) {
return this.lastNotCheckedNode[dst] || [];
},
// (逻辑)最新取消(之前选中-现在取消)
_getLastNotChecked: function(dst, currentChecked, currentNotChecked) {
var lastNotCheckedNode = currentNotChecked;
var data = [];
for (i in lastNotCheckedNode) {
if ($.inArray(lastNotCheckedNode[i], this.checkedNode[dst]) != -1) {
data.push(lastNotCheckedNode[i]);
}
}
return data;
},
// 获取未选中数据
getNotChecked: function(dst){
var inputs = $(dst).find('.authtree-checkitem:not(:checked)');
var data = [];
inputs.each(function(index, item){
data.push(item.value);
});
// console.log(data);
return data;
}
};
exports('authtree', obj);
});