quxin
2026-09-02 55f0ca7c32e522ddbdefea0487c7fbde047707d9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import type { Ref } from 'vue';
 
import type { AxiosProgressEvent } from '#/api/infra/file';
 
import { computed, unref } from 'vue';
 
import { useAppConfig } from '@vben/hooks';
import { $t } from '@vben/locales';
 
import { requestClient } from '#/api/request';
 
const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
 
/**
 * 上传类型
 */
enum UPLOAD_TYPE {
  // 客户端直接上传(只支持S3服务)
  CLIENT = 'client',
  // 客户端发送到后端上传
  SERVER = 'server',
}
 
/**
 * 上传类型钩子函数
 * @param acceptRef 接受的文件类型
 * @param helpTextRef 帮助文本
 * @param maxNumberRef 最大文件数量
 * @param maxSizeRef 最大文件大小
 * @returns 文件类型限制和帮助文本的计算属性
 */
export function useUploadType({
  acceptRef,
  helpTextRef,
  maxNumberRef,
  maxSizeRef,
}: {
  acceptRef: Ref<string[]>;
  helpTextRef: Ref<string>;
  maxNumberRef: Ref<number>;
  maxSizeRef: Ref<number>;
}) {
  // 文件类型限制
  const getAccept = computed(() => {
    const accept = unref(acceptRef);
    if (accept && accept.length > 0) {
      return accept;
    }
    return [];
  });
  const getStringAccept = computed(() => {
    return unref(getAccept)
      .map((item) => {
        return item.indexOf('/') > 0 || item.startsWith('.')
          ? item
          : `.${item}`;
      })
      .join(',');
  });
 
  // 支持jpg、jpeg、png格式,不超过2M,最多可选择10张图片,。
  const getHelpText = computed(() => {
    const helpText = unref(helpTextRef);
    if (helpText) {
      return helpText;
    }
    const helpTexts: string[] = [];
 
    const accept = unref(acceptRef);
    if (accept.length > 0) {
      helpTexts.push($t('ui.upload.accept', [accept.join(',')]));
    }
 
    const maxSize = unref(maxSizeRef);
    if (maxSize) {
      helpTexts.push($t('ui.upload.maxSize', [maxSize]));
    }
 
    const maxNumber = unref(maxNumberRef);
    if (maxNumber && maxNumber !== Infinity) {
      helpTexts.push($t('ui.upload.maxNumber', [maxNumber]));
    }
    return helpTexts.join(',');
  });
  return { getAccept, getStringAccept, getHelpText };
}
 
/**
 * 上传钩子函数
 * @param directory 上传目录
 * @returns 上传 URL 和自定义上传方法
 */
export function useUpload(directory?: string) {
  // 后端上传地址
  const uploadUrl = getUploadUrl();
  // 是否使用前端直连上传
  const isClientUpload =
    UPLOAD_TYPE.CLIENT === import.meta.env.VITE_UPLOAD_TYPE;
  // 重写上传方法,统一走 system 模块 Storage API
  async function httpRequest(
    file: File,
    onUploadProgress?: AxiosProgressEvent,
  ) {
    const formData = new FormData();
    formData.append('files', file);
    // 使用公共上传接口,返回永久公开 previewURL
    const blobs = await requestClient.post('/system/storage-blob/public-upload', formData, {
      onUploadProgress,
      headers: { 'Content-Type': 'multipart/form-data' },
    });
    const blob = Array.isArray(blobs) ? blobs[0] : blobs;
    // 存储完整 previewURL(含 publicKey),确保可访问
    return blob ? { 
      url: blob.previewURL || blob.url,
      name: blob.name || blob.originalFilename, 
      id: blob.id 
    } : {};
  }
 
  return {
    uploadUrl,
    httpRequest,
  };
}
 
/**
 * 获得上传 URL(兼容旧版,实际调用走 httpRequest)
 */
export function getUploadUrl(): string {
  return `${apiURL}/system/storage-blob/upload`;
}