vue-simple-uploader实现多文件/文件夹以及可拖拽上传


vue-simple-uploader是基于simple-uploader.js的vue上传组件

  1. 支持多文件/文件夹上传,拖拽上传
  2. 可暂停、继续上传
  3. 上传错误处理
  4. 支持“秒传”,通过文件判断服务端是否已存在从而实现“秒传”
  5. 支持进度、预估剩余时间、出错自动重试、重传等操作

这里有参考文档事例

1.效果图展示

后面有使用拖拽上传文件/文件夹

在这里插入图片描述
上传遇到同名的文件会有一个弹框提示,并有相关的操作
在这里插入图片描述

2.安装

npm install vue-simple-uploader --save

3.vue2使用(vue3使用会报错)

import uploader from 'vue-simple-uploader'
//已经创建Vue实例了
Vue.use(uploader)

4.代码

这里我只贴主要的内容,代码也都给有注释哦

 <uploader
    ref="uploader"
    :options="options"
    :auto-start="false"
    :file-status-text="fileStatusText"
    class="uploader-example"
    @file-added="onFileAdded"
    @files-added="onFilesAdded"
    @file-error="onFileError"
    @file-complete="onFileComplete"
  >
    <uploader-unsupport /> //不支持上传时显示内容
    //这个组件也有两个上传的按钮 但是我这边给隐藏了 自定义了下拉框 点击的时候 获取对应的实例 然后触发点击事件
    <uploader-btn
      id="uploader_btn"
      ref="uploadBtn"
    >选择文件</uploader-btn>
    <uploader-btn
      id="uploader_btn"
      ref="uploadFolderBtn"
      :directory="true"
    >选择文件夹</uploader-btn>
    //可拖拽的区域
    <uploader-drop class="drop">
    	//我把这个table区域变成可拖拽的了 具体看自己写的内容吧
    	<el-table></el-table>
    </uploader-drop>
    //这是上传文件显示的上传弹框,在右下角 有文件上传时显示 默认是隐藏
    <div
      v-show="isShowDropUploadFileLists"
      class="drog_list"
    >
      <uploader-list>
        <div
          slot-scope="props"
          class="file-panel"
          :class="{ collapse: collapse }"
        >
          <div class="file-title">
            <div class="title">文件列表</div>
            <div class="operate">
              <el-button
                type="text"
                :title="collapse ? '展开' : '折叠'"
                @click="collapse = !collapse"
              >
                <i :class="collapse ? 'el-icon-full-screen' : 'el-icon-minus'" />
              </el-button>
              <el-button
                type="text"
                title="关闭"
                @click="CloseFilesUploadList"
              >
                <i class="el-icon-close" />
              </el-button>
            </div>
          </div>
          <ul class="file-list">
            <li
              v-for="file in props.fileList"
              :key="file.id"
              class="file-item"
              :class="`file-${file.id}`"
            >
              <uploader-file
                ref="files"
                :class="'file_' + file.id"
                :file="file"
                :list="true"
              />
            </li>
            <div
              v-if="!props.fileList.length"
              class="no-file"
            >
              <i class="iconfont icon-empty-file" /> 暂无待上传文件
            </div>
          </ul>
        </div>
      </uploader-list>
    </div>
  </uploader>

相关js我是单独拎出来了然后通过mixins混入到当前vue文件了

import {
    
     GetBatchFilesId, GetDocumentSameNameInfo } from "@/api/file"; //批量上传获取ID,对比上传文件是否跟已存在的同名的两个接口
import {
    
     CreateFilePath, CreateFileName } from "@/utils/handleFile";//这个是入参对文件/文件夹路径和名字的处理
export default {
    
    
  data () {
    
    
    return {
    
    
      options: {
    
    
        target: "http://test.hhh.com.cn/api/Files",//上传地址,如果有文件上传地址不同时 可以是个函数来改变
        testChunks: false,//测试每个块是否在服务端已经上传了
        allowDuplicateUploads: true, // 一个文件以及上传过了是否还允许再次上传
        query: (file, chunk) => {
    
     //上传时所带的参数 可以是个函数在选择文件时 自定义(拿到的两个参数分别是Uploader.File 实例、当前块 Uploader.Chunk 以及是否是测试模式)
          return {
    
    
            ...file.params,
            isUseLastVersionFieldValueWhenUpdate: false, // 請帶false
            path: CreateFilePath(file.relativePath, true),
            autoIdNamingType: "Auto", // 自动编码
            directoryId: 14, // 项目ID
          };
        },
      },
      fileStatusText: {
    
    //显示的状态
        success: "上传成功",
        error: "上传失败",
        uploading: "上传中",
        paused: "已暂停",
        waiting: "等待上传",
      },
    }
  },
  
 computed: {
    
    
    // 获取上传文件实例
    uploaderRef() {
    
    
      return this.$refs.uploader.uploader;
    },
 },
 
  methods: {
    
    
  //值得注意的是我这个方法是@files-added="onFilesAdded",是对当前一次性上传的所有文件做的处理,比如说上传5个文件 那就是会获得一个数组里面包括5个文件,这个函数是在file-added全部执行完后走一次
 // @file-added="onFileAdded"获取到的单个当前上传文件,按照上传顺序来的,比如说上传5个文件,那这个函数会执行5次
    // 全部文件处理 
    onFilesAdded (files, filelist) {
    
    
      this.nowUploadFiles = [...files]//当前上传的文件列表保存了一下 后面有同名弹框操作后 方便继续上传文件 不然触发到同名文件弹框了 后续的操作获取不到已经上传到一半的文件了
      let getBatchIds = ''
      const filesLength = files.length
      // 调用文件同名验证接口,这边入参用的都formData格式
      var formdata = new FormData();
      formdata.append("id", 3);
      formdata.append("directoryId", 6);
      formdata.append("isReserveDirStructure", true);
      this.fileList.forEach((v) => {
    
    
        formdata.append("paths[]", v);
      });
      this.fileNameList.forEach((v) => {
    
    
        formdata.append("filenames[]", v);
      });
      this.fileList = []
      this.fileNameList = []
      const getSameFileInfo = new Promise((resolve, reject) => {
    
    
        formdata.append("autoIdNamingType", "Auto");
        GetDocumentSameNameInfo(formdata).then(_res => {
    
    
          resolve(_res)
        }).catch(err => reject(err))
      })
      if (filesLength > 1) {
    
     //多文件或者文件夹上传的时候 入参需要带个批量ID 看自己需求哈
        // 批量上传文件
        getBatchIds = new Promise((resolve, reject) => {
    
    
          formdata.append("isTemp", false);
          GetBatchFilesId(formdata).then(_res => {
    
    
            resolve(_res)
          }).catch(err => reject(err))
        })
      }
      Promise.all([getSameFileInfo, getBatchIds]).then(res => {
    
    
        if (res && res.length) {
    
    
          // 同名文件对比弹框弹出
          this.isShowSameFiledialog = !!(res[0] && res[0].length)
          this.isShowSameFileInfo = res[0] // 同名文件列表
          // 保存批量文件ID
          this.BatchUploadId = res[1].BatchUploadId || ''
          if (!this.isShowSameFiledialog) {
    
    
            // 单个文件无同名时直接上传,不显示弹框
            console.log(files[0].name)
            files.forEach(item => {
    
    
              item.params = {
    
    
                sameNameUpdateisTrue: this.sameNameUpdateisTrue, // 遇到同名檔案是否更版
                isReserveDirStructure: false,
              }
              item.resume();
            })
            // 显示上传文件弹框
            this.isShowDropUploadFileLists = true
            console.log('单个文件无同名时直接上传,不显示弹框')
          }
        }
      }).catch(err => console.log(err, '哈哈哈'))
    },

    // 文件上传获取单个文件
    onFileAdded (file, fileList) {
    
    
      const _dataPath = file.relativePath;
      // 获取到文件的路径数组和对应的文件名数组
      this.fileList.push(CreateFilePath(_dataPath, this.isFromDragDrop));
      this.fileNameList.push(CreateFileName(_dataPath));
    },

    // 根文件上传成功
    onFileComplete (rootFile) {
    
    
      this.$message({
    
    
        message: "上传成功!",
        type: "success",
      });
    },

    // 文件上传失败
    onFileError (file) {
    
    
      this.$message({
    
    
        message: "上传失败,请重试!",
        type: "error",
      });
    },

    // 点击关闭按钮
    CloseFilesUploadList () {
    
    
      this.uploaderRef.cancel();
      this.isShowDropUploadFileLists = false;
    },

  }
}


