mirror of
				https://github.com/YunaiV/ruoyi-vue-pro.git
				synced 2025-10-31 18:49:06 +08:00 
			
		
		
		
	代码生成:简化现有 crud 模版的各种 pretty 相关的判断,交给 Java 代码后置统一处理。
This commit is contained in:
		| @ -50,4 +50,20 @@ public class StrUtils { | ||||
|         return Arrays.stream(integers).boxed().collect(Collectors.toList()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 移除字符串中,包含指定字符串的行 | ||||
|      * | ||||
|      * @param content 字符串 | ||||
|      * @param sequence 包含的字符串 | ||||
|      * @return 移除后的字符串 | ||||
|      */ | ||||
|     public static String removeLineContains(String content, String sequence) { | ||||
|         if (StrUtil.isEmpty(content) || StrUtil.isEmpty(sequence)) { | ||||
|             return content; | ||||
|         } | ||||
|         return Arrays.stream(content.split("\n")) | ||||
|                 .filter(line -> !line.contains(sequence)) | ||||
|                 .collect(Collectors.joining("\n")); | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -6,6 +6,7 @@ import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum; | ||||
| import cn.iocoder.yudao.module.infra.enums.codegen.CodegenSceneEnum; | ||||
| import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum; | ||||
| import com.baomidou.mybatisplus.annotation.KeySequence; | ||||
| import com.baomidou.mybatisplus.annotation.TableField; | ||||
| import com.baomidou.mybatisplus.annotation.TableId; | ||||
| import com.baomidou.mybatisplus.annotation.TableName; | ||||
| import com.baomidou.mybatisplus.generator.config.po.TableInfo; | ||||
| @ -123,12 +124,14 @@ public class CodegenTableDO extends BaseDO { | ||||
|      * | ||||
|      * 关联 {@link CodegenTableDO#getId()} | ||||
|      */ | ||||
|     @TableField(exist = false) | ||||
|     private Long masterTableId; | ||||
|     /** | ||||
|      * 【自己】子表关联主表的字段编号 | ||||
|      * | ||||
|      * 关联 {@link CodegenColumnDO#getId()} | ||||
|      */ | ||||
|     @TableField(exist = false) | ||||
|     private Long subJoinColumnId; | ||||
|     /** | ||||
|      * 主表与子表是否一对多 | ||||
| @ -136,6 +139,7 @@ public class CodegenTableDO extends BaseDO { | ||||
|      * true:一对多 | ||||
|      * false:一对一 | ||||
|      */ | ||||
|     @TableField(exist = false) | ||||
|     private Boolean subJoinMany; | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -2,7 +2,6 @@ package cn.iocoder.yudao.module.infra.service.codegen.inner; | ||||
|  | ||||
| import cn.hutool.core.collection.CollUtil; | ||||
| import cn.hutool.core.map.MapUtil; | ||||
| import cn.hutool.core.util.ObjUtil; | ||||
| import cn.hutool.core.util.StrUtil; | ||||
| import cn.hutool.extra.template.TemplateConfig; | ||||
| import cn.hutool.extra.template.TemplateEngine; | ||||
| @ -15,6 +14,7 @@ import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | ||||
| import cn.iocoder.yudao.framework.common.util.date.DateUtils; | ||||
| import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; | ||||
| import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; | ||||
| import cn.iocoder.yudao.framework.common.util.string.StrUtils; | ||||
| import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; | ||||
| import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; | ||||
| import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; | ||||
| @ -27,7 +27,6 @@ import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO; | ||||
| import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO; | ||||
| import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum; | ||||
| import cn.iocoder.yudao.module.infra.enums.codegen.CodegenSceneEnum; | ||||
| import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum; | ||||
| import cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties; | ||||
| import com.google.common.annotations.VisibleForTesting; | ||||
| import com.google.common.collect.ImmutableTable; | ||||
| @ -224,11 +223,43 @@ public class CodegenEngine { | ||||
|                               String filePath, Map<String, Object> bindingMap) { | ||||
|         filePath = formatFilePath(filePath, bindingMap); | ||||
|         String content = templateEngine.getTemplate(vmPath).render(bindingMap); | ||||
|         // 去除字段后面多余的 , 逗号 | ||||
|         content = content.replaceAll(",\n}", "\n}").replaceAll(",\n  }", "\n  }"); | ||||
|         // 格式化代码 | ||||
|         content = prettyCode(content); | ||||
|         result.put(filePath, content); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 格式化生成后的代码 | ||||
|      * | ||||
|      * 因为尽量让 vm 模版简单,所以统一的处理都在这个方法。 | ||||
|      * 如果不处理,Vue 的 Pretty 格式校验可能会报错 | ||||
|      * | ||||
|      * @param content 格式化前的代码 | ||||
|      * @return 格式化后的代码 | ||||
|      */ | ||||
|     private String prettyCode(String content) { | ||||
|         // Vue 界面:去除字段后面多余的 , 逗号,解决前端的 Pretty 代码格式检查的报错 | ||||
|         content = content.replaceAll(",\n}", "\n}").replaceAll(",\n  }", "\n  }"); | ||||
|         // Vue 界面:去除多的 dateFormatter,只有一个的情况下,说明没使用到 | ||||
|         if (StrUtil.count(content, "dateFormatter") == 1) { | ||||
|             content = StrUtils.removeLineContains(content, "dateFormatter"); | ||||
|         } | ||||
|         // Vue 界面:去除多的 dict 相关,只有一个的情况下,说明没使用到 | ||||
|         if (StrUtil.count(content, "getIntDictOptions") == 1) { | ||||
|             content = content.replace("getIntDictOptions, ", ""); | ||||
|         } | ||||
|         if (StrUtil.count(content, "getStrDictOptions") == 1) { | ||||
|             content = content.replace("getStrDictOptions, ", ""); | ||||
|         } | ||||
|         if (StrUtil.count(content, "getBoolDictOptions") == 1) { | ||||
|             content = content.replace("getBoolDictOptions, ", ""); | ||||
|         } | ||||
|         if (StrUtil.count(content, "DICT_TYPE.") == 0) { | ||||
|             content = StrUtils.removeLineContains(content, "DICT_TYPE"); | ||||
|         } | ||||
|         return content; | ||||
|     } | ||||
|  | ||||
|     private Map<String, Object> initBindingMap(CodegenTableDO table, List<CodegenColumnDO> columns, | ||||
|                                                List<CodegenTableDO> subTables, List<List<CodegenColumnDO>> subColumnsList) { | ||||
|         // 创建 bindingMap | ||||
|  | ||||
| @ -6,9 +6,11 @@ import java.util.*; | ||||
| #foreach ($column in $columns) | ||||
| #if (${column.javaType} == "BigDecimal") | ||||
| import java.math.BigDecimal; | ||||
| #break | ||||
| #end | ||||
| #if (${column.javaType} == "LocalDateTime") | ||||
| import java.time.LocalDateTime; | ||||
| #break | ||||
| #end | ||||
| #end | ||||
| import javax.validation.constraints.*; | ||||
|  | ||||
| @ -6,9 +6,11 @@ import java.util.*; | ||||
| #foreach ($column in $columns) | ||||
| #if (${column.javaType} == "BigDecimal") | ||||
| import java.math.BigDecimal; | ||||
| #break | ||||
| #end | ||||
| #if (${column.javaType} == "LocalDateTime") | ||||
| import java.time.LocalDateTime; | ||||
| #break | ||||
| #end | ||||
| #end | ||||
|  | ||||
|  | ||||
| @ -48,7 +48,7 @@ public class ${table.className}ServiceImpl implements ${table.className}Service | ||||
|  | ||||
|     @Override | ||||
| ## 特殊:主子表专属逻辑 | ||||
| #if ( $subTables.size() > 0) | ||||
| #if ( $subTables && $subTables.size() > 0 ) | ||||
|     @Transactional(rollbackFor = Exception.class) | ||||
| #end | ||||
|     public ${primaryColumn.javaType} create${simpleClassName}(${sceneEnum.prefixClass}${table.className}CreateReqVO createReqVO) { | ||||
| @ -56,7 +56,7 @@ public class ${table.className}ServiceImpl implements ${table.className}Service | ||||
|         ${table.className}DO ${classNameVar} = ${table.className}Convert.INSTANCE.convert(createReqVO); | ||||
|         ${classNameVar}Mapper.insert(${classNameVar}); | ||||
| ## 特殊:主子表专属逻辑 | ||||
| #if ( $subTables.size() > 0) | ||||
| #if ( $subTables && $subTables.size() > 0) | ||||
|  | ||||
|         // 插入子表($subTable.classComment) | ||||
| #foreach ($subTable in $subTables) | ||||
| @ -77,7 +77,7 @@ public class ${table.className}ServiceImpl implements ${table.className}Service | ||||
|  | ||||
|     @Override | ||||
| ## 特殊:主子表专属逻辑 | ||||
| #if ( $subTables.size() > 0) | ||||
| #if ( $subTables && $subTables.size() > 0) | ||||
|     @Transactional(rollbackFor = Exception.class) | ||||
| #end | ||||
|     public void update${simpleClassName}(${sceneEnum.prefixClass}${table.className}UpdateReqVO updateReqVO) { | ||||
| @ -87,7 +87,7 @@ public class ${table.className}ServiceImpl implements ${table.className}Service | ||||
|         ${table.className}DO updateObj = ${table.className}Convert.INSTANCE.convert(updateReqVO); | ||||
|         ${classNameVar}Mapper.updateById(updateObj); | ||||
| ## 特殊:主子表专属逻辑 | ||||
| #if ( $subTables.size() > 0) | ||||
| #if ( $subTables && $subTables.size() > 0) | ||||
|  | ||||
|         // 更新子表 | ||||
| #foreach ($subTable in $subTables) | ||||
| @ -106,7 +106,7 @@ public class ${table.className}ServiceImpl implements ${table.className}Service | ||||
|  | ||||
|     @Override | ||||
| ## 特殊:主子表专属逻辑 | ||||
| #if ( $subTables.size() > 0) | ||||
| #if ( $subTables && $subTables.size() > 0) | ||||
|     @Transactional(rollbackFor = Exception.class) | ||||
| #end | ||||
|     public void delete${simpleClassName}(${primaryColumn.javaType} id) { | ||||
| @ -115,7 +115,7 @@ public class ${table.className}ServiceImpl implements ${table.className}Service | ||||
|         // 删除 | ||||
|         ${classNameVar}Mapper.deleteById(id); | ||||
| ## 特殊:主子表专属逻辑 | ||||
| #if ( $subTables.size() > 0) | ||||
| #if ( $subTables && $subTables.size() > 0) | ||||
|  | ||||
|         // 删除子表 | ||||
| #foreach ($subTable in $subTables) | ||||
|  | ||||
| @ -7,7 +7,6 @@ | ||||
|       label-width="100px" | ||||
|       v-loading="formLoading" | ||||
|     > | ||||
| #set ($dictMethods = [])## 使用到的 dict 字典方法 | ||||
| #foreach($column in $columns) | ||||
|     #if ($column.createOperation || $column.updateOperation) | ||||
|         #set ($dictType = $column.dictType) | ||||
| @ -45,9 +44,6 @@ | ||||
|       <el-form-item label="${comment}" prop="${javaField}"> | ||||
|         <el-select v-model="formData.${javaField}" placeholder="请选择${comment}"> | ||||
|                 #if ("" != $dictType)## 有数据字典 | ||||
|                     #if (!$dictMethods.contains($dictMethod))## 如果不存在,则添加到 dictMethods 数组中,后续好 import | ||||
|                       #set($ignore = $dictMethods.add($dictMethod) ) | ||||
|                     #end | ||||
|           <el-option | ||||
|             v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())" | ||||
|             :key="dict.value" | ||||
| @ -63,9 +59,6 @@ | ||||
|       <el-form-item label="${comment}" prop="${javaField}"> | ||||
|         <el-checkbox-group v-model="formData.${javaField}"> | ||||
|                 #if ("" != $dictType)## 有数据字典 | ||||
|                     #if (!$dictMethods.contains($dictMethod))## 如果不存在,则添加到 dictMethods 数组中,后续好 import | ||||
|                       #set($ignore = $dictMethods.add($dictMethod) ) | ||||
|                     #end | ||||
|           <el-checkbox | ||||
|             v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())" | ||||
|             :key="dict.value" | ||||
| @ -82,9 +75,6 @@ | ||||
|       <el-form-item label="${comment}" prop="${javaField}"> | ||||
|         <el-radio-group v-model="formData.${javaField}"> | ||||
|                 #if ("" != $dictType)## 有数据字典 | ||||
|                     #if (!$dictMethods.contains($dictMethod))## 如果不存在,则添加到 dictMethods 数组中,后续好 import | ||||
|                       #set($ignore = $dictMethods.add($dictMethod) ) | ||||
|                     #end | ||||
|           <el-radio | ||||
|             v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())" | ||||
|             :key="dict.value" | ||||
| @ -121,9 +111,7 @@ | ||||
|   </Dialog> | ||||
| </template> | ||||
| <script setup lang="ts"> | ||||
| #if ($dictMethods.size() > 0) | ||||
| import { DICT_TYPE#foreach ($dictMethod in $dictMethods), ${dictMethod}#end } from '@/utils/dict' | ||||
| #end | ||||
| import { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict' | ||||
| import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${classNameVar}' | ||||
|  | ||||
| const { t } = useI18n() // 国际化 | ||||
| @ -134,33 +122,21 @@ const dialogTitle = ref('') // 弹窗的标题 | ||||
| const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 | ||||
| const formType = ref('') // 表单的类型:create - 新增;update - 修改 | ||||
| const formData = ref({ | ||||
| #set ($listOperationLastIndex = -1)## 求最后一个需要 , 的地方 | ||||
| #foreach ($column in $columns) | ||||
|     #if ($column.createOperation || $column.updateOperation) | ||||
|         #set ($listOperationLastIndex = $foreach.index) | ||||
|     #end | ||||
| #end | ||||
| #foreach ($column in $columns) | ||||
|     #if ($column.createOperation || $column.updateOperation) | ||||
|         #if ($column.htmlType == "checkbox") | ||||
|   $column.javaField: []#if($foreach.index < $listOperationLastIndex),#end | ||||
|   $column.javaField: [], | ||||
|         #else | ||||
|   $column.javaField: undefined#if($foreach.index < $listOperationLastIndex),#end | ||||
|   $column.javaField: undefined, | ||||
|         #end | ||||
|     #end | ||||
| #end | ||||
| }) | ||||
| const formRules = reactive({ | ||||
| #set ($listOperationLastIndex = -1)## 求最后一个需要 , 的地方 | ||||
| #foreach ($column in $columns) | ||||
|     #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键 | ||||
|         #set ($listOperationLastIndex = $foreach.index) | ||||
|     #end | ||||
| #end | ||||
| #foreach ($column in $columns) | ||||
|     #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键 | ||||
|         #set($comment=$column.columnComment) | ||||
|   $column.javaField: [{ required: true, message: '${comment}不能为空', trigger: #if($column.htmlType == 'select')'change'#else'blur'#end }]#if($foreach.index < $listOperationLastIndex),#end | ||||
|   $column.javaField: [{ required: true, message: '${comment}不能为空', trigger: #if($column.htmlType == 'select')'change'#else'blur'#end }], | ||||
|     #end | ||||
| #end | ||||
| }) | ||||
| @ -188,9 +164,7 @@ defineExpose({ open }) // 提供 open 方法,用于打开弹窗 | ||||
| const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 | ||||
| const submitForm = async () => { | ||||
|   // 校验表单 | ||||
|   if (!formRef) return | ||||
|   const valid = await formRef.value.validate() | ||||
|   if (!valid) return | ||||
|   await formRef.value.validate() | ||||
|   // 提交请求 | ||||
|   formLoading.value = true | ||||
|   try { | ||||
| @ -213,18 +187,12 @@ const submitForm = async () => { | ||||
| /** 重置表单 */ | ||||
| const resetForm = () => { | ||||
|   formData.value = { | ||||
| #set ($listOperationLastIndex = -1)## 求最后一个需要 , 的地方 | ||||
| #foreach ($column in $columns) | ||||
|   #if ($column.createOperation || $column.updateOperation) | ||||
|       #set ($listOperationLastIndex = $foreach.index) | ||||
|   #end | ||||
| #end | ||||
| #foreach ($column in $columns) | ||||
|   #if ($column.createOperation || $column.updateOperation) | ||||
|       #if ($column.htmlType == "checkbox") | ||||
|     $column.javaField: []#if($foreach.index < $listOperationLastIndex),#end | ||||
|     $column.javaField: [], | ||||
|       #else | ||||
|     $column.javaField: undefined#if($foreach.index < $listOperationLastIndex),#end | ||||
|     $column.javaField: undefined, | ||||
|       #end | ||||
|   #end | ||||
| #end | ||||
|  | ||||
| @ -8,7 +8,6 @@ | ||||
|       :inline="true" | ||||
|       label-width="68px" | ||||
|     > | ||||
|     #set ($dictMethods = [])## 使用到的 dict 字典方法 | ||||
|     #foreach($column in $columns) | ||||
|         #if ($column.listOperation) | ||||
|             #set ($dictType = $column.dictType) | ||||
| @ -36,20 +35,13 @@ | ||||
|       </el-form-item> | ||||
|             #elseif ($column.htmlType == "select" || $column.htmlType == "radio") | ||||
|       <el-form-item label="${comment}" prop="${javaField}"> | ||||
|                 #if ($javaField.length() + $comment.length() > 8) | ||||
|         <el-select | ||||
|           v-model="queryParams.${javaField}" | ||||
|           placeholder="请选择${comment}" | ||||
|           clearable | ||||
|           class="!w-240px" | ||||
|         > | ||||
|                 #else | ||||
|         <el-select v-model="queryParams.${javaField}" placeholder="请选择${comment}" clearable class="!w-240px"> | ||||
|                 #end | ||||
|                 #if ("" != $dictType)## 设置了 dictType 数据字典的情况 | ||||
|                     #if (!$dictMethods.contains($dictMethod))## 如果不存在,则添加到 dictMethods 数组中,后续好 import | ||||
|                         #set($ignore = $dictMethods.add($dictMethod) ) | ||||
|                     #end | ||||
|           <el-option | ||||
|             v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())" | ||||
|             :key="dict.value" | ||||
| @ -92,16 +84,12 @@ | ||||
|       <el-form-item> | ||||
|         <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> | ||||
|         <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> | ||||
|         #if ($permissionPrefix.length() <= 12) | ||||
|         <el-button type="primary" @click="openForm('create')" v-hasPermi="['${permissionPrefix}:create']"> | ||||
|         #else | ||||
|         <el-button | ||||
|           type="primary" | ||||
|           plain | ||||
|           @click="openForm('create')" | ||||
|           v-hasPermi="['${permissionPrefix}:create']" | ||||
|         > | ||||
|         #end | ||||
|           <Icon icon="ep:plus" class="mr-5px" /> 新增 | ||||
|         </el-button> | ||||
|         <el-button | ||||
| @ -180,15 +168,8 @@ | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| #if ($dictMethods.size() > 0) | ||||
| import { DICT_TYPE#foreach ($dictMethod in $dictMethods), ${dictMethod}#end } from '@/utils/dict' | ||||
| #end | ||||
| #foreach ($column in $columns) | ||||
|     #if ($column.listOperationResult && $column.htmlType == "datetime") | ||||
| import { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict' | ||||
| import { dateFormatter } from '@/utils/formatTime' | ||||
|     #break | ||||
|     #end | ||||
| #end | ||||
| import download from '@/utils/download' | ||||
| import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${classNameVar}' | ||||
| import ${simpleClassName}Form from './${simpleClassName}Form.vue' | ||||
| @ -204,19 +185,13 @@ const list = ref([]) // 列表的数据 | ||||
| const queryParams = reactive({ | ||||
|   pageNo: 1, | ||||
|   pageSize: 10, | ||||
|   #set ($listOperationLastIndex = -1)## 求最后一个需要 , 的地方 | ||||
|   #foreach ($column in $columns) | ||||
|     #if ($column.listOperation) | ||||
|       #set ($listOperationLastIndex = $foreach.index) | ||||
|     #end | ||||
|   #end | ||||
|   #foreach ($column in $columns) | ||||
|     #if ($column.listOperation) | ||||
|       #if ($column.listOperationCondition != 'BETWEEN') | ||||
|   $column.javaField: null#if($foreach.index < $listOperationLastIndex),#end | ||||
|   $column.javaField: null, | ||||
|   #end | ||||
|       #if ($column.htmlType == "datetime" || $column.listOperationCondition == "BETWEEN") | ||||
|   $column.javaField: []#if($foreach.index < $listOperationLastIndex),#end | ||||
|   $column.javaField: [], | ||||
|       #end | ||||
|     #end | ||||
|   #end | ||||
|  | ||||
| @ -1,10 +1,5 @@ | ||||
| import type { CrudSchema } from '@/hooks/web/useCrudSchemas' | ||||
| #foreach ($column in $columns) | ||||
|     #if ($column.listOperationResult && $column.htmlType == "datetime") | ||||
| import { dateFormatter } from '@/utils/formatTime' | ||||
|     #break | ||||
|     #end | ||||
| #end | ||||
|  | ||||
| // 表单校验 | ||||
| export const rules = reactive({ | ||||
|  | ||||
| @ -8,7 +8,7 @@ | ||||
|   </Dialog> | ||||
| </template> | ||||
| <script setup lang="ts"> | ||||
|   import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${classNameVar}' | ||||
| import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${classNameVar}' | ||||
| import { rules, allSchemas } from './${classNameVar}.data' | ||||
| const { t } = useI18n() // 国际化 | ||||
| const message = useMessage() // 消息弹窗 | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 YunaiV
					YunaiV