bianjunhui 13 часов назад
Родитель
Сommit
acd6794ecd

+ 3 - 12
packages/Longyi/DynamicMenu/src/Config/menu.php

@@ -1,12 +1,11 @@
 <?php
 
 return [
-    // 直接返回菜单数组,不要再用 admin 键包裹
     [
-        'key' => 'settings.dynamicmenu',
-        'name' => '动态菜单',
+        'key'   => 'settings.dynamicmenu',
+        'name'  => '测试菜单',
         'route' => 'admin.dynamicmenu.index',
-        'sort' => 10,
+        'sort' => 12,
         'icon' => 'icon-menu',
     ],
     [
@@ -17,12 +16,4 @@ return [
         'icon' => 'icon-list',
         'parent' => 'settings.dynamicmenu'
     ],
-    [
-        'key' => 'settings.dynamicmenu.permission',
-        'name' => '菜单权限',
-        'route' => 'admin.dynamicmenu.permission',
-        'sort' => 2,
-        'icon' => 'icon-lock',
-        'parent' => 'settings.dynamicmenu'
-    ],
 ];

+ 5 - 12
packages/Longyi/DynamicMenu/src/Http/Controllers/MenuController.php

@@ -22,20 +22,14 @@ class MenuController extends Controller
     public function index()
     {
         
-         \Log::info('===== MenuController::index 开始 =====');
-    
+        
     try {
-        \Log::info('开始查询菜单数据');
+        
         $allItems = MenuItem::with('children')
             ->orderBy('parent_id')
             ->orderBy('sort_order')
             ->get();
-        \Log::info('查询完成,总数: ' . $allItems->count());
-        
         $menuItems = $allItems->whereNull('parent_id');
-        \Log::info('顶级菜单数: ' . $menuItems->count());
-        
-        \Log::info('准备返回视图');
         return view('dynamicmenu::admin.menu.index', compact('menuItems', 'allItems'));
         
     } catch (\Exception $e) {
@@ -43,8 +37,7 @@ class MenuController extends Controller
         \Log::error($e->getTraceAsString());
         throw $e;
     }
-    
-    \Log::info('===== MenuController::index 结束 =====');
+        //\Log::info('===== MenuController::index 结束 =====');
     }
     
     public function create()
@@ -87,9 +80,9 @@ class MenuController extends Controller
     
     public function update(Request $request, $id)
     {
-        $this->validate($request, [
+        $request->validate([
             'name' => 'required|string|max:255',
-            'key' => 'required|string|unique:dynamic_menu_items,key,' . $id,
+            'key' => 'required|string|unique:dynamic_menu_items,key',
             'route' => 'required|string',
             'icon' => 'nullable|string',
             'parent_id' => 'nullable|exists:dynamic_menu_items,id',

+ 21 - 58
packages/Longyi/DynamicMenu/src/Models/MenuItem.php

@@ -3,7 +3,7 @@
 namespace Longyi\DynamicMenu\Models;
 
 use Illuminate\Database\Eloquent\Model;
-
+use Illuminate\Support\Facades\Cache;
 class MenuItem extends Model
 {
     protected $table = 'dynamic_menu_items';
@@ -23,74 +23,37 @@ class MenuItem extends Model
         'status' => 'boolean',
         'sort_order' => 'integer'
     ];
-    
-    /**
-     * 获取菜单链接
-     */
-    public function getUrlAttribute()
+    protected static function boot()
     {
-        if (empty($this->route)) {
-            return '#';
-        }
+        parent::boot();
         
-        // 如果是完整的URL(以http://或https://开头)
-        if (str_starts_with($this->route, 'http://') || str_starts_with($this->route, 'https://')) {
-            return $this->route;
-        }
+        // 当菜单项被创建、更新或删除时,清除缓存
+        static::saved(function () {
+            Cache::forget('dynamic_menu_config');
+            Cache::forget('dynamic_menu_items');
+            \Log::info('菜单缓存已清除 (saved)');
+        });
         
-        // 如果是路由名称
-        try {
-            if (route_exists($this->route)) {
-                return route($this->route);
-            }
-        } catch (\Exception $e) {
-            // 路由不存在,返回原始值
-        }
-        
-        // 如果是相对路径
-        return url($this->route);
+        static::deleted(function () {
+            Cache::forget('dynamic_menu_config');
+            Cache::forget('dynamic_menu_items');
+            \Log::info('菜单缓存已清除 (deleted)');
+        });
     }
-    
+
     /**
-     * 检查菜单是否当前活跃
+     * 获取父菜单
      */
-    public function getIsActiveAttribute()
-    {
-        $currentRoute = request()->route() ? request()->route()->getName() : '';
-        $currentPath = request()->path();
-        
-        // 检查路由名称匹配
-        if ($this->route == $currentRoute) {
-            return true;
-        }
-        
-        // 检查URL路径匹配
-        $menuPath = trim($this->route, '/');
-        $currentPath = trim($currentPath, '/');
-        
-        return $menuPath == $currentPath || str_starts_with($currentPath, $menuPath);
-    }
-    
-    // ... 其他关系方法
-
-
     public function parent()
     {
         return $this->belongsTo(MenuItem::class, 'parent_id');
     }
-
+    
+    /**
+     * 获取子菜单
+     */
     public function children()
     {
         return $this->hasMany(MenuItem::class, 'parent_id')->orderBy('sort_order');
     }
-
-    public function creator()
-    {
-        return $this->belongsTo(Admin::class, 'created_by');
-    }
-
-    public function roles()
-    {
-        return $this->belongsToMany(Admin::class, 'menu_item_role', 'menu_item_id', 'role_id');
-    }
-}
+}

+ 74 - 54
packages/Longyi/DynamicMenu/src/Providers/DynamicMenuServiceProvider.php

@@ -3,86 +3,106 @@
 namespace Longyi\DynamicMenu\Providers;
 
 use Illuminate\Support\ServiceProvider;
-use Illuminate\Support\Facades\Route; // 添加这行!很重要
-use Illuminate\Support\Facades\Event;
-use Longyi\DynamicMenu\Http\Middleware\CheckMenuPermission;
+use Illuminate\Support\Facades\Cache;
+use Longyi\DynamicMenu\Models\MenuItem;
 
 class DynamicMenuServiceProvider extends ServiceProvider
 {
     public function boot()
     {
+        // 加载迁移
         $this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations');
+        
+        // 加载路由
         $this->loadRoutesFrom(__DIR__ . '/../Routes/admin-routes.php');
+        
+        // 加载视图
         $this->loadViewsFrom(__DIR__ . '/../Resources/views', 'dynamicmenu');
+         // 加载语言文件
         $this->loadTranslationsFrom(__DIR__ . '/../Resources/lang', 'dynamicmenu');
-
+        // 发布资源
         $this->publishes([
             __DIR__ . '/../../publishable/assets' => public_path('vendor/dynamicmenu'),
         ], 'public');
-
-        // 注册菜单
-        $this->registerAdminMenu();
     }
 
     public function register()
     {
-        $this->mergeConfigFrom(
-            __DIR__ . '/../Config/menu.php', 'menu.admin'
-        );
-        $this->app->singleton('dynamicmenu', function () {
-            return new \Longyi\DynamicMenu\DynamicMenu();
+        // 延迟合并配置,等到服务提供者注册完成后
+        $this->app->booted(function () {
+            $this->mergeDynamicMenuConfig();
         });
     }
 
-    protected function registerAdminMenu()
+    /**
+     * 合并动态菜单配置
+     */
+    protected function mergeDynamicMenuConfig()
     {
-        Event::listen('bagisto.admin.menu.build', function ($menu) {
-            // 在 Settings 菜单下添加 Dynamic Menu
-            $menu->add('settings.dynamic-menu', 'dynamicmenu::app.admin.menu.title', 'admin.dynamicmenu.index', 5, 'icon-menu')
-                 ->addSub('settings.dynamic-menu.items', 'dynamicmenu::app.admin.menu.items', 'admin.dynamicmenu.index', 1, 'icon-list')
-                 ->addSub('settings.dynamic-menu.permissions', 'dynamicmenu::app.admin.menu.permissions', 'admin.dynamicmenu.permission', 2, 'icon-permission');
-        });
+        try {
+            // 从数据库获取菜单配置
+            $menuConfig = $this->getMenuConfigFromDatabase();
+            // 获取现有配置
+            $existingConfig = $this->app['config']->get('menu.admin', []);
+            // 合并配置
+            $mergedConfig = array_merge($existingConfig, $menuConfig);
+            // 设置配置
+            $this->app['config']->set('menu.admin', $mergedConfig);
+            
+        } catch (\Exception $e) {
+            \Log::error('合并动态菜单配置失败: ' . $e->getMessage());
+        }
     }
-    protected function registerMenu()
+
+    /**
+     * 从数据库获取菜单配置
+     */
+    protected function getMenuConfigFromDatabase()
     {
-        $this->app->booted(function () {
-            if (!$this->app->runningInConsole()) {
-                $this->app['events']->listen('bagisto.menu.admin.build', function ($menu) {
-                    $this->buildMenu($menu);
-                });
+        $menuItems = MenuItem::with('children')
+                    ->where('status', 1)
+                    ->orderBy('sort_order')
+                    ->get();
+                
+        return $this->buildMenuConfig($menuItems);
+        // 使用缓存
+        /*
+        return Cache::remember('dynamic_menu_config', 3600, function () {
+            try {
+                $menuItems = MenuItem::with('children')
+                    ->whereNull('parent_id')
+                    ->where('status', 1)
+                    ->orderBy('sort_order')
+                    ->get();
+                
+                return $this->buildMenuConfig($menuItems);
+                
+            } catch (\Exception $e) {
+                \Log::warning('无法从数据库获取菜单: ' . $e->getMessage());
+                return [];
             }
         });
+        */
     }
-    
-    protected function buildMenu($menu)
+
+    /**
+     * 构建菜单配置数组
+     */
+    protected function buildMenuConfig($menuItems)
     {
-        try {
-            // 从数据库加载菜单项
-            $items = \Longyi\DynamicMenu\Models\MenuItem::where('status', 1)
-                ->orderBy('parent_id')
-                ->orderBy('sort_order')
-                ->get();
-            
-            foreach ($items as $item) {
-                // 确保 key 存在
-                if (empty($item->key)) {
-                    \Log::warning('菜单项缺少 key', ['id' => $item->id, 'name' => $item->name]);
-                    continue;
-                }
-                
-                $parentKey = $item->parent ? $item->parent->key : null;
-                
-                $menu->addItem(
-                    $item->key,
-                    $item->name,
-                    $parentKey,
-                    $item->route,
-                    $item->icon,
-                    (int) $item->sort_order
-                );
-            }
-        } catch (\Exception $e) {
-            // 表可能不存在,静默失败
+        $config = [];
+        
+        foreach ($menuItems as $item) {
+            $menuItem = [
+                'key'   => $item->key,
+                'name'  => $item->name,
+                'route' => $item->route ?: 'admin.dynamicmenu.index',
+                'sort'  => (int) $item->sort_order,
+                'icon'  => $item->icon ?: 'icon-menu',
+            ];
+            $config[] = $menuItem;
         }
+        
+        return $config;
     }
 }

+ 126 - 0
packages/Longyi/DynamicMenu/src/Providers/MenuProvider.php

@@ -0,0 +1,126 @@
+<?php
+
+namespace Longyi\DynamicMenu\Providers;
+
+use Illuminate\Support\ServiceProvider;
+use Longyi\DynamicMenu\Services\MenuService;
+
+class MenuProvider extends ServiceProvider
+{
+    public function boot(MenuService $menuService)
+    {
+        // 在 Bagisto 菜单构建时添加动态菜单
+        $this->app['events']->listen('bagisto.admin.menu.build', function ($menu) use ($menuService) {
+            $this->registerDynamicMenus($menu, $menuService);
+        });
+    }
+    
+    /**
+     * 注册动态菜单到 Bagisto
+     */
+    protected function registerDynamicMenus($menu, MenuService $menuService)
+    {
+        // 获取所有顶级菜单
+        $topLevelMenus = $menuService->buildMenuTree();
+        
+        foreach ($topLevelMenus as $menuItem) {
+            // 检查菜单是否应该显示在 Settings 下
+            if (strpos($menuItem['key'], 'settings.') === 0) {
+                // 如果菜单key以settings.开头,添加到Settings菜单下
+                $this->addToSettingsMenu($menu, $menuItem);
+            } else {
+                // 否则作为顶级菜单添加
+                $this->addMenuItem($menu, null, $menuItem);
+            }
+        }
+    }
+    
+    /**
+     * 添加菜单项到指定父菜单
+     */
+    protected function addMenuItem($menu, $parentKey = null, $item)
+    {
+        if (isset($item['children']) && count($item['children']) > 0) {
+            // 有子菜单
+            $parent = $menu->add(
+                $item['key'],
+                $item['name'],
+                $item['route'],
+                $item['sort'],
+                $item['icon']
+            );
+            
+            // 添加子菜单
+            foreach ($item['children'] as $child) {
+                $this->addMenuItem($parent, $item['key'], $child);
+            }
+        } else {
+            // 无子菜单
+            if ($parentKey) {
+                // 添加到指定父菜单
+                $menu->add(
+                    $item['key'],
+                    $item['name'],
+                    $item['route'],
+                    $item['sort'],
+                    $item['icon'],
+                    $parentKey
+                );
+            } else {
+                // 作为顶级菜单
+                $menu->add(
+                    $item['key'],
+                    $item['name'],
+                    $item['route'],
+                    $item['sort'],
+                    $item['icon']
+                );
+            }
+        }
+    }
+    
+    /**
+     * 添加到 Settings 菜单
+     */
+    protected function addToSettingsMenu($menu, $item)
+    {
+        // 确保 Settings 菜单存在
+        $settings = $menu->get('settings');
+        
+        if (!$settings) {
+            // 如果 Settings 菜单不存在,先创建
+            $settings = $menu->add('settings', 'Settings', 'admin.settings.index', 1, 'icon-settings');
+        }
+        
+        if (isset($item['children']) && count($item['children']) > 0) {
+            // 有子菜单
+            $parent = $settings->addChild(
+                $item['key'],
+                $item['name'],
+                $item['route'],
+                $item['sort'],
+                $item['icon']
+            );
+            
+            // 添加子菜单
+            foreach ($item['children'] as $child) {
+                $parent->addChild(
+                    $child['key'],
+                    $child['name'],
+                    $child['route'],
+                    $child['sort'],
+                    $child['icon']
+                );
+            }
+        } else {
+            // 无子菜单,直接添加到 Settings
+            $settings->addChild(
+                $item['key'],
+                $item['name'],
+                $item['route'],
+                $item['sort'],
+                $item['icon']
+            );
+        }
+    }
+}

+ 69 - 26
packages/Longyi/DynamicMenu/src/Resources/views/admin/menu/create.blade.php

@@ -57,14 +57,16 @@
                             Key <span style="color: red;">*</span>
                         </label>
                         <input type="text" 
-                               name="key" 
-                               class="control" 
-                               value="{{ old('key') }}" 
-                               required
-                               placeholder="例如:admin.dashboard"
-                               style="width: 100%; padding: 8px 12px; border: 1px solid #dee2e6; border-radius: 4px; font-size: 14px;">
+                            name="key" 
+                            class="control" 
+                            value="{{ old('key') }}" 
+                            required
+                            placeholder="例如:settings.dynamic-menu"
+                            style="width: 100%; padding: 8px 12px; border: 1px solid #dee2e6; border-radius: 4px; font-size: 14px;">
                         <small class="control-info" style="display: block; margin-top: 5px; font-size: 12px; color: #6c757d;">
-                            用于权限验证和菜单激活状态判断
+                            <strong style="color: #dc3545;">重要提示:</strong> 
+                            如果想让菜单显示在 Settings 下,Key 必须以 <strong style="background: #f0f0f0; padding: 2px 5px; border-radius: 3px;">settings.</strong> 开头<br>
+                            例如:<code>settings.dynamic-menu</code>、<code>settings.my-menu</code>、<code>settings.custom</code>
                         </small>
                     </div>
 
@@ -156,26 +158,67 @@
     </div>
 
     {{-- 引入必要的JavaScript --}}
-    @push('scripts')
-    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
-    <script>
-        $(document).ready(function() {
-            // 自动生成key
-            $('input[name="name"]').on('blur', function() {
-                var name = $(this).val();
-                var keyInput = $('input[name="key"]');
+@push('scripts')
+<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
+<script>
+    $(document).ready(function() {
+        // 自动生成key
+        $('input[name="name"]').on('blur', function() {
+            var name = $(this).val();
+            var keyInput = $('input[name="key"]');
+            
+            if (keyInput.val() === '') {
+                // 将名称转换为小写,空格替换为横线,移除特殊字符
+                var key = name.toLowerCase()
+                             .replace(/\s+/g, '-')
+                             .replace(/[^a-z0-9-]/g, '');
+                // 默认添加到 settings 下
+                keyInput.val('settings.' + key);
                 
-                if (keyInput.val() === '') {
-                    var key = name.toLowerCase()
-                                 .replace(/\s+/g, '.')
-                                 .replace(/[^a-z0-9.]/g, '');
-                    keyInput.val('admin.' + key);
+                // 添加一个视觉提示
+                keyInput.css('border-color', '#28a745');
+                setTimeout(function() {
+                    keyInput.css('border-color', '');
+                }, 1000);
+                
+                // 显示成功提示
+                var successMsg = $('<small class="key-success" style="display: block; margin-top: 5px; font-size: 12px; color: #28a745;">' +
+                                 '✓ 已自动生成Key: settings.' + key + '</small>');
+                keyInput.after(successMsg);
+                setTimeout(function() {
+                    $('.key-success').remove();
+                }, 3000);
+            }
+        });
+        
+        // 为所有表单元素添加Bagisto样式类
+        $('.control').addClass('form-control');
+        
+        // 检查key格式
+        $('form').on('submit', function(e) {
+            var keyValue = $('input[name="key"]').val();
+            if (keyValue && keyValue.indexOf('settings.') !== 0) {
+                if (!confirm('当前Key不以"settings."开头,菜单可能不会显示在Settings下。确定要继续吗?')) {
+                    e.preventDefault();
                 }
-            });
-            
-            // 为所有表单元素添加Bagisto样式类
-            $('.control').addClass('form-control');
+            }
+        });
+        
+        // 添加提示
+        $('input[name="key"]').on('focus', function() {
+            var currentVal = $(this).val();
+            if (currentVal && currentVal.indexOf('settings.') !== 0) {
+                $(this).after(
+                    '<small class="key-warning" style="display: block; margin-top: 5px; font-size: 12px; color: #dc3545;">' +
+                    '⚠️ 警告:当前Key不以"settings."开头,菜单可能不会显示在Settings下</small>'
+                );
+                
+                setTimeout(function() {
+                    $('.key-warning').remove();
+                }, 3000);
+            }
         });
-    </script>
-    @endpush
+    });
+</script>
+@endpush
 </x-admin::layouts>

+ 15 - 17
packages/Longyi/DynamicMenu/src/Resources/views/admin/menu/edit.blade.php

@@ -52,20 +52,17 @@
                                style="width: 100%; padding: 8px 12px; border: 1px solid #dee2e6; border-radius: 4px; font-size: 14px;">
                     </div>
 
-                    {{-- Key --}}
-                    <div class="form-group" style="margin-bottom: 20px;">
-                        <label class="required" style="display: block; margin-bottom: 5px; font-weight: 500;">
-                            Key <span style="color: red;">*</span>
-                        </label>
+                    {{-- Key 字段 --}}
+                    <div class="form-group">
+                        <label class="required">Key</label>
                         <input type="text" 
-                               name="key" 
-                               class="control" 
-                               value="{{ old('key', $menuItem->key) }}" 
-                               required
-                               placeholder="例如:admin.dashboard"
-                               style="width: 100%; padding: 8px 12px; border: 1px solid #dee2e6; border-radius: 4px; font-size: 14px;">
-                        <small class="control-info" style="display: block; margin-top: 5px; font-size: 12px; color: #6c757d;">
-                            用于权限验证和菜单激活状态判断
+                            name="key" 
+                            class="control" 
+                            value="{{ old('key', $menuItem->key ?? '') }}" 
+                            required
+                            placeholder="例如:settings.my-menu">
+                        <small class="control-info">
+                            建议以 'settings.' 开头,这样菜单会显示在 Settings 下。例如:settings.dynamic-menu
                         </small>
                     </div>
 
@@ -163,16 +160,17 @@
     <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
     <script>
         $(document).ready(function() {
-            // 自动生成key(如果key为空)
+            // 自动生成key
             $('input[name="name"]').on('blur', function() {
                 var name = $(this).val();
                 var keyInput = $('input[name="key"]');
                 
                 if (keyInput.val() === '') {
                     var key = name.toLowerCase()
-                                 .replace(/\s+/g, '.')
-                                 .replace(/[^a-z0-9.]/g, '');
-                    keyInput.val('admin.' + key);
+                                .replace(/\s+/g, '-')
+                                .replace(/[^a-z0-9-]/g, '');
+                    // 默认添加到 settings 下
+                    keyInput.val('settings.' + key);
                 }
             });
         });

+ 7 - 10
packages/Longyi/DynamicMenu/src/Routes/admin-routes.php

@@ -6,43 +6,40 @@ use Longyi\DynamicMenu\Http\Controllers\MenuController;
 // 关键:使用正确的路由组
 Route::group(['middleware' => ['web', 'admin'], 'prefix' => 'admin'], function () {
     
-    // 测试路由(用于验证)
-    Route::get('test-dynamicmenu', function() {
-        return '后台路由工作正常!';
-    })->name('admin.test.dynamicmenu');
+    
     
     // 菜单管理路由 - 注意这里不要重复加 'admin' 前缀
-    Route::get('dynamicmenu-items', [MenuController::class, 'index'])
+    Route::get('dynamicmenu', [MenuController::class, 'index'])
         ->name('admin.dynamicmenu.index')
         ->defaults('_config', [
             'view' => 'dynamicmenu::admin.menu.index'
         ]);
     
-    Route::get('dynamicmenu-items/create', [MenuController::class, 'create'])
+    Route::get('dynamicmenu/create', [MenuController::class, 'create'])
         ->name('admin.dynamicmenu.create')
         ->defaults('_config', [
             'view' => 'dynamicmenu::admin.menu.create'
         ]);
     
-    Route::post('dynamicmenu-items', [MenuController::class, 'store'])
+    Route::post('dynamicmenu', [MenuController::class, 'store'])
         ->name('admin.dynamicmenu.store')
         ->defaults('_config', [
             'redirect' => 'admin.dynamicmenu.index'
         ]);
     
-    Route::get('dynamicmenu-items/{id}/edit', [MenuController::class, 'edit'])
+    Route::get('dynamicmenu/{id}/edit', [MenuController::class, 'edit'])
         ->name('admin.dynamicmenu.edit')
         ->defaults('_config', [
             'view' => 'dynamicmenu::admin.menu.edit'
         ]);
     
-    Route::put('dynamicmenu-items/{id}', [MenuController::class, 'update'])
+    Route::put('dynamicmenu/{id}', [MenuController::class, 'update'])
         ->name('admin.dynamicmenu.update')
         ->defaults('_config', [
             'redirect' => 'admin.dynamicmenu.index'
         ]);
     
-    Route::delete('dynamicmenu-items/{id}', [MenuController::class, 'destroy'])
+    Route::delete('dynamicmenu/{id}', [MenuController::class, 'destroy'])
         ->name('admin.dynamicmenu.delete');
     
     // 权限管理路由

+ 49 - 0
packages/Longyi/DynamicMenu/src/Services/MenuService.php

@@ -0,0 +1,49 @@
+<?php
+
+namespace Longyi\DynamicMenu\Services;
+
+use Longyi\DynamicMenu\Models\MenuItem;
+use Illuminate\Support\Facades\Cache;
+
+class MenuService
+{
+
+    public function boot()
+    {
+        // ... 其他代码
+
+        Event::listen('bagisto.admin.menu.build', function ($menu) {
+            $settings = $menu->get('settings');
+            
+            if ($settings) {
+                // 使用缓存,避免每次请求都查询数据库
+                $menuItems = Cache::remember('dynamic_menu_items', 3600, function () {
+                    return app(MenuService::class)->getEnabledMenuItems();
+                });
+                
+                foreach ($menuItems as $item) {
+                    $this->addMenuItemToMenu($settings, $item);
+                }
+            }
+        });
+    }
+    /**
+     * 获取所有启用的菜单项
+     */
+    public function getEnabledMenuItems()
+    {
+        return MenuItem::with('children')
+            ->whereNull('parent_id')
+            ->where('status', 1)
+            ->orderBy('sort_order')
+            ->get();
+    }
+    
+    /**
+     * 清除菜单缓存
+     */
+    public function clearCache()
+    {
+        Cache::forget('dynamic_menu_items');
+    }
+}