主页

fastadmin多菜单顶部为一级菜单左侧为子菜单模式

两人怕辜负   IP属地: 青海
0 点赞 0阅读 1342

在开发过程中有的是设计师提前设计好了后面菜单布局,但是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");
    }
});
© 著作权归作者所有,转载或内容合作请联系作者