Commit b47c4fbd8df97c6a5f8230f5a2fc8b71d43ba8ff

Authored by zhijiantianya@gmail.com
1 parent 06649624

代码生成器:增加树形结构的 vm 模版

Showing 15 changed files with 246 additions and 20 deletions
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/codegen/CodegenTableDO.java
@@ -138,4 +138,21 @@ public class CodegenTableDO extends BaseDO { @@ -138,4 +138,21 @@ public class CodegenTableDO extends BaseDO {
138 */ 138 */
139 private Boolean subJoinMany; 139 private Boolean subJoinMany;
140 140
  141 + // ========== 树表相关字段 ==========
  142 +
  143 + /**
  144 + * 树表的父字段编号
  145 + *
  146 + * 关联 {@link CodegenColumnDO#getId()}
  147 + */
  148 + private Long treeParentColumnId;
  149 + /**
  150 + * 树表的名字字段编号
  151 + *
  152 + * 名字的用途:新增或修改时,select 框展示的字段
  153 + *
  154 + * 关联 {@link CodegenColumnDO#getId()}
  155 + */
  156 + private Long treeNameColumnId;
  157 +
141 } 158 }
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenTemplateTypeEnum.java
@@ -4,6 +4,8 @@ import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; @@ -4,6 +4,8 @@ import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
4 import lombok.AllArgsConstructor; 4 import lombok.AllArgsConstructor;
5 import lombok.Getter; 5 import lombok.Getter;
6 6
  7 +import java.util.Objects;
  8 +
7 /** 9 /**
8 * 代码生成模板类型 10 * 代码生成模板类型
9 * 11 *
@@ -38,4 +40,14 @@ public enum CodegenTemplateTypeEnum { @@ -38,4 +40,14 @@ public enum CodegenTemplateTypeEnum {
38 MASTER_NORMAL.type, MASTER_ERP.type, MASTER_INNER.type); 40 MASTER_NORMAL.type, MASTER_ERP.type, MASTER_INNER.type);
39 } 41 }
40 42
  43 + /**
  44 + * 是否为树表
  45 + *
  46 + * @param type 类型
  47 + * @return 是否树表
  48 + */
  49 + public static boolean isTree(Integer type) {
  50 + return Objects.equals(type, TREE.type);
  51 + }
  52 +
