You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

212 lines
7.4 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\AdminMenu;
use App\Models\RoleMenuPermission;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Schema;
class AdminMenuController extends Controller
{
public function index(Request $request): JsonResponse
{
$query = AdminMenu::query()
->where('is_visible', true)
->where(function ($query) {
$query->whereNull('path')->orWhere('path', '!=', '/study-tours/categories');
})
->orderBy('sort')
->orderBy('id');
$user = $request->user();
// 场馆管理员与普通后台角色:按 role_menu_permissions 与「角色管理」勾选一致。
// 仅字面角色 super_admin 返回全部可见菜单(内置超管不因 full_access 等绕过勾选)。
$filterByRolePermissions = $user
&& Schema::hasTable('role_menu_permissions')
&& (($user->role ?? '') !== 'super_admin');
if ($filterByRolePermissions) {
$roleMenuIds = RoleMenuPermission::query()
->where('role', (string) $user->role)
->pluck('menu_id')
->map(fn ($id) => (int) $id)
->values();
if ($roleMenuIds->isEmpty()) {
return response()->json([]);
}
$query->whereIn('id', $roleMenuIds);
}
$menus = $query->get(['id', 'name', 'path', 'icon', 'parent_id', 'sort']);
$visibleIds = $menus->pluck('id')->map(fn ($id) => (int) $id)->values();
if ($visibleIds->isNotEmpty()) {
$parentIds = $menus->pluck('parent_id')->map(fn ($id) => (int) $id)->filter(fn ($id) => $id > 0)->unique()->values();
if ($parentIds->isNotEmpty()) {
$parents = AdminMenu::query()
->whereIn('id', $parentIds)
->where('is_visible', true)
->get(['id', 'name', 'path', 'icon', 'parent_id', 'sort']);
$menus = $menus->merge($parents)->unique('id')->sortBy([['sort', 'asc'], ['id', 'asc']])->values();
}
}
return response()->json($this->buildTree($menus));
}
public function listAll(Request $request): JsonResponse
{
$menus = AdminMenu::query()
->orderBy('sort')
->orderBy('id')
->get(['id', 'name', 'path', 'icon', 'parent_id', 'sort', 'is_visible']);
return response()->json($menus);
}
public function store(Request $request): JsonResponse
{
$this->ensureSuperAdmin($request);
$data = $request->validate([
'name' => ['required', 'string', 'max:100'],
'path' => ['nullable', 'string', 'max:255'],
'icon' => ['nullable', 'string', 'max:100'],
'parent_id' => ['nullable', 'integer', 'min:0'],
'sort' => ['nullable', 'integer', 'min:0'],
'is_visible' => ['nullable', 'boolean'],
]);
$parentId = (int) ($data['parent_id'] ?? 0);
if ($parentId > 0) {
$exists = AdminMenu::query()->where('id', $parentId)->exists();
abort_unless($exists, 422, '父级菜单不存在');
}
$menu = AdminMenu::create([
'name' => $data['name'],
'path' => $data['path'] ?? null,
'icon' => $data['icon'] ?? null,
'parent_id' => $parentId,
'sort' => (int) ($data['sort'] ?? 0),
'is_visible' => (bool) ($data['is_visible'] ?? true),
]);
$this->grantRoleMenuPermissionsForNewMenu($menu);
return response()->json($menu, 201);
}
/**
* 新建菜单默认不写入 role_menu_permissions侧栏会被完全过滤掉。
* 规则:有父级则继承父菜单已授权的角色;并保证 super_admin 至少可见。
*/
private function grantRoleMenuPermissionsForNewMenu(AdminMenu $menu): void
{
if (! Schema::hasTable('role_menu_permissions')) {
return;
}
$parentId = (int) $menu->parent_id;
if ($parentId > 0) {
$roles = RoleMenuPermission::query()
->where('menu_id', $parentId)
->pluck('role')
->unique()
->values();
foreach ($roles as $role) {
RoleMenuPermission::firstOrCreate([
'role' => (string) $role,
'menu_id' => $menu->id,
]);
}
}
RoleMenuPermission::firstOrCreate([
'role' => 'super_admin',
'menu_id' => $menu->id,
]);
}
public function update(Request $request, AdminMenu $adminMenu): JsonResponse
{
$this->ensureSuperAdmin($request);
$data = $request->validate([
'name' => ['required', 'string', 'max:100'],
'path' => ['nullable', 'string', 'max:255'],
'icon' => ['nullable', 'string', 'max:100'],
'parent_id' => ['nullable', 'integer', 'min:0'],
'sort' => ['nullable', 'integer', 'min:0'],
'is_visible' => ['nullable', 'boolean'],
]);
$parentId = (int) ($data['parent_id'] ?? 0);
if ($parentId > 0) {
abort_unless($parentId !== $adminMenu->id, 422, '父级菜单不能是自身');
$exists = AdminMenu::query()->where('id', $parentId)->exists();
abort_unless($exists, 422, '父级菜单不存在');
}
$adminMenu->fill([
'name' => $data['name'],
'path' => $data['path'] ?? null,
'icon' => $data['icon'] ?? null,
'parent_id' => $parentId,
'sort' => (int) ($data['sort'] ?? 0),
'is_visible' => (bool) ($data['is_visible'] ?? true),
])->save();
return response()->json($adminMenu);
}
public function destroy(Request $request, AdminMenu $adminMenu): JsonResponse
{
$this->ensureSuperAdmin($request);
$hasChildren = AdminMenu::query()->where('parent_id', $adminMenu->id)->exists();
abort_if($hasChildren, 422, '请先删除子菜单');
$adminMenu->delete();
return response()->json(['message' => '删除成功']);
}
private function normalizeMenuName(string $name): string
{
if (in_array($name, ['黑名单管理', '灰名单管理'], true)) {
return '用户管理';
}
return $name;
}
private function ensureSuperAdmin(Request $request): void
{
abort_unless($request->user()?->isSuperAdmin(), 403, '仅超级管理员可操作');
}
private function buildTree($menus): array
{
return $menus->where('parent_id', 0)->values()->map(function (AdminMenu $menu) use ($menus) {
$children = $menus
->where('parent_id', $menu->id)
->values()
->map(fn (AdminMenu $child) => [
'id' => $child->id,
'name' => $this->normalizeMenuName($child->name),
'path' => $child->path,
'icon' => $child->icon,
])
->all();
return [
'id' => $menu->id,
'name' => $this->normalizeMenuName($menu->name),
'path' => $menu->path,
'icon' => $menu->icon,
'children' => $children,
];
})->all();
}
}