Przeglądaj źródła

更新配置文件和添加Longyi模块后台导航菜单管理

bianjunhui 2 dni temu
rodzic
commit
fcd19281e0
23 zmienionych plików z 861 dodań i 3 usunięć
  1. 1 0
      .gitignore
  2. 1 0
      bootstrap/providers.php
  3. 2 1
      composer.json
  4. 37 0
      config/longyi/menu.php
  5. 38 0
      database/migrations/Longyi/2024_01_01_000001_create_dynamic_menu_items_table.php
  6. 33 0
      database/migrations/Longyi/2024_01_01_000002_create_menu_item_role_table.php
  7. 28 0
      packages/Longyi/DynamicMenu/src/Config/menu.php
  8. 48 0
      packages/Longyi/DynamicMenu/src/DynamicMenu.php
  9. 63 0
      packages/Longyi/DynamicMenu/src/DynamicMenuServiceProvider.php
  10. 160 0
      packages/Longyi/DynamicMenu/src/Http/Controllers/MenuController.php
  11. 40 0
      packages/Longyi/DynamicMenu/src/Http/Middleware/CheckMenuPermission.php
  12. 47 0
      packages/Longyi/DynamicMenu/src/Models/MenuItem.php
  13. 17 0
      packages/Longyi/DynamicMenu/src/Models/MenuItemRole.php
  14. 83 0
      packages/Longyi/DynamicMenu/src/Providers/DynamicMenuServiceProvider.php
  15. 44 0
      packages/Longyi/DynamicMenu/src/Repositories/MenuItemRepository.php
  16. 40 0
      packages/Longyi/DynamicMenu/src/Resources/lang/en/app.php
  17. 35 0
      packages/Longyi/DynamicMenu/src/Resources/views/admin/menu/create.blade.php
  18. 23 0
      packages/Longyi/DynamicMenu/src/Resources/views/admin/menu/index.blade.php
  19. 36 0
      packages/Longyi/DynamicMenu/src/Resources/views/admin/menu/partials/tree.blade.php
  20. 22 0
      packages/Longyi/DynamicMenu/src/Resources/views/admin/permission/index.blade.php
  21. 60 0
      packages/Longyi/DynamicMenu/src/Routes/admin-routes.php
  22. 3 0
      routes/web.php
  23. 0 2
      storage/framework/cache/data/.gitignore

+ 1 - 0
.gitignore

@@ -30,3 +30,4 @@ yarn.lock
 yarn-error.log
 /test-results
 /playwright-report
+/themes/

+ 1 - 0
bootstrap/providers.php

@@ -43,4 +43,5 @@ return [
     Webkul\Tax\Providers\TaxServiceProvider::class,
     Webkul\Theme\Providers\ThemeServiceProvider::class,
     Webkul\User\Providers\UserServiceProvider::class,
+    Longyi\DynamicMenu\Providers\DynamicMenuServiceProvider::class,
 ];

+ 2 - 1
composer.json

@@ -95,7 +95,8 @@
             "Webkul\\SocialShare\\": "packages/Webkul/SocialShare/src",
             "Webkul\\Tax\\": "packages/Webkul/Tax/src",
             "Webkul\\Theme\\": "packages/Webkul/Theme/src",