41 } 53 }
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java
1 package cn.iocoder.yudao.module.infra.service.codegen.inner; 1 package cn.iocoder.yudao.module.infra.service.codegen.inner;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.lang.Filter;
4 import cn.hutool.core.map.MapUtil; 5 import cn.hutool.core.map.MapUtil;
5 import cn.hutool.core.util.ObjectUtil; 6 import cn.hutool.core.util.ObjectUtil;
6 import cn.hutool.core.util.StrUtil; 7 import cn.hutool.core.util.StrUtil;
@@ -67,7 +68,7 @@ public class CodegenEngine { @@ -67,7 +68,7 @@ public class CodegenEngine {
67 .put(javaTemplatePath("controller/vo/pageReqVO"), javaModuleImplVOFilePath("PageReqVO")) 68 .put(javaTemplatePath("controller/vo/pageReqVO"), javaModuleImplVOFilePath("PageReqVO"))
68 .put(javaTemplatePath("controller/vo/respVO"), javaModuleImplVOFilePath("RespVO")) 69 .put(javaTemplatePath("controller/vo/respVO"), javaModuleImplVOFilePath("RespVO"))
69 .put(javaTemplatePath("controller/vo/updateReqVO"), javaModuleImplVOFilePath("UpdateReqVO")) 70 .put(javaTemplatePath("controller/vo/updateReqVO"), javaModuleImplVOFilePath("UpdateReqVO"))
70 - .put(javaTemplatePath("controller/vo/exportReqVO"), javaModuleImplVOFilePath("ExportReqVO")) 71 + .put(javaTemplatePath("controller/vo/listReqVO"), javaModuleImplVOFilePath("ListReqVO"))
71 .put(javaTemplatePath("controller/vo/excelVO"), javaModuleImplVOFilePath("ExcelVO")) 72 .put(javaTemplatePath("controller/vo/excelVO"), javaModuleImplVOFilePath("ExcelVO"))
72 .put(javaTemplatePath("controller/controller"), javaModuleImplControllerFilePath()) 73 .put(javaTemplatePath("controller/controller"), javaModuleImplControllerFilePath())
73 .put(javaTemplatePath("convert/convert"), 74 .put(javaTemplatePath("convert/convert"),
@@ -216,8 +217,14 @@ public class CodegenEngine { @@ -216,8 +217,14 @@ public class CodegenEngine {
216 if (isSubTemplate(vmPath)) { 217 if (isSubTemplate(vmPath)) {
217 generateSubCode(table, subTables, result, vmPath, filePath, bindingMap); 218 generateSubCode(table, subTables, result, vmPath, filePath, bindingMap);
218 return; 219 return;
  220 + // 2.2 特殊:树表专属逻辑
  221 + } else if (isPageTemplate(vmPath)) {
  222 + // 减少多余的类生成,例如说 PageVO.java 类
  223 + if (CodegenTemplateTypeEnum.isTree(table.getTemplateType())) {
  224 + return;
  225 + }
219 } 226 }
220 - // 2.2 默认生成 227 + // 2.3 默认生成
221 generateCode(result, vmPath, filePath, bindingMap); 228 generateCode(result, vmPath, filePath, bindingMap);
222 }); 229 });
223 return result; 230 return result;
@@ -314,6 +321,14 @@ public class CodegenEngine { @@ -314,6 +321,14 @@ public class CodegenEngine {
314 // permission 前缀 321 // permission 前缀
315 bindingMap.put("permissionPrefix", table.getModuleName() + ":" + simpleClassNameStrikeCase); 322 bindingMap.put("permissionPrefix", table.getModuleName() + ":" + simpleClassNameStrikeCase);
316 323
  324 + // 特殊:树表专属逻辑
  325 + if (CodegenTemplateTypeEnum.isTree(table.getTemplateType())) {
  326 + bindingMap.put("treeParentColumn", CollUtil.findOne(columns,
  327 + column -> Objects.equals(column.getId(), table.getTreeParentColumnId())));
  328 + bindingMap.put("treeNameColumn", CollUtil.findOne(columns,
  329 + column -> Objects.equals(column.getId(), table.getTreeNameColumnId())));
  330 + }
  331 +
317 // 特殊:主子表专属逻辑 332 // 特殊:主子表专属逻辑
318 if (CollUtil.isNotEmpty(subTables)) { 333 if (CollUtil.isNotEmpty(subTables)) {
319 // 创建 bindingMap 334 // 创建 bindingMap
@@ -454,4 +469,8 @@ public class CodegenEngine { @@ -454,4 +469,8 @@ public class CodegenEngine {
454 return path.contains("_sub"); 469 return path.contains("_sub");
455 } 470 }
456 471
  472 + private static boolean isPageTemplate(String path) {
  473 + return path.contains("page");
  474 + }
  475 +
457 } 476 }
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/controller.vm
@@ -85,28 +85,42 @@ public class ${sceneEnum.prefixClass}${table.className}Controller { @@ -85,28 +85,42 @@ public class ${sceneEnum.prefixClass}${table.className}Controller {
85 return success(${table.className}Convert.INSTANCE.convert(${classNameVar})); 85 return success(${table.className}Convert.INSTANCE.convert(${classNameVar}));
86 } 86 }
87 87
  88 +#if ( $table.templateType != 2 )
88 @GetMapping("/page") 89 @GetMapping("/page")
89 @Operation(summary = "获得${table.classComment}分页") 90 @Operation(summary = "获得${table.classComment}分页")
90 #if ($sceneEnum.scene == 1) 91 #if ($sceneEnum.scene == 1)
91 @PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')") 92 @PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')")
92 #end 93 #end
93 - public CommonResult<PageResult<${sceneEnum.prefixClass}${table.className}RespVO>> get${simpleClassName}Page(@Valid ${sceneEnum.prefixClass}${table.className}PageReqVO pageVO) {  
94 - PageResult<${table.className}DO> pageResult = ${classNameVar}Service.get${simpleClassName}Page(pageVO); 94 + public CommonResult<PageResult<${sceneEnum.prefixClass}${table.className}RespVO>> get${simpleClassName}Page(@Valid ${sceneEnum.prefixClass}${table.className}PageReqVO pageReqVO) {
  95 + PageResult<${table.className}DO> pageResult = ${classNameVar}Service.get${simpleClassName}Page(pageReqVO);
95 return success(${table.className}Convert.INSTANCE.convertPage(pageResult)); 96 return success(${table.className}Convert.INSTANCE.convertPage(pageResult));
96 } 97 }
97 98
  99 +## 特殊:树表专属逻辑(树不需要分页接口)
  100 +#else
  101 + @GetMapping("/list")
  102 + @Operation(summary = "获得${table.classComment}列表")
  103 +#if ($sceneEnum.scene == 1)
  104 + @PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')")
  105 +#end
  106 + public CommonResult<List<${sceneEnum.prefixClass}${table.className}RespVO>> get${simpleClassName}List(@Valid ${sceneEnum.prefixClass}${table.className}ListReqVO listReqVO) {
  107 + List<${table.className}DO> list = ${classNameVar}Service.get${simpleClassName}List(listReqVO);
  108 + return success(${table.className}Convert.INSTANCE.convertList(list));
  109 + }
  110 +
  111 +#end
98 @GetMapping("/export-excel") 112 @GetMapping("/export-excel")
99 @Operation(summary = "导出${table.classComment} Excel") 113 @Operation(summary = "导出${table.classComment} Excel")
100 #if ($sceneEnum.scene == 1) 114 #if ($sceneEnum.scene == 1)
101 @PreAuthorize("@ss.hasPermission('${permissionPrefix}:export')") 115 @PreAuthorize("@ss.hasPermission('${permissionPrefix}:export')")
102 #end 116 #end
103 @OperateLog(type = EXPORT) 117 @OperateLog(type = EXPORT)
104 - public void export${simpleClassName}Excel(@Valid ${sceneEnum.prefixClass}${table.className}ExportReqVO exportReqVO, 118 + public void export${simpleClassName}Excel(@Valid ${sceneEnum.prefixClass}${table.className}ListReqVO listReqVO,
105 HttpServletResponse response) throws IOException { 119 HttpServletResponse response) throws IOException {
106 - List<${table.className}DO> list = ${classNameVar}Service.get${simpleClassName}List(exportReqVO); 120 + List<${table.className}DO> list = ${classNameVar}Service.get${simpleClassName}List(listReqVO);
107 // 导出 Excel 121 // 导出 Excel
108 - List<${sceneEnum.prefixClass}${table.className}ExcelVO> datas = ${table.className}Convert.INSTANCE.convertList02(list);  
109 - ExcelUtils.write(response, "${table.classComment}.xls", "数据", ${sceneEnum.prefixClass}${table.className}ExcelVO.class, datas); 122 + List<${sceneEnum.prefixClass}${table.className}ExcelVO> dataList = ${table.className}Convert.INSTANCE.convertList02(list);
  123 + ExcelUtils.write(response, "${table.classComment}.xls", "数据", ${sceneEnum.prefixClass}${table.className}ExcelVO.class, dataList);
110 } 124 }
111 125
112 ## 特殊:主子表专属逻辑 126 ## 特殊:主子表专属逻辑
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/exportReqVO.vm renamed to yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/listReqVO.vm
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/convert/convert.vm
@@ -26,8 +26,10 @@ public interface ${table.className}Convert { @@ -26,8 +26,10 @@ public interface ${table.className}Convert {
26 ${sceneEnum.prefixClass}${table.className}RespVO convert(${table.className}DO bean); 26 ${sceneEnum.prefixClass}${table.className}RespVO convert(${table.className}DO bean);
27 27
28 List<${sceneEnum.prefixClass}${table.className}RespVO> convertList(List<${table.className}DO> list); 28 List<${sceneEnum.prefixClass}${table.className}RespVO> convertList(List<${table.className}DO> list);
29 - 29 +## 特殊:树表专属逻辑(树不需要分页接口)
  30 +#if ( $table.templateType != 2 )
30 PageResult<${sceneEnum.prefixClass}${table.className}RespVO> convertPage(PageResult<${table.className}DO> page); 31 PageResult<${sceneEnum.prefixClass}${table.className}RespVO> convertPage(PageResult<${table.className}DO> page);
  32 +#end
31 33
32 List<${sceneEnum.prefixClass}${table.className}ExcelVO> convertList02(List<${table.className}DO> list); 34 List<${sceneEnum.prefixClass}${table.className}ExcelVO> convertList02(List<${table.className}DO> list);
33 35
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/mapper.vm
@@ -49,6 +49,8 @@ import ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePack @@ -49,6 +49,8 @@ import ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePack
49 @Mapper 49 @Mapper
50 public interface ${table.className}Mapper extends BaseMapperX<${table.className}DO> { 50 public interface ${table.className}Mapper extends BaseMapperX<${table.className}DO> {
51 51
  52 +## 特殊:树表专属逻辑(树不需要分页接口)
  53 +#if ( $table.templateType != 2 )
52 default PageResult<${table.className}DO> selectPage(${sceneEnum.prefixClass}${table.className}PageReqVO reqVO) { 54 default PageResult<${table.className}DO> selectPage(${sceneEnum.prefixClass}${table.className}PageReqVO reqVO) {
53 return selectPage(reqVO, new LambdaQueryWrapperX<${table.className}DO>() 55 return selectPage(reqVO, new LambdaQueryWrapperX<${table.className}DO>()
54 #listCondition() 56 #listCondition()
@@ -56,11 +58,20 @@ public interface ${table.className}Mapper extends BaseMapperX&lt;${table.className} @@ -56,11 +58,20 @@ public interface ${table.className}Mapper extends BaseMapperX&lt;${table.className}
56 58
57 } 59 }
58 60
59 - default List<${table.className}DO> selectList(${sceneEnum.prefixClass}${table.className}ExportReqVO reqVO) { 61 +#end
  62 + default List<${table.className}DO> selectList(${sceneEnum.prefixClass}${table.className}ListReqVO reqVO) {
60 return selectList(new LambdaQueryWrapperX<${table.className}DO>() 63 return selectList(new LambdaQueryWrapperX<${table.className}DO>()
61 #listCondition() 64 #listCondition()
62 .orderByDesc(${table.className}DO::getId));## 大多数情况下,id 倒序 65 .orderByDesc(${table.className}DO::getId));## 大多数情况下,id 倒序
63 66
64 } 67 }
65 68
  69 +## 特殊:树表专属逻辑
  70 +#if ( $table.templateType == 2 )
  71 +#set ($ParentJavaField = $treeParentColumn.javaField.substring(0,1).toUpperCase() + ${treeParentColumn.javaField.substring(1)})##首字母大写
  72 + default Long selectCountBy${ParentJavaField}(${treeParentColumn.javaType} ${treeParentColumn.javaField}) {
  73 + return selectCount(${table.className}DO::get${ParentJavaField}, ${treeParentColumn.javaField});
  74 + }
  75 +
  76 +#end
66 } 77 }
67 \ No newline at end of file 78 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/enums/errorcode.vm
1 // TODO 待办:请将下面的错误码复制到 yudao-module-${table.moduleName}-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!! 1 // TODO 待办:请将下面的错误码复制到 yudao-module-${table.moduleName}-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!!
2 // ========== ${table.classComment} TODO 补充编号 ========== 2 // ========== ${table.classComment} TODO 补充编号 ==========
3 -ErrorCode ${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS = new ErrorCode(TODO 补充编号, "${table.classComment}不存在");  
4 \ No newline at end of file 3 \ No newline at end of file
  4 +ErrorCode ${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS = new ErrorCode(TODO 补充编号, "${table.classComment}不存在");
  5 +ErrorCode ${simpleClassName_underlineCase.toUpperCase()}_EXITS_CHILDREN = new ErrorCode(TODO 补充编号, "存在存在子${table.classComment},无法删除");
5 \ No newline at end of file 6 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/service/service.vm
@@ -48,6 +48,8 @@ public interface ${table.className}Service { @@ -48,6 +48,8 @@ public interface ${table.className}Service {
48 */ 48 */
49 ${table.className}DO get${simpleClassName}(${primaryColumn.javaType} id); 49 ${table.className}DO get${simpleClassName}(${primaryColumn.javaType} id);
50 50
  51 +## 特殊:树表专属逻辑(树不需要分页接口)
  52 +#if ( $table.templateType != 2 )
51 /** 53 /**
52 * 获得${table.classComment}分页 54 * 获得${table.classComment}分页
53 * 55 *
@@ -56,13 +58,14 @@ public interface ${table.className}Service { @@ -56,13 +58,14 @@ public interface ${table.className}Service {
56 */ 58 */
57 PageResult<${table.className}DO> get${simpleClassName}Page(${sceneEnum.prefixClass}${table.className}PageReqVO pageReqVO); 59 PageResult<${table.className}DO> get${simpleClassName}Page(${sceneEnum.prefixClass}${table.className}PageReqVO pageReqVO);
58 60
  61 +#end
59 /** 62 /**
60 - * 获得${table.classComment}列表, 用于 Excel 导出 63 + * 获得${table.classComment}列表
61 * 64 *
62 - * @param exportReqVO 查询条件 65 + * @param listReqVO 查询条件
63 * @return ${table.classComment}列表 66 * @return ${table.classComment}列表
64 */ 67 */
65 - List<${table.className}DO> get${simpleClassName}List(${sceneEnum.prefixClass}${table.className}ExportReqVO exportReqVO); 68 + List<${table.className}DO> get${simpleClassName}List(${sceneEnum.prefixClass}${table.className}ListReqVO listReqVO);
66 69
67 ## 特殊:主子表专属逻辑 70 ## 特殊:主子表专属逻辑
68 #foreach ($subTable in $subTables) 71 #foreach ($subTable in $subTables)
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/service/serviceImpl.vm
@@ -110,6 +110,14 @@ public class ${table.className}ServiceImpl implements ${table.className}Service @@ -110,6 +110,14 @@ public class ${table.className}ServiceImpl implements ${table.className}Service
110 public void delete${simpleClassName}(${primaryColumn.javaType} id) { 110 public void delete${simpleClassName}(${primaryColumn.javaType} id) {
111 // 校验存在 111 // 校验存在
112 validate${simpleClassName}Exists(id); 112 validate${simpleClassName}Exists(id);
  113 +## 特殊:树表专属逻辑
  114 +#if ( $table.templateType == 2 )
  115 +#set ($ParentJavaField = $treeParentColumn.javaField.substring(0,1).toUpperCase() + ${treeParentColumn.javaField.substring(1)})##首字母大写
  116 + // 校验是否有子${table.classComment}
  117 + if (${classNameVar}Mapper.selectCountBy${ParentJavaField}(id) > 0) {
  118 + throw exception(${simpleClassName_underlineCase.toUpperCase()}_EXITS_CHILDREN);
  119 + }
  120 +#end
113 // 删除 121 // 删除
114 ${classNameVar}Mapper.deleteById(id); 122 ${classNameVar}Mapper.deleteById(id);
115 ## 特殊:主子表专属逻辑 123 ## 特殊:主子表专属逻辑
@@ -137,14 +145,17 @@ public class ${table.className}ServiceImpl implements ${table.className}Service @@ -137,14 +145,17 @@ public class ${table.className}ServiceImpl implements ${table.className}Service
137 return ${classNameVar}Mapper.selectById(id); 145 return ${classNameVar}Mapper.selectById(id);
138 } 146 }
139 147
  148 +## 特殊:树表专属逻辑(树不需要分页接口)
  149 +#if ( $table.templateType != 2 )
140 @Override 150 @Override
141 public PageResult<${table.className}DO> get${simpleClassName}Page(${sceneEnum.prefixClass}${table.className}PageReqVO pageReqVO) { 151 public PageResult<${table.className}DO> get${simpleClassName}Page(${sceneEnum.prefixClass}${table.className}PageReqVO pageReqVO) {
142 return ${classNameVar}Mapper.selectPage(pageReqVO); 152 return ${classNameVar}Mapper.selectPage(pageReqVO);
143 } 153 }
144 154
  155 +#end
145 @Override 156 @Override
146 - public List<${table.className}DO> get${simpleClassName}List(${sceneEnum.prefixClass}${table.className}ExportReqVO exportReqVO) {  
147 - return ${classNameVar}Mapper.selectList(exportReqVO); 157 + public List<${table.className}DO> get${simpleClassName}List(${sceneEnum.prefixClass}${table.className}ListReqVO listReqVO) {
  158 + return ${classNameVar}Mapper.selectList(listReqVO);
148 } 159 }
149 160
150 ## 特殊:主子表专属逻辑 161 ## 特殊:主子表专属逻辑
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/test/serviceTest.vm
@@ -137,6 +137,8 @@ public class ${table.className}ServiceImplTest extends BaseDbUnitTest { @@ -137,6 +137,8 @@ public class ${table.className}ServiceImplTest extends BaseDbUnitTest {
137 assertServiceException(() -> ${classNameVar}Service.delete${simpleClassName}(id), ${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS); 137 assertServiceException(() -> ${classNameVar}Service.delete${simpleClassName}(id), ${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS);
138 } 138 }
139 139
  140 +## 特殊:树表专属逻辑(树不需要分页接口)
  141 +#if ( $table.templateType != 2 )
140 @Test 142 @Test
141 @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 143 @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
142 public void testGet${simpleClassName}Page() { 144 public void testGet${simpleClassName}Page() {
@@ -150,10 +152,11 @@ public class ${table.className}ServiceImplTest extends BaseDbUnitTest { @@ -150,10 +152,11 @@ public class ${table.className}ServiceImplTest extends BaseDbUnitTest {
150 assertPojoEquals(db${simpleClassName}, pageResult.getList().get(0)); 152 assertPojoEquals(db${simpleClassName}, pageResult.getList().get(0));
151 } 153 }
152 154
  155 +#end
153 @Test 156 @Test
154 @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 157 @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
155 public void testGet${simpleClassName}List() { 158 public void testGet${simpleClassName}List() {
156 - #getPageCondition("ExportReqVO") 159 + #getPageCondition("ListReqVO")
157 160
158 // 调用 161 // 调用
159 List<${table.className}DO> list = ${classNameVar}Service.get${simpleClassName}List(reqVO); 162 List<${table.className}DO> list = ${classNameVar}Service.get${simpleClassName}List(reqVO);
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/api/api.ts.vm
@@ -15,10 +15,17 @@ export interface ${simpleClassName}VO { @@ -15,10 +15,17 @@ export interface ${simpleClassName}VO {
15 #end 15 #end
16 } 16 }
17 17
18 -// 查询${table.classComment}列表 18 +#if ( $table.templateType != 2 )
  19 +// 查询${table.classComment}分页
19 export const get${simpleClassName}Page = async (params) => { 20 export const get${simpleClassName}Page = async (params) => {
20 return await request.get({ url: `${baseURL}/page`, params }) 21 return await request.get({ url: `${baseURL}/page`, params })
21 } 22 }
  23 +#else
  24 +// 查询${table.classComment}列表
  25 +export const get${simpleClassName}List = async (params) => {
  26 + return await request.get({ url: `${baseURL}/list`, params })
  27 +}
  28 +#end
22 29
23 // 查询${table.classComment}详情 30 // 查询${table.classComment}详情
24 export const get${simpleClassName} = async (id: number) => { 31 export const get${simpleClassName} = async (id: number) => {
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/form.vue.vm
@@ -22,7 +22,22 @@ @@ -22,7 +22,22 @@
22 #elseif ($javaType == "Boolean") 22 #elseif ($javaType == "Boolean")
23 #set ($dictMethod = "getBoolDictOptions") 23 #set ($dictMethod = "getBoolDictOptions")
24 #end 24 #end
25 - #if ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里 25 + #if ( $table.templateType == 2 && $column.id == $treeParentColumn.id )
  26 + <el-form-item label="${comment}" prop="${javaField}">
  27 + <el-tree-select
  28 + v-model="formData.${javaField}"
  29 + :data="${classNameVar}Tree"
  30 + #if ($treeNameColumn.javaField == "name")
  31 + :props="defaultProps"
  32 + #else
  33 + :props="{...defaultProps, label: '$treeNameColumn.javaField'}"
  34 + #end
  35 + check-strictly
  36 + default-expand-all
  37 + placeholder="请选择${comment}"
  38 + />
  39 + </el-form-item>
  40 + #elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
26 <el-form-item label="${comment}" prop="${javaField}"> 41 <el-form-item label="${comment}" prop="${javaField}">
27 <el-input v-model="formData.${javaField}" placeholder="请输入${comment}" /> 42 <el-input v-model="formData.${javaField}" placeholder="请输入${comment}" />
28 </el-form-item> 43 </el-form-item>
@@ -126,6 +141,10 @@ @@ -126,6 +141,10 @@
126 <script setup lang="ts"> 141 <script setup lang="ts">
127 import { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict' 142 import { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
128 import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${table.businessName}' 143 import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${table.businessName}'
  144 +## 特殊:树表专属逻辑
  145 +#if ( $table.templateType == 2 )
  146 +import { defaultProps, handleTree } from '@/utils/tree'
  147 +#end
129 ## 特殊:主子表专属逻辑 148 ## 特殊:主子表专属逻辑
130 #foreach ($subSimpleClassName in $subSimpleClassNames) 149 #foreach ($subSimpleClassName in $subSimpleClassNames)
131 import ${subSimpleClassName}Form from './components/${subSimpleClassName}Form.vue' 150 import ${subSimpleClassName}Form from './components/${subSimpleClassName}Form.vue'
@@ -158,6 +177,10 @@ const formRules = reactive({ @@ -158,6 +177,10 @@ const formRules = reactive({
158 #end 177 #end
159 }) 178 })
160 const formRef = ref() // 表单 Ref 179 const formRef = ref() // 表单 Ref
  180 +## 特殊:树表专属逻辑
  181 +#if ( $table.templateType == 2 )
  182 +const ${classNameVar}Tree = ref() // 树形结构
  183 +#end
161 ## 特殊:主子表专属逻辑 184 ## 特殊:主子表专属逻辑
162 #if ( $subTables && $subTables.size() > 0 ) 185 #if ( $subTables && $subTables.size() > 0 )
163 186
@@ -183,6 +206,10 @@ const open = async (type: string, id?: number) =&gt; { @@ -183,6 +206,10 @@ const open = async (type: string, id?: number) =&gt; {
183 formLoading.value = false 206 formLoading.value = false
184 } 207 }
185 } 208 }
  209 +## 特殊:树表专属逻辑
  210 +#if ( $table.templateType == 2 )
  211 + await get${simpleClassName}Tree()
  212 +#end
186 } 213 }
187 defineExpose({ open }) // 提供 open 方法,用于打开弹窗 214 defineExpose({ open }) // 提供 open 方法,用于打开弹窗
188 215
@@ -248,4 +275,16 @@ const resetForm = () =&gt; { @@ -248,4 +275,16 @@ const resetForm = () =&gt; {
248 } 275 }
249 formRef.value?.resetFields() 276 formRef.value?.resetFields()
250 } 277 }
  278 +## 特殊:树表专属逻辑
  279 +#if ( $table.templateType == 2 )
  280 +
  281 +/** 获得${table.classComment}树 */
  282 +const get${simpleClassName}Tree = async () => {
  283 + ${classNameVar}Tree.value = []
  284 + const data = await ${simpleClassName}Api.get${simpleClassName}List()
  285 + const root: Tree = { id: 0, name: '顶级${table.classComment}', children: [] }
  286 + root.children = handleTree(data, 'id', '${treeParentColumn.javaField}')
  287 + ${classNameVar}Tree.value.push(root)
  288 +}
  289 +#end
251 </script> 290 </script>
252 \ No newline at end of file 291 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm
@@ -101,6 +101,12 @@ @@ -101,6 +101,12 @@
101 > 101 >
102 <Icon icon="ep:download" class="mr-5px" /> 导出 102 <Icon icon="ep:download" class="mr-5px" /> 导出
103 </el-button> 103 </el-button>
  104 +## 特殊:树表专属逻辑
  105 +#if ( $table.templateType == 2 )
  106 + <el-button type="danger" plain @click="toggleExpandAll">
  107 + <Icon icon="ep:sort" class="mr-5px" /> 展开/折叠
  108 + </el-button>
  109 +#end
104 </el-form-item> 110 </el-form-item>
105 </el-form> 111 </el-form>
106 </ContentWrap> 112 </ContentWrap>
@@ -117,6 +123,17 @@ @@ -117,6 +123,17 @@
117 highlight-current-row 123 highlight-current-row
118 @current-change="handleCurrentChange" 124 @current-change="handleCurrentChange"
119 > 125 >
  126 +## 特殊:树表专属逻辑
  127 +#elseif ( $table.templateType == 2 )
  128 + <el-table
  129 + v-loading="loading"
  130 + :data="list"
  131 + :stripe="true"
  132 + :show-overflow-tooltip="true"
  133 + row-key="id"
  134 + :default-expand-all="isExpandAll"
  135 + v-if="refreshTable"
  136 + >
120 #else 137 #else
121 <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> 138 <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
122 #end 139 #end
@@ -218,6 +235,10 @@ @@ -218,6 +235,10 @@
218 <script setup lang="ts"> 235 <script setup lang="ts">
219 import { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict' 236 import { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
220 import { dateFormatter } from '@/utils/formatTime' 237 import { dateFormatter } from '@/utils/formatTime'
  238 +## 特殊:树表专属逻辑
  239 +#if ( $table.templateType == 2 )
  240 +import { handleTree } from '@/utils/tree'
  241 +#end
221 import download from '@/utils/download' 242 import download from '@/utils/download'
222 import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${table.businessName}' 243 import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${table.businessName}'
223 import ${simpleClassName}Form from './${simpleClassName}Form.vue' 244 import ${simpleClassName}Form from './${simpleClassName}Form.vue'
@@ -232,11 +253,17 @@ const message = useMessage() // 消息弹窗 @@ -232,11 +253,17 @@ const message = useMessage() // 消息弹窗
232 const { t } = useI18n() // 国际化 253 const { t } = useI18n() // 国际化
233 254
234 const loading = ref(true) // 列表的加载中 255 const loading = ref(true) // 列表的加载中
235 -const total = ref(0) // 列表的总页数  
236 const list = ref([]) // 列表的数据 256 const list = ref([]) // 列表的数据
  257 +## 特殊:树表专属逻辑(树不需要分页接口)
  258 +#if ( $table.templateType != 2 )
  259 +const total = ref(0) // 列表的总页数
  260 +#end
237 const queryParams = reactive({ 261 const queryParams = reactive({
  262 +## 特殊:树表专属逻辑(树不需要分页接口)
  263 +#if ( $table.templateType != 2 )
238 pageNo: 1, 264 pageNo: 1,
239 pageSize: 10, 265 pageSize: 10,
  266 +#end
240 #foreach ($column in $columns) 267 #foreach ($column in $columns)
241 #if ($column.listOperation) 268 #if ($column.listOperation)
242 #if ($column.listOperationCondition != 'BETWEEN') 269 #if ($column.listOperationCondition != 'BETWEEN')
@@ -255,9 +282,15 @@ const exportLoading = ref(false) // 导出的加载中 @@ -255,9 +282,15 @@ const exportLoading = ref(false) // 导出的加载中
255 const getList = async () => { 282 const getList = async () => {
256 loading.value = true 283 loading.value = true
257 try { 284 try {
  285 +## 特殊:树表专属逻辑(树不需要分页接口)
  286 + #if ( $table.templateType == 2 )
  287 + const data = await ${simpleClassName}Api.get${simpleClassName}List(queryParams)
  288 + list.value = handleTree(data, 'id', '${treeParentColumn.javaField}')
  289 + #else
258 const data = await ${simpleClassName}Api.get${simpleClassName}Page(queryParams) 290 const data = await ${simpleClassName}Api.get${simpleClassName}Page(queryParams)
259 list.value = data.list 291 list.value = data.list
260 total.value = data.total 292 total.value = data.total
  293 + #end
261 } finally { 294 } finally {
262 loading.value = false 295 loading.value = false
263 } 296 }
@@ -317,6 +350,19 @@ const handleCurrentChange = (row) =&gt; { @@ -317,6 +350,19 @@ const handleCurrentChange = (row) =&gt; {
317 currentRow.value = row 350 currentRow.value = row
318 } 351 }
319 #end 352 #end
  353 +## 特殊:树表专属逻辑
  354 +#if ( $table.templateType == 2 )
  355 +
  356 +/** 展开/折叠操作 */
  357 +const isExpandAll = ref(true) // 是否展开,默认全部展开
  358 +const refreshTable = ref(true) // 重新渲染表格状态
  359 +const toggleExpandAll = async () => {
  360 + refreshTable.value = false
  361 + isExpandAll.value = !isExpandAll.value
  362 + await nextTick()
  363 + refreshTable.value = true
  364 +}
  365 +#end
320 366
321 /** 初始化 **/ 367 /** 初始化 **/
322 onMounted(() => { 368 onMounted(() => {
yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineTest.java
@@ -43,6 +43,47 @@ public class CodegenEngineTest extends BaseMockitoUnitTest { @@ -43,6 +43,47 @@ public class CodegenEngineTest extends BaseMockitoUnitTest {
43 } 43 }
44 44
45 @Test 45 @Test
  46 + public void testExecute_vue3_tree() {
  47 + // 准备请求参数
  48 + CodegenTableDO table = new CodegenTableDO().setScene(CodegenSceneEnum.ADMIN.getScene()).setParentMenuId(888L)
  49 + .setTableName("infra_demo66_student").setTableComment("学生表")
  50 + .setModuleName("infra").setBusinessName("demo66").setClassName("InfraDemo66Student")
  51 + .setClassComment("学生").setAuthor("芋道源码")
  52 + .setTemplateType(CodegenTemplateTypeEnum.TREE.getType())
  53 + .setFrontType(CodegenFrontTypeEnum.VUE3.getType())
  54 + .setTreeParentColumnId(22L).setTreeNameColumnId(11L)
  55 + ;
  56 + CodegenColumnDO idColumn = new CodegenColumnDO().setColumnName("id").setDataType(JdbcType.BIGINT.name())
  57 + .setColumnComment("编号").setNullable(false).setPrimaryKey(true).setAutoIncrement(true)
  58 + .setJavaType("Long").setJavaField("id").setExample("1024")
  59 + .setCreateOperation(false).setUpdateOperation(true)
  60 + .setListOperation(false)
  61 + .setListOperationResult(true);
  62 + CodegenColumnDO nameColumn = new CodegenColumnDO().setColumnName("name").setDataType(JdbcType.VARCHAR.name())
  63 + .setId(11L)
  64 + .setColumnComment("名字").setNullable(false)
  65 + .setJavaType("String").setJavaField("name").setExample("芋头")
  66 + .setCreateOperation(true).setUpdateOperation(true)
  67 + .setListOperation(true).setListOperationCondition(CodegenColumnListConditionEnum.LIKE.getCondition())
  68 + .setListOperationResult(true)
  69 + .setHtmlType(CodegenColumnHtmlTypeEnum.INPUT.getType());
  70 + CodegenColumnDO parentIdColumn = new CodegenColumnDO().setColumnName("description").setDataType(JdbcType.VARCHAR.name())
  71 + .setId(22L)
  72 + .setColumnComment("父编号").setNullable(false)
  73 + .setJavaType("Long").setJavaField("parentId").setExample("2048")
  74 + .setCreateOperation(true).setUpdateOperation(true)
  75 + .setListOperation(false)
  76 + .setListOperationResult(true);
  77 + List<CodegenColumnDO> columns = Arrays.asList(idColumn, nameColumn, parentIdColumn);
  78 +
  79 + // 调用
  80 + Map<String, String> result = codegenEngine.execute(table, columns, null, null);
  81 +
  82 + // 构建 zip 包
  83 + writeFile(result, "/Users/yunai/test/demo66.zip");
  84 + }
  85 +
  86 + @Test
46 public void testExecute_vue3_crud() { 87 public void testExecute_vue3_crud() {
47 // 准备请求参数 88 // 准备请求参数
48 CodegenTableDO table = new CodegenTableDO().setScene(CodegenSceneEnum.ADMIN.getScene()).setParentMenuId(888L) 89 CodegenTableDO table = new CodegenTableDO().setScene(CodegenSceneEnum.ADMIN.getScene()).setParentMenuId(888L)