feat(srm): 供应商档案新增 Excel 导入导出
- 工具栏新增 导入/导出 按钮(权限 srm:supplier:import / export)
- 新增导入弹窗(下载导入模板/选择文件/是否更新),导入后按新增/更新/失败汇总提示
- API 新增 exportSupplierExcel、getSupplierImportTemplate、importSupplierExcel
Co-Authored-By: Claude <noreply@anthropic.com>
| | |
| | | export function deleteSupplier(id: number) { |
| | | return requestClient.delete(`/srm/supplier/delete?id=${id}`); |
| | | } |
| | | |
| | | /** 导åºä¾åºåæ¡£æ¡ Excelï¼æå表æ¥è¯¢æ¡ä»¶ï¼ */ |
| | | export function exportSupplierExcel(params: any) { |
| | | return requestClient.download('/srm/supplier/export-excel', { params }); |
| | | } |
| | | |
| | | /** ä¸è½½ä¾åºåæ¡£æ¡å¯¼å
¥æ¨¡æ¿ */ |
| | | export function getSupplierImportTemplate() { |
| | | return requestClient.download('/srm/supplier/get-import-template'); |
| | | } |
| | | |
| | | /** 导å
¥ä¾åºåæ¡£æ¡ Excel */ |
| | | export function importSupplierExcel(data: { |
| | | file: File; |
| | | updateSupport: boolean; |
| | | }) { |
| | | return requestClient.upload('/srm/supplier/import-excel', data); |
| | | } |
| | |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** 导å
¥å¼¹çªç表å */ |
| | | export function useImportFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'file', |
| | | label: 'ä¾åºåæ¡£æ¡', |
| | | component: 'Upload', |
| | | rules: 'required', |
| | | help: 'ä»
å
许导å
¥ xlsãxlsx æ ¼å¼æä»¶', |
| | | }, |
| | | { |
| | | fieldName: 'updateSupport', |
| | | label: 'æ¯å¦æ´æ°', |
| | | component: 'Switch', |
| | | componentProps: { |
| | | checkedChildren: 'æ¯', |
| | | unCheckedChildren: 'å¦', |
| | | }, |
| | | rules: z.boolean().default(false), |
| | | help: 'å¾éåæâä¾åºåç¼ç âå¹é
å·²å卿¡£æ¡å¹¶è¦çæ´æ°ï¼ä¸å¾éæ¶éå¤ç¼ç 计å
¥å¤±è´¥æç»', |
| | | }, |
| | | ]; |
| | | } |
| | |
| | | import type { SrmSupplierApi } from '#/api/srm/supplier'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { downloadFileFromBlobPart } from '@vben/utils'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | deleteSupplier, |
| | | exportSupplierExcel, |
| | | getSupplierPage, |
| | | } from '#/api/srm/supplier'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import SupplierForm from './modules/form.vue'; |
| | | import SupplierImportForm from './modules/import-form.vue'; |
| | | |
| | | defineOptions({ name: 'SrmSupplier' }); |
| | | |
| | |
| | | |
| | | function handleEdit(row: SrmSupplierApi.SupplierVO) { |
| | | formModalApi.setData(row).open(); |
| | | } |
| | | |
| | | /** 导å
¥ä¾åºå */ |
| | | function handleImport() { |
| | | importModalApi.open(); |
| | | } |
| | | |
| | | /** 导åºä¾åºåï¼æå½åæç´¢æ¡ä»¶ï¼ */ |
| | | async function handleExport() { |
| | | const formValues = await gridApi.formApi.getValues(); |
| | | const data = await exportSupplierExcel(formValues); |
| | | downloadFileFromBlobPart({ fileName: 'ä¾åºåæ°æ®.xlsx', source: data }); |
| | | } |
| | | |
| | | async function handleDelete(row: SrmSupplierApi.SupplierVO) { |
| | |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: SupplierForm, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [SupplierImportModal, importModalApi] = useVbenModal({ |
| | | connectedComponent: SupplierImportForm, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <SupplierImportModal @success="handleRefresh" /> |
| | | <Grid table-title="ä¾åºåå表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | |
| | | auth: ['srm:supplier:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: $t('ui.actionTitle.import'), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.UPLOAD, |
| | | auth: ['srm:supplier:import'], |
| | | onClick: handleImport, |
| | | }, |
| | | { |
| | | label: $t('ui.actionTitle.export'), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.DOWNLOAD, |
| | | auth: ['srm:supplier:export'], |
| | | onClick: handleExport, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FileType } from 'ant-design-vue/es/upload/interface'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | import { downloadFileFromBlobPart } from '@vben/utils'; |
| | | |
| | | import { Button, message, Upload } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | getSupplierImportTemplate, |
| | | importSupplierExcel, |
| | | } from '#/api/srm/supplier'; |
| | | |
| | | import { useImportFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | formItemClass: 'col-span-2', |
| | | labelWidth: 110, |
| | | }, |
| | | layout: 'horizontal', |
| | | schema: useImportFormSchema(), |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | const data = await formApi.getValues(); |
| | | modalApi.lock(); |
| | | try { |
| | | const res = (await importSupplierExcel({ |
| | | file: data.file, |
| | | updateSupport: data.updateSupport, |
| | | })) as any; |
| | | // å
¼å®¹ååºç»æï¼ä¸å¡æ°æ®ç´æ¥è¿åæå
å¨ { code, data } ä¸ |
| | | const result = res?.data ?? res ?? {}; |
| | | const createList = result.createList ?? []; |
| | | const updateList = result.updateList ?? []; |
| | | const failureList = result.failureList ?? {}; |
| | | const failureCount = Object.keys(failureList).length; |
| | | let tip = `导å
¥å®æï¼æ°å¢ ${createList.length} æ¡ãæ´æ° ${updateList.length} æ¡ã失败 ${failureCount} æ¡ã`; |
| | | if (failureCount > 0) { |
| | | const sample = Object.entries(failureList) |
| | | .slice(0, 5) |
| | | .map(([name, reason]) => `${name}ï¼${reason}`) |
| | | .join('ï¼'); |
| | | tip += `失败åå 示ä¾ï¼${sample}`; |
| | | } |
| | | if (failureCount > 0) { |
| | | message.warning(tip); |
| | | } else { |
| | | message.success(tip); |
| | | } |
| | | // å
³éå¹¶å·æ° |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | |
| | | /** ä¸ä¼ å */ |
| | | function beforeUpload(file: FileType) { |
| | | formApi.setFieldValue('file', file); |
| | | return false; |
| | | } |
| | | |
| | | /** ä¸è½½æ¨¡æ¿ */ |
| | | async function handleDownload() { |
| | | const data = await getSupplierImportTemplate(); |
| | | downloadFileFromBlobPart({ fileName: 'ä¾åºå导å
¥æ¨¡æ¿.xlsx', source: data }); |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal title="ä¾åºåæ¡£æ¡å¯¼å
¥" class="w-1/3"> |
| | | <Form class="mx-4"> |
| | | <template #file> |
| | | <div class="w-full"> |
| | | <Upload :max-count="1" accept=".xls,.xlsx" :before-upload="beforeUpload"> |
| | | <Button type="primary"> éæ© Excel æä»¶ </Button> |
| | | </Upload> |
| | | </div> |
| | | </template> |
| | | </Form> |
| | | <template #prepend-footer> |
| | | <div class="flex flex-auto items-center"> |
| | | <Button @click="handleDownload"> ä¸è½½å¯¼å
¥æ¨¡æ¿ </Button> |
| | | </div> |
| | | </template> |
| | | </Modal> |
| | | </template> |