first(); // 获取用户及其关联角色资源和直接分配的资源 $user = User::with(['roles.resources', 'resources'])->find($userId); if (!$user) { return collect(); } $all = collect(); // 合并角色资源,排除不需要权限认证的资源 foreach ($user->roles as $role) { $all = $all->merge( $role->resources->filter(function ($resource) use($system) { // var_dump($resource->toArray()); // 只保留需要权限认证的资源(或公共资源) return !$resource->no_auth && ($system->id == $resource->system_id || $resource->system_id == 0); }) ); } // 合并用户直接分配的资源,排除不需要权限认证的资源 $all = $all->merge( $user->resources->filter(function ($resource) { // 只保留需要权限认证的资源(或公共资源) return !$resource->no_auth; }) ); // 加入公共菜单,公共菜单不管是否授权,所有人都能访问 $publicResources = Resource::where('no_auth', 1) ->orderBy('sort', 'asc') // 按 sort 字段升序排序 ->get(); // 合并所有公共资源 $all = $all->merge($publicResources); // 返回唯一的资源列表,并按 id 排序 // 使用 sortBy('sort') 来确保最终结果根据 'sort' 字段排序 return $all->unique('id')->values()->sortBy('sort'); } /** * 构建目录/菜单树 * * @param \Illuminate\Support\Collection $resources 全部资源集合 * @param int $parentId 当前父级资源 ID * @param string $parentPath 父级完整路径(用于递归) * @return array */ public function buildTree_bak($resources, int $parentId = 0, string $parentPath = ''): array { $tree = []; foreach ($resources as $node) { // 只处理目录和菜单,不把 permission 当作菜单节点 if ($node->parent_id !== $parentId || $node->type === 'permission') { continue; } // —— 拼接当前节点的完整路径 —— $currentPath = rtrim($parentPath, '/'); if (!empty($node->url)) { $urlSegment = ltrim($node->url, '/'); $currentPath = $currentPath === '' ? $urlSegment : "{$currentPath}/{$urlSegment}"; } // —— 递归构建子节点 —— // 这里只会继续收目录和菜单,permission 永远不会进入此方法 $children = $this->buildTree($resources, $node->id, $currentPath); // —— 本级权限列表 —— $authList = $resources ->filter(fn($r) => $r->type === 'permission' && $r->parent_id === $node->id) ->map(fn($r) => [ 'id' => $r->id, 'title' => $r->name, 'auth_mark' => $r->code, ]) ->values() ->all(); // —— 判断是否有子目录或子菜单 —— $hasChildrenMenu = ! empty($children); // —— 计算 component 字段 —— if ($hasChildrenMenu) { if ($node->type === 'menu' && !empty($node->url)) { // menu 类型且自身有 url,用自身 url(加前导 slash) $componentValue = '/' . ltrim($node->url, '/'); } else { // 其它含子项的节点,固定 Layout 组件 $componentValue = '/index/index'; } } else { // 叶子节点:拼接路径,确保以 slash 开头 $componentValue = $currentPath !== '' ? '/' . ltrim($currentPath, '/') : ''; } $tree[] = [ 'id' => $node->id, 'name' => $node->name, 'path' => $node->url, 'component' => $componentValue, 'meta' => [ 'title' => $node->name, 'icon' => $node->icon, 'keepAlive' => true, 'authList' => $authList, 'openType' => $node->open_type, 'isHide' => $node->is_hide == 0 ? false : true, 'isHideTab' => $node->is_hide_tab == 0 ? false : true ], 'children' => $children ]; } return $tree; } public function buildTree_bak2($resources, int $parentId = 0, string $parentPath = ''): array { $tree = []; foreach ($resources as $node) { if ($node->parent_id !== $parentId) { continue; } // —— 拼接当前节点的完整路径 —— $currentPath = $parentPath; // 去掉末尾多余 slash $currentPath = rtrim($currentPath, '/'); if (!empty($node->url)) { // 如果当前节点有 url,则拼接 $urlSegment = ltrim($node->url, '/'); $currentPath = $currentPath === '' ? $urlSegment : "{$currentPath}/{$urlSegment}"; } // —— 递归构建子节点 —— $children = []; if ($node->type === 'directory') { $children = $this->buildTree($resources, $node->id, $currentPath); } // —— 收集本级的 permission 权限列表 —— $authList = $resources ->filter(fn($r) => $r->type === 'permission' && $r->parent_id === $node->id) ->map(fn($r) => [ 'id' => $r->id, 'title' => $r->name, 'auth_mark' => $r->code, ]) ->values() ->all(); // —— 判断是否有子菜单/目录 —— $hasChildrenMenu = $resources ->where('parent_id', $node->id) ->whereIn('type', ['directory', 'menu']) ->isNotEmpty(); // —— 计算 component 字段 —— if ($hasChildrenMenu) { // 只要有子项,就一律显示顶层 Layout 组件 $componentValue = '/index/index'; } else { // 叶子节点:如果定义了 url,就使用拼接后的路径;否则用占位符(这里用空串,你可替换) $componentValue = $currentPath !== '' ? $currentPath : ''; } $tree[] = [ 'id' => $node->id, 'name' => $node->name, 'path' => $node->url, 'component' => $componentValue, 'meta' => [ 'title' => $node->name, 'icon' => $node->icon, 'keepAlive' => true, 'authList' => $authList, 'openType' => $node->open_type, ], 'children' => $children, ]; } return $tree; } public function buildTree($resources, int $parentId = 0, string $parentPath = ''): array { $tree = []; foreach ($resources as $node) { if ($node->parent_id !== $parentId) continue; // 拼接完整路径 $currentPath = $parentPath; if ($currentPath && substr($currentPath, -1) === '/') { $currentPath = rtrim($currentPath, '/'); } if (!empty($node->url)) { if (!$currentPath) { $currentPath = $node->url; } else { $url = ltrim($node->url, '/'); $currentPath = $currentPath . '/' . $url; } } // 子节点递归 $children = []; if ($node->type === 'directory') { $children = $this->buildTree($resources, $node->id, $currentPath); } // 权限 $authList = $resources ->filter(fn($r) => $r->type === 'permission' && $r->parent_id === $node->id) ->map(fn($r) => [ 'id' => $r->id, 'title' => $r->name, 'auth_mark' => $r->code, ])->values()->all(); // 判断是否有子级菜单(非权限) $hasChildrenMenu = collect($resources)->where('parent_id', $node->id) ->whereIn('type', ['directory', 'menu'])->isNotEmpty(); $componentValue = $hasChildrenMenu ? '/index/index' : $currentPath; $tree[] = [ 'id' => $node->id, 'name' => $node->name, 'path' => $node->url, 'component' => $componentValue, 'meta' => [ 'title' => $node->name, 'icon' => $node->icon, 'keepAlive' => true, 'authList' => $authList, 'openType' => $node->open_type, 'isHide' => $node->is_hide == 0 ? false : true, 'isHideTab' => $node->is_hide_tab == 0 ? false : true ], 'children' => $children, ]; } return $tree; } /** * 获取用户所有权限code */ public function getPermissionCodes(int $userId, string $systemCode): array { $resources = $this->getUserResources($userId, $systemCode); return $resources ->filter(fn($res) => $res->type === 'permission') ->pluck('code') ->unique() ->values() ->all(); } }