Explorar el Código

【功能新增】INFRA:代码生成 vben5 antd 模版 50%

puhui999 hace 6 meses
padre
commit
ef2d0b354a

+ 12 - 10
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java

@@ -164,22 +164,24 @@ public class CodegenEngine {
             .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/list_sub_erp.vue"),  // 特殊:主子表专属逻辑
                     vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue"))
             // VUE3_VBEN5_ANTD
-            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdGeneralTemplatePath("views/index.vue"),
+            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdSchemaTemplatePath("views/data.ts"),
+                    vue3FilePath("views/${table.moduleName}/${table.businessName}/data.ts"))
+            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdSchemaTemplatePath("views/index.vue"),
                     vue3FilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
-            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdGeneralTemplatePath("views/form.vue"),
-                    vue3FilePath("views/${table.moduleName}/${table.businessName}/form.vue"))
-            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/form_sub_normal.vue"),  // 特殊:主子表专属逻辑
+            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdSchemaTemplatePath("views/form.vue"),
+                    vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/form.vue"))
+            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdSchemaTemplatePath("api/api.ts"),
+                    vue3FilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
+            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/form_sub_normal.vue"),  // 特殊:主子表专属逻辑
                     vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue"))
-            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/form_sub_inner.vue"),  // 特殊:主子表专属逻辑
+            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/form_sub_inner.vue"),  // 特殊:主子表专属逻辑
                     vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue"))
-            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/form_sub_erp.vue"),  // 特殊:主子表专属逻辑
+            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/form_sub_erp.vue"),  // 特殊:主子表专属逻辑
                     vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue"))
-            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/list_sub_inner.vue"),  // 特殊:主子表专属逻辑
+            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/list_sub_inner.vue"),  // 特殊:主子表专属逻辑
                     vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue"))
-            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/list_sub_erp.vue"),  // 特殊:主子表专属逻辑
+            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/list_sub_erp.vue"),  // 特殊:主子表专属逻辑
                     vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue"))
-            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD.getType(), vue3Vben5AntdGeneralTemplatePath("api/api.ts"),
-                    vue3FilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
             .build();
 
     @Resource

+ 85 - 84
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/api/api.ts.vm

@@ -4,30 +4,14 @@ import { requestClient } from '#/api/request';
 #set ($baseURL = "/${table.moduleName}/${simpleClassName_strikeCase}")
 
 export namespace ${simpleClassName}Api {
-  ## 特殊:主子表专属逻辑
-  #foreach ($subTable in $subTables)
-    #set ($index = $foreach.count - 1)
-    #set ($subSimpleClassName = $subSimpleClassNames.get($index))
-    #set ($subColumns = $subColumnsList.get($index))##当前字段数组
-    /** ${subTable.classComment}信息 */
-    export interface ${subSimpleClassName} {
-      #foreach ($column in $subColumns)
-        #if ($column.createOperation || $column.updateOperation)
-          #if(${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer" || ${column.javaType.toLowerCase()} == "short" || ${column.javaType.toLowerCase()} == "double" || ${column.javaType.toLowerCase()} == "bigdecimal")
-              ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: number; // ${column.columnComment}
-          #elseif(${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdate" || ${column.javaType.toLowerCase()} == "localdatetime")
-              ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: Date; // ${column.columnComment}
-          #else
-              ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: ${column.javaType.toLowerCase()}; // ${column.columnComment}
-          #end
-        #end
-      #end
-    }
-
-  #end
-  /** ${table.classComment}信息 */
-  export interface ${simpleClassName} {
-    #foreach ($column in $columns)
+## 特殊:主子表专属逻辑
+#foreach ($subTable in $subTables)
+  #set ($index = $foreach.count - 1)
+  #set ($subSimpleClassName = $subSimpleClassNames.get($index))
+  #set ($subColumns = $subColumnsList.get($index))##当前字段数组
+  /** ${subTable.classComment}信息 */
+  export interface ${subSimpleClassName} {
+    #foreach ($column in $subColumns)
       #if ($column.createOperation || $column.updateOperation)
         #if(${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer" || ${column.javaType.toLowerCase()} == "short" || ${column.javaType.toLowerCase()} == "double" || ${column.javaType.toLowerCase()} == "bigdecimal")
             ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: number; // ${column.columnComment}
@@ -38,21 +22,37 @@ export namespace ${simpleClassName}Api {
         #end
       #end
     #end
-    #if ( $table.templateType == 2 )
-      children?: ${simpleClassName}[];
-    #end
-    ## 特殊:主子表专属逻辑
-    #if ( $table.templateType == 10 || $table.templateType == 12 )
-      #foreach ($subTable in $subTables)
-        #set ($index = $foreach.count - 1)
-        #set ($subSimpleClassName = $subSimpleClassNames.get($index))
-        #if ( $subTable.subJoinMany )
-            ${subSimpleClassName.toLowerCase()}s?: ${subSimpleClassName}[]
-        #else
-            ${subSimpleClassName.toLowerCase()}?: ${subSimpleClassName}
-        #end
-      #end
+  }
+
+#end
+  /** ${table.classComment}信息 */
+  export interface ${simpleClassName} {
+#foreach ($column in $columns)
+#if ($column.createOperation || $column.updateOperation)
+#if(${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer" || ${column.javaType.toLowerCase()} == "short" || ${column.javaType.toLowerCase()} == "double" || ${column.javaType.toLowerCase()} == "bigdecimal")
+    ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: number; // ${column.columnComment}
+#elseif(${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdate" || ${column.javaType.toLowerCase()} == "localdatetime")
+    ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: Date; // ${column.columnComment}
+#else
+    ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: ${column.javaType.toLowerCase()}; // ${column.columnComment}
+#end
+#end
+#end
+#if ( $table.templateType == 2 )
+  children?: ${simpleClassName}[];
+#end
+## 特殊:主子表专属逻辑
+#if ( $table.templateType == 10 || $table.templateType == 12 )
+  #foreach ($subTable in $subTables)
+    #set ($index = $foreach.count - 1)
+    #set ($subSimpleClassName = $subSimpleClassNames.get($index))
+    #if ( $subTable.subJoinMany )
+        ${subSimpleClassName.toLowerCase()}s?: ${subSimpleClassName}[]
+    #else
+        ${subSimpleClassName.toLowerCase()}?: ${subSimpleClassName}
     #end
+  #end
+#end
   }
 }
 
@@ -95,57 +95,58 @@ export function export${simpleClassName}(params: any) {
 
 ## 特殊:主子表专属逻辑
 #foreach ($subTable in $subTables)
-  #set ($index = $foreach.count - 1)
-  #set ($subSimpleClassName = $subSimpleClassNames.get($index))
-  #set ($subPrimaryColumn = $subPrimaryColumns.get($index))##当前 primary 字段
-  #set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段
-  #set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
-  #set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))
-  #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
-  #set ($subClassNameVar = $subClassNameVars.get($index))
+#set ($index = $foreach.count - 1)
+#set ($subSimpleClassName = $subSimpleClassNames.get($index))
+#set ($subPrimaryColumn = $subPrimaryColumns.get($index))##当前 primary 字段
+#set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段
+#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
+#set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))
+#set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
+#set ($subClassNameVar = $subClassNameVars.get($index))
 
 // ==================== 子表($subTable.classComment) ====================
 
-  ## 情况一:MASTER_ERP 时,需要分查询页子表
-  #if ( $table.templateType == 11 )
-  /** 获得${subTable.classComment}分页 */
-  export function get${subSimpleClassName}Page(params: PageParam) {
-    return requestClient.get<PageResult<${simpleClassName}Api.${subSimpleClassName}>>(`${baseURL}/${subSimpleClassName_strikeCase}/page`, { params });
-  }
-    ## 情况二:非 MASTER_ERP 时,需要列表查询子表
+## 情况一:MASTER_ERP 时,需要分查询页子表
+#if ( $table.templateType == 11 )
+/** 获得${subTable.classComment}分页 */
+export function get${subSimpleClassName}Page(params: PageParam) {
+  return requestClient.get<PageResult<${simpleClassName}Api.${subSimpleClassName}>>(`${baseURL}/${subSimpleClassName_strikeCase}/page`, { params });
+}
+## 情况二:非 MASTER_ERP 时,需要列表查询子表
+#else
+  #if ( $subTable.subJoinMany )
+/** 获得${subTable.classComment}列表 */
+export function get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaField}: number) {
+  return requestClient.get<${simpleClassName}Api.${subSimpleClassName}[]>(`${baseURL}/${subSimpleClassName_strikeCase}/list-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=${${subJoinColumn.javaField}}`);
+}
   #else
-    #if ( $subTable.subJoinMany )
-    /** 获得${subTable.classComment}列表 */
-    export function get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaField}: number) {
-      return requestClient.get<${simpleClassName}Api.${subSimpleClassName}[]>(`${baseURL}/${subSimpleClassName_strikeCase}/list-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=${${subJoinColumn.javaField}}`);
-    }
-    #else
-    /** 获得${subTable.classComment} */
-    export function get${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaField}: number) {
-      return requestClient.get<${simpleClassName}Api.${subSimpleClassName}>(`${baseURL}/${subSimpleClassName_strikeCase}/get-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=${${subJoinColumn.javaField}}`);
-    }
-    #end
+/** 获得${subTable.classComment} */
+export function get${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaField}: number) {
+  return requestClient.get<${simpleClassName}Api.${subSimpleClassName}>(`${baseURL}/${subSimpleClassName_strikeCase}/get-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=${${subJoinColumn.javaField}}`);
+}
   #end
-  ## 特殊:MASTER_ERP 时,支持单个的新增、修改、删除操作
-  #if ( $table.templateType == 11 )
-  /** 新增${subTable.classComment} */
-  export function create${subSimpleClassName}(data: ${simpleClassName}Api.${subSimpleClassName}) {
-    return requestClient.post(`${baseURL}/${subSimpleClassName_strikeCase}/create`, data);
-  }
+#end
+## 特殊:MASTER_ERP 时,支持单个的新增、修改、删除操作
+#if ( $table.templateType == 11 )
+/** 新增${subTable.classComment} */
+export function create${subSimpleClassName}(data: ${simpleClassName}Api.${subSimpleClassName}) {
+  return requestClient.post(`${baseURL}/${subSimpleClassName_strikeCase}/create`, data);
+}
 
-  /** 修改${subTable.classComment} */
-  export function update${subSimpleClassName}(data: ${simpleClassName}Api.${subSimpleClassName}) {
-    return requestClient.put(`${baseURL}/${subSimpleClassName_strikeCase}/update`, data);
-  }
+/** 修改${subTable.classComment} */
+export function update${subSimpleClassName}(data: ${simpleClassName}Api.${subSimpleClassName}) {
+  return requestClient.put(`${baseURL}/${subSimpleClassName_strikeCase}/update`, data);
+}
 
-  /** 删除${subTable.classComment} */
-  export function delete${subSimpleClassName}(id: number) {
-    return requestClient.delete(`${baseURL}/${subSimpleClassName_strikeCase}/delete?id=${id}`);
-  }
+/** 删除${subTable.classComment} */
+export function delete${subSimpleClassName}(id: number) {
+  return requestClient.delete(`${baseURL}/${subSimpleClassName_strikeCase}/delete?id=${id}`);
+}
+
+/** 获得${subTable.classComment} */
+export function get${subSimpleClassName}(id: number) {
+  return requestClient.get<${simpleClassName}Api.${subSimpleClassName}>(`${baseURL}/${subSimpleClassName_strikeCase}/get?id=${id}`);
+}
+#end
+#end
 
-  /** 获得${subTable.classComment} */
-  export function get${subSimpleClassName}(id: number) {
-    return requestClient.get<${simpleClassName}Api.${subSimpleClassName}>(`${baseURL}/${subSimpleClassName_strikeCase}/get?id=${id}`);
-  }
-  #end
-#end

+ 349 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/data.ts.vm

