Commit 40d2882890f596d6e2b68ba5ca4c0db24622049f

Authored by YunaiV
1 parent 7d89ce5a

🎈 perf(codegen): 优化代码生成器 Excel 导入:新增 import-enable 开关,Vue3 / Vben5 Schema 抽独立 Im…

…portForm 组件对齐 user 模块,补齐 Vben5 4 套 frontType 单测与 importEnable=true 断言
基于 #1082 #1083 的二开
Showing 34 changed files with 1123 additions and 336 deletions
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/codegen/config/CodegenProperties.java
@@ -55,4 +55,10 @@ public class CodegenProperties { @@ -55,4 +55,10 @@ public class CodegenProperties {
55 @NotNull(message = "是否生成单元测试不能为空") 55 @NotNull(message = "是否生成单元测试不能为空")
56 private Boolean unitTestEnable; 56 private Boolean unitTestEnable;
57 57
  58 + /**
  59 + * 是否生成 Excel 导入接口
  60 + */
  61 + @NotNull(message = "是否生成 Excel 导入接口不能为空")
  62 + private Boolean importEnable;
  63 +
58 } 64 }
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java
@@ -128,6 +128,8 @@ public class CodegenEngine { @@ -128,6 +128,8 @@ public class CodegenEngine {
128 vue3FilePath("views/${table.moduleName}/${table.businessName}/index.vue")) 128 vue3FilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
129 .put(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), vue3TemplatePath("views/form.vue"), 129 .put(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), vue3TemplatePath("views/form.vue"),
130 vue3FilePath("views/${table.moduleName}/${table.businessName}/${simpleClassName}Form.vue")) 130 vue3FilePath("views/${table.moduleName}/${table.businessName}/${simpleClassName}Form.vue"))
  131 + .put(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), vue3TemplatePath("views/import.vue"),
  132 + vue3FilePath("views/${table.moduleName}/${table.businessName}/${simpleClassName}ImportForm.vue"))
131 .put(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), vue3TemplatePath("views/components/form_sub_normal.vue"), // 特殊:主子表专属逻辑 133 .put(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), vue3TemplatePath("views/components/form_sub_normal.vue"), // 特殊:主子表专属逻辑
132 vue3FilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}Form.vue")) 134 vue3FilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}Form.vue"))
133 .put(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), vue3TemplatePath("views/components/form_sub_inner.vue"), // 特殊:主子表专属逻辑 135 .put(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), vue3TemplatePath("views/components/form_sub_inner.vue"), // 特殊:主子表专属逻辑
@@ -166,6 +168,8 @@ public class CodegenEngine { @@ -166,6 +168,8 @@ public class CodegenEngine {
166 vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/index.vue")) 168 vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
167 .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/form.vue"), 169 .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/form.vue"),
168 vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/form.vue")) 170 vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/form.vue"))
  171 + .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/import.vue"),
  172 + vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/import-form.vue"))
169 .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("api/api.ts"), 173 .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("api/api.ts"),
170 vue3VbenFilePath("api/${table.moduleName}/${table.businessName}/index.ts")) 174 vue3VbenFilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
171 .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/form_sub_normal.vue"), // 特殊:主子表专属逻辑 175 .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/form_sub_normal.vue"), // 特殊:主子表专属逻辑
@@ -183,6 +187,8 @@ public class CodegenEngine { @@ -183,6 +187,8 @@ public class CodegenEngine {
183 vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/index.vue")) 187 vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
184 .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/form.vue"), 188 .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/form.vue"),
185 vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/form.vue")) 189 vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/form.vue"))
  190 + .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/import.vue"),
  191 + vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/import-form.vue"))
186 .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("api/api.ts"), 192 .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("api/api.ts"),
187 vue3VbenFilePath("api/${table.moduleName}/${table.businessName}/index.ts")) 193 vue3VbenFilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
188 .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/form_sub_normal.vue"), // 特殊:主子表专属逻辑 194 .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/form_sub_normal.vue"), // 特殊:主子表专属逻辑
@@ -202,6 +208,8 @@ public class CodegenEngine { @@ -202,6 +208,8 @@ public class CodegenEngine {
202 vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/index.vue")) 208 vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
203 .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath("views/form.vue"), 209 .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath("views/form.vue"),
204 vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/form.vue")) 210 vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/form.vue"))
  211 + .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath("views/import.vue"),
  212 + vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/import-form.vue"))
205 .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath("api/api.ts"), 213 .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath("api/api.ts"),
206 vue3VbenFilePath("api/${table.moduleName}/${table.businessName}/index.ts")) 214 vue3VbenFilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
207 .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath("views/modules/form_sub_normal.vue"), // 特殊:主子表专属逻辑 215 .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath("views/modules/form_sub_normal.vue"), // 特殊:主子表专属逻辑
@@ -219,6 +227,8 @@ public class CodegenEngine { @@ -219,6 +227,8 @@ public class CodegenEngine {
219 vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/index.vue")) 227 vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
220 .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL.getType(), vue3Vben5EpGeneralTemplatePath("views/form.vue"), 228 .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL.getType(), vue3Vben5EpGeneralTemplatePath("views/form.vue"),
221 vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/form.vue")) 229 vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/form.vue"))
  230 + .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL.getType(), vue3Vben5EpGeneralTemplatePath("views/import.vue"),
  231 + vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/import-form.vue"))
222 .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL.getType(), vue3Vben5EpGeneralTemplatePath("api/api.ts"), 232 .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL.getType(), vue3Vben5EpGeneralTemplatePath("api/api.ts"),
223 vue3VbenFilePath("api/${table.moduleName}/${table.businessName}/index.ts")) 233 vue3VbenFilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
224 .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL.getType(), vue3Vben5EpGeneralTemplatePath("views/modules/form_sub_normal.vue"), // 特殊:主子表专属逻辑 234 .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL.getType(), vue3Vben5EpGeneralTemplatePath("views/modules/form_sub_normal.vue"), // 特殊:主子表专属逻辑
@@ -286,6 +296,7 @@ public class CodegenEngine { @@ -286,6 +296,7 @@ public class CodegenEngine {
286 globalBindingMap.put("jakartaPackage", jakartaEnable ? "jakarta" : "javax"); 296 globalBindingMap.put("jakartaPackage", jakartaEnable ? "jakarta" : "javax");
287 globalBindingMap.put("voType", codegenProperties.getVoType()); 297 globalBindingMap.put("voType", codegenProperties.getVoType());
288 globalBindingMap.put("deleteBatchEnable", codegenProperties.getDeleteBatchEnable()); 298 globalBindingMap.put("deleteBatchEnable", codegenProperties.getDeleteBatchEnable());
  299 + globalBindingMap.put("importEnable", codegenProperties.getImportEnable());
289 // 全局 Java Bean 300 // 全局 Java Bean
290 globalBindingMap.put("CommonResultClassName", CommonResult.class.getName()); 301 globalBindingMap.put("CommonResultClassName", CommonResult.class.getName());
291 globalBindingMap.put("PageResultClassName", PageResult.class.getName()); 302 globalBindingMap.put("PageResultClassName", PageResult.class.getName());
@@ -345,6 +356,11 @@ public class CodegenEngine { @@ -345,6 +356,11 @@ public class CodegenEngine {
345 if (!CodegenTemplateTypeEnum.isTree(table.getTemplateType())) { 356 if (!CodegenTemplateTypeEnum.isTree(table.getTemplateType())) {
346 return; 357 return;
347 } 358 }
  359 + } else if (isImportTemplate(vmPath)) {
  360 + // 关闭 import 时,跳过 ImportExcelVO / ImportRespVO 的生成
  361 + if (!Boolean.TRUE.equals(codegenProperties.getImportEnable())) {
  362 + return;
  363 + }
348 } 364 }
349 // 2.3 默认生成 365 // 2.3 默认生成
350 generateCode(result, vmPath, filePath, bindingMap); 366 generateCode(result, vmPath, filePath, bindingMap);
@@ -678,4 +694,9 @@ public class CodegenEngine { @@ -678,4 +694,9 @@ public class CodegenEngine {
678 return path.contains("listReqVO"); 694 return path.contains("listReqVO");
679 } 695 }
680 696
  697 + private static boolean isImportTemplate(String path) {
  698 + return path.contains("importExcelVO") || path.contains("importRespVO")
  699 + || path.contains("views/import.vue");
  700 + }
  701 +
681 } 702 }
yudao-module-infra/src/main/resources/codegen/java/controller/controller.vm
1 package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}; 1 package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName};
2 2
3 import org.springframework.web.bind.annotation.*; 3 import org.springframework.web.bind.annotation.*;
  4 +#if ($importEnable)
4 import org.springframework.web.multipart.MultipartFile; 5 import org.springframework.web.multipart.MultipartFile;
  6 +#end
5 import ${jakartaPackage}.annotation.Resource; 7 import ${jakartaPackage}.annotation.Resource;
6 import org.springframework.validation.annotation.Validated; 8 import org.springframework.validation.annotation.Validated;
7 #if ($sceneEnum.scene == 1)import org.springframework.security.access.prepost.PreAuthorize;#end 9 #if ($sceneEnum.scene == 1)import org.springframework.security.access.prepost.PreAuthorize;#end
@@ -160,13 +162,14 @@ public class ${sceneEnum.prefixClass}${table.className}Controller { @@ -160,13 +162,14 @@ public class ${sceneEnum.prefixClass}${table.className}Controller {
160 BeanUtils.toBean(list, ${table.className}RespVO.class)); 162 BeanUtils.toBean(list, ${table.className}RespVO.class));
161 } 163 }
162 #end 164 #end
  165 +#if ($importEnable)
163 166
164 @GetMapping("/get-import-template") 167 @GetMapping("/get-import-template")
165 @Operation(summary = "获得导入${table.classComment}模板") 168 @Operation(summary = "获得导入${table.classComment}模板")
166 #if ($sceneEnum.scene == 1) 169 #if ($sceneEnum.scene == 1)
167 @PreAuthorize("@ss.hasPermission('${permissionPrefix}:import')") 170 @PreAuthorize("@ss.hasPermission('${permissionPrefix}:import')")
168 #end 171 #end
169 - public void import${simpleClassName}Template(HttpServletResponse response) throws IOException { 172 + public void importTemplate(HttpServletResponse response) throws IOException {
170 ExcelUtils.write(response, "${table.classComment}导入模板.xls", "数据", 173 ExcelUtils.write(response, "${table.classComment}导入模板.xls", "数据",
171 ${sceneEnum.prefixClass}${table.className}ImportExcelVO.class, Collections.emptyList()); 174 ${sceneEnum.prefixClass}${table.className}ImportExcelVO.class, Collections.emptyList());
172 } 175 }
@@ -177,11 +180,11 @@ public class ${sceneEnum.prefixClass}${table.className}Controller { @@ -177,11 +180,11 @@ public class ${sceneEnum.prefixClass}${table.className}Controller {
177 #if ($sceneEnum.scene == 1) 180 #if ($sceneEnum.scene == 1)
178 @PreAuthorize("@ss.hasPermission('${permissionPrefix}:import')") 181 @PreAuthorize("@ss.hasPermission('${permissionPrefix}:import')")
179 #end 182 #end
180 - @ApiAccessLog(operateType = IMPORT)  
181 - public CommonResult<${sceneEnum.prefixClass}${table.className}ImportRespVO> import${simpleClassName}(@RequestParam("file") MultipartFile file) throws Exception { 183 + public CommonResult<${sceneEnum.prefixClass}${table.className}ImportRespVO> importExcel(@RequestParam("file") MultipartFile file) throws Exception {
182 List<${sceneEnum.prefixClass}${table.className}ImportExcelVO> list = ExcelUtils.read(file, ${sceneEnum.prefixClass}${table.className}ImportExcelVO.class); 184 List<${sceneEnum.prefixClass}${table.className}ImportExcelVO> list = ExcelUtils.read(file, ${sceneEnum.prefixClass}${table.className}ImportExcelVO.class);
183 return success(${classNameVar}Service.import${simpleClassName}List(list)); 185 return success(${classNameVar}Service.import${simpleClassName}List(list));
184 } 186 }
  187 +#end
185 188
186 ## 特殊:主子表专属逻辑 189 ## 特殊:主子表专属逻辑
187 #foreach ($subTable in $subTables) 190 #foreach ($subTable in $subTables)
yudao-module-infra/src/main/resources/codegen/java/service/service.vm
@@ -25,6 +25,7 @@ public interface ${table.className}Service { @@ -25,6 +25,7 @@ public interface ${table.className}Service {
25 * @return 编号 25 * @return 编号
26 */ 26 */
27 ${primaryColumn.javaType} create${simpleClassName}(@Valid ${saveReqVOClass} ${saveReqVOVar}); 27 ${primaryColumn.javaType} create${simpleClassName}(@Valid ${saveReqVOClass} ${saveReqVOVar});
  28 +#if ($importEnable)
28 29
29 /** 30 /**
30 * 导入${table.classComment} 31 * 导入${table.classComment}
@@ -33,6 +34,7 @@ public interface ${table.className}Service { @@ -33,6 +34,7 @@ public interface ${table.className}Service {
33 * @return 导入结果 34 * @return 导入结果
34 */ 35 */
35 ${sceneEnum.prefixClass}${table.className}ImportRespVO import${simpleClassName}List(List<${sceneEnum.prefixClass}${table.className}ImportExcelVO> importList); 36 ${sceneEnum.prefixClass}${table.className}ImportRespVO import${simpleClassName}List(List<${sceneEnum.prefixClass}${table.className}ImportExcelVO> importList);
  37 +#end
36 38
37 /** 39 /**
38 * 更新${table.classComment} 40 * 更新${table.classComment}
yudao-module-infra/src/main/resources/codegen/java/service/serviceImpl.vm
@@ -7,7 +7,9 @@ import org.springframework.validation.annotation.Validated; @@ -7,7 +7,9 @@ import org.springframework.validation.annotation.Validated;
7 import org.springframework.transaction.annotation.Transactional; 7 import org.springframework.transaction.annotation.Transactional;
8 8
9 import java.util.*; 9 import java.util.*;
  10 +#if ($importEnable)
10 import java.util.concurrent.atomic.AtomicInteger; 11 import java.util.concurrent.atomic.AtomicInteger;
  12 +#end
11 import ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*; 13 import ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*;
12 import ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO; 14 import ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO;
13 ## 特殊:主子表专属逻辑 15 ## 特殊:主子表专属逻辑
@@ -92,46 +94,32 @@ public class ${table.className}ServiceImpl implements ${table.className}Service @@ -92,46 +94,32 @@ public class ${table.className}ServiceImpl implements ${table.className}Service
92 // 返回 94 // 返回
93 return ${classNameVar}.getId(); 95 return ${classNameVar}.getId();
94 } 96 }
  97 +#if ($importEnable)
95 98
96 @Override 99 @Override
97 - @Transactional(rollbackFor = Exception.class) 100 + @Transactional(rollbackFor = Exception.class) // 添加事务,异常则回滚所有导入
98 public ${sceneEnum.prefixClass}${table.className}ImportRespVO import${simpleClassName}List(List<${sceneEnum.prefixClass}${table.className}ImportExcelVO> importList) { 101 public ${sceneEnum.prefixClass}${table.className}ImportRespVO import${simpleClassName}List(List<${sceneEnum.prefixClass}${table.className}ImportExcelVO> importList) {
99 if (CollUtil.isEmpty(importList)) { 102 if (CollUtil.isEmpty(importList)) {
100 return ${sceneEnum.prefixClass}${table.className}ImportRespVO.builder() 103 return ${sceneEnum.prefixClass}${table.className}ImportRespVO.builder()
101 - .successCount(0)  
102 - .failureCount(0)  
103 - .failureRows(new LinkedHashMap<>())  
104 - .build(); 104 + .successCount(0).failureCount(0).failureRows(new LinkedHashMap<>()).build();
105 } 105 }
106 - AtomicInteger successCount = new AtomicInteger();  
107 - Map<Integer, String> failureRows = new LinkedHashMap<>(); 106 + // 遍历,逐个创建
  107 + ${sceneEnum.prefixClass}${table.className}ImportRespVO respVO = ${sceneEnum.prefixClass}${table.className}ImportRespVO.builder()
  108 + .successCount(0).failureCount(0).failureRows(new LinkedHashMap<>()).build();
108 AtomicInteger index = new AtomicInteger(1); 109 AtomicInteger index = new AtomicInteger(1);
109 importList.forEach(importItem -> { 110 importList.forEach(importItem -> {
110 int currentIndex = index.getAndIncrement(); 111 int currentIndex = index.getAndIncrement();
111 try { 112 try {
112 create${simpleClassName}(BeanUtils.toBean(importItem, ${saveReqVOClass}.class)); 113 create${simpleClassName}(BeanUtils.toBean(importItem, ${saveReqVOClass}.class));
113 - successCount.incrementAndGet(); 114 + respVO.setSuccessCount(respVO.getSuccessCount() + 1);
114 } catch (Exception ex) { 115 } catch (Exception ex) {
115 - failureRows.put(currentIndex, resolveImportFailureMessage(ex)); 116 + respVO.getFailureRows().put(currentIndex, ex.getMessage());
116 } 117 }
117 }); 118 });
118 - return ${sceneEnum.prefixClass}${table.className}ImportRespVO.builder()  
119 - .successCount(successCount.get())  
120 - .failureCount(failureRows.size())  
121 - .failureRows(failureRows)  
122 - .build();  
123 - }  
124 -  
125 - /**  
126 - * 仅返回根因异常信息,避免把 MyBatis 长堆栈直接暴露给前端。  
127 - */  
128 - private String resolveImportFailureMessage(Exception ex) {  
129 - Throwable root = ex;  
130 - while (root.getCause() != null && root.getCause() != root) {  
131 - root = root.getCause();  
132 - }  
133 - return root.getMessage() != null ? root.getMessage() : ex.getMessage(); 119 + respVO.setFailureCount(respVO.getFailureRows().size());
  120 + return respVO;
134 } 121 }
  122 +#end
135 123
136 @Override 124 @Override
137 ## 特殊:主子表专属逻辑(非 ERP 模式) 125 ## 特殊:主子表专属逻辑(非 ERP 模式)
yudao-module-infra/src/main/resources/codegen/sql/sql.vm
1 ## 通用变量定义 1 ## 通用变量定义
  2 +#if ($importEnable)
2 #set ($functionNames = ['查询', '创建', '更新', '删除', '导出', '导入']) 3 #set ($functionNames = ['查询', '创建', '更新', '删除', '导出', '导入'])
3 #set ($functionOps = ['query', 'create', 'update', 'delete', 'export', 'import']) 4 #set ($functionOps = ['query', 'create', 'update', 'delete', 'export', 'import'])
  5 +#else
  6 +#set ($functionNames = ['查询', '创建', '更新', '删除', '导出'])
  7 +#set ($functionOps = ['query', 'create', 'update', 'delete', 'export'])
  8 +#end
4 ## 9 ##
5 ## 宏定义:生成按钮 SQL(通用部分) 10 ## 宏定义:生成按钮 SQL(通用部分)
6 #macro(insertButtonSql $parentIdVar) 11 #macro(insertButtonSql $parentIdVar)
yudao-module-infra/src/main/resources/codegen/vue/api/api.js.vm
@@ -73,6 +73,7 @@ export function export${simpleClassName}Excel(params) { @@ -73,6 +73,7 @@ export function export${simpleClassName}Excel(params) {
73 responseType: 'blob' 73 responseType: 'blob'
74 }) 74 })
75 } 75 }
  76 +#if ($importEnable)
76 77
77 // 下载${table.classComment}导入模板 78 // 下载${table.classComment}导入模板
78 export function import${simpleClassName}Template() { 79 export function import${simpleClassName}Template() {
@@ -91,6 +92,7 @@ export function import${simpleClassName}(data) { @@ -91,6 +92,7 @@ export function import${simpleClassName}(data) {
91 data 92 data
92 }) 93 })
93 } 94 }
  95 +#end
94 ## 特殊:主子表专属逻辑 96 ## 特殊:主子表专属逻辑
95 #foreach ($subTable in $subTables) 97 #foreach ($subTable in $subTables)
96 #set ($index = $foreach.count - 1) 98 #set ($index = $foreach.count - 1)
yudao-module-infra/src/main/resources/codegen/vue/views/index.vue.vm
@@ -49,6 +49,7 @@ @@ -49,6 +49,7 @@
49 <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="openForm(undefined)" 49 <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="openForm(undefined)"
50 v-hasPermi="['${permissionPrefix}:create']">新增</el-button> 50 v-hasPermi="['${permissionPrefix}:create']">新增</el-button>
51 </el-col> 51 </el-col>
  52 +#if ($importEnable)
52 <el-col :span="1.5"> 53 <el-col :span="1.5">
53 <el-button type="info" plain icon="el-icon-upload2" size="mini" @click="handleImport" 54 <el-button type="info" plain icon="el-icon-upload2" size="mini" @click="handleImport"
54 :loading="importLoading" v-hasPermi="['${permissionPrefix}:import']">导入</el-button> 55 :loading="importLoading" v-hasPermi="['${permissionPrefix}:import']">导入</el-button>
@@ -57,6 +58,7 @@ @@ -57,6 +58,7 @@
57 <el-button type="info" plain icon="el-icon-document" size="mini" @click="handleImportTemplate" 58 <el-button type="info" plain icon="el-icon-document" size="mini" @click="handleImportTemplate"
58 v-hasPermi="['${permissionPrefix}:import']">导入模板</el-button> 59 v-hasPermi="['${permissionPrefix}:import']">导入模板</el-button>
59 </el-col> 60 </el-col>
  61 +#end
60 <el-col :span="1.5"> 62 <el-col :span="1.5">
61 <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" :loading="exportLoading" 63 <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" :loading="exportLoading"
62 v-hasPermi="['${permissionPrefix}:export']">导出</el-button> 64 v-hasPermi="['${permissionPrefix}:export']">导出</el-button>
@@ -86,7 +88,9 @@ @@ -86,7 +88,9 @@
86 #end 88 #end
87 <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> 89 <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
88 </el-row> 90 </el-row>
  91 +#if ($importEnable)
89 <input ref="importFileRef" type="file" style="display: none" accept=".xls,.xlsx" @change="handleImportFileChange" /> 92 <input ref="importFileRef" type="file" style="display: none" accept=".xls,.xlsx" @change="handleImportFileChange" />
  93 +#end
