文字游戏
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

400 lines
7.6 KiB

2 weeks ago
# API 集成指南
## 概述
项目已完整配置 axios,包括请求拦截器、响应拦截器和 API 服务层。
## 文件结构
```
src/api/
├── index.ts # axios 实例配置(请求/响应拦截器)
└── auth.ts # 登录相关 API 接口定义
```
## 核心概念
### 1. axios 实例配置 (`src/api/index.ts`)
```typescript
// 创建 axios 实例
const instance = axios.create({
baseURL: 'http://localhost:3000/api', // 从环境变量读取
timeout: 10000
})
```
**请求拦截器**:自动在请求头添加 Authorization token
```typescript
instance.interceptors.request.use((config) => {
const token = localStorage.getItem('auth_token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})
```
**响应拦截器**:统一处理错误和 token 过期
```typescript
instance.interceptors.response.use(
(response) => response.data, // 成功时返回 data
(error) => {
if (error.response?.status === 401) {
// Token 过期,清除登录状态并跳转到登录页
}
}
)
```
### 2. API 服务层 (`src/api/auth.ts`)
定义所有 API 接口和类型:
```typescript
// 定义请求/响应类型
export interface LoginRequest {
username: string
password: string
}
export interface LoginResponse {
code: number
message: string
data: {
token: string
user: User
}
}
// 定义 API 接口
export const loginApi = (credentials: LoginRequest): Promise<LoginResponse> => {
return http.post('/auth/login', credentials)
}
```
### 3. 状态管理 (`src/stores/auth.ts`)
在 Pinia store 中调用 API:
```typescript
const login = async (username: string, password: string): Promise<boolean> => {
try {
const response = await loginApi({ username, password })
if (response.code === 200 && response.data) {
token.value = response.data.token
user.value = response.data.user
// 保存到本地存储
localStorage.setItem('auth_token', token.value)
localStorage.setItem('user', JSON.stringify(user.value))
return true
}
} catch (error) {
console.error('登录失败:', error)
return false
}
}
```
## 登录流程详解
### 前端请求流程
```
1. 用户在登录页输入用户名和密码
2. 点击"登录"按钮调用 handleLogin()
3. authStore.login(username, password) 被调用
4. loginApi({ username, password }) 发送 HTTP 请求
5. axios 拦截器处理(添加 headers 等)
6. 发送 POST /auth/login 请求到后端
```
### 后端需要返回的数据格式
```json
{
"code": 200,
"message": "登录成功",
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "1",
"username": "admin",
"email": "admin@example.com",
"role": "admin"
}
}
}
```
**字段说明:**
- `code`: HTTP 状态码(200 表示成功)
- `message`: 返回消息
- `data.token`: JWT token,用于后续请求认证
- `data.user`: 用户信息对象
- `id`: 用户 ID
- `username`: 用户名
- `email`: 邮箱
- `role`: 用户角色(admin 或 user)
## 环境配置
### `.env.development` (开发环境)
```
VITE_API_URL=http://localhost:3000/api
```
### `.env.production` (生产环境)
```
VITE_API_URL=https://api.example.com/api
```
### 在代码中获取环境变量
```typescript
const baseURL = import.meta.env.VITE_API_URL || 'http://localhost:3000/api'
```
## 使用示例
### 1. 调用登录 API
```typescript
import { loginApi } from '@/api/auth'
const response = await loginApi({
username: 'admin',
password: '123456'
})
if (response.code === 200) {
console.log('登录成功,用户信息:', response.data.user)
console.log('Token:', response.data.token)
}
```
### 2. 在组件中使用 store
```vue
<script setup lang="ts">
import { useAuthStore } from '@/stores/auth'
const authStore = useAuthStore()
const login = async () => {
const success = await authStore.login('admin', '123456')
if (success) {
// 登录成功,跳转
router.push('/admin/dashboard')
}
}
</script>
```
### 3. 创建新的 API 接口
`src/api/` 中创建新文件,如 `users.ts`
```typescript
import http from './index'
export interface User {
id: number
username: string
email: string
role: string
}
// 获取用户列表
export const getUsersApi = (page: number, limit: number) => {
return http.get('/users', {
params: { page, limit }
})
}
// 创建用户
export const createUserApi = (data: User) => {
return http.post('/users', data)
}
// 更新用户
export const updateUserApi = (id: number, data: Partial<User>) => {
return http.put(`/users/${id}`, data)
}
// 删除用户
export const deleteUserApi = (id: number) => {
return http.delete(`/users/${id}`)
}
```
## 错误处理
### 全局错误处理
axios 响应拦截器已处理以下情况:
| 状态码 | 处理方式 |
|-------|--------|
| 401 | Token 过期,清除登录状态,跳转到登录页 |
| 其他 4xx/5xx | 返回错误信息 |
| 网络错误 | 返回"网络错误"提示 |
### 在组件中处理错误
```typescript
try {
const response = await loginApi({ username, password })
// 处理成功
} catch (error: any) {
ElMessage.error(error?.message || '登录失败')
}
```
## 请求示例
### POST 请求 (登录)
```typescript
// 请求
POST /api/auth/login
Content-Type: application/json
Authorization: Bearer <token>
{
"username": "admin",
"password": "123456"
}
// 响应
{
"code": 200,
"message": "登录成功",
"data": {
"token": "...",
"user": { ... }
}
}
```
### GET 请求 (获取列表)
```typescript
// 请求
GET /api/users?page=1&limit=10
Authorization: Bearer <token>
// 响应
{
"code": 200,
"message": "成功",
"data": {
"items": [...],
"total": 100
}
}
```
### PUT 请求 (更新)
```typescript
// 请求
PUT /api/users/1
Content-Type: application/json
Authorization: Bearer <token>
{
"username": "new_name",
"email": "new@email.com"
}
// 响应
{
"code": 200,
"message": "更新成功",
"data": { ... }
}
```
## 常见问题
### Q: Token 过期怎么处理?
A: 后端返回 401 时,拦截器会自动清除 token 并跳转到登录页。
### Q: 如何添加其他请求头?
A: 修改 `src/api/index.ts` 中的拦截器:
```typescript
instance.interceptors.request.use((config) => {
config.headers['Custom-Header'] = 'value'
return config
})
```
### Q: 如何处理超时?
A: 已在 axios 配置中设置 `timeout: 10000`(10秒)。修改 `src/api/index.ts` 中的 `timeout` 值。
### Q: 如何调用 API 时显示加载状态?
A: 在组件中使用 `loading` ref:
```typescript
const loading = ref(false)
loading.value = true
try {
const response = await loginApi(...)
} finally {
loading.value = false
}
```
## 集成后端步骤
1. **启动后端服务**
```bash
# 后端服务应该运行在 http://localhost:3000
```
2. **更新 API 地址** (如果不同)
```
修改 .env.development 中的 VITE_API_URL
```
3. **确保后端返回正确格式**
```json
{
"code": 200,
"message": "...",
"data": { ... }
}
```
4. **测试登录流程**
- 访问 http://localhost:5173
- 输入用户名和密码
- 查看浏览器控制台的网络请求
5. **检查 Token 存储**
- 打开浏览器开发者工具 → Application → LocalStorage
- 应该看到 `auth_token``user` 字段
## 下一步
- [ ] 集成后端登录接口
- [ ] 添加用户列表 API (`src/api/users.ts`)
- [ ] 添加产品 API (`src/api/products.ts`)
- [ ] 添加订单 API (`src/api/orders.ts`)
- [ ] 实现 token 刷新机制
- [ ] 添加错误日志上报