<template>
  <div class="form_upload">
    <ul class="form_upload_img_box">
      <fileList-view
        ref="fileListView"
        :file="fileList"
        :fileListStyle="formUploadStyle"
        :fileListDraggable="uploadItemDraggable"
        :hideUploadDelete="hideUploadDelete"
        @form-upload-event="formUploadRemove"
        @dragend="dragend"
      >
        <template #fileListSlot>
          <el-upload
            v-bind="$attrs"
            v-on="$listeners"
            ref="elUpload"
            class="form_upload_add"
            action="#"
            list-type="picture"
            :auto-upload="false"
            :disabled="disabled"
            :style="formUploadStyle"
            :accept="accept"
            :show-file-list="false"
            :file-list="fileList"
            :limit="limit"
            :multiple="multiple"
            :data="fileUploadPramas"
            :before-upload="formBeforeUpload"
            :on-error="formOnError"
            :on-change="onChangeFileUpLoad"
          >
            <div class="form_upload_add_box">
              <i slot="default" class="el-icon-plus upload_icon"></i>
              <div class="upload__text">{{ uploadText }}</div>
            </div>
          </el-upload>
        </template>
      </fileList-view>
    </ul>

    <div v-if="!!formUploadTips" class="upload_tip">{{ formUploadTips }}</div>

    <dialog-view
      width="600px"
      :visible.sync="popupView"
      dialog-title="上传进度条"
      :buttons="[{ id: 'close', text: '关闭' }]"
    >
      <template #dialogBody>
        <div style="padding: 20px 15px">
          <ul class="progress_bar">
            <li v-for="(item, index) in layerFileList" :key="index">
              <div class="img"></div>
              <div class="info">
                <div class="file-name-item">
                  <span>{{ index + 1 }}.{{ item.name }}</span>
                  <span>
                    {{ filterSize(item.size) }}
                  </span>
                </div>
                <el-progress
                  :text-inside="true"
                  :stroke-width="20"
                  :percentage="item.percentage"
                  :color="color"
                ></el-progress>
              </div>
              <div class="speed">{{ item.speed || 0 }}/s</div>
            </li>
          </ul>
        </div>
      </template>
    </dialog-view>
  </div>
</template>

<script>
import fileListView from './baseView/fileList'
import DialogView from './baseView/dialog'
const OSS = require('ali-oss')

