diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java index 3175e4386..ebcf729af 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.infra.service.codegen.inner; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.extra.template.TemplateConfig; import cn.hutool.extra.template.TemplateEngine; @@ -27,6 +28,7 @@ import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO; import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO; import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum; import cn.iocoder.yudao.module.infra.enums.codegen.CodegenSceneEnum; +import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum; import cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableTable; @@ -111,6 +113,16 @@ public class CodegenEngine { vue3FilePath("views/${table.moduleName}/${classNameVar}/index.vue")) .put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("views/form.vue"), vue3FilePath("views/${table.moduleName}/${classNameVar}/${simpleClassName}Form.vue")) + .put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("views/components/form_sub_normal.vue"), // 特殊:主子表专属逻辑 + vue3FilePath("views/${table.moduleName}/${classNameVar}/components/${subSimpleClassName}Form.vue")) + .put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("views/components/form_sub_inner.vue"), // 特殊:主子表专属逻辑 + vue3FilePath("views/${table.moduleName}/${classNameVar}/components/${subSimpleClassName}Form.vue")) + .put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("views/components/form_sub_erp.vue"), // 特殊:主子表专属逻辑 + vue3FilePath("views/${table.moduleName}/${classNameVar}/components/${subSimpleClassName}Form.vue")) + .put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("views/components/list_sub_inner.vue"), // 特殊:主子表专属逻辑 + vue3FilePath("views/${table.moduleName}/${classNameVar}/components/${subSimpleClassName}List.vue")) + .put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("views/components/list_sub_erp.vue"), // 特殊:主子表专属逻辑 + vue3FilePath("views/${table.moduleName}/${classNameVar}/components/${subSimpleClassName}List.vue")) .put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("api/api.ts"), vue3FilePath("api/${table.moduleName}/${classNameVar}/index.ts")) // Vue3 Schema 模版 @@ -202,17 +214,9 @@ public class CodegenEngine { templates.forEach((vmPath, filePath) -> { // 2.1 特殊:主子表专属逻辑 if (isSubTemplate(vmPath)) { - if (CollUtil.isEmpty(subTables)) { - return; - } - for (int i = 0; i < subTables.size(); i++) { - bindingMap.put("subIndex", i); - generateCode(result, vmPath, filePath, bindingMap); - } - bindingMap.remove("subIndex"); + generateSubCode(table, subTables, result, vmPath, filePath, bindingMap); return; } - // 2.2 默认生成 generateCode(result, vmPath, filePath, bindingMap); }); @@ -228,6 +232,35 @@ public class CodegenEngine { result.put(filePath, content); } + private void generateSubCode(CodegenTableDO table, List subTables, + Map result, String vmPath, + String filePath, Map bindingMap) { + // 没有子表,所以不生成 + if (CollUtil.isEmpty(subTables)) { + return; + } + // 主子表的模式匹配。目的:过滤掉个性化的模版 + if (vmPath.contains("_normal") + && ObjectUtil.notEqual(table.getTemplateType(), CodegenTemplateTypeEnum.MASTER_NORMAL.getType())) { + return; + } + if (vmPath.contains("_erp") + && ObjectUtil.notEqual(table.getTemplateType(), CodegenTemplateTypeEnum.MASTER_ERP.getType())) { + return; + } + if (vmPath.contains("_inner") + && ObjectUtil.notEqual(table.getTemplateType(), CodegenTemplateTypeEnum.MASTER_INNER.getType())) { + return; + } + + // 逐个生成 + for (int i = 0; i < subTables.size(); i++) { + bindingMap.put("subIndex", i); + generateCode(result, vmPath, filePath, bindingMap); + } + bindingMap.remove("subIndex"); + } + /** * 格式化生成后的代码 * @@ -323,6 +356,7 @@ public class CodegenEngine { return templates; } + @SuppressWarnings("unchecked") private String formatFilePath(String filePath, Map bindingMap) { filePath = StrUtil.replace(filePath, "${basePackage}", getStr(bindingMap, "basePackage").replaceAll("\\.", "/")); @@ -346,6 +380,8 @@ public class CodegenEngine { filePath = StrUtil.replace(filePath, "${subTable.moduleName}", subTable.getModuleName()); filePath = StrUtil.replace(filePath, "${subTable.businessName}", subTable.getBusinessName()); filePath = StrUtil.replace(filePath, "${subTable.className}", subTable.getClassName()); + filePath = StrUtil.replace(filePath, "${subSimpleClassName}", + ((List) bindingMap.get("subSimpleClassNames")).get(subIndex)); } return filePath; } @@ -417,4 +453,5 @@ public class CodegenEngine { private static boolean isSubTemplate(String path) { return path.contains("_sub"); } + } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/api/api.ts.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/api/api.ts.vm index 5476bcbae..63f92751e 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/api/api.ts.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/api/api.ts.vm @@ -43,4 +43,57 @@ export const delete${simpleClassName} = async (id: number) => { // 导出${table.classComment} Excel export const export${simpleClassName} = async (params) => { return await request.download({ url: `${baseURL}/export-excel`, params }) -} \ No newline at end of file +} +## 特殊:主子表专属逻辑 +#foreach ($subTable in $subTables) +#set ($index = $foreach.count - 1) +#set ($subSimpleClassName = $subSimpleClassNames.get($index)) +#set ($subPrimaryColumn = $subPrimaryColumns.get($index))##当前 primary 字段 +#set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段 +#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写 +#set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index)) +#set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index)) +#set ($subClassNameVar = $subClassNameVars.get($index)) + +// ==================== 子表($subTable.classComment) ==================== +## 情况一:MASTER_ERP 时,需要分查询页子表 +#if ( $table.templateType == 11 ) + +// 获得${subTable.classComment}分页 +export const get${subSimpleClassName}Page = async (params) => { + return await request.get({ url: `${baseURL}/${subSimpleClassName_strikeCase}/page`, params }) +} +## 情况二:非 MASTER_ERP 时,需要列表查询子表 +#else + #if ( $subTable.subJoinMany ) + +// 获得${subTable.classComment}列表 +export const get${subSimpleClassName}ListBy${SubJoinColumnName} = async (${subJoinColumn.javaField}) => { + return await request.get({ url: `${baseURL}/${subSimpleClassName_strikeCase}/list-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=` + ${subJoinColumn.javaField} }) +} + #else + +// 获得${subTable.classComment} +export const get${subSimpleClassName}By${SubJoinColumnName} = async (${subJoinColumn.javaField}) => { + return await request.get({ url: `${baseURL}/${subSimpleClassName_strikeCase}/get-by-${subJoinColumn_strikeCase}`, ${subJoinColumn.javaField} }) +} + #end +#end +## 特殊:MASTER_ERP 时,支持单个的新增、修改、删除操作 +#if ( $table.templateType == 11 ) +// 新增${subTable.classComment} +export const create${subSimpleClassName} = async (data) => { + return await request.post({ url: `${baseURL}/${subSimpleClassName_strikeCase}/create`, data }) +} + +// 修改${subTable.classComment} +export const update${subSimpleClassName} = async (data) => { + return await request.put({ url: `${baseURL}/${subSimpleClassName_strikeCase}/update`, data }) +} + +// 删除${subTable.classComment} +export const delete${subSimpleClassName} = async (id: number) => { + return await request.delete({ url: `${baseURL}/${subSimpleClassName_strikeCase}/delete?id=` + id }) +} +#end +#end \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub_erp.vue.vm similarity index 100% rename from yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub.vue.vm rename to yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub_erp.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub_inner.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub_inner.vue.vm new file mode 100644 index 000000000..cffb5f491 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub_inner.vue.vm @@ -0,0 +1,2 @@ +## 主表的 normal 和 inner 使用相同的 form 表单 +#parse("codegen/vue3/views/components/form_sub_inner.vue.vm") \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub_normal.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub_normal.vue.vm new file mode 100644 index 000000000..135ec1d10 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub_normal.vue.vm @@ -0,0 +1,226 @@ +#set ($subTable = $subTables.get($subIndex))##当前表 +#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组 +#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段 +#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex)) +#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段 +#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写 + + \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/list_sub.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/list_sub_erp.vue.vm similarity index 100% rename from yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/list_sub.vue.vm rename to yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/list_sub_erp.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/list_sub_inner.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/list_sub_inner.vue.vm new file mode 100644 index 000000000..e69de29bb diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/form.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/form.vue.vm index 2bc6f315d..f2b3423e7 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/form.vue.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/form.vue.vm @@ -27,12 +27,10 @@ #elseif($column.htmlType == "imageUpload")## 图片上传 - #set ($hasImageUploadColumn = true) #elseif($column.htmlType == "fileUpload")## 文件上传 - #set ($hasFileUploadColumn = true) @@ -124,11 +122,11 @@ const formType = ref('') // 表单的类型:create - 新增;update - 修改 const formData = ref({ #foreach ($column in $columns) #if ($column.createOperation || $column.updateOperation) - #if ($column.htmlType == "checkbox") + #if ($column.htmlType == "checkbox") $column.javaField: [], - #else + #else $column.javaField: undefined, - #end + #end #end #end }) diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineTest.java index 2bbe3309b..7c7384bcd 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineTest.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineTest.java @@ -208,7 +208,63 @@ public class CodegenEngineTest extends BaseMockitoUnitTest { .setCreateOperation(true).setUpdateOperation(true).setListOperation(true) .setListOperationCondition(CodegenColumnListConditionEnum.LIKE.getCondition()).setListOperationResult(true) .setHtmlType(CodegenColumnHtmlTypeEnum.INPUT.getType()); - List contactColumns = Arrays.asList(contactIdColumn, contactStudentIdColumn, contactNameColumn); + CodegenColumnDO contactsAvatarColumn = new CodegenColumnDO().setColumnName("avatar").setDataType(JdbcType.VARCHAR.name()) + .setColumnComment("头像").setNullable(false).setPrimaryKey(false) + .setOrdinalPosition(4).setJavaType("String").setJavaField("avatar").setExample("https://www.iocoder.cn/1.png") + .setCreateOperation(true).setUpdateOperation(true).setListOperation(false) + .setListOperationResult(true) + .setHtmlType(CodegenColumnHtmlTypeEnum.UPLOAD_IMAGE.getType()); + CodegenColumnDO contactVideoColumn = new CodegenColumnDO().setColumnName("video").setDataType(JdbcType.VARCHAR.name()) + .setColumnComment("视频").setNullable(false).setPrimaryKey(false) + .setOrdinalPosition(5).setJavaType("String").setJavaField("video").setExample("https://www.iocoder.cn/1.mp4") + .setCreateOperation(true).setUpdateOperation(true).setListOperation(false) + .setListOperationResult(true) + .setHtmlType(CodegenColumnHtmlTypeEnum.UPLOAD_FILE.getType()); + CodegenColumnDO contactDescriptionColumn = new CodegenColumnDO().setColumnName("description").setDataType(JdbcType.VARCHAR.name()) + .setColumnComment("个人简介").setNullable(false).setPrimaryKey(false) + .setOrdinalPosition(6).setJavaType("String").setJavaField("description").setExample("我是介绍") + .setCreateOperation(true).setUpdateOperation(true).setListOperation(false) + .setListOperationResult(true) + .setHtmlType(CodegenColumnHtmlTypeEnum.EDITOR.getType()); + CodegenColumnDO contactSex1Column = new CodegenColumnDO().setColumnName("sex1").setDataType(JdbcType.VARCHAR.name()) + .setColumnComment("性别 1").setNullable(false).setPrimaryKey(false) + .setOrdinalPosition(7).setJavaType("String").setJavaField("sex1").setExample("男") + .setCreateOperation(true).setUpdateOperation(true).setListOperation(true) + .setListOperationCondition(CodegenColumnListConditionEnum.EQ.getCondition()).setListOperationResult(true) + .setHtmlType(CodegenColumnHtmlTypeEnum.SELECT.getType()).setDictType("system_sex1"); + CodegenColumnDO contactSex2Column = new CodegenColumnDO().setColumnName("sex2").setDataType(JdbcType.INTEGER.name()) + .setColumnComment("性别 2").setNullable(false).setPrimaryKey(false) + .setOrdinalPosition(8).setJavaType("Integer").setJavaField("sex2").setExample("1") + .setCreateOperation(true).setUpdateOperation(true).setListOperation(true) + .setListOperationCondition(CodegenColumnListConditionEnum.EQ.getCondition()).setListOperationResult(true) + .setHtmlType(CodegenColumnHtmlTypeEnum.CHECKBOX.getType()).setDictType("system_sex2"); + CodegenColumnDO contactSex3Column = new CodegenColumnDO().setColumnName("sex3").setDataType(JdbcType.BOOLEAN.name()) + .setColumnComment("性别 3").setNullable(false).setPrimaryKey(false) + .setOrdinalPosition(9).setJavaType("Boolean").setJavaField("sex3").setExample("true") + .setCreateOperation(true).setUpdateOperation(true).setListOperation(true) + .setListOperationResult(true) + .setHtmlType(CodegenColumnHtmlTypeEnum.RADIO.getType()).setDictType("system_sex3"); + CodegenColumnDO contactBirthdayColumn = new CodegenColumnDO().setColumnName("birthday").setDataType(JdbcType.DATE.name()) + .setColumnComment("出生日期").setNullable(false).setPrimaryKey(false) + .setOrdinalPosition(10).setJavaType("LocalDateTime").setJavaField("birthday") + .setCreateOperation(true).setUpdateOperation(true).setListOperation(true) + .setListOperationCondition(CodegenColumnListConditionEnum.EQ.getCondition()).setListOperationResult(true) + .setHtmlType(CodegenColumnHtmlTypeEnum.DATETIME.getType()); + CodegenColumnDO contactMemoColumn = new CodegenColumnDO().setColumnName("memo").setDataType(JdbcType.VARCHAR.name()) + .setColumnComment("备注").setNullable(false).setPrimaryKey(false) + .setOrdinalPosition(11).setJavaType("String").setJavaField("memo").setExample("我是备注") + .setCreateOperation(true).setUpdateOperation(true).setListOperation(false) + .setListOperationResult(true) + .setHtmlType(CodegenColumnHtmlTypeEnum.TEXTAREA.getType()); + CodegenColumnDO contactCreateTimeColumn = new CodegenColumnDO().setColumnName("create_time").setDataType(JdbcType.DATE.name()) + .setColumnComment("创建时间").setNullable(false).setPrimaryKey(false) + .setOrdinalPosition(12).setJavaType("LocalDateTime").setJavaField("createTime") + .setCreateOperation(false).setUpdateOperation(false).setListOperation(true) + .setListOperationCondition(CodegenColumnListConditionEnum.BETWEEN.getCondition()).setListOperationResult(true) + .setHtmlType(CodegenColumnHtmlTypeEnum.DATETIME.getType()); + List contactColumns = Arrays.asList(contactIdColumn, contactStudentIdColumn, + contactNameColumn, contactsAvatarColumn, contactVideoColumn, contactDescriptionColumn, + contactSex1Column, contactSex2Column, contactSex3Column, contactBirthdayColumn, contactMemoColumn, contactCreateTimeColumn); // 子表(地址) CodegenTableDO addressTable = new CodegenTableDO().setScene(CodegenSceneEnum.ADMIN.getScene()) .setTableName("infra_demo_student_address").setTableComment("学生地址表") @@ -222,26 +278,20 @@ public class CodegenEngineTest extends BaseMockitoUnitTest { .setOrdinalPosition(1).setJavaType("Long").setJavaField("id").setExample("1024") .setCreateOperation(false).setUpdateOperation(true).setListOperation(false) .setListOperationResult(true); - CodegenColumnDO addressStudentColumn = new CodegenColumnDO().setColumnName("student_id").setDataType(JdbcType.BIGINT.name()) + CodegenColumnDO addressStudentIdColumn = new CodegenColumnDO().setColumnName("student_id").setDataType(JdbcType.BIGINT.name()) .setColumnComment("学生编号").setNullable(false).setPrimaryKey(false) .setOrdinalPosition(2).setJavaType("Long").setJavaField("studentId").setExample("2048") .setCreateOperation(false).setUpdateOperation(true).setListOperation(false) .setListOperationResult(true) .setId(200L); - CodegenColumnDO addressDetailColumn = new CodegenColumnDO().setColumnName("detail").setDataType(JdbcType.VARCHAR.name()) - .setColumnComment("明细").setNullable(false).setPrimaryKey(false) - .setOrdinalPosition(3).setJavaType("String").setJavaField("detail").setExample("码头路 88 号") - .setCreateOperation(true).setUpdateOperation(true).setListOperation(true) - .setListOperationCondition(CodegenColumnListConditionEnum.LIKE.getCondition()).setListOperationResult(true) - .setHtmlType(CodegenColumnHtmlTypeEnum.INPUT.getType()); - List addressColumns = Arrays.asList(addressIdColumn, addressStudentColumn, addressDetailColumn); + List addressColumns = Arrays.asList(addressIdColumn, addressStudentIdColumn); // 调用 Map result = codegenEngine.execute(table, columns, Arrays.asList(contactTable, addressTable), Arrays.asList(contactColumns, addressColumns)); // 断言 - assertEquals(25, result.size()); + assertEquals(27, result.size()); for (Map.Entry entry : result.entrySet()) { System.out.println(entry.getKey());