Bootstrap

el-upload文件上传组件的封装

样式1

 样式2

 上传的格式

// annexUrl 数据格式如下

[
    {
        "uid": 1682329534561,
        "name": "2023/04/24/273f36b860a74e79be3faed3ce20236f.pdf",
        "suffix": ".pdf",
        "url": "http://192.168.0.254:19000/annex/2023/04/24/273f36b860a74e79be3faed3ce20236f.pdf",
        "status": "success"
    },
    {
        "uid": 1682386966277,
        "name": "2023/04/25/eee1b9f5271543989d792d0fd1bb690c.doc",
        "suffix": ".doc",
        "url": "http://192.168.0.254:19000/annex/2023/04/25/eee1b9f5271543989d792d0fd1bb690c.doc",
        "status": "success"
    },
    {
        "name": "2023/04/25/9f910e55bad44e91a9f96b6d4a1aaeb6.pdf",
        "suffix": ".pdf",
        "url": "http://192.168.0.254:19000/annex/2023/04/25/9f910e55bad44e91a9f96b6d4a1aaeb6.pdf"
    },
    {
        "name": "2023/04/25/02b17209170f4765908d49ed8aa82908.docx",
        "suffix": ".docx",
        "url": "http://192.168.0.254:19000/annex/2023/04/25/02b17209170f4765908d49ed8aa82908.docx"
    }
]

 upload接口返回的数据格式如下

 

组件封装

<!--
 * @Description: 文件上传通用 组件 页面
 * @Author: mhf
 * @Date: 2023-04-24 18:32:39
 * @Desc: 具体使用请参考 attachConfigDialog.vue 页面
-->
<template>
  <div class="">
    <el-upload
      class="upload-demo"
      :disabled="utilsObj.isDisabled"
      :action="actionUrl"
      :headers="headerObj"
      :file-list="utilsObj.fileList"
      :limit="utilsObj.limitNum"
      :multiple="utilsObj.isMultiple"
      :on-preview="handlePreview"
      :on-success="handleSuccess"
      :on-remove="handleRemove"
      :before-upload="handBeforeUpload"
      :on-exceed="handleExceed"
    >
      <!-- 上传按钮样式选择 -->
      <div v-if="utilsObj.typeStyle === 0">
        <!-- 按钮样式 -->
        <el-button
          :disabled="utilsObj.isDisabled"
          size="small"
          icon="iconfont if-biaodancaozuo-xinzeng"
          class=""
        >附件
        </el-button>
      </div>

      <!-- el-icon样式 -->
      <div v-if="utilsObj.typeStyle === 1">
        <div v-if="!utilsObj.isDisabled" class="fileBox">
          <i class="iconfont if-daoru"/>
          <div>点击上传</div>
        </div>
      </div>
      <!-- 提示:若想使用自定义样式,可添加插槽:<slot></slot> -->
      <!-- 上传按钮样式选择 -->
    </el-upload>
  </div>
</template>

