Files
vue3-element-admin/src/components/Upload/FileUpload.vue
胡少翔 f09973ddb7 fix: 🐛 修复图片\文件上传之后删除失败的bug
修复图片\文件上传之后删除失败的bug
2024-09-14 09:37:59 +08:00

333 lines
7.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- 文件上传组件 -->
<template>
<div>
<el-upload
v-model:file-list="fileList"
:class="props.showUploadBtn ? 'show-upload-btn' : 'hide-upload-btn'"
:style="props.style"
multiple
:headers="props.headers"
:data="props.data"
:name="props.name"
:before-upload="handleBeforeUpload"
:on-remove="handleRemove"
:on-progress="handleProgress"
:on-success="handleSuccessFile"
:on-error="handleError"
:action="props.action"
:accept="props.accept"
:limit="props.limit"
>
<el-button
type="primary"
v-if="props.showUploadBtn"
:disabled="fileList.length >= props.limit"
>
{{ props.uploadBtnText }}
</el-button>
<template #tip v-if="props.showTip">
<div class="el-upload__tip">
{{ props.tip }}
</div>
</template>
<template #file="{ file }">
<div class="el-upload-list__item-info">
<a class="el-upload-list__item-name" @click="downloadFile(file)">
<el-icon><Document /></el-icon>
<span class="el-upload-list__item-file-name">{{ file.name }}</span>
<span
class="el-icon--close"
v-if="props.showDelBtn"
@click.stop="handleRemove(file)"
>
<el-icon><Close /></el-icon>
</span>
</a>
</div>
</template>
</el-upload>
<el-progress
:style="{
display: showUploadPercent ? 'inline-flex' : 'none',
width: '100%',
}"
:percentage="uploadPercent"
:color="customColorMethod"
v-if="showUploadPercent"
/>
</div>
</template>
<script lang="ts" setup>
import {
UploadRawFile,
UploadUserFile,
UploadFile,
UploadProgressEvent,
UploadFiles,
} from "element-plus";
import { TOKEN_KEY } from "@/enums/CacheEnum";
import FileAPI from "@/api/file";
import { ref, watch } from "vue";
import { ElMessage } from "element-plus";
import { ResultEnum } from "@/enums/ResultEnum";
const emit = defineEmits(["update:modelValue"]);
const props = defineProps({
/**
* 文件集合
*/
modelValue: {
type: Array<UploadUserFile>,
default: () => [],
},
/**
* 上传地址
*/
action: {
type: String,
default: FileAPI.uploadUrl,
},
/**
* 文件上传数量限制
*/
limit: {
type: Number,
default: 10,
},
/**
* 是否显示删除按钮
*/
showDelBtn: {
type: Boolean,
default: true,
},
/**
* 是否显示上传按钮
*/
showUploadBtn: {
type: Boolean,
default: true,
},
/**
* 单个文件上传大小限制(单位byte)
*/
uploadMaxSize: {
type: Number,
default: 2 * 1024 * 1024,
},
/**
* 上传文件类型
*/
accept: {
type: String,
default: "*",
},
/**
* 上传按钮文本
*/
uploadBtnText: {
type: String,
default: "上传文件",
},
/**
* 是否展示提示信息
*/
showTip: {
type: Boolean,
default: false,
},
/**
* 提示信息内容
*/
tip: {
type: String,
default: "",
},
/**
* 请求头
*/
headers: {
type: Object,
default: () => {
return {
Authorization: localStorage.getItem(TOKEN_KEY),
};
},
},
/**
* 请求携带的额外参数
*/
data: {
type: Object,
default: () => {
return {};
},
},
/**
* 上传文件的参数名
*/
name: {
type: String,
default: "file",
},
/**
* 样式
*/
style: {
type: Object,
default: () => {
return {
width: "300px",
};
},
},
});
const fileList = ref([] as UploadUserFile[]);
const valFileList = ref([] as UploadUserFile[]);
const showUploadPercent = ref(false);
const uploadPercent = ref(0);
watch(
() => props.modelValue,
(newVal: UploadUserFile[]) => {
const filePaths = fileList.value.map((file) => file.url);
const fileNames = fileList.value.map((file) => file.name);
// 监听modelValue文件集合值未变化时跳过赋值
if (
filePaths.length > 0 &&
filePaths.length === newVal.length &&
filePaths.every((x) => newVal.some((y) => y.url === x)) &&
newVal.every((y) => filePaths.some((x) => x === y.url)) &&
fileNames.every((x) => newVal.some((y) => y.name === x)) &&
newVal.every((y) => fileNames.some((x) => x === y.name))
) {
return;
}
if (newVal.length <= 0) {
fileList.value = [];
valFileList.value = [];
return;
}
fileList.value = newVal.map((file) => {
return { name: file.name, url: file.url } as UploadFile;
});
valFileList.value = newVal.map((file) => {
return { name: file.name, url: file.url } as UploadFile;
});
},
{ immediate: true }
);
/**
* 限制用户上传文件的大小
*/
function handleBeforeUpload(file: UploadRawFile) {
if (file.size > props.uploadMaxSize) {
ElMessage.warning(
"上传文件不能大于" + Math.trunc(props.uploadMaxSize / 1024 / 1024) + "M"
);
return false;
}
uploadPercent.value = 0;
showUploadPercent.value = true;
return true;
}
const handleSuccessFile = (response: any, file: UploadFile) => {
showUploadPercent.value = false;
uploadPercent.value = 0;
if (response.code === ResultEnum.SUCCESS) {
ElMessage.success("上传成功");
valFileList.value.push({
name: file.name,
url: response.data.url,
});
emit("update:modelValue", valFileList.value);
return;
} else {
ElMessage.error(response.msg || "上传失败");
}
};
const handleError = (error: any) => {
showUploadPercent.value = false;
uploadPercent.value = 0;
ElMessage.error("上传失败");
};
const customColorMethod = (percentage: number) => {
if (percentage < 30) {
return "#909399";
}
if (percentage < 70) {
return "#375ee8";
}
return "#67c23a";
};
const handleProgress = (event: UploadProgressEvent) => {
uploadPercent.value = event.percent;
};
/**
* 删除文件
*/
function handleRemove(removeFile: UploadUserFile) {
const filePath = removeFile.url;
if (filePath) {
FileAPI.deleteByPath(filePath).then(() => {
// 删除成功回调
valFileList.value = valFileList.value.filter(
(file) => file.url !== filePath
);
emit("update:modelValue", valFileList.value);
});
}
}
/**
* 下载文件
*/
function downloadFile(file: UploadUserFile) {
const filePath = file.url;
if (filePath) {
FileAPI.downloadFile(filePath, file.name);
}
}
</script>
<style lang="scss" scoped>
.el-upload-list__item .el-icon--close {
position: absolute;
top: 50%;
right: 5px;
color: var(--el-text-color-regular);
cursor: pointer;
opacity: 0.75;
transition: opacity var(--el-transition-duration);
transform: translateY(-50%);
}
:deep(.el-upload-list) {
margin: 0;
}
:deep(.el-upload-list__item) {
margin: 0;
}
.show-upload-btn {
:deep(.el-upload) {
display: inline-flex;
}
}
.hide-upload-btn {
:deep(.el-upload) {
display: none;
}
}
</style>