zhangzengfei
2023-10-09 8e40a69fcfe8bc799fee141fec953a2b0892dbd4
src/pages/ai/FileUpload/index.vue
@@ -1,306 +1,306 @@
<template>
  <div class="file-uploader">
    <uploader
      v-if="single"
      ref="uploader"
      :options="options"
      :file-status-text="statusText"
      class="uploader-single"
      :sourceType="sourceType"
      @file-added="onFileAdded"
      @complete="onComplete"
    >
      <div class="up-bar" v-if="isDrag == true">
        <div class="name">{{ fileName || uploadPlaceholder }}</div>
        <uploader-btn slot="suffix" :attrs="attrs">
          <el-tooltip :content="tipWords" placement="top" v-if="tip">
            <div class="open-file-btn">
              <span class="icon iconfont">&#xe712;</span>
            </div>
          </el-tooltip>
        </uploader-btn>
      </div>
      <el-input
        :placeholder="uploadPlaceholder"
        v-if="isDrag == false"
        size="small"
        :readonly="true"
        v-model="fileName"
      >
        <uploader-btn slot="suffix" :attrs="attrs">
          <el-tooltip :content="tipWords" placement="top" v-if="tip">
            <i
              class="el-icon-upload2"
              style="font-size: 18px; color: #0088ff"
            ></i>
          </el-tooltip>
          <i
            v-else
            class="el-icon-upload2"
            style="font-size: 18px; color: #0088ff"
          ></i>
        </uploader-btn>
      </el-input>
      <uploader-list />
    </uploader>
    <uploader
      v-else
      ref="uploader"
      :options="options"
      :file-status-text="statusText"
      class="uploader-example"
      @file-added="onFileAdded"
      @file-complete="fileComplete"
      @complete="onComplete"
      @close="closeHandle"
    >
      <uploader-btn ref="button" :sourceType="sourceType">
        <i class="el-icon-upload2" style="font-size: 18px; color: #0088ff"></i>
        上传
      </uploader-btn>
      <uploader-list />
    </uploader>
  </div>
