<template>
  <div>
    <a-row type="flex">
      <a-col flex="auto">
        <a-upload
          class="upload-wrapper"
          :showUploadList="false"
          :disabled="maxFile && tableDate.length >= maxFile"
          :accept="accept"
          :before-upload="beforeUpload"
          :customRequest="customRequestUpload"
          :multiple="true"
          @change="handleChange">
          <!-- <a-input style="width:100%" :value="fileList.map(item=> item.name).join('、')" /> -->
          <a-button :disabled="maxFile && tableDate.length >= maxFile">
            <a-icon type="upload" /> 文档上传
          </a-button>
        </a-upload>
      </a-col>
      <a-col flex="100px" v-if="false" >
        <a-button @click="customRequestUpload">
          <a-icon type="upload" /> 文档上传
        </a-button>
      </a-col>
    </a-row>
    <div class="result-wrapper" :style="{minHeight: boxHeight + 'px'}" >
      <div class="item" v-for="(file,idx) in tableDate" :key="file.uid">
        <div class="content">
          <div class="body">
            <div class="fileName"> {{ file.fileName }}</div>
            <div>
              <a-space>
                <a-button v-if="canDownload" type="link" style="padding: 0" @click="handleDownload(file)">下载</a-button>
                <a-popconfirm
                  title="是否确认删除该文件?"
                  @confirm="handleDelete(file, idx)"
                >
                  <a-icon style="margin-left:10px; cursor: pointer;" type="delete" />
                </a-popconfirm>
              </a-space>
            </div>
            <a-progress
              class="progress"
              v-if="autoUpload"
              :percent="file.percentage"
              :strokeWidth="3"
              :showInfo="false" />
          </div>
        </div>
        <slot name="extra" :idx="idx" />
      </div>
    </div>
  </div>
</template>

<script>
import SparkMD5 from 'spark-md5'
import { postAction,downloadFile2 } from '@/api/manage'
import { verifyFileExist } from '@/api/fileapi'


