
在前端开发中,文件上传是一个常见但复杂的功能需求。今天我将介绍一个基于 Element Plus 实现的增强版文件上传组件,它不仅提供了基本的上传功能,还集成了文件预览、状态管理、批量操作等高级特性。
组件功能概述
这个高级文件上传组件具有以下核心功能:
- 多文件上传:支持同时选择多个文件
- 文件预览:支持图片预览和其他文件下载
- 状态管理:显示每个文件的上传状态(待上传/上传中/成功/失败)
- 大小限制:可配置单个文件大小限制
- 数量限制:可配置最大上传文件数量
- 类型限制:可配置接受的文件类型
- 表格展示:以表格形式展示文件列表,包含文件名、大小和状态
- 手动上传:支持先选择文件,再统一上传
- 测试功能:内置测试实例创建按钮(可根据实际需求调整)
组件代码解析
模板部分
xml
html
<template>
<el-dialog v-model="dialogVisible" :title="title" :width="width" @close="handleDialogClose">
<!-- 上传区域 -->
<el-upload class="upload-demo" :action="uploadAction" :multiple="multiple"
:on-preview="handlePreview" :on-remove="handleRemove" :on-success="handleSuccess"
:on-error="handleError" :file-list="fileList" :list-type="listType"
:limit="limit" :on-exceed="handleExceed" :accept="accept"
:before-upload="beforeUpload" :auto-upload="false" ref="uploadRef"
:on-change="handleChange" :show-file-list="false">
<el-button :type="buttonType">{{ buttonText }}</el-button>
<template #tip>
<div class="el-upload__tip">{{ tipText }}</div>
</template>
</el-upload>
<!-- 文件信息表格 -->
<el-table :data="fileList" style="width: 100%; margin-top: 20px;" border size="small">
<!-- 表格列定义 -->
</el-table>
<!-- 底部操作区 -->
<template #footer>
<span class="dialog-footer">
<el-button @click="handleCancel">{{ cancelText }}</el-button>
<el-button :type="uploadButtonType" @click="startUpload"
:disabled="fileList.length === 0 || uploadDisabled" :loading="uploadLoading">
{{ uploadButtonText }}
</el-button>
<el-button v-if="showTestButton" :type="testButtonType" @click="handleTestInstance">
{{ testButtonText }}
</el-button>
</span>
</template>
</el-dialog>
</template>
脚本部分
typescript
javascript
<script lang="ts" setup>
import { ref, computed, nextTick } from 'vue';
import { ElMessage } from 'element-plus';
// 定义 Props
interface Props {
title?: string;
width?: string;
uploadAction?: string;
multiple?: boolean;
listType?: 'text' | 'picture' | 'picture-card' | 'table';
limit?: number;
accept?: string;
maxSize?: number;
buttonText?: string;
buttonType?: string;
tipText?: string;
cancelText?: string;
uploadButtonText?: string;
uploadButtonType?: string;
showTestButton?: boolean;
testButtonText?: string;
testButtonType?: string;
uploadDisabled?: boolean;
}
// 默认 Props
const props = withDefaults(defineProps<Props>(), {
// 默认值设置
});
// 定义 Emits
const emit = defineEmits<{
// 事件定义
}>()
// 组件内部状态
const dialogVisible = defineModel<boolean>('visible', { default: false });
const fileList = ref<any[]>([]);
const uploadRef = ref();
const uploadLoading = ref(false);
// 工具方法
const formatFileSize = (size: number) => { /* ... */ };
const getFileStatusType = (status: string) => { /* ... */ };
const getFileStatusText = (status: string) => { /* ... */ };
// 事件处理
const handleChange = (file: any, changedFileList: any[]) => { /* ... */ };
const handleRemove = (file: any, index: number) => { /* ... */ };
const handlePreview = (file: any) => { /* ... */ };
const handleSuccess = (response: any, file: any, uploadFiles: any[]) => { /* ... */ };
const handleError = (error: any, file: any, uploadFiles: any[]) => { /* ... */ };
const handleExceed = (files: any[], uploadFiles: any[]) => { /* ... */ };
const beforeUpload = (file: any) => { /* ... */ };
const startUpload = async () => { /* ... */ };
const handleTestInstance = () => { /* ... */ };
const handleCancel = () => { /* ... */ };
const handleDialogClose = () => { /* ... */ };
// 对外暴露的方法
const open = () => {
dialogVisible.value = true;
};
defineExpose({
open
});
</script>
核心功能实现细节
1. 文件状态管理
组件为每个文件维护了状态信息(ready/uploading/success/fail),并通过不同的标签样式展示:
c
javascript
const getFileStatusType = (status: string) => {
switch (status) {
case 'ready': return 'info';
case 'uploading': return 'warning';
case 'success': return 'success';
case 'fail': return 'danger';
default: return 'info';
}
};
const getFileStatusText = (status: string) => {
switch (status) {
case 'ready': return '待上传';
case 'uploading': return '上传中';
case 'success': return '上传成功';
case 'fail': return '上传失败';
default: return '未知';
}
};
2. 文件预览功能
实现了智能的文件预览功能,根据文件类型采用不同的预览方式:
markdown
javascript
const handlePreview = (file: any) => {
try {
if (file.raw) {
const fileType = file.raw.type;
// 图片文件预览
if (fileType.startsWith('image/')) {
const url = URL.createObjectURL(file.raw);
ElImageViewer({
urlList: [url],
onClose: () => URL.revokeObjectURL(url)
});
}
// 其他文件下载预览
else {
const url = URL.createObjectURL(file.raw);
const link = document.createElement('a');
link.href = url;
link.download = file.name;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
}
} else if (file.url) {
window.open(file.url, '_blank');
} else {
ElMessage.warning('无法预览此文件');
}
} catch (error) {
ElMessage.error('文件预览失败');
}
};
3. 批量上传控制
通过 auto-upload="false"
禁用自动上传,实现手动控制上传时机:
ini
javascript
const startUpload = async () => {
if (fileList.value.length === 0) return;
uploadLoading.value = true;
try {
await uploadRef.value!.submit();
// 检查是否所有文件都已完成上传
const uploadingFiles = fileList.value.filter((file: any) => file.status === 'uploading');
if (uploadingFiles.length === 0) {
uploadLoading.value = false;
emit('upload-complete', fileList.value);
}
} catch (error) {
uploadLoading.value = false;
ElMessage.error('上传过程中发生错误');
}
};
4. 文件大小限制
在上传前检查文件大小:
ini
javascript
const beforeUpload = (file: any) => {
const isLtMaxSize = file.size / 1024 < props.maxSize;
if (!isLtMaxSize) {
ElMessage.error(`上传文件大小不能超过 ${props.maxSize}kb!`);
return false;
}
file.status = 'ready';
return true;
};
使用示例
基本用法
xml
html
<template>
<AdvancedUpload v-model="visible" @upload-complete="handleUploadComplete" />
</template>
<script setup>
import { ref } from 'vue';
import AdvancedUpload from './AdvancedUpload.vue';
const visible = ref(false);
const handleUploadComplete = (fileList) => {
console.log('所有文件上传完成', fileList);
};
</script>
自定义配置
ini
html
<AdvancedUpload
v-model="visible"
title="自定义上传标题"
:limit="5"
:max-size="1024"
accept=".jpg,.png,.pdf"
button-text="选择文件"
tip-text="请上传JPG/PNG图片或PDF文档,每个不超过1MB"
@upload-complete="handleUploadComplete"
/>
组件优势
- 更好的用户体验:通过表格展示文件状态,让用户清楚了解上传进度
- 更强的控制力:可以精确控制每个文件的上传状态和结果
- 更灵活的配置:通过丰富的props可以适应各种上传场景
- 更好的错误处理:对各种异常情况进行了处理并给出友好提示
- 类型安全:使用TypeScript编写,提供良好的类型提示
可能的改进方向
- 分片上传:对于大文件支持分片上传
- 断点续传:记录上传进度,支持中断后继续上传
- 拖拽上传:增加拖拽文件到区域上传的功能
- 更丰富的预览:支持更多文件类型的预览(如PDF、视频等)
- 国际化:支持多语言
这个组件已经在实际项目中得到应用,证明了其稳定性和实用性。根据具体项目需求,你可以进一步扩展或调整其功能。