@@ -0,0 +1,349 @@
+import type { VxeTableGridOptions } from '@vben/plugins/vxe-table';
+import type { VbenFormSchema } from '#/adapter/form';
+import type { OnActionClickFn } from '#/adapter/vxe-table';
+import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
+
+import { z } from '#/adapter/form';
+#if(${table.templateType} == 2)## 树表需要导入这些
+import { get${simpleClassName}List } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
+import { handleTree } from '#/utils/tree';
+#end
+import { DICT_TYPE, getDictOptions } from '#/utils/dict';
+import { getRangePickerDefaultProps } from '#/utils/date';
+import { useAccess } from '@vben/access';
+
+const { hasAccessByCodes } = useAccess();
+
+/** 列表的搜索表单 */
+export function useGridFormSchema(): VbenFormSchema[] {
+  return [
+#foreach($column in $columns)
+#if ($column.listOperation)
+  #set ($dictType = $column.dictType)
+  #set ($javaType = $column.javaType)
+  #set ($javaField = $column.javaField)
+  #set ($comment = $column.columnComment)
+  #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
+    #set ($dictMethod = "number")
+  #elseif ($javaType == "String")
+    #set ($dictMethod = "string")
+  #elseif ($javaType == "Boolean")
+    #set ($dictMethod = "boolean")
+  #end
+    {
+      fieldName: '${javaField}',
+      label: '${comment}',
+  #if ($column.htmlType == "input" || $column.htmlType == "textarea" || $column.htmlType == "editor")
+      component: 'Input',
+      componentProps: {
+        allowClear: true,
+        placeholder: '请输入${comment}',
+      },
+  #elseif ($column.htmlType == "select" || $column.htmlType == "radio")
+      component: 'Select',
+      componentProps: {
+        allowClear: true,
+        #if ("" != $dictType)## 设置了 dictType 数据字典的情况
+        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
+        #else## 未设置 dictType 数据字典的情况
+        options: [],
+        #end
+        placeholder: '请选择${comment}',
+      },
+  #elseif($column.htmlType == "datetime")
+      component: 'RangePicker',
+      componentProps: {
+        ...getRangePickerDefaultProps(),
+        allowClear: true,
+      },
+  #end
+    },
+#end
+#end
+  ];
+}
+
+/** 列表的字段 */
+export function useGridColumns(
+  onActionClick?: OnActionClickFn<${simpleClassName}Api.${simpleClassName}>,
+): VxeTableGridOptions<${simpleClassName}Api.${simpleClassName}>['columns'] {
+  return [
+#if ($table.templateType == 12) ## 内嵌情况
+      { type: 'expand', width: 80, slots: { content: 'expand_content' } },
+#end
+#foreach($column in $columns)
+#if ($column.listOperationResult)
+  #set ($dictType = $column.dictType)
+  #set ($javaField = $column.javaField)
+  #set ($comment = $column.columnComment)
+    {
+      field: '${javaField}',
+      title: '${comment}',
+      minWidth: 120,
+  #if ($column.javaType == "LocalDateTime")## 时间类型
+      formatter: 'formatDateTime',
+  #elseif("" != $dictType)## 数据字典
+      cellRender: {
+        name: 'CellDict',
+        props: { type: DICT_TYPE.$dictType.toUpperCase() },
+      },
+  #end
+  #if (${table.templateType} == 2 && $column.id == $treeNameColumn.id)## 树表特有:标记树节点列
+      treeNode: true,
+  #end
+    },
+#end
+#end
+    {
+      field: 'operation',
+      title: '操作',
+      minWidth: 200,
+      align: 'center',
+      fixed: 'right',
+      headerAlign: 'center',
+      showOverflow: false,
+      cellRender: {
+        attrs: {
+          nameField: '${columns[0].javaField}',
+          nameTitle: '${table.classComment}',
+          onClick: onActionClick,
+        },
+        name: 'CellOperation',
+        options: [
+#if (${table.templateType} == 2)## 树表特有操作
+          {
+            code: 'append',
+            text: '新增下级',
+            show: hasAccessByCodes(['${table.moduleName}:${simpleClassName_strikeCase}:create']),
+          },
+#end
+          {
+            code: 'edit',
+            show: hasAccessByCodes(['${table.moduleName}:${simpleClassName_strikeCase}:update']),
+          },
+          {
+            code: 'delete',
+            show: hasAccessByCodes(['${table.moduleName}:${simpleClassName_strikeCase}:delete']),
+#if (${table.templateType} == 2)## 树表禁止删除带有子节点的数据
+            disabled: (row: ${simpleClassName}Api.${simpleClassName}) => {
+                return !!(row.children && row.children.length > 0);
+            },
+#end
+          },
+        ],
+      },
+    },
+  ];
+}
+
+## 标准模式和内嵌模式时,主子关系一对一则生成表单schema,一对多则生成列表schema(内嵌模式时表单schema也要生成)。erp 模式时都生成
+## 特殊:主子表专属逻辑
+#foreach ($subTable in $subTables)
+    #set ($index = $foreach.count - 1)
+    #set ($subColumns = $subColumnsList.get($index))##当前字段数组
+    #set ($subSimpleClassName = $subSimpleClassNames.get($index))
+    #set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段
+    #set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))
+// ==================== 子表($subTable.classComment) ====================
+
+#if ($table.templateType == 11) ## erp 情况
+/** 列表的搜索表单 */
+export function use${subSimpleClassName}GridFormSchema(): VbenFormSchema[] {
+    return [
+        #foreach($column in $subColumns)
+            #if ($column.listOperation)
+                #set ($dictType = $column.dictType)
+                #set ($javaType = $column.javaType)
+                #set ($javaField = $column.javaField)
+                #set ($comment = $column.columnComment)
+                #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
+                    #set ($dictMethod = "number")
+                #elseif ($javaType == "String")
+                    #set ($dictMethod = "string")
+                #elseif ($javaType == "Boolean")
+                    #set ($dictMethod = "boolean")
+                #end
+                {
+                    fieldName: '${javaField}',
+                    label: '${comment}',
+                    #if ($column.htmlType == "input" || $column.htmlType == "textarea" || $column.htmlType == "editor")
+                        component: 'Input',
+                        componentProps: {
+                            allowClear: true,
+                            placeholder: '请输入${comment}',
+                        },
+                    #elseif ($column.htmlType == "select" || $column.htmlType == "radio")
+                        component: 'Select',
+                        componentProps: {
+                            allowClear: true,
+                            #if ("" != $dictType)## 设置了 dictType 数据字典的情况
+                                options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
+                            #else## 未设置 dictType 数据字典的情况
+                                options: [],
+                            #end
+                            placeholder: '请选择${comment}',
+                        },
+                    #elseif($column.htmlType == "datetime")
+                        component: 'RangePicker',
+                        componentProps: {
+                            ...getRangePickerDefaultProps(),
+                            allowClear: true,
+                        },
+                    #end
+                },
+            #end
+        #end
+    ];
+}
+
+/** 列表的字段 */
+export function use${subSimpleClassName}GridColumns(
+    onActionClick?: OnActionClickFn<${simpleClassName}Api.${subSimpleClassName}>,
+): VxeTableGridOptions<${simpleClassName}Api.${subSimpleClassName}>['columns'] {
+    return [
+        #foreach($column in $subColumns)
+            #if ($column.listOperationResult)
+                #set ($dictType = $column.dictType)
+                #set ($javaField = $column.javaField)
+                #set ($comment = $column.columnComment)
+                {
+                    field: '${javaField}',
+                    title: '${comment}',
+                    minWidth: 120,
+                    #if ($column.javaType == "LocalDateTime")## 时间类型
+                        formatter: 'formatDateTime',
+                    #elseif("" != $dictType)## 数据字典
+                        cellRender: {
+                            name: 'CellDict',
+                            props: { type: DICT_TYPE.$dictType.toUpperCase() },
+                        },
+                    #end
+                },
+            #end
+        #end
+        {
+            field: 'operation',
+            title: '操作',
+            minWidth: 200,
+            align: 'center',
+            fixed: 'right',
+            headerAlign: 'center',
+            showOverflow: false,
+            cellRender: {
+                attrs: {
+                    nameField: '${columns[0].javaField}',
+                    nameTitle: '${subTable.classComment}',
+                    onClick: onActionClick,
+                },
+                name: 'CellOperation',
+                options: [
+                    {
+                        code: 'edit',
+                        show: hasAccessByCodes(['${table.moduleName}:${simpleClassName_strikeCase}:update']),
+                    },
+                    {
+                        code: 'delete',
+                        show: hasAccessByCodes(['${table.moduleName}:${simpleClassName_strikeCase}:delete']),
+                    },
+                ],
+            },
+        },
+    ];
+}
+
+#else
+    #if ($subTable.subJoinMany) ## 一对多
+    /** 新增/修改列表的字段 */
+    export function use${subSimpleClassName}GridEditColumns(
+        onActionClick?: OnActionClickFn<${simpleClassName}Api.${subSimpleClassName}>,
+    ): VxeTableGridOptions<${simpleClassName}Api.${subSimpleClassName}>['columns'] {
+        return [
+            #foreach($column in $subColumns)
+                #if ($column.createOperation || $column.updateOperation)
+                    #if (!$column.primaryKey && $column.listOperationResult && $column.id != $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
+                        #set ($dictType = $column.dictType)
+                        #set ($javaField = $column.javaField)
+                        #set ($comment = $column.columnComment)
+                        #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
+                            #set ($dictMethod = "number")
+                        #elseif ($javaType == "String")
+                            #set ($dictMethod = "string")
+                        #elseif ($javaType == "Boolean")
+                            #set ($dictMethod = "boolean")
+                        #end
+                        {
+                            field: '${javaField}',
+                            title: '${comment}',
+                            minWidth: 120,
+                            slots: { default: '${javaField}' },
+                            #if ($column.htmlType == "select" || $column.htmlType == "checkbox" || $column.htmlType == "radio")
+                                #if ("" != $dictType)## 有数据字典
+                                    params: {
+                                        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
+                                    },
+                                #else
+                                    params: {
+                                        options: [],
+                                    },
+                                #end
+                            #end
+                        },
+                    #end
+                #end
+            #end
+            {
+                field: 'operation',
+                title: '操作',
+                minWidth: 60,
+                align: 'center',
+                fixed: 'right',
+                headerAlign: 'center',
+                showOverflow: false,
+                cellRender: {
+                    attrs: {
+                        nameField: '${columns[0].javaField}',
+                        nameTitle: '${table.classComment}',
+                        onClick: onActionClick,
+                    },
+                    name: 'CellOperation',
+                    options: [
+                        {
+                            code: 'delete',
+                            show: hasAccessByCodes(['${table.moduleName}:${simpleClassName_strikeCase}:delete']),
+                        },
+                    ],
+                },
+            },
+        ];
+    }
+    #end
+    #if ($table.templateType == 12) ## 内嵌情况
+    /** 列表的字段 */
+    export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${simpleClassName}Api.${subSimpleClassName}>['columns'] {
+        return [
+            #foreach($column in $subColumns)
+                #if ($column.listOperationResult)
+                    #set ($dictType = $column.dictType)
+                    #set ($javaField = $column.javaField)
+                    #set ($comment = $column.columnComment)
+                    {
+                        field: '${javaField}',
+                        title: '${comment}',
+                        minWidth: 120,
+                        #if ($column.javaType == "LocalDateTime")## 时间类型
+                            formatter: 'formatDateTime',
+                        #elseif("" != $dictType)## 数据字典
+                            cellRender: {
+                                name: 'CellDict',
+                                props: { type: DICT_TYPE.$dictType.toUpperCase() },
+                            },
+                        #end
+                    },
+                #end
+            #end
+        ];
+    }
+
+    #end
+#end
+#end

+ 267 - 275
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/form.vue.vm

