Procházet zdrojové kódy

!1326 代码生成: vue3_vben5_antd schema 主子表标准模式和内嵌模式模板完成
Merge pull request !1326 from puhui999/master-jdk17

芋道源码 před 6 měsíci
rodič
revize
08eb75ea0d
13 změnil soubory, kde provedl 1474 přidání a 431 odebrání
  1. 20 15
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java
  2. 0 0
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/index.vue.vm
  3. 38 6
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/api/api.ts.vm
  4. 654 0
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/data.ts.vm
  5. 164 0
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/form.vue.vm
  6. 51 16
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/index.vue.vm
  7. 193 0
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_erp.vue.vm
  8. 2 0
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_inner.vue.vm
  9. 203 0
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_normal.vue.vm
  10. 145 0
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/list_sub_erp.vue.vm
  11. 4 0
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/list_sub_inner.vue.vm
  12. 0 276
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/data.ts.vm
  13. 0 118
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/form.vue.vm

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

@@ -146,23 +146,24 @@ public class CodegenEngine {
                     vue3FilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
             // VUE3_VBEN5_ANTD_SCHEMA
             // TODO @puhui999:目录改成 vue3_vben5_antd;然后里面有 schema(目前我们在写的)和 general(你微信里提的,原生的,感觉也要搞!)
-            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3VbenNextSchemaTemplatePath("views/data.ts"),
+            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/data.ts"),
                     vue3FilePath("views/${table.moduleName}/${table.businessName}/data.ts"))
-            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3VbenNextSchemaTemplatePath("views/index.vue"),
+            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/index.vue"),
                     vue3FilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
-            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3VbenNextSchemaTemplatePath("views/form.vue"),
+            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/form.vue"),
                     vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/form.vue"))
-            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3VbenNextSchemaTemplatePath("api/api.ts"),
+            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("api/api.ts"),
                     vue3FilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
-            // 主子表模板配置 - Vue3 vben5 schema 模版
-            //.put(CodegenFrontTypeEnum.VUE3_VBEN_NEXT_SCHEMA.getType(), vue3VbenNextSchemaTemplatePath("views/master_slave_data.ts"),
-            //        vue3FilePath("views/${table.moduleName}/${table.businessName}/data.ts"))
-            //.put(CodegenFrontTypeEnum.VUE3_VBEN_NEXT_SCHEMA.getType(), vue3VbenNextSchemaTemplatePath("views/master_slave_index.vue"),
-            //        vue3FilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
-            //.put(CodegenFrontTypeEnum.VUE3_VBEN_NEXT_SCHEMA.getType(), vue3VbenNextSchemaTemplatePath("views/modules/master_slave_form.vue"),
-            //        vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/form.vue"))
-            //.put(CodegenFrontTypeEnum.VUE3_VBEN_NEXT_SCHEMA.getType(), vue3VbenNextSchemaTemplatePath("views/modules/sub_table.vue"),
-            //        vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/sub_table.vue"))
+            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/form_sub_normal.vue"),  // 特殊:主子表专属逻辑
+                    vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName}Form.vue"))
+            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/form_sub_inner.vue"),  // 特殊:主子表专属逻辑
+                    vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName}Form.vue"))
+            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/form_sub_erp.vue"),  // 特殊:主子表专属逻辑
+                    vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName}Form.vue"))
+            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/list_sub_inner.vue"),  // 特殊:主子表专属逻辑
+                    vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName}List.vue"))
+            .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/list_sub_erp.vue"),  // 特殊:主子表专属逻辑
+                    vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName}List.vue"))
             .build();
 
     @Resource
@@ -515,8 +516,12 @@ public class CodegenEngine {
         return "codegen/vue3_vben/" + path + ".vm";
     }
 
-    private static String vue3VbenNextSchemaTemplatePath(String path) {
-        return "codegen/vue3_vben_next/schema/" + path + ".vm";
+    private static String vue3Vben5AntdSchemaTemplatePath(String path) {
+        return "codegen/vue3_vben5_antd/schema/" + path + ".vm";
+    }
+
+    private static String vue3Vben5AntdGeneralTemplatePath(String path) {
+        return "codegen/vue3_vben5_antd/general/" + path + ".vm";
     }
 
     private static boolean isSubTemplate(String path) {

+ 0 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/ant_design_vue/index.vue.vm → yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/index.vue.vm


+ 38 - 6
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/api/api.ts.vm → yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/api/api.ts.vm

@@ -4,6 +4,26 @@ 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)
@@ -19,6 +39,18 @@ export namespace ${simpleClassName}Api {
 #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
   }
 }
