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 55 @NotNull(message = "是否生成单元测试不能为空")
56 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 128 vue3FilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
129 129 .put(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), vue3TemplatePath("views/form.vue"),
130 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 133 .put(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), vue3TemplatePath("views/components/form_sub_normal.vue"), // 特殊:主子表专属逻辑
132 134 vue3FilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}Form.vue"))
133 135 .put(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), vue3TemplatePath("views/components/form_sub_inner.vue"), // 特殊:主子表专属逻辑
... ... @@ -166,6 +168,8 @@ public class CodegenEngine {
166 168 vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
167 169 .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/form.vue"),
168 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 173 .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("api/api.ts"),
170 174 vue3VbenFilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
171 175 .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/form_sub_normal.vue"), // 特殊:主子表专属逻辑
... ... @@ -183,6 +187,8 @@ public class CodegenEngine {
183 187 vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
184 188 .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/form.vue"),
185 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 192 .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("api/api.ts"),
187 193 vue3VbenFilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
188 194 .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/form_sub_normal.vue"), // 特殊:主子表专属逻辑
... ... @@ -202,6 +208,8 @@ public class CodegenEngine {
202 208 vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
203 209 .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath("views/form.vue"),
204 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 213 .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath("api/api.ts"),
206 214 vue3VbenFilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
207 215 .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath("views/modules/form_sub_normal.vue"), // 特殊:主子表专属逻辑
... ... @@ -219,6 +227,8 @@ public class CodegenEngine {
219 227 vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
220 228 .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL.getType(), vue3Vben5EpGeneralTemplatePath("views/form.vue"),
221 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 232 .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL.getType(), vue3Vben5EpGeneralTemplatePath("api/api.ts"),
223 233 vue3VbenFilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
224 234 .put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL.getType(), vue3Vben5EpGeneralTemplatePath("views/modules/form_sub_normal.vue"), // 特殊:主子表专属逻辑
... ... @@ -286,6 +296,7 @@ public class CodegenEngine {
286 296 globalBindingMap.put("jakartaPackage", jakartaEnable ? "jakarta" : "javax");
287 297 globalBindingMap.put("voType", codegenProperties.getVoType());
288 298 globalBindingMap.put("deleteBatchEnable", codegenProperties.getDeleteBatchEnable());
  299 + globalBindingMap.put("importEnable", codegenProperties.getImportEnable());
289 300 // 全局 Java Bean
290 301 globalBindingMap.put("CommonResultClassName", CommonResult.class.getName());
291 302 globalBindingMap.put("PageResultClassName", PageResult.class.getName());
... ... @@ -345,6 +356,11 @@ public class CodegenEngine {
345 356 if (!CodegenTemplateTypeEnum.isTree(table.getTemplateType())) {
346 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 365 // 2.3 默认生成
350 366 generateCode(result, vmPath, filePath, bindingMap);
... ... @@ -678,4 +694,9 @@ public class CodegenEngine {
678 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 1 package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName};
2 2  
3 3 import org.springframework.web.bind.annotation.*;
  4 +#if ($importEnable)
4 5 import org.springframework.web.multipart.MultipartFile;
  6 +#end
5 7 import ${jakartaPackage}.annotation.Resource;
6 8 import org.springframework.validation.annotation.Validated;
7 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 162 BeanUtils.toBean(list, ${table.className}RespVO.class));
161 163 }
162 164 #end
  165 +#if ($importEnable)
163 166  
164 167 @GetMapping("/get-import-template")
165 168 @Operation(summary = "获得导入${table.classComment}模板")
166 169 #if ($sceneEnum.scene == 1)
167 170 @PreAuthorize("@ss.hasPermission('${permissionPrefix}:import')")
168 171 #end
169   - public void import${simpleClassName}Template(HttpServletResponse response) throws IOException {
  172 + public void importTemplate(HttpServletResponse response) throws IOException {
170 173 ExcelUtils.write(response, "${table.classComment}导入模板.xls", "数据",
171 174 ${sceneEnum.prefixClass}${table.className}ImportExcelVO.class, Collections.emptyList());
172 175 }
... ... @@ -177,11 +180,11 @@ public class ${sceneEnum.prefixClass}${table.className}Controller {
177 180 #if ($sceneEnum.scene == 1)
178 181 @PreAuthorize("@ss.hasPermission('${permissionPrefix}:import')")
179 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 184 List<${sceneEnum.prefixClass}${table.className}ImportExcelVO> list = ExcelUtils.read(file, ${sceneEnum.prefixClass}${table.className}ImportExcelVO.class);
183 185 return success(${classNameVar}Service.import${simpleClassName}List(list));
184 186 }
  187 +#end
185 188  
186 189 ## 特殊:主子表专属逻辑
187 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 25 * @return 编号
26 26 */
27 27 ${primaryColumn.javaType} create${simpleClassName}(@Valid ${saveReqVOClass} ${saveReqVOVar});
  28 +#if ($importEnable)
28 29  
29 30 /**
30 31 * 导入${table.classComment}
... ... @@ -33,6 +34,7 @@ public interface ${table.className}Service {
33 34 * @return 导入结果
34 35 */
35 36 ${sceneEnum.prefixClass}${table.className}ImportRespVO import${simpleClassName}List(List<${sceneEnum.prefixClass}${table.className}ImportExcelVO> importList);
  37 +#end
36 38  
37 39 /**
38 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 import org.springframework.transaction.annotation.Transactional;
8 8  
9 9 import java.util.*;
  10 +#if ($importEnable)
10 11 import java.util.concurrent.atomic.AtomicInteger;
  12 +#end
11 13 import ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*;
12 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 94 // 返回
93 95 return ${classNameVar}.getId();
94 96 }
  97 +#if ($importEnable)
95 98  
96 99 @Override
97   - @Transactional(rollbackFor = Exception.class)
  100 + @Transactional(rollbackFor = Exception.class) // 添加事务,异常则回滚所有导入
98 101 public ${sceneEnum.prefixClass}${table.className}ImportRespVO import${simpleClassName}List(List<${sceneEnum.prefixClass}${table.className}ImportExcelVO> importList) {
99 102 if (CollUtil.isEmpty(importList)) {
100 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 109 AtomicInteger index = new AtomicInteger(1);
109 110 importList.forEach(importItem -> {
110 111 int currentIndex = index.getAndIncrement();
111 112 try {
112 113 create${simpleClassName}(BeanUtils.toBean(importItem, ${saveReqVOClass}.class));
113   - successCount.incrementAndGet();
  114 + respVO.setSuccessCount(respVO.getSuccessCount() + 1);
114 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 124 @Override
137 125 ## 特殊:主子表专属逻辑(非 ERP 模式)
... ...
yudao-module-infra/src/main/resources/codegen/sql/sql.vm
1 1 ## 通用变量定义
  2 +#if ($importEnable)
2 3 #set ($functionNames = ['查询', '创建', '更新', '删除', '导出', '导入'])
3 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 10 ## 宏定义:生成按钮 SQL(通用部分)
6 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 73 responseType: 'blob'
74 74 })
75 75 }
  76 +#if ($importEnable)
76 77  
77 78 // 下载${table.classComment}导入模板
78 79 export function import${simpleClassName}Template() {
... ... @@ -91,6 +92,7 @@ export function import${simpleClassName}(data) {
91 92 data
92 93 })
93 94 }
  95 +#end
94 96 ## 特殊:主子表专属逻辑
95 97 #foreach ($subTable in $subTables)
96 98 #set ($index = $foreach.count - 1)
... ...
yudao-module-infra/src/main/resources/codegen/vue/views/index.vue.vm
... ... @@ -49,6 +49,7 @@
49 49 <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="openForm(undefined)"
50 50 v-hasPermi="['${permissionPrefix}:create']">新增</el-button>
51 51 </el-col>
  52 +#if ($importEnable)
52 53 <el-col :span="1.5">
53 54 <el-button type="info" plain icon="el-icon-upload2" size="mini" @click="handleImport"
54 55 :loading="importLoading" v-hasPermi="['${permissionPrefix}:import']">导入</el-button>
... ... @@ -57,6 +58,7 @@
57 58 <el-button type="info" plain icon="el-icon-document" size="mini" @click="handleImportTemplate"
58 59 v-hasPermi="['${permissionPrefix}:import']">导入模板</el-button>
59 60 </el-col>
  61 +#end
60 62 <el-col :span="1.5">
61 63 <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" :loading="exportLoading"
62 64 v-hasPermi="['${permissionPrefix}:export']">导出</el-button>
... ... @@ -86,7 +88,9 @@
86 88 #end
87 89 <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
88 90 </el-row>
  91 +#if ($importEnable)
89 92 <input ref="importFileRef" type="file" style="display: none" accept=".xls,.xlsx" @change="handleImportFileChange" />
  93 +#end
90 94  
91 95 ## 特殊:主子表专属逻辑
92 96 #if ( $table.templateType == 11 && $subTables && $subTables.size() > 0 )
... ... @@ -253,8 +257,10 @@ export default {
253 257 loading: true,
254 258 // 导出遮罩层
255 259 exportLoading: false,
  260 +#if ($importEnable)
256 261 // 导入遮罩层
257 262 importLoading: false,
  263 +#end
258 264 // 显示搜索条件
259 265 showSearch: true,
260 266 ## 特殊:树表专属逻辑(树不需要分页接口)
... ... @@ -333,6 +339,7 @@ export default {
333 339 openForm(id) {
334 340 this.#[[$]]#refs["formRef"].open(id);
335 341 },
  342 +#if ($importEnable)
336 343 /** 导入按钮操作 */
337 344 handleImport() {
338 345 this.$refs.importFileRef && this.$refs.importFileRef.click();
... ... @@ -369,6 +376,7 @@ export default {
369 376 this.importLoading = false;
370 377 }
371 378 },
  379 +#end
372 380 /** 删除按钮操作 */
373 381 async handleDelete(row) {
374 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 98 export${simpleClassName}: async (params) => {
99 99 return await request.download({ url: `${baseURL}/export-excel`, params })
100 100 },
  101 +#if ($importEnable)
101 102  
102 103 // 下载${table.classComment}导入模板
103 104 import${simpleClassName}Template: async () => {
... ... @@ -108,6 +109,7 @@ export const ${simpleClassName}Api = {
108 109 import${simpleClassName}: async (data: FormData) => {
109 110 return await request.upload({ url: `${baseURL}/import`, data })
110 111 },
  112 +#end
111 113 ## 特殊:主子表专属逻辑
112 114 #foreach ($subTable in $subTables)
113 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 92 >
93 93 <Icon icon="ep:plus" class="mr-5px" /> 新增
94 94 </el-button>
  95 +#if ($importEnable)
95 96 <el-button
96 97 type="warning"
97 98 plain
... ... @@ -100,6 +101,7 @@
100 101 >
101 102 <Icon icon="ep:upload" class="mr-5px" /> 导入
102 103 </el-button>
  104 +#end
103 105 <el-button
104 106 type="success"
105 107 plain
... ... @@ -245,43 +247,11 @@
245 247 @pagination="getList"
246 248 />
247 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 257 <${simpleClassName}Form ref="formRef" @success="getList" />
... ... @@ -308,7 +278,9 @@
308 278 import { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
309 279 import { isEmpty } from '@/utils/is'
310 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 285 #if ( $table.templateType == 2 )
314 286 import { handleTree } from '@/utils/tree'
... ... @@ -354,10 +326,9 @@ const queryParams = reactive({
354 326 })
355 327 const queryFormRef = ref() // 搜索的表单
356 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 334 const getList = async () => {
... ... @@ -394,58 +365,13 @@ const formRef = ref()
394 365 const openForm = (type: string, id?: number) => {
395 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 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 30 export function export${simpleClassName}(params) {
31 31 return defHttp.download({ url: '${baseURL}/export-excel', params }, '${table.classComment}.xls')
32 32 }
  33 +#if ($importEnable)
33 34  
34 35 // 下载${table.classComment}导入模板
35 36 export function import${simpleClassName}Template() {
... ... @@ -40,3 +41,4 @@ export function import${simpleClassName}Template() {
40 41 export function import${simpleClassName}(data: FormData) {
41 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 11 delete${simpleClassName},
12 12 export${simpleClassName},
13 13 get${simpleClassName}Page,
  14 +#if ($importEnable)
14 15 import${simpleClassName},
15 16 import${simpleClassName}Template,
  17 +#end
16 18 } from '@/api/${table.moduleName}/${table.businessName}'
17 19  
18 20 defineOptions({ name: '${table.className}' })
... ... @@ -20,8 +22,10 @@ defineOptions({ name: &#39;${table.className}&#39; })
20 22 const { t } = useI18n()
21 23 const { createConfirm, createMessage } = useMessage()
22 24 const [registerModal, { openModal }] = useModal()
  25 +#if ($importEnable)
23 26 const importFileRef = ref<HTMLInputElement>()
24 27 const importLoading = ref(false)
  28 +#end
25 29  
26 30 const [registerTable, { getForm, reload }] = useTable({
27 31 title: '${table.classComment}列表',
... ... @@ -57,6 +61,7 @@ async function handleExport() {
57 61 },
58 62 })
59 63 }
  64 +#if ($importEnable)
60 65  
61 66 function handleImport() {
62 67 importFileRef.value?.click()
... ... @@ -93,6 +98,7 @@ async function handleImportFileChange(event: Event) {
93 98 importLoading.value = false
94 99 }
95 100 }
  101 +#end
96 102  
97 103 async function handleDelete(record: Recordable) {
98 104 await delete${simpleClassName}(record.id)
... ... @@ -107,12 +113,14 @@ async function handleDelete(record: Recordable) {
107 113 <a-button type="primary" v-auth="['${permissionPrefix}:create']" :preIcon="IconEnum.ADD" @click="handleCreate">
108 114 {{ t('action.create') }}
109 115 </a-button>
  116 +#if ($importEnable)
110 117 <a-button v-auth="['${permissionPrefix}:import']" :loading="importLoading" @click="handleImport">
111 118 导入
112 119 </a-button>
113 120 <a-button v-auth="['${permissionPrefix}:import']" @click="handleImportTemplateDownload">
114 121 导入模板
115 122 </a-button>
  123 +#end
116 124 <a-button v-auth="['${permissionPrefix}:export']" :preIcon="IconEnum.EXPORT" @click="handleExport">
117 125 {{ t('action.export') }}
118 126 </a-button>
... ... @@ -138,7 +146,9 @@ async function handleDelete(record: Recordable) {
138 146 </template>
139 147 </template>
140 148 </BasicTable>
  149 +#if ($importEnable)
141 150 <input ref="importFileRef" type="file" accept=".xls,.xlsx" class="hidden" @change="handleImportFileChange" />
  151 +#end
142 152 <${simpleClassName}Modal @register="registerModal" @success="reload()" />
143 153 </div>
144 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 100 export function export${simpleClassName}(params: any) {
101 101 return requestClient.download('${baseURL}/export-excel', { params });
102 102 }
  103 +#if ($importEnable)
103 104  
104 105 /** 下载${table.classComment}导入模板 */
105 106 export function import${simpleClassName}Template() {
... ... @@ -110,6 +111,7 @@ export function import${simpleClassName}Template() {
110 111 export function import${simpleClassName}(data: FormData) {
111 112 return requestClient.post('${baseURL}/import', data);
112 113 }
  114 +#end
113 115  
114 116 ## 特殊:主子表专属逻辑
115 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 27 import { $t } from '#/locales';
28 28 #if (${table.templateType} == 2)## 树表接口
29 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 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 36 #end
34 37  
35 38 #if ($table.templateType == 12 || $table.templateType == 11) ## 内嵌和erp情况
... ... @@ -73,8 +76,6 @@ const queryParams = reactive({
73 76 })
74 77 const queryFormRef = ref() // 搜索的表单
75 78 const exportLoading = ref(false) // 导出的加载中
76   -const importLoading = ref(false) // 导入的加载中
77   -const importFileRef = ref<HTMLInputElement>()
78 79  
79 80 /** 查询列表 */
80 81 async function getList() {
... ... @@ -121,6 +122,17 @@ const [FormModal, formModalApi] = useVbenModal({
121 122 connectedComponent: ${simpleClassName}Form,
122 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 137 /** 创建${table.classComment} */
126 138 function handleCreate() {
... ... @@ -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 208 #if (${table.templateType} == 2)
234 209 /** 切换树形展开/收缩状态 */
... ... @@ -249,7 +224,9 @@ onMounted(() =&gt; {
249 224 <template>
250 225 <Page auto-content-height>
251 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 231 <Card v-if="!hiddenSearchBar" class="mb-4">
255 232 <!-- 搜索工作栏 -->
... ... @@ -355,23 +332,16 @@ onMounted(() =&gt; {
355 332 >
356 333 {{ $t('ui.actionTitle.create', ['${table.classComment}']) }}
357 334 </Button>
  335 +#if ($importEnable)
358 336 <Button
359 337 class="ml-2"
360 338 type="primary"
361   - :loading="importLoading"
362 339 @click="handleImport"
363 340 v-access:code="['${permissionPrefix}:import']"
364 341 >
365 342 导入
366 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 345 <Button
376 346 :icon="h(Download)"
377 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 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 153 export function useGridFormSchema(): VbenFormSchema[] {
139 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 16 delete${simpleClassName},
17 17 export${simpleClassName},
18 18 get${simpleClassName}List,
19   - import${simpleClassName},
20   - import${simpleClassName}Template,
21 19 } from '#/api/${table.moduleName}/${table.businessName}';
22 20 #else## 标准表接口
23 21 import {
... ... @@ -26,10 +24,11 @@ import {
26 24 delete${simpleClassName}List,#end
27 25 export${simpleClassName},
28 26 get${simpleClassName}Page,
29   - import${simpleClassName},
30   - import${simpleClassName}Template,
31 27 } from '#/api/${table.moduleName}/${table.businessName}';
32 28 #end
  29 +#if ($importEnable)
  30 +import ${simpleClassName}Import from './modules/import-form.vue';
  31 +#end
33 32 import { $t } from '#/locales';
34 33  
35 34 import { useGridColumns, useGridFormSchema } from './data';
... ... @@ -54,8 +53,17 @@ const [FormModal, formModalApi] = useVbenModal({
54 53 connectedComponent: Form,
55 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 67 #if (${table.templateType} == 2)## 树表特有:控制表格展开收缩
60 68  
61 69 /** 切换树形展开/收缩状态 */
... ... @@ -141,43 +149,6 @@ async function handleExport() {
141 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 153 const [Grid, gridApi] = useVbenVxeGrid({
183 154 formOptions: {
... ... @@ -254,7 +225,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
254 225 <template>
255 226 <Page auto-content-height>
256 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 231 #if ($table.templateType == 11) ## erp情况
259 232 <div>
260 233 #end
... ... @@ -292,19 +265,14 @@ const [Grid, gridApi] = useVbenVxeGrid({
292 265 onClick: handleExpand,
293 266 },
294 267 #end
  268 +#if ($importEnable)
295 269 {
296 270 label: '导入',
297 271 type: 'primary',
298 272 auth: ['${table.moduleName}:${simpleClassName_strikeCase}:import'],
299   - disabled: importLoading.value,
300 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 277 label: $t('ui.actionTitle.export'),
310 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 100 export function export${simpleClassName}(params: any) {
101 101 return requestClient.download('${baseURL}/export-excel', { params });
102 102 }
  103 +#if ($importEnable)
103 104  
104 105 /** 下载${table.classComment}导入模板 */
105 106 export function import${simpleClassName}Template() {
... ... @@ -110,6 +111,7 @@ export function import${simpleClassName}Template() {
110 111 export function import${simpleClassName}(data: FormData) {
111 112 return requestClient.post('${baseURL}/import', data);
112 113 }
  114 +#end
113 115  
114 116 ## 特殊:主子表专属逻辑
115 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 26 import { $t } from '#/locales';
27 27 #if (${table.templateType} == 2)## 树表接口
28 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 30 #else## 标准表接口
31 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 36 #end
34 37  
35 38 #if ($table.templateType == 12 || $table.templateType == 11) ## 内嵌和erp情况
... ... @@ -73,8 +76,6 @@ const queryParams = reactive({
73 76 })
74 77 const queryFormRef = ref() // 搜索的表单
75 78 const exportLoading = ref(false) // 导出的加载中
76   -const importLoading = ref(false) // 导入的加载中
77   -const importFileRef = ref<HTMLInputElement>()
78 79  
79 80 /** 查询列表 */
80 81 const getList = async () => {
... ... @@ -121,6 +122,17 @@ const [FormModal, formModalApi] = useVbenModal({
121 122 connectedComponent: ${simpleClassName}Form,
122 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 137 /** 创建${table.classComment} */
126 138 function handleCreate() {
... ... @@ -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 206 #if (${table.templateType} == 2)
232 207 /** 切换树形展开/收缩状态 */
... ... @@ -247,7 +222,9 @@ onMounted(() =&gt; {
247 222 <template>
248 223 <Page auto-content-height>
249 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 229 <ContentWrap v-if="!hiddenSearchBar">
253 230 <!-- 搜索工作栏 -->
... ... @@ -356,23 +333,16 @@ onMounted(() =&gt; {
356 333 >
357 334 {{ $t('ui.actionTitle.create', ['${table.classComment}']) }}
358 335 </el-button>
  336 +#if ($importEnable)
359 337 <el-button
360 338 class="ml-2"
361 339 type="primary"
362   - :loading="importLoading"
363 340 @click="handleImport"
364 341 v-access:code="['${permissionPrefix}:import']"
365 342 >
366 343 导入
367 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 346 <el-button
377 347 :icon="h(Download)"
378 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 113 export function export${simpleClassName}(params: any) {
114 114 return requestClient.download('${baseURL}/export-excel', { params });
115 115 }
  116 +#if ($importEnable)
116 117  
117 118 /** 下载${table.classComment}导入模板 */
118 119 export function import${simpleClassName}Template() {
... ... @@ -120,9 +121,10 @@ export function import${simpleClassName}Template() {
120 121 }
121 122  
122 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 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 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 156 export function useGridFormSchema(): VbenFormSchema[] {
142 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 16 delete${simpleClassName},
17 17 export${simpleClassName},
18 18 get${simpleClassName}List,
19   - import${simpleClassName},
20   - import${simpleClassName}Template,
21 19 } from '#/api/${table.moduleName}/${table.businessName}';
22 20 #else## 标准表接口
23 21 import {
... ... @@ -26,10 +24,11 @@ import {
26 24 delete${simpleClassName}List,#end
27 25 export${simpleClassName},
28 26 get${simpleClassName}Page,
29   - import${simpleClassName},
30   - import${simpleClassName}Template,
31 27 } from '#/api/${table.moduleName}/${table.businessName}';
32 28 #end
  29 +#if ($importEnable)
  30 +import ${simpleClassName}Import from './modules/import-form.vue';
  31 +#end
33 32 import { $t } from '#/locales';
34 33  
35 34 import { useGridColumns, useGridFormSchema } from './data';
... ... @@ -54,8 +53,17 @@ const [FormModal, formModalApi] = useVbenModal({
54 53 connectedComponent: Form,
55 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 67 #if (${table.templateType} == 2)## 树表特有:控制表格展开收缩
60 68  
61 69 /** 切换树形展开/收缩状态 */
... ... @@ -139,43 +147,6 @@ async function handleExport() {
139 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 151 const [Grid, gridApi] = useVbenVxeGrid({
181 152 formOptions: {
... ... @@ -252,7 +223,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
252 223 <template>
253 224 <Page auto-content-height>
254 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 229 #if ($table.templateType == 11) ## erp情况
257 230 <div>
258 231 #end
... ... @@ -290,19 +263,14 @@ const [Grid, gridApi] = useVbenVxeGrid({
290 263 onClick: handleExpand,
291 264 },
292 265 #end
  266 +#if ($importEnable)
293 267 {
294 268 label: '导入',
295 269 type: 'primary',
296 270 auth: ['${table.moduleName}:${simpleClassName_strikeCase}:import'],
297   - disabled: importLoading.value,
298 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 275 label: $t('ui.actionTitle.export'),
308 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 10 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
11 11 import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
12 12 import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
  13 +import cn.iocoder.yudao.module.infra.enums.codegen.CodegenVOTypeEnum;
13 14 import cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties;
14 15 import org.junit.jupiter.api.BeforeEach;
15 16 import org.mockito.InjectMocks;
... ... @@ -19,8 +20,10 @@ import java.io.ByteArrayInputStream;
19 20 import java.io.ByteArrayOutputStream;
20 21 import java.util.ArrayList;
21 22 import java.util.HashMap;
  23 +import java.util.LinkedHashSet;
22 24 import java.util.List;
23 25 import java.util.Map;
  26 +import java.util.Set;
24 27  
25 28 import static org.junit.jupiter.api.Assertions.assertEquals;
26 29  
... ... @@ -41,16 +44,18 @@ public abstract class CodegenEngineAbstractTest extends BaseMockitoUnitTest {
41 44  
42 45 @Spy
43 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 53 @BeforeEach
47 54 public void setUp() {
48 55 codegenEngine.setJakartaEnable(true); // 强制使用 jakarta,保证单测可以基于 jakarta 断言
49 56 codegenEngine.initGlobalBindingMap();
50   - // 单测强制使用
51   - // 获取测试文件 resources 路径
  57 + // 获取测试文件 resources 路径,writeResult 调试用
52 58 String absolutePath = FileUtil.getAbsolutePath("application-unit-test.yaml");
53   - // 系统不一样生成的文件也有差异,那就各自生成各自的
54 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 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 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 101 String assertContent = ResourceUtil.readUtf8Str("codegen/" + path + "/assert.json");
88 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 108 asserts.forEach(assertMap -> {
92 109 String contentPath = (String) assertMap.get("contentPath");
93 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 5 import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;
6 6 import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
7 7 import com.baomidou.mybatisplus.annotation.DbType;
8   -import org.junit.jupiter.api.Disabled;
9 8 import org.junit.jupiter.api.Test;
10 9  
11 10 import java.util.Arrays;
... ... @@ -17,7 +16,6 @@ import java.util.Map;
17 16 *
18 17 * @author 芋道源码
19 18 */
20   -@Disabled
21 19 public class CodegenEngineVue2Test extends CodegenEngineAbstractTest {
22 20  
23 21 @Test
... ... @@ -37,6 +35,23 @@ public class CodegenEngineVue2Test extends CodegenEngineAbstractTest {
37 35 }
38 36  
39 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 55 public void testExecute_vue2_tree() {
41 56 // 准备参数
42 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 5 import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;
6 6 import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
7 7 import com.baomidou.mybatisplus.annotation.DbType;
8   -import org.junit.jupiter.api.Disabled;
9 8 import org.junit.jupiter.api.Test;
10 9  
11 10 import java.util.Arrays;
... ... @@ -17,7 +16,6 @@ import java.util.Map;
17 16 *
18 17 * @author 芋道源码
19 18 */
20   -@Disabled
21 19 public class CodegenEngineVue3Test extends CodegenEngineAbstractTest {
22 20  
23 21 @Test
... ... @@ -37,6 +35,23 @@ public class CodegenEngineVue3Test extends CodegenEngineAbstractTest {
37 35 }
38 36  
39 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 55 public void testExecute_vue3_tree() {
41 56 // 准备参数
42 57 CodegenTableDO table = getTable("category")
... ...
yudao-server/src/main/resources/application.yaml
... ... @@ -307,6 +307,7 @@ yudao:
307 307 vo-type: 10 # VO 的类型,参见 CodegenVOTypeEnum 枚举类
308 308 delete-batch-enable: true # 是否生成批量删除接口
309 309 unit-test-enable: false # 是否生成单元测试
  310 + import-enable: true # 是否生成 Excel 导入接口
310 311 tenant: # 多租户相关配置项
311 312 enable: true
312 313 ignore-urls:
... ...