-            "Webkul\\User\\": "packages/Webkul/User/src"
+            "Webkul\\User\\": "packages/Webkul/User/src",
+            "Longyi\\DynamicMenu\\": "packages/Longyi/DynamicMenu/src"
         }
     },
     "autoload-dev": {

+ 37 - 0
config/longyi/menu.php

@@ -0,0 +1,37 @@
+<?php
+
+return [
+    'admin' => [
+        [
+            'key' => 'settings',
+            'name' => 'Settings',
+            'route' => 'admin.settings.index',
+            'sort' => 1,
+            'icon' => 'icon-settings',
+        ],
+        [
+            'key' => 'settings.dynamicmenu',
+            'name' => 'Dynamic Menu',
+            'route' => 'admin.dynamicmenu.index',
+            'sort' => 10,
+            'icon' => 'icon-menu',
+            'parent' => 'settings'
+        ],
+        [
+            'key' => 'settings.dynamicmenu.menu',
+            'name' => 'Menu Items',
+            'route' => 'admin.dynamicmenu.index',
+            'sort' => 1,
+            'icon' => 'icon-list',
+            'parent' => 'settings.dynamicmenu'
+        ],
+        [
+            'key' => 'settings.dynamicmenu.permission',
+            'name' => 'Menu Permissions',
+            'route' => 'admin.dynamicmenu.permission',
+            'sort' => 2,
+            'icon' => 'icon-lock',
+            'parent' => 'settings.dynamicmenu'
+        ],
+    ],
+];

+ 38 - 0
database/migrations/Longyi/2024_01_01_000001_create_dynamic_menu_items_table.php

@@ -0,0 +1,38 @@
+<?php
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreateDynamicMenuItemsTable extends Migration
+{
+    public function up()
+    {
+        Schema::create('dynamic_menu_items', function (Blueprint $table) {
+            $table->id();
+            $table->string('name');
+            $table->string('key')->unique();
+            $table->string('route');
+            $table->string('icon')->nullable();
+            $table->unsignedBigInteger('parent_id')->nullable();
+            $table->integer('sort_order')->default(0);
+            $table->boolean('status')->default(true);
+            $table->unsignedBigInteger('created_by')->nullable();
+            $table->timestamps();
+
+            $table->foreign('parent_id')
+                ->references('id')
+                ->on('dynamic_menu_items')
+                ->onDelete('cascade');
+
+            $table->foreign('created_by')
+                ->references('id')
+                ->on('admins')
+                ->onDelete('set null');
+        });
+    }
+
+    public function down()
+    {
+        Schema::dropIfExists('dynamic_menu_items');
+    }
+}

+ 33 - 0
database/migrations/Longyi/2024_01_01_000002_create_menu_item_role_table.php

@@ -0,0 +1,33 @@
+<?php
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreateMenuItemRoleTable extends Migration
+{
+    public function up()
+    {
+        Schema::create('menu_item_role', function (Blueprint $table) {
+            $table->id();
+            $table->unsignedBigInteger('menu_item_id');
+            $table->unsignedBigInteger('role_id');
+
+            $table->foreign('menu_item_id')
+                ->references('id')
+                ->on('dynamic_menu_items')
+                ->onDelete('cascade');
+
+            $table->foreign('role_id')
+                ->references('id')
+                ->on('roles')
+                ->onDelete('cascade');
+
+            $table->unique(['menu_item_id', 'role_id']);
+        });
+    }
+
+    public function down()
+    {
+        Schema::dropIfExists('menu_item_role');
+    }
+}

+ 28 - 0
packages/Longyi/DynamicMenu/src/Config/menu.php

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

+ 48 - 0
packages/Longyi/DynamicMenu/src/DynamicMenu.php

@@ -0,0 +1,48 @@
+<?php
+
+namespace Longyi\DynamicMenu;
+
+use Longyi\DynamicMenu\Models\MenuItem as MenuItemModel;
+use Illuminate\Support\Facades\Route;
+
+class DynamicMenu
+{
+    public function registerMenuItems()
+    {
+        try {
+            $items = MenuItemModel::where('status', 1)
+                ->orderBy('parent_id')
+                ->orderBy('sort_order')
+                ->get();
+                
+            foreach ($items as $item) {
+                $this->addToBagistoMenu($item);
+            }
+        } catch (\Exception $e) {
+            report($e);
+        }
+    }
+    
+    private function addToBagistoMenu($item)
+    {
+        $parent = $item->parent ? $item->parent->key : null;
+        
+        // 方法1:使用路由名称(推荐)
+        // Bagisto 会在内部使用 route() 辅助函数生成URL
+        menu()->addItem(
+            $item->key,
+            $item->name,
+            $parent,
+            $item->route,  // 这里应该是路由名称,如 'admin.dynamicmenu.index'
+            $item->icon,
+            (int) $item->sort_order
+        );
+        
+        // 调试日志
+        \Log::info('注册菜单项', [
+            'key' => $item->key,
+            'route_name' => $item->route,
+            'generated_url' => route($item->route, [], false)
+        ]);
+    }
+}

+ 63 - 0
packages/Longyi/DynamicMenu/src/DynamicMenuServiceProvider.php

@@ -0,0 +1,63 @@
+<?php
+
+namespace Longyi\DynamicMenu;
+
+use Illuminate\Support\ServiceProvider;
+use Illuminate\Routing\Router;
+use Longyi\DynamicMenu\Http\Middleware\CheckMenuPermission;
+
+class DynamicMenuServiceProvider extends ServiceProvider
+{
+    public function boot(Router $router)
+    {
+        // Load routes
+        $this->loadRoutesFrom(__DIR__ . '/Routes/admin-routes.php');
+
+        // Load views
+        $this->loadViewsFrom(__DIR__ . '/Resources/views', 'dynamicmenu');
+
+        // Load translations
+        $this->loadTranslationsFrom(__DIR__ . '/Resources/lang', 'dynamicmenu');
+
+        // Load migrations
+        $this->loadMigrationsFrom(__DIR__ . '/Database/Migrations');
+
+        // Register middleware
+        $router->aliasMiddleware('menu.permission', CheckMenuPermission::class);
+
+        // Publish assets
+        $this->publishes([
+            __DIR__ . '/Resources/views' => resource_path('views/vendor/longyi/dynamicmenu'),
+        ], 'dynamicmenu-views');
+
+        $this->publishes([
+            __DIR__ . '/Database/Seeders' => database_path('seeders'),
+        ], 'dynamicmenu-seeds');
+
+        $this->publishes([
+            __DIR__ . '/Config' => config_path('longyi'),
+        ], 'dynamicmenu-config');
+
+        // Register menu items
+        $this->app->booted(function () {
+            $this->registerMenuItems();
+        });
+    }
+
+    public function register()
+    {
+        $this->mergeConfigFrom(__DIR__ . '/Config/menu.php', 'dynamicmenu');
+
+        $this->app->singleton('dynamicmenu', function () {
+            return new DynamicMenu();
+        });
+    }
+
+    private function registerMenuItems()
+    {
+        if (app('request')->is('admin*')) {
+            $dynamicMenu = app('dynamicmenu');
+            $dynamicMenu->registerMenuItems();
+        }
+    }
+}

+ 160 - 0
packages/Longyi/DynamicMenu/src/Http/Controllers/MenuController.php

@@ -0,0 +1,160 @@
+<?php
+
+namespace Longyi\DynamicMenu\Http\Controllers;
+
+use Illuminate\Http\Request;
+use Illuminate\Routing\Controller;
+use Longyi\DynamicMenu\Models\MenuItem;
+use Webkul\User\Repositories\RoleRepository;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\View;
+
+class MenuController extends Controller
+{
+    protected $roleRepository;
+    
+    public function __construct(RoleRepository $roleRepository)
+    {
+        $this->roleRepository = $roleRepository;
+        $this->_config = request('_config');
+    }
+    
+    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) {
+        \Log::error('MenuController 错误: ' . $e->getMessage());
+        \Log::error($e->getTraceAsString());
+        throw $e;
+    }
+    
+    \Log::info('===== MenuController::index 结束 =====');
+    }
+    
+    public function create()
+    {
+        $menuItems = MenuItem::orderBy('parent_id')->orderBy('sort_order')->get();
+        return view($this->_config['view'], compact('menuItems'));
+    }
+    
+    public function store(Request $request)
+    {
+        $this->validate($request, [
+            'name' => 'required|string|max:255',
+            'key' => 'required|string|unique:ly_dynamic_menu_items,key',
+            'route' => 'required|string',
+            'icon' => 'nullable|string',
+            'parent_id' => 'nullable|exists:ly_dynamic_menu_items,id',
+            'sort_order' => 'integer',
+            'status' => 'boolean'
+        ]);
+        
+        $data = $request->all();
+        $data['created_by'] = auth()->guard('admin')->user()->id;
+        
+        MenuItem::create($data);
+        
+        session()->flash('success', '菜单项创建成功');
+        
+        return redirect()->route($this->_config['redirect']);
+    }
+    
+    public function edit($id)
+    {
+        $menuItem = MenuItem::with('parent')->findOrFail($id);
+        $menuItems = MenuItem::orderBy('parent_id')->orderBy('sort_order')->get();
+        
+        return view($this->_config['view'], compact('menuItem', 'menuItems'));
+    }
+    
+    public function update(Request $request, $id)
+    {
+        $this->validate($request, [
+            'name' => 'required|string|max:255',
+            'key' => 'required|string|unique:ly_dynamic_menu_items,key,' . $id,
+            'route' => 'required|string',
+            'icon' => 'nullable|string',
+            'parent_id' => 'nullable|exists:ly_dynamic_menu_items,id',
+            'sort_order' => 'integer',
+            'status' => 'boolean'
+        ]);
+        
+        $menuItem = MenuItem::findOrFail($id);
+        $menuItem->update($request->all());
+        
+        session()->flash('success', '菜单项更新成功');
+        
+        return redirect()->route($this->_config['redirect']);
+    }
+    
+    public function destroy($id)
+    {
+        $menuItem = MenuItem::with('children')->findOrFail($id);
+        
+        if ($menuItem->children->count() > 0) {
+            session()->flash('error', '请先删除子菜单项');
+            return redirect()->back();
+        }
+        
+        $menuItem->delete();
+        
+        session()->flash('success', '菜单项删除成功');
+        
+        return redirect()->route($this->_config['redirect']);
+    }
+    
+    public function permission()
+    {
+        $roles = $this->roleRepository->all();
+        $menuItems = MenuItem::with('children')->orderBy('parent_id')->orderBy('sort_order')->get();
+        
+        return view($this->_config['view'], compact('roles', 'menuItems'));
+    }
+    
+    public function updatePermission(Request $request)
+    {
+        $roleId = $request->input('role_id');
+        $menuItemIds = $request->input('menu_items', []);
+        
+        DB::table('menu_item_role')->where('role_id', $roleId)->delete();
+        
+        foreach ($menuItemIds as $menuItemId) {
+            DB::table('menu_item_role')->insert([
+                'menu_item_id' => $menuItemId,
+                'role_id' => $roleId
+            ]);
+        }
+        
+        session()->flash('success', '权限更新成功');
+        
+        return redirect()->back();
+    }
+    
+    public function getRolePermissions(Request $request)
+    {
+        $roleId = $request->input('role_id');
+        
+        $permissions = DB::table('menu_item_role')
+            ->where('role_id', $roleId)
+            ->pluck('menu_item_id')
+            ->toArray();
+            
+        return response()->json($permissions);
+    }
+}