<script>
  import { getToken } from '@/utils/auth'

  export default {
    name: 'index',
    components: {},
    props: {
      /* 注意: 如果props里面的对象有默认参数时,必须用函数return一个对象 */
      utilsObj: {
        type: Object,
        default: () => ({
          isDisabled: false, // 是否禁用
          fileList: [], // 附件列表
          limitNum: 3, // 限制上传的文件数量 (个)
          fileSize: 50, // 单文件上传大小(MB)
          typeStyle: 0, // 文件上传的样式控制
          isMultiple: false // 是否支持同时选择多个文件
        })
      }, // 附件上传的配置项
      actionUrl: {
        type: String,
        default: process.env.VUE_APP_BASE_API + '/tlxx-modules-annex/minioAnnex/upload'
      }, // 文件上传接口,
      headerObj: {
        type: Object,
        default: function() {
          return {
            AuthorizationSys: getToken()
          }
        }
      }, // 文件上传请求头参数 --- token
    },
    data() {
      return {
        resFileArr: [], // 最终需要的文件数据
      }
    },
    methods: {
      /**
       * @Event 方法
       * @description: 点击文件列表中已上传的文件时的钩子
       * */
      handlePreview(file) {
        if (file.response) {
          // 上传文件的时候 查看
          window.open(file.response.data.url)
        } else {
          // 文件上传成功之后返回值 查看
          window.open(file.url)
        }
      },

      /**
       * @Event 方法
       * @description: 文件上传成功时的钩子
       * */
      handleSuccess(file) {
        if (file.code === 1) {
          this.resFileArr.push(file.data)
          console.log(this.resFileArr, 'resFileArr')
          this.$emit("getFileUploadYt", this.resFileArr);
        } else {
          this.$message.warning(file.message)
        }
      },

      /**
       * @Event 方法
       * @description: 文件列表移除文件时的钩子
       * */
      handleRemove(file) {
        console.log(file.response, this.resFileArr, file)
        if (file.response) {
          console.log('response have')
          this.resFileArr.map((item, index) => {
            if (item === file.response.data || item.url === file.response.data.url) {
              this.resFileArr.splice(index, 1)
              console.log(index)
              this.$emit("getFileUploadYt", this.resFileArr);
            }
          })
        } else {
          console.log('no response')
          this.resFileArr.map((item, index) => {
            if (item === file || item.url === file.url) {
              this.resFileArr.splice(index, 1)
              console.log(index)
              this.$emit("getFileUploadYt", this.resFileArr);
            }
          })
        }
      },

      /**
       * @Event 方法
       * @description: 上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。
       * */
      handBeforeUpload(file) {
        if (
          [
            'application/vnd.android.package-archive',
            'application/x-zip-compressed',
            'application/pdf',
            'application/msword',
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            'application/vnd.ms-excel',
            'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
          ].indexOf(file.type) === -1
        ) {
          this.$message.error(
            '请上传后缀名为 apk、zip、pdf、doc、docx、xls、xlsx的文件'
          )
          return false
        }
        if (file.size > this.utilsObj.fileSize * 1024 * 1024) {
          this.$message.error(`文件大小不能超过 ${this.utilsObj.fileSize}MB`)
          return false
        }
      },

      /**
       * @Event 方法
       * @description: 文件超出个数限制时的钩子
       * */
      handleExceed(files, fileList) {
        this.$message.warning(`当前限制选择 ${this.utilsObj.limitNum} 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`)
      }

    },
    created() {
    },
    mounted() {
      setTimeout(() =>{
        if (this.utilsObj.fileList) {
          this.resFileArr = this.utilsObj.fileList
        }
      }, 500)

    }
  }
</script>

<style lang="scss" scoped>
  .fileBox {
    height: 36px;
    background: rgba(255, 255, 255, 0);
    border-radius: 4px;
    display: flex;
    align-items: center;
    color: #1492ff;
    font-weight: bold;
    font-size: 16px;
    i {
      margin-right: 5px;
    }
  }
  .disabledClass {
    margin-top: -35px;
  }
</style>

页面中使用(红框部分)

效果:

    <el-form-item label="附件 :" prop="annexUrl">
        <fileUploadYt ref="fileUploadYt" @getFileUploadYt="getAnnexUrl" :utilsObj="fileUploadUtils"/>
      </el-form-item>
data() {
    return {
               fileUploadUtils: {
                    isDisabled: false, // 是否禁用
                    fileList: [], // 回显的附件列表
                    limitNum: 4, // 限制上传的文件数量 (个)
                    typeStyle: 0, // 文件上传的样式控制
                  }, // 附件上传的配置项
        
    }
}




 /**
    * @Event 方法
    * @description: 弹窗关闭事件
    * */
hideDialog() {
      this.fileUploadUtils.fileList = []; // 关闭弹窗时,清空附件列表
},



 /**
    * @Interface 接口
    * @description: 获取详情
    * */
   getDetail(id) {
      getProjectAnnexInfo(id).then((res) => {
        if (res.code === 1) {
          this.formData = res.data;
          this.formData.annexUrl = JSON.parse(res.data.annexUrl);
          if (!this.formData.annexUrl) {
            this.formData.annexUrl = [];
          }
          this.fileUploadUtils.fileList = this.formData.annexUrl; // 附件回显
          console.log(this.fileUploadUtils.fileList, 0)
        } else {
          this.$message.error("获取详情数据失败!");
        }
      });
    },








 /**
    * @Event 方法
    * @description: 获取组件上传得到的最终文件数组
    * */
    getAnnexUrl(data) {
      console.log(data)
      this.formData.annexUrl = data
    }

