layui 自身提供一個 tree 樹形菜單,但是并不適用于權(quán)限控制中,比如:當(dāng)我需要選擇用戶組的權(quán)限(樹形結(jié)構(gòu)),需要使用 form 提交用戶所選權(quán)限數(shù)據(jù)的時(shí)候。
<form class="layui-form">
<div class="layui-form-item">
<label class="layui-form-label">角色名稱</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="name" placeholder="請輸入角色名稱" />
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">選擇權(quán)限</label>
<div class="layui-input-block">
<div id="LAY-auth-tree-index"></div>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn" type="submit" lay-submit lay-filter="LAY-auth-tree-submit">提交</button>
<button class="layui-btn layui-btn-primary" type="reset">重置</button>
</div>
</div>
</form>
layui.config({
base: 'extends/',
}).extend({
authtree: 'authtree',
});
layui.use(['jquery', 'authtree', 'form', 'layer'], function(){至此,一個最基礎(chǔ)的權(quán)限樹就已經(jīng)完成了,如果想實(shí)現(xiàn)演示GIF內(nèi)的各種擴(kuò)展功能,請繼續(xù)往下看,并參考源碼
var $ = layui.jquery;
var authtree = layui.authtree;
var form = layui.form;
var layer = layui.layer;
// 一般來說,權(quán)限數(shù)據(jù)是異步傳遞過來的
$.ajax({
url: 'tree.json',
dataType: 'json',
success: function(data){
var trees = data.data.trees;
// 如果后臺返回的不是樹結(jié)構(gòu),請使用 authtree.listConvert 轉(zhuǎn)換
authtree.render('#LAY-auth-tree-index', trees, {
inputname: 'authids[]',
layfilter: 'lay-check-auth',
autowidth: true,
});
}
});
});
<?php
$role_id = $_GET['role_id'];
$auth_list = .....;// 獲取數(shù)據(jù)庫中 權(quán)限表數(shù)據(jù)
$role_auth_list = ....;// 獲取數(shù)據(jù)庫中某個角色的 角色-權(quán)限表數(shù)據(jù)
$data = [
'code' => 0,
'msg' => '獲取成功',
'data' => [
'list' => $auth_list,
'checkedId' => array_column($role_auth_list, 'authid'),
],
];
echo json_encode($data);
{
"code": 0,
"msg": "獲取成功",
"data": {
"list": [
{ "id": 1, "name": "用戶管理", "pid": 0 },
{ "id": 2, "name": "用戶組管理", "pid": 0 },
{ "id": 3, "name": "角色管理", "pid": 2 },
{ "id": 4, "name": "添加角色", "pid": 3},
{ "id": 5, "name": "角色列表", "pid": 3 },
{ "id": 6, "name": "管理員管理", "pid": 0 },
{ "id": 7, "name": "添加管理員", "pid": 6 },
{ "id": 8, "name": "管理員列表", "pid": 6 }
],
"checkedId": [ 1, 2, 3, 4 ]
}
}
var trees = authtree.listConvert(res.data.list, {
primaryKey: 'id'
,startPid: 0
,parentKey: 'pid'
,nameKey: 'name'
,valueKey: 'id'
,checkedKey: res.data.checkedId
});
{
"code": 0,
"msg": "獲取成功",
"data": {
"trees":[
{"name": "用戶管理", "value": "xsgl", "checked": true, "list": [
{"name": "用戶組", "value": "xsgl-basic", "checked": true, "list": [
{"name": "本站用戶", "value": "xsgl-basic-xsxm", "checked": true, "list": [
{"name": "用戶列表", "value": "xsgl-basic-xsxm-readonly", "checked": true},
{"name": "新增用戶", "value": "xsgl-basic-xsxm-editable", "checked": false}
]},
{"name": "第三方用戶", "value": "xsgl-basic-xsxm", "checked": true, "list": [
{"name": "用戶列表", "value": "xsgl-basic-xsxm-readonly", "checked": true}
]}
]}
]},
{"name": "用戶組管理", "value": "sbgl", "checked": true, "list": [
{"name": "角色管理", "value": "sbgl-sbsjlb", "checked": true, "list":[
{"name": "添加角色", "value": "sbgl-sbsjlb-dj", "checked": true},
{"name": "角色列表", "value": "sbgl-sbsjlb-yl", "checked": false}
]},
{"name": "管理員管理", "value": "sbgl-sbsjlb", "checked": true, "list":[
{"name": "添加管理員", "value": "sbgl-sbsjlb-dj", "checked": true},
{"name": "管理員列表", "value": "sbgl-sbsjlb-yl", "checked": false}
]}
]}
]
}
}
{
"code": 0,
"msg": "獲取成功",
"data": {
"list": [
{ "id": 1, "name": "用戶管理", "alias": "yhgl", "palias": "0" },
{ "id": 2, "name": "用戶組管理", "alias": "yhzgl", "palias": "0" },
{ "id": 3, "name": "角色管理", "alias": "yhzgl-jsgl", "palias": "yhzgl" },
{ "id": 4, "name": "添加角色", "alias": "yhzgl-jsgl-tjjs", "palias": "yhzgl-jsgl" },
{ "id": 5, "name": "角色列表", "alias": "yhzgl-jsgl-jslb", "palias": "yhzgl-jsgl" },
{ "id": 6, "name": "管理員管理", "alias": "glygl", "palias": "0" },
{ "id": 7, "name": "添加管理員", "alias": "glygl-tjgly", "palias": "glygl" },
{ "id": 8, "name": "管理員列表", "alias": "glygl-glylb", "palias": "glygl" }
],
"checkedAlias": [ "yhgl", "yhzgl", "yhzgl-jsgl", "yhzgl-jsgl-tjjs" ]
}
}
{
"code": 0,
"msg": "獲取成功",
"data": {
"list": [
{ "id": 1, "name": "用戶管理", "pid": 0 },
{ "id": 2, "name": "用戶組管理", "pid": 0 },
{ "id": 3, "name": "角色管理", "pid": 2 },
{ "id": 4, "name": "添加角色", "pid": 3},
{ "id": 5, "name": "角色列表", "pid": 3 },
{ "id": 6, "name": "管理員管理", "pid": 0 },
{ "id": 7, "name": "添加管理員", "pid": 6 },
{ "id": 8, "name": "管理員列表", "pid": 6 }
],
"checkedId": [ 1, 2, 3, 4 ]
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>權(quán)限樹擴(kuò)展分享</title>
<link rel="stylesheet" type="text/css" href="layui/css/layui.css">
<script type="text/javascript" src="layui/layui.js"></script>
</head>
<body>
<div class="layui-container">
<div class="layui-row">
<div class="layui-col-md11 layui-col-md-offset1">
<fieldset class="layui-elem-field layui-field-title"><legend>權(quán)限樹操作演示</legend></fieldset>
<div class="layui-form">
<div class="layui-form-item">
<div class="layui-form-label">普通操作</div>
<div class="layui-form-block">
<button type="button" class="layui-btn layui-btn-primary" onclick="getMaxDept('#LAY-auth-tree-index')">獲取樹的深度</button>
<button type="button" class="layui-btn layui-btn-primary" onclick="checkAll('#LAY-auth-tree-index')">全選</button>
<button type="button" class="layui-btn layui-btn-primary" onclick="uncheckAll('#LAY-auth-tree-index')">全不選</button>
<button type="button" class="layui-btn layui-btn-primary" onclick="showAll('#LAY-auth-tree-index')">全部展開</button>
<button type="button" class="layui-btn layui-btn-primary" onclick="closeAll('#LAY-auth-tree-index')">全部隱藏</button>
<button type="button" class="layui-btn layui-btn-primary" onclick="getNodeStatus('#LAY-auth-tree-index')">獲取節(jié)點(diǎn)狀態(tài)</button>
</div>
</div>
<div class="layui-form-item">
<div class="layui-form-label">特殊操作</div>
<div class="layui-form-block">
<button type="button" class="layui-btn layui-btn-primary" onclick="showDept('#LAY-auth-tree-index')">展開到某層</button>
<button type="button" class="layui-btn layui-btn-primary" onclick="closeDept('#LAY-auth-tree-index')">關(guān)閉某層后所有的層</button>
<button type="button" class="layui-btn layui-btn-primary" onclick="listConvert('list.json')">列表轉(zhuǎn)樹</button>
</div>
</div>
</div>
</div>
<div class="layui-col-md6 layui-col-md-offset1">
<fieldset class="layui-elem-field layui-field-title"><legend>權(quán)限樹擴(kuò)展分享</legend></fieldset>
<!-- 此擴(kuò)展能遞歸渲染一個權(quán)限樹,點(diǎn)擊深層次節(jié)點(diǎn),父級節(jié)點(diǎn)中沒有被選中的節(jié)點(diǎn)會被自動選中,單獨(dú)點(diǎn)擊父節(jié)點(diǎn),子節(jié)點(diǎn)會全部 選中/去選中 -->
<form class="layui-form">
<div class="layui-form-item">
<label class="layui-form-label">角色名稱</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="name" placeholder="請輸入角色名稱" />
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">選擇權(quán)限</label>
<div class="layui-input-block">
<div id="LAY-auth-tree-index"></div>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn" type="submit" lay-submit lay-filter="LAY-auth-tree-submit">提交</button>
<button class="layui-btn layui-btn-primary" type="reset">重置</button>
</div>
</div>
</form>
</div>
</div>
</div>
</body>
<script type="text/javascript">
layui.config({
base: 'extends/',
}).extend({
authtree: 'authtree',
});
layui.use(['jquery', 'authtree', 'form', 'layer'], function(){
var $ = layui.jquery;
var authtree = layui.authtree;
var form = layui.form;
var layer = layui.layer;
// 初始化
$.ajax({
url: 'tree.json',
dataType: 'json',
success: function(data){
// 渲染時(shí)傳入渲染目標(biāo)ID,樹形結(jié)構(gòu)數(shù)據(jù)(具體結(jié)構(gòu)看樣例,checked表示默認(rèn)選中),以及input表單的名字
authtree.render('#LAY-auth-tree-index', data.data.trees, {
inputname: 'authids[]'
,layfilter: 'lay-check-auth'
// ,autoclose: false
// ,autochecked: false
// ,openchecked: true
// ,openall: true
,autowidth: true
});
// PS:使用 form.on() 會引起了事件冒泡延遲的BUG,需要 setTimeout(),并且無法監(jiān)聽全選/全不選
// PS:如果開啟雙擊展開配置,form.on()會記錄兩次點(diǎn)擊事件,authtree.on()不會
form.on('checkbox(lay-check-auth)', function(data){
// 注意這里:需要等待事件冒泡完成,不然獲取葉子節(jié)點(diǎn)不準(zhǔn)確。
setTimeout(function(){
console.log('監(jiān)聽 form 觸發(fā)事件數(shù)據(jù)', data);
// 獲取選中的葉子節(jié)點(diǎn)
var leaf = authtree.getLeaf('#LAY-auth-tree-index');
console.log('leaf', leaf);
// 獲取最新選中
var lastChecked = authtree.getLastChecked('#LAY-auth-tree-index');
console.log('lastChecked', lastChecked);
// 獲取最新取消
var lastNotChecked = authtree.getLastNotChecked('#LAY-auth-tree-index');
console.log('lastNotChecked', lastNotChecked);
}, 100);
});
// 使用 authtree.on() 不會有冒泡延遲
authtree.on('change(lay-check-auth)', function(data) {
console.log('監(jiān)聽 authtree 觸發(fā)事件數(shù)據(jù)', data);
// 獲取所有節(jié)點(diǎn)
var all = authtree.getAll('#LAY-auth-tree-index');
console.log('all', all);
// 獲取所有已選中節(jié)點(diǎn)
var checked = authtree.getChecked('#LAY-auth-tree-index');
console.log('checked', checked);
// 獲取所有未選中節(jié)點(diǎn)
var notchecked = authtree.getNotChecked('#LAY-auth-tree-index');
console.log('notchecked', notchecked);
// 獲取選中的葉子節(jié)點(diǎn)
var leaf = authtree.getLeaf('#LAY-auth-tree-index');
console.log('leaf', leaf);
// 獲取最新選中
var lastChecked = authtree.getLastChecked('#LAY-auth-tree-index');
console.log('lastChecked', lastChecked);
// 獲取最新取消
var lastNotChecked = authtree.getLastNotChecked('#LAY-auth-tree-index');
console.log('lastNotChecked', lastNotChecked);
});
authtree.on('deptChange(lay-check-auth)', function(data) {
console.log('監(jiān)聽到顯示層數(shù)改變',data);
});
}
});
form.on('submit(LAY-auth-tree-submit)', function(obj){
var authids = authtree.getAll('#LAY-auth-tree-index');
console.log('Choosed authids is', authids);
obj.field.authids = authids;
$.ajax({
url: 'tree.json',
dataType: 'json',
data: obj.field,
success: function(res){
layer.alert('提交成功!');
}
});
return false;
});
});
</script>
<script type="text/javascript">
// 獲取最大深度樣例
function getMaxDept(dst){
layui.use(['jquery', 'layer', 'authtree'], function(){
var layer = layui.layer;
var authtree = layui.authtree;
layer.alert('樹'+dst+'的最大深度為:'+authtree.getMaxDept(dst));
});
}
// 全選樣例
function checkAll(dst){
layui.use(['jquery', 'layer', 'authtree'], function(){
var layer = layui.layer;
var authtree = layui.authtree;
authtree.checkAll(dst);
});
}
// 全不選樣例
function uncheckAll(dst){
layui.use(['jquery', 'layer', 'authtree'], function(){
var layer = layui.layer;
var authtree = layui.authtree;
authtree.uncheckAll(dst);
});
}
// 顯示全部
function showAll(dst){
layui.use(['jquery', 'layer', 'authtree'], function(){
var layer = layui.layer;
var authtree = layui.authtree;
authtree.showAll(dst);
});
}
// 隱藏全部
function closeAll(dst){
layui.use(['jquery', 'layer', 'authtree'], function(){
var layer = layui.layer;
var authtree = layui.authtree;
authtree.closeAll(dst);
});
}
// 獲取節(jié)點(diǎn)狀態(tài)
function getNodeStatus(dst){
layui.use(['jquery', 'layer', 'authtree', 'laytpl'], function(){
var layer = layui.layer;
var authtree = layui.authtree;
var laytpl = layui.laytpl;
// 獲取所有節(jié)點(diǎn)
var all = authtree.getAll('#LAY-auth-tree-index');
// 獲取所有已選中節(jié)點(diǎn)
var checked = authtree.getChecked('#LAY-auth-tree-index');
// 獲取所有未選中節(jié)點(diǎn)
var notchecked = authtree.getNotChecked('#LAY-auth-tree-index');
// 獲取選中的葉子節(jié)點(diǎn)
var leaf = authtree.getLeaf('#LAY-auth-tree-index');
// 獲取最新選中
var lastChecked = authtree.getLastChecked('#LAY-auth-tree-index');
// 獲取最新取消
var lastNotChecked = authtree.getLastNotChecked('#LAY-auth-tree-index');
var data = [
{func: 'getAll', desc: '獲取所有節(jié)點(diǎn)', data: all},
{func: 'getChecked', desc: '獲取所有已選中節(jié)點(diǎn)', data: checked},
{func: 'getNotChecked', desc: '獲取所有未選中節(jié)點(diǎn)', data: notchecked},
{func: 'getLeaf', desc: '獲取選中的葉子節(jié)點(diǎn)', data: leaf},
{func: 'getLastChecked', desc: '獲取最新選中', data: lastChecked},
{func: 'getLastNotChecked', desc: '獲取最新取消', data: lastNotChecked},
];
var string = laytpl($('#LAY-auth-tree-nodes').html()).render({
data: data,
});
layer.open({
title: '節(jié)點(diǎn)狀態(tài)'
,content: string
,area: '800px'
,tipsMore: true
});
$('body').unbind('click').on('click', '.LAY-auth-tree-show-detail', function(){
layer.open({
type: 1,
title: $(this).data('title')+'-節(jié)點(diǎn)詳情',
content: '['+$(this).data('content')+']',
tipsMore: true
});
});
});
}
// 顯示到某層
function showDept(dst) {
layui.use(['layer', 'authtree', 'jquery'], function(){
var jquery = layui.jquery;
var layer = layui.layer;
var authtree = layui.authtree;
layer.prompt({title: '顯示到某層'}, function(value, index, elem) {
authtree.showDept(dst, value);
layer.close(index);
});
});
}
// 關(guān)閉某層以后的所有層
function closeDept(dst) {
layui.use(['layer', 'authtree', 'jquery'], function(){
var jquery = layui.jquery;
var layer = layui.layer;
var authtree = layui.authtree;
layer.prompt({title: '關(guān)閉某層以后的所有層'}, function(value, index, elem) {
authtree.closeDept(dst, value);
layer.close(index);
});
});
}
// 轉(zhuǎn)換列表
function listConvert(url) {
layui.use(['layer', 'authtree', 'jquery', 'form', 'code'], function(){
var jquery = layui.jquery;
var layer = layui.layer;
var authtree = layui.authtree;
var form = layui.form;
layer.open({
title: '列表轉(zhuǎn)樹演示'
,content: '<fieldset class="layui-elem-field layui-field-title"><legend>列表數(shù)據(jù)轉(zhuǎn)權(quán)限樹</legend></fieldset><form class="layui-form"> <div class="layui-form-item"> <label class="layui-form-label">多選權(quán)限</label> <div class="layui-input-block"> <div id="LAY-auth-tree-convert-index"></div> </div> </div> <div class="layui-form-item"> <div class="layui-input-block"> <button class="layui-btn" type="submit" lay-submit lay-filter="LAY-auth-tree-submit">提交</button> <button class="layui-btn layui-btn-primary" type="reset">重置</button> </div> </div></form><pre class="layui-code" id="LAY-auth-tree-convert-code"></pre>'
,area: ['800px', '400px']
,tipsMore: true
,success: function() {
$.ajax({
url: url,
dataType: 'json',
success: function(res){
$('#LAY-auth-tree-convert-code').text(JSON.stringify(res, null, 2));
layui.code({
title: '返回的列表數(shù)據(jù)'
});
// 支持自定義遞歸字段、數(shù)組權(quán)限判斷等
// 深坑注意:如果API返回的數(shù)據(jù)是字符串,那么 startPid 的數(shù)據(jù)類型也需要是字符串
var trees = authtree.listConvert(res.data.list, {
primaryKey: 'alias'
,startPid: '0'
,parentKey: 'palias'
,nameKey: 'name'
,valueKey: 'alias'
,checkedKey: res.data.checkedAlias
});
// 如果頁面中多個樹共存,需要注意 layfilter 需要不一樣
authtree.render('#LAY-auth-tree-convert-index', trees, {
inputname: 'authids[]',
layfilter: 'lay-check-convert-auth',
// openall: true,
autowidth: true,
});
}
});
},
});
});
}
</script>
<!-- 狀態(tài)模板 -->
<script type="text/html" id="LAY-auth-tree-nodes">
<style type="text/css">
.layui-layer-page .layui-layer-content{
padding: 20px;
line-height: 22px;
}
</style>
<table class="layui-table">
<thead>
<th>方法名</th>
<th>描述</th>
<th>節(jié)點(diǎn)</th>
</thead>
<tbody>
{{# layui.each(d.data, function(index, item) { }}
<tr>
<td>{{item.func}}</td>
<td>{{item.desc}}</td>
<td><a class="LAY-auth-tree-show-detail" href="javascript:;" data-title="{{item.desc}}" data-content="{{item.data.join(']<br>[')}}">查看詳情</a>({{item.data.length}})</td>
</tr>
{{# });}}
</tbody>
</table>
</script>
</html>