# API服务封装文档 ## 1. Axios基础配置 ### 1.1 Axios实例创建 ```javascript // services/api.js import axios from 'axios'; // 创建axios实例 const api = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL || 'http://localhost:5000/api/v1', timeout: 10000, headers: { 'Content-Type': 'application/json', }, }); export default api; ``` ### 1.2 环境变量配置 ```env # .env.development VITE_API_BASE_URL=http://localhost:5000/api/v1 VITE_APP_TITLE=海洋项目管理系统 # .env.production VITE_API_BASE_URL=https://api.example.com/api/v1 VITE_APP_TITLE=海洋项目管理系统 ``` ## 2. 请求拦截器 ### 2.1 请求前拦截 ```javascript // services/api.js // 请求拦截器 api.interceptors.request.use( (config) => { // 添加Token const token = localStorage.getItem('token'); if (token) { config.headers.Authorization = `Bearer ${token}`; } // 添加时间戳防止缓存 if (config.method === 'get') { config.params = { ...config.params, _t: Date.now(), }; } return config; }, (error) => { return Promise.reject(error); } ); ``` ### 2.2 请求日志(开发环境) ```javascript // services/api.js if (import.meta.env.DEV) { api.interceptors.request.use((config) => { console.log('Request:', { url: config.url, method: config.method, params: config.params, data: config.data, }); return config; }); } ``` ## 3. 响应拦截器 ### 3.1 响应数据处理 ```javascript // services/api.js import { message } from 'antd'; // 响应拦截器 api.interceptors.response.use( (response) => { const { data } = response; // 检查业务状态码 if (data.success === false) { message.error(data.message || '请求失败'); return Promise.reject(new Error(data.message || '请求失败')); } // 返回数据部分 return data.data || data; }, (error) => { // 错误处理 if (error.response) { handleResponseError(error.response); } else if (error.request) { handleNetworkError(error); } else { handleOtherError(error); } return Promise.reject(error); } ); ``` ### 3.2 错误处理函数 ```javascript // services/api.js /** * 处理响应错误 */ const handleResponseError = (response) => { const { status, data } = response; switch (status) { case 400: message.error(data.message || '请求参数错误'); break; case 401: message.error('登录已过期,请重新登录'); clearAuth(); window.location.href = '/login'; break; case 403: message.error('权限不足'); break; case 404: message.error('请求的资源不存在'); break; case 409: message.error(data.message || '资源已存在'); break; case 500: message.error('服务器错误,请稍后重试'); break; default: message.error(data.message || '请求失败'); } }; /** * 处理网络错误 */ const handleNetworkError = (error) => { if (error.code === 'ECONNABORTED') { message.error('请求超时,请检查网络连接'); } else { message.error('网络错误,请检查网络连接'); } }; /** * 处理其他错误 */ const handleOtherError = (error) => { message.error(error.message || '发生未知错误'); }; /** * 清除认证信息 */ const clearAuth = () => { localStorage.removeItem('token'); localStorage.removeItem('user'); }; ``` ### 3.3 响应日志(开发环境) ```javascript // services/api.js if (import.meta.env.DEV) { api.interceptors.response.use( (response) => { console.log('Response:', response); return response; }, (error) => { console.error('Error:', error); return Promise.reject(error); } ); } ``` ## 4. API模块化设计 ### 4.1 认证API (services/auth.js) ```javascript // services/auth.js import api from './api'; export const authAPI = { /** * 用户登录 * @param {string} username - 用户名 * @param {string} password - 密码 */ login: (username, password) => { return api.post('/auth/login', { username, password }); }, /** * 获取当前用户信息 */ getCurrentUser: () => { return api.get('/auth/me'); }, /** * 登出 */ logout: () => { return api.post('/auth/logout'); }, }; ``` ### 4.2 用户API (services/user.js) ```javascript // services/user.js import api from './api'; export const userAPI = { /** * 获取用户列表 * @param {Object} params - 查询参数 * @param {number} params.page - 页码 * @param {number} params.page_size - 每页数量 * @param {string} params.department - 部门筛选 * @param {string} params.role - 角色筛选 * @param {string} params.keyword - 关键词搜索 */ getList: (params) => { return api.get('/users', { params }); }, /** * 获取用户详情 * @param {number} id - 用户ID */ getDetail: (id) => { return api.get(`/users/${id}`); }, /** * 创建用户 * @param {Object} data - 用户数据 */ create: (data) => { return api.post('/users', data); }, /** * 更新用户 * @param {number} id - 用户ID * @param {Object} data - 更新数据 */ update: (id, data) => { return api.put(`/users/${id}`, data); }, /** * 删除用户 * @param {number} id - 用户ID */ delete: (id) => { return api.delete(`/users/${id}`); }, /** * 重置用户密码 * @param {number} id - 用户ID * @param {string} new_password - 新密码 */ resetPassword: (id, new_password) => { return api.post(`/users/${id}/reset-password`, { new_password }); }, }; ``` ### 4.3 项目API (services/project.js) ```javascript // services/project.js import api from './api'; export const projectAPI = { /** * 获取项目列表 * @param {Object} params - 查询参数 */ getList: (params) => { return api.get('/projects', { params }); }, /** * 获取项目详情 * @param {number} id - 项目ID */ getDetail: (id) => { return api.get(`/projects/${id}`); }, /** * 创建项目 * @param {Object} data - 项目数据 */ create: (data) => { return api.post('/projects', data); }, /** * 更新项目 * @param {number} id - 项目ID * @param {Object} data - 更新数据 */ update: (id, data) => { return api.put(`/projects/${id}`, data); }, /** * 删除项目 * @param {number} id - 项目ID */ delete: (id) => { return api.delete(`/projects/${id}`); }, /** * 批量删除项目 * @param {Array} ids - 项目ID数组 */ batchDelete: (ids) => { return api.post('/projects/batch-delete', { ids }); }, /** * 获取基础统计 * @param {Object} params - 查询参数 */ getStatistics: (params) => { return api.get('/projects/statistics', { params }); }, /** * 获取分组统计 * @param {Object} params - 查询参数 * @param {string} params.group_by - 分组字段 */ getGroupStatistics: (params) => { return api.get('/projects/statistics/group', { params }); }, /** * 获取时间维度统计 * @param {Object} params - 查询参数 * @param {string} params.time_field - 时间字段 */ getTimelineStatistics: (params) => { return api.get('/projects/statistics/timeline', { params }); }, /** * 导出项目数据 * @param {Object} params - 查询参数 */ export: (params) => { return api.get('/projects/export', { params, responseType: 'blob', }); }, }; ``` ### 4.4 统计API (services/statistics.js) ```javascript // services/statistics.js import api from './api'; import { projectAPI } from './project'; // 重用projectAPI的统计方法 export const statisticsAPI = { ...projectAPI.getStatistics, ...projectAPI.getGroupStatistics, ...projectAPI.getTimelineStatistics, /** * 获取仪表盘数据 */ getDashboard: () => { return api.get('/statistics/dashboard'); }, /** * 获取项目趋势 * @param {Object} params - 查询参数 */ getTrend: (params) => { return api.get('/statistics/trend', { params }); }, }; ``` ## 5. 自定义Hooks封装 ### 5.1 useApi Hook ```javascript // hooks/useApi.js import { useState, useCallback } from 'react'; import { message } from 'antd'; /** * API调用Hook * @param {Function} apiFunc - API函数 */ export const useApi = (apiFunc) => { const [data, setData] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const execute = useCallback( async (...args) => { try { setLoading(true); setError(null); const result = await apiFunc(...args); setData(result); return result; } catch (err) { setError(err); throw err; } finally { setLoading(false); } }, [apiFunc] ); return { data, loading, error, execute }; }; ``` ### 5.2 useList Hook ```javascript // hooks/useList.js import { useState, useCallback } from 'react'; /** * 列表数据Hook * @param {Function} apiFunc - API函数 */ export const useList = (apiFunc) => { const [list, setList] = useState([]); const [total, setTotal] = useState(0); const [loading, setLoading] = useState(false); const [pagination, setPagination] = useState({ current: 1, pageSize: 10, }); const fetchList = useCallback( async (params = {}) => { try { setLoading(true); const result = await apiFunc({ page: pagination.current, page_size: pagination.pageSize, ...params, }); setList(result.items || []); setTotal(result.total || 0); } catch (error) { console.error('Fetch list error:', error); } finally { setLoading(false); } }, [apiFunc, pagination] ); const handleTableChange = useCallback( (newPagination) => { setPagination(newPagination); }, [] ); const refresh = useCallback(() => { fetchList(); }, [fetchList]); return { list, total, loading, pagination, fetchList, handleTableChange, refresh, }; }; ``` ### 5.3 useCreate Hook ```javascript // hooks/useCreate.js import { useState, useCallback } from 'react'; import { message } from 'antd'; /** * 创建数据Hook * @param {Function} apiFunc - API函数 * @param {Function} onSuccess - 成功回调 */ export const useCreate = (apiFunc, onSuccess) => { const [loading, setLoading] = useState(false); const create = useCallback( async (data) => { try { setLoading(true); await apiFunc(data); message.success('创建成功'); if (onSuccess) { onSuccess(); } } catch (error) { console.error('Create error:', error); } finally { setLoading(false); } }, [apiFunc, onSuccess] ); return { create, loading }; }; ``` ### 5.4 useUpdate Hook ```javascript // hooks/useUpdate.js import { useState, useCallback } from 'react'; import { message } from 'antd'; /** * 更新数据Hook * @param {Function} apiFunc - API函数 * @param {Function} onSuccess - 成功回调 */ export const useUpdate = (apiFunc, onSuccess) => { const [loading, setLoading] = useState(false); const update = useCallback( async (id, data) => { try { setLoading(true); await apiFunc(id, data); message.success('更新成功'); if (onSuccess) { onSuccess(); } } catch (error) { console.error('Update error:', error); } finally { setLoading(false); } }, [apiFunc, onSuccess] ); return { update, loading }; }; ``` ### 5.5 useDelete Hook ```javascript // hooks/useDelete.js import { useState, useCallback } from 'react'; import { Modal, message } from 'antd'; /** * 删除数据Hook * @param {Function} apiFunc - API函数 * @param {Function} onSuccess - 成功回调 */ export const useDelete = (apiFunc, onSuccess) => { const [loading, setLoading] = useState(false); const handleDelete = useCallback( async (id, options = {}) => { const { title = '确认删除', content = '确定要删除吗?' } = options; Modal.confirm({ title, content, onOk: async () => { try { setLoading(true); await apiFunc(id); message.success('删除成功'); if (onSuccess) { onSuccess(); } } catch (error) { console.error('Delete error:', error); } finally { setLoading(false); } }, }); }, [apiFunc, onSuccess] ); return { handleDelete, loading }; }; ``` ## 6. 请求工具函数 ### 6.1 防抖函数 ```javascript // utils/debounce.js /** * 防抖函数 * @param {Function} func - 需要防抖的函数 * @param {number} delay - 延迟时间 */ export const debounce = (func, delay = 300) => { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => { func.apply(this, args); }, delay); }; }; ``` ### 6.2 节流函数 ```javascript // utils/throttle.js /** * 节流函数 * @param {Function} func - 需要节流的函数 * @param {number} delay - 延迟时间 */ export const throttle = (func, delay = 300) => { let lastCall = 0; return (...args) => { const now = Date.now(); if (now - lastCall >= delay) { func.apply(this, args); lastCall = now; } }; }; ``` ### 6.3 取消重复请求 ```javascript // utils/request.js const pendingRequests = new Map(); /** * 生成请求key */ const generateRequestKey = (config) => { const { method, url, params, data } = config; return [method, url, JSON.stringify(params), JSON.stringify(data)].join('&'); }; /** * 取消重复请求 */ export const cancelDuplicateRequest = (config) => { const requestKey = generateRequestKey(config); if (pendingRequests.has(requestKey)) { const cancel = pendingRequests.get(requestKey); cancel('取消重复请求'); pendingRequests.delete(requestKey); } }; /** * 添加待处理请求 */ export const addPendingRequest = (config, cancel) => { const requestKey = generateRequestKey(config); pendingRequests.set(requestKey, cancel); }; /** * 移除待处理请求 */ export const removePendingRequest = (config) => { const requestKey = generateRequestKey(config); pendingRequests.delete(requestKey); }; ``` ## 7. 文件上传和下载 ### 7.1 文件上传 ```javascript // services/upload.js import api from './api'; /** * 上传文件 * @param {File} file - 文件对象 * @param {Function} onProgress - 进度回调 */ export const uploadFile = (file, onProgress) => { const formData = new FormData(); formData.append('file', file); return api.post('/upload', formData, { headers: { 'Content-Type': 'multipart/form-data', }, onUploadProgress: (progressEvent) => { const percentCompleted = Math.round( (progressEvent.loaded * 100) / progressEvent.total ); if (onProgress) { onProgress(percentCompleted); } }, }); }; /** * 批量上传文件 * @param {File[]} files - 文件数组 */ export const uploadFiles = async (files) => { const uploadPromises = files.map((file) => uploadFile(file)); return Promise.all(uploadPromises); }; ``` ### 7.2 文件下载 ```javascript // services/download.js import api from './api'; /** * 下载文件 * @param {string} url - 下载地址 * @param {string} filename - 文件名 */ export const downloadFile = async (url, filename) => { try { const response = await api.get(url, { responseType: 'blob', }); const blob = new Blob([response]); const downloadUrl = window.URL.createObjectURL(blob); const link = document.createElement('a'); link.href = downloadUrl; link.download = filename; document.body.appendChild(link); link.click(); document.body.removeChild(link); window.URL.revokeObjectURL(downloadUrl); } catch (error) { console.error('Download error:', error); } }; /** * 导出Excel * @param {Object} params - 查询参数 */ export const exportExcel = async (params) => { try { const response = await api.get('/export', { params, responseType: 'blob', }); const blob = new Blob([response], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', }); const downloadUrl = window.URL.createObjectURL(blob); const link = document.createElement('a'); link.href = downloadUrl; link.download = `export_${Date.now()}.xlsx`; document.body.appendChild(link); link.click(); document.body.removeChild(link); window.URL.revokeObjectURL(downloadUrl); } catch (error) { console.error('Export error:', error); } }; ``` ## 8. 错误处理和重试 ### 8.1 请求重试 ```javascript // services/api.js /** * 请求重试配置 */ const retryConfig = { retries: 3, retryDelay: 1000, retryCondition: (error) => { // 网络错误或5xx错误时重试 return !error.response || error.response.status >= 500; }, }; api.interceptors.response.use(undefined, async (error) => { const { config } = error; if (!config || !config.retryCount) { return Promise.reject(error); } const { retries, retryDelay, retryCondition } = retryConfig; // 检查是否满足重试条件 if (!retryCondition(error)) { return Promise.reject(error); } // 检查重试次数 config.retryCount = config.retryCount || 0; if (config.retryCount >= retries) { return Promise.reject(error); } // 增加重试计数 config.retryCount++; // 延迟重试 await new Promise((resolve) => setTimeout(resolve, retryDelay)); // 重新请求 return api(config); }); ``` ## 9. 缓存策略 ### 9.1 内存缓存 ```javascript // utils/cache.js const cache = new Map(); /** * 设置缓存 */ export const setCache = (key, value, ttl = 60000) => { const expiry = Date.now() + ttl; cache.set(key, { value, expiry }); }; /** * 获取缓存 */ export const getCache = (key) => { const item = cache.get(key); if (!item) { return null; } if (Date.now() > item.expiry) { cache.delete(key); return null; } return item.value; }; /** * 清除缓存 */ export const clearCache = (key) => { if (key) { cache.delete(key); } else { cache.clear(); } }; ``` ### 9.2 带缓存的请求 ```javascript // hooks/useCachedApi.js import { useState, useEffect } from 'react'; import { getCache, setCache } from '../utils/cache'; /** * 带缓存的API调用Hook */ export const useCachedApi = (apiFunc, cacheKey, ttl = 60000) => { const [data, setData] = useState(null); const [loading, setLoading] = useState(false); useEffect(() => { const fetchData = async () => { // 检查缓存 const cached = getCache(cacheKey); if (cached) { setData(cached); return; } // 发起请求 try { setLoading(true); const result = await apiFunc(); setData(result); setCache(cacheKey, result, ttl); } catch (error) { console.error('API error:', error); } finally { setLoading(false); } }; fetchData(); }, [apiFunc, cacheKey, ttl]); return { data, loading }; }; ``` ## 10. API使用示例 ### 10.1 在组件中使用 ```jsx import React, { useEffect } from 'react'; import { useList } from '../hooks/useList'; import { projectAPI } from '../services/project'; import Table from '../components/Common/Table'; const ProjectList = () => { const { list, total, loading, pagination, handleTableChange } = useList( projectAPI.getList ); const columns = [ { title: '项目编号', dataIndex: 'project_no', key: 'project_no', }, { title: '项目名称', dataIndex: 'name', key: 'name', }, // ...其他列 ]; return ( ); }; export default ProjectList; ``` ### 10.2 表单提交 ```jsx import React from 'react'; import { Form, Input, Button } from 'antd'; import { useCreate } from '../hooks/useCreate'; import { projectAPI } from '../services/project'; const ProjectForm = ({ onSuccess }) => { const [form] = Form.useForm(); const { create, loading } = useCreate(projectAPI.create, onSuccess); const handleSubmit = async (values) => { await create(values); }; return ( {/* ...其他表单项 */} ); }; export default ProjectForm; ``` --- **文档维护**: 前端程序员 **文档类型**: API服务封装文档 **最后更新**: 2026-01-25