Become a sponsor

概述
代码生成器使用 PHP 原生模板引擎渲染代码,模板文件存放在 resources/templates/ 目录下(由 config/generator.php 的 template_dir 配置)。修改模板即可自定义生成代码的格式。
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/ # 树形结构模板(4个前端文件)
├── api.ts.tpl
├── index.vue.tpl
├── edit.vue.tpl
└── detail.vue.tpl后端 4 张模板的目标输出路径由 config/generator.php 的 backend_outputs 决定({ClassName} 占位符替换为模型类名),前端输出路径由 GeneratorLogic 按 app_name 与模块名拼接。
模板使用 PHP 原生语法,变量通过 GeneratorService::buildTemplateVars() 构建,渲染时通过 extract($vars) 注入。
| 变量 | 类型 | 说明 | 示例 |
|---|---|---|---|
$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 |
$route_prefix | string | 路由前缀 | example |
$api_path | string | API 路径 | tool/example |
$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 | 父级字段名(pid 优先) | parent_id |
$parent_id_field_camel | string | 父级字段名(驼峰) | parentId |
$display_field | string | 显示字段名(按候选列表自动探测) | name |
$display_field_camel | string | 显示字段名(驼峰) | name |
| 变量 | 类型 | 说明 |
|---|---|---|
$fields | array | 全部字段列表 |
$list_fields | array | 列表页展示字段(排除隐藏字段) |
$form_fields | array | 表单字段(编辑弹窗用) |
$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 | 新增接口验证规则(必填 + 类型 + 长度约束,含 id) |
$validate_rules_update | array | 修改接口验证规则(required 降级为 nullable,其余约束保持) |
$validate_messages | array | 验证提示信息 |
$validate_fields | array | 必填字段列表 |
$serialize_maps | array | 枚举字段 → 字典编码映射 |
$field_labels | array | 字段中文名映射(唯一性冲突提示用) |
$casts | array | 字段类型转换(时间列 → datetime / date) |
$hidden_fields | array | 需隐藏的敏感字段(密码、令牌等) |
| 变量 | 类型 | 说明 |
|---|---|---|
$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) |
| 变量 | 类型 | 说明 | 示例 |
|---|---|---|---|
$order_field | string | 默认排序字段 | id 或 sort |
$order_type | string | 默认排序方向 | desc 或 asc |
每个字段为一个数组,包含以下属性:
$field = [
'name' => 'status', // 字段名(下划线)
'camel_name' => 'status', // 字段名(驼峰)
'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' => [ // 选项列表(有值时可生成 ATag 渲染)
['value' => 0, 'label' => '正常', 'type' => 'success'],
['value' => 1, 'label' => '禁用', 'type' => 'danger'],
],
];模板使用 PHP 原生语法,通过 <?php echo ?> 输出变量,使用 <?php foreach ?> 等控制结构:
<?php echo '<?php' ?>
namespace App\Services;
use App\Models\<?php echo $model_class_name ?>;
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;
<?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): ?>
'<?php echo $field ?>' => '<?php echo $dict ?>',
<?php endforeach; ?>
];
<?php endif; ?>
<?php if ($is_tree): ?>
protected string $treeParentField = '<?php echo $parent_id_field ?>';
protected string $treeLikeField = '<?php echo $display_field ?>';
<?php endif; ?>
}import { h } from 'vue';
import { Tag, Avatar } from 'ant-design-vue';
export const columns = [
{ title: 'ID', dataIndex: 'id', fixed: 'left', width: 50 },
<?php foreach ($list_fields as $field): ?>
{
title: '<?php echo $field['label'] ?>',
dataIndex: '<?php echo $field['camel_name'] ?>',
key: '<?php echo $field['camel_name'] ?>',
<?php if ($field['is_image']): ?>
customRender({ record }) {
return h(Avatar, { size: 48, src: record.<?php echo $field['camel_name'] ?>, shape: 'square' });
},
<?php elseif ($field['choices']): ?>
customRender({ record }) {
const map = {
<?php foreach ($field['choices'] as $choice): ?>
<?php echo $choice['value'] ?>: { text: '<?php echo $choice['label'] ?>', color: '<?php echo ($choice['type'] === 'danger') ? 'error' : (($choice['type'] === 'info') ? 'default' : $choice['type']) ?>' },
<?php endforeach; ?>
};
const s = map[record.<?php echo $field['camel_name'] ?>];
return h(Tag, { color: s?.color || 'default' }, { default: () => s?.text });
},
<?php endif; ?>
},
<?php endforeach; ?>
];resources/templates/ 目录下的 .tpl 文件(或调整 config/generator.php 的 template_dir 指向其它目录)$model_class_name、$module_comment 等)注意事项
overwrite 参数)<?php echo ?> 输出变量,与现有模板保持一致(现有模板均不含短标签)ui2/ 模板,普通模块使用 ui/ 模板backend_outputs,注意同步 GeneratorService::getGenerateFiles() 的文件清单表结构中包含 parent_id 或 pid 字段?
│
├─ 是 → 使用 ui2/ 树形模板(自动,无需手动选择)
│
└─ 否 → 使用 ui/ 普通列表模板ui/ 模板生成 6 个文件(ui2/ 生成 4 个,无 columns.ts 和 querySchemas.ts):
| 文件 | 说明 | 引用关系 |
|---|---|---|
api.ts | API 接口模块 | 被 index.vue、edit.vue 导入 |
index.vue | 列表页 | 导入 columns.ts、querySchemas.ts |
edit.vue | 编辑弹窗 | 被 index.vue 动态加载 |
detail.vue | 详情弹窗 | 被 index.vue 动态加载 |
columns.ts | 表格列配置 | 被 index.vue 导入,含 ATag/AAvatar 渲染 |
querySchemas.ts | 查询表单配置 | 被 index.vue 导入,含 Select/Input/DatePicker 组件 |