Commit 3ed98e152a0c3efd22dcd26dfeecbbdb60bfb52f

Authored by YunaiV
1 parent c8a584c5

fix(infra): 修复前端代码生成模板的组件绑定与主子表行为

修复 Vben5 字典控件、日期及树选择属性、导入文件移除和 API 导入路径。
修复主子表数据获取、临时编号、必填校验和主表切换后的旧数据残留。
补齐工具方法与组件导入,修正 Ele 工具栏标签及 Vue3 单选多选绑定。
同步 Vue2 组件标签与 prop 声明、旧 Vben 日期范围格式。
更新生成快照,增加跨前端组件契约测试。

验证:85 条 CodegenEngine 测试、138 份 Vue 模板编译通过;
Codex、Qwen、DeepSeek 三路评审通过。
Showing 120 changed files with 3131 additions and 1753 deletions

Too many changes.

To preserve performance only 100 of 120 files are displayed.

yudao-module-infra/src/main/resources/codegen/vue/views/components/form_sub_normal.vue.vm
@@ -244,9 +244,13 @@ @@ -244,9 +244,13 @@
244 Editor, 244 Editor,
245 #end 245 #end
246 }, 246 },
247 - props:[  
248 - '${subJoinColumn.javaField}'  
249 - ],// ${subJoinColumn.columnComment}(主表的关联字段) 247 + props: {
  248 + // ${subJoinColumn.columnComment}(主表的关联字段)
  249 + ${subJoinColumn.javaField}: {
  250 + type: #if ($subJoinColumn.javaType == "String")String#else Number#end,
  251 + default: undefined
  252 + }
  253 + },
250 data() { 254 data() {
251 return { 255 return {
252 // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 256 // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
yudao-module-infra/src/main/resources/codegen/vue/views/components/list_sub_erp.vue.vm
@@ -78,7 +78,7 @@ @@ -78,7 +78,7 @@
78 </el-table> 78 </el-table>
79 #if ($table.templateType == 11) 79 #if ($table.templateType == 11)
80 <!-- 分页组件 --> 80 <!-- 分页组件 -->
81 - <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" 81 + <Pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
82 @pagination="getList"/> 82 @pagination="getList"/>
83 <!-- 对话框(添加 / 修改) --> 83 <!-- 对话框(添加 / 修改) -->
84 <${subSimpleClassName}Form ref="formRef" @success="getList" /> 84 <${subSimpleClassName}Form ref="formRef" @success="getList" />
@@ -98,9 +98,13 @@ @@ -98,9 +98,13 @@
98 ${subSimpleClassName}Form 98 ${subSimpleClassName}Form
99 }, 99 },
100 #end 100 #end
101 - props:[  
102 - '${subJoinColumn.javaField}'  
103 - ],// ${subJoinColumn.columnComment}(主表的关联字段) 101 + props: {
  102 + // ${subJoinColumn.columnComment}(主表的关联字段)
  103 + ${subJoinColumn.javaField}: {
  104 + type: #if ($subJoinColumn.javaType == "String")String#else Number#end,
  105 + default: undefined
  106 + }
  107 + },
104 data() { 108 data() {
105 return { 109 return {
106 // 遮罩层 110 // 遮罩层
yudao-module-infra/src/main/resources/codegen/vue/views/index.vue.vm
@@ -86,7 +86,7 @@ @@ -86,7 +86,7 @@
86 </el-button> 86 </el-button>
87 </el-col> 87 </el-col>
88 #end 88 #end
89 - <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> 89 + <RightToolbar :showSearch.sync="showSearch" @queryTable="getList"></RightToolbar>
90 </el-row> 90 </el-row>
91 #if ($importEnable) 91 #if ($importEnable)
92 <input ref="importFileRef" type="file" style="display: none" accept=".xls,.xlsx" @change="handleImportFileChange" /> 92 <input ref="importFileRef" type="file" style="display: none" accept=".xls,.xlsx" @change="handleImportFileChange" />
@@ -186,7 +186,7 @@ @@ -186,7 +186,7 @@
186 ## 特殊:树表专属逻辑(树不需要分页) 186 ## 特殊:树表专属逻辑(树不需要分页)
187 #if ( $table.templateType != 2 ) 187 #if ( $table.templateType != 2 )
188 <!-- 分页组件 --> 188 <!-- 分页组件 -->
189 - <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" 189 + <Pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
190 @pagination="getList"/> 190 @pagination="getList"/>
191 #end 191 #end
192 <!-- 对话框(添加 / 修改) --> 192 <!-- 对话框(添加 / 修改) -->
yudao-module-infra/src/main/resources/codegen/vue3/views/components/form_sub_erp.vue.vm
@@ -79,7 +79,7 @@ @@ -79,7 +79,7 @@
79 <el-radio 79 <el-radio
80 v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())" 80 v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
81 :key="dict.value" 81 :key="dict.value"
82 - :label="dict.value" 82 + :value="dict.value"
83 > 83 >
84 {{ dict.label }} 84 {{ dict.label }}
85 </el-radio> 85 </el-radio>
yudao-module-infra/src/main/resources/codegen/vue3/views/components/form_sub_normal.vue.vm
@@ -111,7 +111,7 @@ @@ -111,7 +111,7 @@
111 <el-radio 111 <el-radio
112 v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())" 112 v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
113 :key="dict.value" 113 :key="dict.value"
114 - :label="dict.value" 114 + :value="dict.value"
115 > 115 >
116 {{ dict.label }} 116 {{ dict.label }}
117 </el-radio> 117 </el-radio>
@@ -233,7 +233,7 @@ @@ -233,7 +233,7 @@
233 <el-radio 233 <el-radio
234 v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())" 234 v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
235 :key="dict.value" 235 :key="dict.value"
236 - :label="dict.value" 236 + :value="dict.value"
237 > 237 >
238 {{ dict.label }} 238 {{ dict.label }}
239 </el-radio> 239 </el-radio>
yudao-module-infra/src/main/resources/codegen/vue3/views/form.vue.vm
@@ -90,7 +90,7 @@ @@ -90,7 +90,7 @@
90 <el-radio 90 <el-radio
91 v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())" 91 v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
92 :key="dict.value" 92 :key="dict.value"
93 - :label="dict.value" 93 + :value="dict.value"
94 > 94 >
95 {{ dict.label }} 95 {{ dict.label }}
96 </el-radio> 96 </el-radio>
yudao-module-infra/src/main/resources/codegen/vue3_vben/views/data.ts.vm
@@ -78,6 +78,12 @@ export const searchFormSchema: FormSchema[] = [ @@ -78,6 +78,12 @@ export const searchFormSchema: FormSchema[] = [
78 }, 78 },
79 #elseif($column.htmlType == "datetime") 79 #elseif($column.htmlType == "datetime")
80 component: 'RangePicker', 80 component: 'RangePicker',
  81 + componentProps: {
  82 + showTime: true,
  83 + format: 'YYYY-MM-DD HH:mm:ss',
  84 + valueFormat: 'YYYY-MM-DD HH:mm:ss',
  85 + allowClear: true,
  86 + },
81 #end 87 #end
82 colProps: { span: 8 }, 88 colProps: { span: 8 },
83 }, 89 },
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/general/views/form.vue.vm
@@ -112,7 +112,10 @@ const [Modal, modalApi] = useVbenModal({ @@ -112,7 +112,10 @@ const [Modal, modalApi] = useVbenModal({
112 #set ($index = $foreach.count - 1) 112 #set ($index = $foreach.count - 1)
113 #set ($subClassNameVar = $subClassNameVars.get($index)) 113 #set ($subClassNameVar = $subClassNameVars.get($index))
114 #if ($subTable.subJoinMany) ## 一对多 114 #if ($subTable.subJoinMany) ## 一对多
115 - ## TODO 列表值校验? 115 + if (!${subClassNameVar}FormRef.value?.validate()) {
  116 + subTabsName.value = '${subClassNameVar}';
  117 + return;
  118 + }
116 #else 119 #else
117 try { 120 try {
118 await ${subClassNameVar}FormRef.value?.validate() 121 await ${subClassNameVar}FormRef.value?.validate()
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/general/views/import.vue.vm
@@ -48,6 +48,11 @@ function beforeUpload(file: FileType) { @@ -48,6 +48,11 @@ function beforeUpload(file: FileType) {
48 return false; 48 return false;
49 } 49 }
50 50
  51 +/** 移除文件 */
  52 +function handleRemove() {
  53 + fileRef.value = null;
  54 +}
  55 +
51 /** 下载导入模板 */ 56 /** 下载导入模板 */
52 async function handleDownload() { 57 async function handleDownload() {
53 const data = await import${simpleClassName}Template(); 58 const data = await import${simpleClassName}Template();
@@ -58,7 +63,12 @@ async function handleDownload() { @@ -58,7 +63,12 @@ async function handleDownload() {
58 <template> 63 <template>
59 <Modal title="导入${table.classComment}" class="w-1/3"> 64 <Modal title="导入${table.classComment}" class="w-1/3">
60 <div class="mx-4"> 65 <div class="mx-4">
61 - <Upload :max-count="1" accept=".xls,.xlsx" :before-upload="beforeUpload"> 66 + <Upload
  67 + :max-count="1"
  68 + accept=".xls,.xlsx"
  69 + :before-upload="beforeUpload"
  70 + :on-remove="handleRemove"
  71 + >
62 <Button type="primary"> 选择 Excel 文件 </Button> 72 <Button type="primary"> 选择 Excel 文件 </Button>
63 </Upload> 73 </Upload>
64 </div> 74 </div>
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/general/views/index.vue.vm
@@ -65,12 +65,7 @@ const queryParams = reactive({ @@ -65,12 +65,7 @@ const queryParams = reactive({
65 #end 65 #end
66 #foreach ($column in $columns) 66 #foreach ($column in $columns)
67 #if ($column.listOperation) 67 #if ($column.listOperation)
68 - #if ($column.listOperationCondition != 'BETWEEN')  
69 - $column.javaField: undefined,  
70 - #end  
71 - #if ($column.htmlType == "datetime" || $column.listOperationCondition == "BETWEEN")  
72 - $column.javaField: undefined,  
73 - #end 68 + $column.javaField: undefined,
74 #end 69 #end
75 #end 70 #end
76 }) 71 })
@@ -197,7 +192,17 @@ function handleRowCheckboxChange({ @@ -197,7 +192,17 @@ function handleRowCheckboxChange({
197 async function handleExport() { 192 async function handleExport() {
198 try { 193 try {
199 exportLoading.value = true; 194 exportLoading.value = true;
200 - const data = await export${simpleClassName}(queryParams); 195 + const params = cloneDeep(queryParams) as any;
  196 + #foreach ($column in $columns)
  197 + #if ($column.listOperation)
  198 + #if ($column.htmlType == "datetime" || $column.listOperationCondition == "BETWEEN")
  199 + if (params.${column.javaField} && Array.isArray(params.${column.javaField})) {
  200 + params.${column.javaField} = (params.${column.javaField} as string[]).join(',');
  201 + }
  202 + #end
  203 + #end
  204 + #end
  205 + const data = await export${simpleClassName}(params);
201 downloadFileFromBlobPart({ fileName: '${table.classComment}.xls', source: data }); 206 downloadFileFromBlobPart({ fileName: '${table.classComment}.xls', source: data });
202 }finally { 207 }finally {
203 exportLoading.value = false; 208 exportLoading.value = false;
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_normal.vue.vm
@@ -46,25 +46,54 @@ async function handleDelete(row: ${simpleClassName}Api.${subSimpleClassName}) { @@ -46,25 +46,54 @@ async function handleDelete(row: ${simpleClassName}Api.${subSimpleClassName}) {
46 await tableRef.value?.remove(row); 46 await tableRef.value?.remove(row);
47 } 47 }
48 48
49 -/** 提供获取表格数据的方法供父组件调用 */  
50 -defineExpose({  
51 - getData: (): ${simpleClassName}Api.${subSimpleClassName}[] => {  
52 - const data = list.value as ${simpleClassName}Api.${subSimpleClassName}[];  
53 - const removeRecords = tableRef.value?.getRemoveRecords() as ${simpleClassName}Api.${subSimpleClassName}[];  
54 - const insertRecords = tableRef.value?.getInsertRecords() as ${simpleClassName}Api.${subSimpleClassName}[];  
55 - return [  
56 - ...data.filter(  
57 - (row) => !removeRecords.some((removed) => removed.id === row.id),  
58 - ),  
59 - ...insertRecords.map((row: any) => ({ ...row, id: undefined })),  
60 - ];  
61 - },  
62 -}); 49 +/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
  50 +function getData(): ${simpleClassName}Api.${subSimpleClassName}[] {
  51 + const table = tableRef.value;
  52 + if (!table) {
  53 + return [];
  54 + }
  55 + const { fullData } = table.getTableData();
  56 + const inserted = new Set(table.getInsertRecords());
  57 + return fullData.map((row) =>
  58 + inserted.has(row) ? { ...row, id: undefined } : { ...row },
  59 + );
  60 +}
  61 +
  62 +#set ($hasRequiredColumn = false)
  63 +#foreach ($column in $subColumns)
  64 +#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey && $column.id != $subJoinColumn.id)
  65 +#set ($hasRequiredColumn = true)
  66 +#end
  67 +#end
  68 +/** 校验子表必填字段 */
  69 +function validate(): boolean {
  70 +#if ($hasRequiredColumn)
  71 + for (const [index, row] of getData().entries()) {
  72 +#foreach ($column in $subColumns)
  73 +#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey && $column.id != $subJoinColumn.id)
  74 + if (
  75 + row.${column.javaField} === null ||
  76 + row.${column.javaField} === undefined ||
  77 + (typeof row.${column.javaField} === 'string' && String(row.${column.javaField}).trim() === '') ||
  78 + (Array.isArray(row.${column.javaField}) && row.${column.javaField}.length === 0)
  79 + ) {
  80 + message.error(`第 #[[${index + 1}]]# 行:${column.columnComment}不能为空`);
  81 + return false;
  82 + }
  83 +#end
  84 +#end
  85 + }
  86 +#end
  87 + return true;
  88 +}
  89 +
  90 +defineExpose({ getData, validate });
63 91
64 /** 监听主表的关联字段的变化,加载对应的子表数据 */ 92 /** 监听主表的关联字段的变化,加载对应的子表数据 */
65 watch( 93 watch(
66 () => props.${subJoinColumn.javaField}, 94 () => props.${subJoinColumn.javaField},
67 async (val) => { 95 async (val) => {
  96 + list.value = [];
68 if (!val) { 97 if (!val) {
69 return; 98 return;
70 } 99 }
@@ -103,6 +132,17 @@ defineExpose({ @@ -103,6 +132,17 @@ defineExpose({
103 watch( 132 watch(
104 () => props.${subJoinColumn.javaField}, 133 () => props.${subJoinColumn.javaField},
105 async (val) => { 134 async (val) => {
  135 + formData.value = {
  136 +#foreach ($column in $subColumns)
  137 +#if ($column.createOperation || $column.updateOperation)
  138 +#if ($column.htmlType == "checkbox")
  139 + $column.javaField: [],
  140 +#else
  141 + $column.javaField: undefined,
  142 +#end
  143 +#end
  144 +#end
  145 + };
106 if (!val) { 146 if (!val) {
107 return; 147 return;
108 } 148 }
@@ -120,6 +160,7 @@ watch( @@ -120,6 +160,7 @@ watch(
120 #foreach($column in $subColumns) 160 #foreach($column in $subColumns)
121 #if ($column.createOperation || $column.updateOperation) 161 #if ($column.createOperation || $column.updateOperation)
122 #set ($comment = $column.columnComment) 162 #set ($comment = $column.columnComment)
  163 + #set ($dictType = $column.dictType)
123 #set ($javaField = $column.javaField) 164 #set ($javaField = $column.javaField)
124 #set ($javaType = $column.javaType) 165 #set ($javaType = $column.javaType)
125 #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short") 166 #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/views/data.ts.vm
@@ -36,7 +36,7 @@ export function useFormSchema(): VbenFormSchema[] { @@ -36,7 +36,7 @@ export function useFormSchema(): VbenFormSchema[] {
36 id: 0, 36 id: 0,
37 ${treeNameColumn.javaField}: '顶级${table.classComment}', 37 ${treeNameColumn.javaField}: '顶级${table.classComment}',
38 }); 38 });
39 - return handleTree(data); 39 + return handleTree(data, 'id', '${treeParentColumn.javaField}');
40 }, 40 },
41 labelField: '${treeNameColumn.javaField}', 41 labelField: '${treeNameColumn.javaField}',
42 valueField: 'id', 42 valueField: 'id',
@@ -89,7 +89,7 @@ export function useFormSchema(): VbenFormSchema[] { @@ -89,7 +89,7 @@ export function useFormSchema(): VbenFormSchema[] {
89 placeholder: '请选择${comment}', 89 placeholder: '请选择${comment}',
90 }, 90 },
91 #elseif($column.htmlType == "checkbox")## 多选框 91 #elseif($column.htmlType == "checkbox")## 多选框
92 - component: 'Checkbox', 92 + component: 'CheckboxGroup',
93 componentProps: { 93 componentProps: {
94 #if ("" != $dictType)## 有数据字典 94 #if ("" != $dictType)## 有数据字典
95 options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), 95 options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
@@ -306,7 +306,7 @@ export function use${subSimpleClassName}FormSchema(): VbenFormSchema[] { @@ -306,7 +306,7 @@ export function use${subSimpleClassName}FormSchema(): VbenFormSchema[] {
306 placeholder: '请选择${comment}', 306 placeholder: '请选择${comment}',
307 }, 307 },
308 #elseif($column.htmlType == "checkbox")## 多选框 308 #elseif($column.htmlType == "checkbox")## 多选框
309 - component: 'Checkbox', 309 + component: 'CheckboxGroup',
310 componentProps: { 310 componentProps: {
311 #if ("" != $dictType)## 有数据字典 311 #if ("" != $dictType)## 有数据字典
312 options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), 312 options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
@@ -446,6 +446,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions&lt;${api @@ -446,6 +446,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions&lt;${api
446 #if ($column.createOperation || $column.updateOperation) 446 #if ($column.createOperation || $column.updateOperation)
447 #if (!$column.primaryKey && $column.listOperationResult && $column.id != $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写 447 #if (!$column.primaryKey && $column.listOperationResult && $column.id != $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
448 #set ($dictType = $column.dictType) 448 #set ($dictType = $column.dictType)
  449 + #set ($javaType = $column.javaType)
449 #set ($javaField = $column.javaField) 450 #set ($javaField = $column.javaField)
450 #set ($comment = $column.columnComment) 451 #set ($comment = $column.columnComment)
451 #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short") 452 #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
@@ -540,7 +541,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions&lt;${api @@ -540,7 +541,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions&lt;${api
540 placeholder: '请选择${comment}', 541 placeholder: '请选择${comment}',
541 }, 542 },
542 #elseif($column.htmlType == "checkbox")## 多选框 543 #elseif($column.htmlType == "checkbox")## 多选框
543 - component: 'Checkbox', 544 + component: 'CheckboxGroup',
544 componentProps: { 545 componentProps: {
545 #if ("" != $dictType)## 有数据字典 546 #if ("" != $dictType)## 有数据字典
546 options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), 547 options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
@@ -590,7 +591,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions&lt;${api @@ -590,7 +591,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions&lt;${api
590 #end 591 #end
591 #if ($table.templateType == 12) ## 内嵌情况 592 #if ($table.templateType == 12) ## 内嵌情况
592 /** 列表的字段 */ 593 /** 列表的字段 */
593 - export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${simpleClassName}Api.${subSimpleClassName}>['columns'] { 594 + export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${apiName}.${subSimpleClassName}>['columns'] {
594 return [ 595 return [
595 #foreach($column in $subColumns) 596 #foreach($column in $subColumns)
596 #if ($column.listOperationResult) 597 #if ($column.listOperationResult)
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/views/form.vue.vm
@@ -6,7 +6,7 @@ import { computed, ref } from &#39;vue&#39;; @@ -6,7 +6,7 @@ import { computed, ref } from &#39;vue&#39;;
6 6
7 import { useVbenModal } from '@vben/common-ui'; 7 import { useVbenModal } from '@vben/common-ui';
8 8
9 -import { message#if ($table.templateType == 11), Tabs#end } from 'ant-design-vue'; 9 +import { message#if ($table.templateType == 10 || $table.templateType == 12), Tabs#end } from 'ant-design-vue';
10 10
11 import { useVbenForm } from '#/adapter/form'; 11 import { useVbenForm } from '#/adapter/form';
12 import { create${simpleClassName}, get${simpleClassName}, update${simpleClassName} } from '#/api/${table.moduleName}/${table.businessName}'; 12 import { create${simpleClassName}, get${simpleClassName}, update${simpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';
@@ -69,15 +69,11 @@ const [Modal, modalApi] = useVbenModal({ @@ -69,15 +69,11 @@ const [Modal, modalApi] = useVbenModal({
69 #foreach ($subTable in $subTables) 69 #foreach ($subTable in $subTables)
70 #set ($index = $foreach.count - 1) 70 #set ($index = $foreach.count - 1)
71 #set ($subClassNameVar = $subClassNameVars.get($index)) 71 #set ($subClassNameVar = $subClassNameVars.get($index))
72 - #if ($subTable.subJoinMany) ## 一对多  
73 - ## TODO 列表值校验?  
74 - #else  
75 const ${subClassNameVar}Valid = await ${subClassNameVar}FormRef.value?.validate(); 72 const ${subClassNameVar}Valid = await ${subClassNameVar}FormRef.value?.validate();
76 if (!${subClassNameVar}Valid) { 73 if (!${subClassNameVar}Valid) {
77 subTabsName.value = '${subClassNameVar}'; 74 subTabsName.value = '${subClassNameVar}';
78 return; 75 return;
79 } 76 }
80 - #end  
81 #end 77 #end
82 #end 78 #end
83 #end 79 #end
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/views/import.vue.vm
@@ -51,6 +51,11 @@ function beforeUpload(file: FileType) { @@ -51,6 +51,11 @@ function beforeUpload(file: FileType) {
51 return false; 51 return false;
52 } 52 }
53 53
  54 +/** 移除文件 */
  55 +function handleRemove() {
  56 + formApi.setFieldValue('file', undefined);
  57 +}
  58 +
54 /** 下载模板 */ 59 /** 下载模板 */
55 async function handleDownload() { 60 async function handleDownload() {
56 const data = await import${simpleClassName}Template(); 61 const data = await import${simpleClassName}Template();
@@ -67,6 +72,7 @@ async function handleDownload() { @@ -67,6 +72,7 @@ async function handleDownload() {
67 :max-count="1" 72 :max-count="1"
68 accept=".xls,.xlsx" 73 accept=".xls,.xlsx"
69 :before-upload="beforeUpload" 74 :before-upload="beforeUpload"
  75 + :on-remove="handleRemove"
70 > 76 >
71 <Button type="primary"> 选择 Excel 文件 </Button> 77 <Button type="primary"> 选择 Excel 文件 </Button>
72 </Upload> 78 </Upload>
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_normal.vue.vm
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 14
15 #if ($subTable.subJoinMany) ## 一对多 15 #if ($subTable.subJoinMany) ## 一对多
16 import { Plus } from "@vben/icons"; 16 import { Plus } from "@vben/icons";
17 -import { Button, Tabs, Checkbox, Input, Textarea, Select,RadioGroup,CheckboxGroup, DatePicker } from 'ant-design-vue'; 17 +import { Button, Checkbox, CheckboxGroup, DatePicker, Input, message, RadioGroup, Select, Tabs, Textarea } from 'ant-design-vue';
18 import { ImageUpload, FileUpload } from "#/components/upload"; 18 import { ImageUpload, FileUpload } from "#/components/upload";
19 import { useVbenVxeGrid } from '#/adapter/vxe-table'; 19 import { useVbenVxeGrid } from '#/adapter/vxe-table';
20 import { use${subSimpleClassName}GridEditColumns } from '../data'; 20 import { use${subSimpleClassName}GridEditColumns } from '../data';
@@ -59,26 +59,54 @@ const handleDelete = async (row: ${apiName}.${subSimpleClassName}) =&gt; { @@ -59,26 +59,54 @@ const handleDelete = async (row: ${apiName}.${subSimpleClassName}) =&gt; {
59 await gridApi.grid.remove(row); 59 await gridApi.grid.remove(row);
60 } 60 }
61 61
62 -/** 提供获取表格数据的方法供父组件调用 */  
63 -defineExpose({  
64 - getData: (): ${apiName}.${subSimpleClassName}[] => {  
65 - const data = gridApi.grid.getData() as ${apiName}.${subSimpleClassName}[];  
66 - const removeRecords = gridApi.grid.getRemoveRecords() as ${apiName}.${subSimpleClassName}[];  
67 - const insertRecords = gridApi.grid.getInsertRecords() as ${apiName}.${subSimpleClassName}[];  
68 - return data  
69 - .filter((row) => !removeRecords.some((removed) => removed.id === row.id))  
70 - .concat(insertRecords.map((row: any) => ({ ...row, id: undefined })));  
71 - },  
72 -}); 62 +/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
  63 +function getData(): ${apiName}.${subSimpleClassName}[] {
  64 + const { fullData } = gridApi.grid.getTableData();
  65 + const inserted = new Set(gridApi.grid.getInsertRecords());
  66 + return fullData.map((row) =>
  67 + inserted.has(row) ? { ...row, id: undefined } : { ...row },
  68 + );
  69 +}
  70 +
  71 +#set ($hasRequiredColumn = false)
  72 +#foreach ($column in $subColumns)
  73 +#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey && $column.id != $subJoinColumn.id)
  74 +#set ($hasRequiredColumn = true)
  75 +#end
  76 +#end
  77 +/** 校验子表必填字段 */
  78 +function validate(): boolean {
  79 +#if ($hasRequiredColumn)
  80 + for (const [index, row] of getData().entries()) {
  81 +#foreach ($column in $subColumns)
  82 +#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey && $column.id != $subJoinColumn.id)
  83 + if (
  84 + row.${column.javaField} === null ||
  85 + row.${column.javaField} === undefined ||
  86 + (typeof row.${column.javaField} === 'string' && String(row.${column.javaField}).trim() === '') ||
  87 + (Array.isArray(row.${column.javaField}) && row.${column.javaField}.length === 0)
  88 + ) {
  89 + message.error(`第 #[[${index + 1}]]# 行:${column.columnComment}不能为空`);
  90 + return false;
  91 + }
  92 +#end
  93 +#end
  94 + }
  95 +#end
  96 + return true;
  97 +}
  98 +
  99 +defineExpose({ getData, validate });
73 100
74 /** 监听主表的关联字段的变化,加载对应的子表数据 */ 101 /** 监听主表的关联字段的变化,加载对应的子表数据 */
75 watch( 102 watch(
76 () => props.${subJoinColumn.javaField}, 103 () => props.${subJoinColumn.javaField},
77 async (val) => { 104 async (val) => {
  105 + await nextTick();
78 if (!val) { 106 if (!val) {
  107 + await gridApi.grid.loadData([]);
79 return; 108 return;
80 } 109 }
81 - await nextTick();  
82 await gridApi.grid.loadData(await get${subSimpleClassName}ListBy${SubJoinColumnName}(props.${subJoinColumn.javaField}!)); 110 await gridApi.grid.loadData(await get${subSimpleClassName}ListBy${SubJoinColumnName}(props.${subJoinColumn.javaField}!));
83 }, 111 },
84 { immediate: true }, 112 { immediate: true },
@@ -110,10 +138,11 @@ defineExpose({ @@ -110,10 +138,11 @@ defineExpose({
110 watch( 138 watch(
111 () => props.${subJoinColumn.javaField}, 139 () => props.${subJoinColumn.javaField},
112 async (val) => { 140 async (val) => {
  141 + await nextTick();
  142 + await formApi.resetForm();
113 if (!val) { 143 if (!val) {
114 return; 144 return;
115 } 145 }
116 - await nextTick();  
117 await formApi.setValues(await get${subSimpleClassName}By${SubJoinColumnName}(props.${subJoinColumn.javaField}!)); 146 await formApi.setValues(await get${subSimpleClassName}By${SubJoinColumnName}(props.${subJoinColumn.javaField}!));
118 }, 147 },
119 { immediate: true }, 148 { immediate: true },
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/general/views/form.vue.vm
@@ -118,7 +118,10 @@ const [Modal, modalApi] = useVbenModal({ @@ -118,7 +118,10 @@ const [Modal, modalApi] = useVbenModal({
118 #set ($index = $foreach.count - 1) 118 #set ($index = $foreach.count - 1)
119 #set ($subClassNameVar = $subClassNameVars.get($index)) 119 #set ($subClassNameVar = $subClassNameVars.get($index))
120 #if ($subTable.subJoinMany) ## 一对多 120 #if ($subTable.subJoinMany) ## 一对多
121 - ## TODO 列表值校验? 121 + if (!${subClassNameVar}FormRef.value?.validate()) {
  122 + subTabsName.value = '${subClassNameVar}';
  123 + return;
  124 + }
122 #else 125 #else
123 try { 126 try {
124 await ${subClassNameVar}FormRef.value?.validate() 127 await ${subClassNameVar}FormRef.value?.validate()
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/general/views/import.vue.vm
@@ -46,6 +46,11 @@ function beforeUpload(file: File) { @@ -46,6 +46,11 @@ function beforeUpload(file: File) {
46 return false; 46 return false;
47 } 47 }
48 48
  49 +/** 移除文件 */
  50 +function handleRemove() {
  51 + fileRef.value = null;
  52 +}
  53 +
49 /** 下载导入模板 */ 54 /** 下载导入模板 */
50 async function handleDownload() { 55 async function handleDownload() {
51 const data = await import${simpleClassName}Template(); 56 const data = await import${simpleClassName}Template();
@@ -56,7 +61,8 @@ async function handleDownload() { @@ -56,7 +61,8 @@ async function handleDownload() {
56 <template> 61 <template>
57 <Modal title="导入${table.classComment}" class="w-1/3"> 62 <Modal title="导入${table.classComment}" class="w-1/3">
58 <div class="mx-4"> 63 <div class="mx-4">
59 - <Upload :max-count="1" accept=".xls,.xlsx" :before-upload="beforeUpload"> 64 + <Upload :max-count="1" accept=".xls,.xlsx" :before-upload="beforeUpload"
  65 + :on-remove="handleRemove">
60 <Button type="primary"> 选择 Excel 文件 </Button> 66 <Button type="primary"> 选择 Excel 文件 </Button>
61 </Upload> 67 </Upload>
62 </div> 68 </div>
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/general/views/index.vue.vm
@@ -77,12 +77,7 @@ const queryParams = reactive({ @@ -77,12 +77,7 @@ const queryParams = reactive({
77 #end 77 #end
78 #foreach ($column in $columns) 78 #foreach ($column in $columns)
79 #if ($column.listOperation) 79 #if ($column.listOperation)
80 - #if ($column.listOperationCondition != 'BETWEEN')  
81 - $column.javaField: undefined,  
82 - #end  
83 - #if ($column.htmlType == "datetime" || $column.listOperationCondition == "BETWEEN")  
84 - $column.javaField: undefined,  
85 - #end 80 + $column.javaField: undefined,
86 #end 81 #end
87 #end 82 #end
88 }) 83 })
@@ -209,7 +204,17 @@ function handleRowCheckboxChange({ @@ -209,7 +204,17 @@ function handleRowCheckboxChange({
209 async function handleExport() { 204 async function handleExport() {
210 try { 205 try {
211 exportLoading.value = true; 206 exportLoading.value = true;
212 - const data = await export${simpleClassName}(queryParams); 207 + const params = cloneDeep(queryParams) as any;
  208 + #foreach ($column in $columns)
  209 + #if ($column.listOperation)
  210 + #if ($column.htmlType == "datetime" || $column.listOperationCondition == "BETWEEN")
  211 + if (params.${column.javaField} && Array.isArray(params.${column.javaField})) {
  212 + params.${column.javaField} = (params.${column.javaField} as string[]).join(',');
  213 + }
  214 + #end
  215 + #end
  216 + #end
  217 + const data = await export${simpleClassName}(params);
213 downloadFileFromBlobPart({ fileName: '${table.classComment}.xls', source: data }); 218 downloadFileFromBlobPart({ fileName: '${table.classComment}.xls', source: data });
214 }finally { 219 }finally {
215 exportLoading.value = false; 220 exportLoading.value = false;
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/general/views/modules/form_sub_normal.vue.vm
@@ -18,7 +18,7 @@ import { computed, ref, h, onMounted, watch, nextTick } from &#39;vue&#39;; @@ -18,7 +18,7 @@ import { computed, ref, h, onMounted, watch, nextTick } from &#39;vue&#39;;
18 import { DICT_TYPE } from '@vben/constants'; 18 import { DICT_TYPE } from '@vben/constants';
19 import { getDictOptions } from '@vben/hooks'; 19 import { getDictOptions } from '@vben/hooks';
20 20
21 -import { Button, Checkbox, CheckboxGroup, DatePicker#if (!$subTable.subJoinMany), Form, FormItem#end, Input, Radio, RadioGroup, Select#if ($selectOptionEnable), SelectOption#end, TextArea } from 'antdv-next'; 21 +import { Button, Checkbox, CheckboxGroup, DatePicker#if (!$subTable.subJoinMany), Form, FormItem#end, Input#if ($subTable.subJoinMany), message#end, Radio, RadioGroup, Select#if ($selectOptionEnable), SelectOption#end, TextArea } from 'antdv-next';
22 import { $t } from '#/locales'; 22 import { $t } from '#/locales';
23 23
24 #if ($subTable.subJoinMany) ## 一对多 24 #if ($subTable.subJoinMany) ## 一对多
@@ -52,25 +52,54 @@ async function handleDelete(row: ${simpleClassName}Api.${subSimpleClassName}) { @@ -52,25 +52,54 @@ async function handleDelete(row: ${simpleClassName}Api.${subSimpleClassName}) {
52 await tableRef.value?.remove(row); 52 await tableRef.value?.remove(row);
53 } 53 }
54 54
55 -/** 提供获取表格数据的方法供父组件调用 */  
56 -defineExpose({  
57 - getData: (): ${simpleClassName}Api.${subSimpleClassName}[] => {  
58 - const data = list.value as ${simpleClassName}Api.${subSimpleClassName}[];  
59 - const removeRecords = tableRef.value?.getRemoveRecords() as ${simpleClassName}Api.${subSimpleClassName}[];  
60 - const insertRecords = tableRef.value?.getInsertRecords() as ${simpleClassName}Api.${subSimpleClassName}[];  
61 - return [  
62 - ...data.filter(  
63 - (row) => !removeRecords.some((removed) => removed.id === row.id),  
64 - ),  
65 - ...insertRecords.map((row: any) => ({ ...row, id: undefined })),  
66 - ];  
67 - },  
68 -}); 55 +/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
  56 +function getData(): ${simpleClassName}Api.${subSimpleClassName}[] {
  57 + const table = tableRef.value;
  58 + if (!table) {
  59 + return [];
  60 + }
  61 + const { fullData } = table.getTableData();
  62 + const inserted = new Set(table.getInsertRecords());
  63 + return fullData.map((row) =>
  64 + inserted.has(row) ? { ...row, id: undefined } : { ...row },
  65 + );
  66 +}
  67 +
  68 +#set ($hasRequiredColumn = false)
  69 +#foreach ($column in $subColumns)
  70 +#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey && $column.id != $subJoinColumn.id)
  71 +#set ($hasRequiredColumn = true)
  72 +#end
  73 +#end
  74 +/** 校验子表必填字段 */
  75 +function validate(): boolean {
  76 +#if ($hasRequiredColumn)
  77 + for (const [index, row] of getData().entries()) {
  78 +#foreach ($column in $subColumns)
  79 +#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey && $column.id != $subJoinColumn.id)
  80 + if (
  81 + row.${column.javaField} === null ||
  82 + row.${column.javaField} === undefined ||
  83 + (typeof row.${column.javaField} === 'string' && String(row.${column.javaField}).trim() === '') ||
  84 + (Array.isArray(row.${column.javaField}) && row.${column.javaField}.length === 0)
  85 + ) {
  86 + message.error(`第 #[[${index + 1}]]# 行:${column.columnComment}不能为空`);
  87 + return false;
  88 + }
  89 +#end
  90 +#end
  91 + }
  92 +#end
  93 + return true;
  94 +}
  95 +
  96 +defineExpose({ getData, validate });
69 97
70 /** 监听主表的关联字段的变化,加载对应的子表数据 */ 98 /** 监听主表的关联字段的变化,加载对应的子表数据 */
71 watch( 99 watch(
72 () => props.${subJoinColumn.javaField}, 100 () => props.${subJoinColumn.javaField},
73 async (val) => { 101 async (val) => {
  102 + list.value = [];
74 if (!val) { 103 if (!val) {
75 return; 104 return;
76 } 105 }
@@ -109,6 +138,17 @@ defineExpose({ @@ -109,6 +138,17 @@ defineExpose({
109 watch( 138 watch(
110 () => props.${subJoinColumn.javaField}, 139 () => props.${subJoinColumn.javaField},
111 async (val) => { 140 async (val) => {
  141 + formData.value = {
  142 +#foreach ($column in $subColumns)
  143 +#if ($column.createOperation || $column.updateOperation)
  144 +#if ($column.htmlType == "checkbox")
  145 + $column.javaField: [],
  146 +#else
  147 + $column.javaField: undefined,
  148 +#end
  149 +#end
  150 +#end
  151 + };
112 if (!val) { 152 if (!val) {
113 return; 153 return;
114 } 154 }
@@ -126,6 +166,7 @@ watch( @@ -126,6 +166,7 @@ watch(
126 #foreach($column in $subColumns) 166 #foreach($column in $subColumns)
127 #if ($column.createOperation || $column.updateOperation) 167 #if ($column.createOperation || $column.updateOperation)
128 #set ($comment = $column.columnComment) 168 #set ($comment = $column.columnComment)
  169 + #set ($dictType = $column.dictType)
129 #set ($javaField = $column.javaField) 170 #set ($javaField = $column.javaField)
130 #set ($javaType = $column.javaType) 171 #set ($javaType = $column.javaType)
131 #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short") 172 #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/schema/views/data.ts.vm
@@ -36,7 +36,7 @@ export function useFormSchema(): VbenFormSchema[] { @@ -36,7 +36,7 @@ export function useFormSchema(): VbenFormSchema[] {
36 id: 0, 36 id: 0,
37 ${treeNameColumn.javaField}: '顶级${table.classComment}', 37 ${treeNameColumn.javaField}: '顶级${table.classComment}',
38 }); 38 });
39 - return handleTree(data); 39 + return handleTree(data, 'id', '${treeParentColumn.javaField}');
40 }, 40 },
41 labelField: '${treeNameColumn.javaField}', 41 labelField: '${treeNameColumn.javaField}',
42 valueField: 'id', 42 valueField: 'id',
@@ -89,7 +89,7 @@ export function useFormSchema(): VbenFormSchema[] { @@ -89,7 +89,7 @@ export function useFormSchema(): VbenFormSchema[] {
89 placeholder: '请选择${comment}', 89 placeholder: '请选择${comment}',
90 }, 90 },
91 #elseif($column.htmlType == "checkbox")## 多选框 91 #elseif($column.htmlType == "checkbox")## 多选框
92 - component: 'Checkbox', 92 + component: 'CheckboxGroup',
93 componentProps: { 93 componentProps: {
94 #if ("" != $dictType)## 有数据字典 94 #if ("" != $dictType)## 有数据字典
95 options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), 95 options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
@@ -305,7 +305,7 @@ export function use${subSimpleClassName}FormSchema(): VbenFormSchema[] { @@ -305,7 +305,7 @@ export function use${subSimpleClassName}FormSchema(): VbenFormSchema[] {
305 placeholder: '请选择${comment}', 305 placeholder: '请选择${comment}',
306 }, 306 },
307 #elseif($column.htmlType == "checkbox")## 多选框 307 #elseif($column.htmlType == "checkbox")## 多选框
308 - component: 'Checkbox', 308 + component: 'CheckboxGroup',
309 componentProps: { 309 componentProps: {
310 #if ("" != $dictType)## 有数据字典 310 #if ("" != $dictType)## 有数据字典
311 options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), 311 options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
@@ -444,6 +444,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions&lt;${api @@ -444,6 +444,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions&lt;${api
444 #if ($column.createOperation || $column.updateOperation) 444 #if ($column.createOperation || $column.updateOperation)
445 #if (!$column.primaryKey && $column.listOperationResult && $column.id != $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写 445 #if (!$column.primaryKey && $column.listOperationResult && $column.id != $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
446 #set ($dictType = $column.dictType) 446 #set ($dictType = $column.dictType)
  447 + #set ($javaType = $column.javaType)
447 #set ($javaField = $column.javaField) 448 #set ($javaField = $column.javaField)
448 #set ($comment = $column.columnComment) 449 #set ($comment = $column.columnComment)
449 #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short") 450 #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
@@ -538,7 +539,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions&lt;${api @@ -538,7 +539,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions&lt;${api
538 placeholder: '请选择${comment}', 539 placeholder: '请选择${comment}',
539 }, 540 },
540 #elseif($column.htmlType == "checkbox")## 多选框 541 #elseif($column.htmlType == "checkbox")## 多选框
541 - component: 'Checkbox', 542 + component: 'CheckboxGroup',
542 componentProps: { 543 componentProps: {
543 #if ("" != $dictType)## 有数据字典 544 #if ("" != $dictType)## 有数据字典
544 options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), 545 options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
@@ -587,7 +588,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions&lt;${api @@ -587,7 +588,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions&lt;${api
587 #end 588 #end
588 #if ($table.templateType == 12) ## 内嵌情况 589 #if ($table.templateType == 12) ## 内嵌情况
589 /** 列表的字段 */ 590 /** 列表的字段 */
590 - export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${simpleClassName}Api.${subSimpleClassName}>['columns'] { 591 + export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${apiName}.${subSimpleClassName}>['columns'] {
591 return [ 592 return [
592 #foreach($column in $subColumns) 593 #foreach($column in $subColumns)
593 #if ($column.listOperationResult) 594 #if ($column.listOperationResult)
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/schema/views/form.vue.vm
@@ -69,15 +69,11 @@ const [Modal, modalApi] = useVbenModal({ @@ -69,15 +69,11 @@ const [Modal, modalApi] = useVbenModal({
69 #foreach ($subTable in $subTables) 69 #foreach ($subTable in $subTables)
70 #set ($index = $foreach.count - 1) 70 #set ($index = $foreach.count - 1)
71 #set ($subClassNameVar = $subClassNameVars.get($index)) 71 #set ($subClassNameVar = $subClassNameVars.get($index))
72 - #if ($subTable.subJoinMany) ## 一对多  
73 - ## TODO 列表值校验?  
74 - #else  
75 const ${subClassNameVar}Valid = await ${subClassNameVar}FormRef.value?.validate(); 72 const ${subClassNameVar}Valid = await ${subClassNameVar}FormRef.value?.validate();
76 if (!${subClassNameVar}Valid) { 73 if (!${subClassNameVar}Valid) {
77 subTabsName.value = '${subClassNameVar}'; 74 subTabsName.value = '${subClassNameVar}';
78 return; 75 return;
79 } 76 }
80 - #end  
81 #end 77 #end
82 #end 78 #end
83 #end 79 #end
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/schema/views/import.vue.vm
@@ -49,6 +49,11 @@ function beforeUpload(file: File) { @@ -49,6 +49,11 @@ function beforeUpload(file: File) {
49 return false; 49 return false;
50 } 50 }
51 51
  52 +/** 移除文件 */
  53 +function handleRemove() {
  54 + formApi.setFieldValue('file', undefined);
  55 +}
  56 +
52 /** 下载模板 */ 57 /** 下载模板 */
53 async function handleDownload() { 58 async function handleDownload() {
54 const data = await import${simpleClassName}Template(); 59 const data = await import${simpleClassName}Template();
@@ -65,6 +70,7 @@ async function handleDownload() { @@ -65,6 +70,7 @@ async function handleDownload() {
65 :max-count="1" 70 :max-count="1"
66 accept=".xls,.xlsx" 71 accept=".xls,.xlsx"
67 :before-upload="beforeUpload" 72 :before-upload="beforeUpload"
  73 + :on-remove="handleRemove"
68 > 74 >
69 <Button type="primary"> 选择 Excel 文件 </Button> 75 <Button type="primary"> 选择 Excel 文件 </Button>
70 </Upload> 76 </Upload>
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/schema/views/modules/form_sub_normal.vue.vm
@@ -26,6 +26,7 @@ import { @@ -26,6 +26,7 @@ import {
26 CheckboxGroup, 26 CheckboxGroup,
27 DatePicker, 27 DatePicker,
28 Input, 28 Input,
  29 + message,
29 RadioGroup, 30 RadioGroup,
30 Select, 31 Select,
31 #if ($selectOptionEnable) 32 #if ($selectOptionEnable)
@@ -77,26 +78,54 @@ const handleDelete = async (row: ${apiName}.${subSimpleClassName}) =&gt; { @@ -77,26 +78,54 @@ const handleDelete = async (row: ${apiName}.${subSimpleClassName}) =&gt; {
77 await gridApi.grid.remove(row); 78 await gridApi.grid.remove(row);
78 } 79 }
79 80
80 -/** 提供获取表格数据的方法供父组件调用 */  
81 -defineExpose({  
82 - getData: (): ${apiName}.${subSimpleClassName}[] => {  
83 - const data = gridApi.grid.getData() as ${apiName}.${subSimpleClassName}[];  
84 - const removeRecords = gridApi.grid.getRemoveRecords() as ${apiName}.${subSimpleClassName}[];  
85 - const insertRecords = gridApi.grid.getInsertRecords() as ${apiName}.${subSimpleClassName}[];  
86 - return data  
87 - .filter((row) => !removeRecords.some((removed) => removed.id === row.id))  
88 - .concat(insertRecords.map((row: any) => ({ ...row, id: undefined })));  
89 - },  
90 -}); 81 +/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
  82 +function getData(): ${apiName}.${subSimpleClassName}[] {
  83 + const { fullData } = gridApi.grid.getTableData();
  84 + const inserted = new Set(gridApi.grid.getInsertRecords());
  85 + return fullData.map((row) =>
  86 + inserted.has(row) ? { ...row, id: undefined } : { ...row },
  87 + );
  88 +}
  89 +
  90 +#set ($hasRequiredColumn = false)
  91 +#foreach ($column in $subColumns)
  92 +#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey && $column.id != $subJoinColumn.id)
  93 +#set ($hasRequiredColumn = true)
  94 +#end
  95 +#end
  96 +/** 校验子表必填字段 */
  97 +function validate(): boolean {
  98 +#if ($hasRequiredColumn)
  99 + for (const [index, row] of getData().entries()) {
  100 +#foreach ($column in $subColumns)
  101 +#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey && $column.id != $subJoinColumn.id)
  102 + if (
  103 + row.${column.javaField} === null ||
  104 + row.${column.javaField} === undefined ||
  105 + (typeof row.${column.javaField} === 'string' && String(row.${column.javaField}).trim() === '') ||
  106 + (Array.isArray(row.${column.javaField}) && row.${column.javaField}.length === 0)
  107 + ) {
  108 + message.error(`第 #[[${index + 1}]]# 行:${column.columnComment}不能为空`);
  109 + return false;
  110 + }
  111 +#end
  112 +#end
  113 + }
  114 +#end
  115 + return true;
  116 +}
  117 +
  118 +defineExpose({ getData, validate });
91 119
92 /** 监听主表的关联字段的变化,加载对应的子表数据 */ 120 /** 监听主表的关联字段的变化,加载对应的子表数据 */
93 watch( 121 watch(
94 () => props.${subJoinColumn.javaField}, 122 () => props.${subJoinColumn.javaField},
95 async (val) => { 123 async (val) => {
  124 + await nextTick();
96 if (!val) { 125 if (!val) {
  126 + await gridApi.grid.loadData([]);
97 return; 127 return;
98 } 128 }
99 - await nextTick();  
100 await gridApi.grid.loadData(await get${subSimpleClassName}ListBy${SubJoinColumnName}(props.${subJoinColumn.javaField}!)); 129 await gridApi.grid.loadData(await get${subSimpleClassName}ListBy${SubJoinColumnName}(props.${subJoinColumn.javaField}!));
101 }, 130 },
102 { immediate: true }, 131 { immediate: true },
@@ -128,10 +157,11 @@ defineExpose({ @@ -128,10 +157,11 @@ defineExpose({
128 watch( 157 watch(
129 () => props.${subJoinColumn.javaField}, 158 () => props.${subJoinColumn.javaField},
130 async (val) => { 159 async (val) => {
  160 + await nextTick();
  161 + await formApi.resetForm();
131 if (!val) { 162 if (!val) {
132 return; 163 return;
133 } 164 }
134 - await nextTick();  
135 await formApi.setValues(await get${subSimpleClassName}By${SubJoinColumnName}(props.${subJoinColumn.javaField}!)); 165 await formApi.setValues(await get${subSimpleClassName}By${SubJoinColumnName}(props.${subJoinColumn.javaField}!));
136 }, 166 },
137 { immediate: true }, 167 { immediate: true },
yudao-module-infra/src/main/resources/codegen/vue3_vben5_ele/general/views/form.vue.vm
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 -import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}'; 2 +import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${table.businessName}';
3 import type { FormRules } from 'element-plus'; 3 import type { FormRules } from 'element-plus';
4 4
5 import { useVbenModal } from '@vben/common-ui'; 5 import { useVbenModal } from '@vben/common-ui';
@@ -9,7 +9,7 @@ import { Tinymce as RichTextarea } from &#39;#/components/tinymce&#39;; @@ -9,7 +9,7 @@ import { Tinymce as RichTextarea } from &#39;#/components/tinymce&#39;;
9 import { ImageUpload, FileUpload } from "#/components/upload"; 9 import { ImageUpload, FileUpload } from "#/components/upload";
10 import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElSelect, ElOption, ElRadioGroup, ElRadio, ElCheckboxGroup, ElCheckbox, ElDatePicker, ElTreeSelect } from 'element-plus'; 10 import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElSelect, ElOption, ElRadioGroup, ElRadio, ElCheckboxGroup, ElCheckbox, ElDatePicker, ElTreeSelect } from 'element-plus';
11 #if($table.templateType == 2)## 树表需要导入这些 11 #if($table.templateType == 2)## 树表需要导入这些
12 -import { get${simpleClassName}List } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}'; 12 +import { get${simpleClassName}List } from '#/api/${table.moduleName}/${table.businessName}';
13 import { handleTree } from '@vben/utils' 13 import { handleTree } from '@vben/utils'
14 #end 14 #end
15 ## 特殊:主子表专属逻辑 15 ## 特殊:主子表专属逻辑
@@ -23,7 +23,7 @@ import { handleTree } from &#39;@vben/utils&#39; @@ -23,7 +23,7 @@ import { handleTree } from &#39;@vben/utils&#39;
23 23
24 import { computed, ref, reactive } from 'vue'; 24 import { computed, ref, reactive } from 'vue';
25 import { $t } from '#/locales'; 25 import { $t } from '#/locales';
26 -import { get${simpleClassName}, create${simpleClassName}, update${simpleClassName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}'; 26 +import { get${simpleClassName}, create${simpleClassName}, update${simpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';
27 27
28 const emit = defineEmits(['success']); 28 const emit = defineEmits(['success']);
29 29
@@ -111,7 +111,10 @@ const [Modal, modalApi] = useVbenModal({ @@ -111,7 +111,10 @@ const [Modal, modalApi] = useVbenModal({
111 #set ($index = $foreach.count - 1) 111 #set ($index = $foreach.count - 1)
112 #set ($subClassNameVar = $subClassNameVars.get($index)) 112 #set ($subClassNameVar = $subClassNameVars.get($index))
113 #if ($subTable.subJoinMany) ## 一对多 113 #if ($subTable.subJoinMany) ## 一对多
114 - ## TODO 列表值校验? 114 + if (!${subClassNameVar}FormRef.value?.validate()) {
  115 + subTabsName.value = '${subClassNameVar}';
  116 + return;
  117 + }
115 #else 118 #else
116 try { 119 try {
117 await ${subClassNameVar}FormRef.value?.validate() 120 await ${subClassNameVar}FormRef.value?.validate()
@@ -180,7 +183,7 @@ const [Modal, modalApi] = useVbenModal({ @@ -180,7 +183,7 @@ const [Modal, modalApi] = useVbenModal({
180 183
181 <template> 184 <template>
182 <Modal :title="getTitle"> 185 <Modal :title="getTitle">
183 - <el-form 186 + <ElForm
184 ref="formRef" 187 ref="formRef"
185 :model="formData" 188 :model="formData"
186 :rules="rules" 189 :rules="rules"
@@ -201,8 +204,8 @@ const [Modal, modalApi] = useVbenModal({ @@ -201,8 +204,8 @@ const [Modal, modalApi] = useVbenModal({
201 #set ($dictMethod = "boolean") 204 #set ($dictMethod = "boolean")
202 #end 205 #end
203 #if ( $table.templateType == 2 && $column.id == $treeParentColumn.id ) 206 #if ( $table.templateType == 2 && $column.id == $treeParentColumn.id )
204 - <el-form-item label="${comment}" prop="${javaField}">  
205 - <el-tree-select 207 + <ElFormItem label="${comment}" prop="${javaField}">
  208 + <ElTreeSelect
206 v-model="formData.${javaField}" 209 v-model="formData.${javaField}"
207 :data="${classNameVar}Tree" 210 :data="${classNameVar}Tree"
208 #if ($treeNameColumn.javaField == "name") 211 #if ($treeNameColumn.javaField == "name")
@@ -222,100 +225,100 @@ const [Modal, modalApi] = useVbenModal({ @@ -222,100 +225,100 @@ const [Modal, modalApi] = useVbenModal({
222 default-expand-all 225 default-expand-all
223 placeholder="请选择${comment}" 226 placeholder="请选择${comment}"
224 /> 227 />
225 - </el-form-item> 228 + </ElFormItem>
226 #elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里 229 #elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
227 - <el-form-item label="${comment}" prop="${javaField}">  
228 - <el-input v-model="formData.${javaField}" placeholder="请输入${comment}" />  
229 - </el-form-item> 230 + <ElFormItem label="${comment}" prop="${javaField}">
  231 + <ElInput v-model="formData.${javaField}" placeholder="请输入${comment}" />
  232 + </ElFormItem>
230 #elseif($column.htmlType == "imageUpload")## 图片上传 233 #elseif($column.htmlType == "imageUpload")## 图片上传
231 - <el-form-item label="${comment}" prop="${javaField}"> 234 + <ElFormItem label="${comment}" prop="${javaField}">
232 <ImageUpload v-model="formData.${javaField}" /> 235 <ImageUpload v-model="formData.${javaField}" />
233 - </el-form-item> 236 + </ElFormItem>
234 #elseif($column.htmlType == "fileUpload")## 文件上传 237 #elseif($column.htmlType == "fileUpload")## 文件上传
235 - <el-form-item label="${comment}" prop="${javaField}"> 238 + <ElFormItem label="${comment}" prop="${javaField}">
236 <FileUpload v-model="formData.${javaField}" /> 239 <FileUpload v-model="formData.${javaField}" />
237 - </el-form-item> 240 + </ElFormItem>
238 #elseif($column.htmlType == "editor")## 文本编辑器 241 #elseif($column.htmlType == "editor")## 文本编辑器
239 - <el-form-item label="${comment}" prop="${javaField}"> 242 + <ElFormItem label="${comment}" prop="${javaField}">
240 <RichTextarea v-model="formData.${javaField}" height="500px" /> 243 <RichTextarea v-model="formData.${javaField}" height="500px" />
241 - </el-form-item> 244 + </ElFormItem>
242 #elseif($column.htmlType == "select")## 下拉框 245 #elseif($column.htmlType == "select")## 下拉框
243 - <el-form-item label="${comment}" prop="${javaField}">  
244 - <el-select v-model="formData.${javaField}" placeholder="请选择${comment}"> 246 + <ElFormItem label="${comment}" prop="${javaField}">
  247 + <ElSelect v-model="formData.${javaField}" placeholder="请选择${comment}">
245 #if ("" != $dictType)## 有数据字典 248 #if ("" != $dictType)## 有数据字典
246 - <el-option 249 + <ElOption
247 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')" 250 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
248 :key="dict.value" 251 :key="dict.value"
249 :value="dict.value" 252 :value="dict.value"
250 :label="dict.label" 253 :label="dict.label"
251 /> 254 />
252 #else##没数据字典 255 #else##没数据字典
253 - <el-option label="请选择字典生成" value="" /> 256 + <ElOption label="请选择字典生成" value="" />
254 #end 257 #end
255 - </el-select>  
256 - </el-form-item> 258 + </ElSelect>
  259 + </ElFormItem>
257 #elseif($column.htmlType == "checkbox")## 多选框 260 #elseif($column.htmlType == "checkbox")## 多选框
258 - <el-form-item label="${comment}" prop="${javaField}">  
259 - <el-checkbox-group v-model="formData.${javaField}"> 261 + <ElFormItem label="${comment}" prop="${javaField}">
  262 + <ElCheckboxGroup v-model="formData.${javaField}">
260 #if ("" != $dictType)## 有数据字典 263 #if ("" != $dictType)## 有数据字典
261 - <el-checkbox 264 + <ElCheckbox
262 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')" 265 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
263 :key="dict.value" 266 :key="dict.value"
264 - :label="dict.value" 267 + :value="dict.value"
265 > 268 >
266 {{ dict.label }} 269 {{ dict.label }}
267 - </el-checkbox> 270 + </ElCheckbox>
268 #else##没数据字典 271 #else##没数据字典
269 - <el-checkbox label="请选择字典生成" /> 272 + <ElCheckbox label="请选择字典生成" />
270 #end 273 #end
271 - </el-checkbox-group>  
272 - </el-form-item> 274 + </ElCheckboxGroup>
  275 + </ElFormItem>
273 #elseif($column.htmlType == "radio")## 单选框 276 #elseif($column.htmlType == "radio")## 单选框
274 - <el-form-item label="${comment}" prop="${javaField}">  
275 - <el-radio-group v-model="formData.${javaField}"> 277 + <ElFormItem label="${comment}" prop="${javaField}">
  278 + <ElRadioGroup v-model="formData.${javaField}">
276 #if ("" != $dictType)## 有数据字典 279 #if ("" != $dictType)## 有数据字典
277 - <el-radio 280 + <ElRadio
278 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')" 281 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
279 :key="dict.value" 282 :key="dict.value"
280 - :label="dict.value" 283 + :value="dict.value"
281 > 284 >
282 {{ dict.label }} 285 {{ dict.label }}
283 - </el-radio> 286 + </ElRadio>
284 #else##没数据字典 287 #else##没数据字典
285 - <el-radio :label="1">请选择字典生成</el-radio> 288 + <ElRadio :label="1">请选择字典生成</ElRadio>
286 #end 289 #end
287 - </el-radio-group>  
288 - </el-form-item> 290 + </ElRadioGroup>
  291 + </ElFormItem>
289 #elseif($column.htmlType == "datetime")## 时间框 292 #elseif($column.htmlType == "datetime")## 时间框
290 - <el-form-item label="${comment}" prop="${javaField}">  
291 - <el-date-picker 293 + <ElFormItem label="${comment}" prop="${javaField}">
  294 + <ElDatePicker
292 v-model="formData.${javaField}" 295 v-model="formData.${javaField}"
293 value-format="x" 296 value-format="x"
294 placeholder="选择${comment}" 297 placeholder="选择${comment}"
295 /> 298 />
296 - </el-form-item> 299 + </ElFormItem>
297 #elseif($column.htmlType == "textarea")## 文本框 300 #elseif($column.htmlType == "textarea")## 文本框
298 - <el-form-item label="${comment}" prop="${javaField}">  
299 - <el-input v-model="formData.${javaField}" type="textarea" placeholder="请输入${comment}" />  
300 - </el-form-item> 301 + <ElFormItem label="${comment}" prop="${javaField}">
  302 + <ElInput v-model="formData.${javaField}" type="textarea" placeholder="请输入${comment}" />
  303 + </ElFormItem>
301 #end 304 #end
302 #end 305 #end
303 #end 306 #end
304 - </el-form> 307 + </ElForm>
305 ## 特殊:主子表专属逻辑 308 ## 特殊:主子表专属逻辑
306 #if ( $table.templateType == 10 || $table.templateType == 12 ) 309 #if ( $table.templateType == 10 || $table.templateType == 12 )
307 <!-- 子表的表单 --> 310 <!-- 子表的表单 -->
308 - <el-tabs v-model="subTabsName"> 311 + <ElTabs v-model="subTabsName">
309 #foreach ($subTable in $subTables) 312 #foreach ($subTable in $subTables)
310 #set ($index = $foreach.count - 1) 313 #set ($index = $foreach.count - 1)
311 #set ($subClassNameVar = $subClassNameVars.get($index)) 314 #set ($subClassNameVar = $subClassNameVars.get($index))
312 #set ($subSimpleClassName = $subSimpleClassNames.get($index)) 315 #set ($subSimpleClassName = $subSimpleClassNames.get($index))
313 #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index)) 316 #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
314 - <el-tab-pane name="$subClassNameVar" label="${subTable.classComment}"> 317 + <ElTabPane name="$subClassNameVar" label="${subTable.classComment}">
315 <${subSimpleClassName}Form ref="${subClassNameVar}FormRef" :${subJoinColumn_strikeCase}="formData?.id" /> 318 <${subSimpleClassName}Form ref="${subClassNameVar}FormRef" :${subJoinColumn_strikeCase}="formData?.id" />
316 - </el-tab-pane> 319 + </ElTabPane>
317 #end 320 #end
318 - </el-tabs> 321 + </ElTabs>
319 #end 322 #end
320 </Modal> 323 </Modal>
321 </template> 324 </template>
yudao-module-infra/src/main/resources/codegen/vue3_vben5_ele/general/views/import.vue.vm
@@ -47,6 +47,16 @@ function handleChange(file: any) { @@ -47,6 +47,16 @@ function handleChange(file: any) {
47 } 47 }
48 } 48 }
49 49
  50 +/** 移除文件 */
  51 +function handleRemove() {
  52 + fileRef.value = null;
  53 +}
  54 +
  55 +/** 超出文件数量 */
  56 +function handleExceed() {
  57 + ElMessage.warning('只能选择一个文件,请先移除已选文件');
  58 +}
  59 +
50 /** 下载导入模板 */ 60 /** 下载导入模板 */
51 async function handleDownload() { 61 async function handleDownload() {
52 const data = await import${simpleClassName}Template(); 62 const data = await import${simpleClassName}Template();
@@ -61,6 +71,8 @@ async function handleDownload() { @@ -61,6 +71,8 @@ async function handleDownload() {
61 :limit="1" 71 :limit="1"
62 accept=".xls,.xlsx" 72 accept=".xls,.xlsx"
63 :on-change="handleChange" 73 :on-change="handleChange"
  74 + :on-remove="handleRemove"
  75 + :on-exceed="handleExceed"
64 :auto-upload="false" 76 :auto-upload="false"
65 > 77 >
66 <ElButton type="primary"> 选择 Excel 文件 </ElButton> 78 <ElButton type="primary"> 选择 Excel 文件 </ElButton>
yudao-module-infra/src/main/resources/codegen/vue3_vben5_ele/general/views/index.vue.vm
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 -import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}'; 2 +import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${table.businessName}';
3 3
4 import { ref, h, reactive, onMounted, nextTick } from 'vue'; 4 import { ref, h, reactive, onMounted, nextTick } from 'vue';
5 5
@@ -26,10 +26,10 @@ import { VxeColumn, VxeTable } from &#39;#/adapter/vxe-table&#39;; @@ -26,10 +26,10 @@ import { VxeColumn, VxeTable } from &#39;#/adapter/vxe-table&#39;;
26 import { $t } from '#/locales'; 26 import { $t } from '#/locales';
27 #if (${table.templateType} == 2)## 树表接口 27 #if (${table.templateType} == 2)## 树表接口
28 import { handleTree,isEmpty } from '@vben/utils' 28 import { handleTree,isEmpty } from '@vben/utils'
29 -import { get${simpleClassName}List, delete${simpleClassName}, export${simpleClassName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}'; 29 +import { get${simpleClassName}List, delete${simpleClassName}, export${simpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';
30 #else## 标准表接口 30 #else## 标准表接口
31 import { isEmpty } from '@vben/utils'; 31 import { isEmpty } from '@vben/utils';
32 -import { get${simpleClassName}Page, delete${simpleClassName},#if ($deleteBatchEnable) delete${simpleClassName}List,#end export${simpleClassName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}'; 32 +import { get${simpleClassName}Page, delete${simpleClassName},#if ($deleteBatchEnable) delete${simpleClassName}List,#end export${simpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';
33 #end 33 #end
34 #if ($importEnable) 34 #if ($importEnable)
35 import ${simpleClassName}Import from './modules/import-form.vue'; 35 import ${simpleClassName}Import from './modules/import-form.vue';
@@ -65,12 +65,7 @@ const queryParams = reactive({ @@ -65,12 +65,7 @@ const queryParams = reactive({
65 #end 65 #end
66 #foreach ($column in $columns) 66 #foreach ($column in $columns)
67 #if ($column.listOperation) 67 #if ($column.listOperation)
68 - #if ($column.listOperationCondition != 'BETWEEN')  
69 - $column.javaField: undefined,  
70 - #end  
71 - #if ($column.htmlType == "datetime" || $column.listOperationCondition == "BETWEEN")  
72 - $column.javaField: undefined,  
73 - #end 68 + $column.javaField: undefined,
74 #end 69 #end
75 #end 70 #end
76 }) 71 })
@@ -195,7 +190,17 @@ function handleRowCheckboxChange({ @@ -195,7 +190,17 @@ function handleRowCheckboxChange({
195 async function handleExport() { 190 async function handleExport() {
196 try { 191 try {
197 exportLoading.value = true; 192 exportLoading.value = true;
198 - const data = await export${simpleClassName}(queryParams); 193 + const params = cloneDeep(queryParams) as any;
  194 + #foreach ($column in $columns)
  195 + #if ($column.listOperation)
  196 + #if ($column.htmlType == "datetime" || $column.listOperationCondition == "BETWEEN")
  197 + if (params.${column.javaField} && Array.isArray(params.${column.javaField})) {
  198 + params.${column.javaField} = (params.${column.javaField} as string[]).join(',');
  199 + }
  200 + #end
  201 + #end
  202 + #end
  203 + const data = await export${simpleClassName}(params);
199 downloadFileFromBlobPart({ fileName: '${table.classComment}.xls', source: data }); 204 downloadFileFromBlobPart({ fileName: '${table.classComment}.xls', source: data });
200 }finally { 205 }finally {
201 exportLoading.value = false; 206 exportLoading.value = false;
@@ -228,7 +233,7 @@ onMounted(() =&gt; { @@ -228,7 +233,7 @@ onMounted(() =&gt; {
228 233
229 <ContentWrap v-if="!hiddenSearchBar"> 234 <ContentWrap v-if="!hiddenSearchBar">
230 <!-- 搜索工作栏 --> 235 <!-- 搜索工作栏 -->
231 - <el-form 236 + <ElForm
232 :model="queryParams" 237 :model="queryParams"
233 ref="queryFormRef" 238 ref="queryFormRef"
234 inline 239 inline
@@ -247,49 +252,49 @@ onMounted(() =&gt; { @@ -247,49 +252,49 @@ onMounted(() =&gt; {
247 #set ($dictMethod = "boolean") 252 #set ($dictMethod = "boolean")
248 #end 253 #end
249 #if ($column.htmlType == "input") 254 #if ($column.htmlType == "input")
250 - <el-form-item label="${comment}">  
251 - <el-input 255 + <ElFormItem label="${comment}">
  256 + <ElInput
252 v-model="queryParams.${javaField}" 257 v-model="queryParams.${javaField}"
253 placeholder="请输入${comment}" 258 placeholder="请输入${comment}"
254 clearable 259 clearable
255 @keyup.enter="handleQuery" 260 @keyup.enter="handleQuery"
256 class="!w-[240px]" 261 class="!w-[240px]"
257 /> 262 />
258 - </el-form-item> 263 + </ElFormItem>
259 #elseif ($column.htmlType == "select" || $column.htmlType == "radio" || $column.htmlType == "checkbox") 264 #elseif ($column.htmlType == "select" || $column.htmlType == "radio" || $column.htmlType == "checkbox")
260 - <el-form-item label="${comment}">  
261 - <el-select 265 + <ElFormItem label="${comment}">
  266 + <ElSelect
262 v-model="queryParams.${javaField}" 267 v-model="queryParams.${javaField}"
263 placeholder="请选择${comment}" 268 placeholder="请选择${comment}"
264 clearable 269 clearable
265 class="!w-[240px]" 270 class="!w-[240px]"
266 > 271 >
267 #if ("" != $dictType)## 设置了 dictType 数据字典的情况 272 #if ("" != $dictType)## 设置了 dictType 数据字典的情况
268 - <el-option 273 + <ElOption
269 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')" 274 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
270 :key="dict.value" 275 :key="dict.value"
271 :value="dict.value" 276 :value="dict.value"
272 :label="dict.label" 277 :label="dict.label"
273 /> 278 />
274 #else## 未设置 dictType 数据字典的情况 279 #else## 未设置 dictType 数据字典的情况
275 - <el-option label="请选择字典生成" value="" /> 280 + <ElOption label="请选择字典生成" value="" />
276 #end 281 #end
277 - </el-select>  
278 - </el-form-item> 282 + </ElSelect>
  283 + </ElFormItem>
279 #elseif($column.htmlType == "datetime") 284 #elseif($column.htmlType == "datetime")
280 #if ($column.listOperationCondition != "BETWEEN")## 非范围 285 #if ($column.listOperationCondition != "BETWEEN")## 非范围
281 - <el-form-item label="${comment}">  
282 - <el-date-picker 286 + <ElFormItem label="${comment}">
  287 + <ElDatePicker
283 v-model="queryParams.${javaField}" 288 v-model="queryParams.${javaField}"
284 value-format="YYYY-MM-DD" 289 value-format="YYYY-MM-DD"
285 placeholder="选择${comment}" 290 placeholder="选择${comment}"
286 clearable 291 clearable
287 class="!w-[240px]" 292 class="!w-[240px]"
288 /> 293 />
289 - </el-form-item> 294 + </ElFormItem>
290 #else## 范围 295 #else## 范围
291 - <el-form-item label="${comment}">  
292 - <el-date-picker 296 + <ElFormItem label="${comment}">
  297 + <ElDatePicker
293 v-model="queryParams.${javaField}" 298 v-model="queryParams.${javaField}"
294 type="daterange" 299 type="daterange"
295 value-format="YYYY-MM-DD" 300 value-format="YYYY-MM-DD"
@@ -298,33 +303,33 @@ onMounted(() =&gt; { @@ -298,33 +303,33 @@ onMounted(() =&gt; {
298 end-placeholder="结束日期" 303 end-placeholder="结束日期"
299 class="!w-[240px]" 304 class="!w-[240px]"
300 /> 305 />
301 - </el-form-item> 306 + </ElFormItem>
302 #end 307 #end
303 #end 308 #end
304 #end 309 #end
305 #end 310 #end
306 - <el-form-item>  
307 - <el-button class="ml-2" @click="resetQuery"> 重置 </el-button>  
308 - <el-button class="ml-2" @click="handleQuery" type="primary"> 311 + <ElFormItem>
  312 + <ElButton class="ml-2" @click="resetQuery"> 重置 </ElButton>
  313 + <ElButton class="ml-2" @click="handleQuery" type="primary">
309 搜索 314 搜索
310 - </el-button>  
311 - </el-form-item>  
312 - </el-form> 315 + </ElButton>
  316 + </ElFormItem>
  317 + </ElForm>
313 </ContentWrap> 318 </ContentWrap>
314 319
315 <!-- 列表 --> 320 <!-- 列表 -->
316 <ContentWrap title="${table.classComment}"> 321 <ContentWrap title="${table.classComment}">
317 <template #extra> 322 <template #extra>
318 - <TableToolbar 323 + <VbenVxeTableToolbar
319 ref="tableToolbarRef" 324 ref="tableToolbarRef"
320 v-model:hidden-search="hiddenSearchBar" 325 v-model:hidden-search="hiddenSearchBar"
321 > 326 >
322 #if (${table.templateType} == 2) 327 #if (${table.templateType} == 2)
323 - <el-button @click="handleExpand" class="mr-2"> 328 + <ElButton @click="handleExpand" class="mr-2">
324 {{ isExpanded ? '收缩' : '展开' }} 329 {{ isExpanded ? '收缩' : '展开' }}
325 - </el-button> 330 + </ElButton>
326 #end 331 #end
327 - <el-button 332 + <ElButton
328 class="ml-2" 333 class="ml-2"
329 :icon="h(Plus)" 334 :icon="h(Plus)"
330 type="primary" 335 type="primary"
@@ -332,18 +337,18 @@ onMounted(() =&gt; { @@ -332,18 +337,18 @@ onMounted(() =&gt; {
332 v-access:code="['${permissionPrefix}:create']" 337 v-access:code="['${permissionPrefix}:create']"
333 > 338 >
334 {{ $t('ui.actionTitle.create', ['${table.classComment}']) }} 339 {{ $t('ui.actionTitle.create', ['${table.classComment}']) }}
335 - </el-button> 340 + </ElButton>
336 #if ($importEnable) 341 #if ($importEnable)
337 - <el-button 342 + <ElButton
338 class="ml-2" 343 class="ml-2"
339 type="primary" 344 type="primary"
340 @click="handleImport" 345 @click="handleImport"
341 v-access:code="['${permissionPrefix}:import']" 346 v-access:code="['${permissionPrefix}:import']"
342 > 347 >
343 导入 348 导入
344 - </el-button> 349 + </ElButton>
345 #end 350 #end
346 - <el-button 351 + <ElButton
347 :icon="h(Download)" 352 :icon="h(Download)"
348 type="primary" 353 type="primary"
349 class="ml-2" 354 class="ml-2"
@@ -352,9 +357,9 @@ onMounted(() =&gt; { @@ -352,9 +357,9 @@ onMounted(() =&gt; {
352 v-access:code="['${permissionPrefix}:export']" 357 v-access:code="['${permissionPrefix}:export']"
353 > 358 >
354 {{ $t('ui.actionTitle.export') }} 359 {{ $t('ui.actionTitle.export') }}
355 - </el-button> 360 + </ElButton>
356 #if ($table.templateType != 2 && $deleteBatchEnable) 361 #if ($table.templateType != 2 && $deleteBatchEnable)
357 - <el-button 362 + <ElButton
358 :icon="h(Trash2)" 363 :icon="h(Trash2)"
359 type="danger" 364 type="danger"
360 class="ml-2" 365 class="ml-2"
@@ -363,11 +368,11 @@ onMounted(() =&gt; { @@ -363,11 +368,11 @@ onMounted(() =&gt; {
363 v-access:code="['${table.moduleName}:${simpleClassName_strikeCase}:delete']" 368 v-access:code="['${table.moduleName}:${simpleClassName_strikeCase}:delete']"
364 > 369 >
365 批量删除 370 批量删除
366 - </el-button> 371 + </ElButton>
367 #end 372 #end
368 - </TableToolbar> 373 + </VbenVxeTableToolbar>
369 </template> 374 </template>
370 - <vxe-table 375 + <VxeTable
371 ref="tableRef" 376 ref="tableRef"
372 :data="list" 377 :data="list"
373 #if ( $table.templateType == 2 ) 378 #if ( $table.templateType == 2 )
@@ -395,27 +400,27 @@ onMounted(() =&gt; { @@ -395,27 +400,27 @@ onMounted(() =&gt; {
395 #end 400 #end
396 > 401 >
397 #if ($table.templateType != 2 && $deleteBatchEnable) 402 #if ($table.templateType != 2 && $deleteBatchEnable)
398 - <vxe-column type="checkbox" width="40"></vxe-column> 403 + <VxeColumn type="checkbox" width="40"></VxeColumn>
399 #end 404 #end
400 ## 特殊:主子表专属逻辑 405 ## 特殊:主子表专属逻辑
401 #if ( $table.templateType == 12 && $subTables && $subTables.size() > 0 ) 406 #if ( $table.templateType == 12 && $subTables && $subTables.size() > 0 )
402 <!-- 子表的列表 --> 407 <!-- 子表的列表 -->
403 - <vxe-column type="expand" width="60"> 408 + <VxeColumn type="expand" width="60">
404 <template #content="{ row }"> 409 <template #content="{ row }">
405 <!-- 子表的表单 --> 410 <!-- 子表的表单 -->
406 - <el-tabs v-model="subTabsName" class="mx-8"> 411 + <ElTabs v-model="subTabsName" class="mx-8">
407 #foreach ($subTable in $subTables) 412 #foreach ($subTable in $subTables)
408 #set ($index = $foreach.count - 1) 413 #set ($index = $foreach.count - 1)
409 #set ($subClassNameVar = $subClassNameVars.get($index)) 414 #set ($subClassNameVar = $subClassNameVars.get($index))
410 #set ($subSimpleClassName = $subSimpleClassNames.get($index)) 415 #set ($subSimpleClassName = $subSimpleClassNames.get($index))
411 #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index)) 416 #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
412 - <el-tab-pane name="$subClassNameVar" label="${subTable.classComment}"> 417 + <ElTabPane name="$subClassNameVar" label="${subTable.classComment}">
413 <${subSimpleClassName}List :${subJoinColumn_strikeCase}="row?.id" /> 418 <${subSimpleClassName}List :${subJoinColumn_strikeCase}="row?.id" />
414 - </el-tab-pane> 419 + </ElTabPane>
415 #end 420 #end
416 - </el-tabs> 421 + </ElTabs>
417 </template> 422 </template>
418 - </vxe-column> 423 + </VxeColumn>
419 #end 424 #end
420 #foreach($column in $columns) 425 #foreach($column in $columns)
421 #if ($column.listOperationResult) 426 #if ($column.listOperationResult)
@@ -424,28 +429,28 @@ onMounted(() =&gt; { @@ -424,28 +429,28 @@ onMounted(() =&gt; {
424 #set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) 429 #set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
425 #set ($comment=$column.columnComment) 430 #set ($comment=$column.columnComment)
426 #if ($column.javaType == "LocalDateTime")## 时间类型 431 #if ($column.javaType == "LocalDateTime")## 时间类型
427 - <vxe-column field="${javaField}" title="${comment}" align="center"> 432 + <VxeColumn field="${javaField}" title="${comment}" align="center">
428 <template #default="{row}"> 433 <template #default="{row}">
429 {{formatDateTime(row.${javaField})}} 434 {{formatDateTime(row.${javaField})}}
430 </template> 435 </template>
431 - </vxe-column> 436 + </VxeColumn>
432 #elseif($column.dictType && "" != $column.dictType)## 数据字典 437 #elseif($column.dictType && "" != $column.dictType)## 数据字典
433 - <vxe-column field="${javaField}" title="${comment}" align="center"> 438 + <VxeColumn field="${javaField}" title="${comment}" align="center">
434 <template #default="{row}"> 439 <template #default="{row}">
435 <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="row.${javaField}" /> 440 <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="row.${javaField}" />
436 </template> 441 </template>
437 - </vxe-column> 442 + </VxeColumn>
438 #elseif ($table.templateType == 2 && $javaField == $treeNameColumn.javaField) 443 #elseif ($table.templateType == 2 && $javaField == $treeNameColumn.javaField)
439 - <vxe-column field="${javaField}" title="${comment}" align="center" tree-node/> 444 + <VxeColumn field="${javaField}" title="${comment}" align="center" tree-node/>
440 #else 445 #else
441 - <vxe-column field="${javaField}" title="${comment}" align="center" /> 446 + <VxeColumn field="${javaField}" title="${comment}" align="center" />
442 #end 447 #end
443 #end 448 #end
444 #end 449 #end
445 - <vxe-column field="operation" title="操作" align="center"> 450 + <VxeColumn field="operation" title="操作" align="center">
446 <template #default="{row}"> 451 <template #default="{row}">
447 #if ( $table.templateType == 2 ) 452 #if ( $table.templateType == 2 )
448 - <el-button 453 + <ElButton
449 size="small" 454 size="small"
450 type="primary" 455 type="primary"
451 link 456 link
@@ -453,9 +458,9 @@ onMounted(() =&gt; { @@ -453,9 +458,9 @@ onMounted(() =&gt; {
453 v-access:code="['${permissionPrefix}:create']" 458 v-access:code="['${permissionPrefix}:create']"
454 > 459 >
455 新增下级 460 新增下级
456 - </el-button> 461 + </ElButton>
457 #end 462 #end
458 - <el-button 463 + <ElButton
459 size="small" 464 size="small"
460 type="primary" 465 type="primary"
461 link 466 link
@@ -463,8 +468,8 @@ onMounted(() =&gt; { @@ -463,8 +468,8 @@ onMounted(() =&gt; {
463 v-access:code="['${permissionPrefix}:update']" 468 v-access:code="['${permissionPrefix}:update']"
464 > 469 >
465 {{ $t('ui.actionTitle.edit') }} 470 {{ $t('ui.actionTitle.edit') }}
466 - </el-button>  
467 - <el-button 471 + </ElButton>
  472 + <ElButton
468 size="small" 473 size="small"
469 type="danger" 474 type="danger"
470 link 475 link
@@ -476,14 +481,14 @@ onMounted(() =&gt; { @@ -476,14 +481,14 @@ onMounted(() =&gt; {
476 v-access:code="['${permissionPrefix}:delete']" 481 v-access:code="['${permissionPrefix}:delete']"
477 > 482 >
478 {{ $t('ui.actionTitle.delete') }} 483 {{ $t('ui.actionTitle.delete') }}
479 - </el-button> 484 + </ElButton>
480 </template> 485 </template>
481 - </vxe-column>  
482 - </vxe-table> 486 + </VxeColumn>
  487 + </VxeTable>
483 #if ( $table.templateType != 2 ) 488 #if ( $table.templateType != 2 )
484 <!-- 分页 --> 489 <!-- 分页 -->
485 <div class="mt-2 flex justify-end"> 490 <div class="mt-2 flex justify-end">
486 - <el-pagination 491 + <ElPagination
487 :total="total" 492 :total="total"
488 v-model:current-page="queryParams.pageNo" 493 v-model:current-page="queryParams.pageNo"
489 v-model:page-size="queryParams.pageSize" 494 v-model:page-size="queryParams.pageSize"
@@ -498,17 +503,17 @@ onMounted(() =&gt; { @@ -498,17 +503,17 @@ onMounted(() =&gt; {
498 #if ($table.templateType == 11) ## erp情况 503 #if ($table.templateType == 11) ## erp情况
499 <ContentWrap> 504 <ContentWrap>
500 <!-- 子表的表单 --> 505 <!-- 子表的表单 -->
501 - <el-tabs v-model="subTabsName"> 506 + <ElTabs v-model="subTabsName">
502 #foreach ($subTable in $subTables) 507 #foreach ($subTable in $subTables)
503 #set ($index = $foreach.count - 1) 508 #set ($index = $foreach.count - 1)
504 #set ($subClassNameVar = $subClassNameVars.get($index)) 509 #set ($subClassNameVar = $subClassNameVars.get($index))
505 #set ($subSimpleClassName = $subSimpleClassNames.get($index)) 510 #set ($subSimpleClassName = $subSimpleClassNames.get($index))
506 #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index)) 511 #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
507 - <el-tab-pane name="$subClassNameVar" label="${subTable.classComment}"> 512 + <ElTabPane name="$subClassNameVar" label="${subTable.classComment}">
508 <${subSimpleClassName}List :${subJoinColumn_strikeCase}="select${simpleClassName}?.id" /> 513 <${subSimpleClassName}List :${subJoinColumn_strikeCase}="select${simpleClassName}?.id" />
509 - </el-tab-pane> 514 + </ElTabPane>
510 #end 515 #end
511 - </el-tabs> 516 + </ElTabs>
512 </ContentWrap> 517 </ContentWrap>
513 #end 518 #end
514 </Page> 519 </Page>
yudao-module-infra/src/main/resources/codegen/vue3_vben5_ele/general/views/modules/form_sub_erp.vue.vm
@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 #set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段 3 #set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
4 #set ($subSimpleClassName = $subSimpleClassNames.get($subIndex)) 4 #set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))
5 <script lang="ts" setup> 5 <script lang="ts" setup>
6 - import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}'; 6 + import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${table.businessName}';
7 import type { FormRules } from 'element-plus'; 7 import type { FormRules } from 'element-plus';
8 8
9 import { computed, ref, reactive } from 'vue'; 9 import { computed, ref, reactive } from 'vue';
@@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
17 17
18 import { $t } from '#/locales'; 18 import { $t } from '#/locales';
19 19
20 - import { get${subSimpleClassName}, create${subSimpleClassName}, update${subSimpleClassName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}'; 20 + import { get${subSimpleClassName}, create${subSimpleClassName}, update${subSimpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';
21 21
22 const emit = defineEmits(['success']); 22 const emit = defineEmits(['success']);
23 const getTitle = computed(() => { 23 const getTitle = computed(() => {
@@ -107,7 +107,7 @@ @@ -107,7 +107,7 @@
107 107
108 <template> 108 <template>
109 <Modal :title="getTitle"> 109 <Modal :title="getTitle">
110 - <el-form 110 + <ElForm
111 ref="formRef" 111 ref="formRef"
112 :model="formData" 112 :model="formData"
113 :rules="rules" 113 :rules="rules"
@@ -128,83 +128,83 @@ @@ -128,83 +128,83 @@
128 #set ($dictMethod = "boolean") 128 #set ($dictMethod = "boolean")
129 #end 129 #end
130 #if ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里 130 #if ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
131 - <el-form-item label="${comment}" prop="${javaField}">  
132 - <el-input v-model="formData.${javaField}" placeholder="请输入${comment}" />  
133 - </el-form-item> 131 + <ElFormItem label="${comment}" prop="${javaField}">
  132 + <ElInput v-model="formData.${javaField}" placeholder="请输入${comment}" />
  133 + </ElFormItem>
134 #elseif($column.htmlType == "imageUpload")## 图片上传 134 #elseif($column.htmlType == "imageUpload")## 图片上传
135 - <el-form-item label="${comment}" prop="${javaField}"> 135 + <ElFormItem label="${comment}" prop="${javaField}">
136 <ImageUpload v-model="formData.${javaField}" /> 136 <ImageUpload v-model="formData.${javaField}" />
137 - </el-form-item> 137 + </ElFormItem>
138 #elseif($column.htmlType == "fileUpload")## 文件上传 138 #elseif($column.htmlType == "fileUpload")## 文件上传
139 - <el-form-item label="${comment}" prop="${javaField}"> 139 + <ElFormItem label="${comment}" prop="${javaField}">
140 <FileUpload v-model="formData.${javaField}" /> 140 <FileUpload v-model="formData.${javaField}" />
141 - </el-form-item> 141 + </ElFormItem>
142 #elseif($column.htmlType == "editor")## 文本编辑器 142 #elseif($column.htmlType == "editor")## 文本编辑器
143 - <el-form-item label="${comment}" prop="${javaField}"> 143 + <ElFormItem label="${comment}" prop="${javaField}">
144 <RichTextarea v-model="formData.${javaField}" height="500px" /> 144 <RichTextarea v-model="formData.${javaField}" height="500px" />
145 - </el-form-item> 145 + </ElFormItem>
146 #elseif($column.htmlType == "select")## 下拉框 146 #elseif($column.htmlType == "select")## 下拉框
147 - <el-form-item label="${comment}" prop="${javaField}">  
148 - <el-select v-model="formData.${javaField}" placeholder="请选择${comment}"> 147 + <ElFormItem label="${comment}" prop="${javaField}">
  148 + <ElSelect v-model="formData.${javaField}" placeholder="请选择${comment}">
149 #if ("" != $dictType)## 有数据字典 149 #if ("" != $dictType)## 有数据字典
150 - <el-option 150 + <ElOption
151 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')" 151 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
152 :key="dict.value" 152 :key="dict.value"
153 :value="dict.value" 153 :value="dict.value"
154 :label="dict.label" 154 :label="dict.label"
155 /> 155 />
156 #else##没数据字典 156 #else##没数据字典
157 - <el-option label="请选择字典生成" value="" /> 157 + <ElOption label="请选择字典生成" value="" />
158 #end 158 #end
159 - </el-select>  
160 - </el-form-item> 159 + </ElSelect>
  160 + </ElFormItem>
161 #elseif($column.htmlType == "checkbox")## 多选框 161 #elseif($column.htmlType == "checkbox")## 多选框
162 - <el-form-item label="${comment}" prop="${javaField}">  
163 - <el-checkbox-group v-model="formData.${javaField}"> 162 + <ElFormItem label="${comment}" prop="${javaField}">
  163 + <ElCheckboxGroup v-model="formData.${javaField}">
164 #if ("" != $dictType)## 有数据字典 164 #if ("" != $dictType)## 有数据字典
165 - <el-checkbox 165 + <ElCheckbox
166 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')" 166 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
167 :key="dict.value" 167 :key="dict.value"
168 - :label="dict.value" 168 + :value="dict.value"
169 > 169 >
170 {{ dict.label }} 170 {{ dict.label }}
171 - </el-checkbox> 171 + </ElCheckbox>
172 #else##没数据字典 172 #else##没数据字典
173 - <el-checkbox label="请选择字典生成" /> 173 + <ElCheckbox label="请选择字典生成" />
174 #end 174 #end
175 - </el-checkbox-group>  
176 - </el-form-item> 175 + </ElCheckboxGroup>
  176 + </ElFormItem>
177 #elseif($column.htmlType == "radio")## 单选框 177 #elseif($column.htmlType == "radio")## 单选框
178 - <el-form-item label="${comment}" prop="${javaField}">  
179 - <el-radio-group v-model="formData.${javaField}"> 178 + <ElFormItem label="${comment}" prop="${javaField}">
  179 + <ElRadioGroup v-model="formData.${javaField}">
180 #if ("" != $dictType)## 有数据字典 180 #if ("" != $dictType)## 有数据字典
181 - <el-radio 181 + <ElRadio
182 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')" 182 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
183 :key="dict.value" 183 :key="dict.value"
184 - :label="dict.value" 184 + :value="dict.value"
185 > 185 >
186 {{ dict.label }} 186 {{ dict.label }}
187 - </el-radio> 187 + </ElRadio>
188 #else##没数据字典 188 #else##没数据字典
189 - <el-radio :label="1">请选择字典生成</el-radio> 189 + <ElRadio :label="1">请选择字典生成</ElRadio>
190 #end 190 #end
191 - </el-radio-group>  
192 - </el-form-item> 191 + </ElRadioGroup>
  192 + </ElFormItem>
193 #elseif($column.htmlType == "datetime")## 时间框 193 #elseif($column.htmlType == "datetime")## 时间框
194 - <el-form-item label="${comment}" prop="${javaField}">  
195 - <el-date-picker 194 + <ElFormItem label="${comment}" prop="${javaField}">
  195 + <ElDatePicker
196 v-model="formData.${javaField}" 196 v-model="formData.${javaField}"
197 value-format="x" 197 value-format="x"
198 placeholder="选择${comment}" 198 placeholder="选择${comment}"
199 /> 199 />
200 - </el-form-item> 200 + </ElFormItem>
201 #elseif($column.htmlType == "textarea")## 文本框 201 #elseif($column.htmlType == "textarea")## 文本框
202 - <el-form-item label="${comment}" prop="${javaField}">  
203 - <el-input v-model="formData.${javaField}" type="textarea" placeholder="请输入${comment}" />  
204 - </el-form-item> 202 + <ElFormItem label="${comment}" prop="${javaField}">
  203 + <ElInput v-model="formData.${javaField}" type="textarea" placeholder="请输入${comment}" />
  204 + </ElFormItem>
205 #end 205 #end
206 #end 206 #end
207 #end 207 #end
208 - </el-form> 208 + </ElForm>
209 </Modal> 209 </Modal>
210 </template> 210 </template>
yudao-module-infra/src/main/resources/codegen/vue3_vben5_ele/general/views/modules/form_sub_normal.vue.vm
@@ -5,26 +5,61 @@ @@ -5,26 +5,61 @@
5 #set ($subClassNameVar = $subClassNameVars.get($subIndex)) 5 #set ($subClassNameVar = $subClassNameVars.get($subIndex))
6 #set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写 6 #set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
7 <script lang="ts" setup> 7 <script lang="ts" setup>
8 -import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}'; 8 +#if ($subTable.subJoinMany)
  9 +import type { VxeTableInstance } from '#/adapter/vxe-table';
  10 +#else
  11 +import type { FormRules } from 'element-plus';
  12 +#end
  13 +import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${table.businessName}';
9 14
10 -import { computed, ref, reactive, h, onMounted,watch,nextTick } from 'vue'; 15 +#if ($subTable.subJoinMany)
  16 +import { ref, watch } from 'vue';
  17 +#else
  18 +import { nextTick, reactive, ref, watch } from 'vue';
  19 +#end
11 20
12 import { DICT_TYPE } from '@vben/constants'; 21 import { DICT_TYPE } from '@vben/constants';
13 import { getDictOptions } from '@vben/hooks'; 22 import { getDictOptions } from '@vben/hooks';
  23 +#if ($subTable.subJoinMany)
  24 +import { Plus } from '@vben/icons';
  25 +#end
14 26
15 -import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElButton, ElSelect, ElOption, ElRadioGroup, ElRadio, ElCheckboxGroup, ElCheckbox, ElDatePicker } from 'element-plus';  
16 -import { $t } from '#/locales'; 27 +#set ($hasCheckbox = false)
  28 +#foreach ($column in $subColumns)
  29 +#if (($column.createOperation || $column.updateOperation) && $column.id != $subJoinColumn.id && $column.htmlType == "checkbox")
  30 +#set ($hasCheckbox = true)
  31 +#end
  32 +#end
  33 +import {
  34 +#if ($subTable.subJoinMany)
  35 + ElButton,
  36 +#else
  37 + ElForm,
  38 + ElFormItem,
  39 +#end
  40 +#if ($hasCheckbox)
  41 + ElCheckbox,
  42 + ElCheckboxGroup,
  43 +#end
  44 + ElDatePicker,
  45 + ElInput,
  46 +#if ($subTable.subJoinMany)
  47 + ElMessage,
  48 +#end
  49 + ElOption,
  50 + ElRadio,
  51 + ElRadioGroup,
  52 + ElSelect,
  53 +} from 'element-plus';
17 54
18 #if ($subTable.subJoinMany) ## 一对多 55 #if ($subTable.subJoinMany) ## 一对多
19 -import type { VxeTableInstance } from '#/adapter/vxe-table';  
20 -import { Plus } from "@vben/icons";  
21 import { VxeColumn, VxeTable } from '#/adapter/vxe-table'; 56 import { VxeColumn, VxeTable } from '#/adapter/vxe-table';
22 -import { get${subSimpleClassName}ListBy${SubJoinColumnName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}'; 57 +import { get${subSimpleClassName}ListBy${SubJoinColumnName} } from '#/api/${table.moduleName}/${table.businessName}';
23 #else 58 #else
24 -import type { FormRules } from 'element-plus';  
25 import { Tinymce as RichTextarea } from '#/components/tinymce'; 59 import { Tinymce as RichTextarea } from '#/components/tinymce';
26 -import { get${subSimpleClassName}By${SubJoinColumnName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}'; 60 +import { get${subSimpleClassName}By${SubJoinColumnName} } from '#/api/${table.moduleName}/${table.businessName}';
27 #end 61 #end
  62 +import { $t } from '#/locales';
28 63
29 const props = defineProps<{ 64 const props = defineProps<{
30 ${subJoinColumn.javaField}?: number // ${subJoinColumn.columnComment}(主表的关联字段) 65 ${subJoinColumn.javaField}?: number // ${subJoinColumn.columnComment}(主表的关联字段)
@@ -43,24 +78,54 @@ async function onDelete(row: ${simpleClassName}Api.${subSimpleClassName}) { @@ -43,24 +78,54 @@ async function onDelete(row: ${simpleClassName}Api.${subSimpleClassName}) {
43 await tableRef.value?.remove(row); 78 await tableRef.value?.remove(row);
44 } 79 }
45 80
46 -/** 提供获取表格数据的方法供父组件调用 */  
47 -defineExpose({  
48 - getData: (): ${simpleClassName}Api.${subSimpleClassName}[] => {  
49 - const data = list.value as ${simpleClassName}Api.${subSimpleClassName}[];  
50 - const removeRecords = tableRef.value?.getRemoveRecords() as ${simpleClassName}Api.${subSimpleClassName}[];  
51 - const insertRecords = tableRef.value?.getInsertRecords() as ${simpleClassName}Api.${subSimpleClassName}[];  
52 - return [  
53 - ...data.filter(  
54 - (row) => !removeRecords.some((removed) => removed.id === row.id),  
55 - ),  
56 - ...insertRecords.map((row: any) => ({ ...row, id: undefined })),  
57 - },  
58 -}); 81 +/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
  82 +function getData(): ${simpleClassName}Api.${subSimpleClassName}[] {
  83 + const table = tableRef.value;
  84 + if (!table) {
  85 + return [];
  86 + }
  87 + const { fullData } = table.getTableData();
  88 + const inserted = new Set(table.getInsertRecords());
  89 + return fullData.map((row) =>
  90 + inserted.has(row) ? { ...row, id: undefined } : { ...row },
  91 + );
  92 +}
  93 +
  94 +#set ($hasRequiredColumn = false)
  95 +#foreach ($column in $subColumns)
  96 +#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey && $column.id != $subJoinColumn.id)
  97 +#set ($hasRequiredColumn = true)
  98 +#end
  99 +#end
  100 +/** 校验子表必填字段 */
  101 +function validate(): boolean {
  102 +#if ($hasRequiredColumn)
  103 + for (const [index, row] of getData().entries()) {
  104 +#foreach ($column in $subColumns)
  105 +#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey && $column.id != $subJoinColumn.id)
  106 + if (
  107 + row.${column.javaField} === null ||
  108 + row.${column.javaField} === undefined ||
  109 + (typeof row.${column.javaField} === 'string' && String(row.${column.javaField}).trim() === '') ||
  110 + (Array.isArray(row.${column.javaField}) && row.${column.javaField}.length === 0)
  111 + ) {
  112 + ElMessage.error(`第 #[[${index + 1}]]# 行:${column.columnComment}不能为空`);
  113 + return false;
  114 + }
  115 +#end
  116 +#end
  117 + }
  118 +#end
  119 + return true;
  120 +}
  121 +
  122 +defineExpose({ getData, validate });
59 123
60 /** 监听主表的关联字段的变化,加载对应的子表数据 */ 124 /** 监听主表的关联字段的变化,加载对应的子表数据 */
61 watch( 125 watch(
62 () => props.${subJoinColumn.javaField}, 126 () => props.${subJoinColumn.javaField},
63 async (val) => { 127 async (val) => {
  128 + list.value = [];
64 if (!val) { 129 if (!val) {
65 return; 130 return;
66 } 131 }
@@ -99,6 +164,17 @@ defineExpose({ @@ -99,6 +164,17 @@ defineExpose({
99 watch( 164 watch(
100 () => props.${subJoinColumn.javaField}, 165 () => props.${subJoinColumn.javaField},
101 async (val) => { 166 async (val) => {
  167 + formData.value = {
  168 +#foreach ($column in $subColumns)
  169 +#if ($column.createOperation || $column.updateOperation)
  170 +#if ($column.htmlType == "checkbox")
  171 + $column.javaField: [],
  172 +#else
  173 + $column.javaField: undefined,
  174 +#end
  175 +#end
  176 +#end
  177 + };
102 if (!val) { 178 if (!val) {
103 return; 179 return;
104 } 180 }
@@ -112,10 +188,11 @@ watch( @@ -112,10 +188,11 @@ watch(
112 188
113 <template> 189 <template>
114 #if ($subTable.subJoinMany) ## 一对多 190 #if ($subTable.subJoinMany) ## 一对多
115 - <vxe-table ref="tableRef" :data="list" show-overflow class="mx-4"> 191 + <VxeTable ref="tableRef" :data="list" show-overflow class="mx-4">
116 #foreach($column in $subColumns) 192 #foreach($column in $subColumns)
117 #if ($column.createOperation || $column.updateOperation) 193 #if ($column.createOperation || $column.updateOperation)
118 #set ($comment = $column.columnComment) 194 #set ($comment = $column.columnComment)
  195 + #set ($dictType = $column.dictType)
119 #set ($javaField = $column.javaField) 196 #set ($javaField = $column.javaField)
120 #set ($javaType = $column.javaType) 197 #set ($javaType = $column.javaType)
121 #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short") 198 #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
@@ -127,99 +204,99 @@ watch( @@ -127,99 +204,99 @@ watch(
127 #end 204 #end
128 #if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写 205 #if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
129 #elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里 206 #elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
130 - <vxe-column field="${javaField}" title="${comment}" align="center"> 207 + <VxeColumn field="${javaField}" title="${comment}" align="center">
131 <template #default="{row}"> 208 <template #default="{row}">
132 - <el-input v-model="row.${javaField}" /> 209 + <ElInput v-model="row.${javaField}" />
133 </template> 210 </template>
134 - </vxe-column> 211 + </VxeColumn>
135 #elseif($column.htmlType == "imageUpload")## 图片上传 212 #elseif($column.htmlType == "imageUpload")## 图片上传
136 - <vxe-column field="${javaField}" title="${comment}" align="center"> 213 + <VxeColumn field="${javaField}" title="${comment}" align="center">
137 <template #default="{row}"> 214 <template #default="{row}">
138 <ImageUpload v-model="row.${javaField}" /> 215 <ImageUpload v-model="row.${javaField}" />
139 </template> 216 </template>
140 - </vxe-column> 217 + </VxeColumn>
141 #elseif($column.htmlType == "fileUpload")## 文件上传 218 #elseif($column.htmlType == "fileUpload")## 文件上传
142 - <vxe-column field="${javaField}" title="${comment}" align="center"> 219 + <VxeColumn field="${javaField}" title="${comment}" align="center">
143 <template #default="{row}"> 220 <template #default="{row}">
144 <FileUpload v-model="row.${javaField}" /> 221 <FileUpload v-model="row.${javaField}" />
145 </template> 222 </template>
146 - </vxe-column> 223 + </VxeColumn>
147 #elseif($column.htmlType == "select")## 下拉框 224 #elseif($column.htmlType == "select")## 下拉框
148 - <vxe-column field="${javaField}" title="${comment}" align="center"> 225 + <VxeColumn field="${javaField}" title="${comment}" align="center">
149 <template #default="{row}"> 226 <template #default="{row}">
150 - <el-select v-model="row.${javaField}" placeholder="请选择${comment}"> 227 + <ElSelect v-model="row.${javaField}" placeholder="请选择${comment}">
151 #if ("" != $dictType)## 有数据字典 228 #if ("" != $dictType)## 有数据字典
152 - <el-option 229 + <ElOption
153 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')" 230 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
154 :key="dict.value" 231 :key="dict.value"
155 :value="dict.value" 232 :value="dict.value"
156 :label="dict.label" 233 :label="dict.label"
157 /> 234 />
158 #else##没数据字典 235 #else##没数据字典
159 - <el-option label="请选择字典生成" value="" /> 236 + <ElOption label="请选择字典生成" value="" />
160 #end 237 #end
161 - </el-select> 238 + </ElSelect>
162 </template> 239 </template>
163 - </vxe-column> 240 + </VxeColumn>
164 #elseif($column.htmlType == "checkbox")## 多选框 241 #elseif($column.htmlType == "checkbox")## 多选框
165 - <vxe-column field="${javaField}" title="${comment}" align="center"> 242 + <VxeColumn field="${javaField}" title="${comment}" align="center">
166 <template #default="{row}"> 243 <template #default="{row}">
167 - <el-checkbox-group v-model="row.${javaField}"> 244 + <ElCheckboxGroup v-model="row.${javaField}">
168 #if ("" != $dictType)## 有数据字典 245 #if ("" != $dictType)## 有数据字典
169 - <el-checkbox 246 + <ElCheckbox
170 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')" 247 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
171 :key="dict.value" 248 :key="dict.value"
172 - :label="dict.value" 249 + :value="dict.value"
173 > 250 >
174 {{ dict.label }} 251 {{ dict.label }}
175 - </el-checkbox> 252 + </ElCheckbox>
176 #else##没数据字典 253 #else##没数据字典
177 - <el-checkbox label="请选择字典生成" /> 254 + <ElCheckbox label="请选择字典生成" />
178 #end 255 #end
179 - </el-checkbox-group> 256 + </ElCheckboxGroup>
180 </template> 257 </template>
181 - </vxe-column> 258 + </VxeColumn>
182 #elseif($column.htmlType == "radio")## 单选框 259 #elseif($column.htmlType == "radio")## 单选框
183 - <vxe-column field="${javaField}" title="${comment}" align="center"> 260 + <VxeColumn field="${javaField}" title="${comment}" align="center">
184 <template #default="{row}"> 261 <template #default="{row}">
185 - <el-radio-group v-model="row.${javaField}"> 262 + <ElRadioGroup v-model="row.${javaField}">
186 #if ("" != $dictType)## 有数据字典 263 #if ("" != $dictType)## 有数据字典
187 - <el-radio 264 + <ElRadio
188 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')" 265 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
189 :key="dict.value" 266 :key="dict.value"
190 - :label="dict.value" 267 + :value="dict.value"
191 > 268 >
192 {{ dict.label }} 269 {{ dict.label }}
193 - </el-radio> 270 + </ElRadio>
194 #else##没数据字典 271 #else##没数据字典
195 - <el-radio :label="1">请选择字典生成</el-radio> 272 + <ElRadio :label="1">请选择字典生成</ElRadio>
196 #end 273 #end
197 - </el-radio-group> 274 + </ElRadioGroup>
198 </template> 275 </template>
199 - </vxe-column> 276 + </VxeColumn>
200 #elseif($column.htmlType == "datetime")## 时间框 277 #elseif($column.htmlType == "datetime")## 时间框
201 - <vxe-column field="${javaField}" title="${comment}" align="center"> 278 + <VxeColumn field="${javaField}" title="${comment}" align="center">
202 <template #default="{row}"> 279 <template #default="{row}">
203 - <el-date-picker 280 + <ElDatePicker
204 v-model="row.${javaField}" 281 v-model="row.${javaField}"
205 type="datetime" 282 type="datetime"
206 value-format="x" 283 value-format="x"
207 format="YYYY-MM-DD HH:mm:ss" 284 format="YYYY-MM-DD HH:mm:ss"
208 /> 285 />
209 </template> 286 </template>
210 - </vxe-column> 287 + </VxeColumn>
211 #elseif($column.htmlType == "textarea" || $column.htmlType == "editor")## 文本框 288 #elseif($column.htmlType == "textarea" || $column.htmlType == "editor")## 文本框
212 - <vxe-column field="${javaField}" title="${comment}" align="center"> 289 + <VxeColumn field="${javaField}" title="${comment}" align="center">
213 <template #default="{row}"> 290 <template #default="{row}">
214 - <el-input v-model="row.${javaField}" type="textarea" /> 291 + <ElInput v-model="row.${javaField}" type="textarea" />
215 </template> 292 </template>
216 - </vxe-column> 293 + </VxeColumn>
217 #end 294 #end
218 #end 295 #end
219 #end 296 #end
220 - <vxe-column field="operation" title="操作" align="center"> 297 + <VxeColumn field="operation" title="操作" align="center">
221 <template #default="{row}"> 298 <template #default="{row}">
222 - <el-button 299 + <ElButton
223 size="small" 300 size="small"
224 type="danger" 301 type="danger"
225 link 302 link
@@ -227,17 +304,17 @@ watch( @@ -227,17 +304,17 @@ watch(
227 v-access:code="['${permissionPrefix}:delete']" 304 v-access:code="['${permissionPrefix}:delete']"
228 > 305 >
229 {{ $t('ui.actionTitle.delete') }} 306 {{ $t('ui.actionTitle.delete') }}
230 - </el-button> 307 + </ElButton>
231 </template> 308 </template>
232 - </vxe-column>  
233 - </vxe-table> 309 + </VxeColumn>
  310 + </VxeTable>
234 <div class="flex justify-center mt-4"> 311 <div class="flex justify-center mt-4">
235 - <el-button :icon="h(Plus)" type="primary" plain @click="handleAdd" v-access:code="['${permissionPrefix}:create']"> 312 + <ElButton :icon="Plus" type="primary" plain @click="handleAdd" v-access:code="['${permissionPrefix}:create']">
236 {{ $t('ui.actionTitle.create', ['${subTable.classComment}']) }} 313 {{ $t('ui.actionTitle.create', ['${subTable.classComment}']) }}
237 - </el-button> 314 + </ElButton>
238 </div> 315 </div>
239 #else 316 #else
240 - <el-form 317 + <ElForm
241 ref="formRef" 318 ref="formRef"
242 class="mx-4" 319 class="mx-4"
243 :model="formData" 320 :model="formData"
@@ -259,83 +336,83 @@ watch( @@ -259,83 +336,83 @@ watch(
259 #set ($dictMethod = "boolean") 336 #set ($dictMethod = "boolean")
260 #end 337 #end
261 #if ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里 338 #if ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
262 - <el-form-item label="${comment}" prop="${javaField}">  
263 - <el-input v-model="formData.${javaField}" placeholder="请输入${comment}" />  
264 - </el-form-item> 339 + <ElFormItem label="${comment}" prop="${javaField}">
  340 + <ElInput v-model="formData.${javaField}" placeholder="请输入${comment}" />
  341 + </ElFormItem>
265 #elseif($column.htmlType == "imageUpload")## 图片上传 342 #elseif($column.htmlType == "imageUpload")## 图片上传
266 - <el-form-item label="${comment}" prop="${javaField}"> 343 + <ElFormItem label="${comment}" prop="${javaField}">
267 <ImageUpload v-model="formData.${javaField}" /> 344 <ImageUpload v-model="formData.${javaField}" />
268 - </el-form-item> 345 + </ElFormItem>
269 #elseif($column.htmlType == "fileUpload")## 文件上传 346 #elseif($column.htmlType == "fileUpload")## 文件上传
270 - <el-form-item label="${comment}" prop="${javaField}"> 347 + <ElFormItem label="${comment}" prop="${javaField}">
271 <FileUpload v-model="formData.${javaField}" /> 348 <FileUpload v-model="formData.${javaField}" />
272 - </el-form-item> 349 + </ElFormItem>
273 #elseif($column.htmlType == "editor")## 文本编辑器 350 #elseif($column.htmlType == "editor")## 文本编辑器
274 - <el-form-item label="${comment}" prop="${javaField}"> 351 + <ElFormItem label="${comment}" prop="${javaField}">
275 <RichTextarea v-model="formData.${javaField}" height="500px" /> 352 <RichTextarea v-model="formData.${javaField}" height="500px" />
276 - </el-form-item> 353 + </ElFormItem>
277 #elseif($column.htmlType == "select")## 下拉框 354 #elseif($column.htmlType == "select")## 下拉框
278 - <el-form-item label="${comment}" prop="${javaField}">  
279 - <el-select v-model="formData.${javaField}" placeholder="请选择${comment}"> 355 + <ElFormItem label="${comment}" prop="${javaField}">
  356 + <ElSelect v-model="formData.${javaField}" placeholder="请选择${comment}">
280 #if ("" != $dictType)## 有数据字典 357 #if ("" != $dictType)## 有数据字典
281 - <el-option 358 + <ElOption
282 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')" 359 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
283 :key="dict.value" 360 :key="dict.value"
284 :value="dict.value" 361 :value="dict.value"
285 :label="dict.label" 362 :label="dict.label"
286 /> 363 />
287 #else##没数据字典 364 #else##没数据字典
288 - <el-option label="请选择字典生成" value="" /> 365 + <ElOption label="请选择字典生成" value="" />
289 #end 366 #end
290 - </el-select>  
291 - </el-form-item> 367 + </ElSelect>
  368 + </ElFormItem>
292 #elseif($column.htmlType == "checkbox")## 多选框 369 #elseif($column.htmlType == "checkbox")## 多选框
293 - <el-form-item label="${comment}" prop="${javaField}">  
294 - <el-checkbox-group v-model="formData.${javaField}"> 370 + <ElFormItem label="${comment}" prop="${javaField}">
  371 + <ElCheckboxGroup v-model="formData.${javaField}">
295 #if ("" != $dictType)## 有数据字典 372 #if ("" != $dictType)## 有数据字典
296 - <el-checkbox 373 + <ElCheckbox
297 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')" 374 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
298 :key="dict.value" 375 :key="dict.value"
299 - :label="dict.value" 376 + :value="dict.value"
300 > 377 >
301 {{ dict.label }} 378 {{ dict.label }}
302 - </el-checkbox> 379 + </ElCheckbox>
303 #else##没数据字典 380 #else##没数据字典
304 - <el-checkbox label="请选择字典生成" /> 381 + <ElCheckbox label="请选择字典生成" />
305 #end 382 #end
306 - </el-checkbox-group>  
307 - </el-form-item> 383 + </ElCheckboxGroup>
  384 + </ElFormItem>
308 #elseif($column.htmlType == "radio")## 单选框 385 #elseif($column.htmlType == "radio")## 单选框
309 - <el-form-item label="${comment}" prop="${javaField}">  
310 - <el-radio-group v-model="formData.${javaField}"> 386 + <ElFormItem label="${comment}" prop="${javaField}">
  387 + <ElRadioGroup v-model="formData.${javaField}">
311 #if ("" != $dictType)## 有数据字典 388 #if ("" != $dictType)## 有数据字典
312 - <el-radio 389 + <ElRadio
313 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')" 390 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
314 :key="dict.value" 391 :key="dict.value"
315 - :label="dict.value" 392 + :value="dict.value"
316 > 393 >
317 {{ dict.label }} 394 {{ dict.label }}
318 - </el-radio> 395 + </ElRadio>
319 #else##没数据字典 396 #else##没数据字典
320 - <el-radio :label="1">请选择字典生成</el-radio> 397 + <ElRadio :label="1">请选择字典生成</ElRadio>
321 #end 398 #end
322 - </el-radio-group>  
323 - </el-form-item> 399 + </ElRadioGroup>
  400 + </ElFormItem>
324 #elseif($column.htmlType == "datetime")## 时间框 401 #elseif($column.htmlType == "datetime")## 时间框
325 - <el-form-item label="${comment}" prop="${javaField}">  
326 - <el-date-picker 402 + <ElFormItem label="${comment}" prop="${javaField}">
  403 + <ElDatePicker
327 v-model="formData.${javaField}" 404 v-model="formData.${javaField}"
328 value-format="x" 405 value-format="x"
329 placeholder="选择${comment}" 406 placeholder="选择${comment}"
330 /> 407 />
331 - </el-form-item> 408 + </ElFormItem>
332 #elseif($column.htmlType == "textarea")## 文本框 409 #elseif($column.htmlType == "textarea")## 文本框
333 - <el-form-item label="${comment}" prop="${javaField}">  
334 - <el-input v-model="formData.${javaField}" type="textarea" placeholder="请输入${comment}" />  
335 - </el-form-item> 410 + <ElFormItem label="${comment}" prop="${javaField}">
  411 + <ElInput v-model="formData.${javaField}" type="textarea" placeholder="请输入${comment}" />
  412 + </ElFormItem>
336 #end 413 #end
337 #end 414 #end
338 #end 415 #end
339 - </el-form> 416 + </ElForm>
340 #end 417 #end
341 </template> 418 </template>
yudao-module-infra/src/main/resources/codegen/vue3_vben5_ele/general/views/modules/list_sub_erp.vue.vm
@@ -6,13 +6,14 @@ @@ -6,13 +6,14 @@
6 #set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($subIndex)) 6 #set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($subIndex))
7 #set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写 7 #set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
8 <script lang="ts" setup> 8 <script lang="ts" setup>
9 - import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}'; 9 + import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${table.businessName}';
10 import type { VxeTableInstance } from '#/adapter/vxe-table'; 10 import type { VxeTableInstance } from '#/adapter/vxe-table';
11 11
12 import { reactive,ref, h, nextTick,watch,onMounted } from 'vue'; 12 import { reactive,ref, h, nextTick,watch,onMounted } from 'vue';
13 13
14 import { DICT_TYPE } from '@vben/constants'; 14 import { DICT_TYPE } from '@vben/constants';
15 import { getDictOptions } from '@vben/hooks'; 15 import { getDictOptions } from '@vben/hooks';
  16 +import { formatDateTime } from '@vben/utils';
16 17
17 import { DictTag } from '#/components/dict-tag'; 18 import { DictTag } from '#/components/dict-tag';
18 import { getRangePickerDefaultProps } from '#/utils'; 19 import { getRangePickerDefaultProps } from '#/utils';
@@ -31,13 +32,13 @@ @@ -31,13 +32,13 @@
31 #end 32 #end
32 33
33 #if ($table.templateType == 11) ## erp 34 #if ($table.templateType == 11) ## erp
34 - import { delete${subSimpleClassName},#if ($deleteBatchEnable) delete${subSimpleClassName}List,#end get${subSimpleClassName}Page } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';  
35 - import { isEmpty } from '@vben/utils'; 35 + import { delete${subSimpleClassName},#if ($deleteBatchEnable) delete${subSimpleClassName}List,#end get${subSimpleClassName}Page } from '#/api/${table.moduleName}/${table.businessName}';
  36 + import { cloneDeep, isEmpty } from '@vben/utils';
36 #else 37 #else
37 #if ($subTable.subJoinMany) ## 一对多 38 #if ($subTable.subJoinMany) ## 一对多
38 - import { get${subSimpleClassName}ListBy${SubJoinColumnName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}'; 39 + import { get${subSimpleClassName}ListBy${SubJoinColumnName} } from '#/api/${table.moduleName}/${table.businessName}';
39 #else 40 #else
40 - import { get${subSimpleClassName}By${SubJoinColumnName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}'; 41 + import { get${subSimpleClassName}By${SubJoinColumnName} } from '#/api/${table.moduleName}/${table.businessName}';
41 #end 42 #end
42 #end 43 #end
43 44
@@ -203,7 +204,7 @@ onMounted(() =&gt; { @@ -203,7 +204,7 @@ onMounted(() =&gt; {
203 <div class="h-[600px]"> 204 <div class="h-[600px]">
204 <ContentWrap v-if="!hiddenSearchBar"> 205 <ContentWrap v-if="!hiddenSearchBar">
205 <!-- 搜索工作栏 --> 206 <!-- 搜索工作栏 -->
206 - <el-form 207 + <ElForm
207 :model="queryParams" 208 :model="queryParams"
208 ref="queryFormRef" 209 ref="queryFormRef"
209 inline 210 inline
@@ -222,49 +223,49 @@ onMounted(() =&gt; { @@ -222,49 +223,49 @@ onMounted(() =&gt; {
222 #set ($dictMethod = "boolean") 223 #set ($dictMethod = "boolean")
223 #end 224 #end
224 #if ($column.htmlType == "input") 225 #if ($column.htmlType == "input")
225 - <el-form-item label="${comment}">  
226 - <el-input 226 + <ElFormItem label="${comment}">
  227 + <ElInput
227 v-model="queryParams.${javaField}" 228 v-model="queryParams.${javaField}"
228 placeholder="请输入${comment}" 229 placeholder="请输入${comment}"
229 clearable 230 clearable
230 @keyup.enter="handleQuery" 231 @keyup.enter="handleQuery"
231 class="!w-[240px]" 232 class="!w-[240px]"
232 /> 233 />
233 - </el-form-item> 234 + </ElFormItem>
234 #elseif ($column.htmlType == "select" || $column.htmlType == "radio" || $column.htmlType == "checkbox") 235 #elseif ($column.htmlType == "select" || $column.htmlType == "radio" || $column.htmlType == "checkbox")
235 - <el-form-item label="${comment}">  
236 - <el-select 236 + <ElFormItem label="${comment}">
  237 + <ElSelect
237 v-model="queryParams.${javaField}" 238 v-model="queryParams.${javaField}"
238 placeholder="请选择${comment}" 239 placeholder="请选择${comment}"
239 clearable 240 clearable
240 class="!w-[240px]" 241 class="!w-[240px]"
241 > 242 >
242 #if ("" != $dictType)## 设置了 dictType 数据字典的情况 243 #if ("" != $dictType)## 设置了 dictType 数据字典的情况
243 - <el-option 244 + <ElOption
244 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')" 245 v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
245 :key="dict.value" 246 :key="dict.value"
246 :value="dict.value" 247 :value="dict.value"
247 :label="dict.label" 248 :label="dict.label"
248 /> 249 />
249 #else## 未设置 dictType 数据字典的情况 250 #else## 未设置 dictType 数据字典的情况
250 - <el-option label="请选择字典生成" value="" /> 251 + <ElOption label="请选择字典生成" value="" />
251 #end 252 #end
252 - </el-select>  
253 - </el-form-item> 253 + </ElSelect>
  254 + </ElFormItem>
254 #elseif($column.htmlType == "datetime") 255 #elseif($column.htmlType == "datetime")
255 #if ($column.listOperationCondition != "BETWEEN")## 非范围 256 #if ($column.listOperationCondition != "BETWEEN")## 非范围
256 - <el-form-item label="${comment}">  
257 - <el-date-picker 257 + <ElFormItem label="${comment}">
  258 + <ElDatePicker
258 v-model="queryParams.${javaField}" 259 v-model="queryParams.${javaField}"
259 value-format="YYYY-MM-DD" 260 value-format="YYYY-MM-DD"
260 placeholder="选择${comment}" 261 placeholder="选择${comment}"
261 clearable 262 clearable
262 class="!w-[240px]" 263 class="!w-[240px]"
263 /> 264 />
264 - </el-form-item> 265 + </ElFormItem>
265 #else## 范围 266 #else## 范围
266 - <el-form-item label="${comment}">  
267 - <el-date-picker 267 + <ElFormItem label="${comment}">
  268 + <ElDatePicker
268 v-model="queryParams.${javaField}" 269 v-model="queryParams.${javaField}"
269 type="daterange" 270 type="daterange"
270 value-format="YYYY-MM-DD" 271 value-format="YYYY-MM-DD"
@@ -273,18 +274,18 @@ onMounted(() =&gt; { @@ -273,18 +274,18 @@ onMounted(() =&gt; {
273 end-placeholder="结束日期" 274 end-placeholder="结束日期"
274 class="!w-[240px]" 275 class="!w-[240px]"
275 /> 276 />
276 - </el-form-item> 277 + </ElFormItem>
277 #end 278 #end
278 #end 279 #end
279 #end 280 #end
280 #end 281 #end
281 - <el-form-item>  
282 - <el-button class="ml-2" @click="resetQuery"> 重置 </el-button>  
283 - <el-button class="ml-2" @click="handleQuery" type="primary"> 282 + <ElFormItem>
  283 + <ElButton class="ml-2" @click="resetQuery"> 重置 </ElButton>
  284 + <ElButton class="ml-2" @click="handleQuery" type="primary">
284 搜索 285 搜索
285 - </el-button>  
286 - </el-form-item>  
287 - </el-form> 286 + </ElButton>
  287 + </ElFormItem>
  288 + </ElForm>
288 </ContentWrap> 289 </ContentWrap>
289 290
290 <!-- 列表 --> 291 <!-- 列表 -->
@@ -294,7 +295,7 @@ onMounted(() =&gt; { @@ -294,7 +295,7 @@ onMounted(() =&gt; {
294 ref="tableToolbarRef" 295 ref="tableToolbarRef"
295 v-model:hidden-search="hiddenSearchBar" 296 v-model:hidden-search="hiddenSearchBar"
296 > 297 >
297 - <el-button 298 + <ElButton
298 class="ml-2" 299 class="ml-2"
299 :icon="h(Plus)" 300 :icon="h(Plus)"
300 type="primary" 301 type="primary"
@@ -302,9 +303,9 @@ onMounted(() =&gt; { @@ -302,9 +303,9 @@ onMounted(() =&gt; {
302 v-access:code="['${permissionPrefix}:create']" 303 v-access:code="['${permissionPrefix}:create']"
303 > 304 >
304 {{ $t('ui.actionTitle.create', ['${table.classComment}']) }} 305 {{ $t('ui.actionTitle.create', ['${table.classComment}']) }}
305 - </el-button> 306 + </ElButton>
306 #if ($deleteBatchEnable) 307 #if ($deleteBatchEnable)
307 - <el-button 308 + <ElButton
308 :icon="h(Trash2)" 309 :icon="h(Trash2)"
309 type="danger" 310 type="danger"
310 class="ml-2" 311 class="ml-2"
@@ -313,11 +314,11 @@ onMounted(() =&gt; { @@ -313,11 +314,11 @@ onMounted(() =&gt; {
313 v-access:code="['${table.moduleName}:${simpleClassName_strikeCase}:delete']" 314 v-access:code="['${table.moduleName}:${simpleClassName_strikeCase}:delete']"
314 > 315 >
315 批量删除 316 批量删除
316 - </el-button> 317 + </ElButton>
317 #end 318 #end
318 - </TableToolbar> 319 + </VbenVxeTableToolbar>
319 </template> 320 </template>
320 - <vxe-table 321 + <VxeTable
321 ref="tableRef" 322 ref="tableRef"
322 :data="list" 323 :data="list"
323 show-overflow 324 show-overflow
@@ -328,7 +329,7 @@ onMounted(() =&gt; { @@ -328,7 +329,7 @@ onMounted(() =&gt; {
328 #end 329 #end
329 > 330 >
330 #if ($deleteBatchEnable) 331 #if ($deleteBatchEnable)
331 - <vxe-column type="checkbox" width="40"></vxe-column> 332 + <VxeColumn type="checkbox" width="40"></VxeColumn>
332 #end 333 #end
333 #foreach($column in $subColumns) 334 #foreach($column in $subColumns)
334 #if ($column.listOperationResult) 335 #if ($column.listOperationResult)
@@ -336,27 +337,27 @@ onMounted(() =&gt; { @@ -336,27 +337,27 @@ onMounted(() =&gt; {
336 #set ($javaField = $column.javaField) 337 #set ($javaField = $column.javaField)
337 #set ($comment=$column.columnComment) 338 #set ($comment=$column.columnComment)
338 #if ($column.javaType == "LocalDateTime")## 时间类型 339 #if ($column.javaType == "LocalDateTime")## 时间类型
339 - <vxe-column field="${javaField}" title="${comment}" align="center"> 340 + <VxeColumn field="${javaField}" title="${comment}" align="center">
340 <template #default="{row}"> 341 <template #default="{row}">
341 {{formatDateTime(row.${javaField})}} 342 {{formatDateTime(row.${javaField})}}
342 </template> 343 </template>
343 - </vxe-column> 344 + </VxeColumn>
344 #elseif($column.dictType && "" != $column.dictType)## 数据字典 345 #elseif($column.dictType && "" != $column.dictType)## 数据字典
345 - <vxe-column field="${javaField}" title="${comment}" align="center"> 346 + <VxeColumn field="${javaField}" title="${comment}" align="center">
346 <template #default="{row}"> 347 <template #default="{row}">
347 <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="row.${javaField}" /> 348 <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="row.${javaField}" />
348 </template> 349 </template>
349 - </vxe-column> 350 + </VxeColumn>
350 #elseif ($table.templateType == 2 && $javaField == $treeNameColumn.javaField) 351 #elseif ($table.templateType == 2 && $javaField == $treeNameColumn.javaField)
351 - <vxe-column field="${javaField}" title="${comment}" align="center" tree-node/> 352 + <VxeColumn field="${javaField}" title="${comment}" align="center" tree-node/>
352 #else 353 #else
353 - <vxe-column field="${javaField}" title="${comment}" align="center" /> 354 + <VxeColumn field="${javaField}" title="${comment}" align="center" />
354 #end 355 #end
355 #end 356 #end
356 #end 357 #end
357 - <vxe-column field="operation" title="操作" align="center"> 358 + <VxeColumn field="operation" title="操作" align="center">
358 <template #default="{row}"> 359 <template #default="{row}">
359 - <el-button 360 + <ElButton
360 size="small" 361 size="small"
361 type="primary" 362 type="primary"
362 link 363 link
@@ -364,8 +365,8 @@ onMounted(() =&gt; { @@ -364,8 +365,8 @@ onMounted(() =&gt; {
364 v-access:code="['${permissionPrefix}:update']" 365 v-access:code="['${permissionPrefix}:update']"
365 > 366 >
366 {{ $t('ui.actionTitle.edit') }} 367 {{ $t('ui.actionTitle.edit') }}
367 - </el-button>  
368 - <el-button 368 + </ElButton>
  369 + <ElButton
369 size="small" 370 size="small"
370 type="danger" 371 type="danger"
371 link 372 link
@@ -374,13 +375,13 @@ onMounted(() =&gt; { @@ -374,13 +375,13 @@ onMounted(() =&gt; {
374 v-access:code="['${permissionPrefix}:delete']" 375 v-access:code="['${permissionPrefix}:delete']"
375 > 376 >
376 {{ $t('ui.actionTitle.delete') }} 377 {{ $t('ui.actionTitle.delete') }}
377 - </el-button> 378 + </ElButton>
378 </template> 379 </template>
379 - </vxe-column>  
380 - </vxe-table> 380 + </VxeColumn>
  381 + </VxeTable>
381 <!-- 分页 --> 382 <!-- 分页 -->
382 <div class="mt-2 flex justify-end"> 383 <div class="mt-2 flex justify-end">
383 - <el-pagination 384 + <ElPagination
384 :total="total" 385 :total="total"
385 v-model:current-page="queryParams.pageNo" 386 v-model:current-page="queryParams.pageNo"
386 v-model:page-size="queryParams.pageSize" 387 v-model:page-size="queryParams.pageSize"
@@ -394,7 +395,7 @@ onMounted(() =&gt; { @@ -394,7 +395,7 @@ onMounted(() =&gt; {
394 </div> 395 </div>
395 #else 396 #else
396 <ContentWrap title="${subTable.classComment}列表"> 397 <ContentWrap title="${subTable.classComment}列表">
397 - <vxe-table 398 + <VxeTable
398 :data="list" 399 :data="list"
399 show-overflow 400 show-overflow
400 :loading="loading" 401 :loading="loading"
@@ -405,23 +406,23 @@ onMounted(() =&gt; { @@ -405,23 +406,23 @@ onMounted(() =&gt; {
405 #set ($javaField = $column.javaField) 406 #set ($javaField = $column.javaField)
406 #set ($comment=$column.columnComment) 407 #set ($comment=$column.columnComment)
407 #if ($column.javaType == "LocalDateTime")## 时间类型 408 #if ($column.javaType == "LocalDateTime")## 时间类型
408 - <vxe-column field="${javaField}" title="${comment}" align="center"> 409 + <VxeColumn field="${javaField}" title="${comment}" align="center">
409 <template #default="{row}"> 410 <template #default="{row}">
410 {{formatDateTime(row.${javaField})}} 411 {{formatDateTime(row.${javaField})}}
411 </template> 412 </template>
412 - </vxe-column> 413 + </VxeColumn>
413 #elseif($column.dictType && "" != $column.dictType)## 数据字典 414 #elseif($column.dictType && "" != $column.dictType)## 数据字典
414 - <vxe-column field="${javaField}" title="${comment}" align="center"> 415 + <VxeColumn field="${javaField}" title="${comment}" align="center">
415 <template #default="{row}"> 416 <template #default="{row}">
416 <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="row.${javaField}" /> 417 <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="row.${javaField}" />
417 </template> 418 </template>
418 - </vxe-column> 419 + </VxeColumn>
419 #else 420 #else
420 - <vxe-column field="${javaField}" title="${comment}" align="center" /> 421 + <VxeColumn field="${javaField}" title="${comment}" align="center" />
421 #end 422 #end
422 #end 423 #end
423 #end 424 #end
424 - </vxe-table> 425 + </VxeTable>
425 </ContentWrap> 426 </ContentWrap>
426 #end 427 #end
427 </template> 428 </template>
yudao-module-infra/src/main/resources/codegen/vue3_vben5_ele/schema/views/data.ts.vm
@@ -36,13 +36,14 @@ export function useFormSchema(): VbenFormSchema[] { @@ -36,13 +36,14 @@ export function useFormSchema(): VbenFormSchema[] {
36 id: 0, 36 id: 0,
37 ${treeNameColumn.javaField}: '顶级${table.classComment}', 37 ${treeNameColumn.javaField}: '顶级${table.classComment}',
38 }); 38 });
39 - return handleTree(data); 39 + return handleTree(data, 'id', '${treeParentColumn.javaField}');
40 }, 40 },
41 labelField: '${treeNameColumn.javaField}', 41 labelField: '${treeNameColumn.javaField}',
42 valueField: 'id', 42 valueField: 'id',
43 childrenField: 'children', 43 childrenField: 'children',
44 placeholder: '请选择上级${table.classComment}', 44 placeholder: '请选择上级${table.classComment}',
45 - treeDefaultExpandAll: true, 45 + defaultExpandAll: true,
  46 + checkStrictly: true,
46 }, 47 },
47 rules: 'selectRequired', 48 rules: 'selectRequired',
48 }, 49 },
@@ -89,7 +90,7 @@ export function useFormSchema(): VbenFormSchema[] { @@ -89,7 +90,7 @@ export function useFormSchema(): VbenFormSchema[] {
89 placeholder: '请选择${comment}', 90 placeholder: '请选择${comment}',
90 }, 91 },
91 #elseif($column.htmlType == "checkbox")## 多选框 92 #elseif($column.htmlType == "checkbox")## 多选框
92 - component: 'Checkbox', 93 + component: 'CheckboxGroup',
93 componentProps: { 94 componentProps: {
94 #if ("" != $dictType)## 有数据字典 95 #if ("" != $dictType)## 有数据字典
95 options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), 96 options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
@@ -105,13 +106,12 @@ export function useFormSchema(): VbenFormSchema[] { @@ -105,13 +106,12 @@ export function useFormSchema(): VbenFormSchema[] {
105 #else##没数据字典 106 #else##没数据字典
106 options: [], 107 options: [],
107 #end 108 #end
108 - buttonStyle: 'solid',  
109 - optionType: 'button', 109 + isButton: true,
110 }, 110 },
111 #elseif($column.htmlType == "datetime")## 时间框 111 #elseif($column.htmlType == "datetime")## 时间框
112 component: 'DatePicker', 112 component: 'DatePicker',
113 componentProps: { 113 componentProps: {
114 - showTime: true, 114 + type: 'datetime',
115 format: 'YYYY-MM-DD HH:mm:ss', 115 format: 'YYYY-MM-DD HH:mm:ss',
116 valueFormat: 'x', 116 valueFormat: 'x',
117 class: '!w-full', 117 class: '!w-full',
@@ -308,7 +308,7 @@ export function use${subSimpleClassName}FormSchema(): VbenFormSchema[] { @@ -308,7 +308,7 @@ export function use${subSimpleClassName}FormSchema(): VbenFormSchema[] {
308 placeholder: '请选择${comment}', 308 placeholder: '请选择${comment}',
309 }, 309 },
310 #elseif($column.htmlType == "checkbox")## 多选框 310 #elseif($column.htmlType == "checkbox")## 多选框
311 - component: 'Checkbox', 311 + component: 'CheckboxGroup',
312 componentProps: { 312 componentProps: {
313 #if ("" != $dictType)## 有数据字典 313 #if ("" != $dictType)## 有数据字典
314 options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), 314 options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
@@ -324,13 +324,12 @@ export function use${subSimpleClassName}FormSchema(): VbenFormSchema[] { @@ -324,13 +324,12 @@ export function use${subSimpleClassName}FormSchema(): VbenFormSchema[] {
324 #else##没数据字典 324 #else##没数据字典
325 options: [], 325 options: [],
326 #end 326 #end
327 - buttonStyle: 'solid',  
328 - optionType: 'button', 327 + isButton: true,
329 }, 328 },
330 #elseif($column.htmlType == "datetime")## 时间框 329 #elseif($column.htmlType == "datetime")## 时间框
331 component: 'DatePicker', 330 component: 'DatePicker',
332 componentProps: { 331 componentProps: {
333 - showTime: true, 332 + type: 'datetime',
334 format: 'YYYY-MM-DD HH:mm:ss', 333 format: 'YYYY-MM-DD HH:mm:ss',
335 valueFormat: 'x', 334 valueFormat: 'x',
336 class: '!w-full', 335 class: '!w-full',
@@ -450,6 +449,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions&lt;${api @@ -450,6 +449,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions&lt;${api
450 #if ($column.createOperation || $column.updateOperation) 449 #if ($column.createOperation || $column.updateOperation)
451 #if (!$column.primaryKey && $column.listOperationResult && $column.id != $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写 450 #if (!$column.primaryKey && $column.listOperationResult && $column.id != $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
452 #set ($dictType = $column.dictType) 451 #set ($dictType = $column.dictType)
  452 + #set ($javaType = $column.javaType)
453 #set ($javaField = $column.javaField) 453 #set ($javaField = $column.javaField)
454 #set ($comment = $column.columnComment) 454 #set ($comment = $column.columnComment)
455 #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short") 455 #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
@@ -544,7 +544,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions&lt;${api @@ -544,7 +544,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions&lt;${api
544 placeholder: '请选择${comment}', 544 placeholder: '请选择${comment}',
545 }, 545 },
546 #elseif($column.htmlType == "checkbox")## 多选框 546 #elseif($column.htmlType == "checkbox")## 多选框
547 - component: 'Checkbox', 547 + component: 'CheckboxGroup',
548 componentProps: { 548 componentProps: {
549 #if ("" != $dictType)## 有数据字典 549 #if ("" != $dictType)## 有数据字典
550 options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), 550 options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
@@ -560,13 +560,12 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions&lt;${api @@ -560,13 +560,12 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions&lt;${api
560 #else##没数据字典 560 #else##没数据字典
561 options: [], 561 options: [],
562 #end 562 #end
563 - buttonStyle: 'solid',  
564 - optionType: 'button', 563 + isButton: true,
565 }, 564 },
566 #elseif($column.htmlType == "datetime")## 时间框 565 #elseif($column.htmlType == "datetime")## 时间框
567 component: 'DatePicker', 566 component: 'DatePicker',
568 componentProps: { 567 componentProps: {
569 - showTime: true, 568 + type: 'datetime',
570 format: 'YYYY-MM-DD HH:mm:ss', 569 format: 'YYYY-MM-DD HH:mm:ss',
571 valueFormat: 'x', 570 valueFormat: 'x',
572 class: '!w-full', 571 class: '!w-full',
@@ -596,7 +595,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions&lt;${api @@ -596,7 +595,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions&lt;${api
596 #end 595 #end
597 #if ($table.templateType == 12) ## 内嵌情况 596 #if ($table.templateType == 12) ## 内嵌情况
598 /** 列表的字段 */ 597 /** 列表的字段 */
599 - export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${simpleClassName}Api.${subSimpleClassName}>['columns'] { 598 + export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${apiName}.${subSimpleClassName}>['columns'] {
600 return [ 599 return [
601 #foreach($column in $subColumns) 600 #foreach($column in $subColumns)
602 #if ($column.listOperationResult) 601 #if ($column.listOperationResult)
yudao-module-infra/src/main/resources/codegen/vue3_vben5_ele/schema/views/form.vue.vm
@@ -6,7 +6,7 @@ import { computed, ref } from &#39;vue&#39;; @@ -6,7 +6,7 @@ import { computed, ref } from &#39;vue&#39;;
6 6
7 import { useVbenModal } from '@vben/common-ui'; 7 import { useVbenModal } from '@vben/common-ui';
8 8
9 -import { ElMessage#if ($table.templateType == 11), ElTabs, ElTabPane#end } from 'element-plus'; 9 +import { ElMessage#if ($table.templateType == 10 || $table.templateType == 12), ElTabPane, ElTabs#end } from 'element-plus';
10 10
11 import { useVbenForm } from '#/adapter/form'; 11 import { useVbenForm } from '#/adapter/form';
12 import { create${simpleClassName}, get${simpleClassName}, update${simpleClassName} } from '#/api/${table.moduleName}/${table.businessName}'; 12 import { create${simpleClassName}, get${simpleClassName}, update${simpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';
@@ -69,15 +69,11 @@ const [Modal, modalApi] = useVbenModal({ @@ -69,15 +69,11 @@ const [Modal, modalApi] = useVbenModal({
69 #foreach ($subTable in $subTables) 69 #foreach ($subTable in $subTables)
70 #set ($index = $foreach.count - 1) 70 #set ($index = $foreach.count - 1)
71 #set ($subClassNameVar = $subClassNameVars.get($index)) 71 #set ($subClassNameVar = $subClassNameVars.get($index))
72 - #if ($subTable.subJoinMany) ## 一对多  
73 - ## TODO 列表值校验?  
74 - #else  
75 const ${subClassNameVar}Valid = await ${subClassNameVar}FormRef.value?.validate(); 72 const ${subClassNameVar}Valid = await ${subClassNameVar}FormRef.value?.validate();
76 if (!${subClassNameVar}Valid) { 73 if (!${subClassNameVar}Valid) {
77 subTabsName.value = '${subClassNameVar}'; 74 subTabsName.value = '${subClassNameVar}';
78 return; 75 return;
79 } 76 }
80 - #end  
81 #end 77 #end
82 #end 78 #end
83 #end 79 #end
yudao-module-infra/src/main/resources/codegen/vue3_vben5_ele/schema/views/import.vue.vm
@@ -50,6 +50,16 @@ function handleChange(file: any) { @@ -50,6 +50,16 @@ function handleChange(file: any) {
50 } 50 }
51 } 51 }
52 52
  53 +/** 移除文件 */
  54 +function handleRemove() {
  55 + formApi.setFieldValue('file', undefined);
  56 +}
  57 +
  58 +/** 超出文件数量 */
  59 +function handleExceed() {
  60 + ElMessage.warning('只能选择一个文件,请先移除已选文件');
  61 +}
  62 +
53 /** 下载模板 */ 63 /** 下载模板 */
54 async function handleDownload() { 64 async function handleDownload() {
55 const data = await import${simpleClassName}Template(); 65 const data = await import${simpleClassName}Template();
@@ -66,6 +76,8 @@ async function handleDownload() { @@ -66,6 +76,8 @@ async function handleDownload() {
66 :limit="1" 76 :limit="1"
67 accept=".xls,.xlsx" 77 accept=".xls,.xlsx"
68 :on-change="handleChange" 78 :on-change="handleChange"
  79 + :on-remove="handleRemove"
  80 + :on-exceed="handleExceed"
69 :auto-upload="false" 81 :auto-upload="false"
70 > 82 >
71 <ElButton type="primary"> 选择 Excel 文件 </ElButton> 83 <ElButton type="primary"> 选择 Excel 文件 </ElButton>
yudao-module-infra/src/main/resources/codegen/vue3_vben5_ele/schema/views/modules/form_sub_normal.vue.vm
@@ -14,10 +14,9 @@ @@ -14,10 +14,9 @@
14 14
15 #if ($subTable.subJoinMany) ## 一对多 15 #if ($subTable.subJoinMany) ## 一对多
16 import { Plus } from "@vben/icons"; 16 import { Plus } from "@vben/icons";
17 -import { ElButton, ElTabs, ElTabPane, ElCheckbox, ElInput, ElSelect, ElOption, ElRadioGroup, ElCheckboxGroup, ElDatePicker } from 'element-plus'; 17 +import { ElButton, ElCheckbox, ElCheckboxGroup, ElDatePicker, ElInput, ElMessage, ElOption, ElRadio, ElRadioGroup, ElSelect, ElTabPane, ElTabs } from 'element-plus';
18 import { ImageUpload, FileUpload } from "#/components/upload"; 18 import { ImageUpload, FileUpload } from "#/components/upload";
19 -import type { OnActionClickParams } from '#/adapter/vxe-table';  
20 -import { useVbenVxeGrid } from '#/adapter/vxe-table'; 19 +import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
21 import { use${subSimpleClassName}GridEditColumns } from '../data'; 20 import { use${subSimpleClassName}GridEditColumns } from '../data';
22 import { get${subSimpleClassName}ListBy${SubJoinColumnName} } from '#/api/${table.moduleName}/${table.businessName}'; 21 import { get${subSimpleClassName}ListBy${SubJoinColumnName} } from '#/api/${table.moduleName}/${table.businessName}';
23 #else 22 #else
@@ -31,22 +30,9 @@ const props = defineProps&lt;{ @@ -31,22 +30,9 @@ const props = defineProps&lt;{
31 }>() 30 }>()
32 31
33 #if ($subTable.subJoinMany) ## 一对多 32 #if ($subTable.subJoinMany) ## 一对多
34 -/** 表格操作按钮的回调函数 */  
35 -function onActionClick({  
36 - code,  
37 - row,  
38 -}: OnActionClickParams<${apiName}.${subSimpleClassName}>) {  
39 - switch (code) {  
40 - case 'delete': {  
41 - onDelete(row);  
42 - break;  
43 - }  
44 - }  
45 -}  
46 -  
47 const [Grid, gridApi] = useVbenVxeGrid({ 33 const [Grid, gridApi] = useVbenVxeGrid({
48 gridOptions: { 34 gridOptions: {
49 - columns: use${subSimpleClassName}GridEditColumns(onActionClick), 35 + columns: use${subSimpleClassName}GridEditColumns(),
50 border: true, 36 border: true,
51 showOverflow: true, 37 showOverflow: true,
52 autoResize: true, 38 autoResize: true,
@@ -65,7 +51,7 @@ gridOptions: { @@ -65,7 +51,7 @@ gridOptions: {
65 51
66 /** 添加${subTable.classComment} */ 52 /** 添加${subTable.classComment} */
67 const onAdd = async () => { 53 const onAdd = async () => {
68 - await gridApi.grid.insertAt({} as ${simpleClassName}Api.${subSimpleClassName}, -1); 54 + await gridApi.grid.insertAt({} as ${apiName}.${subSimpleClassName}, -1);
69 } 55 }
70 56
71 /** 删除${subTable.classComment} */ 57 /** 删除${subTable.classComment} */
@@ -73,26 +59,54 @@ const onDelete = async (row: ${apiName}.${subSimpleClassName}) =&gt; { @@ -73,26 +59,54 @@ const onDelete = async (row: ${apiName}.${subSimpleClassName}) =&gt; {
73 await gridApi.grid.remove(row); 59 await gridApi.grid.remove(row);
74 } 60 }
75 61
76 -/** 提供获取表格数据的方法供父组件调用 */  
77 -defineExpose({  
78 - getData: (): ${apiName}.${subSimpleClassName}[] => {  
79 - const data = gridApi.grid.getData() as ${apiName}.${subSimpleClassName}[];  
80 - const removeRecords = gridApi.grid.getRemoveRecords() as ${apiName}.${subSimpleClassName}[];  
81 - const insertRecords = gridApi.grid.getInsertRecords() as ${apiName}.${subSimpleClassName}[];  
82 - return data  
83 - .filter((row) => !removeRecords.some((removed) => removed.id === row.id))  
84 - .concat(insertRecords.map((row: any) => ({ ...row, id: undefined })));  
85 - },  
86 -}); 62 +/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
  63 +function getData(): ${apiName}.${subSimpleClassName}[] {
  64 + const { fullData } = gridApi.grid.getTableData();
  65 + const inserted = new Set(gridApi.grid.getInsertRecords());
  66 + return fullData.map((row) =>
  67 + inserted.has(row) ? { ...row, id: undefined } : { ...row },
  68 + );
  69 +}
  70 +
  71 +#set ($hasRequiredColumn = false)
  72 +#foreach ($column in $subColumns)
  73 +#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey && $column.id != $subJoinColumn.id)
  74 +#set ($hasRequiredColumn = true)
  75 +#end
  76 +#end
  77 +/** 校验子表必填字段 */
  78 +function validate(): boolean {
  79 +#if ($hasRequiredColumn)
  80 + for (const [index, row] of getData().entries()) {
  81 +#foreach ($column in $subColumns)
  82 +#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey && $column.id != $subJoinColumn.id)
  83 + if (
  84 + row.${column.javaField} === null ||
  85 + row.${column.javaField} === undefined ||
  86 + (typeof row.${column.javaField} === 'string' && String(row.${column.javaField}).trim() === '') ||
  87 + (Array.isArray(row.${column.javaField}) && row.${column.javaField}.length === 0)
  88 + ) {
  89 + ElMessage.error(`第 #[[${index + 1}]]# 行:${column.columnComment}不能为空`);
  90 + return false;
  91 + }
  92 +#end
  93 +#end
  94 + }
  95 +#end
  96 + return true;
  97 +}
  98 +
  99 +defineExpose({ getData, validate });
87 100
88 /** 监听主表的关联字段的变化,加载对应的子表数据 */ 101 /** 监听主表的关联字段的变化,加载对应的子表数据 */
89 watch( 102 watch(
90 () => props.${subJoinColumn.javaField}, 103 () => props.${subJoinColumn.javaField},
91 async (val) => { 104 async (val) => {
  105 + await nextTick();
92 if (!val) { 106 if (!val) {
  107 + await gridApi.grid.loadData([]);
93 return; 108 return;
94 } 109 }
95 - await nextTick();  
96 await gridApi.grid.loadData(await get${subSimpleClassName}ListBy${SubJoinColumnName}(props.${subJoinColumn.javaField}!)); 110 await gridApi.grid.loadData(await get${subSimpleClassName}ListBy${SubJoinColumnName}(props.${subJoinColumn.javaField}!));
97 }, 111 },
98 { immediate: true }, 112 { immediate: true },
@@ -124,10 +138,11 @@ defineExpose({ @@ -124,10 +138,11 @@ defineExpose({
124 watch( 138 watch(
125 () => props.${subJoinColumn.javaField}, 139 () => props.${subJoinColumn.javaField},
126 async (val) => { 140 async (val) => {
  141 + await nextTick();
  142 + await formApi.resetForm();
127 if (!val) { 143 if (!val) {
128 return; 144 return;
129 } 145 }
130 - await nextTick();  
131 await formApi.setValues(await get${subSimpleClassName}By${SubJoinColumnName}(props.${subJoinColumn.javaField}!)); 146 await formApi.setValues(await get${subSimpleClassName}By${SubJoinColumnName}(props.${subJoinColumn.javaField}!));
132 }, 147 },
133 { immediate: true }, 148 { immediate: true },
@@ -144,7 +159,7 @@ watch( @@ -144,7 +159,7 @@ watch(
144 #if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写 159 #if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
145 #elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里 160 #elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
146 <template #${javaField}="{ row }"> 161 <template #${javaField}="{ row }">
147 - <el-input v-model="row.${javaField}" /> 162 + <ElInput v-model="row.${javaField}" />
148 </template> 163 </template>
149 #elseif($column.htmlType == "imageUpload")## 图片上传 164 #elseif($column.htmlType == "imageUpload")## 图片上传
150 <template #${javaField}="{ row }"> 165 <template #${javaField}="{ row }">
@@ -156,29 +171,29 @@ watch( @@ -156,29 +171,29 @@ watch(
156 </template> 171 </template>
157 #elseif($column.htmlType == "select")## 下拉框 172 #elseif($column.htmlType == "select")## 下拉框
158 <template #${javaField}="{ row, column }"> 173 <template #${javaField}="{ row, column }">
159 - <el-select v-model="row.${javaField}" class="w-full">  
160 - <el-option v-for="option in column.params.options" :key="option.value" :value="option.value" :label="option.label" />  
161 - </el-select> 174 + <ElSelect v-model="row.${javaField}" class="w-full">
  175 + <ElOption v-for="option in column.params.options" :key="option.value" :value="option.value" :label="option.label" />
  176 + </ElSelect>
162 </template> 177 </template>
163 #elseif($column.htmlType == "checkbox")## 多选框 178 #elseif($column.htmlType == "checkbox")## 多选框
164 <template #${javaField}="{ row, column }"> 179 <template #${javaField}="{ row, column }">
165 - <el-checkbox-group v-model="row.${javaField}">  
166 - <el-checkbox v-for="option in column.params.options" :key="option.value" :label="option.value"> 180 + <ElCheckboxGroup v-model="row.${javaField}">
  181 + <ElCheckbox v-for="option in column.params.options" :key="option.value" :value="option.value">
167 {{ option.label }} 182 {{ option.label }}
168 - </el-checkbox>  
169 - </el-checkbox-group> 183 + </ElCheckbox>
  184 + </ElCheckboxGroup>
170 </template> 185 </template>
171 #elseif($column.htmlType == "radio")## 单选框 186 #elseif($column.htmlType == "radio")## 单选框
172 <template #${javaField}="{ row, column }"> 187 <template #${javaField}="{ row, column }">
173 - <el-radio-group v-model="row.${javaField}">  
174 - <el-radio v-for="option in column.params.options" :key="option.value" :label="option.value"> 188 + <ElRadioGroup v-model="row.${javaField}">
  189 + <ElRadio v-for="option in column.params.options" :key="option.value" :value="option.value">
175 {{ option.label }} 190 {{ option.label }}
176 - </el-radio>  
177 - </el-radio-group> 191 + </ElRadio>
  192 + </ElRadioGroup>
178 </template> 193 </template>
179 #elseif($column.htmlType == "datetime")## 时间框 194 #elseif($column.htmlType == "datetime")## 时间框
180 <template #${javaField}="{ row }"> 195 <template #${javaField}="{ row }">
181 - <el-date-picker 196 + <ElDatePicker
182 v-model="row.${javaField}" 197 v-model="row.${javaField}"
183 type="datetime" 198 type="datetime"
184 value-format="x" 199 value-format="x"
@@ -187,16 +202,29 @@ watch( @@ -187,16 +202,29 @@ watch(
187 </template> 202 </template>
188 #elseif($column.htmlType == "textarea" || $column.htmlType == "editor")## 文本框 203 #elseif($column.htmlType == "textarea" || $column.htmlType == "editor")## 文本框
189 <template #${javaField}="{ row }"> 204 <template #${javaField}="{ row }">
190 - <el-input v-model="row.${javaField}" type="textarea" /> 205 + <ElInput v-model="row.${javaField}" type="textarea" />
191 </template> 206 </template>
192 #end 207 #end
193 #end 208 #end
194 #end 209 #end
  210 + <template #actions="{ row }">
  211 + <TableAction
  212 + :actions="[
  213 + {
  214 + label: $t('common.delete'),
  215 + type: 'danger', link: true,
  216 + icon: ACTION_ICON.DELETE,
  217 + auth: ['${permissionPrefix}:delete'],
  218 + onClick: onDelete.bind(null, row),
  219 + },
  220 + ]"
  221 + />
  222 + </template>
195 </Grid> 223 </Grid>
196 <div class="flex justify-center -mt-4"> 224 <div class="flex justify-center -mt-4">
197 - <el-button :icon="Plus" type="primary" plain @click="onAdd" v-access:code="['${subTable.moduleName}:${simpleClassName_strikeCase}:create']"> 225 + <ElButton :icon="Plus" type="primary" plain @click="onAdd" v-access:code="['${subTable.moduleName}:${simpleClassName_strikeCase}:create']">
198 {{ $t('ui.actionTitle.create', ['${subTable.classComment}']) }} 226 {{ $t('ui.actionTitle.create', ['${subTable.classComment}']) }}
199 - </el-button> 227 + </ElButton>
200 </div> 228 </div>
201 #else 229 #else
202 <Form class="mx-4" /> 230 <Form class="mx-4" />
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineFrontendContractTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.service.codegen.inner;
  2 +
  3 +import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
  4 +import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
  5 +import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;
  6 +import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
  7 +import com.baomidou.mybatisplus.annotation.DbType;
  8 +import org.junit.jupiter.api.Test;
  9 +import org.junit.jupiter.params.ParameterizedTest;
  10 +import org.junit.jupiter.params.provider.EnumSource;
  11 +
  12 +import java.util.List;
  13 +import java.util.Map;
  14 +
  15 +import static org.junit.jupiter.api.Assertions.*;
  16 +
  17 +/**
  18 + * {@link CodegenEngine} 的前端组件契约回归测试
  19 + *
  20 + * @author 芋道源码
  21 + */
  22 +public class CodegenEngineFrontendContractTest extends CodegenEngineAbstractTest {
  23 +
  24 + @ParameterizedTest
  25 + @EnumSource(value = CodegenFrontTypeEnum.class, names = {
  26 + "VUE3_VBEN5_ANTD_SCHEMA", "VUE3_VBEN5_ANTDV_NEXT_SCHEMA", "VUE3_VBEN5_EP_SCHEMA"})
  27 + public void testExecute_dictionaryControls(CodegenFrontTypeEnum frontType) {
  28 + // 准备参数:同一字典的多选、单选和数字必填字段
  29 + CodegenTableDO table = getTable("student").setFrontType(frontType.getType())
  30 + .setTemplateType(CodegenTemplateTypeEnum.ONE.getType());
  31 + List<CodegenColumnDO> columns = getColumnList("student");
  32 + columns.add(new CodegenColumnDO().setJavaField("tags").setJavaType("String")
  33 + .setColumnComment("标签").setDictType("system_user_sex").setHtmlType("checkbox")
  34 + .setCreateOperation(true).setUpdateOperation(true).setNullable(false).setPrimaryKey(false));
  35 + columns.add(new CodegenColumnDO().setJavaField("status").setJavaType("Integer")
  36 + .setColumnComment("状态").setDictType("system_user_sex").setHtmlType("radio")
  37 + .setCreateOperation(true).setUpdateOperation(true).setNullable(false).setPrimaryKey(false));
  38 + columns.add(new CodegenColumnDO().setJavaField("amount").setJavaType("Integer")
  39 + .setColumnComment("金额").setHtmlType("inputNumber").setDictType("")
  40 + .setCreateOperation(true).setUpdateOperation(true).setNullable(false).setPrimaryKey(false));
  41 +
  42 + // 调用
  43 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);
  44 + // 调试用:导出真实生成结果,供前端组件验证
  45 + String outputPath = System.getProperty("codegen.contractOutput");
  46 + if (outputPath != null) {
  47 + writeResult(result, outputPath + "/" + frontType.name());
  48 + }
  49 + // 断言:组选项不能变成 boolean Checkbox,必填数字不能被 Zod 推导为 null
  50 + String data = findFile(result, "/data.ts");
  51 + assertTrue(data.contains("component: 'CheckboxGroup'"));
  52 + assertFalse(data.contains("component: 'Checkbox',"));
  53 + assertTrue(data.contains("rules: 'required'"));
  54 + assertFalse(data.contains("z.number("));
  55 + if (frontType == CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA) {
  56 + assertTrue(data.contains("isButton: true"));
  57 + assertFalse(data.contains("buttonStyle:"));
  58 + assertFalse(data.contains("optionType:"));
  59 + assertFalse(data.contains("allowClear:"));
  60 + } else {
  61 + assertTrue(data.contains("optionType: 'button'"));
  62 + assertFalse(data.contains("clearable:"));
  63 + }
  64 + }
  65 +
  66 + @Test
  67 + public void testExecute_eleTreeParentSelectable() {
  68 + // 准备参数
  69 + CodegenTableDO table = getTable("category").setFrontType(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType())
  70 + .setTemplateType(CodegenTemplateTypeEnum.TREE.getType());
  71 + // 调用
  72 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, getColumnList("category"), null, null);
  73 + // 断言:父节点可作为上级,使用 Element Plus 属性
  74 + String data = findFile(result, "/data.ts");
  75 + assertTrue(data.contains("defaultExpandAll: true"));
  76 + assertTrue(data.contains("checkStrictly: true"));
  77 + assertFalse(data.contains("treeDefaultExpandAll"));
  78 + }
  79 +
  80 + @ParameterizedTest
  81 + @EnumSource(value = CodegenFrontTypeEnum.class, names = {
  82 + "VUE3_VBEN5_ANTD_SCHEMA", "VUE3_VBEN5_ANTD_GENERAL",
  83 + "VUE3_VBEN5_ANTDV_NEXT_SCHEMA", "VUE3_VBEN5_ANTDV_NEXT_GENERAL",
  84 + "VUE3_VBEN5_EP_SCHEMA", "VUE3_VBEN5_EP_GENERAL"})
  85 + public void testExecute_importRemovalClearsPayload(CodegenFrontTypeEnum frontType) {
  86 + // 准备参数
  87 + codegenProperties.setImportEnable(true);
  88 + codegenEngine.initGlobalBindingMap();
  89 + CodegenTableDO table = getTable("student").setFrontType(frontType.getType())
  90 + .setTemplateType(CodegenTemplateTypeEnum.ONE.getType());
  91 + // 调用
  92 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, getColumnList("student"), null, null);
  93 + // 断言:移除可视文件时也清空提交来源
  94 + String form = findFile(result, "/import-form.vue");
  95 + assertTrue(form.contains(":on-remove=\"handleRemove\""));
  96 + assertTrue(form.contains("formApi.setFieldValue('file', undefined)") || form.contains("fileRef.value = null"));
  97 + if (frontType == CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA
  98 + || frontType == CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL) {
  99 + assertTrue(form.contains(":on-exceed=\"handleExceed\""));
  100 + }
  101 + }
  102 +
  103 + @ParameterizedTest
  104 + @EnumSource(value = CodegenFrontTypeEnum.class, names = {
  105 + "VUE3_VBEN5_ANTD_SCHEMA", "VUE3_VBEN5_ANTD_GENERAL",
  106 + "VUE3_VBEN5_ANTDV_NEXT_SCHEMA", "VUE3_VBEN5_ANTDV_NEXT_GENERAL",
  107 + "VUE3_VBEN5_EP_SCHEMA", "VUE3_VBEN5_EP_GENERAL"})
  108 + public void testExecute_masterChildEditing(CodegenFrontTypeEnum frontType) {
  109 + // 准备参数:一对多联系人和一对一班主任
  110 + CodegenTableDO table = getTable("student").setFrontType(frontType.getType())
  111 + .setTemplateType(CodegenTemplateTypeEnum.MASTER_NORMAL.getType());
  112 + CodegenTableDO contact = getTable("contact").setFrontType(frontType.getType())
  113 + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType()).setSubJoinColumnId(100L).setSubJoinMany(true);
  114 + CodegenTableDO teacher = getTable("teacher").setFrontType(frontType.getType())
  115 + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType()).setSubJoinColumnId(200L).setSubJoinMany(false);
  116 + // 调用
  117 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, getColumnList("student"),
  118 + List.of(contact, teacher), List.of(getColumnList("contact"), getColumnList("teacher")));
  119 + // 断言:新增和删除以当前表格为准,不重复追加,新增临时编号不提交
  120 + String form = findFile(result, "/student-contact-form.vue");
  121 + assertTrue(form.contains("getTableData()"));
  122 + assertTrue(form.contains("inserted.has(row) ? { ...row, id: undefined } : { ...row }"));
  123 + assertFalse(form.contains("getRemoveRecords()"));
  124 + assertFalse(form.contains(".concat(insertRecords"));
  125 + assertFalse(form.contains("$dictType"));
  126 + assertTrue(form.contains("getStudentContactListByStudentId"));
  127 + if (frontType.name().endsWith("SCHEMA")) {
  128 + assertTrue(form.contains("<template #actions="));
  129 + }
  130 + result.forEach((path, content) -> {
  131 + if (path.endsWith(".vue") || path.endsWith("data.ts")) {
  132 + assertFalse(content.contains("#/api/infra/student"), path + ":API 导入必须匹配生成目录");
  133 + assertFalse(content.contains("VxeTableGridOptions<StudentApi.StudentContact>") && content.contains("import type { InfraStudentApi }"),
  134 + path + ":子表命名空间应与导入一致");
  135 + }
  136 + });
  137 + if (frontType == CodegenFrontTypeEnum.VUE3_VBEN5_ANTDV_NEXT_GENERAL) {
  138 + assertTrue(form.matches("(?s).*import\\s*\\{[^}]*\\bmessage\\b[^}]*}\\s*from 'antdv-next'.*"),
  139 + "子表校验提示必须导入实际的消息组件");
  140 + }
  141 + if (frontType == CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL) {
  142 + assertTrue(form.contains(":icon=\"Plus\""));
  143 + assertFalse(form.contains("h(Plus)"), "图标使用导入的组件,避免未绑定的 h");
  144 + }
  145 + assertTrue(form.contains("function validate(): boolean"));
  146 + assertTrue(form.contains("=== undefined"));
  147 + assertTrue(form.contains("loadData([])") || form.contains("list.value = []"));
  148 + String parentForm = findFile(result, "/modules/form.vue");
  149 + assertTrue(parentForm.contains("studentContactFormRef.value?.validate()"));
  150 + assertFalse(parentForm.contains("TODO 列表值校验"));
  151 + }
  152 +
  153 + @ParameterizedTest
  154 + @EnumSource(value = CodegenTemplateTypeEnum.class, names = { "MASTER_ERP", "MASTER_INNER" })
  155 + public void testExecute_eleChildListImports(CodegenTemplateTypeEnum templateType) {
  156 + // 准备参数
  157 + CodegenFrontTypeEnum frontType = CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL;
  158 + CodegenTableDO table = getTable("student").setFrontType(frontType.getType())
  159 + .setTemplateType(templateType.getType());
  160 + CodegenTableDO contact = getTable("contact").setFrontType(frontType.getType())
  161 + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType()).setSubJoinColumnId(100L).setSubJoinMany(true);
  162 + // 调用
  163 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, getColumnList("student"),
  164 + List.of(contact), List.of(getColumnList("contact")));
  165 + // 断言:ERP 子表查询实际调用的工具必须导入
  166 + String index = findFile(result, "/index.vue");
  167 + assertTrue(index.contains("<VbenVxeTableToolbar"));
  168 + assertTrue(index.contains("</VbenVxeTableToolbar>"));
  169 + assertFalse(index.contains("<TableToolbar"));
  170 + String list = findFile(result, "/student-contact-list.vue");
  171 + if (templateType == CodegenTemplateTypeEnum.MASTER_ERP) {
  172 + assertTrue(list.contains("<VbenVxeTableToolbar"));
  173 + assertTrue(list.contains("</VbenVxeTableToolbar>"));
  174 + assertFalse(list.contains("</TableToolbar>"));
  175 + assertTrue(list.contains("cloneDeep(queryParams)"));
  176 + assertTrue(list.matches("(?s).*import\\s*\\{[^}]*\\bcloneDeep\\b[^}]*}\\s*from '@vben/utils'.*"));
  177 + }
  178 + assertTrue(list.contains("formatDateTime(row."));
  179 + assertTrue(list.matches("(?s).*import\\s*\\{[^}]*\\bformatDateTime\\b[^}]*}\\s*from '@vben/utils'.*"));
  180 + }
  181 +
  182 + @Test
  183 + public void testExecute_vue3ImportUploadResponse() {
  184 + // 准备参数
  185 + codegenProperties.setImportEnable(true);
  186 + codegenEngine.initGlobalBindingMap();
  187 + CodegenTableDO table = getTable("student").setFrontType(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType())
  188 + .setTemplateType(CodegenTemplateTypeEnum.ONE.getType());
  189 + // 调用
  190 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, getColumnList("student"), null, null);
  191 + // 断言
  192 + String form = findFile(result, "/StudentImportForm.vue");
  193 + assertTrue(form.contains("const res = await StudentApi.importStudent(formData)"));
  194 + assertTrue(form.contains("const data = res.data"));
  195 + }
  196 +
  197 +
  198 + @Test
  199 + public void testExecute_vben2DateRangeFormat() {
  200 + // 准备参数
  201 + CodegenTableDO table = getTable("student").setFrontType(CodegenFrontTypeEnum.VUE3_VBEN2_ANTD_SCHEMA.getType())
  202 + .setTemplateType(CodegenTemplateTypeEnum.ONE.getType());
  203 + // 调用
  204 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, getColumnList("student"), null, null);
  205 + // 断言:查询区间提交后端需要的时间字符串
  206 + String data = findFile(result, ".data.ts");
  207 + assertTrue(data.contains("valueFormat: 'YYYY-MM-DD HH:mm:ss'"));
  208 + assertTrue(data.contains("allowClear: true"));
  209 + }
  210 +
  211 + @Test
  212 + public void testExecute_uniappPaginationContract() {
  213 + // 准备参数
  214 + CodegenTableDO table = getTable("student").setFrontType(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType())
  215 + .setTemplateType(CodegenTemplateTypeEnum.ONE.getType());
  216 + // 调用
  217 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, getColumnList("student"), null, null);
  218 + // 断言:移动端已有分页/失败恢复契约保持有效,避免机械套用 PC 组件写法
  219 + String api = findFile(result, "/index.ts");
  220 + assertTrue(api.contains("params: PageParam"));
  221 + assertFalse(api.contains("PageReqVO"));
  222 + String list = result.entrySet().stream()
  223 + .filter(entry -> entry.getKey().endsWith("/index.vue") && entry.getValue().contains("<z-paging"))
  224 + .findFirst().orElseThrow().getValue();
  225 + assertTrue(list.contains("completeByTotal(data.list, data.total)"));
  226 + assertTrue(list.contains("complete(false)"));
  227 + assertTrue(list.contains("uni.$off("));
  228 + }
  229 +
  230 + private static String findFile(Map<String, String> result, String suffix) {
  231 + return result.entrySet().stream().filter(entry -> entry.getKey().endsWith(suffix))
  232 + .findFirst().orElseThrow(() -> new AssertionError("缺少生成文件:" + suffix)).getValue();
  233 + }
  234 +}
yudao-module-infra/src/test/resources/codegen/vben5_antd_general_master_erp/vue/index
@@ -35,12 +35,11 @@ const total = ref(0) // 列表的总页数 @@ -35,12 +35,11 @@ const total = ref(0) // 列表的总页数
35 const queryParams = reactive({ 35 const queryParams = reactive({
36 pageNo: 1, 36 pageNo: 1,
37 pageSize: 10, 37 pageSize: 10,
38 - name: undefined,  
39 - birthday: undefined,  
40 - birthday: undefined,  
41 - sex: undefined,  
42 - enabled: undefined,  
43 - createTime: undefined, 38 + name: undefined,
  39 + birthday: undefined,
  40 + sex: undefined,
  41 + enabled: undefined,
  42 + createTime: undefined,
44 }) 43 })
45 const queryFormRef = ref() // 搜索的表单 44 const queryFormRef = ref() // 搜索的表单
46 const exportLoading = ref(false) // 导出的加载中 45 const exportLoading = ref(false) // 导出的加载中
@@ -136,7 +135,14 @@ function handleRowCheckboxChange({ @@ -136,7 +135,14 @@ function handleRowCheckboxChange({
136 async function handleExport() { 135 async function handleExport() {
137 try { 136 try {
138 exportLoading.value = true; 137 exportLoading.value = true;
139 - const data = await exportStudent(queryParams); 138 + const params = cloneDeep(queryParams) as any;
  139 + if (params.birthday && Array.isArray(params.birthday)) {
  140 + params.birthday = (params.birthday as string[]).join(',');
  141 + }
  142 + if (params.createTime && Array.isArray(params.createTime)) {
  143 + params.createTime = (params.createTime as string[]).join(',');
  144 + }
  145 + const data = await exportStudent(params);
140 downloadFileFromBlobPart({ fileName: '学生.xls', source: data }); 146 downloadFileFromBlobPart({ fileName: '学生.xls', source: data });
141 }finally { 147 }finally {
142 exportLoading.value = false; 148 exportLoading.value = false;
yudao-module-infra/src/test/resources/codegen/vben5_antd_general_master_inner/vue/form
@@ -72,7 +72,11 @@ const [Modal, modalApi] = useVbenModal({ @@ -72,7 +72,11 @@ const [Modal, modalApi] = useVbenModal({
72 async onConfirm() { 72 async onConfirm() {
73 await formRef.value?.validate(); 73 await formRef.value?.validate();
74 // 校验子表单 74 // 校验子表单
75 - try { 75 + if (!studentContactFormRef.value?.validate()) {
  76 + subTabsName.value = 'studentContact';
  77 + return;
  78 + }
  79 + try {
76 await studentTeacherFormRef.value?.validate() 80 await studentTeacherFormRef.value?.validate()
77 } catch (e) { 81 } catch (e) {
78 subTabsName.value = 'studentTeacher' 82 subTabsName.value = 'studentTeacher'
yudao-module-infra/src/test/resources/codegen/vben5_antd_general_master_inner/vue/index
@@ -31,12 +31,11 @@ const total = ref(0) // 列表的总页数 @@ -31,12 +31,11 @@ const total = ref(0) // 列表的总页数
31 const queryParams = reactive({ 31 const queryParams = reactive({
32 pageNo: 1, 32 pageNo: 1,
33 pageSize: 10, 33 pageSize: 10,
34 - name: undefined,  
35 - birthday: undefined,  
36 - birthday: undefined,  
37 - sex: undefined,  
38 - enabled: undefined,  
39 - createTime: undefined, 34 + name: undefined,
  35 + birthday: undefined,
  36 + sex: undefined,
  37 + enabled: undefined,
  38 + createTime: undefined,
40 }) 39 })
41 const queryFormRef = ref() // 搜索的表单 40 const queryFormRef = ref() // 搜索的表单
42 const exportLoading = ref(false) // 导出的加载中 41 const exportLoading = ref(false) // 导出的加载中
@@ -132,7 +131,14 @@ function handleRowCheckboxChange({ @@ -132,7 +131,14 @@ function handleRowCheckboxChange({
132 async function handleExport() { 131 async function handleExport() {
133 try { 132 try {
134 exportLoading.value = true; 133 exportLoading.value = true;
135 - const data = await exportStudent(queryParams); 134 + const params = cloneDeep(queryParams) as any;
  135 + if (params.birthday && Array.isArray(params.birthday)) {
  136 + params.birthday = (params.birthday as string[]).join(',');
  137 + }
  138 + if (params.createTime && Array.isArray(params.createTime)) {
  139 + params.createTime = (params.createTime as string[]).join(',');
  140 + }
  141 + const data = await exportStudent(params);
136 downloadFileFromBlobPart({ fileName: '学生.xls', source: data }); 142 downloadFileFromBlobPart({ fileName: '学生.xls', source: data });
137 }finally { 143 }finally {
138 exportLoading.value = false; 144 exportLoading.value = false;
yudao-module-infra/src/test/resources/codegen/vben5_antd_general_master_inner/vue/student-contact-form
@@ -32,25 +32,96 @@ async function handleDelete(row: StudentApi.StudentContact) { @@ -32,25 +32,96 @@ async function handleDelete(row: StudentApi.StudentContact) {
32 await tableRef.value?.remove(row); 32 await tableRef.value?.remove(row);
33 } 33 }
34 34
35 -/** 提供获取表格数据的方法供父组件调用 */  
36 -defineExpose({  
37 - getData: (): StudentApi.StudentContact[] => {  
38 - const data = list.value as StudentApi.StudentContact[];  
39 - const removeRecords = tableRef.value?.getRemoveRecords() as StudentApi.StudentContact[];  
40 - const insertRecords = tableRef.value?.getInsertRecords() as StudentApi.StudentContact[];  
41 - return [  
42 - ...data.filter(  
43 - (row) => !removeRecords.some((removed) => removed.id === row.id),  
44 - ),  
45 - ...insertRecords.map((row: any) => ({ ...row, id: undefined })),  
46 - ];  
47 - },  
48 -}); 35 +/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
  36 +function getData(): StudentApi.StudentContact[] {
  37 + const table = tableRef.value;
  38 + if (!table) {
  39 + return [];
  40 + }
  41 + const { fullData } = table.getTableData();
  42 + const inserted = new Set(table.getInsertRecords());
  43 + return fullData.map((row) =>
  44 + inserted.has(row) ? { ...row, id: undefined } : { ...row },
  45 + );
  46 +}
  47 +
  48 +/** 校验子表必填字段 */
  49 +function validate(): boolean {
  50 + for (const [index, row] of getData().entries()) {
  51 + if (
  52 + row.name === null ||
  53 + row.name === undefined ||
  54 + (typeof row.name === 'string' && String(row.name).trim() === '') ||
  55 + (Array.isArray(row.name) && row.name.length === 0)
  56 + ) {
  57 + message.error(`第 ${index + 1} 行:名字不能为空`);
  58 + return false;
  59 + }
  60 + if (
  61 + row.description === null ||
  62 + row.description === undefined ||
  63 + (typeof row.description === 'string' && String(row.description).trim() === '') ||
  64 + (Array.isArray(row.description) && row.description.length === 0)
  65 + ) {
  66 + message.error(`第 ${index + 1} 行:简介不能为空`);
  67 + return false;
  68 + }
  69 + if (
  70 + row.birthday === null ||
  71 + row.birthday === undefined ||
  72 + (typeof row.birthday === 'string' && String(row.birthday).trim() === '') ||
  73 + (Array.isArray(row.birthday) && row.birthday.length === 0)
  74 + ) {
  75 + message.error(`第 ${index + 1} 行:出生日期不能为空`);
  76 + return false;
  77 + }
  78 + if (
  79 + row.sex === null ||
  80 + row.sex === undefined ||
  81 + (typeof row.sex === 'string' && String(row.sex).trim() === '') ||
  82 + (Array.isArray(row.sex) && row.sex.length === 0)
  83 + ) {
  84 + message.error(`第 ${index + 1} 行:性别不能为空`);
  85 + return false;
  86 + }
  87 + if (
  88 + row.enabled === null ||
  89 + row.enabled === undefined ||
  90 + (typeof row.enabled === 'string' && String(row.enabled).trim() === '') ||
  91 + (Array.isArray(row.enabled) && row.enabled.length === 0)
  92 + ) {
  93 + message.error(`第 ${index + 1} 行:是否有效不能为空`);
  94 + return false;
  95 + }
  96 + if (
  97 + row.avatar === null ||
  98 + row.avatar === undefined ||
  99 + (typeof row.avatar === 'string' && String(row.avatar).trim() === '') ||
  100 + (Array.isArray(row.avatar) && row.avatar.length === 0)
  101 + ) {
  102 + message.error(`第 ${index + 1} 行:头像不能为空`);
  103 + return false;
  104 + }
  105 + if (
  106 + row.memo === null ||
  107 + row.memo === undefined ||
  108 + (typeof row.memo === 'string' && String(row.memo).trim() === '') ||
  109 + (Array.isArray(row.memo) && row.memo.length === 0)
  110 + ) {
  111 + message.error(`第 ${index + 1} 行:备注不能为空`);
  112 + return false;
  113 + }
  114 + }
  115 + return true;
  116 +}
  117 +
  118 +defineExpose({ getData, validate });
49 119
50 /** 监听主表的关联字段的变化,加载对应的子表数据 */ 120 /** 监听主表的关联字段的变化,加载对应的子表数据 */
51 watch( 121 watch(
52 () => props.studentId, 122 () => props.studentId,
53 async (val) => { 123 async (val) => {
  124 + list.value = [];
54 if (!val) { 125 if (!val) {
55 return; 126 return;
56 } 127 }
@@ -86,7 +157,7 @@ watch( @@ -86,7 +157,7 @@ watch(
86 <template #default="{ row }"> 157 <template #default="{ row }">
87 <Select v-model:value="row.sex" placeholder="请选择性别"> 158 <Select v-model:value="row.sex" placeholder="请选择性别">
88 <Select.Option 159 <Select.Option
89 - v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number')" 160 + v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
90 :key="dict.value" 161 :key="dict.value"
91 :value="dict.value" 162 :value="dict.value"
92 > 163 >
@@ -99,7 +170,7 @@ watch( @@ -99,7 +170,7 @@ watch(
99 <template #default="{ row }"> 170 <template #default="{ row }">
100 <RadioGroup v-model:value="row.enabled"> 171 <RadioGroup v-model:value="row.enabled">
101 <Radio 172 <Radio
102 - v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'boolean')" 173 + v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
103 :key="dict.value" 174 :key="dict.value"
104 :value="dict.value" 175 :value="dict.value"
105 > 176 >
yudao-module-infra/src/test/resources/codegen/vben5_antd_general_master_inner/vue/student-teacher-form
@@ -51,6 +51,18 @@ defineExpose({ @@ -51,6 +51,18 @@ defineExpose({
51 watch( 51 watch(
52 () => props.studentId, 52 () => props.studentId,
53 async (val) => { 53 async (val) => {
  54 + formData.value = {
  55 + id: undefined,
  56 + studentId: undefined,
  57 + name: undefined,
  58 + description: undefined,
  59 + birthday: undefined,
  60 + sex: undefined,
  61 + enabled: undefined,
  62 + avatar: undefined,
  63 + video: undefined,
  64 + memo: undefined,
  65 + };
54 if (!val) { 66 if (!val) {
55 return; 67 return;
56 } 68 }
yudao-module-infra/src/test/resources/codegen/vben5_antd_general_master_normal/vue/form
@@ -72,7 +72,11 @@ const [Modal, modalApi] = useVbenModal({ @@ -72,7 +72,11 @@ const [Modal, modalApi] = useVbenModal({
72 async onConfirm() { 72 async onConfirm() {
73 await formRef.value?.validate(); 73 await formRef.value?.validate();
74 // 校验子表单 74 // 校验子表单
75 - try { 75 + if (!studentContactFormRef.value?.validate()) {
  76 + subTabsName.value = 'studentContact';
  77 + return;
  78 + }
  79 + try {
76 await studentTeacherFormRef.value?.validate() 80 await studentTeacherFormRef.value?.validate()
77 } catch (e) { 81 } catch (e) {
78 subTabsName.value = 'studentTeacher' 82 subTabsName.value = 'studentTeacher'
yudao-module-infra/src/test/resources/codegen/vben5_antd_general_master_normal/vue/index
@@ -27,12 +27,11 @@ const total = ref(0) // 列表的总页数 @@ -27,12 +27,11 @@ const total = ref(0) // 列表的总页数
27 const queryParams = reactive({ 27 const queryParams = reactive({
28 pageNo: 1, 28 pageNo: 1,
29 pageSize: 10, 29 pageSize: 10,
30 - name: undefined,  
31 - birthday: undefined,  
32 - birthday: undefined,  
33 - sex: undefined,  
34 - enabled: undefined,  
35 - createTime: undefined, 30 + name: undefined,
  31 + birthday: undefined,
  32 + sex: undefined,
  33 + enabled: undefined,
  34 + createTime: undefined,
36 }) 35 })
37 const queryFormRef = ref() // 搜索的表单 36 const queryFormRef = ref() // 搜索的表单
38 const exportLoading = ref(false) // 导出的加载中 37 const exportLoading = ref(false) // 导出的加载中
@@ -128,7 +127,14 @@ function handleRowCheckboxChange({ @@ -128,7 +127,14 @@ function handleRowCheckboxChange({
128 async function handleExport() { 127 async function handleExport() {
129 try { 128 try {
130 exportLoading.value = true; 129 exportLoading.value = true;
131 - const data = await exportStudent(queryParams); 130 + const params = cloneDeep(queryParams) as any;
  131 + if (params.birthday && Array.isArray(params.birthday)) {
  132 + params.birthday = (params.birthday as string[]).join(',');
  133 + }
  134 + if (params.createTime && Array.isArray(params.createTime)) {
  135 + params.createTime = (params.createTime as string[]).join(',');
  136 + }
  137 + const data = await exportStudent(params);
132 downloadFileFromBlobPart({ fileName: '学生.xls', source: data }); 138 downloadFileFromBlobPart({ fileName: '学生.xls', source: data });
133 }finally { 139 }finally {
134 exportLoading.value = false; 140 exportLoading.value = false;
yudao-module-infra/src/test/resources/codegen/vben5_antd_general_master_normal/vue/student-contact-form
@@ -32,25 +32,96 @@ async function handleDelete(row: StudentApi.StudentContact) { @@ -32,25 +32,96 @@ async function handleDelete(row: StudentApi.StudentContact) {
32 await tableRef.value?.remove(row); 32 await tableRef.value?.remove(row);
33 } 33 }
34 34
35 -/** 提供获取表格数据的方法供父组件调用 */  
36 -defineExpose({  
37 - getData: (): StudentApi.StudentContact[] => {  
38 - const data = list.value as StudentApi.StudentContact[];  
39 - const removeRecords = tableRef.value?.getRemoveRecords() as StudentApi.StudentContact[];  
40 - const insertRecords = tableRef.value?.getInsertRecords() as StudentApi.StudentContact[];  
41 - return [  
42 - ...data.filter(  
43 - (row) => !removeRecords.some((removed) => removed.id === row.id),  
44 - ),  
45 - ...insertRecords.map((row: any) => ({ ...row, id: undefined })),  
46 - ];  
47 - },  
48 -}); 35 +/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
  36 +function getData(): StudentApi.StudentContact[] {
  37 + const table = tableRef.value;
  38 + if (!table) {
  39 + return [];
  40 + }
  41 + const { fullData } = table.getTableData();
  42 + const inserted = new Set(table.getInsertRecords());
  43 + return fullData.map((row) =>
  44 + inserted.has(row) ? { ...row, id: undefined } : { ...row },
  45 + );
  46 +}
  47 +
  48 +/** 校验子表必填字段 */
  49 +function validate(): boolean {
  50 + for (const [index, row] of getData().entries()) {
  51 + if (
  52 + row.name === null ||
  53 + row.name === undefined ||
  54 + (typeof row.name === 'string' && String(row.name).trim() === '') ||
  55 + (Array.isArray(row.name) && row.name.length === 0)
  56 + ) {
  57 + message.error(`第 ${index + 1} 行:名字不能为空`);
  58 + return false;
  59 + }
  60 + if (
  61 + row.description === null ||
  62 + row.description === undefined ||
  63 + (typeof row.description === 'string' && String(row.description).trim() === '') ||
  64 + (Array.isArray(row.description) && row.description.length === 0)
  65 + ) {
  66 + message.error(`第 ${index + 1} 行:简介不能为空`);
  67 + return false;
  68 + }
  69 + if (
  70 + row.birthday === null ||
  71 + row.birthday === undefined ||
  72 + (typeof row.birthday === 'string' && String(row.birthday).trim() === '') ||
  73 + (Array.isArray(row.birthday) && row.birthday.length === 0)
  74 + ) {
  75 + message.error(`第 ${index + 1} 行:出生日期不能为空`);
  76 + return false;
  77 + }
  78 + if (
  79 + row.sex === null ||
  80 + row.sex === undefined ||
  81 + (typeof row.sex === 'string' && String(row.sex).trim() === '') ||
  82 + (Array.isArray(row.sex) && row.sex.length === 0)
  83 + ) {
  84 + message.error(`第 ${index + 1} 行:性别不能为空`);
  85 + return false;
  86 + }
  87 + if (
  88 + row.enabled === null ||
  89 + row.enabled === undefined ||
  90 + (typeof row.enabled === 'string' && String(row.enabled).trim() === '') ||
  91 + (Array.isArray(row.enabled) && row.enabled.length === 0)
  92 + ) {
  93 + message.error(`第 ${index + 1} 行:是否有效不能为空`);
  94 + return false;
  95 + }
  96 + if (
  97 + row.avatar === null ||
  98 + row.avatar === undefined ||
  99 + (typeof row.avatar === 'string' && String(row.avatar).trim() === '') ||
  100 + (Array.isArray(row.avatar) && row.avatar.length === 0)
  101 + ) {
  102 + message.error(`第 ${index + 1} 行:头像不能为空`);
  103 + return false;
  104 + }
  105 + if (
  106 + row.memo === null ||
  107 + row.memo === undefined ||
  108 + (typeof row.memo === 'string' && String(row.memo).trim() === '') ||
  109 + (Array.isArray(row.memo) && row.memo.length === 0)
  110 + ) {
  111 + message.error(`第 ${index + 1} 行:备注不能为空`);
  112 + return false;
  113 + }
  114 + }
  115 + return true;
  116 +}
  117 +
  118 +defineExpose({ getData, validate });
49 119
50 /** 监听主表的关联字段的变化,加载对应的子表数据 */ 120 /** 监听主表的关联字段的变化,加载对应的子表数据 */
51 watch( 121 watch(
52 () => props.studentId, 122 () => props.studentId,
53 async (val) => { 123 async (val) => {
  124 + list.value = [];
54 if (!val) { 125 if (!val) {
55 return; 126 return;
56 } 127 }
@@ -86,7 +157,7 @@ watch( @@ -86,7 +157,7 @@ watch(
86 <template #default="{ row }"> 157 <template #default="{ row }">
87 <Select v-model:value="row.sex" placeholder="请选择性别"> 158 <Select v-model:value="row.sex" placeholder="请选择性别">
88 <Select.Option 159 <Select.Option
89 - v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number')" 160 + v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
90 :key="dict.value" 161 :key="dict.value"
91 :value="dict.value" 162 :value="dict.value"
92 > 163 >
@@ -99,7 +170,7 @@ watch( @@ -99,7 +170,7 @@ watch(
99 <template #default="{ row }"> 170 <template #default="{ row }">
100 <RadioGroup v-model:value="row.enabled"> 171 <RadioGroup v-model:value="row.enabled">
101 <Radio 172 <Radio
102 - v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'boolean')" 173 + v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
103 :key="dict.value" 174 :key="dict.value"
104 :value="dict.value" 175 :value="dict.value"
105 > 176 >
yudao-module-infra/src/test/resources/codegen/vben5_antd_general_master_normal/vue/student-teacher-form
@@ -51,6 +51,18 @@ defineExpose({ @@ -51,6 +51,18 @@ defineExpose({
51 watch( 51 watch(
52 () => props.studentId, 52 () => props.studentId,
53 async (val) => { 53 async (val) => {
  54 + formData.value = {
  55 + id: undefined,
  56 + studentId: undefined,
  57 + name: undefined,
  58 + description: undefined,
  59 + birthday: undefined,
  60 + sex: undefined,
  61 + enabled: undefined,
  62 + avatar: undefined,
  63 + video: undefined,
  64 + memo: undefined,
  65 + };
54 if (!val) { 66 if (!val) {
55 return; 67 return;
56 } 68 }
yudao-module-infra/src/test/resources/codegen/vben5_antd_general_one/vue/index
@@ -27,12 +27,11 @@ const total = ref(0) // 列表的总页数 @@ -27,12 +27,11 @@ const total = ref(0) // 列表的总页数
27 const queryParams = reactive({ 27 const queryParams = reactive({
28 pageNo: 1, 28 pageNo: 1,
29 pageSize: 10, 29 pageSize: 10,
30 - name: undefined,  
31 - birthday: undefined,  
32 - birthday: undefined,  
33 - sex: undefined,  
34 - enabled: undefined,  
35 - createTime: undefined, 30 + name: undefined,
  31 + birthday: undefined,
  32 + sex: undefined,
  33 + enabled: undefined,
  34 + createTime: undefined,
36 }) 35 })
37 const queryFormRef = ref() // 搜索的表单 36 const queryFormRef = ref() // 搜索的表单
38 const exportLoading = ref(false) // 导出的加载中 37 const exportLoading = ref(false) // 导出的加载中
@@ -128,7 +127,14 @@ function handleRowCheckboxChange({ @@ -128,7 +127,14 @@ function handleRowCheckboxChange({
128 async function handleExport() { 127 async function handleExport() {
129 try { 128 try {
130 exportLoading.value = true; 129 exportLoading.value = true;
131 - const data = await exportStudent(queryParams); 130 + const params = cloneDeep(queryParams) as any;
  131 + if (params.birthday && Array.isArray(params.birthday)) {
  132 + params.birthday = (params.birthday as string[]).join(',');
  133 + }
  134 + if (params.createTime && Array.isArray(params.createTime)) {
  135 + params.createTime = (params.createTime as string[]).join(',');
  136 + }
  137 + const data = await exportStudent(params);
132 downloadFileFromBlobPart({ fileName: '学生.xls', source: data }); 138 downloadFileFromBlobPart({ fileName: '学生.xls', source: data });
133 }finally { 139 }finally {
134 exportLoading.value = false; 140 exportLoading.value = false;
yudao-module-infra/src/test/resources/codegen/vben5_antd_general_one_importEnable/vue/import-form
@@ -48,6 +48,11 @@ function beforeUpload(file: FileType) { @@ -48,6 +48,11 @@ function beforeUpload(file: FileType) {
48 return false; 48 return false;
49 } 49 }
50 50
  51 +/** 移除文件 */
  52 +function handleRemove() {
  53 + fileRef.value = null;
  54 +}
  55 +
51 /** 下载导入模板 */ 56 /** 下载导入模板 */
52 async function handleDownload() { 57 async function handleDownload() {
53 const data = await importStudentTemplate(); 58 const data = await importStudentTemplate();
@@ -58,7 +63,12 @@ async function handleDownload() { @@ -58,7 +63,12 @@ async function handleDownload() {
58 <template> 63 <template>
59 <Modal title="导入学生" class="w-1/3"> 64 <Modal title="导入学生" class="w-1/3">
60 <div class="mx-4"> 65 <div class="mx-4">
61 - <Upload :max-count="1" accept=".xls,.xlsx" :before-upload="beforeUpload"> 66 + <Upload
  67 + :max-count="1"
  68 + accept=".xls,.xlsx"
  69 + :before-upload="beforeUpload"
  70 + :on-remove="handleRemove"
  71 + >
62 <Button type="primary"> 选择 Excel 文件 </Button> 72 <Button type="primary"> 选择 Excel 文件 </Button>
63 </Upload> 73 </Upload>
64 </div> 74 </div>
yudao-module-infra/src/test/resources/codegen/vben5_antd_general_one_importEnable/vue/index
@@ -28,12 +28,11 @@ const total = ref(0) // 列表的总页数 @@ -28,12 +28,11 @@ const total = ref(0) // 列表的总页数
28 const queryParams = reactive({ 28 const queryParams = reactive({
29 pageNo: 1, 29 pageNo: 1,
30 pageSize: 10, 30 pageSize: 10,
31 - name: undefined,  
32 - birthday: undefined,  
33 - birthday: undefined,  
34 - sex: undefined,  
35 - enabled: undefined,  
36 - createTime: undefined, 31 + name: undefined,
  32 + birthday: undefined,
  33 + sex: undefined,
  34 + enabled: undefined,
  35 + createTime: undefined,
37 }) 36 })
38 const queryFormRef = ref() // 搜索的表单 37 const queryFormRef = ref() // 搜索的表单
39 const exportLoading = ref(false) // 导出的加载中 38 const exportLoading = ref(false) // 导出的加载中
@@ -138,7 +137,14 @@ function handleRowCheckboxChange({ @@ -138,7 +137,14 @@ function handleRowCheckboxChange({
138 async function handleExport() { 137 async function handleExport() {
139 try { 138 try {
140 exportLoading.value = true; 139 exportLoading.value = true;
141 - const data = await exportStudent(queryParams); 140 + const params = cloneDeep(queryParams) as any;
  141 + if (params.birthday && Array.isArray(params.birthday)) {
  142 + params.birthday = (params.birthday as string[]).join(',');
  143 + }
  144 + if (params.createTime && Array.isArray(params.createTime)) {
  145 + params.createTime = (params.createTime as string[]).join(',');
  146 + }
  147 + const data = await exportStudent(params);
142 downloadFileFromBlobPart({ fileName: '学生.xls', source: data }); 148 downloadFileFromBlobPart({ fileName: '学生.xls', source: data });
143 }finally { 149 }finally {
144 exportLoading.value = false; 150 exportLoading.value = false;
yudao-module-infra/src/test/resources/codegen/vben5_antd_general_tree/vue/index
@@ -24,7 +24,7 @@ const loading = ref(true) // 列表的加载中 @@ -24,7 +24,7 @@ const loading = ref(true) // 列表的加载中
24 const list = ref<any[]>([]) // 树列表的数据 24 const list = ref<any[]>([]) // 树列表的数据
25 25
26 const queryParams = reactive({ 26 const queryParams = reactive({
27 - name: undefined, 27 + name: undefined,
28 }) 28 })
29 const queryFormRef = ref() // 搜索的表单 29 const queryFormRef = ref() // 搜索的表单
30 const exportLoading = ref(false) // 导出的加载中 30 const exportLoading = ref(false) // 导出的加载中
@@ -91,7 +91,8 @@ async function handleDelete(row: CategoryApi.Category) { @@ -91,7 +91,8 @@ async function handleDelete(row: CategoryApi.Category) {
91 async function handleExport() { 91 async function handleExport() {
92 try { 92 try {
93 exportLoading.value = true; 93 exportLoading.value = true;
94 - const data = await exportCategory(queryParams); 94 + const params = cloneDeep(queryParams) as any;
  95 + const data = await exportCategory(params);
95 downloadFileFromBlobPart({ fileName: '分类.xls', source: data }); 96 downloadFileFromBlobPart({ fileName: '分类.xls', source: data });
96 }finally { 97 }finally {
97 exportLoading.value = false; 98 exportLoading.value = false;
yudao-module-infra/src/test/resources/codegen/vben5_antd_schema_master_erp/vue/form
@@ -5,7 +5,7 @@ import { computed, ref } from &#39;vue&#39;; @@ -5,7 +5,7 @@ import { computed, ref } from &#39;vue&#39;;
5 5
6 import { useVbenModal } from '@vben/common-ui'; 6 import { useVbenModal } from '@vben/common-ui';
7 7
8 -import { message, Tabs } from 'ant-design-vue'; 8 +import { message } from 'ant-design-vue';
9 9
10 import { useVbenForm } from '#/adapter/form'; 10 import { useVbenForm } from '#/adapter/form';
11 import { createStudent, getStudent, updateStudent } from '#/api/infra/demo'; 11 import { createStudent, getStudent, updateStudent } from '#/api/infra/demo';
yudao-module-infra/src/test/resources/codegen/vben5_antd_schema_master_inner/ts/data
@@ -269,7 +269,7 @@ export function useGridColumns(): VxeTableGridOptions&lt;InfraStudentApi.Student&gt;[&#39; @@ -269,7 +269,7 @@ export function useGridColumns(): VxeTableGridOptions&lt;InfraStudentApi.Student&gt;[&#39;
269 minWidth: 120, 269 minWidth: 120,
270 slots: { default: 'sex' }, 270 slots: { default: 'sex' },
271 params: { 271 params: {
272 - options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'boolean'), 272 + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'),
273 }, 273 },
274 }, 274 },
275 { 275 {
@@ -309,7 +309,7 @@ export function useGridColumns(): VxeTableGridOptions&lt;InfraStudentApi.Student&gt;[&#39; @@ -309,7 +309,7 @@ export function useGridColumns(): VxeTableGridOptions&lt;InfraStudentApi.Student&gt;[&#39;
309 } 309 }
310 310
311 /** 列表的字段 */ 311 /** 列表的字段 */
312 - export function useStudentContactGridColumns(): VxeTableGridOptions<StudentApi.StudentContact>['columns'] { 312 + export function useStudentContactGridColumns(): VxeTableGridOptions<InfraStudentApi.StudentContact>['columns'] {
313 return [ 313 return [
314 { 314 {
315 field: 'id', 315 field: 'id',
@@ -490,7 +490,7 @@ export function useGridColumns(): VxeTableGridOptions&lt;InfraStudentApi.Student&gt;[&#39; @@ -490,7 +490,7 @@ export function useGridColumns(): VxeTableGridOptions&lt;InfraStudentApi.Student&gt;[&#39;
490 } 490 }
491 491
492 /** 列表的字段 */ 492 /** 列表的字段 */
493 - export function useStudentTeacherGridColumns(): VxeTableGridOptions<StudentApi.StudentTeacher>['columns'] { 493 + export function useStudentTeacherGridColumns(): VxeTableGridOptions<InfraStudentApi.StudentTeacher>['columns'] {
494 return [ 494 return [
495 { 495 {
496 field: 'id', 496 field: 'id',
yudao-module-infra/src/test/resources/codegen/vben5_antd_schema_master_inner/vue/form
@@ -5,7 +5,7 @@ import { computed, ref } from &#39;vue&#39;; @@ -5,7 +5,7 @@ import { computed, ref } from &#39;vue&#39;;
5 5
6 import { useVbenModal } from '@vben/common-ui'; 6 import { useVbenModal } from '@vben/common-ui';
7 7
8 -import { message } from 'ant-design-vue'; 8 +import { message, Tabs } from 'ant-design-vue';
9 9
10 import { useVbenForm } from '#/adapter/form'; 10 import { useVbenForm } from '#/adapter/form';
11 import { createStudent, getStudent, updateStudent } from '#/api/infra/demo'; 11 import { createStudent, getStudent, updateStudent } from '#/api/infra/demo';
@@ -48,7 +48,12 @@ const [Modal, modalApi] = useVbenModal({ @@ -48,7 +48,12 @@ const [Modal, modalApi] = useVbenModal({
48 return; 48 return;
49 } 49 }
50 // 校验子表单 50 // 校验子表单
51 - const studentTeacherValid = await studentTeacherFormRef.value?.validate(); 51 + const studentContactValid = await studentContactFormRef.value?.validate();
  52 + if (!studentContactValid) {
  53 + subTabsName.value = 'studentContact';
  54 + return;
  55 + }
  56 + const studentTeacherValid = await studentTeacherFormRef.value?.validate();
52 if (!studentTeacherValid) { 57 if (!studentTeacherValid) {
53 subTabsName.value = 'studentTeacher'; 58 subTabsName.value = 'studentTeacher';
54 return; 59 return;
yudao-module-infra/src/test/resources/codegen/vben5_antd_schema_master_inner/vue/student-contact-form
@@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
6 import { $t } from '#/locales'; 6 import { $t } from '#/locales';
7 7
8 import { Plus } from "@vben/icons"; 8 import { Plus } from "@vben/icons";
9 -import { Button, Tabs, Checkbox, Input, Textarea, Select,RadioGroup,CheckboxGroup, DatePicker } from 'ant-design-vue'; 9 +import { Button, Checkbox, CheckboxGroup, DatePicker, Input, message, RadioGroup, Select, Tabs, Textarea } from 'ant-design-vue';
10 import { ImageUpload, FileUpload } from "#/components/upload"; 10 import { ImageUpload, FileUpload } from "#/components/upload";
11 import { useVbenVxeGrid } from '#/adapter/vxe-table'; 11 import { useVbenVxeGrid } from '#/adapter/vxe-table';
12 import { useStudentContactGridEditColumns } from '../data'; 12 import { useStudentContactGridEditColumns } from '../data';
@@ -45,26 +45,96 @@ const handleDelete = async (row: InfraStudentApi.StudentContact) =&gt; { @@ -45,26 +45,96 @@ const handleDelete = async (row: InfraStudentApi.StudentContact) =&gt; {
45 await gridApi.grid.remove(row); 45 await gridApi.grid.remove(row);
46 } 46 }
47 47
48 -/** 提供获取表格数据的方法供父组件调用 */  
49 -defineExpose({  
50 - getData: (): InfraStudentApi.StudentContact[] => {  
51 - const data = gridApi.grid.getData() as InfraStudentApi.StudentContact[];  
52 - const removeRecords = gridApi.grid.getRemoveRecords() as InfraStudentApi.StudentContact[];  
53 - const insertRecords = gridApi.grid.getInsertRecords() as InfraStudentApi.StudentContact[];  
54 - return data  
55 - .filter((row) => !removeRecords.some((removed) => removed.id === row.id))  
56 - .concat(insertRecords.map((row: any) => ({ ...row, id: undefined })));  
57 - },  
58 -}); 48 +/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
  49 +function getData(): InfraStudentApi.StudentContact[] {
  50 + const { fullData } = gridApi.grid.getTableData();
  51 + const inserted = new Set(gridApi.grid.getInsertRecords());
  52 + return fullData.map((row) =>
  53 + inserted.has(row) ? { ...row, id: undefined } : { ...row },
  54 + );
  55 +}
  56 +
  57 +/** 校验子表必填字段 */
  58 +function validate(): boolean {
  59 + for (const [index, row] of getData().entries()) {
  60 + if (
  61 + row.name === null ||
  62 + row.name === undefined ||
  63 + (typeof row.name === 'string' && String(row.name).trim() === '') ||
  64 + (Array.isArray(row.name) && row.name.length === 0)
  65 + ) {
  66 + message.error(`第 ${index + 1} 行:名字不能为空`);
  67 + return false;
  68 + }
  69 + if (
  70 + row.description === null ||
  71 + row.description === undefined ||
  72 + (typeof row.description === 'string' && String(row.description).trim() === '') ||
  73 + (Array.isArray(row.description) && row.description.length === 0)
  74 + ) {
  75 + message.error(`第 ${index + 1} 行:简介不能为空`);
  76 + return false;
  77 + }
  78 + if (
  79 + row.birthday === null ||
  80 + row.birthday === undefined ||
  81 + (typeof row.birthday === 'string' && String(row.birthday).trim() === '') ||
  82 + (Array.isArray(row.birthday) && row.birthday.length === 0)
  83 + ) {
  84 + message.error(`第 ${index + 1} 行:出生日期不能为空`);
  85 + return false;
  86 + }
  87 + if (
  88 + row.sex === null ||
  89 + row.sex === undefined ||
  90 + (typeof row.sex === 'string' && String(row.sex).trim() === '') ||
  91 + (Array.isArray(row.sex) && row.sex.length === 0)
  92 + ) {
  93 + message.error(`第 ${index + 1} 行:性别不能为空`);
  94 + return false;
  95 + }
  96 + if (
  97 + row.enabled === null ||
  98 + row.enabled === undefined ||
  99 + (typeof row.enabled === 'string' && String(row.enabled).trim() === '') ||
  100 + (Array.isArray(row.enabled) && row.enabled.length === 0)
  101 + ) {
  102 + message.error(`第 ${index + 1} 行:是否有效不能为空`);
  103 + return false;
  104 + }
  105 + if (
  106 + row.avatar === null ||
  107 + row.avatar === undefined ||
  108 + (typeof row.avatar === 'string' && String(row.avatar).trim() === '') ||
  109 + (Array.isArray(row.avatar) && row.avatar.length === 0)
  110 + ) {
  111 + message.error(`第 ${index + 1} 行:头像不能为空`);
  112 + return false;
  113 + }
  114 + if (
  115 + row.memo === null ||
  116 + row.memo === undefined ||
  117 + (typeof row.memo === 'string' && String(row.memo).trim() === '') ||
  118 + (Array.isArray(row.memo) && row.memo.length === 0)
  119 + ) {
  120 + message.error(`第 ${index + 1} 行:备注不能为空`);
  121 + return false;
  122 + }
  123 + }
  124 + return true;
  125 +}
  126 +
  127 +defineExpose({ getData, validate });
59 128
60 /** 监听主表的关联字段的变化,加载对应的子表数据 */ 129 /** 监听主表的关联字段的变化,加载对应的子表数据 */
61 watch( 130 watch(
62 () => props.studentId, 131 () => props.studentId,
63 async (val) => { 132 async (val) => {
  133 + await nextTick();
64 if (!val) { 134 if (!val) {
  135 + await gridApi.grid.loadData([]);
65 return; 136 return;
66 } 137 }
67 - await nextTick();  
68 await gridApi.grid.loadData(await getStudentContactListByStudentId(props.studentId!)); 138 await gridApi.grid.loadData(await getStudentContactListByStudentId(props.studentId!));
69 }, 139 },
70 { immediate: true }, 140 { immediate: true },
yudao-module-infra/src/test/resources/codegen/vben5_antd_schema_master_inner/vue/student-teacher-form
@@ -39,10 +39,11 @@ defineExpose({ @@ -39,10 +39,11 @@ defineExpose({
39 watch( 39 watch(
40 () => props.studentId, 40 () => props.studentId,
41 async (val) => { 41 async (val) => {
  42 + await nextTick();
  43 + await formApi.resetForm();
42 if (!val) { 44 if (!val) {
43 return; 45 return;
44 } 46 }
45 - await nextTick();  
46 await formApi.setValues(await getStudentTeacherByStudentId(props.studentId!)); 47 await formApi.setValues(await getStudentTeacherByStudentId(props.studentId!));
47 }, 48 },
48 { immediate: true }, 49 { immediate: true },
yudao-module-infra/src/test/resources/codegen/vben5_antd_schema_master_normal/ts/data
@@ -268,7 +268,7 @@ export function useGridColumns(): VxeTableGridOptions&lt;InfraStudentApi.Student&gt;[&#39; @@ -268,7 +268,7 @@ export function useGridColumns(): VxeTableGridOptions&lt;InfraStudentApi.Student&gt;[&#39;
268 minWidth: 120, 268 minWidth: 120,
269 slots: { default: 'sex' }, 269 slots: { default: 'sex' },
270 params: { 270 params: {
271 - options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'boolean'), 271 + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'),
272 }, 272 },
273 }, 273 },
274 { 274 {
yudao-module-infra/src/test/resources/codegen/vben5_antd_schema_master_normal/vue/form
@@ -5,7 +5,7 @@ import { computed, ref } from &#39;vue&#39;; @@ -5,7 +5,7 @@ import { computed, ref } from &#39;vue&#39;;
5 5
6 import { useVbenModal } from '@vben/common-ui'; 6 import { useVbenModal } from '@vben/common-ui';
7 7
8 -import { message } from 'ant-design-vue'; 8 +import { message, Tabs } from 'ant-design-vue';
9 9
10 import { useVbenForm } from '#/adapter/form'; 10 import { useVbenForm } from '#/adapter/form';
11 import { createStudent, getStudent, updateStudent } from '#/api/infra/demo'; 11 import { createStudent, getStudent, updateStudent } from '#/api/infra/demo';
@@ -48,7 +48,12 @@ const [Modal, modalApi] = useVbenModal({ @@ -48,7 +48,12 @@ const [Modal, modalApi] = useVbenModal({
48 return; 48 return;
49 } 49 }
50 // 校验子表单 50 // 校验子表单
51 - const studentTeacherValid = await studentTeacherFormRef.value?.validate(); 51 + const studentContactValid = await studentContactFormRef.value?.validate();
  52 + if (!studentContactValid) {
  53 + subTabsName.value = 'studentContact';
  54 + return;
  55 + }
  56 + const studentTeacherValid = await studentTeacherFormRef.value?.validate();
52 if (!studentTeacherValid) { 57 if (!studentTeacherValid) {
53 subTabsName.value = 'studentTeacher'; 58 subTabsName.value = 'studentTeacher';
54 return; 59 return;
yudao-module-infra/src/test/resources/codegen/vben5_antd_schema_master_normal/vue/student-contact-form
@@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
6 import { $t } from '#/locales'; 6 import { $t } from '#/locales';
7 7
8 import { Plus } from "@vben/icons"; 8 import { Plus } from "@vben/icons";
9 -import { Button, Tabs, Checkbox, Input, Textarea, Select,RadioGroup,CheckboxGroup, DatePicker } from 'ant-design-vue'; 9 +import { Button, Checkbox, CheckboxGroup, DatePicker, Input, message, RadioGroup, Select, Tabs, Textarea } from 'ant-design-vue';
10 import { ImageUpload, FileUpload } from "#/components/upload"; 10 import { ImageUpload, FileUpload } from "#/components/upload";
11 import { useVbenVxeGrid } from '#/adapter/vxe-table'; 11 import { useVbenVxeGrid } from '#/adapter/vxe-table';
12 import { useStudentContactGridEditColumns } from '../data'; 12 import { useStudentContactGridEditColumns } from '../data';
@@ -45,26 +45,96 @@ const handleDelete = async (row: InfraStudentApi.StudentContact) =&gt; { @@ -45,26 +45,96 @@ const handleDelete = async (row: InfraStudentApi.StudentContact) =&gt; {
45 await gridApi.grid.remove(row); 45 await gridApi.grid.remove(row);
46 } 46 }
47 47
48 -/** 提供获取表格数据的方法供父组件调用 */  
49 -defineExpose({  
50 - getData: (): InfraStudentApi.StudentContact[] => {  
51 - const data = gridApi.grid.getData() as InfraStudentApi.StudentContact[];  
52 - const removeRecords = gridApi.grid.getRemoveRecords() as InfraStudentApi.StudentContact[];  
53 - const insertRecords = gridApi.grid.getInsertRecords() as InfraStudentApi.StudentContact[];  
54 - return data  
55 - .filter((row) => !removeRecords.some((removed) => removed.id === row.id))  
56 - .concat(insertRecords.map((row: any) => ({ ...row, id: undefined })));  
57 - },  
58 -}); 48 +/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
  49 +function getData(): InfraStudentApi.StudentContact[] {
  50 + const { fullData } = gridApi.grid.getTableData();
  51 + const inserted = new Set(gridApi.grid.getInsertRecords());
  52 + return fullData.map((row) =>
  53 + inserted.has(row) ? { ...row, id: undefined } : { ...row },
  54 + );
  55 +}
  56 +
  57 +/** 校验子表必填字段 */
  58 +function validate(): boolean {
  59 + for (const [index, row] of getData().entries()) {
  60 + if (
  61 + row.name === null ||
  62 + row.name === undefined ||
  63 + (typeof row.name === 'string' && String(row.name).trim() === '') ||
  64 + (Array.isArray(row.name) && row.name.length === 0)
  65 + ) {
  66 + message.error(`第 ${index + 1} 行:名字不能为空`);
  67 + return false;
  68 + }
  69 + if (
  70 + row.description === null ||
  71 + row.description === undefined ||
  72 + (typeof row.description === 'string' && String(row.description).trim() === '') ||
  73 + (Array.isArray(row.description) && row.description.length === 0)
  74 + ) {
  75 + message.error(`第 ${index + 1} 行:简介不能为空`);
  76 + return false;
  77 + }
  78 + if (
  79 + row.birthday === null ||
  80 + row.birthday === undefined ||
  81 + (typeof row.birthday === 'string' && String(row.birthday).trim() === '') ||
  82 + (Array.isArray(row.birthday) && row.birthday.length === 0)
  83 + ) {
  84 + message.error(`第 ${index + 1} 行:出生日期不能为空`);
  85 + return false;
  86 + }
  87 + if (
  88 + row.sex === null ||
  89 + row.sex === undefined ||
  90 + (typeof row.sex === 'string' && String(row.sex).trim() === '') ||
  91 + (Array.isArray(row.sex) && row.sex.length === 0)
  92 + ) {
  93 + message.error(`第 ${index + 1} 行:性别不能为空`);
  94 + return false;
  95 + }
  96 + if (
  97 + row.enabled === null ||
  98 + row.enabled === undefined ||
  99 + (typeof row.enabled === 'string' && String(row.enabled).trim() === '') ||
  100 + (Array.isArray(row.enabled) && row.enabled.length === 0)
  101 + ) {
  102 + message.error(`第 ${index + 1} 行:是否有效不能为空`);
  103 + return false;
  104 + }
  105 + if (
  106 + row.avatar === null ||
  107 + row.avatar === undefined ||
  108 + (typeof row.avatar === 'string' && String(row.avatar).trim() === '') ||
  109 + (Array.isArray(row.avatar) && row.avatar.length === 0)
  110 + ) {
  111 + message.error(`第 ${index + 1} 行:头像不能为空`);
  112 + return false;
  113 + }
  114 + if (
  115 + row.memo === null ||
  116 + row.memo === undefined ||
  117 + (typeof row.memo === 'string' && String(row.memo).trim() === '') ||
  118 + (Array.isArray(row.memo) && row.memo.length === 0)
  119 + ) {
  120 + message.error(`第 ${index + 1} 行:备注不能为空`);
  121 + return false;
  122 + }
  123 + }
  124 + return true;
  125 +}
  126 +
  127 +defineExpose({ getData, validate });
59 128
60 /** 监听主表的关联字段的变化,加载对应的子表数据 */ 129 /** 监听主表的关联字段的变化,加载对应的子表数据 */
61 watch( 130 watch(
62 () => props.studentId, 131 () => props.studentId,
63 async (val) => { 132 async (val) => {
  133 + await nextTick();
64 if (!val) { 134 if (!val) {
  135 + await gridApi.grid.loadData([]);
65 return; 136 return;
66 } 137 }
67 - await nextTick();  
68 await gridApi.grid.loadData(await getStudentContactListByStudentId(props.studentId!)); 138 await gridApi.grid.loadData(await getStudentContactListByStudentId(props.studentId!));
69 }, 139 },
70 { immediate: true }, 140 { immediate: true },
yudao-module-infra/src/test/resources/codegen/vben5_antd_schema_master_normal/vue/student-teacher-form
@@ -39,10 +39,11 @@ defineExpose({ @@ -39,10 +39,11 @@ defineExpose({
39 watch( 39 watch(
40 () => props.studentId, 40 () => props.studentId,
41 async (val) => { 41 async (val) => {
  42 + await nextTick();
  43 + await formApi.resetForm();
42 if (!val) { 44 if (!val) {
43 return; 45 return;
44 } 46 }
45 - await nextTick();  
46 await formApi.setValues(await getStudentTeacherByStudentId(props.studentId!)); 47 await formApi.setValues(await getStudentTeacherByStudentId(props.studentId!));
47 }, 48 },
48 { immediate: true }, 49 { immediate: true },
yudao-module-infra/src/test/resources/codegen/vben5_antd_schema_one_importEnable/vue/import-form
@@ -51,6 +51,11 @@ function beforeUpload(file: FileType) { @@ -51,6 +51,11 @@ function beforeUpload(file: FileType) {
51 return false; 51 return false;
52 } 52 }
53 53
  54 +/** 移除文件 */
  55 +function handleRemove() {
  56 + formApi.setFieldValue('file', undefined);
  57 +}
  58 +
54 /** 下载模板 */ 59 /** 下载模板 */
55 async function handleDownload() { 60 async function handleDownload() {
56 const data = await importStudentTemplate(); 61 const data = await importStudentTemplate();
@@ -67,6 +72,7 @@ async function handleDownload() { @@ -67,6 +72,7 @@ async function handleDownload() {
67 :max-count="1" 72 :max-count="1"
68 accept=".xls,.xlsx" 73 accept=".xls,.xlsx"
69 :before-upload="beforeUpload" 74 :before-upload="beforeUpload"
  75 + :on-remove="handleRemove"
70 > 76 >
71 <Button type="primary"> 选择 Excel 文件 </Button> 77 <Button type="primary"> 选择 Excel 文件 </Button>
72 </Upload> 78 </Upload>
yudao-module-infra/src/test/resources/codegen/vben5_antd_schema_tree/ts/data
@@ -32,7 +32,7 @@ export function useFormSchema(): VbenFormSchema[] { @@ -32,7 +32,7 @@ export function useFormSchema(): VbenFormSchema[] {
32 id: 0, 32 id: 0,
33 name: '顶级分类', 33 name: '顶级分类',
34 }); 34 });
35 - return handleTree(data); 35 + return handleTree(data, 'id', 'parentId');
36 }, 36 },
37 labelField: 'name', 37 labelField: 'name',
38 valueField: 'id', 38 valueField: 'id',
yudao-module-infra/src/test/resources/codegen/vben5_ele_general_master_erp/vue/form
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 -import type { StudentApi } from '#/api/infra/student'; 2 +import type { StudentApi } from '#/api/infra/demo';
3 import type { FormRules } from 'element-plus'; 3 import type { FormRules } from 'element-plus';
4 4
5 import { useVbenModal } from '@vben/common-ui'; 5 import { useVbenModal } from '@vben/common-ui';
@@ -11,7 +11,7 @@ import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElSelect, El @@ -11,7 +11,7 @@ import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElSelect, El
11 11
12 import { computed, ref, reactive } from 'vue'; 12 import { computed, ref, reactive } from 'vue';
13 import { $t } from '#/locales'; 13 import { $t } from '#/locales';
14 -import { getStudent, createStudent, updateStudent } from '#/api/infra/student'; 14 +import { getStudent, createStudent, updateStudent } from '#/api/infra/demo';
15 15
16 const emit = defineEmits(['success']); 16 const emit = defineEmits(['success']);
17 17
@@ -102,56 +102,56 @@ const [Modal, modalApi] = useVbenModal({ @@ -102,56 +102,56 @@ const [Modal, modalApi] = useVbenModal({
102 102
103 <template> 103 <template>
104 <Modal :title="getTitle"> 104 <Modal :title="getTitle">
105 - <el-form 105 + <ElForm
106 ref="formRef" 106 ref="formRef"
107 :model="formData" 107 :model="formData"
108 :rules="rules" 108 :rules="rules"
109 label-width="120px" 109 label-width="120px"
110 label-position="right" 110 label-position="right"
111 > 111 >
112 - <el-form-item label="名字" prop="name">  
113 - <el-input v-model="formData.name" placeholder="请输入名字" />  
114 - </el-form-item>  
115 - <el-form-item label="简介" prop="description">  
116 - <el-input v-model="formData.description" type="textarea" placeholder="请输入简介" />  
117 - </el-form-item>  
118 - <el-form-item label="出生日期" prop="birthday">  
119 - <el-date-picker 112 + <ElFormItem label="名字" prop="name">
  113 + <ElInput v-model="formData.name" placeholder="请输入名字" />
  114 + </ElFormItem>
  115 + <ElFormItem label="简介" prop="description">
  116 + <ElInput v-model="formData.description" type="textarea" placeholder="请输入简介" />
  117 + </ElFormItem>
  118 + <ElFormItem label="出生日期" prop="birthday">
  119 + <ElDatePicker
120 v-model="formData.birthday" 120 v-model="formData.birthday"
121 value-format="x" 121 value-format="x"
122 placeholder="选择出生日期" 122 placeholder="选择出生日期"
123 /> 123 />
124 - </el-form-item>  
125 - <el-form-item label="性别" prop="sex">  
126 - <el-select v-model="formData.sex" placeholder="请选择性别">  
127 - <el-option 124 + </ElFormItem>
  125 + <ElFormItem label="性别" prop="sex">
  126 + <ElSelect v-model="formData.sex" placeholder="请选择性别">
  127 + <ElOption
128 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')" 128 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
129 :key="dict.value" 129 :key="dict.value"
130 :value="dict.value" 130 :value="dict.value"
131 :label="dict.label" 131 :label="dict.label"
132 /> 132 />
133 - </el-select>  
134 - </el-form-item>  
135 - <el-form-item label="是否有效" prop="enabled">  
136 - <el-radio-group v-model="formData.enabled">  
137 - <el-radio 133 + </ElSelect>
  134 + </ElFormItem>
  135 + <ElFormItem label="是否有效" prop="enabled">
  136 + <ElRadioGroup v-model="formData.enabled">
  137 + <ElRadio
138 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')" 138 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
139 :key="dict.value" 139 :key="dict.value"
140 - :label="dict.value" 140 + :value="dict.value"
141 > 141 >
142 {{ dict.label }} 142 {{ dict.label }}
143 - </el-radio>  
144 - </el-radio-group>  
145 - </el-form-item>  
146 - <el-form-item label="头像" prop="avatar"> 143 + </ElRadio>
  144 + </ElRadioGroup>
  145 + </ElFormItem>
  146 + <ElFormItem label="头像" prop="avatar">
147 <ImageUpload v-model="formData.avatar" /> 147 <ImageUpload v-model="formData.avatar" />
148 - </el-form-item>  
149 - <el-form-item label="附件" prop="video"> 148 + </ElFormItem>
  149 + <ElFormItem label="附件" prop="video">
150 <FileUpload v-model="formData.video" /> 150 <FileUpload v-model="formData.video" />
151 - </el-form-item>  
152 - <el-form-item label="备注" prop="memo"> 151 + </ElFormItem>
  152 + <ElFormItem label="备注" prop="memo">
153 <RichTextarea v-model="formData.memo" height="500px" /> 153 <RichTextarea v-model="formData.memo" height="500px" />
154 - </el-form-item>  
155 - </el-form> 154 + </ElFormItem>
  155 + </ElForm>
156 </Modal> 156 </Modal>
157 </template> 157 </template>
yudao-module-infra/src/test/resources/codegen/vben5_ele_general_master_erp/vue/index
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 -import type { StudentApi } from '#/api/infra/student'; 2 +import type { StudentApi } from '#/api/infra/demo';
3 3
4 import { ref, h, reactive, onMounted, nextTick } from 'vue'; 4 import { ref, h, reactive, onMounted, nextTick } from 'vue';
5 5
@@ -19,7 +19,7 @@ import { VxeColumn, VxeTable } from &#39;#/adapter/vxe-table&#39;; @@ -19,7 +19,7 @@ import { VxeColumn, VxeTable } from &#39;#/adapter/vxe-table&#39;;
19 19
20 import { $t } from '#/locales'; 20 import { $t } from '#/locales';
21 import { isEmpty } from '@vben/utils'; 21 import { isEmpty } from '@vben/utils';
22 -import { getStudentPage, deleteStudent, deleteStudentList, exportStudent } from '#/api/infra/student'; 22 +import { getStudentPage, deleteStudent, deleteStudentList, exportStudent } from '#/api/infra/demo';
23 23
24 /** 子表的列表 */ 24 /** 子表的列表 */
25 const subTabsName = ref('studentContact') 25 const subTabsName = ref('studentContact')
@@ -35,12 +35,11 @@ const total = ref(0) // 列表的总页数 @@ -35,12 +35,11 @@ const total = ref(0) // 列表的总页数
35 const queryParams = reactive({ 35 const queryParams = reactive({
36 pageNo: 1, 36 pageNo: 1,
37 pageSize: 10, 37 pageSize: 10,
38 - name: undefined,  
39 - birthday: undefined,  
40 - birthday: undefined,  
41 - sex: undefined,  
42 - enabled: undefined,  
43 - createTime: undefined, 38 + name: undefined,
  39 + birthday: undefined,
  40 + sex: undefined,
  41 + enabled: undefined,
  42 + createTime: undefined,
44 }) 43 })
45 const queryFormRef = ref() // 搜索的表单 44 const queryFormRef = ref() // 搜索的表单
46 const exportLoading = ref(false) // 导出的加载中 45 const exportLoading = ref(false) // 导出的加载中
@@ -134,7 +133,14 @@ function handleRowCheckboxChange({ @@ -134,7 +133,14 @@ function handleRowCheckboxChange({
134 async function handleExport() { 133 async function handleExport() {
135 try { 134 try {
136 exportLoading.value = true; 135 exportLoading.value = true;
137 - const data = await exportStudent(queryParams); 136 + const params = cloneDeep(queryParams) as any;
  137 + if (params.birthday && Array.isArray(params.birthday)) {
  138 + params.birthday = (params.birthday as string[]).join(',');
  139 + }
  140 + if (params.createTime && Array.isArray(params.createTime)) {
  141 + params.createTime = (params.createTime as string[]).join(',');
  142 + }
  143 + const data = await exportStudent(params);
138 downloadFileFromBlobPart({ fileName: '学生.xls', source: data }); 144 downloadFileFromBlobPart({ fileName: '学生.xls', source: data });
139 }finally { 145 }finally {
140 exportLoading.value = false; 146 exportLoading.value = false;
@@ -156,61 +162,61 @@ onMounted(() =&gt; { @@ -156,61 +162,61 @@ onMounted(() =&gt; {
156 162
157 <ContentWrap v-if="!hiddenSearchBar"> 163 <ContentWrap v-if="!hiddenSearchBar">
158 <!-- 搜索工作栏 --> 164 <!-- 搜索工作栏 -->
159 - <el-form 165 + <ElForm
160 :model="queryParams" 166 :model="queryParams"
161 ref="queryFormRef" 167 ref="queryFormRef"
162 inline 168 inline
163 > 169 >
164 - <el-form-item label="名字">  
165 - <el-input 170 + <ElFormItem label="名字">
  171 + <ElInput
166 v-model="queryParams.name" 172 v-model="queryParams.name"
167 placeholder="请输入名字" 173 placeholder="请输入名字"
168 clearable 174 clearable
169 @keyup.enter="handleQuery" 175 @keyup.enter="handleQuery"
170 class="!w-[240px]" 176 class="!w-[240px]"
171 /> 177 />
172 - </el-form-item>  
173 - <el-form-item label="出生日期">  
174 - <el-date-picker 178 + </ElFormItem>
  179 + <ElFormItem label="出生日期">
  180 + <ElDatePicker
175 v-model="queryParams.birthday" 181 v-model="queryParams.birthday"
176 value-format="YYYY-MM-DD" 182 value-format="YYYY-MM-DD"
177 placeholder="选择出生日期" 183 placeholder="选择出生日期"
178 clearable 184 clearable
179 class="!w-[240px]" 185 class="!w-[240px]"
180 /> 186 />
181 - </el-form-item>  
182 - <el-form-item label="性别">  
183 - <el-select 187 + </ElFormItem>
  188 + <ElFormItem label="性别">
  189 + <ElSelect
184 v-model="queryParams.sex" 190 v-model="queryParams.sex"
185 placeholder="请选择性别" 191 placeholder="请选择性别"
186 clearable 192 clearable
187 class="!w-[240px]" 193 class="!w-[240px]"
188 > 194 >
189 - <el-option 195 + <ElOption
190 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')" 196 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
191 :key="dict.value" 197 :key="dict.value"
192 :value="dict.value" 198 :value="dict.value"
193 :label="dict.label" 199 :label="dict.label"
194 /> 200 />
195 - </el-select>  
196 - </el-form-item>  
197 - <el-form-item label="是否有效">  
198 - <el-select 201 + </ElSelect>
  202 + </ElFormItem>
  203 + <ElFormItem label="是否有效">
  204 + <ElSelect
199 v-model="queryParams.enabled" 205 v-model="queryParams.enabled"
200 placeholder="请选择是否有效" 206 placeholder="请选择是否有效"
201 clearable 207 clearable
202 class="!w-[240px]" 208 class="!w-[240px]"
203 > 209 >
204 - <el-option 210 + <ElOption
205 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')" 211 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
206 :key="dict.value" 212 :key="dict.value"
207 :value="dict.value" 213 :value="dict.value"
208 :label="dict.label" 214 :label="dict.label"
209 /> 215 />
210 - </el-select>  
211 - </el-form-item>  
212 - <el-form-item label="创建时间">  
213 - <el-date-picker 216 + </ElSelect>
  217 + </ElFormItem>
  218 + <ElFormItem label="创建时间">
  219 + <ElDatePicker
214 v-model="queryParams.createTime" 220 v-model="queryParams.createTime"
215 type="daterange" 221 type="daterange"
216 value-format="YYYY-MM-DD" 222 value-format="YYYY-MM-DD"
@@ -219,24 +225,24 @@ onMounted(() =&gt; { @@ -219,24 +225,24 @@ onMounted(() =&gt; {
219 end-placeholder="结束日期" 225 end-placeholder="结束日期"
220 class="!w-[240px]" 226 class="!w-[240px]"
221 /> 227 />
222 - </el-form-item>  
223 - <el-form-item>  
224 - <el-button class="ml-2" @click="resetQuery"> 重置 </el-button>  
225 - <el-button class="ml-2" @click="handleQuery" type="primary"> 228 + </ElFormItem>
  229 + <ElFormItem>
  230 + <ElButton class="ml-2" @click="resetQuery"> 重置 </ElButton>
  231 + <ElButton class="ml-2" @click="handleQuery" type="primary">
226 搜索 232 搜索
227 - </el-button>  
228 - </el-form-item>  
229 - </el-form> 233 + </ElButton>
  234 + </ElFormItem>
  235 + </ElForm>
230 </ContentWrap> 236 </ContentWrap>
231 237
232 <!-- 列表 --> 238 <!-- 列表 -->
233 <ContentWrap title="学生"> 239 <ContentWrap title="学生">
234 <template #extra> 240 <template #extra>
235 - <TableToolbar 241 + <VbenVxeTableToolbar
236 ref="tableToolbarRef" 242 ref="tableToolbarRef"
237 v-model:hidden-search="hiddenSearchBar" 243 v-model:hidden-search="hiddenSearchBar"
238 > 244 >
239 - <el-button 245 + <ElButton
240 class="ml-2" 246 class="ml-2"
241 :icon="h(Plus)" 247 :icon="h(Plus)"
242 type="primary" 248 type="primary"
@@ -244,8 +250,8 @@ onMounted(() =&gt; { @@ -244,8 +250,8 @@ onMounted(() =&gt; {
244 v-access:code="['infra:student:create']" 250 v-access:code="['infra:student:create']"
245 > 251 >
246 {{ $t('ui.actionTitle.create', ['学生']) }} 252 {{ $t('ui.actionTitle.create', ['学生']) }}
247 - </el-button>  
248 - <el-button 253 + </ElButton>
  254 + <ElButton
249 :icon="h(Download)" 255 :icon="h(Download)"
250 type="primary" 256 type="primary"
251 class="ml-2" 257 class="ml-2"
@@ -254,8 +260,8 @@ onMounted(() =&gt; { @@ -254,8 +260,8 @@ onMounted(() =&gt; {
254 v-access:code="['infra:student:export']" 260 v-access:code="['infra:student:export']"
255 > 261 >
256 {{ $t('ui.actionTitle.export') }} 262 {{ $t('ui.actionTitle.export') }}
257 - </el-button>  
258 - <el-button 263 + </ElButton>
  264 + <ElButton
259 :icon="h(Trash2)" 265 :icon="h(Trash2)"
260 type="danger" 266 type="danger"
261 class="ml-2" 267 class="ml-2"
@@ -264,10 +270,10 @@ onMounted(() =&gt; { @@ -264,10 +270,10 @@ onMounted(() =&gt; {
264 v-access:code="['infra:student:delete']" 270 v-access:code="['infra:student:delete']"
265 > 271 >
266 批量删除 272 批量删除
267 - </el-button>  
268 - </TableToolbar> 273 + </ElButton>
  274 + </VbenVxeTableToolbar>
269 </template> 275 </template>
270 - <vxe-table 276 + <VxeTable
271 ref="tableRef" 277 ref="tableRef"
272 :data="list" 278 :data="list"
273 @cell-click="onCellClick" 279 @cell-click="onCellClick"
@@ -281,36 +287,36 @@ onMounted(() =&gt; { @@ -281,36 +287,36 @@ onMounted(() =&gt; {
281 @checkboxAll="handleRowCheckboxChange" 287 @checkboxAll="handleRowCheckboxChange"
282 @checkboxChange="handleRowCheckboxChange" 288 @checkboxChange="handleRowCheckboxChange"
283 > 289 >
284 - <vxe-column type="checkbox" width="40"></vxe-column>  
285 - <vxe-column field="id" title="编号" align="center" />  
286 - <vxe-column field="name" title="名字" align="center" />  
287 - <vxe-column field="description" title="简介" align="center" />  
288 - <vxe-column field="birthday" title="出生日期" align="center"> 290 + <VxeColumn type="checkbox" width="40"></VxeColumn>
  291 + <VxeColumn field="id" title="编号" align="center" />
  292 + <VxeColumn field="name" title="名字" align="center" />
  293 + <VxeColumn field="description" title="简介" align="center" />
  294 + <VxeColumn field="birthday" title="出生日期" align="center">
289 <template #default="{row}"> 295 <template #default="{row}">
290 {{formatDateTime(row.birthday)}} 296 {{formatDateTime(row.birthday)}}
291 </template> 297 </template>
292 - </vxe-column>  
293 - <vxe-column field="sex" title="性别" align="center"> 298 + </VxeColumn>
  299 + <VxeColumn field="sex" title="性别" align="center">
294 <template #default="{row}"> 300 <template #default="{row}">
295 <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" /> 301 <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" />
296 </template> 302 </template>
297 - </vxe-column>  
298 - <vxe-column field="enabled" title="是否有效" align="center"> 303 + </VxeColumn>
  304 + <VxeColumn field="enabled" title="是否有效" align="center">
299 <template #default="{row}"> 305 <template #default="{row}">
300 <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.enabled" /> 306 <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.enabled" />
301 </template> 307 </template>
302 - </vxe-column>  
303 - <vxe-column field="avatar" title="头像" align="center" />  
304 - <vxe-column field="video" title="附件" align="center" />  
305 - <vxe-column field="memo" title="备注" align="center" />  
306 - <vxe-column field="createTime" title="创建时间" align="center"> 308 + </VxeColumn>
  309 + <VxeColumn field="avatar" title="头像" align="center" />
  310 + <VxeColumn field="video" title="附件" align="center" />
  311 + <VxeColumn field="memo" title="备注" align="center" />
  312 + <VxeColumn field="createTime" title="创建时间" align="center">
307 <template #default="{row}"> 313 <template #default="{row}">
308 {{formatDateTime(row.createTime)}} 314 {{formatDateTime(row.createTime)}}
309 </template> 315 </template>
310 - </vxe-column>  
311 - <vxe-column field="operation" title="操作" align="center"> 316 + </VxeColumn>
  317 + <VxeColumn field="operation" title="操作" align="center">
312 <template #default="{row}"> 318 <template #default="{row}">
313 - <el-button 319 + <ElButton
314 size="small" 320 size="small"
315 type="primary" 321 type="primary"
316 link 322 link
@@ -318,8 +324,8 @@ onMounted(() =&gt; { @@ -318,8 +324,8 @@ onMounted(() =&gt; {
318 v-access:code="['infra:student:update']" 324 v-access:code="['infra:student:update']"
319 > 325 >
320 {{ $t('ui.actionTitle.edit') }} 326 {{ $t('ui.actionTitle.edit') }}
321 - </el-button>  
322 - <el-button 327 + </ElButton>
  328 + <ElButton
323 size="small" 329 size="small"
324 type="danger" 330 type="danger"
325 link 331 link
@@ -328,13 +334,13 @@ onMounted(() =&gt; { @@ -328,13 +334,13 @@ onMounted(() =&gt; {
328 v-access:code="['infra:student:delete']" 334 v-access:code="['infra:student:delete']"
329 > 335 >
330 {{ $t('ui.actionTitle.delete') }} 336 {{ $t('ui.actionTitle.delete') }}
331 - </el-button> 337 + </ElButton>
332 </template> 338 </template>
333 - </vxe-column>  
334 - </vxe-table> 339 + </VxeColumn>
  340 + </VxeTable>
335 <!-- 分页 --> 341 <!-- 分页 -->
336 <div class="mt-2 flex justify-end"> 342 <div class="mt-2 flex justify-end">
337 - <el-pagination 343 + <ElPagination
338 :total="total" 344 :total="total"
339 v-model:current-page="queryParams.pageNo" 345 v-model:current-page="queryParams.pageNo"
340 v-model:page-size="queryParams.pageSize" 346 v-model:page-size="queryParams.pageSize"
@@ -347,14 +353,14 @@ onMounted(() =&gt; { @@ -347,14 +353,14 @@ onMounted(() =&gt; {
347 </ContentWrap> 353 </ContentWrap>
348 <ContentWrap> 354 <ContentWrap>
349 <!-- 子表的表单 --> 355 <!-- 子表的表单 -->
350 - <el-tabs v-model="subTabsName">  
351 - <el-tab-pane name="studentContact" label="学生联系人"> 356 + <ElTabs v-model="subTabsName">
  357 + <ElTabPane name="studentContact" label="学生联系人">
352 <StudentContactList :student-id="selectStudent?.id" /> 358 <StudentContactList :student-id="selectStudent?.id" />
353 - </el-tab-pane>  
354 - <el-tab-pane name="studentTeacher" label="学生班主任"> 359 + </ElTabPane>
  360 + <ElTabPane name="studentTeacher" label="学生班主任">
355 <StudentTeacherList :student-id="selectStudent?.id" /> 361 <StudentTeacherList :student-id="selectStudent?.id" />
356 - </el-tab-pane>  
357 - </el-tabs> 362 + </ElTabPane>
  363 + </ElTabs>
358 </ContentWrap> 364 </ContentWrap>
359 </Page> 365 </Page>
360 </template> 366 </template>
yudao-module-infra/src/test/resources/codegen/vben5_ele_general_master_erp/vue/student-contact-form
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import type { StudentApi } from '#/api/infra/student'; 2 + import type { StudentApi } from '#/api/infra/demo';
3 import type { FormRules } from 'element-plus'; 3 import type { FormRules } from 'element-plus';
4 4
5 import { computed, ref, reactive } from 'vue'; 5 import { computed, ref, reactive } from 'vue';
@@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
13 13
14 import { $t } from '#/locales'; 14 import { $t } from '#/locales';
15 15
16 - import { getStudentContact, createStudentContact, updateStudentContact } from '#/api/infra/student'; 16 + import { getStudentContact, createStudentContact, updateStudentContact } from '#/api/infra/demo';
17 17
18 const emit = defineEmits(['success']); 18 const emit = defineEmits(['success']);
19 const getTitle = computed(() => { 19 const getTitle = computed(() => {
@@ -107,56 +107,56 @@ @@ -107,56 +107,56 @@
107 107
108 <template> 108 <template>
109 <Modal :title="getTitle"> 109 <Modal :title="getTitle">
110 - <el-form 110 + <ElForm
111 ref="formRef" 111 ref="formRef"
112 :model="formData" 112 :model="formData"
113 :rules="rules" 113 :rules="rules"
114 label-width="120px" 114 label-width="120px"
115 label-position="right" 115 label-position="right"
116 > 116 >
117 - <el-form-item label="名字" prop="name">  
118 - <el-input v-model="formData.name" placeholder="请输入名字" />  
119 - </el-form-item>  
120 - <el-form-item label="简介" prop="description">  
121 - <el-input v-model="formData.description" type="textarea" placeholder="请输入简介" />  
122 - </el-form-item>  
123 - <el-form-item label="出生日期" prop="birthday">  
124 - <el-date-picker 117 + <ElFormItem label="名字" prop="name">
  118 + <ElInput v-model="formData.name" placeholder="请输入名字" />
  119 + </ElFormItem>
  120 + <ElFormItem label="简介" prop="description">
  121 + <ElInput v-model="formData.description" type="textarea" placeholder="请输入简介" />
  122 + </ElFormItem>
  123 + <ElFormItem label="出生日期" prop="birthday">
  124 + <ElDatePicker
125 v-model="formData.birthday" 125 v-model="formData.birthday"
126 value-format="x" 126 value-format="x"
127 placeholder="选择出生日期" 127 placeholder="选择出生日期"
128 /> 128 />
129 - </el-form-item>  
130 - <el-form-item label="性别" prop="sex">  
131 - <el-select v-model="formData.sex" placeholder="请选择性别">  
132 - <el-option 129 + </ElFormItem>
  130 + <ElFormItem label="性别" prop="sex">
  131 + <ElSelect v-model="formData.sex" placeholder="请选择性别">
  132 + <ElOption
133 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')" 133 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
134 :key="dict.value" 134 :key="dict.value"
135 :value="dict.value" 135 :value="dict.value"
136 :label="dict.label" 136 :label="dict.label"
137 /> 137 />
138 - </el-select>  
139 - </el-form-item>  
140 - <el-form-item label="是否有效" prop="enabled">  
141 - <el-radio-group v-model="formData.enabled">  
142 - <el-radio 138 + </ElSelect>
  139 + </ElFormItem>
  140 + <ElFormItem label="是否有效" prop="enabled">
  141 + <ElRadioGroup v-model="formData.enabled">
  142 + <ElRadio
143 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')" 143 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
144 :key="dict.value" 144 :key="dict.value"
145 - :label="dict.value" 145 + :value="dict.value"
146 > 146 >
147 {{ dict.label }} 147 {{ dict.label }}
148 - </el-radio>  
149 - </el-radio-group>  
150 - </el-form-item>  
151 - <el-form-item label="头像" prop="avatar"> 148 + </ElRadio>
  149 + </ElRadioGroup>
  150 + </ElFormItem>
  151 + <ElFormItem label="头像" prop="avatar">
152 <ImageUpload v-model="formData.avatar" /> 152 <ImageUpload v-model="formData.avatar" />
153 - </el-form-item>  
154 - <el-form-item label="附件" prop="video"> 153 + </ElFormItem>
  154 + <ElFormItem label="附件" prop="video">
155 <FileUpload v-model="formData.video" /> 155 <FileUpload v-model="formData.video" />
156 - </el-form-item>  
157 - <el-form-item label="备注" prop="memo"> 156 + </ElFormItem>
  157 + <ElFormItem label="备注" prop="memo">
158 <RichTextarea v-model="formData.memo" height="500px" /> 158 <RichTextarea v-model="formData.memo" height="500px" />
159 - </el-form-item>  
160 - </el-form> 159 + </ElFormItem>
  160 + </ElForm>
161 </Modal> 161 </Modal>
162 </template> 162 </template>
yudao-module-infra/src/test/resources/codegen/vben5_ele_general_master_erp/vue/student-contact-list
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import type { StudentApi } from '#/api/infra/student'; 2 + import type { StudentApi } from '#/api/infra/demo';
3 import type { VxeTableInstance } from '#/adapter/vxe-table'; 3 import type { VxeTableInstance } from '#/adapter/vxe-table';
4 4
5 import { reactive,ref, h, nextTick,watch,onMounted } from 'vue'; 5 import { reactive,ref, h, nextTick,watch,onMounted } from 'vue';
6 6
7 import { DICT_TYPE } from '@vben/constants'; 7 import { DICT_TYPE } from '@vben/constants';
8 import { getDictOptions } from '@vben/hooks'; 8 import { getDictOptions } from '@vben/hooks';
  9 +import { formatDateTime } from '@vben/utils';
9 10
10 import { DictTag } from '#/components/dict-tag'; 11 import { DictTag } from '#/components/dict-tag';
11 import { getRangePickerDefaultProps } from '#/utils'; 12 import { getRangePickerDefaultProps } from '#/utils';
@@ -21,8 +22,8 @@ @@ -21,8 +22,8 @@
21 import { Plus, Trash2 } from '@vben/icons'; 22 import { Plus, Trash2 } from '@vben/icons';
22 import { $t } from '#/locales'; 23 import { $t } from '#/locales';
23 24
24 - import { deleteStudentContact, deleteStudentContactList, getStudentContactPage } from '#/api/infra/student';  
25 - import { isEmpty } from '@vben/utils'; 25 + import { deleteStudentContact, deleteStudentContactList, getStudentContactPage } from '#/api/infra/demo';
  26 + import { cloneDeep, isEmpty } from '@vben/utils';
26 27
27 const props = defineProps<{ 28 const props = defineProps<{
28 studentId?: number // 学生编号(主表的关联字段) 29 studentId?: number // 学生编号(主表的关联字段)
@@ -159,61 +160,61 @@ onMounted(() =&gt; { @@ -159,61 +160,61 @@ onMounted(() =&gt; {
159 <div class="h-[600px]"> 160 <div class="h-[600px]">
160 <ContentWrap v-if="!hiddenSearchBar"> 161 <ContentWrap v-if="!hiddenSearchBar">
161 <!-- 搜索工作栏 --> 162 <!-- 搜索工作栏 -->
162 - <el-form 163 + <ElForm
163 :model="queryParams" 164 :model="queryParams"
164 ref="queryFormRef" 165 ref="queryFormRef"
165 inline 166 inline
166 > 167 >
167 - <el-form-item label="名字">  
168 - <el-input 168 + <ElFormItem label="名字">
  169 + <ElInput
169 v-model="queryParams.name" 170 v-model="queryParams.name"
170 placeholder="请输入名字" 171 placeholder="请输入名字"
171 clearable 172 clearable
172 @keyup.enter="handleQuery" 173 @keyup.enter="handleQuery"
173 class="!w-[240px]" 174 class="!w-[240px]"
174 /> 175 />
175 - </el-form-item>  
176 - <el-form-item label="出生日期">  
177 - <el-date-picker 176 + </ElFormItem>
  177 + <ElFormItem label="出生日期">
  178 + <ElDatePicker
178 v-model="queryParams.birthday" 179 v-model="queryParams.birthday"
179 value-format="YYYY-MM-DD" 180 value-format="YYYY-MM-DD"
180 placeholder="选择出生日期" 181 placeholder="选择出生日期"
181 clearable 182 clearable
182 class="!w-[240px]" 183 class="!w-[240px]"
183 /> 184 />
184 - </el-form-item>  
185 - <el-form-item label="性别">  
186 - <el-select 185 + </ElFormItem>
  186 + <ElFormItem label="性别">
  187 + <ElSelect
187 v-model="queryParams.sex" 188 v-model="queryParams.sex"
188 placeholder="请选择性别" 189 placeholder="请选择性别"
189 clearable 190 clearable
190 class="!w-[240px]" 191 class="!w-[240px]"
191 > 192 >
192 - <el-option 193 + <ElOption
193 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')" 194 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
194 :key="dict.value" 195 :key="dict.value"
195 :value="dict.value" 196 :value="dict.value"
196 :label="dict.label" 197 :label="dict.label"
197 /> 198 />
198 - </el-select>  
199 - </el-form-item>  
200 - <el-form-item label="是否有效">  
201 - <el-select 199 + </ElSelect>
  200 + </ElFormItem>
  201 + <ElFormItem label="是否有效">
  202 + <ElSelect
202 v-model="queryParams.enabled" 203 v-model="queryParams.enabled"
203 placeholder="请选择是否有效" 204 placeholder="请选择是否有效"
204 clearable 205 clearable
205 class="!w-[240px]" 206 class="!w-[240px]"
206 > 207 >
207 - <el-option 208 + <ElOption
208 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')" 209 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
209 :key="dict.value" 210 :key="dict.value"
210 :value="dict.value" 211 :value="dict.value"
211 :label="dict.label" 212 :label="dict.label"
212 /> 213 />
213 - </el-select>  
214 - </el-form-item>  
215 - <el-form-item label="创建时间">  
216 - <el-date-picker 214 + </ElSelect>
  215 + </ElFormItem>
  216 + <ElFormItem label="创建时间">
  217 + <ElDatePicker
217 v-model="queryParams.createTime" 218 v-model="queryParams.createTime"
218 type="daterange" 219 type="daterange"
219 value-format="YYYY-MM-DD" 220 value-format="YYYY-MM-DD"
@@ -222,14 +223,14 @@ onMounted(() =&gt; { @@ -222,14 +223,14 @@ onMounted(() =&gt; {
222 end-placeholder="结束日期" 223 end-placeholder="结束日期"
223 class="!w-[240px]" 224 class="!w-[240px]"
224 /> 225 />
225 - </el-form-item>  
226 - <el-form-item>  
227 - <el-button class="ml-2" @click="resetQuery"> 重置 </el-button>  
228 - <el-button class="ml-2" @click="handleQuery" type="primary"> 226 + </ElFormItem>
  227 + <ElFormItem>
  228 + <ElButton class="ml-2" @click="resetQuery"> 重置 </ElButton>
  229 + <ElButton class="ml-2" @click="handleQuery" type="primary">
229 搜索 230 搜索
230 - </el-button>  
231 - </el-form-item>  
232 - </el-form> 231 + </ElButton>
  232 + </ElFormItem>
  233 + </ElForm>
233 </ContentWrap> 234 </ContentWrap>
234 235
235 <!-- 列表 --> 236 <!-- 列表 -->
@@ -239,7 +240,7 @@ onMounted(() =&gt; { @@ -239,7 +240,7 @@ onMounted(() =&gt; {
239 ref="tableToolbarRef" 240 ref="tableToolbarRef"
240 v-model:hidden-search="hiddenSearchBar" 241 v-model:hidden-search="hiddenSearchBar"
241 > 242 >
242 - <el-button 243 + <ElButton
243 class="ml-2" 244 class="ml-2"
244 :icon="h(Plus)" 245 :icon="h(Plus)"
245 type="primary" 246 type="primary"
@@ -247,8 +248,8 @@ onMounted(() =&gt; { @@ -247,8 +248,8 @@ onMounted(() =&gt; {
247 v-access:code="['infra:student:create']" 248 v-access:code="['infra:student:create']"
248 > 249 >
249 {{ $t('ui.actionTitle.create', ['学生']) }} 250 {{ $t('ui.actionTitle.create', ['学生']) }}
250 - </el-button>  
251 - <el-button 251 + </ElButton>
  252 + <ElButton
252 :icon="h(Trash2)" 253 :icon="h(Trash2)"
253 type="danger" 254 type="danger"
254 class="ml-2" 255 class="ml-2"
@@ -257,10 +258,10 @@ onMounted(() =&gt; { @@ -257,10 +258,10 @@ onMounted(() =&gt; {
257 v-access:code="['infra:student:delete']" 258 v-access:code="['infra:student:delete']"
258 > 259 >
259 批量删除 260 批量删除
260 - </el-button>  
261 - </TableToolbar> 261 + </ElButton>
  262 + </VbenVxeTableToolbar>
262 </template> 263 </template>
263 - <vxe-table 264 + <VxeTable
264 ref="tableRef" 265 ref="tableRef"
265 :data="list" 266 :data="list"
266 show-overflow 267 show-overflow
@@ -268,37 +269,37 @@ onMounted(() =&gt; { @@ -268,37 +269,37 @@ onMounted(() =&gt; {
268 @checkboxAll="handleRowCheckboxChange" 269 @checkboxAll="handleRowCheckboxChange"
269 @checkboxChange="handleRowCheckboxChange" 270 @checkboxChange="handleRowCheckboxChange"
270 > 271 >
271 - <vxe-column type="checkbox" width="40"></vxe-column>  
272 - <vxe-column field="id" title="编号" align="center" />  
273 - <vxe-column field="studentId" title="学生编号" align="center" />  
274 - <vxe-column field="name" title="名字" align="center" />  
275 - <vxe-column field="description" title="简介" align="center" />  
276 - <vxe-column field="birthday" title="出生日期" align="center"> 272 + <VxeColumn type="checkbox" width="40"></VxeColumn>
  273 + <VxeColumn field="id" title="编号" align="center" />
  274 + <VxeColumn field="studentId" title="学生编号" align="center" />
  275 + <VxeColumn field="name" title="名字" align="center" />
  276 + <VxeColumn field="description" title="简介" align="center" />
  277 + <VxeColumn field="birthday" title="出生日期" align="center">
277 <template #default="{row}"> 278 <template #default="{row}">
278 {{formatDateTime(row.birthday)}} 279 {{formatDateTime(row.birthday)}}
279 </template> 280 </template>
280 - </vxe-column>  
281 - <vxe-column field="sex" title="性别" align="center"> 281 + </VxeColumn>
  282 + <VxeColumn field="sex" title="性别" align="center">
282 <template #default="{row}"> 283 <template #default="{row}">
283 <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" /> 284 <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" />
284 </template> 285 </template>
285 - </vxe-column>  
286 - <vxe-column field="enabled" title="是否有效" align="center"> 286 + </VxeColumn>
  287 + <VxeColumn field="enabled" title="是否有效" align="center">
287 <template #default="{row}"> 288 <template #default="{row}">
288 <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.enabled" /> 289 <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.enabled" />
289 </template> 290 </template>
290 - </vxe-column>  
291 - <vxe-column field="avatar" title="头像" align="center" />  
292 - <vxe-column field="video" title="附件" align="center" />  
293 - <vxe-column field="memo" title="备注" align="center" />  
294 - <vxe-column field="createTime" title="创建时间" align="center"> 291 + </VxeColumn>
  292 + <VxeColumn field="avatar" title="头像" align="center" />
  293 + <VxeColumn field="video" title="附件" align="center" />
  294 + <VxeColumn field="memo" title="备注" align="center" />
  295 + <VxeColumn field="createTime" title="创建时间" align="center">
295 <template #default="{row}"> 296 <template #default="{row}">
296 {{formatDateTime(row.createTime)}} 297 {{formatDateTime(row.createTime)}}
297 </template> 298 </template>
298 - </vxe-column>  
299 - <vxe-column field="operation" title="操作" align="center"> 299 + </VxeColumn>
  300 + <VxeColumn field="operation" title="操作" align="center">
300 <template #default="{row}"> 301 <template #default="{row}">
301 - <el-button 302 + <ElButton
302 size="small" 303 size="small"
303 type="primary" 304 type="primary"
304 link 305 link
@@ -306,8 +307,8 @@ onMounted(() =&gt; { @@ -306,8 +307,8 @@ onMounted(() =&gt; {
306 v-access:code="['infra:student:update']" 307 v-access:code="['infra:student:update']"
307 > 308 >
308 {{ $t('ui.actionTitle.edit') }} 309 {{ $t('ui.actionTitle.edit') }}
309 - </el-button>  
310 - <el-button 310 + </ElButton>
  311 + <ElButton
311 size="small" 312 size="small"
312 type="danger" 313 type="danger"
313 link 314 link
@@ -316,13 +317,13 @@ onMounted(() =&gt; { @@ -316,13 +317,13 @@ onMounted(() =&gt; {
316 v-access:code="['infra:student:delete']" 317 v-access:code="['infra:student:delete']"
317 > 318 >
318 {{ $t('ui.actionTitle.delete') }} 319 {{ $t('ui.actionTitle.delete') }}
319 - </el-button> 320 + </ElButton>
320 </template> 321 </template>
321 - </vxe-column>  
322 - </vxe-table> 322 + </VxeColumn>
  323 + </VxeTable>
323 <!-- 分页 --> 324 <!-- 分页 -->
324 <div class="mt-2 flex justify-end"> 325 <div class="mt-2 flex justify-end">
325 - <el-pagination 326 + <ElPagination
326 :total="total" 327 :total="total"
327 v-model:current-page="queryParams.pageNo" 328 v-model:current-page="queryParams.pageNo"
328 v-model:page-size="queryParams.pageSize" 329 v-model:page-size="queryParams.pageSize"
yudao-module-infra/src/test/resources/codegen/vben5_ele_general_master_erp/vue/student-teacher-form
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import type { StudentApi } from '#/api/infra/student'; 2 + import type { StudentApi } from '#/api/infra/demo';
3 import type { FormRules } from 'element-plus'; 3 import type { FormRules } from 'element-plus';
4 4
5 import { computed, ref, reactive } from 'vue'; 5 import { computed, ref, reactive } from 'vue';
@@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
13 13
14 import { $t } from '#/locales'; 14 import { $t } from '#/locales';
15 15
16 - import { getStudentTeacher, createStudentTeacher, updateStudentTeacher } from '#/api/infra/student'; 16 + import { getStudentTeacher, createStudentTeacher, updateStudentTeacher } from '#/api/infra/demo';
17 17
18 const emit = defineEmits(['success']); 18 const emit = defineEmits(['success']);
19 const getTitle = computed(() => { 19 const getTitle = computed(() => {
@@ -107,56 +107,56 @@ @@ -107,56 +107,56 @@
107 107
108 <template> 108 <template>
109 <Modal :title="getTitle"> 109 <Modal :title="getTitle">
110 - <el-form 110 + <ElForm
111 ref="formRef" 111 ref="formRef"
112 :model="formData" 112 :model="formData"
113 :rules="rules" 113 :rules="rules"
114 label-width="120px" 114 label-width="120px"
115 label-position="right" 115 label-position="right"
116 > 116 >
117 - <el-form-item label="名字" prop="name">  
118 - <el-input v-model="formData.name" placeholder="请输入名字" />  
119 - </el-form-item>  
120 - <el-form-item label="简介" prop="description">  
121 - <el-input v-model="formData.description" type="textarea" placeholder="请输入简介" />  
122 - </el-form-item>  
123 - <el-form-item label="出生日期" prop="birthday">  
124 - <el-date-picker 117 + <ElFormItem label="名字" prop="name">
  118 + <ElInput v-model="formData.name" placeholder="请输入名字" />
  119 + </ElFormItem>
  120 + <ElFormItem label="简介" prop="description">
  121 + <ElInput v-model="formData.description" type="textarea" placeholder="请输入简介" />
  122 + </ElFormItem>
  123 + <ElFormItem label="出生日期" prop="birthday">
  124 + <ElDatePicker
125 v-model="formData.birthday" 125 v-model="formData.birthday"
126 value-format="x" 126 value-format="x"
127 placeholder="选择出生日期" 127 placeholder="选择出生日期"
128 /> 128 />
129 - </el-form-item>  
130 - <el-form-item label="性别" prop="sex">  
131 - <el-select v-model="formData.sex" placeholder="请选择性别">  
132 - <el-option 129 + </ElFormItem>
  130 + <ElFormItem label="性别" prop="sex">
  131 + <ElSelect v-model="formData.sex" placeholder="请选择性别">
  132 + <ElOption
133 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')" 133 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
134 :key="dict.value" 134 :key="dict.value"
135 :value="dict.value" 135 :value="dict.value"
136 :label="dict.label" 136 :label="dict.label"
137 /> 137 />
138 - </el-select>  
139 - </el-form-item>  
140 - <el-form-item label="是否有效" prop="enabled">  
141 - <el-radio-group v-model="formData.enabled">  
142 - <el-radio 138 + </ElSelect>
  139 + </ElFormItem>
  140 + <ElFormItem label="是否有效" prop="enabled">
  141 + <ElRadioGroup v-model="formData.enabled">
  142 + <ElRadio
143 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')" 143 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
144 :key="dict.value" 144 :key="dict.value"
145 - :label="dict.value" 145 + :value="dict.value"
146 > 146 >
147 {{ dict.label }} 147 {{ dict.label }}
148 - </el-radio>  
149 - </el-radio-group>  
150 - </el-form-item>  
151 - <el-form-item label="头像" prop="avatar"> 148 + </ElRadio>
  149 + </ElRadioGroup>
  150 + </ElFormItem>
  151 + <ElFormItem label="头像" prop="avatar">
152 <ImageUpload v-model="formData.avatar" /> 152 <ImageUpload v-model="formData.avatar" />
153 - </el-form-item>  
154 - <el-form-item label="附件" prop="video"> 153 + </ElFormItem>
  154 + <ElFormItem label="附件" prop="video">
155 <FileUpload v-model="formData.video" /> 155 <FileUpload v-model="formData.video" />
156 - </el-form-item>  
157 - <el-form-item label="备注" prop="memo"> 156 + </ElFormItem>
  157 + <ElFormItem label="备注" prop="memo">
158 <RichTextarea v-model="formData.memo" height="500px" /> 158 <RichTextarea v-model="formData.memo" height="500px" />
159 - </el-form-item>  
160 - </el-form> 159 + </ElFormItem>
  160 + </ElForm>
161 </Modal> 161 </Modal>
162 </template> 162 </template>
yudao-module-infra/src/test/resources/codegen/vben5_ele_general_master_erp/vue/student-teacher-list
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import type { StudentApi } from '#/api/infra/student'; 2 + import type { StudentApi } from '#/api/infra/demo';
3 import type { VxeTableInstance } from '#/adapter/vxe-table'; 3 import type { VxeTableInstance } from '#/adapter/vxe-table';
4 4
5 import { reactive,ref, h, nextTick,watch,onMounted } from 'vue'; 5 import { reactive,ref, h, nextTick,watch,onMounted } from 'vue';
6 6
7 import { DICT_TYPE } from '@vben/constants'; 7 import { DICT_TYPE } from '@vben/constants';
8 import { getDictOptions } from '@vben/hooks'; 8 import { getDictOptions } from '@vben/hooks';
  9 +import { formatDateTime } from '@vben/utils';
9 10
10 import { DictTag } from '#/components/dict-tag'; 11 import { DictTag } from '#/components/dict-tag';
11 import { getRangePickerDefaultProps } from '#/utils'; 12 import { getRangePickerDefaultProps } from '#/utils';
@@ -21,8 +22,8 @@ @@ -21,8 +22,8 @@
21 import { Plus, Trash2 } from '@vben/icons'; 22 import { Plus, Trash2 } from '@vben/icons';
22 import { $t } from '#/locales'; 23 import { $t } from '#/locales';
23 24
24 - import { deleteStudentTeacher, deleteStudentTeacherList, getStudentTeacherPage } from '#/api/infra/student';  
25 - import { isEmpty } from '@vben/utils'; 25 + import { deleteStudentTeacher, deleteStudentTeacherList, getStudentTeacherPage } from '#/api/infra/demo';
  26 + import { cloneDeep, isEmpty } from '@vben/utils';
26 27
27 const props = defineProps<{ 28 const props = defineProps<{
28 studentId?: number // 学生编号(主表的关联字段) 29 studentId?: number // 学生编号(主表的关联字段)
@@ -159,61 +160,61 @@ onMounted(() =&gt; { @@ -159,61 +160,61 @@ onMounted(() =&gt; {
159 <div class="h-[600px]"> 160 <div class="h-[600px]">
160 <ContentWrap v-if="!hiddenSearchBar"> 161 <ContentWrap v-if="!hiddenSearchBar">
161 <!-- 搜索工作栏 --> 162 <!-- 搜索工作栏 -->
162 - <el-form 163 + <ElForm
163 :model="queryParams" 164 :model="queryParams"
164 ref="queryFormRef" 165 ref="queryFormRef"
165 inline 166 inline
166 > 167 >
167 - <el-form-item label="名字">  
168 - <el-input 168 + <ElFormItem label="名字">
  169 + <ElInput
169 v-model="queryParams.name" 170 v-model="queryParams.name"
170 placeholder="请输入名字" 171 placeholder="请输入名字"
171 clearable 172 clearable
172 @keyup.enter="handleQuery" 173 @keyup.enter="handleQuery"
173 class="!w-[240px]" 174 class="!w-[240px]"
174 /> 175 />
175 - </el-form-item>  
176 - <el-form-item label="出生日期">  
177 - <el-date-picker 176 + </ElFormItem>
  177 + <ElFormItem label="出生日期">
  178 + <ElDatePicker
178 v-model="queryParams.birthday" 179 v-model="queryParams.birthday"
179 value-format="YYYY-MM-DD" 180 value-format="YYYY-MM-DD"
180 placeholder="选择出生日期" 181 placeholder="选择出生日期"
181 clearable 182 clearable
182 class="!w-[240px]" 183 class="!w-[240px]"
183 /> 184 />
184 - </el-form-item>  
185 - <el-form-item label="性别">  
186 - <el-select 185 + </ElFormItem>
  186 + <ElFormItem label="性别">
  187 + <ElSelect
187 v-model="queryParams.sex" 188 v-model="queryParams.sex"
188 placeholder="请选择性别" 189 placeholder="请选择性别"
189 clearable 190 clearable
190 class="!w-[240px]" 191 class="!w-[240px]"
191 > 192 >
192 - <el-option 193 + <ElOption
193 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')" 194 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
194 :key="dict.value" 195 :key="dict.value"
195 :value="dict.value" 196 :value="dict.value"
196 :label="dict.label" 197 :label="dict.label"
197 /> 198 />
198 - </el-select>  
199 - </el-form-item>  
200 - <el-form-item label="是否有效">  
201 - <el-select 199 + </ElSelect>
  200 + </ElFormItem>
  201 + <ElFormItem label="是否有效">
  202 + <ElSelect
202 v-model="queryParams.enabled" 203 v-model="queryParams.enabled"
203 placeholder="请选择是否有效" 204 placeholder="请选择是否有效"
204 clearable 205 clearable
205 class="!w-[240px]" 206 class="!w-[240px]"
206 > 207 >
207 - <el-option 208 + <ElOption
208 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')" 209 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
209 :key="dict.value" 210 :key="dict.value"
210 :value="dict.value" 211 :value="dict.value"
211 :label="dict.label" 212 :label="dict.label"
212 /> 213 />
213 - </el-select>  
214 - </el-form-item>  
215 - <el-form-item label="创建时间">  
216 - <el-date-picker 214 + </ElSelect>
  215 + </ElFormItem>
  216 + <ElFormItem label="创建时间">
  217 + <ElDatePicker
217 v-model="queryParams.createTime" 218 v-model="queryParams.createTime"
218 type="daterange" 219 type="daterange"
219 value-format="YYYY-MM-DD" 220 value-format="YYYY-MM-DD"
@@ -222,14 +223,14 @@ onMounted(() =&gt; { @@ -222,14 +223,14 @@ onMounted(() =&gt; {
222 end-placeholder="结束日期" 223 end-placeholder="结束日期"
223 class="!w-[240px]" 224 class="!w-[240px]"
224 /> 225 />
225 - </el-form-item>  
226 - <el-form-item>  
227 - <el-button class="ml-2" @click="resetQuery"> 重置 </el-button>  
228 - <el-button class="ml-2" @click="handleQuery" type="primary"> 226 + </ElFormItem>
  227 + <ElFormItem>
  228 + <ElButton class="ml-2" @click="resetQuery"> 重置 </ElButton>
  229 + <ElButton class="ml-2" @click="handleQuery" type="primary">
229 搜索 230 搜索
230 - </el-button>  
231 - </el-form-item>  
232 - </el-form> 231 + </ElButton>
  232 + </ElFormItem>
  233 + </ElForm>
233 </ContentWrap> 234 </ContentWrap>
234 235
235 <!-- 列表 --> 236 <!-- 列表 -->
@@ -239,7 +240,7 @@ onMounted(() =&gt; { @@ -239,7 +240,7 @@ onMounted(() =&gt; {
239 ref="tableToolbarRef" 240 ref="tableToolbarRef"
240 v-model:hidden-search="hiddenSearchBar" 241 v-model:hidden-search="hiddenSearchBar"
241 > 242 >
242 - <el-button 243 + <ElButton
243 class="ml-2" 244 class="ml-2"
244 :icon="h(Plus)" 245 :icon="h(Plus)"
245 type="primary" 246 type="primary"
@@ -247,8 +248,8 @@ onMounted(() =&gt; { @@ -247,8 +248,8 @@ onMounted(() =&gt; {
247 v-access:code="['infra:student:create']" 248 v-access:code="['infra:student:create']"
248 > 249 >
249 {{ $t('ui.actionTitle.create', ['学生']) }} 250 {{ $t('ui.actionTitle.create', ['学生']) }}
250 - </el-button>  
251 - <el-button 251 + </ElButton>
  252 + <ElButton
252 :icon="h(Trash2)" 253 :icon="h(Trash2)"
253 type="danger" 254 type="danger"
254 class="ml-2" 255 class="ml-2"
@@ -257,10 +258,10 @@ onMounted(() =&gt; { @@ -257,10 +258,10 @@ onMounted(() =&gt; {
257 v-access:code="['infra:student:delete']" 258 v-access:code="['infra:student:delete']"
258 > 259 >
259 批量删除 260 批量删除
260 - </el-button>  
261 - </TableToolbar> 261 + </ElButton>
  262 + </VbenVxeTableToolbar>
262 </template> 263 </template>
263 - <vxe-table 264 + <VxeTable
264 ref="tableRef" 265 ref="tableRef"
265 :data="list" 266 :data="list"
266 show-overflow 267 show-overflow
@@ -268,37 +269,37 @@ onMounted(() =&gt; { @@ -268,37 +269,37 @@ onMounted(() =&gt; {
268 @checkboxAll="handleRowCheckboxChange" 269 @checkboxAll="handleRowCheckboxChange"
269 @checkboxChange="handleRowCheckboxChange" 270 @checkboxChange="handleRowCheckboxChange"
270 > 271 >
271 - <vxe-column type="checkbox" width="40"></vxe-column>  
272 - <vxe-column field="id" title="编号" align="center" />  
273 - <vxe-column field="studentId" title="学生编号" align="center" />  
274 - <vxe-column field="name" title="名字" align="center" />  
275 - <vxe-column field="description" title="简介" align="center" />  
276 - <vxe-column field="birthday" title="出生日期" align="center"> 272 + <VxeColumn type="checkbox" width="40"></VxeColumn>
  273 + <VxeColumn field="id" title="编号" align="center" />
  274 + <VxeColumn field="studentId" title="学生编号" align="center" />
  275 + <VxeColumn field="name" title="名字" align="center" />
  276 + <VxeColumn field="description" title="简介" align="center" />
  277 + <VxeColumn field="birthday" title="出生日期" align="center">
277 <template #default="{row}"> 278 <template #default="{row}">
278 {{formatDateTime(row.birthday)}} 279 {{formatDateTime(row.birthday)}}
279 </template> 280 </template>
280 - </vxe-column>  
281 - <vxe-column field="sex" title="性别" align="center"> 281 + </VxeColumn>
  282 + <VxeColumn field="sex" title="性别" align="center">
282 <template #default="{row}"> 283 <template #default="{row}">
283 <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" /> 284 <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" />
284 </template> 285 </template>
285 - </vxe-column>  
286 - <vxe-column field="enabled" title="是否有效" align="center"> 286 + </VxeColumn>
  287 + <VxeColumn field="enabled" title="是否有效" align="center">
287 <template #default="{row}"> 288 <template #default="{row}">
288 <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.enabled" /> 289 <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.enabled" />
289 </template> 290 </template>
290 - </vxe-column>  
291 - <vxe-column field="avatar" title="头像" align="center" />  
292 - <vxe-column field="video" title="附件" align="center" />  
293 - <vxe-column field="memo" title="备注" align="center" />  
294 - <vxe-column field="createTime" title="创建时间" align="center"> 291 + </VxeColumn>
  292 + <VxeColumn field="avatar" title="头像" align="center" />
  293 + <VxeColumn field="video" title="附件" align="center" />
  294 + <VxeColumn field="memo" title="备注" align="center" />
  295 + <VxeColumn field="createTime" title="创建时间" align="center">
295 <template #default="{row}"> 296 <template #default="{row}">
296 {{formatDateTime(row.createTime)}} 297 {{formatDateTime(row.createTime)}}
297 </template> 298 </template>
298 - </vxe-column>  
299 - <vxe-column field="operation" title="操作" align="center"> 299 + </VxeColumn>
  300 + <VxeColumn field="operation" title="操作" align="center">
300 <template #default="{row}"> 301 <template #default="{row}">
301 - <el-button 302 + <ElButton
302 size="small" 303 size="small"
303 type="primary" 304 type="primary"
304 link 305 link
@@ -306,8 +307,8 @@ onMounted(() =&gt; { @@ -306,8 +307,8 @@ onMounted(() =&gt; {
306 v-access:code="['infra:student:update']" 307 v-access:code="['infra:student:update']"
307 > 308 >
308 {{ $t('ui.actionTitle.edit') }} 309 {{ $t('ui.actionTitle.edit') }}
309 - </el-button>  
310 - <el-button 310 + </ElButton>
  311 + <ElButton
311 size="small" 312 size="small"
312 type="danger" 313 type="danger"
313 link 314 link
@@ -316,13 +317,13 @@ onMounted(() =&gt; { @@ -316,13 +317,13 @@ onMounted(() =&gt; {
316 v-access:code="['infra:student:delete']" 317 v-access:code="['infra:student:delete']"
317 > 318 >
318 {{ $t('ui.actionTitle.delete') }} 319 {{ $t('ui.actionTitle.delete') }}
319 - </el-button> 320 + </ElButton>
320 </template> 321 </template>
321 - </vxe-column>  
322 - </vxe-table> 322 + </VxeColumn>
  323 + </VxeTable>
323 <!-- 分页 --> 324 <!-- 分页 -->
324 <div class="mt-2 flex justify-end"> 325 <div class="mt-2 flex justify-end">
325 - <el-pagination 326 + <ElPagination
326 :total="total" 327 :total="total"
327 v-model:current-page="queryParams.pageNo" 328 v-model:current-page="queryParams.pageNo"
328 v-model:page-size="queryParams.pageSize" 329 v-model:page-size="queryParams.pageSize"
yudao-module-infra/src/test/resources/codegen/vben5_ele_general_master_inner/vue/form
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 -import type { StudentApi } from '#/api/infra/student'; 2 +import type { StudentApi } from '#/api/infra/demo';
3 import type { FormRules } from 'element-plus'; 3 import type { FormRules } from 'element-plus';
4 4
5 import { useVbenModal } from '@vben/common-ui'; 5 import { useVbenModal } from '@vben/common-ui';
@@ -13,7 +13,7 @@ import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElSelect, El @@ -13,7 +13,7 @@ import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElSelect, El
13 13
14 import { computed, ref, reactive } from 'vue'; 14 import { computed, ref, reactive } from 'vue';
15 import { $t } from '#/locales'; 15 import { $t } from '#/locales';
16 -import { getStudent, createStudent, updateStudent } from '#/api/infra/student'; 16 +import { getStudent, createStudent, updateStudent } from '#/api/infra/demo';
17 17
18 const emit = defineEmits(['success']); 18 const emit = defineEmits(['success']);
19 19
@@ -71,7 +71,11 @@ const [Modal, modalApi] = useVbenModal({ @@ -71,7 +71,11 @@ const [Modal, modalApi] = useVbenModal({
71 async onConfirm() { 71 async onConfirm() {
72 await formRef.value?.validate(); 72 await formRef.value?.validate();
73 // 校验子表单 73 // 校验子表单
74 - try { 74 + if (!studentContactFormRef.value?.validate()) {
  75 + subTabsName.value = 'studentContact';
  76 + return;
  77 + }
  78 + try {
75 await studentTeacherFormRef.value?.validate() 79 await studentTeacherFormRef.value?.validate()
76 } catch (e) { 80 } catch (e) {
77 subTabsName.value = 'studentTeacher' 81 subTabsName.value = 'studentTeacher'
@@ -118,65 +122,65 @@ const [Modal, modalApi] = useVbenModal({ @@ -118,65 +122,65 @@ const [Modal, modalApi] = useVbenModal({
118 122
119 <template> 123 <template>
120 <Modal :title="getTitle"> 124 <Modal :title="getTitle">
121 - <el-form 125 + <ElForm
122 ref="formRef" 126 ref="formRef"
123 :model="formData" 127 :model="formData"
124 :rules="rules" 128 :rules="rules"
125 label-width="120px" 129 label-width="120px"
126 label-position="right" 130 label-position="right"
127 > 131 >
128 - <el-form-item label="名字" prop="name">  
129 - <el-input v-model="formData.name" placeholder="请输入名字" />  
130 - </el-form-item>  
131 - <el-form-item label="简介" prop="description">  
132 - <el-input v-model="formData.description" type="textarea" placeholder="请输入简介" />  
133 - </el-form-item>  
134 - <el-form-item label="出生日期" prop="birthday">  
135 - <el-date-picker 132 + <ElFormItem label="名字" prop="name">
  133 + <ElInput v-model="formData.name" placeholder="请输入名字" />
  134 + </ElFormItem>
  135 + <ElFormItem label="简介" prop="description">
  136 + <ElInput v-model="formData.description" type="textarea" placeholder="请输入简介" />
  137 + </ElFormItem>
  138 + <ElFormItem label="出生日期" prop="birthday">
  139 + <ElDatePicker
136 v-model="formData.birthday" 140 v-model="formData.birthday"
137 value-format="x" 141 value-format="x"
138 placeholder="选择出生日期" 142 placeholder="选择出生日期"
139 /> 143 />
140 - </el-form-item>  
141 - <el-form-item label="性别" prop="sex">  
142 - <el-select v-model="formData.sex" placeholder="请选择性别">  
143 - <el-option 144 + </ElFormItem>
  145 + <ElFormItem label="性别" prop="sex">
  146 + <ElSelect v-model="formData.sex" placeholder="请选择性别">
  147 + <ElOption
144 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')" 148 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
145 :key="dict.value" 149 :key="dict.value"
146 :value="dict.value" 150 :value="dict.value"
147 :label="dict.label" 151 :label="dict.label"
148 /> 152 />
149 - </el-select>  
150 - </el-form-item>  
151 - <el-form-item label="是否有效" prop="enabled">  
152 - <el-radio-group v-model="formData.enabled">  
153 - <el-radio 153 + </ElSelect>
  154 + </ElFormItem>
  155 + <ElFormItem label="是否有效" prop="enabled">
  156 + <ElRadioGroup v-model="formData.enabled">
  157 + <ElRadio
154 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')" 158 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
155 :key="dict.value" 159 :key="dict.value"
156 - :label="dict.value" 160 + :value="dict.value"
157 > 161 >
158 {{ dict.label }} 162 {{ dict.label }}
159 - </el-radio>  
160 - </el-radio-group>  
161 - </el-form-item>  
162 - <el-form-item label="头像" prop="avatar"> 163 + </ElRadio>
  164 + </ElRadioGroup>
  165 + </ElFormItem>
  166 + <ElFormItem label="头像" prop="avatar">
163 <ImageUpload v-model="formData.avatar" /> 167 <ImageUpload v-model="formData.avatar" />
164 - </el-form-item>  
165 - <el-form-item label="附件" prop="video"> 168 + </ElFormItem>
  169 + <ElFormItem label="附件" prop="video">
166 <FileUpload v-model="formData.video" /> 170 <FileUpload v-model="formData.video" />
167 - </el-form-item>  
168 - <el-form-item label="备注" prop="memo"> 171 + </ElFormItem>
  172 + <ElFormItem label="备注" prop="memo">
169 <RichTextarea v-model="formData.memo" height="500px" /> 173 <RichTextarea v-model="formData.memo" height="500px" />
170 - </el-form-item>  
171 - </el-form> 174 + </ElFormItem>
  175 + </ElForm>
172 <!-- 子表的表单 --> 176 <!-- 子表的表单 -->
173 - <el-tabs v-model="subTabsName">  
174 - <el-tab-pane name="studentContact" label="学生联系人"> 177 + <ElTabs v-model="subTabsName">
  178 + <ElTabPane name="studentContact" label="学生联系人">
175 <StudentContactForm ref="studentContactFormRef" :student-id="formData?.id" /> 179 <StudentContactForm ref="studentContactFormRef" :student-id="formData?.id" />
176 - </el-tab-pane>  
177 - <el-tab-pane name="studentTeacher" label="学生班主任"> 180 + </ElTabPane>
  181 + <ElTabPane name="studentTeacher" label="学生班主任">
178 <StudentTeacherForm ref="studentTeacherFormRef" :student-id="formData?.id" /> 182 <StudentTeacherForm ref="studentTeacherFormRef" :student-id="formData?.id" />
179 - </el-tab-pane>  
180 - </el-tabs> 183 + </ElTabPane>
  184 + </ElTabs>
181 </Modal> 185 </Modal>
182 </template> 186 </template>
yudao-module-infra/src/test/resources/codegen/vben5_ele_general_master_inner/vue/index
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 -import type { StudentApi } from '#/api/infra/student'; 2 +import type { StudentApi } from '#/api/infra/demo';
3 3
4 import { ref, h, reactive, onMounted, nextTick } from 'vue'; 4 import { ref, h, reactive, onMounted, nextTick } from 'vue';
5 5
@@ -19,7 +19,7 @@ import { VxeColumn, VxeTable } from &#39;#/adapter/vxe-table&#39;; @@ -19,7 +19,7 @@ import { VxeColumn, VxeTable } from &#39;#/adapter/vxe-table&#39;;
19 19
20 import { $t } from '#/locales'; 20 import { $t } from '#/locales';
21 import { isEmpty } from '@vben/utils'; 21 import { isEmpty } from '@vben/utils';
22 -import { getStudentPage, deleteStudent, deleteStudentList, exportStudent } from '#/api/infra/student'; 22 +import { getStudentPage, deleteStudent, deleteStudentList, exportStudent } from '#/api/infra/demo';
23 23
24 /** 子表的列表 */ 24 /** 子表的列表 */
25 const subTabsName = ref('studentContact') 25 const subTabsName = ref('studentContact')
@@ -31,12 +31,11 @@ const total = ref(0) // 列表的总页数 @@ -31,12 +31,11 @@ const total = ref(0) // 列表的总页数
31 const queryParams = reactive({ 31 const queryParams = reactive({
32 pageNo: 1, 32 pageNo: 1,
33 pageSize: 10, 33 pageSize: 10,
34 - name: undefined,  
35 - birthday: undefined,  
36 - birthday: undefined,  
37 - sex: undefined,  
38 - enabled: undefined,  
39 - createTime: undefined, 34 + name: undefined,
  35 + birthday: undefined,
  36 + sex: undefined,
  37 + enabled: undefined,
  38 + createTime: undefined,
40 }) 39 })
41 const queryFormRef = ref() // 搜索的表单 40 const queryFormRef = ref() // 搜索的表单
42 const exportLoading = ref(false) // 导出的加载中 41 const exportLoading = ref(false) // 导出的加载中
@@ -130,7 +129,14 @@ function handleRowCheckboxChange({ @@ -130,7 +129,14 @@ function handleRowCheckboxChange({
130 async function handleExport() { 129 async function handleExport() {
131 try { 130 try {
132 exportLoading.value = true; 131 exportLoading.value = true;
133 - const data = await exportStudent(queryParams); 132 + const params = cloneDeep(queryParams) as any;
  133 + if (params.birthday && Array.isArray(params.birthday)) {
  134 + params.birthday = (params.birthday as string[]).join(',');
  135 + }
  136 + if (params.createTime && Array.isArray(params.createTime)) {
  137 + params.createTime = (params.createTime as string[]).join(',');
  138 + }
  139 + const data = await exportStudent(params);
134 downloadFileFromBlobPart({ fileName: '学生.xls', source: data }); 140 downloadFileFromBlobPart({ fileName: '学生.xls', source: data });
135 }finally { 141 }finally {
136 exportLoading.value = false; 142 exportLoading.value = false;
@@ -152,61 +158,61 @@ onMounted(() =&gt; { @@ -152,61 +158,61 @@ onMounted(() =&gt; {
152 158
153 <ContentWrap v-if="!hiddenSearchBar"> 159 <ContentWrap v-if="!hiddenSearchBar">
154 <!-- 搜索工作栏 --> 160 <!-- 搜索工作栏 -->
155 - <el-form 161 + <ElForm
156 :model="queryParams" 162 :model="queryParams"
157 ref="queryFormRef" 163 ref="queryFormRef"
158 inline 164 inline
159 > 165 >
160 - <el-form-item label="名字">  
161 - <el-input 166 + <ElFormItem label="名字">
  167 + <ElInput
162 v-model="queryParams.name" 168 v-model="queryParams.name"
163 placeholder="请输入名字" 169 placeholder="请输入名字"
164 clearable 170 clearable
165 @keyup.enter="handleQuery" 171 @keyup.enter="handleQuery"
166 class="!w-[240px]" 172 class="!w-[240px]"
167 /> 173 />
168 - </el-form-item>  
169 - <el-form-item label="出生日期">  
170 - <el-date-picker 174 + </ElFormItem>
  175 + <ElFormItem label="出生日期">
  176 + <ElDatePicker
171 v-model="queryParams.birthday" 177 v-model="queryParams.birthday"
172 value-format="YYYY-MM-DD" 178 value-format="YYYY-MM-DD"
173 placeholder="选择出生日期" 179 placeholder="选择出生日期"
174 clearable 180 clearable
175 class="!w-[240px]" 181 class="!w-[240px]"
176 /> 182 />
177 - </el-form-item>  
178 - <el-form-item label="性别">  
179 - <el-select 183 + </ElFormItem>
  184 + <ElFormItem label="性别">
  185 + <ElSelect
180 v-model="queryParams.sex" 186 v-model="queryParams.sex"
181 placeholder="请选择性别" 187 placeholder="请选择性别"
182 clearable 188 clearable
183 class="!w-[240px]" 189 class="!w-[240px]"
184 > 190 >
185 - <el-option 191 + <ElOption
186 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')" 192 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
187 :key="dict.value" 193 :key="dict.value"
188 :value="dict.value" 194 :value="dict.value"
189 :label="dict.label" 195 :label="dict.label"
190 /> 196 />
191 - </el-select>  
192 - </el-form-item>  
193 - <el-form-item label="是否有效">  
194 - <el-select 197 + </ElSelect>
  198 + </ElFormItem>
  199 + <ElFormItem label="是否有效">
  200 + <ElSelect
195 v-model="queryParams.enabled" 201 v-model="queryParams.enabled"
196 placeholder="请选择是否有效" 202 placeholder="请选择是否有效"
197 clearable 203 clearable
198 class="!w-[240px]" 204 class="!w-[240px]"
199 > 205 >
200 - <el-option 206 + <ElOption
201 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')" 207 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
202 :key="dict.value" 208 :key="dict.value"
203 :value="dict.value" 209 :value="dict.value"
204 :label="dict.label" 210 :label="dict.label"
205 /> 211 />
206 - </el-select>  
207 - </el-form-item>  
208 - <el-form-item label="创建时间">  
209 - <el-date-picker 212 + </ElSelect>
  213 + </ElFormItem>
  214 + <ElFormItem label="创建时间">
  215 + <ElDatePicker
210 v-model="queryParams.createTime" 216 v-model="queryParams.createTime"
211 type="daterange" 217 type="daterange"
212 value-format="YYYY-MM-DD" 218 value-format="YYYY-MM-DD"
@@ -215,24 +221,24 @@ onMounted(() =&gt; { @@ -215,24 +221,24 @@ onMounted(() =&gt; {
215 end-placeholder="结束日期" 221 end-placeholder="结束日期"
216 class="!w-[240px]" 222 class="!w-[240px]"
217 /> 223 />
218 - </el-form-item>  
219 - <el-form-item>  
220 - <el-button class="ml-2" @click="resetQuery"> 重置 </el-button>  
221 - <el-button class="ml-2" @click="handleQuery" type="primary"> 224 + </ElFormItem>
  225 + <ElFormItem>
  226 + <ElButton class="ml-2" @click="resetQuery"> 重置 </ElButton>
  227 + <ElButton class="ml-2" @click="handleQuery" type="primary">
222 搜索 228 搜索
223 - </el-button>  
224 - </el-form-item>  
225 - </el-form> 229 + </ElButton>
  230 + </ElFormItem>
  231 + </ElForm>
226 </ContentWrap> 232 </ContentWrap>
227 233
228 <!-- 列表 --> 234 <!-- 列表 -->
229 <ContentWrap title="学生"> 235 <ContentWrap title="学生">
230 <template #extra> 236 <template #extra>
231 - <TableToolbar 237 + <VbenVxeTableToolbar
232 ref="tableToolbarRef" 238 ref="tableToolbarRef"
233 v-model:hidden-search="hiddenSearchBar" 239 v-model:hidden-search="hiddenSearchBar"
234 > 240 >
235 - <el-button 241 + <ElButton
236 class="ml-2" 242 class="ml-2"
237 :icon="h(Plus)" 243 :icon="h(Plus)"
238 type="primary" 244 type="primary"
@@ -240,8 +246,8 @@ onMounted(() =&gt; { @@ -240,8 +246,8 @@ onMounted(() =&gt; {
240 v-access:code="['infra:student:create']" 246 v-access:code="['infra:student:create']"
241 > 247 >
242 {{ $t('ui.actionTitle.create', ['学生']) }} 248 {{ $t('ui.actionTitle.create', ['学生']) }}
243 - </el-button>  
244 - <el-button 249 + </ElButton>
  250 + <ElButton
245 :icon="h(Download)" 251 :icon="h(Download)"
246 type="primary" 252 type="primary"
247 class="ml-2" 253 class="ml-2"
@@ -250,8 +256,8 @@ onMounted(() =&gt; { @@ -250,8 +256,8 @@ onMounted(() =&gt; {
250 v-access:code="['infra:student:export']" 256 v-access:code="['infra:student:export']"
251 > 257 >
252 {{ $t('ui.actionTitle.export') }} 258 {{ $t('ui.actionTitle.export') }}
253 - </el-button>  
254 - <el-button 259 + </ElButton>
  260 + <ElButton
255 :icon="h(Trash2)" 261 :icon="h(Trash2)"
256 type="danger" 262 type="danger"
257 class="ml-2" 263 class="ml-2"
@@ -260,10 +266,10 @@ onMounted(() =&gt; { @@ -260,10 +266,10 @@ onMounted(() =&gt; {
260 v-access:code="['infra:student:delete']" 266 v-access:code="['infra:student:delete']"
261 > 267 >
262 批量删除 268 批量删除
263 - </el-button>  
264 - </TableToolbar> 269 + </ElButton>
  270 + </VbenVxeTableToolbar>
265 </template> 271 </template>
266 - <vxe-table 272 + <VxeTable
267 ref="tableRef" 273 ref="tableRef"
268 :data="list" 274 :data="list"
269 show-overflow 275 show-overflow
@@ -271,50 +277,50 @@ onMounted(() =&gt; { @@ -271,50 +277,50 @@ onMounted(() =&gt; {
271 @checkboxAll="handleRowCheckboxChange" 277 @checkboxAll="handleRowCheckboxChange"
272 @checkboxChange="handleRowCheckboxChange" 278 @checkboxChange="handleRowCheckboxChange"
273 > 279 >
274 - <vxe-column type="checkbox" width="40"></vxe-column> 280 + <VxeColumn type="checkbox" width="40"></VxeColumn>
275 <!-- 子表的列表 --> 281 <!-- 子表的列表 -->
276 - <vxe-column type="expand" width="60"> 282 + <VxeColumn type="expand" width="60">
277 <template #content="{ row }"> 283 <template #content="{ row }">
278 <!-- 子表的表单 --> 284 <!-- 子表的表单 -->
279 - <el-tabs v-model="subTabsName" class="mx-8">  
280 - <el-tab-pane name="studentContact" label="学生联系人"> 285 + <ElTabs v-model="subTabsName" class="mx-8">
  286 + <ElTabPane name="studentContact" label="学生联系人">
281 <StudentContactList :student-id="row?.id" /> 287 <StudentContactList :student-id="row?.id" />
282 - </el-tab-pane>  
283 - <el-tab-pane name="studentTeacher" label="学生班主任"> 288 + </ElTabPane>
  289 + <ElTabPane name="studentTeacher" label="学生班主任">
284 <StudentTeacherList :student-id="row?.id" /> 290 <StudentTeacherList :student-id="row?.id" />
285 - </el-tab-pane>  
286 - </el-tabs> 291 + </ElTabPane>
  292 + </ElTabs>
287 </template> 293 </template>
288 - </vxe-column>  
289 - <vxe-column field="id" title="编号" align="center" />  
290 - <vxe-column field="name" title="名字" align="center" />  
291 - <vxe-column field="description" title="简介" align="center" />  
292 - <vxe-column field="birthday" title="出生日期" align="center"> 294 + </VxeColumn>
  295 + <VxeColumn field="id" title="编号" align="center" />
  296 + <VxeColumn field="name" title="名字" align="center" />
  297 + <VxeColumn field="description" title="简介" align="center" />
  298 + <VxeColumn field="birthday" title="出生日期" align="center">
293 <template #default="{row}"> 299 <template #default="{row}">
294 {{formatDateTime(row.birthday)}} 300 {{formatDateTime(row.birthday)}}
295 </template> 301 </template>
296 - </vxe-column>  
297 - <vxe-column field="sex" title="性别" align="center"> 302 + </VxeColumn>
  303 + <VxeColumn field="sex" title="性别" align="center">
298 <template #default="{row}"> 304 <template #default="{row}">
299 <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" /> 305 <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" />
300 </template> 306 </template>
301 - </vxe-column>  
302 - <vxe-column field="enabled" title="是否有效" align="center"> 307 + </VxeColumn>
  308 + <VxeColumn field="enabled" title="是否有效" align="center">
303 <template #default="{row}"> 309 <template #default="{row}">
304 <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.enabled" /> 310 <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.enabled" />
305 </template> 311 </template>
306 - </vxe-column>  
307 - <vxe-column field="avatar" title="头像" align="center" />  
308 - <vxe-column field="video" title="附件" align="center" />  
309 - <vxe-column field="memo" title="备注" align="center" />  
310 - <vxe-column field="createTime" title="创建时间" align="center"> 312 + </VxeColumn>
  313 + <VxeColumn field="avatar" title="头像" align="center" />
  314 + <VxeColumn field="video" title="附件" align="center" />
  315 + <VxeColumn field="memo" title="备注" align="center" />
  316 + <VxeColumn field="createTime" title="创建时间" align="center">
311 <template #default="{row}"> 317 <template #default="{row}">
312 {{formatDateTime(row.createTime)}} 318 {{formatDateTime(row.createTime)}}
313 </template> 319 </template>
314 - </vxe-column>  
315 - <vxe-column field="operation" title="操作" align="center"> 320 + </VxeColumn>
  321 + <VxeColumn field="operation" title="操作" align="center">
316 <template #default="{row}"> 322 <template #default="{row}">
317 - <el-button 323 + <ElButton
318 size="small" 324 size="small"
319 type="primary" 325 type="primary"
320 link 326 link
@@ -322,8 +328,8 @@ onMounted(() =&gt; { @@ -322,8 +328,8 @@ onMounted(() =&gt; {
322 v-access:code="['infra:student:update']" 328 v-access:code="['infra:student:update']"
323 > 329 >
324 {{ $t('ui.actionTitle.edit') }} 330 {{ $t('ui.actionTitle.edit') }}
325 - </el-button>  
326 - <el-button 331 + </ElButton>
  332 + <ElButton
327 size="small" 333 size="small"
328 type="danger" 334 type="danger"
329 link 335 link
@@ -332,13 +338,13 @@ onMounted(() =&gt; { @@ -332,13 +338,13 @@ onMounted(() =&gt; {
332 v-access:code="['infra:student:delete']" 338 v-access:code="['infra:student:delete']"
333 > 339 >
334 {{ $t('ui.actionTitle.delete') }} 340 {{ $t('ui.actionTitle.delete') }}
335 - </el-button> 341 + </ElButton>
336 </template> 342 </template>
337 - </vxe-column>  
338 - </vxe-table> 343 + </VxeColumn>
  344 + </VxeTable>
339 <!-- 分页 --> 345 <!-- 分页 -->
340 <div class="mt-2 flex justify-end"> 346 <div class="mt-2 flex justify-end">
341 - <el-pagination 347 + <ElPagination
342 :total="total" 348 :total="total"
343 v-model:current-page="queryParams.pageNo" 349 v-model:current-page="queryParams.pageNo"
344 v-model:page-size="queryParams.pageSize" 350 v-model:page-size="queryParams.pageSize"
yudao-module-infra/src/test/resources/codegen/vben5_ele_general_master_inner/vue/student-contact-form
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 -import type { StudentApi } from '#/api/infra/student'; 2 +import type { VxeTableInstance } from '#/adapter/vxe-table';
  3 +import type { StudentApi } from '#/api/infra/demo';
3 4
4 -import { computed, ref, reactive, h, onMounted,watch,nextTick } from 'vue'; 5 +import { ref, watch } from 'vue';
5 6
6 import { DICT_TYPE } from '@vben/constants'; 7 import { DICT_TYPE } from '@vben/constants';
7 import { getDictOptions } from '@vben/hooks'; 8 import { getDictOptions } from '@vben/hooks';
  9 +import { Plus } from '@vben/icons';
8 10
9 -import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElButton, ElSelect, ElOption, ElRadioGroup, ElRadio, ElCheckboxGroup, ElCheckbox, ElDatePicker } from 'element-plus';  
10 -import { $t } from '#/locales'; 11 +import {
  12 + ElButton,
  13 + ElDatePicker,
  14 + ElInput,
  15 + ElMessage,
  16 + ElOption,
  17 + ElRadio,
  18 + ElRadioGroup,
  19 + ElSelect,
  20 +} from 'element-plus';
11 21
12 - import type { VxeTableInstance } from '#/adapter/vxe-table';  
13 -import { Plus } from "@vben/icons";  
14 -import { VxeColumn, VxeTable } from '#/adapter/vxe-table';  
15 -import { getStudentContactListByStudentId } from '#/api/infra/student'; 22 + import { VxeColumn, VxeTable } from '#/adapter/vxe-table';
  23 +import { getStudentContactListByStudentId } from '#/api/infra/demo';
  24 +import { $t } from '#/locales';
16 25
17 const props = defineProps<{ 26 const props = defineProps<{
18 studentId?: number // 学生编号(主表的关联字段) 27 studentId?: number // 学生编号(主表的关联字段)
@@ -30,24 +39,96 @@ async function onDelete(row: StudentApi.StudentContact) { @@ -30,24 +39,96 @@ async function onDelete(row: StudentApi.StudentContact) {
30 await tableRef.value?.remove(row); 39 await tableRef.value?.remove(row);
31 } 40 }
32 41
33 -/** 提供获取表格数据的方法供父组件调用 */  
34 -defineExpose({  
35 - getData: (): StudentApi.StudentContact[] => {  
36 - const data = list.value as StudentApi.StudentContact[];  
37 - const removeRecords = tableRef.value?.getRemoveRecords() as StudentApi.StudentContact[];  
38 - const insertRecords = tableRef.value?.getInsertRecords() as StudentApi.StudentContact[];  
39 - return [  
40 - ...data.filter(  
41 - (row) => !removeRecords.some((removed) => removed.id === row.id),  
42 - ),  
43 - ...insertRecords.map((row: any) => ({ ...row, id: undefined })),  
44 - },  
45 -}); 42 +/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
  43 +function getData(): StudentApi.StudentContact[] {
  44 + const table = tableRef.value;
  45 + if (!table) {
  46 + return [];
  47 + }
  48 + const { fullData } = table.getTableData();
  49 + const inserted = new Set(table.getInsertRecords());
  50 + return fullData.map((row) =>
  51 + inserted.has(row) ? { ...row, id: undefined } : { ...row },
  52 + );
  53 +}
  54 +
  55 +/** 校验子表必填字段 */
  56 +function validate(): boolean {
  57 + for (const [index, row] of getData().entries()) {
  58 + if (
  59 + row.name === null ||
  60 + row.name === undefined ||
  61 + (typeof row.name === 'string' && String(row.name).trim() === '') ||
  62 + (Array.isArray(row.name) && row.name.length === 0)
  63 + ) {
  64 + ElMessage.error(`第 ${index + 1} 行:名字不能为空`);
  65 + return false;
  66 + }
  67 + if (
  68 + row.description === null ||
  69 + row.description === undefined ||
  70 + (typeof row.description === 'string' && String(row.description).trim() === '') ||
  71 + (Array.isArray(row.description) && row.description.length === 0)
  72 + ) {
  73 + ElMessage.error(`第 ${index + 1} 行:简介不能为空`);
  74 + return false;
  75 + }
  76 + if (
  77 + row.birthday === null ||
  78 + row.birthday === undefined ||
  79 + (typeof row.birthday === 'string' && String(row.birthday).trim() === '') ||
  80 + (Array.isArray(row.birthday) && row.birthday.length === 0)
  81 + ) {
  82 + ElMessage.error(`第 ${index + 1} 行:出生日期不能为空`);
  83 + return false;
  84 + }
  85 + if (
  86 + row.sex === null ||
  87 + row.sex === undefined ||
  88 + (typeof row.sex === 'string' && String(row.sex).trim() === '') ||
  89 + (Array.isArray(row.sex) && row.sex.length === 0)
  90 + ) {
  91 + ElMessage.error(`第 ${index + 1} 行:性别不能为空`);
  92 + return false;
  93 + }
  94 + if (
  95 + row.enabled === null ||
  96 + row.enabled === undefined ||
  97 + (typeof row.enabled === 'string' && String(row.enabled).trim() === '') ||
  98 + (Array.isArray(row.enabled) && row.enabled.length === 0)
  99 + ) {
  100 + ElMessage.error(`第 ${index + 1} 行:是否有效不能为空`);
  101 + return false;
  102 + }
  103 + if (
  104 + row.avatar === null ||
  105 + row.avatar === undefined ||
  106 + (typeof row.avatar === 'string' && String(row.avatar).trim() === '') ||
  107 + (Array.isArray(row.avatar) && row.avatar.length === 0)
  108 + ) {
  109 + ElMessage.error(`第 ${index + 1} 行:头像不能为空`);
  110 + return false;
  111 + }
  112 + if (
  113 + row.memo === null ||
  114 + row.memo === undefined ||
  115 + (typeof row.memo === 'string' && String(row.memo).trim() === '') ||
  116 + (Array.isArray(row.memo) && row.memo.length === 0)
  117 + ) {
  118 + ElMessage.error(`第 ${index + 1} 行:备注不能为空`);
  119 + return false;
  120 + }
  121 + }
  122 + return true;
  123 +}
  124 +
  125 +defineExpose({ getData, validate });
46 126
47 /** 监听主表的关联字段的变化,加载对应的子表数据 */ 127 /** 监听主表的关联字段的变化,加载对应的子表数据 */
48 watch( 128 watch(
49 () => props.studentId, 129 () => props.studentId,
50 async (val) => { 130 async (val) => {
  131 + list.value = [];
51 if (!val) { 132 if (!val) {
52 return; 133 return;
53 } 134 }
@@ -58,70 +139,70 @@ watch( @@ -58,70 +139,70 @@ watch(
58 </script> 139 </script>
59 140
60 <template> 141 <template>
61 - <vxe-table ref="tableRef" :data="list" show-overflow class="mx-4">  
62 - <vxe-column field="name" title="名字" align="center"> 142 + <VxeTable ref="tableRef" :data="list" show-overflow class="mx-4">
  143 + <VxeColumn field="name" title="名字" align="center">
63 <template #default="{row}"> 144 <template #default="{row}">
64 - <el-input v-model="row.name" /> 145 + <ElInput v-model="row.name" />
65 </template> 146 </template>
66 - </vxe-column>  
67 - <vxe-column field="description" title="简介" align="center"> 147 + </VxeColumn>
  148 + <VxeColumn field="description" title="简介" align="center">
68 <template #default="{row}"> 149 <template #default="{row}">
69 - <el-input v-model="row.description" type="textarea" /> 150 + <ElInput v-model="row.description" type="textarea" />
70 </template> 151 </template>
71 - </vxe-column>  
72 - <vxe-column field="birthday" title="出生日期" align="center"> 152 + </VxeColumn>
  153 + <VxeColumn field="birthday" title="出生日期" align="center">
73 <template #default="{row}"> 154 <template #default="{row}">
74 - <el-date-picker 155 + <ElDatePicker
75 v-model="row.birthday" 156 v-model="row.birthday"
76 type="datetime" 157 type="datetime"
77 value-format="x" 158 value-format="x"
78 format="YYYY-MM-DD HH:mm:ss" 159 format="YYYY-MM-DD HH:mm:ss"
79 /> 160 />
80 </template> 161 </template>
81 - </vxe-column>  
82 - <vxe-column field="sex" title="性别" align="center"> 162 + </VxeColumn>
  163 + <VxeColumn field="sex" title="性别" align="center">
83 <template #default="{row}"> 164 <template #default="{row}">
84 - <el-select v-model="row.sex" placeholder="请选择性别">  
85 - <el-option  
86 - v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number')" 165 + <ElSelect v-model="row.sex" placeholder="请选择性别">
  166 + <ElOption
  167 + v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
87 :key="dict.value" 168 :key="dict.value"
88 :value="dict.value" 169 :value="dict.value"
89 :label="dict.label" 170 :label="dict.label"
90 /> 171 />
91 - </el-select> 172 + </ElSelect>
92 </template> 173 </template>
93 - </vxe-column>  
94 - <vxe-column field="enabled" title="是否有效" align="center"> 174 + </VxeColumn>
  175 + <VxeColumn field="enabled" title="是否有效" align="center">
95 <template #default="{row}"> 176 <template #default="{row}">
96 - <el-radio-group v-model="row.enabled">  
97 - <el-radio  
98 - v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'boolean')" 177 + <ElRadioGroup v-model="row.enabled">
  178 + <ElRadio
  179 + v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
99 :key="dict.value" 180 :key="dict.value"
100 - :label="dict.value" 181 + :value="dict.value"
101 > 182 >
102 {{ dict.label }} 183 {{ dict.label }}
103 - </el-radio>  
104 - </el-radio-group> 184 + </ElRadio>
  185 + </ElRadioGroup>
105 </template> 186 </template>
106 - </vxe-column>  
107 - <vxe-column field="avatar" title="头像" align="center"> 187 + </VxeColumn>
  188 + <VxeColumn field="avatar" title="头像" align="center">
108 <template #default="{row}"> 189 <template #default="{row}">
109 <ImageUpload v-model="row.avatar" /> 190 <ImageUpload v-model="row.avatar" />
110 </template> 191 </template>
111 - </vxe-column>  
112 - <vxe-column field="video" title="附件" align="center"> 192 + </VxeColumn>
  193 + <VxeColumn field="video" title="附件" align="center">
113 <template #default="{row}"> 194 <template #default="{row}">
114 <FileUpload v-model="row.video" /> 195 <FileUpload v-model="row.video" />
115 </template> 196 </template>
116 - </vxe-column>  
117 - <vxe-column field="memo" title="备注" align="center"> 197 + </VxeColumn>
  198 + <VxeColumn field="memo" title="备注" align="center">
118 <template #default="{row}"> 199 <template #default="{row}">
119 - <el-input v-model="row.memo" type="textarea" /> 200 + <ElInput v-model="row.memo" type="textarea" />
120 </template> 201 </template>
121 - </vxe-column>  
122 - <vxe-column field="operation" title="操作" align="center"> 202 + </VxeColumn>
  203 + <VxeColumn field="operation" title="操作" align="center">
123 <template #default="{row}"> 204 <template #default="{row}">
124 - <el-button 205 + <ElButton
125 size="small" 206 size="small"
126 type="danger" 207 type="danger"
127 link 208 link
@@ -129,13 +210,13 @@ watch( @@ -129,13 +210,13 @@ watch(
129 v-access:code="['infra:student:delete']" 210 v-access:code="['infra:student:delete']"
130 > 211 >
131 {{ $t('ui.actionTitle.delete') }} 212 {{ $t('ui.actionTitle.delete') }}
132 - </el-button> 213 + </ElButton>
133 </template> 214 </template>
134 - </vxe-column>  
135 - </vxe-table> 215 + </VxeColumn>
  216 + </VxeTable>
136 <div class="flex justify-center mt-4"> 217 <div class="flex justify-center mt-4">
137 - <el-button :icon="h(Plus)" type="primary" plain @click="handleAdd" v-access:code="['infra:student:create']"> 218 + <ElButton :icon="Plus" type="primary" plain @click="handleAdd" v-access:code="['infra:student:create']">
138 {{ $t('ui.actionTitle.create', ['学生联系人']) }} 219 {{ $t('ui.actionTitle.create', ['学生联系人']) }}
139 - </el-button> 220 + </ElButton>
140 </div> 221 </div>
141 </template> 222 </template>
yudao-module-infra/src/test/resources/codegen/vben5_ele_general_master_inner/vue/student-contact-list
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import type { StudentApi } from '#/api/infra/student'; 2 + import type { StudentApi } from '#/api/infra/demo';
3 import type { VxeTableInstance } from '#/adapter/vxe-table'; 3 import type { VxeTableInstance } from '#/adapter/vxe-table';
4 4
5 import { reactive,ref, h, nextTick,watch,onMounted } from 'vue'; 5 import { reactive,ref, h, nextTick,watch,onMounted } from 'vue';
6 6
7 import { DICT_TYPE } from '@vben/constants'; 7 import { DICT_TYPE } from '@vben/constants';
8 import { getDictOptions } from '@vben/hooks'; 8 import { getDictOptions } from '@vben/hooks';
  9 +import { formatDateTime } from '@vben/utils';
9 10
10 import { DictTag } from '#/components/dict-tag'; 11 import { DictTag } from '#/components/dict-tag';
11 import { getRangePickerDefaultProps } from '#/utils'; 12 import { getRangePickerDefaultProps } from '#/utils';
@@ -13,7 +14,7 @@ @@ -13,7 +14,7 @@
13 import { ContentWrap } from '#/components/content-wrap'; 14 import { ContentWrap } from '#/components/content-wrap';
14 15
15 16
16 - import { getStudentContactListByStudentId } from '#/api/infra/student'; 17 + import { getStudentContactListByStudentId } from '#/api/infra/demo';
17 18
18 const props = defineProps<{ 19 const props = defineProps<{
19 studentId?: number // 学生编号(主表的关联字段) 20 studentId?: number // 学生编号(主表的关联字段)
@@ -52,38 +53,38 @@ watch( @@ -52,38 +53,38 @@ watch(
52 53
53 <template> 54 <template>
54 <ContentWrap title="学生联系人列表"> 55 <ContentWrap title="学生联系人列表">
55 - <vxe-table 56 + <VxeTable
56 :data="list" 57 :data="list"
57 show-overflow 58 show-overflow
58 :loading="loading" 59 :loading="loading"
59 > 60 >
60 - <vxe-column field="id" title="编号" align="center" />  
61 - <vxe-column field="studentId" title="学生编号" align="center" />  
62 - <vxe-column field="name" title="名字" align="center" />  
63 - <vxe-column field="description" title="简介" align="center" />  
64 - <vxe-column field="birthday" title="出生日期" align="center"> 61 + <VxeColumn field="id" title="编号" align="center" />
  62 + <VxeColumn field="studentId" title="学生编号" align="center" />
  63 + <VxeColumn field="name" title="名字" align="center" />
  64 + <VxeColumn field="description" title="简介" align="center" />
  65 + <VxeColumn field="birthday" title="出生日期" align="center">
65 <template #default="{row}"> 66 <template #default="{row}">
66 {{formatDateTime(row.birthday)}} 67 {{formatDateTime(row.birthday)}}
67 </template> 68 </template>
68 - </vxe-column>  
69 - <vxe-column field="sex" title="性别" align="center"> 69 + </VxeColumn>
  70 + <VxeColumn field="sex" title="性别" align="center">
70 <template #default="{row}"> 71 <template #default="{row}">
71 <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" /> 72 <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" />
72 </template> 73 </template>
73 - </vxe-column>  
74 - <vxe-column field="enabled" title="是否有效" align="center"> 74 + </VxeColumn>
  75 + <VxeColumn field="enabled" title="是否有效" align="center">
75 <template #default="{row}"> 76 <template #default="{row}">
76 <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.enabled" /> 77 <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.enabled" />
77 </template> 78 </template>
78 - </vxe-column>  
79 - <vxe-column field="avatar" title="头像" align="center" />  
80 - <vxe-column field="video" title="附件" align="center" />  
81 - <vxe-column field="memo" title="备注" align="center" />  
82 - <vxe-column field="createTime" title="创建时间" align="center"> 79 + </VxeColumn>
  80 + <VxeColumn field="avatar" title="头像" align="center" />
  81 + <VxeColumn field="video" title="附件" align="center" />
  82 + <VxeColumn field="memo" title="备注" align="center" />
  83 + <VxeColumn field="createTime" title="创建时间" align="center">
83 <template #default="{row}"> 84 <template #default="{row}">
84 {{formatDateTime(row.createTime)}} 85 {{formatDateTime(row.createTime)}}
85 </template> 86 </template>
86 - </vxe-column>  
87 - </vxe-table> 87 + </VxeColumn>
  88 + </VxeTable>
88 </ContentWrap> 89 </ContentWrap>
89 </template> 90 </template>
yudao-module-infra/src/test/resources/codegen/vben5_ele_general_master_inner/vue/student-teacher-form
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 -import type { StudentApi } from '#/api/infra/student'; 2 +import type { FormRules } from 'element-plus';
  3 +import type { StudentApi } from '#/api/infra/demo';
3 4
4 -import { computed, ref, reactive, h, onMounted,watch,nextTick } from 'vue'; 5 +import { nextTick, reactive, ref, watch } from 'vue';
5 6
6 import { DICT_TYPE } from '@vben/constants'; 7 import { DICT_TYPE } from '@vben/constants';
7 import { getDictOptions } from '@vben/hooks'; 8 import { getDictOptions } from '@vben/hooks';
8 9
9 -import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElButton, ElSelect, ElOption, ElRadioGroup, ElRadio, ElCheckboxGroup, ElCheckbox, ElDatePicker } from 'element-plus';  
10 -import { $t } from '#/locales'; 10 +import {
  11 + ElForm,
  12 + ElFormItem,
  13 + ElDatePicker,
  14 + ElInput,
  15 + ElOption,
  16 + ElRadio,
  17 + ElRadioGroup,
  18 + ElSelect,
  19 +} from 'element-plus';
11 20
12 -import type { FormRules } from 'element-plus';  
13 import { Tinymce as RichTextarea } from '#/components/tinymce'; 21 import { Tinymce as RichTextarea } from '#/components/tinymce';
14 -import { getStudentTeacherByStudentId } from '#/api/infra/student'; 22 +import { getStudentTeacherByStudentId } from '#/api/infra/demo';
  23 +import { $t } from '#/locales';
15 24
16 const props = defineProps<{ 25 const props = defineProps<{
17 studentId?: number // 学生编号(主表的关联字段) 26 studentId?: number // 学生编号(主表的关联字段)
@@ -50,6 +59,18 @@ defineExpose({ @@ -50,6 +59,18 @@ defineExpose({
50 watch( 59 watch(
51 () => props.studentId, 60 () => props.studentId,
52 async (val) => { 61 async (val) => {
  62 + formData.value = {
  63 + id: undefined,
  64 + studentId: undefined,
  65 + name: undefined,
  66 + description: undefined,
  67 + birthday: undefined,
  68 + sex: undefined,
  69 + enabled: undefined,
  70 + avatar: undefined,
  71 + video: undefined,
  72 + memo: undefined,
  73 + };
53 if (!val) { 74 if (!val) {
54 return; 75 return;
55 } 76 }
@@ -61,7 +82,7 @@ watch( @@ -61,7 +82,7 @@ watch(
61 </script> 82 </script>
62 83
63 <template> 84 <template>
64 - <el-form 85 + <ElForm
65 ref="formRef" 86 ref="formRef"
66 class="mx-4" 87 class="mx-4"
67 :model="formData" 88 :model="formData"
@@ -69,48 +90,48 @@ watch( @@ -69,48 +90,48 @@ watch(
69 label-width="120px" 90 label-width="120px"
70 label-position="right" 91 label-position="right"
71 > 92 >
72 - <el-form-item label="名字" prop="name">  
73 - <el-input v-model="formData.name" placeholder="请输入名字" />  
74 - </el-form-item>  
75 - <el-form-item label="简介" prop="description">  
76 - <el-input v-model="formData.description" type="textarea" placeholder="请输入简介" />  
77 - </el-form-item>  
78 - <el-form-item label="出生日期" prop="birthday">  
79 - <el-date-picker 93 + <ElFormItem label="名字" prop="name">
  94 + <ElInput v-model="formData.name" placeholder="请输入名字" />
  95 + </ElFormItem>
  96 + <ElFormItem label="简介" prop="description">
  97 + <ElInput v-model="formData.description" type="textarea" placeholder="请输入简介" />
  98 + </ElFormItem>
  99 + <ElFormItem label="出生日期" prop="birthday">
  100 + <ElDatePicker
80 v-model="formData.birthday" 101 v-model="formData.birthday"
81 value-format="x" 102 value-format="x"
82 placeholder="选择出生日期" 103 placeholder="选择出生日期"
83 /> 104 />
84 - </el-form-item>  
85 - <el-form-item label="性别" prop="sex">  
86 - <el-select v-model="formData.sex" placeholder="请选择性别">  
87 - <el-option 105 + </ElFormItem>
  106 + <ElFormItem label="性别" prop="sex">
  107 + <ElSelect v-model="formData.sex" placeholder="请选择性别">
  108 + <ElOption
88 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')" 109 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
89 :key="dict.value" 110 :key="dict.value"
90 :value="dict.value" 111 :value="dict.value"
91 :label="dict.label" 112 :label="dict.label"
92 /> 113 />
93 - </el-select>  
94 - </el-form-item>  
95 - <el-form-item label="是否有效" prop="enabled">  
96 - <el-radio-group v-model="formData.enabled">  
97 - <el-radio 114 + </ElSelect>
  115 + </ElFormItem>
  116 + <ElFormItem label="是否有效" prop="enabled">
  117 + <ElRadioGroup v-model="formData.enabled">
  118 + <ElRadio
98 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')" 119 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
99 :key="dict.value" 120 :key="dict.value"
100 - :label="dict.value" 121 + :value="dict.value"
101 > 122 >
102 {{ dict.label }} 123 {{ dict.label }}
103 - </el-radio>  
104 - </el-radio-group>  
105 - </el-form-item>  
106 - <el-form-item label="头像" prop="avatar"> 124 + </ElRadio>
  125 + </ElRadioGroup>
  126 + </ElFormItem>
  127 + <ElFormItem label="头像" prop="avatar">
107 <ImageUpload v-model="formData.avatar" /> 128 <ImageUpload v-model="formData.avatar" />
108 - </el-form-item>  
109 - <el-form-item label="附件" prop="video"> 129 + </ElFormItem>
  130 + <ElFormItem label="附件" prop="video">
110 <FileUpload v-model="formData.video" /> 131 <FileUpload v-model="formData.video" />
111 - </el-form-item>  
112 - <el-form-item label="备注" prop="memo"> 132 + </ElFormItem>
  133 + <ElFormItem label="备注" prop="memo">
113 <RichTextarea v-model="formData.memo" height="500px" /> 134 <RichTextarea v-model="formData.memo" height="500px" />
114 - </el-form-item>  
115 - </el-form> 135 + </ElFormItem>
  136 + </ElForm>
116 </template> 137 </template>
yudao-module-infra/src/test/resources/codegen/vben5_ele_general_master_inner/vue/student-teacher-list
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import type { StudentApi } from '#/api/infra/student'; 2 + import type { StudentApi } from '#/api/infra/demo';
3 import type { VxeTableInstance } from '#/adapter/vxe-table'; 3 import type { VxeTableInstance } from '#/adapter/vxe-table';
4 4
5 import { reactive,ref, h, nextTick,watch,onMounted } from 'vue'; 5 import { reactive,ref, h, nextTick,watch,onMounted } from 'vue';
6 6
7 import { DICT_TYPE } from '@vben/constants'; 7 import { DICT_TYPE } from '@vben/constants';
8 import { getDictOptions } from '@vben/hooks'; 8 import { getDictOptions } from '@vben/hooks';
  9 +import { formatDateTime } from '@vben/utils';
9 10
10 import { DictTag } from '#/components/dict-tag'; 11 import { DictTag } from '#/components/dict-tag';
11 import { getRangePickerDefaultProps } from '#/utils'; 12 import { getRangePickerDefaultProps } from '#/utils';
@@ -13,7 +14,7 @@ @@ -13,7 +14,7 @@
13 import { ContentWrap } from '#/components/content-wrap'; 14 import { ContentWrap } from '#/components/content-wrap';
14 15
15 16
16 - import { getStudentTeacherByStudentId } from '#/api/infra/student'; 17 + import { getStudentTeacherByStudentId } from '#/api/infra/demo';
17 18
18 const props = defineProps<{ 19 const props = defineProps<{
19 studentId?: number // 学生编号(主表的关联字段) 20 studentId?: number // 学生编号(主表的关联字段)
@@ -52,38 +53,38 @@ watch( @@ -52,38 +53,38 @@ watch(
52 53
53 <template> 54 <template>
54 <ContentWrap title="学生班主任列表"> 55 <ContentWrap title="学生班主任列表">
55 - <vxe-table 56 + <VxeTable
56 :data="list" 57 :data="list"
57 show-overflow 58 show-overflow
58 :loading="loading" 59 :loading="loading"
59 > 60 >
60 - <vxe-column field="id" title="编号" align="center" />  
61 - <vxe-column field="studentId" title="学生编号" align="center" />  
62 - <vxe-column field="name" title="名字" align="center" />  
63 - <vxe-column field="description" title="简介" align="center" />  
64 - <vxe-column field="birthday" title="出生日期" align="center"> 61 + <VxeColumn field="id" title="编号" align="center" />
  62 + <VxeColumn field="studentId" title="学生编号" align="center" />
  63 + <VxeColumn field="name" title="名字" align="center" />
  64 + <VxeColumn field="description" title="简介" align="center" />
  65 + <VxeColumn field="birthday" title="出生日期" align="center">
65 <template #default="{row}"> 66 <template #default="{row}">
66 {{formatDateTime(row.birthday)}} 67 {{formatDateTime(row.birthday)}}
67 </template> 68 </template>
68 - </vxe-column>  
69 - <vxe-column field="sex" title="性别" align="center"> 69 + </VxeColumn>
  70 + <VxeColumn field="sex" title="性别" align="center">
70 <template #default="{row}"> 71 <template #default="{row}">
71 <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" /> 72 <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" />
72 </template> 73 </template>
73 - </vxe-column>  
74 - <vxe-column field="enabled" title="是否有效" align="center"> 74 + </VxeColumn>
  75 + <VxeColumn field="enabled" title="是否有效" align="center">
75 <template #default="{row}"> 76 <template #default="{row}">
76 <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.enabled" /> 77 <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.enabled" />
77 </template> 78 </template>
78 - </vxe-column>  
79 - <vxe-column field="avatar" title="头像" align="center" />  
80 - <vxe-column field="video" title="附件" align="center" />  
81 - <vxe-column field="memo" title="备注" align="center" />  
82 - <vxe-column field="createTime" title="创建时间" align="center"> 79 + </VxeColumn>
  80 + <VxeColumn field="avatar" title="头像" align="center" />
  81 + <VxeColumn field="video" title="附件" align="center" />
  82 + <VxeColumn field="memo" title="备注" align="center" />
  83 + <VxeColumn field="createTime" title="创建时间" align="center">
83 <template #default="{row}"> 84 <template #default="{row}">
84 {{formatDateTime(row.createTime)}} 85 {{formatDateTime(row.createTime)}}
85 </template> 86 </template>
86 - </vxe-column>  
87 - </vxe-table> 87 + </VxeColumn>
  88 + </VxeTable>
88 </ContentWrap> 89 </ContentWrap>
89 </template> 90 </template>
yudao-module-infra/src/test/resources/codegen/vben5_ele_general_master_normal/vue/form
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 -import type { StudentApi } from '#/api/infra/student'; 2 +import type { StudentApi } from '#/api/infra/demo';
3 import type { FormRules } from 'element-plus'; 3 import type { FormRules } from 'element-plus';
4 4
5 import { useVbenModal } from '@vben/common-ui'; 5 import { useVbenModal } from '@vben/common-ui';
@@ -13,7 +13,7 @@ import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElSelect, El @@ -13,7 +13,7 @@ import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElSelect, El
13 13
14 import { computed, ref, reactive } from 'vue'; 14 import { computed, ref, reactive } from 'vue';
15 import { $t } from '#/locales'; 15 import { $t } from '#/locales';
16 -import { getStudent, createStudent, updateStudent } from '#/api/infra/student'; 16 +import { getStudent, createStudent, updateStudent } from '#/api/infra/demo';
17 17
18 const emit = defineEmits(['success']); 18 const emit = defineEmits(['success']);
19 19
@@ -71,7 +71,11 @@ const [Modal, modalApi] = useVbenModal({ @@ -71,7 +71,11 @@ const [Modal, modalApi] = useVbenModal({
71 async onConfirm() { 71 async onConfirm() {
72 await formRef.value?.validate(); 72 await formRef.value?.validate();
73 // 校验子表单 73 // 校验子表单
74 - try { 74 + if (!studentContactFormRef.value?.validate()) {
  75 + subTabsName.value = 'studentContact';
  76 + return;
  77 + }
  78 + try {
75 await studentTeacherFormRef.value?.validate() 79 await studentTeacherFormRef.value?.validate()
76 } catch (e) { 80 } catch (e) {
77 subTabsName.value = 'studentTeacher' 81 subTabsName.value = 'studentTeacher'
@@ -118,65 +122,65 @@ const [Modal, modalApi] = useVbenModal({ @@ -118,65 +122,65 @@ const [Modal, modalApi] = useVbenModal({
118 122
119 <template> 123 <template>
120 <Modal :title="getTitle"> 124 <Modal :title="getTitle">
121 - <el-form 125 + <ElForm
122 ref="formRef" 126 ref="formRef"
123 :model="formData" 127 :model="formData"
124 :rules="rules" 128 :rules="rules"
125 label-width="120px" 129 label-width="120px"
126 label-position="right" 130 label-position="right"
127 > 131 >
128 - <el-form-item label="名字" prop="name">  
129 - <el-input v-model="formData.name" placeholder="请输入名字" />  
130 - </el-form-item>  
131 - <el-form-item label="简介" prop="description">  
132 - <el-input v-model="formData.description" type="textarea" placeholder="请输入简介" />  
133 - </el-form-item>  
134 - <el-form-item label="出生日期" prop="birthday">  
135 - <el-date-picker 132 + <ElFormItem label="名字" prop="name">
  133 + <ElInput v-model="formData.name" placeholder="请输入名字" />
  134 + </ElFormItem>
  135 + <ElFormItem label="简介" prop="description">
  136 + <ElInput v-model="formData.description" type="textarea" placeholder="请输入简介" />
  137 + </ElFormItem>
  138 + <ElFormItem label="出生日期" prop="birthday">
  139 + <ElDatePicker
136 v-model="formData.birthday" 140 v-model="formData.birthday"
137 value-format="x" 141 value-format="x"
138 placeholder="选择出生日期" 142 placeholder="选择出生日期"
139 /> 143 />
140 - </el-form-item>  
141 - <el-form-item label="性别" prop="sex">  
142 - <el-select v-model="formData.sex" placeholder="请选择性别">  
143 - <el-option 144 + </ElFormItem>
  145 + <ElFormItem label="性别" prop="sex">
  146 + <ElSelect v-model="formData.sex" placeholder="请选择性别">
  147 + <ElOption
144 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')" 148 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
145 :key="dict.value" 149 :key="dict.value"
146 :value="dict.value" 150 :value="dict.value"
147 :label="dict.label" 151 :label="dict.label"
148 /> 152 />
149 - </el-select>  
150 - </el-form-item>  
151 - <el-form-item label="是否有效" prop="enabled">  
152 - <el-radio-group v-model="formData.enabled">  
153 - <el-radio 153 + </ElSelect>
  154 + </ElFormItem>
  155 + <ElFormItem label="是否有效" prop="enabled">
  156 + <ElRadioGroup v-model="formData.enabled">
  157 + <ElRadio
154 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')" 158 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
155 :key="dict.value" 159 :key="dict.value"
156 - :label="dict.value" 160 + :value="dict.value"
157 > 161 >
158 {{ dict.label }} 162 {{ dict.label }}
159 - </el-radio>  
160 - </el-radio-group>  
161 - </el-form-item>  
162 - <el-form-item label="头像" prop="avatar"> 163 + </ElRadio>
  164 + </ElRadioGroup>
  165 + </ElFormItem>
  166 + <ElFormItem label="头像" prop="avatar">
163 <ImageUpload v-model="formData.avatar" /> 167 <ImageUpload v-model="formData.avatar" />
164 - </el-form-item>  
165 - <el-form-item label="附件" prop="video"> 168 + </ElFormItem>
  169 + <ElFormItem label="附件" prop="video">
166 <FileUpload v-model="formData.video" /> 170 <FileUpload v-model="formData.video" />
167 - </el-form-item>  
168 - <el-form-item label="备注" prop="memo"> 171 + </ElFormItem>
  172 + <ElFormItem label="备注" prop="memo">
169 <RichTextarea v-model="formData.memo" height="500px" /> 173 <RichTextarea v-model="formData.memo" height="500px" />
170 - </el-form-item>  
171 - </el-form> 174 + </ElFormItem>
  175 + </ElForm>
172 <!-- 子表的表单 --> 176 <!-- 子表的表单 -->
173 - <el-tabs v-model="subTabsName">  
174 - <el-tab-pane name="studentContact" label="学生联系人"> 177 + <ElTabs v-model="subTabsName">
  178 + <ElTabPane name="studentContact" label="学生联系人">
175 <StudentContactForm ref="studentContactFormRef" :student-id="formData?.id" /> 179 <StudentContactForm ref="studentContactFormRef" :student-id="formData?.id" />
176 - </el-tab-pane>  
177 - <el-tab-pane name="studentTeacher" label="学生班主任"> 180 + </ElTabPane>
  181 + <ElTabPane name="studentTeacher" label="学生班主任">
178 <StudentTeacherForm ref="studentTeacherFormRef" :student-id="formData?.id" /> 182 <StudentTeacherForm ref="studentTeacherFormRef" :student-id="formData?.id" />
179 - </el-tab-pane>  
180 - </el-tabs> 183 + </ElTabPane>
  184 + </ElTabs>
181 </Modal> 185 </Modal>
182 </template> 186 </template>
yudao-module-infra/src/test/resources/codegen/vben5_ele_general_master_normal/vue/index
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 -import type { StudentApi } from '#/api/infra/student'; 2 +import type { StudentApi } from '#/api/infra/demo';
3 3
4 import { ref, h, reactive, onMounted, nextTick } from 'vue'; 4 import { ref, h, reactive, onMounted, nextTick } from 'vue';
5 5
@@ -17,7 +17,7 @@ import { VxeColumn, VxeTable } from &#39;#/adapter/vxe-table&#39;; @@ -17,7 +17,7 @@ import { VxeColumn, VxeTable } from &#39;#/adapter/vxe-table&#39;;
17 17
18 import { $t } from '#/locales'; 18 import { $t } from '#/locales';
19 import { isEmpty } from '@vben/utils'; 19 import { isEmpty } from '@vben/utils';
20 -import { getStudentPage, deleteStudent, deleteStudentList, exportStudent } from '#/api/infra/student'; 20 +import { getStudentPage, deleteStudent, deleteStudentList, exportStudent } from '#/api/infra/demo';
21 21
22 22
23 const loading = ref(true) // 列表的加载中 23 const loading = ref(true) // 列表的加载中
@@ -27,12 +27,11 @@ const total = ref(0) // 列表的总页数 @@ -27,12 +27,11 @@ const total = ref(0) // 列表的总页数
27 const queryParams = reactive({ 27 const queryParams = reactive({
28 pageNo: 1, 28 pageNo: 1,
29 pageSize: 10, 29 pageSize: 10,
30 - name: undefined,  
31 - birthday: undefined,  
32 - birthday: undefined,  
33 - sex: undefined,  
34 - enabled: undefined,  
35 - createTime: undefined, 30 + name: undefined,
  31 + birthday: undefined,
  32 + sex: undefined,
  33 + enabled: undefined,
  34 + createTime: undefined,
36 }) 35 })
37 const queryFormRef = ref() // 搜索的表单 36 const queryFormRef = ref() // 搜索的表单
38 const exportLoading = ref(false) // 导出的加载中 37 const exportLoading = ref(false) // 导出的加载中
@@ -126,7 +125,14 @@ function handleRowCheckboxChange({ @@ -126,7 +125,14 @@ function handleRowCheckboxChange({
126 async function handleExport() { 125 async function handleExport() {
127 try { 126 try {
128 exportLoading.value = true; 127 exportLoading.value = true;
129 - const data = await exportStudent(queryParams); 128 + const params = cloneDeep(queryParams) as any;
  129 + if (params.birthday && Array.isArray(params.birthday)) {
  130 + params.birthday = (params.birthday as string[]).join(',');
  131 + }
  132 + if (params.createTime && Array.isArray(params.createTime)) {
  133 + params.createTime = (params.createTime as string[]).join(',');
  134 + }
  135 + const data = await exportStudent(params);
130 downloadFileFromBlobPart({ fileName: '学生.xls', source: data }); 136 downloadFileFromBlobPart({ fileName: '学生.xls', source: data });
131 }finally { 137 }finally {
132 exportLoading.value = false; 138 exportLoading.value = false;
@@ -148,61 +154,61 @@ onMounted(() =&gt; { @@ -148,61 +154,61 @@ onMounted(() =&gt; {
148 154
149 <ContentWrap v-if="!hiddenSearchBar"> 155 <ContentWrap v-if="!hiddenSearchBar">
150 <!-- 搜索工作栏 --> 156 <!-- 搜索工作栏 -->
151 - <el-form 157 + <ElForm
152 :model="queryParams" 158 :model="queryParams"
153 ref="queryFormRef" 159 ref="queryFormRef"
154 inline 160 inline
155 > 161 >
156 - <el-form-item label="名字">  
157 - <el-input 162 + <ElFormItem label="名字">
  163 + <ElInput
158 v-model="queryParams.name" 164 v-model="queryParams.name"
159 placeholder="请输入名字" 165 placeholder="请输入名字"
160 clearable 166 clearable
161 @keyup.enter="handleQuery" 167 @keyup.enter="handleQuery"
162 class="!w-[240px]" 168 class="!w-[240px]"
163 /> 169 />
164 - </el-form-item>  
165 - <el-form-item label="出生日期">  
166 - <el-date-picker 170 + </ElFormItem>
  171 + <ElFormItem label="出生日期">
  172 + <ElDatePicker
167 v-model="queryParams.birthday" 173 v-model="queryParams.birthday"
168 value-format="YYYY-MM-DD" 174 value-format="YYYY-MM-DD"
169 placeholder="选择出生日期" 175 placeholder="选择出生日期"
170 clearable 176 clearable
171 class="!w-[240px]" 177 class="!w-[240px]"
172 /> 178 />
173 - </el-form-item>  
174 - <el-form-item label="性别">  
175 - <el-select 179 + </ElFormItem>
  180 + <ElFormItem label="性别">
  181 + <ElSelect
176 v-model="queryParams.sex" 182 v-model="queryParams.sex"
177 placeholder="请选择性别" 183 placeholder="请选择性别"
178 clearable 184 clearable
179 class="!w-[240px]" 185 class="!w-[240px]"
180 > 186 >
181 - <el-option 187 + <ElOption
182 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')" 188 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
183 :key="dict.value" 189 :key="dict.value"
184 :value="dict.value" 190 :value="dict.value"
185 :label="dict.label" 191 :label="dict.label"
186 /> 192 />
187 - </el-select>  
188 - </el-form-item>  
189 - <el-form-item label="是否有效">  
190 - <el-select 193 + </ElSelect>
  194 + </ElFormItem>
  195 + <ElFormItem label="是否有效">
  196 + <ElSelect
191 v-model="queryParams.enabled" 197 v-model="queryParams.enabled"
192 placeholder="请选择是否有效" 198 placeholder="请选择是否有效"
193 clearable 199 clearable
194 class="!w-[240px]" 200 class="!w-[240px]"
195 > 201 >
196 - <el-option 202 + <ElOption
197 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')" 203 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
198 :key="dict.value" 204 :key="dict.value"
199 :value="dict.value" 205 :value="dict.value"
200 :label="dict.label" 206 :label="dict.label"
201 /> 207 />
202 - </el-select>  
203 - </el-form-item>  
204 - <el-form-item label="创建时间">  
205 - <el-date-picker 208 + </ElSelect>
  209 + </ElFormItem>
  210 + <ElFormItem label="创建时间">
  211 + <ElDatePicker
206 v-model="queryParams.createTime" 212 v-model="queryParams.createTime"
207 type="daterange" 213 type="daterange"
208 value-format="YYYY-MM-DD" 214 value-format="YYYY-MM-DD"
@@ -211,24 +217,24 @@ onMounted(() =&gt; { @@ -211,24 +217,24 @@ onMounted(() =&gt; {
211 end-placeholder="结束日期" 217 end-placeholder="结束日期"
212 class="!w-[240px]" 218 class="!w-[240px]"
213 /> 219 />
214 - </el-form-item>  
215 - <el-form-item>  
216 - <el-button class="ml-2" @click="resetQuery"> 重置 </el-button>  
217 - <el-button class="ml-2" @click="handleQuery" type="primary"> 220 + </ElFormItem>
  221 + <ElFormItem>
  222 + <ElButton class="ml-2" @click="resetQuery"> 重置 </ElButton>
  223 + <ElButton class="ml-2" @click="handleQuery" type="primary">
218 搜索 224 搜索
219 - </el-button>  
220 - </el-form-item>  
221 - </el-form> 225 + </ElButton>
  226 + </ElFormItem>
  227 + </ElForm>
222 </ContentWrap> 228 </ContentWrap>
223 229
224 <!-- 列表 --> 230 <!-- 列表 -->
225 <ContentWrap title="学生"> 231 <ContentWrap title="学生">
226 <template #extra> 232 <template #extra>
227 - <TableToolbar 233 + <VbenVxeTableToolbar
228 ref="tableToolbarRef" 234 ref="tableToolbarRef"
229 v-model:hidden-search="hiddenSearchBar" 235 v-model:hidden-search="hiddenSearchBar"
230 > 236 >
231 - <el-button 237 + <ElButton
232 class="ml-2" 238 class="ml-2"
233 :icon="h(Plus)" 239 :icon="h(Plus)"
234 type="primary" 240 type="primary"
@@ -236,8 +242,8 @@ onMounted(() =&gt; { @@ -236,8 +242,8 @@ onMounted(() =&gt; {
236 v-access:code="['infra:student:create']" 242 v-access:code="['infra:student:create']"
237 > 243 >
238 {{ $t('ui.actionTitle.create', ['学生']) }} 244 {{ $t('ui.actionTitle.create', ['学生']) }}
239 - </el-button>  
240 - <el-button 245 + </ElButton>
  246 + <ElButton
241 :icon="h(Download)" 247 :icon="h(Download)"
242 type="primary" 248 type="primary"
243 class="ml-2" 249 class="ml-2"
@@ -246,8 +252,8 @@ onMounted(() =&gt; { @@ -246,8 +252,8 @@ onMounted(() =&gt; {
246 v-access:code="['infra:student:export']" 252 v-access:code="['infra:student:export']"
247 > 253 >
248 {{ $t('ui.actionTitle.export') }} 254 {{ $t('ui.actionTitle.export') }}
249 - </el-button>  
250 - <el-button 255 + </ElButton>
  256 + <ElButton
251 :icon="h(Trash2)" 257 :icon="h(Trash2)"
252 type="danger" 258 type="danger"
253 class="ml-2" 259 class="ml-2"
@@ -256,10 +262,10 @@ onMounted(() =&gt; { @@ -256,10 +262,10 @@ onMounted(() =&gt; {
256 v-access:code="['infra:student:delete']" 262 v-access:code="['infra:student:delete']"
257 > 263 >
258 批量删除 264 批量删除
259 - </el-button>  
260 - </TableToolbar> 265 + </ElButton>
  266 + </VbenVxeTableToolbar>
261 </template> 267 </template>
262 - <vxe-table 268 + <VxeTable
263 ref="tableRef" 269 ref="tableRef"
264 :data="list" 270 :data="list"
265 show-overflow 271 show-overflow
@@ -267,36 +273,36 @@ onMounted(() =&gt; { @@ -267,36 +273,36 @@ onMounted(() =&gt; {
267 @checkboxAll="handleRowCheckboxChange" 273 @checkboxAll="handleRowCheckboxChange"
268 @checkboxChange="handleRowCheckboxChange" 274 @checkboxChange="handleRowCheckboxChange"
269 > 275 >
270 - <vxe-column type="checkbox" width="40"></vxe-column>  
271 - <vxe-column field="id" title="编号" align="center" />  
272 - <vxe-column field="name" title="名字" align="center" />  
273 - <vxe-column field="description" title="简介" align="center" />  
274 - <vxe-column field="birthday" title="出生日期" align="center"> 276 + <VxeColumn type="checkbox" width="40"></VxeColumn>
  277 + <VxeColumn field="id" title="编号" align="center" />
  278 + <VxeColumn field="name" title="名字" align="center" />
  279 + <VxeColumn field="description" title="简介" align="center" />
  280 + <VxeColumn field="birthday" title="出生日期" align="center">
275 <template #default="{row}"> 281 <template #default="{row}">
276 {{formatDateTime(row.birthday)}} 282 {{formatDateTime(row.birthday)}}
277 </template> 283 </template>
278 - </vxe-column>  
279 - <vxe-column field="sex" title="性别" align="center"> 284 + </VxeColumn>
  285 + <VxeColumn field="sex" title="性别" align="center">
280 <template #default="{row}"> 286 <template #default="{row}">
281 <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" /> 287 <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" />
282 </template> 288 </template>
283 - </vxe-column>  
284 - <vxe-column field="enabled" title="是否有效" align="center"> 289 + </VxeColumn>
  290 + <VxeColumn field="enabled" title="是否有效" align="center">
285 <template #default="{row}"> 291 <template #default="{row}">
286 <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.enabled" /> 292 <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.enabled" />
287 </template> 293 </template>
288 - </vxe-column>  
289 - <vxe-column field="avatar" title="头像" align="center" />  
290 - <vxe-column field="video" title="附件" align="center" />  
291 - <vxe-column field="memo" title="备注" align="center" />  
292 - <vxe-column field="createTime" title="创建时间" align="center"> 294 + </VxeColumn>
  295 + <VxeColumn field="avatar" title="头像" align="center" />
  296 + <VxeColumn field="video" title="附件" align="center" />
  297 + <VxeColumn field="memo" title="备注" align="center" />
  298 + <VxeColumn field="createTime" title="创建时间" align="center">
293 <template #default="{row}"> 299 <template #default="{row}">
294 {{formatDateTime(row.createTime)}} 300 {{formatDateTime(row.createTime)}}
295 </template> 301 </template>
296 - </vxe-column>  
297 - <vxe-column field="operation" title="操作" align="center"> 302 + </VxeColumn>
  303 + <VxeColumn field="operation" title="操作" align="center">
298 <template #default="{row}"> 304 <template #default="{row}">
299 - <el-button 305 + <ElButton
300 size="small" 306 size="small"
301 type="primary" 307 type="primary"
302 link 308 link
@@ -304,8 +310,8 @@ onMounted(() =&gt; { @@ -304,8 +310,8 @@ onMounted(() =&gt; {
304 v-access:code="['infra:student:update']" 310 v-access:code="['infra:student:update']"
305 > 311 >
306 {{ $t('ui.actionTitle.edit') }} 312 {{ $t('ui.actionTitle.edit') }}
307 - </el-button>  
308 - <el-button 313 + </ElButton>
  314 + <ElButton
309 size="small" 315 size="small"
310 type="danger" 316 type="danger"
311 link 317 link
@@ -314,13 +320,13 @@ onMounted(() =&gt; { @@ -314,13 +320,13 @@ onMounted(() =&gt; {
314 v-access:code="['infra:student:delete']" 320 v-access:code="['infra:student:delete']"
315 > 321 >
316 {{ $t('ui.actionTitle.delete') }} 322 {{ $t('ui.actionTitle.delete') }}
317 - </el-button> 323 + </ElButton>
318 </template> 324 </template>
319 - </vxe-column>  
320 - </vxe-table> 325 + </VxeColumn>
  326 + </VxeTable>
321 <!-- 分页 --> 327 <!-- 分页 -->
322 <div class="mt-2 flex justify-end"> 328 <div class="mt-2 flex justify-end">
323 - <el-pagination 329 + <ElPagination
324 :total="total" 330 :total="total"
325 v-model:current-page="queryParams.pageNo" 331 v-model:current-page="queryParams.pageNo"
326 v-model:page-size="queryParams.pageSize" 332 v-model:page-size="queryParams.pageSize"
yudao-module-infra/src/test/resources/codegen/vben5_ele_general_master_normal/vue/student-contact-form
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 -import type { StudentApi } from '#/api/infra/student'; 2 +import type { VxeTableInstance } from '#/adapter/vxe-table';
  3 +import type { StudentApi } from '#/api/infra/demo';
3 4
4 -import { computed, ref, reactive, h, onMounted,watch,nextTick } from 'vue'; 5 +import { ref, watch } from 'vue';
5 6
6 import { DICT_TYPE } from '@vben/constants'; 7 import { DICT_TYPE } from '@vben/constants';
7 import { getDictOptions } from '@vben/hooks'; 8 import { getDictOptions } from '@vben/hooks';
  9 +import { Plus } from '@vben/icons';
8 10
9 -import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElButton, ElSelect, ElOption, ElRadioGroup, ElRadio, ElCheckboxGroup, ElCheckbox, ElDatePicker } from 'element-plus';  
10 -import { $t } from '#/locales'; 11 +import {
  12 + ElButton,
  13 + ElDatePicker,
  14 + ElInput,
  15 + ElMessage,
  16 + ElOption,
  17 + ElRadio,
  18 + ElRadioGroup,
  19 + ElSelect,
  20 +} from 'element-plus';
11 21
12 - import type { VxeTableInstance } from '#/adapter/vxe-table';  
13 -import { Plus } from "@vben/icons";  
14 -import { VxeColumn, VxeTable } from '#/adapter/vxe-table';  
15 -import { getStudentContactListByStudentId } from '#/api/infra/student'; 22 + import { VxeColumn, VxeTable } from '#/adapter/vxe-table';
  23 +import { getStudentContactListByStudentId } from '#/api/infra/demo';
  24 +import { $t } from '#/locales';
16 25
17 const props = defineProps<{ 26 const props = defineProps<{
18 studentId?: number // 学生编号(主表的关联字段) 27 studentId?: number // 学生编号(主表的关联字段)
@@ -30,24 +39,96 @@ async function onDelete(row: StudentApi.StudentContact) { @@ -30,24 +39,96 @@ async function onDelete(row: StudentApi.StudentContact) {
30 await tableRef.value?.remove(row); 39 await tableRef.value?.remove(row);
31 } 40 }
32 41
33 -/** 提供获取表格数据的方法供父组件调用 */  
34 -defineExpose({  
35 - getData: (): StudentApi.StudentContact[] => {  
36 - const data = list.value as StudentApi.StudentContact[];  
37 - const removeRecords = tableRef.value?.getRemoveRecords() as StudentApi.StudentContact[];  
38 - const insertRecords = tableRef.value?.getInsertRecords() as StudentApi.StudentContact[];  
39 - return [  
40 - ...data.filter(  
41 - (row) => !removeRecords.some((removed) => removed.id === row.id),  
42 - ),  
43 - ...insertRecords.map((row: any) => ({ ...row, id: undefined })),  
44 - },  
45 -}); 42 +/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
  43 +function getData(): StudentApi.StudentContact[] {
  44 + const table = tableRef.value;
  45 + if (!table) {
  46 + return [];
  47 + }
  48 + const { fullData } = table.getTableData();
  49 + const inserted = new Set(table.getInsertRecords());
  50 + return fullData.map((row) =>
  51 + inserted.has(row) ? { ...row, id: undefined } : { ...row },
  52 + );
  53 +}
  54 +
  55 +/** 校验子表必填字段 */
  56 +function validate(): boolean {
  57 + for (const [index, row] of getData().entries()) {
  58 + if (
  59 + row.name === null ||
  60 + row.name === undefined ||
  61 + (typeof row.name === 'string' && String(row.name).trim() === '') ||
  62 + (Array.isArray(row.name) && row.name.length === 0)
  63 + ) {
  64 + ElMessage.error(`第 ${index + 1} 行:名字不能为空`);
  65 + return false;
  66 + }
  67 + if (
  68 + row.description === null ||
  69 + row.description === undefined ||
  70 + (typeof row.description === 'string' && String(row.description).trim() === '') ||
  71 + (Array.isArray(row.description) && row.description.length === 0)
  72 + ) {
  73 + ElMessage.error(`第 ${index + 1} 行:简介不能为空`);
  74 + return false;
  75 + }
  76 + if (
  77 + row.birthday === null ||
  78 + row.birthday === undefined ||
  79 + (typeof row.birthday === 'string' && String(row.birthday).trim() === '') ||
  80 + (Array.isArray(row.birthday) && row.birthday.length === 0)
  81 + ) {
  82 + ElMessage.error(`第 ${index + 1} 行:出生日期不能为空`);
  83 + return false;
  84 + }
  85 + if (
  86 + row.sex === null ||
  87 + row.sex === undefined ||
  88 + (typeof row.sex === 'string' && String(row.sex).trim() === '') ||
  89 + (Array.isArray(row.sex) && row.sex.length === 0)
  90 + ) {
  91 + ElMessage.error(`第 ${index + 1} 行:性别不能为空`);
  92 + return false;
  93 + }
  94 + if (
  95 + row.enabled === null ||
  96 + row.enabled === undefined ||
  97 + (typeof row.enabled === 'string' && String(row.enabled).trim() === '') ||
  98 + (Array.isArray(row.enabled) && row.enabled.length === 0)
  99 + ) {
  100 + ElMessage.error(`第 ${index + 1} 行:是否有效不能为空`);
  101 + return false;
  102 + }
  103 + if (
  104 + row.avatar === null ||
  105 + row.avatar === undefined ||
  106 + (typeof row.avatar === 'string' && String(row.avatar).trim() === '') ||
  107 + (Array.isArray(row.avatar) && row.avatar.length === 0)
  108 + ) {
  109 + ElMessage.error(`第 ${index + 1} 行:头像不能为空`);
  110 + return false;
  111 + }
  112 + if (
  113 + row.memo === null ||
  114 + row.memo === undefined ||
  115 + (typeof row.memo === 'string' && String(row.memo).trim() === '') ||
  116 + (Array.isArray(row.memo) && row.memo.length === 0)
  117 + ) {
  118 + ElMessage.error(`第 ${index + 1} 行:备注不能为空`);
  119 + return false;
  120 + }
  121 + }
  122 + return true;
  123 +}
  124 +
  125 +defineExpose({ getData, validate });
46 126
47 /** 监听主表的关联字段的变化,加载对应的子表数据 */ 127 /** 监听主表的关联字段的变化,加载对应的子表数据 */
48 watch( 128 watch(
49 () => props.studentId, 129 () => props.studentId,
50 async (val) => { 130 async (val) => {
  131 + list.value = [];
51 if (!val) { 132 if (!val) {
52 return; 133 return;
53 } 134 }
@@ -58,70 +139,70 @@ watch( @@ -58,70 +139,70 @@ watch(
58 </script> 139 </script>
59 140
60 <template> 141 <template>
61 - <vxe-table ref="tableRef" :data="list" show-overflow class="mx-4">  
62 - <vxe-column field="name" title="名字" align="center"> 142 + <VxeTable ref="tableRef" :data="list" show-overflow class="mx-4">
  143 + <VxeColumn field="name" title="名字" align="center">
63 <template #default="{row}"> 144 <template #default="{row}">
64 - <el-input v-model="row.name" /> 145 + <ElInput v-model="row.name" />
65 </template> 146 </template>
66 - </vxe-column>  
67 - <vxe-column field="description" title="简介" align="center"> 147 + </VxeColumn>
  148 + <VxeColumn field="description" title="简介" align="center">
68 <template #default="{row}"> 149 <template #default="{row}">
69 - <el-input v-model="row.description" type="textarea" /> 150 + <ElInput v-model="row.description" type="textarea" />
70 </template> 151 </template>
71 - </vxe-column>  
72 - <vxe-column field="birthday" title="出生日期" align="center"> 152 + </VxeColumn>
  153 + <VxeColumn field="birthday" title="出生日期" align="center">
73 <template #default="{row}"> 154 <template #default="{row}">
74 - <el-date-picker 155 + <ElDatePicker
75 v-model="row.birthday" 156 v-model="row.birthday"
76 type="datetime" 157 type="datetime"
77 value-format="x" 158 value-format="x"
78 format="YYYY-MM-DD HH:mm:ss" 159 format="YYYY-MM-DD HH:mm:ss"
79 /> 160 />
80 </template> 161 </template>
81 - </vxe-column>  
82 - <vxe-column field="sex" title="性别" align="center"> 162 + </VxeColumn>
  163 + <VxeColumn field="sex" title="性别" align="center">
83 <template #default="{row}"> 164 <template #default="{row}">
84 - <el-select v-model="row.sex" placeholder="请选择性别">  
85 - <el-option  
86 - v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number')" 165 + <ElSelect v-model="row.sex" placeholder="请选择性别">
  166 + <ElOption
  167 + v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
87 :key="dict.value" 168 :key="dict.value"
88 :value="dict.value" 169 :value="dict.value"
89 :label="dict.label" 170 :label="dict.label"
90 /> 171 />
91 - </el-select> 172 + </ElSelect>
92 </template> 173 </template>
93 - </vxe-column>  
94 - <vxe-column field="enabled" title="是否有效" align="center"> 174 + </VxeColumn>
  175 + <VxeColumn field="enabled" title="是否有效" align="center">
95 <template #default="{row}"> 176 <template #default="{row}">
96 - <el-radio-group v-model="row.enabled">  
97 - <el-radio  
98 - v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'boolean')" 177 + <ElRadioGroup v-model="row.enabled">
  178 + <ElRadio
  179 + v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
99 :key="dict.value" 180 :key="dict.value"
100 - :label="dict.value" 181 + :value="dict.value"
101 > 182 >
102 {{ dict.label }} 183 {{ dict.label }}
103 - </el-radio>  
104 - </el-radio-group> 184 + </ElRadio>
  185 + </ElRadioGroup>
105 </template> 186 </template>
106 - </vxe-column>  
107 - <vxe-column field="avatar" title="头像" align="center"> 187 + </VxeColumn>
  188 + <VxeColumn field="avatar" title="头像" align="center">
108 <template #default="{row}"> 189 <template #default="{row}">
109 <ImageUpload v-model="row.avatar" /> 190 <ImageUpload v-model="row.avatar" />
110 </template> 191 </template>
111 - </vxe-column>  
112 - <vxe-column field="video" title="附件" align="center"> 192 + </VxeColumn>
  193 + <VxeColumn field="video" title="附件" align="center">
113 <template #default="{row}"> 194 <template #default="{row}">
114 <FileUpload v-model="row.video" /> 195 <FileUpload v-model="row.video" />
115 </template> 196 </template>
116 - </vxe-column>  
117 - <vxe-column field="memo" title="备注" align="center"> 197 + </VxeColumn>
  198 + <VxeColumn field="memo" title="备注" align="center">
118 <template #default="{row}"> 199 <template #default="{row}">
119 - <el-input v-model="row.memo" type="textarea" /> 200 + <ElInput v-model="row.memo" type="textarea" />
120 </template> 201 </template>
121 - </vxe-column>  
122 - <vxe-column field="operation" title="操作" align="center"> 202 + </VxeColumn>
  203 + <VxeColumn field="operation" title="操作" align="center">
123 <template #default="{row}"> 204 <template #default="{row}">
124 - <el-button 205 + <ElButton
125 size="small" 206 size="small"
126 type="danger" 207 type="danger"
127 link 208 link
@@ -129,13 +210,13 @@ watch( @@ -129,13 +210,13 @@ watch(
129 v-access:code="['infra:student:delete']" 210 v-access:code="['infra:student:delete']"
130 > 211 >
131 {{ $t('ui.actionTitle.delete') }} 212 {{ $t('ui.actionTitle.delete') }}
132 - </el-button> 213 + </ElButton>
133 </template> 214 </template>
134 - </vxe-column>  
135 - </vxe-table> 215 + </VxeColumn>
  216 + </VxeTable>
136 <div class="flex justify-center mt-4"> 217 <div class="flex justify-center mt-4">
137 - <el-button :icon="h(Plus)" type="primary" plain @click="handleAdd" v-access:code="['infra:student:create']"> 218 + <ElButton :icon="Plus" type="primary" plain @click="handleAdd" v-access:code="['infra:student:create']">
138 {{ $t('ui.actionTitle.create', ['学生联系人']) }} 219 {{ $t('ui.actionTitle.create', ['学生联系人']) }}
139 - </el-button> 220 + </ElButton>
140 </div> 221 </div>
141 </template> 222 </template>
yudao-module-infra/src/test/resources/codegen/vben5_ele_general_master_normal/vue/student-teacher-form
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 -import type { StudentApi } from '#/api/infra/student'; 2 +import type { FormRules } from 'element-plus';
  3 +import type { StudentApi } from '#/api/infra/demo';
3 4
4 -import { computed, ref, reactive, h, onMounted,watch,nextTick } from 'vue'; 5 +import { nextTick, reactive, ref, watch } from 'vue';
5 6
6 import { DICT_TYPE } from '@vben/constants'; 7 import { DICT_TYPE } from '@vben/constants';
7 import { getDictOptions } from '@vben/hooks'; 8 import { getDictOptions } from '@vben/hooks';
8 9
9 -import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElButton, ElSelect, ElOption, ElRadioGroup, ElRadio, ElCheckboxGroup, ElCheckbox, ElDatePicker } from 'element-plus';  
10 -import { $t } from '#/locales'; 10 +import {
  11 + ElForm,
  12 + ElFormItem,
  13 + ElDatePicker,
  14 + ElInput,
  15 + ElOption,
  16 + ElRadio,
  17 + ElRadioGroup,
  18 + ElSelect,
  19 +} from 'element-plus';
11 20
12 -import type { FormRules } from 'element-plus';  
13 import { Tinymce as RichTextarea } from '#/components/tinymce'; 21 import { Tinymce as RichTextarea } from '#/components/tinymce';
14 -import { getStudentTeacherByStudentId } from '#/api/infra/student'; 22 +import { getStudentTeacherByStudentId } from '#/api/infra/demo';
  23 +import { $t } from '#/locales';
15 24
16 const props = defineProps<{ 25 const props = defineProps<{
17 studentId?: number // 学生编号(主表的关联字段) 26 studentId?: number // 学生编号(主表的关联字段)
@@ -50,6 +59,18 @@ defineExpose({ @@ -50,6 +59,18 @@ defineExpose({
50 watch( 59 watch(
51 () => props.studentId, 60 () => props.studentId,
52 async (val) => { 61 async (val) => {
  62 + formData.value = {
  63 + id: undefined,
  64 + studentId: undefined,
  65 + name: undefined,
  66 + description: undefined,
  67 + birthday: undefined,
  68 + sex: undefined,
  69 + enabled: undefined,
  70 + avatar: undefined,
  71 + video: undefined,
  72 + memo: undefined,
  73 + };
53 if (!val) { 74 if (!val) {
54 return; 75 return;
55 } 76 }
@@ -61,7 +82,7 @@ watch( @@ -61,7 +82,7 @@ watch(
61 </script> 82 </script>
62 83
63 <template> 84 <template>
64 - <el-form 85 + <ElForm
65 ref="formRef" 86 ref="formRef"
66 class="mx-4" 87 class="mx-4"
67 :model="formData" 88 :model="formData"
@@ -69,48 +90,48 @@ watch( @@ -69,48 +90,48 @@ watch(
69 label-width="120px" 90 label-width="120px"
70 label-position="right" 91 label-position="right"
71 > 92 >
72 - <el-form-item label="名字" prop="name">  
73 - <el-input v-model="formData.name" placeholder="请输入名字" />  
74 - </el-form-item>  
75 - <el-form-item label="简介" prop="description">  
76 - <el-input v-model="formData.description" type="textarea" placeholder="请输入简介" />  
77 - </el-form-item>  
78 - <el-form-item label="出生日期" prop="birthday">  
79 - <el-date-picker 93 + <ElFormItem label="名字" prop="name">
  94 + <ElInput v-model="formData.name" placeholder="请输入名字" />
  95 + </ElFormItem>
  96 + <ElFormItem label="简介" prop="description">
  97 + <ElInput v-model="formData.description" type="textarea" placeholder="请输入简介" />
  98 + </ElFormItem>
  99 + <ElFormItem label="出生日期" prop="birthday">
  100 + <ElDatePicker
80 v-model="formData.birthday" 101 v-model="formData.birthday"
81 value-format="x" 102 value-format="x"
82 placeholder="选择出生日期" 103 placeholder="选择出生日期"
83 /> 104 />
84 - </el-form-item>  
85 - <el-form-item label="性别" prop="sex">  
86 - <el-select v-model="formData.sex" placeholder="请选择性别">  
87 - <el-option 105 + </ElFormItem>
  106 + <ElFormItem label="性别" prop="sex">
  107 + <ElSelect v-model="formData.sex" placeholder="请选择性别">
  108 + <ElOption
88 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')" 109 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
89 :key="dict.value" 110 :key="dict.value"
90 :value="dict.value" 111 :value="dict.value"
91 :label="dict.label" 112 :label="dict.label"
92 /> 113 />
93 - </el-select>  
94 - </el-form-item>  
95 - <el-form-item label="是否有效" prop="enabled">  
96 - <el-radio-group v-model="formData.enabled">  
97 - <el-radio 114 + </ElSelect>
  115 + </ElFormItem>
  116 + <ElFormItem label="是否有效" prop="enabled">
  117 + <ElRadioGroup v-model="formData.enabled">
  118 + <ElRadio
98 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')" 119 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
99 :key="dict.value" 120 :key="dict.value"
100 - :label="dict.value" 121 + :value="dict.value"
101 > 122 >
102 {{ dict.label }} 123 {{ dict.label }}
103 - </el-radio>  
104 - </el-radio-group>  
105 - </el-form-item>  
106 - <el-form-item label="头像" prop="avatar"> 124 + </ElRadio>
  125 + </ElRadioGroup>
  126 + </ElFormItem>
  127 + <ElFormItem label="头像" prop="avatar">
107 <ImageUpload v-model="formData.avatar" /> 128 <ImageUpload v-model="formData.avatar" />
108 - </el-form-item>  
109 - <el-form-item label="附件" prop="video"> 129 + </ElFormItem>
  130 + <ElFormItem label="附件" prop="video">
110 <FileUpload v-model="formData.video" /> 131 <FileUpload v-model="formData.video" />
111 - </el-form-item>  
112 - <el-form-item label="备注" prop="memo"> 132 + </ElFormItem>
  133 + <ElFormItem label="备注" prop="memo">
113 <RichTextarea v-model="formData.memo" height="500px" /> 134 <RichTextarea v-model="formData.memo" height="500px" />
114 - </el-form-item>  
115 - </el-form> 135 + </ElFormItem>
  136 + </ElForm>
116 </template> 137 </template>
yudao-module-infra/src/test/resources/codegen/vben5_ele_general_one/vue/form
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 -import type { StudentApi } from '#/api/infra/student'; 2 +import type { StudentApi } from '#/api/infra/demo';
3 import type { FormRules } from 'element-plus'; 3 import type { FormRules } from 'element-plus';
4 4
5 import { useVbenModal } from '@vben/common-ui'; 5 import { useVbenModal } from '@vben/common-ui';
@@ -11,7 +11,7 @@ import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElSelect, El @@ -11,7 +11,7 @@ import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElSelect, El
11 11
12 import { computed, ref, reactive } from 'vue'; 12 import { computed, ref, reactive } from 'vue';
13 import { $t } from '#/locales'; 13 import { $t } from '#/locales';
14 -import { getStudent, createStudent, updateStudent } from '#/api/infra/student'; 14 +import { getStudent, createStudent, updateStudent } from '#/api/infra/demo';
15 15
16 const emit = defineEmits(['success']); 16 const emit = defineEmits(['success']);
17 17
@@ -102,56 +102,56 @@ const [Modal, modalApi] = useVbenModal({ @@ -102,56 +102,56 @@ const [Modal, modalApi] = useVbenModal({
102 102
103 <template> 103 <template>
104 <Modal :title="getTitle"> 104 <Modal :title="getTitle">
105 - <el-form 105 + <ElForm
106 ref="formRef" 106 ref="formRef"
107 :model="formData" 107 :model="formData"
108 :rules="rules" 108 :rules="rules"
109 label-width="120px" 109 label-width="120px"
110 label-position="right" 110 label-position="right"
111 > 111 >
112 - <el-form-item label="名字" prop="name">  
113 - <el-input v-model="formData.name" placeholder="请输入名字" />  
114 - </el-form-item>  
115 - <el-form-item label="简介" prop="description">  
116 - <el-input v-model="formData.description" type="textarea" placeholder="请输入简介" />  
117 - </el-form-item>  
118 - <el-form-item label="出生日期" prop="birthday">  
119 - <el-date-picker 112 + <ElFormItem label="名字" prop="name">
  113 + <ElInput v-model="formData.name" placeholder="请输入名字" />
  114 + </ElFormItem>
  115 + <ElFormItem label="简介" prop="description">
  116 + <ElInput v-model="formData.description" type="textarea" placeholder="请输入简介" />
  117 + </ElFormItem>
  118 + <ElFormItem label="出生日期" prop="birthday">
  119 + <ElDatePicker
120 v-model="formData.birthday" 120 v-model="formData.birthday"
121 value-format="x" 121 value-format="x"
122 placeholder="选择出生日期" 122 placeholder="选择出生日期"
123 /> 123 />
124 - </el-form-item>  
125 - <el-form-item label="性别" prop="sex">  
126 - <el-select v-model="formData.sex" placeholder="请选择性别">  
127 - <el-option 124 + </ElFormItem>
  125 + <ElFormItem label="性别" prop="sex">
  126 + <ElSelect v-model="formData.sex" placeholder="请选择性别">
  127 + <ElOption
128 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')" 128 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
129 :key="dict.value" 129 :key="dict.value"
130 :value="dict.value" 130 :value="dict.value"
131 :label="dict.label" 131 :label="dict.label"
132 /> 132 />
133 - </el-select>  
134 - </el-form-item>  
135 - <el-form-item label="是否有效" prop="enabled">  
136 - <el-radio-group v-model="formData.enabled">  
137 - <el-radio 133 + </ElSelect>
  134 + </ElFormItem>
  135 + <ElFormItem label="是否有效" prop="enabled">
  136 + <ElRadioGroup v-model="formData.enabled">
  137 + <ElRadio
138 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')" 138 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
139 :key="dict.value" 139 :key="dict.value"
140 - :label="dict.value" 140 + :value="dict.value"
141 > 141 >
142 {{ dict.label }} 142 {{ dict.label }}
143 - </el-radio>  
144 - </el-radio-group>  
145 - </el-form-item>  
146 - <el-form-item label="头像" prop="avatar"> 143 + </ElRadio>
  144 + </ElRadioGroup>
  145 + </ElFormItem>
  146 + <ElFormItem label="头像" prop="avatar">
147 <ImageUpload v-model="formData.avatar" /> 147 <ImageUpload v-model="formData.avatar" />
148 - </el-form-item>  
149 - <el-form-item label="附件" prop="video"> 148 + </ElFormItem>
  149 + <ElFormItem label="附件" prop="video">
150 <FileUpload v-model="formData.video" /> 150 <FileUpload v-model="formData.video" />
151 - </el-form-item>  
152 - <el-form-item label="备注" prop="memo"> 151 + </ElFormItem>
  152 + <ElFormItem label="备注" prop="memo">
153 <RichTextarea v-model="formData.memo" height="500px" /> 153 <RichTextarea v-model="formData.memo" height="500px" />
154 - </el-form-item>  
155 - </el-form> 154 + </ElFormItem>
  155 + </ElForm>
156 </Modal> 156 </Modal>
157 </template> 157 </template>
yudao-module-infra/src/test/resources/codegen/vben5_ele_general_one/vue/index
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 -import type { StudentApi } from '#/api/infra/student'; 2 +import type { StudentApi } from '#/api/infra/demo';
3 3
4 import { ref, h, reactive, onMounted, nextTick } from 'vue'; 4 import { ref, h, reactive, onMounted, nextTick } from 'vue';
5 5
@@ -17,7 +17,7 @@ import { VxeColumn, VxeTable } from &#39;#/adapter/vxe-table&#39;; @@ -17,7 +17,7 @@ import { VxeColumn, VxeTable } from &#39;#/adapter/vxe-table&#39;;
17 17
18 import { $t } from '#/locales'; 18 import { $t } from '#/locales';
19 import { isEmpty } from '@vben/utils'; 19 import { isEmpty } from '@vben/utils';
20 -import { getStudentPage, deleteStudent, deleteStudentList, exportStudent } from '#/api/infra/student'; 20 +import { getStudentPage, deleteStudent, deleteStudentList, exportStudent } from '#/api/infra/demo';
21 21
22 22
23 const loading = ref(true) // 列表的加载中 23 const loading = ref(true) // 列表的加载中
@@ -27,12 +27,11 @@ const total = ref(0) // 列表的总页数 @@ -27,12 +27,11 @@ const total = ref(0) // 列表的总页数
27 const queryParams = reactive({ 27 const queryParams = reactive({
28 pageNo: 1, 28 pageNo: 1,
29 pageSize: 10, 29 pageSize: 10,
30 - name: undefined,  
31 - birthday: undefined,  
32 - birthday: undefined,  
33 - sex: undefined,  
34 - enabled: undefined,  
35 - createTime: undefined, 30 + name: undefined,
  31 + birthday: undefined,
  32 + sex: undefined,
  33 + enabled: undefined,
  34 + createTime: undefined,
36 }) 35 })
37 const queryFormRef = ref() // 搜索的表单 36 const queryFormRef = ref() // 搜索的表单
38 const exportLoading = ref(false) // 导出的加载中 37 const exportLoading = ref(false) // 导出的加载中
@@ -126,7 +125,14 @@ function handleRowCheckboxChange({ @@ -126,7 +125,14 @@ function handleRowCheckboxChange({
126 async function handleExport() { 125 async function handleExport() {
127 try { 126 try {
128 exportLoading.value = true; 127 exportLoading.value = true;
129 - const data = await exportStudent(queryParams); 128 + const params = cloneDeep(queryParams) as any;
  129 + if (params.birthday && Array.isArray(params.birthday)) {
  130 + params.birthday = (params.birthday as string[]).join(',');
  131 + }
  132 + if (params.createTime && Array.isArray(params.createTime)) {
  133 + params.createTime = (params.createTime as string[]).join(',');
  134 + }
  135 + const data = await exportStudent(params);
130 downloadFileFromBlobPart({ fileName: '学生.xls', source: data }); 136 downloadFileFromBlobPart({ fileName: '学生.xls', source: data });
131 }finally { 137 }finally {
132 exportLoading.value = false; 138 exportLoading.value = false;
@@ -148,61 +154,61 @@ onMounted(() =&gt; { @@ -148,61 +154,61 @@ onMounted(() =&gt; {
148 154
149 <ContentWrap v-if="!hiddenSearchBar"> 155 <ContentWrap v-if="!hiddenSearchBar">
150 <!-- 搜索工作栏 --> 156 <!-- 搜索工作栏 -->
151 - <el-form 157 + <ElForm
152 :model="queryParams" 158 :model="queryParams"
153 ref="queryFormRef" 159 ref="queryFormRef"
154 inline 160 inline
155 > 161 >
156 - <el-form-item label="名字">  
157 - <el-input 162 + <ElFormItem label="名字">
  163 + <ElInput
158 v-model="queryParams.name" 164 v-model="queryParams.name"
159 placeholder="请输入名字" 165 placeholder="请输入名字"
160 clearable 166 clearable
161 @keyup.enter="handleQuery" 167 @keyup.enter="handleQuery"
162 class="!w-[240px]" 168 class="!w-[240px]"
163 /> 169 />
164 - </el-form-item>  
165 - <el-form-item label="出生日期">  
166 - <el-date-picker 170 + </ElFormItem>
  171 + <ElFormItem label="出生日期">
  172 + <ElDatePicker
167 v-model="queryParams.birthday" 173 v-model="queryParams.birthday"
168 value-format="YYYY-MM-DD" 174 value-format="YYYY-MM-DD"
169 placeholder="选择出生日期" 175 placeholder="选择出生日期"
170 clearable 176 clearable
171 class="!w-[240px]" 177 class="!w-[240px]"
172 /> 178 />
173 - </el-form-item>  
174 - <el-form-item label="性别">  
175 - <el-select 179 + </ElFormItem>
  180 + <ElFormItem label="性别">
  181 + <ElSelect
176 v-model="queryParams.sex" 182 v-model="queryParams.sex"
177 placeholder="请选择性别" 183 placeholder="请选择性别"
178 clearable 184 clearable
179 class="!w-[240px]" 185 class="!w-[240px]"
180 > 186 >
181 - <el-option 187 + <ElOption
182 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')" 188 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
183 :key="dict.value" 189 :key="dict.value"
184 :value="dict.value" 190 :value="dict.value"
185 :label="dict.label" 191 :label="dict.label"
186 /> 192 />
187 - </el-select>  
188 - </el-form-item>  
189 - <el-form-item label="是否有效">  
190 - <el-select 193 + </ElSelect>
  194 + </ElFormItem>
  195 + <ElFormItem label="是否有效">
  196 + <ElSelect
191 v-model="queryParams.enabled" 197 v-model="queryParams.enabled"
192 placeholder="请选择是否有效" 198 placeholder="请选择是否有效"
193 clearable 199 clearable
194 class="!w-[240px]" 200 class="!w-[240px]"
195 > 201 >
196 - <el-option 202 + <ElOption
197 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')" 203 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
198 :key="dict.value" 204 :key="dict.value"
199 :value="dict.value" 205 :value="dict.value"
200 :label="dict.label" 206 :label="dict.label"
201 /> 207 />
202 - </el-select>  
203 - </el-form-item>  
204 - <el-form-item label="创建时间">  
205 - <el-date-picker 208 + </ElSelect>
  209 + </ElFormItem>
  210 + <ElFormItem label="创建时间">
  211 + <ElDatePicker
206 v-model="queryParams.createTime" 212 v-model="queryParams.createTime"
207 type="daterange" 213 type="daterange"
208 value-format="YYYY-MM-DD" 214 value-format="YYYY-MM-DD"
@@ -211,24 +217,24 @@ onMounted(() =&gt; { @@ -211,24 +217,24 @@ onMounted(() =&gt; {
211 end-placeholder="结束日期" 217 end-placeholder="结束日期"
212 class="!w-[240px]" 218 class="!w-[240px]"
213 /> 219 />
214 - </el-form-item>  
215 - <el-form-item>  
216 - <el-button class="ml-2" @click="resetQuery"> 重置 </el-button>  
217 - <el-button class="ml-2" @click="handleQuery" type="primary"> 220 + </ElFormItem>
  221 + <ElFormItem>
  222 + <ElButton class="ml-2" @click="resetQuery"> 重置 </ElButton>
  223 + <ElButton class="ml-2" @click="handleQuery" type="primary">
218 搜索 224 搜索
219 - </el-button>  
220 - </el-form-item>  
221 - </el-form> 225 + </ElButton>
  226 + </ElFormItem>
  227 + </ElForm>
222 </ContentWrap> 228 </ContentWrap>
223 229
224 <!-- 列表 --> 230 <!-- 列表 -->
225 <ContentWrap title="学生"> 231 <ContentWrap title="学生">
226 <template #extra> 232 <template #extra>
227 - <TableToolbar 233 + <VbenVxeTableToolbar
228 ref="tableToolbarRef" 234 ref="tableToolbarRef"
229 v-model:hidden-search="hiddenSearchBar" 235 v-model:hidden-search="hiddenSearchBar"
230 > 236 >
231 - <el-button 237 + <ElButton
232 class="ml-2" 238 class="ml-2"
233 :icon="h(Plus)" 239 :icon="h(Plus)"
234 type="primary" 240 type="primary"
@@ -236,8 +242,8 @@ onMounted(() =&gt; { @@ -236,8 +242,8 @@ onMounted(() =&gt; {
236 v-access:code="['infra:student:create']" 242 v-access:code="['infra:student:create']"
237 > 243 >
238 {{ $t('ui.actionTitle.create', ['学生']) }} 244 {{ $t('ui.actionTitle.create', ['学生']) }}
239 - </el-button>  
240 - <el-button 245 + </ElButton>
  246 + <ElButton
241 :icon="h(Download)" 247 :icon="h(Download)"
242 type="primary" 248 type="primary"
243 class="ml-2" 249 class="ml-2"
@@ -246,8 +252,8 @@ onMounted(() =&gt; { @@ -246,8 +252,8 @@ onMounted(() =&gt; {
246 v-access:code="['infra:student:export']" 252 v-access:code="['infra:student:export']"
247 > 253 >
248 {{ $t('ui.actionTitle.export') }} 254 {{ $t('ui.actionTitle.export') }}
249 - </el-button>  
250 - <el-button 255 + </ElButton>
  256 + <ElButton
251 :icon="h(Trash2)" 257 :icon="h(Trash2)"
252 type="danger" 258 type="danger"
253 class="ml-2" 259 class="ml-2"
@@ -256,10 +262,10 @@ onMounted(() =&gt; { @@ -256,10 +262,10 @@ onMounted(() =&gt; {
256 v-access:code="['infra:student:delete']" 262 v-access:code="['infra:student:delete']"
257 > 263 >
258 批量删除 264 批量删除
259 - </el-button>  
260 - </TableToolbar> 265 + </ElButton>
  266 + </VbenVxeTableToolbar>
261 </template> 267 </template>
262 - <vxe-table 268 + <VxeTable
263 ref="tableRef" 269 ref="tableRef"
264 :data="list" 270 :data="list"
265 show-overflow 271 show-overflow
@@ -267,36 +273,36 @@ onMounted(() =&gt; { @@ -267,36 +273,36 @@ onMounted(() =&gt; {
267 @checkboxAll="handleRowCheckboxChange" 273 @checkboxAll="handleRowCheckboxChange"
268 @checkboxChange="handleRowCheckboxChange" 274 @checkboxChange="handleRowCheckboxChange"
269 > 275 >
270 - <vxe-column type="checkbox" width="40"></vxe-column>  
271 - <vxe-column field="id" title="编号" align="center" />  
272 - <vxe-column field="name" title="名字" align="center" />  
273 - <vxe-column field="description" title="简介" align="center" />  
274 - <vxe-column field="birthday" title="出生日期" align="center"> 276 + <VxeColumn type="checkbox" width="40"></VxeColumn>
  277 + <VxeColumn field="id" title="编号" align="center" />
  278 + <VxeColumn field="name" title="名字" align="center" />
  279 + <VxeColumn field="description" title="简介" align="center" />
  280 + <VxeColumn field="birthday" title="出生日期" align="center">
275 <template #default="{row}"> 281 <template #default="{row}">
276 {{formatDateTime(row.birthday)}} 282 {{formatDateTime(row.birthday)}}
277 </template> 283 </template>
278 - </vxe-column>  
279 - <vxe-column field="sex" title="性别" align="center"> 284 + </VxeColumn>
  285 + <VxeColumn field="sex" title="性别" align="center">
280 <template #default="{row}"> 286 <template #default="{row}">
281 <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" /> 287 <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" />
282 </template> 288 </template>
283 - </vxe-column>  
284 - <vxe-column field="enabled" title="是否有效" align="center"> 289 + </VxeColumn>
  290 + <VxeColumn field="enabled" title="是否有效" align="center">
285 <template #default="{row}"> 291 <template #default="{row}">
286 <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.enabled" /> 292 <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.enabled" />
287 </template> 293 </template>
288 - </vxe-column>  
289 - <vxe-column field="avatar" title="头像" align="center" />  
290 - <vxe-column field="video" title="附件" align="center" />  
291 - <vxe-column field="memo" title="备注" align="center" />  
292 - <vxe-column field="createTime" title="创建时间" align="center"> 294 + </VxeColumn>
  295 + <VxeColumn field="avatar" title="头像" align="center" />
  296 + <VxeColumn field="video" title="附件" align="center" />
  297 + <VxeColumn field="memo" title="备注" align="center" />
  298 + <VxeColumn field="createTime" title="创建时间" align="center">
293 <template #default="{row}"> 299 <template #default="{row}">
294 {{formatDateTime(row.createTime)}} 300 {{formatDateTime(row.createTime)}}
295 </template> 301 </template>
296 - </vxe-column>  
297 - <vxe-column field="operation" title="操作" align="center"> 302 + </VxeColumn>
  303 + <VxeColumn field="operation" title="操作" align="center">
298 <template #default="{row}"> 304 <template #default="{row}">
299 - <el-button 305 + <ElButton
300 size="small" 306 size="small"
301 type="primary" 307 type="primary"
302 link 308 link
@@ -304,8 +310,8 @@ onMounted(() =&gt; { @@ -304,8 +310,8 @@ onMounted(() =&gt; {
304 v-access:code="['infra:student:update']" 310 v-access:code="['infra:student:update']"
305 > 311 >
306 {{ $t('ui.actionTitle.edit') }} 312 {{ $t('ui.actionTitle.edit') }}
307 - </el-button>  
308 - <el-button 313 + </ElButton>
  314 + <ElButton
309 size="small" 315 size="small"
310 type="danger" 316 type="danger"
311 link 317 link
@@ -314,13 +320,13 @@ onMounted(() =&gt; { @@ -314,13 +320,13 @@ onMounted(() =&gt; {
314 v-access:code="['infra:student:delete']" 320 v-access:code="['infra:student:delete']"
315 > 321 >
316 {{ $t('ui.actionTitle.delete') }} 322 {{ $t('ui.actionTitle.delete') }}
317 - </el-button> 323 + </ElButton>
318 </template> 324 </template>
319 - </vxe-column>  
320 - </vxe-table> 325 + </VxeColumn>
  326 + </VxeTable>
321 <!-- 分页 --> 327 <!-- 分页 -->
322 <div class="mt-2 flex justify-end"> 328 <div class="mt-2 flex justify-end">
323 - <el-pagination 329 + <ElPagination
324 :total="total" 330 :total="total"
325 v-model:current-page="queryParams.pageNo" 331 v-model:current-page="queryParams.pageNo"
326 v-model:page-size="queryParams.pageSize" 332 v-model:page-size="queryParams.pageSize"
yudao-module-infra/src/test/resources/codegen/vben5_ele_general_one_importEnable/vue/form
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 -import type { StudentApi } from '#/api/infra/student'; 2 +import type { StudentApi } from '#/api/infra/demo';
3 import type { FormRules } from 'element-plus'; 3 import type { FormRules } from 'element-plus';
4 4
5 import { useVbenModal } from '@vben/common-ui'; 5 import { useVbenModal } from '@vben/common-ui';
@@ -11,7 +11,7 @@ import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElSelect, El @@ -11,7 +11,7 @@ import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElSelect, El
11 11
12 import { computed, ref, reactive } from 'vue'; 12 import { computed, ref, reactive } from 'vue';
13 import { $t } from '#/locales'; 13 import { $t } from '#/locales';
14 -import { getStudent, createStudent, updateStudent } from '#/api/infra/student'; 14 +import { getStudent, createStudent, updateStudent } from '#/api/infra/demo';
15 15
16 const emit = defineEmits(['success']); 16 const emit = defineEmits(['success']);
17 17
@@ -102,56 +102,56 @@ const [Modal, modalApi] = useVbenModal({ @@ -102,56 +102,56 @@ const [Modal, modalApi] = useVbenModal({
102 102
103 <template> 103 <template>
104 <Modal :title="getTitle"> 104 <Modal :title="getTitle">
105 - <el-form 105 + <ElForm
106 ref="formRef" 106 ref="formRef"
107 :model="formData" 107 :model="formData"
108 :rules="rules" 108 :rules="rules"
109 label-width="120px" 109 label-width="120px"
110 label-position="right" 110 label-position="right"
111 > 111 >
112 - <el-form-item label="名字" prop="name">  
113 - <el-input v-model="formData.name" placeholder="请输入名字" />  
114 - </el-form-item>  
115 - <el-form-item label="简介" prop="description">  
116 - <el-input v-model="formData.description" type="textarea" placeholder="请输入简介" />  
117 - </el-form-item>  
118 - <el-form-item label="出生日期" prop="birthday">  
119 - <el-date-picker 112 + <ElFormItem label="名字" prop="name">
  113 + <ElInput v-model="formData.name" placeholder="请输入名字" />
  114 + </ElFormItem>
  115 + <ElFormItem label="简介" prop="description">
  116 + <ElInput v-model="formData.description" type="textarea" placeholder="请输入简介" />
  117 + </ElFormItem>
  118 + <ElFormItem label="出生日期" prop="birthday">
  119 + <ElDatePicker
120 v-model="formData.birthday" 120 v-model="formData.birthday"
121 value-format="x" 121 value-format="x"
122 placeholder="选择出生日期" 122 placeholder="选择出生日期"
123 /> 123 />
124 - </el-form-item>  
125 - <el-form-item label="性别" prop="sex">  
126 - <el-select v-model="formData.sex" placeholder="请选择性别">  
127 - <el-option 124 + </ElFormItem>
  125 + <ElFormItem label="性别" prop="sex">
  126 + <ElSelect v-model="formData.sex" placeholder="请选择性别">
  127 + <ElOption
128 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')" 128 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
129 :key="dict.value" 129 :key="dict.value"
130 :value="dict.value" 130 :value="dict.value"
131 :label="dict.label" 131 :label="dict.label"
132 /> 132 />
133 - </el-select>  
134 - </el-form-item>  
135 - <el-form-item label="是否有效" prop="enabled">  
136 - <el-radio-group v-model="formData.enabled">  
137 - <el-radio 133 + </ElSelect>
  134 + </ElFormItem>
  135 + <ElFormItem label="是否有效" prop="enabled">
  136 + <ElRadioGroup v-model="formData.enabled">
  137 + <ElRadio
138 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')" 138 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
139 :key="dict.value" 139 :key="dict.value"
140 - :label="dict.value" 140 + :value="dict.value"
141 > 141 >
142 {{ dict.label }} 142 {{ dict.label }}
143 - </el-radio>  
144 - </el-radio-group>  
145 - </el-form-item>  
146 - <el-form-item label="头像" prop="avatar"> 143 + </ElRadio>
  144 + </ElRadioGroup>
  145 + </ElFormItem>
  146 + <ElFormItem label="头像" prop="avatar">
147 <ImageUpload v-model="formData.avatar" /> 147 <ImageUpload v-model="formData.avatar" />
148 - </el-form-item>  
149 - <el-form-item label="附件" prop="video"> 148 + </ElFormItem>
  149 + <ElFormItem label="附件" prop="video">
150 <FileUpload v-model="formData.video" /> 150 <FileUpload v-model="formData.video" />
151 - </el-form-item>  
152 - <el-form-item label="备注" prop="memo"> 151 + </ElFormItem>
  152 + <ElFormItem label="备注" prop="memo">
153 <RichTextarea v-model="formData.memo" height="500px" /> 153 <RichTextarea v-model="formData.memo" height="500px" />
154 - </el-form-item>  
155 - </el-form> 154 + </ElFormItem>
  155 + </ElForm>
156 </Modal> 156 </Modal>
157 </template> 157 </template>
yudao-module-infra/src/test/resources/codegen/vben5_ele_general_one_importEnable/vue/import-form
@@ -47,6 +47,16 @@ function handleChange(file: any) { @@ -47,6 +47,16 @@ function handleChange(file: any) {
47 } 47 }
48 } 48 }
49 49
  50 +/** 移除文件 */
  51 +function handleRemove() {
  52 + fileRef.value = null;
  53 +}
  54 +
  55 +/** 超出文件数量 */
  56 +function handleExceed() {
  57 + ElMessage.warning('只能选择一个文件,请先移除已选文件');
  58 +}
  59 +
50 /** 下载导入模板 */ 60 /** 下载导入模板 */
51 async function handleDownload() { 61 async function handleDownload() {
52 const data = await importStudentTemplate(); 62 const data = await importStudentTemplate();
@@ -61,6 +71,8 @@ async function handleDownload() { @@ -61,6 +71,8 @@ async function handleDownload() {
61 :limit="1" 71 :limit="1"
62 accept=".xls,.xlsx" 72 accept=".xls,.xlsx"
63 :on-change="handleChange" 73 :on-change="handleChange"
  74 + :on-remove="handleRemove"
  75 + :on-exceed="handleExceed"
64 :auto-upload="false" 76 :auto-upload="false"
65 > 77 >
66 <ElButton type="primary"> 选择 Excel 文件 </ElButton> 78 <ElButton type="primary"> 选择 Excel 文件 </ElButton>
yudao-module-infra/src/test/resources/codegen/vben5_ele_general_one_importEnable/vue/index
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 -import type { StudentApi } from '#/api/infra/student'; 2 +import type { StudentApi } from '#/api/infra/demo';
3 3
4 import { ref, h, reactive, onMounted, nextTick } from 'vue'; 4 import { ref, h, reactive, onMounted, nextTick } from 'vue';
5 5
@@ -17,7 +17,7 @@ import { VxeColumn, VxeTable } from &#39;#/adapter/vxe-table&#39;; @@ -17,7 +17,7 @@ import { VxeColumn, VxeTable } from &#39;#/adapter/vxe-table&#39;;
17 17
18 import { $t } from '#/locales'; 18 import { $t } from '#/locales';
19 import { isEmpty } from '@vben/utils'; 19 import { isEmpty } from '@vben/utils';
20 -import { getStudentPage, deleteStudent, deleteStudentList, exportStudent } from '#/api/infra/student'; 20 +import { getStudentPage, deleteStudent, deleteStudentList, exportStudent } from '#/api/infra/demo';
21 import StudentImport from './modules/import-form.vue'; 21 import StudentImport from './modules/import-form.vue';
22 22
23 23
@@ -28,12 +28,11 @@ const total = ref(0) // 列表的总页数 @@ -28,12 +28,11 @@ const total = ref(0) // 列表的总页数
28 const queryParams = reactive({ 28 const queryParams = reactive({
29 pageNo: 1, 29 pageNo: 1,
30 pageSize: 10, 30 pageSize: 10,
31 - name: undefined,  
32 - birthday: undefined,  
33 - birthday: undefined,  
34 - sex: undefined,  
35 - enabled: undefined,  
36 - createTime: undefined, 31 + name: undefined,
  32 + birthday: undefined,
  33 + sex: undefined,
  34 + enabled: undefined,
  35 + createTime: undefined,
37 }) 36 })
38 const queryFormRef = ref() // 搜索的表单 37 const queryFormRef = ref() // 搜索的表单
39 const exportLoading = ref(false) // 导出的加载中 38 const exportLoading = ref(false) // 导出的加载中
@@ -136,7 +135,14 @@ function handleRowCheckboxChange({ @@ -136,7 +135,14 @@ function handleRowCheckboxChange({
136 async function handleExport() { 135 async function handleExport() {
137 try { 136 try {
138 exportLoading.value = true; 137 exportLoading.value = true;
139 - const data = await exportStudent(queryParams); 138 + const params = cloneDeep(queryParams) as any;
  139 + if (params.birthday && Array.isArray(params.birthday)) {
  140 + params.birthday = (params.birthday as string[]).join(',');
  141 + }
  142 + if (params.createTime && Array.isArray(params.createTime)) {
  143 + params.createTime = (params.createTime as string[]).join(',');
  144 + }
  145 + const data = await exportStudent(params);
140 downloadFileFromBlobPart({ fileName: '学生.xls', source: data }); 146 downloadFileFromBlobPart({ fileName: '学生.xls', source: data });
141 }finally { 147 }finally {
142 exportLoading.value = false; 148 exportLoading.value = false;
@@ -159,61 +165,61 @@ onMounted(() =&gt; { @@ -159,61 +165,61 @@ onMounted(() =&gt; {
159 165
160 <ContentWrap v-if="!hiddenSearchBar"> 166 <ContentWrap v-if="!hiddenSearchBar">
161 <!-- 搜索工作栏 --> 167 <!-- 搜索工作栏 -->
162 - <el-form 168 + <ElForm
163 :model="queryParams" 169 :model="queryParams"
164 ref="queryFormRef" 170 ref="queryFormRef"
165 inline 171 inline
166 > 172 >
167 - <el-form-item label="名字">  
168 - <el-input 173 + <ElFormItem label="名字">
  174 + <ElInput
169 v-model="queryParams.name" 175 v-model="queryParams.name"
170 placeholder="请输入名字" 176 placeholder="请输入名字"
171 clearable 177 clearable
172 @keyup.enter="handleQuery" 178 @keyup.enter="handleQuery"
173 class="!w-[240px]" 179 class="!w-[240px]"
174 /> 180 />
175 - </el-form-item>  
176 - <el-form-item label="出生日期">  
177 - <el-date-picker 181 + </ElFormItem>
  182 + <ElFormItem label="出生日期">
  183 + <ElDatePicker
178 v-model="queryParams.birthday" 184 v-model="queryParams.birthday"
179 value-format="YYYY-MM-DD" 185 value-format="YYYY-MM-DD"
180 placeholder="选择出生日期" 186 placeholder="选择出生日期"
181 clearable 187 clearable
182 class="!w-[240px]" 188 class="!w-[240px]"
183 /> 189 />
184 - </el-form-item>  
185 - <el-form-item label="性别">  
186 - <el-select 190 + </ElFormItem>
  191 + <ElFormItem label="性别">
  192 + <ElSelect
187 v-model="queryParams.sex" 193 v-model="queryParams.sex"
188 placeholder="请选择性别" 194 placeholder="请选择性别"
189 clearable 195 clearable
190 class="!w-[240px]" 196 class="!w-[240px]"
191 > 197 >
192 - <el-option 198 + <ElOption
193 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')" 199 v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
194 :key="dict.value" 200 :key="dict.value"
195 :value="dict.value" 201 :value="dict.value"
196 :label="dict.label" 202 :label="dict.label"
197 /> 203 />
198 - </el-select>  
199 - </el-form-item>  
200 - <el-form-item label="是否有效">  
201 - <el-select 204 + </ElSelect>
  205 + </ElFormItem>
  206 + <ElFormItem label="是否有效">
  207 + <ElSelect
202 v-model="queryParams.enabled" 208 v-model="queryParams.enabled"
203 placeholder="请选择是否有效" 209 placeholder="请选择是否有效"
204 clearable 210 clearable
205 class="!w-[240px]" 211 class="!w-[240px]"
206 > 212 >
207 - <el-option 213 + <ElOption
208 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')" 214 v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
209 :key="dict.value" 215 :key="dict.value"
210 :value="dict.value" 216 :value="dict.value"
211 :label="dict.label" 217 :label="dict.label"
212 /> 218 />
213 - </el-select>  
214 - </el-form-item>  
215 - <el-form-item label="创建时间">  
216 - <el-date-picker 219 + </ElSelect>
  220 + </ElFormItem>
  221 + <ElFormItem label="创建时间">
  222 + <ElDatePicker
217 v-model="queryParams.createTime" 223 v-model="queryParams.createTime"
218 type="daterange" 224 type="daterange"
219 value-format="YYYY-MM-DD" 225 value-format="YYYY-MM-DD"
@@ -222,24 +228,24 @@ onMounted(() =&gt; { @@ -222,24 +228,24 @@ onMounted(() =&gt; {
222 end-placeholder="结束日期" 228 end-placeholder="结束日期"
223 class="!w-[240px]" 229 class="!w-[240px]"
224 /> 230 />
225 - </el-form-item>  
226 - <el-form-item>  
227 - <el-button class="ml-2" @click="resetQuery"> 重置 </el-button>  
228 - <el-button class="ml-2" @click="handleQuery" type="primary"> 231 + </ElFormItem>
  232 + <ElFormItem>
  233 + <ElButton class="ml-2" @click="resetQuery"> 重置 </ElButton>
  234 + <ElButton class="ml-2" @click="handleQuery" type="primary">
229 搜索 235 搜索
230 - </el-button>  
231 - </el-form-item>  
232 - </el-form> 236 + </ElButton>
  237 + </ElFormItem>
  238 + </ElForm>
233 </ContentWrap> 239 </ContentWrap>
234 240
235 <!-- 列表 --> 241 <!-- 列表 -->
236 <ContentWrap title="学生"> 242 <ContentWrap title="学生">
237 <template #extra> 243 <template #extra>
238 - <TableToolbar 244 + <VbenVxeTableToolbar
239 ref="tableToolbarRef" 245 ref="tableToolbarRef"
240 v-model:hidden-search="hiddenSearchBar" 246 v-model:hidden-search="hiddenSearchBar"
241 > 247 >
242 - <el-button 248 + <ElButton
243 class="ml-2" 249 class="ml-2"
244 :icon="h(Plus)" 250 :icon="h(Plus)"
245 type="primary" 251 type="primary"
@@ -247,16 +253,16 @@ onMounted(() =&gt; { @@ -247,16 +253,16 @@ onMounted(() =&gt; {
247 v-access:code="['infra:student:create']" 253 v-access:code="['infra:student:create']"
248 > 254 >
249 {{ $t('ui.actionTitle.create', ['学生']) }} 255 {{ $t('ui.actionTitle.create', ['学生']) }}
250 - </el-button>  
251 - <el-button 256 + </ElButton>
  257 + <ElButton
252 class="ml-2" 258 class="ml-2"
253 type="primary" 259 type="primary"
254 @click="handleImport" 260 @click="handleImport"
255 v-access:code="['infra:student:import']" 261 v-access:code="['infra:student:import']"
256 > 262 >
257 导入 263 导入
258 - </el-button>  
259 - <el-button 264 + </ElButton>
  265 + <ElButton
260 :icon="h(Download)" 266 :icon="h(Download)"
261 type="primary" 267 type="primary"
262 class="ml-2" 268 class="ml-2"
@@ -265,8 +271,8 @@ onMounted(() =&gt; { @@ -265,8 +271,8 @@ onMounted(() =&gt; {
265 v-access:code="['infra:student:export']" 271 v-access:code="['infra:student:export']"
266 > 272 >
267 {{ $t('ui.actionTitle.export') }} 273 {{ $t('ui.actionTitle.export') }}
268 - </el-button>  
269 - <el-button 274 + </ElButton>
  275 + <ElButton
270 :icon="h(Trash2)" 276 :icon="h(Trash2)"
271 type="danger" 277 type="danger"
272 class="ml-2" 278 class="ml-2"
@@ -275,10 +281,10 @@ onMounted(() =&gt; { @@ -275,10 +281,10 @@ onMounted(() =&gt; {
275 v-access:code="['infra:student:delete']" 281 v-access:code="['infra:student:delete']"
276 > 282 >
277 批量删除 283 批量删除
278 - </el-button>  
279 - </TableToolbar> 284 + </ElButton>
  285 + </VbenVxeTableToolbar>
280 </template> 286 </template>
281 - <vxe-table 287 + <VxeTable
282 ref="tableRef" 288 ref="tableRef"
283 :data="list" 289 :data="list"
284 show-overflow 290 show-overflow
@@ -286,36 +292,36 @@ onMounted(() =&gt; { @@ -286,36 +292,36 @@ onMounted(() =&gt; {
286 @checkboxAll="handleRowCheckboxChange" 292 @checkboxAll="handleRowCheckboxChange"
287 @checkboxChange="handleRowCheckboxChange" 293 @checkboxChange="handleRowCheckboxChange"
288 > 294 >
289 - <vxe-column type="checkbox" width="40"></vxe-column>  
290 - <vxe-column field="id" title="编号" align="center" />  
291 - <vxe-column field="name" title="名字" align="center" />  
292 - <vxe-column field="description" title="简介" align="center" />  
293 - <vxe-column field="birthday" title="出生日期" align="center"> 295 + <VxeColumn type="checkbox" width="40"></VxeColumn>
  296 + <VxeColumn field="id" title="编号" align="center" />
  297 + <VxeColumn field="name" title="名字" align="center" />
  298 + <VxeColumn field="description" title="简介" align="center" />
  299 + <VxeColumn field="birthday" title="出生日期" align="center">
294 <template #default="{row}"> 300 <template #default="{row}">
295 {{formatDateTime(row.birthday)}} 301 {{formatDateTime(row.birthday)}}
296 </template> 302 </template>
297 - </vxe-column>  
298 - <vxe-column field="sex" title="性别" align="center"> 303 + </VxeColumn>
  304 + <VxeColumn field="sex" title="性别" align="center">
299 <template #default="{row}"> 305 <template #default="{row}">
300 <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" /> 306 <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" />
301 </template> 307 </template>
302 - </vxe-column>  
303 - <vxe-column field="enabled" title="是否有效" align="center"> 308 + </VxeColumn>
  309 + <VxeColumn field="enabled" title="是否有效" align="center">
304 <template #default="{row}"> 310 <template #default="{row}">
305 <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.enabled" /> 311 <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.enabled" />
306 </template> 312 </template>
307 - </vxe-column>  
308 - <vxe-column field="avatar" title="头像" align="center" />  
309 - <vxe-column field="video" title="附件" align="center" />  
310 - <vxe-column field="memo" title="备注" align="center" />  
311 - <vxe-column field="createTime" title="创建时间" align="center"> 313 + </VxeColumn>
  314 + <VxeColumn field="avatar" title="头像" align="center" />
  315 + <VxeColumn field="video" title="附件" align="center" />
  316 + <VxeColumn field="memo" title="备注" align="center" />
  317 + <VxeColumn field="createTime" title="创建时间" align="center">
312 <template #default="{row}"> 318 <template #default="{row}">
313 {{formatDateTime(row.createTime)}} 319 {{formatDateTime(row.createTime)}}
314 </template> 320 </template>
315 - </vxe-column>  
316 - <vxe-column field="operation" title="操作" align="center"> 321 + </VxeColumn>
  322 + <VxeColumn field="operation" title="操作" align="center">
317 <template #default="{row}"> 323 <template #default="{row}">
318 - <el-button 324 + <ElButton
319 size="small" 325 size="small"
320 type="primary" 326 type="primary"
321 link 327 link
@@ -323,8 +329,8 @@ onMounted(() =&gt; { @@ -323,8 +329,8 @@ onMounted(() =&gt; {
323 v-access:code="['infra:student:update']" 329 v-access:code="['infra:student:update']"
324 > 330 >
325 {{ $t('ui.actionTitle.edit') }} 331 {{ $t('ui.actionTitle.edit') }}
326 - </el-button>  
327 - <el-button 332 + </ElButton>
  333 + <ElButton
328 size="small" 334 size="small"
329 type="danger" 335 type="danger"
330 link 336 link
@@ -333,13 +339,13 @@ onMounted(() =&gt; { @@ -333,13 +339,13 @@ onMounted(() =&gt; {
333 v-access:code="['infra:student:delete']" 339 v-access:code="['infra:student:delete']"
334 > 340 >
335 {{ $t('ui.actionTitle.delete') }} 341 {{ $t('ui.actionTitle.delete') }}
336 - </el-button> 342 + </ElButton>
337 </template> 343 </template>
338 - </vxe-column>  
339 - </vxe-table> 344 + </VxeColumn>
  345 + </VxeTable>
340 <!-- 分页 --> 346 <!-- 分页 -->
341 <div class="mt-2 flex justify-end"> 347 <div class="mt-2 flex justify-end">
342 - <el-pagination 348 + <ElPagination
343 :total="total" 349 :total="total"
344 v-model:current-page="queryParams.pageNo" 350 v-model:current-page="queryParams.pageNo"
345 v-model:page-size="queryParams.pageSize" 351 v-model:page-size="queryParams.pageSize"
yudao-module-infra/src/test/resources/codegen/vben5_ele_general_tree/vue/form
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 -import type { CategoryApi } from '#/api/infra/category'; 2 +import type { CategoryApi } from '#/api/infra/demo';
3 import type { FormRules } from 'element-plus'; 3 import type { FormRules } from 'element-plus';
4 4
5 import { useVbenModal } from '@vben/common-ui'; 5 import { useVbenModal } from '@vben/common-ui';
@@ -7,12 +7,12 @@ import { getDictOptions } from &#39;@vben/hooks&#39;; @@ -7,12 +7,12 @@ import { getDictOptions } from &#39;@vben/hooks&#39;;
7 import { Tinymce as RichTextarea } from '#/components/tinymce'; 7 import { Tinymce as RichTextarea } from '#/components/tinymce';
8 import { ImageUpload, FileUpload } from "#/components/upload"; 8 import { ImageUpload, FileUpload } from "#/components/upload";
9 import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElSelect, ElOption, ElRadioGroup, ElRadio, ElCheckboxGroup, ElCheckbox, ElDatePicker, ElTreeSelect } from 'element-plus'; 9 import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElSelect, ElOption, ElRadioGroup, ElRadio, ElCheckboxGroup, ElCheckbox, ElDatePicker, ElTreeSelect } from 'element-plus';
10 -import { getCategoryList } from '#/api/infra/category'; 10 +import { getCategoryList } from '#/api/infra/demo';
11 import { handleTree } from '@vben/utils' 11 import { handleTree } from '@vben/utils'
12 12
13 import { computed, ref, reactive } from 'vue'; 13 import { computed, ref, reactive } from 'vue';
14 import { $t } from '#/locales'; 14 import { $t } from '#/locales';
15 -import { getCategory, createCategory, updateCategory } from '#/api/infra/category'; 15 +import { getCategory, createCategory, updateCategory } from '#/api/infra/demo';
16 16
17 const emit = defineEmits(['success']); 17 const emit = defineEmits(['success']);
18 18
@@ -98,18 +98,18 @@ const [Modal, modalApi] = useVbenModal({ @@ -98,18 +98,18 @@ const [Modal, modalApi] = useVbenModal({
98 98
99 <template> 99 <template>
100 <Modal :title="getTitle"> 100 <Modal :title="getTitle">
101 - <el-form 101 + <ElForm
102 ref="formRef" 102 ref="formRef"
103 :model="formData" 103 :model="formData"
104 :rules="rules" 104 :rules="rules"
105 label-width="120px" 105 label-width="120px"
106 label-position="right" 106 label-position="right"
107 > 107 >
108 - <el-form-item label="名字" prop="name">  
109 - <el-input v-model="formData.name" placeholder="请输入名字" />  
110 - </el-form-item>  
111 - <el-form-item label="父编号" prop="parentId">  
112 - <el-tree-select 108 + <ElFormItem label="名字" prop="name">
  109 + <ElInput v-model="formData.name" placeholder="请输入名字" />
  110 + </ElFormItem>
  111 + <ElFormItem label="父编号" prop="parentId">
  112 + <ElTreeSelect
113 v-model="formData.parentId" 113 v-model="formData.parentId"
114 :data="categoryTree" 114 :data="categoryTree"
115 :props="{ 115 :props="{
@@ -121,7 +121,7 @@ const [Modal, modalApi] = useVbenModal({ @@ -121,7 +121,7 @@ const [Modal, modalApi] = useVbenModal({
121 default-expand-all 121 default-expand-all
122 placeholder="请选择父编号" 122 placeholder="请选择父编号"
123 /> 123 />
124 - </el-form-item>  
125 - </el-form> 124 + </ElFormItem>
  125 + </ElForm>
126 </Modal> 126 </Modal>
127 </template> 127 </template>
128 \ No newline at end of file 128 \ No newline at end of file
yudao-module-infra/src/test/resources/codegen/vben5_ele_general_tree/vue/index
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 -import type { CategoryApi } from '#/api/infra/category'; 2 +import type { CategoryApi } from '#/api/infra/demo';
3 3
4 import { ref, h, reactive, onMounted, nextTick } from 'vue'; 4 import { ref, h, reactive, onMounted, nextTick } from 'vue';
5 5
@@ -16,14 +16,14 @@ import { VxeColumn, VxeTable } from &#39;#/adapter/vxe-table&#39;; @@ -16,14 +16,14 @@ import { VxeColumn, VxeTable } from &#39;#/adapter/vxe-table&#39;;
16 16
17 import { $t } from '#/locales'; 17 import { $t } from '#/locales';
18 import { handleTree,isEmpty } from '@vben/utils' 18 import { handleTree,isEmpty } from '@vben/utils'
19 -import { getCategoryList, deleteCategory, exportCategory } from '#/api/infra/category'; 19 +import { getCategoryList, deleteCategory, exportCategory } from '#/api/infra/demo';
20 20
21 21
22 const loading = ref(true) // 列表的加载中 22 const loading = ref(true) // 列表的加载中
23 const list = ref<any[]>([]) // 树列表的数据 23 const list = ref<any[]>([]) // 树列表的数据
24 24
25 const queryParams = reactive({ 25 const queryParams = reactive({
26 - name: undefined, 26 + name: undefined,
27 }) 27 })
28 const queryFormRef = ref() // 搜索的表单 28 const queryFormRef = ref() // 搜索的表单
29 const exportLoading = ref(false) // 导出的加载中 29 const exportLoading = ref(false) // 导出的加载中
@@ -89,7 +89,8 @@ async function handleDelete(row: CategoryApi.Category) { @@ -89,7 +89,8 @@ async function handleDelete(row: CategoryApi.Category) {
89 async function handleExport() { 89 async function handleExport() {
90 try { 90 try {
91 exportLoading.value = true; 91 exportLoading.value = true;
92 - const data = await exportCategory(queryParams); 92 + const params = cloneDeep(queryParams) as any;
  93 + const data = await exportCategory(params);
93 downloadFileFromBlobPart({ fileName: '分类.xls', source: data }); 94 downloadFileFromBlobPart({ fileName: '分类.xls', source: data });
94 }finally { 95 }finally {
95 exportLoading.value = false; 96 exportLoading.value = false;
@@ -117,40 +118,40 @@ onMounted(() =&gt; { @@ -117,40 +118,40 @@ onMounted(() =&gt; {
117 118
118 <ContentWrap v-if="!hiddenSearchBar"> 119 <ContentWrap v-if="!hiddenSearchBar">
119 <!-- 搜索工作栏 --> 120 <!-- 搜索工作栏 -->
120 - <el-form 121 + <ElForm
121 :model="queryParams" 122 :model="queryParams"
122 ref="queryFormRef" 123 ref="queryFormRef"
123 inline 124 inline
124 > 125 >
125 - <el-form-item label="名字">  
126 - <el-input 126 + <ElFormItem label="名字">
  127 + <ElInput
127 v-model="queryParams.name" 128 v-model="queryParams.name"
128 placeholder="请输入名字" 129 placeholder="请输入名字"
129 clearable 130 clearable
130 @keyup.enter="handleQuery" 131 @keyup.enter="handleQuery"
131 class="!w-[240px]" 132 class="!w-[240px]"
132 /> 133 />
133 - </el-form-item>  
134 - <el-form-item>  
135 - <el-button class="ml-2" @click="resetQuery"> 重置 </el-button>  
136 - <el-button class="ml-2" @click="handleQuery" type="primary"> 134 + </ElFormItem>
  135 + <ElFormItem>
  136 + <ElButton class="ml-2" @click="resetQuery"> 重置 </ElButton>
  137 + <ElButton class="ml-2" @click="handleQuery" type="primary">
137 搜索 138 搜索
138 - </el-button>  
139 - </el-form-item>  
140 - </el-form> 139 + </ElButton>
  140 + </ElFormItem>
  141 + </ElForm>
141 </ContentWrap> 142 </ContentWrap>
142 143
143 <!-- 列表 --> 144 <!-- 列表 -->
144 <ContentWrap title="分类"> 145 <ContentWrap title="分类">
145 <template #extra> 146 <template #extra>
146 - <TableToolbar 147 + <VbenVxeTableToolbar
147 ref="tableToolbarRef" 148 ref="tableToolbarRef"
148 v-model:hidden-search="hiddenSearchBar" 149 v-model:hidden-search="hiddenSearchBar"
149 > 150 >
150 - <el-button @click="handleExpand" class="mr-2"> 151 + <ElButton @click="handleExpand" class="mr-2">
151 {{ isExpanded ? '收缩' : '展开' }} 152 {{ isExpanded ? '收缩' : '展开' }}
152 - </el-button>  
153 - <el-button 153 + </ElButton>
  154 + <ElButton
154 class="ml-2" 155 class="ml-2"
155 :icon="h(Plus)" 156 :icon="h(Plus)"
156 type="primary" 157 type="primary"
@@ -158,8 +159,8 @@ onMounted(() =&gt; { @@ -158,8 +159,8 @@ onMounted(() =&gt; {
158 v-access:code="['infra:category:create']" 159 v-access:code="['infra:category:create']"
159 > 160 >
160 {{ $t('ui.actionTitle.create', ['分类']) }} 161 {{ $t('ui.actionTitle.create', ['分类']) }}
161 - </el-button>  
162 - <el-button 162 + </ElButton>
  163 + <ElButton
163 :icon="h(Download)" 164 :icon="h(Download)"
164 type="primary" 165 type="primary"
165 class="ml-2" 166 class="ml-2"
@@ -168,10 +169,10 @@ onMounted(() =&gt; { @@ -168,10 +169,10 @@ onMounted(() =&gt; {
168 v-access:code="['infra:category:export']" 169 v-access:code="['infra:category:export']"
169 > 170 >
170 {{ $t('ui.actionTitle.export') }} 171 {{ $t('ui.actionTitle.export') }}
171 - </el-button>  
172 - </TableToolbar> 172 + </ElButton>
  173 + </VbenVxeTableToolbar>
173 </template> 174 </template>
174 - <vxe-table 175 + <VxeTable
175 ref="tableRef" 176 ref="tableRef"
176 :data="list" 177 :data="list"
177 :tree-config="{ 178 :tree-config="{
@@ -184,12 +185,12 @@ onMounted(() =&gt; { @@ -184,12 +185,12 @@ onMounted(() =&gt; {
184 show-overflow 185 show-overflow
185 :loading="loading" 186 :loading="loading"
186 > 187 >
187 - <vxe-column field="id" title="编号" align="center" />  
188 - <vxe-column field="name" title="名字" align="center" tree-node/>  
189 - <vxe-column field="parentId" title="父编号" align="center" />  
190 - <vxe-column field="operation" title="操作" align="center"> 188 + <VxeColumn field="id" title="编号" align="center" />
  189 + <VxeColumn field="name" title="名字" align="center" tree-node/>
  190 + <VxeColumn field="parentId" title="父编号" align="center" />
  191 + <VxeColumn field="operation" title="操作" align="center">
191 <template #default="{row}"> 192 <template #default="{row}">
192 - <el-button 193 + <ElButton
193 size="small" 194 size="small"
194 type="primary" 195 type="primary"
195 link 196 link
@@ -197,8 +198,8 @@ onMounted(() =&gt; { @@ -197,8 +198,8 @@ onMounted(() =&gt; {
197 v-access:code="['infra:category:create']" 198 v-access:code="['infra:category:create']"
198 > 199 >
199 新增下级 200 新增下级
200 - </el-button>  
201 - <el-button 201 + </ElButton>
  202 + <ElButton
202 size="small" 203 size="small"
203 type="primary" 204 type="primary"
204 link 205 link
@@ -206,8 +207,8 @@ onMounted(() =&gt; { @@ -206,8 +207,8 @@ onMounted(() =&gt; {
206 v-access:code="['infra:category:update']" 207 v-access:code="['infra:category:update']"
207 > 208 >
208 {{ $t('ui.actionTitle.edit') }} 209 {{ $t('ui.actionTitle.edit') }}
209 - </el-button>  
210 - <el-button 210 + </ElButton>
  211 + <ElButton
211 size="small" 212 size="small"
212 type="danger" 213 type="danger"
213 link 214 link
@@ -217,10 +218,10 @@ onMounted(() =&gt; { @@ -217,10 +218,10 @@ onMounted(() =&gt; {
217 v-access:code="['infra:category:delete']" 218 v-access:code="['infra:category:delete']"
218 > 219 >
219 {{ $t('ui.actionTitle.delete') }} 220 {{ $t('ui.actionTitle.delete') }}
220 - </el-button> 221 + </ElButton>
221 </template> 222 </template>
222 - </vxe-column>  
223 - </vxe-table> 223 + </VxeColumn>
  224 + </VxeTable>
224 </ContentWrap> 225 </ContentWrap>
225 </Page> 226 </Page>
226 </template> 227 </template>
227 \ No newline at end of file 228 \ No newline at end of file
yudao-module-infra/src/test/resources/codegen/vben5_ele_schema_master_erp/ts/data
@@ -42,7 +42,7 @@ export function useFormSchema(): VbenFormSchema[] { @@ -42,7 +42,7 @@ export function useFormSchema(): VbenFormSchema[] {
42 rules: 'required', 42 rules: 'required',
43 component: 'DatePicker', 43 component: 'DatePicker',
44 componentProps: { 44 componentProps: {
45 - showTime: true, 45 + type: 'datetime',
46 format: 'YYYY-MM-DD HH:mm:ss', 46 format: 'YYYY-MM-DD HH:mm:ss',
47 valueFormat: 'x', 47 valueFormat: 'x',
48 class: '!w-full', 48 class: '!w-full',
@@ -65,8 +65,7 @@ export function useFormSchema(): VbenFormSchema[] { @@ -65,8 +65,7 @@ export function useFormSchema(): VbenFormSchema[] {
65 component: 'RadioGroup', 65 component: 'RadioGroup',
66 componentProps: { 66 componentProps: {
67 options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'), 67 options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'),
68 - buttonStyle: 'solid',  
69 - optionType: 'button', 68 + isButton: true,
70 }, 69 },
71 }, 70 },
72 { 71 {
@@ -277,7 +276,7 @@ export function useStudentContactFormSchema(): VbenFormSchema[] { @@ -277,7 +276,7 @@ export function useStudentContactFormSchema(): VbenFormSchema[] {
277 rules: 'required', 276 rules: 'required',
278 component: 'DatePicker', 277 component: 'DatePicker',
279 componentProps: { 278 componentProps: {
280 - showTime: true, 279 + type: 'datetime',
281 format: 'YYYY-MM-DD HH:mm:ss', 280 format: 'YYYY-MM-DD HH:mm:ss',
282 valueFormat: 'x', 281 valueFormat: 'x',
283 class: '!w-full', 282 class: '!w-full',
@@ -300,8 +299,7 @@ export function useStudentContactFormSchema(): VbenFormSchema[] { @@ -300,8 +299,7 @@ export function useStudentContactFormSchema(): VbenFormSchema[] {
300 component: 'RadioGroup', 299 component: 'RadioGroup',
301 componentProps: { 300 componentProps: {
302 options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'), 301 options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'),
303 - buttonStyle: 'solid',  
304 - optionType: 'button', 302 + isButton: true,
305 }, 303 },
306 }, 304 },
307 { 305 {
@@ -520,7 +518,7 @@ export function useStudentTeacherFormSchema(): VbenFormSchema[] { @@ -520,7 +518,7 @@ export function useStudentTeacherFormSchema(): VbenFormSchema[] {
520 rules: 'required', 518 rules: 'required',
521 component: 'DatePicker', 519 component: 'DatePicker',
522 componentProps: { 520 componentProps: {
523 - showTime: true, 521 + type: 'datetime',
524 format: 'YYYY-MM-DD HH:mm:ss', 522 format: 'YYYY-MM-DD HH:mm:ss',
525 valueFormat: 'x', 523 valueFormat: 'x',
526 class: '!w-full', 524 class: '!w-full',
@@ -543,8 +541,7 @@ export function useStudentTeacherFormSchema(): VbenFormSchema[] { @@ -543,8 +541,7 @@ export function useStudentTeacherFormSchema(): VbenFormSchema[] {
543 component: 'RadioGroup', 541 component: 'RadioGroup',
544 componentProps: { 542 componentProps: {
545 options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'), 543 options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'),
546 - buttonStyle: 'solid',  
547 - optionType: 'button', 544 + isButton: true,
548 }, 545 },
549 }, 546 },
550 { 547 {
yudao-module-infra/src/test/resources/codegen/vben5_ele_schema_master_erp/vue/form
@@ -5,7 +5,7 @@ import { computed, ref } from &#39;vue&#39;; @@ -5,7 +5,7 @@ import { computed, ref } from &#39;vue&#39;;
5 5
6 import { useVbenModal } from '@vben/common-ui'; 6 import { useVbenModal } from '@vben/common-ui';
7 7
8 -import { ElMessage, ElTabs, ElTabPane } from 'element-plus'; 8 +import { ElMessage } from 'element-plus';
9 9
10 import { useVbenForm } from '#/adapter/form'; 10 import { useVbenForm } from '#/adapter/form';
11 import { createStudent, getStudent, updateStudent } from '#/api/infra/demo'; 11 import { createStudent, getStudent, updateStudent } from '#/api/infra/demo';
yudao-module-infra/src/test/resources/codegen/vben5_ele_schema_master_inner/ts/data
@@ -42,7 +42,7 @@ export function useFormSchema(): VbenFormSchema[] { @@ -42,7 +42,7 @@ export function useFormSchema(): VbenFormSchema[] {
42 rules: 'required', 42 rules: 'required',
43 component: 'DatePicker', 43 component: 'DatePicker',
44 componentProps: { 44 componentProps: {
45 - showTime: true, 45 + type: 'datetime',
46 format: 'YYYY-MM-DD HH:mm:ss', 46 format: 'YYYY-MM-DD HH:mm:ss',
47 valueFormat: 'x', 47 valueFormat: 'x',
48 class: '!w-full', 48 class: '!w-full',
@@ -65,8 +65,7 @@ export function useFormSchema(): VbenFormSchema[] { @@ -65,8 +65,7 @@ export function useFormSchema(): VbenFormSchema[] {
65 component: 'RadioGroup', 65 component: 'RadioGroup',
66 componentProps: { 66 componentProps: {
67 options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'), 67 options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'),
68 - buttonStyle: 'solid',  
69 - optionType: 'button', 68 + isButton: true,
70 }, 69 },
71 }, 70 },
72 { 71 {
@@ -270,7 +269,7 @@ export function useGridColumns(): VxeTableGridOptions&lt;InfraStudentApi.Student&gt;[&#39; @@ -270,7 +269,7 @@ export function useGridColumns(): VxeTableGridOptions&lt;InfraStudentApi.Student&gt;[&#39;
270 minWidth: 120, 269 minWidth: 120,
271 slots: { default: 'sex' }, 270 slots: { default: 'sex' },
272 params: { 271 params: {
273 - options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'boolean'), 272 + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'),
274 }, 273 },
275 }, 274 },
276 { 275 {
@@ -310,7 +309,7 @@ export function useGridColumns(): VxeTableGridOptions&lt;InfraStudentApi.Student&gt;[&#39; @@ -310,7 +309,7 @@ export function useGridColumns(): VxeTableGridOptions&lt;InfraStudentApi.Student&gt;[&#39;
310 } 309 }
311 310
312 /** 列表的字段 */ 311 /** 列表的字段 */
313 - export function useStudentContactGridColumns(): VxeTableGridOptions<StudentApi.StudentContact>['columns'] { 312 + export function useStudentContactGridColumns(): VxeTableGridOptions<InfraStudentApi.StudentContact>['columns'] {
314 return [ 313 return [
315 { 314 {
316 field: 'id', 315 field: 'id',
@@ -444,7 +443,7 @@ export function useGridColumns(): VxeTableGridOptions&lt;InfraStudentApi.Student&gt;[&#39; @@ -444,7 +443,7 @@ export function useGridColumns(): VxeTableGridOptions&lt;InfraStudentApi.Student&gt;[&#39;
444 rules: 'required', 443 rules: 'required',
445 component: 'DatePicker', 444 component: 'DatePicker',
446 componentProps: { 445 componentProps: {
447 - showTime: true, 446 + type: 'datetime',
448 format: 'YYYY-MM-DD HH:mm:ss', 447 format: 'YYYY-MM-DD HH:mm:ss',
449 valueFormat: 'x', 448 valueFormat: 'x',
450 class: '!w-full', 449 class: '!w-full',
@@ -467,8 +466,7 @@ export function useGridColumns(): VxeTableGridOptions&lt;InfraStudentApi.Student&gt;[&#39; @@ -467,8 +466,7 @@ export function useGridColumns(): VxeTableGridOptions&lt;InfraStudentApi.Student&gt;[&#39;
467 component: 'RadioGroup', 466 component: 'RadioGroup',
468 componentProps: { 467 componentProps: {
469 options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'), 468 options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'),
470 - buttonStyle: 'solid',  
471 - optionType: 'button', 469 + isButton: true,
472 }, 470 },
473 }, 471 },
474 { 472 {
@@ -492,7 +490,7 @@ export function useGridColumns(): VxeTableGridOptions&lt;InfraStudentApi.Student&gt;[&#39; @@ -492,7 +490,7 @@ export function useGridColumns(): VxeTableGridOptions&lt;InfraStudentApi.Student&gt;[&#39;
492 } 490 }
493 491
494 /** 列表的字段 */ 492 /** 列表的字段 */
495 - export function useStudentTeacherGridColumns(): VxeTableGridOptions<StudentApi.StudentTeacher>['columns'] { 493 + export function useStudentTeacherGridColumns(): VxeTableGridOptions<InfraStudentApi.StudentTeacher>['columns'] {
496 return [ 494 return [
497 { 495 {
498 field: 'id', 496 field: 'id',
yudao-module-infra/src/test/resources/codegen/vben5_ele_schema_master_inner/vue/form
@@ -5,7 +5,7 @@ import { computed, ref } from &#39;vue&#39;; @@ -5,7 +5,7 @@ import { computed, ref } from &#39;vue&#39;;
5 5
6 import { useVbenModal } from '@vben/common-ui'; 6 import { useVbenModal } from '@vben/common-ui';
7 7
8 -import { ElMessage } from 'element-plus'; 8 +import { ElMessage, ElTabPane, ElTabs } from 'element-plus';
9 9
10 import { useVbenForm } from '#/adapter/form'; 10 import { useVbenForm } from '#/adapter/form';
11 import { createStudent, getStudent, updateStudent } from '#/api/infra/demo'; 11 import { createStudent, getStudent, updateStudent } from '#/api/infra/demo';
@@ -48,7 +48,12 @@ const [Modal, modalApi] = useVbenModal({ @@ -48,7 +48,12 @@ const [Modal, modalApi] = useVbenModal({
48 return; 48 return;
49 } 49 }
50 // 校验子表单 50 // 校验子表单
51 - const studentTeacherValid = await studentTeacherFormRef.value?.validate(); 51 + const studentContactValid = await studentContactFormRef.value?.validate();
  52 + if (!studentContactValid) {
  53 + subTabsName.value = 'studentContact';
  54 + return;
  55 + }
  56 + const studentTeacherValid = await studentTeacherFormRef.value?.validate();
52 if (!studentTeacherValid) { 57 if (!studentTeacherValid) {
53 subTabsName.value = 'studentTeacher'; 58 subTabsName.value = 'studentTeacher';
54 return; 59 return;
yudao-module-infra/src/test/resources/codegen/vben5_ele_schema_master_inner/vue/student-contact-form
@@ -6,10 +6,9 @@ @@ -6,10 +6,9 @@
6 import { $t } from '#/locales'; 6 import { $t } from '#/locales';
7 7
8 import { Plus } from "@vben/icons"; 8 import { Plus } from "@vben/icons";
9 -import { ElButton, ElTabs, ElTabPane, ElCheckbox, ElInput, ElSelect, ElOption, ElRadioGroup, ElCheckboxGroup, ElDatePicker } from 'element-plus'; 9 +import { ElButton, ElCheckbox, ElCheckboxGroup, ElDatePicker, ElInput, ElMessage, ElOption, ElRadio, ElRadioGroup, ElSelect, ElTabPane, ElTabs } from 'element-plus';
10 import { ImageUpload, FileUpload } from "#/components/upload"; 10 import { ImageUpload, FileUpload } from "#/components/upload";
11 -import type { OnActionClickParams } from '#/adapter/vxe-table';  
12 -import { useVbenVxeGrid } from '#/adapter/vxe-table'; 11 +import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
13 import { useStudentContactGridEditColumns } from '../data'; 12 import { useStudentContactGridEditColumns } from '../data';
14 import { getStudentContactListByStudentId } from '#/api/infra/demo'; 13 import { getStudentContactListByStudentId } from '#/api/infra/demo';
15 14
@@ -17,22 +16,9 @@ const props = defineProps&lt;{ @@ -17,22 +16,9 @@ const props = defineProps&lt;{
17 studentId?: number // 学生编号(主表的关联字段) 16 studentId?: number // 学生编号(主表的关联字段)
18 }>() 17 }>()
19 18
20 - /** 表格操作按钮的回调函数 */  
21 -function onActionClick({  
22 - code,  
23 - row,  
24 -}: OnActionClickParams<InfraStudentApi.StudentContact>) {  
25 - switch (code) {  
26 - case 'delete': {  
27 - onDelete(row);  
28 - break;  
29 - }  
30 - }  
31 -}  
32 -  
33 -const [Grid, gridApi] = useVbenVxeGrid({ 19 + const [Grid, gridApi] = useVbenVxeGrid({
34 gridOptions: { 20 gridOptions: {
35 - columns: useStudentContactGridEditColumns(onActionClick), 21 + columns: useStudentContactGridEditColumns(),
36 border: true, 22 border: true,
37 showOverflow: true, 23 showOverflow: true,
38 autoResize: true, 24 autoResize: true,
@@ -51,7 +37,7 @@ gridOptions: { @@ -51,7 +37,7 @@ gridOptions: {
51 37
52 /** 添加学生联系人 */ 38 /** 添加学生联系人 */
53 const onAdd = async () => { 39 const onAdd = async () => {
54 - await gridApi.grid.insertAt({} as StudentApi.StudentContact, -1); 40 + await gridApi.grid.insertAt({} as InfraStudentApi.StudentContact, -1);
55 } 41 }
56 42
57 /** 删除学生联系人 */ 43 /** 删除学生联系人 */
@@ -59,26 +45,96 @@ const onDelete = async (row: InfraStudentApi.StudentContact) =&gt; { @@ -59,26 +45,96 @@ const onDelete = async (row: InfraStudentApi.StudentContact) =&gt; {
59 await gridApi.grid.remove(row); 45 await gridApi.grid.remove(row);
60 } 46 }
61 47
62 -/** 提供获取表格数据的方法供父组件调用 */  
63 -defineExpose({  
64 - getData: (): InfraStudentApi.StudentContact[] => {  
65 - const data = gridApi.grid.getData() as InfraStudentApi.StudentContact[];  
66 - const removeRecords = gridApi.grid.getRemoveRecords() as InfraStudentApi.StudentContact[];  
67 - const insertRecords = gridApi.grid.getInsertRecords() as InfraStudentApi.StudentContact[];  
68 - return data  
69 - .filter((row) => !removeRecords.some((removed) => removed.id === row.id))  
70 - .concat(insertRecords.map((row: any) => ({ ...row, id: undefined })));  
71 - },  
72 -}); 48 +/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
  49 +function getData(): InfraStudentApi.StudentContact[] {
  50 + const { fullData } = gridApi.grid.getTableData();
  51 + const inserted = new Set(gridApi.grid.getInsertRecords());
  52 + return fullData.map((row) =>
  53 + inserted.has(row) ? { ...row, id: undefined } : { ...row },
  54 + );
  55 +}
  56 +
  57 +/** 校验子表必填字段 */
  58 +function validate(): boolean {
  59 + for (const [index, row] of getData().entries()) {
  60 + if (
  61 + row.name === null ||
  62 + row.name === undefined ||
  63 + (typeof row.name === 'string' && String(row.name).trim() === '') ||
  64 + (Array.isArray(row.name) && row.name.length === 0)
  65 + ) {
  66 + ElMessage.error(`第 ${index + 1} 行:名字不能为空`);
  67 + return false;
  68 + }
  69 + if (
  70 + row.description === null ||
  71 + row.description === undefined ||
  72 + (typeof row.description === 'string' && String(row.description).trim() === '') ||
  73 + (Array.isArray(row.description) && row.description.length === 0)
  74 + ) {
  75 + ElMessage.error(`第 ${index + 1} 行:简介不能为空`);
  76 + return false;
  77 + }
  78 + if (
  79 + row.birthday === null ||
  80 + row.birthday === undefined ||
  81 + (typeof row.birthday === 'string' && String(row.birthday).trim() === '') ||
  82 + (Array.isArray(row.birthday) && row.birthday.length === 0)
  83 + ) {
  84 + ElMessage.error(`第 ${index + 1} 行:出生日期不能为空`);
  85 + return false;
  86 + }
  87 + if (
  88 + row.sex === null ||
  89 + row.sex === undefined ||
  90 + (typeof row.sex === 'string' && String(row.sex).trim() === '') ||
  91 + (Array.isArray(row.sex) && row.sex.length === 0)
  92 + ) {
  93 + ElMessage.error(`第 ${index + 1} 行:性别不能为空`);
  94 + return false;
  95 + }
  96 + if (
  97 + row.enabled === null ||
  98 + row.enabled === undefined ||
  99 + (typeof row.enabled === 'string' && String(row.enabled).trim() === '') ||
  100 + (Array.isArray(row.enabled) && row.enabled.length === 0)
  101 + ) {
  102 + ElMessage.error(`第 ${index + 1} 行:是否有效不能为空`);
  103 + return false;
  104 + }
  105 + if (
  106 + row.avatar === null ||
  107 + row.avatar === undefined ||
  108 + (typeof row.avatar === 'string' && String(row.avatar).trim() === '') ||
  109 + (Array.isArray(row.avatar) && row.avatar.length === 0)
  110 + ) {
  111 + ElMessage.error(`第 ${index + 1} 行:头像不能为空`);
  112 + return false;
  113 + }
  114 + if (
  115 + row.memo === null ||
  116 + row.memo === undefined ||
  117 + (typeof row.memo === 'string' && String(row.memo).trim() === '') ||
  118 + (Array.isArray(row.memo) && row.memo.length === 0)
  119 + ) {
  120 + ElMessage.error(`第 ${index + 1} 行:备注不能为空`);
  121 + return false;
  122 + }
  123 + }
  124 + return true;
  125 +}
  126 +
  127 +defineExpose({ getData, validate });
73 128
74 /** 监听主表的关联字段的变化,加载对应的子表数据 */ 129 /** 监听主表的关联字段的变化,加载对应的子表数据 */
75 watch( 130 watch(
76 () => props.studentId, 131 () => props.studentId,
77 async (val) => { 132 async (val) => {
  133 + await nextTick();
78 if (!val) { 134 if (!val) {
  135 + await gridApi.grid.loadData([]);
79 return; 136 return;
80 } 137 }
81 - await nextTick();  
82 await gridApi.grid.loadData(await getStudentContactListByStudentId(props.studentId!)); 138 await gridApi.grid.loadData(await getStudentContactListByStudentId(props.studentId!));
83 }, 139 },
84 { immediate: true }, 140 { immediate: true },
@@ -88,13 +144,13 @@ watch( @@ -88,13 +144,13 @@ watch(
88 <template> 144 <template>
89 <Grid class="mx-4"> 145 <Grid class="mx-4">
90 <template #name="{ row }"> 146 <template #name="{ row }">
91 - <el-input v-model="row.name" /> 147 + <ElInput v-model="row.name" />
92 </template> 148 </template>
93 <template #description="{ row }"> 149 <template #description="{ row }">
94 - <el-input v-model="row.description" type="textarea" /> 150 + <ElInput v-model="row.description" type="textarea" />
95 </template> 151 </template>
96 <template #birthday="{ row }"> 152 <template #birthday="{ row }">
97 - <el-date-picker 153 + <ElDatePicker
98 v-model="row.birthday" 154 v-model="row.birthday"
99 type="datetime" 155 type="datetime"
100 value-format="x" 156 value-format="x"
@@ -102,16 +158,16 @@ watch( @@ -102,16 +158,16 @@ watch(
102 /> 158 />
103 </template> 159 </template>
104 <template #sex="{ row, column }"> 160 <template #sex="{ row, column }">
105 - <el-select v-model="row.sex" class="w-full">  
106 - <el-option v-for="option in column.params.options" :key="option.value" :value="option.value" :label="option.label" />  
107 - </el-select> 161 + <ElSelect v-model="row.sex" class="w-full">
  162 + <ElOption v-for="option in column.params.options" :key="option.value" :value="option.value" :label="option.label" />
  163 + </ElSelect>
108 </template> 164 </template>
109 <template #enabled="{ row, column }"> 165 <template #enabled="{ row, column }">
110 - <el-radio-group v-model="row.enabled">  
111 - <el-radio v-for="option in column.params.options" :key="option.value" :label="option.value"> 166 + <ElRadioGroup v-model="row.enabled">
  167 + <ElRadio v-for="option in column.params.options" :key="option.value" :value="option.value">
112 {{ option.label }} 168 {{ option.label }}
113 - </el-radio>  
114 - </el-radio-group> 169 + </ElRadio>
  170 + </ElRadioGroup>
115 </template> 171 </template>
116 <template #avatar="{ row }"> 172 <template #avatar="{ row }">
117 <ImageUpload v-model="row.avatar" /> 173 <ImageUpload v-model="row.avatar" />
@@ -120,12 +176,25 @@ watch( @@ -120,12 +176,25 @@ watch(
120 <FileUpload v-model="row.video" /> 176 <FileUpload v-model="row.video" />
121 </template> 177 </template>
122 <template #memo="{ row }"> 178 <template #memo="{ row }">
123 - <el-input v-model="row.memo" type="textarea" /> 179 + <ElInput v-model="row.memo" type="textarea" />
124 </template> 180 </template>
  181 + <template #actions="{ row }">
  182 + <TableAction
  183 + :actions="[
  184 + {
  185 + label: $t('common.delete'),
  186 + type: 'danger', link: true,
  187 + icon: ACTION_ICON.DELETE,
  188 + auth: ['infra:student:delete'],
  189 + onClick: onDelete.bind(null, row),
  190 + },
  191 + ]"
  192 + />
  193 + </template>
125 </Grid> 194 </Grid>
126 <div class="flex justify-center -mt-4"> 195 <div class="flex justify-center -mt-4">
127 - <el-button :icon="Plus" type="primary" plain @click="onAdd" v-access:code="['infra:student:create']"> 196 + <ElButton :icon="Plus" type="primary" plain @click="onAdd" v-access:code="['infra:student:create']">
128 {{ $t('ui.actionTitle.create', ['学生联系人']) }} 197 {{ $t('ui.actionTitle.create', ['学生联系人']) }}
129 - </el-button> 198 + </ElButton>
130 </div> 199 </div>
131 </template> 200 </template>
132 \ No newline at end of file 201 \ No newline at end of file
yudao-module-infra/src/test/resources/codegen/vben5_ele_schema_master_inner/vue/student-teacher-form
@@ -39,10 +39,11 @@ defineExpose({ @@ -39,10 +39,11 @@ defineExpose({
39 watch( 39 watch(
40 () => props.studentId, 40 () => props.studentId,
41 async (val) => { 41 async (val) => {
  42 + await nextTick();
  43 + await formApi.resetForm();
42 if (!val) { 44 if (!val) {
43 return; 45 return;
44 } 46 }
45 - await nextTick();  
46 await formApi.setValues(await getStudentTeacherByStudentId(props.studentId!)); 47 await formApi.setValues(await getStudentTeacherByStudentId(props.studentId!));
47 }, 48 },
48 { immediate: true }, 49 { immediate: true },
yudao-module-infra/src/test/resources/codegen/vben5_ele_schema_master_normal/ts/data
@@ -42,7 +42,7 @@ export function useFormSchema(): VbenFormSchema[] { @@ -42,7 +42,7 @@ export function useFormSchema(): VbenFormSchema[] {
42 rules: 'required', 42 rules: 'required',
43 component: 'DatePicker', 43 component: 'DatePicker',
44 componentProps: { 44 componentProps: {
45 - showTime: true, 45 + type: 'datetime',
46 format: 'YYYY-MM-DD HH:mm:ss', 46 format: 'YYYY-MM-DD HH:mm:ss',
47 valueFormat: 'x', 47 valueFormat: 'x',
48 class: '!w-full', 48 class: '!w-full',
@@ -65,8 +65,7 @@ export function useFormSchema(): VbenFormSchema[] { @@ -65,8 +65,7 @@ export function useFormSchema(): VbenFormSchema[] {
65 component: 'RadioGroup', 65 component: 'RadioGroup',
66 componentProps: { 66 componentProps: {
67 options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'), 67 options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'),
68 - buttonStyle: 'solid',  
69 - optionType: 'button', 68 + isButton: true,
70 }, 69 },
71 }, 70 },
72 { 71 {
@@ -269,7 +268,7 @@ export function useGridColumns(): VxeTableGridOptions&lt;InfraStudentApi.Student&gt;[&#39; @@ -269,7 +268,7 @@ export function useGridColumns(): VxeTableGridOptions&lt;InfraStudentApi.Student&gt;[&#39;
269 minWidth: 120, 268 minWidth: 120,
270 slots: { default: 'sex' }, 269 slots: { default: 'sex' },
271 params: { 270 params: {
272 - options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'boolean'), 271 + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'),
273 }, 272 },
274 }, 273 },
275 { 274 {
@@ -345,7 +344,7 @@ export function useGridColumns(): VxeTableGridOptions&lt;InfraStudentApi.Student&gt;[&#39; @@ -345,7 +344,7 @@ export function useGridColumns(): VxeTableGridOptions&lt;InfraStudentApi.Student&gt;[&#39;
345 rules: 'required', 344 rules: 'required',
346 component: 'DatePicker', 345 component: 'DatePicker',
347 componentProps: { 346 componentProps: {
348 - showTime: true, 347 + type: 'datetime',
349 format: 'YYYY-MM-DD HH:mm:ss', 348 format: 'YYYY-MM-DD HH:mm:ss',
350 valueFormat: 'x', 349 valueFormat: 'x',
351 class: '!w-full', 350 class: '!w-full',
@@ -368,8 +367,7 @@ export function useGridColumns(): VxeTableGridOptions&lt;InfraStudentApi.Student&gt;[&#39; @@ -368,8 +367,7 @@ export function useGridColumns(): VxeTableGridOptions&lt;InfraStudentApi.Student&gt;[&#39;
368 component: 'RadioGroup', 367 component: 'RadioGroup',
369 componentProps: { 368 componentProps: {
370 options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'), 369 options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'),
371 - buttonStyle: 'solid',  
372 - optionType: 'button', 370 + isButton: true,
373 }, 371 },
374 }, 372 },
375 { 373 {
yudao-module-infra/src/test/resources/codegen/vben5_ele_schema_master_normal/vue/form
@@ -5,7 +5,7 @@ import { computed, ref } from &#39;vue&#39;; @@ -5,7 +5,7 @@ import { computed, ref } from &#39;vue&#39;;
5 5
6 import { useVbenModal } from '@vben/common-ui'; 6 import { useVbenModal } from '@vben/common-ui';
7 7
8 -import { ElMessage } from 'element-plus'; 8 +import { ElMessage, ElTabPane, ElTabs } from 'element-plus';
9 9
10 import { useVbenForm } from '#/adapter/form'; 10 import { useVbenForm } from '#/adapter/form';
11 import { createStudent, getStudent, updateStudent } from '#/api/infra/demo'; 11 import { createStudent, getStudent, updateStudent } from '#/api/infra/demo';
@@ -48,7 +48,12 @@ const [Modal, modalApi] = useVbenModal({ @@ -48,7 +48,12 @@ const [Modal, modalApi] = useVbenModal({
48 return; 48 return;
49 } 49 }
50 // 校验子表单 50 // 校验子表单
51 - const studentTeacherValid = await studentTeacherFormRef.value?.validate(); 51 + const studentContactValid = await studentContactFormRef.value?.validate();
  52 + if (!studentContactValid) {
  53 + subTabsName.value = 'studentContact';
  54 + return;
  55 + }
  56 + const studentTeacherValid = await studentTeacherFormRef.value?.validate();
52 if (!studentTeacherValid) { 57 if (!studentTeacherValid) {
53 subTabsName.value = 'studentTeacher'; 58 subTabsName.value = 'studentTeacher';
54 return; 59 return;
yudao-module-infra/src/test/resources/codegen/vben5_ele_schema_master_normal/vue/student-contact-form
@@ -6,10 +6,9 @@ @@ -6,10 +6,9 @@
6 import { $t } from '#/locales'; 6 import { $t } from '#/locales';
7 7
8 import { Plus } from "@vben/icons"; 8 import { Plus } from "@vben/icons";
9 -import { ElButton, ElTabs, ElTabPane, ElCheckbox, ElInput, ElSelect, ElOption, ElRadioGroup, ElCheckboxGroup, ElDatePicker } from 'element-plus'; 9 +import { ElButton, ElCheckbox, ElCheckboxGroup, ElDatePicker, ElInput, ElMessage, ElOption, ElRadio, ElRadioGroup, ElSelect, ElTabPane, ElTabs } from 'element-plus';
10 import { ImageUpload, FileUpload } from "#/components/upload"; 10 import { ImageUpload, FileUpload } from "#/components/upload";
11 -import type { OnActionClickParams } from '#/adapter/vxe-table';  
12 -import { useVbenVxeGrid } from '#/adapter/vxe-table'; 11 +import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
13 import { useStudentContactGridEditColumns } from '../data'; 12 import { useStudentContactGridEditColumns } from '../data';
14 import { getStudentContactListByStudentId } from '#/api/infra/demo'; 13 import { getStudentContactListByStudentId } from '#/api/infra/demo';
15 14
@@ -17,22 +16,9 @@ const props = defineProps&lt;{ @@ -17,22 +16,9 @@ const props = defineProps&lt;{
17 studentId?: number // 学生编号(主表的关联字段) 16 studentId?: number // 学生编号(主表的关联字段)
18 }>() 17 }>()
19 18
20 - /** 表格操作按钮的回调函数 */  
21 -function onActionClick({  
22 - code,  
23 - row,  
24 -}: OnActionClickParams<InfraStudentApi.StudentContact>) {  
25 - switch (code) {  
26 - case 'delete': {  
27 - onDelete(row);  
28 - break;  
29 - }  
30 - }  
31 -}  
32 -  
33 -const [Grid, gridApi] = useVbenVxeGrid({ 19 + const [Grid, gridApi] = useVbenVxeGrid({
34 gridOptions: { 20 gridOptions: {
35 - columns: useStudentContactGridEditColumns(onActionClick), 21 + columns: useStudentContactGridEditColumns(),
36 border: true, 22 border: true,
37 showOverflow: true, 23 showOverflow: true,
38 autoResize: true, 24 autoResize: true,
@@ -51,7 +37,7 @@ gridOptions: { @@ -51,7 +37,7 @@ gridOptions: {
51 37
52 /** 添加学生联系人 */ 38 /** 添加学生联系人 */
53 const onAdd = async () => { 39 const onAdd = async () => {
54 - await gridApi.grid.insertAt({} as StudentApi.StudentContact, -1); 40 + await gridApi.grid.insertAt({} as InfraStudentApi.StudentContact, -1);
55 } 41 }
56 42
57 /** 删除学生联系人 */ 43 /** 删除学生联系人 */
@@ -59,26 +45,96 @@ const onDelete = async (row: InfraStudentApi.StudentContact) =&gt; { @@ -59,26 +45,96 @@ const onDelete = async (row: InfraStudentApi.StudentContact) =&gt; {
59 await gridApi.grid.remove(row); 45 await gridApi.grid.remove(row);
60 } 46 }
61 47
62 -/** 提供获取表格数据的方法供父组件调用 */  
63 -defineExpose({  
64 - getData: (): InfraStudentApi.StudentContact[] => {  
65 - const data = gridApi.grid.getData() as InfraStudentApi.StudentContact[];  
66 - const removeRecords = gridApi.grid.getRemoveRecords() as InfraStudentApi.StudentContact[];  
67 - const insertRecords = gridApi.grid.getInsertRecords() as InfraStudentApi.StudentContact[];  
68 - return data  
69 - .filter((row) => !removeRecords.some((removed) => removed.id === row.id))  
70 - .concat(insertRecords.map((row: any) => ({ ...row, id: undefined })));  
71 - },  
72 -}); 48 +/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
  49 +function getData(): InfraStudentApi.StudentContact[] {
  50 + const { fullData } = gridApi.grid.getTableData();
  51 + const inserted = new Set(gridApi.grid.getInsertRecords());
  52 + return fullData.map((row) =>
  53 + inserted.has(row) ? { ...row, id: undefined } : { ...row },
  54 + );
  55 +}
  56 +
  57 +/** 校验子表必填字段 */
  58 +function validate(): boolean {
  59 + for (const [index, row] of getData().entries()) {
  60 + if (
  61 + row.name === null ||
  62 + row.name === undefined ||
  63 + (typeof row.name === 'string' && String(row.name).trim() === '') ||
  64 + (Array.isArray(row.name) && row.name.length === 0)
  65 + ) {
  66 + ElMessage.error(`第 ${index + 1} 行:名字不能为空`);
  67 + return false;
  68 + }
  69 + if (
  70 + row.description === null ||
  71 + row.description === undefined ||
  72 + (typeof row.description === 'string' && String(row.description).trim() === '') ||
  73 + (Array.isArray(row.description) && row.description.length === 0)
  74 + ) {
  75 + ElMessage.error(`第 ${index + 1} 行:简介不能为空`);
  76 + return false;
  77 + }
  78 + if (
  79 + row.birthday === null ||
  80 + row.birthday === undefined ||
  81 + (typeof row.birthday === 'string' && String(row.birthday).trim() === '') ||
  82 + (Array.isArray(row.birthday) && row.birthday.length === 0)
  83 + ) {
  84 + ElMessage.error(`第 ${index + 1} 行:出生日期不能为空`);
  85 + return false;
  86 + }
  87 + if (
  88 + row.sex === null ||
  89 + row.sex === undefined ||
  90 + (typeof row.sex === 'string' && String(row.sex).trim() === '') ||
  91 + (Array.isArray(row.sex) && row.sex.length === 0)
  92 + ) {
  93 + ElMessage.error(`第 ${index + 1} 行:性别不能为空`);
  94 + return false;
  95 + }
  96 + if (
  97 + row.enabled === null ||
  98 + row.enabled === undefined ||
  99 + (typeof row.enabled === 'string' && String(row.enabled).trim() === '') ||
  100 + (Array.isArray(row.enabled) && row.enabled.length === 0)
  101 + ) {
  102 + ElMessage.error(`第 ${index + 1} 行:是否有效不能为空`);
  103 + return false;
  104 + }
  105 + if (
  106 + row.avatar === null ||
  107 + row.avatar === undefined ||
  108 + (typeof row.avatar === 'string' && String(row.avatar).trim() === '') ||
  109 + (Array.isArray(row.avatar) && row.avatar.length === 0)
  110 + ) {
  111 + ElMessage.error(`第 ${index + 1} 行:头像不能为空`);
  112 + return false;
  113 + }
  114 + if (
  115 + row.memo === null ||
  116 + row.memo === undefined ||
  117 + (typeof row.memo === 'string' && String(row.memo).trim() === '') ||
  118 + (Array.isArray(row.memo) && row.memo.length === 0)
  119 + ) {
  120 + ElMessage.error(`第 ${index + 1} 行:备注不能为空`);
  121 + return false;
  122 + }
  123 + }
  124 + return true;
  125 +}
  126 +
  127 +defineExpose({ getData, validate });
73 128
74 /** 监听主表的关联字段的变化,加载对应的子表数据 */ 129 /** 监听主表的关联字段的变化,加载对应的子表数据 */
75 watch( 130 watch(
76 () => props.studentId, 131 () => props.studentId,
77 async (val) => { 132 async (val) => {
  133 + await nextTick();
78 if (!val) { 134 if (!val) {
  135 + await gridApi.grid.loadData([]);
79 return; 136 return;
80 } 137 }
81 - await nextTick();  
82 await gridApi.grid.loadData(await getStudentContactListByStudentId(props.studentId!)); 138 await gridApi.grid.loadData(await getStudentContactListByStudentId(props.studentId!));
83 }, 139 },
84 { immediate: true }, 140 { immediate: true },
@@ -88,13 +144,13 @@ watch( @@ -88,13 +144,13 @@ watch(
88 <template> 144 <template>
89 <Grid class="mx-4"> 145 <Grid class="mx-4">
90 <template #name="{ row }"> 146 <template #name="{ row }">
91 - <el-input v-model="row.name" /> 147 + <ElInput v-model="row.name" />
92 </template> 148 </template>
93 <template #description="{ row }"> 149 <template #description="{ row }">
94 - <el-input v-model="row.description" type="textarea" /> 150 + <ElInput v-model="row.description" type="textarea" />
95 </template> 151 </template>
96 <template #birthday="{ row }"> 152 <template #birthday="{ row }">
97 - <el-date-picker 153 + <ElDatePicker
98 v-model="row.birthday" 154 v-model="row.birthday"
99 type="datetime" 155 type="datetime"
100 value-format="x" 156 value-format="x"
@@ -102,16 +158,16 @@ watch( @@ -102,16 +158,16 @@ watch(
102 /> 158 />
103 </template> 159 </template>
104 <template #sex="{ row, column }"> 160 <template #sex="{ row, column }">
105 - <el-select v-model="row.sex" class="w-full">  
106 - <el-option v-for="option in column.params.options" :key="option.value" :value="option.value" :label="option.label" />  
107 - </el-select> 161 + <ElSelect v-model="row.sex" class="w-full">
  162 + <ElOption v-for="option in column.params.options" :key="option.value" :value="option.value" :label="option.label" />
  163 + </ElSelect>
108 </template> 164 </template>
109 <template #enabled="{ row, column }"> 165 <template #enabled="{ row, column }">
110 - <el-radio-group v-model="row.enabled">  
111 - <el-radio v-for="option in column.params.options" :key="option.value" :label="option.value"> 166 + <ElRadioGroup v-model="row.enabled">
  167 + <ElRadio v-for="option in column.params.options" :key="option.value" :value="option.value">
112 {{ option.label }} 168 {{ option.label }}
113 - </el-radio>  
114 - </el-radio-group> 169 + </ElRadio>
  170 + </ElRadioGroup>
115 </template> 171 </template>
116 <template #avatar="{ row }"> 172 <template #avatar="{ row }">
117 <ImageUpload v-model="row.avatar" /> 173 <ImageUpload v-model="row.avatar" />
@@ -120,12 +176,25 @@ watch( @@ -120,12 +176,25 @@ watch(
120 <FileUpload v-model="row.video" /> 176 <FileUpload v-model="row.video" />
121 </template> 177 </template>
122 <template #memo="{ row }"> 178 <template #memo="{ row }">
123 - <el-input v-model="row.memo" type="textarea" /> 179 + <ElInput v-model="row.memo" type="textarea" />
124 </template> 180 </template>
  181 + <template #actions="{ row }">
  182 + <TableAction
  183 + :actions="[
  184 + {
  185 + label: $t('common.delete'),
  186 + type: 'danger', link: true,
  187 + icon: ACTION_ICON.DELETE,
  188 + auth: ['infra:student:delete'],
  189 + onClick: onDelete.bind(null, row),
  190 + },
  191 + ]"
  192 + />
  193 + </template>
125 </Grid> 194 </Grid>
126 <div class="flex justify-center -mt-4"> 195 <div class="flex justify-center -mt-4">
127 - <el-button :icon="Plus" type="primary" plain @click="onAdd" v-access:code="['infra:student:create']"> 196 + <ElButton :icon="Plus" type="primary" plain @click="onAdd" v-access:code="['infra:student:create']">
128 {{ $t('ui.actionTitle.create', ['学生联系人']) }} 197 {{ $t('ui.actionTitle.create', ['学生联系人']) }}
129 - </el-button> 198 + </ElButton>
130 </div> 199 </div>
131 </template> 200 </template>
132 \ No newline at end of file 201 \ No newline at end of file
yudao-module-infra/src/test/resources/codegen/vben5_ele_schema_master_normal/vue/student-teacher-form
@@ -39,10 +39,11 @@ defineExpose({ @@ -39,10 +39,11 @@ defineExpose({
39 watch( 39 watch(
40 () => props.studentId, 40 () => props.studentId,
41 async (val) => { 41 async (val) => {
  42 + await nextTick();
  43 + await formApi.resetForm();
42 if (!val) { 44 if (!val) {
43 return; 45 return;
44 } 46 }
45 - await nextTick();  
46 await formApi.setValues(await getStudentTeacherByStudentId(props.studentId!)); 47 await formApi.setValues(await getStudentTeacherByStudentId(props.studentId!));
47 }, 48 },
48 { immediate: true }, 49 { immediate: true },
yudao-module-infra/src/test/resources/codegen/vben5_ele_schema_one/ts/data
@@ -42,7 +42,7 @@ export function useFormSchema(): VbenFormSchema[] { @@ -42,7 +42,7 @@ export function useFormSchema(): VbenFormSchema[] {
42 rules: 'required', 42 rules: 'required',
43 component: 'DatePicker', 43 component: 'DatePicker',
44 componentProps: { 44 componentProps: {
45 - showTime: true, 45 + type: 'datetime',
46 format: 'YYYY-MM-DD HH:mm:ss', 46 format: 'YYYY-MM-DD HH:mm:ss',
47 valueFormat: 'x', 47 valueFormat: 'x',
48 class: '!w-full', 48 class: '!w-full',
@@ -65,8 +65,7 @@ export function useFormSchema(): VbenFormSchema[] { @@ -65,8 +65,7 @@ export function useFormSchema(): VbenFormSchema[] {
65 component: 'RadioGroup', 65 component: 'RadioGroup',
66 componentProps: { 66 componentProps: {
67 options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'), 67 options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'),
68 - buttonStyle: 'solid',  
69 - optionType: 'button', 68 + isButton: true,
70 }, 69 },
71 }, 70 },
72 { 71 {
yudao-module-infra/src/test/resources/codegen/vben5_ele_schema_one_importEnable/ts/data
@@ -42,7 +42,7 @@ export function useFormSchema(): VbenFormSchema[] { @@ -42,7 +42,7 @@ export function useFormSchema(): VbenFormSchema[] {
42 rules: 'required', 42 rules: 'required',
43 component: 'DatePicker', 43 component: 'DatePicker',
44 componentProps: { 44 componentProps: {
45 - showTime: true, 45 + type: 'datetime',
46 format: 'YYYY-MM-DD HH:mm:ss', 46 format: 'YYYY-MM-DD HH:mm:ss',
47 valueFormat: 'x', 47 valueFormat: 'x',
48 class: '!w-full', 48 class: '!w-full',
@@ -65,8 +65,7 @@ export function useFormSchema(): VbenFormSchema[] { @@ -65,8 +65,7 @@ export function useFormSchema(): VbenFormSchema[] {
65 component: 'RadioGroup', 65 component: 'RadioGroup',
66 componentProps: { 66 componentProps: {
67 options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'), 67 options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'),
68 - buttonStyle: 'solid',  
69 - optionType: 'button', 68 + isButton: true,
70 }, 69 },
71 }, 70 },
72 { 71 {
yudao-module-infra/src/test/resources/codegen/vben5_ele_schema_one_importEnable/vue/import-form
@@ -50,6 +50,16 @@ function handleChange(file: any) { @@ -50,6 +50,16 @@ function handleChange(file: any) {
50 } 50 }
51 } 51 }
52 52
  53 +/** 移除文件 */
  54 +function handleRemove() {
  55 + formApi.setFieldValue('file', undefined);
  56 +}
  57 +
  58 +/** 超出文件数量 */
  59 +function handleExceed() {
  60 + ElMessage.warning('只能选择一个文件,请先移除已选文件');
  61 +}
  62 +
53 /** 下载模板 */ 63 /** 下载模板 */
54 async function handleDownload() { 64 async function handleDownload() {
55 const data = await importStudentTemplate(); 65 const data = await importStudentTemplate();
@@ -66,6 +76,8 @@ async function handleDownload() { @@ -66,6 +76,8 @@ async function handleDownload() {
66 :limit="1" 76 :limit="1"
67 accept=".xls,.xlsx" 77 accept=".xls,.xlsx"
68 :on-change="handleChange" 78 :on-change="handleChange"
  79 + :on-remove="handleRemove"
  80 + :on-exceed="handleExceed"
69 :auto-upload="false" 81 :auto-upload="false"
70 > 82 >
71 <ElButton type="primary"> 选择 Excel 文件 </ElButton> 83 <ElButton type="primary"> 选择 Excel 文件 </ElButton>
yudao-module-infra/src/test/resources/codegen/vben5_ele_schema_tree/ts/data
@@ -32,13 +32,14 @@ export function useFormSchema(): VbenFormSchema[] { @@ -32,13 +32,14 @@ export function useFormSchema(): VbenFormSchema[] {
32 id: 0, 32 id: 0,
33 name: '顶级分类', 33 name: '顶级分类',
34 }); 34 });
35 - return handleTree(data); 35 + return handleTree(data, 'id', 'parentId');
36 }, 36 },
37 labelField: 'name', 37 labelField: 'name',
38 valueField: 'id', 38 valueField: 'id',
39 childrenField: 'children', 39 childrenField: 'children',
40 placeholder: '请选择上级分类', 40 placeholder: '请选择上级分类',
41 - treeDefaultExpandAll: true, 41 + defaultExpandAll: true,
  42 + checkStrictly: true,
42 }, 43 },
43 rules: 'selectRequired', 44 rules: 'selectRequired',
44 }, 45 },
yudao-module-infra/src/test/resources/codegen/vue2_master_erp/vue/StudentContactList
@@ -64,7 +64,7 @@ @@ -64,7 +64,7 @@
64 </el-table-column> 64 </el-table-column>
65 </el-table> 65 </el-table>
66 <!-- 分页组件 --> 66 <!-- 分页组件 -->
67 - <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" 67 + <Pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
68 @pagination="getList"/> 68 @pagination="getList"/>
69 <!-- 对话框(添加 / 修改) --> 69 <!-- 对话框(添加 / 修改) -->
70 <StudentContactForm ref="formRef" @success="getList" /> 70 <StudentContactForm ref="formRef" @success="getList" />
@@ -79,9 +79,13 @@ @@ -79,9 +79,13 @@
79 components: { 79 components: {
80 StudentContactForm 80 StudentContactForm
81 }, 81 },
82 - props:[  
83 - 'studentId'  
84 - ],// 学生编号(主表的关联字段) 82 + props: {
  83 + // 学生编号(主表的关联字段)
  84 + studentId: {
  85 + type: Number,
  86 + default: undefined
  87 + }
  88 + },
85 data() { 89 data() {
86 return { 90 return {
87 // 遮罩层 91 // 遮罩层
yudao-module-infra/src/test/resources/codegen/vue2_master_erp/vue/StudentTeacherList
@@ -64,7 +64,7 @@ @@ -64,7 +64,7 @@
64 </el-table-column> 64 </el-table-column>
65 </el-table> 65 </el-table>
66 <!-- 分页组件 --> 66 <!-- 分页组件 -->
67 - <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" 67 + <Pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
68 @pagination="getList"/> 68 @pagination="getList"/>
69 <!-- 对话框(添加 / 修改) --> 69 <!-- 对话框(添加 / 修改) -->
70 <StudentTeacherForm ref="formRef" @success="getList" /> 70 <StudentTeacherForm ref="formRef" @success="getList" />
@@ -79,9 +79,13 @@ @@ -79,9 +79,13 @@
79 components: { 79 components: {
80 StudentTeacherForm 80 StudentTeacherForm
81 }, 81 },
82 - props:[  
83 - 'studentId'  
84 - ],// 学生编号(主表的关联字段) 82 + props: {
  83 + // 学生编号(主表的关联字段)
  84 + studentId: {
  85 + type: Number,
  86 + default: undefined
  87 + }
  88 + },
85 data() { 89 data() {
86 return { 90 return {
87 // 遮罩层 91 // 遮罩层
yudao-module-infra/src/test/resources/codegen/vue2_master_erp/vue/index
@@ -53,7 +53,7 @@ @@ -53,7 +53,7 @@
53 批量删除 53 批量删除
54 </el-button> 54 </el-button>
55 </el-col> 55 </el-col>
56 - <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> 56 + <RightToolbar :showSearch.sync="showSearch" @queryTable="getList"></RightToolbar>
57 </el-row> 57 </el-row>
58 58
59 <el-table 59 <el-table
@@ -127,7 +127,7 @@ @@ -127,7 +127,7 @@
127 </el-table-column> 127 </el-table-column>
128 </el-table> 128 </el-table>
129 <!-- 分页组件 --> 129 <!-- 分页组件 -->
130 - <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" 130 + <Pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
131 @pagination="getList"/> 131 @pagination="getList"/>
132 <!-- 对话框(添加 / 修改) --> 132 <!-- 对话框(添加 / 修改) -->
133 <StudentForm ref="formRef" @success="getList" /> 133 <StudentForm ref="formRef" @success="getList" />
yudao-module-infra/src/test/resources/codegen/vue2_master_inner/vue/StudentContactForm
@@ -97,9 +97,13 @@ @@ -97,9 +97,13 @@
97 FileUpload, 97 FileUpload,
98 Editor, 98 Editor,
99 }, 99 },
100 - props:[  
101 - 'studentId'  
102 - ],// 学生编号(主表的关联字段) 100 + props: {
  101 + // 学生编号(主表的关联字段)
  102 + studentId: {
  103 + type: Number,
  104 + default: undefined
  105 + }
  106 + },
103 data() { 107 data() {
104 return { 108 return {
105 // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 109 // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
yudao-module-infra/src/test/resources/codegen/vue2_master_inner/vue/StudentContactList
@@ -48,9 +48,13 @@ @@ -48,9 +48,13 @@
48 import * as StudentApi from '@/api/infra/demo'; 48 import * as StudentApi from '@/api/infra/demo';
49 export default { 49 export default {
50 name: "StudentContactList", 50 name: "StudentContactList",
51 - props:[  
52 - 'studentId'  
53 - ],// 学生编号(主表的关联字段) 51 + props: {
  52 + // 学生编号(主表的关联字段)
  53 + studentId: {
  54 + type: Number,
  55 + default: undefined
  56 + }
  57 + },
54 data() { 58 data() {
55 return { 59 return {
56 // 遮罩层 60 // 遮罩层