docs(plop): 更新 README 文档
新增内容: - 服务类型说明(CrudService/RelationCrudService/ManyToManyCrudService) - 关系配置 DSL 语法(一对多/多对一/多对多) - 种子脚本生成和执行说明 - 权限控制说明(@RequirePermission + PermissionGuard) - 新增模板文件(page.hbs、module-seed.hbs) - Helpers 完整列表(含 openBrace/closeBrace) - FAQ 扩展(QueryDto 生成、JSX 花括号、种子脚本) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
172
plop/README.md
172
plop/README.md
@@ -1,6 +1,6 @@
|
|||||||
# CRUD 代码生成器
|
# CRUD 代码生成器
|
||||||
|
|
||||||
基于 Plop.js 的全栈 CRUD 代码生成器,支持一键生成后端模块、前端模块、共享类型和 Prisma Model。
|
基于 Plop.js 的全栈 CRUD 代码生成器,支持一键生成后端模块、前端模块、共享类型、Prisma Model 和菜单/权限种子脚本。
|
||||||
|
|
||||||
## 快速开始
|
## 快速开始
|
||||||
|
|
||||||
@@ -16,12 +16,24 @@ pnpm generate
|
|||||||
|------|------|------|------|
|
|------|------|------|------|
|
||||||
| 1 | 模块名称 | 英文,小写开头 | `product` |
|
| 1 | 模块名称 | 英文,小写开头 | `product` |
|
||||||
| 2 | 模块中文名 | 用于注释和 API 标签 | `产品` |
|
| 2 | 模块中文名 | 用于注释和 API 标签 | `产品` |
|
||||||
| 3 | 复数名称 | API 路径和表名 | `products` |
|
| 3 | 生成目标 | 多选:后端/前端/共享类型/Prisma/种子脚本 | 全选 |
|
||||||
| 4 | 生成目标 | 多选:后端/前端/共享类型/Prisma | 全选 |
|
| 4 | 服务类型 | 单表/带关联/多对多 | `CrudService` |
|
||||||
| 5 | 软删除 | 是否启用软删除 | `Yes` |
|
| 5 | 软删除 | 是否启用软删除 | `Yes` |
|
||||||
| 6 | 字段定义 | DSL 语法定义字段 | 见下方 |
|
| 6 | 字段定义 | DSL 语法定义字段 | 见下方 |
|
||||||
| 7 | 搜索字段 | 选择可搜索的字段 | `name, status` |
|
| 7 | 关系配置 | 一对多/多对一/多对多关系 | 交互式配置 |
|
||||||
| 8 | 分页配置 | 默认/最大分页、排序 | `20/100/createdAt/desc` |
|
| 8 | 搜索字段 | 选择可搜索的字段 | `name, status` |
|
||||||
|
| 9 | 分页配置 | 默认/最大分页、排序 | `20/100/createdAt/desc` |
|
||||||
|
| 10 | 菜单配置 | 图标、排序(生成种子脚本时) | `Users / 50` |
|
||||||
|
|
||||||
|
## 服务类型
|
||||||
|
|
||||||
|
生成器支持三种服务类型,根据模块复杂度选择:
|
||||||
|
|
||||||
|
| 类型 | 说明 | 适用场景 |
|
||||||
|
|------|------|----------|
|
||||||
|
| `CrudService` | 单表 CRUD | 简单实体,无关联关系 |
|
||||||
|
| `RelationCrudService` | 带关联查询 | 有外键关联,需要返回关联数据 |
|
||||||
|
| `ManyToManyCrudService` | 多对多关系 | 需要管理多对多关系(如班级-教师) |
|
||||||
|
|
||||||
## 字段 DSL 语法
|
## 字段 DSL 语法
|
||||||
|
|
||||||
@@ -91,6 +103,47 @@ isActive:boolean 是否激活 "true"
|
|||||||
publishedAt:datetime? 发布时间 "2026-01-16T10:00:00Z"
|
publishedAt:datetime? 发布时间 "2026-01-16T10:00:00Z"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 关系配置
|
||||||
|
|
||||||
|
### 一对多关系
|
||||||
|
|
||||||
|
新模型包含多个目标模型(如:宿舍包含多个学生)
|
||||||
|
|
||||||
|
```
|
||||||
|
# 格式: 关系名:目标模型 [optional]
|
||||||
|
students:Student optional
|
||||||
|
```
|
||||||
|
|
||||||
|
### 多对一关系
|
||||||
|
|
||||||
|
新模型属于一个目标模型(如:成绩属于学生)
|
||||||
|
|
||||||
|
```
|
||||||
|
# 格式: 关联名:目标模型 [optional]
|
||||||
|
student:Student
|
||||||
|
class:Class optional
|
||||||
|
```
|
||||||
|
|
||||||
|
### 多对多关系
|
||||||
|
|
||||||
|
多对多关联(如:班级-教师)
|
||||||
|
|
||||||
|
```
|
||||||
|
# 格式: 关系名:中间表:外键:目标键:目标模型 字段1,字段2,...
|
||||||
|
teachers:ClassTeacher:classId:teacherId:Teacher id,name,teacherNo
|
||||||
|
```
|
||||||
|
|
||||||
|
### 查询关联配置
|
||||||
|
|
||||||
|
配置 API 响应返回哪些关联字段:
|
||||||
|
|
||||||
|
```
|
||||||
|
# 格式: 关联名:目标模型 字段1,字段2,... [noList]
|
||||||
|
# noList: 不在列表中显示(仅详情显示)
|
||||||
|
class:Class id,code,name
|
||||||
|
students:Student id,name,studentNo noList
|
||||||
|
```
|
||||||
|
|
||||||
## 生成的文件
|
## 生成的文件
|
||||||
|
|
||||||
### 后端 (apps/api)
|
### 后端 (apps/api)
|
||||||
@@ -98,19 +151,28 @@ publishedAt:datetime? 发布时间 "2026-01-16T10:00:00Z"
|
|||||||
| 文件 | 说明 |
|
| 文件 | 说明 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| `src/{module}/dto/{module}.dto.ts` | CreateDto、UpdateDto、ResponseDto、QueryDto |
|
| `src/{module}/dto/{module}.dto.ts` | CreateDto、UpdateDto、ResponseDto、QueryDto |
|
||||||
| `src/{module}/{module}.service.ts` | CRUD 服务,继承 CrudService |
|
| `src/{module}/{module}.service.ts` | CRUD 服务,继承对应基类 |
|
||||||
| `src/{module}/{module}.controller.ts` | RESTful 控制器,含 Swagger 文档 |
|
| `src/{module}/{module}.controller.ts` | RESTful 控制器,含 Swagger 文档和权限控制 |
|
||||||
| `src/{module}/{module}.module.ts` | NestJS 模块 |
|
| `src/{module}/{module}.module.ts` | NestJS 模块 |
|
||||||
|
|
||||||
|
**权限控制**:生成的 Controller 使用 `@RequirePermission()` 装饰器控制接口权限:
|
||||||
|
- `{module}:create` - 创建权限
|
||||||
|
- `{module}:read` - 查看权限
|
||||||
|
- `{module}:update` - 更新权限
|
||||||
|
- `{module}:delete` - 删除权限
|
||||||
|
|
||||||
### 前端 (apps/web)
|
### 前端 (apps/web)
|
||||||
|
|
||||||
| 文件 | 说明 |
|
| 文件 | 说明 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| `src/services/{module}.service.ts` | API 调用封装 |
|
| `src/services/{module}.service.ts` | API 调用封装 |
|
||||||
| `src/hooks/use{Module}s.ts` | TanStack Query hooks |
|
| `src/hooks/use{Module}s.ts` | TanStack Query hooks |
|
||||||
| `src/components/{module}s/{Module}sTable.tsx` | 数据表格组件 |
|
| `src/components/{module}s/{Module}sTable.tsx` | 数据表格组件(含错误状态处理) |
|
||||||
| `src/components/{module}s/{Module}CreateDialog.tsx` | 创建对话框 |
|
| `src/components/{module}s/{Module}CreateDialog.tsx` | 创建对话框 |
|
||||||
| `src/components/{module}s/{Module}EditDialog.tsx` | 编辑对话框 |
|
| `src/components/{module}s/{Module}EditDialog.tsx` | 编辑对话框 |
|
||||||
|
| `src/app/(dashboard)/{modules}/page.tsx` | 页面组件 |
|
||||||
|
|
||||||
|
**权限控制**:生成的前端组件使用 `<PermissionGuard>` 包裹需要权限的操作按钮。
|
||||||
|
|
||||||
### 共享类型 (packages/shared)
|
### 共享类型 (packages/shared)
|
||||||
|
|
||||||
@@ -124,6 +186,21 @@ publishedAt:datetime? 发布时间 "2026-01-16T10:00:00Z"
|
|||||||
|------|------|
|
|------|------|
|
||||||
| `prisma/schema.prisma` | 追加模型定义 |
|
| `prisma/schema.prisma` | 追加模型定义 |
|
||||||
|
|
||||||
|
### 种子脚本 (apps/api/prisma/seeds)
|
||||||
|
|
||||||
|
| 文件 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `{module}.seed.ts` | 菜单和权限种子数据 |
|
||||||
|
|
||||||
|
生成的种子脚本包含:
|
||||||
|
- 4 个 CRUD 权限(create/read/update/delete)
|
||||||
|
- 1 个菜单项(可配置图标和排序)
|
||||||
|
|
||||||
|
可独立运行或被主 seed.ts 导入:
|
||||||
|
```bash
|
||||||
|
cd apps/api && npx ts-node prisma/seeds/{module}.seed.ts
|
||||||
|
```
|
||||||
|
|
||||||
## 自动集成
|
## 自动集成
|
||||||
|
|
||||||
生成器会自动修改以下文件完成集成:
|
生成器会自动修改以下文件完成集成:
|
||||||
@@ -141,7 +218,10 @@ publishedAt:datetime? 发布时间 "2026-01-16T10:00:00Z"
|
|||||||
# 1. 同步数据库
|
# 1. 同步数据库
|
||||||
pnpm db:generate && pnpm db:push
|
pnpm db:generate && pnpm db:push
|
||||||
|
|
||||||
# 2. 重启开发服务器
|
# 2. 执行种子脚本(如果生成了)
|
||||||
|
cd apps/api && npx ts-node prisma/seeds/{module}.seed.ts
|
||||||
|
|
||||||
|
# 3. 重启开发服务器
|
||||||
pnpm dev
|
pnpm dev
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -154,8 +234,8 @@ $ pnpm generate
|
|||||||
|
|
||||||
? 模块名称(英文,如 product): product
|
? 模块名称(英文,如 product): product
|
||||||
? 模块中文名(如 产品): 产品
|
? 模块中文名(如 产品): 产品
|
||||||
? 复数名称(如 products): products
|
? 选择要生成的模块: 后端 (NestJS), 前端 (Next.js), 共享类型, Prisma Model, 菜单/权限种子脚本
|
||||||
? 选择要生成的模块: 后端 (NestJS), 前端 (Next.js), 共享类型, Prisma Model
|
? 选择服务类型: CrudService(单表 CRUD)
|
||||||
? 是否启用软删除? Yes
|
? 是否启用软删除? Yes
|
||||||
? 定义字段:
|
? 定义字段:
|
||||||
name:string 名称 "示例产品" min:2 max:100
|
name:string 名称 "示例产品" min:2 max:100
|
||||||
@@ -168,6 +248,8 @@ $ pnpm generate
|
|||||||
? 最大分页大小: 100
|
? 最大分页大小: 100
|
||||||
? 默认排序字段: createdAt
|
? 默认排序字段: createdAt
|
||||||
? 默认排序方向: desc
|
? 默认排序方向: desc
|
||||||
|
? 菜单图标名称: Package
|
||||||
|
? 菜单排序值: 50
|
||||||
|
|
||||||
✔ 生成 apps/api/src/product/dto/product.dto.ts
|
✔ 生成 apps/api/src/product/dto/product.dto.ts
|
||||||
✔ 生成 apps/api/src/product/product.service.ts
|
✔ 生成 apps/api/src/product/product.service.ts
|
||||||
@@ -178,6 +260,8 @@ $ pnpm generate
|
|||||||
✔ 生成 apps/web/src/components/products/ProductsTable.tsx
|
✔ 生成 apps/web/src/components/products/ProductsTable.tsx
|
||||||
✔ 生成 apps/web/src/components/products/ProductCreateDialog.tsx
|
✔ 生成 apps/web/src/components/products/ProductCreateDialog.tsx
|
||||||
✔ 生成 apps/web/src/components/products/ProductEditDialog.tsx
|
✔ 生成 apps/web/src/components/products/ProductEditDialog.tsx
|
||||||
|
✔ 生成 apps/web/src/app/(dashboard)/products/page.tsx
|
||||||
|
✔ 生成 apps/api/prisma/seeds/product.seed.ts
|
||||||
✔ 生成 packages/shared/src/types/product.ts
|
✔ 生成 packages/shared/src/types/product.ts
|
||||||
✔ 修改 apps/api/prisma/schema.prisma
|
✔ 修改 apps/api/prisma/schema.prisma
|
||||||
✔ 修改 apps/api/src/app.module.ts
|
✔ 修改 apps/api/src/app.module.ts
|
||||||
@@ -194,12 +278,15 @@ $ pnpm generate
|
|||||||
plop/
|
plop/
|
||||||
├── plopfile.ts # 主配置入口
|
├── plopfile.ts # 主配置入口
|
||||||
├── package.json # ESM 模块配置
|
├── package.json # ESM 模块配置
|
||||||
|
├── README.md # 本文档
|
||||||
├── generators/
|
├── generators/
|
||||||
│ └── crud.ts # CRUD 生成器逻辑
|
│ └── crud.ts # CRUD 生成器逻辑
|
||||||
├── helpers/
|
├── helpers/
|
||||||
│ └── index.ts # Handlebars helpers
|
│ └── index.ts # Handlebars helpers
|
||||||
├── utils/
|
├── utils/
|
||||||
│ └── field-parser.ts # 字段 DSL 解析器
|
│ ├── field-parser.ts # 字段 DSL 解析器
|
||||||
|
│ ├── relation-parser.ts # 关系配置解析器
|
||||||
|
│ └── schema-parser.ts # Prisma schema 解析器
|
||||||
└── templates/
|
└── templates/
|
||||||
├── api/ # 后端模板
|
├── api/ # 后端模板
|
||||||
│ ├── dto.hbs
|
│ ├── dto.hbs
|
||||||
@@ -211,11 +298,14 @@ plop/
|
|||||||
│ ├── hooks.hbs
|
│ ├── hooks.hbs
|
||||||
│ ├── table.hbs
|
│ ├── table.hbs
|
||||||
│ ├── create-dialog.hbs
|
│ ├── create-dialog.hbs
|
||||||
│ └── edit-dialog.hbs
|
│ ├── edit-dialog.hbs
|
||||||
|
│ └── page.hbs
|
||||||
├── shared/ # 共享类型模板
|
├── shared/ # 共享类型模板
|
||||||
│ └── types.hbs
|
│ └── types.hbs
|
||||||
└── prisma/ # Prisma 模板
|
├── prisma/ # Prisma 模板
|
||||||
└── model.hbs
|
│ └── model.hbs
|
||||||
|
└── seed/ # 种子脚本模板
|
||||||
|
└── module-seed.hbs
|
||||||
```
|
```
|
||||||
|
|
||||||
## 扩展模板
|
## 扩展模板
|
||||||
@@ -232,9 +322,36 @@ plop/
|
|||||||
| `snakeCase` | 转 snake_case | `productItem` → `product_item` |
|
| `snakeCase` | 转 snake_case | `productItem` → `product_item` |
|
||||||
| `constantCase` | 转 CONSTANT_CASE | `product` → `PRODUCT` |
|
| `constantCase` | 转 CONSTANT_CASE | `product` → `PRODUCT` |
|
||||||
| `tsType` | 获取 TS 类型 | `string`, `number` 等 |
|
| `tsType` | 获取 TS 类型 | `string`, `number` 等 |
|
||||||
|
| `tsResponseType` | 获取响应 TS 类型 | `string`, `number` 等 |
|
||||||
| `prismaType` | 获取 Prisma 类型 | `String`, `Float` 等 |
|
| `prismaType` | 获取 Prisma 类型 | `String`, `Float` 等 |
|
||||||
| `zodValidation` | 生成 Zod 验证 | `z.string().min(2)` |
|
| `zodValidation` | 生成 Zod 验证 | `z.string().min(2)` |
|
||||||
| `formControl` | 生成表单控件 | `<Input .../>` |
|
| `formControl` | 生成表单控件 | `<Input .../>` |
|
||||||
|
| `cellRenderer` | 生成表格单元格渲染 | `<Badge .../>` |
|
||||||
|
| `validationDecorators` | 生成 class-validator 装饰器 | `@IsString()` |
|
||||||
|
| `formattedExample` | 格式化示例值 | `"text"` / `123` |
|
||||||
|
| `openBrace` | 输出 `{` | 用于 JSX 模板 |
|
||||||
|
| `closeBrace` | 输出 `}` | 用于 JSX 模板 |
|
||||||
|
|
||||||
|
### 逻辑 Helpers
|
||||||
|
|
||||||
|
| Helper | 说明 |
|
||||||
|
|--------|------|
|
||||||
|
| `eq a b` | 相等判断 |
|
||||||
|
| `ne a b` | 不等判断 |
|
||||||
|
| `and a b` | 逻辑与 |
|
||||||
|
| `or a b` | 逻辑或 |
|
||||||
|
| `not a` | 逻辑非 |
|
||||||
|
| `includes arr value` | 数组包含 |
|
||||||
|
|
||||||
|
### 条件 Helpers
|
||||||
|
|
||||||
|
| Helper | 说明 |
|
||||||
|
|--------|------|
|
||||||
|
| `hasValidation fields` | 字段是否有验证规则 |
|
||||||
|
| `hasTransform fields` | 字段是否需要转换(date/datetime) |
|
||||||
|
| `hasTextarea fields` | 字段是否有长文本 |
|
||||||
|
| `hasSelect fields` | 字段是否有枚举 |
|
||||||
|
| `hasSwitch fields` | 字段是否有布尔值 |
|
||||||
|
|
||||||
## 常见问题
|
## 常见问题
|
||||||
|
|
||||||
@@ -249,3 +366,28 @@ plop/
|
|||||||
### Q: 生成后 TypeScript 报错?
|
### Q: 生成后 TypeScript 报错?
|
||||||
|
|
||||||
确保运行 `pnpm db:generate` 更新 Prisma Client 类型。
|
确保运行 `pnpm db:generate` 更新 Prisma Client 类型。
|
||||||
|
|
||||||
|
### Q: 如何在 JSX 模板中输出花括号?
|
||||||
|
|
||||||
|
使用 `{{openBrace}}` 和 `{{closeBrace}}` helpers:
|
||||||
|
```handlebars
|
||||||
|
<span>{{openBrace}}item.id{{closeBrace}}</span>
|
||||||
|
```
|
||||||
|
输出:`<span>{item.id}</span>`
|
||||||
|
|
||||||
|
### Q: QueryDto 什么时候会被生成?
|
||||||
|
|
||||||
|
无论是否选择搜索字段,QueryDto 都会被生成。这便于后续扩展查询参数。`hasQueryDto` 标志仅控制 Controller 中是否解构查询字段。
|
||||||
|
|
||||||
|
### Q: 如何执行种子脚本?
|
||||||
|
|
||||||
|
生成的种子脚本支持独立运行:
|
||||||
|
```bash
|
||||||
|
cd apps/api && npx ts-node prisma/seeds/{module}.seed.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
或在主 `seed.ts` 中导入调用:
|
||||||
|
```typescript
|
||||||
|
import { seedProductModule } from './seeds/product.seed';
|
||||||
|
await seedProductModule(prisma);
|
||||||
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user