+ 40 - 0
packages/Longyi/DynamicMenu/src/Http/Middleware/CheckMenuPermission.php

@@ -0,0 +1,40 @@
+<?php
+
+namespace Longyi\DynamicMenu\Http\Middleware;
+
+use Closure;
+use Longyi\DynamicMenu\Models\MenuItem;
+
+class CheckMenuPermission
+{
+    public function handle($request, Closure $next)
+    {
+        if (! auth()->guard('admin')->check()) {
+            return $next($request);
+        }
+
+        $admin = auth()->guard('admin')->user();
+        $routeName = $request->route()->getName();
+
+        // Skip check for super admin
+        if ($admin->role->permission_type === 'all') {
+            return $next($request);
+        }
+
+        // Find menu item by route
+        $menuItem = MenuItem::where('route', $routeName)->first();
+
+        if ($menuItem) {
+            $hasPermission = $admin->role->menuItems()
+                ->where('menu_item_id', $menuItem->id)
+                ->exists();
+
+            if (! $hasPermission) {
+                session()->flash('warning', trans('dynamicmenu::app.permission.denied'));
+                return redirect()->route('admin.dashboard.index');
+            }
+        }
+
+        return $next($request);
+    }
+}

+ 47 - 0
packages/Longyi/DynamicMenu/src/Models/MenuItem.php

