Dcat菜单列表重写

是滑稽啊
2023-05-22 / 0 评论 / 54 阅读 / 正在检测是否收录...

前提

在项目中使用的dcatadmin框架
多用户模式,只有一个单后台,

用户角色分为:administrator(超管), member(会员), sub_account(会员子账号)

每个会员及其子账号 只能看见自己创建的自定义表单在菜单显示(例如A会员和A会员创建的A1子会员,A2子会员 大家登录后看见都是一样的, 但是A会员和B会员看见的自定义表单菜单是不一样的)。

仔仔细细看了下文档 只能动态添加菜单,可是不能根据当前登录用户来决定菜单是否显示,此时脑中浮现两种方式,

方式一,在创建自定义表单的时候,同时往admin_menu,admin_role,admin_role_users等表中插入菜单关联数据需要操作3-5个关系表(菜单,角色,权限及其关系表),然后每个用户都创建一个单独的与用户对应的角色,在将角色和菜单关联,让dcat来自动显示是否显示。 但是这种做需要同时操作的数据库表多张,关系繁琐弊端较多 妥妥的下下测。

方式二,重构菜单列表,在渲染菜单列表的时候使用文档中通过Menu::add接口动态添加菜单 然后我也就不需要在把创建的自定义表单,单独的存入admin_menu中菜单中。仅仅只需要获取当前登录用户然后在原本菜单列表渲染后面动态add一份自定义表单即可。

创建任务:在/App/Admin中重写菜单列表渲染

下面即是我阅读dcat源码后进行的操作过程:

复制数据格式

源位置/vendor/dcat/laravel-admin/src/Layout/Menu.php

复制到/app/Admin/Layout/Menu.php

<?php
/**
 * 菜单列表重写
 */
namespace App\Admin\Layout;
//namespace Dcat\Admin\Layout;

use Dcat\Admin\Admin;
use Dcat\Admin\Support\Helper;
use Illuminate\Support\Facades\Lang;

class Menu
{
    protected static $helperNodes = [
        [
            'id'        => 1,
            'title'     => 'Helpers',
            'icon'      => 'fa fa-keyboard-o',
            'uri'       => '',
            'parent_id' => 0,
        ],
        [
            'id'        => 2,
            'title'     => 'Extensions',
            'icon'      => '',
            'uri'       => 'auth/extensions',
            'parent_id' => 1,
        ],
        [
            'id'        => 3,
            'title'     => 'Scaffold',
            'icon'      => '',
            'uri'       => 'helpers/scaffold',
            'parent_id' => 1,
        ],
        [
            'id'        => 4,
            'title'     => 'Icons',
            'icon'      => '',
            'uri'       => 'helpers/icons',
            'parent_id' => 1,
        ],
    ];

    protected $view = 'admin::partials.menu';

    public function register()
    {
        if (! admin_has_default_section(Admin::SECTION['LEFT_SIDEBAR_MENU'])) {
            admin_inject_default_section(Admin::SECTION['LEFT_SIDEBAR_MENU'], function () {
                $menuModel = config('admin.database.menu_model');

                return $this->toHtml((new $menuModel())->allNodes()->toArray());
            });
        }
        //================================================================================
        //在此处追加-添加自定义的菜单
        $this->add([
            [
                'id'            => 400, // 此id只要保证当前的数组中是唯一的即可
                'title'         => '测试菜单999',
                'icon'          => 'fa-file-text-o',
                'uri'           => '',
                'parent_id'     => 0, 
                'permission_id' => '7', // 与权限绑定
                'roles'         => 'user2', // 与角色绑定
            ],
            [
                'id'            => 401, // 此id只要保证当前的数组中是唯一的即可
                'title'         => '测试菜单-item',
                'icon'          => 'fa-file-text-o',
                'uri'           => '',
                'parent_id'     => 400, 
                'permission_id' => '7', // 与权限绑定
                'roles'         => 'user2', // 与角色绑定
            ]

        ],20);
        //===================================================================================

        //\Illuminate\Support\Facades\Log::debug('111111111');
        if (config('app.debug') && config('admin.helpers.enable', true)) {
            $this->add(static::$helperNodes, 20);
            //$this->add(static::$helperNodes, 20);
            //$this->add(static::$helperNodes, 20);
            //$this->add(static::$helperNodes, 20);
            //$this->add(static::$helperNodes, 20);
        }
        
    }