@@ -76,31 +108,31 @@ export function export${simpleClassName}(params: any) {
 #if ( $table.templateType == 11 )
 /** 获得${subTable.classComment}分页 */
 export function get${subSimpleClassName}Page(params: PageParam) {
-  return requestClient.get<PageResult<${simpleClassName}Api.${simpleClassName}>>(`${baseURL}/${subSimpleClassName_strikeCase}/page`, { params });
+  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.${simpleClassName}[]>(`${baseURL}/${subSimpleClassName_strikeCase}/list-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=${${subJoinColumn.javaField}}`);
+  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.${simpleClassName}>(`${baseURL}/${subSimpleClassName_strikeCase}/get-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=${${subJoinColumn.javaField}}`);
+  return requestClient.get<${simpleClassName}Api.${subSimpleClassName}>(`${baseURL}/${subSimpleClassName_strikeCase}/get-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=${${subJoinColumn.javaField}}`);
 }
   #end
 #end
 ## 特殊:MASTER_ERP 时,支持单个的新增、修改、删除操作
 #if ( $table.templateType == 11 )
 /** 新增${subTable.classComment} */
-export function create${subSimpleClassName}(data: ${simpleClassName}Api.${simpleClassName}) {
+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.${simpleClassName}) {
+export function update${subSimpleClassName}(data: ${simpleClassName}Api.${subSimpleClassName}) {
   return requestClient.put(`${baseURL}/${subSimpleClassName_strikeCase}/update`, data);
 }
 
@@ -111,7 +143,7 @@ export function delete${subSimpleClassName}(id: number) {
 
 /** 获得${subTable.classComment} */
 export function get${subSimpleClassName}(id: number) {
-  return requestClient.get<${simpleClassName}Api.${simpleClassName}>(`${baseURL}/${subSimpleClassName_strikeCase}/get?id=${id}`);
+  return requestClient.get<${simpleClassName}Api.${subSimpleClassName}>(`${baseURL}/${subSimpleClassName_strikeCase}/get?id=${id}`);
 }
 #end
 #end

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

@@ -0,0 +1,654 @@
+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 useFormSchema(): VbenFormSchema[] {
+  return [
+    {
+      fieldName: 'id',
+      component: 'Input',
+      dependencies: {
+        triggerFields: [''],
+        show: () => false,
+      },
+    },
+#if(${table.templateType} == 2)## 树表特有字段:上级
+    {
+      fieldName: '${treeParentColumn.javaField}',
+      label: '上级${table.classComment}',
+      component: 'ApiTreeSelect',
+      componentProps: {
+        allowClear: true,
+        api: async () => {
+          const data = await get${simpleClassName}List({});
+          data.unshift({
+            id: 0,
+            ${treeNameColumn.javaField}: '顶级${table.classComment}',
+          });
+          return handleTree(data);
+        },
+        class: 'w-full',
+        labelField: '${treeNameColumn.javaField}',
+        valueField: 'id',
+        childrenField: 'children',
+        placeholder: '请选择上级${table.classComment}',
+        treeDefaultExpandAll: true,
+      },
+      rules: 'selectRequired',
+    },
+#end
+#foreach($column in $columns)
+#if ($column.createOperation || $column.updateOperation)
+#if (!$column.primaryKey && ($table.templateType != 2 || ($table.templateType == 2 && $column.id != $treeParentColumn.id)))## 树表中已经添加了父ID字段,这里排除
+  #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.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键
+      rules: 'required',
+  #end
+  #if ($column.htmlType == "input")
+      component: 'Input',
+      componentProps: {
+        placeholder: '请输入${comment}',
+      },
+  #elseif($column.htmlType == "imageUpload")## 图片上传
+      component: 'FileUpload',
+      componentProps: {
+        fileType: 'image',
+        maxCount: 1,
+      },
+  #elseif($column.htmlType == "fileUpload")## 文件上传
+      component: 'FileUpload',
+      componentProps: {
+        fileType: 'file',
+        maxCount: 1,
+      },
+  #elseif($column.htmlType == "editor")## 文本编辑器
+      component: 'Editor',
+  #elseif($column.htmlType == "select")## 下拉框
+      component: 'Select',
+      componentProps: {
+        #if ("" != $dictType)## 有数据字典
+        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
+        #else##没数据字典
+        options: [],
+        #end
+        placeholder: '请选择${comment}',
+        class: 'w-full',
+      },
+  #elseif($column.htmlType == "checkbox")## 多选框
+      component: 'Checkbox',
+      componentProps: {
+        #if ("" != $dictType)## 有数据字典
+        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
+        #else##没数据字典
+        options: [],
+        #end
+      },
+  #elseif($column.htmlType == "radio")## 单选框
+      component: 'RadioGroup',
+      componentProps: {
+        #if ("" != $dictType)## 有数据字典
+        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
+        #else##没数据字典
+        options: [],
+        #end
+        buttonStyle: 'solid',
+        optionType: 'button',
+      },
+  #elseif($column.htmlType == "datetime")## 时间框
+      component: 'DatePicker',
+      componentProps: {
+        showTime: true,
+        format: 'YYYY-MM-DD HH:mm:ss',
+        valueFormat: 'x',
+      },
+  #elseif($column.htmlType == "textarea")## 文本域
+      component: 'Textarea',
+      componentProps: {
+        placeholder: '请输入${comment}',
+      },
+  #elseif($column.htmlType == "inputNumber")## 数字输入框
+      component: 'InputNumber',
+      componentProps: {
+        min: 0,
+        class: 'w-full',
+        controlsPosition: 'right',
+        placeholder: '请输入${comment}',
+      },
+  #end
+    },
+#end
+#end
+#end
+  ];
+}
+
+/** 列表的搜索表单 */
+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}FormSchema(): VbenFormSchema[] {
+    return [
+        {
+            fieldName: 'id',
+            component: 'Input',
+            dependencies: {
+                triggerFields: [''],
+                show: () => false,
+            },
+        },
+        #foreach($column in $subColumns)
+            #if ($column.createOperation || $column.updateOperation)
+                #if (!$column.primaryKey && ($table.templateType != 2 || ($table.templateType == 2 && $column.id != $treeParentColumn.id)))## 树表中已经添加了父ID字段,这里排除
+                    #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
+                    #if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
+                    #else
+                        {
+                            fieldName: '${javaField}',
+                            label: '${comment}',
+                            #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键
+                                rules: 'required',
+                            #end
+                            #if ($column.htmlType == "input")
+                                component: 'Input',
+                                componentProps: {
+                                    placeholder: '请输入${comment}',
+                                },
+                            #elseif($column.htmlType == "imageUpload")## 图片上传
+                                component: 'FileUpload',
+                                componentProps: {
+                                    fileType: 'image',
+                                    maxCount: 1,
+                                },
+                            #elseif($column.htmlType == "fileUpload")## 文件上传
+                                component: 'FileUpload',
+                                componentProps: {
+                                    fileType: 'file',
+                                    maxCount: 1,
+                                },
+                            #elseif($column.htmlType == "editor")## 文本编辑器
+                                component: 'Editor',
+                            #elseif($column.htmlType == "select")## 下拉框
+                                component: 'Select',
+                                componentProps: {
+                                    #if ("" != $dictType)## 有数据字典
+                                        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
+                                    #else##没数据字典
+                                        options: [],
+                                    #end
+                                    placeholder: '请选择${comment}',
+                                    class: 'w-full',
+                                },
+                            #elseif($column.htmlType == "checkbox")## 多选框
+                                component: 'Checkbox',
+                                componentProps: {
+                                    #if ("" != $dictType)## 有数据字典
+                                        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
+                                    #else##没数据字典
+                                        options: [],
+                                    #end
+                                },
+                            #elseif($column.htmlType == "radio")## 单选框
+                                component: 'RadioGroup',
+                                componentProps: {
+                                    #if ("" != $dictType)## 有数据字典
+                                        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
+                                    #else##没数据字典
+                                        options: [],
+                                    #end
+                                    buttonStyle: 'solid',
+                                    optionType: 'button',
+                                },
+                            #elseif($column.htmlType == "datetime")## 时间框
+                                component: 'DatePicker',
+                                componentProps: {
+                                    showTime: true,
+                                    format: 'YYYY-MM-DD HH:mm:ss',
+                                    valueFormat: 'x',
+                                },
+                            #elseif($column.htmlType == "textarea")## 文本域
+                                component: 'Textarea',
+                                componentProps: {
+                                    placeholder: '请输入${comment}',
+                                },
+                            #elseif($column.htmlType == "inputNumber")## 数字输入框
+                                component: 'InputNumber',
+                                componentProps: {
+                                    min: 0,
+                                    class: 'w-full',
+                                    controlsPosition: 'right',
+                                    placeholder: '请输入${comment}',
+                                },
+                            #end
+                        },
+                    #end
+                #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(['${subTable.moduleName}:${subSimpleClassName_strikeCase}:update']),
+                    },
+                    {
+                        code: 'delete',
+                        show: hasAccessByCodes(['${subTable.moduleName}:${subSimpleClassName_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']),
+                        },
+                    ],
+                },
+            },
+        ];
+    }
+    #else
+    /** 新增/修改的表单 */
+    export function use${subSimpleClassName}FormSchema(): VbenFormSchema[] {
+        return [
+            {
+                fieldName: 'id',
+                component: 'Input',
+                dependencies: {
+                    triggerFields: [''],
+                    show: () => false,
+                },
+            },
+            #foreach($column in $subColumns)
+                #if ($column.createOperation || $column.updateOperation)
+                    #if (!$column.primaryKey && ($table.templateType != 2 || ($table.templateType == 2 && $column.id != $treeParentColumn.id)))## 树表中已经添加了父ID字段,这里排除
+                        #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
+                        #if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
+                        #else
+                            {
+                                fieldName: '${javaField}',
+                                label: '${comment}',
+                                #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键
+                                    rules: 'required',
+                                #end
+                                #if ($column.htmlType == "input")
+                                    component: 'Input',
+                                    componentProps: {
+                                        placeholder: '请输入${comment}',
+                                    },
+                                #elseif($column.htmlType == "imageUpload")## 图片上传
+                                    component: 'FileUpload',
+                                    componentProps: {
+                                        fileType: 'image',
+                                        maxCount: 1,
+                                    },
+                                #elseif($column.htmlType == "fileUpload")## 文件上传
+                                    component: 'FileUpload',
+                                    componentProps: {
+                                        fileType: 'file',
+                                        maxCount: 1,
+                                    },
+                                #elseif($column.htmlType == "editor")## 文本编辑器
+                                    component: 'Editor',
+                                #elseif($column.htmlType == "select")## 下拉框
+                                    component: 'Select',
+                                    componentProps: {
+                                        #if ("" != $dictType)## 有数据字典
+                                            options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
+                                        #else##没数据字典
+                                            options: [],
+                                        #end
+                                        placeholder: '请选择${comment}',
+                                        class: 'w-full',
+                                    },
+                                #elseif($column.htmlType == "checkbox")## 多选框
+                                    component: 'Checkbox',
+                                    componentProps: {
+                                        #if ("" != $dictType)## 有数据字典
+                                            options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
+                                        #else##没数据字典
+                                            options: [],
+                                        #end
+                                    },
+                                #elseif($column.htmlType == "radio")## 单选框
+                                    component: 'RadioGroup',
+                                    componentProps: {
+                                        #if ("" != $dictType)## 有数据字典
+                                            options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
+                                        #else##没数据字典
+                                            options: [],
+                                        #end
+                                        buttonStyle: 'solid',
+                                        optionType: 'button',
+                                    },
+                                #elseif($column.htmlType == "datetime")## 时间框
+                                    component: 'DatePicker',
+                                    componentProps: {
+                                        showTime: true,
+                                        format: 'YYYY-MM-DD HH:mm:ss',
+                                        valueFormat: 'x',
+                                    },
+                                #elseif($column.htmlType == "textarea")## 文本域
+                                    component: 'Textarea',
+                                    componentProps: {
+                                        placeholder: '请输入${comment}',
+                                    },
+                                #elseif($column.htmlType == "inputNumber")## 数字输入框
+                                    component: 'InputNumber',
+                                    componentProps: {
+                                        min: 0,
+                                        class: 'w-full',
+                                        controlsPosition: 'right',
+                                        placeholder: '请输入${comment}',
+                                    },
+                                #end
+                            },
+                        #end
+                    #end
+                #end
+            #end
+        ];
+    }
+    #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