@@ -0,0 +1,47 @@
+<?php
+
+namespace Longyi\DynamicMenu\Models;
+
+use Illuminate\Database\Eloquent\Model;
+use Webkul\User\Models\Admin;
+
+class MenuItem extends Model
+{
+    protected $table = 'dynamic_menu_items';
+
+    protected $fillable = [
+        'name',
+        'key',
+        'route',
+        'icon',
+        'parent_id',
+        'sort_order',
+        'status',
+        'created_by'
+    ];
+
+    protected $casts = [
+        'status' => 'boolean',
+        'sort_order' => 'integer'
+    ];
+
+    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');
+    }
+}

+ 17 - 0
packages/Longyi/DynamicMenu/src/Models/MenuItemRole.php

@@ -0,0 +1,17 @@
+<?php
+
+namespace Longyi\DynamicMenu\Models;
+
+use Illuminate\Database\Eloquent\Model;
+
+class MenuItemRole extends Model
+{
+    protected $table = 'menu_item_role';
+
+    protected $fillable = [
+        'menu_item_id',
+        'role_id'
+    ];
+
+    public $timestamps = false;
+}

+ 83 - 0
packages/Longyi/DynamicMenu/src/Providers/DynamicMenuServiceProvider.php

@@ -0,0 +1,83 @@
+<?php
+
+namespace Longyi\DynamicMenu\Providers;
+
+use Illuminate\Support\ServiceProvider;
+use Illuminate\Support\Facades\Route; // 添加这行!很重要
+use Longyi\DynamicMenu\Http\Middleware\CheckMenuPermission;
+
+class DynamicMenuServiceProvider extends ServiceProvider
+{
+    public function boot()
+    {
+       
+        // 检查路由文件是否存在
+        $routePath = __DIR__ . '/../Routes/admin-routes.php';
+        $this->loadRoutesFrom($routePath);
+        $this->loadViewsFrom(__DIR__ . '/../Resources/views', 'dynamicmenu');
+        $this->loadTranslationsFrom(__DIR__ . '/../Resources/lang', 'dynamicmenu');
+        $this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations');
+        
+        $this->app['router']->aliasMiddleware('menu.permission', CheckMenuPermission::class);
+        
+        $this->publishes([
+            __DIR__ . '/../Resources/views' => resource_path('views/vendor/longyi/dynamicmenu'),
+        ], 'views');
+        
+        // 注册菜单
+        $this->registerMenu();
+    }
+    
+    public function register()
+    {
+        // 合并配置时,确保所有菜单项都有 key
+        $this->mergeConfigFrom(__DIR__ . '/../Config/menu.php', 'menu.admin');
+        
+        $this->app->singleton('dynamicmenu', function () {
+            return new \Longyi\DynamicMenu\DynamicMenu();
+        });
+    }
+    
+    protected function registerMenu()
+    {
+        $this->app->booted(function () {
+            if (!$this->app->runningInConsole()) {
+                $this->app['events']->listen('bagisto.menu.admin.build', function ($menu) {
+                    $this->buildMenu($menu);
+                });
+            }
+        });
+    }
+    
+    protected function buildMenu($menu)
+    {
+        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) {
+            // 表可能不存在,静默失败
+        }
+    }
+}

