优雅封装el-upload,实现只需要一个回调函数即可进行不同的赋值操作

177 阅读1分钟

优雅封装el-upload使之更为通用

通过对el-upload进行进一步封装,可以实现只需要一个回调函数即可进行不同的赋值操作,从而简化代码逻辑。这样可以避免每个文件上传都需要一个单独的回调函数,使代码更加简洁和易于维护。

业务场景

对于一个多文件分开上传的表单,需要保存很多个上传之后返回的路径,这样的话,我们需要使用多个el-upload组件,并定义多个on-success回调方法来将返回的路径保存在对象中,模拟页面如下:

image-20230626153000828.png

实例代码如下:

   <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进行进一步封装,只需要一个回调函数就能进行不同的赋值操作呢?答案是可以的,下面请看进一步封装的结果。

实现过程

  1. 首先,将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>
    
  2. 在props中定义一个属性objectAttribute,用来接收父组件传递的属性名称

       <script>
        props: {
            // 接收父组件传递的属性名称
            objectAttribute: String,
            imgStyle: {
              typeof: String,
              default: "width: 100%;height: 100%",
            },
            successUrl: {
              typeof: String,
              default: "",
            },
          },
         </script>
    
  3. 定义一个通用的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;
               }
             }
    
  4. 在父组件中,引入该组件,定义@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>