export default {
  name: 'file-Upload-view',
  components: {
    DialogView,
    fileListView
  },
  props: {
    /*只能上传一个文件，这个参数是自定义的,
        因为使用limit:1限制只能上传一个的话，
        再次上传不会替换原图，使用singleUpload则会
      */
    singleUpload: {
      type: Boolean
    },
    //是否可拖拽
    uploadItemDraggable: {
      type: Boolean
    },
    //  接受上传的文件类型
    accept: {
      type: String,
      default: '.jpg,.png,.gif'
    },
    // 上传提示文字
    formUploadTips: {
      type: String
    },
    // 是否禁用
    disabled: {
      type: Boolean
    },
    // 是否隐藏删除功能按钮
    hideUploadDelete: {
      type: Boolean
    },
    // 是否支持多选文件
    multiple: {
      type: Boolean
    },
    // 最大允许上传个数
    limit: {
      type: Number
    },
    // 上传的文件列表
    files: {
      type: Array,
      default: () => []
    },
    // 文件列表的样式
    formUploadStyle: {
      type: [Object],
      default: () => ({
        height: '80px',
        width: '80px'
      })
    },
    // 上传框内的文字
    uploadText: {
      type: String,
      default: '上传图片'
    },
    // 上传时附带的额外参数
    uploadPramas: {
      type: Object,
      default: () => ({})
    },
    // 接口名称，用于拼接上传时附带的额外参数
    apiName: {
      type: String
    },

    // 必选参数，上传的地址
    action: {
      require: true,
      type: String,
      default: '/attachment/upload'
    },

    // 是否开启大文件上传
    isLargeFile: {
      type: Boolean,
      default: false
    },
    // 是否开启遮罩层
    isMaskLayer: {
      type: Boolean,
      default: true
    },
    // 切片大小
    partSize: {
      type: Number,
      default: 1024 * 1024
    }
  },
  computed: {
    // 上传时附带的额外参数
    fileUploadPramas() {
      let obj = {}
      if (this.apiName) obj.bizCode = this.apiName
      return Object.assign({}, obj, this.uploadPramas)
    }
  },
  data() {
    return {
      msg: '',
      loading: false,
      oldData: null, // 开始排序时按住的旧数据
      newData: null, // 拖拽过程的数据
      fileList: [],

      layerFileList: [],
      file: null,
      parallel: 5,
      checkpoints: {},
      credentials: null,
      fileMap: {},
      map_max_key: 0,
      popupView: false,
      color: [
        { color: '#f56c6c', percentage: 20 },
        { color: '#e6a23c', percentage: 40 },
        { color: '#6f7ad3', percentage: 60 },
        { color: '#1989fa', percentage: 80 },
        { color: '#5cb87a', percentage: 100 }
      ]
    }
  },
  watch: {
    files(file) {
      this.fileList = file || []
    }
  },
  methods: {
    /**
     * @description file upload
     * @author MR.Lin
     */
    onChangeFileUpLoad(file, layerFileList) {
      if (this.isLargeFile) {
        layerFileList.forEach(item => {
          item.client = null
          item.abortCheckpoint = false
        })
        this.layerFileList = layerFileList
        this.file = file.raw
        this.popupView = this.isMaskLayer

        setTimeout(() => {
          this.onLargeFileUpload()
        }, 200)
      } else {
        let formData = new FormData()
        formData.append('file', file.raw)
        this.$api.post(this.action, formData).then(res => {
          this.formUploadSuccess(res)
        })
      }
    },
    /**
     * @description Large file upload
     * @author MR.Lin
     */
    async onLargeFileUpload() {
      if (!this.layerFileList.length) {
        this.$message.error('请选择文件后在提交')
        return
      }
      this.layerFileList.forEach(async item => {
        const getAliyunOSS = await this.getAliYunOss()
        const { accessKeyId, accessKeySecret, securityToken, bucket, region } =
          this.credentials
        item.client = new OSS({
          secure: true,
          accessKeyId: accessKeyId,
          accessKeySecret: accessKeySecret,
          stsToken: securityToken,
          bucket,
          region
        })
        if (!getAliyunOSS.pass) {
          return this.$message.error('获取oss上传凭证异常')
        } else {
          await this.ossUpload(item)
        }
      })
    },
    /**
     * @description get AliYunOss
     * @author MR.Lin
     */
    async getAliYunOss() {
      let res = await this.$api.get('/attachment/getStsToken').then(res => {
        return res
      })
      let isPass = {
        pass: true
      }
      if (res) {
        this.credentials = res
      } else {
        isPass.pass = false
      }

      return isPass
    },

    async ossUpload(item) {
      let isPass = {
        pass: true,
        filePath: ''
      }
      try {
        const { raw, percentage } = item
        item.partSize = 0
        if (percentage < 100) {
          const file = raw
          const path = file.name
          await item.client
            .multipartUpload(path, file, {
              parallel: this.parallel,
              partSize: this.partSize,
              progress: async (p, checkpoint, res) => {
                await this.onUploadProgress(item, p, checkpoint, res, path)
              }
            })
            .then(res => {
              let obj = {
                name: res.name,
                url: res.res.requestUrls[0]
              }
              this.formUploadSuccess(obj)
            })
            .catch(async err => {})
        }
      } catch (e) {
        isPass = {
          ...e,
          pass: false,
          filePath: ''
        }
      }
      return isPass
    },

    onUploadProgress(item, p, checkpoint, res, path) {
      if (checkpoint) {
        this.checkpoints[checkpoint.uploadId] = checkpoint
        item.tempCheckpoint = checkpoint
        item.abortCheckpoint = checkpoint
        item.upload = checkpoint.uploadId
        item.speed = this.handle_network_speed(res, this.partSize, p)
      }

      item.uploadName = path
      item.percentage = Number((p * 100).toFixed(2))
    },
    handle_network_speed(res, partSize, p) {
      const spend_time = res.rt / 1000 //单位s
      const end_time = new Date(res.headers.date).getTime()
      const start_time = end_time - spend_time
      let network_speed = parseInt(partSize / spend_time)
      if (p === 0) network_speed = 0
      if (network_speed === 0) {
      } else {
        this.handle_network_speed_change(start_time, end_time, network_speed)
      }
      return network_speed ? this.filterSize(network_speed) : 0
    },
    async handle_network_speed_change(start_time, end_time, network_speed) {
      if (start_time - this.map_max_key >= 10000) {
        this.fileMap = {}
      }
      for (let i = start_time; i <= end_time; i++) {
        const value = await this.fileMap[i]
        if (value) {
          await this.change(i, value + network_speed)
        } else {
          await this.change(i, network_speed)
        }
      }
    },
    async change(i, value) {
      this.fileMap[i] = value
      this.map_max_key = i
    },
    filterSize(size) {
      if (!size) return '-'
      if (size < this.pow1024(1)) return size + ' B'
      if (size < this.pow1024(2))
        return (size / this.pow1024(1)).toFixed(2) + ' KB'
      if (size < this.pow1024(3))
        return (size / this.pow1024(2)).toFixed(2) + ' MB'
      if (size < this.pow1024(4))
        return (size / this.pow1024(3)).toFixed(2) + ' GB'
      return (size / this.pow1024(4)).toFixed(2) + ' TB'
    },
    pow1024(num) {
      return Math.pow(1024, num)
    },

    // 重置数据
    resetData() {
      this.fileList = []
      this.$refs.fileListView.resetData()
      console.log('重置数据', this.fileList)
    },
    // 上传成功
    formUploadSuccess(res, file, fileList) {
      console.log('上传成功', res)
      this.loading = false
      if (this.singleUpload) {
        this.fileList = [res]
      } else {
        this.fileList.push(res)
      }
      this.popupView = false
      this.$emit('form-upload-event', this.fileList)
    },
    // 上传失败
    formOnError(err, file, fileList) {
      console.log(err)
      let errorTips = JSON.parse(err.message).message || '上传失败,请重试!'
      this.$api.error(errorTips)
      this.loading = false
      this.$emit('form-on-error', err, file, fileList)
    },
    // 上传文件之前的钩子
    formBeforeUpload(file) {
      this.loading = true
      // return false
      this.$emit('form-before-upload', file)
      return false
    },
    // 删除事件
    formUploadRemove(fileList) {
      this.$emit('form-upload-event', fileList)
    },
    dragend(fileList) {
      console.log('拖拽完成', fileList)
      this.fileList = fileList
    }
  }
}
</script>
<style lang="less" scoped>
.form_upload .form_upload_add:hover {
  border: 1px dashed #eee;
}
.clamp2 {
  word-break: break-all;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2; /* 这里是超出几行省略 */
  overflow: hidden;
}
.form_upload {
  display: flex;
  position: relative;
  .upload_tip {
    position: absolute;
    left: 0;
    bottom: -18px;
    color: rgba(0, 0, 0, 0.65);
    font-size: 12px;
  }
  .form_upload_add {
    // height:60px;
    // width:60px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 5px;
    border: 1px dashed #dcdfe6;
    margin: 0 10px 10px 0;
    .form_upload_add_box {
      height: 48px;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: space-between;
      .upload_icon {
        font-size: 18px;
        font-weight: bold;
      }
      .upload__text {
        font-size: 12px;
        line-height: 20px;
        color: #bfbfbf;
      }
    }
  }
}
.progress_bar {
  li {
    display: flex;
    align-items: center;
    font-size: 16px;
    .img {
      display: none;
      min-width: 60px;
      width: 60px;
      height: 60px;
      border-radius: 5px;
      margin-right: 15px;
    }
    .info {
      flex: 1;
      overflow: hidden;
      .file-name-item {
        line-height: 1.2;
        margin-bottom: 5px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        span {
          white-space: nowrap;
          text-overflow: ellipsis;
          overflow: hidden;
          display: inline-block;
          margin-right: 10px;
          max-width: 70%;
        }
      }
    }
    .speed {
      margin-left: 15px;
    }
  }
}
</style>