+ 44 - 0
packages/Longyi/DynamicMenu/src/Repositories/MenuItemRepository.php

@@ -0,0 +1,44 @@
+<?php
+
+namespace Longyi\DynamicMenu\Repositories;
+
+use Webkul\Core\Eloquent\Repository;
+use Longyi\DynamicMenu\Models\MenuItem;
+
+class MenuItemRepository extends Repository
+{
+    public function model()
+    {
+        return MenuItem::class;
+    }
+
+    public function getMenuTree()
+    {
+        $items = $this->all();
+        return $this->buildTree($items);
+    }
+
+    private function buildTree($items, $parentId = null)
+    {
+        $tree = [];
+
+        foreach ($items as $item) {
+            if ($item->parent_id == $parentId) {
+                $children = $this->buildTree($items, $item->id);
+                if ($children) {
+                    $item->children = $children;
+                }
+                $tree[] = $item;
+            }
+        }
+
+        return $tree;
+    }
+
+    public function getMenuItemsForRole($roleId)
+    {
+        return $this->model->whereHas('roles', function($query) use ($roleId) {
+            $query->where('role_id', $roleId);
+        })->where('status', 1)->get();
+    }
+}

+ 40 - 0
packages/Longyi/DynamicMenu/src/Resources/lang/en/app.php

