- Add environment configs for dev/staging/prod environments - Implement centralized axios request utility with standardized error handling - Add shared TypeScript types for API responses and requests - Create comprehensive API client modules for all core endpoints - Configure vue router with all application page routes - Add icon fonts, static assets, and loading animations - Set up project documentation and collaboration guidelines - Remove deprecated uni-app bridge component files
8.5 KiB
8.5 KiB
统一请求库封装计划(src/utils/request.ts 版)
Summary
目标:把统一请求库集中封装在 src/utils/request.ts,类型集中在 src/shared/,实现:
- baseURL/timeout/取消请求(Axios Web)
- 默认 header 注入:Authorization(Bearer)、clientId、X-Latitude、X-Longitude、language
- 业务错误/网络错误/HTTP 错误的统一归一化(只抛错,不做 Toast/跳转)
- 输出说明文档到
docs/request.md
Current State Analysis(以实际仓库为准)
已确认的事实:
- 依赖中已包含
axios,但src/内暂无 axios 的实际封装与使用。 src/utils/request.ts存在但为空文件,且命名疑似历史遗留。- 代码中存在大量“缺失模块引用”(详见文末“历史遗留问题清单”),其中
@/request/api/*属于最集中、最影响后续迁移的一类。
结论:
- 本次统一请求库规划要做到“新增即可用、可渐进替换”,不依赖现有(缺失的)API 模块结构。
Assumptions & Decisions(你已确认)
- 底层实现:Axios(Web)
- baseURL:从
import.meta.env的环境变量读取(本计划固定变量名为VITE_API_BASE_URL) - header 值来源:全局上下文注入(内存态),由登录/定位/语言切换时写入
- Authorization:
Bearer <token>(token 为空则不带 Authorization) - 错误处理:请求库只做错误标准化并抛错,不做 Toast/跳转
- header 字段:
Authorization、clientId、X-Latitude、X-Longitude、language
为什么不拆得更细(回答你的问题)
可以都放在 src/utils/request.ts 里完成,且更适合你现在“先落地一个可用的统一入口”的诉求:
- 单文件落地成本低:少目录、少入口、少导出点,替换/回滚都更直接
- 不牺牲可维护性:在单文件里用“内部分区函数 + 类型从 shared 引入”的方式,同样能保持边界清晰
- 何时再拆分:当出现“单文件 > 300~500 行、多人同时改动频繁、需要独立单测/Mock”的情况,再把 errors/context/http 拆出去更合理
因此本计划采用:实现集中在 request.ts,类型抽到 shared 的折中方案。
Proposed Changes(全新规划)
1) 新增类型目录:src/shared/
新增目录:src/shared/
新增文件(建议):
src/shared/request-types.ts
包含的类型定义(稳定、可复用):
export interface ApiResponse<T> { code: number; message?: string; data: T }export interface RequestOptions { signal?: AbortSignal; headers?: Record<string, string>; skipAuth?: boolean }export type RequestContext = { token: string | null; clientId: string | null; latitude: number | null; longitude: number | null; language: string | null }export type NormalizedErrorKind = "business" | "http" | "network" | "unknown"export interface NormalizedError extends Error { kind: NormalizedErrorKind; code?: number; httpStatus?: number; response?: unknown }
2) 统一请求实现:src/utils/request.ts
新增文件:src/utils/request.ts
职责(全部在一个文件内完成):
2.1 上下文容器(内存态)
提供以下导出函数(由业务在合适时机调用):
setAuthToken(token: string | null): voidsetClientId(clientId: string | null): voidsetLocation(latitude: number | null, longitude: number | null): voidsetLanguage(language: string | null): voidgetRequestContext(): RequestContext
默认值策略:
language:如果未显式设置,尝试读取src/i18n/index.ts的getCurrentLocale()(读取失败则不注入 language header)
2.2 Axios 实例创建与默认配置
baseURL:import.meta.env.VITE_API_BASE_URL- 若缺失:直接抛出清晰错误(提示需要配置 baseURL)
timeout:可选import.meta.env.VITE_API_TIMEOUT_MS,缺失则默认 15000
2.3 请求拦截器(header 注入)
组装 headers 规则:
- 合并顺序:
默认headers < 上下文headers < 调用方options.headers Authorization:仅当 token 存在时注入Bearer <token>;当options.skipAuth === true时跳过clientId:存在则注入X-Latitude/X-Longitude:存在则注入(数值转字符串)language:存在则注入
2.4 响应处理(业务码 + 错误归一化)
- 正常响应:
- 若响应体符合
ApiResponse<T>且code === 0:返回整个ApiResponse<T> - 若响应体符合
ApiResponse<T>且code !== 0:抛出NormalizedError(kind="business", code, message, response)
- 若响应体符合
- 异常响应(axios error):
- 有
response.status:抛NormalizedError(kind="http", httpStatus, response) - 无
response:抛NormalizedError(kind="network")
- 有
对外导出(建议最小集合):
export async function request<T>(config, options?: RequestOptions): Promise<ApiResponse<T>>- 可选:
export async function requestData<T>(...): Promise<T>(内部调用 request 并返回data,用于未来更干净的调用风格)
3) 文档输出到 docs/
新增目录:docs/
新增文档:docs/request.md(必须包含):
- 环境变量约定:
VITE_API_BASE_URL、VITE_API_TIMEOUT_MS(可选) - 上下文写入时机:
- 登录成功:
setAuthToken(token) - 应用初始化:
setClientId(clientId) - 定位成功:
setLocation(lat, lng) - 语言切换:
setLanguage(locale)(或依赖默认读取 i18n)
- 登录成功:
- 使用方式:
- 旧代码风格兼容:
const res = await request<T>(); res.data... - 新代码推荐:
const data = await requestData<T>()
- 旧代码风格兼容:
- 错误处理示例:区分 business/http/network/unknown 并给出上层建议策略
历史遗留问题清单(独立梳理,方便逐个替换)
说明:以下均为“当前仓库扫描可证实”的问题点,按模块引用位置列出,便于你后续按优先级逐个替换/补齐。
A. @/request/api/* 悬空引用(13 个文件)
src/pages/quick/index.vuesrc/components/Feedback/index.vuesrc/components/CreateServiceOrder/index.vuesrc/pages/service/order/components/OrderCard/index.vuesrc/pages/booking/components/FooterSection/index.vuesrc/pages/goods/index.vuesrc/pages/order/order/components/FooterSection/index.vuesrc/pages/quick/components/Tabs/index.vuesrc/pages/order/order/list.vuesrc/pages/service/order/index.vuesrc/pages/booking/index.vuesrc/pages/login/index.vuesrc/pages/order/order/detail.vue
建议后续替换策略:
- 先确定“这些 API 模块的真实来源”(是否在其它分支/其它仓库/尚未迁入)。
- 如果确认不会迁回,则逐文件把
@/request/api/*替换为基于src/utils/request.ts的真实调用(需要你提供具体 URL/参数约定)。
B. @/utils 目录级导入缺失(6 个文件)
当前仓库没有 src/utils/index.*,但以下文件在导入 @/utils:
src/pages/quick/index.vuesrc/pages/booking/components/FooterSection/index.vuesrc/pages/goods/index.vuesrc/pages/order/order/components/FooterSection/index.vuesrc/pages/booking/index.vuesrc/pages/home/index.vue
C. @/store 与实际目录 src/stores/ 命名不一致(6 个文件)
src/components/ImageSwiper/index.vuesrc/pages/quick/components/Card/index.vuesrc/pages/goods/album/index.vuesrc/pages/goods/index.vuesrc/pages/booking/index.vuesrc/pages/home/index.vue
D. @/hooks/* 缺失(3 个文件)
src/components/SwipeCards/index.vuesrc/pages/login/index.vuesrc/pages/home/index.vue
E. @/constant/* 缺失(6 个文件)
src/components/ModuleTitle/index.vuesrc/components/Feedback/index.vuesrc/components/SurveyQuestionnaire/index.vuesrc/components/CreateServiceOrder/index.vuesrc/pages/booking/index.vuesrc/pages/login/index.vue
F. src/utils/request.ts(空文件 + 疑似拼写问题)
- 该文件当前为空,且命名疑似应为
requests或request - 建议后续统一策略:保留/替换/删除前先全仓搜索确认是否在其它分支被引用
Verification(执行阶段你手动确认运行)
你在实现完成后手动执行:
yarn typecheckyarn build- 如有用例:
yarn test
验收标准:
src/utils/request.ts+src/shared/request-types.ts编译通过- 发起请求时能注入 headers(Authorization/clientId/X-Latitude/X-Longitude/language)
code !== 0时抛出稳定结构的错误对象(上层可根据 kind 做统一处理)