- 从服务器拉取完整代码 - 按框架规范整理项目结构 - 配置 Drone CI 测试环境部署 - 包含后端(FastAPI)、前端(Vue3)、管理端 技术栈: Vue3 + TypeScript + FastAPI + MySQL
269 lines
6.6 KiB
Markdown
269 lines
6.6 KiB
Markdown
# 用户管理页面分页和搜索功能修复
|
||
|
||
## 问题描述
|
||
用户反馈在 https://kpl.ireborn.com.cn/admin/user-management 页面:
|
||
1. ❌ 只显示了20个用户(总共有117个)
|
||
2. ❌ 翻页功能无效
|
||
3. ❌ 搜索员工名字找不到
|
||
|
||
## 问题原因
|
||
|
||
前端代码使用了**客户端过滤和分页**,但应该使用**服务端分页和搜索**。
|
||
|
||
### 原有逻辑(错误)
|
||
1. `loadUserList()` 只加载第1页的20条数据
|
||
2. 使用 `filteredUsers` 计算属性在这20条数据中进行客户端过滤
|
||
3. 翻页和搜索都只在本地已加载的20条数据中操作
|
||
4. 后端有117个用户,但前端只能看到和搜索前20个
|
||
|
||
### 正确逻辑(修复后)
|
||
1. 所有筛选和分页都向后端发送请求
|
||
2. 每次筛选/翻页/搜索都重新调用 `loadUserList()`
|
||
3. 直接显示后端返回的数据,不在前端过滤
|
||
|
||
## 修复内容
|
||
|
||
### 文件:`kaopeilian-frontend/src/views/admin/user-management.vue`
|
||
|
||
#### 1. 移除前端过滤逻辑
|
||
**修改前**:
|
||
```typescript
|
||
const filteredUsers = computed(() => {
|
||
return userList.value.filter(user => {
|
||
const matchUsername = !filterForm.username ||
|
||
user.username.toLowerCase().includes(filterForm.username.toLowerCase()) ||
|
||
(user.realName && user.realName.toLowerCase().includes(filterForm.username.toLowerCase()))
|
||
|
||
const matchStatus = !filterForm.status || user.status === filterForm.status
|
||
const matchRole = !filterForm.role || user.role === filterForm.role
|
||
const matchPosition = !filterForm.position || user.position === filterForm.position
|
||
|
||
return matchUsername && matchStatus && matchRole && matchPosition
|
||
})
|
||
})
|
||
```
|
||
|
||
**修改后**:
|
||
```typescript
|
||
// 显示的用户数据(直接使用后端返回的数据,不在前端过滤)
|
||
const filteredUsers = computed(() => {
|
||
return userList.value
|
||
})
|
||
```
|
||
|
||
#### 2. 实时搜索触发后端请求
|
||
**修改前**:
|
||
```typescript
|
||
const handleRealTimeSearch = () => {
|
||
// 筛选逻辑在计算属性中处理
|
||
}
|
||
```
|
||
|
||
**修改后**:
|
||
```typescript
|
||
const handleRealTimeSearch = () => {
|
||
// 重置到第一页并重新加载数据
|
||
currentPage.value = 1
|
||
loadUserList()
|
||
}
|
||
```
|
||
|
||
#### 3. 重置功能触发后端请求
|
||
**修改前**:
|
||
```typescript
|
||
const handleReset = () => {
|
||
filterForm.username = ''
|
||
filterForm.status = ''
|
||
filterForm.role = ''
|
||
filterForm.position = ''
|
||
ElMessage.success('已重置所有筛选条件')
|
||
}
|
||
```
|
||
|
||
**修改后**:
|
||
```typescript
|
||
const handleReset = () => {
|
||
filterForm.username = ''
|
||
filterForm.status = ''
|
||
filterForm.role = ''
|
||
filterForm.position = ''
|
||
currentPage.value = 1
|
||
loadUserList() // 重新加载数据
|
||
ElMessage.success('已重置所有筛选条件')
|
||
}
|
||
```
|
||
|
||
#### 4. 清除单个筛选条件时触发后端请求
|
||
为 `clearUsername()`, `clearStatus()`, `clearRole()`, `clearPosition()` 四个函数都添加:
|
||
```typescript
|
||
currentPage.value = 1
|
||
loadUserList()
|
||
```
|
||
|
||
#### 5. 修正API参数格式
|
||
**修改前**:
|
||
```typescript
|
||
const params = {
|
||
page: currentPage.value,
|
||
pageSize: pageSize.value, // 错误:应该是page_size
|
||
keyword: filterForm.username,
|
||
status: filterForm.status, // 错误:后端不支持status参数
|
||
role: filterForm.role
|
||
}
|
||
```
|
||
|
||
**修改后**:
|
||
```typescript
|
||
const params: any = {
|
||
page: currentPage.value,
|
||
page_size: pageSize.value // 正确:使用下划线
|
||
}
|
||
|
||
// 添加关键词搜索(用户名或姓名)
|
||
if (filterForm.username && filterForm.username.trim()) {
|
||
params.keyword = filterForm.username.trim()
|
||
}
|
||
|
||
// 添加角色筛选
|
||
if (filterForm.role) {
|
||
params.role = filterForm.role
|
||
}
|
||
|
||
// 添加状态筛选(后端使用is_active参数)
|
||
if (filterForm.status) {
|
||
if (filterForm.status === 'active') {
|
||
params.is_active = true
|
||
} else if (filterForm.status === 'disabled') {
|
||
params.is_active = false
|
||
}
|
||
// pending状态不传is_active参数
|
||
}
|
||
```
|
||
|
||
## 修复结果
|
||
|
||
### ✅ 功能验证
|
||
|
||
#### 1. 分页功能
|
||
- ✅ 可以正常翻页查看所有117个用户
|
||
- ✅ 每页显示20条记录
|
||
- ✅ 总共6页(20+20+20+20+20+17)
|
||
|
||
#### 2. 搜索功能
|
||
- ✅ 输入员工姓名可以搜索到对应员工
|
||
- ✅ 例如搜索"何平",能找到对应的员工记录
|
||
- ✅ 搜索会在所有117个用户中进行,不局限于当前页
|
||
|
||
#### 3. 筛选功能
|
||
- ✅ 角色筛选:可以筛选admin/manager/trainee
|
||
- ✅ 状态筛选:可以筛选active/disabled
|
||
- ✅ 组合筛选:可以同时使用多个筛选条件
|
||
|
||
#### 4. 清除筛选
|
||
- ✅ 可以单独清除某个筛选条件
|
||
- ✅ 可以一次清空所有筛选条件
|
||
- ✅ 清除后自动重新加载数据
|
||
|
||
## 后端API说明
|
||
|
||
### 用户列表API
|
||
**接口**: `GET /api/v1/users/`
|
||
|
||
**支持的参数**:
|
||
- `page`: 页码(从1开始)
|
||
- `page_size`: 每页数量
|
||
- `keyword`: 关键词搜索(匹配username、email、full_name)
|
||
- `role`: 角色筛选(admin/manager/trainee)
|
||
- `is_active`: 状态筛选(true/false)
|
||
|
||
**返回格式**:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "success",
|
||
"data": {
|
||
"items": [...],
|
||
"total": 117,
|
||
"page": 1,
|
||
"page_size": 20,
|
||
"pages": 6
|
||
}
|
||
}
|
||
```
|
||
|
||
## 测试用例
|
||
|
||
### 1. 分页测试
|
||
```bash
|
||
# 第1页
|
||
GET /api/v1/users/?page=1&page_size=20
|
||
# 应返回20条记录
|
||
|
||
# 第2页
|
||
GET /api/v1/users/?page=2&page_size=20
|
||
# 应返回20条记录(不同于第1页)
|
||
|
||
# 最后一页
|
||
GET /api/v1/users/?page=6&page_size=20
|
||
# 应返回17条记录
|
||
```
|
||
|
||
### 2. 搜索测试
|
||
```bash
|
||
# 搜索"何平"
|
||
GET /api/v1/users/?keyword=何平
|
||
# 应返回1条记录
|
||
|
||
# 搜索"美学规划师"
|
||
GET /api/v1/users/?keyword=美学规划师
|
||
# 应返回包含"美学规划师"姓名的记录
|
||
```
|
||
|
||
### 3. 筛选测试
|
||
```bash
|
||
# 筛选管理者
|
||
GET /api/v1/users/?role=manager
|
||
# 应返回5条记录
|
||
|
||
# 筛选学员
|
||
GET /api/v1/users/?role=trainee
|
||
# 应返回111条记录
|
||
|
||
# 组合筛选:管理者+激活状态
|
||
GET /api/v1/users/?role=manager&is_active=true
|
||
# 应返回激活的管理者
|
||
```
|
||
|
||
## 技术要点
|
||
|
||
### 前端开发注意事项
|
||
1. **服务端分页vs客户端分页**
|
||
- 数据量大时(>100条)必须使用服务端分页
|
||
- 客户端分页仅适用于小数据量(<100条)
|
||
|
||
2. **API参数命名**
|
||
- 注意后端使用的参数名(如 `page_size` 而不是 `pageSize`)
|
||
- 不同后端可能有不同的命名风格
|
||
|
||
3. **筛选条件变化时**
|
||
- 重置到第1页:`currentPage.value = 1`
|
||
- 重新加载数据:`loadUserList()`
|
||
|
||
4. **用户体验优化**
|
||
- 实时搜索:输入时立即触发
|
||
- 加载状态:显示loading
|
||
- 搜索防抖:避免频繁请求(可选)
|
||
|
||
## 相关文件
|
||
|
||
- 前端组件:`kaopeilian-frontend/src/views/admin/user-management.vue`
|
||
- 后端API:`kaopeilian-backend/app/api/v1/users.py`
|
||
- 用户服务:`kaopeilian-backend/app/services/user_service.py`
|
||
|
||
## 更新时间
|
||
2025-11-11 20:00
|
||
|
||
## 修复状态
|
||
✅ 已完成并验证
|
||
|