From b8f3dc696c6907da579bc281e0682a0236c2e8d9 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期五, 24 七月 2026 14:21:20 +0800
Subject: [PATCH] refactor(upload): 重构文件上传组件以支持 blobId 存储

---
 src/views/im/manager/channel/material/data.ts        |    3 
 src/views/erp/purchase/in/data.ts                    |   13 -
 src/views/erp/finance/payment/data.ts                |   13 -
 src/views/mes/pd/project/modules/document-form.vue   |   20 +
 src/views/erp/purchase/order/data.ts                 |   13 -
 src/views/erp/purchase/request/data.ts               |    3 
 src/views/hrm/resignation/modules/form.vue           |   12 +
 src/api/hrm/salary/payment/index.ts                  |    4 
 src/api/hrm/resignation/index.ts                     |    4 
 src/views/erp/sale/return/data.ts                    |   13 -
 src/api/system/storage/index.ts                      |   68 +++++++
 src/api/crm/saleQuotation/index.ts                   |    4 
 src/components/upload/file-upload.vue                |   18 +
 src/views/hrm/leave/modules/form.vue                 |   12 +
 src/views/im/manager/face/pack/data.ts               |    6 
 src/api/hrm/leave/index.ts                           |    4 
 src/views/hrm/transfer/data.ts                       |    8 
 src/components/upload/typing.ts                      |    6 
 src/views/hrm/attendance/exception/data.ts           |   11 +
 src/views/erp/stock/move/data.ts                     |   13 -
 src/api/hrm/transfer/index.ts                        |    4 
 src/views/hrm/resignation/data.ts                    |    8 
 src/views/crm/followup/data.ts                       |    7 
 src/views/im/manager/channel/list/data.ts            |    3 
 src/views/erp/purchase/return/data.ts                |   13 -
 src/api/crm/followup/index.ts                        |    4 
 src/views/hrm/leave/data.ts                          |    8 
 src/views/erp/finance/receipt/data.ts                |   13 -
 src/views/erp/sale/order/data.ts                     |   13 -
 src/views/hrm/transfer/modules/form.vue              |    9 +
 src/views/mes/md/item/modules/product-media-list.vue |    1 
 src/api/hrm/attendance/exception/index.ts            |    4 
 src/views/crm/followup/modules/form.vue              |    9 +
 src/views/erp/stock/check/data.ts                    |   13 -
 src/views/erp/stock/out/data.ts                      |   13 -
 src/api/mes/pd/document/index.ts                     |    5 
 src/views/hrm/attendance/exception/modules/form.vue  |   12 +
 src/views/erp/sale/out/data.ts                       |   13 -
 src/components/upload/use-upload.ts                  |   80 +-------
 src/views/erp/stock/in/data.ts                       |   13 -
 40 files changed, 221 insertions(+), 272 deletions(-)

diff --git a/src/api/crm/followup/index.ts b/src/api/crm/followup/index.ts
index 20592a9..1bfb01e 100644
--- a/src/api/crm/followup/index.ts
+++ b/src/api/crm/followup/index.ts
@@ -1,6 +1,7 @@
 import type { PageParam, PageResult } from '@vben/request';
 
 import { requestClient } from '#/api/request';