</template>
<script>
import uploader from "./uploader";
import SparkMD5 from "spark-md5";
import UploaderBtn from "./btn";
import UploaderList from "./list";
import UploaderDrop from "./drop";
export default {
  components: {
    uploader,
    UploaderBtn,
    UploaderList,
    UploaderDrop,
  },
  props: {
    sourceType: {
      type: Number,
    },
    tip: {
      type: Boolean,
      default: false,
    },
    tipWords: {
      type: String,
      default: "",
    },
    single: {
      type: Boolean,
      default: false,
    },
    uploadPlaceholder: {
      type: String,
      default: "",
    },
    isDrag: {
      type: Boolean,
      default: false,
    },
    url: {
      type: String,
      default: "/data/api-f/file/upload",
    },
    attrs: {
      type: Object,
      default() {
        return {};
      },
    },
  },
  data() {
    return {
      fileName: "",shouldStop:false,
      fileMd5: "",
      statusText: {
        success: "上传成功",
        error: "上传失败",
        uploading: "上传中",
        paused: "暂停中",
        waiting: "等待中",
      },
    };
  },
  computed: {
    uploader() {
      return this.$refs.uploader.uploader;
    },
    options() {
      return {
        target: this.url,
        testChunks: true,
        headers: {
          Authorization:
            sessionStorage.getItem("loginedInfo") &&
            JSON.parse(sessionStorage.getItem("loginedInfo")).access_token,
        },
      };
    },
  },
  methods: {
    onFileAdded(file) {
      // if (this.sourceType == 3) {
      //   if (
      //     !file.name.endsWith(".tar") ||
      //     !file.name.endsWith(".gz") ||
      //     !file.name.endsWith(".tar.gz")
      //   ) {
      //     this.shouldStop = true
      //     this.$notify.warning("仅支持.tar/.gz/.tar.gz三种格式文件");
      //     return
      //   }
      // }
      if (this.single) {
        this.uploader.fileList = this.uploader.fileList.slice([-1]);
        this.$emit("file-added");
      }
      this.computeMD5(file);
    },
    computeMD5(file) {
      let fileReader = new FileReader();
      let time = new Date().getTime();
      let blobSlice =
        File.prototype.slice ||
        File.prototype.mozSlice ||
        File.prototype.webkitSlice;
      let currentChunk = 0;
      const chunkSize = 10 * 1024 * 1000;
      let chunks = Math.ceil(file.size / chunkSize);
      let spark = new SparkMD5.ArrayBuffer();
      // 文件状态设为"计算MD5"
      this.statusText.paused = "准备上传,正在检查文件";
      file.pause();
      loadNext();
      fileReader.onload = (e) => {
        spark.append(e.target.result);
        if (currentChunk < chunks) {
          currentChunk++;
          loadNext();
        } else {
          let md5 = spark.end();
          this.computeMD5Success(md5, file);
          this.fileName = file.name;
          this.fileMd5 = md5;
        }
      };
      fileReader.onerror = function () {
        this.error(`文件${file.name}读取出错,请检查该文件`);
        file.cancel();
      };
      function loadNext() {
        let start = currentChunk * chunkSize;
        let end =
          start + chunkSize >= file.size ? file.size : start + chunkSize;
        fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
      }
    },
    computeMD5Success(md5, file) {
      //将自定义参数直接加载uploader实例的opts上
      if (location.href.indexOf("dataStack") >= 0) {
        Object.assign(this.uploader.opts, {
          query: {
            stackId: this.DataStackPool.selectedDir.id,
            // ...this.params,
          },
        });
      }
      file.uniqueIdentifier = md5;
      file.resume();
      this.statusText.paused = "暂停中";
    },
    onComplete() {
      if (this.shouldStop) {
        return
      }
      this.$emit("complete", {
        filename: this.fileName,
        identifier: this.fileMd5,
      });
    },
    fileComplete() {},
    closeHandle() {
      this.$emit("close");
    },
  },
  mounted() {
    this.isDrag;
    this.$nextTick(() => {
      window.uploader = this.$refs.uploader.uploader;
    });
  },
};
</script>
<style lang="scss">
.file-uploader {
  .el-input__suffix,
  .el-input__suffix-inner {
    outline: none !important;
  }
  .el-tooltip.focusing {
    outline: none;
  }
  .uploader-example {
    width: 99%;
    // padding: 15px;
    // margin: 40px auto 0;
    font-size: 12px;
    // box-shadow: 0 0 10px rgba(0, 0, 0, 0.4);
    background-color: #fff;
  }
  .uploader-example .uploader-btn {
    position: relative;
    display: none;
    // float: right;
    // top: -45px;
  }
  .uploader-example .uploader-list {
    max-height: 440px;
    overflow: auto;
    overflow-x: hidden;
    overflow-y: auto;
  }
  .uploader-single {
    position: unset;
    .close {
      display: none;
    }
    .uploader-btn {
      border: 0px;
    }
    .uploader-file {
      // height: 2px;
      .uploader-file-progress {
        // background: #3d68e1;
      }
      .uploader-file-info {
        // display: none;
      }
    }
    .up-bar {
      height: 30px;
      background: #f2f2f7;
      border-radius: 2px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      box-sizing: border-box;
      padding: 0 20px;
      .iconfont {
        font-size: 16px;
        color: #333;
      }
      .name {
        color: #bdbdbd;
        font-size: 14px;
      }
    }
  }
}
</style>>
<template>
  <div class="file-uploader">
    <uploader
      v-if="single"
      ref="uploader"
      :options="options"
      :file-status-text="statusText"
      class="uploader-single"
      :sourceType="sourceType"
      @file-added="onFileAdded"
      @complete="onComplete"
    >
      <div class="up-bar" v-if="isDrag == true">
        <div class="name">{{ fileName || uploadPlaceholder }}</div>
        <uploader-btn slot="suffix" :attrs="attrs">
          <el-tooltip :content="tipWords" placement="top" v-if="tip">
            <div class="open-file-btn">
              <span class="icon iconfont">&#xe712;</span>
            </div>
          </el-tooltip>
        </uploader-btn>
      </div>
      <el-input
        :placeholder="uploadPlaceholder"
        v-if="isDrag == false"
        size="small"
        :readonly="true"
        v-model="fileName"
      >
        <uploader-btn slot="suffix" :attrs="attrs">
          <el-tooltip :content="tipWords" placement="top" v-if="tip">
            <i
              class="el-icon-upload2"
              style="font-size: 18px; color: #0088ff"
            ></i>
          </el-tooltip>
          <i
            v-else
            class="el-icon-upload2"
            style="font-size: 18px; color: #0088ff"
          ></i>
        </uploader-btn>
      </el-input>
      <uploader-list />
    </uploader>
    <uploader
      v-else
      ref="uploader"
      :options="options"
      :file-status-text="statusText"
      class="uploader-example"
      @file-added="onFileAdded"
      @file-complete="fileComplete"
      @complete="onComplete"
      @close="closeHandle"
    >
      <uploader-btn ref="button" :sourceType="sourceType">
        <i class="el-icon-upload2" style="font-size: 18px; color: #0088ff"></i>
        上传
      </uploader-btn>
      <uploader-list />
    </uploader>
  </div>
