一、Layui树形组件:treetable.js
可在layui下创建一个文件夹保存组件的js代码,以便引用。
// 基于layui table的树形表格实现,只支持页面仅有一个表格的情况
layui.define(['table', 'util'], function(exports) {
var $ = layui.$,
util = layui.util,
table = layui.table;
var ICON_FILE = 'layui-icon-file',
ICON_RIGHT = 'layui-icon-triangle-r',
ICON_DOWN = 'layui-icon-triangle-d',
TABLE_VIEW = '.layui-table-view';
var treetable = {
config: {
tempTop: 0,
currIndex: -1,
field: ''
}, //全局配置项
render: function(options) {
var that = this;
that.config = $.extend({}, that.config, options);
render(options);
},
renderNode: function(node, data) {
this.config.field = node.field;
return renderNode(node, data);
},
done: function(datas) {
var that = this;
that.config.data = datas;
done(that.config);
},
add: function(param) {
add(this.config, param);
},
del: function(param) {
del(this.config, param);
},
expandAll: function(tableID, successEvent) {
this.config.successEvent = successEvent;
expandAll(this.config, tableID);
},
foldAll: function(tableID) {
foldAll(tableID);
}
};
var render = function() {
var that = treetable,
options = that.config;
var data = {
id: 0
};
options.level = 0;
options.data = data;
if (options.load) {
options.load(options, function(data) {
$.each(data, function(index, item) {
item.level = 1; //菜单级别,初始化时level=1:一级
});
table.reload(options.id, {
data: data
});
});
}
};
var renderNode = function(node, data) {
var name = node.field;
var spread = data.spread && true;
let result = ';
let icon = 'file';
let icon2 = '';
let event = '';
if (!data.leaf) {
icon = spread ? 'triangle-d' : 'triangle-r';
icon2 = ' ';
event = ' lay-op-event="toggle"';
}
var style = '" style="margin-left: ' + (data.left || 0) + 'px"';
var fname = data[name] || '';
return result + icon + style + event + '>' + icon2 + ' ' +
fname + '';
};
var done = function(config) {
var rows = config.data;
var tableID = config.id;
var tempTop = config.tempTop;
var currIndex = config.currIndex;
var trs = $('[lay-id="' + tableID + '"] .layui-table-main tr');
//新增行、展开行被选中
if (currIndex > -1) {
trs.eq(currIndex).addClass('layui-table-click').siblings().removeClass(
'layui-table-click');
}
for (let i = 0; i < rows.length; i++) {
//隐藏加载前的行
if (rows[i].hide) {
trs.eq(i).addClass('layui-hide');
}
//添加级别
trs.eq(i).attr('lay-level', rows[i].level);
}
//重回到上次浏览位置
$("[lay-id='" + tableID + "']").find(".layui-table-main").animate({
scrollTop: tempTop
}, 0);
}
var add = function(config, param) {
var tableID = param.elem;
var currTr = param.tr; //
var data = param.data;
var field = config.field;
//叶子
//获取新ID
//获取选中行
var rowIndex;
var oldData = table.cache[tableID];
var newData = {
level: 0,
parent: 0,
leaf: true
};
$.extend(newData, data);
if (currTr.length == 0) {
//在末尾添加行
rowIndex = oldData.length;
config.currIndex = oldData.length;
oldData.push(newData);
} else {
var currIndex = currTr.eq(0).data('index');
//在选中行子集中末尾插入行
config.currIndex = currIndex;
var rowData = oldData[currIndex];
var opIcon = currTr.find('[data-field="' + field + '"] .layui-icon').eq(0);
var left = Number(opIcon.css("margin-left").replace('px', ''));
var level = rowData.level;
var id = rowData.id;
//改变操作列图标
if (opIcon.hasClass('layui-icon-file')) {
//文件图标改为可点击图标
rowData.leaf = false;
rowData.spread = true;
rowData.loaded = true;
} else if (opIcon.hasClass('layui-icon-triangle-r')) {
//调用点击展开事件
opIcon.trigger('click');
}
//同级的下一个节点即为新增节点的下标,如果同级没有下一个节点,则上级的下一节点
var maxIndex = config.currIndex + 1;
var breakFlag = false;
for (let i = maxIndex; i < oldData.length; i++) {
if (oldData[i].level <= level) {
maxIndex = i;
breakFlag = true;
break;
}
}
if (!breakFlag) {
maxIndex = oldData.length;
}
rowIndex = maxIndex;
config.currIndex = maxIndex;
newData.left = left + 20;
newData.level = level + 1;
newData.parent = rowData.id;
oldData.splice(rowIndex, 0, newData); //插入
}
var rowHeight = $("[lay-id='" + tableID + "']").find(".layui-table-main").find('tr').height() + 1;
config.tempTop = $("[lay-id='" + tableID + "']").find(".layui-table-main").scrollTop() + rowHeight;
table.reload(tableID, {
data: oldData
});
}
var del = function(config, param) {
var tableID = param.elem;
var rowIndex = param.index;
var onlyLeaf = param.onlyLeaf;
//获取选中行
var currTr = $("[lay-id='" + tableID + "'] .layui-table-main").find("tr").eq(rowIndex);
var oldData = table.cache[tableID];
var rowData = oldData[rowIndex];
if (!rowData) {
return;
}
var pid = rowData.parent;
//如果只能删除叶子节点
if (onlyLeaf) {
//判断是否是叶子节点
if (rowData.leaf) {
oldData.splice(rowIndex, 1);
}
} else {
//删除选中节点及子节点
var ids = [];
setDeleteIds(rowData.id);
oldData = oldData.filter(function(v) {
return ids.indexOf(v.id) == -1;
});
function setDeleteIds(id) {
ids.push(id);
//是否有子节点
var children = oldData.filter(function(v) {
return v.parent == id;
});
if (children.length > 0) {
for (var i = 0; i < children.length; i++) {
setDeleteIds(children[i].id);
}
}
}
}
if (pid != 0) {
//检查父节点是否还有子节点
var siblings = oldData.filter(function(v) {
return v.parent == pid;
});
if (siblings.length == 0) {
var parentData = oldData.filter(function(v) {
return v.id == pid;
})[0];
oldData[parentData.LAY_TABLE_INDEX].leaf = true;
}
}
config.tempTop = $("[lay-id='" + tableID + "']").find(".layui-table-main").scrollTop();
table.reload(tableID, {
data: oldData
});
}
var expandAll = function(options, tableID) {
options.expandAll = true;
var ICONS = {
ICON_ADD: 'layui-icon-triangle-r',
ICON_SUB: 'layui-icon-triangle-d',
ICON_FILE: 'layui-icon-file'
};
tableID = '#' + tableID;
var elem = $(tableID).next();
if (options.expanded) {
//显示所有行
elem.find('tr.layui-hide').removeClass('layui-hide');
//将折叠图标变为展开图标
elem.find('.' + ICONS.ICON_ADD).addClass(ICONS.ICON_SUB).removeClass(ICONS.ICON_ADD);
//回调方法
if (options.successEvent) {
options.successEvent();
}
} else {
//触发展开图标的点击事件
elem.find('.' + ICONS.ICON_ADD).addClass('spread').trigger('click');
//设置定时任务,每0.5秒检查是否有未展开节点
var count = 0;
var interval = setInterval(function(e) {
elem = $(tableID).next();
count++;
var addCount = elem.find('.' + ICONS.ICON_ADD).not('.spread').length;
if (addCount == 0) {
// 已全部展开标志
options.expanded = true;
delete options.expandAll;
clearInterval(interval);
//回调方法
if (options.successEvent) {
options.successEvent();
}
}else{
elem.find('.' + ICONS.ICON_ADD).addClass('spread').trigger('click');
}
}, 200);
}
}
var foldAll = function(tableID) {
// 将缓存数据设置为收缩
var datas = table.cache[tableID];
for (var i = 0; i < datas.length; i++) {
var item = datas[i];
if (item.spread) {
item.spread = false;
}
if (item.level > 1) {
item.hide = true;
}
}
var layBody = $('[lay-id="' + tableID + '"] .layui-table-main');
// 隐藏除一级节点外的所有节点
layBody.find('tr').not('[lay-level="1"]').addClass('layui-hide');
// 将已展开节点图标变为未展开
layBody.find('.' + ICON_DOWN).addClass(ICON_RIGHT).removeClass(ICON_DOWN);
// 移除选中
layBody.find('.layui-table-click').removeClass('layui-table-click');
// 重新调整表格尺寸
table.resize(tableID);
}
util.event("lay-op-event", {
toggle: function(obj) { // 伸缩图标点击事件
var config = treetable.config;
//获取DOM元素
var tableID = $(obj).parents(TABLE_VIEW).attr('lay-id');
var tr = $(obj).parents('tr').eq(0);
var td = $(obj).parents('td').eq(0);
var rowIndex = tr.data('index');
var rowData = table.cache[tableID][rowIndex];
config.tempTop = $('[lay-id="' + tableID + '"] .layui-table-main').scrollTop();
if (!rowData.loaded) { //未加载过
rowData.loaded = true; //设为已加载
rowData.spread = true; //设为已展开
var left = Number($(obj).css('margin-left').replace('px', ''));
var level = rowData.level || 0;
//未加载过
var node = {
level: level,
data: rowData
}
config.load(node, function(data) {
var oldData = table.cache[tableID];
for (let i = 0; i < data.length; i++) {
let item = $.extend(data[i], {
left: left + 20,
level: level + 1
});
oldData.splice(rowIndex + i + 1, 0, item);
}
currIndex = rowIndex;
table.reload(tableID, {
data: oldData
});
});
return;
}
//显示隐藏标志
var showFlag = $(obj).hasClass(ICON_RIGHT); //true-显示
var pid = rowData.id;
if (showFlag) {
$(obj).addClass(ICON_DOWN).removeClass(ICON_RIGHT);
showChildren(pid);
} else {
$(obj).addClass(ICON_RIGHT).removeClass(ICON_DOWN);
hideChildren(pid);
}
rowData.spread = showFlag;
//重载尺寸
table.resize(tableID);
}
});
//展开
var showChildren = function(pid) {
//获取显示子菜单
var children = table.cache[tableID].filter(function(v) {
return v.parent == pid;
});
var trs = $('[lay-id="' + tableID + '"] .layui-table-main tr');
for (let i = 0; i < children.length; i++) {
trs.eq(children[i].LAY_TABLE_INDEX).removeClass('layui-hide');
children[i].hide = false;
//如果有子菜单,则显示子菜单
if (children[i].spread && !children[i].leaf) {
showChildren(children[i].id);
}
}
}
//收缩
var hideChildren = function(pid) {
//获取隐藏子菜单
var children = table.cache[tableID].filter(function(v) {
return v.parent == pid;
});
var trs = $('[lay-id="' + tableID + '"] .layui-table-main tr');
for (let i = 0; i < children.length; i++) {
trs.eq(children[i].LAY_TABLE_INDEX).addClass('layui-hide');
children[i].hide = true;
//如果有子菜单,则隐藏子菜单
if (children[i].spread && !children[i].leaf) {
hideChildren(children[i].id);
}
}
}
//输出treetable接口
exports('treetable', treetable);
});
二、组件测试代码:treetableDemo.html
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="../layui/css/layui.css" />
<title>title>
<style>
/* 树形表格树下拉节点样式 */
.layui-treetable-node-op{
position: relative;
bottom: 1px;
}
.layui-treetable-node-span{
position: relative;
bottom: 2px;
}
style>
head>
<body>
<div class="layui-fluid">
<div class="layui-row">
<div class="layui-btn-group">
<button class="layui-btn layui-btn-sm" lay-event="add"><i class="layui-icon layui-icon-add-1">i> 新增button>
<button class="layui-btn layui-btn-sm" lay-event="del"><i class="layui-icon layui-icon-delete">i> 删除button>
<button class="layui-btn layui-btn-sm" lay-event="expandAll"><i class="layui-icon layui-icon-down">i> 全部展开button>
<button class="layui-btn layui-btn-sm" lay-event="foldAll"><i class="layui-icon layui-icon-up">i> 全部折叠button>
div>
div>
<div class="layui-row">
<table class="layui-hide" id="table" lay-filter="table">table>
div>
div>
body>
<script src="../layui/layui.js">script>
<script>
layui.extend({
treetable: '../layui/extend/treetable'
});
var tableID = 'table';
/******************** layui.use初始化 *************************/
layui.use(['table', 'treetable', 'util'], function() {
var $ = layui.$,
table = layui.table,
util = layui.util,
treetable = layui.treetable;
//初始化渲染表格
// var datas = getTableDatas(0);
var tableIns = table.render({
elem: '#' + tableID,
id: tableID,
height: 'full-60',
page: false,
size: 'sm',
limit: '1000',
cols: [
[{
type: 'numbers'
}, {
field: 'name',
title: '菜单名称',
width: '20%',
templet: function(d) {
//渲染树形图标
return treetable.renderNode(this, d);
}
}, {
field: 'code',
title: '菜单代码',
width: '12%',
edit: 'text',
event: 'kk'
}, {
field: 'page',
title: '关联页面'
}, {
field: 'valid',
title: '启用标志',
width: 120
}]
],
data: [],
done: function(res, data, curr) {
//调用表格加载完成的方法
treetable.done(res.data);
}
});
//初始化加载表格数据
treetable.render({
id: tableID,
load: function(node, render) {
var pid = 0;
if (node.level > 0) {
pid = node.data.id;
}
render(getTableDatas(pid));
}
});
/***************** 监听区 *******************/
//监听行点击事件
table.on('row(' + tableID + ')', function(obj) {
//选中行高亮显示
obj.tr.addClass('layui-table-click').siblings().removeClass('layui-table-click');
});
//监听工具点击事件
/***************** 事件区 *******************/
var newID = 1000;
util.event("lay-event", {
add: function() {
treetable.add({
elem: tableID,
tr: $('[lay-id="' + tableID + '"] .layui-table-main').find(".layui-table-click"),
data: {
id: newID++,
name: 'testAdd'
}
});
},
del: function() {
treetable.del({
elem: tableID,
index: $('[lay-id="' + tableID + '"] .layui-table-main').find(".layui-table-click").eq(0).data('index'),
// onlyLeaf: true,//只能删除叶子节点
});
},
expandAll: function() {
treetable.expandAll(tableID, function() {
console.log('加载成功!');
});
},
foldAll: function() {
treetable.foldAll(tableID);
}
});
});
script>
<script>
//造测试数据,实际开发使用ajax从后台查询子数据
var map = new Map();
//一级
var datas1 = [];
for (var i = 1; i < 10; i++) {
datas1.push({
id: i,
name: '一级菜单-' + i,
parent: 0
});
//二级菜单
var datas2 = [];
for (var j = i * 10; j < i * 10 + 5; j++) {
datas2.push({
id: j,
name: '二级菜单-' + j,
parent: i
});
//三级菜单
var datas3 = [];
for (var k = j * 10; k < j * 10 + 9; k++) {
datas3.push({
id: k,
name: '三级菜单-' + k,
leaf: true,
parent: j
});
}
map.set(j, datas3);
}
map.set(i, datas2);
}
map.set(0, datas1);
function getTableDatas(pid) {
var $ = layui.$;
var data = [];
$.ajax({
type: 'post',
url: 'http://localhost:8081/getMenus',
data: {
node: pid
},
async: false,
success: function(obj) {
data = obj.data;
$.each(data, function(index, item){
item.leaf = item.leaf == 1;
});
}
});
return data;
// return map.get(pid) || [];
}
script>
html>