3 小时以前 b8f3dc696c6907da579bc281e0682a0236c2e8d9
refactor(upload): 重构文件上传组件以支持 blobId 存储

- 将 fileUrl 字段统一替换为 blobIds,实现附件存储的标准化
- 为 FileUpload 组件添加 valueKey、maxNumber 和 multiple 属性配置
- 移除各个模块中限制文件上传数量和类型的组件属性设置
- 重构文件上传逻辑,统一使用 system/storage API 进行文件上传
- 添加对 blobId 数组格式的支持,增强组件的数据处理能力
- 优化表单详情加载时的附件回显逻辑
- 新增 SystemStorageApi 相关类型定义和 API 方法
- 调整 ImageUpload 组件的默认配置,移除不必要的 maxNumber 限制
已添加1个文件
已修改39个文件
493 ■■■■■ 文件已修改
src/api/crm/followup/index.ts 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/crm/saleQuotation/index.ts 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/hrm/attendance/exception/index.ts 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/hrm/leave/index.ts 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/hrm/resignation/index.ts 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/hrm/salary/payment/index.ts 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/hrm/transfer/index.ts 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pd/document/index.ts 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/system/storage/index.ts 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/upload/file-upload.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/upload/typing.ts 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/upload/use-upload.ts 80 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/crm/followup/data.ts 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/crm/followup/modules/form.vue 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/finance/payment/data.ts 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/finance/receipt/data.ts 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/purchase/in/data.ts 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/purchase/order/data.ts 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/purchase/request/data.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/purchase/return/data.ts 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/sale/order/data.ts 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/sale/out/data.ts 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/sale/return/data.ts 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/stock/check/data.ts 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/stock/in/data.ts 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/stock/move/data.ts 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/stock/out/data.ts 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/hrm/attendance/exception/data.ts 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/hrm/attendance/exception/modules/form.vue 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/hrm/leave/data.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/hrm/leave/modules/form.vue 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/hrm/resignation/data.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/hrm/resignation/modules/form.vue 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/hrm/transfer/data.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/hrm/transfer/modules/form.vue 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/im/manager/channel/list/data.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/im/manager/channel/material/data.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/im/manager/face/pack/data.ts 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/md/item/modules/product-media-list.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pd/project/modules/document-form.vue 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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[]; // å…³è”的商机数组
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;
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;
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;
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;
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;
  }
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;
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;
    /** åˆ›å»ºæ—¶é—´ */
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,
  });
}
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;
    });
  // å•个文件的情况,根据输入参数类型决定返回格式
src/components/upload/typing.ts
@@ -23,12 +23,14 @@
  helpText?: string;
  listType?: UploadListType;
  maxNumber?: number; // æœ€å¤§æ•°é‡çš„æ–‡ä»¶ï¼ŒInfinity不限制
  modelValue?: string | string[]; // v-model æ”¯æŒ
  modelValue?: string | string[] | number[]; // v-model æ”¯æŒ
  maxSize?: number; // æ–‡ä»¶æœ€å¤§å¤šå°‘MB
  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 è¿”回值的前缀
}
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;
}
/**
 * ç”Ÿæˆæ–‡ä»¶åç§°ï¼ˆä½¿ç”¨ç®—法SHA256)
 *
 * @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`;
}
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',
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();
    }
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',
      },
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',
      },
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',
      },
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',
      },
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',
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',
      },
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',
      },
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',
      },
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',
      },
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',
      },
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',
      },
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',
      },
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',
      },
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',
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();
    }
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,
      },
    },
    {
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();
    }
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,
      },
    },
    {
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();
    }
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,
      },
    },
    {
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();
    }
src/views/im/manager/channel/list/data.ts
@@ -41,9 +41,6 @@
      fieldName: 'avatar',
      label: '频道头像',
      component: 'ImageUpload',
      componentProps: {
        maxNumber: 1
      },
      rules: 'required'
    },
    {
src/views/im/manager/channel/material/data.ts
@@ -52,9 +52,6 @@
      fieldName: 'coverUrl',
      label: '封面图',
      component: 'ImageUpload',
      componentProps: {
        maxNumber: 1,
      },
    },
    {
      fieldName: 'summary',
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',
    },
    {
src/views/mes/md/item/modules/product-media-list.vue
@@ -125,7 +125,6 @@
      label: '图片',
      component: markRaw(ImageUpload),
      componentProps: {
        maxNumber: 1,
        showDescription: false,
      },
    },
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();
      }