available() ->first(); if (!$oCurrentMenu) { return [ "aTopMenus" => $this->aGetTopMenus(), "aSubMenus" => [], "aTabMenus" => [], "aMenuPath" => [], ]; } $aTopMenus = $this->aGetTopMenus(); // 2. 获取面包屑的原始集合(链条顺序依然是:[一级, 二级, 三级...]) $cMenuPathCollection = $oCurrentMenu->cGetAncestorsAndSelf(); // ================= 核心修改:利用 keyBy 将 sComponentType 作为键 ================= // 它会自动将数组转换为:['TOP_BAR' => [...], 'LEFT_MENU' => [...]] 的高内聚结构 $aMenuPath = $cMenuPathCollection->keyBy('sComponentType')->toArray(); // ============================================================================== // 3. 提取一级根节点(注意:集合 keyBy 后内部指针会变,所以我们从原集合中安全 get(0) 获取一级) $oRootMenu = $cMenuPathCollection->first(); $aSubMenus = []; $aTabMenus = []; if ($oRootMenu) { // 拉出该顶级菜单下的所有无限级子树 $oRootMenuWithTree = NasaNavigation::with('childrenRecursive') ->where('id', $oRootMenu->id) ->first(); if ($oRootMenuWithTree && $oRootMenuWithTree->childrenRecursive) { // 【二级侧边栏逻辑】:保持原样 $aSubMenus = $oRootMenuWithTree->childrenRecursive ->transform(function (NasaNavigation $oSubMenu) { $oSubMenu->sUrl = $oSubMenu->sGetFinalUrl(); return $oSubMenu; }) ->groupBy('sGroup') ->toArray(); // ================= 三级 Tab 菜单解析优化 ================= // 以前是通过 ->get(1) 硬编码拿二级。现在既然改了结构, // 我们可以直接通过你定义的二级类型键(假设叫 TYPE_SIDE_BAR,请根据你 Model 里的常量自行替换)安全捞取 $oLevel2Menu = $cMenuPathCollection->firstWhere('sComponentType', NasaNavigation::TYPE_SIDEBAR_MENU); if ($oLevel2Menu) { // 去子树内存里捞出这个二级节点 $oCurrentLevel2InTree = $oRootMenuWithTree->childrenRecursive ->firstWhere('id', $oLevel2Menu->id); if ($oCurrentLevel2InTree && $oCurrentLevel2InTree->childrenRecursive->isNotEmpty()) { $aTabMenus = $oCurrentLevel2InTree->childrenRecursive ->transform(function (NasaNavigation $oTabMenu) { $oTabMenu->sUrl = $oTabMenu->sGetFinalUrl(); return $oTabMenu; }) ->toArray(); } } } } return [ "aTopMenus" => $aTopMenus, "aSubMenus" => $aSubMenus, "aTabMenus" => $aTabMenus, "aMenuPath" => $aMenuPath, // 这里的键已经是你的组件类型字符串了 ]; } /** * 获取全局顶级大区 (TOP_BAR) */ public function aGetTopMenus(): array { return NasaNavigation::with('childrenRecursive') // 一口气拉出无限级树结构,只有 2-3 条 SQL ->where('iParentId', 0) ->where('sComponentType', NasaNavigation::TYPE_TOP_BAR) ->available() ->orderBy('iSort', 'asc') ->get() ->transform(function (NasaNavigation $oMenu) { // 让顶级菜单自己去递归嗅探最底层的有效 URL $oMenu->sUrl = $oMenu->sGetFinalUrl(); return $oMenu; }) ->groupBy('sGroup') ->toArray(); } /** * 抓取侧边栏业务菜单树 */ public function aGetSubMenus($iParentId): Array { return NasaNavigation::where('iParentId', $iParentId) ->where('sComponentType', NasaNavigation::TYPE_SIDEBAR_MENU) ->available() ->orderBy('iSort', 'asc') ->get() ->map(function ($oMenu) { // 将模型转成数组 $aMenu = $oMenu->toArray(); // 🎯 核心防空伞:动态算好 sUrl,有就给,没有就给空字符串 '' $aMenu['sUrl'] = \Illuminate\Support\Facades\Route::has($oMenu->sPermissionSlug) ? route($oMenu->sPermissionSlug) : ''; return $aMenu; }) ->groupBy('sGroup') ->toArray(); } }