+import type { SystemStorageApi } from '#/api/system/storage';
 
 export namespace CrmFollowUpApi {
   /** 璺熻繘璁板綍淇℃伅 */
@@ -11,7 +12,8 @@
     type: number; // 璺熻繘绫诲瀷
     content: string; // 璺熻繘鍐呭
     picUrls: string[]; // 鍥剧墖
-    fileUrls: string[]; // 闄勪欢
+    blobIds?: number[]; // 闄勪欢 blobId 鍒楄〃
+    attachmentList?: SystemStorageApi.StorageBlob[]; // 闄勪欢鍒楄〃
     nextTime: Date; // 涓嬫鑱旂郴鏃堕棿
     businessIds: number[]; // 鍏宠仈鐨勫晢鏈虹紪鍙锋暟缁�
     businesses: Business[]; // 鍏宠仈鐨勫晢鏈烘暟缁�
diff --git a/src/api/crm/saleQuotation/index.ts b/src/api/crm/saleQuotation/index.ts
index ca0fe6e..108444b 100644
--- a/src/api/crm/saleQuotation/index.ts
+++ b/src/api/crm/saleQuotation/index.ts
@@ -1,6 +1,7 @@
 import type { PageParam, PageResult } from '@vben/request';
 
 import { requestClient } from '#/api/request';
+import type { SystemStorageApi } from '#/api/system/storage';
 
 export namespace CrmSaleQuotationApi {
   /** 鎶ヤ环鍗曚俊鎭� */
@@ -20,7 +21,8 @@
     discountPercent: number;
     totalPrice: number;
     taxRate?: number;
-    fileUrl?: string;
+    blobIds?: number[];
+    attachmentList?: SystemStorageApi.StorageBlob[];
     remark?: string;
     status: number;
     contractId?: number;
diff --git a/src/api/hrm/attendance/exception/index.ts b/src/api/hrm/attendance/exception/index.ts
index 1e822e3..1adeb2a 100644
--- a/src/api/hrm/attendance/exception/index.ts
+++ b/src/api/hrm/attendance/exception/index.ts
@@ -1,6 +1,7 @@
 import type { PageParam, PageResult } from '#/packages/effects/request/src';
 
 import { requestClient } from '#/api/request';
+import type { SystemStorageApi } from '#/api/system/storage';
 
 export namespace HrmAttendanceExceptionApi {
   /** 鑰冨嫟寮傚父鐢宠 */
@@ -13,7 +14,8 @@
     exceptionType?: number;
     exceptionTypeName?: string;
     reason?: string;
-    fileUrl?: string;
+    blobIds?: number[];
+    attachmentList?: SystemStorageApi.StorageBlob[];
     status?: number;
     processInstanceId?: string;
     remark?: string;
diff --git a/src/api/hrm/leave/index.ts b/src/api/hrm/leave/index.ts
index 981d7ff..e280286 100644
--- a/src/api/hrm/leave/index.ts
+++ b/src/api/hrm/leave/index.ts
@@ -1,6 +1,7 @@
 import type { PageParam, PageResult } from '#/packages/effects/request/src';
 
 import { requestClient } from '#/api/request';
+import type { SystemStorageApi } from '#/api/system/storage';
 
 export namespace HrmLeaveApi {
   /** 璇峰亣鐢宠 */
@@ -14,7 +15,8 @@
     endTime?: string;
     duration?: number;
     reason?: string;
-    fileUrl?: string;
+    blobIds?: number[];
+    attachmentList?: SystemStorageApi.StorageBlob[];
     status?: number;
     processInstanceId?: string;
     remark?: string;
diff --git a/src/api/hrm/resignation/index.ts b/src/api/hrm/resignation/index.ts
index 0e01e44..8b90cd2 100644
--- a/src/api/hrm/resignation/index.ts
+++ b/src/api/hrm/resignation/index.ts
@@ -1,6 +1,7 @@
 import type { PageParam, PageResult } from '#/packages/effects/request/src';
 
 import { requestClient } from '#/api/request';
+import type { SystemStorageApi } from '#/api/system/storage';
 
 export namespace HrmResignationApi {
   /** 绂昏亴鐢宠 */
@@ -16,7 +17,8 @@
     handoverUserId?: number;
     handoverUserName?: string;
     handoverStatus?: number;
-    fileUrl?: string;
+    blobIds?: number[];
+    attachmentList?: SystemStorageApi.StorageBlob[];
     status?: number;
     processInstanceId?: string;
     remark?: string;
diff --git a/src/api/hrm/salary/payment/index.ts b/src/api/hrm/salary/payment/index.ts
index 624558e..141d002 100644
--- a/src/api/hrm/salary/payment/index.ts
+++ b/src/api/hrm/salary/payment/index.ts
@@ -1,6 +1,7 @@
 import type { PageParam, PageResult } from '#/packages/effects/request/src';
 
 import { requestClient } from '#/api/request';
+import type { SystemStorageApi } from '#/api/system/storage';
 
 export namespace HrmSalaryPaymentApi {
   /** 钖祫鍙戞斁鍙拌处 */
@@ -13,7 +14,8 @@
     paymentMethod?: number;
     paymentTime?: string;
     status?: number;
-    fileUrl?: string;
+    blobIds?: number[];
+    attachmentList?: SystemStorageApi.StorageBlob[];
     remark?: string;
     createTime?: string;
   }
diff --git a/src/api/hrm/transfer/index.ts b/src/api/hrm/transfer/index.ts
index 23631c8..e992674 100644
--- a/src/api/hrm/transfer/index.ts
+++ b/src/api/hrm/transfer/index.ts
@@ -1,6 +1,7 @@
 import type { PageParam, PageResult } from '#/packages/effects/request/src';
 
 import { requestClient } from '#/api/request';
+import type { SystemStorageApi } from '#/api/system/storage';
 
 export namespace HrmTransferApi {
   /** 璋冨矖鐢宠 */
@@ -19,7 +20,8 @@
     transferType?: number;
     effectiveDate?: string;
     reason?: string;
-    fileUrl?: string;
+    blobIds?: number[];
+    attachmentList?: SystemStorageApi.StorageBlob[];
     status?: number;
     processInstanceId?: string;
     remark?: string;
diff --git a/src/api/mes/pd/document/index.ts b/src/api/mes/pd/document/index.ts
index c326c12..9ee756c 100644
--- a/src/api/mes/pd/document/index.ts
+++ b/src/api/mes/pd/document/index.ts
@@ -1,6 +1,7 @@
 import type { PageParam, PageResult } from '#/packages/effects/request/src';
 
 import { requestClient } from '#/api/request';
+import type { SystemStorageApi } from '#/api/system/storage';
 
 export namespace MesPdDocumentApi {
   /** 璁捐璧勬枡 */
@@ -22,8 +23,8 @@
     uploadUserNickname?: string;
     /** 涓婁紶鏃堕棿 */
     uploadTime?: number;
-    /** 鏂囦欢URL */
-    fileUrl?: string;
+    blobIds?: number[];
+    attachmentList?: SystemStorageApi.StorageBlob[];
     /** 澶囨敞 */
     remark?: string;
     /** 鍒涘缓鏃堕棿 */
diff --git a/src/api/system/storage/index.ts b/src/api/system/storage/index.ts
new file mode 100644
index 0000000..95ad345
--- /dev/null
+++ b/src/api/system/storage/index.ts
@@ -0,0 +1,68 @@
+import { requestClient } from '#/api/request';
+
+export namespace SystemStorageApi {
+  /** 涓婁紶鏂囦欢杩斿洖鐨� Blob 淇℃伅 */
+  export interface StorageBlob {
+    id: number; // 鏂囦欢 ID
+    resourceKey?: string; // 璧勬簮鍞竴鏍囪瘑
+    contentType?: string; // MIME 绫诲瀷
+    originalFilename?: string; // 鍘熸枃浠跺悕
+    uidFilename?: string; // 鍞竴鏂囦欢鍚�
+    byteSize?: number; // 鏂囦欢澶у皬锛堝瓧鑺傦級
+    path?: string; // 鏂囦欢鐩稿璺緞
+    previewURL?: string; // 棰勮鍦板潃
+    url?: string; // 鏂囦欢璁块棶 URL锛堝吋瀹瑰瓧娈碉級
+    name?: string; // 鍘熸枃浠跺悕锛堝吋瀹瑰瓧娈碉級
+    downloadURL?: string; // 涓嬭浇鍦板潃
+    storageAttachmentId?: number; // 鍏宠仈鐨勯檮浠� ID
+    application?: string; // 鏂囦欢鐢ㄩ��
+  }
+
+  /** 闄勪欢缁戝畾璇锋眰 */
+  export interface AttachmentBindReq {
+    application?: string; // 鏂囦欢鐢ㄩ��
+    recordType: string; // 鍏宠仈璁板綍绫诲瀷
+    recordId: number; // 鍏宠仈璁板綍 ID
+    blobItems: { blobId: number; application?: string }[]; // 寰呯粦瀹氱殑鏂囦欢鍒楄〃
+  }
+
+  /** 闄勪欢鍒楄〃鏌ヨ鍙傛暟 */
+  export interface AttachmentListParams {
+    recordType: string;
+    recordId: number;
+    application?: string;
+  }
+}
+
+/** 涓婁紶鏂囦欢 */
+export function uploadFile(files: globalThis.File[]) {
+  const formData = new FormData();
+  files.forEach((file) => formData.append('files', file));
+  return requestClient.post<SystemStorageApi.StorageBlob[]>(
+    '/system/storage-blob/upload',
+    formData,
+  );
+}
+
+/** 鏌ヨ闄勪欢鍒楄〃 */
+export function listAttachments(params: SystemStorageApi.AttachmentListParams) {
+  return requestClient.get<SystemStorageApi.StorageBlob[]>(
+    '/system/storage-attachment/list',
+    { params },
+  );
+}
+
+/** 缁戝畾闄勪欢鍒颁笟鍔¤褰� */
+export function bindAttachments(data: SystemStorageApi.AttachmentBindReq) {
+  return requestClient.post<boolean>(
+    '/system/storage-attachment/bind',
+    data,
+  );
+}
+
+/** 鎵归噺鍒犻櫎闄勪欢 */
+export function deleteAttachments(ids: number[]) {
+  return requestClient.delete<boolean>('/system/storage-attachment/delete', {
+    data: ids,
+  });
+}
diff --git a/src/components/upload/file-upload.vue b/src/components/upload/file-upload.vue
index 8f78d2f..e784c68 100644
--- a/src/components/upload/file-upload.vue
+++ b/src/components/upload/file-upload.vue
@@ -35,6 +35,8 @@
   returnText: false,
   showDescription: false,
   showDownloadIcon: true,
+  valueKey: 'url',
+  valuePrefix: '',
 });
 const emit = defineEmits([
   'change',
@@ -95,6 +97,14 @@
             };
           } else if (item && isObject(item)) {
             return item;
+          } else if (item && (typeof item === 'number' || typeof item === 'string')) {
+            return {
+              uid: `${-i}`,
+              name: `鏂囦欢 ${String(item)}`,
+              status: UploadResultStatus.DONE,
+              url: item,
+              response: { [props.valueKey]: item },
+            } as UploadFile;
           }
           return null;
         })
@@ -255,7 +265,13 @@
       if (item?.response && props?.resultField) {
         return item?.response;
       }
-      return item?.url || item?.response?.url || item?.response;
+      const val =
+        item?.response?.[props.valueKey] ??
+        item?.[props.valueKey] ??
+        item?.url ??
+        item?.response?.url ??
+        item?.response;
+      return props.valuePrefix ? `${props.valuePrefix}${val}` : val;
     });
 
   // 鍗曚釜鏂囦欢鐨勬儏鍐碉紝鏍规嵁杈撳叆鍙傛暟绫诲瀷鍐冲畾杩斿洖鏍煎紡
diff --git a/src/components/upload/typing.ts b/src/components/upload/typing.ts
index 7a9475f..fa2a0e9 100644
--- a/src/components/upload/typing.ts
+++ b/src/components/upload/typing.ts
@@ -23,12 +23,14 @@
   helpText?: string;
   listType?: UploadListType;
   maxNumber?: number; // 鏈�澶ф暟閲忕殑鏂囦欢锛孖nfinity涓嶉檺鍒�
-  modelValue?: string | string[]; // v-model 鏀寔
+  modelValue?: string | string[] | number[]; // v-model 鏀寔
   maxSize?: number; // 鏂囦欢鏈�澶у灏慚B
   multiple?: boolean; // 鏄惁鏀寔澶氶��
   resultField?: string; // support xxx.xxx.xx
   returnText?: boolean; // 鏄惁杩斿洖鏂囦欢鏂囨湰鍐呭
   showDescription?: boolean; // 鏄惁鏄剧ず涓嬮潰鐨勬弿杩�
   showDownloadIcon?: boolean; // 鏄惁鏄剧ず涓嬭浇鎸夐挳
-  value?: string | string[];
+  value?: string | string[] | number[];
+  valueKey?: string; // 杩斿洖鍊肩殑瀛楁鍚嶏紝榛樿 'url'锛岃涓� 'id' 鏃惰繑鍥� blobId
+  valuePrefix?: string; // valueKey 杩斿洖鍊肩殑鍓嶇紑
 }
