签入版本

This commit is contained in:
2026-10-08 17:50:29 +08:00
parent 809750b47e
commit e213a9b31d
444 changed files with 39366 additions and 37533 deletions
@@ -1,7 +1,7 @@
# 10.2 CLI 命令行使用
:::tip 概述
代码生成器提供 CLI 命令行方式,适合批量生成或 CI/CD 集成场景。
代码生成器提供 CLI 命令行方式,适合批量生成或 CI/CD 集成场景。CLI 底层复用 `GeneratorLogic` 与 `GeneratorService`——与 Web 端完全相同的能力,且不受权限与演示模式限制。
:::
## 命令格式
@@ -10,87 +10,99 @@
# =============================================================================
# 代码生成器 CLI 命令
# =============================================================================
# php think generate:code → ThinkPHP 命令行调用代码生成器
# <表名> → 数据库表名(不含表前缀)
# |<注释> → 可选,用 | 分隔表注释(用于生成菜单标题)
# php artisan generate:code → Artisan 命令行调用代码生成器
# <表名> → 数据库表名(含或不含表前缀均可)
# |<注释> → 可选,用 | 分隔表注释(用于生成菜单标题)
# =============================================================================
php think generate:code <表名>[|注释]
php artisan generate:code <表名>[|注释]
```
## 参数说明
| 参数 | 必填 | 说明 | 示例 |
|------|------|------|------|
| `table`(参数) | 否 | 数据库表名,多表用逗号分隔(需配合 `--batch`) | `think_example` 或 `think_example\|案例演示` |
| `table`(参数) | 否 | 数据库表名,多表用逗号分隔(需配合 `--batch`) | `laravel_example` 或 `laravel_example\|案例演示` |
| `--config` / `-c` | 否 | 指定已有的 JSON 配置文件路径,跳过表解析 | `--config config.json` |
| `--output` / `-o` | 否 | 将配置导出为 JSON 文件 | `--output config.json` |
| `--dry-run` / `-d` | 否 | 仅预览,不写入代码文件 | `--dry-run` |
| `--type` / `-t` | 否 | 模板类型:`ui`(默认)或 `ui2` | `--type ui2` |
| `--overwrite` / `-w` | 否 | 覆盖已存在的文件 | `--overwrite` |
| `--type` / `-t` | 否 | 模板类型:`ui`(默认)或 `ui2`(树形) | `--type ui2` |
| `--overwrite` / `-w` | 否 | 覆盖已存在的文件(**高危,谨慎使用**) | `--overwrite` |
| `--no-menu` | 否 | 不生成菜单 | `--no-menu` |
| `--batch` / `-b` | 否 | 批量模式,table 参数支持逗号分隔多表 | `--batch` |
(完整签名见 `app/Console/Commands/GeneratorCommand.php`。)
## 使用示例
```bash
# -----------------------------------------------------------------------------
# 列出所有可生成的数据库表
# -----------------------------------------------------------------------------
php think generate:code
php artisan generate:code
# -----------------------------------------------------------------------------
# 预览生成代码(不写入文件,仅输出到终端)
# -----------------------------------------------------------------------------
php think generate:code think_example --dry-run
php artisan generate:code laravel_example --dry-run
# -----------------------------------------------------------------------------
# 生成案例演示模块(带表注释)
# 生成案例演示模块(带表注释,注释用于菜单标题)
# -----------------------------------------------------------------------------
php think generate:code think_example|案例演示
php artisan generate:code "laravel_example|案例演示"
# -----------------------------------------------------------------------------
# 生成文章管理模块(覆盖已有文件)
# -----------------------------------------------------------------------------
php think generate:code think_article|文章管理 --overwrite
php artisan generate:code "laravel_article|文章管理" --overwrite
# -----------------------------------------------------------------------------
# 生成用户管理模块(使用 ui2 树形模板)
# 生成部门管理模块(使用 ui2 树形模板)
# -----------------------------------------------------------------------------
php think generate:code think_user|用户管理 --type ui2
php artisan generate:code "laravel_dept|部门管理" --type ui2
# -----------------------------------------------------------------------------
# 批量生成多张表
# -----------------------------------------------------------------------------
php think generate:code think_order,think_product,think_category --batch
php artisan generate:code laravel_order,laravel_product,laravel_category --batch
# -----------------------------------------------------------------------------
# 导出配置为 JSON 文件
# -----------------------------------------------------------------------------
php think generate:code think_example --output config.json
php artisan generate:code laravel_example --output config.json
# -----------------------------------------------------------------------------
# 从配置文件生成(跳过表解析)
# -----------------------------------------------------------------------------
php think generate:code --config config.json
php artisan generate:code --config config.json
```
:::tip Windows 下参数含中文或竖线
`表名|注释` 中的 `|` 在部分 shell 中是管道符,请用引号包裹整个参数:`php artisan generate:code "laravel_example|案例演示"`。
:::
## 生成的文件
执行命令后,自动生成以下文件:
```text
app/controller/ExampleController.php # 控制器(定义 API 接口方法)
app/logic/ExampleLogic.php # 业务逻辑(继承 BaseLogic 获得 CRUD)
app/model/Example.php # 数据模型(继承 BaseModel 获得软删除/时间戳)
app/validate/ExampleValidate.php # 验证器(字段验证规则)
ui/src/api/tool/example.ts # 前端 API 接口定义
ui/src/views/tool/example/index.vue # 列表页(分页表格 + 搜索)
ui/src/views/tool/example/edit.vue # 编辑弹窗(新增/编辑表单)
ui/src/views/tool/example/detail.vue # 详情弹窗(只读展示)
ui/src/views/tool/example/columns.ts # 表格列配置(仅 ui/ 模板)
ui/src/views/tool/example/querySchemas.ts # 查询表单配置(仅 ui/ 模板)
app/Http/Controllers/Api/ExampleController.php # 控制器(注解 + 快捷方法)
app/Services/ExampleService.php # 业务服务(属性配置 + 钩子骨架)
app/Models/Example.php # 数据模型(双 Trait + fillable)
app/Http/Requests/ExampleRequest.php # FormRequest(字段验证规则)
ui/src/api/tool/example.ts # 前端 API 接口定义
ui/src/views/tool/example/index.vue # 列表页(分页表格 + 搜索)
ui/src/views/tool/example/edit.vue # 编辑弹窗(新增/编辑表单)
ui/src/views/tool/example/detail.vue # 详情弹窗(只读展示)
ui/src/views/tool/example/columns.ts # 表格列配置(仅 ui/ 模板)
ui/src/views/tool/example/querySchemas.ts # 查询表单配置(仅 ui/ 模板)
```
同时自动完成:
- `routes/api.php` 插入 CRUD 路由组(9 条标准路由)
- `laravel_menu` 创建菜单与权限节点(挂载 `GENERATOR_PARENT_MENU_ID` 指定的父菜单下)
- `laravel_dict` / `laravel_dict_item` 创建关联字典(幂等,不重复)
## 生成后操作
生成完成后,路由、菜单、字典均已自动创建。通常只需:
@@ -99,17 +111,17 @@ ui/src/views/tool/example/querySchemas.ts # 查询表单配置(仅 ui/ 模板
# -----------------------------------------------------------------------------
# 第 1 步:检查生成的文件是否正确
# -----------------------------------------------------------------------------
ls app/controller/Example* # 控制器
ls app/logic/Example* # 业务逻辑
ls app/model/Example* # 数据模型
ls app/validate/Example* # 验证器
ls ui/src/api/tool/example* # 前端 API
ls ui/src/views/tool/example/ # 前端页面
ls app/Http/Controllers/Api/Example* # 控制器
ls app/Services/Example* # 业务服务
ls app/Models/Example* # 数据模型
ls app/Http/Requests/Example* # FormRequest
ls ui/src/api/tool/example* # 前端 API
ls ui/src/views/tool/example/ # 前端页面
# -----------------------------------------------------------------------------
# 第 2 步:根据业务需求调整 Logic 属性配置
# 第 2 步:根据业务需求调整 Service 属性配置
# -----------------------------------------------------------------------------
# 可调整:pageLikeFields、pageEqFields、serializeMaps 等
# 可调整:pageLikeFields、pageEqFields、serializeMaps、uniqueFields 等
# -----------------------------------------------------------------------------
# 第 3 步:在菜单管理中调整菜单排序和图标
@@ -1,19 +1,19 @@
# 10.5 代码生成引擎
:::tip 概述
代码生成引擎基于模板文件,将表结构信息渲染为实际代码文件。渲染使用 PHP 原生 `include` + `extract` 机制,模板变量由 `GeneratorService::buildTemplateVars()` 统一构建。
代码生成引擎基于模板文件,将表结构信息渲染为实际代码文件。渲染使用 PHP 原生 `include` + `extract` 机制,模板变量由 `GeneratorService::buildTemplateVars()` 统一构建,预览与生成的编排由 `GeneratorLogic` 负责。
:::
## 生成文件清单
### 后端文件
### 后端文件(4 个文件,输出路径见 `config/generator.php` 的 `backend_outputs`)
| 文件 | 模板 | 说明 |
|------|------|------|
| `app/controller/{Name}Controller.php` | controller.php.tpl | 控制器,含分页/列表/详情/增删改/批量删除/导入/导出 |
| `app/logic/{Name}Logic.php` | logic.php.tpl | 业务逻辑,含属性配置、serializeMaps、导入导出方法 |
| `app/model/{Name}.php` | model.php.tpl | 数据模型,含软删除、审计字段自动填充;树形结构额外生成 `children()` 自关联 |
| `app/validate/{Name}Validate.php` | validate.php.tpl | 验证器,按字段分析自动生成验证规则(camelCase 字段名、require 规则、add/update 场景) |
| `app/Http/Controllers/Api/{Name}Controller.php` | controller.php.tpl | 控制器,含分页/列表/详情/增删改/批量删除/导入/导出 |
| `app/Services/{Name}Service.php` | service.php.tpl | 业务服务,含属性配置、serializeMaps、导入导出方法 |
| `app/Models/{Name}.php` | model.php.tpl | 数据模型,含软删除(is_delete)、审计字段自动填充;树形结构额外生成 `children()` 自关联 |
| `app/Http/Requests/{Name}Request.php` | request.php.tpl | FormRequest 验证器,按字段分析自动生成验证规则(camelCase 字段名、required 规则、add/update 场景) |
### 前端文件(ui/ 普通列表模板 — 6 个文件)
@@ -36,13 +36,13 @@
| `ui/src/views/{app}/{name}/detail.vue` | detail.vue.tpl | 详情弹窗 |
:::info 模板选择
表结构中包含 `parent_id` 或 `pid` 字段时自动使用 `ui2/` 树形模板,否则使用 `ui/` 普通列表模板。树形模板不生成 `columns.ts` 和 `querySchemas.ts`。
表结构中包含 `parent_id` 或 `pid` 字段时自动切换为 `ui2/` 树形模板(`GeneratorService::buildTemplateVars()` 内检测,结果体现在 `is_tree` 变量),否则使用 `ui/` 普通列表模板。树形模板不生成 `columns.ts` 和 `querySchemas.ts`。
:::
## GeneratorService 核心方法
```php
// app/service/GeneratorService.php
// app/Services/GeneratorService.php
/**
* 获取可生成的表列表
@@ -50,57 +50,46 @@
* 读取当前数据库所有表,返回表名、注释、引擎、行数等信息。
* 支持 MySQL / PostgreSQL / SQL Server / Oracle / SQLite。
*/
public function page(array $params): array { ... }
public function getTableList(): array { ... }
/**
* 解析单表结构
*
* 读取列信息、主键、索引与表注释,输出字段归一化结果。
*/
public function parseTableStructure(string $tableName): array { ... }
/**
* 构建模板变量
*
* 解析表结构 → 字段分析 → 构建全部模板变量。
* 是生成引擎的核心,决定模板中可用的所有变量。
* 自动检测树形结构(parent_id / pid),切换 template_type 为 ui2。
*/
public function buildTemplateVars(string $tableName, string $moduleComment, string $templateType): array { ... }
public function buildTemplateVars(string $tableName, string $moduleComment = '', string $templateType = 'ui'): array { ... }
/**
* 渲染模板
*
* 使用 PHP extract + include 机制渲染 .tpl 文件。
* 渲染前预检:禁止短标签 <?= 和 UTF-8 BOM 头。
* 模板不存在或渲染抛错时,带模板文件名上下文重新抛出异常。
*/
public function renderTemplate(string $templatePath, array $vars): string { ... }
/**
* 预览生成代码(不写入文件)
*
* 返回所有待生成文件的渲染结果,供前端预览。
*/
public function preview(array $params): array { ... }
/**
* 生成代码(写入文件)
*
* 渲染模板 → 写入文件 → 创建菜单 → 创建字典 → 注册路由。
*/
public function generate(array $params): array { ... }
/**
* 批量生成
*
* 遍历多张表,逐表调用 generate。
*/
public function batchGenerate(array $params): array { ... }
/**
* 生成单个文件
*
* 检查文件是否存在(可跳过或覆盖)→ 创建目录 → 写入文件。
* 已存在且未开启覆盖 → skipped;目录不存在自动创建(0755)。
*/
public function generateFile(string $path, string $content, bool $overwrite): array { ... }
public function generateFile(string $path, string $content, bool $overwrite = false): array { ... }
/**
* 创建菜单和权限节点
*
* 自动创建主菜单(type=0)和标准权限节点(type=1):
* 在 GENERATOR_PARENT_MENU_ID 指定的父菜单下创建主菜单(type=0)
* 和 9 个标准权限节点(type=1):
* page / list / detail / add / update / delete / batchDelete / import / export。
* path 已存在则跳过(幂等),排序值按父级 max(sort) + 5 递增。
*/
public function createMenu(string $moduleComment, string $moduleName): array { ... }
@@ -115,12 +104,40 @@ public function createDictData(string $moduleComment, string $moduleName, array
/**
* 注册模块路由
*
* 在 route/app.php 中自动插入标准 CRUD 路由组(含中间件)。
* 在 routes/api.php 末尾的中间件组闭合 `});` 之前插入标准 CRUD 路由组。
* 分组注释取表注释(为空时回退模块名),幂等以路由路径判断。
* 树形结构不生成 page 路由。
*/
public function registerRoute(string $moduleName, bool $isTree): array { ... }
public function registerRoute(string $moduleName, string $controllerName, bool $isTree = false, string $moduleComment = ''): array { ... }
/**
* 获取生成的文件列表
*
* 返回后端与前端应生成的文件相对路径,供生成结果核对或展示。
*/
public function getGenerateFiles(string $moduleName, string $templateType = 'ui'): array { ... }
```
## GeneratorLogic 编排方法
```php
// app/Services/GeneratorLogic.php — 面向控制器的编排层
// 获取表列表(含分页参数处理)
public function getTableList(array $params = []): array { ... }
// 预览生成代码(不写入文件):渲染全部模板,返回内容 + 是否已存在
public function preview(array $params): array { ... }
// 生成代码(写入文件):写后端 → 写前端 → 创建菜单(可选)→ 创建字典 → 注册路由
public function generate(array $params): array { ... }
// 批量生成:逐表调用 generate,单表失败不中断整体
public function batchGenerate(array $params): array { ... }
```
`generate()` 参数:`tableNames`(必填)、`templateType`(ui/ui2,可省略)、`generateMenu`(默认 true)、`overwrite`(默认 false,不覆盖已存在文件)。
## 字段分析流程
```text
@@ -133,15 +150,16 @@ public function registerRoute(string $moduleName, bool $isTree): array { ... }
▼
属性分析(详见 10.4 表结构解析引擎)
│
├─ 图片字段 → 注释/字段名匹配 /图片|头像|logo|icon|image|avatar|photo/i 且类型为 varchar
├─ 图片字段 → 注释/字段名匹配 /图片|封面|头像|logo|icon|image|avatar|photo/i 且类型为 varchar
├─ 富文本 → 注释匹配 /内容|详情|正文/i 且类型为 text
├─ 多行文本 → 注释匹配 /备注|说明|描述|介绍|简介/i 且类型为 varchar
├─ 选项字段 → 注释中解析 "数字-文字" 格式(如 "1-启用 2-停用")
├─ 选项字段 → 注释中解析 "数字-文字" 格式(如 "1-正常 2-停用")
├─ 状态字段 → 字段名 status 且无选项时,自动补 0=正常 1=停用
├─ 模糊搜索 → 字段名匹配 /名称|标题|name|title|username/i 且类型为 varchar
├─ 唯一字段 → 字段名匹配 /编码|code|username|mobile|email/i
├─ 外键字段 → 字段名以 _id 或 _ids 结尾
└─ 排序字段 → 字段名为 sort
├─ 模糊搜索 → 字段名匹配 /名称|标题|name|title|username|realname/i 且类型为 varchar
├─ 唯一字段 → 字段名匹配 /编码|code|username|mobile|email|account/i
├─ 排序字段 → 字段名为 sort(标记 has_sort)
└─ 排除字段 → id / tenant_id / create_user / create_time / update_user / update_time / is_delete
不进表单与列表
▼
serialize_maps 构建
│ 有 choices 的字段自动生成映射:字段名 → "{模块名}_{字段名}"
@@ -149,41 +167,47 @@ serialize_maps 构建
▼
输出分组字段列表
│ $fields['all'] 全部字段
│ $fields['list'] 列表展示字段
│ $fields['form'] 表单字段
│ $fields['list'] 列表展示字段(text 长文本默认不进列表)
│ $fields['form'] 表单字段(全量生成规则,含 id → validate_rules / validate_rules_update)
│ $fields['filter'] 筛选字段(含模糊搜索和精确匹配)
│ $fields['file'] 文件/图片字段
│ $fields['file'] 单文件/图片字段
│ $fields['multi'] 多文件字段(逗号分隔的多条路径,如图集)
│ $fields['content'] 富文本字段
│ $fields['like'] 模糊搜索字段
│ $fields['eq'] 精确匹配字段(含选项字段)
│ $fields['unique'] 唯一字段
│ $fields['serialize_maps'] 枚举映射(字段 → 字典编码)
│ $fields['foreign_keys'] 外键字段
│ $fields['serialize_maps'] 枚举映射(字段 → 字典编码)
│ $fields['field_labels'] / casts / hidden 字段中文名 / 类型转换 / 敏感字段
│ $fields['validate_rules'] / validate_rules_update / validate_messages / validate_fields FormRequest 规则
```
## 渲染机制
```php
// 渲染原理(简化)
// 渲染原理(简化,见 GeneratorService::renderTemplate)
public function renderTemplate(string $templatePath, array $vars): string
{
// 预检:禁止短标签和 BOM
$tplContent = file_get_contents($templatePath);
if (preg_match('/<\?=(?!.*<\?php echo)/s', $tplContent)) {
throw new \Exception('模板包含短标签 <?= ,请改为 <?php echo');
if (! file_exists($templatePath)) {
throw new \Exception('模板文件不存在: '.$templatePath);
}
extract($vars); // 将变量注入当前作用域
ob_start(); // 开启输出缓冲
include $templatePath; // 执行模板文件
try {
include $templatePath; // 执行模板文件
} catch (\Throwable $e) {
ob_end_clean(); // 丢弃半成品输出
throw new \Exception('模板渲染失败 ['.basename($templatePath).'] '.$e->getMessage());
}
return ob_get_clean(); // 返回渲染结果
}
```
:::warning 模板编写规范
- 必须使用 `<?php echo ?>` 输出变量,不能使用短标签 `<?= ?>`
- 使用 `<?php echo ?>` 输出变量,与 `resources/templates/` 现有模板保持一致(现有模板均不含短标签)
- 文件编码必须为 UTF-8 无 BOM
- 控制结构(foreach/if)使用完整的 `<?php ?>` 标签
- 模板渲染异常会带文件名抛出,便于定位是哪张模板出错
:::
## 生成后自动操作
@@ -192,10 +216,15 @@ public function renderTemplate(string $templatePath, array $vars): string
| 操作 | 说明 | 幂等性 |
|------|------|--------|
| 写入文件 | 生成 Controller / Logic / Model / Validate / API / Vue 文件 | 文件已存在则跳过 |
| 创建菜单 | 在指定父菜单下创建主菜单和权限节点 | 路径已存在则跳过 |
| 写入文件 | 生成 Controller / Service / Model / Request / API / Vue 文件 | 文件已存在则跳过(除非 `overwrite=true`) |
| 创建菜单 | 在 `GENERATOR_PARENT_MENU_ID` 父菜单下创建主菜单和 9 个权限节点 | path 已存在则跳过 |
| 创建字典 | 为带选项的字段自动创建字典和字典项 | 字典编码已存在则跳过 |
| 注册路由 | 在 route/app.php 中插入 CRUD 路由组 | 路由组已存在则跳过 |
| 注册路由 | 在 `routes/api.php` 插入 CRUD 路由组(分组注释取表注释,如 `// --- 案例演示 ---`) | 同模块路由已存在则跳过 |
:::warning 生成后需手动生效
- 路由写入 `routes/api.php` 后,若生产环境执行过 `php artisan route:cache`,需重新执行 `php artisan route:cache`
- 菜单与权限写入数据库后,需重新登录或刷新前端动态菜单才能看到新菜单
:::
## 相关文档
@@ -1,27 +1,55 @@
# 10.7 配置文件详解
:::tip 概述
代码生成器通过 `.env` 和 Web 界面配置,主要控制生成行为和默认值。
代码生成器通过 `.env` 与 `config/generator.php` 配置菜单挂载、应用名、模板与输出路径;Web 界面配置单次生成的参数。
:::
## .env 配置
```ini
; 代码生成器配置
[GENERATOR]
; 父菜单ID(生成的菜单挂在此菜单下)
; 在菜单管理中查看目标父菜单的 ID
PARENT_MENU_ID = 189
# 代码生成器配置
# 父菜单ID(生成的菜单挂在此菜单下)
# 在菜单管理中查看目标父菜单的 ID
GENERATOR_PARENT_MENU_ID=189
; 应用名(路由前缀)
; 影响生成的菜单路径和前端目录
; 如:tool → /tool/xxx,system → /system/xxx
APP_NAME = tool
# 应用名(路由前缀、前端目录)
# 影响生成的菜单路径和前端目录
# 如:tool → 路由 /tool/xxx,前端 ui/src/api/tool/
GENERATOR_APP_NAME=tool
```
## config/generator.php(来自项目实际代码)
```php
return [
// 主菜单父ID(生成的菜单挂在该父菜单下)
// 读取方:GeneratorService::getParentMenuId()
'parent_menu_id' => (int) env('GENERATOR_PARENT_MENU_ID', 189),
// 应用名(路由前缀、前端目录)
// 影响范围:路由 path(/{appName}/{module})、前端目录(ui/src/api|views/{appName})
'app_name' => env('GENERATOR_APP_NAME', 'tool'),
// 模板目录(相对于项目根目录)
'template_dir' => 'resources/templates',
// 后端输出目录:模板文件 => 目标路径({ClassName} 会被替换为模型类名)
'backend_outputs' => [
'model.php.tpl' => 'app/Models/{ClassName}.php',
'service.php.tpl' => 'app/Services/{ClassName}Service.php',
'controller.php.tpl' => 'app/Http/Controllers/Api/{ClassName}Controller.php',
'request.php.tpl' => 'app/Http/Requests/{ClassName}Request.php',
],
];
```
:::tip 前端输出路径
前端输出路径(`ui/src/api|views/{app_name}/{module}`)在 `GeneratorLogic` 中按 `app_name` 与模块名拼接,不在 `backend_outputs` 配置中。
:::
## Web 界面配置
通过「工具管理 → 代码生成」页面配置:
通过「开发工具 → 代码生成」页面配置:
| 配置项 | 说明 | 默认值 |
|--------|------|--------|
@@ -29,19 +57,18 @@ APP_NAME = tool
| 模块名 | 生成的模块名(影响路由前缀和前端目录) | 表名去掉前缀 |
| 功能名 | 功能中文名称(用于注释和菜单标题) | 表注释 |
| 作者 | 代码注释中的作者 | admin |
| 包名 | 生成文件的命名空间 | app |
| 是否导入导出 | 是否生成 import/export 方法 | 是 |
| 树形结构 | 是否使用树形模板(需有 parent_id 字段) | 否 |
| 上级菜单 | 生成菜单挂载到的父菜单 | 由 PARENT_MENU_ID 决定 |
| 上级菜单 | 生成菜单挂载到的父菜单 | 由 `GENERATOR_PARENT_MENU_ID` 决定 |
## 生成的文件路径
| 文件 | 路径 | 说明 |
|------|------|------|
| Controller | `app/controller/{Name}Controller.php` | 控制器 |
| Logic | `app/logic/{Name}Logic.php` | 业务逻辑 |
| Model | `app/model/{Name}.php` | 数据模型 |
| Validate | `app/validate/{Name}Validate.php` | 验证器 |
| Model | `app/Models/{Name}.php` | 数据模型 |
| FormRequest | `app/Http/Requests/{Name}Request.php` | 参数验证 |
| Service | `app/Services/{Name}Service.php` | 业务服务 |
| Controller | `app/Http/Controllers/Api/{Name}Controller.php` | 控制器 |
| API | `ui/src/api/{app}/{name}.ts` | 前端 API |
| 列表页 | `ui/src/views/{app}/{name}/index.vue` | 列表页 |
| 编辑弹窗 | `ui/src/views/{app}/{name}/edit.vue` | 编辑弹窗 |
@@ -49,19 +76,19 @@ APP_NAME = tool
| 表格列配置 | `ui/src/views/{app}/{name}/columns.ts` | 表格列定义(仅 ui/ 模板) |
| 查询表单配置 | `ui/src/views/{app}/{name}/querySchemas.ts` | 搜索表单定义(仅 ui/ 模板) |
## 自动生成的 Logic 属性
## 自动生成的 Service 属性
代码生成器会根据表结构自动分析并生成以下 Logic 属性:
代码生成器会根据表结构自动分析并生成以下属性:
| 属性 | 生成条件 | 说明 |
|------|----------|------|
| `$fileFields` | 字段名含 image/avatar/photo/pic 等且类型为 varchar | 文件/图片字段,自动迁移临时文件并补全 URL |
| `$fileFields` | 字段名含 image/avatar/photo/pic 等且类型为 varchar | 文件/图片字段,需 `use HasFileProcessing` |
| `$fileSaveDir` | 有 `$fileFields` 时生成 | 文件上传子目录 |
| `$contentFields` | 字段名含 content/body/description 且类型为 text | 富文本字段,自动处理临时文件和图片占位符 |
| `$pageLikeFields` | 字段名含 name/title/keyword 且类型为 varchar | 模糊搜索字段,分页查询时 LIKE 匹配 |
| `$pageEqFields` | 字段名含 status/type/group 且类型为 int | 精确匹配字段,分页查询时等值过滤 |
| `$uniqueFields` | 字段有 UNIQUE 索引 | 唯一性校验字段,新增/修改时自动校验 |
| `$serializeMaps` | 字段有选项(choices)时自动生成 | 枚举显示名映射,查询时自动补 `{字段名}Text` |
| `$contentFields` | 字段名含 content/body/description 且类型为 text | 富文本字段,`[IMG_URL]` 占位处理 |
| `$pageLikeFields` | 字段名含 name/title/keyword 且类型为 varchar | 模糊搜索字段 |
| `$pageEqFields` | 字段名含 status/type/group 且类型为 int | 精确匹配字段 |
| `$uniqueFields` | 字段有 UNIQUE 索引 | 唯一性校验字段 |
| `$serializeMaps` | 字段有选项(choices)时自动生成 | 枚举显示名映射 |
| `$treeParentField` | 表含 parent_id 或 pid 字段 | 树形结构父级字段 |
| `$treeLikeField` | 树形结构时生成 | 树形查询模糊搜索字段 |
@@ -72,7 +99,7 @@ APP_NAME = tool
1. **自动生成 `$serializeMaps` 映射**
```php
// 示例:字段 status 有选项(0=正常, 1=禁用)
// 示例:字段 status 有选项(1=在用, 2=停用)
protected array $serializeMaps = [
'status' => 'example_status', // 字典编码 = {模块名}_{字段名}
];
@@ -80,30 +107,32 @@ APP_NAME = tool
2. **自动创建字典数据**
在 `think_dict` 和 `think_dict_item` 表中自动创建对应记录:
在 `laravel_dict` 和 `laravel_dict_item` 表中自动创建对应记录:
| 表 | 字段 | 值 |
|----|------|------|
| think_dict | name | 案例-状态 |
| think_dict | code | example_status |
| think_dict_item | name / value | 正常 / 0,禁用 / 1 |
| laravel_dict | name | 案例-状态 |
| laravel_dict | code | example_status |
| laravel_dict_item | name / value | 在用 / 1,停用 / 2 |
3. **查询时自动翻译**
`BaseLogic::processSerializeMaps()` 在查询结果中自动补 `statusText` 字段,值为字典翻译后的文本。
`HasSerializeMaps` 在查询结果中自动补 `statusText` 字段,值为字典翻译后的文本。
:::info 幂等性
字典创建是幂等的:如果 `think_dict` 中已存在相同 `code` 的记录,会自动跳过,不会重复创建。
字典创建是幂等的:如果 `laravel_dict` 中已存在相同 `code` 的记录,会自动跳过,不会重复创建。
:::
## 配置影响总结
| 配置项 | 影响范围 |
|--------|----------|
| `GENERATOR.PARENT_MENU_ID` | 生成的菜单挂载位置 |
| `GENERATOR.APP_NAME` | 菜单路径、前端目录、API 路径、路由前缀 |
| 是否导入导出 | Controller 中是否生成 import/export 方法,Logic 中是否生成对应方法 |
| 树形结构 | 使用 ui/ 还是 ui2/ 模板,Controller 中是否生成 page 方法 |
| `GENERATOR_PARENT_MENU_ID` | 生成的菜单挂载位置 |
| `GENERATOR_APP_NAME` | 菜单路径、前端目录、API 路径、路由前缀 |
| `config/generator.php → backend_outputs` | 后端文件的输出路径映射 |
| `config/generator.php → template_dir` | 模板目录位置 |
| 是否导入导出 | Controller/Service 中是否生成 import/export 方法 |
| 树形结构 | 使用 ui/ 还是 ui2/ 模板 |
## 相关文档
@@ -7,11 +7,12 @@
## 第 1 步:建表
```sql
CREATE TABLE think_example (
CREATE TABLE laravel_example (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(200) NOT NULL COMMENT '案例名称',
type TINYINT(1) DEFAULT 0 COMMENT '案例类型',
status TINYINT(1) DEFAULT 0 COMMENT '案例状态',
code VARCHAR(50) DEFAULT '' COMMENT '案例编码',
type TINYINT(1) DEFAULT 1 COMMENT '案例类型',
status TINYINT(1) DEFAULT 1 COMMENT '案例状态',
sort INT DEFAULT 0 COMMENT '排序',
avatar VARCHAR(500) DEFAULT '' COMMENT '头像',
is_delete TINYINT(1) DEFAULT 0,
@@ -22,10 +23,12 @@ CREATE TABLE think_example (
);
```
(含公共字段说明见 [3.2 数据库建表](/zh/guide/database.html)。)
## 第 2 步:代码生成器操作
1. 进入「工具管理 → 代码生成」
2. 找到 `think_example` 表
1. 进入「开发工具 → 代码生成」
2. 找到 `laravel_example` 表
3. 配置:
- 模块名:`example`
- 功能名:`案例演示`
@@ -34,41 +37,54 @@ CREATE TABLE think_example (
4. 点击「预览」查看生成代码
5. 点击「生成」创建文件
或使用 CLI:
```bash
php artisan generate:code "laravel_example|案例演示"
```
## 第 3 步:生成的文件
```text
app/controller/ExampleController.php # 控制器
app/logic/ExampleLogic.php # 业务逻辑
app/model/Example.php # 数据模型
app/validate/ExampleValidate.php # 验证器
ui/src/api/tool/example.ts # 前端 API
ui/src/views/tool/example/index.vue # 列表页
ui/src/views/tool/example/edit.vue # 编辑弹窗
ui/src/views/tool/example/detail.vue # 详情弹窗
ui/src/views/tool/example/columns.ts # 表格列配置
ui/src/views/tool/example/querySchemas.ts # 查询表单配置
app/Http/Controllers/Api/ExampleController.php # 控制器
app/Services/ExampleService.php # 业务服务
app/Models/Example.php # 数据模型
app/Http/Requests/ExampleRequest.php # FormRequest
ui/src/api/tool/example.ts # 前端 API
ui/src/views/tool/example/index.vue # 列表页
ui/src/views/tool/example/edit.vue # 编辑弹窗
ui/src/views/tool/example/detail.vue # 详情弹窗
ui/src/views/tool/example/columns.ts # 表格列配置
ui/src/views/tool/example/querySchemas.ts # 查询表单配置
```
:::tip 本项目内置示例
项目初始化数据中已内置完整的 `example` 模块(后端代码 + 菜单权限 + 字典),可直接对照生成结果与 `app/Services/ExampleService.php` 等源码。
:::
## 第 4 步:后续操作
生成完成后,以下内容已自动创建:
| 项目 | 状态 | 说明 |
|------|------|------|
| 路由 | ✅ 自动注册 | `route/app.php` 中已插入 CRUD 路由组 |
| 菜单 | ✅ 自动创建 | 主菜单和权限节点已创建 |
| 字典 | ✅ 自动创建 | 带选项的字段已创建字典和字典项 |
| 路由 | ✅ 自动注册 | `routes/api.php` 中已插入 9 条 CRUD 路由 |
| 菜单 | ✅ 自动创建 | 主菜单(type=0)和权限节点(type=1)已创建 |
| 字典 | ✅ 自动创建 | 带选项的字段已创建字典和字典项(幂等) |
通常还需要手动调整:
1. 根据业务需求调整 Logic 属性配置(如 `pageLikeFields`、`pageEqFields`)
1. 根据业务需求调整 Service 属性配置(如 `pageLikeFields`、`pageEqFields`)
2. 补充自定义钩子(如 `beforeDelete`、`afterDetail` 等)
3. 在菜单管理中调整菜单排序和图标
4. 为测试角色分配新菜单权限(**不分配则前端看不到菜单**)
:::tip 生成后调整
生成的代码是基础骨架,通常需要:
- 调整 Logic 的查询条件配置
- 添加删除前校验(beforeDelete)
- 补充详情后的关联数据(afterDetail)
- 配置文件字段和富文本字段
:::
- 调整 Service 的查询条件配置
- 添加删除前校验(`beforeDelete`)
- 补充详情后的关联数据(`afterDetail`)
- 配置文件字段(`fileFields` + `use HasFileProcessing`)和富文本字段
- 核对 FormRequest 规则与业务约束是否一致
:::
@@ -5,55 +5,60 @@
A: 后端文件生成到 `app/` 目录,前端文件生成到 `ui/src/` 目录。具体路径:
```text
app/controller/{Name}Controller.php # 控制器
app/logic/{Name}Logic.php # 业务逻辑
app/model/{Name}.php # 数据模型
app/validate/{Name}Validate.php # 验证器
ui/src/api/{app}/{name}.ts # API 接口
ui/src/views/{app}/{name}/index.vue # 列表页
ui/src/views/{app}/{name}/edit.vue # 编辑弹窗
ui/src/views/{app}/{name}/detail.vue # 详情弹窗
ui/src/views/{app}/{name}/columns.ts # 表格列配置(仅 ui/ 模板)
ui/src/views/{app}/{name}/querySchemas.ts # 查询表单配置(仅 ui/ 模板)
app/Http/Controllers/Api/{Name}Controller.php # 控制器
app/Services/{Name}Service.php # 业务服务
app/Models/{Name}.php # 数据模型
app/Http/Requests/{Name}Request.php # FormRequest
ui/src/api/{app}/{name}.ts # API 接口
ui/src/views/{app}/{name}/index.vue # 列表页
ui/src/views/{app}/{name}/edit.vue # 编辑弹窗
ui/src/views/{app}/{name}/detail.vue # 详情弹窗
ui/src/views/{app}/{name}/columns.ts # 表格列配置(仅 ui/ 模板)
ui/src/views/{app}/{name}/querySchemas.ts # 查询表单配置(仅 ui/ 模板)
```
树形结构模块(含 `parent_id` 字段)使用 `ui2/` 模板,不生成 `columns.ts` 和 `querySchemas.ts`。
### Q: 生成后需要手动修改吗?
A: 通常需要。生成的是基础 CRUD 代码,复杂业务逻辑(如关联查询、钩子处理、导入导出)需要手动补充。
A: 通常需要。生成的是基础 CRUD 代码,复杂业务逻辑(如关联查询、钩子处理、导入导出细节)需要手动补充。见 [10.8 实战案例](/zh/generator/demo.html)「生成后调整」。
### Q: 可以重新生成吗?
A: 可以,但会覆盖已有文件。建议先备份自定义修改。预览接口(preview)不会写入文件,可安全查看。
A: 可以,但会覆盖已有文件(默认跳过已存在文件,`--overwrite` 强制覆盖)。建议先备份自定义修改。预览接口(preview)/ `--dry-run` 不会写入文件,可安全查看。
### Q: 树形结构表怎么生成?
A: 表中包含 `parent_id` 字段时,生成器会自动使用 `ui2/` 树形模板。
A: 表中包含 `parent_id` 字段时,生成器会自动使用 `ui2/` 树形模板;CLI 可用 `--type ui2` 显式指定。
### Q: 生成的权限码格式是什么?
A: 格式为 `sys:{模块名}:{操作}`,如 `sys:example:add`。
A: 格式为 `sys:{模块名}:{操作}`,如 `sys:example:add`。生成的权限节点已写入 `laravel_menu`(type=1),**记得为角色分配后前端才可见**。
### Q: 支持哪些数据库?
A: 支持 MySQL、PostgreSQL、SQL Server、Oracle、SQLite 五种数据库。生成器会自动检测当前配置的数据库类型。
A: 支持 MySQL、PostgreSQL、SQL Server、Oracle、SQLite 五种数据库。生成器会自动检测当前 `.env` 中 `DB_CONNECTION` 配置的数据库类型。
### Q: 生成时报错"表名格式不合法"
A: 表名只允许字母、数字、下划线,且不能以数字开头。检查表名是否包含特殊字符。
### Q: 生成的代码路由 404?
A: 检查 `routes/api.php` 是否已插入路由组(`php artisan route:list | findstr example`),以及前端 API 的 URL 是否与路由一致。
### Q: 演示环境下能用吗?
A: 预览功能(preview)在演示环境下可用,实际生成(generate)会被拦截。
A: 预览功能(preview / `--dry-run`)在演示环境下可用,实际生成(generate)会被演示模式拦截。
### Q: 可以用命令行生成吗?
A: 可以。使用 `php think generate:code <表名>` 命令行生成。CLI 不受演示环境限制,也不需要权限配置。详见 [10.2 CLI 命令行使用](/zh/generator/cli.html)。
A: 可以。使用 `php artisan generate:code <表名>` 命令行生成。CLI 不受演示环境限制,也不需要权限配置。详见 [10.2 CLI 命令行使用](/zh/generator/cli.html)。
### Q: CLI 命令和 Web 端有什么区别?
A: 底层引擎完全相同,区别在于操作方式:
A: 底层引擎完全相同(`GeneratorLogic` + `GeneratorService`),区别在于操作方式:
- CLI 适合开发环境、批量操作、CI/CD 集成
- Web 端适合日常开发、可视化配置
- CLI 不受权限和演示模式限制
@@ -62,6 +67,11 @@ A: 底层引擎完全相同,区别在于操作方式:
### Q: 如何批量生成多张表?
A: Web 端可多选表后点击批量生成。CLI 使用 `--batch` 选项:
```bash
php think generate:code table1,table2,table3 --batch
```
php artisan generate:code table1,table2,table3 --batch
```
### Q: 生成的菜单挂错了位置?
A: 修改 `.env` 中 `GENERATOR_PARENT_MENU_ID` 为正确的父菜单 ID(在菜单管理中查看),改后 `php artisan config:clear`。已生成的菜单在菜单管理中手动移动即可。
@@ -1,7 +1,7 @@
# 10.1 概述与架构
:::tip 概述
代码生成器可根据数据库表结构一键生成 CRUD 模块的全部代码,包括后端 Controller/Logic/Model/Validate 和前端 API/View。支持 MySQL、PostgreSQL、SQL Server、Oracle、SQLite 五种数据库。
代码生成器可根据数据库表结构一键生成 CRUD 模块的全部代码,包括后端 Controller/Service/Model/FormRequest 和前端 API/View。支持 MySQL、PostgreSQL、SQL Server、Oracle、SQLite 五种数据库。
:::
## 使用方式
@@ -12,17 +12,17 @@
# -----------------------------------------------------------------------------
# 代码生成器 CLI 命令
# -----------------------------------------------------------------------------
# php think generate:code → 调用 ThinkPHP 命令行的代码生成器
# Example "案例演示" → 表名与注释(用 | 分隔,如 think_example|案例演示)
# --dry-run → 仅预览,不写入文件
php think generate:code think_example|案例演示 --dry-run
# php artisan generate:code → 调用 Artisan 的代码生成器
# laravel_example|案例演示 → 表名与注释(用 | 分隔,注释用于菜单标题)
# --dry-run → 仅预览,不写入文件
php artisan generate:code "laravel_example|案例演示" --dry-run
```
详见 [CLI 命令行使用](/zh/generator/cli.html)。
### 方式二:Web 管理界面
进入「工具管理 → 代码生成」菜单,可视化操作。
进入「开发工具 → 代码生成」菜单,可视化操作。
详见 [Web 管理界面](/zh/generator/web-ui.html)。
@@ -30,7 +30,7 @@ php think generate:code think_example|案例演示 --dry-run
```text
┌──────────────┐
│ 数据库表 │ 如 think_example
│ 数据库表 │ 如 laravel_example
└──────┬───────┘
│
▼
@@ -42,27 +42,27 @@ php think generate:code think_example|案例演示 --dry-run
▼
┌──────────────┐
│ GeneratorService│ 字段类型归一化 + 属性分析
│ ::getTableColumns│ (图片、富文本、筛选项、唯一等)
│ (表结构解析) │ (图片、富文本、筛选项、唯一等)
└──────┬───────┘
│
▼
┌──────────────┐
│ GeneratorService│ 模板变量构建 + 模板渲染
│ GeneratorLogic │ 模板变量构建 + 模板渲染
│ ::preview() │
└──────┬───────┘
│
▼
┌──────────────┐
│ GeneratorService│ 文件生成 + 菜单创建
│ GeneratorLogic │ 文件生成 + 菜单/字典/路由创建
│ ::generate() │
└──────┬───────┘
│
┌────┼────┬────┬────┐
▼ ▼ ▼ ▼ ▼
┌────┐┌────┐┌────┐┌────┐┌────┐
│Ctrl││Logi││Mode││Vali││API │
│ . ││ c ││ l ││date││+Vue│
│php ││php ││php ││php ││ │
│Ctrl││Serv││Mode││Req ││API │
│ . ││ ice││ l ││ est││+Vue│
│php ││ .php││php ││php ││ │
└────┘└────┘└────┘└────┘└────┘
```
@@ -72,19 +72,19 @@ php think generate:code think_example|案例演示 --dry-run
|------|------|
| 多数据库适配 | MySQL / PostgreSQL / SQL Server / Oracle / SQLite |
| 字段分析 | 自动识别图片字段、富文本字段、筛选字段、唯一字段 |
| 模板渲染 | 基于 PHP 模板引擎,支持自定义模板 |
| 菜单创建 | 自动生成菜单和权限节点 |
| 模板渲染 | 基于 PHP 模板引擎(`resources/templates/`),支持自定义模板 |
| 菜单创建 | 自动生成菜单和权限节点(挂载位置由 `GENERATOR_PARENT_MENU_ID` 决定) |
| 字典创建 | 自动创建关联字典(含字典项),幂等不重复 |
| 路由注册 | 自动在 route/app.php 中插入 CRUD 路由组 |
| 路由注册 | 自动在 `routes/api.php` 中插入 CRUD 路由组 |
| 安全校验 | 表名合法性校验,防止 SQL 注入 |
| CLI 命令行 | `php think generate:code` 命令行操作,支持预览、导出配置、批量生成 |
| CLI 命令行 | `php artisan generate:code`,支持预览、导出配置、批量生成 |
## API 接口
| 方法 | 路径 | 权限码 | 说明 |
|------|------|--------|------|
| GET | `/api/generator/page` | sys:generator:page | 获取可生成的表列表 |
| POST | `/api/generator/preview` | sys:generator:generate | 预览生成代码(不写入文件) |
| GET | `/api/generator/preview` | sys:generator:generate | 预览生成代码(不写入文件) |
| POST | `/api/generator/generate` | sys:generator:generate | 一键生成 |
| POST | `/api/generator/batchGenerate` | sys:generator:generate | 批量生成 |
@@ -96,30 +96,27 @@ php think generate:code think_example|案例演示 --dry-run
# -----------------------------------------------------------------------------
# 列出所有可生成的数据库表
# -----------------------------------------------------------------------------
php think generate:code
php artisan generate:code
# -----------------------------------------------------------------------------
# 预览生成代码(不写入文件,仅输出到终端)
# -----------------------------------------------------------------------------
# --dry-run → 干运行模式(只预览不执行)
php think generate:code think_example --dry-run
php artisan generate:code laravel_example --dry-run
# -----------------------------------------------------------------------------
# 一键生成(实际写入文件到 app/ 和 ui/src/)
# -----------------------------------------------------------------------------
php think generate:code think_example
php artisan generate:code laravel_example
# -----------------------------------------------------------------------------
# 导出配置(将生成参数保存为 JSON,便于重复使用)
# -----------------------------------------------------------------------------
# --output config.json → 输出配置文件路径
php think generate:code think_example --output config.json
php artisan generate:code laravel_example --output config.json
# -----------------------------------------------------------------------------
# 从配置生成(读取之前导出的配置文件)
# -----------------------------------------------------------------------------
# --config config.json → 读取配置文件
php think generate:code --config config.json
php artisan generate:code --config config.json
```
详见 [10.2 CLI 命令行使用](/zh/generator/cli.html)
@@ -131,26 +128,21 @@ preview 接口标注了 `#[DemoAllow]`,演示环境下也可使用。generate
## 核心代码
```php
// app/controller/GeneratorController.php — 代码生成器控制器
// app/Http/Controllers/Api/GeneratorController.php — 代码生成器控制器(形态示例)
class GeneratorController extends BaseController
class GeneratorController extends Controller
{
protected GeneratorLogic $logic;
protected function initialize(): void
{
$this->logic = new GeneratorLogic(); // 初始化代码生成器业务逻辑
}
public function __construct(protected GeneratorLogic $logic) {}
// 预览生成代码(不写入文件,仅返回渲染结果)
#[Log('代码生成-预览代码', Log::TYPE_QUERY)]
#[OperationLog('代码生成-预览代码', OperationLog::TYPE_QUERY)]
#[Permission('sys:generator:generate', '代码生成')]
#[DemoAllow] // 演示模式允许(只读操作)
public function preview(): Json
public function preview(): JsonResponse
{
try {
$params = $this->getJsonBody(); // 获取前端传入的 JSON 参数
$result = $this->logic->preview($params); // 调用逻辑层预览方法
$params = $this->getJsonBody(); // 获取前端传入的 JSON 参数
$result = $this->logic->preview($params); // 调用逻辑层预览方法
return $this->success($result);
} catch (\Throwable $e) {
// 返回详细错误信息(包含文件名和行号,便于调试)
@@ -159,13 +151,13 @@ class GeneratorController extends BaseController
}
// 一键生成(实际写入文件到 app/ 和 ui/src/ 目录)
#[Log('代码生成-一键生成', Log::TYPE_GENERATE)]
#[OperationLog('代码生成-一键生成', OperationLog::TYPE_GENERATE)]
#[Permission('sys:generator:generate', '代码生成')]
public function generate(): Json
public function generate(): JsonResponse
{
try {
$params = $this->getJsonBody();
$result = $this->logic->generate($params); // 调用逻辑层生成方法
$result = $this->logic->generate($params);
$generated = count($result['generated']); // 成功生成的文件数
$skipped = count($result['skipped']); // 跳过的文件数(已存在)
@@ -186,21 +178,36 @@ class GeneratorController extends BaseController
## 模板文件
```text
templates/ # 代码生成器模板目录
├── controller.php.tpl # 控制器模板
├── logic.php.tpl # 业务逻辑模板(含 serializeMaps、导入导出)
├── model.php.tpl # 数据模型模板
├── validate.php.tpl # 验证器模板
├── ui/ # 普通列表模板(6个前端文件)
│ ├── api.ts.tpl # API 接口模块
│ ├── index.vue.tpl # 列表页
│ ├── edit.vue.tpl # 编辑弹窗
│ ├── detail.vue.tpl # 详情弹窗
│ ├── columns.ts.tpl # 表格列配置
│ └── querySchemas.ts.tpl # 查询表单配置
└── ui2/ # 树形结构模板(4个前端文件)
├── api.ts.tpl
├── index.vue.tpl
├── edit.vue.tpl
└── detail.vue.tpl
resources/templates/ # 代码生成器模板目录
├── controller.php.tpl # 控制器模板
├── service.php.tpl # 服务层模板(含 serializeMaps、导入导出)
├── model.php.tpl # 数据模型模板
├── request.php.tpl # FormRequest 模板
├── ui/ # 普通列表模板(6 个前端文件)
│ ├── api.ts.tpl # API 接口模块
│ ├── index.vue.tpl # 列表页
│ ├── edit.vue.tpl # 编辑弹窗
│ ├── detail.vue.tpl # 详情弹窗
│ ├── columns.ts.tpl # 表格列配置
│ └── querySchemas.ts.tpl # 查询表单配置
└── ui2/ # 树形结构模板(前端文件)
```
模板与输出路径的映射关系见 [10.7 配置文件详解](/zh/generator/config.html),自定义模板见 [10.6 自定义模板开发](/zh/generator/templates.html)。
## 生成流程时序(从建表到可用)
```text
1. 数据库建表(含公共字段,见 3.2)
2. 生成(Web 或 CLI)
├─ 后端 4 文件:Model / FormRequest / Service / Controller
├─ 前端文件:api.ts + views(index/edit/detail/columns/querySchemas)
├─ routes/api.php 自动插入 CRUD 路由组
├─ laravel_menu 自动创建菜单 + 权限节点(挂 GENERATOR_PARENT_MENU_ID 下)
└─ 字典自动创建(带选项字段,幂等)
3. 调整(Service 属性 / 钩子 / 菜单图标排序)
4. 为角色分配菜单权限
5. 前端刷新(重新登录使动态路由生效)→ 测试
```
(完整实战见 [10.8 实战案例](/zh/generator/demo.html),手动开发流程见 [第 3 章 模块开发实战](/zh/guide/overview.html)。)
@@ -1,33 +1,32 @@
# 10.4 表结构解析引擎
:::tip 概述
解析引擎读取数据库表的字段信息(名称、类型、注释、是否必填等),并自动分析字段属性(图片、富文本、选项、外键等),作为代码生成的数据源。支持 MySQL、PostgreSQL、SQL Server、Oracle、SQLite 五种数据库。
解析引擎读取数据库表的字段信息(名称、类型、注释、是否必填等),并自动分析字段属性(图片、富文本、选项等),作为代码生成的数据源。支持 MySQL、PostgreSQL、SQL Server、Oracle、SQLite 五种数据库。
:::
## 多数据库适配
```php
// app/service/GeneratorService.php
// app/Services/GeneratorService.php
// 获取当前数据库驱动类型
protected function getDbDriver(): string
{
$default = config('database.default', 'mysql');
return strtolower(config("database.connections.{$default}.type", $default));
}
// 根据驱动分发到对应的查询实现
// 根据默认数据库驱动分发到对应的查询实现
public function getTableList(): array
{
$driver = $this->getDbDriver();
// MySQL: SHOW TABLE STATUS
// PostgreSQL: information_schema.tables
// SQL Server: sys.tables
// Oracle: all_tables
// SQLite: sqlite_master
$driver = config('database.default');
return match ($driver) {
'sqlite' => $this->queryTableListSqlite(), // sqlite_master
'mysql' => $this->queryTableListMysql(), // SHOW TABLE STATUS
'pgsql' => $this->queryTableListPgsql(), // information_schema.tables
'sqlsrv' => $this->queryTableListSqlsrv(), // sys.tables
'oracle' => $this->queryTableListOracle(), // all_tables
default => $this->queryTableListMysql(),
};
}
```
每种驱动还实现了对应的列解析方法(`PRAGMA table_info` / `information_schema.COLUMNS` / `pg_attribute` 等),输出统一的字段结构。
## 基础解析内容
| 信息 | 用途 |
@@ -35,23 +34,25 @@ public function getTableList(): array
| 字段名 | 生成属性名、查询条件 |
| 字段类型 | 生成验证规则、表单组件 |
| 字段注释 | 生成标签名、注释 |
| 是否必填 | 生成 require 验证规则 |
| 是否必填 | 生成 required 验证规则(`!isNull && default === null`) |
| 默认值 | 生成默认值配置 |
| 索引信息 | 判断是否唯一校验 |
| 字段顺序 | 表单 / 列表字段顺序与表结构保持一致 |
## 字段类型映射
| 数据库类型 | 归一化类型 | 表单组件 | 验证规则 |
|-----------|---------|---------|---------|
| VARCHAR | CharField | Input | max:N |
| TEXT | TextField | Editor | - |
| INT | IntegerField | InputNumber | number |
| TINYINT | IntegerField | Select / Radio | in:0,1 |
| DECIMAL | DecimalField | InputNumber | float |
| DATETIME | DateTimeField | DatePicker | date |
| DATE | DateField | DatePicker | date |
| TIME | TimeField | TimePicker | - |
| BOOLEAN | BooleanField | Switch | - |
原始类型先经 `parseFieldType()` 归一化(boolean / int / decimal / varchar / text / datetime / date / time,无法识别归为 varchar),再由 `toConfigType()` 映射为配置类名:
| 数据库类型 | 归一化类型 | 配置类名 | 表单组件 | 验证规则 |
|-----------|---------|---------|---------|---------|
| VARCHAR | varchar | CharField | Input | max:N |
| TEXT | text | TextField | Editor | - |
| INT / BIGINT / SMALLINT | int | IntegerField | InputNumber | number |
| TINYINT | int(`tinyint(1)`/`bit` → boolean) | IntegerField / BooleanField | Select / Radio / Switch | in:0,1 |
| DECIMAL / FLOAT / DOUBLE | decimal | DecimalField | InputNumber | float |
| DATETIME / TIMESTAMP | datetime | DateTimeField | DatePicker | date |
| DATE | date | DateField | DatePicker | date |
| TIME | time | TimeField | TimePicker | - |
| BOOLEAN / BIT | boolean | BooleanField | Switch | - |
## 字段自动识别规则
@@ -64,7 +65,7 @@ public function getTableList(): array
| 类型 | VARCHAR |
| 匹配 | 注释或字段名匹配 `/图片\|封面\|头像\|logo\|icon\|image\|avatar\|photo/i` |
识别后:`is_image = true`,归入 `file_fields`,前端用 ElAvatar 渲染。
识别后:`is_image = true`,归入 `file` 字段组,前端用 ElAvatar 渲染。
### 富文本字段
@@ -73,7 +74,7 @@ public function getTableList(): array
| 类型 | TEXT |
| 匹配 | 注释匹配 `/内容\|详情\|正文/i` |
识别后:`is_rich_text = true`,归入 `content_fields`,自动处理临时文件和图片占位符。
识别后:`is_rich_text = true`,归入 `content` 字段组,自动处理临时文件和图片占位符。
### 多行文本字段
@@ -86,7 +87,7 @@ public function getTableList(): array
### 选项字段(枚举)
解析引擎从字段注释中自动解析选项,支持以下注释格式:
解析引擎从字段注释中自动解析选项(`parseChoicesFromComment()`),支持以下注释格式:
```text
状态:1-启用 2-停用 ← 中文冒号 + 数字-文字
@@ -97,10 +98,10 @@ public function getTableList(): array
解析规则:
1. 以冒号(中文 `:` 或英文 `:`)分割,取冒号后面的部分
2. 匹配 `数字-文字` 或 `数字:文字` 格式
2. 匹配 `数字-文字` 或 `数字:文字` 格式(正则 `(\d+)\s*[\-\:]\s*(.+?)(?=\s+\d+[\-\:]|$)`)
3. 每个匹配项生成一个选项:`{value: 数字, label: 文字, type: 标签颜色}`
标签颜色映射:
标签颜色映射(`guessChoiceTagType()`):
| 选项值 | 标签颜色 | Element Plus type |
|--------|---------|-------------------|
@@ -109,14 +110,14 @@ public function getTableList(): array
| 3 | 橙色 | `warning` |
| 其他 | 灰色 | `info` |
识别后:`choices` 数组非空,自动归入 `eq_fields`(精确匹配)和 `serialize_maps`(字典映射)。
识别后:`choices` 数组非空,自动归入 `eq` 字段组(精确匹配)和 `serialize_maps`(字典映射,编码为 `{模块名}_{字段名}`)。
### 状态字段自动补全
| 条件 | 规则 |
|------|------|
| 字段名 | `status` |
| 类型 | INT |
| 类型 | int |
| 前提 | 注释中未解析出选项 |
自动生成默认选项:
@@ -131,9 +132,9 @@ public function getTableList(): array
| 条件 | 规则 |
|------|------|
| 类型 | VARCHAR |
| 匹配 | 字段名匹配 `/名称\|标题\|name\|title\|username\|realname\|nickname\|job_name\|category_name\|dept_name\|role_name/i` |
| 匹配 | 字段名匹配 `/名称\|标题\|name\|title\|username\|realname/i` |
识别后:归入 `like_fields`,分页查询时 LIKE 匹配,同时加入筛选表单。
识别后:归入 `like` 字段组,分页查询时 LIKE 匹配,同时加入筛选表单。
### 唯一字段
@@ -141,15 +142,7 @@ public function getTableList(): array
|------|------|
| 匹配 | 字段名匹配 `/编码\|code\|username\|mobile\|email\|account/i` |
识别后:归入 `unique_fields`,新增/修改时自动校验唯一性。
### 外键字段
| 条件 | 规则 |
|------|------|
| 匹配 | 字段名以 `_id` 或 `_ids` 结尾(排除 `id` 本身) |
识别后:`is_foreign_key = true`,记录 `fk_target`(如 `dept_id` → `dept`),供模板渲染下拉选择使用。
识别后:归入 `unique` 字段组,新增/修改时由 Service 层自动校验唯一性(见 10.5 与 3.5 Service 层开发)。
### 排序字段
@@ -157,7 +150,7 @@ public function getTableList(): array
|------|------|
| 字段名 | `sort` |
识别后:`has_sort = true`,默认排序改为 `sort asc`(而非 `id desc`)。
识别后:`has_sort = true`,默认排序改为 `sort asc`(`order_field` / `order_type` 变量,而非 `id desc`)。
### 系统字段排除
@@ -167,7 +160,7 @@ public function getTableList(): array
### 字段注释清洗
注释中的选项部分会被自动移除,只保留中文名称:
注释中的选项部分会被自动移除(`cleanFieldComment()`),只保留中文名称:
```text
"案例状态:1-正常 2-禁用" → "案例状态"
@@ -188,12 +181,12 @@ protected function validateTableName(string $tableName): string
```
:::warning 安全提示
涉及表名拼接的 SQL 一律先经 `validateTableName` 校验,只允许字母、数字、下划线,防止 SQL 注入。
涉及表名拼接的 SQL 一律先经 `validateTableName` 校验,只允许字母、数字、下划线,防止 SQL 注入。所有表名解析入口(`parseTableStructure` 及各驱动的列查询)都会先调用该方法。
:::
## 自动创建的权限节点
生成菜单时,自动创建以下 10 个权限节点:
生成菜单时(`createMenu()`),自动创建以下 9 个权限节点:
| 权限码 | 说明 |
|--------|------|
@@ -204,7 +197,6 @@ protected function validateTableName(string $tableName): string
| `sys:{模块名}:update` | 编辑 |
| `sys:{模块名}:delete` | 删除 |
| `sys:{模块名}:batchDelete` | 批量删除 |
| `sys:{模块名}:status` | 状态管理 |
| `sys:{模块名}:import` | 导入 |
| `sys:{模块名}:export` | 导出 |
@@ -1,17 +1,17 @@
# 10.6 自定义模板开发
:::tip 概述
代码生成器使用 PHP 原生模板引擎渲染代码,模板文件存放在 `templates/` 目录下。修改模板即可自定义生成代码的格式。
代码生成器使用 PHP 原生模板引擎渲染代码,模板文件存放在 `resources/templates/` 目录下(由 `config/generator.php` 的 `template_dir` 配置)。修改模板即可自定义生成代码的格式。
:::
## 模板目录结构
```text
templates/
resources/templates/
├── controller.php.tpl # 控制器模板
├── logic.php.tpl # 业务逻辑模板(含 serializeMaps、导入导出)
├── service.php.tpl # 业务服务模板(含 serializeMaps、导入导出)
├── model.php.tpl # 模型模板
├── validate.php.tpl # 验证器模板
├── request.php.tpl # FormRequest 验证器模板
├── ui/ # 普通列表模板(6个前端文件)
│ ├── api.ts.tpl # API 接口模块模板
│ ├── index.vue.tpl # 列表页模板
@@ -26,6 +26,8 @@ templates/
└── detail.vue.tpl
```
后端 4 张模板的目标输出路径由 `config/generator.php` 的 `backend_outputs` 决定(`{ClassName}` 占位符替换为模型类名),前端输出路径由 `GeneratorLogic` 按 `app_name` 与模块名拼接。
## 模板变量
模板使用 PHP 原生语法,变量通过 `GeneratorService::buildTemplateVars()` 构建,渲染时通过 `extract($vars)` 注入。
@@ -34,8 +36,8 @@ templates/
| 变量 | 类型 | 说明 | 示例 |
|------|------|------|------|
| `$app_name` | string | 应用名(路由前缀) | `tool` |
| `$module_name` | string | 模块名(小写下划线) | `example` |
| `$app_name` | string | 应用名(路由前缀、前端目录),读 `GENERATOR_APP_NAME` | `tool` |
| `$module_name` | string | 模块名(小写下划线,表名去前缀) | `example` |
| `$module_comment` | string | 功能名称(表注释清洗后) | `案例` |
| `$model_class_name` | string | 模型类名(大驼峰) | `Example` |
| `$model_class_name_camel` | string | 模型类名(小驼峰) | `example` |
@@ -44,15 +46,16 @@ templates/
| `$permission_prefix` | string | 权限码前缀 | `sys:example` |
| `$primary_key` | string | 主键字段名 | `id` |
| `$table_name` | string | 表名(不含前缀) | `example` |
| `$parent_menu_id` | int | 菜单挂载父ID,读 `GENERATOR_PARENT_MENU_ID` | `189` |
### 树形结构变量
| 变量 | 类型 | 说明 | 示例 |
|------|------|------|------|
| `$is_tree` | bool | 是否为树形结构 | `true` |
| `$parent_id_field` | string | 父级字段名 | `parent_id` |
| `$parent_id_field` | string | 父级字段名(pid 优先) | `parent_id` |
| `$parent_id_field_camel` | string | 父级字段名(驼峰) | `parentId` |
| `$display_field` | string | 显示字段名 | `name` |
| `$display_field` | string | 显示字段名(按候选列表自动探测) | `name` |
| `$display_field_camel` | string | 显示字段名(驼峰) | `name` |
### 字段列表变量
@@ -65,26 +68,31 @@ templates/
| `$filter_fields` | array | 筛选字段(搜索表单用) |
| `$file_fields` | array | 文件/图片字段名列表 |
| `$content_fields` | array | 富文本字段名列表 |
| `$multi_file_fields` | array | 多文件字段名列表(逗号分隔的多条路径) |
| `$like_fields` | array | 模糊搜索字段名列表 |
| `$eq_fields` | array | 精确匹配字段名列表 |
| `$unique_fields` | array | 唯一性校验字段名列表 |
| `$validate_rules` | array | 验证规则 |
| `$validate_rules` | array | 新增接口验证规则(必填 + 类型 + 长度约束,含 `id`) |
| `$validate_rules_update` | array | 修改接口验证规则(required 降级为 nullable,其余约束保持) |
| `$validate_messages` | array | 验证提示信息 |
| `$validate_fields` | array | 需验证的字段列表 |
| `$validate_fields` | array | 必填字段列表 |
| `$serialize_maps` | array | 枚举字段 → 字典编码映射 |
| `$foreign_keys` | array | 外键关联信息 |
| `$field_labels` | array | 字段中文名映射(唯一性冲突提示用) |
| `$casts` | array | 字段类型转换(时间列 → datetime / date) |
| `$hidden_fields` | array | 需隐藏的敏感字段(密码、令牌等) |
### 功能标记变量
| 变量 | 类型 | 说明 |
|------|------|------|
| `$has_import` | bool | 是否生成导入方法 |
| `$has_export` | bool | 是否生成导出方法 |
| `$has_import` | bool | 是否生成导入方法(当前恒为 true) |
| `$has_export` | bool | 是否生成导出方法(当前恒为 true) |
| `$has_image_field` | bool | 是否包含图片字段 |
| `$has_rich_text_field` | bool | 是否包含富文本字段 |
| `$has_sort` | bool | 是否包含排序字段 |
| `$is_tree_structure` | bool | 是否为树形结构(`$is_tree` 别名) |
| `$template_type` | string | 模板类型:`ui` 或 `ui2` |
| `$tenant_scope` | bool | 表中是否含 `tenant_id`(决定继承 `BaseTenantService`) |
### 排序变量
@@ -101,12 +109,22 @@ templates/
$field = [
'name' => 'status', // 字段名(下划线)
'camel_name' => 'status', // 字段名(驼峰)
'label' => '状态', // 中文标签(取字段 COMMENT)
'type' => 'IntegerField', // 字段类型归一化后的类名
'db_type' => 'int', // 原始数据库类型
'label' => '状态', // 中文标签(注释清洗后,无注释回退字段名)
'comment' => '状态:1-正常 2-停用', // 原始字段注释
'type' => 'IntegerField', // 字段类型归一化后的配置类名
'rawType' => 'int', // 原始数据库类型
'default' => 0, // 默认值
'required' => false, // 是否必填(非空且无默认值)
'nullable' => true, // 数据库是否允许 NULL
'is_image' => false, // 是否为图片字段
'is_rich_text' => false, // 是否为富文本字段
'is_textarea' => false, // 是否为多行文本
'is_number' => false, // 是否为数字字段
'is_datetime' => false, // 是否为时间字段
'in_form' => true, // 是否进表单
'in_list' => true, // 是否进列表
'filterable' => false, // 是否可筛选
'filter_type' => '', // 筛选方式:'精确匹配' / '' / '模糊'
'choices' => [ // 选项列表(有值时可生成 ElTag 渲染)
['value' => 0, 'label' => '正常', 'type' => 'success'],
['value' => 1, 'label' => '禁用', 'type' => 'danger'],
@@ -118,28 +136,28 @@ $field = [
模板使用 PHP 原生语法,通过 `<?php echo ?>` 输出变量,使用 `<?php foreach ?>` 等控制结构:
### 后端模板示例(logic.php.tpl 片段)
### 后端模板示例(service.php.tpl 片段)
```php
<?php echo '<?php' ?>
declare(strict_types=1);
namespace App\Services;
namespace app\logic;
use App\Models\<?php echo $model_class_name ?>;
use app\BaseLogic;
use app\model\<?php echo $model_class_name ?>;
class <?php echo $model_class_name ?>Logic extends BaseLogic
class <?php echo $model_class_name ?>Service extends <?php echo $is_tree ? 'BaseService' : ($tenant_scope ? 'BaseTenantService' : 'BaseService') ?>
{
use HasCrudOperations, HasQueryScopes, HasSerializeMaps<?php if (!empty($unique_fields)) { echo ', HasUniqueCheck'; } ?>;
protected string $modelClass = <?php echo $model_class_name ?>::class;
protected string $validateClass = \app\validate\<?php echo $model_class_name ?>Validate::class;
<?php if (!empty($file_fields)): ?>
protected array $fileFields = [<?php echo implode(', ', array_map(fn($f) => "'$f'", $file_fields)) ?>];
protected string $fileSaveDir = '<?php echo $module_name ?>';
<?php endif; ?>
protected array $pageOrderBy = ['field' => '<?php echo $order_field ?>', 'type' => '<?php echo $order_type ?>'];
<?php if (!empty($serialize_maps)): ?>
protected array $serializeMaps = [
<?php foreach ($serialize_maps as $field => $dict): ?>
@@ -190,15 +208,16 @@ export const columns = [
## 自定义模板步骤
1. 修改 `templates/` 目录下的 `.tpl` 文件
1. 修改 `resources/templates/` 目录下的 `.tpl` 文件(或调整 `config/generator.php` 的 `template_dir` 指向其它目录)
2. 使用上述模板变量(`$model_class_name`、`$module_comment` 等)
3. 下次生成时自动使用新模板
:::warning 注意事项
- 修改模板后,已生成的代码不会自动更新
- 模板中不能使用短标签 `<?= ?>`,必须用 `<?php echo ?>`(渲染前有预检)
- 模板文件不能含 UTF-8 BOM 头
- 修改模板后,已生成的代码不会自动更新(需重新生成,且注意 `overwrite` 参数)
- 模板中使用 `<?php echo ?>` 输出变量,与现有模板保持一致(现有模板均不含短标签)
- 模板文件不能含 UTF-8 BOM 头,否则渲染结果开头会带不可见字符
- 树形结构模块使用 `ui2/` 模板,普通模块使用 `ui/` 模板
- 后端模板输出路径若调整 `backend_outputs`,注意同步 `GeneratorService::getGenerateFiles()` 的文件清单
:::
## 模板选择逻辑
@@ -1,29 +1,29 @@
# 10.3 Web 管理界面
:::tip 概述
代码生成器提供 Web 管理界面,通过可视化操作完成代码生成,无需命令行。支持表选择、配置生成选项、预览代码和一键生成。界面位于「工具管理 → 代码生成」菜单。
代码生成器提供 Web 管理界面,通过可视化操作完成代码生成,无需命令行。支持表选择、配置生成选项、预览代码和一键生成。界面位于「开发工具 → 代码生成」菜单。
:::
## 操作流程
```text
1. 登录管理后台
2. 进入「工具管理 → 代码生成」菜单
2. 进入「开发工具 → 代码生成」菜单
3. 查看可生成的数据库表列表(自动读取当前数据库的所有表)
4. 选择目标表,点击「配置」
5. 配置生成选项(模块名、功能名、作者等)
5. 配置生成选项(模板类型、生成菜单、覆盖文件等)
6. 点击「预览」查看生成代码(不写入文件,可安全查看)
7. 确认无误后点击「生成」创建文件
8. 在 route/app.php 中添加路由(通常已自动生成)
8. 检查 routes/api.php 中的路由(通常已自动插入)
9. 在菜单管理中配置菜单权限
10. 刷新前端页面,新模块即可使用
10. 重新登录或刷新前端动态菜单,新模块即可使用
```
## 界面说明
### 表列表页
进入「工具管理 → 代码生成」后,首先进入表列表页:
进入「开发工具 → 代码生成」后,首先进入表列表页:
| 列 | 说明 |
|----|------|
@@ -31,8 +31,7 @@
| 表注释 | 表的 COMMENT 字段(显示为中文说明) |
| 引擎 | 存储引擎(InnoDB / MyISAM) |
| 行数 | 表中的数据行数 |
| 创建时间 | 表的创建时间 |
| 操作 | 配置 / 预览 / 生成 / 删除 |
| 操作 | 配置 / 预览 / 生成 |
### 配置弹窗
@@ -40,7 +39,7 @@
| 配置项 | 参数名 | 说明 | 示例 |
|--------|--------|------|------|
| 表名 | `tableNames` | 选择要生成的表(自动带入,格式 `"表名\|注释"`) | `["think_example\|案例演示"]` |
| 表名 | `tableNames` | 选择要生成的表(自动带入,格式 `"表名\|注释"`) | `["laravel_example\|案例演示"]` |
| 模板类型 | `templateType` | `ui` 普通列表 或 `ui2` 树形(含 parent_id 时自动切换) | `ui` |
| 生成菜单 | `generateMenu` | 是否自动创建菜单和权限节点 | `true` |
| 覆盖文件 | `overwrite` | 是否覆盖已存在的文件 | `false` |
@@ -50,7 +49,7 @@
点击「预览」按钮,可查看即将生成的所有文件代码:
- 预览模式**不会写入文件**,可安全查看
- 支持切换查看不同文件(Controller / Logic / Model / Validate / API / Vue / columns.ts / querySchemas.ts)
- 支持切换查看不同文件(Controller / Service / Model / Request / API / Vue / columns.ts / querySchemas.ts)
- 确认无误后再点击「生成」实际创建文件
### 批量生成
@@ -83,51 +82,52 @@
- 支持拖拽排序、展开/折叠
- 自动处理父子关系的新增和编辑
- 树形模板不生成 `columns.ts` 和 `querySchemas.ts`
- Controller 中不生成 `page()` 方法
- 注册路由时不含 `page` 路由
### 导入导出(默认生成)
默认生成 `import()` 和 `export()` 方法:
- 后端基于 `ExcelService` 实现分批导入导出
- 枚举字段自动做正向/反向映射(值 ↔ 文本)
- 枚举字段自动做正向/反向映射(值 ↔ 文本,反向映射用 `DictCacheService::getValue()`)
- 前端生成导入/导出按钮
### 字典自动创建
带选项(choices)的字段会自动创建字典:
- 在 `think_dict` 和 `think_dict_item` 表中创建记录
- Logic 中自动生成 `$serializeMaps` 映射
- 查询时自动补 `{字段名}Text` 翻译字段
- 在 `laravel_dict` 和 `laravel_dict_item` 表中创建记录(`create_user = 'system'`)
- Service 中自动生成 `$serializeMaps` 映射
- 查询时自动补 `{字段名}Text` 翻译字段(`HasSerializeMaps` Trait)
- 幂等:字典编码已存在则跳过
### 路由自动注册
在 `route/app.php` 中自动插入 CRUD 路由组:
在 `routes/api.php` 中自动插入 CRUD 路由组(`GeneratorService::registerRoute()`):
- 包含 list / detail / add / update / delete / batchDelete / import / export
- 非树形结构额外包含 page 路由
- 幂等:路由组已存在则跳过
- 插入位置为文件末尾中间件组闭合 `});` 之前,分组注释取表注释(如 `// --- 案例演示 ---`),表注释为空时回退模块名
- 幂等:同模块路由已存在则跳过(以路由路径判断)
## API 接口
| 方法 | 路径 | 权限码 | 说明 |
|------|------|--------|------|
| GET | `/api/generator/page` | sys:generator:page | 获取可生成的表列表(分页) |
| POST | `/api/generator/preview` | sys:generator:generate | 预览生成代码(不写入文件) |
| GET | `/api/generator/preview` | sys:generator:generate | 预览生成代码(不写入文件) |
| POST | `/api/generator/generate` | sys:generator:generate | 一键生成(写入文件) |
| POST | `/api/generator/batchGenerate` | sys:generator:generate | 批量生成(多表) |
:::tip 演示模式
预览接口(preview)标注了 `#[DemoAllow]`,演示环境下也可使用。生成接口(generate / batchGenerate)在演示环境下会被拦截。
预览接口(preview)标注了 `#[DemoAllow]`,演示环境下也可使用(且本身是 GET 请求)。生成接口(generate / batchGenerate)为写操作,演示环境下会被 `DemoMiddleware` 拦截(`config('app.demo_mode')` 开启时)。
:::
### 请求参数
```json
{
"tableNames": ["think_example|案例演示"],
"tableNames": ["laravel_example|案例演示"],
"templateType": "ui",
"generateMenu": true,
"overwrite": false
@@ -151,14 +151,14 @@
# -----------------------------------------------------------------------------
# 预览生成代码(不写入文件)
# -----------------------------------------------------------------------------
# POST /api/generator/preview
# GET /api/generator/preview
# Content-Type: application/json
# Authorization: Bearer <token>
curl -X POST http://localhost:8000/api/generator/preview \
curl -X GET http://localhost:8000/api/generator/preview \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{
"tableNames": ["think_example|案例演示"],
"tableNames": ["laravel_example|案例演示"],
"templateType": "ui"
}'
```
@@ -173,14 +173,14 @@ curl -X POST http://localhost:8000/api/generator/preview \
"data": {
"files": [
{
"path": "app/controller/ExampleController.php",
"content": "<?php\nnamespace app\\controller;\n// ...",
"path": "app/Http/Controllers/Api/ExampleController.php",
"content": "<?php\nnamespace App\\Http\\Controllers\\Api;\n// ...",
"exists": false,
"type": "backend"
},
{
"path": "app/logic/ExampleLogic.php",
"content": "<?php\nnamespace app\\logic;\n// ...",
"path": "app/Services/ExampleService.php",
"content": "<?php\nnamespace App\\Services;\n// ...",
"exists": false,
"type": "backend"
},
@@ -192,16 +192,12 @@ curl -X POST http://localhost:8000/api/generator/preview \
}
],
"vars": {
"app_name": "tool",
"module_name": "example",
"module_comment": "案例",
"model_class_name": "Example",
"template_type": "ui",
"is_tree": false
},
"menu_info": {
"name": "案例",
"path": "/tool/example",
"component": "tool/example/index"
}
}
}
@@ -210,7 +206,7 @@ curl -X POST http://localhost:8000/api/generator/preview \
:::tip 响应结构说明
- `files`:待生成文件数组,每项含 `path`(相对路径)、`content`(渲染后内容)、`exists`(是否已存在)、`type`(backend/frontend)
- `vars`:模板变量,供调试和导出配置使用
- `menu_info`:将要创建的菜单信息
- 一键生成接口的 `msg` 为多行统计消息:📄 文件(生成/跳过/失败数量)、📋 菜单、📖 字典、🔀 路由
:::
## 生成后操作清单
@@ -219,29 +215,37 @@ curl -X POST http://localhost:8000/api/generator/preview \
# -----------------------------------------------------------------------------
# 第 1 步:检查生成的文件
# -----------------------------------------------------------------------------
ls app/controller/Example* # 控制器
ls app/logic/Example* # 业务逻辑
ls app/model/Example* # 数据模型
ls app/validate/Example* # 验证器
ls ui/src/api/tool/example* # 前端 API
ls ui/src/views/tool/example/ # 前端页面(含 index.vue / edit.vue / detail.vue / columns.ts / querySchemas.ts)
ls app/Http/Controllers/Api/Example* # 控制器
ls app/Services/Example* # 业务服务
ls app/Models/Example* # 数据模型
ls app/Http/Requests/Example* # FormRequest 验证器
ls ui/src/api/tool/example* # 前端 API
ls ui/src/views/tool/example/ # 前端页面(含 index.vue / edit.vue / detail.vue / columns.ts / querySchemas.ts)
# -----------------------------------------------------------------------------
# 第 2 步:在 route/app.php 中添加路由(通常已自动生成)
# 第 2 步:检查 routes/api.php 中的路由(通常已自动插入,带 // --- 案例演示 --- 表注释)
# -----------------------------------------------------------------------------
grep -n "// --- 案例演示 ---" routes/api.php
# -----------------------------------------------------------------------------
# 第 3 步:在菜单管理中配置菜单权限
# -----------------------------------------------------------------------------
# 新增菜单:案例演示(路径:/example,组件:tool/example/index)
# 新增按钮权限:sys:example:page / sys:example:add / sys:example:update / sys:example:delete
# 菜单已自动创建:案例(路径:/tool/example,组件:tool/example/index)
# 权限节点已自动创建:sys:example:page / list / detail / add / update / delete / batchDelete / import / export
# 只需为角色勾选相应权限
# -----------------------------------------------------------------------------
# 第 4 步:根据业务需求调整 Logic 属性配置
# 第 4 步:根据业务需求调整 Service 属性配置
# -----------------------------------------------------------------------------
# 可调整:fileFields(文件字段)、serializeMaps(字典映射)、pageLikeFields(模糊查询)等
# 可调整:fileFields(文件字段)、serializeMaps(字典映射)、
# pageLikeFields(模糊查询)、uniqueFields(唯一校验)等
```
:::warning 生产环境注意
- 路由写入后,若执行过 `php artisan route:cache`,需重新缓存
- 新菜单需重新登录(刷新 Redis 中的 JWT 权限缓存)或刷新动态菜单后生效
:::
## 与 CLI 命令对比
| 特性 | Web 界面 | CLI 命令 |