前提
在项目中使用的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(' ', $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(' ', $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菜单列表我觉得还是可以在优化一下的。仅仅记录下解决方法,如果您有更好的方式欢迎告知我。
评论