@@ -0,0 +1,40 @@
+<?php
+
+return [
+    'menu' => [
+        'title' => 'Dynamic Menu Management',
+        'add-title' => 'Add Menu Item',
+        'edit-title' => 'Edit Menu Item',
+        'name' => 'Menu Name',
+        'key' => 'Menu Key',
+        'key-info' => 'Unique identifier for the menu item (e.g., catalog.products)',
+        'route' => 'Route Name',
+        'route-info' => 'Laravel route name (e.g., admin.catalog.products.index)',
+        'icon' => 'Icon Class',
+        'icon-info' => 'Icon class name (e.g., icon-product, icon-catalog)',
+        'parent' => 'Parent Menu',
+        'no-parent' => 'No Parent (Top Level)',
+        'sort-order' => 'Sort Order',
+        'status' => 'Status',
+        'active' => 'Active',
+        'inactive' => 'Inactive',
+        'save' => 'Save Menu Item',
+        'cancel' => 'Cancel',
+        'created-success' => 'Menu item created successfully',
+        'updated-success' => 'Menu item updated successfully',
+        'deleted-success' => 'Menu item deleted successfully',
+        'delete-warning' => 'Are you sure you want to delete this menu item?',
+        'cannot-delete' => 'Cannot delete menu item with children',
+        'delete-children-first' => 'Please delete child menu items first',
+    ],
+
+    'permission' => [
+        'title' => 'Menu Permissions',
+        'select-role' => 'Select Role',
+        'choose-role' => '-- Choose Role --',
+        'menu-items' => 'Menu Items',
+        'save' => 'Save Permissions',
+        'updated-success' => 'Menu permissions updated successfully',
+        'denied' => 'You do not have permission to access this page',
+    ],
+];

+ 35 - 0
packages/Longyi/DynamicMenu/src/Resources/views/admin/menu/create.blade.php

@@ -0,0 +1,35 @@
+@extends('admin::layouts.master')
+
+@section('page_title')
+    添加菜单项
+@stop
+
+@section('content-wrapper')
+    <div class="content full-page">
+        <div class="page-header">
+            <div class="page-title">
+                <h1>添加菜单项</h1>
+            </div>
+        </div>
+        <div class="page-content">
+            <form method="POST" action="{{ route('admin.dynamicmenu.store') }}">
+                @csrf
+                <div class="form-group">
+                    <label>名称</label>
+                    <input type="text" name="name" class="control" required>
+                </div>
+                <div class="form-group">
+                    <label>Key</label>
+                    <input type="text" name="key" class="control" required>
+                </div>
+                <div class="form-group">
+                    <label>路由</label>
+                    <input type="text" name="route" class="control" required>
+                </div>
+                <div class="form-group">
+                    <button type="submit" class="btn btn-lg btn-primary">保存</button>
+                </div>
+            </form>
+        </div>
+    </div>
+@stop

+ 23 - 0
packages/Longyi/DynamicMenu/src/Resources/views/admin/menu/index.blade.php

@@ -0,0 +1,23 @@
+{{-- 不继承任何布局,直接输出 --}}
+<!DOCTYPE html>
+<html>
+<head>
+    <title>动态菜单管理</title>
+    <link rel="stylesheet" href="{{ asset('vendor/webkul/admin/assets/css/admin.css') }}">
+</head>
+<body>
+    <div class="content" style="padding: 20px;">
+        <div class="page-header">
+            <h1>动态菜单管理</h1>
+            <a href="{{ route('admin.dynamicmenu.create') }}" class="btn btn-primary">添加菜单项</a>
+        </div>
+        <div class="page-content">
+            <div class="accordion">
+                @foreach($menuItems as $item)
+                    @include('dynamicmenu::admin.menu.partials.tree', ['item' => $item])
+                @endforeach
+            </div>
+        </div>
+    </div>
+</body>
+</html>

+ 36 - 0
packages/Longyi/DynamicMenu/src/Resources/views/admin/menu/partials/tree.blade.php

