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. jenkins 配置flyway报错No value provided for placeholder expressions: ${name}

    业务场景:使用flyway将一个数据库的变更同步到另一个数据库,数据同步到一半的时候报错 No value provided for placeholder expressions: ${name}. ...

  2. HOW MANY OF THEM?(让人匪夷所思的一题)

    题面 由n个节点构成的,割边数不超过m条的无向连通图个数(无自环和重边),答案对1e9+7取模. \[------------------------------------------- \] 真是 ...

  3. NebulaGraph 的云产品交付实践

    作者:乔雷,Vesoft.Inc 云原生技术专家 NebulaGraph 介绍 NebulaGraph 是由杭州悦数科技有限公司自主研发的一款开源分布式图数据库产品,擅长处理千亿节点万亿条边的超大数据 ...

  4. Reviewbot 开源 | 为什么我们要打造自己的代码审查服务?

    Reviewbot 是七牛云开源的一个项目,旨在提供一个自托管的代码审查服务, 方便做 code review/静态检查, 以及自定义工程规范的落地. 静态检查不是个新鲜事. 我记得早在几年前,我们就 ...

  5. HAL+CubeIDE,输入输出重定向

    ①将以下代码段复制到usart.c里: /******************************************************************************* ...

  6. 题解:CF507C Guess Your Way Out!

    CF507C Guess Your Way Out! 题解 算法 模拟 思路 按照左右左右的方式先往下找,每次找到一个子节点时就看此节点为根的子树是否包含目标节点,如果包含就继续往下走,不包含说明目标 ...

  7. 10.Kubernetes核心技术Service

    Kubernetes核心技术Service 前言 前面我们了解到 Deployment 只是保证了支撑服务的微服务Pod的数量,但是没有解决如何访问这些服务的问题.一个Pod只是一个运行服务的实例,随 ...

  8. Air780E之TCP应用,你了解吗?

    ​ 一.TCP简介 TCP(TransmissionControlProtocol,传输控制协议)是一种面向连接的.可靠的.基于字节流的传输层通信协议.它主要用于在不可靠的网络环境中提供稳定的数据传输 ...

  9. gearman任务分发改进

    基于我上次在这里发现的问题,就是一次性投递20个消息,用sleep等待后发现,最后一个任务需要等前面19个都跑完才能执行,所以这里做一下改进. client.php <?php $client ...

  10. GodoOS 入选 Gitee 最有价值开源项目

    2024年11月1日,GodoOS荣耀地入选了GVP--Gitee最有价值开源项目.在GVP平台收录的418个杰出项目中,GodoOS作为唯一一款用GO语言开发的.维护中的跨平台webOS的桌面应用, ...