本文适用场景:Vue3+TS+Axios 中后台管理系统、遵循RESTful接口规范项目、大量同质化增删改查业务
阅读收益:一套可直接落地的通用CRUD封装、区分简易/复杂分页查询最佳实践、对接自定义表格Hook完整用法、RPC旧式接口迁移方案
前置依赖 :项目已封装全局axios请求实例 @/utils/http
一、项目痛点
开发中后台系统,绝大多数业务模块(用户、角色、菜单、商品、部门)接口高度同质化:分页列表、详情、新增、编辑、单删、批量删除。
传统写法存在大量问题:
- 每个业务模块重复手写7个基础请求方法,代码冗余、维护成本高
- 旧式RPC接口命名杂乱:
queryPage、save、update、batchDelete,接口风格不统一 - 多条件筛选、日期区间、多选数组参数,GET请求超长导致接口报错
- 分页参数、返回体类型重复定义,TS类型复用性极差
基于以上问题,本文封装一套符合企业级RESTful规范 的通用 createBaseAPI,一行代码生成全套CRUD接口,开箱即用。
二、统一RESTful接口约定
适配说明:本文约定为适配中后台最优方案,可根据后端接口规范,自行修改请求路径、请求方式、批量删除入参格式
以资源 users 为例,基础路径 baseUrl: /api/users,全套接口规范如下:
| 请求方法 | 接口路径 | 业务说明 | 适用场景 |
|---|---|---|---|
| GET | /api/users |
简易分页列表 | 筛选条件少、无数组/日期范围,参数拼URL |
| POST | /api/users/search |
复杂分页查询 | 多选、日期区间、多表单筛选,参数放Body,规避URL超长 |
| GET | /api/users/:id |
获取单条详情 | 表单回显、弹窗详情 |
| POST | /api/users |
新增资源 | 新增业务数据 |
| PUT | /api/users/:id |
全量编辑资源 | 覆盖式修改数据 |
| DELETE | /api/users/:id |
单条删除 | 单行删除操作 |
| DELETE | /api/users |
批量删除 | 入参 {ids: string[]},表格多选删除 |
核心选型技巧
-
简单筛选:优先GET list,写法简洁、缓存友好
-
复杂筛选:必用POST search,避免浏览器URL长度限制、特殊参数转义问题
-
后端批量路径自定义:可手动扩展方法适配
/batch-delete这类非标接口
三、核心工具类 createBaseAPI 源码
新建文件 @/utils/createBaseAPI.ts,完整类型封装,支持泛型约束,完美适配TS类型提示
typescript
import { http } from '@/utils/http'
/** 后端统一分页返回结构体 */
export interface PageResult<T> {
list: T[]
total: number
}
/** 通用分页入参 */
export interface PageParams {
page: number
size: number
}
/** 业务自定义参数映射:列表筛选/新增/编辑类型约束 */
interface QueryMap {
/** 列表查询筛选参数 */
list?: Record<string, any>
/** 新增请求体 */
create?: Record<string, any>
/** 更新请求体 */
update?: Record<string, any>
}
/**
* 生成RESTful通用CRUD API
* @param baseUrl 资源基础接口地址
* @returns 内置全套增删改查方法
*/
export function createBaseAPI<T, Q extends QueryMap = QueryMap>(params: { baseUrl: string }) {
const { baseUrl } = params
return {
/** GET /resource/:id 获取详情 */
getById(id: string) {
return http.get<T>(`${baseUrl}/${id}`)
},
/** GET /resource 简易分页列表 */
list(params: PageParams & Q['list']) {
return http.get<PageResult<T>>(baseUrl, params)
},
/** POST /resource/search 复杂分页筛选 */
search(params: PageParams & Q['list']) {
return http.post<PageResult<T>>(`${baseUrl}/search`, params)
},
/** POST /resource 新增资源 */
create(data: Q['create']) {
return http.post<T>(baseUrl, data)
},
/** PUT /resource/:id 全量更新资源 */
update(id: string, data: Q['update']) {
return http.put<T>(`${baseUrl}/${id}`, data)
},
/** DELETE /resource/:id 单条删除 */
remove(id: string) {
return http.delete<void>(`${baseUrl}/${id}`)
},
/** DELETE /resource 批量删除 */
removeBatch(ids: string[]) {
return http.delete<void>(baseUrl, { data: { ids } })
}
}
}
四、业务模块快速定义API
以用户模块为例,新建 @/apis/user.ts,仅需定义实体类型+自定义筛选类型,一行生成全部CRUD,非标接口单独拓展即可。
typescript
import { createBaseAPI } from '@/utils/createBaseAPI'
import type { PageResult } from '@/utils/createBaseAPI'
/** 用户实体类型 */
export interface UserItem {
id: string
name: string
age: number
phone: string
address: string
status: number
}
/** 用户角色附属类型(拓展非标接口) */
export interface UserRoleItem {
id: string
roleName: string
}
/** 自定义用户模块:列表筛选、新增、编辑入参类型 */
interface UserQuery {
list?: {
name?: string
status?: number
// 多选状态筛选
statusList?: number[]
// 日期区间筛选
dateRange?: string[]
}
// 新增字段约束
create?: Partial<UserItem>
// 编辑字段约束
update?: Partial<UserItem>
}
/** 自动生成用户全套CRUD接口 */
export const userAPI = createBaseAPI<UserItem, UserQuery>({ baseUrl: '/api/users' })
/** ========== 非标REST接口单独拓展 ========== */
/** 获取用户绑定角色 */
export function getUserRoleList(id: string) {
return http.get<UserRoleItem[]>(`/api/users/${id}/roles`)
}
五、页面直接调用使用
组合式API页面直接调用,TS自动推导入参出参,自带代码提示,无需重复写url和method
php
import { userAPI } from '@/apis/user'
// 1. 获取详情
const detail = await userAPI.getById('1')
// 2. 简易分页查询(少量筛选条件)
const pageData = await userAPI.list({ page: 1, size: 10, name: '', status: 1 })
// 3. 复杂条件查询(多选、日期范围)
const searchData = await userAPI.search({
page: 1,
size: 10,
name: '',
statusList: [1, 2],
dateRange: ['2024-01-01', '2024-12-31']
})
// 4. 新增用户
await userAPI.create({ name: '张三', age: 18, status: 1 })
// 5. 编辑用户
await userAPI.update('1', { name: '李四' })
// 6. 单条删除
await userAPI.remove('1')
// 7. 批量删除
await userAPI.removeBatch(['1', '2', '3'])
六、适配项目通用表格Hook useTable
中后台必备表格Hook联动,自动判断筛选条件,动态切换 GET/list / POST/search,优化请求体验,适配Element Plus/Ant Design Vue表格。
typescript
import { userAPI } from '@/apis/user'
import { useTable } from '@/hooks/useTable'
import { computed, reactive } from 'vue'
// 表格筛选条件
const queryParams = reactive({
name: '',
status: undefined as number | undefined,
statusList: [] as number[],
dateRange: [] as string[]
})
// 判定:存在数组/日期条件,自动走POST复杂查询
const useSearchAPI = computed(() =>
queryParams.statusList.length > 0 || queryParams.dateRange.length > 0
)
// 表格全局状态、请求、分页封装
const { tableData, pagination, search, loading } = useTable({
listAPI: (pageInfo) => useSearchAPI.value
? userAPI.search({ ...pageInfo, ...queryParams })
: userAPI.list({ ...pageInfo, ...queryParams }),
rowKey: 'id',
deleteAPI: (ids) => userAPI.removeBatch(ids)
})
七、旧式RPC接口 vs RESTful新规范对照
适配老旧项目迁移改造,快速对齐新旧接口语义,方便统一项目接口风格
| 旧式RPC风格接口 | 标准化RESTful接口 |
|---|---|
GET /api/user/queryById?id=1 |
GET /api/users/1 |
GET /api/user/pageQueryList |
GET /api/users / POST /api/users/search |
POST /api/user/saveInsert |
POST /api/users |
POST /api/user/saveUpdate |
PUT /api/users/:id |
POST /api/user/delete |
DELETE /api/users/:id |
POST /api/user/batchDelete |
DELETE /api/users |
补充适配方案
后端提供「新增表单默认值」接口 GET /api/users/new,不属于基础CRUD,直接在业务API手动拓展即可,不改动基础封装代码。
八、项目自定义改造指南
1. 适配PATCH局部更新
后端使用PATCH做部分字段更新,在createBaseAPI内新增方法即可:
typescript
/** PATCH 局部更新 */
patch(id: string, data: Q['update']) {
return http.patch<T>(`${baseUrl}/${id}`, data)
}
2. 适配非标批量删除路径
后端批量删除为 POST /api/users/batch-del,业务层直接拓展方法:
typescript
// userAPI拓展自定义批量删除
userAPI.batchDel = (ids: string[]) => http.post('/api/users/batch-del', { ids })
3. 修改分页字段
后端分页参数不为page/size,直接修改PageParams全局类型即可全局生效。
九、总结
- 这套封装零第三方依赖,依托项目原有axios请求实例,接入成本极低
- 区分GET简易查询 / POST复杂查询,完美解决URL超长、参数转义问题
- 泛型全覆盖,业务层只需定义实体类型,全程TS类型兜底,减少接口报错
- 基础CRUD零代码编写,非标接口灵活拓展,兼容新旧后端接口规范
后续新增任意业务模块(角色、菜单、字典),仅需3行代码即可生成全套CRUD接口,开发效率直接翻倍✨