90 94
91 ## 特殊:主子表专属逻辑 95 ## 特殊:主子表专属逻辑
92 #if ( $table.templateType == 11 && $subTables && $subTables.size() > 0 ) 96 #if ( $table.templateType == 11 && $subTables && $subTables.size() > 0 )
@@ -253,8 +257,10 @@ export default { @@ -253,8 +257,10 @@ export default {
253 loading: true, 257 loading: true,
254 // 导出遮罩层 258 // 导出遮罩层
255 exportLoading: false, 259 exportLoading: false,
  260 +#if ($importEnable)
256 // 导入遮罩层 261 // 导入遮罩层
257 importLoading: false, 262 importLoading: false,
  263 +#end
258 // 显示搜索条件 264 // 显示搜索条件
259 showSearch: true, 265 showSearch: true,
260 ## 特殊:树表专属逻辑(树不需要分页接口) 266 ## 特殊:树表专属逻辑(树不需要分页接口)
@@ -333,6 +339,7 @@ export default { @@ -333,6 +339,7 @@ export default {
333 openForm(id) { 339 openForm(id) {
334 this.#[[$]]#refs["formRef"].open(id); 340 this.#[[$]]#refs["formRef"].open(id);
335 }, 341 },
  342 +#if ($importEnable)
336 /** 导入按钮操作 */ 343 /** 导入按钮操作 */
337 handleImport() { 344 handleImport() {
338 this.$refs.importFileRef && this.$refs.importFileRef.click(); 345 this.$refs.importFileRef && this.$refs.importFileRef.click();
@@ -369,6 +376,7 @@ export default { @@ -369,6 +376,7 @@ export default {
369 this.importLoading = false; 376 this.importLoading = false;
370 } 377 }
371 }, 378 },
  379 +#end
372 /** 删除按钮操作 */ 380 /** 删除按钮操作 */
373 async handleDelete(row) { 381 async handleDelete(row) {
374 const ${primaryColumn.javaField} = row.${primaryColumn.javaField}; 382 const ${primaryColumn.javaField} = row.${primaryColumn.javaField};
yudao-module-infra/src/main/resources/codegen/vue3/api/api.ts.vm
@@ -98,6 +98,7 @@ export const ${simpleClassName}Api = { @@ -98,6 +98,7 @@ export const ${simpleClassName}Api = {
98 export${simpleClassName}: async (params) => { 98 export${simpleClassName}: async (params) => {
99 return await request.download({ url: `${baseURL}/export-excel`, params }) 99 return await request.download({ url: `${baseURL}/export-excel`, params })
100 }, 100 },
  101 +#if ($importEnable)
101 102
102 // 下载${table.classComment}导入模板 103 // 下载${table.classComment}导入模板
103 import${simpleClassName}Template: async () => { 104 import${simpleClassName}Template: async () => {
@@ -108,6 +109,7 @@ export const ${simpleClassName}Api = { @@ -108,6 +109,7 @@ export const ${simpleClassName}Api = {
108 import${simpleClassName}: async (data: FormData) => { 109 import${simpleClassName}: async (data: FormData) => {
109 return await request.upload({ url: `${baseURL}/import`, data }) 110 return await request.upload({ url: `${baseURL}/import`, data })
110 }, 111 },
  112 +#end
111 ## 特殊:主子表专属逻辑 113 ## 特殊:主子表专属逻辑
112 #foreach ($subTable in $subTables) 114 #foreach ($subTable in $subTables)
113 #set ($index = $foreach.count - 1) 115 #set ($index = $foreach.count - 1)
yudao-module-infra/src/main/resources/codegen/vue3/views/import.vue.vm 0 → 100644
  1 +<template>
  2 + <Dialog v-model="dialogVisible" title="${table.classComment}导入" width="400">
  3 + <el-upload
  4 + ref="uploadRef"
  5 + v-model:file-list="fileList"
  6 + :auto-upload="false"
  7 + :disabled="formLoading"
  8 + :limit="1"
  9 + :on-exceed="handleExceed"
  10 + accept=".xlsx, .xls"
  11 + action="none"
  12 + drag
  13 + >
  14 + <Icon icon="ep:upload" />
  15 + <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
  16 + <template #tip>
  17 + <div class="el-upload__tip text-center">
  18 + <span>仅允许导入 xls、xlsx 格式文件。</span>
  19 + <el-link
  20 + :underline="false"
  21 + style="font-size: 12px; vertical-align: baseline"
  22 + type="primary"
  23 + @click="handleDownloadTemplate"
  24 + >
  25 + 下载模板
  26 + </el-link>
  27 + </div>
  28 + </template>
  29 + </el-upload>
  30 + <template #footer>
  31 + <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
  32 + <el-button @click="dialogVisible = false">取 消</el-button>
  33 + </template>
  34 + </Dialog>
  35 +</template>
  36 +<script setup lang="ts">
  37 +import type { UploadUserFile } from 'element-plus'
  38 +import download from '@/utils/download'
  39 +import { ${simpleClassName}Api } from '@/api/${table.moduleName}/${table.businessName}'
  40 +
  41 +/** ${table.classComment} 导入 */
  42 +defineOptions({ name: '${simpleClassName}ImportForm' })
  43 +
  44 +const message = useMessage() // 消息弹窗
  45 +
  46 +const dialogVisible = ref(false) // 弹窗的是否展示
  47 +const formLoading = ref(false) // 表单的加载中:上传、下载模板
  48 +const uploadRef = ref()
  49 +const fileList = ref<UploadUserFile[]>([])
  50 +
  51 +/** 打开弹窗 */
  52 +const open = async () => {
  53 + dialogVisible.value = true
  54 + await resetForm()
  55 +}
  56 +defineExpose({ open }) // 提供 open 方法,用于打开弹窗
  57 +
  58 +/** 提交导入 */
  59 +const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
  60 +const submitForm = async () => {
  61 + if (fileList.value.length === 0) {
  62 + message.error('请上传文件')
  63 + return
  64 + }
  65 + formLoading.value = true
  66 + try {
  67 + const formData = new FormData()
  68 + formData.append('file', fileList.value[0].raw as Blob)
  69 + const res = await ${simpleClassName}Api.import${simpleClassName}(formData)
  70 + const data = res.data
  71 + let text = '导入成功数量:' + data.successCount + ';导入失败数量:' + data.failureCount + ';'
  72 + if (data.failureCount > 0) {
  73 + for (const rowNo in data.failureRows) {
  74 + text += '< 第' + rowNo + '行: ' + data.failureRows[rowNo] + ' >'
  75 + }
  76 + }
  77 + message.alert(text)
  78 + dialogVisible.value = false
  79 + emit('success')
  80 + } finally {
  81 + formLoading.value = false
  82 + await resetForm()
  83 + }
  84 +}
  85 +
  86 +/** 下载导入模板 */
  87 +const handleDownloadTemplate = async () => {
  88 + const data = await ${simpleClassName}Api.import${simpleClassName}Template()
  89 + download.excel(data, '${table.classComment}导入模板.xls')
  90 +}
  91 +
  92 +/** 文件超限 */
  93 +const handleExceed = (): void => {
  94 + message.error('最多只能上传一个文件!')
  95 +}
  96 +
  97 +/** 重置表单 */
  98 +const resetForm = async () => {
  99 + fileList.value = []
  100 + await nextTick()
  101 + uploadRef.value?.clearFiles()
  102 +}
  103 +</script>
yudao-module-infra/src/main/resources/codegen/vue3/views/index.vue.vm
@@ -92,6 +92,7 @@ @@ -92,6 +92,7 @@
92 > 92 >
93 <Icon icon="ep:plus" class="mr-5px" /> 新增 93 <Icon icon="ep:plus" class="mr-5px" /> 新增
94 </el-button> 94 </el-button>
  95 +#if ($importEnable)
95 <el-button 96 <el-button
96 type="warning" 97 type="warning"
97 plain 98 plain
@@ -100,6 +101,7 @@ @@ -100,6 +101,7 @@
100 > 101 >
101 <Icon icon="ep:upload" class="mr-5px" /> 导入 102 <Icon icon="ep:upload" class="mr-5px" /> 导入
102 </el-button> 103 </el-button>
  104 +#end
103 <el-button 105 <el-button
104 type="success" 106 type="success"
105 plain 107 plain
@@ -245,43 +247,11 @@ @@ -245,43 +247,11 @@
245 @pagination="getList" 247 @pagination="getList"
246 /> 248 />
247 </ContentWrap> 249 </ContentWrap>
  250 +#if ($importEnable)
248 251
249 <!-- 导入弹窗 --> 252 <!-- 导入弹窗 -->
250 - <Dialog v-model="importDialogVisible" title="${table.classComment}导入" width="400">  
251 - <el-upload  
252 - ref="uploadRef"  
253 - v-model:file-list="fileList"  
254 - :auto-upload="false"  
255 - :disabled="importFormLoading"  
256 - :limit="1"  
257 - :on-exceed="handleImportExceed"  
258 - accept=".xlsx, .xls"  
259 - action="none"  
260 - drag  
261 - >  
262 - <Icon icon="ep:upload" />  
263 - <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>  
264 - <template #tip>  
265 - <div class="el-upload__tip text-center">  
266 - <span>仅允许导入 xls、xlsx 格式文件。</span>  
267 - <el-link  
268 - :underline="false"  
269 - style="font-size: 12px; vertical-align: baseline"  
270 - type="primary"  
271 - @click="handleImportTemplate"  
272 - >  
273 - 下载模板  
274 - </el-link>  
275 - </div>  
276 - </template>  
277 - </el-upload>  
278 - <template #footer>  
279 - <el-button :disabled="importFormLoading" type="primary" @click="submitImportForm">  
280 - 确 定  
281 - </el-button>  
282 - <el-button @click="importDialogVisible = false">取 消</el-button>  
283 - </template>  
284 - </Dialog> 253 + <${simpleClassName}ImportForm ref="importRef" @success="getList" />
  254 +#end
285 255
286 <!-- 表单弹窗:添加/修改 --> 256 <!-- 表单弹窗:添加/修改 -->
287 <${simpleClassName}Form ref="formRef" @success="getList" /> 257 <${simpleClassName}Form ref="formRef" @success="getList" />
@@ -308,7 +278,9 @@ @@ -308,7 +278,9 @@
308 import { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict' 278 import { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
309 import { isEmpty } from '@/utils/is' 279 import { isEmpty } from '@/utils/is'
310 import { dateFormatter } from '@/utils/formatTime' 280 import { dateFormatter } from '@/utils/formatTime'
311 -import type { UploadUserFile } from 'element-plus' 281 +#if ($importEnable)
  282 +import ${simpleClassName}ImportForm from './${simpleClassName}ImportForm.vue'
  283 +#end
312 ## 特殊:树表专属逻辑 284 ## 特殊:树表专属逻辑
313 #if ( $table.templateType == 2 ) 285 #if ( $table.templateType == 2 )
314 import { handleTree } from '@/utils/tree' 286 import { handleTree } from '@/utils/tree'
@@ -354,10 +326,9 @@ const queryParams = reactive({ @@ -354,10 +326,9 @@ const queryParams = reactive({
354 }) 326 })
355 const queryFormRef = ref() // 搜索的表单 327 const queryFormRef = ref() // 搜索的表单
356 const exportLoading = ref(false) // 导出的加载中 328 const exportLoading = ref(false) // 导出的加载中
357 -const importDialogVisible = ref(false) // 导入弹窗  
358 -const importFormLoading = ref(false) // 导入的加载中  
359 -const uploadRef = ref()  
360 -const fileList = ref<UploadUserFile[]>([]) 329 +#if ($importEnable)
  330 +const importRef = ref() // ${table.classComment} 导入组件的 Ref
  331 +#end
361 332
362 /** 查询列表 */ 333 /** 查询列表 */
363 const getList = async () => { 334 const getList = async () => {
@@ -394,58 +365,13 @@ const formRef = ref() @@ -394,58 +365,13 @@ const formRef = ref()
394 const openForm = (type: string, id?: number) => { 365 const openForm = (type: string, id?: number) => {
395 formRef.value.open(type, id) 366 formRef.value.open(type, id)
396 } 367 }
  368 +#if ($importEnable)
397 369
398 /** 导入按钮操作 */ 370 /** 导入按钮操作 */
399 -const handleImport = async () => {  
400 - importDialogVisible.value = true  
401 - await resetImportForm()  
402 -}  
403 -  
404 -/** 提交导入 */  
405 -const submitImportForm = async () => {  
406 - if (fileList.value.length === 0) {  
407 - message.error('请上传文件')  
408 - return  
409 - }  
410 - importFormLoading.value = true  
411 - try {  
412 - const formData = new FormData()  
413 - formData.append('file', fileList.value[0].raw as Blob)  
414 - const res = await ${simpleClassName}Api.import${simpleClassName}(formData)  
415 - const data = res.data  
416 - let text = '导入成功数量:' + data.successCount + ';导入失败数量:' + data.failureCount + ';'  
417 - if (data.failureCount > 0) {  
418 - for (const rowNo in data.failureRows) {  
419 - text += '< 第' + rowNo + '行: ' + data.failureRows[rowNo] + ' >'  
420 - }  
421 - }  
422 - message.alert(text)  
423 - importDialogVisible.value = false  
424 - await getList()  
425 - } catch {  
426 - } finally {  
427 - importFormLoading.value = false  
428 - await resetImportForm()  
429 - }  
430 -}  
431 -  
432 -/** 下载导入模板 */  
433 -const handleImportTemplate = async () => {  
434 - const data = await ${simpleClassName}Api.import${simpleClassName}Template()  
435 - download.excel(data, '${table.classComment}导入模板.xls')  
436 -}  
437 -  
438 -/** 导入文件超限 */  
439 -const handleImportExceed = (): void => {  
440 - message.error('最多只能上传一个文件!')  
441 -}  
442 -  
443 -/** 重置导入表单 */  
444 -const resetImportForm = async () => {  
445 - fileList.value = []  
446 - await nextTick()  
447 - uploadRef.value?.clearFiles() 371 +const handleImport = () => {
  372 + importRef.value.open()
448 } 373 }
  374 +#end
449 375
450 /** 删除按钮操作 */ 376 /** 删除按钮操作 */
451 const handleDelete = async (id: number) => { 377 const handleDelete = async (id: number) => {
yudao-module-infra/src/main/resources/codegen/vue3_vben/api/api.ts.vm
@@ -30,6 +30,7 @@ export function delete${simpleClassName}(id: number) { @@ -30,6 +30,7 @@ export function delete${simpleClassName}(id: number) {
30 export function export${simpleClassName}(params) { 30 export function export${simpleClassName}(params) {
31 return defHttp.download({ url: '${baseURL}/export-excel', params }, '${table.classComment}.xls') 31 return defHttp.download({ url: '${baseURL}/export-excel', params }, '${table.classComment}.xls')
32 } 32 }
  33 +#if ($importEnable)
33 34
34 // 下载${table.classComment}导入模板 35 // 下载${table.classComment}导入模板
35 export function import${simpleClassName}Template() { 36 export function import${simpleClassName}Template() {
@@ -40,3 +41,4 @@ export function import${simpleClassName}Template() { @@ -40,3 +41,4 @@ export function import${simpleClassName}Template() {
40 export function import${simpleClassName}(data: FormData) { 41 export function import${simpleClassName}(data: FormData) {
41 return defHttp.post({ url: '${baseURL}/import', data }) 42 return defHttp.post({ url: '${baseURL}/import', data })
42 } 43 }
  44 +#end
yudao-module-infra/src/main/resources/codegen/vue3_vben/views/index.vue.vm
@@ -11,8 +11,10 @@ import { @@ -11,8 +11,10 @@ import {
11 delete${simpleClassName}, 11 delete${simpleClassName},
12 export${simpleClassName}, 12 export${simpleClassName},
13 get${simpleClassName}Page, 13 get${simpleClassName}Page,
  14 +#if ($importEnable)
14 import${simpleClassName}, 15 import${simpleClassName},
15 import${simpleClassName}Template, 16 import${simpleClassName}Template,
  17 +#end
16 } from '@/api/${table.moduleName}/${table.businessName}' 18 } from '@/api/${table.moduleName}/${table.businessName}'
17 19
18 defineOptions({ name: '${table.className}' }) 20 defineOptions({ name: '${table.className}' })
@@ -20,8 +22,10 @@ defineOptions({ name: &#39;${table.className}&#39; }) @@ -20,8 +22,10 @@ defineOptions({ name: &#39;${table.className}&#39; })
20 const { t } = useI18n() 22 const { t } = useI18n()
21 const { createConfirm, createMessage } = useMessage() 23 const { createConfirm, createMessage } = useMessage()
22 const [registerModal, { openModal }] = useModal() 24 const [registerModal, { openModal }] = useModal()
  25 +#if ($importEnable)
23 const importFileRef = ref<HTMLInputElement>() 26 const importFileRef = ref<HTMLInputElement>()
24 const importLoading = ref(false) 27 const importLoading = ref(false)
  28 +#end
25 29
26 const [registerTable, { getForm, reload }] = useTable({ 30 const [registerTable, { getForm, reload }] = useTable({
27 title: '${table.classComment}列表', 31 title: '${table.classComment}列表',
@@ -57,6 +61,7 @@ async function handleExport() { @@ -57,6 +61,7 @@ async function handleExport() {
57 }, 61 },
58 }) 62 })
59 } 63 }
  64 +#if ($importEnable)
60 65
61 function handleImport() { 66 function handleImport() {
62 importFileRef.value?.click() 67 importFileRef.value?.click()
@@ -93,6 +98,7 @@ async function handleImportFileChange(event: Event) { @@ -93,6 +98,7 @@ async function handleImportFileChange(event: Event) {
93 importLoading.value = false 98 importLoading.value = false
94 } 99 }
95 } 100 }
  101 +#end
96 102
97 async function handleDelete(record: Recordable) { 103 async function handleDelete(record: Recordable) {
98 await delete${simpleClassName}(record.id) 104 await delete${simpleClassName}(record.id)
@@ -107,12 +113,14 @@ async function handleDelete(record: Recordable) { @@ -107,12 +113,14 @@ async function handleDelete(record: Recordable) {
107 <a-button type="primary" v-auth="['${permissionPrefix}:create']" :preIcon="IconEnum.ADD" @click="handleCreate"> 113 <a-button type="primary" v-auth="['${permissionPrefix}:create']" :preIcon="IconEnum.ADD" @click="handleCreate">
108 {{ t('action.create') }} 114 {{ t('action.create') }}
109 </a-button> 115 </a-button>
  116 +#if ($importEnable)
110 <a-button v-auth="['${permissionPrefix}:import']" :loading="importLoading" @click="handleImport"> 117 <a-button v-auth="['${permissionPrefix}:import']" :loading="importLoading" @click="handleImport">
111 导入 118 导入
112 </a-button> 119 </a-button>
113 <a-button v-auth="['${permissionPrefix}:import']" @click="handleImportTemplateDownload"> 120 <a-button v-auth="['${permissionPrefix}:import']" @click="handleImportTemplateDownload">
114 导入模板 121 导入模板
115 </a-button> 122 </a-button>
  123 +#end
116 <a-button v-auth="['${permissionPrefix}:export']" :preIcon="IconEnum.EXPORT" @click="handleExport"> 124 <a-button v-auth="['${permissionPrefix}:export']" :preIcon="IconEnum.EXPORT" @click="handleExport">
117 {{ t('action.export') }} 125 {{ t('action.export') }}
118 </a-button> 126 </a-button>
@@ -138,7 +146,9 @@ async function handleDelete(record: Recordable) { @@ -138,7 +146,9 @@ async function handleDelete(record: Recordable) {
138 </template> 146 </template>
139 </template> 147 </template>
140 </BasicTable> 148 </BasicTable>
  149 +#if ($importEnable)
141 <input ref="importFileRef" type="file" accept=".xls,.xlsx" class="hidden" @change="handleImportFileChange" /> 150 <input ref="importFileRef" type="file" accept=".xls,.xlsx" class="hidden" @change="handleImportFileChange" />
  151 +#end
142 <${simpleClassName}Modal @register="registerModal" @success="reload()" /> 152 <${simpleClassName}Modal @register="registerModal" @success="reload()" />
143 </div> 153 </div>
144 </template> 154 </template>
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/general/api/api.ts.vm
@@ -100,6 +100,7 @@ export function delete${simpleClassName}List(ids: number[]) { @@ -100,6 +100,7 @@ export function delete${simpleClassName}List(ids: number[]) {
100 export function export${simpleClassName}(params: any) { 100 export function export${simpleClassName}(params: any) {
101 return requestClient.download('${baseURL}/export-excel', { params }); 101 return requestClient.download('${baseURL}/export-excel', { params });
102 } 102 }
  103 +#if ($importEnable)
103 104
104 /** 下载${table.classComment}导入模板 */ 105 /** 下载${table.classComment}导入模板 */
105 export function import${simpleClassName}Template() { 106 export function import${simpleClassName}Template() {
@@ -110,6 +111,7 @@ export function import${simpleClassName}Template() { @@ -110,6 +111,7 @@ export function import${simpleClassName}Template() {
110 export function import${simpleClassName}(data: FormData) { 111 export function import${simpleClassName}(data: FormData) {
111 return requestClient.post('${baseURL}/import', data); 112 return requestClient.post('${baseURL}/import', data);
112 } 113 }
  114 +#end
113 115
114 ## 特殊:主子表专属逻辑 116 ## 特殊:主子表专属逻辑
115 #foreach ($subTable in $subTables) 117 #foreach ($subTable in $subTables)
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/general/views/import.vue.vm 0 → 100644
  1 +<script lang="ts" setup>
  2 +import type { FileType } from 'ant-design-vue/es/upload/interface';
  3 +
  4 +import { ref } from 'vue';
  5 +
  6 +import { useVbenModal } from '@vben/common-ui';
  7 +import { downloadFileFromBlobPart } from '@vben/utils';
  8 +
  9 +import { Button, message, Upload } from 'ant-design-vue';
  10 +
  11 +import { import${simpleClassName}, import${simpleClassName}Template } from '#/api/${table.moduleName}/${table.businessName}';
  12 +
  13 +defineOptions({ name: '${simpleClassName}Import' });
  14 +
  15 +const emit = defineEmits(['success']);
  16 +const fileRef = ref<File | null>(null);
  17 +
  18 +const [Modal, modalApi] = useVbenModal({
  19 + async onConfirm() {
  20 + if (!fileRef.value) {
  21 + message.error('请上传文件');
  22 + return;
  23 + }
  24 + modalApi.lock();
  25 + try {
  26 + const formData = new FormData();
  27 + formData.append('file', fileRef.value);
  28 + const response: any = await import${simpleClassName}(formData);
  29 + const data = response?.data ?? response ?? {};
  30 + let text = '导入成功数量:' + (data.successCount || 0) + ';导入失败数量:' + (data.failureCount || 0) + ';';
  31 + if (data.failureRows) {
  32 + Object.keys(data.failureRows).forEach((rowNo) => {
  33 + text += '< 第' + rowNo + '行: ' + data.failureRows[rowNo] + ' >';
  34 + });
  35 + }
  36 + message.info(text);
  37 + await modalApi.close();
  38 + emit('success');
  39 + } finally {
  40 + modalApi.unlock();
  41 + }
  42 + },
  43 +});
  44 +
  45 +/** 上传前:拦截 antd Upload 的自动上传,文件存到 ref */
  46 +function beforeUpload(file: FileType) {
  47 + fileRef.value = file as unknown as File;
  48 + return false;
  49 +}
  50 +
  51 +/** 下载导入模板 */
  52 +async function handleDownload() {
  53 + const data = await import${simpleClassName}Template();
  54 + downloadFileFromBlobPart({ fileName: '${table.classComment}导入模板.xls', source: data });
  55 +}
  56 +</script>
  57 +
  58 +<template>
  59 + <Modal title="导入${table.classComment}" class="w-1/3">
  60 + <div class="mx-4">
  61 + <Upload :max-count="1" accept=".xls,.xlsx" :before-upload="beforeUpload">
  62 + <Button type="primary"> 选择 Excel 文件 </Button>
  63 + </Upload>
  64 + </div>
  65 + <template #prepend-footer>
  66 + <div class="flex flex-auto items-center">
  67 + <Button @click="handleDownload"> 下载导入模板 </Button>
  68 + </div>
  69 + </template>
  70 + </Modal>
  71 +</template>
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/general/views/index.vue.vm
@@ -27,9 +27,12 @@ import { getRangePickerDefaultProps } from &#39;#/utils/rangePickerProps&#39;; @@ -27,9 +27,12 @@ import { getRangePickerDefaultProps } from &#39;#/utils/rangePickerProps&#39;;
27 import { $t } from '#/locales'; 27 import { $t } from '#/locales';
28 #if (${table.templateType} == 2)## 树表接口 28 #if (${table.templateType} == 2)## 树表接口
29 import { handleTree } from '@vben/utils' 29 import { handleTree } from '@vben/utils'
30 -import { get${simpleClassName}List, delete${simpleClassName}, export${simpleClassName}, import${simpleClassName}, import${simpleClassName}Template } from '#/api/${table.moduleName}/${table.businessName}'; 30 +import { get${simpleClassName}List, delete${simpleClassName}, export${simpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';
31 #else## 标准表接口 31 #else## 标准表接口
32 -import { get${simpleClassName}Page, delete${simpleClassName},#if ($deleteBatchEnable) delete${simpleClassName}List,#end export${simpleClassName}, import${simpleClassName}, import${simpleClassName}Template } from '#/api/${table.moduleName}/${table.businessName}'; 32 +import { get${simpleClassName}Page, delete${simpleClassName},#if ($deleteBatchEnable) delete${simpleClassName}List,#end export${simpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';
  33 +#end
  34 +#if ($importEnable)
  35 +import ${simpleClassName}Import from './modules/import-form.vue';
33 #end 36 #end
34 37
35 #if ($table.templateType == 12 || $table.templateType == 11) ## 内嵌和erp情况 38 #if ($table.templateType == 12 || $table.templateType == 11) ## 内嵌和erp情况
@@ -73,8 +76,6 @@ const queryParams = reactive({ @@ -73,8 +76,6 @@ const queryParams = reactive({
73 }) 76 })
74 const queryFormRef = ref() // 搜索的表单 77 const queryFormRef = ref() // 搜索的表单
75 const exportLoading = ref(false) // 导出的加载中 78 const exportLoading = ref(false) // 导出的加载中
76 -const importLoading = ref(false) // 导入的加载中  
77 -const importFileRef = ref<HTMLInputElement>()  
78 79
79 /** 查询列表 */ 80 /** 查询列表 */
80 async function getList() { 81 async function getList() {
@@ -121,6 +122,17 @@ const [FormModal, formModalApi] = useVbenModal({ @@ -121,6 +122,17 @@ const [FormModal, formModalApi] = useVbenModal({
121 connectedComponent: ${simpleClassName}Form, 122 connectedComponent: ${simpleClassName}Form,
122 destroyOnClose: true, 123 destroyOnClose: true,
123 }); 124 });
  125 +#if ($importEnable)
  126 +const [ImportFormModal, importFormModalApi] = useVbenModal({
  127 + connectedComponent: ${simpleClassName}Import,
  128 + destroyOnClose: true,
  129 +});
  130 +
  131 +/** 导入${table.classComment} */
  132 +function handleImport() {
  133 + importFormModalApi.open();
  134 +}
  135 +#end
124 136
125 /** 创建${table.classComment} */ 137 /** 创建${table.classComment} */
126 function handleCreate() { 138 function handleCreate() {
@@ -192,43 +204,6 @@ try { @@ -192,43 +204,6 @@ try {
192 } 204 }
193 } 205 }
194 206
195 -/** 导入模板下载 */  
196 -async function handleImportTemplateDownload() {  
197 - const data = await import${simpleClassName}Template();  
198 - downloadFileFromBlobPart({ fileName: '${table.classComment}导入模板.xls', source: data });  
199 -}  
200 -  
201 -/** 导入按钮操作 */  
202 -function handleImport() {  
203 - importFileRef.value?.click();  
204 -}  
205 -  
206 -/** 导入文件变更 */  
207 -async function handleImportFileChange(event: Event) {  
208 - const target = event.target as HTMLInputElement;  
209 - const file = target.files?.[0];  
210 - if (!file) {  
211 - return;  
212 - }  
213 - importLoading.value = true;  
214 - try {  
215 - const formData = new FormData();  
216 - formData.append('file', file);  
217 - const response: any = await import${simpleClassName}(formData);  
218 - const data = response?.data ?? response ?? {};  
219 - let text = '导入成功数量:' + (data.successCount || 0) + ';导入失败数量:' + (data.failureCount || 0) + ';';  
220 - if (data.failureRows) {  
221 - Object.keys(data.failureRows).forEach((rowNo) => {  
222 - text += '< 第' + rowNo + '行: ' + data.failureRows[rowNo] + ' >';  
223 - });  
224 - }  
225 - message.info(text);  
226 - await getList();  
227 - } finally {  
228 - target.value = '';  
229 - importLoading.value = false;  
230 - }  
231 -}  
232 207
233 #if (${table.templateType} == 2) 208 #if (${table.templateType} == 2)
234 /** 切换树形展开/收缩状态 */ 209 /** 切换树形展开/收缩状态 */
@@ -249,7 +224,9 @@ onMounted(() =&gt; { @@ -249,7 +224,9 @@ onMounted(() =&gt; {
249 <template> 224 <template>
250 <Page auto-content-height> 225 <Page auto-content-height>
251 <FormModal @success="getList" /> 226 <FormModal @success="getList" />
252 - <input ref="importFileRef" type="file" accept=".xls,.xlsx" class="hidden" @change="handleImportFileChange" /> 227 +#if ($importEnable)
  228 + <ImportFormModal @success="getList" />
  229 +#end
253 230
254 <Card v-if="!hiddenSearchBar" class="mb-4"> 231 <Card v-if="!hiddenSearchBar" class="mb-4">
255 <!-- 搜索工作栏 --> 232 <!-- 搜索工作栏 -->
@@ -355,23 +332,16 @@ onMounted(() =&gt; { @@ -355,23 +332,16 @@ onMounted(() =&gt; {
355 > 332 >
356 {{ $t('ui.actionTitle.create', ['${table.classComment}']) }} 333 {{ $t('ui.actionTitle.create', ['${table.classComment}']) }}
357 </Button> 334 </Button>
  335 +#if ($importEnable)
358 <Button 336 <Button
359 class="ml-2" 337 class="ml-2"
360 type="primary" 338 type="primary"
361 - :loading="importLoading"  
362 @click="handleImport" 339 @click="handleImport"
363 v-access:code="['${permissionPrefix}:import']" 340 v-access:code="['${permissionPrefix}:import']"
364 > 341 >
365 导入 342 导入
366 </Button> 343 </Button>
367 - <Button  
368 - class="ml-2"  
369 - type="primary"  
370 - @click="handleImportTemplateDownload"  
371 - v-access:code="['${permissionPrefix}:import']"  
372 - >  
373 - 导入模板  
374 - </Button> 344 +#end
375 <Button 345 <Button
376 :icon="h(Download)" 346 :icon="h(Download)"
377 type="primary" 347 type="primary"
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/views/data.ts.vm
@@ -134,6 +134,21 @@ export function useFormSchema(): VbenFormSchema[] { @@ -134,6 +134,21 @@ export function useFormSchema(): VbenFormSchema[] {
134 ]; 134 ];
135 } 135 }
136 136
  137 +#if ($importEnable)
  138 +/** 导入的表单 */
  139 +export function useImportFormSchema(): VbenFormSchema[] {
  140 + return [
  141 + {
  142 + fieldName: 'file',
  143 + label: '${table.classComment}数据',
  144 + component: 'Upload',
  145 + rules: 'required',
  146 + help: '仅允许导入 xls、xlsx 格式文件',
  147 + },
  148 + ];
  149 +}
  150 +
  151 +#end
137 /** 列表的搜索表单 */ 152 /** 列表的搜索表单 */
138 export function useGridFormSchema(): VbenFormSchema[] { 153 export function useGridFormSchema(): VbenFormSchema[] {
139 return [ 154 return [
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/views/import.vue.vm 0 → 100644
  1 +<script lang="ts" setup>
  2 +import type { FileType } from 'ant-design-vue/es/upload/interface';
  3 +
  4 +import { useVbenModal } from '@vben/common-ui';
  5 +import { downloadFileFromBlobPart } from '@vben/utils';
  6 +
  7 +import { Button, message, Upload } from 'ant-design-vue';
  8 +
  9 +import { useVbenForm } from '#/adapter/form';
  10 +import { import${simpleClassName}, import${simpleClassName}Template } from '#/api/${table.moduleName}/${table.businessName}';
  11 +import { $t } from '#/locales';
  12 +
  13 +import { useImportFormSchema } from '../data';
  14 +
  15 +const emit = defineEmits(['success']);
  16 +
  17 +const [Form, formApi] = useVbenForm({
  18 + commonConfig: {
  19 + formItemClass: 'col-span-2',
  20 + labelWidth: 120,
  21 + },
  22 + layout: 'horizontal',
  23 + schema: useImportFormSchema(),
  24 + showDefaultActions: false,
  25 +});
  26 +
  27 +const [Modal, modalApi] = useVbenModal({
  28 + async onConfirm() {
  29 + const { valid } = await formApi.validate();
  30 + if (!valid) {
  31 + return;
  32 + }
  33 + modalApi.lock();
  34 + // 提交表单
  35 + const data = await formApi.getValues();
  36 + try {
  37 + await import${simpleClassName}(data.file);
  38 + // 关闭并提示
  39 + await modalApi.close();
  40 + emit('success');
  41 + message.success($t('ui.actionMessage.operationSuccess'));
  42 + } finally {
  43 + modalApi.unlock();
  44 + }
  45 + },
  46 +});
  47 +
  48 +/** 上传前 */
  49 +function beforeUpload(file: FileType) {
  50 + formApi.setFieldValue('file', file);
  51 + return false;
  52 +}
  53 +
  54 +/** 下载模板 */
  55 +async function handleDownload() {
  56 + const data = await import${simpleClassName}Template();
  57 + downloadFileFromBlobPart({ fileName: '${table.classComment}导入模板.xls', source: data });
  58 +}
  59 +</script>
  60 +
  61 +<template>
  62 + <Modal title="导入${table.classComment}" class="w-1/3">
  63 + <Form class="mx-4">
  64 + <template #file>
  65 + <div class="w-full">
  66 + <Upload
  67 + :max-count="1"
  68 + accept=".xls,.xlsx"
  69 + :before-upload="beforeUpload"
  70 + >
  71 + <Button type="primary"> 选择 Excel 文件 </Button>
  72 + </Upload>
  73 + </div>
  74 + </template>
  75 + </Form>
  76 + <template #prepend-footer>
  77 + <div class="flex flex-auto items-center">
  78 + <Button @click="handleDownload"> 下载导入模板 </Button>
  79 + </div>
  80 + </template>
  81 + </Modal>
  82 +</template>
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/views/index.vue.vm
@@ -16,8 +16,6 @@ import { @@ -16,8 +16,6 @@ import {
16 delete${simpleClassName}, 16 delete${simpleClassName},
17 export${simpleClassName}, 17 export${simpleClassName},
18 get${simpleClassName}List, 18 get${simpleClassName}List,
19 - import${simpleClassName},  
20 - import${simpleClassName}Template,  
21 } from '#/api/${table.moduleName}/${table.businessName}'; 19 } from '#/api/${table.moduleName}/${table.businessName}';
22 #else## 标准表接口 20 #else## 标准表接口
23 import { 21 import {
@@ -26,10 +24,11 @@ import { @@ -26,10 +24,11 @@ import {
26 delete${simpleClassName}List,#end 24 delete${simpleClassName}List,#end
27 export${simpleClassName}, 25 export${simpleClassName},
28 get${simpleClassName}Page, 26 get${simpleClassName}Page,
29 - import${simpleClassName},  
30 - import${simpleClassName}Template,  
31 } from '#/api/${table.moduleName}/${table.businessName}'; 27 } from '#/api/${table.moduleName}/${table.businessName}';
32 #end 28 #end
  29 +#if ($importEnable)
  30 +import ${simpleClassName}Import from './modules/import-form.vue';
  31 +#end
33 import { $t } from '#/locales'; 32 import { $t } from '#/locales';
34 33
35 import { useGridColumns, useGridFormSchema } from './data'; 34 import { useGridColumns, useGridFormSchema } from './data';
@@ -54,8 +53,17 @@ const [FormModal, formModalApi] = useVbenModal({ @@ -54,8 +53,17 @@ const [FormModal, formModalApi] = useVbenModal({
54 connectedComponent: Form, 53 connectedComponent: Form,
55 destroyOnClose: true, 54 destroyOnClose: true,
56 }); 55 });
57 -const importLoading = ref(false);  
58 -const importFileRef = ref<HTMLInputElement>(); 56 +#if ($importEnable)
  57 +const [ImportFormModal, importFormModalApi] = useVbenModal({
  58 + connectedComponent: ${simpleClassName}Import,
  59 + destroyOnClose: true,
  60 +});
  61 +
  62 +/** 导入${table.classComment} */
  63 +function handleImport() {
  64 + importFormModalApi.open();
  65 +}
  66 +#end
59 #if (${table.templateType} == 2)## 树表特有:控制表格展开收缩 67 #if (${table.templateType} == 2)## 树表特有:控制表格展开收缩
60 68
61 /** 切换树形展开/收缩状态 */ 69 /** 切换树形展开/收缩状态 */
@@ -141,43 +149,6 @@ async function handleExport() { @@ -141,43 +149,6 @@ async function handleExport() {
141 downloadFileFromBlobPart({ fileName: '${table.classComment}.xls', source: data }); 149 downloadFileFromBlobPart({ fileName: '${table.classComment}.xls', source: data });
142 } 150 }
143 151
144 -/** 导入模板下载 */  
145 -async function handleImportTemplateDownload() {  
146 - const data = await import${simpleClassName}Template();  
147 - downloadFileFromBlobPart({ fileName: '${table.classComment}导入模板.xls', source: data });  
148 -}  
149 -  
150 -/** 导入按钮操作 */  
151 -function handleImport() {  
152 - importFileRef.value?.click();  
153 -}  
154 -  
155 -/** 导入文件变更 */  
156 -async function handleImportFileChange(event: Event) {  
157 - const target = event.target as HTMLInputElement;  
158 - const file = target.files?.[0];  
159 - if (!file) {  
160 - return;  
161 - }  
162 - importLoading.value = true;  
163 - try {  
164 - const formData = new FormData();  
165 - formData.append('file', file);  
166 - const response: any = await import${simpleClassName}(formData);  
167 - const data = response?.data ?? response ?? {};  
168 - let text = '导入成功数量:' + (data.successCount || 0) + ';导入失败数量:' + (data.failureCount || 0) + ';';  
169 - if (data.failureRows) {  
170 - Object.keys(data.failureRows).forEach((rowNo) => {  
171 - text += '< 第' + rowNo + '行: ' + data.failureRows[rowNo] + ' >';  
172 - });  
173 - }  
174 - message.info(text);  
175 - handleRefresh();  
176 - } finally {  
177 - target.value = '';  
178 - importLoading.value = false;  
179 - }  
180 -}  
181 152
182 const [Grid, gridApi] = useVbenVxeGrid({ 153 const [Grid, gridApi] = useVbenVxeGrid({
183 formOptions: { 154 formOptions: {
@@ -254,7 +225,9 @@ const [Grid, gridApi] = useVbenVxeGrid({ @@ -254,7 +225,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
254 <template> 225 <template>
255 <Page auto-content-height> 226 <Page auto-content-height>
256 <FormModal @success="handleRefresh" /> 227 <FormModal @success="handleRefresh" />
257 - <input ref="importFileRef" type="file" accept=".xls,.xlsx" class="hidden" @change="handleImportFileChange" /> 228 +#if ($importEnable)
  229 + <ImportFormModal @success="handleRefresh" />
  230 +#end
258 #if ($table.templateType == 11) ## erp情况 231 #if ($table.templateType == 11) ## erp情况
259 <div> 232 <div>
260 #end 233 #end
@@ -292,19 +265,14 @@ const [Grid, gridApi] = useVbenVxeGrid({ @@ -292,19 +265,14 @@ const [Grid, gridApi] = useVbenVxeGrid({
292 onClick: handleExpand, 265 onClick: handleExpand,
293 }, 266 },
294 #end 267 #end
  268 +#if ($importEnable)
295 { 269 {
296 label: '导入', 270 label: '导入',
297 type: 'primary', 271 type: 'primary',
298 auth: ['${table.moduleName}:${simpleClassName_strikeCase}:import'], 272 auth: ['${table.moduleName}:${simpleClassName_strikeCase}:import'],
299 - disabled: importLoading.value,  
300 onClick: handleImport, 273 onClick: handleImport,
301 }, 274 },
302 - {  
303 - label: '导入模板',  
304 - type: 'primary',  
305 - auth: ['${table.moduleName}:${simpleClassName_strikeCase}:import'],  
306 - onClick: handleImportTemplateDownload,  
307 - }, 275 +#end
308 { 276 {
309 label: $t('ui.actionTitle.export'), 277 label: $t('ui.actionTitle.export'),
310 type: 'primary', 278 type: 'primary',
yudao-module-infra/src/main/resources/codegen/vue3_vben5_ele/general/api/api.ts.vm
@@ -100,6 +100,7 @@ export function delete${simpleClassName}List(ids: number[]) { @@ -100,6 +100,7 @@ export function delete${simpleClassName}List(ids: number[]) {
100 export function export${simpleClassName}(params: any) { 100 export function export${simpleClassName}(params: any) {
101 return requestClient.download('${baseURL}/export-excel', { params }); 101 return requestClient.download('${baseURL}/export-excel', { params });
102 } 102 }
  103 +#if ($importEnable)
103 104
104 /** 下载${table.classComment}导入模板 */ 105 /** 下载${table.classComment}导入模板 */
105 export function import${simpleClassName}Template() { 106 export function import${simpleClassName}Template() {
@@ -110,6 +111,7 @@ export function import${simpleClassName}Template() { @@ -110,6 +111,7 @@ export function import${simpleClassName}Template() {
110 export function import${simpleClassName}(data: FormData) { 111 export function import${simpleClassName}(data: FormData) {
111 return requestClient.post('${baseURL}/import', data); 112 return requestClient.post('${baseURL}/import', data);
112 } 113 }
  114 +#end
113 115
114 ## 特殊:主子表专属逻辑 116 ## 特殊:主子表专属逻辑
115 #foreach ($subTable in $subTables) 117 #foreach ($subTable in $subTables)
yudao-module-infra/src/main/resources/codegen/vue3_vben5_ele/general/views/import.vue.vm 0 → 100644
  1 +<script lang="ts" setup>
  2 +import { ref } from 'vue';
  3 +
  4 +import { useVbenModal } from '@vben/common-ui';
  5 +import { downloadFileFromBlobPart } from '@vben/utils';
  6 +
  7 +import { ElButton, ElMessage, ElUpload } from 'element-plus';
  8 +
  9 +import { import${simpleClassName}, import${simpleClassName}Template } from '#/api/${table.moduleName}/${table.businessName}';
  10 +
  11 +defineOptions({ name: '${simpleClassName}Import' });
  12 +
  13 +const emit = defineEmits(['success']);
  14 +const fileRef = ref<File | null>(null);
  15 +
  16 +const [Modal, modalApi] = useVbenModal({
  17 + async onConfirm() {
  18 + if (!fileRef.value) {
  19 + ElMessage.error('请上传文件');
  20 + return;
  21 + }
  22 + modalApi.lock();
  23 + try {
  24 + const formData = new FormData();
  25 + formData.append('file', fileRef.value);
  26 + const response: any = await import${simpleClassName}(formData);
  27 + const data = response?.data ?? response ?? {};
  28 + let text = '导入成功数量:' + (data.successCount || 0) + ';导入失败数量:' + (data.failureCount || 0) + ';';
  29 + if (data.failureRows) {
  30 + Object.keys(data.failureRows).forEach((rowNo) => {
  31 + text += '< 第' + rowNo + '行: ' + data.failureRows[rowNo] + ' >';
  32 + });
  33 + }
  34 + ElMessage.info(text);
  35 + await modalApi.close();
  36 + emit('success');
  37 + } finally {
  38 + modalApi.unlock();
  39 + }
  40 + },
  41 +});
  42 +
  43 +/** 文件改变 */
  44 +function handleChange(file: any) {
  45 + if (file.raw) {
  46 + fileRef.value = file.raw;
  47 + }
  48 +}
  49 +
  50 +/** 下载导入模板 */
  51 +async function handleDownload() {
  52 + const data = await import${simpleClassName}Template();
  53 + downloadFileFromBlobPart({ fileName: '${table.classComment}导入模板.xls', source: data });
  54 +}
  55 +</script>
  56 +
  57 +<template>
  58 + <Modal title="导入${table.classComment}" class="w-1/3">
  59 + <div class="mx-4">
  60 + <ElUpload
  61 + :limit="1"
  62 + accept=".xls,.xlsx"
  63 + :on-change="handleChange"
  64 + :auto-upload="false"
  65 + >
  66 + <ElButton type="primary"> 选择 Excel 文件 </ElButton>
  67 + </ElUpload>
  68 + </div>
  69 + <template #prepend-footer>
  70 + <div class="flex flex-auto items-center">
  71 + <ElButton @click="handleDownload"> 下载导入模板 </ElButton>
  72 + </div>
  73 + </template>
  74 + </Modal>
  75 +</template>
yudao-module-infra/src/main/resources/codegen/vue3_vben5_ele/general/views/index.vue.vm
@@ -26,10 +26,13 @@ import { VxeColumn, VxeTable } from &#39;#/adapter/vxe-table&#39;; @@ -26,10 +26,13 @@ import { VxeColumn, VxeTable } from &#39;#/adapter/vxe-table&#39;;
26 import { $t } from '#/locales'; 26 import { $t } from '#/locales';
27 #if (${table.templateType} == 2)## 树表接口 27 #if (${table.templateType} == 2)## 树表接口
28 import { handleTree,isEmpty } from '@vben/utils' 28 import { handleTree,isEmpty } from '@vben/utils'
29 -import { get${simpleClassName}List, delete${simpleClassName}, export${simpleClassName}, import${simpleClassName}, import${simpleClassName}Template } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}'; 29 +import { get${simpleClassName}List, delete${simpleClassName}, export${simpleClassName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
30 #else## 标准表接口 30 #else## 标准表接口
31 import { isEmpty } from '@vben/utils'; 31 import { isEmpty } from '@vben/utils';
32 -import { get${simpleClassName}Page, delete${simpleClassName},#if ($deleteBatchEnable) delete${simpleClassName}List,#end export${simpleClassName}, import${simpleClassName}, import${simpleClassName}Template } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}'; 32 +import { get${simpleClassName}Page, delete${simpleClassName},#if ($deleteBatchEnable) delete${simpleClassName}List,#end export${simpleClassName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
  33 +#end
  34 +#if ($importEnable)
  35 +import ${simpleClassName}Import from './modules/import-form.vue';
33 #end 36 #end
34 37
35 #if ($table.templateType == 12 || $table.templateType == 11) ## 内嵌和erp情况 38 #if ($table.templateType == 12 || $table.templateType == 11) ## 内嵌和erp情况
@@ -73,8 +76,6 @@ const queryParams = reactive({ @@ -73,8 +76,6 @@ const queryParams = reactive({
73 }) 76 })
74 const queryFormRef = ref() // 搜索的表单 77 const queryFormRef = ref() // 搜索的表单
75 const exportLoading = ref(false) // 导出的加载中 78 const exportLoading = ref(false) // 导出的加载中
76 -const importLoading = ref(false) // 导入的加载中  
77 -const importFileRef = ref<HTMLInputElement>()  
78 79
79 /** 查询列表 */ 80 /** 查询列表 */
80 const getList = async () => { 81 const getList = async () => {
@@ -121,6 +122,17 @@ const [FormModal, formModalApi] = useVbenModal({ @@ -121,6 +122,17 @@ const [FormModal, formModalApi] = useVbenModal({
121 connectedComponent: ${simpleClassName}Form, 122 connectedComponent: ${simpleClassName}Form,
122 destroyOnClose: true, 123 destroyOnClose: true,
123 }); 124 });
  125 +#if ($importEnable)
  126 +const [ImportFormModal, importFormModalApi] = useVbenModal({
  127 + connectedComponent: ${simpleClassName}Import,
  128 + destroyOnClose: true,
  129 +});
  130 +
  131 +/** 导入${table.classComment} */
  132 +function handleImport() {
  133 + importFormModalApi.open();
  134 +}
  135 +#end
124 136
125 /** 创建${table.classComment} */ 137 /** 创建${table.classComment} */
126 function handleCreate() { 138 function handleCreate() {
@@ -190,43 +202,6 @@ try { @@ -190,43 +202,6 @@ try {
190 } 202 }
191 } 203 }
192 204
193 -/** 导入模板下载 */  
194 -async function handleImportTemplateDownload() {  
195 - const data = await import${simpleClassName}Template();  
196 - downloadFileFromBlobPart({ fileName: '${table.classComment}导入模板.xls', source: data });  
197 -}  
198 -  
199 -/** 导入按钮操作 */  
200 -function handleImport() {  
201 - importFileRef.value?.click();  
202 -}  
203 -  
204 -/** 导入文件变更 */  
205 -async function handleImportFileChange(event: Event) {  
206 - const target = event.target as HTMLInputElement;  
207 - const file = target.files?.[0];  
208 - if (!file) {  
209 - return;  
210 - }  
211 - importLoading.value = true;  
212 - try {  
213 - const formData = new FormData();  
214 - formData.append('file', file);  
215 - const response: any = await import${simpleClassName}(formData);  
216 - const data = response?.data ?? response ?? {};  
217 - let text = '导入成功数量:' + (data.successCount || 0) + ';导入失败数量:' + (data.failureCount || 0) + ';';  
218 - if (data.failureRows) {  
219 - Object.keys(data.failureRows).forEach((rowNo) => {  
220 - text += '< 第' + rowNo + '行: ' + data.failureRows[rowNo] + ' >';  
221 - });  
222 - }  
223 - ElMessage.info(text);  
224 - await getList();  
225 - } finally {  
226 - target.value = '';  
227 - importLoading.value = false;  
228 - }  
229 -}  
230 205
231 #if (${table.templateType} == 2) 206 #if (${table.templateType} == 2)
232 /** 切换树形展开/收缩状态 */ 207 /** 切换树形展开/收缩状态 */
@@ -247,7 +222,9 @@ onMounted(() =&gt; { @@ -247,7 +222,9 @@ onMounted(() =&gt; {
247 <template> 222 <template>
248 <Page auto-content-height> 223 <Page auto-content-height>
249 <FormModal @success="getList" /> 224 <FormModal @success="getList" />
250 - <input ref="importFileRef" type="file" accept=".xls,.xlsx" class="hidden" @change="handleImportFileChange" /> 225 +#if ($importEnable)
  226 + <ImportFormModal @success="getList" />
  227 +#end
251 228
252 <ContentWrap v-if="!hiddenSearchBar"> 229 <ContentWrap v-if="!hiddenSearchBar">
253 <!-- 搜索工作栏 --> 230 <!-- 搜索工作栏 -->
@@ -356,23 +333,16 @@ onMounted(() =&gt; { @@ -356,23 +333,16 @@ onMounted(() =&gt; {
356 > 333 >
357 {{ $t('ui.actionTitle.create', ['${table.classComment}']) }} 334 {{ $t('ui.actionTitle.create', ['${table.classComment}']) }}
358 </el-button> 335 </el-button>
  336 +#if ($importEnable)
359 <el-button 337 <el-button
360 class="ml-2" 338 class="ml-2"
361 type="primary" 339 type="primary"
362 - :loading="importLoading"  
363 @click="handleImport" 340 @click="handleImport"
364 v-access:code="['${permissionPrefix}:import']" 341 v-access:code="['${permissionPrefix}:import']"
365 > 342 >
366 导入 343 导入
367 </el-button> 344 </el-button>
368 - <el-button  
369 - class="ml-2"  
370 - type="primary"  
371 - @click="handleImportTemplateDownload"  
372 - v-access:code="['${permissionPrefix}:import']"  
373 - >  
374 - 导入模板  
375 - </el-button> 345 +#end
376 <el-button 346 <el-button
377 :icon="h(Download)" 347 :icon="h(Download)"
378 type="primary" 348 type="primary"
yudao-module-infra/src/main/resources/codegen/vue3_vben5_ele/schema/api/api.ts.vm
@@ -113,6 +113,7 @@ export function delete${simpleClassName}List(ids: number[]) { @@ -113,6 +113,7 @@ export function delete${simpleClassName}List(ids: number[]) {
113 export function export${simpleClassName}(params: any) { 113 export function export${simpleClassName}(params: any) {
114 return requestClient.download('${baseURL}/export-excel', { params }); 114 return requestClient.download('${baseURL}/export-excel', { params });
115 } 115 }
  116 +#if ($importEnable)
116 117
117 /** 下载${table.classComment}导入模板 */ 118 /** 下载${table.classComment}导入模板 */
118 export function import${simpleClassName}Template() { 119 export function import${simpleClassName}Template() {
@@ -120,9 +121,10 @@ export function import${simpleClassName}Template() { @@ -120,9 +121,10 @@ export function import${simpleClassName}Template() {
120 } 121 }
121 122
122 /** 导入${table.classComment} */ 123 /** 导入${table.classComment} */
123 -export function import${simpleClassName}(data: FormData) {  
124 - return requestClient.post('${baseURL}/import', data); 124 +export function import${simpleClassName}(file: File) {
  125 + return requestClient.upload('${baseURL}/import', { file });
125 } 126 }
  127 +#end
126 128
127 ## 特殊:主子表专属逻辑 129 ## 特殊:主子表专属逻辑
128 #foreach ($subTable in $subTables) 130 #foreach ($subTable in $subTables)
yudao-module-infra/src/main/resources/codegen/vue3_vben5_ele/schema/views/data.ts.vm
@@ -137,6 +137,21 @@ export function useFormSchema(): VbenFormSchema[] { @@ -137,6 +137,21 @@ export function useFormSchema(): VbenFormSchema[] {
137 ]; 137 ];
138 } 138 }
139 139
  140 +#if ($importEnable)
  141 +/** 导入的表单 */
  142 +export function useImportFormSchema(): VbenFormSchema[] {
  143 + return [
  144 + {
  145 + fieldName: 'file',
  146 + label: '${table.classComment}数据',
  147 + component: 'Upload',
  148 + rules: 'required',
  149 + help: '仅允许导入 xls、xlsx 格式文件',
  150 + },
  151 + ];
  152 +}
  153 +
  154 +#end
140 /** 列表的搜索表单 */ 155 /** 列表的搜索表单 */
141 export function useGridFormSchema(): VbenFormSchema[] { 156 export function useGridFormSchema(): VbenFormSchema[] {
142 return [ 157 return [
yudao-module-infra/src/main/resources/codegen/vue3_vben5_ele/schema/views/import.vue.vm 0 → 100644
  1 +<script lang="ts" setup>
  2 +import { useVbenModal } from '@vben/common-ui';
  3 +import { downloadFileFromBlobPart } from '@vben/utils';
  4 +
  5 +import { ElButton, ElMessage, ElUpload } from 'element-plus';
  6 +
  7 +import { useVbenForm } from '#/adapter/form';
  8 +import { import${simpleClassName}, import${simpleClassName}Template } from '#/api/${table.moduleName}/${table.businessName}';
  9 +import { $t } from '#/locales';
  10 +
  11 +import { useImportFormSchema } from '../data';
  12 +
  13 +const emit = defineEmits(['success']);
  14 +
  15 +const [Form, formApi] = useVbenForm({
  16 + commonConfig: {
  17 + formItemClass: 'col-span-2',
  18 + labelWidth: 120,
  19 + },
  20 + layout: 'horizontal',
  21 + schema: useImportFormSchema(),
  22 + showDefaultActions: false,
  23 +});
  24 +
  25 +const [Modal, modalApi] = useVbenModal({
  26 + async onConfirm() {
  27 + const { valid } = await formApi.validate();
  28 + if (!valid) {
  29 + return;
  30 + }
  31 + modalApi.lock();
  32 + // 提交表单
  33 + const data = await formApi.getValues();
  34 + try {
  35 + await import${simpleClassName}(data.file);
  36 + // 关闭并提示
  37 + await modalApi.close();
  38 + emit('success');
  39 + ElMessage.success($t('ui.actionMessage.operationSuccess'));
  40 + } finally {
  41 + modalApi.unlock();
  42 + }
  43 + },
  44 +});
  45 +
  46 +/** 文件改变时 */
  47 +function handleChange(file: any) {
  48 + if (file.raw) {
  49 + formApi.setFieldValue('file', file.raw);
  50 + }
  51 +}
  52 +
  53 +/** 下载模板 */
  54 +async function handleDownload() {
  55 + const data = await import${simpleClassName}Template();
  56 + downloadFileFromBlobPart({ fileName: '${table.classComment}导入模板.xls', source: data });
  57 +}
  58 +</script>
  59 +
  60 +<template>
  61 + <Modal title="导入${table.classComment}" class="w-1/3">
  62 + <Form class="mx-4">
  63 + <template #file>
  64 + <div class="w-full">
  65 + <ElUpload
  66 + :limit="1"
  67 + accept=".xls,.xlsx"
  68 + :on-change="handleChange"
  69 + :auto-upload="false"
  70 + >
  71 + <ElButton type="primary"> 选择 Excel 文件 </ElButton>
  72 + </ElUpload>
  73 + </div>
  74 + </template>
  75 + </Form>
  76 + <template #prepend-footer>
  77 + <div class="flex flex-auto items-center">
  78 + <ElButton @click="handleDownload"> 下载导入模板 </ElButton>
  79 + </div>
  80 + </template>
  81 + </Modal>
  82 +</template>
yudao-module-infra/src/main/resources/codegen/vue3_vben5_ele/schema/views/index.vue.vm
@@ -16,8 +16,6 @@ import { @@ -16,8 +16,6 @@ import {
16 delete${simpleClassName}, 16 delete${simpleClassName},
17 export${simpleClassName}, 17 export${simpleClassName},
18 get${simpleClassName}List, 18 get${simpleClassName}List,
19 - import${simpleClassName},  
20 - import${simpleClassName}Template,  
21 } from '#/api/${table.moduleName}/${table.businessName}'; 19 } from '#/api/${table.moduleName}/${table.businessName}';
22 #else## 标准表接口 20 #else## 标准表接口
23 import { 21 import {
@@ -26,10 +24,11 @@ import { @@ -26,10 +24,11 @@ import {
26 delete${simpleClassName}List,#end 24 delete${simpleClassName}List,#end
27 export${simpleClassName}, 25 export${simpleClassName},
28 get${simpleClassName}Page, 26 get${simpleClassName}Page,
29 - import${simpleClassName},  
30 - import${simpleClassName}Template,  
31 } from '#/api/${table.moduleName}/${table.businessName}'; 27 } from '#/api/${table.moduleName}/${table.businessName}';
32 #end 28 #end
  29 +#if ($importEnable)
  30 +import ${simpleClassName}Import from './modules/import-form.vue';
  31 +#end
33 import { $t } from '#/locales'; 32 import { $t } from '#/locales';
34 33
35 import { useGridColumns, useGridFormSchema } from './data'; 34 import { useGridColumns, useGridFormSchema } from './data';
@@ -54,8 +53,17 @@ const [FormModal, formModalApi] = useVbenModal({ @@ -54,8 +53,17 @@ const [FormModal, formModalApi] = useVbenModal({
54 connectedComponent: Form, 53 connectedComponent: Form,
55 destroyOnClose: true, 54 destroyOnClose: true,
56 }); 55 });
57 -const importLoading = ref(false);  
58 -const importFileRef = ref<HTMLInputElement>(); 56 +#if ($importEnable)
  57 +const [ImportFormModal, importFormModalApi] = useVbenModal({
  58 + connectedComponent: ${simpleClassName}Import,
  59 + destroyOnClose: true,
  60 +});
  61 +
  62 +/** 导入${table.classComment} */
  63 +function handleImport() {
  64 + importFormModalApi.open();
  65 +}
  66 +#end
59 #if (${table.templateType} == 2)## 树表特有:控制表格展开收缩 67 #if (${table.templateType} == 2)## 树表特有:控制表格展开收缩
60 68
61 /** 切换树形展开/收缩状态 */ 69 /** 切换树形展开/收缩状态 */
@@ -139,43 +147,6 @@ async function handleExport() { @@ -139,43 +147,6 @@ async function handleExport() {
139 downloadFileFromBlobPart({ fileName: '${table.classComment}.xls', source: data }); 147 downloadFileFromBlobPart({ fileName: '${table.classComment}.xls', source: data });
140 } 148 }
141 149
142 -/** 导入模板下载 */  
143 -async function handleImportTemplateDownload() {  
144 - const data = await import${simpleClassName}Template();  
145 - downloadFileFromBlobPart({ fileName: '${table.classComment}导入模板.xls', source: data });  
146 -}  
147 -  
148 -/** 导入按钮操作 */  
149 -function handleImport() {  
150 - importFileRef.value?.click();  
151 -}  
152 -  
153 -/** 导入文件变更 */  
154 -async function handleImportFileChange(event: Event) {  
155 - const target = event.target as HTMLInputElement;  
156 - const file = target.files?.[0];  
157 - if (!file) {  
158 - return;  
159 - }  
160 - importLoading.value = true;  
161 - try {  
162 - const formData = new FormData();  
163 - formData.append('file', file);  
164 - const response: any = await import${simpleClassName}(formData);  
165 - const data = response?.data ?? response ?? {};  
166 - let text = '导入成功数量:' + (data.successCount || 0) + ';导入失败数量:' + (data.failureCount || 0) + ';';  
167 - if (data.failureRows) {  
168 - Object.keys(data.failureRows).forEach((rowNo) => {  
169 - text += '< 第' + rowNo + '行: ' + data.failureRows[rowNo] + ' >';  
170 - });  
171 - }  
172 - ElMessage.info(text);  
173 - handleRefresh();  
174 - } finally {  
175 - target.value = '';  
176 - importLoading.value = false;  
177 - }  
178 -}  
179 150
180 const [Grid, gridApi] = useVbenVxeGrid({ 151 const [Grid, gridApi] = useVbenVxeGrid({
181 formOptions: { 152 formOptions: {
@@ -252,7 +223,9 @@ const [Grid, gridApi] = useVbenVxeGrid({ @@ -252,7 +223,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
252 <template> 223 <template>
253 <Page auto-content-height> 224 <Page auto-content-height>
254 <FormModal @success="handleRefresh" /> 225 <FormModal @success="handleRefresh" />
255 - <input ref="importFileRef" type="file" accept=".xls,.xlsx" class="hidden" @change="handleImportFileChange" /> 226 +#if ($importEnable)
  227 + <ImportFormModal @success="handleRefresh" />
  228 +#end
256 #if ($table.templateType == 11) ## erp情况 229 #if ($table.templateType == 11) ## erp情况
257 <div> 230 <div>
258 #end 231 #end
@@ -290,19 +263,14 @@ const [Grid, gridApi] = useVbenVxeGrid({ @@ -290,19 +263,14 @@ const [Grid, gridApi] = useVbenVxeGrid({
290 onClick: handleExpand, 263 onClick: handleExpand,
291 }, 264 },
292 #end 265 #end
  266 +#if ($importEnable)
293 { 267 {
294 label: '导入', 268 label: '导入',
295 type: 'primary', 269 type: 'primary',
296 auth: ['${table.moduleName}:${simpleClassName_strikeCase}:import'], 270 auth: ['${table.moduleName}:${simpleClassName_strikeCase}:import'],
297 - disabled: importLoading.value,  
298 onClick: handleImport, 271 onClick: handleImport,
299 }, 272 },
300 - {  
301 - label: '导入模板',  
302 - type: 'primary',  
303 - auth: ['${table.moduleName}:${simpleClassName_strikeCase}:import'],  
304 - onClick: handleImportTemplateDownload,  
305 - }, 273 +#end
306 { 274 {
307 label: $t('ui.actionTitle.export'), 275 label: $t('ui.actionTitle.export'),
308 type: 'primary', 276 type: 'primary',
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineAbstractTest.java
@@ -10,6 +10,7 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils; @@ -10,6 +10,7 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
10 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; 10 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
11 import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO; 11 import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
12 import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO; 12 import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
  13 +import cn.iocoder.yudao.module.infra.enums.codegen.CodegenVOTypeEnum;
13 import cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties; 14 import cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties;
14 import org.junit.jupiter.api.BeforeEach; 15 import org.junit.jupiter.api.BeforeEach;
15 import org.mockito.InjectMocks; 16 import org.mockito.InjectMocks;
@@ -19,8 +20,10 @@ import java.io.ByteArrayInputStream; @@ -19,8 +20,10 @@ import java.io.ByteArrayInputStream;
19 import java.io.ByteArrayOutputStream; 20 import java.io.ByteArrayOutputStream;
20 import java.util.ArrayList; 21 import java.util.ArrayList;
21 import java.util.HashMap; 22 import java.util.HashMap;
  23 +import java.util.LinkedHashSet;
22 import java.util.List; 24 import java.util.List;
23 import java.util.Map; 25 import java.util.Map;
  26 +import java.util.Set;
24 27
25 import static org.junit.jupiter.api.Assertions.assertEquals; 28 import static org.junit.jupiter.api.Assertions.assertEquals;
26 29
@@ -41,16 +44,18 @@ public abstract class CodegenEngineAbstractTest extends BaseMockitoUnitTest { @@ -41,16 +44,18 @@ public abstract class CodegenEngineAbstractTest extends BaseMockitoUnitTest {
41 44
42 @Spy 45 @Spy
43 protected CodegenProperties codegenProperties = new CodegenProperties() 46 protected CodegenProperties codegenProperties = new CodegenProperties()
44 - .setBasePackage("cn.iocoder.yudao"); 47 + .setBasePackage("cn.iocoder.yudao")
  48 + .setVoType(CodegenVOTypeEnum.VO.getType())
  49 + .setDeleteBatchEnable(true)
  50 + .setUnitTestEnable(true)
  51 + .setImportEnable(false);
45 52
46 @BeforeEach 53 @BeforeEach
47 public void setUp() { 54 public void setUp() {
48 codegenEngine.setJakartaEnable(true); // 强制使用 jakarta,保证单测可以基于 jakarta 断言 55 codegenEngine.setJakartaEnable(true); // 强制使用 jakarta,保证单测可以基于 jakarta 断言
49 codegenEngine.initGlobalBindingMap(); 56 codegenEngine.initGlobalBindingMap();
50 - // 单测强制使用  
51 - // 获取测试文件 resources 路径 57 + // 获取测试文件 resources 路径,writeResult 调试用
52 String absolutePath = FileUtil.getAbsolutePath("application-unit-test.yaml"); 58 String absolutePath = FileUtil.getAbsolutePath("application-unit-test.yaml");
53 - // 系统不一样生成的文件也有差异,那就各自生成各自的  
54 resourcesPath = absolutePath.split("/target")[0] + "/src/test/resources/codegen/"; 59 resourcesPath = absolutePath.split("/target")[0] + "/src/test/resources/codegen/";
55 } 60 }
56 61
@@ -82,17 +87,32 @@ public abstract class CodegenEngineAbstractTest extends BaseMockitoUnitTest { @@ -82,17 +87,32 @@ public abstract class CodegenEngineAbstractTest extends BaseMockitoUnitTest {
82 return list; 87 return list;
83 } 88 }
84 89
  90 + /**
  91 + * 重新生成断言数据的开关,命令行加 {@code -Dcodegen.regenerate=true} 启用
  92 + */
  93 + private static final boolean REGENERATE = Boolean.parseBoolean(System.getProperty("codegen.regenerate", "false"));
  94 +
85 @SuppressWarnings("rawtypes") 95 @SuppressWarnings("rawtypes")
86 - protected static void assertResult(Map<String, String> result, String path) { 96 + protected void assertResult(Map<String, String> result, String path) {
  97 + if (REGENERATE) {
  98 + writeResult(result, resourcesPath + path);
  99 + return;
  100 + }
87 String assertContent = ResourceUtil.readUtf8Str("codegen/" + path + "/assert.json"); 101 String assertContent = ResourceUtil.readUtf8Str("codegen/" + path + "/assert.json");
88 List<HashMap> asserts = JsonUtils.parseArray(assertContent, HashMap.class); 102 List<HashMap> asserts = JsonUtils.parseArray(assertContent, HashMap.class);
89 - assertEquals(asserts.size(), result.size());  
90 - // 校验每个文件 103 + Set<String> expectedFiles = asserts.stream()
  104 + .map(m -> (String) m.get("filePath"))
  105 + .collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new));
  106 + assertEquals(expectedFiles, result.keySet(), "生成文件集合不匹配");
  107 + // 校验每个文件;归一化 \r\n 为 \n,让断言不依赖文件落盘的换行风格
91 asserts.forEach(assertMap -> { 108 asserts.forEach(assertMap -> {
92 String contentPath = (String) assertMap.get("contentPath"); 109 String contentPath = (String) assertMap.get("contentPath");
93 String filePath = (String) assertMap.get("filePath"); 110 String filePath = (String) assertMap.get("filePath");
94 - String content = ResourceUtil.readUtf8Str("codegen/" + path + "/" + contentPath);  
95 - assertEquals(content, result.get(filePath), filePath + ":不匹配"); 111 + String expected = ResourceUtil.readUtf8Str("codegen/" + path + "/" + contentPath)
  112 + .replace("\r\n", "\n");
  113 + String actual = result.get(filePath);
  114 + assertEquals(expected, actual == null ? null : actual.replace("\r\n", "\n"),
  115 + filePath + ":不匹配");
96 }); 116 });
97 } 117 }
98 118
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineVben5AntdGeneralTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.service.codegen.inner;
  2 +
  3 +import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
  4 +import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
  5 +import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;
  6 +import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
  7 +import com.baomidou.mybatisplus.annotation.DbType;
  8 +import org.junit.jupiter.api.Test;
  9 +
  10 +import java.util.Arrays;
  11 +import java.util.List;
  12 +import java.util.Map;
  13 +
  14 +/**
  15 + * {@link CodegenEngine} 的 Vue3 + Vben5 + AntD + General 单元测试
  16 + *
  17 + * @author 芋道源码
  18 + */
  19 +public class CodegenEngineVben5AntdGeneralTest extends CodegenEngineAbstractTest {
  20 +
  21 + private static final Integer FRONT_TYPE = CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType();
  22 +
  23 + @Test
  24 + public void testExecute_one() {
  25 + // 准备参数
  26 + CodegenTableDO table = getTable("student")
  27 + .setFrontType(FRONT_TYPE)
  28 + .setTemplateType(CodegenTemplateTypeEnum.ONE.getType());
  29 + List<CodegenColumnDO> columns = getColumnList("student");
  30 +
  31 + // 调用
  32 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);
  33 + // 断言
  34 + assertResult(result, "/vben5_antd_general_one");
  35 + }
  36 +
  37 + @Test
  38 + public void testExecute_one_importEnable() {
  39 + // 开启 import 开关
  40 + codegenProperties.setImportEnable(true);
  41 + codegenEngine.initGlobalBindingMap();
  42 + // 准备参数
  43 + CodegenTableDO table = getTable("student")
  44 + .setFrontType(FRONT_TYPE)
  45 + .setTemplateType(CodegenTemplateTypeEnum.ONE.getType());
  46 + List<CodegenColumnDO> columns = getColumnList("student");
  47 +
  48 + // 调用
  49 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);
  50 + // 断言
  51 + assertResult(result, "/vben5_antd_general_one_importEnable");
  52 + }
  53 +
  54 + @Test
  55 + public void testExecute_tree() {
  56 + // 准备参数
  57 + CodegenTableDO table = getTable("category")
  58 + .setFrontType(FRONT_TYPE)
  59 + .setTemplateType(CodegenTemplateTypeEnum.TREE.getType());
  60 + List<CodegenColumnDO> columns = getColumnList("category");
  61 +
  62 + // 调用
  63 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);
  64 + // 断言
  65 + assertResult(result, "/vben5_antd_general_tree");
  66 + }
  67 +
  68 + @Test
  69 + public void testExecute_master_normal() {
  70 + testExecute_master(CodegenTemplateTypeEnum.MASTER_NORMAL, "/vben5_antd_general_master_normal");
  71 + }
  72 +
  73 + @Test
  74 + public void testExecute_master_erp() {
  75 + testExecute_master(CodegenTemplateTypeEnum.MASTER_ERP, "/vben5_antd_general_master_erp");
  76 + }
  77 +
  78 + @Test
  79 + public void testExecute_master_inner() {
  80 + testExecute_master(CodegenTemplateTypeEnum.MASTER_INNER, "/vben5_antd_general_master_inner");
  81 + }
  82 +
  83 + private void testExecute_master(CodegenTemplateTypeEnum templateType, String path) {
  84 + // 准备参数
  85 + CodegenTableDO table = getTable("student")
  86 + .setFrontType(FRONT_TYPE)
  87 + .setTemplateType(templateType.getType());
  88 + List<CodegenColumnDO> columns = getColumnList("student");
  89 + // 准备参数(子表)
  90 + CodegenTableDO contactTable = getTable("contact")
  91 + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType())
  92 + .setFrontType(FRONT_TYPE)
  93 + .setSubJoinColumnId(100L).setSubJoinMany(true);
  94 + List<CodegenColumnDO> contactColumns = getColumnList("contact");
  95 + // 准备参数(班主任)
  96 + CodegenTableDO teacherTable = getTable("teacher")
  97 + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType())
  98 + .setFrontType(FRONT_TYPE)
  99 + .setSubJoinColumnId(200L).setSubJoinMany(false);
  100 + List<CodegenColumnDO> teacherColumns = getColumnList("teacher");
  101 +
  102 + // 调用
  103 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns,
  104 + Arrays.asList(contactTable, teacherTable), Arrays.asList(contactColumns, teacherColumns));
  105 + // 断言
  106 + assertResult(result, path);
  107 + }
  108 +
  109 +}
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineVben5AntdSchemaTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.service.codegen.inner;
  2 +
  3 +import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
  4 +import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
  5 +import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;
  6 +import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
  7 +import com.baomidou.mybatisplus.annotation.DbType;
  8 +import org.junit.jupiter.api.Test;
  9 +
  10 +import java.util.Arrays;
  11 +import java.util.List;
  12 +import java.util.Map;
  13 +
  14 +/**
  15 + * {@link CodegenEngine} 的 Vue3 + Vben5 + AntD + Schema 单元测试
  16 + *
  17 + * @author 芋道源码
  18 + */
  19 +public class CodegenEngineVben5AntdSchemaTest extends CodegenEngineAbstractTest {
  20 +
  21 + private static final Integer FRONT_TYPE = CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType();
  22 +
  23 + @Test
  24 + public void testExecute_one() {
  25 + // 准备参数
  26 + CodegenTableDO table = getTable("student")
  27 + .setFrontType(FRONT_TYPE)
  28 + .setTemplateType(CodegenTemplateTypeEnum.ONE.getType());
  29 + List<CodegenColumnDO> columns = getColumnList("student");
  30 +
  31 + // 调用
  32 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);
  33 + // 断言
  34 + assertResult(result, "/vben5_antd_schema_one");
  35 + }
  36 +
  37 + @Test
  38 + public void testExecute_one_importEnable() {
  39 + // 开启 import 开关
  40 + codegenProperties.setImportEnable(true);
  41 + codegenEngine.initGlobalBindingMap();
  42 + // 准备参数
  43 + CodegenTableDO table = getTable("student")
  44 + .setFrontType(FRONT_TYPE)
  45 + .setTemplateType(CodegenTemplateTypeEnum.ONE.getType());
  46 + List<CodegenColumnDO> columns = getColumnList("student");
  47 +
  48 + // 调用
  49 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);
  50 + // 断言
  51 + assertResult(result, "/vben5_antd_schema_one_importEnable");
  52 + }
  53 +
  54 + @Test
  55 + public void testExecute_tree() {
  56 + // 准备参数
  57 + CodegenTableDO table = getTable("category")
  58 + .setFrontType(FRONT_TYPE)
  59 + .setTemplateType(CodegenTemplateTypeEnum.TREE.getType());
  60 + List<CodegenColumnDO> columns = getColumnList("category");
  61 +
  62 + // 调用
  63 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);
  64 + // 断言
  65 + assertResult(result, "/vben5_antd_schema_tree");
  66 + }
  67 +
  68 + @Test
  69 + public void testExecute_master_normal() {
  70 + testExecute_master(CodegenTemplateTypeEnum.MASTER_NORMAL, "/vben5_antd_schema_master_normal");
  71 + }
  72 +
  73 + @Test
  74 + public void testExecute_master_erp() {
  75 + testExecute_master(CodegenTemplateTypeEnum.MASTER_ERP, "/vben5_antd_schema_master_erp");
  76 + }
  77 +
  78 + @Test
  79 + public void testExecute_master_inner() {
  80 + testExecute_master(CodegenTemplateTypeEnum.MASTER_INNER, "/vben5_antd_schema_master_inner");
  81 + }
  82 +
  83 + private void testExecute_master(CodegenTemplateTypeEnum templateType, String path) {
  84 + // 准备参数
  85 + CodegenTableDO table = getTable("student")
  86 + .setFrontType(FRONT_TYPE)
  87 + .setTemplateType(templateType.getType());
  88 + List<CodegenColumnDO> columns = getColumnList("student");
  89 + // 准备参数(子表)
  90 + CodegenTableDO contactTable = getTable("contact")
  91 + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType())
  92 + .setFrontType(FRONT_TYPE)
  93 + .setSubJoinColumnId(100L).setSubJoinMany(true);
  94 + List<CodegenColumnDO> contactColumns = getColumnList("contact");
  95 + // 准备参数(班主任)
  96 + CodegenTableDO teacherTable = getTable("teacher")
  97 + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType())
  98 + .setFrontType(FRONT_TYPE)
  99 + .setSubJoinColumnId(200L).setSubJoinMany(false);
  100 + List<CodegenColumnDO> teacherColumns = getColumnList("teacher");
  101 +
  102 + // 调用
  103 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns,
  104 + Arrays.asList(contactTable, teacherTable), Arrays.asList(contactColumns, teacherColumns));
  105 + // 断言
  106 + assertResult(result, path);
  107 + }
  108 +
  109 +}
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineVben5EleGeneralTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.service.codegen.inner;
  2 +
  3 +import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
  4 +import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
  5 +import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;
  6 +import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
  7 +import com.baomidou.mybatisplus.annotation.DbType;
  8 +import org.junit.jupiter.api.Test;
  9 +
  10 +import java.util.Arrays;
  11 +import java.util.List;
  12 +import java.util.Map;
  13 +
  14 +/**
  15 + * {@link CodegenEngine} 的 Vue3 + Vben5 + Element Plus + General 单元测试
  16 + *
  17 + * @author 芋道源码
  18 + */
  19 +public class CodegenEngineVben5EleGeneralTest extends CodegenEngineAbstractTest {
  20 +
  21 + private static final Integer FRONT_TYPE = CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL.getType();
  22 +
  23 + @Test
  24 + public void testExecute_one() {
  25 + // 准备参数
  26 + CodegenTableDO table = getTable("student")
  27 + .setFrontType(FRONT_TYPE)
  28 + .setTemplateType(CodegenTemplateTypeEnum.ONE.getType());
  29 + List<CodegenColumnDO> columns = getColumnList("student");
  30 +
  31 + // 调用
  32 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);
  33 + // 断言
  34 + assertResult(result, "/vben5_ele_general_one");
  35 + }
  36 +
  37 + @Test
  38 + public void testExecute_one_importEnable() {
  39 + // 开启 import 开关
  40 + codegenProperties.setImportEnable(true);
  41 + codegenEngine.initGlobalBindingMap();
  42 + // 准备参数
  43 + CodegenTableDO table = getTable("student")
  44 + .setFrontType(FRONT_TYPE)
  45 + .setTemplateType(CodegenTemplateTypeEnum.ONE.getType());
  46 + List<CodegenColumnDO> columns = getColumnList("student");
  47 +
  48 + // 调用
  49 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);
  50 + // 断言
  51 + assertResult(result, "/vben5_ele_general_one_importEnable");
  52 + }
  53 +
  54 + @Test
  55 + public void testExecute_tree() {
  56 + // 准备参数
  57 + CodegenTableDO table = getTable("category")
  58 + .setFrontType(FRONT_TYPE)
  59 + .setTemplateType(CodegenTemplateTypeEnum.TREE.getType());
  60 + List<CodegenColumnDO> columns = getColumnList("category");
  61 +
  62 + // 调用
  63 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);
  64 + // 断言
  65 + assertResult(result, "/vben5_ele_general_tree");
  66 + }
  67 +
  68 + @Test
  69 + public void testExecute_master_normal() {
  70 + testExecute_master(CodegenTemplateTypeEnum.MASTER_NORMAL, "/vben5_ele_general_master_normal");
  71 + }
  72 +
  73 + @Test
  74 + public void testExecute_master_erp() {
  75 + testExecute_master(CodegenTemplateTypeEnum.MASTER_ERP, "/vben5_ele_general_master_erp");
  76 + }
  77 +
  78 + @Test
  79 + public void testExecute_master_inner() {
  80 + testExecute_master(CodegenTemplateTypeEnum.MASTER_INNER, "/vben5_ele_general_master_inner");
  81 + }
  82 +
  83 + private void testExecute_master(CodegenTemplateTypeEnum templateType, String path) {
  84 + // 准备参数
  85 + CodegenTableDO table = getTable("student")
  86 + .setFrontType(FRONT_TYPE)
  87 + .setTemplateType(templateType.getType());
  88 + List<CodegenColumnDO> columns = getColumnList("student");
  89 + // 准备参数(子表)
  90 + CodegenTableDO contactTable = getTable("contact")
  91 + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType())
  92 + .setFrontType(FRONT_TYPE)
  93 + .setSubJoinColumnId(100L).setSubJoinMany(true);
  94 + List<CodegenColumnDO> contactColumns = getColumnList("contact");
  95 + // 准备参数(班主任)
  96 + CodegenTableDO teacherTable = getTable("teacher")
  97 + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType())
  98 + .setFrontType(FRONT_TYPE)
  99 + .setSubJoinColumnId(200L).setSubJoinMany(false);
  100 + List<CodegenColumnDO> teacherColumns = getColumnList("teacher");
  101 +
  102 + // 调用
  103 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns,
  104 + Arrays.asList(contactTable, teacherTable), Arrays.asList(contactColumns, teacherColumns));
  105 + // 断言
  106 + assertResult(result, path);
  107 + }
  108 +
  109 +}
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineVben5EleSchemaTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.service.codegen.inner;
  2 +
  3 +import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
  4 +import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
  5 +import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;
  6 +import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
  7 +import com.baomidou.mybatisplus.annotation.DbType;
  8 +import org.junit.jupiter.api.Test;
  9 +
  10 +import java.util.Arrays;
  11 +import java.util.List;
  12 +import java.util.Map;
  13 +
  14 +/**
  15 + * {@link CodegenEngine} 的 Vue3 + Vben5 + Element Plus + Schema 单元测试
  16 + *
  17 + * @author 芋道源码
  18 + */
  19 +public class CodegenEngineVben5EleSchemaTest extends CodegenEngineAbstractTest {
  20 +
  21 + private static final Integer FRONT_TYPE = CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType();
  22 +
  23 + @Test
  24 + public void testExecute_one() {
  25 + // 准备参数
  26 + CodegenTableDO table = getTable("student")
  27 + .setFrontType(FRONT_TYPE)
  28 + .setTemplateType(CodegenTemplateTypeEnum.ONE.getType());
  29 + List<CodegenColumnDO> columns = getColumnList("student");
  30 +
  31 + // 调用
  32 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);
  33 + // 断言
  34 + assertResult(result, "/vben5_ele_schema_one");
  35 + }
  36 +
  37 + @Test
  38 + public void testExecute_one_importEnable() {
  39 + // 开启 import 开关
  40 + codegenProperties.setImportEnable(true);
  41 + codegenEngine.initGlobalBindingMap();
  42 + // 准备参数
  43 + CodegenTableDO table = getTable("student")
  44 + .setFrontType(FRONT_TYPE)
  45 + .setTemplateType(CodegenTemplateTypeEnum.ONE.getType());
  46 + List<CodegenColumnDO> columns = getColumnList("student");
  47 +
  48 + // 调用
  49 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);
  50 + // 断言
  51 + assertResult(result, "/vben5_ele_schema_one_importEnable");
  52 + }
  53 +
  54 + @Test
  55 + public void testExecute_tree() {
  56 + // 准备参数
  57 + CodegenTableDO table = getTable("category")
  58 + .setFrontType(FRONT_TYPE)
  59 + .setTemplateType(CodegenTemplateTypeEnum.TREE.getType());
  60 + List<CodegenColumnDO> columns = getColumnList("category");
  61 +
  62 + // 调用
  63 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);
  64 + // 断言
  65 + assertResult(result, "/vben5_ele_schema_tree");
  66 + }
  67 +
  68 + @Test
  69 + public void testExecute_master_normal() {
  70 + testExecute_master(CodegenTemplateTypeEnum.MASTER_NORMAL, "/vben5_ele_schema_master_normal");
  71 + }
  72 +
  73 + @Test
  74 + public void testExecute_master_erp() {
  75 + testExecute_master(CodegenTemplateTypeEnum.MASTER_ERP, "/vben5_ele_schema_master_erp");
  76 + }
  77 +
  78 + @Test
  79 + public void testExecute_master_inner() {
  80 + testExecute_master(CodegenTemplateTypeEnum.MASTER_INNER, "/vben5_ele_schema_master_inner");
  81 + }
  82 +
  83 + private void testExecute_master(CodegenTemplateTypeEnum templateType, String path) {
  84 + // 准备参数
  85 + CodegenTableDO table = getTable("student")
  86 + .setFrontType(FRONT_TYPE)
  87 + .setTemplateType(templateType.getType());
  88 + List<CodegenColumnDO> columns = getColumnList("student");
  89 + // 准备参数(子表)
  90 + CodegenTableDO contactTable = getTable("contact")
  91 + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType())
  92 + .setFrontType(FRONT_TYPE)
  93 + .setSubJoinColumnId(100L).setSubJoinMany(true);
  94 + List<CodegenColumnDO> contactColumns = getColumnList("contact");
  95 + // 准备参数(班主任)
  96 + CodegenTableDO teacherTable = getTable("teacher")
  97 + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType())
  98 + .setFrontType(FRONT_TYPE)
  99 + .setSubJoinColumnId(200L).setSubJoinMany(false);
  100 + List<CodegenColumnDO> teacherColumns = getColumnList("teacher");
  101 +
  102 + // 调用
  103 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns,
  104 + Arrays.asList(contactTable, teacherTable), Arrays.asList(contactColumns, teacherColumns));
  105 + // 断言
  106 + assertResult(result, path);
  107 + }
  108 +
  109 +}
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineVue2Test.java
@@ -5,7 +5,6 @@ import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO; @@ -5,7 +5,6 @@ import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
5 import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum; 5 import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;
6 import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum; 6 import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
7 import com.baomidou.mybatisplus.annotation.DbType; 7 import com.baomidou.mybatisplus.annotation.DbType;
8 -import org.junit.jupiter.api.Disabled;  
9 import org.junit.jupiter.api.Test; 8 import org.junit.jupiter.api.Test;
10 9
11 import java.util.Arrays; 10 import java.util.Arrays;
@@ -17,7 +16,6 @@ import java.util.Map; @@ -17,7 +16,6 @@ import java.util.Map;
17 * 16 *
18 * @author 芋道源码 17 * @author 芋道源码
19 */ 18 */
20 -@Disabled  
21 public class CodegenEngineVue2Test extends CodegenEngineAbstractTest { 19 public class CodegenEngineVue2Test extends CodegenEngineAbstractTest {
22 20
23 @Test 21 @Test
@@ -37,6 +35,23 @@ public class CodegenEngineVue2Test extends CodegenEngineAbstractTest { @@ -37,6 +35,23 @@ public class CodegenEngineVue2Test extends CodegenEngineAbstractTest {
37 } 35 }
38 36
39 @Test 37 @Test
  38 + public void testExecute_vue2_one_importEnable() {
  39 + // 开启 import 开关
  40 + codegenProperties.setImportEnable(true);
  41 + codegenEngine.initGlobalBindingMap();
  42 + // 准备参数
  43 + CodegenTableDO table = getTable("student")
  44 + .setFrontType(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType())
  45 + .setTemplateType(CodegenTemplateTypeEnum.ONE.getType());
  46 + List<CodegenColumnDO> columns = getColumnList("student");
  47 +
  48 + // 调用
  49 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);
  50 + // 断言
  51 + assertResult(result, "/vue2_one_importEnable");
  52 + }
  53 +
  54 + @Test
40 public void testExecute_vue2_tree() { 55 public void testExecute_vue2_tree() {
41 // 准备参数 56 // 准备参数
42 CodegenTableDO table = getTable("category") 57 CodegenTableDO table = getTable("category")
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineVue3Test.java
@@ -5,7 +5,6 @@ import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO; @@ -5,7 +5,6 @@ import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
5 import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum; 5 import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;
6 import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum; 6 import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
7 import com.baomidou.mybatisplus.annotation.DbType; 7 import com.baomidou.mybatisplus.annotation.DbType;
8 -import org.junit.jupiter.api.Disabled;  
9 import org.junit.jupiter.api.Test; 8 import org.junit.jupiter.api.Test;
10 9
11 import java.util.Arrays; 10 import java.util.Arrays;
@@ -17,7 +16,6 @@ import java.util.Map; @@ -17,7 +16,6 @@ import java.util.Map;
17 * 16 *
18 * @author 芋道源码 17 * @author 芋道源码
19 */ 18 */
20 -@Disabled  
21 public class CodegenEngineVue3Test extends CodegenEngineAbstractTest { 19 public class CodegenEngineVue3Test extends CodegenEngineAbstractTest {
22 20
23 @Test 21 @Test
@@ -37,6 +35,23 @@ public class CodegenEngineVue3Test extends CodegenEngineAbstractTest { @@ -37,6 +35,23 @@ public class CodegenEngineVue3Test extends CodegenEngineAbstractTest {
37 } 35 }
38 36
39 @Test 37 @Test
  38 + public void testExecute_vue3_one_importEnable() {
  39 + // 开启 import 开关
  40 + codegenProperties.setImportEnable(true);
  41 + codegenEngine.initGlobalBindingMap();
  42 + // 准备参数
  43 + CodegenTableDO table = getTable("student")
  44 + .setFrontType(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType())
  45 + .setTemplateType(CodegenTemplateTypeEnum.ONE.getType());
  46 + List<CodegenColumnDO> columns = getColumnList("student");
  47 +
  48 + // 调用
  49 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);
  50 + // 断言
  51 + assertResult(result, "/vue3_one_importEnable");
  52 + }
  53 +
  54 + @Test
40 public void testExecute_vue3_tree() { 55 public void testExecute_vue3_tree() {
41 // 准备参数 56 // 准备参数
42 CodegenTableDO table = getTable("category") 57 CodegenTableDO table = getTable("category")
yudao-server/src/main/resources/application.yaml
@@ -307,6 +307,7 @@ yudao: @@ -307,6 +307,7 @@ yudao:
307 vo-type: 10 # VO 的类型,参见 CodegenVOTypeEnum 枚举类 307 vo-type: 10 # VO 的类型,参见 CodegenVOTypeEnum 枚举类
308 delete-batch-enable: true # 是否生成批量删除接口 308 delete-batch-enable: true # 是否生成批量删除接口
309 unit-test-enable: false # 是否生成单元测试 309 unit-test-enable: false # 是否生成单元测试
  310 + import-enable: true # 是否生成 Excel 导入接口
310 tenant: # 多租户相关配置项 311 tenant: # 多租户相关配置项
311 enable: true 312 enable: true
312 ignore-urls: 313 ignore-urls: