<!--
 * @Author: zhangyu
 * @Date: 2019-11-28 10:05:28
 * @Info: 
 * @LastEditTime : 2020-01-03 15:31:04
 -->
<template>
  <div id="global-uploader">

    <!-- 上传 -->
    <uploader
        ref="uploader"
        :options="options"
        :fileStatusText="fileStatusText"
        :autoStart="false"
        @file-added="onFileAdded"
        @file-success="onFileSuccess"
        @file-progress="onFileProgress"
        @file-error="onFileError"
        @file-removed="onFileRemoved"
        class="uploader-app">
      <uploader-unsupport></uploader-unsupport>

      <uploader-btn id="global-uploader-btn" :attrs="attrs" ref="uploadBtn">选择文件</uploader-btn>

      <uploader-list v-show="panelShow">
        <div class="file-panel" slot-scope="props" :class="{'collapse': collapse}">
          <div class="file-title">
            <h2>文件列表</h2>
            <div class="operate">
              <el-button @click="fileListShow" style="color:#999;" type="text" :title="collapse ? '折叠':'展开' ">
                <i class="iconfont" :class="collapse ? 'icon-bottom': 'icon-top'"></i>
              </el-button>
              <el-button @click="close" style="color:#999;" type="text" title="关闭">
                <i class="iconfont icon-guanbi"></i>
              </el-button>
            </div>
          </div>

          <ul class="file-list" v-show="collapse">
            <el-scrollbar style="height:100%;">
              <li v-for="file in props.fileList" :key="file.id">
                <uploader-file :class="'file_' + file.id" ref="files" :file="file" :list="true"></uploader-file>
              </li>
              <div class="no-file" v-if="!props.fileList.length"><i class="iconfont icon-wushuju"></i> 暂无待上传文件</div>
            </el-scrollbar>
          </ul>
        </div>
      </uploader-list>

    </uploader>

  </div>
</template>

<script>
/**
 *   全局上传插件
 *   调用方法：Bus.$emit('openUploader', {}) 打开文件选择框，参数为需要传递的额外参数
 *   监听函数：Bus.$on('fileAdded', fn); 文件选择后的回调
 *            Bus.$on('fileSuccess', fn); 文件上传成功的回调
 */

import Bus from '@/utils/bus.js';
import SparkMD5 from 'spark-md5';
import { getUploadId, mergeMultipart } from '@/api/uploader';
import request from "@/api/model/file.js";
import { mapGetters, mapMutations } from 'vuex'

