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 ...
随机推荐
- 托管服务简介IHostedService接口 继承 BackgroundSerice接口
1. 场景:代码运行在后台,比如服务器启动的时候在后台预先加载数据到缓存,每天凌晨3 点把数据到处到数据库备份,每隔5秒在两张表之间同步一次数据 : 2. 托管服务实现IHoutedService接口 ...
- 从url地址获取主机名
function getHost(url) { var host = "null"; if(typeof url == "undefined"|| null = ...
- JDBC后端实现登录的逻辑
// 包名 package com.zhulx.JDBC; // 导入实例类 import com.zhulx.pojo.Account; import java.sql.Connection; im ...
- 6. CSS有哪些方法可以提升层级
1. 使用 z-index 2. 使用定位,脱离标准流
- 妙用编辑器:使用Notepad--正则表达式从命令结果报文快速生成新命令
应用场景 日常工作中有些维护场景,比如检查设备状态,执行查询命令后,得到精简结果报文,如果要更深入的检查状态,可能还要执行其他命令,逐个对象进行查询,这里涉及到快速从报文生成查询指令的功能. 比如有如 ...
- 《使用Gin框架构建分布式应用》阅读笔记:p52-p76
<用Gin框架构建分布式应用>学习第4天,p32-p76总结,总计25页. 一.技术总结 1.Go知识点 (1)iouti 书上使用ioutil包读取JSON文件,但是从go 1.16 开 ...
- ESP8266 + MQTT (platformio 开发环境)加用户名和密码
ESP8266 + MQTT git 地址: https://gitee.com/zhudachangs/esp8266-mqtt.git (如果无法打开说明在审核) 引用库 include < ...
- Issac_GYM对Go2机器人的仿真心得
override 覆盖 torques 扭矩 1 args()参数信息等 cd /home/yyds/桌面/Gym2/legged_robot_competition-master/legged_gy ...
- ARC151C 01 Game
ARC151C 01 Game 题目链接:ARC151C 01 Game \(SG\) 函数好题. 思路 考虑把原问题分成多个区间的不同问题,求 \(SG\) 在异或起来. 设: 1.\(SG_1(l ...
- python3的json数据库-TinyDB效率篇
安装了这个TinyDB库后,我突然想到一般来说python执行的速度并不算高,那这个库写文件速度如何呢? 测试代码如下: from tinydb import TinyDB import time # ...