diff --git a/.clinerules/admin-rule.md b/.clinerules/admin-rule.md index fdc5969..7769030 100644 --- a/.clinerules/admin-rule.md +++ b/.clinerules/admin-rule.md @@ -243,56 +243,113 @@ export default [ **后端菜单数据格式**: +菜单权限节点需要遵循以下规范: + +1. **顶级菜单**(parent_id=0)不需要 component 值 + - 顶级菜单作为分组容器,不需要指定组件路径 + - 示例:系统管理菜单 + +2. **只有最后一级的菜单**才需要 component 值 + - 如果菜单没有子菜单,则需要指定 component + - 如果菜单有子菜单,则不需要指定 component + +3. **非菜单类型**(如 button)不需要 component 值 + - 按钮权限只用于权限控制,不涉及页面路由 + +4. **所有页面组件的菜单**在前端处理时都拉平挂载在 Layouts 框架组件下 + - 前端会将菜单数据转换为路由,所有页面路由都会挂载到 Layout 布局下 + - 不需要在前端维护嵌套的路由结构 + ```javascript +// 示例:顶级菜单(无 component) { - path: '/system', - name: 'System', - title: '系统管理', - icon: 'Setting', - component: 'views/system', // 组件路径,相对于 pages 目录 - redirect: '/system/user', - children: [ - { - path: 'user', - name: 'SystemUser', - title: '用户管理', - icon: 'User', - component: 'views/system/user' - } - ] + name: '系统管理', + code: 'system', + type: 'menu', + parent_id: 0, + route: '/system', + component: null, // 顶级菜单不需要 component + meta: { + icon: 'Setting', + hidden: false + }, + sort: 1 +} + +// 示例:最后一级菜单(有 component) +{ + name: '用户管理', + code: 'system.users', + type: 'menu', + parent_id: 0, + route: '/system/users', + component: 'system/users/index', // 最后一级菜单需要 component + meta: { + icon: 'User', + hidden: false + }, + sort: 1 +} + +// 示例:按钮权限(无 component) +{ + name: '查看用户', + code: 'system.users.view', + type: 'button', + parent_id: 0, + route: 'admin.users.index', + component: null, // 非菜单类型不需要 component + meta: null, + sort: 1 } ``` **路由转换逻辑**: +前端会将后端返回的菜单数据转换为 Vue Router 路由格式,所有页面路由都会拉平挂载在 Layout 布局组件下: + ```javascript // 将后端菜单转换为路由格式 function transformMenusToRoutes(menus) { return menus.map(menu => { const route = { - path: menu.path, - name: menu.name, + path: menu.route, + name: menu.name || menu.code, meta: { - title: menu.title, - icon: menu.icon, - hidden: menu.hidden, - keepAlive: menu.keepAlive || false + title: menu.name, + icon: menu.meta?.icon, + hidden: menu.meta?.hidden, + keepAlive: menu.meta?.keepAlive || false } } - if (menu.component) { + // 只有菜单类型且有 component 值的才加载组件 + if (menu.type === 'menu' && menu.component) { route.component = loadComponent(menu.component) } - if (menu.children) { - route.children = transformMenusToRoutes(menu.children) - } + // 不处理 children,所有菜单都会拉平到 Layout 下 + // if (menu.children) { + // route.children = transformMenusToRoutes(menu.children) + // } return route }) } + +// 加载组件函数 +function loadComponent(componentPath) { + return () => import(`@/pages/${componentPath}.vue`) +} ``` +**菜单拉平处理说明**: + +由于前端将所有页面菜单拉平挂载在 Layout 下,因此: +- 后端菜单的 parent_id 主要用于构建菜单树的层级关系(侧边栏显示) +- 路由层面不需要维护嵌套结构,所有页面路由都在同一层级 +- component 值只需要在最后一级菜单(叶子节点)中设置 + #### 路由守卫 系统通过路由守卫实现权限控制和动态路由加载: diff --git a/.clinerules/rule.md b/.clinerules/rule.md index 41df354..dcb8747 100644 --- a/.clinerules/rule.md +++ b/.clinerules/rule.md @@ -669,6 +669,96 @@ return new class extends Migration - 文件路径: `Modules/{ModuleName}/Database/seeders/` +#### 菜单权限节点 Seed 规范 + +在 Seeder 文件中创建菜单权限节点时,需要遵循以下规范: + +**菜单结构规范:** + +1. **顶级菜单**(parent_id=0)不需要 component 值 + - 顶级菜单作为分组容器,不需要指定组件路径 + - 示例:系统管理菜单 + +2. **只有最后一级的菜单**才需要 component 值 + - 如果菜单没有子菜单,则需要指定 component + - 如果菜单有子菜单,则不需要指定 component + +3. **非菜单类型**(如 button)不需要 component 值 + - 按钮权限只用于权限控制,不涉及页面路由 + +4. **所有页面组件的菜单**在前端处理时都拉平挂载在 Layouts 框架组件下 + - 前端会将菜单数据转换为路由,所有页面路由都会挂载到 Layout 布局下 + - 不需要在前端维护嵌套的路由结构 + +**Seeder 示例:** + +```php + '系统管理', + 'code' => 'system', + 'type' => 'menu', + 'parent_id' => 0, + 'route' => '/system', + 'component' => null, // 顶级菜单不需要 component + 'meta' => json_encode([ + 'icon' => 'Setting', + 'hidden' => false, + 'hiddenBreadcrumb' => false, + ]), + 'sort' => 1, + 'status' => 1, + ], + + // 最后一级菜单(有 component) + [ + 'name' => '用户管理', + 'code' => 'system.users', + 'type' => 'menu', + 'parent_id' => 0, + 'route' => '/system/users', + 'component' => 'system/users/index', // 最后一级菜单需要 component + 'meta' => json_encode([ + 'icon' => 'User', + 'hidden' => false, + 'hiddenBreadcrumb' => false, + ]), + 'sort' => 1, + 'status' => 1, + ], + + // 按钮权限(无 component) + [ + 'name' => '查看用户', + 'code' => 'system.users.view', + 'type' => 'button', + 'parent_id' => 0, + 'route' => 'admin.users.index', + 'component' => null, // 非菜单类型不需要 component + 'meta' => null, + 'sort' => 1, + 'status' => 1, + ], + ]; + + foreach ($permissions as $permission) { + Permission::create($permission); + } +} +``` + +**重要说明:** + +- `parent_id` 主要用于构建菜单树的层级关系(侧边栏显示) +- 路由层面不需要维护嵌套结构,所有页面路由都在同一层级 +- `component` 值只需要在最后一级菜单(叶子节点)中设置 +- 前端会根据 `type` 字段判断是否需要加载组件 + ## 认证与授权 ### JWT 认证