elementui附件上传自定义文件列表,实现传完即可预览、下载、删除,二次封装el-upload

背景

当前 elementui 的文件上传组件在上传完文件之后只支持删除,用户希望可以看到附件信息,还可以预览自己刚刚上传但未提交的文件,还希望可以下载,因为公司的下载功能当前是通过 OnlyOffice 实现了文件格式转换,所以我们需要对 el-upload 做二次封装,从而实现这些功能。

效果

图片直接用查看器打开,文档则用OnlyOffice打开。

实现方案

1. 隐藏掉 elementui 组件自带的文件列表

博主选这个了这个方案,主要是想预防其他同事在用我的组件时因不知情而传入了file-list,更加友好高效,想怎么用都行。

css 复制代码
::v-deep .el-upload-list{
  display: none;
}

或者也可以直接设置不显示文件上传列表,

java 复制代码
'show-file-list': false,

2.写自己的文件列表和事件、样式

页面

html 复制代码
<template>
<div class="file-upload-wrap">
  <!-- 上传区域 -->
  <div class="top-upload" v-if="showEdit">
    <el-upload
      :key="fileTable.length"
      ref="fileUploadComponent"
      :data="formData"
      :action="doUpload"
      :headers="headerFiles"
      :multiple="multiple"
      :before-upload="beforeUpload"
      v-bind="$attrs"
      :accept="acceptType"
      :class="{ 'file-upload-container':showType==='icons'}"
      v-on="$listeners"
    >
      <slot name="pre-content" />
      <div v-if="showType === 'icons' && showUpload" class="file-upload-icons-container">
        <div class="el-upload__text">
          <span><i class="el-icon-upload" type="primary"/> </span>
          <span v-if="$attrs.drag">将文件拖到此处,或</span><em>点击上传</em>
        </div>
        <div slot="tip" class="el-upload__tip">支持扩展名:{{ acceptType }}</div>
      </div>
      <el-button v-if="showType === 'buttons' && showUpload" :size="size" type="primary">点击上传</el-button>
      <slot name="after-content" />
    </el-upload>
  </div>
  <!-- 文件列表 -->
  <div>
    <div v-for="(fileItem, fileIndex) in fileTable" :key="fileIndex" class="file-list-class">
      <div><img :src="getImgSrc(fileItem)" :alt="fileItem.name" class="img-class" :key="fileItem.id" />{{ fileItem.name}}</div>
      <div class="right-buts-class">
        <div class="size-text">{{ fileItem.size }}</div>
        <div><i class="el-icon-success" style="color:#22b77d;margin-right: 1px;"/>成功</div>
        <div>
          <el-tooltip class="item" effect="dark" content="预览" placement="top">
            <i class="el-icon-zoom-in icon-but-class" @click="check(fileItem)"/>
          </el-tooltip>
        </div>
        <div class="convert-class">
          <el-tooltip class="item" effect="dark" content="下载" placement="top">
            <convert :scrop="{row: fileItem}" size="mini" :key="fileItem.id">
              <i class="el-icon-download icon-but-class2" slot="convertSlot"/>
            </convert>
          </el-tooltip>
        </div>
        <div>
          <el-tooltip class="item" effect="dark" content="删除" placement="top">
            <i class="el-icon-close icon-but-class2" @click="fileRemove(fileItem)" v-if="showEdit"/>
          </el-tooltip>
        </div>
      </div>
    </div>
  </div>
  <!-- 图片查看 -->
  <el-image-viewer
    v-if="showViewer"
    :on-close="closeViewer"
    :url-list="[imageUrl]"
  />
</div>
</template>

方法

