Commit 0cb951f3344107f01a3c7168a24dd49493bc0914

Authored by YunaiV
1 parent ef9f43e7

fix(codegen): 修复 UniApp Wot 生成代码兼容问题

- 完善日期、浮点数、布尔字典和上传字段生成逻辑
- 修复主子表必填校验和详情按钮类型
- 修复树表查询及生成快照路径问题
- 增加主子表类名、属性名和输出路径碰撞校验
- 补充 normal、inner、ERP 等场景单元测试
Showing 18 changed files with 453 additions and 156 deletions
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants.java
@@ -48,6 +48,10 @@ public interface ErrorCodeConstants { @@ -48,6 +48,10 @@ public interface ErrorCodeConstants {
48 ErrorCode CODEGEN_MASTER_TABLE_NOT_EXISTS = new ErrorCode(1_001_004_010, "主表(id={})定义不存在,请检查"); 48 ErrorCode CODEGEN_MASTER_TABLE_NOT_EXISTS = new ErrorCode(1_001_004_010, "主表(id={})定义不存在,请检查");
49 ErrorCode CODEGEN_SUB_COLUMN_NOT_EXISTS = new ErrorCode(1_001_004_011, "子表的字段(id={})不存在,请检查"); 49 ErrorCode CODEGEN_SUB_COLUMN_NOT_EXISTS = new ErrorCode(1_001_004_011, "子表的字段(id={})不存在,请检查");
50 ErrorCode CODEGEN_MASTER_GENERATION_FAIL_NO_SUB_TABLE = new ErrorCode(1_001_004_012, "主表生成代码失败,原因:它没有子表"); 50 ErrorCode CODEGEN_MASTER_GENERATION_FAIL_NO_SUB_TABLE = new ErrorCode(1_001_004_012, "主表生成代码失败,原因:它没有子表");
  51 + ErrorCode CODEGEN_MASTER_TABLE_NAME_DUPLICATE = new ErrorCode(1_001_004_013,
  52 + "主子表规范化类名({})重复,请调整主表或子表类名");
  53 + ErrorCode CODEGEN_MASTER_TABLE_FIELD_DUPLICATE = new ErrorCode(1_001_004_014,
  54 + "主子表属性名({})重复,请调整主表字段、子表类名或关联关系");
51 55
52 // ========== 文件配置 1-001-006-000 ========== 56 // ========== 文件配置 1-001-006-000 ==========
53 ErrorCode FILE_CONFIG_NOT_EXISTS = new ErrorCode(1_001_006_000, "文件配置不存在"); 57 ErrorCode FILE_CONFIG_NOT_EXISTS = new ErrorCode(1_001_006_000, "文件配置不存在");
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java
@@ -48,6 +48,9 @@ import java.util.*; @@ -48,6 +48,9 @@ import java.util.*;
48 48
49 import static cn.hutool.core.map.MapUtil.getStr; 49 import static cn.hutool.core.map.MapUtil.getStr;
50 import static cn.hutool.core.text.CharSequenceUtil.*; 50 import static cn.hutool.core.text.CharSequenceUtil.*;
  51 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  52 +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.CODEGEN_MASTER_TABLE_FIELD_DUPLICATE;
  53 +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.CODEGEN_MASTER_TABLE_NAME_DUPLICATE;
51 54
52 /** 55 /**
53 * 代码生成的引擎,用于具体生成代码 56 * 代码生成的引擎,用于具体生成代码
@@ -426,16 +429,23 @@ public class CodegenEngine { @@ -426,16 +429,23 @@ public class CodegenEngine {
426 private void generateCode(Map<String, String> result, Map<String, String> generatedSources, String vmPath, 429 private void generateCode(Map<String, String> result, Map<String, String> generatedSources, String vmPath,
427 String filePath, Map<String, Object> bindingMap) { 430 String filePath, Map<String, Object> bindingMap) {
428 filePath = formatFilePath(filePath, bindingMap); 431 filePath = formatFilePath(filePath, bindingMap);
429 - String source = vmPath + (bindingMap.containsKey("subIndex") ? "[subIndex=" + bindingMap.get("subIndex") + "]" : ""); 432 + registerGeneratedSource(generatedSources, filePath, vmPath, bindingMap);
  433 + String content = templateEngine.getTemplate(vmPath).render(bindingMap);
  434 + // 格式化代码
  435 + content = prettyCode(content, vmPath);
  436 + result.put(filePath, content);
  437 + }
  438 +
  439 + @VisibleForTesting
  440 + static void registerGeneratedSource(Map<String, String> generatedSources, String filePath, String vmPath,
  441 + Map<String, Object> bindingMap) {
  442 + String source = vmPath
  443 + + (bindingMap.containsKey("subIndex") ? "[subIndex=" + bindingMap.get("subIndex") + "]" : "");
430 String previousSource = generatedSources.putIfAbsent(filePath, source); 444 String previousSource = generatedSources.putIfAbsent(filePath, source);
431 if (previousSource != null) { 445 if (previousSource != null) {
432 throw new IllegalStateException(String.format("生成文件路径重复:%s,来源模板:%s、%s", 446 throw new IllegalStateException(String.format("生成文件路径重复:%s,来源模板:%s、%s",
433 filePath, previousSource, source)); 447 filePath, previousSource, source));
434 } 448 }
435 - String content = templateEngine.getTemplate(vmPath).render(bindingMap);  
436 - // 格式化代码  
437 - content = prettyCode(content, vmPath);  
438 - result.put(filePath, content);  
439 } 449 }
440 450
441 private void generateSubCode(CodegenTableDO table, List<CodegenTableDO> subTables, 451 private void generateSubCode(CodegenTableDO table, List<CodegenTableDO> subTables,
@@ -561,6 +571,14 @@ public class CodegenEngine { @@ -561,6 +571,14 @@ public class CodegenEngine {
561 List<String> subClassNameVars = new ArrayList<>(); 571 List<String> subClassNameVars = new ArrayList<>();
562 List<String> simpleClassNameUnderlineCases = new ArrayList<>(); 572 List<String> simpleClassNameUnderlineCases = new ArrayList<>();
563 List<String> subSimpleClassNameStrikeCases = new ArrayList<>(); 573 List<String> subSimpleClassNameStrikeCases = new ArrayList<>();
  574 + Set<String> masterSymbols = new HashSet<>();
  575 + masterSymbols.add(simpleClassName);
  576 + boolean mergeSubTableFields = ObjectUtils.equalsAny(table.getTemplateType(),
  577 + CodegenTemplateTypeEnum.MASTER_NORMAL.getType(), CodegenTemplateTypeEnum.MASTER_INNER.getType());
  578 + Set<String> masterFieldSymbols = new HashSet<>();
  579 + if (mergeSubTableFields) {
  580 + columns.forEach(column -> masterFieldSymbols.add(column.getJavaField()));
  581 + }
564 for (int i = 0; i < subTables.size(); i++) { 582 for (int i = 0; i < subTables.size(); i++) {
565 CodegenTableDO subTable = subTables.get(i); 583 CodegenTableDO subTable = subTables.get(i);
566 List<CodegenColumnDO> subColumns = subColumnsList.get(i); 584 List<CodegenColumnDO> subColumns = subColumnsList.get(i);
@@ -571,9 +589,18 @@ public class CodegenEngine { @@ -571,9 +589,18 @@ public class CodegenEngine {
571 subJoinColumnStrikeCases.add(toSymbolCase(subColumn.getJavaField(), '-')); // 将 DictType 转换成 dict-type 589 subJoinColumnStrikeCases.add(toSymbolCase(subColumn.getJavaField(), '-')); // 将 DictType 转换成 dict-type
572 // className 相关 590 // className 相关
573 String subSimpleClassName = removePrefix(subTable.getClassName(), upperFirst(subTable.getModuleName())); 591 String subSimpleClassName = removePrefix(subTable.getClassName(), upperFirst(subTable.getModuleName()));
  592 + if (!masterSymbols.add(subSimpleClassName)) {
  593 + throw exception(CODEGEN_MASTER_TABLE_NAME_DUPLICATE, subSimpleClassName);
  594 + }
  595 + String subClassNameVar = lowerFirst(subSimpleClassName);
  596 + String subFieldName = subClassNameVar
  597 + + (Boolean.TRUE.equals(subTable.getSubJoinMany()) ? "s" : "");
  598 + if (mergeSubTableFields && !masterFieldSymbols.add(subFieldName)) {
  599 + throw exception(CODEGEN_MASTER_TABLE_FIELD_DUPLICATE, subFieldName);
  600 + }
574 subSimpleClassNames.add(subSimpleClassName); 601 subSimpleClassNames.add(subSimpleClassName);
575 simpleClassNameUnderlineCases.add(toUnderlineCase(subSimpleClassName)); // 将 DictType 转换成 dict_type 602 simpleClassNameUnderlineCases.add(toUnderlineCase(subSimpleClassName)); // 将 DictType 转换成 dict_type
576 - subClassNameVars.add(lowerFirst(subSimpleClassName)); // 将 DictType 转换成 dictType,用于变量 603 + subClassNameVars.add(subClassNameVar); // 将 DictType 转换成 dictType,用于变量
577 subSimpleClassNameStrikeCases.add(toSymbolCase(subSimpleClassName, '-')); // 将 DictType 转换成 dict-type 604 subSimpleClassNameStrikeCases.add(toSymbolCase(subSimpleClassName, '-')); // 将 DictType 转换成 dict-type
578 } 605 }
579 bindingMap.put("subPrimaryColumns", subPrimaryColumns); 606 bindingMap.put("subPrimaryColumns", subPrimaryColumns);
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/api/api.ts.vm
@@ -4,7 +4,7 @@ import type { PageParam, PageResult } from &#39;@/http/types&#39; @@ -4,7 +4,7 @@ import type { PageParam, PageResult } from &#39;@/http/types&#39;
4 import { http } from '@/http/http' 4 import { http } from '@/http/http'
5 5
6 #set ($primaryJavaType = $primaryColumn.javaType.toLowerCase()) 6 #set ($primaryJavaType = $primaryColumn.javaType.toLowerCase())
7 -#if(${primaryJavaType} == "long" || ${primaryJavaType} == "integer" || ${primaryJavaType} == "short" || ${primaryJavaType} == "double" || ${primaryJavaType} == "bigdecimal" || ${primaryJavaType} == "byte") 7 +#if(${primaryJavaType} == "long" || ${primaryJavaType} == "integer" || ${primaryJavaType} == "short" || ${primaryJavaType} == "float" || ${primaryJavaType} == "double" || ${primaryJavaType} == "bigdecimal" || ${primaryJavaType} == "byte")
8 #set ($primaryTsType = "number") 8 #set ($primaryTsType = "number")
9 #else 9 #else
10 #set ($primaryTsType = "string") 10 #set ($primaryTsType = "string")
@@ -20,7 +20,7 @@ export interface ${subSimpleClassName} { @@ -20,7 +20,7 @@ export interface ${subSimpleClassName} {
20 #if ($column.primaryKey || $column.createOperation || $column.updateOperation || $column.listOperationResult) 20 #if ($column.primaryKey || $column.createOperation || $column.updateOperation || $column.listOperationResult)
21 #set ($javaType = $column.javaType.toLowerCase()) 21 #set ($javaType = $column.javaType.toLowerCase())
22 #set ($optional = $column.nullable || $column.primaryKey || ($table.templateType != 11 && $column.id == $subJoinColumn.id)) 22 #set ($optional = $column.nullable || $column.primaryKey || ($table.templateType != 11 && $column.id == $subJoinColumn.id))
23 -#if(${javaType} == "long" || ${javaType} == "integer" || ${javaType} == "short" || ${javaType} == "double" || ${javaType} == "bigdecimal" || ${javaType} == "byte") 23 +#if(${javaType} == "long" || ${javaType} == "integer" || ${javaType} == "short" || ${javaType} == "float" || ${javaType} == "double" || ${javaType} == "bigdecimal" || ${javaType} == "byte")
24 ${column.javaField}#if($optional)?#end: number 24 ${column.javaField}#if($optional)?#end: number
25 #elseif(${javaType} == "date" || ${javaType} == "localdate" || ${javaType} == "localdatetime") 25 #elseif(${javaType} == "date" || ${javaType} == "localdate" || ${javaType} == "localdatetime")
26 ${column.javaField}#if($optional)?#end: number 26 ${column.javaField}#if($optional)?#end: number
@@ -40,7 +40,7 @@ export interface ${simpleClassName} { @@ -40,7 +40,7 @@ export interface ${simpleClassName} {
40 #if ($column.primaryKey || $column.createOperation || $column.updateOperation || $column.listOperationResult) 40 #if ($column.primaryKey || $column.createOperation || $column.updateOperation || $column.listOperationResult)
41 #set ($javaType = $column.javaType.toLowerCase()) 41 #set ($javaType = $column.javaType.toLowerCase())
42 #set ($optional = $column.nullable || $column.primaryKey) 42 #set ($optional = $column.nullable || $column.primaryKey)
43 -#if(${javaType} == "long" || ${javaType} == "integer" || ${javaType} == "short" || ${javaType} == "double" || ${javaType} == "bigdecimal" || ${javaType} == "byte") 43 +#if(${javaType} == "long" || ${javaType} == "integer" || ${javaType} == "short" || ${javaType} == "float" || ${javaType} == "double" || ${javaType} == "bigdecimal" || ${javaType} == "byte")
44 ${column.javaField}#if($optional)?#end: number 44 ${column.javaField}#if($optional)?#end: number
45 #elseif(${javaType} == "date" || ${javaType} == "localdate" || ${javaType} == "localdatetime") 45 #elseif(${javaType} == "date" || ${javaType} == "localdate" || ${javaType} == "localdatetime")
46 ${column.javaField}#if($optional)?#end: number 46 ${column.javaField}#if($optional)?#end: number
@@ -101,13 +101,13 @@ export function delete${simpleClassName}(id: ${primaryTsType}) { @@ -101,13 +101,13 @@ export function delete${simpleClassName}(id: ${primaryTsType}) {
101 #set ($subPrimaryColumn = $subPrimaryColumns.get($index)) 101 #set ($subPrimaryColumn = $subPrimaryColumns.get($index))
102 #set ($subJoinColumn = $subJoinColumns.get($index)) 102 #set ($subJoinColumn = $subJoinColumns.get($index))
103 #set ($subPrimaryJavaType = $subPrimaryColumn.javaType.toLowerCase()) 103 #set ($subPrimaryJavaType = $subPrimaryColumn.javaType.toLowerCase())
104 -#if(${subPrimaryJavaType} == "long" || ${subPrimaryJavaType} == "integer" || ${subPrimaryJavaType} == "short" || ${subPrimaryJavaType} == "double" || ${subPrimaryJavaType} == "bigdecimal" || ${subPrimaryJavaType} == "byte") 104 +#if(${subPrimaryJavaType} == "long" || ${subPrimaryJavaType} == "integer" || ${subPrimaryJavaType} == "short" || ${subPrimaryJavaType} == "float" || ${subPrimaryJavaType} == "double" || ${subPrimaryJavaType} == "bigdecimal" || ${subPrimaryJavaType} == "byte")
105 #set ($subPrimaryTsType = "number") 105 #set ($subPrimaryTsType = "number")
106 #else 106 #else
107 #set ($subPrimaryTsType = "string") 107 #set ($subPrimaryTsType = "string")
108 #end 108 #end
109 #set ($subJoinJavaType = $subJoinColumn.javaType.toLowerCase()) 109 #set ($subJoinJavaType = $subJoinColumn.javaType.toLowerCase())
110 -#if(${subJoinJavaType} == "long" || ${subJoinJavaType} == "integer" || ${subJoinJavaType} == "short" || ${subJoinJavaType} == "double" || ${subJoinJavaType} == "bigdecimal" || ${subJoinJavaType} == "byte") 110 +#if(${subJoinJavaType} == "long" || ${subJoinJavaType} == "integer" || ${subJoinJavaType} == "short" || ${subJoinJavaType} == "float" || ${subJoinJavaType} == "double" || ${subJoinJavaType} == "bigdecimal" || ${subJoinJavaType} == "byte")
111 #set ($subJoinTsType = "number") 111 #set ($subJoinTsType = "number")
112 #else 112 #else
113 #set ($subJoinTsType = "string") 113 #set ($subJoinTsType = "string")
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/components/search-form.vue.vm
@@ -145,7 +145,7 @@ const formData = reactive({ @@ -145,7 +145,7 @@ const formData = reactive({
145 #else 145 #else
146 ${column.javaField}: undefined as number | undefined, 146 ${column.javaField}: undefined as number | undefined,
147 #end 147 #end
148 - #elseif(${javaType} == "long" || ${javaType} == "integer" || ${javaType} == "short" || ${javaType} == "double" || ${javaType} == "bigdecimal" || ${javaType} == "byte") 148 + #elseif(${javaType} == "long" || ${javaType} == "integer" || ${javaType} == "short" || ${javaType} == "float" || ${javaType} == "double" || ${javaType} == "bigdecimal" || ${javaType} == "byte")
149 ${column.javaField}: undefined as number | undefined, 149 ${column.javaField}: undefined as number | undefined,
150 #elseif(${javaType} == "boolean") 150 #elseif(${javaType} == "boolean")
151 ${column.javaField}: undefined as boolean | undefined, 151 ${column.javaField}: undefined as boolean | undefined,
@@ -174,7 +174,7 @@ const placeholder = computed(() =&gt; { @@ -174,7 +174,7 @@ const placeholder = computed(() =&gt; {
174 conditions.push(`${comment}:#[[${]]#getDictLabel(DICT_TYPE.${dictType.toUpperCase()}, formData.${javaField})#[[}]]#`) 174 conditions.push(`${comment}:#[[${]]#getDictLabel(DICT_TYPE.${dictType.toUpperCase()}, formData.${javaField})#[[}]]#`)
175 } 175 }
176 #else 176 #else
177 - #if(${javaType} == "long" || ${javaType} == "integer" || ${javaType} == "short" || ${javaType} == "double" || ${javaType} == "bigdecimal" || ${javaType} == "byte" || ${javaType} == "boolean") 177 + #if(${javaType} == "long" || ${javaType} == "integer" || ${javaType} == "short" || ${javaType} == "float" || ${javaType} == "double" || ${javaType} == "bigdecimal" || ${javaType} == "byte" || ${javaType} == "boolean")
178 if (formData.${javaField} !== undefined) { 178 if (formData.${javaField} !== undefined) {
179 conditions.push(`${comment}:#[[${]]#formData.${javaField}#[[}]]#`) 179 conditions.push(`${comment}:#[[${]]#formData.${javaField}#[[}]]#`)
180 } 180 }
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/views/detail/index.vue.vm
@@ -50,7 +50,7 @@ @@ -50,7 +50,7 @@
50 50
51 <script lang="ts" setup> 51 <script lang="ts" setup>
52 #set ($primaryJavaType = $primaryColumn.javaType.toLowerCase()) 52 #set ($primaryJavaType = $primaryColumn.javaType.toLowerCase())
53 -#if(${primaryJavaType} == "long" || ${primaryJavaType} == "integer" || ${primaryJavaType} == "short" || ${primaryJavaType} == "double" || ${primaryJavaType} == "bigdecimal" || ${primaryJavaType} == "byte") 53 +#if(${primaryJavaType} == "long" || ${primaryJavaType} == "integer" || ${primaryJavaType} == "short" || ${primaryJavaType} == "float" || ${primaryJavaType} == "double" || ${primaryJavaType} == "bigdecimal" || ${primaryJavaType} == "byte")
54 #set ($primaryTsType = "number") 54 #set ($primaryTsType = "number")
55 #else 55 #else
56 #set ($primaryTsType = "string") 56 #set ($primaryTsType = "string")
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/views/detail_erp.vue.vm
@@ -101,7 +101,7 @@ @@ -101,7 +101,7 @@
101 101
102 <script lang="ts" setup> 102 <script lang="ts" setup>
103 #set ($primaryJavaType = $primaryColumn.javaType.toLowerCase()) 103 #set ($primaryJavaType = $primaryColumn.javaType.toLowerCase())
104 -#if(${primaryJavaType} == "long" || ${primaryJavaType} == "integer" || ${primaryJavaType} == "short" || ${primaryJavaType} == "double" || ${primaryJavaType} == "bigdecimal" || ${primaryJavaType} == "byte") 104 +#if(${primaryJavaType} == "long" || ${primaryJavaType} == "integer" || ${primaryJavaType} == "short" || ${primaryJavaType} == "float" || ${primaryJavaType} == "double" || ${primaryJavaType} == "bigdecimal" || ${primaryJavaType} == "byte")
105 #set ($primaryTsType = "number") 105 #set ($primaryTsType = "number")
106 #else 106 #else
107 #set ($primaryTsType = "string") 107 #set ($primaryTsType = "string")
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/views/detail_master.vue.vm
@@ -94,7 +94,7 @@ @@ -94,7 +94,7 @@
94 94
95 <script lang="ts" setup> 95 <script lang="ts" setup>
96 #set ($primaryJavaType = $primaryColumn.javaType.toLowerCase()) 96 #set ($primaryJavaType = $primaryColumn.javaType.toLowerCase())
97 -#if(${primaryJavaType} == "long" || ${primaryJavaType} == "integer" || ${primaryJavaType} == "short" || ${primaryJavaType} == "double" || ${primaryJavaType} == "bigdecimal" || ${primaryJavaType} == "byte") 97 +#if(${primaryJavaType} == "long" || ${primaryJavaType} == "integer" || ${primaryJavaType} == "short" || ${primaryJavaType} == "float" || ${primaryJavaType} == "double" || ${primaryJavaType} == "bigdecimal" || ${primaryJavaType} == "byte")
98 #set ($primaryTsType = "number") 98 #set ($primaryTsType = "number")
99 #else 99 #else
100 #set ($primaryTsType = "string") 100 #set ($primaryTsType = "string")
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/views/form/index.vue.vm
@@ -42,10 +42,14 @@ @@ -42,10 +42,14 @@
42 label="${comment}" 42 label="${comment}"
43 label-width="180rpx" 43 label-width="180rpx"
44 prop="${javaField}" 44 prop="${javaField}"
  45 +#if ($javaType == "Boolean")
  46 + :columns="getBoolDictOptions(DICT_TYPE.${dictType.toUpperCase()})"
  47 +#else
45 :dict-type="DICT_TYPE.${dictType.toUpperCase()}" 48 :dict-type="DICT_TYPE.${dictType.toUpperCase()}"
46 #if ($javaType == "String") 49 #if ($javaType == "String")
47 dict-kind="str" 50 dict-kind="str"
48 #end 51 #end
  52 +#end
49 placeholder="请选择${comment}" 53 placeholder="请选择${comment}"
50 /> 54 />
51 ## 单选(字典)→ radio-group,适合选项较少 55 ## 单选(字典)→ radio-group,适合选项较少
@@ -62,14 +66,14 @@ @@ -62,14 +66,14 @@
62 </wd-radio-group> 66 </wd-radio-group>
63 </wd-form-item> 67 </wd-form-item>
64 ## 数字类型(无字典) 68 ## 数字类型(无字典)
65 -#elseif (${javaType.toLowerCase()} == "long" || ${javaType.toLowerCase()} == "integer" || ${javaType.toLowerCase()} == "short" || ${javaType.toLowerCase()} == "double" || ${javaType.toLowerCase()} == "bigdecimal" || ${javaType.toLowerCase()} == "byte") 69 +#elseif (${javaType.toLowerCase()} == "long" || ${javaType.toLowerCase()} == "integer" || ${javaType.toLowerCase()} == "short" || ${javaType.toLowerCase()} == "float" || ${javaType.toLowerCase()} == "double" || ${javaType.toLowerCase()} == "bigdecimal" || ${javaType.toLowerCase()} == "byte")
66 <wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}"> 70 <wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}">
67 <wd-input-number 71 <wd-input-number
68 v-model="formData.${javaField}" 72 v-model="formData.${javaField}"
69 #if ($column.nullable) 73 #if ($column.nullable)
70 allow-null 74 allow-null
71 #end 75 #end
72 -#if (${javaType.toLowerCase()} == "double" || ${javaType.toLowerCase()} == "bigdecimal") 76 +#if (${javaType.toLowerCase()} == "float" || ${javaType.toLowerCase()} == "double" || ${javaType.toLowerCase()} == "bigdecimal")
73 :precision="2" 77 :precision="2"
74 input-type="number" 78 input-type="number"
75 #end 79 #end
@@ -104,20 +108,20 @@ @@ -104,20 +108,20 @@
104 v-model:visible="${javaField}Visible" 108 v-model:visible="${javaField}Visible"
105 :model-value="formData.${javaField} || Date.now()" 109 :model-value="formData.${javaField} || Date.now()"
106 type="${pickerType}" 110 type="${pickerType}"
107 -#if ($pickerType == "datetime")  
108 @update:model-value="value => formData.${javaField} = value" 111 @update:model-value="value => formData.${javaField} = value"
109 -#else  
110 - @update:model-value="value => formData.${javaField} = value"  
111 -#end  
112 /> 112 />
113 ## 文本域 113 ## 文本域
114 -#elseif ($column.htmlType == "textarea") 114 +#elseif ($column.htmlType == "textarea" || $column.htmlType == "editor")
115 <wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}"> 115 <wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}">
116 <wd-textarea 116 <wd-textarea
117 v-model="formData.${javaField}" 117 v-model="formData.${javaField}"
118 clearable 118 clearable
119 placeholder="请输入${comment}" 119 placeholder="请输入${comment}"
  120 +#if ($column.htmlType == "editor")
  121 + :maxlength="2000"
  122 +#else
120 :maxlength="200" 123 :maxlength="200"
  124 +#end
121 show-word-limit 125 show-word-limit
122 /> 126 />
123 </wd-form-item> 127 </wd-form-item>
@@ -129,7 +133,7 @@ @@ -129,7 +133,7 @@
129 ## 文件上传 → yd-upload-file 133 ## 文件上传 → yd-upload-file
130 #elseif ($column.htmlType == "fileUpload") 134 #elseif ($column.htmlType == "fileUpload")
131 <wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}"> 135 <wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}">
132 - <yd-upload-file v-model="formData.${javaField}" directory="${table.moduleName}/${table.businessName}" /> 136 + <yd-upload-file v-model="formData.${javaField}" :limit="1" directory="${table.moduleName}/${table.businessName}" />
133 </wd-form-item> 137 </wd-form-item>
134 ## 默认:文本输入 138 ## 默认:文本输入
135 #else 139 #else
@@ -202,10 +206,14 @@ @@ -202,10 +206,14 @@
202 v-model="item.${column.javaField}" 206 v-model="item.${column.javaField}"
203 label="${column.columnComment}" 207 label="${column.columnComment}"
204 label-width="180rpx" 208 label-width="180rpx"
  209 +#if ($column.javaType == "Boolean")
  210 + :columns="getBoolDictOptions(DICT_TYPE.${column.dictType.toUpperCase()})"
  211 +#else
205 :dict-type="DICT_TYPE.${column.dictType.toUpperCase()}" 212 :dict-type="DICT_TYPE.${column.dictType.toUpperCase()}"
206 #if ($column.javaType == "String") 213 #if ($column.javaType == "String")
207 dict-kind="str" 214 dict-kind="str"
208 #end 215 #end
  216 +#end
209 placeholder="请选择${column.columnComment}" 217 placeholder="请选择${column.columnComment}"
210 /> 218 />
211 #elseif ($column.htmlType == "radio" && $column.dictType && "" != $column.dictType) 219 #elseif ($column.htmlType == "radio" && $column.dictType && "" != $column.dictType)
@@ -224,14 +232,14 @@ @@ -224,14 +232,14 @@
224 <wd-form-item title="${column.columnComment}" title-width="180rpx" center> 232 <wd-form-item title="${column.columnComment}" title-width="180rpx" center>
225 <wd-switch v-model="item.${column.javaField}" /> 233 <wd-switch v-model="item.${column.javaField}" />
226 </wd-form-item> 234 </wd-form-item>
227 -#elseif ($javaType == "long" || $javaType == "integer" || $javaType == "short" || $javaType == "double" || $javaType == "bigdecimal" || $javaType == "byte") 235 +#elseif ($javaType == "long" || $javaType == "integer" || $javaType == "short" || $javaType == "float" || $javaType == "double" || $javaType == "bigdecimal" || $javaType == "byte")
228 <wd-form-item title="${column.columnComment}" title-width="180rpx"> 236 <wd-form-item title="${column.columnComment}" title-width="180rpx">
229 <wd-input-number 237 <wd-input-number
230 v-model="item.${column.javaField}" 238 v-model="item.${column.javaField}"
231 #if($column.nullable) 239 #if($column.nullable)
232 allow-null 240 allow-null
233 #end 241 #end
234 -#if ($javaType == "double" || $javaType == "bigdecimal") 242 +#if ($javaType == "float" || $javaType == "double" || $javaType == "bigdecimal")
235 :precision="2" 243 :precision="2"
236 input-type="number" 244 input-type="number"
237 #end 245 #end
@@ -259,15 +267,20 @@ @@ -259,15 +267,20 @@
259 v-model:visible="subDateVisible[`${subClassNameVar}s.#[[${]]#index#[[}]]#.${column.javaField}`]" 267 v-model:visible="subDateVisible[`${subClassNameVar}s.#[[${]]#index#[[}]]#.${column.javaField}`]"
260 :model-value="item.${column.javaField} || Date.now()" 268 :model-value="item.${column.javaField} || Date.now()"
261 type="${subPickerType}" 269 type="${subPickerType}"
262 -#if ($javaType == "localdatetime")  
263 @update:model-value="value => item.${column.javaField} = value" 270 @update:model-value="value => item.${column.javaField} = value"
264 -#else  
265 - @update:model-value="value => item.${column.javaField} = value"  
266 -#end  
267 /> 271 />
268 -#elseif ($column.htmlType == "textarea") 272 +#elseif ($column.htmlType == "textarea" || $column.htmlType == "editor")
269 <wd-form-item title="${column.columnComment}" title-width="180rpx"> 273 <wd-form-item title="${column.columnComment}" title-width="180rpx">
270 - <wd-textarea v-model="item.${column.javaField}" clearable placeholder="请输入${column.columnComment}" /> 274 + <wd-textarea
  275 + v-model="item.${column.javaField}"
  276 + clearable
  277 + placeholder="请输入${column.columnComment}"
  278 +#if ($column.htmlType == "editor")
  279 + :maxlength="2000"
  280 +#else
  281 + :maxlength="200"
  282 +#end
  283 + />
271 </wd-form-item> 284 </wd-form-item>
272 #elseif ($column.htmlType == "imageUpload") 285 #elseif ($column.htmlType == "imageUpload")
273 <wd-form-item title="${column.columnComment}" title-width="180rpx"> 286 <wd-form-item title="${column.columnComment}" title-width="180rpx">
@@ -275,7 +288,7 @@ @@ -275,7 +288,7 @@
275 </wd-form-item> 288 </wd-form-item>
276 #elseif ($column.htmlType == "fileUpload") 289 #elseif ($column.htmlType == "fileUpload")
277 <wd-form-item title="${column.columnComment}" title-width="180rpx"> 290 <wd-form-item title="${column.columnComment}" title-width="180rpx">
278 - <yd-upload-file v-model="item.${column.javaField}" directory="${table.moduleName}/${table.businessName}" /> 291 + <yd-upload-file v-model="item.${column.javaField}" :limit="1" directory="${table.moduleName}/${table.businessName}" />
279 </wd-form-item> 292 </wd-form-item>
280 #else 293 #else
281 <wd-form-item title="${column.columnComment}" title-width="180rpx"> 294 <wd-form-item title="${column.columnComment}" title-width="180rpx">
@@ -306,10 +319,14 @@ @@ -306,10 +319,14 @@
306 v-model="${subClassNameVar}.${column.javaField}" 319 v-model="${subClassNameVar}.${column.javaField}"
307 label="${column.columnComment}" 320 label="${column.columnComment}"
308 label-width="180rpx" 321 label-width="180rpx"
  322 +#if ($column.javaType == "Boolean")
  323 + :columns="getBoolDictOptions(DICT_TYPE.${column.dictType.toUpperCase()})"
  324 +#else
309 :dict-type="DICT_TYPE.${column.dictType.toUpperCase()}" 325 :dict-type="DICT_TYPE.${column.dictType.toUpperCase()}"
310 #if ($column.javaType == "String") 326 #if ($column.javaType == "String")
311 dict-kind="str" 327 dict-kind="str"
312 #end 328 #end
  329 +#end
313 placeholder="请选择${column.columnComment}" 330 placeholder="请选择${column.columnComment}"
314 /> 331 />
315 #elseif ($column.htmlType == "radio" && $column.dictType && "" != $column.dictType) 332 #elseif ($column.htmlType == "radio" && $column.dictType && "" != $column.dictType)
@@ -328,14 +345,14 @@ @@ -328,14 +345,14 @@
328 <wd-form-item title="${column.columnComment}" title-width="180rpx" center> 345 <wd-form-item title="${column.columnComment}" title-width="180rpx" center>
329 <wd-switch v-model="${subClassNameVar}.${column.javaField}" /> 346 <wd-switch v-model="${subClassNameVar}.${column.javaField}" />
330 </wd-form-item> 347 </wd-form-item>
331 -#elseif ($javaType == "long" || $javaType == "integer" || $javaType == "short" || $javaType == "double" || $javaType == "bigdecimal" || $javaType == "byte") 348 +#elseif ($javaType == "long" || $javaType == "integer" || $javaType == "short" || $javaType == "float" || $javaType == "double" || $javaType == "bigdecimal" || $javaType == "byte")
332 <wd-form-item title="${column.columnComment}" title-width="180rpx"> 349 <wd-form-item title="${column.columnComment}" title-width="180rpx">
333 <wd-input-number 350 <wd-input-number
334 v-model="${subClassNameVar}.${column.javaField}" 351 v-model="${subClassNameVar}.${column.javaField}"
335 #if($column.nullable) 352 #if($column.nullable)
336 allow-null 353 allow-null
337 #end 354 #end
338 -#if ($javaType == "double" || $javaType == "bigdecimal") 355 +#if ($javaType == "float" || $javaType == "double" || $javaType == "bigdecimal")
339 :precision="2" 356 :precision="2"
340 input-type="number" 357 input-type="number"
341 #end 358 #end
@@ -363,15 +380,20 @@ @@ -363,15 +380,20 @@
363 v-model:visible="subDateVisible['${subClassNameVar}.${column.javaField}']" 380 v-model:visible="subDateVisible['${subClassNameVar}.${column.javaField}']"
364 :model-value="${subClassNameVar}.${column.javaField} || Date.now()" 381 :model-value="${subClassNameVar}.${column.javaField} || Date.now()"
365 type="${subPickerType}" 382 type="${subPickerType}"
366 -#if ($javaType == "localdatetime")  
367 - @update:model-value="value => ${subClassNameVar}.${column.javaField} = value"  
368 -#else  
369 @update:model-value="value => ${subClassNameVar}.${column.javaField} = value" 383 @update:model-value="value => ${subClassNameVar}.${column.javaField} = value"
370 -#end  
371 /> 384 />
372 -#elseif ($column.htmlType == "textarea") 385 +#elseif ($column.htmlType == "textarea" || $column.htmlType == "editor")
373 <wd-form-item title="${column.columnComment}" title-width="180rpx"> 386 <wd-form-item title="${column.columnComment}" title-width="180rpx">
374 - <wd-textarea v-model="${subClassNameVar}.${column.javaField}" clearable placeholder="请输入${column.columnComment}" /> 387 + <wd-textarea
  388 + v-model="${subClassNameVar}.${column.javaField}"
  389 + clearable
  390 + placeholder="请输入${column.columnComment}"
  391 +#if ($column.htmlType == "editor")
  392 + :maxlength="2000"
  393 +#else
  394 + :maxlength="200"
  395 +#end
  396 + />
375 </wd-form-item> 397 </wd-form-item>
376 #elseif ($column.htmlType == "imageUpload") 398 #elseif ($column.htmlType == "imageUpload")
377 <wd-form-item title="${column.columnComment}" title-width="180rpx"> 399 <wd-form-item title="${column.columnComment}" title-width="180rpx">
@@ -379,7 +401,7 @@ @@ -379,7 +401,7 @@
379 </wd-form-item> 401 </wd-form-item>
380 #elseif ($column.htmlType == "fileUpload") 402 #elseif ($column.htmlType == "fileUpload")
381 <wd-form-item title="${column.columnComment}" title-width="180rpx"> 403 <wd-form-item title="${column.columnComment}" title-width="180rpx">
382 - <yd-upload-file v-model="${subClassNameVar}.${column.javaField}" directory="${table.moduleName}/${table.businessName}" /> 404 + <yd-upload-file v-model="${subClassNameVar}.${column.javaField}" :limit="1" directory="${table.moduleName}/${table.businessName}" />
383 </wd-form-item> 405 </wd-form-item>
384 #else 406 #else
385 <wd-form-item title="${column.columnComment}" title-width="180rpx"> 407 <wd-form-item title="${column.columnComment}" title-width="180rpx">
@@ -412,7 +434,7 @@ @@ -412,7 +434,7 @@
412 <script lang="ts" setup> 434 <script lang="ts" setup>
413 import type { FormInstance } from '@wot-ui/ui/components/wd-form/types' 435 import type { FormInstance } from '@wot-ui/ui/components/wd-form/types'
414 #set ($primaryJavaType = $primaryColumn.javaType.toLowerCase()) 436 #set ($primaryJavaType = $primaryColumn.javaType.toLowerCase())
415 -#if(${primaryJavaType} == "long" || ${primaryJavaType} == "integer" || ${primaryJavaType} == "short" || ${primaryJavaType} == "double" || ${primaryJavaType} == "bigdecimal" || ${primaryJavaType} == "byte") 437 +#if(${primaryJavaType} == "long" || ${primaryJavaType} == "integer" || ${primaryJavaType} == "short" || ${primaryJavaType} == "float" || ${primaryJavaType} == "double" || ${primaryJavaType} == "bigdecimal" || ${primaryJavaType} == "byte")
416 #set ($primaryTsType = "number") 438 #set ($primaryTsType = "number")
417 #else 439 #else
418 #set ($primaryTsType = "string") 440 #set ($primaryTsType = "string")
@@ -434,7 +456,7 @@ import type { FormInstance } from &#39;@wot-ui/ui/components/wd-form/types&#39; @@ -434,7 +456,7 @@ import type { FormInstance } from &#39;@wot-ui/ui/components/wd-form/types&#39;
434 #set ($hasGetIntDictOptions = 1) 456 #set ($hasGetIntDictOptions = 1)
435 #elseif ($column.htmlType == "radio" && $column.javaType == "String") 457 #elseif ($column.htmlType == "radio" && $column.javaType == "String")
436 #set ($hasGetStrDictOptions = 1) 458 #set ($hasGetStrDictOptions = 1)
437 -#elseif ($column.htmlType == "radio" && $column.javaType == "Boolean") 459 +#elseif (($column.htmlType == "select" || $column.htmlType == "radio") && $column.javaType == "Boolean")
438 #set ($hasGetBoolDictOptions = 1) 460 #set ($hasGetBoolDictOptions = 1)
439 #elseif ($column.htmlType == "radio") 461 #elseif ($column.htmlType == "radio")
440 #set ($hasGetDictOptions = 1) 462 #set ($hasGetDictOptions = 1)
@@ -460,7 +482,7 @@ import type { FormInstance } from &#39;@wot-ui/ui/components/wd-form/types&#39; @@ -460,7 +482,7 @@ import type { FormInstance } from &#39;@wot-ui/ui/components/wd-form/types&#39;
460 #set ($hasGetIntDictOptions = 1) 482 #set ($hasGetIntDictOptions = 1)
461 #elseif ($column.htmlType == "radio" && $column.javaType == "String") 483 #elseif ($column.htmlType == "radio" && $column.javaType == "String")
462 #set ($hasGetStrDictOptions = 1) 484 #set ($hasGetStrDictOptions = 1)
463 -#elseif ($column.htmlType == "radio" && $column.javaType == "Boolean") 485 +#elseif (($column.htmlType == "select" || $column.htmlType == "radio") && $column.javaType == "Boolean")
464 #set ($hasGetBoolDictOptions = 1) 486 #set ($hasGetBoolDictOptions = 1)
465 #elseif ($column.htmlType == "radio") 487 #elseif ($column.htmlType == "radio")
466 #set ($hasGetDictOptions = 1) 488 #set ($hasGetDictOptions = 1)
@@ -567,7 +589,7 @@ definePage({ @@ -567,7 +589,7 @@ definePage({
567 const toast = useToast() 589 const toast = useToast()
568 const getTitle = computed(() => props.id ? '编辑${table.classComment}' : '新增${table.classComment}') 590 const getTitle = computed(() => props.id ? '编辑${table.classComment}' : '新增${table.classComment}')
569 const formLoading = ref(false) // 表单提交状态 591 const formLoading = ref(false) // 表单提交状态
570 -const formData = ref<${simpleClassName}>({ 592 +const formData = ref<Partial<${simpleClassName}>>({
571 #foreach($column in $columns) 593 #foreach($column in $columns)
572 #if (($column.createOperation || $column.updateOperation) || $column.primaryKey) 594 #if (($column.createOperation || $column.updateOperation) || $column.primaryKey)
573 #set ($javaType = $column.javaType.toLowerCase()) 595 #set ($javaType = $column.javaType.toLowerCase())
@@ -577,7 +599,9 @@ const formData = ref&lt;${simpleClassName}&gt;({ @@ -577,7 +599,9 @@ const formData = ref&lt;${simpleClassName}&gt;({
577 ${column.javaField}: undefined, 599 ${column.javaField}: undefined,
578 #elseif($table.templateType == 2 && $column.id == $treeParentColumn.id) 600 #elseif($table.templateType == 2 && $column.id == $treeParentColumn.id)
579 ${column.javaField}: props.${column.javaField} ?? 0, 601 ${column.javaField}: props.${column.javaField} ?? 0,
580 -#elseif(${javaType} == "long" || ${javaType} == "integer" || ${javaType} == "short" || ${javaType} == "double" || ${javaType} == "bigdecimal" || ${javaType} == "byte") 602 +#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $column.dictType && "" != $column.dictType)
  603 + ${column.javaField}: undefined,
  604 +#elseif(${javaType} == "long" || ${javaType} == "integer" || ${javaType} == "short" || ${javaType} == "float" || ${javaType} == "double" || ${javaType} == "bigdecimal" || ${javaType} == "byte")
581 #if($column.nullable) 605 #if($column.nullable)
582 ${column.javaField}: undefined, 606 ${column.javaField}: undefined,
583 #else 607 #else
@@ -586,7 +610,7 @@ const formData = ref&lt;${simpleClassName}&gt;({ @@ -586,7 +610,7 @@ const formData = ref&lt;${simpleClassName}&gt;({
586 #elseif(${javaType} == "boolean") 610 #elseif(${javaType} == "boolean")
587 ${column.javaField}: false, 611 ${column.javaField}: false,
588 #elseif(${javaType} == "date" || ${javaType} == "localdate" || ${javaType} == "localdatetime") 612 #elseif(${javaType} == "date" || ${javaType} == "localdate" || ${javaType} == "localdatetime")
589 - ${column.javaField}:#if($column.nullable || $column.primaryKey || $javaFieldLower == "createtime" || $javaFieldLower == "updatetime") undefined#else 0#end, 613 + ${column.javaField}: undefined,
590 #else 614 #else
591 ${column.javaField}: '', 615 ${column.javaField}: '',
592 #end 616 #end
@@ -601,9 +625,9 @@ const formData = ref&lt;${simpleClassName}&gt;({ @@ -601,9 +625,9 @@ const formData = ref&lt;${simpleClassName}&gt;({
601 #set ($subColumns = $subColumnsList.get($index)) 625 #set ($subColumns = $subColumnsList.get($index))
602 #set ($subJoinColumn = $subJoinColumns.get($index)) 626 #set ($subJoinColumn = $subJoinColumns.get($index))
603 #if ($subTable.subJoinMany) 627 #if ($subTable.subJoinMany)
604 -const ${subClassNameVar}s = ref<${subSimpleClassName}[]>([]) // ${subTable.classComment}数据 628 +const ${subClassNameVar}s = ref<Partial<${subSimpleClassName}>[]>([]) // ${subTable.classComment}数据
605 #else 629 #else
606 -const ${subClassNameVar} = ref<${subSimpleClassName} | null>(null) // ${subTable.classComment}数据 630 +const ${subClassNameVar} = ref<Partial<${subSimpleClassName}> | null>(null) // ${subTable.classComment}数据
607 #end 631 #end
608 #end 632 #end
609 #end 633 #end
@@ -646,7 +670,11 @@ function add${subClassNameVar.substring(0,1).toUpperCase()}${subClassNameVar.sub @@ -646,7 +670,11 @@ function add${subClassNameVar.substring(0,1).toUpperCase()}${subClassNameVar.sub
646 #foreach ($column in $subColumns) 670 #foreach ($column in $subColumns)
647 #if (($column.createOperation || $column.updateOperation) && !$column.primaryKey && $column.id != $subJoinColumn.id) 671 #if (($column.createOperation || $column.updateOperation) && !$column.primaryKey && $column.id != $subJoinColumn.id)
648 #set ($javaType = $column.javaType.toLowerCase()) 672 #set ($javaType = $column.javaType.toLowerCase())
649 - ${column.javaField}:#if($column.nullable) undefined#elseif($javaType == "long" || $javaType == "integer" || $javaType == "short" || $javaType == "double" || $javaType == "bigdecimal" || $javaType == "byte" || $javaType == "date" || $javaType == "localdate" || $javaType == "localdatetime") 0#elseif($javaType == "boolean") false#else ''#end, 673 +#if((($column.htmlType == "select" || $column.htmlType == "radio") && $column.dictType && "" != $column.dictType) || $javaType == "date" || $javaType == "localdate" || $javaType == "localdatetime")
  674 + ${column.javaField}: undefined,
  675 +#else
  676 + ${column.javaField}:#if($column.nullable) undefined#elseif($javaType == "long" || $javaType == "integer" || $javaType == "short" || $javaType == "float" || $javaType == "double" || $javaType == "bigdecimal" || $javaType == "byte") 0#elseif($javaType == "boolean") false#else ''#end,
  677 +#end
650 #end 678 #end
651 #end 679 #end
652 }) 680 })
@@ -664,7 +692,11 @@ function add${subClassNameVar.substring(0,1).toUpperCase()}${subClassNameVar.sub @@ -664,7 +692,11 @@ function add${subClassNameVar.substring(0,1).toUpperCase()}${subClassNameVar.sub
664 #foreach ($column in $subColumns) 692 #foreach ($column in $subColumns)
665 #if (($column.createOperation || $column.updateOperation) && !$column.primaryKey && $column.id != $subJoinColumn.id) 693 #if (($column.createOperation || $column.updateOperation) && !$column.primaryKey && $column.id != $subJoinColumn.id)
666 #set ($javaType = $column.javaType.toLowerCase()) 694 #set ($javaType = $column.javaType.toLowerCase())
667 - ${column.javaField}:#if($column.nullable) undefined#elseif($javaType == "long" || $javaType == "integer" || $javaType == "short" || $javaType == "double" || $javaType == "bigdecimal" || $javaType == "byte" || $javaType == "date" || $javaType == "localdate" || $javaType == "localdatetime") 0#elseif($javaType == "boolean") false#else ''#end, 695 +#if((($column.htmlType == "select" || $column.htmlType == "radio") && $column.dictType && "" != $column.dictType) || $javaType == "date" || $javaType == "localdate" || $javaType == "localdatetime")
  696 + ${column.javaField}: undefined,
  697 +#else
  698 + ${column.javaField}:#if($column.nullable) undefined#elseif($javaType == "long" || $javaType == "integer" || $javaType == "short" || $javaType == "float" || $javaType == "double" || $javaType == "bigdecimal" || $javaType == "byte") 0#elseif($javaType == "boolean") false#else ''#end,
  699 +#end
668 #end 700 #end
669 #end 701 #end
670 } 702 }
@@ -771,16 +803,16 @@ async function handleSubmit() { @@ -771,16 +803,16 @@ async function handleSubmit() {
771 formLoading.value = true 803 formLoading.value = true
772 try { 804 try {
773 #if ($table.templateType == 10 || $table.templateType == 12) 805 #if ($table.templateType == 10 || $table.templateType == 12)
774 - const data: ${simpleClassName} = { 806 + const data = {
775 ...formData.value, 807 ...formData.value,
776 #foreach ($subTable in $subTables) 808 #foreach ($subTable in $subTables)
777 #set ($index = $foreach.count - 1) 809 #set ($index = $foreach.count - 1)
778 #set ($subClassNameVar = $subClassNameVars.get($index)) 810 #set ($subClassNameVar = $subClassNameVars.get($index))
779 ${subClassNameVar}#if($subTable.subJoinMany)s#end: ${subClassNameVar}#if($subTable.subJoinMany)s#end.value, 811 ${subClassNameVar}#if($subTable.subJoinMany)s#end: ${subClassNameVar}#if($subTable.subJoinMany)s#end.value,
780 #end 812 #end
781 - } 813 + } as ${simpleClassName}
782 #else 814 #else
783 - const data = formData.value 815 + const data = formData.value as ${simpleClassName}
784 #end 816 #end
785 if (props.id) { 817 if (props.id) {
786 await update${simpleClassName}(data) 818 await update${simpleClassName}(data)
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/views/form_sub_erp.vue.vm
@@ -4,24 +4,28 @@ @@ -4,24 +4,28 @@
4 #set ($subPrimaryColumn = $subPrimaryColumns.get($subIndex)) 4 #set ($subPrimaryColumn = $subPrimaryColumns.get($subIndex))
5 #set ($subJoinColumn = $subJoinColumns.get($subIndex)) 5 #set ($subJoinColumn = $subJoinColumns.get($subIndex))
6 #set ($subPrimaryJavaType = $subPrimaryColumn.javaType.toLowerCase()) 6 #set ($subPrimaryJavaType = $subPrimaryColumn.javaType.toLowerCase())
7 -#if(${subPrimaryJavaType} == "long" || ${subPrimaryJavaType} == "integer" || ${subPrimaryJavaType} == "short" || ${subPrimaryJavaType} == "double" || ${subPrimaryJavaType} == "bigdecimal" || ${subPrimaryJavaType} == "byte") 7 +#if(${subPrimaryJavaType} == "long" || ${subPrimaryJavaType} == "integer" || ${subPrimaryJavaType} == "short" || ${subPrimaryJavaType} == "float" || ${subPrimaryJavaType} == "double" || ${subPrimaryJavaType} == "bigdecimal" || ${subPrimaryJavaType} == "byte")
8 #set ($subPrimaryTsType = "number") 8 #set ($subPrimaryTsType = "number")
9 #else 9 #else
10 #set ($subPrimaryTsType = "string") 10 #set ($subPrimaryTsType = "string")
11 #end 11 #end
12 #set ($subJoinJavaType = $subJoinColumn.javaType.toLowerCase()) 12 #set ($subJoinJavaType = $subJoinColumn.javaType.toLowerCase())
13 -#if(${subJoinJavaType} == "long" || ${subJoinJavaType} == "integer" || ${subJoinJavaType} == "short" || ${subJoinJavaType} == "double" || ${subJoinJavaType} == "bigdecimal" || ${subJoinJavaType} == "byte") 13 +#if(${subJoinJavaType} == "long" || ${subJoinJavaType} == "integer" || ${subJoinJavaType} == "short" || ${subJoinJavaType} == "float" || ${subJoinJavaType} == "double" || ${subJoinJavaType} == "bigdecimal" || ${subJoinJavaType} == "byte")
14 #set ($subJoinTsType = "number") 14 #set ($subJoinTsType = "number")
15 #else 15 #else
16 #set ($subJoinTsType = "string") 16 #set ($subJoinTsType = "string")
17 #end 17 #end
18 #set ($hasDict = 0) 18 #set ($hasDict = 0)
  19 +#set ($hasBoolDict = 0)
19 #set ($hasDate = 0) 20 #set ($hasDate = 0)
20 #set ($hasDateTime = 0) 21 #set ($hasDateTime = 0)
21 #foreach ($column in $subColumns) 22 #foreach ($column in $subColumns)
22 #if (($column.createOperation || $column.updateOperation) && !$column.primaryKey && $column.id != $subJoinColumn.id) 23 #if (($column.createOperation || $column.updateOperation) && !$column.primaryKey && $column.id != $subJoinColumn.id)
23 #if (($column.htmlType == "select" || $column.htmlType == "radio") && $column.dictType && "" != $column.dictType) 24 #if (($column.htmlType == "select" || $column.htmlType == "radio") && $column.dictType && "" != $column.dictType)
24 #set ($hasDict = 1) 25 #set ($hasDict = 1)
  26 +#if ($column.javaType == "Boolean")
  27 +#set ($hasBoolDict = 1)
  28 +#end
25 #end 29 #end
26 #if (${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdate") 30 #if (${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdate")
27 #set ($hasDate = 1) 31 #set ($hasDate = 1)
@@ -48,10 +52,14 @@ @@ -48,10 +52,14 @@
48 label="${column.columnComment}" 52 label="${column.columnComment}"
49 label-width="180rpx" 53 label-width="180rpx"
50 prop="${column.javaField}" 54 prop="${column.javaField}"
  55 +#if ($column.javaType == "Boolean")
  56 + :columns="getBoolDictOptions(DICT_TYPE.${column.dictType.toUpperCase()})"
  57 +#else
51 :dict-type="DICT_TYPE.${column.dictType.toUpperCase()}" 58 :dict-type="DICT_TYPE.${column.dictType.toUpperCase()}"
52 #if ($column.javaType == "String") 59 #if ($column.javaType == "String")
53 dict-kind="str" 60 dict-kind="str"
54 #end 61 #end
  62 +#end
55 placeholder="请选择${column.columnComment}" 63 placeholder="请选择${column.columnComment}"
56 /> 64 />
57 #elseif ($javaType == "date" || $javaType == "localdate" || $javaType == "localdatetime") 65 #elseif ($javaType == "date" || $javaType == "localdate" || $javaType == "localdatetime")
@@ -76,34 +84,39 @@ @@ -76,34 +84,39 @@
76 v-model:visible="${column.javaField}Visible" 84 v-model:visible="${column.javaField}Visible"
77 :model-value="formData.${column.javaField} || Date.now()" 85 :model-value="formData.${column.javaField} || Date.now()"
78 type="${pickerType}" 86 type="${pickerType}"
79 -#if ($javaType == "localdatetime")  
80 @update:model-value="value => formData.${column.javaField} = value" 87 @update:model-value="value => formData.${column.javaField} = value"
81 -#else  
82 - @update:model-value="value => formData.${column.javaField} = value"  
83 -#end  
84 /> 88 />
85 #else 89 #else
86 <wd-form-item title="${column.columnComment}" title-width="180rpx" prop="${column.javaField}"> 90 <wd-form-item title="${column.columnComment}" title-width="180rpx" prop="${column.javaField}">
87 #if ($javaType == "boolean") 91 #if ($javaType == "boolean")
88 <wd-switch v-model="formData.${column.javaField}" /> 92 <wd-switch v-model="formData.${column.javaField}" />
89 -#elseif ($javaType == "long" || $javaType == "integer" || $javaType == "short" || $javaType == "double" || $javaType == "bigdecimal" || $javaType == "byte") 93 +#elseif ($javaType == "long" || $javaType == "integer" || $javaType == "short" || $javaType == "float" || $javaType == "double" || $javaType == "bigdecimal" || $javaType == "byte")
90 <wd-input-number 94 <wd-input-number
91 v-model="formData.${column.javaField}" 95 v-model="formData.${column.javaField}"
92 #if($column.nullable) 96 #if($column.nullable)
93 allow-null 97 allow-null
94 #end 98 #end
95 -#if ($javaType == "double" || $javaType == "bigdecimal") 99 +#if ($javaType == "float" || $javaType == "double" || $javaType == "bigdecimal")
96 :precision="2" 100 :precision="2"
97 input-type="number" 101 input-type="number"
98 #end 102 #end
99 :min="0" 103 :min="0"
100 /> 104 />
101 -#elseif ($column.htmlType == "textarea")  
102 - <wd-textarea v-model="formData.${column.javaField}" clearable placeholder="请输入${column.columnComment}" /> 105 +#elseif ($column.htmlType == "textarea" || $column.htmlType == "editor")
  106 + <wd-textarea
  107 + v-model="formData.${column.javaField}"
  108 + clearable
  109 + placeholder="请输入${column.columnComment}"
  110 +#if ($column.htmlType == "editor")
  111 + :maxlength="2000"
  112 +#else
  113 + :maxlength="200"
  114 +#end
  115 + />
103 #elseif ($column.htmlType == "imageUpload") 116 #elseif ($column.htmlType == "imageUpload")
104 <yd-upload-img v-model="formData.${column.javaField}" directory="${table.moduleName}/${table.businessName}" /> 117 <yd-upload-img v-model="formData.${column.javaField}" directory="${table.moduleName}/${table.businessName}" />
105 #elseif ($column.htmlType == "fileUpload") 118 #elseif ($column.htmlType == "fileUpload")
106 - <yd-upload-file v-model="formData.${column.javaField}" directory="${table.moduleName}/${table.businessName}" /> 119 + <yd-upload-file v-model="formData.${column.javaField}" :limit="1" directory="${table.moduleName}/${table.businessName}" />
107 #else 120 #else
108 <wd-input v-model="formData.${column.javaField}" clearable placeholder="请输入${column.columnComment}" /> 121 <wd-input v-model="formData.${column.javaField}" clearable placeholder="请输入${column.columnComment}" />
109 #end 122 #end
@@ -130,6 +143,9 @@ import { computed, onMounted, ref } from &#39;vue&#39; @@ -130,6 +143,9 @@ import { computed, onMounted, ref } from &#39;vue&#39;
130 import { useToast } from '@wot-ui/ui/components/wd-toast' 143 import { useToast } from '@wot-ui/ui/components/wd-toast'
131 import { create${subSimpleClassName}, get${subSimpleClassName}, update${subSimpleClassName} } from '@/api/${table.moduleName}/${table.businessName}' 144 import { create${subSimpleClassName}, get${subSimpleClassName}, update${subSimpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
132 import { delay, navigateBackPlus } from '@/utils' 145 import { delay, navigateBackPlus } from '@/utils'
  146 +#if ($hasBoolDict == 1)
  147 +import { getBoolDictOptions } from '@/hooks/useDict'
  148 +#end
133 #if ($hasDict == 1) 149 #if ($hasDict == 1)
134 import { DICT_TYPE } from '@/utils/constants' 150 import { DICT_TYPE } from '@/utils/constants'
135 #end 151 #end
@@ -159,7 +175,7 @@ definePage({ @@ -159,7 +175,7 @@ definePage({
159 const toast = useToast() 175 const toast = useToast()
160 const getTitle = computed(() => props.id ? '编辑${subTable.classComment}' : '新增${subTable.classComment}') 176 const getTitle = computed(() => props.id ? '编辑${subTable.classComment}' : '新增${subTable.classComment}')
161 const formLoading = ref(false) // 表单提交状态 177 const formLoading = ref(false) // 表单提交状态
162 -const formData = ref<${subSimpleClassName}>({ 178 +const formData = ref<Partial<${subSimpleClassName}>>({
163 #foreach ($column in $subColumns) 179 #foreach ($column in $subColumns)
164 #if ($column.primaryKey) 180 #if ($column.primaryKey)
165 ${column.javaField}: undefined, 181 ${column.javaField}: undefined,
@@ -167,7 +183,11 @@ const formData = ref&lt;${subSimpleClassName}&gt;({ @@ -167,7 +183,11 @@ const formData = ref&lt;${subSimpleClassName}&gt;({
167 ${column.javaField}: props.${column.javaField}, 183 ${column.javaField}: props.${column.javaField},
168 #elseif ($column.createOperation || $column.updateOperation) 184 #elseif ($column.createOperation || $column.updateOperation)
169 #set ($javaType = $column.javaType.toLowerCase()) 185 #set ($javaType = $column.javaType.toLowerCase())
170 - ${column.javaField}:#if($column.nullable) undefined#elseif($javaType == "long" || $javaType == "integer" || $javaType == "short" || $javaType == "double" || $javaType == "bigdecimal" || $javaType == "byte" || $javaType == "date" || $javaType == "localdate" || $javaType == "localdatetime") 0#elseif($javaType == "boolean") false#else ''#end, 186 +#if((($column.htmlType == "select" || $column.htmlType == "radio") && $column.dictType && "" != $column.dictType) || $javaType == "date" || $javaType == "localdate" || $javaType == "localdatetime")
  187 + ${column.javaField}: undefined,
  188 +#else
  189 + ${column.javaField}:#if($column.nullable) undefined#elseif($javaType == "long" || $javaType == "integer" || $javaType == "short" || $javaType == "float" || $javaType == "double" || $javaType == "bigdecimal" || $javaType == "byte") 0#elseif($javaType == "boolean") false#else ''#end,
  190 +#end
171 #end 191 #end
172 #end 192 #end
173 }) // 表单数据 193 }) // 表单数据
@@ -206,7 +226,10 @@ async function handleSubmit() { @@ -206,7 +226,10 @@ async function handleSubmit() {
206 } 226 }
207 formLoading.value = true 227 formLoading.value = true
208 try { 228 try {
209 - const data = { ...formData.value, ${subJoinColumn.javaField}: props.${subJoinColumn.javaField} ?? formData.value.${subJoinColumn.javaField} } 229 + const data = {
  230 + ...formData.value,
  231 + ${subJoinColumn.javaField}: props.${subJoinColumn.javaField} ?? formData.value.${subJoinColumn.javaField},
  232 + } as ${subSimpleClassName}
210 if (props.id) { 233 if (props.id) {
211 await update${subSimpleClassName}(data) 234 await update${subSimpleClassName}(data)
212 toast.success('修改成功') 235 toast.success('修改成功')
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/views/index_tree.vue.vm
@@ -61,7 +61,7 @@ @@ -61,7 +61,7 @@
61 61
62 <script lang="ts" setup> 62 <script lang="ts" setup>
63 #set ($primaryJavaType = $primaryColumn.javaType.toLowerCase()) 63 #set ($primaryJavaType = $primaryColumn.javaType.toLowerCase())
64 -#if(${primaryJavaType} == "long" || ${primaryJavaType} == "integer" || ${primaryJavaType} == "short" || ${primaryJavaType} == "double" || ${primaryJavaType} == "bigdecimal" || ${primaryJavaType} == "byte") 64 +#if(${primaryJavaType} == "long" || ${primaryJavaType} == "integer" || ${primaryJavaType} == "short" || ${primaryJavaType} == "float" || ${primaryJavaType} == "double" || ${primaryJavaType} == "bigdecimal" || ${primaryJavaType} == "byte")
65 #set ($primaryTsType = "number") 65 #set ($primaryTsType = "number")
66 #else 66 #else
67 #set ($primaryTsType = "string") 67 #set ($primaryTsType = "string")
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineUniappTest.java
1 package cn.iocoder.yudao.module.infra.service.codegen.inner; 1 package cn.iocoder.yudao.module.infra.service.codegen.inner;
2 2
  3 +import cn.iocoder.yudao.framework.common.exception.ServiceException;
3 import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO; 4 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.dal.dataobject.codegen.CodegenTableDO;
5 import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum; 6 import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;
@@ -8,6 +9,7 @@ import com.baomidou.mybatisplus.annotation.DbType; @@ -8,6 +9,7 @@ import com.baomidou.mybatisplus.annotation.DbType;
8 import org.junit.jupiter.api.Test; 9 import org.junit.jupiter.api.Test;
9 10
10 import java.util.Arrays; 11 import java.util.Arrays;
  12 +import java.util.HashMap;
11 import java.util.List; 13 import java.util.List;
12 import java.util.Map; 14 import java.util.Map;
13 import java.util.Objects; 15 import java.util.Objects;
@@ -83,6 +85,8 @@ public class CodegenEngineUniappTest extends CodegenEngineAbstractTest { @@ -83,6 +85,8 @@ public class CodegenEngineUniappTest extends CodegenEngineAbstractTest {
83 .setFrontType(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType()) 85 .setFrontType(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType())
84 .setTemplateType(CodegenTemplateTypeEnum.ONE.getType()); 86 .setTemplateType(CodegenTemplateTypeEnum.ONE.getType());
85 List<CodegenColumnDO> columns = getColumnList("student"); 87 List<CodegenColumnDO> columns = getColumnList("student");
  88 + columns.stream().filter(column -> "memo".equals(column.getJavaField())).findFirst().orElseThrow()
  89 + .setJavaType("Float").setHtmlType("input");
86 90
87 // 调用 91 // 调用
88 Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null); 92 Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);
@@ -90,7 +94,11 @@ public class CodegenEngineUniappTest extends CodegenEngineAbstractTest { @@ -90,7 +94,11 @@ public class CodegenEngineUniappTest extends CodegenEngineAbstractTest {
90 String api = result.get("yudao-ui-admin-uniapp/src/api/infra/demo/index.ts"); 94 String api = result.get("yudao-ui-admin-uniapp/src/api/infra/demo/index.ts");
91 assertTrue(api.contains("birthday: number")); 95 assertTrue(api.contains("birthday: number"));
92 assertTrue(api.contains("createTime?: number")); 96 assertTrue(api.contains("createTime?: number"));
  97 + assertTrue(api.contains("memo: number"));
93 assertFalse(api.contains("createTime?: Date")); 98 assertFalse(api.contains("createTime?: Date"));
  99 + String form = result.get("yudao-ui-admin-uniapp/src/pages-infra/demo/form/index.vue");
  100 + assertTrue(form.contains("v-model=\"formData.memo\""));
  101 + assertTrue(form.contains(":precision=\"2\""));
94 String detail = result.get("yudao-ui-admin-uniapp/src/pages-infra/demo/detail/index.vue"); 102 String detail = result.get("yudao-ui-admin-uniapp/src/pages-infra/demo/detail/index.vue");
95 assertTrue(detail.contains("type=\"danger\"")); 103 assertTrue(detail.contains("type=\"danger\""));
96 assertFalse(detail.contains("type=\"error\"")); 104 assertFalse(detail.contains("type=\"error\""));
@@ -150,7 +158,7 @@ public class CodegenEngineUniappTest extends CodegenEngineAbstractTest { @@ -150,7 +158,7 @@ public class CodegenEngineUniappTest extends CodegenEngineAbstractTest {
150 } 158 }
151 159
152 @Test 160 @Test
153 - public void testExecute_duplicateSubPath() { 161 + public void testExecute_duplicateErpSymbol() {
154 // 准备主表 162 // 准备主表
155 CodegenTableDO table = getTable("student") 163 CodegenTableDO table = getTable("student")
156 .setFrontType(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType()) 164 .setFrontType(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType())
@@ -169,13 +177,93 @@ public class CodegenEngineUniappTest extends CodegenEngineAbstractTest { @@ -169,13 +177,93 @@ public class CodegenEngineUniappTest extends CodegenEngineAbstractTest {
169 List<CodegenColumnDO> erpItemColumns = getColumnList("contact"); 177 List<CodegenColumnDO> erpItemColumns = getColumnList("contact");
170 178
171 // 调用 179 // 调用
172 - IllegalStateException exception = assertThrows(IllegalStateException.class, 180 + ServiceException exception = assertThrows(ServiceException.class,
173 () -> codegenEngine.execute(DbType.MYSQL, table, columns, 181 () -> codegenEngine.execute(DbType.MYSQL, table, columns,
174 Arrays.asList(crmItemTable, erpItemTable), Arrays.asList(crmItemColumns, erpItemColumns))); 182 Arrays.asList(crmItemTable, erpItemTable), Arrays.asList(crmItemColumns, erpItemColumns)));
175 // 断言 183 // 断言
176 - assertTrue(exception.getMessage().contains("生成文件路径重复"));  
177 - assertTrue(exception.getMessage().contains("subIndex=0"));  
178 - assertTrue(exception.getMessage().contains("subIndex=1")); 184 + assertTrue(exception.getMessage().contains("主子表规范化类名(OrderItem)重复"));
  185 + }
  186 +
  187 + @Test
  188 + public void testExecute_duplicateMasterSymbol() {
  189 + // 准备主表
  190 + CodegenTableDO table = getTable("student")
  191 + .setFrontType(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType())
  192 + .setTemplateType(CodegenTemplateTypeEnum.MASTER_NORMAL.getType());
  193 + List<CodegenColumnDO> columns = getColumnList("student");
  194 + // 准备两个规范化名称相同的子表
  195 + CodegenTableDO crmItemTable = getTable("contact").setModuleName("crm").setClassName("CrmOrderItem")
  196 + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType()).setSubJoinColumnId(100L).setSubJoinMany(true);
  197 + CodegenTableDO erpItemTable = getTable("contact").setModuleName("erp").setClassName("ErpOrderItem")
  198 + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType()).setSubJoinColumnId(100L).setSubJoinMany(true);
  199 + List<CodegenColumnDO> crmItemColumns = getColumnList("contact");
  200 + List<CodegenColumnDO> erpItemColumns = getColumnList("contact");
  201 +
  202 + // 调用并断言
  203 + ServiceException exception = assertThrows(ServiceException.class,
  204 + () -> codegenEngine.execute(DbType.MYSQL, table, columns,
  205 + Arrays.asList(crmItemTable, erpItemTable), Arrays.asList(crmItemColumns, erpItemColumns)));
  206 + assertTrue(exception.getMessage().contains("主子表规范化类名(OrderItem)重复"));
  207 + }
  208 +
  209 + @Test
  210 + public void testExecute_duplicateMasterFieldNormal() {
  211 + testExecute_duplicateMasterField(CodegenTemplateTypeEnum.MASTER_NORMAL);
  212 + }
  213 +
  214 + @Test
  215 + public void testExecute_duplicateMasterFieldInner() {
  216 + testExecute_duplicateMasterField(CodegenTemplateTypeEnum.MASTER_INNER);
  217 + }
  218 +
  219 + @Test
  220 + public void testExecute_duplicateMasterFieldWithMainNormal() {
  221 + testExecute_duplicateMasterFieldWithMain(CodegenTemplateTypeEnum.MASTER_NORMAL);
  222 + }
  223 +
  224 + @Test
  225 + public void testExecute_duplicateMasterFieldWithMainInner() {
  226 + testExecute_duplicateMasterFieldWithMain(CodegenTemplateTypeEnum.MASTER_INNER);
  227 + }
  228 +
  229 + @Test
  230 + public void testExecute_duplicateMasterFieldErp() {
  231 + // 准备主表
  232 + CodegenTableDO table = getTable("student")
  233 + .setFrontType(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType())
  234 + .setTemplateType(CodegenTemplateTypeEnum.MASTER_ERP.getType());
  235 + List<CodegenColumnDO> columns = getColumnList("student");
  236 + // 准备属性名同为 items 的子表:Item 一对多、Items 一对一
  237 + CodegenTableDO crmItemTable = getTable("contact").setModuleName("crm").setClassName("CrmItem")
  238 + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType()).setSubJoinColumnId(100L).setSubJoinMany(true);
  239 + CodegenTableDO erpItemsTable = getTable("contact").setModuleName("erp").setClassName("ErpItems")
  240 + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType()).setSubJoinColumnId(100L).setSubJoinMany(false);
  241 +
  242 + // 调用
  243 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns,
  244 + Arrays.asList(crmItemTable, erpItemsTable),
  245 + Arrays.asList(getColumnList("contact"), getColumnList("contact")));
  246 + // 断言
  247 + assertTrue(result.containsKey("yudao-ui-admin-uniapp/src/pages-infra/demo/item/form/index.vue"));
  248 + assertTrue(result.containsKey("yudao-ui-admin-uniapp/src/pages-infra/demo/items/form/index.vue"));
  249 + }
  250 +
  251 + @Test
  252 + public void testRegisterGeneratedSource_duplicatePath() {
  253 + // 准备参数
  254 + Map<String, String> generatedSources = new HashMap<>();
  255 + Map<String, Object> bindingMap = new HashMap<>();
  256 + bindingMap.put("subIndex", 0);
  257 + CodegenEngine.registerGeneratedSource(generatedSources, "same.vue", "form_sub.vm", bindingMap);
  258 + bindingMap.put("subIndex", 1);
  259 +
  260 + // 调用
  261 + IllegalStateException exception = assertThrows(IllegalStateException.class,
  262 + () -> CodegenEngine.registerGeneratedSource(generatedSources, "same.vue", "form_sub.vm", bindingMap));
  263 + // 断言
  264 + assertTrue(exception.getMessage().contains("生成文件路径重复:same.vue"));
  265 + assertTrue(exception.getMessage().contains("form_sub.vm[subIndex=0]"));
  266 + assertTrue(exception.getMessage().contains("form_sub.vm[subIndex=1]"));
179 } 267 }
180 268
181 @Test 269 @Test
@@ -193,6 +281,46 @@ public class CodegenEngineUniappTest extends CodegenEngineAbstractTest { @@ -193,6 +281,46 @@ public class CodegenEngineUniappTest extends CodegenEngineAbstractTest {
193 testExecute_master(CodegenTemplateTypeEnum.MASTER_ERP, "/vue3_admin_uniapp_master_erp"); 281 testExecute_master(CodegenTemplateTypeEnum.MASTER_ERP, "/vue3_admin_uniapp_master_erp");
194 } 282 }
195 283
  284 + private void testExecute_duplicateMasterField(CodegenTemplateTypeEnum templateType) {
  285 + // 准备主表
  286 + CodegenTableDO table = getTable("student")
  287 + .setFrontType(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType())
  288 + .setTemplateType(templateType.getType());
  289 + List<CodegenColumnDO> columns = getColumnList("student");
  290 + // 准备属性名同为 items 的子表:Item 一对多、Items 一对一
  291 + CodegenTableDO crmItemTable = getTable("contact").setModuleName("crm").setClassName("CrmItem")
  292 + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType()).setSubJoinColumnId(100L).setSubJoinMany(true);
  293 + CodegenTableDO erpItemsTable = getTable("contact").setModuleName("erp").setClassName("ErpItems")
  294 + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType()).setSubJoinColumnId(100L).setSubJoinMany(false);
  295 + List<CodegenColumnDO> crmItemColumns = getColumnList("contact");
  296 + List<CodegenColumnDO> erpItemsColumns = getColumnList("contact");
  297 +
  298 + // 调用并断言
  299 + ServiceException exception = assertThrows(ServiceException.class,
  300 + () -> codegenEngine.execute(DbType.MYSQL, table, columns,
  301 + Arrays.asList(crmItemTable, erpItemsTable), Arrays.asList(crmItemColumns, erpItemsColumns)));
  302 + assertTrue(exception.getMessage().contains("主子表属性名(items)重复"));
  303 + }
  304 +
  305 + private void testExecute_duplicateMasterFieldWithMain(CodegenTemplateTypeEnum templateType) {
  306 + // 准备主表
  307 + CodegenTableDO table = getTable("student")
  308 + .setFrontType(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType())
  309 + .setTemplateType(templateType.getType());
  310 + List<CodegenColumnDO> columns = getColumnList("student");
  311 + columns.stream().filter(column -> "memo".equals(column.getJavaField())).findFirst().orElseThrow()
  312 + .setJavaField("items");
  313 + // 准备一对多子表 Item,生成属性名 items
  314 + CodegenTableDO itemTable = getTable("contact").setModuleName("crm").setClassName("CrmItem")
  315 + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType()).setSubJoinColumnId(100L).setSubJoinMany(true);
  316 +
  317 + // 调用并断言
  318 + ServiceException exception = assertThrows(ServiceException.class,
  319 + () -> codegenEngine.execute(DbType.MYSQL, table, columns,
  320 + List.of(itemTable), List.of(getColumnList("contact"))));
  321 + assertTrue(exception.getMessage().contains("主子表属性名(items)重复"));
  322 + }
  323 +
196 private void testExecute_master(CodegenTemplateTypeEnum templateType, String path) { 324 private void testExecute_master(CodegenTemplateTypeEnum templateType, String path) {
197 // 准备主表 325 // 准备主表
198 CodegenTableDO table = getTable("student") 326 CodegenTableDO table = getTable("student")
@@ -221,13 +349,16 @@ public class CodegenEngineUniappTest extends CodegenEngineAbstractTest { @@ -221,13 +349,16 @@ public class CodegenEngineUniappTest extends CodegenEngineAbstractTest {
221 String api = result.get("yudao-ui-admin-uniapp/src/api/infra/demo/index.ts"); 349 String api = result.get("yudao-ui-admin-uniapp/src/api/infra/demo/index.ts");
222 assertTrue(api.contains("studentTeacher?: StudentTeacher | null")); 350 assertTrue(api.contains("studentTeacher?: StudentTeacher | null"));
223 String form = result.get("yudao-ui-admin-uniapp/src/pages-infra/demo/form/index.vue"); 351 String form = result.get("yudao-ui-admin-uniapp/src/pages-infra/demo/form/index.vue");
224 - assertTrue(form.contains("ref<StudentTeacher | null>(null)")); 352 + assertTrue(form.contains("ref<Partial<StudentTeacher> | null>(null)"));
225 assertTrue(form.contains("studentTeacher.value = studentTeacherData || null")); 353 assertTrue(form.contains("studentTeacher.value = studentTeacherData || null"));
226 assertTrue(form.contains(":model-value=\"item.birthday || Date.now()\"")); 354 assertTrue(form.contains(":model-value=\"item.birthday || Date.now()\""));
227 assertTrue(form.contains("v-model=\"item.sex\"")); 355 assertTrue(form.contains("v-model=\"item.sex\""));
228 assertTrue(form.contains("<yd-upload-img v-model=\"item.avatar\"")); 356 assertTrue(form.contains("<yd-upload-img v-model=\"item.avatar\""));
229 assertTrue(form.contains("<yd-upload-file v-model=\"item.video\"")); 357 assertTrue(form.contains("<yd-upload-file v-model=\"item.video\""));
230 - assertTrue(form.contains("<wd-textarea v-model=\"item.description\"")); 358 + assertTrue(form.contains("<yd-upload-file v-model=\"item.video\" :limit=\"1\""));
  359 + assertTrue(form.contains("v-model=\"item.description\""));
  360 + assertTrue(form.contains("v-model=\"item.memo\""));
  361 + assertTrue(form.contains("birthday: undefined"));
231 assertTrue(form.contains("function validateSubTables()")); 362 assertTrue(form.contains("function validateSubTables()"));
232 } else { 363 } else {
233 String contactForm = result.get( 364 String contactForm = result.get(
@@ -236,7 +367,11 @@ public class CodegenEngineUniappTest extends CodegenEngineAbstractTest { @@ -236,7 +367,11 @@ public class CodegenEngineUniappTest extends CodegenEngineAbstractTest {
236 assertTrue(contactForm.contains("v-model=\"formData.sex\"")); 367 assertTrue(contactForm.contains("v-model=\"formData.sex\""));
237 assertTrue(contactForm.contains("<yd-upload-img v-model=\"formData.avatar\"")); 368 assertTrue(contactForm.contains("<yd-upload-img v-model=\"formData.avatar\""));
238 assertTrue(contactForm.contains("<yd-upload-file v-model=\"formData.video\"")); 369 assertTrue(contactForm.contains("<yd-upload-file v-model=\"formData.video\""));
239 - assertTrue(contactForm.contains("<wd-textarea v-model=\"formData.description\"")); 370 + assertTrue(contactForm.contains("<yd-upload-file v-model=\"formData.video\" :limit=\"1\""));
  371 + assertTrue(contactForm.contains("v-model=\"formData.description\""));
  372 + assertTrue(contactForm.contains("v-model=\"formData.memo\""));
  373 + assertTrue(contactForm.contains("birthday: undefined"));
  374 + assertTrue(contactForm.contains(":columns=\"getBoolDictOptions"));
240 } 375 }
241 } 376 }
242 377
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_erp/vue/form/index
@@ -65,13 +65,15 @@ @@ -65,13 +65,15 @@
65 <yd-upload-img v-model="formData.avatar" directory="infra/demo" /> 65 <yd-upload-img v-model="formData.avatar" directory="infra/demo" />
66 </wd-form-item> 66 </wd-form-item>
67 <wd-form-item title="附件" title-width="180rpx" prop="video"> 67 <wd-form-item title="附件" title-width="180rpx" prop="video">
68 - <yd-upload-file v-model="formData.video" directory="infra/demo" /> 68 + <yd-upload-file v-model="formData.video" :limit="1" directory="infra/demo" />
69 </wd-form-item> 69 </wd-form-item>
70 <wd-form-item title="备注" title-width="180rpx" prop="memo"> 70 <wd-form-item title="备注" title-width="180rpx" prop="memo">
71 - <wd-input 71 + <wd-textarea
72 v-model="formData.memo" 72 v-model="formData.memo"
73 clearable 73 clearable
74 placeholder="请输入备注" 74 placeholder="请输入备注"
  75 + :maxlength="2000"
  76 + show-word-limit
75 /> 77 />
76 </wd-form-item> 78 </wd-form-item>
77 </wd-cell-group> 79 </wd-cell-group>
@@ -120,13 +122,13 @@ definePage({ @@ -120,13 +122,13 @@ definePage({
120 const toast = useToast() 122 const toast = useToast()
121 const getTitle = computed(() => props.id ? '编辑学生' : '新增学生') 123 const getTitle = computed(() => props.id ? '编辑学生' : '新增学生')
122 const formLoading = ref(false) // 表单提交状态 124 const formLoading = ref(false) // 表单提交状态
123 -const formData = ref<Student>({ 125 +const formData = ref<Partial<Student>>({
124 id: undefined, 126 id: undefined,
125 name: '', 127 name: '',
126 description: '', 128 description: '',
127 - birthday: 0,  
128 - sex: 0,  
129 - enabled: false, 129 + birthday: undefined,
  130 + sex: undefined,
  131 + enabled: undefined,
130 avatar: '', 132 avatar: '',
131 video: '', 133 video: '',
132 memo: '', 134 memo: '',
@@ -166,7 +168,7 @@ async function handleSubmit() { @@ -166,7 +168,7 @@ async function handleSubmit() {
166 168
167 formLoading.value = true 169 formLoading.value = true
168 try { 170 try {
169 - const data = formData.value 171 + const data = formData.value as Student
170 if (props.id) { 172 if (props.id) {
171 await updateStudent(data) 173 await updateStudent(data)
172 toast.success('修改成功') 174 toast.success('修改成功')
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_erp/vue/student-contact/form/index
@@ -10,7 +10,12 @@ @@ -10,7 +10,12 @@
10 <wd-input v-model="formData.name" clearable placeholder="请输入名字" /> 10 <wd-input v-model="formData.name" clearable placeholder="请输入名字" />
11 </wd-form-item> 11 </wd-form-item>
12 <wd-form-item title="简介" title-width="180rpx" prop="description"> 12 <wd-form-item title="简介" title-width="180rpx" prop="description">
13 - <wd-textarea v-model="formData.description" clearable placeholder="请输入简介" /> 13 + <wd-textarea
  14 + v-model="formData.description"
  15 + clearable
  16 + placeholder="请输入简介"
  17 + :maxlength="200"
  18 + />
14 </wd-form-item> 19 </wd-form-item>
15 <wd-form-item 20 <wd-form-item
16 title="出生日期" 21 title="出生日期"
@@ -40,17 +45,22 @@ @@ -40,17 +45,22 @@
40 label="是否有效" 45 label="是否有效"
41 label-width="180rpx" 46 label-width="180rpx"
42 prop="enabled" 47 prop="enabled"
43 - :dict-type="DICT_TYPE.INFRA_BOOLEAN_STRING" 48 + :columns="getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
44 placeholder="请选择是否有效" 49 placeholder="请选择是否有效"
45 /> 50 />
46 <wd-form-item title="头像" title-width="180rpx" prop="avatar"> 51 <wd-form-item title="头像" title-width="180rpx" prop="avatar">
47 <yd-upload-img v-model="formData.avatar" directory="infra/demo" /> 52 <yd-upload-img v-model="formData.avatar" directory="infra/demo" />
48 </wd-form-item> 53 </wd-form-item>
49 <wd-form-item title="附件" title-width="180rpx" prop="video"> 54 <wd-form-item title="附件" title-width="180rpx" prop="video">
50 - <yd-upload-file v-model="formData.video" directory="infra/demo" /> 55 + <yd-upload-file v-model="formData.video" :limit="1" directory="infra/demo" />
51 </wd-form-item> 56 </wd-form-item>
52 <wd-form-item title="备注" title-width="180rpx" prop="memo"> 57 <wd-form-item title="备注" title-width="180rpx" prop="memo">
53 - <wd-input v-model="formData.memo" clearable placeholder="请输入备注" /> 58 + <wd-textarea
  59 + v-model="formData.memo"
  60 + clearable
  61 + placeholder="请输入备注"
  62 + :maxlength="2000"
  63 + />
54 </wd-form-item> 64 </wd-form-item>
55 </wd-cell-group> 65 </wd-cell-group>
56 </wd-form> 66 </wd-form>
@@ -71,6 +81,7 @@ import { computed, onMounted, ref } from &#39;vue&#39; @@ -71,6 +81,7 @@ import { computed, onMounted, ref } from &#39;vue&#39;
71 import { useToast } from '@wot-ui/ui/components/wd-toast' 81 import { useToast } from '@wot-ui/ui/components/wd-toast'
72 import { createStudentContact, getStudentContact, updateStudentContact } from '@/api/infra/demo' 82 import { createStudentContact, getStudentContact, updateStudentContact } from '@/api/infra/demo'
73 import { delay, navigateBackPlus } from '@/utils' 83 import { delay, navigateBackPlus } from '@/utils'
  84 +import { getBoolDictOptions } from '@/hooks/useDict'
74 import { DICT_TYPE } from '@/utils/constants' 85 import { DICT_TYPE } from '@/utils/constants'
75 import { formatDateTime } from '@/utils/date' 86 import { formatDateTime } from '@/utils/date'
76 import { createFormSchema } from '@/utils/wot' 87 import { createFormSchema } from '@/utils/wot'
@@ -87,14 +98,14 @@ definePage({ @@ -87,14 +98,14 @@ definePage({
87 const toast = useToast() 98 const toast = useToast()
88 const getTitle = computed(() => props.id ? '编辑学生联系人' : '新增学生联系人') 99 const getTitle = computed(() => props.id ? '编辑学生联系人' : '新增学生联系人')
89 const formLoading = ref(false) // 表单提交状态 100 const formLoading = ref(false) // 表单提交状态
90 -const formData = ref<StudentContact>({ 101 +const formData = ref<Partial<StudentContact>>({
91 id: undefined, 102 id: undefined,
92 studentId: props.studentId, 103 studentId: props.studentId,
93 name: '', 104 name: '',
94 description: '', 105 description: '',
95 - birthday: 0,  
96 - sex: 0,  
97 - enabled: false, 106 + birthday: undefined,
  107 + sex: undefined,
  108 + enabled: undefined,
98 avatar: '', 109 avatar: '',
99 video: undefined, 110 video: undefined,
100 memo: '', 111 memo: '',
@@ -132,7 +143,10 @@ async function handleSubmit() { @@ -132,7 +143,10 @@ async function handleSubmit() {
132 } 143 }
133 formLoading.value = true 144 formLoading.value = true
134 try { 145 try {
135 - const data = { ...formData.value, studentId: props.studentId ?? formData.value.studentId } 146 + const data = {
  147 + ...formData.value,
  148 + studentId: props.studentId ?? formData.value.studentId,
  149 + } as StudentContact
136 if (props.id) { 150 if (props.id) {
137 await updateStudentContact(data) 151 await updateStudentContact(data)
138 toast.success('修改成功') 152 toast.success('修改成功')
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_erp/vue/student-teacher/form/index
@@ -10,7 +10,12 @@ @@ -10,7 +10,12 @@
10 <wd-input v-model="formData.name" clearable placeholder="请输入名字" /> 10 <wd-input v-model="formData.name" clearable placeholder="请输入名字" />
11 </wd-form-item> 11 </wd-form-item>
12 <wd-form-item title="简介" title-width="180rpx" prop="description"> 12 <wd-form-item title="简介" title-width="180rpx" prop="description">
13 - <wd-textarea v-model="formData.description" clearable placeholder="请输入简介" /> 13 + <wd-textarea
  14 + v-model="formData.description"
  15 + clearable
  16 + placeholder="请输入简介"
  17 + :maxlength="200"
  18 + />
14 </wd-form-item> 19 </wd-form-item>
15 <wd-form-item 20 <wd-form-item
16 title="出生日期" 21 title="出生日期"
@@ -40,17 +45,22 @@ @@ -40,17 +45,22 @@
40 label="是否有效" 45 label="是否有效"
41 label-width="180rpx" 46 label-width="180rpx"
42 prop="enabled" 47 prop="enabled"
43 - :dict-type="DICT_TYPE.INFRA_BOOLEAN_STRING" 48 + :columns="getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
44 placeholder="请选择是否有效" 49 placeholder="请选择是否有效"
45 /> 50 />
46 <wd-form-item title="头像" title-width="180rpx" prop="avatar"> 51 <wd-form-item title="头像" title-width="180rpx" prop="avatar">
47 <yd-upload-img v-model="formData.avatar" directory="infra/demo" /> 52 <yd-upload-img v-model="formData.avatar" directory="infra/demo" />
48 </wd-form-item> 53 </wd-form-item>
49 <wd-form-item title="附件" title-width="180rpx" prop="video"> 54 <wd-form-item title="附件" title-width="180rpx" prop="video">
50 - <yd-upload-file v-model="formData.video" directory="infra/demo" /> 55 + <yd-upload-file v-model="formData.video" :limit="1" directory="infra/demo" />
51 </wd-form-item> 56 </wd-form-item>
52 <wd-form-item title="备注" title-width="180rpx" prop="memo"> 57 <wd-form-item title="备注" title-width="180rpx" prop="memo">
53 - <wd-input v-model="formData.memo" clearable placeholder="请输入备注" /> 58 + <wd-textarea
  59 + v-model="formData.memo"
  60 + clearable
  61 + placeholder="请输入备注"
  62 + :maxlength="2000"
  63 + />
54 </wd-form-item> 64 </wd-form-item>
55 </wd-cell-group> 65 </wd-cell-group>
56 </wd-form> 66 </wd-form>
@@ -71,6 +81,7 @@ import { computed, onMounted, ref } from &#39;vue&#39; @@ -71,6 +81,7 @@ import { computed, onMounted, ref } from &#39;vue&#39;
71 import { useToast } from '@wot-ui/ui/components/wd-toast' 81 import { useToast } from '@wot-ui/ui/components/wd-toast'
72 import { createStudentTeacher, getStudentTeacher, updateStudentTeacher } from '@/api/infra/demo' 82 import { createStudentTeacher, getStudentTeacher, updateStudentTeacher } from '@/api/infra/demo'
73 import { delay, navigateBackPlus } from '@/utils' 83 import { delay, navigateBackPlus } from '@/utils'
  84 +import { getBoolDictOptions } from '@/hooks/useDict'
74 import { DICT_TYPE } from '@/utils/constants' 85 import { DICT_TYPE } from '@/utils/constants'
75 import { formatDateTime } from '@/utils/date' 86 import { formatDateTime } from '@/utils/date'
76 import { createFormSchema } from '@/utils/wot' 87 import { createFormSchema } from '@/utils/wot'
@@ -87,14 +98,14 @@ definePage({ @@ -87,14 +98,14 @@ definePage({
87 const toast = useToast() 98 const toast = useToast()
88 const getTitle = computed(() => props.id ? '编辑学生班主任' : '新增学生班主任') 99 const getTitle = computed(() => props.id ? '编辑学生班主任' : '新增学生班主任')
89 const formLoading = ref(false) // 表单提交状态 100 const formLoading = ref(false) // 表单提交状态
90 -const formData = ref<StudentTeacher>({ 101 +const formData = ref<Partial<StudentTeacher>>({
91 id: undefined, 102 id: undefined,
92 studentId: props.studentId, 103 studentId: props.studentId,
93 name: '', 104 name: '',
94 description: '', 105 description: '',
95 - birthday: 0,  
96 - sex: 0,  
97 - enabled: false, 106 + birthday: undefined,
  107 + sex: undefined,
  108 + enabled: undefined,
98 avatar: '', 109 avatar: '',
99 video: undefined, 110 video: undefined,
100 memo: '', 111 memo: '',
@@ -132,7 +143,10 @@ async function handleSubmit() { @@ -132,7 +143,10 @@ async function handleSubmit() {
132 } 143 }
133 formLoading.value = true 144 formLoading.value = true
134 try { 145 try {
135 - const data = { ...formData.value, studentId: props.studentId ?? formData.value.studentId } 146 + const data = {
  147 + ...formData.value,
  148 + studentId: props.studentId ?? formData.value.studentId,
  149 + } as StudentTeacher
136 if (props.id) { 150 if (props.id) {
137 await updateStudentTeacher(data) 151 await updateStudentTeacher(data)
138 toast.success('修改成功') 152 toast.success('修改成功')
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_inner/vue/form/index
@@ -65,13 +65,15 @@ @@ -65,13 +65,15 @@
65 <yd-upload-img v-model="formData.avatar" directory="infra/demo" /> 65 <yd-upload-img v-model="formData.avatar" directory="infra/demo" />
66 </wd-form-item> 66 </wd-form-item>
67 <wd-form-item title="附件" title-width="180rpx" prop="video"> 67 <wd-form-item title="附件" title-width="180rpx" prop="video">
68 - <yd-upload-file v-model="formData.video" directory="infra/demo" /> 68 + <yd-upload-file v-model="formData.video" :limit="1" directory="infra/demo" />
69 </wd-form-item> 69 </wd-form-item>
70 <wd-form-item title="备注" title-width="180rpx" prop="memo"> 70 <wd-form-item title="备注" title-width="180rpx" prop="memo">
71 - <wd-input 71 + <wd-textarea
72 v-model="formData.memo" 72 v-model="formData.memo"
73 clearable 73 clearable
74 placeholder="请输入备注" 74 placeholder="请输入备注"
  75 + :maxlength="2000"
  76 + show-word-limit
75 /> 77 />
76 </wd-form-item> 78 </wd-form-item>
77 </wd-cell-group> 79 </wd-cell-group>
@@ -102,7 +104,12 @@ @@ -102,7 +104,12 @@
102 <wd-input v-model="item.name" clearable placeholder="请输入名字" /> 104 <wd-input v-model="item.name" clearable placeholder="请输入名字" />
103 </wd-form-item> 105 </wd-form-item>
104 <wd-form-item title="简介" title-width="180rpx"> 106 <wd-form-item title="简介" title-width="180rpx">
105 - <wd-textarea v-model="item.description" clearable placeholder="请输入简介" /> 107 + <wd-textarea
  108 + v-model="item.description"
  109 + clearable
  110 + placeholder="请输入简介"
  111 + :maxlength="200"
  112 + />
106 </wd-form-item> 113 </wd-form-item>
107 <wd-form-item 114 <wd-form-item
108 title="出生日期" 115 title="出生日期"
@@ -140,10 +147,15 @@ @@ -140,10 +147,15 @@
140 <yd-upload-img v-model="item.avatar" directory="infra/demo" /> 147 <yd-upload-img v-model="item.avatar" directory="infra/demo" />
141 </wd-form-item> 148 </wd-form-item>
142 <wd-form-item title="附件" title-width="180rpx"> 149 <wd-form-item title="附件" title-width="180rpx">
143 - <yd-upload-file v-model="item.video" directory="infra/demo" /> 150 + <yd-upload-file v-model="item.video" :limit="1" directory="infra/demo" />
144 </wd-form-item> 151 </wd-form-item>
145 <wd-form-item title="备注" title-width="180rpx"> 152 <wd-form-item title="备注" title-width="180rpx">
146 - <wd-input v-model="item.memo" clearable placeholder="请输入备注" /> 153 + <wd-textarea
  154 + v-model="item.memo"
  155 + clearable
  156 + placeholder="请输入备注"
  157 + :maxlength="2000"
  158 + />
147 </wd-form-item> 159 </wd-form-item>
148 </view> 160 </view>
149 </view> 161 </view>
@@ -170,7 +182,12 @@ @@ -170,7 +182,12 @@
170 <wd-input v-model="studentTeacher.name" clearable placeholder="请输入名字" /> 182 <wd-input v-model="studentTeacher.name" clearable placeholder="请输入名字" />
171 </wd-form-item> 183 </wd-form-item>
172 <wd-form-item title="简介" title-width="180rpx"> 184 <wd-form-item title="简介" title-width="180rpx">
173 - <wd-textarea v-model="studentTeacher.description" clearable placeholder="请输入简介" /> 185 + <wd-textarea
  186 + v-model="studentTeacher.description"
  187 + clearable
  188 + placeholder="请输入简介"
  189 + :maxlength="200"
  190 + />
174 </wd-form-item> 191 </wd-form-item>
175 <wd-form-item 192 <wd-form-item
176 title="出生日期" 193 title="出生日期"
@@ -208,10 +225,15 @@ @@ -208,10 +225,15 @@
208 <yd-upload-img v-model="studentTeacher.avatar" directory="infra/demo" /> 225 <yd-upload-img v-model="studentTeacher.avatar" directory="infra/demo" />
209 </wd-form-item> 226 </wd-form-item>
210 <wd-form-item title="附件" title-width="180rpx"> 227 <wd-form-item title="附件" title-width="180rpx">
211 - <yd-upload-file v-model="studentTeacher.video" directory="infra/demo" /> 228 + <yd-upload-file v-model="studentTeacher.video" :limit="1" directory="infra/demo" />
212 </wd-form-item> 229 </wd-form-item>
213 <wd-form-item title="备注" title-width="180rpx"> 230 <wd-form-item title="备注" title-width="180rpx">
214 - <wd-input v-model="studentTeacher.memo" clearable placeholder="请输入备注" /> 231 + <wd-textarea
  232 + v-model="studentTeacher.memo"
  233 + clearable
  234 + placeholder="请输入备注"
  235 + :maxlength="2000"
  236 + />
215 </wd-form-item> 237 </wd-form-item>
216 </view> 238 </view>
217 </view> 239 </view>
@@ -267,19 +289,19 @@ definePage({ @@ -267,19 +289,19 @@ definePage({
267 const toast = useToast() 289 const toast = useToast()
268 const getTitle = computed(() => props.id ? '编辑学生' : '新增学生') 290 const getTitle = computed(() => props.id ? '编辑学生' : '新增学生')
269 const formLoading = ref(false) // 表单提交状态 291 const formLoading = ref(false) // 表单提交状态
270 -const formData = ref<Student>({ 292 +const formData = ref<Partial<Student>>({
271 id: undefined, 293 id: undefined,
272 name: '', 294 name: '',
273 description: '', 295 description: '',
274 - birthday: 0,  
275 - sex: 0,  
276 - enabled: false, 296 + birthday: undefined,
  297 + sex: undefined,
  298 + enabled: undefined,
277 avatar: '', 299 avatar: '',
278 video: '', 300 video: '',
279 memo: '', 301 memo: '',
280 }) // 表单数据 302 }) // 表单数据
281 -const studentContacts = ref<StudentContact[]>([]) // 学生联系人数据  
282 -const studentTeacher = ref<StudentTeacher | null>(null) // 学生班主任数据 303 +const studentContacts = ref<Partial<StudentContact>[]>([]) // 学生联系人数据
  304 +const studentTeacher = ref<Partial<StudentTeacher> | null>(null) // 学生班主任数据
283 const birthdayVisible = ref(false) // 出生日期选择器状态 305 const birthdayVisible = ref(false) // 出生日期选择器状态
284 const subDateVisible = ref<Record<string, boolean>>({}) // 子表日期选择器状态 306 const subDateVisible = ref<Record<string, boolean>>({}) // 子表日期选择器状态
285 const formSchema = createFormSchema({ 307 const formSchema = createFormSchema({
@@ -299,9 +321,9 @@ function addStudentContact() { @@ -299,9 +321,9 @@ function addStudentContact() {
299 studentContacts.value.push({ 321 studentContacts.value.push({
300 name: '', 322 name: '',
301 description: '', 323 description: '',
302 - birthday: 0,  
303 - sex: 0,  
304 - enabled: false, 324 + birthday: undefined,
  325 + sex: undefined,
  326 + enabled: undefined,
305 avatar: '', 327 avatar: '',
306 video: undefined, 328 video: undefined,
307 memo: '', 329 memo: '',
@@ -318,9 +340,9 @@ function addStudentTeacher() { @@ -318,9 +340,9 @@ function addStudentTeacher() {
318 studentTeacher.value = { 340 studentTeacher.value = {
319 name: '', 341 name: '',
320 description: '', 342 description: '',
321 - birthday: 0,  
322 - sex: 0,  
323 - enabled: false, 343 + birthday: undefined,
  344 + sex: undefined,
  345 + enabled: undefined,
324 avatar: '', 346 avatar: '',
325 video: undefined, 347 video: undefined,
326 memo: '', 348 memo: '',
@@ -429,11 +451,11 @@ async function handleSubmit() { @@ -429,11 +451,11 @@ async function handleSubmit() {
429 451
430 formLoading.value = true 452 formLoading.value = true
431 try { 453 try {
432 - const data: Student = { 454 + const data = {
433 ...formData.value, 455 ...formData.value,
434 studentContacts: studentContacts.value, 456 studentContacts: studentContacts.value,
435 studentTeacher: studentTeacher.value, 457 studentTeacher: studentTeacher.value,
436 - } 458 + } as Student
437 if (props.id) { 459 if (props.id) {
438 await updateStudent(data) 460 await updateStudent(data)
439 toast.success('修改成功') 461 toast.success('修改成功')
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_normal/vue/form/index
@@ -65,13 +65,15 @@ @@ -65,13 +65,15 @@
65 <yd-upload-img v-model="formData.avatar" directory="infra/demo" /> 65 <yd-upload-img v-model="formData.avatar" directory="infra/demo" />
66 </wd-form-item> 66 </wd-form-item>
67 <wd-form-item title="附件" title-width="180rpx" prop="video"> 67 <wd-form-item title="附件" title-width="180rpx" prop="video">
68 - <yd-upload-file v-model="formData.video" directory="infra/demo" /> 68 + <yd-upload-file v-model="formData.video" :limit="1" directory="infra/demo" />
69 </wd-form-item> 69 </wd-form-item>
70 <wd-form-item title="备注" title-width="180rpx" prop="memo"> 70 <wd-form-item title="备注" title-width="180rpx" prop="memo">
71 - <wd-input 71 + <wd-textarea
72 v-model="formData.memo" 72 v-model="formData.memo"
73 clearable 73 clearable
74 placeholder="请输入备注" 74 placeholder="请输入备注"
  75 + :maxlength="2000"
  76 + show-word-limit
75 /> 77 />
76 </wd-form-item> 78 </wd-form-item>
77 </wd-cell-group> 79 </wd-cell-group>
@@ -102,7 +104,12 @@ @@ -102,7 +104,12 @@
102 <wd-input v-model="item.name" clearable placeholder="请输入名字" /> 104 <wd-input v-model="item.name" clearable placeholder="请输入名字" />
103 </wd-form-item> 105 </wd-form-item>
104 <wd-form-item title="简介" title-width="180rpx"> 106 <wd-form-item title="简介" title-width="180rpx">
105 - <wd-textarea v-model="item.description" clearable placeholder="请输入简介" /> 107 + <wd-textarea
  108 + v-model="item.description"
  109 + clearable
  110 + placeholder="请输入简介"
  111 + :maxlength="200"
  112 + />
106 </wd-form-item> 113 </wd-form-item>
107 <wd-form-item 114 <wd-form-item
108 title="出生日期" 115 title="出生日期"
@@ -140,10 +147,15 @@ @@ -140,10 +147,15 @@
140 <yd-upload-img v-model="item.avatar" directory="infra/demo" /> 147 <yd-upload-img v-model="item.avatar" directory="infra/demo" />
141 </wd-form-item> 148 </wd-form-item>
142 <wd-form-item title="附件" title-width="180rpx"> 149 <wd-form-item title="附件" title-width="180rpx">
143 - <yd-upload-file v-model="item.video" directory="infra/demo" /> 150 + <yd-upload-file v-model="item.video" :limit="1" directory="infra/demo" />
144 </wd-form-item> 151 </wd-form-item>
145 <wd-form-item title="备注" title-width="180rpx"> 152 <wd-form-item title="备注" title-width="180rpx">
146 - <wd-input v-model="item.memo" clearable placeholder="请输入备注" /> 153 + <wd-textarea
  154 + v-model="item.memo"
  155 + clearable
  156 + placeholder="请输入备注"
  157 + :maxlength="2000"
  158 + />
147 </wd-form-item> 159 </wd-form-item>
148 </view> 160 </view>
149 </view> 161 </view>
@@ -170,7 +182,12 @@ @@ -170,7 +182,12 @@
170 <wd-input v-model="studentTeacher.name" clearable placeholder="请输入名字" /> 182 <wd-input v-model="studentTeacher.name" clearable placeholder="请输入名字" />
171 </wd-form-item> 183 </wd-form-item>
172 <wd-form-item title="简介" title-width="180rpx"> 184 <wd-form-item title="简介" title-width="180rpx">
173 - <wd-textarea v-model="studentTeacher.description" clearable placeholder="请输入简介" /> 185 + <wd-textarea
  186 + v-model="studentTeacher.description"
  187 + clearable
  188 + placeholder="请输入简介"
  189 + :maxlength="200"
  190 + />
174 </wd-form-item> 191 </wd-form-item>
175 <wd-form-item 192 <wd-form-item
176 title="出生日期" 193 title="出生日期"
@@ -208,10 +225,15 @@ @@ -208,10 +225,15 @@
208 <yd-upload-img v-model="studentTeacher.avatar" directory="infra/demo" /> 225 <yd-upload-img v-model="studentTeacher.avatar" directory="infra/demo" />
209 </wd-form-item> 226 </wd-form-item>
210 <wd-form-item title="附件" title-width="180rpx"> 227 <wd-form-item title="附件" title-width="180rpx">
211 - <yd-upload-file v-model="studentTeacher.video" directory="infra/demo" /> 228 + <yd-upload-file v-model="studentTeacher.video" :limit="1" directory="infra/demo" />
212 </wd-form-item> 229 </wd-form-item>
213 <wd-form-item title="备注" title-width="180rpx"> 230 <wd-form-item title="备注" title-width="180rpx">
214 - <wd-input v-model="studentTeacher.memo" clearable placeholder="请输入备注" /> 231 + <wd-textarea
  232 + v-model="studentTeacher.memo"
  233 + clearable
  234 + placeholder="请输入备注"
  235 + :maxlength="2000"
  236 + />
215 </wd-form-item> 237 </wd-form-item>
216 </view> 238 </view>
217 </view> 239 </view>
@@ -267,19 +289,19 @@ definePage({ @@ -267,19 +289,19 @@ definePage({
267 const toast = useToast() 289 const toast = useToast()
268 const getTitle = computed(() => props.id ? '编辑学生' : '新增学生') 290 const getTitle = computed(() => props.id ? '编辑学生' : '新增学生')
269 const formLoading = ref(false) // 表单提交状态 291 const formLoading = ref(false) // 表单提交状态
270 -const formData = ref<Student>({ 292 +const formData = ref<Partial<Student>>({
271 id: undefined, 293 id: undefined,
272 name: '', 294 name: '',
273 description: '', 295 description: '',
274 - birthday: 0,  
275 - sex: 0,  
276 - enabled: false, 296 + birthday: undefined,
  297 + sex: undefined,
  298 + enabled: undefined,
277 avatar: '', 299 avatar: '',
278 video: '', 300 video: '',
279 memo: '', 301 memo: '',
280 }) // 表单数据 302 }) // 表单数据
281 -const studentContacts = ref<StudentContact[]>([]) // 学生联系人数据  
282 -const studentTeacher = ref<StudentTeacher | null>(null) // 学生班主任数据 303 +const studentContacts = ref<Partial<StudentContact>[]>([]) // 学生联系人数据
  304 +const studentTeacher = ref<Partial<StudentTeacher> | null>(null) // 学生班主任数据
283 const birthdayVisible = ref(false) // 出生日期选择器状态 305 const birthdayVisible = ref(false) // 出生日期选择器状态
284 const subDateVisible = ref<Record<string, boolean>>({}) // 子表日期选择器状态 306 const subDateVisible = ref<Record<string, boolean>>({}) // 子表日期选择器状态
285 const formSchema = createFormSchema({ 307 const formSchema = createFormSchema({
@@ -299,9 +321,9 @@ function addStudentContact() { @@ -299,9 +321,9 @@ function addStudentContact() {
299 studentContacts.value.push({ 321 studentContacts.value.push({
300 name: '', 322 name: '',
301 description: '', 323 description: '',
302 - birthday: 0,  
303 - sex: 0,  
304 - enabled: false, 324 + birthday: undefined,
  325 + sex: undefined,
  326 + enabled: undefined,
305 avatar: '', 327 avatar: '',
306 video: undefined, 328 video: undefined,
307 memo: '', 329 memo: '',
@@ -318,9 +340,9 @@ function addStudentTeacher() { @@ -318,9 +340,9 @@ function addStudentTeacher() {
318 studentTeacher.value = { 340 studentTeacher.value = {
319 name: '', 341 name: '',
320 description: '', 342 description: '',
321 - birthday: 0,  
322 - sex: 0,  
323 - enabled: false, 343 + birthday: undefined,
  344 + sex: undefined,
  345 + enabled: undefined,
324 avatar: '', 346 avatar: '',
325 video: undefined, 347 video: undefined,
326 memo: '', 348 memo: '',
@@ -429,11 +451,11 @@ async function handleSubmit() { @@ -429,11 +451,11 @@ async function handleSubmit() {
429 451
430 formLoading.value = true 452 formLoading.value = true
431 try { 453 try {
432 - const data: Student = { 454 + const data = {
433 ...formData.value, 455 ...formData.value,
434 studentContacts: studentContacts.value, 456 studentContacts: studentContacts.value,
435 studentTeacher: studentTeacher.value, 457 studentTeacher: studentTeacher.value,
436 - } 458 + } as Student
437 if (props.id) { 459 if (props.id) {
438 await updateStudent(data) 460 await updateStudent(data)
439 toast.success('修改成功') 461 toast.success('修改成功')
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/vue/form/index
@@ -65,13 +65,15 @@ @@ -65,13 +65,15 @@
65 <yd-upload-img v-model="formData.avatar" directory="infra/demo" /> 65 <yd-upload-img v-model="formData.avatar" directory="infra/demo" />
66 </wd-form-item> 66 </wd-form-item>
67 <wd-form-item title="附件" title-width="180rpx" prop="video"> 67 <wd-form-item title="附件" title-width="180rpx" prop="video">
68 - <yd-upload-file v-model="formData.video" directory="infra/demo" /> 68 + <yd-upload-file v-model="formData.video" :limit="1" directory="infra/demo" />
69 </wd-form-item> 69 </wd-form-item>
70 <wd-form-item title="备注" title-width="180rpx" prop="memo"> 70 <wd-form-item title="备注" title-width="180rpx" prop="memo">
71 - <wd-input 71 + <wd-textarea
72 v-model="formData.memo" 72 v-model="formData.memo"
73 clearable 73 clearable
74 placeholder="请输入备注" 74 placeholder="请输入备注"
  75 + :maxlength="2000"
  76 + show-word-limit
75 /> 77 />
76 </wd-form-item> 78 </wd-form-item>
77 </wd-cell-group> 79 </wd-cell-group>
@@ -120,13 +122,13 @@ definePage({ @@ -120,13 +122,13 @@ definePage({
120 const toast = useToast() 122 const toast = useToast()
121 const getTitle = computed(() => props.id ? '编辑学生' : '新增学生') 123 const getTitle = computed(() => props.id ? '编辑学生' : '新增学生')
122 const formLoading = ref(false) // 表单提交状态 124 const formLoading = ref(false) // 表单提交状态
123 -const formData = ref<Student>({ 125 +const formData = ref<Partial<Student>>({
124 id: undefined, 126 id: undefined,
125 name: '', 127 name: '',
126 description: '', 128 description: '',
127 - birthday: 0,  
128 - sex: 0,  
129 - enabled: false, 129 + birthday: undefined,
  130 + sex: undefined,
  131 + enabled: undefined,
130 avatar: '', 132 avatar: '',
131 video: '', 133 video: '',
132 memo: '', 134 memo: '',
@@ -166,7 +168,7 @@ async function handleSubmit() { @@ -166,7 +168,7 @@ async function handleSubmit() {
166 168
167 formLoading.value = true 169 formLoading.value = true
168 try { 170 try {
169 - const data = formData.value 171 + const data = formData.value as Student
170 if (props.id) { 172 if (props.id) {
171 await updateStudent(data) 173 await updateStudent(data)
172 toast.success('修改成功') 174 toast.success('修改成功')
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_tree/vue/form/index
@@ -71,7 +71,7 @@ definePage({ @@ -71,7 +71,7 @@ definePage({
71 const toast = useToast() 71 const toast = useToast()
72 const getTitle = computed(() => props.id ? '编辑分类' : '新增分类') 72 const getTitle = computed(() => props.id ? '编辑分类' : '新增分类')
73 const formLoading = ref(false) // 表单提交状态 73 const formLoading = ref(false) // 表单提交状态
74 -const formData = ref<Category>({ 74 +const formData = ref<Partial<Category>>({
75 id: undefined, 75 id: undefined,
76 name: '', 76 name: '',
77 parentId: props.parentId ?? 0, 77 parentId: props.parentId ?? 0,
@@ -105,7 +105,7 @@ async function handleSubmit() { @@ -105,7 +105,7 @@ async function handleSubmit() {
105 105
106 formLoading.value = true 106 formLoading.value = true
107 try { 107 try {
108 - const data = formData.value 108 + const data = formData.value as Category
109 if (props.id) { 109 if (props.id) {
110 await updateCategory(data) 110 await updateCategory(data)
111 toast.success('修改成功') 111 toast.success('修改成功')