# API 调用模式和通用实现指南 ## 核心 API 架构 ### ApiClient 核心类 **位置**:`src/services/api.ts` ```typescript class ApiClient { private instance: any; private isRefreshing = false; private failedQueue: Array<{ resolve: (value: any) => void; reject: (reason: any) => void; }> = []; // 特性: // 1. 自动附加 Authorization 头 // 2. 401 错误时自动刷新 token // 3. Token 刷新期间的请求入队 // 4. 自动处理 token 过期和重新登录 } export const apiClient = new ApiClient(); ``` --- ## API 服务层设计模式 ### 模式 1:列表 API(用户等级配置) **文件**:`src/services/userLevelConfigApi.ts` ```typescript import { apiClient } from './api'; import type { UserLevelConfig, UserLevelConfigListRequest, UserLevelConfigListResponse } from '../types/userLevelConfig'; // 模式:响应数据提取 // - apiClient 返回完整响应对象 // - 最后一层 API 函数负责提取数据或返回完整响应 export const getUserLevelConfigList = async ( params: UserLevelConfigListRequest ): Promise => { const response = await apiClient.get('/admin/user-level-configs', { params }); return response.data; // 返回响应数据部分 }; export const getAllUserLevelConfigs = async (): Promise => { const response = await apiClient.get('/admin/user-level-configs/all'); return response.data; }; ``` ### 模式 2:列表 API(系统配置) **文件**:`src/services/systemConfigApi.ts` ```typescript import { apiClient } from './api'; import type { SystemConfig, SystemConfigListRequest, SystemConfigListResponse } from '../types/systemConfig'; // 注意:不同的 API 可能返回不同的数据结构 // 系统配置返回完整响应对象(包含 code, message, data) export const getSystemConfigList = async ( params: SystemConfigListRequest ): Promise => { const response = await apiClient.get('/admin/system-configs', { params }); return response; // 返回完整响应对象 }; ``` ### 模式 3:CRUD 操作 ```typescript // 创建 export const createUserLevelConfig = async ( data: UserLevelConfigCreateRequest ): Promise => { const response = await apiClient.post('/admin/user-level-configs', data); return response.data; }; // 读取 export const getUserLevelConfigById = async (id: number): Promise => { const response = await apiClient.get(`/admin/user-level-configs/${id}`); return response.data; }; // 更新 export const updateUserLevelConfig = async ( id: number, data: UserLevelConfigUpdateRequest ): Promise => { const response = await apiClient.put(`/admin/user-level-configs/${id}`, data); return response.data; }; // 删除 export const deleteUserLevelConfig = async (id: number): Promise => { await apiClient.delete(`/admin/user-level-configs/${id}`); }; // 特殊操作:状态更新 export const updateUserLevelConfigStatus = async ( id: number, data: UserLevelConfigStatusRequest ): Promise => { await apiClient.put(`/admin/user-level-configs/${id}/status`, data); }; ``` --- ## 类型定义模式 ### 完整类型定义示例 **文件**:`src/types/userLevelConfig.ts` ```typescript // 1. 主体数据类型 export interface UserLevelConfig { id: number; level_name: string; level_code: string; project_limit: number; description: string; sort_order: number; status: number; // 1=启用, 0=禁用 created_at: string; updated_at: string; } // 2. 请求类型 export interface UserLevelConfigListRequest { level_name?: string; status?: number; page?: number; size?: number; } export interface UserLevelConfigCreateRequest { level_name: string; level_code: string; project_limit: number; description?: string; sort_order?: number; } export interface UserLevelConfigUpdateRequest { level_name: string; project_limit: number; description?: string; sort_order?: number; } export interface UserLevelConfigStatusRequest { status: number; } // 3. 响应类型 export interface UserLevelConfigListResponse { data: UserLevelConfig[]; total: number; page: number; size: number; } ``` --- ## 页面组件实现模式 ### CRUD 页面的标准结构 **文件**:`src/pages/UserLevelConfigs.tsx` ```typescript import React, { useEffect, useState } from 'react'; import { Table, Button, Modal, Form, Input, message } from 'antd'; import type { UserLevelConfig } from '../types/userLevelConfig'; import { getUserLevelConfigList, createUserLevelConfig, updateUserLevelConfig, deleteUserLevelConfig } from '../services/userLevelConfigApi'; export default function UserLevelConfigs() { // ==================== 状态管理 ==================== const [loading, setLoading] = useState(false); const [list, setList] = useState([]); const [total, setTotal] = useState(0); const [page, setPage] = useState(1); const [size, setSize] = useState(10); // 编辑弹窗状态 const [editOpen, setEditOpen] = useState(false); const [editing, setEditing] = useState(null); const [form] = Form.useForm(); // 创建弹窗状态 const [createOpen, setCreateOpen] = useState(false); const [createForm] = Form.useForm(); // ==================== 数据加载 ==================== const fetchList = async () => { setLoading(true); try { const res = await getUserLevelConfigList({ page, size }); // 处理响应数据:兼容数组和对象两种格式 if (Array.isArray(res)) { setList(res); setTotal(res.length); } else { setList(res?.data ?? []); setTotal(res?.total ?? 0); } } catch (e) { console.error('获取列表失败:', e); message.error('获取列表失败'); } finally { setLoading(false); } }; useEffect(() => { fetchList(); }, [page, size]); // ==================== 创建操作 ==================== const openCreate = () => { createForm.resetFields(); setCreateOpen(true); }; const submitCreate = async () => { try { const values = await createForm.validateFields(); await createUserLevelConfig(values); message.success('创建成功'); setCreateOpen(false); fetchList(); } catch (error: any) { message.error(error?.response?.data?.message || '创建失败'); } }; // ==================== 编辑操作 ==================== const openEdit = (config: UserLevelConfig) => { setEditing(config); form.setFieldsValue({ level_name: config.level_name, project_limit: config.project_limit, description: config.description, sort_order: config.sort_order, }); setEditOpen(true); }; const submitEdit = async () => { try { const values = await form.validateFields(); if (!editing) return; await updateUserLevelConfig(editing.id, values); message.success('更新成功'); setEditOpen(false); fetchList(); } catch (error) { message.error('更新失败'); } }; // ==================== 删除操作 ==================== const handleDelete = async (config: UserLevelConfig) => { try { await deleteUserLevelConfig(config.id); message.success('删除成功'); fetchList(); } catch (error) { message.error('删除失败'); } }; // ==================== 渲染 ==================== const columns = [ // 列定义... ]; return (
{/* 主表格 */} {/* 创建弹窗 */}
{/* 表单字段 */}
{/* 编辑弹窗 */}
{/* 表单字段 */}
); } ``` --- ## 权限和路由集成 ### 添加新页面的完整检查清单 #### 1. 创建类型定义 - [ ] `src/types/xxxConfig.ts` - 定义数据模型和请求/响应类型 #### 2. 创建 API 服务 - [ ] `src/services/xxxConfigApi.ts` - 实现 CRUD API 方法 #### 3. 创建页面组件 - [ ] `src/pages/XxxConfigs.tsx` - 实现 CRUD UI #### 4. 注册路由 - [ ] `src/App.tsx` - 添加路由定义 ```typescript } /> ``` #### 5. 添加菜单项 - [ ] `src/components/DynamicMenu.tsx` - 添加菜单项定义 ```typescript { key: 'system-xxx-configs', label: 'Xxx 配置', icon: , path: '/xxx-configs', } ``` #### 6. 更新 Layout 子导航 - [ ] `src/components/Layout.tsx` - 添加子导航按钮(如果需要) ```typescript ``` --- ## 错误处理最佳实践 ### API 错误处理 ```typescript // 页面中的错误处理 const fetchList = async () => { try { const res = await getUserLevelConfigList({ page, size }); setList(res?.data ?? []); } catch (error: any) { // 1. 优先使用 API 返回的错误信息 const errorMsg = error?.response?.data?.message; // 2. 其次使用通用错误信息 message.error(errorMsg || '获取列表失败'); // 3. 记录详细错误用于调试 console.error('获取列表失败:', error); } }; // 创建/更新时的错误处理 const submitCreate = async () => { try { const values = await createForm.validateFields(); await createUserLevelConfig(values); message.success('创建成功'); } catch (error: any) { // 区分表单验证错误和 API 错误 if (error?.response?.data?.message) { message.error(error.response.data.message); } else { message.error('创建失败'); } } }; ``` --- ## 通用 CRUD 表格列定义 ### 标准列定义模式 ```typescript const columns = [ // 基本字段列 { title: '名称', dataIndex: 'level_name', key: 'level_name', }, { title: '代码', dataIndex: 'level_code', key: 'level_code', }, // 数值列(带格式化) { title: '数值', dataIndex: 'value', key: 'value', render: (value: number) => (value === 0 ? '不限' : value), }, // 状态列(带标签) { title: '状态', dataIndex: 'status', key: 'status', render: (value: number) => value === 1 ? 启用 : 禁用, }, // 操作列(最后) { title: '操作', key: 'action', render: (_: any, record: T) => ( ), }, ]; ``` --- ## 表单字段最佳实践 ### 创建表单示例 ```typescript
{/* 必填字段 */} {/* 代码字段(创建时必填,编辑时只读) */} {/* 数值字段 */} {/* 可选文本字段 */} {/* 排序字段 */} ``` --- ## 分页实现 ### 标准分页逻辑 ```typescript // 状态 const [page, setPage] = useState(1); const [size, setSize] = useState(10); const [total, setTotal] = useState(0); // 依赖项变化时重新加载 useEffect(() => { fetchList(); }, [page, size]); // 表格分页配置
{ setPage(p); setSize(s); }, }} /> ``` --- ## 总结 ### API 调用流程 ``` 用户操作 ↓ 页面组件(使用 API 函数) ↓ API 服务函数(处理参数和响应) ↓ ApiClient(自动处理 token 和拦截器) ↓ Axios(HTTP 请求) ↓ 后端 API ``` ### 关键要点 1. **分层设计**:每一层职责清晰 2. **类型安全**:完整的 TypeScript 类型支持 3. **错误处理**:统一的错误处理机制 4. **权限集成**:无缝的权限管理 5. **状态管理**:简化的本地状态管理 6. **自动化**:自动 Token 管理和刷新