javascript 复制代码
    getImgSrc(fileItem) {
      const fileTypeIcons = {
        '.file': 'file.png',
        '.html': 'html.png',
        '.psd': 'psd.png',

        '.doc': 'doc.png',
        '.docx': 'doc.png',
        '.xls': 'xls.png',
        '.xlsx': 'xls.png',
        '.ppt': 'ppt.png',
        '.pptx': 'ppt.png',
        '.pdf': 'pdf.png',

        '.jpg': 'jpg.png',
        '.jpeg': 'jpg.png',
        '.png': 'png.png',
        '.gif': 'gif.png',
        '.tiff': 'tiff.png',
        '.txt': 'txt.png',
        '.zip': 'zip.png',
        '.rar': 'zip.png',
        '.xml': 'xml.png',
        '.cad': 'cad.png',
      };
      // 获取文件后缀名
      const fileName = fileItem.name || '';
      const lastDotIndex = fileName.lastIndexOf('.');
      const ext = lastDotIndex > -1 ? fileName.substring(lastDotIndex).toLowerCase() : '';
      const iconName = fileTypeIcons[ext] || 'unknown.png';
      return `@/../images/${iconName}`;
    },
    check(data) {
      const imgType = ['jpg', 'jpeg', 'png', 'gif', 'tiff']
      if (data?.url && imgType.indexOf(data?.type)>-1) {
        this.imageUrl = this.getImageUrl(data?.url)
        this.showViewer = true
        return
      }
      return opendFileByOnlyOffice(data)
    },
    fileRemove(data) {
      this.$refs.fileUploadComponent.handleRemove(data)
    },

样式

javascript 复制代码
<style lang="scss" scoped>
::v-deep .el-upload-list{
  display: none;
}
.file-upload-container {
  width: 100%;
  display: inline-grid;
  text-align: center;
  height: 45px;
  ::v-deep .el-upload-dragger{
    height: 45px;
    width: 100%;
    display: flex;
    justify-content: center;
    align-items: anchor-center;
  }
  .top-upload{
    width: 100%;
  }
}
.file-upload-wrap{
  width: 100%;
  display: flex;
  flex-direction: column;
}

::v-deep .el-icon-upload{
  font-size: 14px;
  color: #409EFF;
}
.el-upload__tip{
  width: 100%;
  margin: 0px;
}
.el-upload__text{
  display: flex;
  flex-direction: row;
  justify-content: center;
  width: 100%;
  height: 15px;
}
.icon-but-class{
  margin-left: 10px;
  font-size: 14px!important;
  &:hover{
    cursor: pointer;
    color: #409EFF;
  }
}
.icon-but-class2{
  font-size: 14px!important;
  &:hover{
    cursor: pointer;
    color: #409EFF;
  }
}
.file-list-class{
  display: flex;
  justify-content: space-between;
  padding: 3px;
  &:hover{
    background-color: #f5f7fa;
  }
}
.size-text{
  margin-right: 10px;
}
.img-class{
  width: 16px;
  vertical-align: middle;
  margin-right: 5px;;
}
.right-buts-class{
  display: inline-flex;
  flex-direction: row;
}
.convert-class{
  width: 40px;
  ::v-deep .moka-convert-selector{
    padding: 0px;
    color: #60647f;
    font-size: 14px!important;
    &:hover{
      color: #409EFF;
    }
  }
  ::v-deep .el-icon--right{
    margin: 0px;
  }
}

.el-icon-upload{
  display: inline;
  font-size: 14px !important;
  margin-right: 3px;
  line-height: normal !important;
}
</style>

3.用法

页面中使用

html 复制代码
<file-upload-wrap
  ref="attachmentFileUpload"
  :showEdit="hasEditPermission"
  v-bind="filesUploadAttr"
  :on-success="(response, file, fileList) => filesUploadSuccess(response, file, fileList)"
  :on-remove="(file, fileList) => filesUploadRemove(file, fileList)"
  :uploadFileList="scrop.row.attachment"
/>

相关方法

javascript 复制代码
filesUploadSuccess(response, file, fileList) {
  if (!file.response.code === 200) return;
  this.messageData.attachment_id = fileList.map(upfiles => upfiles?.response?.data?.id || upfiles.id)
  const newData = fileList.map(upfiles => upfiles?.response?.data || upfiles)
  this.$set(this.messageData, 'attachment', newData);
},
filesUploadRemove(file, fileList) {
  this.messageData.attachment_id = fileList.map(upfiles => upfiles?.response?.data?.id || upfiles.id)
  const newData = fileList.map(upfiles => upfiles?.response?.data || upfiles)
  this.$set(this.messageData, 'attachment', newData);
},

这样就大功告成啦!