export default {
  data() {
    return {
      fileStatusText: {
        success: '成功',
        error: '失败',
        uploading: '上传中',
        paused: '暂停',
        waiting: '等待'
      },
      options: {
        target: '/image-service/common/multipart_upload',
        chunkSize: 1*1024*1024,
        fileParameterName: 'file',
        maxChunkRetries: 3,
        testChunks: false,   //是否开启服务器分片校验
        // 服务器分片校验函数，秒传及断点续传基础
        // checkChunkUploadedByResponse: function (chunk, message) {
        //   let objMessage = JSON.parse(message);
        //   if (objMessage.skipUpload) {
        //     return true;
        //   }

        //   return (objMessage.uploaded || []).indexOf(chunk.offset + 1) >= 0
        // },
        // headers: {
          // Authorization: Ticket.get() && "Bearer " + Ticket.get().access_token
        // },
        query: this.getFileQuery
      },
      attrs: {
        // accept: ACCEPT_CONFIG.getAll()
      },
      panelShow: false,   //选择文件后，展示上传panel
      collapse: false,
    }
  },
  mounted() {
    Bus.$on('openUploader', (query, file) => {
      if(!this.uploaderList.some(item => {return item.name === file.name})){
        if(query.uploadId){
        // this.params = query || {};
          file.uploadId = query.uploadId
          file.modelId = query.modelId? query.modelId: ''
          this.uploader.addFile(file)
          this.setUploaderList(this.uploader.files.map(item => {
            item.modelId = item.file.modelId?item.file.modelId:''
            return item
          }))
        } else {
          getUploadId({
            systemId: query.systemId?query.systemId:'revit', 
            secret: query.secret?query.secret:'63afbef6906c342b', 
            overwrite: query.overwrite? query.overwrite: false,
            key: file.name
          }, res => {
            if(res.UploadId){
              // this.params = Object.assign(query, { uploadId: res.UploadId }) || {};
              file = Object.assign(file, {
                ...query,
                uploadId: res.UploadId 
              })
              this.uploader.addFile(file)
              this.setUploaderList(this.uploader.files.map(item => {
                item.modelId = item.file.modelId?item.file.modelId:'';
                return item
              }))
            } else {
              this.$message.error(`请求分片上传接口失败！`);
            }
          })
        }
      }else {
        this.$message.error(`该文件在上传列表中已存在！`);
      }

      // if (this.$refs.uploadBtn) {
      //   document.getElementById('global-uploader-btn').click();
      //   // $('#global-uploader-btn').click();
      // }
    });
  },
  computed: {
    ...mapGetters('layout', ['uploaderList']),
    //Uploader实例
    uploader() {
      return this.$refs.uploader.uploader;
    }
  },
  methods: {
    ...mapMutations('layout', ['setUploaderList']),
    onFileAdded(file) {
      this.panelShow = true;
      this.collapse = true;
      this.computeMD5(file);

      Bus.$emit('fileAdded');
    },
    onFileProgress(rootFile, file, chunk) {
      console.log(`上传中 ${file.name}，chunk：${chunk.startByte / 1024 / 1024} ~ ${chunk.endByte / 1024 / 1024}`)
    },
    onFileSuccess(rootFile, file, response, chunk) {
      let res = JSON.parse(response);

      // 服务器自定义的错误（即虽返回200，但是是错误的情况），这种错误是Uploader无法拦截的
      if (!res.Result || res.Result != 'success') {
        this.$message({ message: res.ResultMsg?res.ResultMsg:'上传失败！', type: 'error' });
        // 文件状态设为“失败”
        this.statusSet(file.id, 'failed');
        return
      }
      // 如果服务端返回需要合并
      console.log('获取分片数',file.chunks.length)
      if (parseInt(res.TotalCount) >= file.chunks.length) {
        // 文件状态设为“合并中”
        this.statusSet(file.id, 'merging');
        if(file.file.modelId){
          //模型文件合并专用接口
          request.mergeModelFile({ ModelId: file.file.modelId, UploadId: file.file.uploadId, Md5: file.file.md5 }, res => {
            Bus.$emit('modelStatusChange')
            // 文件合并成功
            Bus.$emit('fileSuccess');
            this.statusRemove(file.id);
          })
        } else {
          mergeMultipart({
            systemId: file.file.systemId?file.file.systemId:'revit', 
            secret: file.file.secret?file.file.secret:'63afbef6906c342b', 
            uploadId: file.file.uploadId
          }, res => {
            // 文件合并成功
            Bus.$emit('fileSuccess');
            this.statusRemove(file.id);
          })
        }

      // 不需要合并
      } else {
        Bus.$emit('fileSuccess');
        console.log('上传成功');
      }
    },
    onFileError(rootFile, file, response, chunk) {
      this.$message({
        message: response,
        type: 'error'
      })
    },
    onFileRemoved(file) {
      this.uploaderList.splice(this.uploaderList.findIndex(item => {
        return item.id == file.id;
      },1))
      this.setUploaderList(this.uploaderList);
      if(file.modelId && !file.isComplete()) {
        request.deleteModelFileList({Id: file.modelId, Force: true}, res => {
          console.log('删除错误模型:',file.modelId);
          Bus.$emit('modelStatusChange')
        })
      }
    },

    /**
     * 计算md5，实现断点续传及秒传
     * @param 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 = 1 * 1024 * 1024;
      let chunks = Math.floor(file.size / chunkSize);
      let spark = new SparkMD5.ArrayBuffer();

      // 文件状态设为"计算MD5"
      this.statusSet(file.id, 'md5');
      file.pause();

      loadNext();

      fileReader.onload = (e => {
        spark.append(e.target.result);

        if (currentChunk < chunks) {
          currentChunk++;
          loadNext();

          // 实时展示MD5的计算进度
          this.$nextTick(() => {
            document.getElementsByClassName(`myStatus_${file.id}`)[0].innerText = '校验MD5 '+ ((currentChunk/chunks)*100).toFixed(0)+'%'
            // $(`.myStatus_${file.id}`).text('校验MD5 '+ ((currentChunk/chunks)*100).toFixed(0)+'%')
          })
        } else {
          let md5 = spark.end();
          file.file.md5 = md5;
          this.computeMD5Success(md5, file);
          console.log(`MD5计算完毕：${file.name} \nMD5：${md5} \n分片：${chunks} 大小:${file.size} 用时：${new Date().getTime() - time} ms`);
        }
      });

      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上
      console.log(this.uploader)
      // Object.assign(this.uploader.opts, {
      //   query: {
      //     uploadId: file.file.uploadId,
      //     md5
      //   }
      // })

      // file.uniqueIdentifier = this.params.uploadId;
      file.resume();
      this.statusRemove(file.id);
    },

    //获取文件参数
    getFileQuery(file, chunk) {
      return {
        uploadId: file.file.uploadId,
        md5: file.file.md5
      }
    },

    fileListShow() {
      this.collapse = this.collapse? false: true;

      // let $list = $('#global-uploader .file-list');
      // let $list = document.querySelector('#global-uploader .file-list')

      // if ($list.style.display == '' && $list.style.display == 'none') {
      //   $list.style.display == 'block';//显示
      //   this.collapse = false;
      //   // $list.slideUp();//隐藏
      //   // this.collapse = true;
      // } else {
      //   $list.style.display == 'none';//隐藏
      //   this.collapse = true;
      //   // $list.slideDown();//显示
      //   // this.collapse = false;
      // }
    },
    close() {
      if(this.uploader.isUploading()) {
        this.$alert(
          "您有文件正在上传，关闭会中断上传，是否继续？",
          "提示",
          {
            confirmButtonText: "确定",
            callback: action => {
              console.log(action);
              if (action == "confirm") {
                this.uploader.cancel();
                this.panelShow = false;
                this.uploader.files = [];
                this.setUploaderList([]);
                Bus.$emit('modelStatusChange')
              } else { }
            }
          }
        );
      } else {
        this.uploader.cancel();
        this.panelShow = false;
      }
    },

    /**
     * 新增的自定义的状态: 'md5'、'transcoding'、'failed'
     * @param id
     * @param status
     */
    statusSet(id, status) {
      let statusMap = {
        md5: {
          text: '校验MD5',
          bgc: '#fff'
        },
        merging: {
          text: '合并中',
          bgc: '#e2eeff'
        },
        transcoding: {
          text: '转码中',
          bgc: '#e2eeff'
        },
        failed: {
          text: '上传失败',
          bgc: '#e2eeff'
        }
      }

      this.$nextTick(() => {
        let node = document.createElement("P");
        let att = document.createAttribute("class");
        att.value = `status_style myStatus_${id}`;
        let text = document.createTextNode(`${statusMap[status].text}`);
	      node.appendChild(text);
        node.setAttributeNode(att);
        node.style.backgroundColor = `${statusMap[status].bgc}`
        document.querySelector(`.file_${id} .uploader-file-status`).appendChild(node);

        // $(`<p class="myStatus_${id}"></p>`).appendTo(`.file_${id} .uploader-file-status`).css({
        //   'position': 'absolute',
        //   'top': '0',
        //   'left': '0',
        //   'right': '0',
        //   'bottom': '0',
        //   'zIndex': '1',
        //   'backgroundColor': statusMap[status].bgc
        // }).text(statusMap[status].text);
      })
    },
    statusRemove(id) {
      this.$nextTick(() => {
        let dom = document.getElementsByClassName(`myStatus_${id}`)[0];
        dom.parentNode.removeChild(dom);
      })
    },

    error(msg) {
      this.$notify({
        title: '错误',
        message: msg,
        type: 'error',
        duration: 2000
      })
    }
  },
  watch: {},
  destroyed() {
    Bus.$off('openUploader');
  },
  components: {}
}
</script>

