451 lines
14 KiB
HTML
451 lines
14 KiB
HTML
<div class="layui-fluid">
|
||
<div class="layui-card">
|
||
<div class="layui-card-header"></div>
|
||
<div class="layui-card-body">
|
||
<table class="layui-hide" id="dataTable" lay-filter="dataTable"></table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- 表格顶部工具条 -->
|
||
<script type="text/html" id="tableBarTpl">
|
||
<div class="layui-btn-group">
|
||
<a
|
||
class="layui-btn layui-btn-sm layui-btn-primary"
|
||
title="新建数据"
|
||
lay-event="dataCreate"
|
||
data-perm="role:add">
|
||
<i class="layui-icon layui-icon-add-1"></i>
|
||
</a>
|
||
<a
|
||
class="layui-btn layui-btn-sm layui-btn-primary"
|
||
title="删除资源"
|
||
lay-event="dataDelete"
|
||
data-perm="role:delete"
|
||
><i class="layui-icon layui-icon-delete"></i>
|
||
</a>
|
||
<a
|
||
class="layui-btn layui-btn-sm layui-btn-primary"
|
||
title="资源回收站"
|
||
lay-event="dataRecybin"
|
||
data-perm="role:recyclebin"
|
||
><i class="layui-icon layui-icon-home"></i>
|
||
</a>
|
||
</div>
|
||
</script>
|
||
|
||
<!-- 表格数据工具条 -->
|
||
<script type="text/html" id="dataBarTpl">
|
||
<div class="layui-btn-group">
|
||
<a
|
||
class="layui-btn layui-btn-sm layui-btn-primary"
|
||
title="编辑数据"
|
||
lay-event="update"
|
||
data-perm="role:edit"
|
||
><i class="layui-icon layui-icon-edit"></i>
|
||
</a>
|
||
<a
|
||
class="layui-btn layui-btn-sm layui-btn-primary"
|
||
title="权限分配"
|
||
lay-event="permission"
|
||
data-perm="role:*"
|
||
><i class="layui-icon layui-icon-auz"></i>
|
||
</a>
|
||
{{# if(d.id > 1) { }}
|
||
<a
|
||
class="layui-btn layui-btn-sm layui-btn-primary"
|
||
title="删除数据"
|
||
lay-event="delete"
|
||
data-perm="role:delete"
|
||
><i class="layui-icon layui-icon-delete"></i>
|
||
</a>
|
||
{{# } }}
|
||
</div>
|
||
</script>
|
||
|
||
<!-- 表格回收站 -->
|
||
<script type="text/html" id="dataRecybinTpl">
|
||
<div class="layui-card">
|
||
<div class="layui-card-header"></div>
|
||
<div class="layui-card-body">
|
||
<table
|
||
class="layui-hide"
|
||
id="dataRecybinTable"
|
||
lay-filter="dataRecybinTable"></table>
|
||
</div>
|
||
</div>
|
||
</script>
|
||
|
||
<!-- 角色状态 -->
|
||
<script type="text/html" id="statusTpl">
|
||
{{# if(d.status == 1) { }}
|
||
<span class="layui-badge layui-bg-green">正常</span>
|
||
{{# } else { }}
|
||
<span class="layui-badge layui-bg-orange">禁用</span>
|
||
{{# } }}
|
||
</script>
|
||
|
||
<!-- 添加/编辑角色表单 -->
|
||
<script type="text/html" id="dataFormTpl">
|
||
<form class="layui-form layui-form-pane" lay-filter="wxapp-form" id="wxapp-form">
|
||
<input type="hidden" name="id" value="{{d.id||''}}">
|
||
<div class="layui-form-item">
|
||
<label class="layui-form-label">角色名称</label>
|
||
<div class="layui-input-block">
|
||
<input type="text" name="title" required lay-verify="required" placeholder="请输入角色名称" autocomplete="off" class="layui-input" value="{{d.title||''}}">
|
||
</div>
|
||
</div>
|
||
<div class="layui-form-item">
|
||
<label class="layui-form-label">编码</label>
|
||
<div class="layui-input-block">
|
||
<input type="text" name="name" required lay-verify="required" placeholder="请输入分组编码" autocomplete="off" class="layui-input" value="{{d.name||''}}">
|
||
</div>
|
||
</div>
|
||
<div class="layui-form-item">
|
||
<label class="layui-form-label">角色描述</label>
|
||
<div class="layui-input-block">
|
||
<textarea name="description" placeholder="请输入角色描述" class="layui-textarea">{{d.description||''}}</textarea>
|
||
</div>
|
||
</div>
|
||
<div class="layui-form-item">
|
||
<label class="layui-form-label">状态</label>
|
||
<div class="layui-input-block">
|
||
<input type="radio" name="status" value="1" title="启用" {{ d.status==undefined || d.status==1 ? 'checked' : '' }}>
|
||
<input type="radio" name="status" value="0" title="禁用" {{ d.status==0 ? 'checked' : '' }}>
|
||
</div>
|
||
</div>
|
||
<div class="layui-form-item layui-hide">
|
||
<button class="layui-btn" lay-submit="" lay-filter="wxapp-form-submit" id="wxapp-form-submit">提交</button>
|
||
</div>
|
||
</form>
|
||
</script>
|
||
|
||
<!-- 权限分配弹窗 -->
|
||
<script type="text/html" id="permissionFormTpl">
|
||
<div class="layui-card">
|
||
<div class="layui-card-header">
|
||
角色:{{d.name}}
|
||
<span style="color:#999;font-weight:normal;">(ID: {{d.id}})</span>
|
||
</div>
|
||
<div class="layui-card-body">
|
||
<div id="permissionTree"></div>
|
||
</div>
|
||
</div>
|
||
</script>
|
||
<script>
|
||
layui.use(
|
||
["table", "form", "layer", "jquery", "tree", "laytpl"],
|
||
function () {
|
||
var table = layui.table,
|
||
form = layui.form,
|
||
layer = layui.layer,
|
||
$ = layui.jquery,
|
||
tree = layui.tree,
|
||
laytpl = layui.laytpl;
|
||
|
||
function api(url, data, type) {
|
||
return $.ajax({
|
||
url: url,
|
||
type: type || "POST",
|
||
data: data || {},
|
||
dataType: "json",
|
||
});
|
||
}
|
||
|
||
function load() {
|
||
api("index").then(
|
||
function (res) {
|
||
if (res.code !== 0) {
|
||
layer.msg(res.message || "加载失败", { icon: 2 });
|
||
return;
|
||
}
|
||
renderTable(res.data || []);
|
||
},
|
||
function () {
|
||
layer.msg("请求失败", { icon: 2 });
|
||
},
|
||
);
|
||
}
|
||
|
||
function renderTable(list) {
|
||
table.render({
|
||
elem: "#dataTable",
|
||
data: list,
|
||
page: false,
|
||
cols: [
|
||
[
|
||
{ type: "checkbox", width: 40 },
|
||
{ field: "id", title: "ID", width: 60 },
|
||
{ field: "name", title: "编码", width: 140 },
|
||
{ field: "title", title: "角色名称", minWidth: 140 },
|
||
{ field: "description", title: "描述", minWidth: 160 },
|
||
{
|
||
field: "status",
|
||
title: "状态",
|
||
width: 90,
|
||
templet: "#statusTpl",
|
||
},
|
||
{
|
||
title: "操作",
|
||
width: 200,
|
||
templet: "#dataBarTpl",
|
||
fixed: "right",
|
||
},
|
||
],
|
||
],
|
||
});
|
||
}
|
||
|
||
// 新建 / 编辑角色
|
||
function openForm(row) {
|
||
laytpl($("#dataFormTpl").html()).render(row || {}, function (html) {
|
||
layer.open({
|
||
type: 1,
|
||
title: row ? "编辑角色" : "新建角色",
|
||
area: ["480px", "auto"],
|
||
content: html,
|
||
btn: ["保存", "取消"],
|
||
success: function () {
|
||
form.render("radio");
|
||
},
|
||
yes: function (index, layero) {
|
||
var params = $(layero).find("#wxapp-form").serialize();
|
||
if (row) {
|
||
params += "&_method=PUT";
|
||
}
|
||
api(row ? "update" : "save", params, "POST").then(
|
||
function (res) {
|
||
if (res.code !== 0) {
|
||
layer.msg(res.message || "保存失败", { icon: 2 });
|
||
return;
|
||
}
|
||
layer.msg(res.message || "已保存", { icon: 1 });
|
||
layer.close(index);
|
||
load();
|
||
},
|
||
function () {
|
||
layer.msg("保存失败", { icon: 2 });
|
||
},
|
||
);
|
||
},
|
||
});
|
||
});
|
||
}
|
||
|
||
// 权限分配(树形勾选 + 保存),后端 Role::permission 提供树与持久化
|
||
function openPermission(row) {
|
||
laytpl($("#permissionFormTpl").html()).render(row, function (html) {
|
||
layer.open({
|
||
type: 1,
|
||
title: "权限分配",
|
||
area: ["360px", "80%"],
|
||
content: html,
|
||
btn: ["保存", "取消"],
|
||
success: function (layero, index) {
|
||
api("permission?id=" + row.id, null, "GET").then(
|
||
function (res) {
|
||
if (res.code !== 0) {
|
||
layer.msg(res.message || "加载失败", { icon: 2 });
|
||
return;
|
||
}
|
||
tree.render({
|
||
elem: "#permissionTree",
|
||
data: convertTree(res.data || []),
|
||
showCheckbox: true,
|
||
id: "permTree",
|
||
});
|
||
// 超级管理员恒为 *,禁用保存
|
||
if (res.message && res.message.indexOf("超级管理员") >= 0) {
|
||
$(layero)
|
||
.find(".layui-layer-btn0")
|
||
.addClass("layui-btn-disabled");
|
||
layer.msg("超级管理员拥有全部权限(*),无需分配", {
|
||
icon: 0,
|
||
});
|
||
}
|
||
},
|
||
function () {
|
||
layer.msg("权限树加载失败", { icon: 2 });
|
||
},
|
||
);
|
||
},
|
||
yes: function (index) {
|
||
var ids = collectIds(tree.getChecked("permTree"), []);
|
||
api(
|
||
"permission?id=" + row.id,
|
||
{ permissions: ids, _method: "PUT" },
|
||
"POST",
|
||
).then(
|
||
function (res) {
|
||
if (res.code !== 0) {
|
||
layer.msg(res.message || "保存失败", { icon: 2 });
|
||
return;
|
||
}
|
||
layer.msg(res.message || "权限已保存", { icon: 1 });
|
||
layer.close(index);
|
||
},
|
||
function () {
|
||
layer.msg("保存失败", { icon: 2 });
|
||
},
|
||
);
|
||
},
|
||
});
|
||
});
|
||
}
|
||
|
||
// Role::permission 返回树节点用 name 展示,转换为 layui tree 的 title 字段
|
||
function convertTree(nodes) {
|
||
return (nodes || []).map(function (n) {
|
||
return {
|
||
title: n.name,
|
||
id: n.id,
|
||
checked: !!n.checked,
|
||
spread: !!n.spread,
|
||
children: convertTree(n.children),
|
||
};
|
||
});
|
||
}
|
||
|
||
// 递归收集勾选节点(含父子)的 id,前端去重
|
||
function collectIds(nodes, acc) {
|
||
(nodes || []).forEach(function (n) {
|
||
if (n.id && acc.indexOf(n.id) < 0) {
|
||
acc.push(n.id);
|
||
}
|
||
if (n.children) {
|
||
collectIds(n.children, acc);
|
||
}
|
||
});
|
||
return acc;
|
||
}
|
||
|
||
// 回收站
|
||
function openRecycle() {
|
||
api("recyclebin").then(
|
||
function (res) {
|
||
if (res.code !== 0) {
|
||
layer.msg(res.message || "加载失败", { icon: 2 });
|
||
return;
|
||
}
|
||
var rows = (res.data || [])
|
||
.map(function (r) {
|
||
return (
|
||
"<tr><td>" +
|
||
r.id +
|
||
"</td><td>" +
|
||
(r.title || "") +
|
||
"</td>" +
|
||
'<td><button type="button" class="layui-btn layui-btn-xs" onclick="restoreRole(' +
|
||
r.id +
|
||
')">恢复</button></td></tr>'
|
||
);
|
||
})
|
||
.join("");
|
||
if (!rows) {
|
||
rows =
|
||
'<tr><td colspan="3" style="color:#999;text-align:center;">回收站为空</td></tr>';
|
||
}
|
||
var html =
|
||
'<div style="padding:15px;"><table class="layui-table"><thead><tr>' +
|
||
"<th>ID</th><th>名称</th><th>操作</th></tr></thead><tbody>" +
|
||
rows +
|
||
"</tbody></table></div>";
|
||
layer.open({
|
||
type: 1,
|
||
title: "角色回收站",
|
||
area: ["420px", "70%"],
|
||
content: html,
|
||
});
|
||
},
|
||
function () {
|
||
layer.msg("加载失败", { icon: 2 });
|
||
},
|
||
);
|
||
}
|
||
window.restoreRole = function (id) {
|
||
api("restore", { ids: id, _method: "PUT" }, "POST").then(
|
||
function (res) {
|
||
if (res.code !== 0) {
|
||
layer.msg(res.message || "恢复失败", { icon: 2 });
|
||
return;
|
||
}
|
||
layer.msg(res.message || "已恢复", { icon: 1 });
|
||
load();
|
||
},
|
||
function () {
|
||
layer.msg("恢复失败", { icon: 2 });
|
||
},
|
||
);
|
||
};
|
||
|
||
// 行内操作
|
||
table.on("tool(dataTable)", function (obj) {
|
||
var row = obj.data;
|
||
if (obj.event === "update") {
|
||
openForm(row);
|
||
} else if (obj.event === "permission") {
|
||
openPermission(row);
|
||
} else if (obj.event === "delete") {
|
||
layer.confirm(
|
||
"确认删除角色「" + (row.title || row.name) + "」?",
|
||
function () {
|
||
api("delete", { ids: row.id, _method: "DELETE" }, "POST").then(
|
||
function (res) {
|
||
if (res.code !== 0) {
|
||
layer.msg(res.message || "删除失败", { icon: 2 });
|
||
return;
|
||
}
|
||
layer.msg(res.message || "已删除", { icon: 1 });
|
||
load();
|
||
},
|
||
function () {
|
||
layer.msg("删除失败", { icon: 2 });
|
||
},
|
||
);
|
||
},
|
||
);
|
||
}
|
||
});
|
||
|
||
// 顶部工具条
|
||
table.on("toolbar(dataTable)", function (obj) {
|
||
if (obj.event === "dataCreate") {
|
||
openForm(null);
|
||
} else if (obj.event === "dataDelete") {
|
||
var check = table.checkStatus("dataTable").data;
|
||
if (!check.length) {
|
||
layer.msg("请先选择角色", { icon: 2 });
|
||
return;
|
||
}
|
||
var ids = check
|
||
.map(function (r) {
|
||
return r.id;
|
||
})
|
||
.join(",");
|
||
layer.confirm(
|
||
"确认删除选中的 " + check.length + " 个角色?",
|
||
function () {
|
||
api("delete", { ids: ids, _method: "DELETE" }, "POST").then(
|
||
function (res) {
|
||
if (res.code !== 0) {
|
||
layer.msg(res.message || "删除失败", { icon: 2 });
|
||
return;
|
||
}
|
||
layer.msg(res.message || "已删除", { icon: 1 });
|
||
load();
|
||
},
|
||
function () {
|
||
layer.msg("删除失败", { icon: 2 });
|
||
},
|
||
);
|
||
},
|
||
);
|
||
} else if (obj.event === "dataRecybin") {
|
||
openRecycle();
|
||
}
|
||
});
|
||
|
||
load();
|
||
},
|
||
);
|
||
</script>
|