2025-04-17 23:30:34 +08:00
|
|
|
|
import type { Ref } from 'vue';
|
2025-04-18 18:30:50 +08:00
|
|
|
|
import type { AxiosProgressEvent, InfraFileApi } from '#/api/infra/file';
|
2025-04-17 23:30:34 +08:00
|
|
|
|
|
|
|
|
|
|
import { computed, unref } from 'vue';
|
|
|
|
|
|
import { $t } from '@vben/locales';
|
2025-04-18 18:30:50 +08:00
|
|
|
|
import CryptoJS from 'crypto-js'
|
|
|
|
|
|
import axios from 'axios'
|
|
|
|
|
|
import { uploadFile, getFilePresignedUrl, createFile } from '#/api/infra/file';
|
2025-04-17 23:30:34 +08:00
|
|
|
|
|
|
|
|
|
|
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 };
|
|
|
|
|
|
}
|
2025-04-18 18:30:50 +08:00
|
|
|
|
|
|
|
|
|
|
// TODO @芋艿:目前保持和 admin-vue3 一致,后续可能重构
|
|
|
|
|
|
export const useUpload = () => {
|
|
|
|
|
|
// 后端上传地址
|
|
|
|
|
|
const uploadUrl = getUploadUrl()
|
|
|
|
|
|
// 是否使用前端直连上传
|
|
|
|
|
|
const isClientUpload = UPLOAD_TYPE.CLIENT === import.meta.env.VITE_UPLOAD_TYPE
|
|
|
|
|
|
// 重写ElUpload上传方法
|
|
|
|
|
|
const httpRequest = async (file: File, onUploadProgress?: AxiosProgressEvent) => {
|
|
|
|
|
|
// 模式一:前端上传
|
|
|
|
|
|
if (isClientUpload) {
|
|
|
|
|
|
// 1.1 生成文件名称
|
|
|
|
|
|
const fileName = await generateFileName(file)
|
|
|
|
|
|
// 1.2 获取文件预签名地址
|
|
|
|
|
|
const presignedInfo = await getFilePresignedUrl(fileName)
|
|
|
|
|
|
// 1.3 上传文件
|
|
|
|
|
|
return axios
|
|
|
|
|
|
.put(presignedInfo.uploadUrl, file, {
|
|
|
|
|
|
headers: {
|
|
|
|
|
|
'Content-Type': file.type
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
.then(() => {
|
|
|
|
|
|
// 1.4. 记录文件信息到后端(异步)
|
|
|
|
|
|
createFile0(presignedInfo, fileName, file)
|
|
|
|
|
|
// 通知成功,数据格式保持与后端上传的返回结果一致
|
|
|
|
|
|
return { data: presignedInfo.url }
|
|
|
|
|
|
})
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 模式二:后端上传
|
|
|
|
|
|
return uploadFile({ file }, onUploadProgress);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
uploadUrl,
|
|
|
|
|
|
httpRequest
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获得上传 URL
|
|
|
|
|
|
*/
|
|
|
|
|
|
export const getUploadUrl = (): string => {
|
|
|
|
|
|
return import.meta.env.VITE_BASE_URL + import.meta.env.VITE_GLOB_API_URL + '/infra/file/upload'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 创建文件信息
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param vo 文件预签名信息
|
|
|
|
|
|
* @param name 文件名称
|
|
|
|
|
|
* @param file 文件
|
|
|
|
|
|
*/
|
|
|
|
|
|
function createFile0(vo: InfraFileApi.FilePresignedUrlRespVO, name: string, file: File) {
|
|
|
|
|
|
const fileVO = {
|
|
|
|
|
|
configId: vo.configId,
|
|
|
|
|
|
url: vo.url,
|
|
|
|
|
|
path: name,
|
|
|
|
|
|
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.substring(file.name.lastIndexOf('.'))
|
|
|
|
|
|
return `${sha256}${ext}`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 上传类型
|
|
|
|
|
|
*/
|
|
|
|
|
|
enum UPLOAD_TYPE {
|
|
|
|
|
|
// 客户端直接上传(只支持S3服务)
|
|
|
|
|
|
CLIENT = 'client',
|
|
|
|
|
|
// 客户端发送到后端上传
|
|
|
|
|
|
SERVER = 'server'
|
|
|
|
|
|
}
|