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.

211 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 与「角色管理」勾选一致。
// 超级管理员(含系统级全权限角色):返回全部可见菜单,避免新建菜单未写入权限表时侧栏/接口都看不到。
$filterByRolePermissions = $user
&& Schema::hasTable('role_menu_permissions')
&& ! $user->isSuperAdmin();
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();
}
}