available() ->first(); // 2. 空白防御:如果路由不存在于菜单配置中,仅渲染全局根树 if (!$oCurrentMenu) { return [ "aMenuTree" => $this->aGetGlobalTree(), "aMenuPath" => [], ]; } // 3. 计算面包屑/溯源链 (链条顺序:[一级, 二级, 三级...]) $cMenuPathCollection = $oCurrentMenu->cGetAncestorsAndSelf(); // 保持匈牙利命名法及纯数组输出,用 iId 或 sPermissionSlug 作为键,彻底停用组件类型做键 $aMenuPath = $cMenuPathCollection->keyBy('iId')->toArray(); // 4. 获取完整的全局树(包含各级孩子节点) $aMenuTree = $this->aGetGlobalTree(); return [ "aMenuTree" => $aMenuTree, // 全量的无限级树状数据,交由前端去递归渲染或按层级拆解 "aMenuPath" => $aMenuPath, // 当前激活的整条链路,前端用来做多级高亮匹配 ]; } /** * 抓取完整的全局无限级菜单树 (支持无限层级拓展) */ public function aGetGlobalTree(): array { return NasaNavigation::with('childrenRecursive') ->where('iParentId', 0) // 从最顶层根节点开始往下拉 ->available() ->orderBy('iSort', 'asc') ->get() ->map(function (NasaNavigation $oMenu) { return $this->aFormatMenuNode($oMenu); }) ->toArray(); } /** * 内部递归格式化节点:解耦并动态计算每一个节点的最终有效 URL */ private function aFormatMenuNode(NasaNavigation $oMenu): array { // 将当前模型转为基础数组 $aMenu = $oMenu->toArray(); // 动态计算 URL 职责收拢 $aMenu['sUrl'] = $oMenu->sGetFinalUrl(); // 核心:如果存在递归子集,递归往下格式化,实现无限级兼容 if ($oMenu->relationLoaded('childrenRecursive') && $oMenu->childrenRecursive->isNotEmpty()) { $aMenu['children_recursive'] = $oMenu->childrenRecursive ->map(function (NasaNavigation $oChildMenu) { return $this->aFormatMenuNode($oChildMenu); }) ->toArray(); } return $aMenu; } }