diff --git a/src/components/upload/use-upload.ts b/src/components/upload/use-upload.ts
index 99b79c0..4b79942 100644
--- a/src/components/upload/use-upload.ts
+++ b/src/components/upload/use-upload.ts
@@ -1,14 +1,13 @@
 import type { Ref } from 'vue';
 
-import type { AxiosProgressEvent, InfraFileApi } from '#/api/infra/file';
+import type { AxiosProgressEvent } from '#/api/infra/file';
 
 import { computed, unref } from 'vue';
 
 import { useAppConfig } from '@vben/hooks';
 import { $t } from '@vben/locales';
 
-import { createFile, getFilePresignedUrl, uploadFile } from '#/api/infra/file';
-import { baseRequestClient } from '#/api/request';
+import { requestClient } from '#/api/request';
 
 const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
 
@@ -97,34 +96,18 @@
   // 鏄惁浣跨敤鍓嶇鐩磋繛涓婁紶
   const isClientUpload =
     UPLOAD_TYPE.CLIENT === import.meta.env.VITE_UPLOAD_TYPE;
-  // 閲嶅啓ElUpload涓婁紶鏂规硶
+  // 閲嶅啓涓婁紶鏂规硶锛岀粺涓�璧� system 妯″潡 Storage API
   async function httpRequest(
     file: File,
     onUploadProgress?: AxiosProgressEvent,
   ) {
-    // 妯″紡涓�锛氬墠绔笂浼�
-    if (isClientUpload) {
-      // 1.1 鐢熸垚鏂囦欢鍚嶇О
-      const fileName = await generateFileName(file);
-      // 1.2 鑾峰彇鏂囦欢棰勭鍚嶅湴鍧�
-      const presignedInfo = await getFilePresignedUrl(fileName, directory);
-      // 1.3 涓婁紶鏂囦欢
-      return baseRequestClient
-        .put(presignedInfo.uploadUrl, file, {
-          headers: {
-            'Content-Type': file.type,
-          },
-        })
-        .then(() => {
-          // 1.4. 璁板綍鏂囦欢淇℃伅鍒板悗绔紙寮傛锛�
-          createFile0(presignedInfo, file);
-          // 閫氱煡鎴愬姛锛屾暟鎹牸寮忎繚鎸佷笌鍚庣涓婁紶鐨勮繑鍥炵粨鏋滀竴鑷�
-          return { url: presignedInfo.url };
-        });
-    } else {
-      // 妯″紡浜岋細鍚庣涓婁紶
-      return uploadFile({ file, directory }, onUploadProgress);
-    }
+    const formData = new FormData();
+    formData.append('files', file);
+    const blobs = await requestClient.post('/system/storage-blob/upload', formData, {
+      onUploadProgress,
+    });
+    const blob = Array.isArray(blobs) ? blobs[0] : blobs;
+    return blob ? { url: blob.url || blob.previewURL, name: blob.name, id: blob.id } : {};
   }
 
   return {
@@ -134,47 +117,8 @@
 }
 
 /**
- * 鑾峰緱涓婁紶 URL
+ * 鑾峰緱涓婁紶 URL锛堝吋瀹规棫鐗堬紝瀹為檯璋冪敤璧� httpRequest锛�
  */
 export function getUploadUrl(): string {
-  return `${apiURL}/infra/file/upload`;
-}
-
-/**
- * 鍒涘缓鏂囦欢淇℃伅
- *
- * @param vo 鏂囦欢棰勭鍚嶄俊鎭�
- * @param file 鏂囦欢
- */
-function createFile0(
-  vo: InfraFileApi.FilePresignedUrlRespVO,
-  file: File,
-): InfraFileApi.File {
-  const fileVO = {
-    configId: vo.configId,
-    url: vo.url,
-    path: vo.path,
-    name: file.name,
-    type: file.type,
-    size: file.size,
-  };
-  createFile(fileVO);
-  return fileVO;
-}
-
-/**
- * 鐢熸垚鏂囦欢鍚嶇О锛堜娇鐢ㄧ畻娉昐HA256锛�
- *
- * @param file 瑕佷笂浼犵殑鏂囦欢
- */
-async function generateFileName(file: File) {
-  // // 璇诲彇鏂囦欢鍐呭
-  // const data = await file.arrayBuffer();
-  // const wordArray = CryptoJS.lib.WordArray.create(data);
-  // // 璁$畻SHA256
-  // const sha256 = CryptoJS.SHA256(wordArray).toString();
-  // // 鎷兼帴鍚庣紑
-  // const ext = file.name.slice(Math.max(0, file.name.lastIndexOf('.')));
-  // return `${sha256}${ext}`;
-  return file.name;
+  return `${apiURL}/system/storage-blob/upload`;
 }
diff --git a/src/views/crm/followup/data.ts b/src/views/crm/followup/data.ts
index 1cf2669..9ff3914 100644
--- a/src/views/crm/followup/data.ts
+++ b/src/views/crm/followup/data.ts
@@ -66,9 +66,14 @@
       component: 'ImageUpload',
     },
     {
-      fieldName: 'fileUrls',
+      fieldName: 'blobIds',
       label: '闄勪欢',
       component: 'FileUpload',
+      componentProps: {
+        valueKey: 'id',
+        maxNumber: 10,
+        multiple: true,
+      },
     },
     {
       fieldName: 'contactIds',
diff --git a/src/views/crm/followup/modules/form.vue b/src/views/crm/followup/modules/form.vue
index b373555..7437c80 100644
--- a/src/views/crm/followup/modules/form.vue
+++ b/src/views/crm/followup/modules/form.vue
@@ -67,6 +67,15 @@
     try {
       // 璁剧疆鍒� values
       await formApi.setValues(data);
+      if (data.attachmentList?.length) {
+        await formApi.setFieldValue('blobIds', data.attachmentList.map(item => ({
+          uid: String(item.id),
+          name: item.name || '',
+          url: item.url || '',
+          status: 'done',
+          id: item.id,
+        })));
+      }
     } finally {
       modalApi.unlock();
     }
diff --git a/src/views/erp/finance/payment/data.ts b/src/views/erp/finance/payment/data.ts
index 771fd0c..b1fb694 100644
--- a/src/views/erp/finance/payment/data.ts
+++ b/src/views/erp/finance/payment/data.ts
@@ -90,19 +90,6 @@
       label: '闄勪欢',
       component: 'FileUpload',
       componentProps: {
-        maxNumber: 1,
-        maxSize: 10,
-        accept: [
-          'pdf',
-          'doc',
-          'docx',
-          'xls',
-          'xlsx',
-          'txt',
-          'jpg',
-          'jpeg',
-          'png',
-        ],
         showDescription: formType !== 'detail',
         disabled: formType === 'detail',
       },
diff --git a/src/views/erp/finance/receipt/data.ts b/src/views/erp/finance/receipt/data.ts
index abe109b..6f51e6a 100644
--- a/src/views/erp/finance/receipt/data.ts
+++ b/src/views/erp/finance/receipt/data.ts
@@ -90,19 +90,6 @@
       label: '闄勪欢',
       component: 'FileUpload',
       componentProps: {
-        maxNumber: 1,
-        maxSize: 10,
-        accept: [
-          'pdf',
-          'doc',
-          'docx',
-          'xls',
-          'xlsx',
-          'txt',
-          'jpg',
-          'jpeg',
-          'png',
-        ],
         showDescription: formType !== 'detail',
         disabled: formType === 'detail',
       },
diff --git a/src/views/erp/purchase/in/data.ts b/src/views/erp/purchase/in/data.ts
index 1861164..2eb48e6 100644
--- a/src/views/erp/purchase/in/data.ts
+++ b/src/views/erp/purchase/in/data.ts
@@ -91,19 +91,6 @@
       label: '闄勪欢',
       component: 'FileUpload',
       componentProps: {
-        maxNumber: 1,
-        maxSize: 10,
-        accept: [
-          'pdf',
-          'doc',
-          'docx',
-          'xls',
-          'xlsx',
-          'txt',
-          'jpg',
-          'jpeg',
-          'png',
-        ],
         showDescription: formType !== 'detail',
         disabled: formType === 'detail',
       },
diff --git a/src/views/erp/purchase/order/data.ts b/src/views/erp/purchase/order/data.ts
index fb82f00..d13eb84 100644
--- a/src/views/erp/purchase/order/data.ts
+++ b/src/views/erp/purchase/order/data.ts
@@ -91,19 +91,6 @@
       label: '闄勪欢',
       component: 'FileUpload',
       componentProps: {
-        maxNumber: 1,
-        maxSize: 10,
-        accept: [
-          'pdf',
-          'doc',
-          'docx',
-          'xls',
-          'xlsx',
-          'txt',
-          'jpg',
-          'jpeg',
-          'png',
-        ],
         showDescription: formType !== 'detail',
         disabled: formType === 'detail',
       },
diff --git a/src/views/erp/purchase/request/data.ts b/src/views/erp/purchase/request/data.ts
index 11c9837..eb869d1 100644
--- a/src/views/erp/purchase/request/data.ts
+++ b/src/views/erp/purchase/request/data.ts
@@ -108,9 +108,6 @@
       component: 'FileUpload',
       componentProps: {
         placeholder: '璇蜂笂浼犻檮浠�',
-        maxNumber: 5,
-        maxSize: 20,
-        accept: ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'jpg', 'png'],
         disabled: formType === 'detail',
       },
       formItemClass: 'col-span-2',