+ 164 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/form.vue.vm

@@ -0,0 +1,164 @@
+<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)
+  import ${subSimpleClassName}Form from './${subSimpleClassName}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<${simpleClassName}Api.${simpleClassName}>();
+#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 [Form, formApi] = useVbenForm({
+  layout: 'horizontal',
+  schema: useFormSchema(),
+  showDefaultActions: false
+});
+
+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 = (await formApi.getValues()) as ${simpleClassName}Api.${simpleClassName};
+    ## 特殊:主子表专属逻辑
+    #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 = undefined;
+      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>
+  <Modal :title="getTitle">
+    <Form class="mx-4" />
+    ## 特殊:主子表专属逻辑
+    #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
+  </Modal>
+</template>

+ 51 - 16
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/index.vue.vm → yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/index.vue.vm

@@ -3,11 +3,18 @@ import type { OnActionClickParams, VxeTableGridOptions } from '#/adapter/vxe-tab
 import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
 
 import { Page, useVbenModal } from '@vben/common-ui';
-import { Button, message } from 'ant-design-vue';
+import { Button, message,Tabs } from 'ant-design-vue';
 import { Download, Plus } from '@vben/icons';
 import Form from './modules/form.vue';
 
-import { ref } from 'vue';
+## 特殊:主子表专属逻辑
+#if ( $table.templateType == 11 || $table.templateType == 12 )
+    #foreach ($subSimpleClassName in $subSimpleClassNames)
+    import ${subSimpleClassName}List from './modules/${subSimpleClassName}List.vue'
+    #end
+#end
+
+import { ref, h } from 'vue';
 import { $t } from '#/locales';
 import { useVbenVxeGrid } from '#/adapter/vxe-table';
 #if (${table.templateType} == 2)## 树表接口
