Merge branch 'main' of http://git.tensent.cn/molong/laravel_swoole
This commit is contained in:
+82
-25
@@ -243,56 +243,113 @@ #### 动态路由加载
|
|||||||
|
|
||||||
**后端菜单数据格式**:
|
**后端菜单数据格式**:
|
||||||
|
|
||||||
|
菜单权限节点需要遵循以下规范:
|
||||||
|
|
||||||
|
1. **顶级菜单**(parent_id=0)不需要 component 值
|
||||||
|
- 顶级菜单作为分组容器,不需要指定组件路径
|
||||||
|
- 示例:系统管理菜单
|
||||||
|
|
||||||
|
2. **只有最后一级的菜单**才需要 component 值
|
||||||
|
- 如果菜单没有子菜单,则需要指定 component
|
||||||
|
- 如果菜单有子菜单,则不需要指定 component
|
||||||
|
|
||||||
|
3. **非菜单类型**(如 button)不需要 component 值
|
||||||
|
- 按钮权限只用于权限控制,不涉及页面路由
|
||||||
|
|
||||||
|
4. **所有页面组件的菜单**在前端处理时都拉平挂载在 Layouts 框架组件下
|
||||||
|
- 前端会将菜单数据转换为路由,所有页面路由都会挂载到 Layout 布局下
|
||||||
|
- 不需要在前端维护嵌套的路由结构
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
// 示例:顶级菜单(无 component)
|
||||||
{
|
{
|
||||||
path: '/system',
|
name: '系统管理',
|
||||||
name: 'System',
|
code: 'system',
|
||||||
title: '系统管理',
|
type: 'menu',
|
||||||
icon: 'Setting',
|
parent_id: 0,
|
||||||
component: 'views/system', // 组件路径,相对于 pages 目录
|
route: '/system',
|
||||||
redirect: '/system/user',
|
component: null, // 顶级菜单不需要 component
|
||||||
children: [
|
meta: {
|
||||||
{
|
icon: 'Setting',
|
||||||
path: 'user',
|
hidden: false
|
||||||
name: 'SystemUser',
|
},
|
||||||
title: '用户管理',
|
sort: 1
|
||||||
icon: 'User',
|
}
|
||||||
component: 'views/system/user'
|
|
||||||
}
|
// 示例:最后一级菜单(有 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
|
```javascript
|
||||||
// 将后端菜单转换为路由格式
|
// 将后端菜单转换为路由格式
|
||||||
function transformMenusToRoutes(menus) {
|
function transformMenusToRoutes(menus) {
|
||||||
return menus.map(menu => {
|
return menus.map(menu => {
|
||||||
const route = {
|
const route = {
|
||||||
path: menu.path,
|
path: menu.route,
|
||||||
name: menu.name,
|
name: menu.name || menu.code,
|
||||||
meta: {
|
meta: {
|
||||||
title: menu.title,
|
title: menu.name,
|
||||||
icon: menu.icon,
|
icon: menu.meta?.icon,
|
||||||
hidden: menu.hidden,
|
hidden: menu.meta?.hidden,
|
||||||
keepAlive: menu.keepAlive || false
|
keepAlive: menu.meta?.keepAlive || false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (menu.component) {
|
// 只有菜单类型且有 component 值的才加载组件
|
||||||
|
if (menu.type === 'menu' && menu.component) {
|
||||||
route.component = loadComponent(menu.component)
|
route.component = loadComponent(menu.component)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (menu.children) {
|
// 不处理 children,所有菜单都会拉平到 Layout 下
|
||||||
route.children = transformMenusToRoutes(menu.children)
|
// if (menu.children) {
|
||||||
}
|
// route.children = transformMenusToRoutes(menu.children)
|
||||||
|
// }
|
||||||
|
|
||||||
return route
|
return route
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 加载组件函数
|
||||||
|
function loadComponent(componentPath) {
|
||||||
|
return () => import(`@/pages/${componentPath}.vue`)
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**菜单拉平处理说明**:
|
||||||
|
|
||||||
|
由于前端将所有页面菜单拉平挂载在 Layout 下,因此:
|
||||||
|
- 后端菜单的 parent_id 主要用于构建菜单树的层级关系(侧边栏显示)
|
||||||
|
- 路由层面不需要维护嵌套结构,所有页面路由都在同一层级
|
||||||
|
- component 值只需要在最后一级菜单(叶子节点)中设置
|
||||||
|
|
||||||
#### 路由守卫
|
#### 路由守卫
|
||||||
|
|
||||||
系统通过路由守卫实现权限控制和动态路由加载:
|
系统通过路由守卫实现权限控制和动态路由加载:
|
||||||
|
|||||||
@@ -669,6 +669,96 @@ #### 业务模块数据填充
|
|||||||
|
|
||||||
- 文件路径: `Modules/{ModuleName}/Database/seeders/`
|
- 文件路径: `Modules/{ModuleName}/Database/seeders/`
|
||||||
|
|
||||||
|
#### 菜单权限节点 Seed 规范
|
||||||
|
|
||||||
|
在 Seeder 文件中创建菜单权限节点时,需要遵循以下规范:
|
||||||
|
|
||||||
|
**菜单结构规范:**
|
||||||
|
|
||||||
|
1. **顶级菜单**(parent_id=0)不需要 component 值
|
||||||
|
- 顶级菜单作为分组容器,不需要指定组件路径
|
||||||
|
- 示例:系统管理菜单
|
||||||
|
|
||||||
|
2. **只有最后一级的菜单**才需要 component 值
|
||||||
|
- 如果菜单没有子菜单,则需要指定 component
|
||||||
|
- 如果菜单有子菜单,则不需要指定 component
|
||||||
|
|
||||||
|
3. **非菜单类型**(如 button)不需要 component 值
|
||||||
|
- 按钮权限只用于权限控制,不涉及页面路由
|
||||||
|
|
||||||
|
4. **所有页面组件的菜单**在前端处理时都拉平挂载在 Layouts 框架组件下
|
||||||
|
- 前端会将菜单数据转换为路由,所有页面路由都会挂载到 Layout 布局下
|
||||||
|
- 不需要在前端维护嵌套的路由结构
|
||||||
|
|
||||||
|
**Seeder 示例:**
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
private function createPermissions(): void
|
||||||
|
{
|
||||||
|
$permissions = [
|
||||||
|
// 顶级菜单(无 component)
|
||||||
|
[
|
||||||
|
'name' => '系统管理',
|
||||||
|
'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 认证
|
### JWT 认证
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class AuthCheckMiddleware
|
|||||||
public function handle(Request $request, Closure $next, ?string $guard = 'api', ?string $permission = null): Response
|
public function handle(Request $request, Closure $next, ?string $guard = 'api', ?string $permission = null): Response
|
||||||
{
|
{
|
||||||
// 检查是否已认证
|
// 检查是否已认证
|
||||||
if (!Auth::guard($guard)->check()) {
|
if (!auth($guard)->check()) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'code' => 401,
|
'code' => 401,
|
||||||
'message' => '未登录或token已过期',
|
'message' => '未登录或token已过期',
|
||||||
@@ -30,7 +30,7 @@ public function handle(Request $request, Closure $next, ?string $guard = 'api',
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前用户
|
// 获取当前用户
|
||||||
$user = Auth::guard($guard)->user();
|
$user = auth($guard)->user();
|
||||||
|
|
||||||
// 检查用户状态
|
// 检查用户状态
|
||||||
if (isset($user->status) && $user->status !== 1) {
|
if (isset($user->status) && $user->status !== 1) {
|
||||||
|
|||||||
@@ -43,10 +43,10 @@ public function login(array $credentials): array
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
// 生成token
|
// 生成token
|
||||||
$token = Auth::guard('admin')->login($user);
|
$token = auth('admin')->login($user);
|
||||||
|
|
||||||
// 生成refresh token
|
// 生成refresh token
|
||||||
$refreshToken = Auth::guard('admin')->refresh();
|
$refreshToken = auth('admin')->refresh();
|
||||||
|
|
||||||
// 获取用户菜单
|
// 获取用户菜单
|
||||||
$menu = $this->getUserMenu($user);
|
$menu = $this->getUserMenu($user);
|
||||||
@@ -68,7 +68,7 @@ public function login(array $credentials): array
|
|||||||
*/
|
*/
|
||||||
public function logout(): void
|
public function logout(): void
|
||||||
{
|
{
|
||||||
Auth::guard('admin')->logout();
|
auth('admin')->logout();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -76,11 +76,11 @@ public function logout(): void
|
|||||||
*/
|
*/
|
||||||
public function refresh(): array
|
public function refresh(): array
|
||||||
{
|
{
|
||||||
$newToken = Auth::guard('admin')->refresh();
|
$newToken = auth('admin')->refresh();
|
||||||
$user = Auth::guard('admin')->user();
|
$user = auth('admin')->user();
|
||||||
|
|
||||||
// 生成新的refresh token
|
// 生成新的refresh token
|
||||||
$newRefreshToken = Auth::guard('admin')->refresh();
|
$newRefreshToken = auth('admin')->refresh();
|
||||||
|
|
||||||
// 获取用户菜单
|
// 获取用户菜单
|
||||||
$menu = $this->getUserMenu($user);
|
$menu = $this->getUserMenu($user);
|
||||||
@@ -102,7 +102,7 @@ public function refresh(): array
|
|||||||
*/
|
*/
|
||||||
public function me(): array
|
public function me(): array
|
||||||
{
|
{
|
||||||
$user = Auth::guard('admin')->user();
|
$user = auth('admin')->user();
|
||||||
return $this->getUserInfo($user);
|
return $this->getUserInfo($user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +132,7 @@ public function resetPassword(array $data): void
|
|||||||
*/
|
*/
|
||||||
public function changePassword(array $data): void
|
public function changePassword(array $data): void
|
||||||
{
|
{
|
||||||
$user = Auth::guard('admin')->user();
|
$user = auth('admin')->user();
|
||||||
|
|
||||||
if (!Hash::check($data['old_password'], $user->password)) {
|
if (!Hash::check($data['old_password'], $user->password)) {
|
||||||
throw ValidationException::withMessages([
|
throw ValidationException::withMessages([
|
||||||
|
|||||||
Reference in New Issue
Block a user