Files
rxthinkcmf-wiki/RXThinkCMF_ThinkPhp8_EleVue/zh/modules/index.md
T
2026-09-29 10:14:04 +08:00

211 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 6.15 首页与个人中心
:::tip 概述
首页与个人中心模块负责当前登录用户的信息获取、菜单加载、密码修改和个人信息更新。该模块不继承 BaseLogic,而是专用业务逻辑。
:::
## 模块特点
| 特性 | 说明 |
|------|------|
| 基类 | 无(直接操作 Model) |
| 用途 | 当前登录用户的自身操作 |
| 权限 | 无需配置 #[Permission],已登录即可访问 |
| 超级管理员 | uid=1 拥有全部权限 `*:*:*` |
## API 接口
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/index/getUser` | 获取当前登录用户信息(含角色和权限) |
| GET | `/api/index/getMenus` | 获取当前用户菜单(树形) |
| PUT | `/api/index/updatePassword` | 修改密码 |
| PUT | `/api/index/updateProfile` | 更新个人信息 |
## 核心代码
### 获取用户信息
```php
// app/logic/IndexLogic.php
public function getUser(int $userId): array
{
$user = User::with(['dept'])->find($userId);
if (!$user) {
throw new \Exception('用户不存在');
}
$data = $user->toArray();
// 头像补全域名前缀
if (!empty($data['avatar'])) {
$data['avatar'] = get_file_url($data['avatar']);
}
// 组装城市编码数组(province_code, city_code, district_code, street_code)
$data['city'] = [
$data['province_code'] ?? '',
$data['city_code'] ?? '',
$data['district_code'] ?? '',
$data['street_code'] ?? '',
];
// 获取角色列表(完整角色对象)
$roleIds = UserRole::where('user_id', $userId)->column('role_id');
if (!empty($roleIds)) {
$data['roles'] = Role::whereIn('id', $roleIds)->select()->toArray();
} else {
$data['roles'] = [];
}
// 获取权限列表
if ($userId === 1) {
$data['permissions'] = ['*:*:*'];
} else {
if (!empty($roleIds)) {
$data['permissions'] = RoleMenu::alias('rm')
->join('think_menu m', 'rm.menu_id = m.id')
->whereIn('rm.role_id', $roleIds)
->where('m.is_delete', 0)
->where('m.type', 1)
->column('m.permission');
} else {
$data['permissions'] = [];
}
}
// authorities 字段(预留)
$data['authorities'] = null;
// 隐藏敏感字段
unset($data['password'], $data['salt']);
return $data;
}
```
:::info 返回结构说明
- `roles`:完整角色对象数组(含 id、name、code 等),非仅 ID
- `permissions`:权限码数组,超级管理员为 `['*:*:*']`
- `city`:4 层城市编码数组 `[省, 市, 区, 街道]`
- `avatar`:自动补全域名前缀
- 字段名由 `Result::success()` 自动转为 camelCase 返回前端
:::
### 获取菜单
```php
public function getMenus(int $userId): array
{
// 超级管理员:全部菜单
// type=0 仅返回可导航菜单(目录+页面),type=1 为权限按钮不返回
if ($userId === 1) {
$menus = Menu::where('status', 0)
->where('hide', 0)
->where('type', 0)
->where('is_delete', 0)
->order('sort', 'asc')
->select()->toArray();
return $this->buildTree($menus);
}
// 普通用户:通过角色关联获取菜单
$roleIds = UserRole::where('user_id', $userId)->column('role_id');
if (empty($roleIds)) {
return [];
}
$menuIds = RoleMenu::whereIn('role_id', $roleIds)->column('menu_id');
$menuIds = array_unique($menuIds);
if (empty($menuIds)) {
return [];
}
$menus = Menu::whereIn('id', $menuIds)
->where('status', 0)
->where('hide', 0)
->where('type', 0)
->where('is_delete', 0)
->order('sort', 'asc')
->select()->toArray();
return $this->buildTree($menus);
}
```
:::info 菜单查询条件
获取菜单(非节点)数据时保证四个条件:`type=0`(可导航菜单)、`status=0`(启用)、`hide=0`(未隐藏)、`is_delete=0`(未删除)。
:::
:::info 菜单类型说明
代码中 `where('type', 0)` 用于筛选可导航的菜单项(目录和页面)。在当前实现中,`type=0` 表示可导航的菜单项(包括目录和页面),`type=1` 表示仅用于权限控制的按钮节点,不会出现在菜单树中。
:::
### 修改密码
```php
public function updatePassword(int $userId, string $oldPassword, string $newPassword): bool
{
$user = User::find($userId);
// 验证旧密码
if (!PasswordService::verify($oldPassword, $user->password, $user->salt)) {
throw new \Exception('旧密码错误');
}
// 加密新密码
$encrypted = PasswordService::encrypt($newPassword);
$user->password = $encrypted['password'];
$user->salt = $encrypted['salt'];
return $user->save() !== false;
}
```
### 更新个人信息
```php
public function updateProfile(int $userId, array $data): bool
{
$user = User::find($userId);
// 处理城市字段
if (isset($data['city']) && is_array($data['city'])) {
$data['province_code'] = $data['city'][0] ?? '';
$data['city_code'] = $data['city'][1] ?? '';
$data['district_code'] = $data['city'][2] ?? '';
$data['street_code'] = $data['city'][3] ?? '';
$data['city_info'] = City::resolveCityNames($data['city']);
unset($data['city']);
}
// 排除敏感字段
unset($data['password'], $data['salt'], $data['role_id'], $data['tenant_id'], $data['id']);
return $user->save($data) !== false;
}
```
:::warning 安全设计
updateProfile 会排除 `password`、`salt`、`role_id`、`tenant_id` 等敏感字段,防止用户越权修改自身权限。
:::
## 前端调用
```typescript
// 获取当前用户信息
const userInfo = await getUserInfo();
// userInfo.permissions = ['sys:user:page', 'sys:article:add', ...]
// userInfo.roles = [1]
// 获取菜单(用于动态路由生成)
const menus = await getMenuList();
// menus = [{id:1, name:'系统管理', path:'/system', children:[...]}]
// 修改密码
await updatePassword({ oldPassword: '123456', newPassword: '654321' });
// 更新个人信息
await updateProfile({ realname: '张三', mobile: '13800138000' });
```