feat: 公共表更名 common/member 前缀 + 插件命令自动加载 + 版本 1.1.1
- 18 张公共表更名(addon/attachment/configure/links/spider_log/spider_stat/sms/notice/ad/task/prop/medal/help/card -> common_*,member_wallets->member_wallet,score_rule/score_log -> member_*,addon_config->common_addonconf),模型全部对齐新表名,Db 直引用清零 - Attachment 模型补 表名绑定,修复富文本上传查 wxapp_attachment 1146 隐患 - install.sql + 迁移 SQL:backend_admin/backend_role delete_at 默认 0,修复软删除(NULL != 0)误过滤导致后台菜单为空 - AppService::boot() 支持插件 info.php 声明 commands 自动注册插件命令(psr-4 自动加载,坏类名自动跳过) - 各插件(haonav/mqttbroker/wxchat/articles/blog/forum 等)字段与配置同步调整 - 框架版本 1.1.0 -> 1.1.1
This commit is contained in:
@@ -121,6 +121,11 @@
|
||||
$.post(window.location.href, data.field, function (r) {
|
||||
if (r.code === 0) {
|
||||
layer.msg(r.message || '保存成功', { icon: 1 });
|
||||
// 保存成功后自动跳回插件列表(兼容 layui admin 的 tab 场景),
|
||||
// 避免停留在配置 tab 需要手动关闭重开才能看到列表。
|
||||
setTimeout(function () {
|
||||
window.location.href = '{:url("addon/index")}';
|
||||
}, 800);
|
||||
} else {
|
||||
layer.msg(r.message || '保存失败', { icon: 2 });
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="data-search-btn">
|
||||
<i class="layui-icon layui-icon-search"></i>
|
||||
</button>
|
||||
<button class="layui-btn layui-btn-primary" id="btn-reset">
|
||||
<button type="button" class="layui-btn layui-btn-primary" id="btn-reset">
|
||||
<i class="layui-icon layui-icon-refresh"></i>
|
||||
</button>
|
||||
</div>
|
||||
@@ -27,15 +27,16 @@
|
||||
<!-- 顶部工具条 -->
|
||||
<script type="text/html" id="tableBar">
|
||||
<div class="layui-btn-group">
|
||||
<a class="layui-btn layui-btn-sm layui-btn-primary" title="新建菜单" lay-event="dataCreate" data-perm="nav:add"> <i class="layui-icon layui-icon-add-1"></i> </a>
|
||||
<a class="layui-btn layui-btn-sm layui-btn-primary" title="新建菜单" lay-event="dataCreate" data-perm="nav: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="nav:delete"> <i class="layui-icon layui-icon-delete"></i> 批量删除 </a>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<!-- 操作列模板 -->
|
||||
<script type="text/html" id="dataBar">
|
||||
<a class="layui-btn layui-btn-xs" lay-event="update" data-perm="nav:edit">编辑</a>
|
||||
{{# if(d.parent_id == 0) { }}
|
||||
<a class="layui-btn layui-btn-xs" lay-event="addChild" data-perm="nav:add">加子项</a>
|
||||
{{# if(d.pid == 0) { }}
|
||||
<a class="layui-btn layui-btn-xs" lay-event="create" data-perm="nav:add">加子项</a>
|
||||
{{# } }}
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="delete" data-perm="nav:delete">删除</a>
|
||||
</script>
|
||||
@@ -45,15 +46,6 @@
|
||||
<input type="checkbox" name="status" value="{{d.id}}" lay-skin="switch" lay-text="显示|隐藏" lay-filter="statusSwitch" {{ d.status == 1 ? 'checked' : '' }}>
|
||||
</script>
|
||||
|
||||
<!-- 父级标识 -->
|
||||
<script type="text/html" id="parentTpl">
|
||||
{{# if(d.parent_id == 0) { }}
|
||||
<span class="layui-badge layui-bg-blue">一级</span>
|
||||
{{# } else { }}
|
||||
<span class="layui-badge-rim">二级</span>
|
||||
{{# } }}
|
||||
</script>
|
||||
|
||||
<!-- 添加/编辑表单(抽屉) -->
|
||||
<script type="text/html" id="dataFormTpl">
|
||||
<form class="layui-form" style="padding: 20px 20px 0;">
|
||||
@@ -82,6 +74,20 @@
|
||||
<div class="layui-form-mid layui-word-aux">留空表示仅作为含有子项的父菜单(下拉)。</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">所属应用</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="app" lay-filter="appSel">
|
||||
<option value="">当前应用(frontend 前台)</option>
|
||||
{{# layui.each(d.plugins || [], function(idx, pname){ }}
|
||||
<option value="{{pname}}" {{ d.app==pname ? 'selected' : '' }}>{{pname}}(插件)</option>
|
||||
{{# }); }}
|
||||
<option value="member" {{ d.app=='member' ? 'selected' : '' }}>member(会员中心)</option>
|
||||
<option value="backend" {{ d.app=='backend' ? 'selected' : '' }}>backend(后台)</option>
|
||||
</select>
|
||||
<div class="layui-form-mid layui-word-aux">选插件则跨应用生成域名链接(如 haonav/index/index);留空=当前应用。</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">图标</label>
|
||||
<div class="layui-input-block">
|
||||
@@ -101,14 +107,10 @@
|
||||
<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/javascript">
|
||||
console.log('nav index');
|
||||
window.navPlugins = {:json_encode($plugins)};
|
||||
layui.use('navbar', function () { });
|
||||
console.log(layui.nav);
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -132,319 +132,9 @@
|
||||
</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;
|
||||
|
||||
layui.use('group');
|
||||
|
||||
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>
|
||||
|
||||
Reference in New Issue
Block a user