Vue 3 + Element Plus 实现文件上传组件:详细解析与实现指南
Vue 3 文件上传组件实现详解
在实际的前端开发中,文件上传是一个常见的需求,尤其是在需要处理文档、图片或其他类型文件的应用中。Vue 3 结合 Element Plus UI 组件库为我们提供了一个简单且灵活的文件上传解决方案。在这篇文章中,我们将详细介绍一个文件上传组件的实现过程,并解释每一部分的逻辑和功能。
项目背景
假设我们正在开发一个表单功能,用户可以在表单中上传附件。这个附件可以是多种格式(如 doc
, pdf
, jpg
等),同时我们希望对上传的文件数量和大小进行限制。为了提高用户体验,我们还需要实现文件的删除功能和实时的上传提示信息。
需求分析
- 文件类型和大小限制:允许用户上传特定类型和大小的文件。
- 上传进度管理:能够在上传过程中显示状态信息。
- 文件删除功能:支持上传文件的删除。
- 实时提示:上传前后提供相应的提示信息(如格式不正确、大小超限等)。
组件实现
我们利用 el-upload
组件来实现文件上传功能。el-upload
是 Element Plus 提供的一个文件上传组件,它集成了文件上传、进度条、错误处理等常见功能。
1. 模板部分(<template>
)
<template><div class="upload-file"><el-uploadmultiple <!-- 支持多文件上传 -->:action="uploadFileUrl" <!-- 上传请求的地址 -->:before-upload="handleBeforeUpload" <!-- 上传前的文件校验 -->:file-list="fileList" <!-- 当前上传的文件列表 -->:limit="limit" <!-- 上传文件数量限制 -->:on-error="handleUploadError" <!-- 上传失败的回调 -->:on-exceed="handleExceed" <!-- 超出文件数量限制的回调 -->:on-success="handleUploadSuccess" <!-- 上传成功的回调 -->:show-file-list="false" <!-- 不显示默认的文件列表 -->:headers="headers" <!-- 上传请求的头部 -->class="upload-file-uploader"ref="fileUpload"><!-- 上传按钮 --><el-button type="primary" size="mini" icon="Upload" plain>上传附件</el-button></el-upload><!-- 上传提示 --><div class="el-upload__tip" v-if="showTip">请上传<template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> </template><template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>的文件</div><!-- 文件列表展示 --><transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul"><li :key="file.uid" class="filelistcont" v-for="(file, index) in fileList"><div class="filelistcont-name"><span class="el-icon-document"> {{ getFileName(file.name) }} </span></div><div class="ele-upload-list__item-content-action"><el-link :underline="false" @click="handleDelete(index)" type="danger">删除</el-link></div></li></transition-group></div>
</template>
2. 脚本部分(<script setup>
)
<script setup>
import { getToken } from "@/utils/auth";// 接收外部传入的属性
const props = defineProps({modelValue: [String, Object, Array], // 绑定的文件值,支持字符串、数组或对象limit: { type: Number, default: 5 }, // 限制上传的文件数量fileSize: { type: Number, default: 5 }, // 限制单个文件的大小(单位:MB)fileType: { type: Array, default: () => ["doc", "xls", "ppt", "txt", "pdf"] }, // 允许的文件类型isShowTip: { type: Boolean, default: true } // 是否显示上传提示
});// 获取当前上下文中的代理对象,便于调用全局方法
const { proxy } = getCurrentInstance();
const emit = defineEmits(); // 定义组件的自定义事件
const number = ref(0); // 记录上传的文件数量
const uploadList = ref([]); // 上传成功的文件列表
const baseUrl = import.meta.env.VITE_APP_BASE_API; // 基础 URL,接口地址
const uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + "/upload"); // 上传文件的接口
const headers = ref({ Authorization: "Bearer " + getToken() }); // 请求头,带上认证 token
const fileList = ref([]); // 当前选择的文件列表
const showTip = computed(() => props.isShowTip && (props.fileType || props.fileSize)); // 控制是否显示上传提示信息// 监听 props.modelValue 的变化,并同步更新文件列表
watch(() => props.modelValue, val => {if (val) {let temp = 1;// 将传入的文件值转换为数组格式const list = Array.isArray(val) ? val : props.modelValue.split(',');// 将文件数组转换为对象数组,包含文件的名称和 URL 等信息fileList.value = list.map(item => {if (typeof item === "string") {item = { name: item, url: item };}item.uid = item.uid || new Date().getTime() + temp++; // 为每个文件生成唯一的 uidreturn item;});} else {fileList.value = []; // 清空文件列表}
}, { deep: true, immediate: true });// 上传前进行文件的类型和大小校验
function handleBeforeUpload(file) {// 校验文件类型if (props.fileType.length) {const fileName = file.name.split('.');const fileExt = fileName[fileName.length - 1]; // 获取文件扩展名const isTypeOk = props.fileType.indexOf(fileExt) >= 0;if (!isTypeOk) {proxy.$modal.msgError(`文件格式不正确, 请上传${props.fileType.join("/")}格式文件!`);return false; // 阻止上传}}// 校验文件大小if (props.fileSize) {const isLt = file.size / 1024 / 1024 < props.fileSize; // 判断文件大小是否小于限制值if (!isLt) {proxy.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);return false; // 阻止上传}}proxy.$modal.loading("正在上传文件,请稍候..."); // 显示上传中的提示number.value++; // 增加上传文件数量return true; // 允许上传
}// 处理文件数量超出限制时的操作
function handleExceed() {proxy.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
}// 处理上传失败的情况
function handleUploadError(err) {proxy.$modal.msgError("上传文件失败");
}// 处理上传成功的回调
function handleUploadSuccess(res, file) {if (res.url) {uploadList.value.push({ name: '/profile/' + res.path + res.filename, url: res.url }); // 保存文件的 URL 和路径uploadedSuccessfully(); // 上传成功后的处理} else {number.value--; // 上传失败,减去上传计数proxy.$modal.closeLoading(); // 关闭加载提示proxy.$modal.msgError(res.msg); // 显示错误信息proxy.$refs.fileUpload.handleRemove(file); // 移除上传失败的文件uploadedSuccessfully(); // 上传失败后的处理}
}// 处理文件删除
function handleDelete(index) {fileList.value.splice(index, 1); // 从文件列表中移除选中的文件emit("update:modelValue", listToString(fileList.value)); // 更新父组件的文件列表
}// 上传成功后,处理文件列表和状态的更新
function uploadedSuccessfully() {if (number.value > 0 && uploadList.value.length === number.value) {// 合并上传成功的文件与原始文件列表,并更新 fileListfileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value);uploadList.value = []; // 清空上传列表number.value = 0; // 重置上传计数emit("update:modelValue", listToString(fileList.value)); // 更新父组件的文件列表proxy.$modal.closeLoading(); // 关闭加载提示}
}
// 获取文件名,避免 URL 显示
function getFileName(name) {if (name.lastIndexOf("/") > -1) {return name.slice(name.lastIndexOf("/") + 1); // 获取文件名(去掉路径部分)} else {return name; // 如果没有路径,直接返回文件名}
}// 将文件列表转换为字符串(以逗号分隔)
function listToString(list, separator) {let strs = "";separator = separator || ","; // 默认使用逗号分隔for (let i in list) {if (list[i].url) {strs += list[i].url + separator; // 拼接 URL 字符串}}return strs !== '' ? strs.substr(0, strs.length - 1) : ''; // 去掉最后一个分隔符
}
</script>
3. 样式部分
<style scoped>
.upload-file {position: relative;
}
.upload-file .filelistcont {display: flex;align-items: center;justify-content: space-between;padding: 8px 12px;border: 1px solid #dcdfe6;margin-bottom: 8px;border-radius: 4px;
}
.upload-file .filelistcont-name {overflow: hidden;text-overflow: ellipsis;white-space: nowrap;
}
.el-upload__tip {margin-top: 12px;font-size: 12px;color: #999;
}
</style>
总结
通过结合 Vue 3 的响应式特性和 Element Plus 的 el-upload
组件,我们实现了一个功能完善的文件上传组件。这个组件不仅支持文件的上传、校验和删除,还提供了上传进度提示和错误处理。通过自定义的事件机制,我们可以将文件列表从子组件传递到父组件,确保数据的同步和状态管理。
在实际开发中,根据不同的需求,组件的功能可以进一步扩展,比如支持上传进度条、拖拽上传、批量处理等。希望通过这篇文章,你能深入理解 Vue 3 文件上传组件的实现,并能够在自己的项目中灵活应用。
相关文章:
Vue 3 + Element Plus 实现文件上传组件:详细解析与实现指南
Vue 3 文件上传组件实现详解 在实际的前端开发中,文件上传是一个常见的需求,尤其是在需要处理文档、图片或其他类型文件的应用中。Vue 3 结合 Element Plus UI 组件库为我们提供了一个简单且灵活的文件上传解决方案。在这篇文章中,我们将详细…...

qt5.12.11+msvc编译器编译qoci驱动
1.之前编译过minGW编译器编译qoci驱动,很顺利就完成了,文章地址:minGW编译qoci驱动详解,今天按照之前的步骤使用msvc编译器进行编译,直接就报错了: 查了些资料,发现两个编译器在编译时,pro文件中引用的库不一样,下面是msvc编译器引用的库,其中编译引用的库我这里安装…...
Ubuntu 20.04 安装 LNMP
1. 更新系统 sudo apt update sudo apt upgrade2. 安装 Nginx sudo apt install nginx3. 安装 MariaDB (作为 MySQL 的替代) sudo apt install mariadb-server mariadb-client初始化 MariaDB 数据库(可选) sudo mysql_secure_installation4. 安装 PH…...

Llama 3 简介(一)
目录 1. 引言 1.1 Llama 3 的简介 1.2 性能评估 1.3 开源计划 1.4 多模态扩展 ps 1. 缩放法则 2. 超额训练(Over-training) 3. 计算训练预算 4. 如何逐步估算和确定最优模型? 2. 概述 2.1 Llama 3 语言模型开发两个主要阶段 2.2…...

在 CentOS 上安装 FFmpeg
在CentOS 上安装 FFmpeg 方法一:在线安装 添加 EPEL 和 RPM Fusion 源: sudo yum install epel-release sudo yum install https://download1.rpmfusion.org/free/el/rpmfusion-free-release-$(rpm -E %rhel).noarch.rpm安装 FFmpeg: sudo yu…...
Python------Pandas的数据结构
Pandas主要处理以下三个数据结构: 序列(Series) 数据帧(DataFrame) 面板(Panel) 数据结构维数描述序列11维结构,值可变,大小不变数据帧22维结构,值可变,大小可变,表结构面板3维标记,值可变…...

矩阵碰一碰发视频源码技术解析,支持OEM
一、引言 随着近场通信技术的不断发展,矩阵碰一碰发视频技术作为一种创新的交互方式,逐渐在各个领域崭露头角,如智能营销、展览展示、教育科普等场景中都有着广泛的应用前景。通过将多个碰一碰设备或感应区域组成矩阵形式,用户能够…...

【汇编语言】外中断(一)—— 外中断的魔法:PC机键盘如何触发计算机响应
文章目录 前言1. 背景介绍2. 接口芯片和端口3. 外中断信息3.1 什么是外中断信息3.2 外中断源的分类3.2.1 可屏蔽中断3.2.1.1 什么是可屏蔽中断?3.2.1.2 可屏蔽中断的处理过程 3.2.2 不可屏蔽中断3.2.2.1 什么是不可屏蔽中断?3.2.2.2 不可屏蔽中断的处理过…...

pymssql-2.1.4.dev5-cp37-cp37m-win_amd64.whl 安装
pip install pymssql 安装pymssql出现下面的问题 error: Microsoft Visual C 14.0 is required. Get it with “Microsoft Visual C Build Tools”: http://landinghub.visualstudio.com/visual-cpp-build-tools 因为要使用python连接sqlserver数据库,需要pymssq…...

在HTML中使用Vue如何使用嵌套循环把集合中的对象集合中的对象元素取出来(我的意思是集合中还有一个集合那种)
在 Vue.js 中处理嵌套集合(即集合中的对象包含另一个集合)时,使用多重 v-for 指令来遍历这些层次结构。每个 v-for 指令可以用于迭代一个特定级别的数据集,并且可以在模板中嵌套多个 v-for 来访问更深层次的数据。 例如ÿ…...

Apriori关联规则算法 HNUST【数据分析技术】(2025)
1.理论知识 Apriori是一种常用的数据关联规则挖掘方法,它可以用来找出数据集中频繁出现的数据集合。该算法第一次实现在大数据集上的可行的关联规则提取,其核心思想是通过连接产生候选项及其支持度,然后通过剪枝生成频繁项集。 Apriori算法的…...
Windows中Microsoft Edge兼容性问题|修复方案
针对Microsoft Edge浏览器在Windows系统中出现的兼容性问题解决步骤和策略: 作者是更改了注册表解决的,问题不一,大家遇到兼容性问题先按照第7个情况进行设置,大部分人是这个情况! 清理缓存和Cookies 按快捷键:ctrlshi…...

Android 蓝牙开发-传输数据
概述 传统蓝牙是通过建立REFCCOM sockect来进行通信的,类似于socket通信,一台设备需要开放服务器套接字并处于listen状态,而另一台设备使用服务器的MAC地址发起连接。连接建立后,服务器和客户端就都通过对BluetoothSocket进行读写…...

webrtc获取IceCandidate流程
在WebRTC(Web Real-Time Communication)中,ICECandidate是一个关键概念,它用于描述在建立点对点(P2P)连接时可以考虑的潜在通信端点。以下是关于WebRTC中ICECandidate的详细解释: 一、ICECandidate的定义 ICECandidate对象通常包含以下关键属性: foundation:用于唯一…...

每天40分玩转Django:Django静态文件
Django静态文件 一、今日学习内容概述 学习模块重要程度主要内容静态文件配置⭐⭐⭐⭐⭐基础设置、路径配置CDN集成⭐⭐⭐⭐⭐CDN配置、资源优化静态文件处理⭐⭐⭐⭐压缩、版本控制部署优化⭐⭐⭐⭐性能优化、缓存策略 二、基础配置 # settings.py import os# 静态文件配置…...
Linux 线程池
1.概念介绍 线程池是一种多线程处理形式,它维护着多个线程,这些线程处于等待状态,随时准备接受任务并执行。线程池的主要目的是为了提高系统的性能和资源利用率,避免在处理短时间任务时频繁创建和销毁线程所带来的开销。 线程池…...

windows使用zip包安装MySQL
windows通过zip包安装MySQL windows通过zip包安装MySQL下载MySQL的zip安装包创建安装目录和数据目录解压zip安装包创建配置目录 etc 和 配置文件 my.ini安装MySQL进入解压后的bin目录执行命令初始化执行命令安装 验证安装查看服务已安装 启动MySQL查看服务运行情况修改密码创建…...

深度学习实战之超分辨率算法(tensorflow)——ESPCN
espcn原理算法请参考上一篇论文,这里主要给实现。 数据集如下:尺寸相等即可 针对数据集,生成样本代码preeate_data.py import imageio from scipy import misc, ndimage import numpy as np import imghdr import shutil import os import…...

Android unitTest 单元测试用例编写(初始)
文章目录 了解测试相关库导入依赖库新建测试文件示例执行查看结果网页结果其他 本片讲解的重点是unitTest,而不是androidTest哦 了解测试相关库 androidx.compose.ui:ui-test-junit4: 用于Compose UI的JUnit 4测试库。 它提供了测试Compose UI组件的工具和API。 and…...
C++简明教程(10)(初识类)
类的教程 C 类的完整教程 C 中,类(class)是面向对象编程的核心概念,用于定义对象的属性(数据成员)和行为(成员函数)。本教程将带你从零开始,循序渐进地学习如何定义和使…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...

ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...

2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...

MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
CSS | transition 和 transform的用处和区别
省流总结: transform用于变换/变形,transition是动画控制器 transform 用来对元素进行变形,常见的操作如下,它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)
macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 🍺 最新版brew安装慢到怀疑人生?别怕,教你轻松起飞! 最近Homebrew更新至最新版,每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

算法打卡第18天
从中序与后序遍历序列构造二叉树 (力扣106题) 给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。 示例 1: 输入:inorder [9,3,15,20,7…...