diff --git a/src/views/erp/purchase/return/data.ts b/src/views/erp/purchase/return/data.ts
index c4d80d3..8f82c78 100644
--- a/src/views/erp/purchase/return/data.ts
+++ b/src/views/erp/purchase/return/data.ts
@@ -91,19 +91,6 @@
       label: '闄勪欢',
       component: 'FileUpload',
       componentProps: {
-        maxNumber: 1,
-        maxSize: 10,
-        accept: [
-          'pdf',
-          'doc',
-          'docx',
-          'xls',
-          'xlsx',
-          'txt',
-          'jpg',
-          'jpeg',
-          'png',
-        ],
         showDescription: formType !== 'detail',
         disabled: formType === 'detail',
       },
diff --git a/src/views/erp/sale/order/data.ts b/src/views/erp/sale/order/data.ts
index 9aea440..1fad18d 100644
--- a/src/views/erp/sale/order/data.ts
+++ b/src/views/erp/sale/order/data.ts
@@ -115,19 +115,6 @@
       label: '闄勪欢',
       component: 'FileUpload',
       componentProps: {
-        maxNumber: 1,
-        maxSize: 10,
-        accept: [
-          'pdf',
-          'doc',
-          'docx',
-          'xls',
-          'xlsx',
-          'txt',
-          'jpg',
-          'jpeg',
-          'png',
-        ],
         showDescription: formType !== 'detail',
         disabled: formType === 'detail',
       },