注意:如果是在弹窗中使用该组件,必须用 v-if 如下图,否则会存在resFileArr不清空完全的问题

完整版示例

<template>
  <el-dialog
    :close-on-click-modal="false"
    :title="title"
    :visible.sync="visibleFlag"
    v-if="visibleFlag"
    width="900px"
    append-to-body
    @close="hideDialog"
  >
    <el-form
      ref="form"
      :model="formData"
      :rules="formRules"
      label-width="110px"
    >
      <el-form-item label="图标 :" prop="iconUrl">
        <el-upload
          class="avatar-uploader"
          :action="sendImgUrl"
          :headers="headerObj"
          :show-file-list="false"
          accept=".jpg, .png, jpeg"
          :on-success="handleAvatarSuccess"
          :before-upload="beforeAvatarUpload"
        >
          <img v-if="formData.iconUrl" :src="formData.iconUrl" class="avatar" />
          <i v-else class="el-icon-plus avatar-uploader-icon" />
        </el-upload>
        <span class="tips">ps:支持png,jpg,小于10M,比例建议1:1</span>
      </el-form-item>

      <el-form-item label="附件名称 :" prop="annexName">
        <el-input v-model="formData.annexName" placeholder="请输入附件名称" />
      </el-form-item>

      <el-form-item label="附件类型 :" prop="annexType">
        <el-select
          v-model="formData.annexType"
          placeholder="附件类型"
          clearable
        >
          <el-option
            v-for="dict in dict.type.sys_annex_type"
            :key="dict.value"
            :label="dict.label"
            :value="dict.value"
          />
        </el-select>
      </el-form-item>

      <el-form-item label="项目名称 :" prop="projectId">
        <el-select
          v-model="formData.projectId"
          placeholder="项目名称"
          clearable
        >
          <el-option
            v-for="item in sysNameList"
            :key="item.id"
            :label="item.sysName"
            :value="item.id"
          />
        </el-select>
      </el-form-item>

      <el-form-item label="备注 :" prop="remark">
        <el-input
          v-model="formData.remark"
          placeholder="请输入备注"
          type="textarea"
        />
      </el-form-item>

      <el-form-item label="附件 :" prop="annexUrl">
        <fileUploadYt
          ref="fileUploadYt"
          @getFileUploadYt="getAnnexUrl"
          :utilsObj="fileUploadUtils"
        />
      </el-form-item>
    </el-form>

    <div slot="footer" class="dialog-footer" style="text-align: center">
      <el-button type="primary" @click="submitForm">确 定</el-button>
      <el-button @click="hideDialog">取 消</el-button>
    </div>
  </el-dialog>
</template>

<script>
import {
  getProjectAllList,
  addProjectAnnex,
  getProjectAnnexInfo,
  updateProjectAnnex,
} from "@/api/system/projectAnnex/attachConfig";
import { getToken } from "@/utils/auth";

