You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

216 lines
5.4 KiB

<template>
<div>
<el-upload
ref="uploadRef"
:action="`${BASE_PATH}/file/upload`"
:headers="{ Authorization: getToken() }"
multiple
:drag="uploadDrag"
:before-upload="beforeUpload"
@progress="uploadProgress"
@success="handleSuccess"
@error="handleError"
:show-file-list="false"
:accept="accept"
class="mb-16"
>
<el-button
>上传
<template #icon>
<icon name="el-icon-upload-filled" />
</template>
</el-button>
<template #tip>
<div class="el-upload__tip">
{{ tips }}
</div>
</template>
</el-upload>
<file-list v-model:files="files" :removeEnable="true" />
</div>
</template>
<script setup>
import feedback from "@/utils/feedback";
import { getToken } from "@/utils/token";
import { BASE_PATH } from "@/api/request";
const props = defineProps({
files: {
type: Array,
default: () => [],
},
tips: {
type: String,
default: ''
},
accept: {
type: String,
default: '*'
},
uploadDrag:{
type:Boolean,
default:false
}
});
const emit = defineEmits(["update:files"]);
const uploadRef = ref();
const files = ref(props.files);
const allowArchiveFallback = ref(false);
const fallbackArchiveExts = ['zip', 'rar', '7z'];
const archiveExts = [...fallbackArchiveExts, 'tar', 'gz', 'bz2', 'xz', 'iso', 'dmg'];
const uploadResponseTimeout = 15000;
const responseTimers = new Map();
function getFileExt(file) {
const name = (file?.name || file?.raw?.name || '').toLowerCase();
return name.includes('.') ? name.split('.').pop() : '';
}
function enableArchiveFallback(file) {
if (archiveExts.includes(getFileExt(file))) {
return false;
}
allowArchiveFallback.value = true;
feedback.msgError('文件上传失败,已临时允许上传一个 ZIP、RAR 或 7Z 压缩包');
return true;
}
function clearResponseTimer(file) {
const timer = responseTimers.get(file?.uid);
if (timer) {
window.clearTimeout(timer);
}
responseTimers.delete(file?.uid);
}
function startUploadResponseTimer(file) {
if (archiveExts.includes(getFileExt(file)) || responseTimers.has(file?.uid)) {
return;
}
const timer = window.setTimeout(() => {
responseTimers.delete(file.uid);
uploadRef.value?.abort(file);
handleError(new Error('文件保存响应超时'), file);
}, uploadResponseTimeout);
responseTimers.set(file.uid, timer);
}
onBeforeUnmount(() => {
responseTimers.forEach((timer) => window.clearTimeout(timer));
responseTimers.clear();
});
watch(
() => props.files,
(newValue) => {
if (newValue) {
files.value = newValue;
} else {
files.value = [];
}
}
);
watch(files, () => {
emit("update:files", files.value);
},{immediate:true});
function beforeUpload(file) {
const ext = getFileExt(file)
// 定义图片扩展名
const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg', 'tiff', 'tif', 'ico', 'heic', 'heif']
// 图片类型:提示转为 PDF
if (imageExts.includes(ext)) {
feedback.msgWarning('图片文件请转为 PDF 后再上传')
return false
}
if (archiveExts.includes(ext)) {
if (!allowArchiveFallback.value) {
feedback.msgWarning('压缩包仅在文件上传失败后允许上传')
return false
}
if (!fallbackArchiveExts.includes(ext)) {
feedback.msgWarning('文件上传失败后仅支持 ZIP、RAR 或 7Z 压缩包')
return false
}
}
// 文件大小限制 100MB
if (file.size >= 104857600) {
feedback.msgError('文件大小不能超过100MB');
return false;
}
if (fallbackArchiveExts.includes(ext)) {
allowArchiveFallback.value = false;
}
if (!archiveExts.includes(ext)) {
allowArchiveFallback.value = false;
}
files.value.push({
uid: file.uid,
percent: 0,
loading: true,
});
}
function uploadProgress(progressEvent, file) {
const filterFiles = files.value.filter((item) => file.uid === item.uid);
if (filterFiles.length) {
const percent = parseInt(progressEvent.percent);
filterFiles[0].percent = Math.min(99, percent);
if (percent >= 100) {
startUploadResponseTimer(file);
}
}
}
function handleSuccess(data, file) {
clearResponseTimer(file);
const filterFiles = files.value.filter((item) => file.uid === item.uid);
if (data.code !== 200) {
if (!enableArchiveFallback(file)) {
feedback.msgError(data.message);
}
if (filterFiles.length) {
files.value.splice(files.value.indexOf(filterFiles[0]), 1);
emit("update:files", files.value);
}
return;
}
if (!archiveExts.includes(getFileExt(file))) {
allowArchiveFallback.value = false;
}
if (filterFiles.length) {
filterFiles[0].percent = 100;
filterFiles[0].fileName = data.data.fileName;
filterFiles[0].filePath = data.data.filePath;
window.setTimeout(() => {
filterFiles[0].loading = false;
emit("update:files", files.value);
}, 300);
}
console.log("file", files.value);
}
function handleError(e, file) {
clearResponseTimer(file);
console.log(e, file);
const fileIndex = files.value.findIndex((item) => file.uid === item.uid);
if (fileIndex > -1) {
files.value.splice(fileIndex, 1);
emit("update:files", files.value);
}
if (enableArchiveFallback(file)) {
return;
}
feedback.msgError("上传失败,请重试!");
}
</script>