在开发过程中有的是设计师提前设计好了后面菜单布局,但是fastadmin模式菜单不能满足就行要改动以下,在fastadmin的后台是有多级菜单导航模式切换,但是开启后是顶部为为主菜单,左侧为子菜单,不满足我要的顶部为子菜单,左侧主菜单需求

不满足就进行代码修改完成想要的效果,找到application/admin/library/Auth.php文件,找到getSidebar方法,替换代码如下:
/**
* 获取左侧和顶部菜单栏
*
* @param array $params URL对应的badge数据
* @param string $fixedPage 默认页
* @return array
*/
public function getSidebar($params = [], $fixedPage = 'dashboard')
{
// 边栏开始
Hook::listen("admin_sidebar_begin", $params);
$colorArr = ['red', 'green', 'yellow', 'blue', 'teal', 'orange', 'purple'];
$colorNums = count($colorArr);
$badgeList = [];
$module = request()->module();
// 生成菜单的badge
foreach ($params as $k => $v) {
$url = $k;
if (is_array($v)) {
$nums = $v[0] ?? 0;
$color = $v[1] ?? $colorArr[(is_numeric($nums) ? $nums : strlen($nums)) % $colorNums];
$class = $v[2] ?? 'label';
} else {
$nums = $v;
$color = $colorArr[(is_numeric($nums) ? $nums : strlen($nums)) % $colorNums];
$class = 'label';
}
//必须nums大于0才显示
if ($nums) {
$badgeList[$url] = '<small class="' . $class . ' pull-right bg-' . $color . '">' . $nums . '</small>';
}
}
// 读取管理员当前拥有的权限节点
$userRule = $this->getRuleList();
$selected = $referer = [];
$refererUrl = Session::get('referer');
// 必须将结果集转换为数组
$ruleList = collection(\app\admin\model\AuthRule::where('status', 'normal')
->where('ismenu', 1)
->order('weigh', 'desc')
->cache("__menu__")
->select())->toArray();
$indexRuleList = \app\admin\model\AuthRule::where('status', 'normal')
->where('ismenu', 0)
->where('name', 'like', '%/index')
->column('name,pid');
$pidArr = array_unique(array_filter(array_column($ruleList, 'pid')));
foreach ($ruleList as $k => &$v) {
if (!in_array(strtolower($v['name']), $userRule)) {
unset($ruleList[$k]);
continue;
}
$indexRuleName = $v['name'] . '/index';
if (isset($indexRuleList[$indexRuleName]) && !in_array($indexRuleName, $userRule)) {
unset($ruleList[$k]);
continue;
}
$v['icon'] = $v['icon'] . ' fa-fw';
$v['url'] = isset($v['url']) && $v['url'] ? $v['url'] : '/' . $module . '/' . $v['name'];
$v['badge'] = $badgeList[$v['name']] ?? '';
$v['title'] = __($v['title']);
$v['url'] = preg_match("/^((?:[a-z]+:)?\/\/|data:image\/)(.*)/i", $v['url']) ? $v['url'] : url($v['url']);
$v['menuclass'] = in_array($v['menutype'], ['dialog', 'ajax']) ? 'btn-' . $v['menutype'] : '';
$v['menutabs'] = !$v['menutype'] || in_array($v['menutype'], ['default', 'addtabs']) ? 'addtabs="' . $v['id'] . '"' : '';
$selected = $v['name'] == $fixedPage ? $v : $selected;
$referer = $v['url'] == $refererUrl ? $v : $referer;
}
$lastArr = array_unique(array_filter(array_column($ruleList, 'pid')));
$pidDiffArr = array_diff($pidArr, $lastArr);
foreach ($ruleList as $index => $item) {
if (in_array($item['id'], $pidDiffArr)) {
unset($ruleList[$index]);
}
}
if ($selected == $referer) {
$referer = [];
}
$select_id = $referer ? $referer['id'] : ($selected ? $selected['id'] : 0);
$menu = $nav = '';
$showSubmenu = config('fastadmin.show_submenu');
if (Config::get('fastadmin.multiplenav')) {
$topList = [];
foreach ($ruleList as $index => $item) {
if (!$item['pid']) {
$topList[] = $item;
}
}
$selectParentIds = [];
$tree = Tree::instance();
$tree->init($ruleList);
if ($select_id) {
$selectParentIds = $tree->getParentsIds($select_id, true);
}
// 获取当前激活的一级菜单ID
$activeTopId = 0;
foreach ($selectParentIds as $pid) {
foreach ($topList as $topItem) {
if ($pid == $topItem['id']) {
$activeTopId = $topItem['id'];
break 2;
}
}
}
if (!$activeTopId && !empty($topList)) {
$activeTopId = $topList[0]['id'];
}
// 提前构建父子关系映射
$childrenMap = [];
foreach ($ruleList as $item) {
$childrenMap[$item['pid']][] = $item;
}
// 1. 左侧菜单 ($menu):显示一级主菜单
foreach ($topList as $index => $item) {
$current = in_array($item['id'], $selectParentIds) || $item['id'] == $activeTopId;
$hasChild = isset($childrenMap[$item['id']]);
if ($hasChild) {
// 【核心修改】:如果有子菜单,a标签仅作为切换顶部的“占位符”,绝不包含任何跳转URL
$menu .= '<li class="treeview ' . ($current ? 'active' : '') . '"><a href="javascript:;" data-pid="' . $item['id'] . '" class=""><i class="' . $item['icon'] . '"></i> <span>' . $item['title'] . '</span> <span class="pull-right-container"></span></a> </li>';
} else {
// 如果是纯一级菜单(无子菜单),正常跳转
$url = $item['url'];
$addUrlRef = (stripos($url, "?") !== false ? "&" : "?") . "ref=addtabs";
$menu .= '<li class="' . ($current ? 'active' : '') . '"><a ' . $item['extend'] . ' href="' . $url . $addUrlRef . '" ' . $item['menutabs'] . ' class="' . $item['menuclass'] . '" url="' . $url . '" title="' . $item['title'] . '"><i class="' . $item['icon'] . '"></i> <span>' . $item['title'] . '</span> <span class="pull-right-container"></span></a> </li>';
}
}
// 2. 顶部导航 ($nav):显示当前一级菜单下的二级子菜单
foreach ($topList as $index => $item) {
$current = in_array($item['id'], $selectParentIds) || $item['id'] == $activeTopId;
$childList = Tree::instance()->getTreeMenu(
$item['id'],
'<li class="@class hidden" pid="@pid"><a @extend href="@url@addtabs" addtabs="@id" class="@menuclass" url="@url" py="@py" pinyin="@pinyin"><i class="@icon"></i> <span>@title</span> <span class="pull-right-container">@caret @badge</span></a> @childlist</li>',
$select_id,
'',
'ul',
'class="treeview-menu' . ($showSubmenu ? ' menu-open' : '') . '"'
);
// 修复 hidden 逻辑:给最外层的 ul 加上 hidden 和 pid 属性
$childList = str_replace(
'class="treeview-menu' . ($showSubmenu ? ' menu-open' : '') . '"',
'class="treeview-menu' . ($showSubmenu ? ' menu-open' : '') . ($current ? '' : ' hidden') . '" pid="' . $item['id'] . '"',
$childList
);
// print_r($childList);
$nav .= $childList;
}
} else {
// 构造菜单数据
Tree::instance()->init($ruleList);
$menu = Tree::instance()->getTreeMenu(
0,
'<li class="@class"><a @extend href="@url@addtabs" @menutabs class="@menuclass" url="@url" py="@py" pinyin="@pinyin"><i class="@icon"></i> <span>@title</span> <span class="pull-right-container">@caret @badge</span></a> @childlist</li>',
$select_id,
'',
'ul',
'class="treeview-menu' . ($showSubmenu ? ' menu-open' : '') . '"'
);
if ($selected) {
$nav .= '<li role="presentation" id="tab_' . $selected['id'] . '" class="' . ($referer ? '' : 'active') . '"><a href="#con_' . $selected['id'] . '" node-id="' . $selected['id'] . '" aria-controls="' . $selected['id'] . '" role="tab" data-toggle="tab"><i class="' . $selected['icon'] . ' fa-fw"></i> <span>' . $selected['title'] . '</span> </a></li>';
}
if ($referer) {
$nav .= '<li role="presentation" id="tab_' . $referer['id'] . '" class="active"><a href="#con_' . $referer['id'] . '" node-id="' . $referer['id'] . '" aria-controls="' . $referer['id'] . '" role="tab" data-toggle="tab"><i class="' . $referer['icon'] . ' fa-fw"></i> <span>' . $referer['title'] . '</span> </a> <i class="close-tab fa fa-remove"></i></li>';
}
}
return [$menu, $nav, $selected, $referer];
}
在添加点击事件/public/assets/js/backend/index.js文件的官方的"点击顶部第一级菜单栏"和"点击左侧菜单栏"代码注释掉,添加两个新的菜单点击事件方法
var firstnav = $("#firstnav .nav-addtabs");
// 1. 点击左侧菜单栏 (包含“有子菜单的主菜单”和“无子菜单的直跳菜单”)
$(document).on('click', '.sidebar-menu > li > a', function (e) {
var $a = $(this);
var pid = $a.attr("data-pid"); // 获取 data-pid (有子菜单的主菜单)
var addtabsId = $a.attr("addtabs"); // 获取 addtabs (无子菜单的直跳菜单)
// 1. 左侧高亮切换 (通用)
$(".sidebar-menu > li").removeClass("active");
$a.closest("li").addClass("active");
// 2. 顶部导航联动处理
// 先默认隐藏所有带 pid 的子菜单组
firstnav.find("li[pid]").addClass("hidden");
// --- 情况A:点击的是有子菜单的主菜单 (存在 data-pid) ---
if (pid) {
e.preventDefault(); // 阻止空链接跳转
// 找到并显示对应的子菜单组
var $targetItems = firstnav.find("li[pid='" + pid + "']");
$targetItems.removeClass("hidden");
// 寻找目标子菜单(优先恢复上次,否则第一个)
var $lastActiveItem = $targetItems.filter(function() {
return $(this).find("a").attr("last-id") !== undefined;
});
if ($lastActiveItem.length === 0) {
$lastActiveItem = $targetItems.first();
}
// 高亮处理
$targetItems.removeClass("active");
$lastActiveItem.addClass("active");
// 触发跳转
var $linkToClick = $lastActiveItem.find("a[addtabs]");
if ($linkToClick.length > 0) {
$linkToClick.trigger("click");
}
}
// --- 情况B:点击的是无子菜单的直跳菜单 (存在 addtabs,不存在 data-pid) ---
else if (addtabsId) {
// 不需要 preventDefault,让它执行默认跳转逻辑,或者手动触发
// 但为了统一状态管理,我们手动触发顶部对应 Tab 的点击
// 1. 检查顶部是否已经存在这个 addtabs 的 Tab (通常这种一级菜单生成的 li 没有 pid)
var $targetTab = firstnav.find("li[addtabs='" + addtabsId + "']");
if ($targetTab.length > 0) {
// 如果存在,高亮它,并触发点击刷新
firstnav.find("li").removeClass("active");
$targetTab.addClass("active");
$targetTab.find("a").trigger("click");
} else {
// 如果不存在(比如页面刚加载还没生成),FastAdmin 的 addtabs 机制会自动处理
// 我们只需确保 a 标签能正常工作
// 此时不做额外操作,依赖 href 的默认行为
}
}
});
// 2. 点击顶部子菜单栏 (负责页面跳转 + 记录状态)
$(document).on('click', '.nav-addtabs > li > a[addtabs]', function (e) {
var $li = $(this).closest("li");
var pid = $li.attr("pid");
var addtabsId = $(this).attr("addtabs");
// 1. 高亮当前点击的子菜单
$(".nav-addtabs > li").removeClass("active");
$li.addClass("active");
// 2. 如果有 pid,记录 last-id (只有子菜单才需要记录)
if (pid) {
$(".nav-addtabs > li[pid='" + pid + "'] > a").removeAttr("last-id");
$(this).attr("last-id", addtabsId);
// 同步左侧主菜单的高亮
var $targetSidebarLi = $(".sidebar-menu li a[data-pid='" + pid + "']").closest("li");
if ($targetSidebarLi.length > 0) {
$(".sidebar-menu > li").removeClass("active");
$targetSidebarLi.addClass("active");
}
} else {
// 如果没有 pid,说明是直跳菜单,同步左侧
// 根据 addtabsId 反查左侧对应的 a 标签
var $sidebarLink = $(".sidebar-menu li a[addtabs='" + addtabsId + "']");
if ($sidebarLink.length > 0) {
$(".sidebar-menu > li").removeClass("active");
$sidebarLink.closest("li").addClass("active");
}
}
// 3. 移动端菜单同步
if (typeof mobilenav !== 'undefined') {
mobilenav.find("a").removeClass("active");
mobilenav.find("a[addtabs='" + addtabsId + "']").addClass("active");
}
});