    /**
     * 增加菜单节点.
     *
     * @param  array  $nodes
     * @param  int  $priority
     * @return void
     */
    public function add(array $nodes = [], int $priority = 10)
    {
        admin_inject_section(Admin::SECTION['LEFT_SIDEBAR_MENU_BOTTOM'], function () use (&$nodes) {
            return $this->toHtml($nodes);
        }, true, $priority);
    }

    /**
     * 转化为HTML.
     *
     * @param  array  $nodes
     * @return string
     *
     * @throws \Throwable
     */
    public function toHtml($nodes)
    {
        $html = '';

        foreach (Helper::buildNestedArray($nodes) as $item) {
            $html .= $this->render($item);
        }

        return $html;
    }

    /**
     * 设置菜单视图.
     *
     * @param  string  $view
     * @return $this
     */
    public function view(string $view)
    {
        $this->view = $view;

        return $this;
    }

    /**
     * 渲染视图.
     *
     * @param  array  $item
     * @return string
     */
    public function render($item)
    {
        return view($this->view, ['item' => &$item, 'builder' => $this])->render();
    }

    /**
     * 判断是否选中.
     *
     * @param  array  $item
     * @param  null|string  $path
     * @return bool
     */
    public function isActive($item, ?string $path = null)
    {
        if (empty($path)) {
            $path = request()->path();
        }

        if (empty($item['children'])) {
            if (empty($item['uri'])) {
                return false;
            }

            return trim($this->getPath($item['uri']), '/') == $path;
        }

        foreach ($item['children'] as $v) {
            if ($path == trim($this->getPath($v['uri']), '/')) {
                return true;
            }
            if (! empty($v['children'])) {
                if ($this->isActive($v, $path)) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * 判断节点是否可见.
     *
     * @param  array  $item
     * @return bool
     */
    public function visible($item)
    {
        if (
            ! $this->checkPermission($item)
            || ! $this->checkExtension($item)
            || ! $this->userCanSeeMenu($item)
        ) {
            return false;
        }

        $show = $item['show'] ?? null;
        if ($show !== null && ! $show) {
            return false;
        }

        return true;
    }

    /**
     * 判断扩展是否启用.
     *
     * @param $item
     * @return bool
     */
    protected function checkExtension($item)
    {
        $extension = $item['extension'] ?? null;

        if (! $extension) {
            return true;
        }

        if (! $extension = Admin::extension($extension)) {
            return false;
        }

        return $extension->enabled();
    }

    /**
     * 判断用户.
     *
     * @param  array|\Dcat\Admin\Models\Menu  $item
     * @return bool
     */
    protected function userCanSeeMenu($item)
    {
        $user = Admin::user();

        if (! $user || ! method_exists($user, 'canSeeMenu')) {
            return true;
        }

        return $user->canSeeMenu($item);
    }

    /**
     * 判断权限.
     *
     * @param $item
     * @return bool
     */
    protected function checkPermission($item)
    {
        $permissionIds = $item['permission_id'] ?? null;
        $roles = array_column(Helper::array($item['roles'] ?? []), 'slug');
        $permissions = array_column(Helper::array($item['permissions'] ?? []), 'slug');

        if (! $permissionIds && ! $roles && ! $permissions) {
            return true;
        }

        $user = Admin::user();

        if (! $user || $user->visible($roles)) {
            return true;
        }

        foreach (array_merge(Helper::array($permissionIds), $permissions) as $permission) {
            if ($user->can($permission)) {
                return true;
            }
        }

        return false;
    }

    /**
     * @param  string  $text
     * @return string
     */
    public function translate($text)
    {
        $titleTranslation = 'menu.titles.'.trim(str_replace(' ', '_', strtolower($text)));

        if (Lang::has($titleTranslation)) {
            return __($titleTranslation);
        }

        return $text;
    }

    /**
     * @param  string  $uri
     * @return string
     */
    public function getPath($uri)
    {
        return $uri
            ? (url()->isValidUrl($uri) ? $uri : admin_base_path($uri))
            : $uri;
    }

    /**
     * @param  string  $uri
     * @return string
     */
    public function getUrl($uri)
    {
        return $uri ? admin_url($uri) : $uri;
    }
}

注册

/app/Providers/AppServiceProvider.php
注册上一步的php文件

use App\Admin\Layout\Menu;

...

public function register()
    {
        //注册菜单列表
        $this->app->singleton('admin.menu', Menu::class);
    }

复制模板文件

原模板所在位置/vendor/dcat/laravel-admin/resoures/views/partials/menu.blade.php
复制到/resoures/views/vendor/admin/partials/menu.blade.php

@php
    $depth = $item['depth'] ?? 0;

    $horizontal = config('admin.layout.horizontal_menu');

    $defaultIcon = config('admin.menu.default_icon', 'feather icon-circle');
@endphp

@if($builder->visible($item))
    @if(empty($item['children']))
        <li class="nav-item">
            <a data-id="{{ $item['id'] ?? '' }}" @if(mb_strpos($item['uri'], '://') !== false) target="_blank" @endif
               href="{{ $builder->getUrl($item['uri']) }}"
               class="nav-link {!! $builder->isActive($item) ? 'active' : '' !!}">
                {!! str_repeat('&nbsp;', $depth) !!}<i class="fa fa-fw {{ $item['icon'] ?: $defaultIcon }}"></i>
                <p>
                    {!! $builder->translate($item['title']) !!}
                </p>
            </a>
        </li>
    @else

        <li class="{{ $horizontal ? 'dropdown' : 'has-treeview' }} {{ $depth > 0 ? 'dropdown-submenu' : '' }} nav-item {{ $builder->isActive($item) ? 'menu-open' : '' }}">
            <a href="#"  data-id="{{ $item['id'] ?? '' }}"
               class="nav-link {{ $builder->isActive($item) ? ($horizontal ? 'active' : '') : '' }}
                    {{ $horizontal ? 'dropdown-toggle' : '' }}">
                {!! str_repeat('&nbsp;', $depth) !!}<i class="fa fa-fw {{ $item['icon'] ?: $defaultIcon }}"></i>
                <p>
                    {!! $builder->translate($item['title']) !!}

                    @if(! $horizontal)
                        <i class="right fa fa-angle-left"></i>
                    @endif
                </p>
            </a>
            <ul class="nav {{ $horizontal ? 'dropdown-menu' : 'nav-treeview' }}">
                @foreach($item['children'] as $item)
                    @php
                        $item['depth'] = $depth + 1;
                    @endphp

                    @include('admin::partials.menu', ['item' => $item])
                @endforeach
            </ul>
        </li>
    @endif
@endif
注意:复制出来的文件不一定按着我这个路径存放,只需要在代码关联中引用正确自动加载就能够加载出来。
就上述三个文件改动即可,然后具体的数据查询根据业务需求去写即可,可以是在/app/Admin/Layout/Menu.php中 56行后面接着写,也可以将数据查询写到Models中,如果在Models中重写需要在/config/admin.php'menu_model' => Dcat\Admin\Models\Menu::class, 改成自定义的模型位置。

总结

使用这种定义好的后端框架确实是开发迅速,但是局限性很高,如果有些特殊的需求或者效果还是需要自定义。
多翻翻源码还有帮助的。

此篇重构dcat菜单列表我觉得还是可以在优化一下的。仅仅记录下解决方法,如果您有更好的方式欢迎告知我。

1

评论

博主关闭了所有页面的评论