Commit 63c0fcdf2f6fc6ad55801d575f42e55e387545e0

Authored by 芋道源码
Committed by GitHub
2 parents 5faf6ea5 f2fdf227

Merge pull request #1083 from itkdm/feat/codegen-import-other-frontends-jdk17-clean

 feat(infra): 代码生成器导入Excel支持其他前端版本
yudao-module-infra/src/main/resources/codegen/vue/api/api.js.vm
... ... @@ -73,6 +73,24 @@ export function export${simpleClassName}Excel(params) {
73 73 responseType: 'blob'
74 74 })
75 75 }
  76 +
  77 +// 下载${table.classComment}导入模板
  78 +export function import${simpleClassName}Template() {
  79 + return request({
  80 + url: '${baseURL}/get-import-template',
  81 + method: 'get',
  82 + responseType: 'blob'
  83 + })
  84 +}
  85 +
  86 +// 导入${table.classComment}
  87 +export function import${simpleClassName}(data) {
  88 + return request({
  89 + url: '${baseURL}/import',
  90 + method: 'post',
  91 + data
  92 + })
  93 +}
76 94 ## 特殊:主子表专属逻辑
77 95 #foreach ($subTable in $subTables)
78 96 #set ($index = $foreach.count - 1)
... ... @@ -157,4 +175,4 @@ export function export${simpleClassName}Excel(params) {
157 175 })
158 176 }
159 177 #end
160   -#end
161 178 \ No newline at end of file
  179 +#end
... ...
yudao-module-infra/src/main/resources/codegen/vue/views/index.vue.vm
... ... @@ -50,6 +50,14 @@
50 50 v-hasPermi="['${permissionPrefix}:create']">新增</el-button>
51 51 </el-col>
52 52 <el-col :span="1.5">
  53 + <el-button type="info" plain icon="el-icon-upload2" size="mini" @click="handleImport"
  54 + :loading="importLoading" v-hasPermi="['${permissionPrefix}:import']">导入</el-button>
  55 + </el-col>
  56 + <el-col :span="1.5">
  57 + <el-button type="info" plain icon="el-icon-document" size="mini" @click="handleImportTemplate"
  58 + v-hasPermi="['${permissionPrefix}:import']">导入模板</el-button>
  59 + </el-col>
  60 + <el-col :span="1.5">
53 61 <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" :loading="exportLoading"
54 62 v-hasPermi="['${permissionPrefix}:export']">导出</el-button>
55 63 </el-col>
... ... @@ -78,6 +86,7 @@
78 86 #end
79 87 <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
80 88 </el-row>
  89 + <input ref="importFileRef" type="file" style="display: none" accept=".xls,.xlsx" @change="handleImportFileChange" />
