本文介绍了一个单文件上传前端组件,基于Vue3、ElementPlus,提供了组件源码及使用示例教程,可供参考和使用。
支持的功能:文件覆盖、限制文件类型、最大文件大小
组件源码
<!--
* 单文件上传组件
*
* Author: GFire
* Date: 2025/01/16
-->
<template>
<div>
<el-upload
ref="upload"
:limit="1"
:accept="props.accept"
:on-exceed="handleExceed"
:on-change="handleChange"
:on-remove="handleRemove"
:auto-upload="false"
>
<!-- 默认插槽,用于放置触发文件选择的元素,如按钮、文字等 -->
<slot name="default"></slot>
<template #tip>
<div style="font-size: 12px; color: var(--el-color-info)">
<div v-if="props.accept">支持的文件类型:{{ props.accept }}</div>
<div v-if="props.maxFileSize">支持的最大文件大小:{{ props.maxFileSize.size + props.maxFileSize.unit }}</div>
</div>
<!-- 用户自定义提示内容插槽 -->
<slot name="tip"></slot>
</template>
</el-upload>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { ElNotification, genFileId } from 'element-plus';
import type { UploadInstance, UploadProps, UploadRawFile, UploadFile } from 'element-plus';
type SizeUnit = 'KB' | 'MB' | 'GB';
const props = defineProps<{
/**
* 接受上传的文件类型,以文件后缀用逗号拼接的字符串,如:`.jpg,.txt,.xlsx`,不传则无限制
*/
accept?: string;
/**
* 支持的最大文件大小,不传则无限制
*/
maxFileSize?: { size: number; unit: SizeUnit };
}>();
const emit = defineEmits<{
(event: 'fileChange', file?: File): void;
}>();
defineExpose({
/**
* 清空文件列表
*/
clearFile() {
upload.value!.clearFiles();
},
});
const upload = ref<UploadInstance>();
let tempFile: UploadFile | undefined;
// 覆盖前一个文件
const handleExceed: UploadProps['onExceed'] = (files) => {
upload.value!.clearFiles();
const file = files[0] as UploadRawFile;
file.uid = genFileId();
upload.value!.handleStart(file);
};
function handleChange(uploadFile: UploadFile) {
if (!isValidFile(uploadFile)) {
// 文件不合法,回退
rollback();
} else {
tempFile = uploadFile;
emit('fileChange', uploadFile.raw);
}
}
// 校验文件是否合法
function isValidFile(uploadFile: UploadFile) {
if (!isValidFileType(uploadFile)) {
ElNotification.error({
title: '文件不合法',
message: `文件类型不支持,需为:${props.accept}`,
position: 'top-right',
});
return false;
}
if (!isValidFileSize(uploadFile)) {
ElNotification.error({
title: '文件不合法',
message: `文件大小超过限制:${props.maxFileSize?.size} ${props.maxFileSize?.unit}`,
position: 'top-right',
});
return false;
}
return true;
}
function rollback() {
if (tempFile) {
upload.value!.clearFiles();
upload.value!.handleStart(tempFile.raw!);
} else {
upload.value!.clearFiles();
}
}
function handleRemove() {
tempFile = undefined;
emit('fileChange', undefined);
}
const acceptTypes = props.accept?.split(',');
function isValidFileType(uploadFile: UploadFile) {
// 无值,代表接受任意文件类型
if (!acceptTypes) {
return true;
}
const fileType = '.' + uploadFile.name.split('.').pop();
for (let type of acceptTypes) {
if (fileType === type) {
return true;
}
}
return false;
}
function isValidFileSize(uploadFile: UploadFile) {
// 无值,代表文件大小无限制
if (!props.maxFileSize) {
return true;
}
let bytes = convertToBytes(props.maxFileSize.size, props.maxFileSize.unit);
if (uploadFile.raw!.size > bytes) {
return false;
} else {
return true;
}
}
function convertToBytes(size: number, unit: SizeUnit) {
const unitMapping = {
KB: 1024,
MB: 1024 * 1024,
GB: 1024 * 1024 * 1024,
};
const multiplier = unitMapping[unit];
if (multiplier) {
return size * multiplier;
} else {
throw new Error('Unsupported unit. Please use KB, MB, or GB.');
}
}
</script>
<style scoped></style>
使用示例
示例代码:
<template>
<SingleFileUpload
style="width: 300px"
ref="fileUploadRef"
accept=".md,.txt"
:maxFileSize="{ size: 50, unit: 'KB' }"
@fileChange="handleFileChange"
>
<el-button>选择文件</el-button>
<template #tip> 请上传符合要求的文件 </template>
</SingleFileUpload>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue';
import SingleFileUpload from '@/components/base/SingleFileUpload.vue';
const fileUploadRef = ref();
// 接收文件变更
function handleFileChange(file: File | undefined) {
form.file = file;
}
const form = reactive({
file: undefined as File | undefined,
});
function submitForm() {
// 模拟提交表单
console.log('提交表单:', form);
// 清空文件
fileUploadRef.value.clearFile();
}
</script>
代码解释:
accept=".md,.txt":指定只接受md、txt的文件:maxFileSize="{ size: 50, unit: 'KB' }":指定支持的最大文件大小为50KB@fileChange="handleFileChange":文件变化事件处理
显示效果:
选择文件,默认限制为提供的文件类型(md、txt):
选择文件后的效果:
当选择的文件大小超过限制,则提示异常:
当选择的文件类型不支持,则提示异常: