以下是企业级标准、完全符合现代前端开发规范、并添加了详尽中文注释的 api.ts.ftl 和 types.ts.ftl 模板文件。
这两个模板是前后端分离架构的核心,用于生成TypeScript 安全调用的 API 客户端和精确的类型定义,确保前端代码与后端接口零歧义、零错误、零沟通成本。
✅ 一、api.ts.ftl —— API 客户端模板(基于 Axios)
作用:为每个实体生成一个独立的 API 模块,提供类型安全、语义清晰的 HTTP 请求方法。
<#--
=================================================================================================
MyBatis-Plus 代码生成器 - API 客户端模板 (TypeScript)
=================================================================================================
作者:CodeGenWeb
生成时间:${now?string("yyyy-MM-dd HH:mm:ss")}
数据库表:${table.comment}(表名:${table.name})
说明:
1. 本模板生成一个独立的 API 模块,命名为 ${entityNameLower}Api,用于与后端 ${table.comment!""} 接口交互。
2. 所有方法均使用 Axios 发起 HTTP 请求,返回 Promise,支持 async/await。
3. 使用 TypeScript 泛型精确描述请求和响应结构,确保类型安全。
4. 所有方法均包含 JSDoc 注释,IDE 支持 Hover 提示、参数自动补全。
5. 响应格式统一为 R<T>,与后端保持一致。
6. 本文件为 Freemarker 模板,生成时会自动替换 ${} 中的变量。
7. 请勿手动修改生成的文件,下次生成将覆盖!
=================================================================================================
-->
import axios from 'axios';
import { ${entity}Type, ${entity}WithDeletedType } from '@/types/${entityNameLower}.types';
import { PageResult } from '@/types/page.types';
// API 基础路径,与后端 Controller 的 @RequestMapping 保持一致
const BASE_URL = '/api/${table.entityName}';
/**
* ${table.comment!""} API 客户端
*
* <p>本模块提供对 ${table.comment!""} 资源的所有标准 CRUD 操作。</p>
* <p>所有方法均返回 Promise,可配合 async/await 使用。</p>
* <p>响应格式统一为:{ code: number, msg: string, data: T },由后端的 R<T> 类型定义。</p>
*
* @author ${author}
* @date ${now?string("yyyy-MM-dd")}
*/
export const ${entityNameLower}Api = {
/**
* 获取 ${table.comment!""} 列表(分页)
*
* <p>用于前端表格展示,支持自定义页码和每页大小。</p>
*
* @param params 分页参数
* - current: 当前页码(从 1 开始),默认值 1
* - size: 每页记录数,默认值 10,最大建议不超过 100
* @returns Promise<PageResult<${entity}Type>> 包含数据列表、总记录数、分页信息
* @example
* const res = await ${entityNameLower}Api.list({ current: 1, size: 20 });
* console.log(res.data.records); // 当前页数据
* console.log(res.data.total); // 总记录数
*/
list(params: { current?: number; size?: number } = {}) {
return axios.get<PageResult<${entity}Type>>(BASE_URL + '/page', { params });
},
/**
* 获取所有 ${table.comment!""}(无分页)
*
* <p>适用于数据量较小的场景,如下拉框、标签选择器等。</p>
* <p>注意:当数据量较大时,请使用分页接口,避免内存溢出。</p>
*
* @returns Promise<${entity}Type[]> 所有 ${table.comment!""} 的数组
* @example
* const users = await ${entityNameLower}Api.getAll();
*/
getAll() {
return axios.get<${entity}Type[]>(BASE_URL + '/list');
},
/**
* 根据 ID 查询单个 ${table.comment!""}
*
* <p>用于详情页加载、编辑前预填充数据。</p>
*
* @param id ${table.comment!""} 的主键 ID(必须为数字)
* @returns Promise<${entity}Type> 单个 ${table.comment!""} 对象,未找到时后端返回 404
* @example
* const user = await ${entityNameLower}Api.get(1);
*/
get(id: number) {
return axios.get<${entity}Type>(`${BASE_URL}/${id}`);
},
/**
* 新增一条 ${table.comment!""} 记录
*
* <p>前端传入的数据应为创建所需字段,**不包含 id**。</p>
* <p>后端会自动生成主键,并填充 createTime 等自动字段。</p>
*
* @param data 创建所需的数据对象,类型为 Omit<${entity}Type, 'id'>,即排除 id 字段
* @returns Promise<boolean> 操作是否成功
* @example
* const success = await ${entityNameLower}Api.create({
* username: 'zhangsan',
* email: 'zhangsan@example.com'
* });
*/
create(data: Omit<${entity}Type, 'id'>) {
return axios.post<boolean>(BASE_URL, data);
},
/**
* 修改一条 ${table.comment!""} 记录
*
* <p>前端必须传入完整的对象,**必须包含 id** 字段。</p>
* <p>后端会根据 id 更新对应记录,未传入的字段保持原值。</p>
*
* @param data 包含 id 的完整 ${table.comment!""} 对象
* @returns Promise<boolean> 操作是否成功
* @example
* const success = await ${entityNameLower}Api.update({
* id: 1,
* username: 'zhangsan_updated',
* email: 'newemail@example.com'
* });
*/
update(data: ${entity}Type) {
return axios.put<boolean>(BASE_URL, data);
},
/**
* 删除一条 ${table.comment!""} 记录(物理删除)
*
* <p>执行物理删除,不可恢复。生产环境建议使用逻辑删除(deleted=1)。</p>
*
* @param id ${table.comment!""} 的主键 ID
* @returns Promise<boolean> 操作是否成功
* @example
* const success = await ${entityNameLower}Api.delete(1);
*/
delete(id: number) {
return axios.delete<boolean>(`${BASE_URL}/${id}`);
}
};
✅ 模板说明(关键点)
| 特性 | 说明 |
|---|---|
Omit<${entity}Type, 'id'> | 核心安全机制!确保新增时前端不会误传 id,后端安全可控。 |
PageResult<T> | 统一分页响应类型,前端无需为每个接口重复定义。 |
| JSDoc 详尽 | 每个方法都有 @param、@returns、@example,IDE 悬停即懂。 |
| URL 与后端严格一致 | /api/${table.entityName} 与后端 Controller 保持一致,避免 404。 |
| 泛型精确 | axios.get<PageResult<${entity}Type>> 确保 TypeScript 编译时校验。 |
| 注释规范 | 使用中文,符合国内团队习惯,新人 10 分钟上手。 |
| 文件命名 | user.api.ts,命名清晰,便于查找。 |
✅ 二、types.ts.ftl —— 类型定义模板(TypeScript Interface)
作用:为每个实体定义精确的 TypeScript 类型,包括基础类型、带删除标记的类型、状态枚举等。
<#--
=================================================================================================
MyBatis-Plus 代码生成器 - TypeScript 类型定义模板
=================================================================================================
作者:CodeGenWeb
生成时间:${now?string("yyyy-MM-dd HH:mm:ss")}
数据库表:${table.comment}(表名:${table.name})
说明:
1. 本模板定义了 ${table.comment!""} 的所有 TypeScript 类型,供 API、组件、状态管理使用。
2. 所有类型都基于数据库字段注释生成,确保与后端数据结构完全一致。
3. 使用 `interface` 定义,支持扩展和继承。
4. 时间字段统一为 string 类型,因为后端返回 ISO 格式字符串(如 "2024-07-05T10:30:00")。
5. 本文件为 Freemarker 模板,生成时会自动替换 ${} 中的变量。
6. 请勿手动修改生成的文件,下次生成将覆盖!
=================================================================================================
-->
/**
* ${table.comment!""} 实体类型定义
*
* <p>此类型定义了 ${table.comment!""} 的所有业务字段,用于前后端数据交互。</p>
* <p>所有字段均来源于数据库表 ${table.name} 的结构。</p>
*
* @author ${author}
* @date ${now?string("yyyy-MM-dd")}
*/
export interface ${entity}Type {
<#list table.fields as field>
<#-- 字段注释:从数据库注释获取,优先显示中文 -->
<#if field.comment??>
/**
* ${field.comment!""}
*/
</#if>
<#-- 类型映射规则 -->
<#-- 字符串 -->
<#if field.type == "String">
${field.name}: string;
<#-- 数字(Long, Integer, Short, Byte, Double, Float) -->
<#elseif field.type == "Long" || field.type == "Integer" || field.type == "Short" || field.type == "Byte" || field.type == "Double" || field.type == "Float">
${field.name}: number;
<#-- 布尔值 -->
<#elseif field.type == "Boolean">
${field.name}: boolean;
<#-- 时间类型(Date, LocalDateTime, Timestamp) -->
<#elseif field.type == "Date" || field.type == "LocalDateTime" || field.type == "Timestamp">
${field.name}: string; // 后端返回 ISO 格式字符串,如 "2024-07-05T10:30:00"
<#-- 其他未知类型 -->
<#else>
${field.name}: any; // 未知类型兜底,建议在数据库中明确类型
</#if>
</#list>
}
<#-- 逻辑删除标记类型 -->
<#if table.logicDeleteField??>
/**
* ${table.comment!""} 带逻辑删除标记的类型
*
* <p>此类型用于需要处理软删除状态的场景,如列表查询时包含已删除记录。</p>
*
* @author ${author}
* @date ${now?string("yyyy-MM-dd")}
*/
export type ${entity}WithDeletedType = ${entity}Type & { ${table.logicDeleteField.name}: number };
</#if>
<#-- 状态枚举类型(示例) -->
<#-- 你可以根据数据库中状态字段(如 status)的值来定义枚举 -->
<#-- 示例:如果表中有一个字段 status,值为 1=启用, 0=禁用 -->
<#-- 你可以在模板中添加逻辑判断,此处为简化示例 -->
<#-- 若需要更智能的枚举生成,请扩展模板逻辑 -->
export type ${entity}Status = 'active' | 'inactive'; // 示例:启用/禁用
<#-- 以下为预留扩展类型示例(请根据业务手动添加) -->
<#-- /**
<#-- * ${table.comment!""} 创建请求参数类型
<#-- * 用于新增时,仅包含必要字段
<#-- */
<#-- export interface ${entity}CreateRequest {
<#-- username: string;
<#-- email: string;
<#-- } -->
<#--
<#-- /**
<#-- * ${table.comment!""} 更新请求参数类型
<#-- * 用于更新时,所有字段可选
<#-- */
<#-- export interface ${entity}UpdateRequest extends Partial<${entity}Type> {} -->
✅ 模板说明(关键点)
| 特性 | 说明 |
|---|---|
| 精确字段映射 | 数据库 VARCHAR → string,BIGINT → number,DATETIME → string,类型安全。 |
Omit<..., 'id'> 支持 | 为 api.ts.ftl 中的 create() 方法提供类型支持,前端传参无误。 |
${entity}WithDeletedType | 核心设计!区分“仅有效数据”和“含删除标记数据”,避免业务逻辑混乱。 |
时间字段为 string | 最佳实践!后端返回 ISO 字符串,前端用 new Date() 解析,避免时区问题。 |
| 枚举类型预留 | 提供 status 枚举示例,强烈建议为每个状态字段定义枚举,提升可读性。 |
| 扩展预留 | 预留了 CreateRequest、UpdateRequest 示例,引导开发者使用请求类型分离。 |
| 注释完整 | 每个类型都有中文说明,明确用途和使用场景。 |
✅ 三、配套:page.types.ts(分页类型定义)
建议:在项目中创建一个全局的
types/page.types.ts文件,避免每个 API 文件重复定义。
<#--
=================================================================================================
MyBatis-Plus 代码生成器 - 分页类型定义模板 (全局)
=================================================================================================
作者:CodeGenWeb
生成时间:${now?string("yyyy-MM-dd HH:mm:ss")}
说明:
1. 本文件定义了统一的分页响应类型,供所有 API 模块复用。
2. 与后端 MyBatis-Plus 的 IPage<T> 结构完全一致。
3. 请将此文件放在项目公共类型目录下,如:src/types/page.types.ts
=================================================================================================
-->
/**
* 分页结果通用类型
*
* <p>与后端 MyBatis-Plus 的 IPage<T> 结构完全一致。</p>
*
* @author ${author}
* @date ${now?string("yyyy-MM-dd")}
*/
export interface PageResult<T> {
records: T[]; // 当前页的数据列表
total: number; // 总记录数
current: number; // 当前页码(从 1 开始)
size: number; // 每页大小
pages: number; // 总页数
}
✅ 在
api.ts.ftl中直接引用:import { PageResult } from '@/types/page.types';
✅ 最终效果与最佳实践
| 场景 | 前端代码示例 | 类型安全保障 |
|---|---|---|
| 获取分页列表 | const res = await userApi.list({current: 1, size: 10}); | res.data.records 是 UserType[],res.data.total 是 number |
| 新增用户 | await userApi.create({username: "张三", email: "zhangsan@example.com"}); | 编译器报错:id 不能传入 |
| 更新用户 | await userApi.update({id: 1, username: "李四", email: "lisi@example.com"}); | 必须传入 id,否则报错 |
| 获取详情 | const user = await userApi.get(1); | user.data 是 UserType,属性自动补全 |
| 状态判断 | if (user.status === 'active') { ... } | user.status 类型为 `‘active’ |
✅ 企业级建议
| 建议 | 说明 |
|---|---|
所有 API 文件放在 src/api/ 目录 | src/api/user.api.ts, src/api/product.api.ts |
所有类型文件放在 src/types/ 目录 | src/types/user.types.ts, src/types/page.types.ts |
使用 Partial<T> 定义更新请求 | 如 export type UserUpdateRequest = Partial<UserType> |
| 为枚举字段定义类型 | 如 `export type UserStatus = ‘active’ |
使用 axios.interceptors 统一处理 token | 在 api/axios.ts 中封装,避免每个 API 文件重复写 |
| 集成 Swagger 生成前端类型 | 可选:使用 swagger-typescript-api 工具自动生成,但本方案更轻量可控 |
✅ 总结
“前端的类型安全,不是靠运气,而是靠规范。”
你已拥有:
user.api.ts:安全、语义清晰、类型精确的 API 调用模块。user.types.ts:与数据库 100% 一致的 TypeScript 类型定义。page.types.ts:全局复用的分页响应类型。
✅ 复制此模板,你的前端项目将拥有业界标准的 API + 类型系统。
✅ 从此,你的团队不再为“接口字段名拼错了”、“传了 id 但后端报错”、“状态值写错了”而 Debug。
✅ 你的代码,就是最好的文档。
296

被折叠的 条评论
为什么被折叠?



