优雅封装el-upload使之更为通用
通过对el-upload进行进一步封装,可以实现只需要一个回调函数即可进行不同的赋值操作,从而简化代码逻辑。这样可以避免每个文件上传都需要一个单独的回调函数,使代码更加简洁和易于维护。
业务场景
对于一个多文件分开上传的表单,需要保存很多个上传之后返回的路径,这样的话,我们需要使用多个el-upload组件,并定义多个on-success回调方法来将返回的路径保存在对象中,模拟页面如下:
实例代码如下:
<template lang="">
<div>
<el-upload
class="upload-demo"
drag
:headers="uploadHeader"
:action="uploadAction"
:show-file-list="false"
:before-upload="beforeUpload"
:on-success="uploadFileSuccess"
>
</el-upload>
<el-upload
class="upload-demo"
drag
:headers="uploadHeader"
:action="uploadAction"
:show-file-list="false"
:before-upload="beforeUpload"
:on-success="uploadImageSuccess"
>
</el-upload>
</div>
</template>
<script>
import { getToken, getAnonymityToken } from "@/utils/auth";
export default {
name: "customUpload",
data() {
return {
uploadHeader:{},
uploadAction:"",
// 需要传给后端的表单对象
formData:{}
};
},
created() {
this.initUploadHeader();
},
methods: {
// 上传校验
beforeUpload(file) {
const isImage = file.type.startsWith("image/");
const isSizeValid = file.size <= 300 * 1024;
if (!isImage) {
this.$message.error("只能上传图片文件!");
return false;
}
if (!isSizeValid) {
this.$message.error("文件大小超过300KB限制!");
return false;
}
return true;
},
// 上传文件成功回调
uploadFileSuccess(res) {
if (res.data.url) {
this.formData.fileUrl = res.data.url;
}
},
// 上传图片成功回调
uploadImageSuccess(res) {
if (res.data.url) {
this.formData.ImageUrl = res.data.url;
}
},
},
//...更多上传回调
};
实现思路
对于该情况而言,每一个文件上传,都需要一个单独的回调函数来接受返回的URL,这样代码会显得比较臃肿,那么能不能对el-upload进行进一步封装,只需要一个回调函数就能进行不同的赋值操作呢?答案是可以的,下面请看进一步封装的结果。
实现过程
-
首先,将el-upload进一步封装成组件,我把它命名为CustomUpload
<template lang=""> <div> <el-upload class="upload-demo" drag :headers="uploadHeader" :action="uploadAction" :show-file-list="false" :before-upload="beforeUpload" :on-success="uploadSuccess" > <div v-if="!successUrl" class="el-upload__text"> <i class="el-icon-upload"></i>点击上传 </div> <div v-else class="uploaded-image"> <img :style="imgStyle" :src="successUrl" alt="Uploaded Image" /> </div> <div class="up-text">(文件大小不超过300k)</div> </el-upload> </div> </template> -
在props中定义一个属性objectAttribute,用来接收父组件传递的属性名称
<script> props: { // 接收父组件传递的属性名称 objectAttribute: String, imgStyle: { typeof: String, default: "width: 100%;height: 100%", }, successUrl: { typeof: String, default: "", }, }, </script> -
定义一个通用的on-success回调函数,根据objectAttribute进行赋值,在该方法中,通过this.$emit来调用父组件中的赋值方法,从而赋值给父组件中dataForm中的指定属性
// 上传成功回调,通过该函数,可以给传入的obj属性进行赋值调用 uploadSuccess(res) { if (res.data.url && this.objectAttribute) { this.$emit("update-obj-attr", { attr: this.objectAttribute, value: res.data.url, }); this.successUrl = res.data.url; } } -
在父组件中,引入该组件,定义@update-obj-attr="updateEmployeeInfoAttr",用来给指定的属性赋值,这样,就完成了通过一个回调函数,给对象中的不同属性进行赋值,太酷啦!
import CustomUpload from "../components/CustomUpload.vue"; //...... <custom-upload :objectAttribute="'employeePhoto'" @update-obj-attr="updateEmployeeInfoAttr" :imgStyle="'width: 60%;height: 100%'" :successUrl="employeeInfo.employeePhoto" /> //...... // 更新employeeInfo中的指定属性 updateEmployeeInfoAttr(payload) { const { attr, value } = payload; this.employeeInfo[attr] = value; },
自定义封装上传组件完整代码如下:
<template lang="">
<div>
<el-upload
class="upload-demo"
drag
:headers="uploadHeader"
:action="uploadAction"
:show-file-list="false"
:before-upload="beforeUpload"
:on-success="uploadSuccess"
>
<div v-if="!successUrl" class="el-upload__text">
<i class="el-icon-upload"></i>点击上传
</div>
<div v-else class="uploaded-image">
<img :style="imgStyle" :src="successUrl" alt="Uploaded Image" />
</div>
<div class="up-text">(文件大小不超过300k)</div>
<div class="el-upload__tip" slot="tip">{{ imgDesc }}</div>
</el-upload>
</div>
</template>
<script>
export default {
name: "customUpload",
props: {
// 接收父组件传递的属性名称
objectAttribute: String,
imgStyle: {
typeof: String,
default: "width: 100%;height: 100%",
},
imgDesc: {
typeof: String,
default: "",
},
successUrl: {
typeof: String,
default: "",
},
},
data() {
return {
uploadAction: process.env.VUE_APP_BASE_API + "/file/upload",
uploadHeader: {},
};
},
methods: {
// 上传校验
beforeUpload(file) {
const isImage = file.type.startsWith("image/");
const isSizeValid = file.size <= 300 * 1024;
if (!isImage) {
this.$message.error("只能上传图片文件!");
return false;
}
if (!isSizeValid) {
this.$message.error("文件大小超过300KB限制!");
return false;
}
return true;
},
// 上传成功回调,通过该函数,可以给传入的obj属性进行赋值调用
uploadSuccess(res) {
if (res.data.url && this.objectAttribute) {
this.$emit("update-obj-attr", {
attr: this.objectAttribute,
value: res.data.url,
});
this.successUrl = res.data.url;
}
},
},
};
</script>
<style>
.upload-demo {
width: 360px;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.uploaded-image {
width: 100%;
height: 100%;
}
.uploaded-image img {
width: 100%;
height: 100%;
}
.el-upload-dragger {
background-color: #f8f8f8;
border: 1px dashed #d9d9d9;
border-radius: 12px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
width: 360px;
height: 180px;
text-align: center;
cursor: pointer;
position: relative;
overflow: hidden;
}
.el-upload-dragger .el-upload__text {
color: #409eff;
font-size: 14px;
text-align: center;
}
.el-upload-dragger .el-icon-upload {
font-size: 15px;
color: #409eff;
margin: 20% 10px 0 0;
line-height: 0;
}
.el-upload__tip {
font-size: 12px;
color: #606266;
margin-top: 7px;
text-align: center;
}
.up-text {
color: #bebebe;
font-size: 12px;
}
</style>