- 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
199 lines
6.9 KiB
PHP
199 lines
6.9 KiB
PHP
<?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\controller;
|
|
|
|
use think\facade\Cache;
|
|
use think\facade\Request;
|
|
use think\facade\View;
|
|
use ywxapp\model\CommonNavMenu as NavMenu;
|
|
use ywxapp\controller\BackendBase;
|
|
|
|
/**
|
|
* 前台主导航菜单管理(支持两级下拉,可后台配置).
|
|
*/
|
|
class Navbar extends BackendBase
|
|
{
|
|
protected function initialize()
|
|
{
|
|
}
|
|
|
|
/**
|
|
* 列表页.
|
|
*/
|
|
public function index()
|
|
{
|
|
if (Request::isAjax()) {
|
|
$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).
|
|
*/
|
|
public function edit($id = 0)
|
|
{
|
|
$row = $id ? NavMenu::find($id) : null;
|
|
if (Request::isPost()) {
|
|
$data = [
|
|
'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),
|
|
];
|
|
if ($data['title'] === '') {
|
|
return json(['code' => 1, 'msg' => '请输入菜单名称']);
|
|
}
|
|
// 不能把自己设为自己的父级
|
|
if ($id && $data['parent_id'] === (int) $id) {
|
|
return json(['code' => 1, 'msg' => '父级不能选择自己']);
|
|
}
|
|
if ($id) {
|
|
$row = NavMenu::find($id);
|
|
$row->save($data);
|
|
return json(['code' => 0, 'msg' => '已保存']);
|
|
}
|
|
NavMenu::create($data);
|
|
return json(['code' => 0, 'msg' => '已添加']);
|
|
}
|
|
// 父级下拉选项(仅一级)
|
|
$parents = NavMenu::where('parent_id', 0)
|
|
->where('delete_at', 0)
|
|
->order('sort', 'asc')
|
|
->field('id,title')
|
|
->select();
|
|
View::assign('parents', $parents);
|
|
View::assign('row', $row);
|
|
return View::fetch();
|
|
}
|
|
|
|
/**
|
|
* 保存(弹窗式 AJAX 提交,与前端 nav.js 对应).
|
|
*/
|
|
public function save()
|
|
{
|
|
$data = [
|
|
'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),
|
|
];
|
|
if ($data['title'] === '') {
|
|
return json(['code' => 1, 'msg' => '请输入菜单名称']);
|
|
}
|
|
$id = (int) Request::post('id', 0);
|
|
if ($id) {
|
|
if ($data['parent_id'] === $id) {
|
|
return json(['code' => 1, 'msg' => '父级不能选择自己']);
|
|
}
|
|
NavMenu::find($id)->save($data);
|
|
return json(['code' => 0, 'msg' => '已保存']);
|
|
}
|
|
NavMenu::create($data);
|
|
return json(['code' => 0, 'msg' => '已添加']);
|
|
}
|
|
|
|
/**
|
|
* 更新(与 save 同逻辑,兼容 PUT).
|
|
*/
|
|
public function update()
|
|
{
|
|
return $this->save();
|
|
}
|
|
|
|
/**
|
|
* 删除(支持单 id 或批量 ids 逗号分隔;有子项时拒绝,避免孤儿).
|
|
*/
|
|
public function delete($id = 0)
|
|
{
|
|
$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', 'in', $ids)->where('delete_at', 0)->count();
|
|
if ($hasChild) {
|
|
return json(['code' => 1, 'msg' => '请先删除所选菜单下的子项']);
|
|
}
|
|
NavMenu::destroy($ids);
|
|
return json(['code' => 0, 'msg' => '已删除']);
|
|
}
|
|
|
|
/**
|
|
* 切换显示/隐藏.
|
|
*/
|
|
public function status($id = 0, $status = 1)
|
|
{
|
|
$id = $id ?: (int) Request::post('id', 0);
|
|
if (! $id) {
|
|
return json(['code' => 1, 'msg' => '参数错误']);
|
|
}
|
|
NavMenu::find($id)->save(['status' => (int) $status]);
|
|
return json(['code' => 0, 'msg' => '已更新']);
|
|
}
|
|
}
|