From d5656aecc633cdac7ebc286a85b07274fed16e1b Mon Sep 17 00:00:00 2001
From: liu <2021943741@qq.com>
Date: 星期四, 03 九月 2026 13:15:32 +0800
Subject: [PATCH] feat(srm): 供应商档案新增 Excel 导入导出

---
 src/views/srm/supplier/modules/import-form.vue |  100 +++++++++++++++++++++++++++++++++
 src/api/srm/supplier/index.ts                  |   18 ++++++
 src/views/srm/supplier/index.vue               |   35 +++++++++++
 src/views/srm/supplier/data.ts                 |   24 ++++++++
 4 files changed, 177 insertions(+), 0 deletions(-)

diff --git a/src/api/srm/supplier/index.ts b/src/api/srm/supplier/index.ts
index e75a362..e4e7c97 100644
--- a/src/api/srm/supplier/index.ts
+++ b/src/api/srm/supplier/index.ts
@@ -55,3 +55,21 @@
 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);
+}
diff --git a/src/views/srm/supplier/data.ts b/src/views/srm/supplier/data.ts
index 58dc484..f323861 100644
--- a/src/views/srm/supplier/data.ts
+++ b/src/views/srm/supplier/data.ts
@@ -176,3 +176,27 @@
     },
   ];
 }
+
+/** 瀵煎叆寮圭獥鐨勮〃鍗� */
+export function useImportFormSchema(): VbenFormSchema[] {
+  return [
+    {
+      fieldName: 'file',
+      label: '渚涘簲鍟嗘。妗�',
+      component: 'Upload',
+      rules: 'required',
+      help: '浠呭厑璁稿鍏� xls銆亁lsx 鏍煎紡鏂囦欢',
+    },
+    {
+      fieldName: 'updateSupport',
+      label: '鏄惁鏇存柊',
+      component: 'Switch',
+      componentProps: {
+        checkedChildren: '鏄�',
+        unCheckedChildren: '鍚�',
+      },
+      rules: z.boolean().default(false),
+      help: '鍕鹃�夊悗鎸夆�滀緵搴斿晢缂栫爜鈥濆尮閰嶅凡瀛樺湪妗f骞惰鐩栨洿鏂帮紱涓嶅嬀閫夋椂閲嶅缂栫爜璁″叆澶辫触鏄庣粏',
+    },
+  ];
+}
diff --git a/src/views/srm/supplier/index.vue b/src/views/srm/supplier/index.vue
index ff73e1a..6fe70eb 100644
--- a/src/views/srm/supplier/index.vue
+++ b/src/views/srm/supplier/index.vue
@@ -3,18 +3,21 @@
 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' });
 
@@ -28,6 +31,18 @@
 
 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) {
@@ -46,6 +61,11 @@
 
 const [FormModal, formModalApi] = useVbenModal({
   connectedComponent: SupplierForm,
+  destroyOnClose: true,
+});
+
+const [SupplierImportModal, importModalApi] = useVbenModal({
+  connectedComponent: SupplierImportForm,
   destroyOnClose: true,
 });
 
@@ -77,6 +97,7 @@
 <template>
   <Page auto-content-height>
     <FormModal @success="handleRefresh" />
+    <SupplierImportModal @success="handleRefresh" />
     <Grid table-title="渚涘簲鍟嗗垪琛�">
       <template #toolbar-tools>
         <TableAction
@@ -88,6 +109,20 @@
               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>
diff --git a/src/views/srm/supplier/modules/import-form.vue b/src/views/srm/supplier/modules/import-form.vue
new file mode 100644
index 0000000..3ca2f54
--- /dev/null
+++ b/src/views/srm/supplier/modules/import-form.vue
@@ -0,0 +1,100 @@
+<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>

--
Gitblit v1.9.3