81 90  
82 91 ## 特殊:主子表专属逻辑
83 92 #if ( $table.templateType == 11 && $subTables && $subTables.size() > 0 )
... ... @@ -244,6 +253,8 @@ export default {
244 253 loading: true,
245 254 // 导出遮罩层
246 255 exportLoading: false,
  256 + // 导入遮罩层
  257 + importLoading: false,
247 258 // 显示搜索条件
248 259 showSearch: true,
249 260 ## 特殊:树表专属逻辑(树不需要分页接口)
... ... @@ -322,6 +333,42 @@ export default {
322 333 openForm(id) {
323 334 this.#[[$]]#refs["formRef"].open(id);
324 335 },
  336 + /** 导入按钮操作 */
  337 + handleImport() {
  338 + this.$refs.importFileRef && this.$refs.importFileRef.click();
  339 + },
  340 + /** 导入模板下载 */
  341 + async handleImportTemplate() {
  342 + const data = await ${simpleClassName}Api.import${simpleClassName}Template();
  343 + this.#[[$]]#download.excel(data, '${table.classComment}导入模板.xls');
  344 + },
  345 + /** 导入文件变更 */
  346 + async handleImportFileChange(event) {
  347 + const target = event.target;
  348 + const file = target.files && target.files[0];
  349 + if (!file) {
  350 + return;
  351 + }
  352 + this.importLoading = true;
  353 + try {
  354 + const formData = new FormData();
  355 + formData.append('file', file);
  356 + const res = await ${simpleClassName}Api.import${simpleClassName}(formData);
  357 + const data = res.data || res;
  358 + let text = '导入成功数量:' + (data.successCount || 0) + ';导入失败数量:' + (data.failureCount || 0) + ';';
  359 + if (data.failureRows) {
  360 + Object.keys(data.failureRows).forEach((rowNo) => {
  361 + text += '< 第' + rowNo + '行: ' + data.failureRows[rowNo] + ' >';
  362 + });
  363 + }
  364 + await this.$alert(text, '${table.classComment}导入结果', { dangerouslyUseHTMLString: true });
  365 + await this.getList();
  366 + } catch {
  367 + } finally {
  368 + target.value = '';
  369 + this.importLoading = false;
  370 + }
  371 + },
325 372 /** 删除按钮操作 */
326 373 async handleDelete(row) {
327 374 const ${primaryColumn.javaField} = row.${primaryColumn.javaField};
... ...
yudao-module-infra/src/main/resources/codegen/vue3_vben/api/api.ts.vm
... ... @@ -30,3 +30,13 @@ export function delete${simpleClassName}(id: number) {
30 30 export function export${simpleClassName}(params) {
31 31 return defHttp.download({ url: '${baseURL}/export-excel', params }, '${table.classComment}.xls')
32 32 }
  33 +
  34 +// 下载${table.classComment}导入模板
  35 +export function import${simpleClassName}Template() {
  36 + return defHttp.download({ url: '${baseURL}/get-import-template' }, '${table.classComment}导入模板.xls')
  37 +}
  38 +
  39 +// 导入${table.classComment}
  40 +export function import${simpleClassName}(data: FormData) {
  41 + return defHttp.post({ url: '${baseURL}/import', data })
  42 +}
... ...
yudao-module-infra/src/main/resources/codegen/vue3_vben/views/index.vue.vm
1 1 <script lang="ts" setup>
2 2 import ${simpleClassName}Modal from './${simpleClassName}Modal.vue'
3 3 import { columns, searchFormSchema } from './${classNameVar}.data'
  4 +import { ref } from 'vue'
4 5 import { useI18n } from '@/hooks/web/useI18n'
5 6 import { useMessage } from '@/hooks/web/useMessage'
6 7 import { useModal } from '@/components/Modal'
7 8 import { IconEnum } from '@/enums/appEnum'
8 9 import { BasicTable, TableAction, useTable } from '@/components/Table'
9   -import { delete${simpleClassName}, export${simpleClassName}, get${simpleClassName}Page } from '@/api/${table.moduleName}/${table.businessName}'
  10 +import {
  11 + delete${simpleClassName},
  12 + export${simpleClassName},
  13 + get${simpleClassName}Page,
  14 + import${simpleClassName},
  15 + import${simpleClassName}Template,
  16 +} from '@/api/${table.moduleName}/${table.businessName}'
10 17  
11 18 defineOptions({ name: '${table.className}' })
12 19  
13 20 const { t } = useI18n()
14 21 const { createConfirm, createMessage } = useMessage()
15 22 const [registerModal, { openModal }] = useModal()
  23 +const importFileRef = ref<HTMLInputElement>()
  24 +const importLoading = ref(false)
16 25  
17 26 const [registerTable, { getForm, reload }] = useTable({
18 27 title: '${table.classComment}列表',
... ... @@ -49,6 +58,42 @@ async function handleExport() {
49 58 })
50 59 }
51 60  
  61 +function handleImport() {
  62 + importFileRef.value?.click()
  63 +}
  64 +
  65 +async function handleImportTemplateDownload() {
  66 + await import${simpleClassName}Template()
  67 + createMessage.success('模板下载已开始')
  68 +}
  69 +
  70 +async function handleImportFileChange(event: Event) {
  71 + const target = event.target as HTMLInputElement
  72 + const file = target.files?.[0]
  73 + if (!file) {
  74 + return
  75 + }
  76 + importLoading.value = true
  77 + try {
  78 + const formData = new FormData()
  79 + formData.append('file', file)
  80 + const response: any = await import${simpleClassName}(formData)
  81 + const data = response?.data ?? response
  82 + let text =
  83 + '导入成功数量:' + (data?.successCount || 0) + ';导入失败数量:' + (data?.failureCount || 0) + ';'
  84 + if (data?.failureRows) {
  85 + Object.keys(data.failureRows).forEach((rowNo) => {
  86 + text += '< 第' + rowNo + '行: ' + data.failureRows[rowNo] + ' >'
  87 + })
  88 + }
  89 + createMessage.success(text)
  90 + await reload()
  91 + } finally {
  92 + target.value = ''
  93 + importLoading.value = false
  94 + }
  95 +}
  96 +
52 97 async function handleDelete(record: Recordable) {
53 98 await delete${simpleClassName}(record.id)
54 99 createMessage.success(t('common.delSuccessText'))
... ... @@ -62,6 +107,12 @@ async function handleDelete(record: Recordable) {
62 107 <a-button type="primary" v-auth="['${permissionPrefix}:create']" :preIcon="IconEnum.ADD" @click="handleCreate">
63 108 {{ t('action.create') }}
64 109 </a-button>
  110 + <a-button v-auth="['${permissionPrefix}:import']" :loading="importLoading" @click="handleImport">
  111 + 导入
  112 + </a-button>
  113 + <a-button v-auth="['${permissionPrefix}:import']" @click="handleImportTemplateDownload">
  114 + 导入模板
  115 + </a-button>
65 116 <a-button v-auth="['${permissionPrefix}:export']" :preIcon="IconEnum.EXPORT" @click="handleExport">
66 117 {{ t('action.export') }}
67 118 </a-button>
... ... @@ -87,6 +138,7 @@ async function handleDelete(record: Recordable) {
87 138 </template>
88 139 </template>
89 140 </BasicTable>
  141 + <input ref="importFileRef" type="file" accept=".xls,.xlsx" class="hidden" @change="handleImportFileChange" />
90 142 <${simpleClassName}Modal @register="registerModal" @success="reload()" />
91 143 </div>
92 144 </template>
... ...
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/general/api/api.ts.vm
... ... @@ -101,6 +101,16 @@ export function export${simpleClassName}(params: any) {
101 101 return requestClient.download('${baseURL}/export-excel', { params });
102 102 }
103 103  
  104 +/** 下载${table.classComment}导入模板 */
  105 +export function import${simpleClassName}Template() {
  106 + return requestClient.download('${baseURL}/get-import-template');
  107 +}
  108 +
  109 +/** 导入${table.classComment} */
  110 +export function import${simpleClassName}(data: FormData) {
  111 + return requestClient.post('${baseURL}/import', data);
  112 +}
  113 +
104 114 ## 特殊:主子表专属逻辑
105 115 #foreach ($subTable in $subTables)
106 116 #set ($index = $foreach.count - 1)
... ...
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/general/views/index.vue.vm
... ... @@ -27,9 +27,9 @@ import { getRangePickerDefaultProps } from &#39;#/utils/rangePickerProps&#39;;
27 27 import { $t } from '#/locales';
28 28 #if (${table.templateType} == 2)## 树表接口
29 29 import { handleTree } from '@vben/utils'
30   -import { get${simpleClassName}List, delete${simpleClassName}, export${simpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';
  30 +import { get${simpleClassName}List, delete${simpleClassName}, export${simpleClassName}, import${simpleClassName}, import${simpleClassName}Template } from '#/api/${table.moduleName}/${table.businessName}';
31 31 #else## 标准表接口
32   -import { get${simpleClassName}Page, delete${simpleClassName},#if ($deleteBatchEnable) delete${simpleClassName}List,#end export${simpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';
  32 +import { get${simpleClassName}Page, delete${simpleClassName},#if ($deleteBatchEnable) delete${simpleClassName}List,#end export${simpleClassName}, import${simpleClassName}, import${simpleClassName}Template } from '#/api/${table.moduleName}/${table.businessName}';
33 33 #end
34 34  
35 35 #if ($table.templateType == 12 || $table.templateType == 11) ## 内嵌和erp情况
... ... @@ -73,6 +73,8 @@ const queryParams = reactive({
73 73 })
74 74 const queryFormRef = ref() // 搜索的表单
75 75 const exportLoading = ref(false) // 导出的加载中
  76 +const importLoading = ref(false) // 导入的加载中
  77 +const importFileRef = ref<HTMLInputElement>()
76 78  
77 79 /** 查询列表 */
78 80 async function getList() {
... ... @@ -190,6 +192,44 @@ try {
190 192 }
191 193 }
192 194  
  195 +/** 导入模板下载 */
  196 +async function handleImportTemplateDownload() {
  197 + const data = await import${simpleClassName}Template();
  198 + downloadFileFromBlobPart({ fileName: '${table.classComment}导入模板.xls', source: data });
  199 +}
  200 +
  201 +/** 导入按钮操作 */
  202 +function handleImport() {
  203 + importFileRef.value?.click();
  204 +}
  205 +
  206 +/** 导入文件变更 */
  207 +async function handleImportFileChange(event: Event) {
  208 + const target = event.target as HTMLInputElement;
  209 + const file = target.files?.[0];
  210 + if (!file) {
  211 + return;
  212 + }
  213 + importLoading.value = true;
  214 + try {
  215 + const formData = new FormData();
  216 + formData.append('file', file);
  217 + const response: any = await import${simpleClassName}(formData);
  218 + const data = response?.data ?? response ?? {};
  219 + let text = '导入成功数量:' + (data.successCount || 0) + ';导入失败数量:' + (data.failureCount || 0) + ';';
  220 + if (data.failureRows) {
  221 + Object.keys(data.failureRows).forEach((rowNo) => {
  222 + text += '< 第' + rowNo + '行: ' + data.failureRows[rowNo] + ' >';
  223 + });
  224 + }
  225 + message.info(text);
  226 + await getList();
  227 + } finally {
  228 + target.value = '';
  229 + importLoading.value = false;
  230 + }
  231 +}
  232 +
193 233 #if (${table.templateType} == 2)
194 234 /** 切换树形展开/收缩状态 */
195 235 const isExpanded = ref(true);
... ... @@ -209,6 +249,7 @@ onMounted(() =&gt; {
209 249 <template>
210 250 <Page auto-content-height>
211 251 <FormModal @success="getList" />
  252 + <input ref="importFileRef" type="file" accept=".xls,.xlsx" class="hidden" @change="handleImportFileChange" />
212 253  
213 254 <Card v-if="!hiddenSearchBar" class="mb-4">
214 255 <!-- 搜索工作栏 -->
... ... @@ -315,6 +356,23 @@ onMounted(() =&gt; {
315 356 {{ $t('ui.actionTitle.create', ['${table.classComment}']) }}
316 357 </Button>
317 358 <Button
  359 + class="ml-2"
  360 + type="primary"
  361 + :loading="importLoading"
  362 + @click="handleImport"
  363 + v-access:code="['${permissionPrefix}:import']"
  364 + >
  365 + 导入
  366 + </Button>
  367 + <Button
  368 + class="ml-2"
  369 + type="primary"
  370 + @click="handleImportTemplateDownload"
  371 + v-access:code="['${permissionPrefix}:import']"
  372 + >
  373 + 导入模板
  374 + </Button>
  375 + <Button
318 376 :icon="h(Download)"
319 377 type="primary"
320 378 class="ml-2"
... ...
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/views/index.vue.vm
... ... @@ -16,6 +16,8 @@ import {
16 16 delete${simpleClassName},
17 17 export${simpleClassName},
18 18 get${simpleClassName}List,
  19 + import${simpleClassName},
  20 + import${simpleClassName}Template,
19 21 } from '#/api/${table.moduleName}/${table.businessName}';
20 22 #else## 标准表接口
21 23 import {
... ... @@ -24,6 +26,8 @@ import {
24 26 delete${simpleClassName}List,#end
25 27 export${simpleClassName},
26 28 get${simpleClassName}Page,
  29 + import${simpleClassName},
  30 + import${simpleClassName}Template,
27 31 } from '#/api/${table.moduleName}/${table.businessName}';
28 32 #end
29 33 import { $t } from '#/locales';
... ... @@ -50,6 +54,8 @@ const [FormModal, formModalApi] = useVbenModal({
50 54 connectedComponent: Form,
51 55 destroyOnClose: true,
52 56 });
  57 +const importLoading = ref(false);
  58 +const importFileRef = ref<HTMLInputElement>();
53 59 #if (${table.templateType} == 2)## 树表特有:控制表格展开收缩
54 60  
55 61 /** 切换树形展开/收缩状态 */
... ... @@ -135,6 +141,44 @@ async function handleExport() {
135 141 downloadFileFromBlobPart({ fileName: '${table.classComment}.xls', source: data });
136 142 }
137 143  
  144 +/** 导入模板下载 */
  145 +async function handleImportTemplateDownload() {
  146 + const data = await import${simpleClassName}Template();
  147 + downloadFileFromBlobPart({ fileName: '${table.classComment}导入模板.xls', source: data });
  148 +}
  149 +
  150 +/** 导入按钮操作 */
  151 +function handleImport() {
  152 + importFileRef.value?.click();
  153 +}
  154 +
  155 +/** 导入文件变更 */
  156 +async function handleImportFileChange(event: Event) {
  157 + const target = event.target as HTMLInputElement;
  158 + const file = target.files?.[0];
  159 + if (!file) {
  160 + return;
  161 + }
  162 + importLoading.value = true;
  163 + try {
  164 + const formData = new FormData();
  165 + formData.append('file', file);
  166 + const response: any = await import${simpleClassName}(formData);
  167 + const data = response?.data ?? response ?? {};
  168 + let text = '导入成功数量:' + (data.successCount || 0) + ';导入失败数量:' + (data.failureCount || 0) + ';';
  169 + if (data.failureRows) {
  170 + Object.keys(data.failureRows).forEach((rowNo) => {
  171 + text += '< 第' + rowNo + '行: ' + data.failureRows[rowNo] + ' >';
  172 + });
  173 + }
  174 + message.info(text);
  175 + handleRefresh();
  176 + } finally {
  177 + target.value = '';
  178 + importLoading.value = false;
  179 + }
  180 +}
  181 +
138 182 const [Grid, gridApi] = useVbenVxeGrid({
139 183 formOptions: {
140 184 schema: useGridFormSchema(),
... ... @@ -210,6 +254,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
210 254 <template>
211 255 <Page auto-content-height>
212 256 <FormModal @success="handleRefresh" />
  257 + <input ref="importFileRef" type="file" accept=".xls,.xlsx" class="hidden" @change="handleImportFileChange" />
213 258 #if ($table.templateType == 11) ## erp情况
214 259 <div>
215 260 #end
... ... @@ -248,6 +293,19 @@ const [Grid, gridApi] = useVbenVxeGrid({
248 293 },
249 294 #end
250 295 {
  296 + label: '导入',
  297 + type: 'primary',
  298 + auth: ['${table.moduleName}:${simpleClassName_strikeCase}:import'],
  299 + disabled: importLoading.value,
  300 + onClick: handleImport,
  301 + },
  302 + {
  303 + label: '导入模板',
  304 + type: 'primary',
  305 + auth: ['${table.moduleName}:${simpleClassName_strikeCase}:import'],
  306 + onClick: handleImportTemplateDownload,
  307 + },
  308 + {
251 309 label: $t('ui.actionTitle.export'),
252 310 type: 'primary',
253 311 icon: ACTION_ICON.DOWNLOAD,
... ... @@ -318,4 +376,4 @@ const [Grid, gridApi] = useVbenVxeGrid({
318 376 </div>
319 377 #end
320 378 </Page>
321   -</template>
322 379 \ No newline at end of file
  380 +</template>
... ...
yudao-module-infra/src/main/resources/codegen/vue3_vben5_ele/general/api/api.ts.vm
... ... @@ -101,6 +101,16 @@ export function export${simpleClassName}(params: any) {
101 101 return requestClient.download('${baseURL}/export-excel', { params });
102 102 }
103 103  
  104 +/** 下载${table.classComment}导入模板 */
  105 +export function import${simpleClassName}Template() {
  106 + return requestClient.download('${baseURL}/get-import-template');
  107 +}
  108 +
  109 +/** 导入${table.classComment} */
  110 +export function import${simpleClassName}(data: FormData) {
  111 + return requestClient.post('${baseURL}/import', data);
  112 +}
  113 +
104 114 ## 特殊:主子表专属逻辑
105 115 #foreach ($subTable in $subTables)
106 116 #set ($index = $foreach.count - 1)
... ...
yudao-module-infra/src/main/resources/codegen/vue3_vben5_ele/general/views/index.vue.vm
... ... @@ -26,12 +26,11 @@ import { VxeColumn, VxeTable } from &#39;#/adapter/vxe-table&#39;;
26 26 import { $t } from '#/locales';
27 27 #if (${table.templateType} == 2)## 树表接口
28 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}, import${simpleClassName}, import${simpleClassName}Template } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
30 30 #else## 标准表接口
31 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}, import${simpleClassName}, import${simpleClassName}Template } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
33 33 #end
34   -import { downloadFileFromBlobPart } from '@vben/utils';
35 34  
36 35 #if ($table.templateType == 12 || $table.templateType == 11) ## 内嵌和erp情况
37 36 /** 子表的列表 */
... ... @@ -74,6 +73,8 @@ const queryParams = reactive({
74 73 })
75 74 const queryFormRef = ref() // 搜索的表单
76 75 const exportLoading = ref(false) // 导出的加载中
  76 +const importLoading = ref(false) // 导入的加载中
  77 +const importFileRef = ref<HTMLInputElement>()
77 78  
78 79 /** 查询列表 */
79 80 const getList = async () => {
... ... @@ -189,6 +190,44 @@ try {
189 190 }
190 191 }
191 192  
  193 +/** 导入模板下载 */
  194 +async function handleImportTemplateDownload() {
  195 + const data = await import${simpleClassName}Template();
  196 + downloadFileFromBlobPart({ fileName: '${table.classComment}导入模板.xls', source: data });
  197 +}
  198 +
  199 +/** 导入按钮操作 */
  200 +function handleImport() {
  201 + importFileRef.value?.click();
  202 +}
  203 +
  204 +/** 导入文件变更 */
  205 +async function handleImportFileChange(event: Event) {
  206 + const target = event.target as HTMLInputElement;
  207 + const file = target.files?.[0];
  208 + if (!file) {
  209 + return;
  210 + }
  211 + importLoading.value = true;
  212 + try {
  213 + const formData = new FormData();
  214 + formData.append('file', file);
  215 + const response: any = await import${simpleClassName}(formData);
  216 + const data = response?.data ?? response ?? {};
  217 + let text = '导入成功数量:' + (data.successCount || 0) + ';导入失败数量:' + (data.failureCount || 0) + ';';
  218 + if (data.failureRows) {
  219 + Object.keys(data.failureRows).forEach((rowNo) => {
  220 + text += '< 第' + rowNo + '行: ' + data.failureRows[rowNo] + ' >';
  221 + });
  222 + }
  223 + ElMessage.info(text);
  224 + await getList();
  225 + } finally {
  226 + target.value = '';
  227 + importLoading.value = false;
  228 + }
  229 +}
  230 +
192 231 #if (${table.templateType} == 2)
193 232 /** 切换树形展开/收缩状态 */
194 233 const isExpanded = ref(true);
... ... @@ -208,6 +247,7 @@ onMounted(() =&gt; {
208 247 <template>
209 248 <Page auto-content-height>
210 249 <FormModal @success="getList" />
  250 + <input ref="importFileRef" type="file" accept=".xls,.xlsx" class="hidden" @change="handleImportFileChange" />
211 251  
212 252 <ContentWrap v-if="!hiddenSearchBar">
213 253 <!-- 搜索工作栏 -->
... ... @@ -317,6 +357,23 @@ onMounted(() =&gt; {
317 357 {{ $t('ui.actionTitle.create', ['${table.classComment}']) }}
318 358 </el-button>
319 359 <el-button
  360 + class="ml-2"
  361 + type="primary"
  362 + :loading="importLoading"
  363 + @click="handleImport"
  364 + v-access:code="['${permissionPrefix}:import']"
  365 + >
  366 + 导入
  367 + </el-button>
  368 + <el-button
  369 + class="ml-2"
  370 + type="primary"
  371 + @click="handleImportTemplateDownload"
  372 + v-access:code="['${permissionPrefix}:import']"
  373 + >
  374 + 导入模板
  375 + </el-button>
  376 + <el-button
320 377 :icon="h(Download)"
321 378 type="primary"
322 379 class="ml-2"
... ...
yudao-module-infra/src/main/resources/codegen/vue3_vben5_ele/schema/api/api.ts.vm
... ... @@ -114,6 +114,16 @@ export function export${simpleClassName}(params: any) {
114 114 return requestClient.download('${baseURL}/export-excel', { params });
115 115 }
116 116  
  117 +/** 下载${table.classComment}导入模板 */
  118 +export function import${simpleClassName}Template() {
  119 + return requestClient.download('${baseURL}/get-import-template');
  120 +}
  121 +
  122 +/** 导入${table.classComment} */
  123 +export function import${simpleClassName}(data: FormData) {
  124 + return requestClient.post('${baseURL}/import', data);
  125 +}
  126 +
117 127 ## 特殊:主子表专属逻辑
118 128 #foreach ($subTable in $subTables)
119 129 #set ($index = $foreach.count - 1)
... ...
yudao-module-infra/src/main/resources/codegen/vue3_vben5_ele/schema/views/index.vue.vm
... ... @@ -16,6 +16,8 @@ import {
16 16 delete${simpleClassName},
17 17 export${simpleClassName},
18 18 get${simpleClassName}List,
  19 + import${simpleClassName},
  20 + import${simpleClassName}Template,
19 21 } from '#/api/${table.moduleName}/${table.businessName}';
20 22 #else## 标准表接口
21 23 import {
... ... @@ -24,6 +26,8 @@ import {
24 26 delete${simpleClassName}List,#end
25 27 export${simpleClassName},
26 28 get${simpleClassName}Page,
  29 + import${simpleClassName},
  30 + import${simpleClassName}Template,
27 31 } from '#/api/${table.moduleName}/${table.businessName}';
28 32 #end
29 33 import { $t } from '#/locales';
... ... @@ -50,6 +54,8 @@ const [FormModal, formModalApi] = useVbenModal({
50 54 connectedComponent: Form,
51 55 destroyOnClose: true,
52 56 });
  57 +const importLoading = ref(false);
  58 +const importFileRef = ref<HTMLInputElement>();
53 59 #if (${table.templateType} == 2)## 树表特有:控制表格展开收缩
54 60  
55 61 /** 切换树形展开/收缩状态 */
... ... @@ -133,6 +139,44 @@ async function handleExport() {
133 139 downloadFileFromBlobPart({ fileName: '${table.classComment}.xls', source: data });
134 140 }
135 141  
  142 +/** 导入模板下载 */
  143 +async function handleImportTemplateDownload() {
  144 + const data = await import${simpleClassName}Template();
  145 + downloadFileFromBlobPart({ fileName: '${table.classComment}导入模板.xls', source: data });
  146 +}
  147 +
  148 +/** 导入按钮操作 */
  149 +function handleImport() {
  150 + importFileRef.value?.click();
  151 +}
  152 +
  153 +/** 导入文件变更 */
  154 +async function handleImportFileChange(event: Event) {
  155 + const target = event.target as HTMLInputElement;
  156 + const file = target.files?.[0];
  157 + if (!file) {
  158 + return;
  159 + }
  160 + importLoading.value = true;
  161 + try {
  162 + const formData = new FormData();
  163 + formData.append('file', file);
  164 + const response: any = await import${simpleClassName}(formData);
  165 + const data = response?.data ?? response ?? {};
  166 + let text = '导入成功数量:' + (data.successCount || 0) + ';导入失败数量:' + (data.failureCount || 0) + ';';
  167 + if (data.failureRows) {
  168 + Object.keys(data.failureRows).forEach((rowNo) => {
  169 + text += '< 第' + rowNo + '行: ' + data.failureRows[rowNo] + ' >';
  170 + });
  171 + }
  172 + ElMessage.info(text);
  173 + handleRefresh();
  174 + } finally {
  175 + target.value = '';
  176 + importLoading.value = false;
  177 + }
  178 +}
  179 +
136 180 const [Grid, gridApi] = useVbenVxeGrid({
137 181 formOptions: {
138 182 schema: useGridFormSchema(),
... ... @@ -208,6 +252,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
208 252 <template>
209 253 <Page auto-content-height>
210 254 <FormModal @success="handleRefresh" />
  255 + <input ref="importFileRef" type="file" accept=".xls,.xlsx" class="hidden" @change="handleImportFileChange" />
211 256 #if ($table.templateType == 11) ## erp情况
212 257 <div>
213 258 #end
... ... @@ -246,6 +291,19 @@ const [Grid, gridApi] = useVbenVxeGrid({
246 291 },
247 292 #end
248 293 {
  294 + label: '导入',
  295 + type: 'primary',
  296 + auth: ['${table.moduleName}:${simpleClassName_strikeCase}:import'],
  297 + disabled: importLoading.value,
  298 + onClick: handleImport,
  299 + },
  300 + {
  301 + label: '导入模板',
  302 + type: 'primary',
  303 + auth: ['${table.moduleName}:${simpleClassName_strikeCase}:import'],
  304 + onClick: handleImportTemplateDownload,
  305 + },
  306 + {
249 307 label: $t('ui.actionTitle.export'),
250 308 type: 'primary',
251 309 icon: ACTION_ICON.DOWNLOAD,
... ... @@ -317,4 +375,4 @@ const [Grid, gridApi] = useVbenVxeGrid({
317 375 </div>
318 376 #end
319 377 </Page>
320   -</template>
321 378 \ No newline at end of file
  379 +</template>
... ...