<style scoped lang="scss">
#global-uploader {
  position: fixed;
  z-index: 101;
  top: auto;
  right: 15px;
  bottom: 0;

  .uploader-app {
    width: 633px;
  }

  .file-panel {
    background-color: #fff;
    border: 1px solid #e2e2e2;
    border-radius: 7px 7px 0 0;
    box-shadow: 0 0 10px rgba(0, 0, 0, .2);

    .file-title {
      display: flex;
      height: 40px;
      line-height: 40px;
      padding: 0 15px;
      border-bottom: 1px solid #ddd;
      h2{
        font-size: 14px;
        font-style: normal;
        cursor: text;
        font-weight: 200;
        color: #666;
      }

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

    .file-list {
      position: relative;
      height: 350px;
      overflow-x: hidden;
      overflow-y: auto;
      background-color: #fff;
      /deep/ .el-scrollbar__wrap{
        overflow-x: hidden;
      }

      > li {
        background-color: #fff;
      }
      /deep/ .uploader-file-info i{
        display: none;
      }
      /deep/ .status_style{
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 1;
      }
    }

    // &.collapse {
    //   .file-title {
    //     background-color: #E7ECF2;
    //   }
    // }
  }

  .no-file {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-size: 16px;
    text-align: center;
  }

  /deep/.uploader-file-icon {
    &:before {
      content: '' !important;
    }

    // &[icon=image] {
    //   background: url(./images/image-icon.png);
    // }
    // &[icon=video] {
    //   background: url(./images/video-icon.png);
    // }
    // &[icon=document] {
    //   background: url(./images/text-icon.png);
    // }
  }

  /deep/ .uploader-file-actions > span {
    margin-right: 6px;
  }
}
/* 隐藏上传按钮 */
#global-uploader-btn {
  position: absolute;
  clip: rect(0, 0, 0, 0);
}
</style>