feat:"完成页面接口的对接"
This commit is contained in:
139
管理后台/登录逻辑说明.md
Normal file
139
管理后台/登录逻辑说明.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# 登录接口逻辑说明
|
||||
|
||||
## 问题描述
|
||||
原先的实现中,登录失败时后端会抛出 `UnauthorizedException`(HTTP 401),前端响应拦截器收到 401 后会自动清空 token 并跳转到登录页,这在登录页面本身会造成逻辑混乱。
|
||||
|
||||
## 解决方案
|
||||
|
||||
### 1. 后端修改 (`后端/src/auth/auth.controller.ts`)
|
||||
|
||||
**修改前**:
|
||||
```typescript
|
||||
async login(@Body() loginDto: LoginDto): Promise<any> {
|
||||
const result = await this.authService.login(loginDto);
|
||||
return { code: 0, message: 'success', data: result };
|
||||
}
|
||||
```
|
||||
- 如果 auth.service 抛出 `UnauthorizedException`,会直接返回 HTTP 401 状态码
|
||||
|
||||
**修改后**:
|
||||
```typescript
|
||||
async login(@Body() loginDto: LoginDto): Promise<any> {
|
||||
try {
|
||||
const result = await this.authService.login(loginDto);
|
||||
return {
|
||||
code: 0,
|
||||
message: '登录成功',
|
||||
data: result,
|
||||
};
|
||||
} catch (error) {
|
||||
// 登录失败返回统一格式,不抛出 HTTP 异常
|
||||
return {
|
||||
code: 401,
|
||||
message: error.message || '用户名或密码错误',
|
||||
data: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**关键变化**:
|
||||
- ✅ HTTP 状态码始终为 200
|
||||
- ✅ 通过响应体的 `code` 字段区分成功(0)和失败(401)
|
||||
- ✅ 错误信息通过 `message` 字段返回
|
||||
|
||||
### 2. 前端修改 (`管理后台/src/utils/request.ts`)
|
||||
|
||||
**修改前**:
|
||||
```typescript
|
||||
if (res.code !== 0 && res.code !== 200) {
|
||||
ElMessage.error(res.message || '请求失败')
|
||||
|
||||
if (res.code === 401) {
|
||||
const userStore = useUserStore()
|
||||
userStore.logout()
|
||||
router.push('/login')
|
||||
}
|
||||
|
||||
return Promise.reject(new Error(res.message || '请求失败'))
|
||||
}
|
||||
```
|
||||
- 所有 401 都会跳转登录页
|
||||
|
||||
**修改后**:
|
||||
```typescript
|
||||
if (res.code !== 0 && res.code !== 200) {
|
||||
const isLoginRequest = response.config.url?.includes('/auth/login')
|
||||
|
||||
if (res.code === 401 && !isLoginRequest) {
|
||||
ElMessage.error('登录已过期,请重新登录')
|
||||
const userStore = useUserStore()
|
||||
userStore.logout()
|
||||
router.push('/login')
|
||||
} else if (!isLoginRequest) {
|
||||
ElMessage.error(res.message || '请求失败')
|
||||
}
|
||||
|
||||
return Promise.reject(new Error(res.message || '请求失败'))
|
||||
}
|
||||
```
|
||||
|
||||
**关键变化**:
|
||||
- ✅ 区分登录请求和其他请求
|
||||
- ✅ 登录接口的 401 不会触发自动跳转
|
||||
- ✅ 登录接口的错误消息不在拦截器中显示(由登录页面处理)
|
||||
|
||||
### 3. 前端登录页面 (`管理后台/src/views/login/index.vue`)
|
||||
|
||||
保持原有的错误处理逻辑:
|
||||
```typescript
|
||||
try {
|
||||
await userStore.login(loginForm.username, loginForm.password)
|
||||
ElMessage.success('登录成功')
|
||||
router.push('/')
|
||||
} catch (error: any) {
|
||||
const errorMessage = error.message || '登录失败,请检查用户名和密码'
|
||||
ElMessage.error(errorMessage)
|
||||
}
|
||||
```
|
||||
|
||||
**关键点**:
|
||||
- ✅ 登录页面自己处理并显示错误信息
|
||||
- ✅ 错误消息来自后端返回的 `message` 字段
|
||||
|
||||
## 最终效果
|
||||
|
||||
### 登录成功
|
||||
```
|
||||
请求: POST /api/auth/login
|
||||
响应: { code: 0, message: '登录成功', data: { user: {...}, access_token: '...', refresh_token: '...' } }
|
||||
结果: 显示"登录成功",跳转到首页
|
||||
```
|
||||
|
||||
### 登录失败(用户名或密码错误)
|
||||
```
|
||||
请求: POST /api/auth/login
|
||||
响应: { code: 401, message: '用户名或密码错误', data: null }
|
||||
结果: 显示"用户名或密码错误",停留在登录页
|
||||
```
|
||||
|
||||
### 登录失败(账户被禁用)
|
||||
```
|
||||
请求: POST /api/auth/login
|
||||
响应: { code: 401, message: '账户已被禁用,请联系管理员', data: null }
|
||||
结果: 显示"账户已被禁用,请联系管理员",停留在登录页
|
||||
```
|
||||
|
||||
### 其他接口 Token 过期
|
||||
```
|
||||
请求: GET /api/case/list
|
||||
响应: { code: 401, message: 'Unauthorized', data: null }
|
||||
结果: 显示"登录已过期,请重新登录",清空 token,跳转到登录页
|
||||
```
|
||||
|
||||
## 总结
|
||||
|
||||
这个修改确保了:
|
||||
1. **登录接口**:失败时返回友好的错误信息,不会触发自动跳转
|
||||
2. **其他接口**:Token 过期时自动清理状态并跳转到登录页
|
||||
3. **用户体验**:错误提示清晰准确,不会出现重复提示或循环跳转
|
||||
Reference in New Issue
Block a user