@@ -1,5 +1,163 @@
+<script lang="ts" setup>
+import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
+
+import { useVbenModal } from '@vben/common-ui';
+import { message, Tabs, Checkbox, Input, Textarea, Select,RadioGroup,CheckboxGroup, DatePicker } from 'ant-design-vue';
+## 特殊:主子表专属逻辑
+#if ( $table.templateType == 10 || $table.templateType == 12 )
+  #foreach ($subSimpleClassName in $subSimpleClassNames)
+  #set ($index = $foreach.count - 1)
+  #set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))
+  import ${subSimpleClassName}Form from './${subSimpleClassName_strikeCase}-form.vue'
+  #end
+#end
+
+import { computed, ref } from 'vue';
+import { $t } from '#/locales';
+import { useVbenForm } from '#/adapter/form';
+import { get${simpleClassName}, create${simpleClassName}, update${simpleClassName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
+
+import { useFormSchema } from '../data';
+
+const emit = defineEmits(['success']);
+const formData = ref<Partial<${simpleClassName}Api.${simpleClassName}>>({
+#foreach ($column in $columns)
+  #if ($column.createOperation || $column.updateOperation)
+    #if ($column.htmlType == "checkbox")
+        $column.javaField: [],
+    #else
+        $column.javaField: undefined,
+    #end
+  #end
+#end
+});
+#if (${table.templateType} == 2)## 树表特有:父ID处理
+const parentId = ref<number>(); // 新增下级时的父级 ID
+
+const getTitle = computed(() => {
+  if (formData.value?.id) {
+    return $t('ui.actionTitle.edit', ['${table.classComment}']);
+  }
+  return parentId.value
+    ? $t('ui.actionTitle.create', ['下级${table.classComment}'])
+    : $t('ui.actionTitle.create', ['${table.classComment}']);
+});
+#else## 标准表标题
+const getTitle = computed(() => {
+  return formData.value?.id
+    ? $t('ui.actionTitle.edit', ['${table.classComment}'])
+    : $t('ui.actionTitle.create', ['${table.classComment}']);
+});
+#end
+
+## 特殊:主子表专属逻辑
+#if ( $table.templateType == 10 || $table.templateType == 12 )
+  #if ( $subTables && $subTables.size() > 0 )
+
+  /** 子表的表单 */
+  const subTabsName = ref('$subClassNameVars.get(0)')
+    #foreach ($subClassNameVar in $subClassNameVars)
+      #set ($index = $foreach.count - 1)
+      #set ($subSimpleClassName = $subSimpleClassNames.get($index))
+      const ${subClassNameVar}FormRef = ref<InstanceType<typeof ${subSimpleClassName}Form>>()
+    #end
+  #end
+#end
+
+const [Modal, modalApi] = useVbenModal({
+  async onConfirm() {
+    const { valid } = await formApi.validate();
+    if (!valid) {
+      return;
+    }
+    ## 特殊:主子表专属逻辑
+    #if ( $table.templateType == 10 || $table.templateType == 12 )
+      #if ( $subTables && $subTables.size() > 0 )
+        // 校验子表单
+        #foreach ($subTable in $subTables)
+          #set ($index = $foreach.count - 1)
+          #set ($subClassNameVar = $subClassNameVars.get($index))
+          #if ($subTable.subJoinMany) ## 一对多
+            ## TODO 列表值校验?
+          #else
+            const ${subClassNameVar}Valid = await ${subClassNameVar}FormRef.value?.validate();
+            if (!${subClassNameVar}Valid) {
+              subTabsName.value = '${subClassNameVar}';
+              return;
+            }
+          #end
+        #end
+      #end
+    #end
+    modalApi.lock();
+    // 提交表单
+    const data = formData.value;
+    ## 特殊:主子表专属逻辑
+    #if ( $table.templateType == 10 || $table.templateType == 12 )
+      #if ( $subTables && $subTables.size() > 0 )
+        // 拼接子表的数据
+        #foreach ($subTable in $subTables)
+          #set ($index = $foreach.count - 1)
+          #set ($subClassNameVar = $subClassNameVars.get($index))
+          #if ($subTable.subJoinMany)
+            data.${subClassNameVar}s = ${subClassNameVar}FormRef.value?.getData();
+          #else
+            data.${subClassNameVar} = await ${subClassNameVar}FormRef.value?.getValues();
+          #end
+        #end
+      #end
+    #end
+    try {
+      await (formData.value?.id ? update${simpleClassName}(data) : create${simpleClassName}(data));
+      // 关闭并提示
+      await modalApi.close();
+      emit('success');
+      message.success({
+        content: $t('ui.actionMessage.operationSuccess'),
+        key: 'action_process_msg',
+      });
+    } finally {
+      modalApi.lock(false);
+    }
+  },
+  async onOpenChange(isOpen: boolean) {
+    if (!isOpen) {
+      formData.value = {
+        #foreach ($column in $columns)
+          #if ($column.createOperation || $column.updateOperation)
+            #if ($column.htmlType == "checkbox")
+                $column.javaField: [],
+            #else
+                $column.javaField: undefined,
+            #end
+          #end
+        #end
+      };
+      return;
+    }
+
+    // 加载数据
+    let data = modalApi.getData<${simpleClassName}Api.${simpleClassName}>();
+    if (!data) {
+      return;
+    }
+    if (data.id) {
+      modalApi.lock();
+      try {
+        data = await get${simpleClassName}(data.id);
+      } finally {
+        modalApi.lock(false);
+      }
+    }
+    // 设置到 values
+    formData.value = data;
+    await formApi.setValues(formData.value);
+  },
+});
+</script>
+
 <template>
-  <Dialog :title="dialogTitle" v-model="dialogVisible">
+  <Modal :title="getTitle">
     <Form
       ref="formRef"
       :model="formData"
@@ -7,294 +165,128 @@
       label-col="{ span: 6 }"
       :loading="formLoading"
     >
-#foreach($column in $columns)
-    #if ($column.createOperation || $column.updateOperation)
-        #set ($dictType = $column.dictType)
-        #set ($javaField = $column.javaField)
-        #set ($javaType = $column.javaType)
-        #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
-        #set ($comment = $column.columnComment)
-        #set ($dictMethod = "getDictOptions")## 计算使用哪个 dict 字典方法
-        #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
+      #foreach($column in $columns)
+        #if ($column.createOperation || $column.updateOperation)
+          #set ($dictType = $column.dictType)
+          #set ($javaField = $column.javaField)
+          #set ($javaType = $column.javaType)
+          #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+          #set ($comment = $column.columnComment)
+          #set ($dictMethod = "getDictOptions")## 计算使用哪个 dict 字典方法
+          #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
             #set ($dictMethod = "getIntDictOptions")
-        #elseif ($javaType == "String")
+          #elseif ($javaType == "String")
             #set ($dictMethod = "getStrDictOptions")
-        #elseif ($javaType == "Boolean")
+          #elseif ($javaType == "Boolean")
             #set ($dictMethod = "getBoolDictOptions")
-        #end
-        #if ( $table.templateType == 2 && $column.id == $treeParentColumn.id )
-      <FormItem label="${comment}" name="${javaField}">
-        <TreeSelect
-          v-model:value="formData.${javaField}"
-          :treeData="${classNameVar}Tree"
-          #if ($treeNameColumn.javaField == "name")
-          :fieldNames="defaultProps"
-          #else
-          :fieldNames="{...defaultProps, label: '$treeNameColumn.javaField'}"
           #end
-          checkable
-          treeDefaultExpandAll
-          placeholder="请选择${comment}"
-        />
-      </FormItem>
-        #elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
-      <FormItem label="${comment}" name="${javaField}">
-        <Input v-model:value="formData.${javaField}" placeholder="请输入${comment}" />
-      </FormItem>
-        #elseif($column.htmlType == "imageUpload")## 图片上传
-      <FormItem label="${comment}" name="${javaField}">
-        <UploadImg v-model:value="formData.${javaField}" />
-      </FormItem>
-        #elseif($column.htmlType == "fileUpload")## 文件上传
-      <FormItem label="${comment}" name="${javaField}">
-        <UploadFile v-model:value="formData.${javaField}" />
-      </FormItem>
-        #elseif($column.htmlType == "editor")## 文本编辑器
-      <FormItem label="${comment}" name="${javaField}">
-        <Editor v-model:value="formData.${javaField}" height="150px" />
-      </FormItem>
-        #elseif($column.htmlType == "select")## 下拉框
-      <FormItem label="${comment}" name="${javaField}">
-        <Select v-model:value="formData.${javaField}" placeholder="请选择${comment}">
+          #if ( $table.templateType == 2 && $column.id == $treeParentColumn.id )
+            <FormItem label="${comment}" name="${javaField}">
+              <TreeSelect
+                      v-model:value="formData.${javaField}"
+                      :treeData="${classNameVar}Tree"
+                #if ($treeNameColumn.javaField == "name")
+                      :fieldNames="defaultProps"
+                #else
+                      :fieldNames="{...defaultProps, label: '$treeNameColumn.javaField'}"
+                #end
+                      checkable
+                      treeDefaultExpandAll
+                      placeholder="请选择${comment}"
+              />
+            </FormItem>
+          #elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
+            <FormItem label="${comment}" name="${javaField}">
+              <Input v-model:value="formData.${javaField}" placeholder="请输入${comment}" />
+            </FormItem>
+          #elseif($column.htmlType == "imageUpload")## 图片上传
+            <FormItem label="${comment}" name="${javaField}">
+              <UploadImg v-model:value="formData.${javaField}" />
+            </FormItem>
+          #elseif($column.htmlType == "fileUpload")## 文件上传
+            <FormItem label="${comment}" name="${javaField}">
+              <UploadFile v-model:value="formData.${javaField}" />
+            </FormItem>
+          #elseif($column.htmlType == "editor")## 文本编辑器
+            <FormItem label="${comment}" name="${javaField}">
+              <Editor v-model:value="formData.${javaField}" height="150px" />
+            </FormItem>
+          #elseif($column.htmlType == "select")## 下拉框
+            <FormItem label="${comment}" name="${javaField}">
+              <Select v-model:value="formData.${javaField}" placeholder="请选择${comment}">
                 #if ("" != $dictType)## 有数据字典
-          <SelectOption
-            v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
+                  <SelectOption
+                          v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
+                          :key="dict.value"
+                          :label="dict.label"
+                          :value="dict.value"
+                  />
                 #else##没数据字典
-          <SelectOption label="请选择字典生成" value="" />
+                  <SelectOption label="请选择字典生成" value="" />
                 #end
-        </Select>
-      </FormItem>
-        #elseif($column.htmlType == "checkbox")## 多选框
-      <FormItem label="${comment}" name="${javaField}">
-        <CheckboxGroup v-model:value="formData.${javaField}">
+              </Select>
+            </FormItem>
+          #elseif($column.htmlType == "checkbox")## 多选框
+            <FormItem label="${comment}" name="${javaField}">
+              <CheckboxGroup v-model:value="formData.${javaField}">
                 #if ("" != $dictType)## 有数据字典
-          <Checkbox
-            v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
+                  <Checkbox
+                          v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
+                          :key="dict.value"
+                          :label="dict.label"
+                          :value="dict.value"
+                  />
                 #else##没数据字典
-          <Checkbox label="请选择字典生成" />
+                  <Checkbox label="请选择字典生成" />
                 #end
-        </CheckboxGroup>
-      </FormItem>
-        #elseif($column.htmlType == "radio")## 单选框
-      <FormItem label="${comment}" name="${javaField}">
-        <RadioGroup v-model:value="formData.${javaField}">
+              </CheckboxGroup>
+            </FormItem>
+          #elseif($column.htmlType == "radio")## 单选框
+            <FormItem label="${comment}" name="${javaField}">
+              <RadioGroup v-model:value="formData.${javaField}">
                 #if ("" != $dictType)## 有数据字典
-          <Radio
-            v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
-            :key="dict.value"
-            :value="dict.value"
-          >
-            {{ dict.label }}
-          </Radio>
+                  <Radio
+                          v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
+                          :key="dict.value"
+                          :value="dict.value"
+                  >
+                    {{ dict.label }}
+                  </Radio>
                 #else##没数据字典
-          <Radio value="1">请选择字典生成</Radio>
+                  <Radio value="1">请选择字典生成</Radio>
                 #end
-        </RadioGroup>
-      </FormItem>
-        #elseif($column.htmlType == "datetime")## 时间框
-      <FormItem label="${comment}" name="${javaField}">
-        <DatePicker
-          v-model:value="formData.${javaField}"
-          valueFormat="x"
-          placeholder="选择${comment}"
-        />
-      </FormItem>
-        #elseif($column.htmlType == "textarea")## 文本框
-      <FormItem label="${comment}" name="${javaField}">
-        <Textarea v-model:value="formData.${javaField}" placeholder="请输入${comment}" />
-      </FormItem>
+              </RadioGroup>
+            </FormItem>
+          #elseif($column.htmlType == "datetime")## 时间框
+            <FormItem label="${comment}" name="${javaField}">
+              <DatePicker
+                      v-model:value="formData.${javaField}"
+                      valueFormat="x"
+                      placeholder="选择${comment}"
+              />
+            </FormItem>
+          #elseif($column.htmlType == "textarea")## 文本框
+            <FormItem label="${comment}" name="${javaField}">
+              <Textarea v-model:value="formData.${javaField}" placeholder="请输入${comment}" />
+            </FormItem>
+          #end
         #end
-    #end
-#end
+      #end
     </Form>
-## 特殊:主子表专属逻辑
-#if ( $table.templateType == 10 || $table.templateType == 12 )
-    <!-- 子表的表单 -->
-    <Tabs v-model:activeKey="subTabsName">
-    #foreach ($subTable in $subTables)
-      #set ($index = $foreach.count - 1)
-      #set ($subClassNameVar = $subClassNameVars.get($index))
-      #set ($subSimpleClassName = $subSimpleClassNames.get($index))
-      #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
-      <TabPane key="$subClassNameVar" tab="${subTable.classComment}">
-        <${subSimpleClassName}Form ref="${subClassNameVar}FormRef" :${subJoinColumn_strikeCase}="formData.id" />
-      </TabPane>
+    ## 特殊:主子表专属逻辑
+    #if ( $table.templateType == 10 || $table.templateType == 12 )
+      <!-- 子表的表单 -->
+      <Tabs v-model:active-key="subTabsName">
+        #foreach ($subTable in $subTables)
+          #set ($index = $foreach.count - 1)
+          #set ($subClassNameVar = $subClassNameVars.get($index))
+          #set ($subSimpleClassName = $subSimpleClassNames.get($index))
+          #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
+          <Tabs.TabPane key="$subClassNameVar" tab="${subTable.classComment}" force-render>
+            <${subSimpleClassName}Form ref="${subClassNameVar}FormRef" :${subJoinColumn_strikeCase}="formData?.id" />
+          </Tabs.TabPane>
+        #end
+      </Tabs>
     #end
-    </Tabs>
-#end
-    <template #footer>
-      <Button @click="submitForm" type="primary" :loading="formLoading">确 定</Button>
-      <Button @click="dialogVisible = false">取 消</Button>
-    </template>
-  </Dialog>
+  </Modal>
 </template>
-<script setup lang="ts">
-import { Form, FormItem, Input, Button, Select, SelectOption, DatePicker, Textarea, Checkbox, CheckboxGroup, Radio, RadioGroup, Tabs, TabPane, TreeSelect } from 'ant-design-vue'
-import { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
-import { ${simpleClassName}Api, ${simpleClassName}VO } from '@/api/${table.moduleName}/${table.businessName}'
-## 特殊:树表专属逻辑
-#if ( $table.templateType == 2 )
-import { defaultProps, handleTree } from '@/utils/tree'
-#end
-## 特殊:主子表专属逻辑
-#if ( $table.templateType == 10 || $table.templateType == 12 )
-#foreach ($subSimpleClassName in $subSimpleClassNames)
-import ${subSimpleClassName}Form from './components/${subSimpleClassName}Form.vue'
-#end
-#end
-
-/** ${table.classComment} 表单 */
-defineOptions({ name: '${simpleClassName}Form' })
-
-const { t } = useI18n() // 国际化
-const message = useMessage() // 消息弹窗
-
-const dialogVisible = ref(false) // 弹窗的是否展示
-const dialogTitle = ref('') // 弹窗的标题
-const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
-const formType = ref('') // 表单的类型:create - 新增;update - 修改
-const formData = ref({
-#foreach ($column in $columns)
-    #if ($column.createOperation || $column.updateOperation)
-      #if ($column.htmlType == "checkbox")
-  $column.javaField: [],
-      #else
-  $column.javaField: undefined,
-      #end
-    #end
-#end
-})
-const formRules = reactive({
-#foreach ($column in $columns)
-    #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键
-        #set($comment=$column.columnComment)
-  $column.javaField: [{ required: true, message: '${comment}不能为空', trigger: #if($column.htmlType == 'select')'change'#else'blur'#end }],
-    #end
-#end
-})
-const formRef = ref() // 表单 Ref
-## 特殊:树表专属逻辑
-#if ( $table.templateType == 2 )
-const ${classNameVar}Tree = ref([]) // 树形结构
-#end
-## 特殊:主子表专属逻辑
-#if ( $table.templateType == 10 || $table.templateType == 12 )
-#if ( $subTables && $subTables.size() > 0 )
-
-/** 子表的表单 */
-const subTabsName = ref('$subClassNameVars.get(0)')
-#foreach ($subClassNameVar in $subClassNameVars)
-const ${subClassNameVar}FormRef = ref()
-#end
-#end
-#end
-
-/** 打开弹窗 */
-const open = async (type: string, id?: number) => {
-  dialogVisible.value = true
-  dialogTitle.value = t('action.' + type)
-  formType.value = type
-  resetForm()
-  // 修改时,设置数据
-  if (id) {
-    formLoading.value = true
-    try {
-      formData.value = await ${simpleClassName}Api.get${simpleClassName}(id)
-    } finally {
-      formLoading.value = false
-    }
-  }
-## 特殊:树表专属逻辑
-#if ( $table.templateType == 2 )
-  await get${simpleClassName}Tree()
-#end
-}
-defineExpose({ open }) // 提供 open 方法,用于打开弹窗
-
-/** 提交表单 */
-const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
-const submitForm = async () => {
-  // 校验表单
-  await formRef.value.validate()
-## 特殊:主子表专属逻辑
-#if ( $table.templateType == 10 || $table.templateType == 12 )
-#if ( $subTables && $subTables.size() > 0 )
-  // 校验子表单
-  #foreach ($subTable in $subTables)
-  #set ($index = $foreach.count - 1)
-  #set ($subClassNameVar = $subClassNameVars.get($index))
-  try {
-    await ${subClassNameVar}FormRef.value.validate()
-  } catch (e) {
-    subTabsName.value = '${subClassNameVar}'
-    return
-  }
-  #end
-#end
-#end
-  // 提交请求
-  formLoading.value = true
-  try {
-    const data = formData.value as unknown as ${simpleClassName}VO
-## 特殊:主子表专属逻辑
-#if ( $table.templateType == 10 || $table.templateType == 12 )
-#if ( $subTables && $subTables.size() > 0 )
-    // 拼接子表的数据
-  #foreach ($subTable in $subTables)
-  #set ($index = $foreach.count - 1)
-  #set ($subClassNameVar = $subClassNameVars.get($index))
-    data.${subClassNameVar}#if ( $subTable.subJoinMany)s#end = ${subClassNameVar}FormRef.value.getData()
-  #end
-#end
-#end
-    if (formType.value === 'create') {
-      await ${simpleClassName}Api.create${simpleClassName}(data)
-      message.success(t('common.createSuccess'))
-    } else {
-      await ${simpleClassName}Api.update${simpleClassName}(data)
-      message.success(t('common.updateSuccess'))
-    }
-    dialogVisible.value = false
-    // 发送操作成功的事件
-    emit('success')
-  } finally {
-    formLoading.value = false
-  }
-}
-
-/** 重置表单 */
-const resetForm = () => {
-  formData.value = {
-#foreach ($column in $columns)
-  #if ($column.createOperation || $column.updateOperation)
-      #if ($column.htmlType == "checkbox")
-    $column.javaField: [],
-      #else
-    $column.javaField: undefined,
-      #end
-  #end
-#end
-  }
-  formRef.value?.resetFields()
-}
-## 特殊:树表专属逻辑
-#if ( $table.templateType == 2 )
-
-/** 获得${table.classComment}树 */
-const get${simpleClassName}Tree = async () => {
-  ${classNameVar}Tree.value = []
-  const data = await ${simpleClassName}Api.get${simpleClassName}List()
-  const root = { id: 0, name: '顶级${table.classComment}', children: [] }
-  root.children = handleTree(data, 'id', '${treeParentColumn.javaField}')
-  ${classNameVar}Tree.value.push(root)
-}
-#end
-</script>

+ 223 - 336
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/index.vue.vm

@@ -1,367 +1,254 @@
-<template>
-  <ContentWrap>
-    <!-- 搜索工作栏 -->
-    <Form
-      class="-mb-15px"
-      :model="queryParams"
-      ref="queryFormRef"
-      layout="inline"
-      label-col="68px"
-    >
-    #foreach($column in $columns)
-        #if ($column.listOperation)
-            #set ($dictType = $column.dictType)
-            #set ($javaField = $column.javaField)
-            #set ($javaType = $column.javaType)
-            #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
-            #set ($comment = $column.columnComment)
-            #set ($dictMethod = "getDictOptions")## 计算使用哪个 dict 字典方法
-            #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
-                #set ($dictMethod = "getIntDictOptions")
-            #elseif ($javaType == "String")
-                #set ($dictMethod = "getStrDictOptions")
-            #elseif ($javaType == "Boolean")
-                #set ($dictMethod = "getBoolDictOptions")
-            #end
-            #if ($column.htmlType == "input")
-      <FormItem label="${comment}" name="${javaField}">
-        <Input
-          v-model:value="queryParams.${javaField}"
-          placeholder="请输入${comment}"
-          allowClear
-          @pressEnter="handleQuery"
-          class="!w-240px"
-        />
-      </FormItem>
-            #elseif ($column.htmlType == "select" || $column.htmlType == "radio")
-      <FormItem label="${comment}" name="${javaField}">
-        <Select
-          v-model:value="queryParams.${javaField}"
-          placeholder="请选择${comment}"
-          allowClear
-          class="!w-240px"
-        >
-                #if ("" != $dictType)## 设置了 dictType 数据字典的情况
-          <SelectOption
-            v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
-                #else## 未设置 dictType 数据字典的情况
-          <SelectOption label="请选择字典生成" value="" />
-                #end
-        </Select>
-      </FormItem>
-    #elseif($column.htmlType == "datetime")
-      #if ($column.listOperationCondition != "BETWEEN")## 非范围
-      <FormItem label="${comment}" name="${javaField}">
-        <DatePicker
-          v-model:value="queryParams.${javaField}"
-          valueFormat="YYYY-MM-DD"
-          placeholder="选择${comment}"
-          allowClear
-          class="!w-240px"
-        />
-      </FormItem>
-      #else## 范围
-      <FormItem label="${comment}" name="${javaField}">
-        <RangePicker
-          v-model:value="queryParams.${javaField}"
-          v-bind="getRangePickerDefaultProps()"
-          class="!w-220px"
-        />
-      </FormItem>
-      #end
-    #end
-    #end
-    #end
-      <FormItem>
-        <Button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</Button>
-        <Button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</Button>
-        <Button
-          type="primary"
-          ghost
-          @click="openForm('create')"
-          v-hasPermi="['${permissionPrefix}:create']"
-        >
-          <Icon icon="ep:plus" class="mr-5px" /> 新增
-        </Button>
-        <Button
-          type="success"
-          ghost
-          @click="handleExport"
-          :loading="exportLoading"
-          v-hasPermi="['${permissionPrefix}:export']"
-        >
-          <Icon icon="ep:download" class="mr-5px" /> 导出
-        </Button>
-## 特殊:树表专属逻辑
-#if ( $table.templateType == 2 )
-        <Button type="danger" ghost @click="toggleExpandAll">
-          <Icon icon="ep:sort" class="mr-5px" /> 展开/折叠
-        </Button>
-#end
-      </FormItem>
-    </Form>
-  </ContentWrap>
+<script lang="ts" setup>
+import type { OnActionClickParams, VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
 
-  <!-- 列表 -->
-  <ContentWrap>
-## 特殊:主子表专属逻辑
-#if ( $table.templateType == 11 && $subTables && $subTables.size() > 0 )
-    <Table
-      :loading="loading"
-      :dataSource="list"
-      :bordered="true"
-      :rowClassName="() => 'editable-row'"
-      @change="handleCurrentChange"
-    >
-## 特殊:树表专属逻辑
-#elseif ( $table.templateType == 2 )
-    <Table
-      :loading="loading"
-      :dataSource="list"
-      :bordered="true"
-      rowKey="id"
-      :expandAllRows="isExpandAll"
-      v-if="refreshTable"
-    >
-#else
-    <Table :loading="loading" :dataSource="list" :bordered="true">
-#end
-## 特殊:主子表专属逻辑
-#if ( $table.templateType == 12 && $subTables && $subTables.size() > 0 )
-      <!-- 子表的列表 -->
-      <template #expandedRowRender="{ record }">
-        <Tabs defaultActiveKey="$subClassNameVars.get(0)">
-            #foreach ($subTable in $subTables)
-              #set ($index = $foreach.count - 1)
-              #set ($subClassNameVar = $subClassNameVars.get($index))
-              #set ($subSimpleClassName = $subSimpleClassNames.get($index))
-              #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
-            <TabPane key="$subClassNameVar" tab="${subTable.classComment}">
-              <${subSimpleClassName}List :${subJoinColumn_strikeCase}="record.id" />
-            </TabPane>
-            #end
-        </Tabs>
-      </template>
-#end
-      #foreach($column in $columns)
-      #if ($column.listOperationResult)
-        #set ($dictType=$column.dictType)
-        #set ($javaField = $column.javaField)
-        #set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
-        #set ($comment=$column.columnComment)
-        #if ($column.javaType == "LocalDateTime")## 时间类型
-      <Column
-        title="${comment}"
-        align="center"
-        dataIndex="${javaField}"
-        :customRender="({ text }) => dateFormatter(text)"
-        width="180px"
-      />
-        #elseif($column.dictType && "" != $column.dictType)## 数据字典
-      <Column title="${comment}" align="center" dataIndex="${javaField}">
-        <template #default="{ text, record }">
-          <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="record.${column.javaField}" />
-        </template>
-      </Column>
-        #else
-      <Column title="${comment}" align="center" dataIndex="${javaField}" />
-        #end
-      #end
-    #end
-      <Column title="操作" align="center" key="action" :width="120">
-        <template #default="{ record }">
-          <Button
-            type="link"
-            @click="openForm('update', record.id)"
-            v-hasPermi="['${permissionPrefix}:update']"
-          >
-            编辑
-          </Button>
-          <Button
-            type="link"
-            danger
-            @click="handleDelete(record.id)"
-            v-hasPermi="['${permissionPrefix}:delete']"
-          >
-            删除
-          </Button>
-        </template>
-      </Column>
-    </Table>
-    <!-- 分页 -->
-    <Pagination
-      :total="total"
-      v-model:current="queryParams.pageNo"
-      v-model:pageSize="queryParams.pageSize"
-      @change="getList"
-    />
-  </ContentWrap>
+import { Page, useVbenModal } from '@vben/common-ui';
+import { Button, message,Tabs } from 'ant-design-vue';
+import { Download, Plus } from '@vben/icons';
+import Form from './modules/form.vue';
 
-  <!-- 表单弹窗:添加/修改 -->
-  <${simpleClassName}Form ref="formRef" @success="getList" />
 ## 特殊:主子表专属逻辑
-#if ( $table.templateType == 11 && $subTables && $subTables.size() > 0 )
-  <!-- 子表的列表 -->
-  <ContentWrap>
-    <Tabs defaultActiveKey="$subClassNameVars.get(0)">
-      #foreach ($subTable in $subTables)
-        #set ($index = $foreach.count - 1)
-        #set ($subClassNameVar = $subClassNameVars.get($index))
-        #set ($subSimpleClassName = $subSimpleClassNames.get($index))
-        #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
-      <TabPane key="$subClassNameVar" tab="${subTable.classComment}">
-        <${subSimpleClassName}List :${subJoinColumn_strikeCase}="currentRow.id" />
-      </TabPane>
-      #end
-    </Tabs>
-  </ContentWrap>
+#if ( $table.templateType == 11 || $table.templateType == 12 )
+    #foreach ($subSimpleClassName in $subSimpleClassNames)
+    #set ($index = $foreach.count - 1)
+    #set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))
+    import ${subSimpleClassName}List from './modules/${subSimpleClassName_strikeCase}-list.vue'
+    #end
 #end
-</template>
 
-<script setup lang="ts">
-import { Form, FormItem, Input, Button, Table, Column, Select, SelectOption, DatePicker, RangePicker, Tabs, TabPane } from 'ant-design-vue'
-import dayjs from 'dayjs'
-import { getRangePickerDefaultProps } from '@vben/utils'
-import { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
-import { dateFormatter } from '@/utils/formatTime'
-## 特殊:树表专属逻辑
-#if ( $table.templateType == 2 )
-import { handleTree } from '@/utils/tree'
-#end
-import download from '@/utils/download'
-import { ${simpleClassName}Api, ${simpleClassName}VO } from '@/api/${table.moduleName}/${table.businessName}'
-import ${simpleClassName}Form from './${simpleClassName}Form.vue'
-## 特殊:主子表专属逻辑
-#if ( $table.templateType != 10 )
-#foreach ($subSimpleClassName in $subSimpleClassNames)
-import ${subSimpleClassName}List from './components/${subSimpleClassName}List.vue'
+import { ref, h } from 'vue';
+import { $t } from '#/locales';
+import { useVbenVxeGrid } from '#/adapter/vxe-table';
+#if (${table.templateType} == 2)## 树表接口
+import { get${simpleClassName}List, delete${simpleClassName}, export${simpleClassName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
+#else## 标准表接口
+import { get${simpleClassName}Page, delete${simpleClassName}, export${simpleClassName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
 #end
-#end
-
-/** ${table.classComment} 列表 */
-defineOptions({ name: '${table.className}' })
+import { downloadByData } from '#/utils/download';
 
-const message = useMessage() // 消息弹窗
-const { t } = useI18n() // 国际化
+import { useGridColumns, useGridFormSchema } from './data';
 
-const loading = ref(true) // 列表的加载中
-const list = ref<${simpleClassName}VO[]>([]) // 列表的数据
-## 特殊:树表专属逻辑(树不需要分页接口)
-#if ( $table.templateType != 2 )
-const total = ref(0) // 列表的总页数
+#if ($table.templateType == 12 || $table.templateType == 11) ## 内嵌和erp情况
+/** 子表的列表 */
+const subTabsName = ref('$subClassNameVars.get(0)')
+#if ($table.templateType == 11)
+const select${simpleClassName} = ref<${simpleClassName}Api.${simpleClassName}>();
 #end
-const queryParams = reactive({
-## 特殊:树表专属逻辑(树不需要分页接口)
-#if ( $table.templateType != 2 )
-  pageNo: 1,
-  pageSize: 10,
 #end
-  #foreach ($column in $columns)
-    #if ($column.listOperation)
-      #if ($column.listOperationCondition != 'BETWEEN')
-  $column.javaField: undefined,
-  #end
-      #if ($column.htmlType == "datetime" || $column.listOperationCondition == "BETWEEN")
-  $column.javaField: [],
-      #end
-    #end
-  #end
-})
-const queryFormRef = ref() // 搜索的表单
-const exportLoading = ref(false) // 导出的加载中
 
-/** 查询列表 */
-const getList = async () => {
-  loading.value = true
-  try {
-## 特殊:树表专属逻辑(树不需要分页接口)
-  #if ( $table.templateType == 2 )
-    const data = await ${simpleClassName}Api.get${simpleClassName}List(queryParams)
-    list.value = handleTree(data, 'id', '${treeParentColumn.javaField}')
-  #else
-    const data = await ${simpleClassName}Api.get${simpleClassName}Page(queryParams)
-    list.value = data.list
-    total.value = data.total
-  #end
-  } finally {
-    loading.value = false
-  }
+const [FormModal, formModalApi] = useVbenModal({
+  connectedComponent: Form,
+  destroyOnClose: true,
+});
+
+#if (${table.templateType} == 2)## 树表特有:控制表格展开收缩
+/** 切换树形展开/收缩状态 */
+const isExpanded = ref(true);
+function toggleExpand() {
+  isExpanded.value = !isExpanded.value;
+  gridApi.grid.setAllTreeExpand(isExpanded.value);
 }
+#end
 
-/** 搜索按钮操作 */
-const handleQuery = () => {
-  queryParams.pageNo = 1
-  getList()
+/** 刷新表格 */
+function onRefresh() {
+#if ($table.templateType == 12) ## 内嵌情况
+  gridApi.reload();
+#else
+  gridApi.query();
+#end
 }
 
-/** 重置按钮操作 */
-const resetQuery = () => {
-  queryFormRef.value.resetFields()
-  handleQuery()
+/** 创建${table.classComment} */
+function onCreate() {
+  formModalApi.setData({}).open();
 }
 
-/** 添加/修改操作 */
-const formRef = ref()
-const openForm = (type: string, id?: number) => {
-  formRef.value.open(type, id)
+/** 编辑${table.classComment} */
+function onEdit(row: ${simpleClassName}Api.${simpleClassName}) {
+  formModalApi.setData(row).open();
 }
 
-/** 删除按钮操作 */
-const handleDelete = async (id: number) => {
-  try {
-    // 删除的二次确认
-    await message.delConfirm()
-    // 发起删除
-    await ${simpleClassName}Api.delete${simpleClassName}(id)
-    message.success(t('common.delSuccess'))
-    // 刷新列表
-    await getList()
-  } catch {}
+#if (${table.templateType} == 2)## 树表特有:新增下级
+/** 新增下级${table.classComment} */
+function onAppend(row: ${simpleClassName}Api.${simpleClassName}) {
+  formModalApi.setData({ ${treeParentColumn.javaField}: row.id }).open();
 }
+#end
 
-/** 导出按钮操作 */
-const handleExport = async () => {
+/** 删除${table.classComment} */
+async function onDelete(row: ${simpleClassName}Api.${simpleClassName}) {
+  const hideLoading = message.loading({
+    content: $t('ui.actionMessage.deleting', [row.id]),
+    duration: 0,
+    key: 'action_process_msg',
+  });
   try {
-    // 导出的二次确认
-    await message.exportConfirm()
-    // 发起导出
-    exportLoading.value = true
-    const data = await ${simpleClassName}Api.export${simpleClassName}(queryParams)
-    download.excel(data, '${table.classComment}.xls')
+    await delete${simpleClassName}(row.id as number);
+    message.success({
+      content: $t('ui.actionMessage.deleteSuccess', [row.id]),
+      key: 'action_process_msg',
+    });
+    onRefresh();
   } catch {
-  } finally {
-    exportLoading.value = false
+    hideLoading();
   }
 }
-## 特殊:主子表专属逻辑
-#if ( $table.templateType == 11 )
 
-/** 选中行操作 */
-const currentRow = ref({}) // 选中行
-const handleCurrentChange = (row) => {
-  currentRow.value = row
+/** 导出表格 */
+async function onExport() {
+  const data = await export${simpleClassName}(await gridApi.formApi.getValues());
+  downloadByData(data, '${table.classComment}.xls');
 }
-#end
-## 特殊:树表专属逻辑
-#if ( $table.templateType == 2 )
 
-/** 展开/折叠操作 */
-const isExpandAll = ref(true) // 是否展开,默认全部展开
-const refreshTable = ref(true) // 重新渲染表格状态
-const toggleExpandAll = async () => {
-  refreshTable.value = false
-  isExpandAll.value = !isExpandAll.value
-  await nextTick()
-  refreshTable.value = true
+/** 表格操作按钮的回调函数 */
+function onActionClick({
+  code,
+  row,
+}: OnActionClickParams<${simpleClassName}Api.${simpleClassName}>) {
+  switch (code) {
+  #if (${table.templateType} == 2)## 树表特有:新增下级
+    case 'append': {
+      onAppend(row);
+      break;
+    }
+  #end
+    case 'edit': {
+      onEdit(row);
+      break;
+    }
+    case 'delete': {
+      onDelete(row);
+      break;
+    }
+  }
 }
-#end
 
-/** 初始化 **/
-onMounted(() => {
-  getList()
-})
+const [Grid, gridApi] = useVbenVxeGrid({
+  formOptions: {
+    schema: useGridFormSchema(),
+  },
+  gridOptions: {
+    columns: useGridColumns(onActionClick),
+#if (${table.templateType} == 11)
+    height: '600px',
+#else
+    height: 'auto',
+#end
+#if (${table.templateType} == 2)## 树表设置
+  treeConfig: {
+    parentField: '${treeParentColumn.javaField}',
+    rowField: 'id',
+    transform: true,
+    expandAll: true,
+    reserve: true,
+  },
+  pagerConfig: {
+    enabled: false,
+  },
+#else## 标准表设置
+    pagerConfig: {
+      enabled: true,
+    },
+#end
+    proxyConfig: {
+      ajax: {
+#if (${table.templateType} == 2)## 树表数据加载
+        query: async (_, formValues) => {
+          return await get${simpleClassName}List(formValues);
+        },
+#else## 标准表数据加载
+        query: async ({ page }, formValues) => {
+          return await get${simpleClassName}Page({
+            pageNo: page.currentPage,
+            pageSize: page.pageSize,
+            ...formValues,
+          });
+        },
+#end
+      },
+    },
+    rowConfig: {
+      keyField: 'id',
+      isHover: true,
+#if (${table.templateType} == 11)
+      isCurrent: true,
+#end
+    },
+    toolbarConfig: {
+      refresh: { code: 'query' },
+      search: true,
+    },
+  } as VxeTableGridOptions<${simpleClassName}Api.${simpleClassName}>,
+#if (${table.templateType} == 11)
+  gridEvents:{
+    cellClick: ({ row }: { row: ${simpleClassName}Api.${simpleClassName}}) => {
+      select${simpleClassName}.value = row;
+    },
+  }
+#end
+});
 </script>
+
+<template>
+  <Page auto-content-height>
+    <FormModal @success="onRefresh" />
+
+#if ($table.templateType == 11) ## erp情况
+  <div>
+#end
+    <Grid table-title="${table.classComment}列表">
+        #if ($table.templateType == 12) ## 内嵌情况
+          <template #expand_content="{ row }">
+            <!-- 子表的表单 -->
+            <Tabs v-model:active-key="subTabsName" class="mx-8">
+                #foreach ($subTable in $subTables)
+                    #set ($index = $foreach.count - 1)
+                    #set ($subClassNameVar = $subClassNameVars.get($index))
+                    #set ($subSimpleClassName = $subSimpleClassNames.get($index))
+                    #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
+                  <Tabs.TabPane key="$subClassNameVar" tab="${subTable.classComment}" force-render>
+                    <${subSimpleClassName}List :${subJoinColumn_strikeCase}="row?.id" />
+                  </Tabs.TabPane>
+                #end
+            </Tabs>
+          </template>
+        #end
+      <template #toolbar-tools>
+#if (${table.templateType} == 2)## 树表特有:展开/收缩按钮
+        <Button @click="toggleExpand" class="mr-2">
+          {{ isExpanded ? '收缩' : '展开' }}
+        </Button>
+#end
+        <Button :icon="h(Plus)" type="primary" @click="onCreate" v-access:code="['${table.moduleName}:${simpleClassName_strikeCase}:create']">
+          {{ $t('ui.actionTitle.create', ['${table.classComment}']) }}
+        </Button>
+        <Button
+          :icon="h(Download)"
+          type="primary"
+          class="ml-2"
+          @click="onExport"
+          v-access:code="['${table.moduleName}:${simpleClassName_strikeCase}:export']"
+        >
+          {{ $t('ui.actionTitle.export') }}
+        </Button>
+      </template>
+    </Grid>
+
+#if ($table.templateType == 11) ## erp情况
+    <!-- 子表的表单 -->
+    <Tabs v-model:active-key="subTabsName" class="mt-2">
+        #foreach ($subTable in $subTables)
+            #set ($index = $foreach.count - 1)
+            #set ($subClassNameVar = $subClassNameVars.get($index))
+            #set ($subSimpleClassName = $subSimpleClassNames.get($index))
+            #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
+          <Tabs.TabPane key="$subClassNameVar" tab="${subTable.classComment}" force-render>
+            <${subSimpleClassName}List :${subJoinColumn_strikeCase}="select${simpleClassName}?.id" />
+          </Tabs.TabPane>
+        #end
+    </Tabs>
+    </div>
+#end
+  </Page>
+</template>

+ 78 - 196
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_erp.vue.vm

@@ -1,204 +1,86 @@
+#set ($subTable = $subTables.get($subIndex))##当前表
 #set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组
-#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))
 #set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
-<template>
-  <Dialog :title="dialogTitle" v-model="dialogVisible">
-    <Form
-      ref="formRef"
-      :model="formData"
-      :rules="formRules"
-      label-col="{ span: 6 }"
-      :loading="formLoading"
-    >
-#foreach($column in $subColumns)
-    #if ($column.createOperation || $column.updateOperation)
-        #set ($dictType = $column.dictType)
-        #set ($javaField = $column.javaField)
-        #set ($javaType = $column.javaType)
-        #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
-        #set ($comment = $column.columnComment)
-        #set ($dictMethod = "getDictOptions")## 计算使用哪个 dict 字典方法
-        #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
-            #set ($dictMethod = "getIntDictOptions")
-        #elseif ($javaType == "String")
-            #set ($dictMethod = "getStrDictOptions")
-        #elseif ($javaType == "Boolean")
-            #set ($dictMethod = "getBoolDictOptions")
-        #end
-        #if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
-        #elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
-      <FormItem label="${comment}" name="${javaField}">
-        <Input v-model:value="formData.${javaField}" placeholder="请输入${comment}" />
-      </FormItem>
-        #elseif($column.htmlType == "imageUpload")## 图片上传
-      <FormItem label="${comment}" name="${javaField}">
-        <UploadImg v-model:value="formData.${javaField}" />
-      </FormItem>
-        #elseif($column.htmlType == "fileUpload")## 文件上传
-      <FormItem label="${comment}" name="${javaField}">
-        <UploadFile v-model:value="formData.${javaField}" />
-      </FormItem>
-        #elseif($column.htmlType == "editor")## 文本编辑器
-      <FormItem label="${comment}" name="${javaField}">
-        <Editor v-model:value="formData.${javaField}" height="150px" />
-      </FormItem>
-        #elseif($column.htmlType == "select")## 下拉框
-      <FormItem label="${comment}" name="${javaField}">
-        <Select v-model:value="formData.${javaField}" placeholder="请选择${comment}">
-                #if ("" != $dictType)## 有数据字典
-          <SelectOption
-            v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
-                #else##没数据字典
-          <SelectOption label="请选择字典生成" value="" />
-                #end
-        </Select>
-      </FormItem>
-        #elseif($column.htmlType == "checkbox")## 多选框
-      <FormItem label="${comment}" name="${javaField}">
-        <CheckboxGroup v-model:value="formData.${javaField}">
-                #if ("" != $dictType)## 有数据字典
-          <Checkbox
-            v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
-                #else##没数据字典
-          <Checkbox label="请选择字典生成" />
-                #end
-        </CheckboxGroup>
-      </FormItem>
-        #elseif($column.htmlType == "radio")## 单选框
-      <FormItem label="${comment}" name="${javaField}">
-        <RadioGroup v-model:value="formData.${javaField}">
-                #if ("" != $dictType)## 有数据字典
-          <Radio
-            v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
-            :key="dict.value"
-            :value="dict.value"
-          >
-            {{ dict.label }}
-          </Radio>
-                #else##没数据字典
-          <Radio value="1">请选择字典生成</Radio>
-                #end
-        </RadioGroup>
-      </FormItem>
-        #elseif($column.htmlType == "datetime")## 时间框
-      <FormItem label="${comment}" name="${javaField}">
-        <DatePicker
-          v-model:value="formData.${javaField}"
-          valueFormat="x"
-          placeholder="选择${comment}"
-        />
-      </FormItem>
-        #elseif($column.htmlType == "textarea")## 文本框
-      <FormItem label="${comment}" name="${javaField}">
-        <Textarea v-model:value="formData.${javaField}" placeholder="请输入${comment}" />
-      </FormItem>
-        #end
-    #end
-#end
-    </Form>
-    <template #footer>
-      <Button @click="submitForm" type="primary" :loading="formLoading">确 定</Button>
-      <Button @click="dialogVisible = false">取 消</Button>
-    </template>
-  </Dialog>
-</template>
-<script setup lang="ts">
-import { Form, FormItem, Input, Button, Select, SelectOption, DatePicker, Textarea, Checkbox, CheckboxGroup, Radio, RadioGroup } from 'ant-design-vue'
-import { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
-import { ${simpleClassName}Api } from '@/api/${table.moduleName}/${table.businessName}'
+#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))
+<script lang="ts" setup>
+  import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
+
+  import { useVbenModal } from '@vben/common-ui';
+  import { message } from 'ant-design-vue';
 
-const { t } = useI18n() // 国际化
-const message = useMessage() // 消息弹窗
+  import { computed, ref } from 'vue';
+  import { $t } from '#/locales';
+  import { useVbenForm } from '#/adapter/form';
+  import { get${subSimpleClassName}, create${subSimpleClassName}, update${subSimpleClassName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
 
-const dialogVisible = ref(false) // 弹窗的是否展示
-const dialogTitle = ref('') // 弹窗的标题
-const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
-const formType = ref('') // 表单的类型:create - 新增;update - 修改
-const formData = ref({
-#foreach ($column in $subColumns)
-    #if ($column.createOperation || $column.updateOperation)
-      #if ($column.htmlType == "checkbox")
-  $column.javaField: [],
-      #else
-  $column.javaField: undefined,
-      #end
-    #end
-#end
-})
-const formRules = reactive({
-#foreach ($column in $subColumns)
-    #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键
-        #set($comment=$column.columnComment)
-  $column.javaField: [{ required: true, message: '${comment}不能为空', trigger: #if($column.htmlType == 'select')'change'#else'blur'#end }],
-    #end
-#end
-})
-const formRef = ref() // 表单 Ref
+  import { use${subSimpleClassName}FormSchema } from '../data';
 
-/** 打开弹窗 */
-const open = async (type: string, id?: number, ${subJoinColumn.javaField}: number) => {
-  dialogVisible.value = true
-  dialogTitle.value = t('action.' + type)
-  formType.value = type
-  resetForm()
-  formData.value.${subJoinColumn.javaField} = ${subJoinColumn.javaField}
-  // 修改时,设置数据
-  if (id) {
-    formLoading.value = true
-    try {
-      formData.value = await ${simpleClassName}Api.get${subSimpleClassName}(id)
-    } finally {
-      formLoading.value = false
-    }
-  }
-}
-defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+  const emit = defineEmits(['success']);
+  const formData = ref<${simpleClassName}Api.${subSimpleClassName}>();
+  const getTitle = computed(() => {
+    return formData.value?.id
+        ? $t('ui.actionTitle.edit', ['${subTable.classComment}'])
+        : $t('ui.actionTitle.create', ['${subTable.classComment}']);
+  });
 
-/** 提交表单 */
-const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
-const submitForm = async () => {
-  // 校验表单
-  await formRef.value.validate()
-  // 提交请求
-  formLoading.value = true
-  try {
-    const data = formData.value
-    if (formType.value === 'create') {
-      await ${simpleClassName}Api.create${subSimpleClassName}(data)
-      message.success(t('common.createSuccess'))
-    } else {
-      await ${simpleClassName}Api.update${subSimpleClassName}(data)
-      message.success(t('common.updateSuccess'))
-    }
-    dialogVisible.value = false
-    // 发送操作成功的事件
-    emit('success')
-  } finally {
-    formLoading.value = false
-  }
-}
+  const [Form, formApi] = useVbenForm({
+    layout: 'horizontal',
+    schema: use${subSimpleClassName}FormSchema(),
+    showDefaultActions: false
+  });
 
-/** 重置表单 */
-const resetForm = () => {
-  formData.value = {
-#foreach ($column in $subColumns)
-  #if ($column.createOperation || $column.updateOperation)
-      #if ($column.htmlType == "checkbox")
-    $column.javaField: [],
-      #else
-    $column.javaField: undefined,
-      #end
-  #end
-#end
-  }
-  formRef.value?.resetFields()
-}
+  const [Modal, modalApi] = useVbenModal({
+    async onConfirm() {
+      const { valid } = await formApi.validate();
+      if (!valid) {
+        return;
+      }
+
+      modalApi.lock();
+      // 提交表单
+      const data = (await formApi.getValues()) as ${simpleClassName}Api.${subSimpleClassName};
+      data.${subJoinColumn.javaField} = formData.value?.${subJoinColumn.javaField};
+      try {
+        await (formData.value?.id ? update${subSimpleClassName}(data) : create${subSimpleClassName}(data));
+        // 关闭并提示
+        await modalApi.close();
+        emit('success');
+        message.success({
+          content: $t('ui.actionMessage.operationSuccess'),
+          key: 'action_process_msg',
+        });
+      } finally {
+        modalApi.lock(false);
+      }
+    },
+    async onOpenChange(isOpen: boolean) {
+      if (!isOpen) {
+        formData.value = undefined;
+        return;
+      }
+
+      // 加载数据
+      let data = modalApi.getData<${simpleClassName}Api.${subSimpleClassName}>();
+      if (!data) {
+        return;
+      }
+      if (data.id) {
+        modalApi.lock();
+        try {
+          data = await get${subSimpleClassName}(data.id);
+        } finally {
+          modalApi.lock(false);
+        }
+      }
+      // 设置到 values
+      formData.value = data;
+      await formApi.setValues(formData.value);
+    },
+  });
 </script>
+
+<template>
+  <Modal :title="getTitle">
+    <Form class="mx-4" />
+  </Modal>
+</template>

+ 1 - 1
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_inner.vue.vm

@@ -1,2 +1,2 @@
 ## 主表的 normal 和 inner 使用相同的 form 表单
-#parse("codegen/vue3_vben5_antd/general/views/modules/form_sub_normal.vue.vm")
+#parse("codegen/vue3_vben5_antd/schema/views/modules/form_sub_normal.vue.vm")

+ 172 - 342
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_normal.vue.vm

@@ -2,361 +2,191 @@
 #set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组
 #set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
 #set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))
-#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
+#set ($subClassNameVar = $subClassNameVars.get($subIndex))
 #set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
-<template>
-#if ( $subTable.subJoinMany )## 情况一:一对多,table + form
-  <Form
-    ref="formRef"
-    :model="formData"
-    :rules="formRules"
-    :loading="formLoading"
-    label-col="{ span: 0 }"
-  >
-    <Table :dataSource="formData" class="-mt-10px">
-      <Column title="序号" width="100">
-        <template #customRender="{ index }">
-          {{ index + 1 }}
-        </template>
-      </Column>
-#foreach($column in $subColumns)
-    #if ($column.createOperation || $column.updateOperation)
-        #set ($dictType = $column.dictType)
-        #set ($javaField = $column.javaField)
-        #set ($javaType = $column.javaType)
-        #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
-        #set ($comment = $column.columnComment)
-        #set ($dictMethod = "getDictOptions")## 计算使用哪个 dict 字典方法
-        #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
-            #set ($dictMethod = "getIntDictOptions")
-        #elseif ($javaType == "String")
-            #set ($dictMethod = "getStrDictOptions")
-        #elseif ($javaType == "Boolean")
-            #set ($dictMethod = "getBoolDictOptions")
-        #end
-        #if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
-        #elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
-      <Column title="${comment}" :minWidth="150">
-        <template #customRender="{ record, index }">
-          <FormItem :name="[index, '${javaField}']" :rules="formRules.${javaField}" class="mb-0px!">
-            <Input v-model:value="record.${javaField}" placeholder="请输入${comment}" />
-          </FormItem>
-        </template>
-      </Column>
-        #elseif($column.htmlType == "imageUpload")## 图片上传
-      <Column title="${comment}" :minWidth="200">
-        <template #customRender="{ record, index }">
-          <FormItem :name="[index, '${javaField}']" :rules="formRules.${javaField}" class="mb-0px!">
-            <UploadImg v-model:value="record.${javaField}" />
-          </FormItem>
-        </template>
-      </Column>
-        #elseif($column.htmlType == "fileUpload")## 文件上传
-      <Column title="${comment}" :minWidth="200">
-        <template #customRender="{ record, index }">
-          <FormItem :name="[index, '${javaField}']" :rules="formRules.${javaField}" class="mb-0px!">
-            <UploadFile v-model:value="record.${javaField}" />
-          </FormItem>
-        </template>
-      </Column>
-        #elseif($column.htmlType == "editor")## 文本编辑器
-      <Column title="${comment}" :minWidth="400">
-        <template #customRender="{ record, index }">
-          <FormItem :name="[index, '${javaField}']" :rules="formRules.${javaField}" class="mb-0px!">
-            <Editor v-model:value="record.${javaField}" height="150px" />
-          </FormItem>
-        </template>
-      </Column>
-        #elseif($column.htmlType == "select")## 下拉框
-      <Column title="${comment}" :minWidth="150">
-        <template #customRender="{ record, index }">
-          <FormItem :name="[index, '${javaField}']" :rules="formRules.${javaField}" class="mb-0px!">
-            <Select v-model:value="record.${javaField}" placeholder="请选择${comment}">
-              #if ("" != $dictType)## 有数据字典
-                <SelectOption
-                  v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
-                  :key="dict.value"
-                  :label="dict.label"
-                  :value="dict.value"
-                />
-              #else##没数据字典
-                <SelectOption label="请选择字典生成" value="" />
-              #end
-            </Select>
-          </FormItem>
-        </template>
-      </Column>
-        #elseif($column.htmlType == "checkbox")## 多选框
-      <Column title="${comment}" :minWidth="150">
-        <template #customRender="{ record, index }">
-          <FormItem :name="[index, '${javaField}']" :rules="formRules.${javaField}" class="mb-0px!">
-            <CheckboxGroup v-model:value="record.${javaField}">
-              #if ("" != $dictType)## 有数据字典
-                <Checkbox
-                  v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
-                  :key="dict.value"
-                  :label="dict.label"
-                  :value="dict.value"
-                />
-              #else##没数据字典
-                <Checkbox label="请选择字典生成" />
-              #end
-            </CheckboxGroup>
-          </FormItem>
-        </template>
-      </Column>
-        #elseif($column.htmlType == "radio")## 单选框
-      <Column title="${comment}" :minWidth="150">
-        <template #customRender="{ record, index }">
-          <FormItem :name="[index, '${javaField}']" :rules="formRules.${javaField}" class="mb-0px!">
-            <RadioGroup v-model:value="record.${javaField}">
-              #if ("" != $dictType)## 有数据字典
-                <Radio
-                  v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
-                  :key="dict.value"
-                  :value="dict.value"
-                >
-                  {{ dict.label }}
-                </Radio>
-              #else##没数据字典
-                <Radio value="1">请选择字典生成</Radio>
-              #end
-            </RadioGroup>
-          </FormItem>
-        </template>
-      </Column>
-        #elseif($column.htmlType == "datetime")## 时间框
-      <Column title="${comment}" :minWidth="150">
-        <template #customRender="{ record, index }">
-          <FormItem :name="[index, '${javaField}']" :rules="formRules.${javaField}" class="mb-0px!">
-            <DatePicker
-              v-model:value="record.${javaField}"
-              valueFormat="x"
-              placeholder="选择${comment}"
-            />
-          </FormItem>
-        </template>
-      </Column>
-        #elseif($column.htmlType == "textarea")## 文本框
-      <Column title="${comment}" :minWidth="200">
-        <template #customRender="{ record, index }">
-          <FormItem :name="[index, '${javaField}']" :rules="formRules.${javaField}" class="mb-0px!">
-            <Textarea v-model:value="record.${javaField}" placeholder="请输入${comment}" />
-          </FormItem>
-        </template>
-      </Column>
-        #end
-    #end
-#end
-      <Column align="center" fixed="right" title="操作" width="60">
-        <template #customRender="{ index }">
-          <Button @click="handleDelete(index)" type="link">—</Button>
-        </template>
-      </Column>
-    </Table>
-  </Form>
-  <Row justify="center" class="mt-3">
-    <Button @click="handleAdd" shape="round">+ 添加${subTable.classComment}</Button>
-  </Row>
-#else## 情况二:一对一,form
-  <Form
-    ref="formRef"
-    :model="formData"
-    :rules="formRules"
-    label-col="{ span: 6 }"
-    :loading="formLoading"
-  >
-#foreach($column in $subColumns)
-  #if ($column.createOperation || $column.updateOperation)
-  #set ($dictType = $column.dictType)
-      #set ($javaField = $column.javaField)
-      #set ($javaType = $column.javaType)
-      #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
-      #set ($comment = $column.columnComment)
-      #set ($dictMethod = "getDictOptions")## 计算使用哪个 dict 字典方法
-      #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
-        #set ($dictMethod = "getIntDictOptions")
-      #elseif ($javaType == "String")
-          #set ($dictMethod = "getStrDictOptions")
-      #elseif ($javaType == "Boolean")
-          #set ($dictMethod = "getBoolDictOptions")
-      #end
-      #if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
-      #elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
-    <FormItem label="${comment}" name="${javaField}">
-      <Input v-model:value="formData.${javaField}" placeholder="请输入${comment}" />
-    </FormItem>
-      #elseif($column.htmlType == "imageUpload")## 图片上传
-    <FormItem label="${comment}" name="${javaField}">
-      <UploadImg v-model:value="formData.${javaField}" />
-    </FormItem>
-      #elseif($column.htmlType == "fileUpload")## 文件上传
-    <FormItem label="${comment}" name="${javaField}">
-      <UploadFile v-model:value="formData.${javaField}" />
-    </FormItem>
-      #elseif($column.htmlType == "editor")## 文本编辑器
-    <FormItem label="${comment}" name="${javaField}">
-      <Editor v-model:value="formData.${javaField}" height="150px" />
-    </FormItem>
-      #elseif($column.htmlType == "select")## 下拉框
-    <FormItem label="${comment}" name="${javaField}">
-      <Select v-model:value="formData.${javaField}" placeholder="请选择${comment}">
-              #if ("" != $dictType)## 有数据字典
-        <SelectOption
-          v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
-          :key="dict.value"
-          :label="dict.label"
-          :value="dict.value"
-        />
-              #else##没数据字典
-        <SelectOption label="请选择字典生成" value="" />
-              #end
-      </Select>
-    </FormItem>
-      #elseif($column.htmlType == "checkbox")## 多选框
-    <FormItem label="${comment}" name="${javaField}">
-      <CheckboxGroup v-model:value="formData.${javaField}">
-              #if ("" != $dictType)## 有数据字典
-        <Checkbox
-          v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
-          :key="dict.value"
-          :label="dict.label"
-          :value="dict.value"
-        />
-              #else##没数据字典
-        <Checkbox label="请选择字典生成" />
-              #end
-      </CheckboxGroup>
-    </FormItem>
-      #elseif($column.htmlType == "radio")## 单选框
-    <FormItem label="${comment}" name="${javaField}">
-      <RadioGroup v-model:value="formData.${javaField}">
-              #if ("" != $dictType)## 有数据字典
-        <Radio
-          v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
-          :key="dict.value"
-          :value="dict.value"
-          >
-          {{ dict.label }}
-        </Radio>
-              #else##没数据字典
-        <Radio value="1">请选择字典生成</Radio>
-              #end
-      </RadioGroup>
-    </FormItem>
-      #elseif($column.htmlType == "datetime")## 时间框
-    <FormItem label="${comment}" name="${javaField}">
-      <DatePicker
-        v-model:value="formData.${javaField}"
-        valueFormat="x"
-        placeholder="选择${comment}"
-      />
-    </FormItem>
-      #elseif($column.htmlType == "textarea")## 文本框
-    <FormItem label="${comment}" name="${javaField}">
-      <Textarea v-model:value="formData.${javaField}" placeholder="请输入${comment}" />
-    </FormItem>
-      #end
-  #end
-#end
-  </Form>
+<script lang="ts" setup>
+  import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
+
+  import { computed, ref, h, onMounted,watch,nextTick } from 'vue';
+  import { $t } from '#/locales';
+
+#if ($subTable.subJoinMany) ## 一对多
+import { Plus } from "@vben/icons";
+import { Button, Tabs, Checkbox, Input, Textarea, Select,RadioGroup,CheckboxGroup, DatePicker } from 'ant-design-vue';
+import type { OnActionClickParams } from '#/adapter/vxe-table';
+import { useVbenVxeGrid } from '#/adapter/vxe-table';
+import { use${subSimpleClassName}GridEditColumns } from '../data';
+import { get${subSimpleClassName}ListBy${SubJoinColumnName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
+#else
+import { useVbenForm } from '#/adapter/form';
+import { use${subSimpleClassName}FormSchema } from '../data';
+import { get${subSimpleClassName}By${SubJoinColumnName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
 #end
-</template>
-<script setup lang="ts">
-import { Form, FormItem, Input, Button, Table, Column, Select, SelectOption, DatePicker, Textarea, Checkbox, CheckboxGroup, Radio, RadioGroup, Row } from 'ant-design-vue'
-import { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
-import { ${simpleClassName}Api } from '@/api/${table.moduleName}/${table.businessName}'
 
 const props = defineProps<{
-  ${subJoinColumn.javaField}: undefined // ${subJoinColumn.columnComment}(主表的关联字段)
+   ${subJoinColumn.javaField}?: number // ${subJoinColumn.columnComment}(主表的关联字段)
 }>()
-const formLoading = ref(false) // 表单的加载中
-const formData = ref([])
-const formRules = reactive({
-#foreach ($column in $subColumns)
-    #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键
-        #set($comment=$column.columnComment)
-  $column.javaField: [{ required: true, message: '${comment}不能为空', trigger: #if($column.htmlType == 'select')'change'#else'blur'#end }],
-    #end
-#end
-})
-const formRef = ref() // 表单 Ref
 
-/** 监听主表的关联字段的变化,加载对应的子表数据 */
-watch(
-  () => props.${subJoinColumn.javaField},
-  async (val) => {
-    // 1. 重置表单
-#if ( $subTable.subJoinMany )
-    formData.value = []
-#else
-    formData.value = {
-    #foreach ($column in $subColumns)
-      #if ($column.createOperation || $column.updateOperation)
-        #if ($column.htmlType == "checkbox")
-      $column.javaField: [],
-        #else
-      $column.javaField: undefined,
-        #end
-      #end
-    #end
+#if ($subTable.subJoinMany) ## 一对多
+/** 表格操作按钮的回调函数 */
+function onActionClick({
+ code,
+ row,
+}: OnActionClickParams<${simpleClassName}Api.${subSimpleClassName}>) {
+  switch (code) {
+    case 'delete': {
+      onDelete(row);
+      break;
     }
-#end
-    // 2. val 非空,则加载数据
-    if (!val) {
-      return;
-    }
-    try {
-      formLoading.value = true
-#if ( $subTable.subJoinMany )
-      formData.value = await ${simpleClassName}Api.get${subSimpleClassName}ListBy${SubJoinColumnName}(val)
-#else
-      const data = await ${simpleClassName}Api.get${subSimpleClassName}By${SubJoinColumnName}(val)
-      if (!data) {
-        return
-      }
-      formData.value = data
-#end
-    } finally {
-      formLoading.value = false
-    }
-  },
-  { immediate: true }
-)
-#if ( $subTable.subJoinMany )
-
-/** 新增按钮操作 */
-const handleAdd = () => {
-  const row = {
-#foreach ($column in $subColumns)
-    #if ($column.createOperation || $column.updateOperation)
-      #if ($column.htmlType == "checkbox")
-    $column.javaField: [],
-      #else
-    $column.javaField: undefined,
-      #end
-  #end
-#end
   }
-  row.${subJoinColumn.javaField} = props.${subJoinColumn.javaField}
-  formData.value.push(row)
 }
 
-/** 删除按钮操作 */
-const handleDelete = (index) => {
-  formData.value.splice(index, 1)
-}
-#end
+const [Grid, gridApi] = useVbenVxeGrid({
+gridOptions: {
+  columns: use${subSimpleClassName}GridEditColumns(onActionClick),
+  border: true,
+  showOverflow: true,
+  autoResize: true,
+  keepSource: true,
+  rowConfig: {
+    keyField: 'id',
+  },
+  pagerConfig: {
+    enabled: false,
+  },
+  toolbarConfig: {
+    enabled: false,
+  },
+},
+});
 
-/** 表单校验 */
-const validate = () => {
-  return formRef.value.validate()
+/** 添加${subTable.classComment} */
+const onAdd = async () => {
+  await gridApi.grid.insertAt({} as ${simpleClassName}Api.${subSimpleClassName}, -1);
 }
 
-/** 表单值 */
-const getData = () => {
-  return formData.value
+/** 删除${subTable.classComment} */
+const onDelete =  async (row: ${simpleClassName}Api.${subSimpleClassName}) => {
+  await gridApi.grid.remove(row);
 }
 
-defineExpose({ validate, getData })
+/** 提供获取表格数据的方法供父组件调用 */
+defineExpose({
+  getData: (): ${simpleClassName}Api.${subSimpleClassName}[] => {
+    const data = gridApi.grid.getData() as ${simpleClassName}Api.${subSimpleClassName}[];
+    const removeRecords = gridApi.grid.getRemoveRecords() as ${simpleClassName}Api.${subSimpleClassName}[];
+    const insertRecords = gridApi.grid.getInsertRecords() as ${simpleClassName}Api.${subSimpleClassName}[];
+    return data
+        .filter((row) => !removeRecords.some((removed) => removed.id === row.id))
+        .concat(insertRecords.map((row: any) => ({ ...row, id: undefined })));
+  },
+});
+
+/** 监听主表的关联字段的变化,加载对应的子表数据 */
+watch(
+    () => props.${subJoinColumn.javaField},
+    async (val) => {
+      if (!val) {
+        return;
+      }
+      await nextTick();
+      await gridApi.grid.loadData(await get${subSimpleClassName}ListBy${SubJoinColumnName}(props.${subJoinColumn.javaField}!));
+    },
+    { immediate: true },
+);
+#else
+const [Form, formApi] = useVbenForm({
+layout: 'horizontal',
+schema: use${subSimpleClassName}FormSchema(),
+showDefaultActions: false
+});
+
+/** 暴露出表单校验方法和表单值获取方法 */
+defineExpose({
+  validate: async () => {
+    const { valid } = await formApi.validate();
+    return valid;
+  },
+  getValues: formApi.getValues,
+});
+
+/** 监听主表的关联字段的变化,加载对应的子表数据 */
+watch(
+    () => props.${subJoinColumn.javaField},
+    async (val) => {
+      if (!val) {
+        return;
+      }
+      await nextTick();
+      await formApi.setValues(await get${subSimpleClassName}By${SubJoinColumnName}(props.${subJoinColumn.javaField}!));
+    },
+    { immediate: true },
+);
+#end
 </script>
+
+<template>
+#if ($subTable.subJoinMany) ## 一对多
+  <Grid class="mx-4">
+      #foreach($column in $subColumns)
+          #if ($column.createOperation || $column.updateOperation)
+              #set ($javaField = $column.javaField)
+              #if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
+              #elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
+                <template #${javaField}="{ row }">
+                  <Input v-model:value="row.${javaField}" />
+                </template>
+              #elseif($column.htmlType == "imageUpload")## 图片上传
+                <template #${javaField}="{ row }">
+                  <UploadImg v-model:value="row.${javaField}" />
+                </template>
+              #elseif($column.htmlType == "fileUpload")## 文件上传
+                <template #${javaField}="{ row }">
+                  <UploadFile v-model:value="row.${javaField}" />
+                </template>
+              #elseif($column.htmlType == "editor")## 文本编辑器
+                <template #${javaField}="{ row }">
+                  <Textarea v-model:value="row.${javaField}" />
+                </template>
+              #elseif($column.htmlType == "select")## 下拉框
+                <template #${javaField}="{ row, column }">
+                  <Select v-model:value="row.${javaField}" class="w-full">
+                    <Select.Option v-for="option in column.params.options" :key="option.value" :value="option.value">
+                      {{ option.label }}
+                    </Select.Option>
+                  </Select>
+                </template>
+              #elseif($column.htmlType == "checkbox")## 多选框
+                <template #${javaField}="{ row, column }">
+                  <CheckboxGroup v-model:value="row.${javaField}" :options="column.params.options" />
+                </template>
+              #elseif($column.htmlType == "radio")## 单选框
+                <template #${javaField}="{ row, column }">
+                  <RadioGroup v-model:value="row.${javaField}" :options="column.params.options" />
+                </template>
+              #elseif($column.htmlType == "datetime")## 时间框
+                <template #${javaField}="{ row }">
+                  <DatePicker
+                      v-model:value="row.${javaField}"
+                      :showTime="true"
+                      format="YYYY-MM-DD HH:mm:ss"
+                      valueFormat='x'
+                  />
+                </template>
+              #elseif($column.htmlType == "textarea")## 文本框
+                <template #${javaField}="{ row }">
+                  <Textarea v-model:value="row.${javaField}" />
+                </template>
+              #end
+          #end
+      #end
+  </Grid>
+  <div class="flex justify-center -mt-4">
+    <Button :icon="h(Plus)" type="primary" ghost @click="onAdd" v-access:code="['${subTable.moduleName}:${simpleClassName_strikeCase}:create']">
+      {{ $t('ui.actionTitle.create', ['${subTable.classComment}']) }}
+    </Button>
+  </div>
+#else
+  <Form class="mx-4" />
+#end
+</template>

+ 158 - 157
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/list_sub_erp.vue.vm

@@ -3,181 +3,182 @@
 #set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
 #set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))
 #set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
+#set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($subIndex))
 #set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
-<template>
-  <!-- 列表 -->
-  <ContentWrap>
-#if ($table.templateType == 11)
-    <Button
-      type="primary"
-      @click="openForm('create')"
-      v-hasPermi="['${permissionPrefix}:create']"
-    >
-      <Icon icon="ep:plus" class="mr-5px" /> 新增
-    </Button>
-#end
-    <Table :loading="loading" :dataSource="list" :rowKey="(record) => record.id" :bordered="true">
-      #foreach($column in $subColumns)
-      #if ($column.listOperationResult)
-        #set ($dictType=$column.dictType)
-        #set ($javaField = $column.javaField)
-        #set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
-        #set ($comment=$column.columnComment)
-        #if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
-        #elseif ($column.javaType == "LocalDateTime")## 时间类型
-      <Column
-        title="${comment}"
-        align="center"
-        dataIndex="${javaField}"
-        :customRender="({ text }) => dateFormatter(text)"
-        width="180px"
-      />
-        #elseif($column.dictType && "" != $column.dictType)## 数据字典
-      <Column title="${comment}" align="center" dataIndex="${javaField}">
-        <template #customRender="{ text }">
-          <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="text" />
-        </template>
-      </Column>
-        #else
-      <Column title="${comment}" align="center" dataIndex="${javaField}" />
-        #end
-      #end
-    #end
-    #if ($table.templateType == 11)
-      <Column title="操作" align="center">
-        <template #customRender="{ record }">
-          <Button
-            type="link"
-            @click="openForm('update', record.id)"
-            v-hasPermi="['${permissionPrefix}:update']"
-          >
-            编辑
-          </Button>
-          <Button
-            type="link"
-            danger
-            @click="handleDelete(record.id)"
-            v-hasPermi="['${permissionPrefix}:delete']"
-          >
-            删除
-          </Button>
-        </template>
-      </Column>
-    #end
-    </Table>
-    #if ($table.templateType == 11)
-    <!-- 分页 -->
-    <Pagination
-      :total="total"
-      v-model:current="queryParams.pageNo"
-      v-model:pageSize="queryParams.pageSize"
-      @change="handleQuery"
-    />
-    #end
-  </ContentWrap>
-#if ($table.templateType == 11)
-    <!-- 表单弹窗:添加/修改 -->
-    <${subSimpleClassName}Form ref="formRef" @success="getList" />
-#end
-</template>
-<script setup lang="ts">
-import { Table, Column, Button, Pagination } from 'ant-design-vue'
-import { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
-import { dateFormatter } from '@/utils/formatTime'
-import { ${simpleClassName}Api } from '@/api/${table.moduleName}/${table.businessName}'
-#if ($table.templateType == 11)
-import ${subSimpleClassName}Form from './${subSimpleClassName}Form.vue'
+<script lang="ts" setup>
+  import type { OnActionClickParams, VxeTableGridOptions } from '#/adapter/vxe-table';
+  import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
+
+#if ($table.templateType == 11) ## erp
+  import ${subSimpleClassName}Form from './${subSimpleClassName_strikeCase}-form.vue'
 #end
+  import { useVbenModal } from '@vben/common-ui';
+  import { Button, message } from 'ant-design-vue';
+  import { Plus } from '@vben/icons';
+  import { #if($table.templateType != 11)ref,#end h, nextTick,watch } from 'vue';
+  import { $t } from '#/locales';
+  import { useVbenVxeGrid } from '#/adapter/vxe-table';
 
-const { t } = useI18n() // 国际化
-const message = useMessage() // 消息弹窗
+
+#if ($table.templateType == 11) ## erp
+  import { delete${subSimpleClassName}, get${subSimpleClassName}Page } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
+  import { use${subSimpleClassName}GridFormSchema, use${subSimpleClassName}GridColumns } from '../data';
+  #else
+  #if ($subTable.subJoinMany) ## 一对多
+  import { get${subSimpleClassName}ListBy${SubJoinColumnName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
+  #else
+  import { get${subSimpleClassName}By${SubJoinColumnName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
+  #end
+  import { use${subSimpleClassName}GridColumns } from '../data';
+#end
 
 const props = defineProps<{
-  ${subJoinColumn.javaField}?: number // ${subJoinColumn.columnComment}(主表的关联字段)
+      ${subJoinColumn.javaField}?: number // ${subJoinColumn.columnComment}(主表的关联字段)
 }>()
-const loading = ref(false) // 列表的加载中
-const list = ref([]) // 列表的数据
-#if ($table.templateType == 11)
-const total = ref(0) // 列表的总页数
-const queryParams = reactive({
-  pageNo: 1,
-  pageSize: 10,
-  ${subJoinColumn.javaField}: undefined as unknown
-})
 
-/** 监听主表的关联字段的变化,加载对应的子表数据 */
-watch(
-  () => props.${subJoinColumn.javaField},
-  (val: number) => {
-    if (!val) {
-      return
-    }
-    queryParams.${subJoinColumn.javaField} = val
-    handleQuery()
-  },
-    { immediate: true, deep: true }
-)
-#end
+#if ($table.templateType == 11) ## erp
+  const [FormModal, formModalApi] = useVbenModal({
+    connectedComponent: ${subSimpleClassName}Form,
+    destroyOnClose: true,
+  });
 
-/** 查询列表 */
-const getList = async () => {
-  loading.value = true
-  try {
-#if ($table.templateType == 11)
-    const data = await ${simpleClassName}Api.get${subSimpleClassName}Page(queryParams)
-    list.value = data.list
-    total.value = data.total
-#else
-  #if ( $subTable.subJoinMany )
-    list.value = await ${simpleClassName}Api.get${subSimpleClassName}ListBy${SubJoinColumnName}(props.${subJoinColumn.javaField})
-  #else
-    const data = await ${simpleClassName}Api.get${subSimpleClassName}By${SubJoinColumnName}(props.${subJoinColumn.javaField})
-    if (!data) {
-      return
-    }
-    list.value.push(data)
-  #end
-#end
-  } finally {
-    loading.value = false
+/** 创建${subTable.classComment} */
+function onCreate() {
+  if (!props.${subJoinColumn.javaField}){
+    message.warning("请先选择一个${table.classComment}!")
+    return
   }
+  formModalApi.setData({${subJoinColumn.javaField}: props.${subJoinColumn.javaField}}).open();
 }
 
-/** 搜索按钮操作 */
-const handleQuery = () => {
-  queryParams.pageNo = 1
-  getList()
+/** 编辑${subTable.classComment} */
+function onEdit(row: ${simpleClassName}Api.${subSimpleClassName}) {
+  formModalApi.setData(row).open();
 }
-#if ($table.templateType == 11)
 
-/** 添加/修改操作 */
-const formRef = ref()
-const openForm = (type: string, id?: number) => {
-  if (!props.${subJoinColumn.javaField}) {
-    message.error('请选择一个${table.classComment}')
-    return
+/** 删除${subTable.classComment} */
+async function onDelete(row: ${simpleClassName}Api.${subSimpleClassName}) {
+  const hideLoading = message.loading({
+    content: $t('ui.actionMessage.deleting', [row.id]),
+    duration: 0,
+    key: 'action_process_msg',
+  });
+  try {
+    await delete${subSimpleClassName}(row.id as number);
+    message.success({
+      content: $t('ui.actionMessage.deleteSuccess', [row.id]),
+      key: 'action_process_msg',
+    });
+    onRefresh();
+  } catch {
+    hideLoading();
   }
-  formRef.value.open(type, id, props.${subJoinColumn.javaField})
 }
 
-/** 删除按钮操作 */
-const handleDelete = async (id: number) => {
-  try {
-    // 删除的二次确认
-    await message.delConfirm()
-    // 发起删除
-    await ${simpleClassName}Api.delete${subSimpleClassName}(id)
-    message.success(t('common.delSuccess'))
-    // 刷新列表
-    await getList()
-  } catch {}
+/** 表格操作按钮的回调函数 */
+function onActionClick({
+ code,
+ row,
+}: OnActionClickParams<${simpleClassName}Api.${subSimpleClassName}>) {
+  switch (code) {
+    case 'edit': {
+      onEdit(row);
+      break;
+    }
+    case 'delete': {
+      onDelete(row);
+      break;
+    }
+  }
 }
+
+#end
+  const [Grid, gridApi] = useVbenVxeGrid({
+#if ($table.templateType == 11)
+    formOptions: {
+      schema: use${subSimpleClassName}GridFormSchema(),
+    },
+#end
+    gridOptions: {
+#if ($table.templateType == 11)
+    columns: use${subSimpleClassName}GridColumns(onActionClick),
+      proxyConfig: {
+        ajax: {
+          query: async ({ page }, formValues) => {
+              if (!props.${subJoinColumn.javaField}){
+                  return []
+              }
+            return await get${subSimpleClassName}Page({
+              pageNo: page.currentPage,
+              pageSize: page.pageSize,
+              ${subJoinColumn.javaField}: props.${subJoinColumn.javaField},
+              ...formValues,
+            });
+          },
+        },
+      },
+    pagerConfig: {
+        enabled: true,
+    },
+    toolbarConfig: {
+        refresh: { code: 'query' },
+        search: true,
+    },
+#else
+    columns: use${subSimpleClassName}GridColumns(),
+    pagerConfig: {
+        enabled: false,
+    },
+    toolbarConfig: {
+        enabled: false,
+    },
 #end
-#if ($table.templateType != 11)
+    height: '600px',
+    rowConfig: {
+        keyField: 'id',
+        isHover: true,
+    },
+    } as VxeTableGridOptions<${simpleClassName}Api.${subSimpleClassName}>,
+  });
 
-/** 初始化 **/
-onMounted(() => {
-  getList()
-})
+/** 刷新表格 */
+const onRefresh = async ()=> {
+#if ($table.templateType == 11) ## erp
+    await gridApi.query();
+#else
+    #if ($subTable.subJoinMany) ## 一对多
+    await gridApi.grid.loadData(await get${subSimpleClassName}ListBy${SubJoinColumnName}(props.${subJoinColumn.javaField}!));
+    #else
+    await gridApi.grid.loadData([await get${subSimpleClassName}By${SubJoinColumnName}(props.${subJoinColumn.javaField}!)]);
+    #end
 #end
+}
+
+  /** 监听主表的关联字段的变化,加载对应的子表数据 */
+  watch(
+      () => props.${subJoinColumn.javaField},
+      async (val) => {
+        if (!val) {
+          return;
+        }
+        await nextTick();
+        await onRefresh()
+      },
+      { immediate: true },
+  );
 </script>
+
+<template>
+    #if ($table.templateType == 11) ## erp
+      <FormModal @success="onRefresh" />
+      <Grid table-title="${subTable.classComment}列表">
+        <template #toolbar-tools>
+          <Button :icon="h(Plus)" type="primary" @click="onCreate" v-access:code="['${table.moduleName}:${simpleClassName_strikeCase}:create']">
+            {{ $t('ui.actionTitle.create', ['${subTable.classComment}']) }}
+          </Button>
+        </template>
+      </Grid>
+    #else
+      <Grid table-title="${subTable.classComment}列表" />
+    #end
+</template>

+ 1 - 1
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/list_sub_inner.vue.vm

@@ -1,4 +1,4 @@
 ## 子表的 erp 和 inner 使用相似的 list 列表,差异主要两点:
 ## 1)inner 使用 list 不分页,erp 使用 page 分页
 ## 2)erp 支持单个子表的新增、修改、删除,inner 不支持
-#parse("codegen/vue3_vben5_antd/general/views/modules/list_sub_erp.vue.vm")
+#parse("codegen/vue3_vben5_antd/schema/views/modules/list_sub_erp.vue.vm")