Vue3封装一个ElementPlus自定义上传组件2--无弹窗
Vue3封装一个ElementPlus自定义上传组件2--无弹窗
写在前面: 无弹窗的上传组件它来了,依旧是小巧又好用,只不过这回我用的是前端直传的方式,采用http-request进行文件上传,中间有一些小坑,但幸运的是全都解决啦,组件很简单,但是用来学习是最好不过了,个人感觉我的注释应该也是浅显易懂的,希望大家在查看的同时不要忘了给我点个赞哦,当然,如果有写的不恰当或者写的不对的地方,也欢迎大家在底下评论
展示效果:
上传前:

上传后:

代码展示:
- 首先就是定义一个ElementPlus的上传组件,我这边就叫NewUpload:
<el-upload
ref="upload"
multiple
:limit="limit"
action="#"
v-model:file-list="fileList"
:before-upload="beforeUpload"
:on-exceed="handleExceed"
:on-success="handleUploadSuccess"
:on-error="handleUploadError"
:on-remove="handleDelete"
:on-preview="handlePreview"
:http-request="handleRequest"
>
<el-button type="primary">上传</el-button>
<template #tip>
<div class="el-upload__tip">
注意:请上传{{ fileSize }}m以下文件,最多支持上传文件数量:{{ limit }}个!
</div>
</template>
</el-upload>
<!-- 如果是图片类型的直接预览,如果是其他类型,则自行添加方法处理 -->
<el-dialog v-model="dialogVisible">
<img :src="dialogImageUrl" alt="Preview Image"/>
</el-dialog>
- 定义js事件处理,里面注释都有,就不说明了
import {ElMessage} from "element-plus";
import axios from "axios";
const props = defineProps({
//文件列表
modelValue: {
type: Array,
default: () => []
},
limit: {
type: Number,
default: 5
},
// 大小限制(MB)
fileSize: {
type: Number,
default: 2,
},
// 文件类型, 例如['png', 'jpg', 'jpeg']
fileType: {
type: Array,
default: () => [".jpg", ".jpeg", ".png", ".doc", ".xls", ".xlsx", ".ppt", ".txt", ".pdf"],
},
// 是否显示提示框
isShowTip: {
type: Boolean,
default: true
},
})
const emit = defineEmits(['update:modelValue']);
//文件ref
const upload = ref()
//文件列表
const fileList = ref([]);
//单文件的参数--这边我定义成全局的,但是需要注意深浅拷贝的问题
const fileData = {
name: "",
url: "",
}
//文件url列表--为了解决ElementPlus的文件属性和我们定义上传的文件属性不一致的问题
let fileUrls = []
//初始化
onMounted(() => {
if (props.modelValue.length > 0) {
fileUrls = props.modelValue;
fileList.value = fileUrls
}
});
//处理事件
const beforeUpload = async (options) => {
//校验文件类型
if (props.fileType) {
let fileExtension = "";
if (options.name.lastIndexOf(".") > -1) {
fileExtension = options.name.substring(options.name.lastIndexOf("."));
}
const isTypeOk = props.fileType.some(type => {
if (options.type.indexOf(type) > -1) return true;
return !!(fileExtension && fileExtension.indexOf(type) > -1);
});
if (!isTypeOk) {
ElMessage.error(`文件格式不正确,请上传${props.fileType.join("/")}格式文件!`)
return false;
}
}
try {
//准备上传的数据--这边我是使用前端直传的方式,所以第一步是获取预上传的Url信息
const {name, url} = await preUpload({fileName: options.name});
fileData.name = name;
fileData.url = url;
fileUrls.push(JSON.parse(JSON.stringify(toRaw(fileData))))
fileList.value = fileUrls
} catch (err) {
ElMessage.error("获取上传地址失败,请稍后再试!");
return false;
}
}
//正式上传
const handleRequest = async ({file, onSuccess, onError}) => {
try {
const config = {
//控制文件类型,方便进行显示还是下载
headers: {
"Content-Type": file.type,
},
};
let newFile = new File([file], fileData.name, {type: file.type});
//这边使用的是minio,用的是put方法,如果是自定义的上传方式,请使用对应的方法
const response = await axios.put(fileData.url, newFile, config);
//minio回调会有个status,具体还得看服务器回调
if (response.status === 200) {
//更新文件列表
//ElementPlus的文件hub属性和我们定义上传的文件属性不一致,所以需要手动更新
//所以这边我直接选择添加一个新的Url属性
fileList.value.forEach((item, index) => {
if (item.name === fileData.name) {
fileList.value[index] = {
...fileList.value[index],
newURL: getUrl(fileData.url)
}
}
})
onSuccess(response, file);
//通知父组件,更新v-model
emit('update:modelValue', fileList.value)
} else {
onError(response);
}
} catch (error) {
onError(error);
}
};
//minio的回调url会有多余的数据,这边处理了一下
const getUrl = (originalUrl) => {
const url = new URL(originalUrl);
return url.origin + url.pathname;
};
//处理删除
const handleDelete = (index) => {
//这边两个列表都需要删除,以免出现数据不一致的情况
fileList.value.slice(index, 1)
fileUrls.splice(index, 1)
emit('update:modelValue', fileList.value)
}
const handleExceed = () => {
ElMessage.error(`只允许上传${props.limit}个文件`)
}
const handleUploadError = (err) => {
ElMessage.error("上传失败,请重试");
}
//上传成功的回调,我们这里不需要处理
const handleUploadSuccess = (file) => {
}
const dialogVisible = ref(false);
const dialogImageUrl = ref("");
//弹出框进行文件预览
const handlePreview = (file) => {
const isImage = checkImageType(file.url);
if (isImage) {
if (file.newURL) {
dialogImageUrl.value = file.newURL;
dialogVisible.value = true;
}
}
};
//校验文件类型
const checkImageType = (urlString) => {
// 定义有效的图片扩展名
const validImageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.tiff'];
// 获取 URL 的后缀
const url = new URL(urlString);
const pathname = url.pathname;
const extension = pathname.slice((Math.max(0, pathname.lastIndexOf(".")) || Infinity) + 1).toLowerCase();
return validImageExtensions.includes(`.${extension}`);
};
调用方式:
调用就超级简单啦
在随便哪个父组件的template中:
<NewUpload v-model="uploadList" />
然后是js中定义uploadList
const uploadList = ref([]);
---到此完成---
全部代码:
<template>
<el-upload
ref="upload"
multiple
:limit="limit"
action="#"
v-model:file-list="fileList"
:before-upload="beforeUpload"
:on-exceed="handleExceed"
:on-success="handleUploadSuccess"
:on-error="handleUploadError"
:on-remove="handleDelete"
:on-preview="handlePreview"
:http-request="handleRequest"
>
<el-button type="primary">上传</el-button>
<template #tip>
<div class="el-upload__tip">
注意:请上传{{ fileSize }}m以下文件,最多支持上传文件数量:{{ limit }}个!
</div>
</template>
</el-upload>
<!-- 如果是图片类型的直接预览,如果是其他类型,则下载 -->
<el-dialog v-model="dialogVisible">
<img :src="dialogImageUrl" alt="Preview Image"/>
</el-dialog>
</template>
<script setup>
import {ElMessage} from "element-plus";
import axios from "axios";
const props = defineProps({
//文件列表
modelValue: {
type: Array,
default: () => []
},
limit: {
type: Number,
default: 5
},
// 大小限制(MB)
fileSize: {
type: Number,
default: 2,
},
// 文件类型, 例如['png', 'jpg', 'jpeg']
fileType: {
type: Array,
default: () => [".jpg", ".jpeg", ".png", ".doc", ".xls", ".xlsx", ".ppt", ".txt", ".pdf"],
},
// 是否显示提示框
isShowTip: {
type: Boolean,
default: true
},
})
const emit = defineEmits(['update:modelValue']);
//文件ref
const upload = ref()
//文件列表
const fileList = ref([]);
//单文件的参数--这边我定义成全局的,但是需要注意深浅拷贝的问题
const fileData = {
name: "",
url: "",
}
//文件url列表--为了解决ElementPlus的文件属性和我们定义上传的文件属性不一致的问题
let fileUrls = []
//初始化
onMounted(() => {
if (props.modelValue.length > 0) {
fileUrls = props.modelValue;
fileList.value = fileUrls
}
});
//处理事件
const beforeUpload = async (options) => {
//校验文件类型
if (props.fileType) {
let fileExtension = "";
if (options.name.lastIndexOf(".") > -1) {
fileExtension = options.name.substring(options.name.lastIndexOf("."));
}
const isTypeOk = props.fileType.some(type => {
if (options.type.indexOf(type) > -1) return true;
return !!(fileExtension && fileExtension.indexOf(type) > -1);
});
if (!isTypeOk) {
ElMessage.error(`文件格式不正确,请上传${props.fileType.join("/")}格式文件!`)
return false;
}
}
try {
//准备上传的数据--这边我是使用前端直传的方式,所以第一步是获取预上传的Url信息
const {name, url} = await preUpload({fileName: options.name});
fileData.name = name;
fileData.url = url;
fileUrls.push(JSON.parse(JSON.stringify(toRaw(fileData))))
fileList.value = fileUrls
} catch (err) {
ElMessage.error("获取上传地址失败,请稍后再试!");
return false;
}
}
//正式上传
const handleRequest = async ({file, onSuccess, onError}) => {
try {
const config = {
//控制文件类型,方便进行显示还是下载
headers: {
"Content-Type": file.type,
},
};
let newFile = new File([file], fileData.name, {type: file.type});
//这边使用的是minio,用的是put方法,如果是自定义的上传方式,请使用对应的方法
const response = await axios.put(fileData.url, newFile, config);
//minio回调会有个status,具体还得看服务器回调
if (response.status === 200) {
//更新文件列表
//ElementPlus的文件hub属性和我们定义上传的文件属性不一致,所以需要手动更新
//所以这边我直接选择添加一个新的Url属性
fileList.value.forEach((item, index) => {
if (item.name === fileData.name) {
fileList.value[index] = {
...fileList.value[index],
newURL: getUrl(fileData.url)
}
}
})
onSuccess(response, file);
//通知父组件,更新v-model
emit('update:modelValue', fileList.value)
} else {
onError(response);
}
} catch (error) {
onError(error);
}
};
//minio的回调url会有多余的数据,这边处理了一下
const getUrl = (originalUrl) => {
const url = new URL(originalUrl);
return url.origin + url.pathname;
};
//处理删除
const handleDelete = (index) => {
//这边两个列表都需要删除,以免出现数据不一致的情况
fileList.value.slice(index, 1)
fileUrls.splice(index, 1)
emit('update:modelValue', fileList.value)
}
const handleExceed = () => {
ElMessage.error(`只允许上传${props.limit}个文件`)
}
const handleUploadError = (err) => {
ElMessage.error("上传失败,请重试");
}
//上传成功的回调,我们这里不需要处理
const handleUploadSuccess = (file) => {
}
const dialogVisible = ref(false);
const dialogImageUrl = ref("");
//弹出框进行文件预览
const handlePreview = (file) => {
const isImage = checkImageType(file.url);
if (isImage) {
if (file.newURL) {
dialogImageUrl.value = file.newURL;
dialogVisible.value = true;
}
}
};
//校验文件类型
const checkImageType = (urlString) => {
// 定义有效的图片扩展名
const validImageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.tiff'];
// 获取 URL 的后缀
const url = new URL(urlString);
const pathname = url.pathname;
const extension = pathname.slice((Math.max(0, pathname.lastIndexOf(".")) || Infinity) + 1).toLowerCase();
return validImageExtensions.includes(`.${extension}`);
};
</script>
Vue3封装一个ElementPlus自定义上传组件2--无弹窗的更多相关文章
- 基于SqlSugar的开发框架循序渐进介绍(13)-- 基于ElementPlus的上传组件进行封装,便于项目使用
在我们实际项目开发过程中,往往需要根据实际情况,对组件进行封装,以更简便的在界面代码中使用,在实际的前端应用中,适当的组件封装,可以减少很多重复的界面代码,并且能够非常简便的使用,本篇随笔介绍基于El ...
- 分享一个react 图片上传组件 支持OSS 七牛云
react-uplod-img 是一个基于 React antd组件的图片上传组件 支持oss qiniu等服务端自定义获取签名,批量上传, 预览, 删除, 排序等功能 需要 react 版本大于 v ...
- 文件上传的一个坑 Apache上传组件和SpringMVC自带上传冲突
List list = upload.parseRequest(request); 接受不到数据,size=0; 原因就是下面这货造成的 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ bean id=&qu ...
- Vue + Element 自定义上传封面组件
前一段时间做项目,频繁使用到上传图片组件,而且只上传一个封面,于是想着自定义一个图片封面上传组件.先来看一下效果: 第一张图片是上传之前,第二张图片是上传 ...
- JS组件系列——Bootstrap文件上传组件:bootstrap fileinput
前言:之前的三篇介绍了下bootstrap table的一些常见用法,发现博主对这种扁平化的风格有点着迷了.前两天做一个excel导入的功能,前端使用原始的input type='file'这种标签, ...
- Jquery图片上传组件,支持多文件上传
Jquery图片上传组件,支持多文件上传http://www.jq22.com/jquery-info230jQuery File Upload 是一个Jquery图片上传组件,支持多文件上传.取消. ...
- Bootstrap文件上传组件
前言:之前的三篇介绍了下bootstrap table的一些常见用法,发现博主对这种扁平化的风格有点着迷了.前两天做一个excel导入的功能,前端使用原始的input type='file'这种标签, ...
- bootstrap-fileinput文件上传组件和laravel引用(未完)
前言:之前的三篇介绍了下bootstrap table的一些常见用法,发现博主对这种扁平化的风格有点着迷了.前两天做一个excel导入的功能,前端使用原始的input type='file'这种标签, ...
- UI组件--element-ui--Upload多组件自定义上传
需求: 提交详细信息的表单, 并上传对应图片(如下图), 后台接口要求表单数据和图片需要一次上传完成.. 分析: 实际上, 每个element-ui Upload组件都应发送一次请求, 很明显不符合我 ...
- element-ui上传组件,通过自定义请求上传文件
记录使用element-ui上传组件,通过自定义请求上传文件需要注意的地方. <el-upload ref="uploadMutiple" :auto-upload=&quo ...
随机推荐
- Linux+PXE+DHCP+TFTP+NFS实现无人值守安装
一.实验环境:OS:Redhat6.4软件:DHCP服务.TFTP服务.NFS服务硬件:Dell R630服务器两台物理连接图: em1 em1 二.工作原理:服务器通过PXE网卡启动,从dhcp服务 ...
- 华为Ensp拓扑,使用MSTP、OSPF、DHCP、VRRP、链路聚合、CHAP
OSPF+DHCP+VRRP+Eth-trunk+PPP(CHAP)+MSTP 实验目标: LSW1和LSW2核心交换机互为备份,配置链路聚合,设备冗余设计,LSW1和LSW2作为核心交换机配置DHC ...
- activiti教程
一.工作流介绍 1.1 概念 工作流(Workflow),就是通过计算机对业务流程自动化执行管理.它主要解决的是"使在多个参与者之间按照某种预定义的规则自动进行传递文档.信息或任务的过程,从 ...
- 可重入锁ReentrantLock
ReentrantLock 重入锁,是实现Lock 接口 的一个类,也是在实际编程中使用频率很高的一个锁,支持重入性,表示能够对共享资源重复加锁,即当前线程获取该锁后再次获取不会被阻塞. 要想支持重入 ...
- JS 识别安卓还是ios苹果、识别是否微信内置浏览器、手机类型
1.识别手机平台是IOS还是安卓 /** * [isMobile 判断平台] * @param test: 0:iPhone 1:Android */ function ismobile(test) ...
- 【小记】Docker容器间SSH公钥自动交换实现免密登录的一次尝试
咋想到这茬了 最近开始忙毕设的事儿了,想部署个伪分布式的Spark + Hadoop集群来进行测试.思来考去,最终咱把目光放在了Docker上. 盘了两天,发现这玩意意外的有趣,镜像构建好后开箱即用, ...
- hbase的架构
HBase中的存储包括HMaster.HRegionSever.HRegion.HLog.Store.MemStore.StoreFile.HFile等角色构成,具体如下HMaster的作用 1.为H ...
- 还在为慢速数据传输苦恼?Linux 零拷贝技术来帮你!
前言 程序员的终极追求是什么?当系统流量大增,用户体验却丝滑依旧?没错!然而,在大量文件传输.数据传递的场景中,传统的"数据搬运"却拖慢了性能.为了解决这一痛点,Linux 推出了 ...
- 记录java接口自动化模板优化
项目路径说明 内容优化 优化内容 1.自动生成的测试报告集成至项目中,可直接通过项目访问测试报告(之前生成测试报告位于项目外,需要手动打开) 优化效果: 2.后续会增加allure测试报告集成使用(实 ...
- 强行修改 User-Agent, 访问对应的端
location /{ proxy_pass http://localhost:18080; proxy_set_header User-Agent "Mozilla/5.0 (Window ...