Vue3封装一个ElementPlus自定义上传组件2--无弹窗

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

展示效果:

上传前:



上传后:

代码展示:

  1. 首先就是定义一个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>
  1. 定义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--无弹窗的更多相关文章

  1. 基于SqlSugar的开发框架循序渐进介绍(13)-- 基于ElementPlus的上传组件进行封装,便于项目使用

    在我们实际项目开发过程中,往往需要根据实际情况,对组件进行封装,以更简便的在界面代码中使用,在实际的前端应用中,适当的组件封装,可以减少很多重复的界面代码,并且能够非常简便的使用,本篇随笔介绍基于El ...

  2. 分享一个react 图片上传组件 支持OSS 七牛云

    react-uplod-img 是一个基于 React antd组件的图片上传组件 支持oss qiniu等服务端自定义获取签名,批量上传, 预览, 删除, 排序等功能 需要 react 版本大于 v ...

  3. 文件上传的一个坑 Apache上传组件和SpringMVC自带上传冲突

    List list = upload.parseRequest(request); 接受不到数据,size=0; 原因就是下面这货造成的 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ bean id=&qu ...

  4. Vue + Element 自定义上传封面组件

    前一段时间做项目,频繁使用到上传图片组件,而且只上传一个封面,于是想着自定义一个图片封面上传组件.先来看一下效果:                        第一张图片是上传之前,第二张图片是上传 ...

  5. JS组件系列——Bootstrap文件上传组件:bootstrap fileinput

    前言:之前的三篇介绍了下bootstrap table的一些常见用法,发现博主对这种扁平化的风格有点着迷了.前两天做一个excel导入的功能,前端使用原始的input type='file'这种标签, ...

  6. Jquery图片上传组件,支持多文件上传

    Jquery图片上传组件,支持多文件上传http://www.jq22.com/jquery-info230jQuery File Upload 是一个Jquery图片上传组件,支持多文件上传.取消. ...

  7. Bootstrap文件上传组件

    前言:之前的三篇介绍了下bootstrap table的一些常见用法,发现博主对这种扁平化的风格有点着迷了.前两天做一个excel导入的功能,前端使用原始的input type='file'这种标签, ...

  8. bootstrap-fileinput文件上传组件和laravel引用(未完)

    前言:之前的三篇介绍了下bootstrap table的一些常见用法,发现博主对这种扁平化的风格有点着迷了.前两天做一个excel导入的功能,前端使用原始的input type='file'这种标签, ...

  9. UI组件--element-ui--Upload多组件自定义上传

    需求: 提交详细信息的表单, 并上传对应图片(如下图), 后台接口要求表单数据和图片需要一次上传完成.. 分析: 实际上, 每个element-ui Upload组件都应发送一次请求, 很明显不符合我 ...

  10. element-ui上传组件,通过自定义请求上传文件

    记录使用element-ui上传组件,通过自定义请求上传文件需要注意的地方. <el-upload ref="uploadMutiple" :auto-upload=&quo ...

随机推荐

  1. document.write 和 innerHTML 的区别

    a document.write 是整个页面的内容,会重写页面b innerHTML 是某个元素的内容,只有给body的innerHTML设置内容才会重写页面

  2. 2024csp初赛总结

    浙江27日下午1:30出分了,j组97,s组61.5,和估分一模一样,还好没有挂分.然后3点的时候上洛谷看了一下,全国分数线出了,j组89分,s组56分.那应该都过了,随后同学的成绩也出来了,sjx, ...

  3. k8s-NFS系统配置

    k8s-NFS系统配置 NFS(network filesystem),nfs文件系统在k8s中主要用于持久化存储,可以被多个pod访问和共享数据. 特点 数据持久性 nfs为k8s的pod提供了一种 ...

  4. Jenkins执行appium没有界面得处理

    原文1:https://www.cnblogs.com/wangjunjiehome/p/10100852.html 原文2:https://www.cnblogs.com/wangjunjiehom ...

  5. Spire.Pdf打印PDF文件

    1 /// <summary> 2 /// Spire.Pdf打印PDF文件 3 /// </summary> 4 /// <param name="fileN ...

  6. .NET云原生应用实践(五):使用Blazor WebAssembly实现前端页面

    本章目标 使用Blazor WebAssembly实现管理"贴纸"页面 集成认证与授权机制 如果你对Blazor WebAssembly的使用不感兴趣,可以跳过本章的阅读.你也可以 ...

  7. 鸿蒙Banner图一多适配不同屏幕

    认识一多 随着终端设备形态日益多样化,分布式技术逐渐打破单一硬件边界,一个应用或服务,可以在不同的硬件设备之间随意调用.互助共享,让用户享受无缝的全场景体验.而作为应用开发者,广泛的设备类型也能为应用 ...

  8. 从2s优化到0.1s

    前言 分类树查询功能,在各个业务系统中可以说随处可见,特别是在电商系统中. 但就是这样一个简单的分类树查询功能,我们却优化了5次. 到底是怎么回事呢? 背景 我们的网站使用了SpringBoot推荐的 ...

  9. 腾讯AICR : 智能化代码评审技术探索与应用实践(上)

  10. 高性能计算-雅可比算法MPI通信优化(5)

    雅可比算法原理:如下图对方阵非边界元素求上下左右元素的均值,全部计算元素的数值计算完成后更新矩阵,进行下一次迭代. 测试目标:用MPI实现对8*8方阵雅可比算法迭代并行计算,用重复非阻塞的通信方式 # ...