1、开始
【目标】layui 渲染 “树型数据”的实现方式。
【可实现效果】
【实现方式】github 搜索后结果
layui-treetable
treetable-lay
推荐使用
treetable-lay
库。
2.1、layui-treetable
layui-treetable 是一个 layui 树表实现的插件。
Gitee 文档
2.2、说明
说明
1、通过阅读这个包中的 layui 源码,这里是对 layui 进行的再开发,是将 treetable 当做原生模块注册的;
// ...
treetable: 'modules/treetable',
// ...
: (n.use(e, i), n)
2、layui 原生是不支持的,所以在自己的项目里需要将 treetable 作为第3方扩展模块注册。
3、且 css 是通过 js 追加的``,要改变为手动 link 标签引入。
// treetable.js
layui.addcss("modules/treetable/treetable.css") // 绝对路径添加 treetable.css 样式表
layui.link(layui.cache.base + '/treetable-lay/treetable.css'); // 可以用这个追加
2.3、处理
1、将 treetable 放入 layui 中;
2、手动引入 css;
/* https://gitcode.com/fangyinggui/study_code/blob/main/layui/19-1-1.html */
<link rel="stylesheet" href="/layui/extends_modules/layui-treetable/treetable.css" />
也可以类似库中的处理用 layui.addcss() 在当前页面添加。
3、作为第3方模块注册;
layui
.config({
base: '/layui/extends_modules',
})
.extend({
treetable: '/layui-treetable/treetable',
})
.use(['form', 'treetable', 'layer'], function () {
var layer = layui.layer,
form = layui.form,
$ = layui.jquery,
treetable = layui.treetable
console.log(1, '有没有treetable 模块?', treetable)
var tree = layui.treetable({
// ...
})
})
4、使用 treetable;
2.4、api
tree.render()
【语法】
/**
* @brief: 渲染一个树表
* @param {Object} options 配置对象
* @return {*} tree 返回一个树表实例
*/
var tree = treetable.render(options)
options 参数详细
【示例】这里
elem
【键】传入元素选择器;
【键值】{String}
例如: ‘#test1’
spreadable
设置是否全展开;
false (默认) 不展开
checkbox
是否显示复选框;
false (默认) 不显示复选框
layout
【键】自定义表头结构。
【键值】{Array} 一个集合
子项配置每一列;
- name:渲染成表头单元格的名称;
- treeNodes:
- headerClass:设置列表头容器的类名;
- colClass:设置列单元格容器的类名;
- style:设置列样式(添加到 内联样式);
【注意】若显示复选框,在 .render() 调用后,需要手动调用 form.render() 渲染复选框。
nodes
【键】要渲染的节点数据。
【键值】{Array} 一个集合
子项配置每一列;
- id {String} 唯一标志;
- name {String} 节点名称;
- spread {Boolean} 是否展开状态;(默认 false)
- children {Object} 这个里面设置子节点;例如:
children: [{name: '子节点',spread: true,children: [{name: '子子节点',children: [……]}]}, {……}]
- checked {Boolean} 在 checkbox 配置项 为true的情况下,设置该节点是否勾选;
回调
【配置位置】所有回调都在 .redner(options) 参数的 callback 键值对象中配置。
【默认】不配置回调钩子,父节点都是允许被折叠和展开的。
treetable.render({
callbacK: {
// 在这里配置
}
})
callback.beforeCheck
【语法】
/**
* @brief: 用于 捕获勾选/取消勾选 之前的事件回调函数
* @param {treeNode} node 进行勾选或取消勾选的节点 JSON 数据对象
* @return {boolean} 根据返回值确定是否 允许勾选/取消勾选
* null (默认)返回值
* true 允许勾选
* false 将不会改变勾选状态,并且无法触发 onCheck 事件回调函数
*/
function treetableBeforeCheck(node) {
return null
}
treetable.render({
// ...
callback: {
beforeCheck: treetableBeforeCheck,
},
})
callback.onCheck
【语法】
/**
* @brief: 用于 捕获checkbox被勾选 或 取消勾选的 事件回调函数
* 【特殊】如果设置了 callback.beforeCheck 方法,且返回 false,将无法触发 onCheck 事件回调函数。
* @param {treeNode} node 被勾选或取消勾选的 节点JSON数据对象
* @return {boolean} 根据返回值确定是否 允许勾选/取消勾选
* null (默认)返回值
* true 允许勾选
* false 将不会改变勾选状态,并且无法触发 onCheck 事件回调函数
*/
function treetableOnCheck(node) {
return null
}
treetable.render({
// ...
callback: {
beforeCheck: treetableOnCheck,
},
})
【示例】beforeCheck 和 onCheck
【示例】beforeCheck 返回 false
【应用1】这里可以动态判断后,自定义复选框的勾选。
callback.beforeCollapse
【语法】
/**
* @brief: 用于 捕获节点折叠之前 的事件回调函数
* 【特殊】如果设置了 callback.beforeCollapse,且返回 false 或 null,将无法触发 onCollapse 事件回调函数。
* @param {treeNode} node 被折叠的节点 JSON 数据对象
* @return {boolean}
* null 将不会折叠节点!!
* true (默认)允许父节点折叠/展开
* false 将不会折叠节点,也无法触发 onCollapse 事件回调函数
*/
function treetableBeforeCollapse(node) {
return null
}
treetable.render({
// ...
callback: {
beforeCollapse: treetableBeforeCollapse,
},
})
【示例】beforeCollapse 和 onCollapse
callback.onCollapse
【语法】
/**
* @brief: 用于 捕获节点被折叠 的事件回调函数
* @param {treeNode} node 被折叠的节点 JSON 数据对象
* @return {boolean} 这里返回值没什么效果
*/
function treetableOnCollapse(node) {
return null
}
treetable.render({
// ...
callback: {
onCollapse: treetableOnCollapse,
},
})
【示例】beforeCollapse 和 onCollapse
【示例】beforeCollapse 返回 false (切换注释)
callback.beforeExpand
【语法】
/**
* @brief: 用于 捕获父节点展开之前 的事件回调函数
* 【特殊】如果设置了 callback.beforeExpand 方法,且返回 false,将无法触发 onExpand 事件回调函数。
* @param {JSON} node 被展开的节点 JSON 数据对象
* @return {*} 这里返回值没什么效果
*/
function treetableBeforeExpand(node) {
return null
}
treetable.render({
// ...
callback: {
beforeExpand: treetableBeforeExpand,
},
})
【示例】beforeCollapse 和 onCollapse
callback.onExpand
【语法】
/**
* @brief: 用于 捕获节点被展开 的事件回调函数
* @param {JSON} node 被展开的节点 JSON 数据对象
* @return {boolean} 根据返回值确定是否允许展开操作
* null /
* true /
* false /
*/
function treetableOnExpand(node) {
return null
}
treetable.render({
// ...
callback: {
onExpand: treetableOnExpand,
},
})
【示例】beforeCollapse 和 onCollapse
2.5、实例方法
【原理】这些实例方法挂载在 treetable 的原型对象上。
var tree = layui.treetable(options);
【示例】treetable 实例对象
【tip】 下面的 tree 就是 treetable 实例化后的实例对象。
【示例】实例的一些方法
tree.collapse()
// 折叠(所有)菜单;
tree.collapse();
tree.collapseNode()
var node = tree.getNode("2");
/**
* @brief: 折叠某个节点
* @param {} node 节点信息
* @param {} boolean
* true:子节点跟随父节点一样折叠;
* false:子节点不折叠;
* @return {*}
*/
tree.collapseNode(node, boolean);
tree.expand()
// 展开(所有)菜单;
tree.expand();
tree.expandNode()
var node = tree.getNode("2");
/**
* @brief: 展开某个节点
* @param {} node 节点信息
* @param {} boolean
* true:子节点跟随父节点一样展开;
* false:子节点不展开;
* @return {*}
*/
tree.expandNode(node, boolean);
tree.getSelected()
/**
* @brief: 获取选中节点
* @return {nodes} 节点集合
*/
const nodes = tree.getSelected();
tree.getNode(id)
/**
* @brief: 获取某个特定节点
* @param {string} id 节点的id
* @return {*}
*/
tree1.getNode(id);
// demo
var node = tree.getNode("2");
tree.getNodes()
/**
* @brief: 获取所有节点
* @return {*} 返回一个所有节点的集合
*/
const result5 = tree.getNodes()
注意:这里源码不对,修改了一下;
const obj = {
getNodes: function () {
var a = this,
arr = [], // !这里缺少空 arr 定义,下面调用 push 会报错
oi = new i((v = v || {})),
nt = tt[v.selector]
for (var key in nt.mapping) {
var treeNode = nt.mapping[key]
if (treeNode && treeNode.item && treeNode.id != 'root') {
arr.push(treeNode.item)
}
}
return arr
},
}
tree.getSelected()
// 销毁 treetable
tree.getSelected();
tree.checkNode()
var node = tree.getNode("1");
/**
* @brief: 勾选或取消勾选节点
* @param {} node 节点信息
* @param {} boolean
* true:勾选;
* false:取消勾选;
* @return {*}
*/
tree.checkNode(node, boolean)
tree.setChkDisabled()
var node = tree.getNode("1");
/**
* @brief: 禁用或解禁 某个节点的 checkbox复选
* @param {} node 节点信息
* @param {} boolean
* true:禁用;
* false:解禁;
* @return {*}
*/
tree.setChkDisabled(node, boolean)
注意:该 api 调用不是很明显,可点击 “全部复选”进行验证。
tree.checkAllNodes()
/**
* @brief:
* @param {boolean} boolean
* true 全选
* false 取消全选
* @return {*}
*/
tree.checkAllNodes(boolean);
tree.getNodeByParam()
/**
* @brief: 根据节点数据的属性搜索
* @param {} key 需要精确匹配的属性名称
* @param {} value:需要精确匹配的属性值
* @param {} [parentNode]: (可选)可以指定在某个父节点下的子节点中搜索,忽略此参数,表示在全部节点中搜索
* @return {json} node
* null 没有结果返回null;
* 获取条件完全匹配的节点数据 JSON 对象集合
*/
// var node = tree.getNodeByParam(key, value, parentNode);
// 例如:
var node = tree.getNodeByParam("name", "子节点21", tree.getNode("1"));
编辑功能
添加节点
【核心】添加节点 api。
tree.addNode(targetNode, node)
【添加1】添加根节点
/*
tree.addNode() api 添加节点;
添加根节点时,第1参数为 null;
第2参数是一个节点对象,children 字段是一级节点。
*/
const node = {
id: '4',
name: '父节点4',
children: [
{
id: '41',
name: '子节点41',
children: [
{
id: '411',
name: '子节点411',
children: [
{
id: '4111',
name: '子节点4111',
},
],
},
],
},
{
id: '42',
name: '子节点42',
children: [
{
id: '421',
name: '子节点421',
children: [
{
id: '4211',
name: '子节点4211',
},
],
},
],
},
],
}
tree.addNode(null, node);
【添加2】某个节点下添加子节点
/*
类似上面;
只是第1参数,变成了某个节点对象
*/
const node = {
id: '22',
name: '子节点22',
children: [
{
id: '221',
name: '子节点221',
children: [
{
id: '2211',
name: '子节点2211',
children: [
{
id: '22111',
name: '子节点22111',
},
],
},
],
},
{
id: '222',
name: '子节点222',
children: [
{
id: '2221',
name: '子节点2221',
children: [
{
id: '22211',
name: '子节点22211',
},
],
},
],
},
],
}
tree.addNode(tree.getNode("2"), node);
编辑节点
var node = tree.getNode("3");
node.name = '我就是变个名字';
tree.editNodeName(node);
删除节点
var node = tree.getNode("2");
tree.removeNode(node);
// tip: 会连同所有子节点一起删除
3.1、treetable-lay
【简介】在 layui 数据表格之上进行扩展实现。
3.2、使用
1、下载包的整个文件夹,放在项目里;
2、使用模块加载的方式使用;
layui.config({
base: 'module/'
}).extend({
treetable: 'treetable-lay/treetable'
}).use(['treetable'], function () {
var treetable = layui.treetable;
});
3、渲染表格;
<table id="table1" class="layui-table" lay-filter="table1"></table>
<script>
layui.use(['treetable'], function () {
var treetable = layui.treetable;
// 渲染表格
treetable.render({
treeColIndex: 2, // treetable新增参数
treeSpid: -1, // treetable新增参数
treeIdName: 'd_id', // treetable新增参数
treePidName: 'd_pid', // treetable新增参数
treeDefaultClose: true, // treetable新增参数
treeLinkage: true, // treetable新增参数
elem: '#table1',
url: 'json/data1.json',
cols: [[
{type: 'numbers'},
{field: 'id', title: 'id'},
{field: 'name', title: 'name'},
{field: 'sex', title: 'sex'},
{field: 'pid', title: 'pid'},
]]
});
});
</script>
4、修改下样式表追加位置:
// treetable.js 内搜索 xxx.css
layui.link(layui.cache.base + '/treetable-lay/treetable.css');
3.3、说明
【注意】
1、可以使用 url 传递数据,也可以使用 data 传递数据,如果使用 url 传递数据,参数是 where 字段。
(和 layui table 组件的使用方式一致。)
2、其他注意实现写在下面。
【数据格式!!】
总而言之就是以id、pid的形式,不是以subMenus的形式,当然id、pid这两个字段的名称可以自定义:
示例返回:
{
"code": 0,
"msg": "ok",
"data": [{
"id": 1,
"name": "xx",
"sex": "male",
"pid": -1
},{
"id": 2,
"name": "xx",
"sex": "male",
"pid": 1
}
]
}
3.4、参数说明
【说明】layui table 组件的所有参数都可以用,除此之外 treetable 新增的参数有:
table 组件
treeColIndex
【键】树形图标(箭头、文件夹、文件图标)显示在第几列。
【键值】{int} 0, 1, 2, 3 …
【注意】
1、是必填项;
2、正整数值从0开始计数;(table cols 数组下标)
3、若有序列号或复选列,也计算在数量内;
【示例】树表图标设置在其它列
treeSpid
【键】设置最上级的父级id。
!必填项
【键值】{object}
【例如】这个渲染中,
设置的是 treeSpid: -1,
data 渲染数据中,id: 1 和 id: 5 的节点被渲染成一级节点;
treetable 是以 id 和 pid 字段来渲染树形结构的,如果你的数据没有 id 和 pid 字段,你可以指定 id 和 pid 字段的名称(用下面2个参数)。
treeIdName
【键】id 在你的数据字段中的名称。。
【键值】{string}
【示例】自定义 treetable 渲染中 id 在数据中的字段,
treePidName
【键】pid 在你的数据字段中的名称。。
【键值】{string}
【示例】自定义 treetable 渲染中 pid 在数据中的字段,
treeDefaultClose
【键】是否默认折叠
【键值】{boolean}
false (默认)是全部展开的
true 设置表格初始都折叠。
【示例】对比其它是示例,这里渲染表格时,默认展开树表,
treeLinkage
【键】父级展开时是否自动展开所有子级。
【键值】{boolean}
false (默认)父级展开不展开子集。
true 父级展开,同时展开子集。
【示例】父级展开时,自动展开子集,
注意!!!
1、不能使用分页功能,即使写了 page: true,也会忽略该参数;(所以这个参数默认是 false)
2、不能使用排序功能,不要开启排序功能;
3、!!table.reload() 不能实现刷新,请参考 demo 的刷新;
4、除了文档上写的 treetable.xxx 的方法之外,其他数据表格的方法都使用 table.xxx;
5、建议删除和修改操作 请求完后台之后,刷新(重新渲染)表格,最好不要使用 obj.delete 方式删除。
3.5、静态方法
treetable.expandAll()
/**
* @brief: 展开所有节点
* @param {strign} selector 树表渲染到的 table 元素的 id 选择器
* @return {*}
*/
// treetable.expandAll(selector)
// demo
treetable.expandAll('#table1')
【示例】手动全部展开,
treetable.foldAll()
/**
* @brief: 折叠所有节点
* @param {strign} selector 树表渲染到的 table 元素的 id 选择器
* @return {*}
*/
// treetable.foldAll(selector)
// demo
treetable.foldAll('#table1')
【示例】手动全部展开
3.6、自定义树表图标
【定义位置】
若项目中统一 一种风格的图标,则在 treetable.css 中修改。
若是单独的,则定义一个样式表,单独引入。
【定义方式】
(1)使用 layui 组件的图标;
(2)使用 iconfont 自定义的图标;
【使用 layui 组件的图标】
渲染效果
统一项目图标风格 + 使用 layui 自己的图标,
【使用 iconfont 自定义的图标】和上面修改类似
1、先将自定义图标上传到 iconfont;
2、下载到项目,link 标签引入;
<link rel="stylesheet" href="/layui/treetable-lay/iconfont/iconfont.css" />
3、追加自定义样式表,因为默认的 treetable.css 是js 追加的,若不追加 css,会被这里的覆盖;(或者将 js 追加 treetable.css 改为 link 引入)
layui.link(location.origin + '/layui/treetable-lay/style2.css')
4、自定义图标的内容;
【示例】iconfont 自定义图标
3.6、重载树表
【注意】虽然上面说了除树表自己的 配置项和api 都用 table 组件的,单这里重载不能调用 table.reload() api。
【实现】重新调用 .render() api。
var renderTable = function() {
treetable.render({
// ...
})
}
// 事件监听中重新调用此方法
【示例】重载表格
【拓展】增强用户体验 —— load 加载提示
<button class="btn1">重载树表</button>
<hr />
<table id="table1" class="layui-table" lay-filter="table1"></table>
function treetableRender() {
layer.load(2)
treetable.render({
...defaultConfig,
...{
done: function () {
layer.closeAll('loading')
},
},
})
}
treetableRender() // 默认渲染
$('.btn1').on('click', function () {
treetableRender()
})
3.7、监听工具条
【语法】同 table 组件。
// 1、cols 中设置表头
// { templet: '#oper-col', title: 'oper' },
// 2、设置解析的工具栏模板;
<!-- 操作列 -->
<script type="text/html" id="oper-col">
<a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="edit">修改</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
</script>
// 3、监听工具条 (依赖 table 模块)
table.on('tool(table1)', function (obj) {
var data = obj.data;
var layEvent = obj.event;
if (layEvent === 'del') {
layer.msg('删除' + data.id);
} else if (layEvent === 'edit') {
layer.msg('修改' + data.id);
}
});
【示例】树表工具栏监听