export default {
  name: "attachConfigDialog",
  dicts: ["sys_annex_type"],
  data() {
    return {
      title: "",
      visibleFlag: false,
      formData: {
        annexUrl: [],
      },
      formRules: {
        annexName: [
          {
            required: true,
            message: "请输入附件名称",
            trigger: "blur",
          },
        ],
        annexType: [
          {
            required: true,
            message: "请选择附件类型",
            trigger: "blur",
          },
        ],
        iconUrl: [
          {
            required: true,
            message: "请选择图标",
            trigger: "blur",
          },
        ],
      },
      sysNameList: [],
      sendImgUrl:
        process.env.VUE_APP_BASE_API + "/tlxx-modules-annex/minioAnnex/upload", // 文件上传接口
      headerObj: {
        AuthorizationSys: getToken(),
      }, // 文件上传token
      fileUploadUtils: {
        isDisabled: false, // 是否禁用
        fileList: [], // 回显的附件列表
        limitNum: 3, // 限制上传的文件数量 (个)
        typeStyle: 0, // 文件上传的样式控制
      }, // 附件上传的配置项
    };
  },
  methods: {
    hideDialog() {
      this.visibleFlag = false;
      this.formData = {};
      this.$parent.getList();
      this.fileUploadUtils.fileList = []; // 关闭弹窗时,清空附件列表
    },
    showDialog(data) {
      this.visibleFlag = true;
      this.title = data.title;
      this.getSysNameList();
      if (this.title === "修改项目附件配置") {
        this.getDetail(data.data.id);
      }
    },
    /* 详情 */
    getDetail(id) {
      getProjectAnnexInfo(id).then((res) => {
        if (res.code === 1) {
          this.formData = res.data;
          this.formData.annexUrl = JSON.parse(res.data.annexUrl);
          if (!this.formData.annexUrl) {
            this.formData.annexUrl = [];
          }
          this.fileUploadUtils.fileList = this.formData.annexUrl; // 附件回显
        } else {
          this.$message.error("获取详情数据失败!");
        }
      });
    },
    submitForm() {
      this.$refs.form.validate((valid) => {
        if (valid) {
          if (this.title === "修改项目附件配置") {
            this.formData.annexUrl !== []
              ? (this.formData.annexUrl = JSON.stringify(
                  this.formData.annexUrl
                ))
              : (this.formData.annexUrl = []);
            updateProjectAnnex(this.formData)
              .then((res) => {
                if (res.code === 1) {
                  this.$message.success("修改成功!");
                  this.hideDialog();
                } else {
                  this.$message.warning(res.message);
                }
              })
              .catch((e) => {
                throw e;
              });
          }
          if (this.title === "新增项目附件配置") {
            this.formData.annexUrl !== []
              ? (this.formData.annexUrl = JSON.stringify(
                  this.formData.annexUrl
                ))
              : (this.formData.annexUrl = []);
            // this.formData.annexUrl = JSON.stringify(this.formData.annexUrl);
            addProjectAnnex(this.formData)
              .then((res) => {
                if (res.code === 1) {
                  this.$message.success("添加成功!");
                  this.hideDialog();
                } else {
                  this.$message.warning(res.message);
                }
              })
              .catch((e) => {
                throw e;
              });
          }
        } else {
          return false;
        }
      });
    },

    /* 获取系统名称的下拉列表 */
    getSysNameList() {
      getProjectAllList().then((res) => {
        if (res.code === 1) {
          this.sysNameList = res.data;
        }
      });
    },

    /* 图片 */
    // 图片添加成功
    handleAvatarSuccess(res) {
      if (res.code === 1) {
        this.$set(this.formData, "iconUrl", res.data.url);
      }
    },
    // 上传图片拦截配置
    beforeAvatarUpload(file) {
      const isType =
        file.type === "image/jpeg" ||
        file.type === "image/png" ||
        file.type === "image/jpg";
      const isLt2M = file.size / 1024 / 1024 < 20;

      if (!isType) {
        this.$message.error("上传图标只能是 JPG、png 格式!");
      }
      if (!isLt2M) {
        this.$message.error("上传图标不能超过 10MB!");
      }
      return isType && isLt2M;
    },
    /* 图片 */

    /* 附件 单个 */
    /**
     * @Event 方法
     * @description: 获取组件上传得到的最终文件数组
     * */
    getAnnexUrl(data) {
      this.formData.annexUrl = data;
      console.log(data, this.formData.annexUrl);
    },
    /* 附件 */
  },
};
</script>

<style scoped>
/deep/ .avatar-uploader .el-upload {
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
}

/deep/ .avatar-uploader .el-upload:hover {
  border-color: #409eff;
}

.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 100px;
  height: 100px;
  line-height: 100px;
  text-align: center;
}

.avatar {
  width: 100px;
  height: 100px;
  display: block;
}

.tips {
  font-size: 14px;
  color: #dd4a68;
}

.tableImg {
  width: 24px;
  height: 24px;
  border-radius: 15%;
}
</style>

 

;