<template>
  <div>
    <el-upload class="upload-demo" ref="upload" action="#"
               :before-upload="beforeUpload"
               :on-change="onChange"
               :on-remove="handleRemove"
               :multiple="false"
               :on-exceed="handleExceed"
               :accept="acceptType"
               :file-list="fileList"
               :auto-upload="true"
               :http-request="uploadFile"
               show-file-list>
      <el-button slot="trigger" size="small" type="primary" @click="isFolder = false">上传文件</el-button>
      <el-button size="small" type="primary" @click="handleUploadFile" >
        上传文件夹
      </el-button>
      <!--    上传-->
      <input v-show="false" ref="fileRef" id="fileFolder" type="file" @change="handleFolderUpload" webkitdirectory
             multiple="multiple" />
      <div slot="tip" class="el-upload__tip" >只能上传{{acceptType}}格式</div>
    </el-upload>
    <div v-show="progressFlag" class="head-img">
      <el-progress :text-inside="true" :stroke-width="14" :percentage="progressPercent" status="success"></el-progress>
    </div>
  </div>
</template>

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

export default {
  props: {
    uploadUrl: {
      type: String,
      required: false
    },
    // 是否多选
    isMultiple: {
      type: Boolean,
      default() {
        return false
      },
      required: false
    },
    // 文件类型
    type: {
      type: Array,
      default() {
        return []
      },
      required: false
    },
    //上传类型
    acceptType: {
      type: String,
      required: false
    },
    // 上传数量
    limit: {
      type: Number,
      default() {
        return 1
      },
      required: false
    },
    // 文件
    dataFile: {
      type: [Object, Array, String],
      default() {
        return ''
      },
      required: false
    },
    isDetail: {
      type: Boolean,
      default: false
    },
  },
  data() {
    return {
      fileList: [],
      progress: 0,
      isUploading: false,
      progressFlag: false, //进度条初始值隐藏
      progressPercent: 0, //进度条初始值
      partSize: 5 * 1024 * 1024,
      fileDetailName: null,
      uploadedNum: 0,
      _shardCount: null, //是否最后一片
      isFolder: false,
      totalFiles: 0,    // 总文件数量
      uploadedCount: 0, // 已上传文件数量
    };
  },
  watch: {
    dataFile: {
      handler(newValue, oldValue) {
        if (newValue && newValue.length > 0) {
          this.fileList = Array.isArray(newValue) ? newValue : [newValue]
          this.fileDetailName = this.fileList[0].name
        } else {
          this.fileList = []
        }
      },
      immediate: true
    }
  },
  methods: {
    created(){
      this.clearFile()
    },
    // 上传前
    beforeUpload(file) {
      /* const fileType = file.type.toLowerCase()
       console.info("fileType=======", fileType)
       const typeFlag = this.type.some(item => {
         return fileType.indexOf(item) != -1
       })
       if (!typeFlag) {
         this.$message.error('上传类型有误，请重新上传')
         return false
       }*/
    },
    //文件状态改变时的钩子，添加文件、上传成功和上传失败时都会被调用
    onChange(file, fileList) {
      this.fileList = fileList;
    },
    handleRemove(file, fileList) {
      this.fileList = fileList;
    },
    handleExceed(files, fileList) {
      /* this.$message.warning(
        "最多之能上传"+ this.limit +"个附件"
      ); */
    },
    async uploadFile({ data, file }) {
      let self = this
      //初始化参数
      if (file.size < this.partSize) {
        let formData = new FormData()
        formData.append("file", file)
        self.progressFlag = true;
        axios({
          url: self.uploadUrl,
          method: 'post',
          data: formData,
          headers: {
            'Authorization': 'Bearer ' + getToken(),
            'Content-Type': 'multipart/form-data'
          },
          onUploadProgress: progressEvent => {
            // progressEvent.loaded:已上传文件大小
            // progressEvent.total:被上传文件的总大小
            //进度条
            self.progressPercent = ((progressEvent.loaded / progressEvent.total) * 100) | 0;
          }
        }).then(res => {
          setTimeout(() => {
            if (res.data.code == 200) {
              setTimeout(function () {
                //文件夹上传
                if(self.isFolder){
                  self.fileList.push(file)
                }
                self.$message({
                  message: '上传成功！',
                  type: 'success',
                  duration: '2000'
                });
                self.progressFlag = false;
                self.progressPercent = 0
                self.handleResult(res,file);
              }, 500);
            } else {
              self.progressFlag = false;
              self.progressPercent = 0
              if(self.$refs.upload){
                self.$refs.upload.clearFiles();
              }
              self.$message({
                message: '上传失败！',
                type: 'error',
                duration: '2000'
              });
            }
          }, 1000);

        }).catch(error => {
          console.error(error)
          self.progressFlag = false;
          self.progressPercent = 0
          if(self.$refs.upload){
            self.$refs.upload.clearFiles();
          }
          self.$message({
            message: '上传失败！',
            type: 'error',
            duration: '2000'
          });
        })
      } else {
       self.uploadIdSharding(file);
      }
    },

    uploadIdSharding(file){
      let self = this
      self.uploadedNum = 0
      file._shardCount = Math.ceil(file.size / this.partSize) //总片数
      file.uploaded = 0
      let formData = new FormData()
      formData.append('fileName', file.name)
      //大文件上传初始化,返回uploadId  initUpload(formData)
      axios({
        url: process.env.VUE_APP_BASE_API + "/common/initUpload",
        method: 'post',
        data: formData,
        headers: {
          'Authorization': 'Bearer ' + getToken(),
          'Content-Type': 'multipart/form-data'
        },
      }).then(function (res) {
        if (res.status == 200) {
          //从第0块开始上传
          file.uploadId = res.data.uploadId
          file.objectKey = res.data
          file.chunkId = 0
          self.uploadByChunk(file)
        } else {
          this.progress--
          self.$message.error(res.msg)
        }
      }).catch(error => {
        self.progressPercent = 0
        self.progressFlag = false;
        console.error(error)
      })
    },
    //分片上传大文件
    uploadByChunk(file) {
      this._start = file.chunkId * this.partSize
      this._end = Math.min(file.size, this._start + this.partSize)//结束时总大小，和 开始的大小+之前的大小比较

      let self = this
      let fileData = file.slice(this._start, this._end)
      //获取文件块MD5
      let reader = new FileReader()
      reader.readAsBinaryString(fileData)
      let form1 = new FormData()//new一个form的实例，可以进行键值对的添加，
      form1.append('chunkFile', fileData) //slice方法用于切出文件的一部分
      form1.append('uploadId', file.uploadId)
      form1.append('chunkId', (file.chunkId + 1).toString())
      form1.append('shardCount', file._shardCount.toString()) //是否最后一片
      self.progressFlag = true;
      //上传大文件的Chunk 返回chunk的MD5 uploadChunk(form1)
      axios({
        url: process.env.VUE_APP_BASE_API + "/common/uploadChunk",
        method: 'post',
        data: form1,
        headers: {
          'Authorization': 'Bearer ' + getToken(),
          'Content-Type': 'multipart/form-data'
        },
      }).then(response => {
        if (response.status == 200) {
          //判断返回的MD5值是否一致，一致继续传下一块，否则重传本块（这里目前前后端MD5一直不一致，暂时先注释掉，有时间了我再研究一下）
          // self.md5Str[index] === response.msg ||
          if (true) {
            // this.$message.success('第' + (index + 1).toString() + '块文件上传成功')
            self.uploadedNum++
            let percent = Math.floor((self.uploadedNum / file._shardCount) * 100)
            self.percentageSend(file, percent)
            //如果没上传完成，继续上传一下块
            file.chunkId++
            if (file.chunkId < file._shardCount) {
              this.uploadByChunk(file)
            }
          } else {
            //不一致，重新传本块
            this.$message.success('第' + (file.chunkId + 1).toString() + '块文件上传不成功，重新上传')
            this.uploadByChunk(file.chunkId)
          }
        } else {
          //出错，跳出循环显示错误
          this.progress--
          this.$message.error(response.msg)
        }
      })
    },
    //接收上传的百分值回调
    percentageSend(file, perNum) {
      let self = this
      self.progressPercent = perNum
      //如果上传完成，合并文件
      if (perNum === 100) {
        let form2 = new FormData()//new一个form的实例，可以进行键值对的添加，
        form2.append('uploadId', file.uploadId)
        form2.append('fileName', file.name)
        // 大文件上传完成后合并
        // 返回文件访问的URL
        axios({
          url: process.env.VUE_APP_BASE_API + "/common/mergeFile",
          method: 'post',
          data: form2,
          headers: {
            'Authorization': 'Bearer ' + getToken(),
            'Content-Type': 'multipart/form-data'
          },
        }).then(res => {
          if (res.status == 200) {
            setTimeout(function () {
              self.$message({
                message: '上传成功！',
                type: 'success',
                duration: '2000'
              });
              self.progressFlag = false;
              self.progressPercent = 0
              self.handleResult(res,file);
            }, 500);
            //文件夹上传
            if(self.isFolder){
              self.fileList.push(file)
            }
          } else {
            self.progressFlag = false;
            self.progressPercent = 0
            if(self.$refs.upload){
              self.$refs.upload.clearFiles();
            }
            self.$message({
              message: '上传失败！',
              type: 'error',
              duration: '2000'
            });
          }
        }).catch(error => {
          self.progressFlag = false;
          self.progressPercent = 0
          if(self.$refs.upload){
            self.$refs.upload.clearFiles();
          }
          self.$message({
            message: '上传失败！',
            type: 'error',
            duration: '2000'
          });
        });
      }
    },

    resetData() {
      this.progress = 0;
    },
    handleResult(res, file) {
      let self = this
      const data = {
        fileName: file.name,
        fileSize: file.size,
        fileUrl: res.data.url,
        filePath: res.data.filePath,
        fileOldName: res.data.originalFilename,
        fileNewName: res.data.newFileName,
        suffixType: res.data.suffixType
      }
      self.$emit("handleSuccess", data)
      self.clearFile()
    },
    /** 清空文件 **/
    clearFile(){
      if(this.$refs.upload){
        this.$refs.upload.clearFiles();
      }

      this.fileList = []
    },
    handleUploadFile() {
      document.getElementById("fileFolder").value = null;
      this.$refs.fileRef.dispatchEvent(new MouseEvent("click"));
    },
/*     async handleFolderUpload(event) {
      let self = this;
      self.isFolder = true
      const files = event.target.files;
      self.fileslength = files.length;
      let filesper = Math.floor( 100 / files.length * 100)/100;
      self.progressShow = true;
      if (self.fileslength == 0) {
        self.progressShow = false;
        self.$modal.msg("上传完成0个，未完成0个");
      }

      for (const file of files) {
        let temp = {
          "data": null,
          "file": file
        }
        await self.uploadFile(temp);
      }
    }, */

   /*  // 处理文件夹上传
    async handleFolderUpload(event) {
      const files = event.target.files;
      this.isFolder = true;
      this.progressFlag = true;

      if (files.length === 0) {
        this.$message.warning('没有选择文件');
        return;
      }

      let successCount = 0;
      let failCount = 0;
      for (const file of files) {
        try {
          await this.uploadFolderFile({ file });
          successCount++;
        } catch (error) {
          failCount++;
        }
      }

      this.$message.success(`上传完成！成功：${successCount}，失败：${failCount}`);
      this.progressFlag = false;
      this.progressPercent = 0;
    }, */
    // 单个文件上传
    uploadFolderFile({ data, file }) {
      let self = this
      //初始化参数
      if (file.size < this.partSize) {
        return new Promise((resolve, reject) => {
          const formData = new FormData();
          formData.append("file", file);
          this.progressFlag = true;

          axios({
            url: self.uploadUrl,
            method: 'post',
            data: formData,
            headers: {
              'Authorization': 'Bearer ' + getToken(),
              'Content-Type': 'multipart/form-data'
            },
           /*  onUploadProgress: progressEvent => {
              this.progressPercent = ((progressEvent.loaded / progressEvent.total) * 100) | 0;
            } */
          }).then(res => {
            setTimeout(() => {
              if (res.data.code === 200) {
                // self.progressFlag = false;
                // self.progressPercent = 0;
                self.handleResult(res,file);
                resolve();  // 上传成功后，调用 resolve
              } else {
                // self.progressFlag = false;
                self.$message.error(file.name + '上传失败！');
                reject();  // 上传失败时调用 reject
              }
            }, 500);
          }).catch(error => {
            // self.progressFlag = false;
            // self.progressPercent = 0;
            self.$message.error(file.name + '上传失败！');
            reject(error);  // 上传失败时调用 reject
          });
        });
      }else{
        return new Promise((resolve, reject) => {
          self.uploadIdShardingFolder(file)
            .then(() => {
              resolve(); // 上传成功，继续下一个文件
            })
            .catch(error => {
              reject(error); // 上传失败，触发 reject
            });
        });
      }
    },


    async handleFolderUpload(event) {
      let self = this
      const files = Array.from(event.target.files);
      this.totalFiles = files.length;
      this.uploadedCount = 0;
      this.progressFlag = true;
      let failCount = 0;
      for (let i = 0; i < files.length; i++) {
        const file = files[i];
        const fileExtension = file.name.slice(file.name.lastIndexOf('.')).toLowerCase();

        // 校验文件格式
        if (!self.acceptType.includes(fileExtension)) {
          failCount++;
          this.$message.error('文件格式不支持: ' + file.name);
          continue;
        }

        try {
          await this.uploadFolderFile(
            {
              data: null,
              file: file
            }
          );
          this.uploadedCount++;
          this.updateProgress();
        } catch (error) {
          failCount++;
        }
      }
      setTimeout(function () {
        self.$message.success('上传完成！成功：' + self.uploadedCount+'，失败：' + failCount);
        self.progressFlag = false;
        self.progressPercent = 0;
      }, 500);
    },
    updateProgress() {
      if (this.uploadedCount === this.totalFiles) {
        this.progressPercent = 100;
      } else {
        this.progressPercent = Math.min(100, Math.floor((this.uploadedCount / this.totalFiles) * 100));
      }
/*
      if (this.uploadedCount % 10 === 0 || this.uploadedCount === this.totalFiles) {
        this.$message.success(`已上传 ${this.uploadedCount} 个文件`);
      } */
    },

    // 初始化分片上传
    uploadIdShardingFolder(file) {
      let self = this
      return new Promise((resolve, reject) => {
        file._shardCount = Math.ceil(file.size / this.partSize); // 总片数
        file.uploaded = 0;

        const formData = new FormData();
        formData.append('fileName', file.name);

        // 大文件上传初始化, 返回 uploadId
        axios({
          url: process.env.VUE_APP_BASE_API + "/common/initUpload",
          method: 'post',
          data: formData,
          headers: {
            'Authorization': 'Bearer ' + getToken(),
            'Content-Type': 'multipart/form-data'
          },
        }).then(res => {
          if (res.status === 200) {
            file.uploadId = res.data.uploadId;
            file.chunkId = 0;
            this.uploadByChunkFolder(file)
              .then(() => resolve())  // 全部分片上传完成，返回 resolve
              .catch(error => reject(error));  // 分片上传出错，返回 reject
          } else {
            reject(new Error('初始化上传失败'));
          }
        }).catch(error => {
          reject(error);
        });
      });
    },

    // 分片上传逻辑
    uploadByChunkFolder(file) {
      let self = this
      return new Promise((resolve, reject) => {
        const uploadChunk = (chunkId) => {
          const _start = chunkId * this.partSize;
          const _end = Math.min(file.size, _start + this.partSize);
          const fileData = file.slice(_start, _end);

          const form1 = new FormData();
          form1.append('chunkFile', fileData);
          form1.append('uploadId', file.uploadId);
          form1.append('chunkId', (chunkId + 1).toString());
          form1.append('shardCount', file._shardCount.toString());

          axios({
            url: process.env.VUE_APP_BASE_API + "/common/uploadChunk",
            method: 'post',
            data: form1,
            headers: {
              'Authorization': 'Bearer ' + getToken(),
              'Content-Type': 'multipart/form-data'
            },
          }).then(response => {
            if (response.status === 200) {
              file.uploadedNum++;
              // const percent = Math.floor((file.uploadedNum / file._shardCount) * 100);
              // this.percentageSend(file, percent);
              if (chunkId + 1 < file._shardCount) {
                // 继续上传下一片
                uploadChunk(chunkId + 1);
              } else {
                // 所有分片上传完成，合并文件
                this.mergeFileFolder(file).then(() => resolve()).catch(error => reject(error));
              }
            } else {
              reject(new Error('分片上传失败'));
            }
          }).catch(error => {
            reject(error);
          });
        };

        // 从第0块开始上传
        uploadChunk(0);
      });
    },

    // 合并文件
    mergeFileFolder(file) {
      let self = this
      return new Promise((resolve, reject) => {
        const form2 = new FormData();
        form2.append('uploadId', file.uploadId);
        form2.append('fileName', file.name);

        axios({
          url: process.env.VUE_APP_BASE_API + "/common/mergeFile",
          method: 'post',
          data: form2,
          headers: {
            'Authorization': 'Bearer ' + getToken(),
            'Content-Type': 'multipart/form-data'
          },
        }).then(res => {
          if (res.status === 200) {
            self.handleResult(res,file);
            resolve(); // 合并成功
          } else {
            reject(new Error('文件合并失败'));
          }
        }).catch(error => {
          reject(error);
        });
      });
    }
  }
};
</script>