diff --git a/src/views/erp/sale/out/data.ts b/src/views/erp/sale/out/data.ts
index e0d67d4..b7fc5b8 100644
--- a/src/views/erp/sale/out/data.ts
+++ b/src/views/erp/sale/out/data.ts
@@ -108,19 +108,6 @@
       label: '闄勪欢',
       component: 'FileUpload',
       componentProps: {
-        maxNumber: 1,
-        maxSize: 10,
-        accept: [
-          'pdf',
-          'doc',
-          'docx',
-          'xls',
-          'xlsx',
-          'txt',
-          'jpg',
-          'jpeg',
-          'png',
-        ],
         showDescription: formType !== 'detail',
         disabled: formType === 'detail',
       },
diff --git a/src/views/erp/sale/return/data.ts b/src/views/erp/sale/return/data.ts
index 1f7d738..206d90d 100644
--- a/src/views/erp/sale/return/data.ts
+++ b/src/views/erp/sale/return/data.ts
@@ -104,19 +104,6 @@
       label: '闄勪欢',
       component: 'FileUpload',
       componentProps: {
-        maxNumber: 1,
-        maxSize: 10,
-        accept: [
-          'pdf',
-          'doc',
-          'docx',
-          'xls',
-          'xlsx',
-          'txt',
-          'jpg',
-          'jpeg',
-          'png',
-        ],
         showDescription: formType !== 'detail',
         disabled: formType === 'detail',
       },
diff --git a/src/views/erp/stock/check/data.ts b/src/views/erp/stock/check/data.ts
index d2082b7..054c35f 100644
--- a/src/views/erp/stock/check/data.ts
+++ b/src/views/erp/stock/check/data.ts
@@ -60,19 +60,6 @@
       label: '闄勪欢',
       component: 'FileUpload',
       componentProps: {
-        maxNumber: 1,
-        maxSize: 10,
-        accept: [
-          'pdf',
-          'doc',
-          'docx',
-          'xls',
-          'xlsx',
-          'txt',
-          'jpg',
-          'jpeg',
-          'png',
-        ],
         showDescription: formType !== 'detail',
         disabled: formType === 'detail',
       },
diff --git a/src/views/erp/stock/in/data.ts b/src/views/erp/stock/in/data.ts
index c521b32..67c3598 100644
--- a/src/views/erp/stock/in/data.ts
+++ b/src/views/erp/stock/in/data.ts
@@ -75,19 +75,6 @@
       label: '闄勪欢',
       component: 'FileUpload',
       componentProps: {
-        maxNumber: 1,
-        maxSize: 10,
-        accept: [
-          'pdf',
-          'doc',
-          'docx',
-          'xls',
-          'xlsx',
-          'txt',
-          'jpg',
-          'jpeg',
-          'png',
-        ],
         showDescription: formType !== 'detail',
         disabled: formType === 'detail',
       },
diff --git a/src/views/erp/stock/move/data.ts b/src/views/erp/stock/move/data.ts
index 7c05d4d..aba8a00 100644
--- a/src/views/erp/stock/move/data.ts
+++ b/src/views/erp/stock/move/data.ts
@@ -60,19 +60,6 @@
       label: '闄勪欢',
       component: 'FileUpload',
       componentProps: {
-        maxNumber: 1,
-        maxSize: 10,
-        accept: [
-          'pdf',
-          'doc',
-          'docx',
-          'xls',
-          'xlsx',
-          'txt',
-          'jpg',
-          'jpeg',
-          'png',
-        ],
         showDescription: formType !== 'detail',
         disabled: formType === 'detail',
       },