@@ -0,0 +1,36 @@
+<div class="accordion-item">
+    <div class="accordion-header" onclick="toggleAccordion(this)">
+        <div class="accordion-title">
+            <i class="{{ $item->icon ?? 'icon-file' }}"></i>
+            <span>{{ $item->name }}</span>
+            <span class="text-muted">({{ $item->key }})</span>
+        </div>
+        <div class="accordion-actions" onclick="event.stopPropagation()">
+            <a href="{{ route('admin.dynamicmenu.edit', $item->id) }}" class="btn-edit">
+                <i class="icon pencil"></i>
+            </a>
+            @if(!$item->children || $item->children->count() == 0)
+                <form action="{{ route('admin.dynamicmenu.delete', $item->id) }}" method="POST" style="display: inline;">
+                    @csrf
+                    @method('DELETE')
+                    <button type="submit" class="btn-delete" onclick="return confirm('确定删除?')">
+                        <i class="icon trash"></i>
+                    </button>
+                </form>
+            @else
+                <span class="btn-delete disabled">
+                    <i class="icon trash"></i>
+                </span>
+            @endif
+        </div>
+    </div>
+    @if($item->children && $item->children->count() > 0)
+        <div class="accordion-content">
+            <div class="accordion">
+                @foreach($item->children as $child)
+                    @include('dynamicmenu::admin.menu.partials.tree', ['item' => $child])
+                @endforeach
+            </div>
+        </div>
+    @endif
+</div>

+ 22 - 0
packages/Longyi/DynamicMenu/src/Resources/views/admin/permission/index.blade.php

@@ -0,0 +1,22 @@
+@extends('admin::layouts.master')
+
+@section('page_title')
+    菜单权限管理
+@stop
+
+@section('content-wrapper')
+    <div class="content full-page">
+        <div class="page-header">
+            <div class="page-title">
+                <h1>菜单权限管理</h1>
+            </div>
+        </div>
+        <div class="page-content">
+            <div class="panel">
+                <div class="panel-body">
+                    <p>权限管理页面</p>
+                </div>
+            </div>
+        </div>
+    </div>
+@stop

+ 60 - 0
packages/Longyi/DynamicMenu/src/Routes/admin-routes.php

@@ -0,0 +1,60 @@
+<?php
+
+use Illuminate\Support\Facades\Route;
+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'])
+        ->name('admin.dynamicmenu.index')
+        ->defaults('_config', [
+            'view' => 'dynamicmenu::admin.menu.index'
+        ]);
+    
+    Route::get('dynamicmenu-items/create', [MenuController::class, 'create'])
+        ->name('admin.dynamicmenu.create')
+        ->defaults('_config', [
+            'view' => 'dynamicmenu::admin.menu.create'
+        ]);
+    
+    Route::post('dynamicmenu-items', [MenuController::class, 'store'])
+        ->name('admin.dynamicmenu.store')
+        ->defaults('_config', [
+            'redirect' => 'admin.dynamicmenu.index'
+        ]);
+    
+    Route::get('dynamicmenu-items/{id}/edit', [MenuController::class, 'edit'])
+        ->name('admin.dynamicmenu.edit')
+        ->defaults('_config', [
+            'view' => 'dynamicmenu::admin.menu.edit'
+        ]);
+    
+    Route::put('dynamicmenu-items/{id}', [MenuController::class, 'update'])
+        ->name('admin.dynamicmenu.update')
+        ->defaults('_config', [
+            'redirect' => 'admin.dynamicmenu.index'
+        ]);
+    
+    Route::delete('dynamicmenu-items/{id}', [MenuController::class, 'destroy'])
+        ->name('admin.dynamicmenu.delete');
+    
+    // 权限管理路由
+    Route::get('dynamicmenu-permissions', [MenuController::class, 'permission'])
+        ->name('admin.dynamicmenu.permission')
+        ->defaults('_config', [
+            'view' => 'dynamicmenu::admin.permission.index'
+        ]);
+    
+    Route::post('dynamicmenu-permissions', [MenuController::class, 'updatePermission'])
+        ->name('admin.dynamicmenu.permission.update');
+    
+    Route::post('dynamicmenu-permissions/get', [MenuController::class, 'getRolePermissions'])
+        ->name('admin.dynamicmenu.permission.get');
+});

+ 3 - 0
routes/web.php

@@ -1 +1,4 @@
 <?php
+if (file_exists(base_path('packages/Longyi/DynamicMenu/src/Routes/admin-routes.php'))) {
+    require base_path('packages/Longyi/DynamicMenu/src/Routes/admin-routes.php');
+}

+ 0 - 2
storage/framework/cache/data/.gitignore

@@ -1,2 +0,0 @@
-*
-!.gitignore