@@ -19,6 +26,11 @@ import { downloadByData } from '#/utils/download';
 
 import { useGridColumns, useGridFormSchema } from './data';
 
+#if ($table.templateType == 12) ## 内嵌情况
+/** 子表的列表 */
+const subTabsName = ref('$subClassNameVars.get(0)')
+#end
+
 const [FormModal, formModalApi] = useVbenModal({
   connectedComponent: Form,
   destroyOnClose: true,
@@ -35,7 +47,11 @@ function toggleExpand() {
 
 /** 刷新表格 */
 function onRefresh() {
+#if ($table.templateType == 12) ## 内嵌情况
+  gridApi.reload();
+#else
   gridApi.query();
+#end
 }
 
 /** 导出表格 */
@@ -46,7 +62,7 @@ async function onExport() {
 
 /** 创建${table.classComment} */
 function onCreate() {
-  formModalApi.setData(null).open();
+  formModalApi.setData({}).open();
 }
 
 /** 编辑${table.classComment} */
@@ -56,7 +72,7 @@ function onEdit(row: ${simpleClassName}Api.${simpleClassName}) {
 
 #if (${table.templateType} == 2)## 树表特有:新增下级
 /** 新增下级${table.classComment} */
-function onAddChild(row: ${simpleClassName}Api.${simpleClassName}) {
+function onAppend(row: ${simpleClassName}Api.${simpleClassName}) {
   formModalApi.setData({ ${treeParentColumn.javaField}: row.id }).open();
 }
 #end
@@ -86,20 +102,20 @@ function onActionClick({
   row,
 }: OnActionClickParams<${simpleClassName}Api.${simpleClassName}>) {
   switch (code) {
-    case 'edit': {
-      onEdit(row);
+  #if (${table.templateType} == 2)## 树表特有:新增下级
+    case 'append': {
+      onAppend(row);
       break;
     }
+  #end
     case 'delete': {
       onDelete(row);
       break;
     }
-#if (${table.templateType} == 2)## 树表特有:新增下级
-    case 'add_child': {
-      onAddChild(row);
+    case 'edit': {
+      onEdit(row);
       break;
     }
-#end
   }
 }
 
@@ -134,12 +150,11 @@ const [Grid, gridApi] = useVbenVxeGrid({
         },
 #else## 标准表数据加载
         query: async ({ page }, formValues) => {
-          const { items, total } = await get${simpleClassName}Page({
+          return await get${simpleClassName}Page({
             pageNo: page.currentPage,
             pageSize: page.pageSize,
             ...formValues,
           });
-          return { items, total };
         },
 #end
       },
@@ -161,18 +176,38 @@ const [Grid, gridApi] = useVbenVxeGrid({
     <FormModal @success="onRefresh" />
 
     <Grid table-title="${table.classComment}列表">
+        #if ($table.templateType == 12) ## 内嵌情况
+          <template #expand_content="{ row }">
+            <!-- 子表的表单 -->
+            <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}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 type="primary" @click="onCreate" v-access:code="['${table.moduleName}:${simpleClassName_strikeCase}:create']">
-          <Plus class="size-5" />
+        <Button :icon="h(Plus)" type="primary" @click="onCreate" v-access:code="['${table.moduleName}:${simpleClassName_strikeCase}:create']">
           {{ $t('ui.actionTitle.create', ['${table.classComment}']) }}
         </Button>
-        <Button type="primary" class="ml-2" @click="onExport" v-access:code="['${table.moduleName}:${simpleClassName_strikeCase}:export']">
-          <Download class="size-5" />
+        <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>

+ 193 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_erp.vue.vm

@@ -0,0 +1,193 @@
+#set ($subTable = $subTables.get($subIndex))##当前表
+#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组
+#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
+#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))
+#set ($subClassNameVar = $subClassNameVars.get($subIndex))
+#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
+<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}GridColumns } 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
+
+const props = defineProps<{
+  ${subJoinColumn.javaField}?: any // ${subJoinColumn.columnComment}(主表的关联字段)
+}>()
+
+#if ($subTable.subJoinMany) ## 一对多
+/** 表格操作按钮的回调函数 */
+function onActionClick({
+   code,
+   row,
+ }: OnActionClickParams<${simpleClassName}Api.${subSimpleClassName}>) {
+switch (code) {
+  case 'delete': {
+    onDelete(row);
+    break;
+  }
+}
+}
+
+const [${subSimpleClassName}Grid, ${subClassNameVar}GridApi] = useVbenVxeGrid({
+gridOptions: {
+  columns: use${subSimpleClassName}GridColumns(onActionClick),
+  border: true,
+  showOverflow: true,
+  autoResize: true,
+  keepSource: true,
+  rowConfig: {
+    keyField: 'id',
+  },
+  pagerConfig: {
+    enabled: false,
+  },
+  toolbarConfig: {
+    enabled: false,
+  },
+},
+});
+
+/** 删除${subTable.classComment} */
+const onDelete =  async (row: ${simpleClassName}Api.${subSimpleClassName}) => {
+await ${subClassNameVar}GridApi.grid.remove(row);
+}
+
+/** 添加${subTable.classComment} */
+const handleAdd = async () => {
+await ${subClassNameVar}GridApi.grid.insertAt({} as ${simpleClassName}Api.${subSimpleClassName}, -1);
+}
+
+/** 提供获取表格数据的方法供父组件调用 */
+defineExpose({
+getData: (): ${simpleClassName}Api.${subSimpleClassName}[] => {
+  return [
+    ...${subClassNameVar}GridApi.grid.getData(),
+    ...${subClassNameVar}GridApi.grid.getInsertRecords().map((row) => {
+      delete row.id;
+      return row;
+    }),
+  ];
+},
+});
+
+/** 监听主表的关联字段的变化,加载对应的子表数据 */
+watch(
+  () => props.${subJoinColumn.javaField},
+  async (val) => {
+    if (!val) {
+      return;
+    }
+
+    await nextTick();
+    await ${subClassNameVar}GridApi.grid.loadData(await get${subSimpleClassName}ListBy${SubJoinColumnName}(props.${subJoinColumn.javaField}!));
+  },
+);
+#else
+const [${subSimpleClassName}Form, ${subClassNameVar}FormApi] = useVbenForm({
+layout: 'horizontal',
+schema: use${subSimpleClassName}FormSchema(),
+showDefaultActions: false
+});
+
+/** 暴露出表单校验方法和表单值获取方法 */
+defineExpose({
+validate: async () => {
+  const { valid } = await ${subClassNameVar}FormApi.validate();
+  return valid;
+},
+getValues: ${subClassNameVar}FormApi.getValues,
+});
+
+/** 监听主表的关联字段的变化,加载对应的子表数据 */
+watch(
+  () => props.${subJoinColumn.javaField},
+  async (val) => {
+    if (!val) {
+      return;
+    }
+
+    await nextTick();
+    await ${subClassNameVar}FormApi.setValues(await get${subSimpleClassName}By${SubJoinColumnName}(props.${subJoinColumn.javaField}!));
+  },
+);
+#end
+</script>
+
+<template>
+    #if ($subTable.subJoinMany) ## 一对多
+      <${subSimpleClassName}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
+      </${subSimpleClassName}Grid>
+      <div class="flex justify-center">
+        <Button :icon="h(Plus)" type="primary" ghost @click="handleAdd" v-access:code="['${subTable.moduleName}:${simpleClassName_strikeCase}:create']">
+          {{ $t('ui.actionTitle.create', ['${subTable.classComment}']) }}
+        </Button>
+      </div>
+    #else
+      <${subSimpleClassName}Form class="mx-4" />
+    #end
+</template>

+ 2 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_inner.vue.vm

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

+ 203 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_normal.vue.vm

@@ -0,0 +1,203 @@
+#set ($subTable = $subTables.get($subIndex))##当前表
+#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组
+#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
+#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))
+#set ($subClassNameVar = $subClassNameVars.get($subIndex))
+#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
+<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
+
+const props = defineProps<{
+   ${subJoinColumn.javaField}?: any // ${subJoinColumn.columnComment}(主表的关联字段)
+}>()
+
+#if ($subTable.subJoinMany) ## 一对多
+/** 表格操作按钮的回调函数 */
+function onActionClick({
+ code,
+ row,
+}: OnActionClickParams<${simpleClassName}Api.${subSimpleClassName}>) {
+  switch (code) {
+    case 'delete': {
+      onDelete(row);
+      break;
+    }
+  }
+}
+
+const [${subSimpleClassName}Grid, ${subClassNameVar}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,
+  },
+},
+});
+
+/** 删除${subTable.classComment} */
+const onDelete =  async (row: ${simpleClassName}Api.${subSimpleClassName}) => {
+  await ${subClassNameVar}GridApi.grid.remove(row);
+}
+
+/** 添加${subTable.classComment} */
+const handleAdd = async () => {
+  await ${subClassNameVar}GridApi.grid.insertAt({} as ${simpleClassName}Api.${subSimpleClassName}, -1);
+}
+
+/** 提供获取表格数据的方法供父组件调用 */
+defineExpose({
+  getData: (): ${simpleClassName}Api.${subSimpleClassName}[] => {
+    // 获取当前数据,但排除已删除的记录
+    const allData = ${subClassNameVar}GridApi.grid.getData();
+    const removedData = ${subClassNameVar}GridApi.grid.getRemoveRecords();
+    const removedIds = new Set(removedData.map((row) => row.id));
+
+    // 过滤掉已删除的记录
+    const currentData = allData.filter((row) => !removedIds.has(row.id));
+
+    // 获取新插入的记录并移除id
+    const insertedData = ${subClassNameVar}GridApi.grid.getInsertRecords().map((row) => {
+      delete row.id;
+      return row;
+    });
+
+    return [...currentData, ...insertedData];
+  },
+});
+
+/** 监听主表的关联字段的变化,加载对应的子表数据 */
+watch(
+    () => props.${subJoinColumn.javaField},
+    async (val) => {
+      if (!val) {
+        return;
+      }
+
+      await nextTick();
+      await ${subClassNameVar}GridApi.grid.loadData(await get${subSimpleClassName}ListBy${SubJoinColumnName}(props.${subJoinColumn.javaField}!));
+    },
+    { immediate: true },
+);
+#else
+const [${subSimpleClassName}Form, ${subClassNameVar}FormApi] = useVbenForm({
+layout: 'horizontal',
+schema: use${subSimpleClassName}FormSchema(),
+showDefaultActions: false
+});
+
+/** 暴露出表单校验方法和表单值获取方法 */
+defineExpose({
+  validate: async () => {
+    const { valid } = await ${subClassNameVar}FormApi.validate();
+    return valid;
+  },
+  getValues: ${subClassNameVar}FormApi.getValues,
+});
+
+/** 监听主表的关联字段的变化,加载对应的子表数据 */
+watch(
+    () => props.${subJoinColumn.javaField},
+    async (val) => {
+      if (!val) {
+        return;
+      }
+
+      await nextTick();
+      await ${subClassNameVar}FormApi.setValues(await get${subSimpleClassName}By${SubJoinColumnName}(props.${subJoinColumn.javaField}!));
+    },
+    { immediate: true },
+);
+#end
+</script>
+
+<template>
+#if ($subTable.subJoinMany) ## 一对多
+  <${subSimpleClassName}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
+  </${subSimpleClassName}Grid>
+  <div class="flex justify-center">
+    <Button :icon="h(Plus)" type="primary" ghost @click="handleAdd" v-access:code="['${subTable.moduleName}:${simpleClassName_strikeCase}:create']">
+      {{ $t('ui.actionTitle.create', ['${subTable.classComment}']) }}
+    </Button>
+  </div>
+#else
+  <${subSimpleClassName}Form class="mx-4" />
+#end
+</template>

+ 145 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/list_sub_erp.vue.vm

@@ -0,0 +1,145 @@
+#set ($subTable = $subTables.get($subIndex))##当前表
+#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组
+#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
+#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))
+#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
+#set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))
+#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
+<script lang="ts" setup>
+  import type { OnActionClickParams, VxeTableGridOptions } from '#/adapter/vxe-table';
+  import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
+
+  import { useVbenModal } from '@vben/common-ui';
+  import { Button, message } from 'ant-design-vue';
+  import { Plus } from '@vben/icons';
+  import { ref, h, nextTick,watch } from 'vue';
+  import { $t } from '#/locales';
+  import { useVbenVxeGrid } from '#/adapter/vxe-table';
+
+
+#if ($table.templateType == 11) ## erp
+  import ${subSimpleClassName}Form from './${subSimpleClassName}Form.vue';
+#end
+  import { use${subSimpleClassName}GridColumns } from '../data';
+#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
+
+const props = defineProps<{
+      ${subJoinColumn.javaField}?: any // ${subJoinColumn.columnComment}(主表的关联字段)
+}>()
+
+#if ($table.templateType == 11) ## erp
+  const [FormModal, formModalApi] = useVbenModal({
+    connectedComponent: ${subSimpleClassName}Form,
+    destroyOnClose: true,
+  });
+
+/** 创建${subTable.classComment} */
+function onCreate() {
+  formModalApi.setData({}).open();
+}
+
+/** 编辑${subTable.classComment} */
+function onEdit(row: ${simpleClassName}Api.${subSimpleClassName}) {
+  formModalApi.setData(row).open();
+}
+/** 删除${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();
+  }
+}
+
+/** 表格操作按钮的回调函数 */
+function onActionClick({
+ code,
+ row,
+}: OnActionClickParams<${simpleClassName}Api.${subSimpleClassName}>) {
+  switch (code) {
+    case 'delete': {
+      onDelete(row);
+      break;
+    }
+    case 'edit': {
+      onEdit(row);
+      break;
+    }
+  }
+}
+#end
+  const [Grid, gridApi] = useVbenVxeGrid({
+    gridOptions: {
+#if ($table.templateType == 11)
+      columns: use${subSimpleClassName}GridColumns(onActionClick),
+#else
+      columns: use${subSimpleClassName}GridColumns(),
+#end
+      height: 'auto',
+      rowConfig: {
+        keyField: 'id',
+        isHover: true,
+      },
+      pagerConfig: {
+        enabled: false,
+      },
+      toolbarConfig: {
+        enabled: false,
+      },
+    } as VxeTableGridOptions<${simpleClassName}Api.${simpleClassName}>,
+  });
+
+/** 刷新表格 */
+const onRefresh = async ()=> {
+  #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
+}
+
+  /** 监听主表的关联字段的变化,加载对应的子表数据 */
+  watch(
+      () => props.${subJoinColumn.javaField},
+      async (val) => {
+        if (!val) {
+          return;
+        }
+
+        await nextTick();
+        await onRefresh()
+      },
+      { immediate: true },
+  );
+</script>
+
+<template>
+    <div class="mx-4">
+        #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="['${subTable.moduleName}:${subSimpleClassName_strikeCase}:create']">
+                {{ $t('ui.actionTitle.create', ['${subTable.classComment}']) }}
+              </Button>
+            </template>
+          </Grid>
+        #else
+        <Grid table-title="${subTable.classComment}列表" />
+        #end
+    </div>
+</template>

+ 4 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/list_sub_inner.vue.vm

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

+ 0 - 276
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/data.ts.vm

@@ -1,276 +0,0 @@
-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 { useAccess } from '@vben/access';
-
-const { hasAccessByCodes } = useAccess();
-
-/** 新增/修改的表单 */
-export function useFormSchema(): VbenFormSchema[] {
-  return [
-    {
-      fieldName: 'id',
-      component: 'Input',
-      dependencies: {
-        triggerFields: [''],
-        show: () => false,
-      },
-    },
-#if(${table.templateType} == 2)## 树表特有字段:上级
-    {
-      fieldName: '${treeParentColumn.javaField}',
-      label: '上级${table.classComment}',
-      component: 'ApiTreeSelect',
-      componentProps: {
-        allowClear: true,
-        api: async () => {
-          const data = await get${simpleClassName}List({});
-          data.unshift({
-            id: 0,
-            ${treeNameColumn.javaField}: '顶级${table.classComment}',
-          });
-          return handleTree(data);
-        },
-        class: 'w-full',
-        labelField: '${treeNameColumn.javaField}',
-        valueField: 'id',
-        childrenField: 'children',
-        placeholder: '请选择上级${table.classComment}',
-        treeDefaultExpandAll: true,
-      },
-      rules: 'selectRequired',
-    },
-#end
-#foreach($column in $columns)
-#if ($column.createOperation || $column.updateOperation)
-#if (!$column.primaryKey && ($table.templateType != 2 || ($table.templateType == 2 && $column.id != $treeParentColumn.id)))## 树表中已经添加了父ID字段,这里排除
-  #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.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键
-      rules: 'required',
-  #end
-  #if ($column.htmlType == "input")
-      component: 'Input',
-      componentProps: {
-        placeholder: '请输入${comment}',
-      },
-  #elseif($column.htmlType == "imageUpload")## 图片上传
-      component: 'FileUpload',
-      componentProps: {
-        fileType: 'image',
-        maxCount: 1,
-      },
-  #elseif($column.htmlType == "fileUpload")## 文件上传
-      component: 'FileUpload',
-      componentProps: {
-        fileType: 'file',
-        maxCount: 1,
-      },
-  #elseif($column.htmlType == "editor")## 文本编辑器
-      component: 'Editor',
-  #elseif($column.htmlType == "select")## 下拉框
-      component: 'Select',
-      componentProps: {
-        #if ("" != $dictType)## 有数据字典
-        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
-        #else##没数据字典
-        options: [],
-        #end
-        placeholder: '请选择${comment}',
-        class: 'w-full',
-      },
-  #elseif($column.htmlType == "checkbox")## 多选框
-      component: 'Checkbox',
-      componentProps: {
-        #if ("" != $dictType)## 有数据字典
-        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
-        #else##没数据字典
-        options: [],
-        #end
-      },
-  #elseif($column.htmlType == "radio")## 单选框
-      component: 'RadioGroup',
-      componentProps: {
-        #if ("" != $dictType)## 有数据字典
-        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
-        #else##没数据字典
-        options: [],
-        #end
-        buttonStyle: 'solid',
-        optionType: 'button',
-      },
-  #elseif($column.htmlType == "datetime")## 时间框
-      component: 'DatePicker',
-      componentProps: {
-        showTime: true,
-        format: 'YYYY-MM-DD HH:mm:ss',
-        valueFormat: 'x',
-      },
-  #elseif($column.htmlType == "textarea")## 文本域
-      component: 'Textarea',
-      componentProps: {
-        placeholder: '请输入${comment}',
-      },
-  #elseif($column.htmlType == "inputNumber")## 数字输入框
-      component: 'InputNumber',
-      componentProps: {
-        min: 0,
-        class: 'w-full',
-        controlsPosition: 'right',
-        placeholder: '请输入${comment}',
-      },
-  #end
-    },
-#end
-#end
-#end
-  ];
-}
-
-/** 列表的搜索表单 */
-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")
-      component: 'Input',
-      componentProps: {
-        allowClear: true,
-        placeholder: '请输入${comment}',
-      },
-  #elseif ($column.htmlType == "select")
-      component: 'Select',
-      componentProps: {
-        allowClear: true,
-        #if ("" != $dictType)## 设置了 dictType 数据字典的情况
-        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
-        #else## 未设置 dictType 数据字典的情况
-        options: [],
-        #end
-        placeholder: '请选择${comment}',
-      },
-  #elseif ($column.htmlType == "radio")
-      component: 'Select',
-      componentProps: {
-        allowClear: true,
-        #if ("" != $dictType)## 设置了 dictType 数据字典的情况
-        options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
-        #else## 未设置 dictType 数据字典的情况
-        options: [],
-        #end
-      },
-  #elseif($column.htmlType == "datetime")
-      component: 'RangePicker',
-      componentProps: {
-        allowClear: true,
-      },
-  #end
-    },
-#end
-#end
-  ];
-}
-
-/** 列表的字段 */
-export function useGridColumns(
-  onActionClick?: OnActionClickFn<${simpleClassName}Api.${simpleClassName}>,
-): VxeTableGridOptions<${simpleClassName}Api.${simpleClassName}>['columns'] {
-  return [
-#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: 'right',
-      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: 'add_child',
-            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
-          },
-        ],
-      },
-    },
-  ];
-}

