Commit d93730d17a71fa21af7bbb1238908b8550e057cf
Merge branch 'master-jdk17' of https://github.com/YunaiV/ruoyi-vue-pro into master-jdk17
Showing
11 changed files
with
397 additions
and
9 deletions
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 '#/utils/rangePickerProps'; |
| 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(() => { |
| 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(() => { |
| 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 '#/adapter/vxe-table'; |
| 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(() => { |
| 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(() => { |
| 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> | ... | ... |