遇到同名文件弹框的操作

  <SameFileInfo
      ref="sameFileDailog"
      :isshow="isShowSameFiledialog" //显示弹框
      :same-file-info="isShowSameFileInfo"//同名文件数据列表
      @handelUpdateFile="handelUpdateSameNameFile"
   />
 // 同名文件弹框操作
    handelUpdateSameNameFile(type) {
    
    
      if (type === "cancel") {
    
     //取消操作
        this.nowUploadFiles &&
          this.nowUploadFiles.forEach((item) => {
    
    
            item.ignored = true; 
          });
        this.uploaderRef.cancel(); //关闭上传 uploaderRef是组件uploader的实例
        this.isShowDropUploadFileLists = false; //关闭上传文件列表
        // 关闭弹框
        this.isShowSameFiledialog = false;
        return false;
      }
      if (type === "skip") {
    
    //跳过
        // 点击跳过
        this.sameNameUpdateisTrue = false;
        // 过滤掉重名的文件
        const _this = this;
        _this.nowUploadFiles &&
          _this.nowUploadFiles.forEach((v) => {
    
    
            _this.isShowSameFileInfo.forEach((val) => {
    
    
              if (val.DocumentName.split(".")[0] === v.name.split(".")[0]) {
    
    
                v.ignored = true; //给当前上传的文件列表中同名文件都加一个属性,后续过滤掉同名文件不上传
              }
            });
          });
      } else if (type === "replace") {
    
    
        this.sameNameUpdateisTrue = true;
      } else if (type === "add") {
    
    
        this.sameNameUpdateisTrue = false;
      }
      // 给上传插件赋值(传参)
      this.nowUploadFiles &&
        this.nowUploadFiles.forEach((item) => {
    
    
          item.params = {
    
    
            sameNameUpdateisTrue: this.sameNameUpdateisTrue, // 遇到同名檔案是否更版
            isReserveDirStructure: true, // 是否保留本地文件夾結構 (單檔可帶false)
            batchUploadId: this.BatchUploadId,
          };
          if (item.ignored) {
    
    
            this.uploaderRef.removeFile(item); //如果有同名过滤属性的则选择不上传
          }
          item.resume();
        });
      // 关闭弹框
      this.isShowSameFiledialog = false;
      // 显示上传文件弹框
      this.isShowDropUploadFileLists = true;
    },

大致逻辑都在这里了
最后贴一下样式代码吧

<style lang="scss">
#uploader_btn {
    
    
  position: absolute;
  clip: rect(0, 0, 0, 0);
}
.drog_list {
    
    
  position: fixed;
  z-index: 20;
  right: 15px;
  bottom: 15px;
  width: 520px;
  box-sizing: border-box;
  .file-panel {
    
    
    background-color: #fff;
    border: 1px solid #e2e2e2;
    border-radius: 7px 7px 0 0;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);

    .file-title {
    
    
      display: flex;
      height: 40px;
      line-height: 40px;
      padding: 0 15px;
      border-bottom: 1px solid #ddd;

      .operate {
    
    
        flex: 1;
        text-align: right;

        i {
    
    
          font-size: 18px;
        }
      }
    }

    .file-list {
    
    
      position: relative;
      height: 240px;
      overflow-x: hidden;
      overflow-y: auto;
      background-color: #fff;
      transition: all 0.3s;
      list-style: none;
      padding: 0 2%;
      font-size: 12px;
      .file-item {
    
    
        background-color: #fff;
      }

      ::v-deep .uploader-file-size {
    
    
        text-indent: 0 !important;
      }
      ::v-deep .uploader-file-status {
    
    
        width: 22%;
      }
      ::v-deep .uploader-file-name {
    
    
        display: flex;
        align-items: center;
        .uploader-file-icon {
    
    
          margin-top: 0;
          margin-right: 0;
        }
      }
      ::v-deep .uploader-file-meta {
    
    
        display: none;
      }
      ::v-deep .uploader-file-actions {
    
    
        width: 12%;
        float: right;
      }
    }

    &.collapse {
    
    
      .file-title {
    
    
        background-color: #e7ecf2;
      }
      .file-list {
    
    
        height: 0;
      }
    }
  }
}
</style>

猜你喜欢

转载自blog.csdn.net/weixin_45324044/article/details/127089382