+ 0 - 118
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/form.vue.vm

@@ -1,118 +0,0 @@
-<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';
-
-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<${simpleClassName}Api.${simpleClassName}>();
-#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
-
-const [Form, formApi] = useVbenForm({
-  layout: 'horizontal',
-  schema: useFormSchema(),
-  showDefaultActions: false
-});
-
-const [Modal, modalApi] = useVbenModal({
-  async onConfirm() {
-    const { valid } = await formApi.validate();
-    if (!valid) {
-      return;
-    }
-    modalApi.lock();
-    // 提交表单
-    const data = (await formApi.getValues()) as ${simpleClassName}Api.${simpleClassName};
-    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) {
-      return;
-    }
-    // 加载数据
-#if (${table.templateType} == 2)## 树表处理传入的父ID
-    let data = modalApi.getData<${simpleClassName}Api.${simpleClassName}>();
-#else## 标准表直接获取
-    const data = modalApi.getData<${simpleClassName}Api.${simpleClassName}>();
-#end
-    if (!data) {
-      return;
-    }
-
-#if (${table.templateType} == 2)## 树表特有:处理新增下级的情况
-    // 处理新增下级的情况
-    if (!data.id && data.${treeParentColumn.javaField}) {
-      parentId.value = data.${treeParentColumn.javaField};
-      formData.value = { ${treeParentColumn.javaField}: parentId.value } as ${simpleClassName}Api.${simpleClassName};
-      await formApi.setValues(formData.value);
-      return;
-    }
-#end
-
-    if (data.id) {
-      // 编辑
-      modalApi.lock();
-      try {
-#if (${table.templateType} == 2)## 树表获取数据后重新赋值
-        data = await get${simpleClassName}(data.id);
-        formData.value = data;
-#else## 标准表设置表单数据
-        formData.value = await get${simpleClassName}(data.id as number);
-#end
-        await formApi.setValues(formData.value);
-      } finally {
-        modalApi.lock(false);
-      }
-    } else {
-      // 新增
-#if (${table.templateType} == 2)## 树表特有:设置顶级ID
-      formData.value = { ${treeParentColumn.javaField}: 0 } as ${simpleClassName}Api.${simpleClassName};
-#else## 标准表:设置空值
-      formData.value = data;
-#end
-      await formApi.setValues(formData.value || {});
-    }
-  },
-});
-</script>
-
-<template>
-  <Modal :title="getTitle">
-    <Form class="mx-4" />
-  </Modal>
-</template>