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:
ywxapp
2026-08-20 20:19:15 +08:00
parent 303f548664
commit 1d49e6f5ee
395 changed files with 13342 additions and 2012 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ use think\facade\Db;
use think\db\exception\DbException;
use think\exception\ValidateException;
use ywxapp\controller\BackendBase;
use ywxapp\model\Ad as AdModel;
use ywxapp\model\CommonAd as AdModel;
use app\backend\validate\Ad as AdValidate;
/**
+1 -1
View File
@@ -48,7 +48,7 @@ class Card extends BackendBase
$uids = array_filter(array_column($items, 'uid'));
$users = [];
if (!empty($uids)) {
$users = Db::name('member')->whereIn('uid', array_unique($uids))
$users = Db::name('member_user')->whereIn('uid', array_unique($uids))
->column('nickname,username', 'uid');
}
foreach ($items as &$row) {
+2 -2
View File
@@ -3,7 +3,7 @@
* @Author: YwxApp <ywx@ywxapp.cn>
* @Date: 2026-04-29 02:32:33
* @LastEditors: YwxApp <ywx@ywxapp.cn>
* @LastEditTime: 2026-08-03 13:32:15
* @LastEditTime: 2026-08-19 22:05:03
* @Description:
* @FilePath: \ywxapp_dev\app\backend\controller\Configure.php
* @CustomString: Copyright (c) 2026 YwxApp
@@ -14,7 +14,7 @@ namespace app\backend\controller;
use think\facade\View;
use think\Request;
use ywxapp\model\Configure as ConfigureModel;
use ywxapp\model\CommonConfigure as ConfigureModel;
use think\facade\Db;
use think\facade\Cache;
use ywxapp\controller\BackendBase;
+3 -3
View File
@@ -64,10 +64,10 @@ class Console extends BackendBase
public function hotsearch()
{
$admin = Db::name('backend_admin')->count();
$user = Db::name('member')->count();
$user = Db::name('member_user')->count();
$article = Db::name('articles_article')->count();
$links = Db::name('links')->count();
$addon = Db::name('addon')->count();
$links = Db::name('common_links')->count();
$addon = Db::name('common_addon')->count();
$data = [
['keywords' => '管理员', 'frequency' => $admin, 'userNums' => $admin],
['keywords' => '会员', 'frequency' => $user, 'userNums' => $user],
+13 -6
View File
@@ -1,14 +1,21 @@
<?php
/*
* @Author: YwxApp <ywx@ywxapp.cn>
* @Date: 2026-08-07 09:44:40
* @LastEditors: YwxApp <ywx@ywxapp.cn>
* @LastEditTime: 2026-08-19 22:06:38
* @Description:
* @FilePath: \ywxapp_dev\app\backend\controller\Help.php
* @CustomString: Copyright (c) 2026 YwxApp
*/
declare(strict_types=1);
namespace app\backend\controller;
use think\Request;
use think\facade\Db;
use think\db\exception\DbException;
use think\exception\ValidateException;
use ywxapp\controller\BackendBase;
use ywxapp\model\BaseModel;
use ywxapp\model\Help as HelpModel;
use ywxapp\controller\BackendBase;
use ywxapp\model\CommonHelp as HelpModel;
use app\backend\validate\Help as HelpValidate;
/**
@@ -22,7 +29,7 @@ class Help extends BackendBase
protected function initialize()
{
// 运行时自愈 help 表结构(category / view_count 等扩展列)
\ywxapp\model\Help::ensureSchema();
\ywxapp\model\CommonHelp::ensureSchema();
$this->model = new HelpModel();
}
+1 -1
View File
@@ -17,7 +17,7 @@ use think\facade\Db;
use think\db\exception\DbException;
use think\exception\ValidateException;
use ywxapp\controller\BackendBase;
use ywxapp\model\Links as LinksModel;
use ywxapp\model\CommonLinks as LinksModel;
use app\backend\validate\Links as LinksValidate;
/**
+2 -2
View File
@@ -7,7 +7,7 @@ use think\facade\Db;
use think\db\exception\DbException;
use think\exception\ValidateException;
use ywxapp\controller\BackendBase;
use ywxapp\model\Medal as MedalModel;
use ywxapp\model\CommonMedal as MedalModel;
use app\backend\validate\Medal as MedalValidate;
/**
@@ -173,7 +173,7 @@ class Medal extends BackendBase
$this->result->error('请选择勋章');
}
// 校验用户存在
$user = Db::name('member')->where('uid', $uid)->find();
$user = Db::name('member_user')->where('uid', $uid)->find();
if (! $user) {
$this->result->error('用户不存在');
}
+62 -9
View File
@@ -10,9 +10,10 @@ declare (strict_types = 1);
namespace app\backend\controller;
use think\facade\Cache;
use think\facade\Request;
use think\facade\View;
use app\backend\model\NavMenu;
use ywxapp\model\CommonNavMenu as NavMenu;
use ywxapp\controller\BackendBase;
/**
@@ -30,12 +31,56 @@ class Navbar extends BackendBase
public function index()
{
if (Request::isAjax()) {
$list = NavMenu::getAdminTree();
return json(['code' => 0, 'msg' => 'ok', 'data' => $list, 'count' => count($list)]);
$title = trim((string) Request::param('title', ''));
$rows = NavMenu::getAdminTree($title);
// 拍平成 treeTable isSimpleData 所需的扁平 pid 列表(treeTable 用 pid 自动建树)
$flat = [];
foreach ($rows as $p) {
$p['pid'] = (int) $p['parent_id'];
$flat[] = $p;
foreach (($p['child'] ?? []) as $c) {
$c['pid'] = (int) $c['parent_id'];
$flat[] = $c;
}
}
foreach ($flat as &$row) {
unset($row['child']);
}
return json(['code' => 0, 'msg' => 'ok', 'data' => $flat, 'count' => count($flat)]);
}
// 已启用插件列表:导航「所属应用」可选插件(插件页面同样支持绑定域名)
View::assign('plugins', $this->enabledPlugins());
return View::fetch();
}
/**
* 获取已启用插件名列表.
*/
private function enabledPlugins(): array
{
$raw = Cache::get('addon_loaded_config');
if (! is_array($raw) || empty($raw)) {
$addonDir = root_path() . 'addon' . DIRECTORY_SEPARATOR;
$raw = [];
if (is_dir($addonDir)) {
foreach (array_diff(scandir($addonDir), ['.', '..']) as $dir) {
$info = @include $addonDir . $dir . DIRECTORY_SEPARATOR . 'info.php';
if (is_array($info)) {
$raw[$dir] = $info;
}
}
}
}
$plugins = [];
foreach ($raw as $info) {
if (! empty($info['state']) && ! empty($info['name'])) {
$plugins[] = (string) $info['name'];
}
}
sort($plugins);
return $plugins;
}
/**
* 添加/编辑页(页面式,复用 edit.html).
*/
@@ -47,6 +92,7 @@ class Navbar extends BackendBase
'parent_id' => (int) Request::post('parent_id', 0),
'title' => trim((string) Request::post('title', '')),
'url' => trim((string) Request::post('url', '')),
'app' => trim((string) Request::post('app', '')),
'icon' => trim((string) Request::post('icon', '')),
'sort' => (int) Request::post('sort', 0),
'status' => (int) Request::post('status', 1),
@@ -86,6 +132,7 @@ class Navbar extends BackendBase
'parent_id' => (int) Request::post('parent_id', 0),
'title' => trim((string) Request::post('title', '')),
'url' => trim((string) Request::post('url', '')),
'app' => trim((string) Request::post('app', '')),
'icon' => trim((string) Request::post('icon', '')),
'sort' => (int) Request::post('sort', 0),
'status' => (int) Request::post('status', 1),
@@ -114,19 +161,25 @@ class Navbar extends BackendBase
}
/**
* 删除(支持单 id;有子项时拒绝,避免孤儿).
* 删除(支持单 id 或批量 ids 逗号分隔;有子项时拒绝,避免孤儿).
*/
public function delete($id = 0)
{
$id = $id ?: (int) Request::post('id', 0);
if (! $id) {
$ids = Request::post('ids', '');
if ($ids !== '' && $ids !== null) {
$ids = array_values(array_unique(array_filter(array_map('intval', explode(',', (string) $ids)))));
} else {
$id = $id ?: (int) Request::post('id', 0);
$ids = $id ? [$id] : [];
}
if (empty($ids)) {
return json(['code' => 1, 'msg' => '请选择要删除的项']);
}
$hasChild = NavMenu::where('parent_id', $id)->where('delete_at', 0)->count();
$hasChild = NavMenu::where('parent_id', 'in', $ids)->where('delete_at', 0)->count();
if ($hasChild) {
return json(['code' => 1, 'msg' => '请先删除该菜单下的子项']);
return json(['code' => 1, 'msg' => '请先删除所选菜单下的子项']);
}
NavMenu::destroy($id);
NavMenu::destroy($ids);
return json(['code' => 0, 'msg' => '已删除']);
}
+1 -1
View File
@@ -8,7 +8,7 @@ use think\db\exception\DbException;
use think\exception\ValidateException;
use ywxapp\controller\BackendBase;
use ywxapp\model\Notice as NoticeModel;
use app\backend\validate\Notice as NoticeValidate;
use app\backend\validate\CommonNotice as NoticeValidate;
/**
* 站点公告管理
+1 -1
View File
@@ -7,7 +7,7 @@ use think\facade\Db;
use think\db\exception\DbException;
use think\exception\ValidateException;
use ywxapp\controller\BackendBase;
use ywxapp\model\Prop as PropModel;
use ywxapp\model\CommonProp as PropModel;
use app\backend\validate\Prop as PropValidate;
/**
+1 -1
View File
@@ -7,7 +7,7 @@ use think\facade\Db;
use think\db\exception\DbException;
use think\exception\ValidateException;
use ywxapp\controller\BackendBase;
use ywxapp\model\Score as ScoreModel;
use ywxapp\model\MebmberScore as ScoreModel;
use app\backend\validate\Score as ScoreValidate;
/**
+1 -1
View File
@@ -7,7 +7,7 @@ use think\facade\Db;
use think\db\exception\DbException;
use think\exception\ValidateException;
use ywxapp\controller\BackendBase;
use ywxapp\model\Shop as ShopModel;
use ywxapp\model\CommonShop as ShopModel;
use app\backend\validate\Shop as ShopValidate;
/**
+1 -1
View File
@@ -7,7 +7,7 @@ use think\facade\Db;
use think\db\exception\DbException;
use think\exception\ValidateException;
use ywxapp\controller\BackendBase;
use ywxapp\model\Sms as SmsModel;
use ywxapp\model\CommonSms as SmsModel;
use app\backend\validate\Sms as SmsValidate;
/**
+4 -4
View File
@@ -40,7 +40,7 @@ class Spider extends BackendBase
$url = $this->request->param('url', '');
$date = $this->request->param('date', ''); // YYYY-MM-DD
$query = Db::name('spider_log');
$query = Db::name('common_spider_log');
if ($spider !== '') {
$query->where('spider', $spider);
}
@@ -88,11 +88,11 @@ class Spider extends BackendBase
$trend = ['dates' => [], 'series' => []];
try {
$rows = Db::name('spider_stat')
$rows = Db::name('common_spider_stat')
->where('stat_date', '>=', $d30)
->select()
->toArray();
$totalRows = Db::name('spider_stat')
$totalRows = Db::name('common_spider_stat')
->field('spider, SUM(`count`) AS total')
->group('spider')
->select()
@@ -163,7 +163,7 @@ class Spider extends BackendBase
$days = $this->request->param('days/d', 30);
$days = max(1, min(365, $days));
try {
$count = Db::name('spider_log')
$count = Db::name('common_spider_log')
->where('create_at', '<', time() - $days * 86400)
->delete();
$this->result->success('', "已清理 {$count} 条 {$days} 天前的明细日志");
+2 -2
View File
@@ -7,8 +7,8 @@ use think\facade\Db;
use think\db\exception\DbException;
use think\exception\ValidateException;
use ywxapp\controller\BackendBase;
use ywxapp\model\Task as TaskModel;
use ywxapp\model\Prop as PropModel;
use ywxapp\model\CommonTask as TaskModel;
use ywxapp\model\CommonProp as PropModel;
use app\backend\validate\Task as TaskValidate;
/**
-114
View File
@@ -1,114 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | YwxApp [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2026-2036 http://ywxapp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: ywxapp<admin@ywxapp.cn>
// +----------------------------------------------------------------------
declare (strict_types = 1);
namespace app\backend\model;
use think\model\concern\SoftDelete;
use ywxapp\model\BaseModel;
/**
* 前台主导航菜单模型(支持两级下拉).
*/
class NavMenu extends BaseModel
{
use SoftDelete;
protected $deleteTime = 'delete_at';
protected function getOptions(): array
{
return [
'strict' => true,
'name' => 'common_nav_menu',
'autoWriteTimestamp' => 'int',
'createTime' => 'create_at',
'updateTime' => 'update_at',
'defaultSoftDelete' => 0,
];
}
/**
* 运行时自愈:确保 common_nav_menu 主表存在(install.sql 为事实源).
*/
public static function ensureSchema(): void
{
BaseModel::ensureTableFromInstall(BaseModel::currentPrefix(), 'common_nav_menu');
}
protected function initialize()
{
parent::initialize();
self::ensureSchema();
}
/**
* 获取启用的导航树(两级:父 + 子)。
* @return array [{id,title,url,icon,child:[...]}]
*/
public static function getNavTree(): array
{
$list = self::where('status', 1)
->where('delete_at', 0)
->order('sort', 'asc')
->order('id', 'asc')
->field('id,parent_id,title,url,icon,sort')
->select()
->toArray();
$parents = [];
$childrenMap = [];
foreach ($list as $item) {
$item['ctrl'] = strtolower(strtok($item['url'], '/') ?: '');
if ((int) $item['parent_id'] === 0) {
$item['child'] = [];
$parents[] = $item;
} else {
$childrenMap[$item['parent_id']][] = $item;
}
}
foreach ($parents as &$p) {
if (isset($childrenMap[$p['id']])) {
$p['child'] = $childrenMap[$p['id']];
}
}
return $parents;
}
/**
* 后台列表:返回两级扁平树(含隐藏项).
*/
public static function getAdminTree(): array
{
$list = self::where('delete_at', 0)
->order('sort', 'asc')
->order('id', 'asc')
->field('id,parent_id,title,url,icon,sort,status')
->select()
->toArray();
$parents = [];
$childrenMap = [];
foreach ($list as $item) {
$item['ctrl'] = strtolower(strtok($item['url'], '/') ?: '');
if ((int) $item['parent_id'] === 0) {
$item['child'] = [];
$parents[] = $item;
} else {
$childrenMap[$item['parent_id']][] = $item;
}
}
foreach ($parents as &$p) {
if (isset($childrenMap[$p['id']])) {
$p['child'] = $childrenMap[$p['id']];
}
}
return $parents;
}
}
+5
View File
@@ -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 });
}
+21 -19
View File
@@ -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>
+2 -312
View File
@@ -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>