diff --git a/src/views/erp/stock/out/data.ts b/src/views/erp/stock/out/data.ts
index c4dac24..50da807 100644
--- a/src/views/erp/stock/out/data.ts
+++ b/src/views/erp/stock/out/data.ts
@@ -77,19 +77,6 @@
       label: '闄勪欢',
       component: 'FileUpload',
       componentProps: {
-        maxNumber: 1,
-        maxSize: 10,
-        accept: [
-          'pdf',
-          'doc',
-          'docx',
-          'xls',
-          'xlsx',
-          'txt',
-          'jpg',
-          'jpeg',
-          'png',
-        ],
         showDescription: formType !== 'detail',
         disabled: formType === 'detail',
       },
diff --git a/src/views/hrm/attendance/exception/data.ts b/src/views/hrm/attendance/exception/data.ts
index 1521cf2..6c6784d 100644
--- a/src/views/hrm/attendance/exception/data.ts
+++ b/src/views/hrm/attendance/exception/data.ts
@@ -59,6 +59,17 @@
       rules: 'required',
     },
     {
+      fieldName: 'blobIds',
+      label: '闄勪欢',
+      component: 'FileUpload',
+      formItemClass: 'col-span-2',
+      componentProps: {
+        valueKey: 'id',
+        maxNumber: 10,
+        multiple: true,
+      },
+    },
+    {
       fieldName: 'remark',
       label: '澶囨敞',
       component: 'Textarea',
diff --git a/src/views/hrm/attendance/exception/modules/form.vue b/src/views/hrm/attendance/exception/modules/form.vue
index abd26f9..79676ef 100644
--- a/src/views/hrm/attendance/exception/modules/form.vue
+++ b/src/views/hrm/attendance/exception/modules/form.vue
@@ -74,7 +74,17 @@
     }
     modalApi.lock();
     try {
-      await formApi.setValues(await getAttendanceException(data.id));
+      const detail = await getAttendanceException(data.id);
+      await formApi.setValues(detail);
+      if (detail.attachmentList?.length) {
+        await formApi.setFieldValue('blobIds', detail.attachmentList.map(item => ({
+          uid: String(item.id),
+          name: item.name || '',
+          url: item.url || '',
+          status: 'done',
+          id: item.id,
+        })));
+      }
     } finally {
       modalApi.unlock();
     }
diff --git a/src/views/hrm/leave/data.ts b/src/views/hrm/leave/data.ts
index 79fc8a1..c6d5154 100644
--- a/src/views/hrm/leave/data.ts
+++ b/src/views/hrm/leave/data.ts
@@ -81,12 +81,14 @@
       rules: 'required',
     },
     {
-      fieldName: 'fileUrl',
+      fieldName: 'blobIds',
       label: '闄勪欢',
-      component: 'Input',
+      component: 'FileUpload',
       formItemClass: 'col-span-2',
       componentProps: {
-        placeholder: '璇蜂笂浼犻檮浠�',
+        valueKey: 'id',
+        maxNumber: 10,
+        multiple: true,
       },
     },
     {
diff --git a/src/views/hrm/leave/modules/form.vue b/src/views/hrm/leave/modules/form.vue
index 9e76099..4aab581 100644
--- a/src/views/hrm/leave/modules/form.vue
+++ b/src/views/hrm/leave/modules/form.vue
@@ -78,7 +78,17 @@
     }
     modalApi.lock();
     try {
-      await formApi.setValues(await getLeave(data.id));
+      const detail = await getLeave(data.id);
+      await formApi.setValues(detail);
+      if (detail.attachmentList?.length) {
+        await formApi.setFieldValue('blobIds', detail.attachmentList.map(item => ({
+          uid: String(item.id),
+          name: item.name || '',
+          url: item.url || '',
+          status: 'done',
+          id: item.id,
+        })));
+      }
     } finally {
       modalApi.unlock();
     }
diff --git a/src/views/hrm/resignation/data.ts b/src/views/hrm/resignation/data.ts
index 0ea0da4..3131c33 100644
--- a/src/views/hrm/resignation/data.ts
+++ b/src/views/hrm/resignation/data.ts
@@ -68,12 +68,14 @@
       rules: 'required',
     },
     {
-      fieldName: 'fileUrl',
+      fieldName: 'blobIds',
       label: '闄勪欢',
-      component: 'Input',
+      component: 'FileUpload',
       formItemClass: 'col-span-2',
       componentProps: {
-        placeholder: '璇蜂笂浼犻檮浠�',
+        valueKey: 'id',
+        maxNumber: 10,
+        multiple: true,
       },
     },
     {
diff --git a/src/views/hrm/resignation/modules/form.vue b/src/views/hrm/resignation/modules/form.vue
index 8ba43bb..32e6b51 100644
--- a/src/views/hrm/resignation/modules/form.vue
+++ b/src/views/hrm/resignation/modules/form.vue
@@ -78,7 +78,17 @@
     }
     modalApi.lock();
     try {
-      await formApi.setValues(await getResignation(data.id));
+      const detail = await getResignation(data.id);
+      await formApi.setValues(detail);
+      if (detail.attachmentList?.length) {
+        await formApi.setFieldValue('blobIds', detail.attachmentList.map(item => ({
+          uid: String(item.id),
+          name: item.name || '',
+          url: item.url || '',
+          status: 'done',
+          id: item.id,
+        })));
+      }
     } finally {
       modalApi.unlock();
     }