</template>
<script>
import uploader from "./uploader";
import SparkMD5 from "spark-md5";
import UploaderBtn from "./btn";
import UploaderList from "./list";
import UploaderDrop from "./drop";
export default {
  components: {
    uploader,
    UploaderBtn,
    UploaderList,
    UploaderDrop,
  },
  props: {
    sourceType: {
      type: Number,
    },
    tip: {
      type: Boolean,
      default: false,
    },
    tipWords: {
      type: String,
      default: "",
    },
    single: {
      type: Boolean,
      default: false,
    },
    uploadPlaceholder: {
      type: String,
      default: "",
    },
    isDrag: {
      type: Boolean,
      default: false,
    },
    url: {
      type: String,
      default: "/data/api-f/file/upload",
    },
    attrs: {
      type: Object,
      default() {
        return {};
      },
    },
  },
  data() {
    return {
      fileName: "",shouldStop:false,
      fileMd5: "",
      statusText: {
        success: "上传成功",
        error: "上传失败",
        uploading: "上传中",
        paused: "暂停中",
        waiting: "等待中",
      },
    };
  },
  computed: {
    uploader() {
      return this.$refs.uploader.uploader;
    },
    options() {
      return {
        target: this.url,
        testChunks: true,
        headers: {
          Authorization:
            sessionStorage.getItem("loginedInfo") &&
            JSON.parse(sessionStorage.getItem("loginedInfo")).access_token,
        },
      };
    },
  },
  methods: {
    onFileAdded(file) {
      // if (this.sourceType == 3) {
      //   if (
      //     !file.name.endsWith(".tar") ||
      //     !file.name.endsWith(".gz") ||
      //     !file.name.endsWith(".tar.gz")
      //   ) {
      //     this.shouldStop = true
      //     this.$notify.warning("仅支持.tar/.gz/.tar.gz三种格式文件");
      //     return
      //   }
      // }
      if (this.single) {
        this.uploader.fileList = this.uploader.fileList.slice([-1]);
        this.$emit("file-added");
      }
      this.computeMD5(file);
    },
    computeMD5(file) {
      let fileReader = new FileReader();
      let time = new Date().getTime();
      let blobSlice =
        File.prototype.slice ||
        File.prototype.mozSlice ||
        File.prototype.webkitSlice;
      let currentChunk = 0;
      const chunkSize = 10 * 1024 * 1000;
      let chunks = Math.ceil(file.size / chunkSize);
      let spark = new SparkMD5.ArrayBuffer();
      // 文件状态设为"计算MD5"
      this.statusText.paused = "准备上传,正在检查文件";
      file.pause();
      loadNext();
      fileReader.onload = (e) => {
        spark.append(e.target.result);
        if (currentChunk < chunks) {
          currentChunk++;
          loadNext();
        } else {
          let md5 = spark.end();
          this.computeMD5Success(md5, file);
          this.fileName = file.name;
          this.fileMd5 = md5;
        }
      };
      fileReader.onerror = function () {
        this.error(`文件${file.name}读取出错,请检查该文件`);
        file.cancel();
      };
      function loadNext() {
        let start = currentChunk * chunkSize;
        let end =
          start + chunkSize >= file.size ? file.size : start + chunkSize;
        fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
      }
    },
    computeMD5Success(md5, file) {
      //将自定义参数直接加载uploader实例的opts上
      if (location.href.indexOf("dataStack") >= 0) {
        Object.assign(this.uploader.opts, {
          query: {
            stackId: this.DataStackPool.selectedDir.id,
            // ...this.params,
          },
        });
      }
      file.uniqueIdentifier = md5;
      file.resume();
      this.statusText.paused = "暂停中";
    },
    onComplete() {
      if (this.shouldStop) {
        return
      }
      this.$emit("complete", {
        filename: this.fileName,
        identifier: this.fileMd5,
      });
    },
    fileComplete() {},
    closeHandle() {
      this.$emit("close");
    },
  },
  mounted() {
    this.isDrag;
    this.$nextTick(() => {
      window.uploader = this.$refs.uploader.uploader;
    });
  },
};
</script>
<style lang="scss">
.file-uploader {
  .el-input__suffix,
  .el-input__suffix-inner {
    outline: none !important;
  }
  .el-tooltip.focusing {
    outline: none;
  }
  .uploader-example {
    width: 99%;
    // padding: 15px;
    // margin: 40px auto 0;
    font-size: 12px;
    // box-shadow: 0 0 10px rgba(0, 0, 0, 0.4);
    background-color: #fff;
  }
  .uploader-example .uploader-btn {
    position: relative;
    display: none;
    // float: right;
    // top: -45px;
  }
  .uploader-example .uploader-list {
    max-height: 440px;
    overflow: auto;
    overflow-x: hidden;
    overflow-y: auto;
  }
  .uploader-single {
    position: unset;
    .close {
      display: none;
    }
    .uploader-btn {
      border: 0px;
    }
    .uploader-file {
      // height: 2px;
      .uploader-file-progress {
        // background: #3d68e1;
      }
      .uploader-file-info {
        // display: none;
      }
    }
    .up-bar {
      height: 30px;
      background: #f2f2f7;
      border-radius: 2px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      box-sizing: border-box;
      padding: 0 20px;
      .iconfont {
        font-size: 16px;
        color: #333;
      }
      .name {
        color: #bdbdbd;
        font-size: 14px;
      }
    }
  }
}
</style>>