export default {
  name: 'SliceUpload',
  props: {
    boxHeight: {
      type: Number,
      default: 300
    },
    // eslint-disable-next-line vue/require-default-prop
    maxFile: Number,
    accept: {
      type: String,
      default: '.jpg,.png,.doc,.docx,.pdf,.txt,.jpeg'
    },
    autoUpload: { // 自动上传
      type: Boolean,
      default: true
    },
    canRepeat: { // 是否可上传重复的文件
      type: Boolean,
      default: true
    },
    canDownload: { // 是否显示下载按钮
      type: Boolean,
      default: false
    },
    dataLinkType: { 
      type: String,
      default: '1'
    }
  },
  data () {
    return {
      fileMD5: {},
      isStop: false,
      fileList: [],
      tableDate: [],
      
    }
  },
  methods: {
    stop (record) {
      this.isStop = true
      record.uploadStatus = 0
    },
    start (record, index) {
      const file = this.fileList[index].originFileObj
      const currentRow = this.tableDate.find((row) => row.uid === file.uid)
      this.isStop = false
      record.uploadStatus = 1
      this.uploadByPieces({
        file, // 文件信息
        currentRow,
        success: (data) => {
          record.percentage = 100
        },
        error: (e) => {
          record.percentage = 0
        }
      })
    },

    // 从外部设置已有文件列表
    setFileList (fileList) {
      this.tableDate = fileList
    },

    deleteFile () {
      this.fileList = []
      this.tableDate = []
    },
    getFileList () {
      return this.tableDate
    },
    bytesToSize (bytes) {
      if (bytes === 0) return '0 B'
      const k = 1024
      const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
      const i = Math.floor(Math.log(bytes) / Math.log(k))
      return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i]
    },
    handleDelete (file, idx) {
      this.tableDate.splice(idx, 1)
      delete this.fileMD5[file.uid]
    },
    async beforeUpload (file) {
      if (!this.canRepeat) {
        const md5 = await this.getMd5(file, file.size)
        const find = Object.values(this.fileMD5).find(v => {
          return v === md5
        })
        if (!this.canRepeat && find) {
          this.$message.warning(`文件重复，重复的文件为：${file.name}`)
          throw new Error('file repeat')
        } else {
          this.fileMD5[file.uid] = md5
        }
      }

      this.fileList.push(file)
      // uploadStatus 上传状态 1开始 0暂停 2完成
      this.tableDate.push({
        fileData: file,
        uid: file.uid,
        fileName: file.name,
        size: file.size,
        type: file.type,
        percentage: 0,
        uploadStatus: 1,
        remarks: ''
      })
      if (!this.autoUpload) {
        throw new Error('not auto upload')
      }
    },
    /**
     * 自定义上传事件
     */
    customRequestUpload ({ file }) {
      // 开始执行上传逻辑
      const currentRow = this.tableDate.find((row) => row.uid === file.uid)
      if (currentRow) {
        // 当前上传进度归0
        currentRow.percentage = 0
        const _20M = 20 * 1024 * 1024
        if (file.size > _20M) { // 20M 以上分片上传
            this.uploadByPieces({ // 这里走分片上传逻辑
              file, // 文件信息
              currentRow,
              success: (data) => {
                currentRow.percentage = 100
              },
              error: (e) => {
                currentRow.percentage = 0
              }
            })
        } else { // 20M 以下直接上传
          this.uploadDirectly(currentRow, file)
        }
      }
    },
    getMd5 (file, chunkSize) {
      return new Promise((resolve, reject) => {
        const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice
        const chunks = Math.ceil(file.size / chunkSize)
        let currentChunk = 0
        const spark = new SparkMD5.ArrayBuffer() // 追加数组缓冲区。
        const fileReader = new FileReader() // 读取文件
        fileReader.onload = function (e) {
          spark.append(e.target.result)
          currentChunk++
          if (currentChunk < chunks) {
            loadNext()
          } else {
            const md5 = spark.end() // 完成md5的计算，返回十六进制结果。
            resolve(md5)
          }
        }

        fileReader.onerror = function (e) {
          reject(e)
        }

        function loadNext () {
          const start = currentChunk * chunkSize
          let end = start + chunkSize
          end > file.size && (end = file.size)
          fileReader.readAsArrayBuffer(blobSlice.call(file, start, end))
        }
        loadNext()
      })
    },

    buildFileFormData (fileName, fileSize, md5Value, shareTotal, shareIndex, file, fileShare) {
      const formData = new FormData()
      formData.append('fileShare', fileShare)
      formData.append('fileName', fileName)
      formData.append('fileSuffix', fileName.substring(fileName.lastIndexOf('.')))
      formData.append('fileSize', fileSize)
      formData.append('md5Value', md5Value)
      formData.append('shareTotal', shareTotal)
      formData.append('shareIndex', shareIndex)
      formData.append('currShareM5', currShareM5)
      formData.append('file', file)
      formData.append("dataLinkType",this.dataLinkType);
      return formData
    },

    /**
     * 直接上传
     * @param {File} file file文件
     */
    async uploadDirectly (currentRow, file) {
      let fileMD5Value = this.fileMD5[file.uid]
      if (!fileMD5Value) {
        fileMD5Value = await this.getMd5(file, file.size)
      }
      try {
        const res = await verifyFileExist({ fileMD5Value })
        if (res.exist) { // 跳过文件验证逻辑
          currentRow.percentage = 100
          currentRow.uploadStatus = 2
          currentRow.result = res
        } else { // 未存在，走上传逻辑
          const formData = this.buildFileFormData(file.name, file.size, fileMD5Value, 0, 0, file, false)
          const url = '/fileDataLink/uploadFile'
          try {
            const res = await postAction(url, formData)
            if (res) {
              currentRow.percentage = 100
              currentRow.uploadStatus = 2
              currentRow.result = {
                fileId: res.id
              }
            } else {
              currentRow.percentage = 0
              currentRow.uploadStatus = 1
            }
          } catch (error) {
            console.error(error)
          }
        }
      } catch (error) {
        console.error(error)
        this.$message.error('获取文件是否已上传状态失败')
      }
    },

    // 断点分片上传
    uploadByPieces ({ file, currentRow, success, error }) {
      // const that = this
      // 上传过程中用到的变量
      var slicingSize = null
      if (file.size <= 20971520) {
        // 20M以内，单个切片大小设置为2MB
        slicingSize = 2 * 1024 * 1024 // 切片大小 单位MB
      } else if (file.size <= 524288000) {
        // 500M以内，单个切片大小设置为5MB
        slicingSize = 5 * 1024 * 1024 // 切片大小 单位MB
      } else {
        // 500M以外，单个切片大小设置为10MB
        slicingSize = 10 * 1024 * 1024 // 切片大小 单位MB
      }
      const sumSlicingCount = Math.ceil(file.size / slicingSize) // 总片数

      currentRow.remarks = '正在获取hash值...'
      this.getMd5(file, slicingSize)
        .then((res) => {
          this.fileMD5[file.uid] = res

          currentRow.remarks = ''
          this.readFileMD5(file, currentRow, slicingSize, sumSlicingCount, success, error)
        })
        .catch((e) => {
          console.error('MD5计算错误', e)
        })
    },
    // 得到某一片的分片 file 总文件； currentIndex 当前切片数，按索引计算； slicingSize 切片大小
    getSlicingInfo (file, currentIndex, slicingSize) {
      const start = currentIndex * slicingSize
      const end = Math.min(file.size, start + slicingSize)
      const slicingInfo = file.slice(start, end)
      return slicingInfo
    },
    // 开始执行切片上传
    readFileMD5 (file, currentRow, slicingSize, sumSlicingCount, success, error) {
      // 检查文件有没有上传过的状态
      verifyFileExist({ fileMD5Value: this.fileMD5[file.uid] })
        .then((res) => {
          const { exist, shareTotal, shareIndex } = res
          if (exist) {
            if (shareIndex === (shareTotal - 1)) { // 相等说之前已经上传完整
              currentRow.percentage = 100
              currentRow.uploadStatus = 2
              currentRow.result = res
            } else { // 不相等说明之前上传中断了，接着再上传
              const pross = (shareIndex + 1 / sumSlicingCount) * 100 // 计算之前上传的进度
              currentRow.percentage = Number(pross.toFixed(2))
              this.uploadSliceFile(file, currentRow, slicingSize, sumSlicingCount, shareIndex + 1, success, error)
            }
          } else { // 没有传过就从第0个分片开始上传
                this.uploadSliceFile(file, currentRow, slicingSize, sumSlicingCount, 0, success, error)
          }
        })
        .catch((e) => {
          error && error(e)
        })
    },
    /**
     * 对切片文件进行上传
     * @param {File} file
     */
    uploadSliceFile (file, currentRow, slicingSize, sumSlicingCount, currIndex, success, error) {
      if (currIndex < sumSlicingCount && !this.isStop) {
        // 得到当前需要上传的分片文件
        const currentInfo = this.getSlicingInfo(file, currIndex, slicingSize)
        const result = new File([currentInfo], currIndex, { type: file.type, lastModified: Date.now() })
        const formData = this.buildFileFormData(file.name, file.size, this.fileMD5[file.uid], sumSlicingCount, currIndex, result, true)

        // 开始上传
        const url = '/fileDataLink/uploadFile'
        postAction(url, formData).then((res) => {
          const { completed, id } = res
          if (completed) { // 已上传完毕
              currentRow.percentage = 100
              currentRow.uploadStatus = 2
              currentRow.result = {
                fileId: id
              }
          } else {
            // 每上传一个就在进度条上加数据
            const pross = ((currIndex + 1) / sumSlicingCount) * 100
            currentRow.percentage = Number(pross.toFixed(2))
            this.uploadSliceFile(file, currentRow, slicingSize, sumSlicingCount, currIndex + 1, success, error)
          }
        })
      }
    },
    handleChange (info) {
      this.fileList = [...info.fileList]
    },

    /**
     * 下载文件
     */
    handleDownload ({ fileName: filterWordName, filePath: filterWordPath }) {
      downloadFile2(filterWordName, { filterWordName, filterWordPath }, '/file/verifyFileExist')
    }
  }
}
</script>

<style lang="less" scoped>
.upload-wrapper{
  display: inline-block;
  width: calc(100% - 10px);
  /deep/.ant-upload{
    width: 100%;
  }
}

.result-wrapper {
  margin-top: 10px;
  width: 100%;
  background: #FFFFFF;
  border: 1px solid #D9DADB;
  padding: 10px;

  .item {
    display: flex;
    justify-content: space-between;
    font-size: 14px;
    font-weight: 400;
    color: #666666;
    line-height: 36px;

    .content {
      flex: 1;
      .body {
        position: relative;
        justify-content: space-between;
        padding-bottom: 4px;
        display: flex;

        .fileName {
          max-width: 400px;
          text-overflow: ellipsis;
          overflow: hidden;
          white-space: nowrap;
        }

        .progress {
          position: absolute;
          left: 0;
          bottom: 0px;
        }
      }
    }
  }
}
</style>