diff --git a/src/views/hrm/transfer/data.ts b/src/views/hrm/transfer/data.ts
index 61cd895..e6f2411 100644
--- a/src/views/hrm/transfer/data.ts
+++ b/src/views/hrm/transfer/data.ts
@@ -122,12 +122,14 @@
       rules: 'required',
     },
     {
-      fieldName: 'fileUrl',
+      fieldName: 'blobIds',
       label: '闄勪欢',
-      component: 'Input',
+      component: 'FileUpload',
       formItemClass: 'col-span-2',
       componentProps: {
-        placeholder: '璇蜂笂浼犻檮浠�',
+        valueKey: 'id',
+        maxNumber: 10,
+        multiple: true,
       },
     },
     {
diff --git a/src/views/hrm/transfer/modules/form.vue b/src/views/hrm/transfer/modules/form.vue
index 5b07236..6e76835 100644
--- a/src/views/hrm/transfer/modules/form.vue
+++ b/src/views/hrm/transfer/modules/form.vue
@@ -95,6 +95,15 @@
       const transfer = await getTransfer(data.id);
       currentEmployeeId.value = transfer.employeeId;
       await formApi.setValues(transfer);
+      if (transfer.attachmentList?.length) {
+        await formApi.setFieldValue('blobIds', transfer.attachmentList.map(item => ({
+          uid: String(item.id),
+          name: item.name || '',
+          url: item.url || '',
+          status: 'done',
+          id: item.id,
+        })));
+      }
     } finally {
       modalApi.unlock();
     }
diff --git a/src/views/im/manager/channel/list/data.ts b/src/views/im/manager/channel/list/data.ts
index b182db3..c3d1f88 100644
--- a/src/views/im/manager/channel/list/data.ts
+++ b/src/views/im/manager/channel/list/data.ts
@@ -41,9 +41,6 @@
       fieldName: 'avatar',
       label: '棰戦亾澶村儚',
       component: 'ImageUpload',
-      componentProps: {
-        maxNumber: 1
-      },
       rules: 'required'
     },
     {
diff --git a/src/views/im/manager/channel/material/data.ts b/src/views/im/manager/channel/material/data.ts
index 34488e4..b10de49 100644
--- a/src/views/im/manager/channel/material/data.ts
+++ b/src/views/im/manager/channel/material/data.ts
@@ -52,9 +52,6 @@
       fieldName: 'coverUrl',
       label: '灏侀潰鍥�',
       component: 'ImageUpload',
-      componentProps: {
-        maxNumber: 1,
-      },
     },
     {
       fieldName: 'summary',
diff --git a/src/views/im/manager/face/pack/data.ts b/src/views/im/manager/face/pack/data.ts
index 143dfbe..b01fdab 100644
--- a/src/views/im/manager/face/pack/data.ts
+++ b/src/views/im/manager/face/pack/data.ts
@@ -22,9 +22,6 @@
       fieldName: 'icon',
       label: '灏侀潰',
       component: 'ImageUpload',
-      componentProps: {
-        maxNumber: 1,
-      },
     },
     {
       fieldName: 'name',
@@ -166,9 +163,6 @@
       fieldName: 'url',
       label: '琛ㄦ儏鍥�',
       component: 'ImageUpload',
-      componentProps: {
-        maxNumber: 1,
-      },
       rules: 'required',
     },
     {
diff --git a/src/views/mes/md/item/modules/product-media-list.vue b/src/views/mes/md/item/modules/product-media-list.vue
index e2b2adc..777e9fd 100644
--- a/src/views/mes/md/item/modules/product-media-list.vue
+++ b/src/views/mes/md/item/modules/product-media-list.vue
@@ -125,7 +125,6 @@
       label: '鍥剧墖',
       component: markRaw(ImageUpload),
       componentProps: {
-        maxNumber: 1,
         showDescription: false,
       },
     },
diff --git a/src/views/mes/pd/project/modules/document-form.vue b/src/views/mes/pd/project/modules/document-form.vue
index b08d6fa..d32b29c 100644
--- a/src/views/mes/pd/project/modules/document-form.vue
+++ b/src/views/mes/pd/project/modules/document-form.vue
@@ -67,17 +67,14 @@
       rules: 'required',
     },
     {
-      fieldName: 'fileUrl',
+      fieldName: 'blobIds',
       label: '鏂囦欢',
       component: markRaw(FileUpload),
       componentProps: {
+        valueKey: 'id',
         directory: 'mes/pd',
-        maxNumber: 1,
-        onChange: async (url: string) => {
-          if (url) {
-            await formApi?.setFieldValue('fileUrl', url);
-          }
-        },
+        maxNumber: 10,
+        multiple: true,
       },
     },
     {
@@ -127,6 +124,15 @@
       try {
         const detail = await getPdDocument(data.id);
         await formApi.setValues(detail);
+        if (detail.attachmentList?.length) {
+          await formApi.setFieldValue('blobIds', detail.attachmentList.map(item => ({
+            uid: String(item.id),
+            name: item.name || '',
+            url: item.url || '',
+            status: 'done',
+            id: item.id,
+          })));
+        }
       } finally {
         modalApi.unlock();
       }

--
Gitblit v1.9.3