<template>
  <div>
    <div v-if="!props.disabled && props.text" style="display: inline-flex;" :class="{ doing: doing || !uploadKey }"
      @click="open()">
      <slot name="button"><el-button>{{ props.text }}</el-button></slot>
    </div>
    <input ref="fileInputRef" v-if="!props.disabled" :accept="props.accept" @change="fileInputChange" type="file"
      style="position: absolute; top:-100vh;" :multiple="props.max != 1" />
    <div class="list">
      <slot :list="list">
        <div v-for="(item, index) in data" :key="index" class="item"
          :class="{ error: item.error, abort: item.abort, border: props.border }"
          :style="{ '--ps': (item.uploading?.loaded || 0) * 100 / (item.uploading?.total || 1) + '%' }">
          <div class="icon" :class="['icon-' + item.ext]"></div>
          <a v-if="item.raw" href="javascript:void(0)">{{ item.name }}</a>
          <a v-else :href="baseUrl + base + 'download?' + request.params({ url: item.url, key: uploadKey }).toString()"
            target="_blank">{{ item.name }}</a>
          <div v-if="item.uploading && !item.abort" class="btn" @click="item.uploading.abort()">取消</div>
          <div v-if="item.url && !props.disabled" @click="remove(item)" class="btn">删除</div>
        </div>
      </slot>
    </div>

  </div>
</template>
<script setup>
import { ref, onMounted, onUnmounted, computed, getCurrentInstance } from 'vue'
import { request } from '@/utils'
import { ElMessage } from 'element-plus'

const base = "/file/upload/"

const baseUrl = import.meta.env.VITE_APP_BASE_API

const { proxy } = getCurrentInstance()

const props = defineProps({
  /**
   * 上传的文件
   */
  modelValue: {
    type: [Array, String],
    required: true
  },
  /**
   * 上传的数量,0：表示无限制,1：表示单文件上传
   */
  max: {
    type: Number,
    default: 0
  },
  /**
   * 获取上传凭证的方法
   */
  getUploadKey: {
    type: Function,
    default: async () => {
      let r = await request.get("/uploadKey")
      return r.data
    }
  },
  /**
   * 上传路径前缀
   */
  prefix: {
    type: String,
    default: "default"
  },
  /**
   * 是否保存上传文件名
   * keepFilename
   */
  keepFilename: {
    type: Boolean,
    default: true
  },
  /**
   * 禁止上传（文件预览模式）
   */
  disabled: {
    type: Boolean,
    default: false
  },
  /**
   * 按钮文字
   */
  text: {
    type: String,
    default: '上传文件'
  },
  /**
   * 上传中?
   */
  uploading: {
    type: Boolean,
    default: false
  },
  /**
   * 允许上传的文件扩展名列表，默认：'' ,office:'.doc,.docx,.ppt,.pptx,.xls,.xlsx'
   */
  accept: {
    type: String,
    default: ''
  },
  /**
   * 允许上传的文件最大大小,0表示不限制，默认：0
   */
  maxSize: {
    type: Number,
    default: 0
  },
  /**
   * 并行上传数量：默认：2
   */
  threadNum: {
    type: Number,
    default: 2
  },
  /** 重试次数，默认：3 */
  retry: {
    type: Number,
    default: 3
  },
  /**
   * 分片大小，默认：5Mb
   */
  chunksize: {
    type: Number,
    default: 5 * 1024 * 1024
  },
  border: {
    type: Boolean,
    default: true
  }

})


const emit = defineEmits(["update:modelValue", "change", "update:uploading"]);

const mv = computed({
  get() {
    return props.modelValue
  },
  set(value) {
    emit("update:modelValue", value)
    emit("change", value)
  }
})

watch(mv, () => {
  if (props.max == 1) {
    data.value = []
    if (mv.value) {
      data.value = [{ url: mv.value ,ext: getExt(mv.value), name: getName(mv.value) }]
    }
  } else {
    data.value = []
    if (mv.value) {
      data.value = mv.value.map(a => ({ url: a ,ext: getExt(a), name: getName(a)}))
    }
  }
})

const uploading = computed({
  get() {
    return props.uploading
  },
  set(value) {
    emit("update:uploading", value)
  }
})

const data = ref([])

const uploadKey = ref("");

const getExt = (url) => {
  return url.substring(url.lastIndexOf(".") + 1)
}

const getName = (url) => {
  return url.substring(url.lastIndexOf("/") + 1)
}

onMounted(async () => {
  uploadKey.value = await props.getUploadKey()
  if (props.max == 1) {
    data.value = []
    if (mv.value) {
      data.value = [{ url: mv.value, ext: getExt(mv.value), name: getName(mv.value) }]
    }
  } else {
    data.value = []
    if (mv.value) {
      data.value = mv.value.map(a => ({ url: a, ext: getExt(a), name: getName(a) }))
    }
  }

})

const remove = (one) => {
  data.value.splice(data.value.indexOf(one), 1)
  if (props.max == 1) {
    mv.value = data.value[0]?.url
  } else {
    mv.value = data.value.filter(a => 'url' in a).map(a => a.url)
  }
  request.post(base + "remove?r=" + Math.random(), request.params({ key: uploadKey.value, url: one.url }))
}

onUnmounted(async () => {
  await request.get(base + "removeUploadKey-" + uploadKey.value)
})
window.addEventListener('beforeunload', async () => {
  await request.get(base + "removeUploadKey-" + uploadKey.value)
})


/**
 * 刷新上传凭证
 */
const reloadUploadKey = async () => {
  request.get(base + "removeUploadKey-" + uploadKey.value)
  uploadKey.value = await props.getUploadKey()
}

const fileInputRef = ref(null);

/**
 * 打开文件选择对话框
 */
const open = () => {
  if (doing.value) {
    return
  }
  try {
    fileInputRef.value.click();
  } catch (e) { }
}

const fileInputChange = () => {

  let files = fileInputRef.value.files
  if (files.length > 0) {
    // fileInputRef.value.value = ''
    for (let file of files) {
      addUploadFile(file)
    }
    uploadFiles()
  }
}

const addUploadFile = async (file) => {
  if (props.max > 1 && props.max == data.value.length) {
    ElMessage.error('最多只允许上传' + props.max + '个文件')
    return
  }
  // 判断类型
  if (props.accept) {
    let ext = file.name.toLowerCase().substring(file.name.lastIndexOf("."))
    if (props.accept.indexOf(ext) == -1) {
      ElMessage.error(file.name + '类型错误')
      return
    }
  }
  if (props.maxSize > 0) {
    if (file.size > props.maxSize) {
      ElMessage.error(file.name + '大小超过了' + props.maxSize.toFileSize())
      return
    }
  }
  if (props.max == 1) {
    data.value.length = 0
  }


  data.value.push({ raw: file, ext: getExt(file.name), name: file.name })

}

let doing = ref(false);
const uploadFiles = async () => {
  if (doing.value) {
    return
  }
  doing.value = true
  uploading.value = true
  await reloadUploadKey()
  let index = 0
  let doOne = async () => {
    while (index < data.value.length) {
      await uploadFile(index++)
    }
  }

  let a = [];
  for (let i = 0; i < props.threadNum; i++) {
    a.push(doOne())
  }
  Promise.all(a).then(() => {
    if (props.max == 1) {
      mv.value = r.data
    } else {
      mv.value = data.value.filter(a => 'url' in a).map(a => a.url)
    }
  }).finally(() => {
    data.value = data.value.filter(a => !a.error && !a.abort)
    doing.value = false
    uploading.value = false
  })
}



const uploadFile = async (index, retry = 0) => {
  let one = data.value[index];
  if (!one) {
    return
  }
  if (one.url) {
    return
  }
  if (retry >= props.retry) {
    one.error = true
    return
  }
  if (one.uploading || one.error || one.abort) {
    return
  }
  retry++

  one.uploading = { loaded: 0, total: one.raw.size }
  one.uploading.controller = new AbortController();
  one.uploading.abort = () => {
    one.uploading.controller.abort()
    if (one.raw.size > props.chunksize) {
      request.post(base + 'multipartUploadAbort?uploadId=' + one.uploading.uploadId)
    }
  }


  if (one.raw.size <= props.chunksize) {

    let formData = new FormData()
    formData.append("prefix", props.prefix)
    formData.append("key", uploadKey.value)
    formData.append("keepFilename", props.keepFilename)
    formData.append("file", one.raw)

    let onUploadProgress = (event) => {
      one.uploading.loaded = event.loaded

    }

    try {
      let r = await request.post(base + "?r=" + Math.random(), formData, {
        onUploadProgress,
        timeout: 600000,
        showLoading: false,
        signal: one.uploading.controller.signal
      })
      if (r.code == 200) {
        delete one.raw
        delete one.uploading
        one.url = r.data
        
      } else {
        delete one.uploading
        await uploadFile(index, retry)
      }
    } catch (e) {
      delete one.uploading
      await uploadFile(index, retry)
    }
  } else {
    try {
      await uploadFileBig(one)
    } catch (e) {
      delete one.uploading
      await uploadFile(index, retry)
    }
  }
}

/**
 * 大文件上传
 * @param one 
 */
const uploadFileBig = async (one) => {
  let chunksList = chunkFile(one.raw, props.chunksize)
  let r = await request.post(base + "multipartUploadInit?r=" + Math.random(), request.params({ prefix: props.prefix, filename: one.raw.name, key: uploadKey.value, keepFilename: props.keepFilename }))
  let uploadId = r.data
  one.uploading.uploadId = uploadId
  for (let i = 0; i < chunksList.length; i++) {
    try {
      await uploadChunk(one, chunksList, i, uploadId, 0)
    } catch (e) {
      one.error = true
      request.post(base + 'multipartUploadAbort?uploadId=' + one.uploading.uploadId)
      return;
    }
  }
  r = await request.post(base + 'multipartUploadComplete?uploadId=' + one.uploading.uploadId)
  if (r.code == 200) {
    delete one.raw
    delete one.uploading
    one.url = r.data

  } else {
    one.error = true
    request.post(base + 'multipartUploadAbort?uploadId=' + one.uploading.uploadId)
    return;
  }
}

/**
 * 上传分片
 * @param index
 * @param retry
 */
const uploadChunk = async (one, chunksList, index, uploadId, retry = 0) => {
  if (retry >= props.retry) {
    throw new Error('上传分片失败')
  }
  retry++
  let formData = new FormData()
  formData.append('uploadId', uploadId)
  formData.append('file', chunksList[index])
  formData.append('partNumber', index + 1)
  let onUploadProgress = (event) => {
    one.uploading.loaded = props.chunksize * index + event.loaded
  }
  try {
    let r = await request.post(base + "multipartUpload?r=" + index + '' + Math.random(), formData, {
      onUploadProgress,
      timeout: 600000,
      showLoading: false,
      signal: one.uploading.controller.signal
    })
    if (r.code != 200) {
      await uploadChunk(one, chunksList, index, uploadId, retry)
    }
  } catch (e) {
    await uploadChunk(one, chunksList, index, uploadId, retry)
  }
}

/**
 * 分片
 * @param file 文件
 * @param chunksize 分片大小 
 */
const chunkFile = (file, chunksize) => {
  const chunks = Math.ceil(file.size / chunksize)
  const chunksList = []
  let currentChunk = 0
  while (currentChunk < chunks) {
    const start = currentChunk * chunksize;
    const end = Math.min(file.size, start + chunksize);
    const chunk = file.slice(start, end);
    chunksList.push(chunk);
    currentChunk++;
  }
  return chunksList;
}

defineExpose({ open, reloadUploadKey })
</script>
<style lang="scss" scoped>
.doing {
  opacity: .5;
  position: relative;

  &::before {
    content: '';
    position: absolute;
    height: 100%;
    width: 100%;
    background-color: #0000;
    cursor: not-allowed;

  }
}

.list {
  .item {
    display: flex;
    align-items: center;

    position: relative;
    overflow: hidden;
    border-radius: .3em;
    margin-top: .5em;
    padding: .5em;

    &.border {
      border: solid 1px #00000007;
    }

    &.abort {
      opacity: .5;
    }

    &.error {
      border-color: var(--el-color-danger, #f56c6c);
    }

    &>:nth-child(2) {
      cursor: pointer;

      &:hover {
        transform: translate(1px, 1px);
      }
    }

    &::before {
      content: '';
      pointer-events: none;
      background-color: #00000005;
      position: absolute;
      top: 0;
      left: 0;
      height: 100%;
      width: var(--ps, 0);
    }

    .btn {
      font-size: .7em;
      color: var(--el-color-danger, #f56c6c);
      margin-left: 1em;
      cursor: pointer;

      &:hover {
        transform: translate(1px, 1px);
      }
    }

  }
}


.icon {
  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABXgAAABGCAMAAABBnQx/AAAAA3NCSVQICAjb4U/gAAAC/VBMVEX///+srKyZmZlzc3NAQkE5OkEoKSsgICAUEw4AAADx1pSZmZmIiIgoKSsUEw4AAAAUEw4AAACsrKwUEw4AAACsrKwgICAAAAC1tbXXrEqsrKy9vb21tbWsrKzV2tTFxcWZmZne3t7FxcW9vb2srKwwXyEoKSsUEw7e3t7W2d7MzMzFxcW1tbWsrKykpKS8ljzpXDshfQEzZplKVIRERkwuR385OkG1EBD////4///+/9r7/uzv///5/8jn/f/39/f59P36/on//2bY+/3w8fX+8c7b9/zw7+7T9v/l7/zk8tjk7vH97bjK9urm5+324vT75rfB8f/m5ub95qq68+zW6PXg5eX239f64KWt7v/d3+j53pze3t7N4fvo4K3x1O742pTR3e/723jN38fW2d7V2tT31ojI45Lx1pTnzvf712vD4anD2O7k0dH20XvI0d7vz4Gs1vb5zFzMzMz2wcGp3Ju2zeyw2Iz4xkzFxcXmxHSnyOr4wy6k03u3w9nqwFmZyfC4wc2Mx/W9vb3ds7qlvd6WyYWyvK7dtFi1tbWJvt77p2j1riXzpJG7sa72qFqtsLiLwW1WxOzXrEr1pxOsrKzWqVh9s+V8ruF1sd6tp5eXrn+kpKSAtGdkvknNnVXekZJhvED1i1fmjHJ4rGPvkQthtkOPnK+ZmZlnoNqHmsRTtThzqGC8ljxso1tbqkDmgylCrxtMqzBYk9pgnVDvckh7ibxQktWIiIjobW03rAl4hLNDnydek1CpfkCyektSh8+SgFdufbg9iNJTikFderaGd2PpXDtMfMXjWlqTdjRLijc5khxAfsBmdqDTXFwvgNBzc3M1jRhJgTT4SUmSaE1Ob7HAU1MzcrVCbLU9b58kcsJmZmaUXighfQHvOTk8YqwzZpl3VyxKVIQzVqDMMzNPUlowXyFvTCIrTZZISlPEKipERkxdQxwuR39AQkHxERF2MR6RJCQ5OkFMNxkAPnMzMzO1EBBCLxMSN11WHSkoKSsgICAUEw4AAABBEXoEAAAA/3RSTlMAERERERERERERIiIiIiIiMzNERERVVVVmmZm7u7vMzMzd3d3d3d3d7u7u7u7u7u7u7u7u7u7u7v////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+8LOaEAAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAIABJREFUeJztnQ18FNW5/6nga6lo9aLoVenV+re1Lb2o11mIbCobRhJZYCABwqrB7GIkoQEXgU0wJllCsxRs+IcloglBgrKgvKgYkssVFVGKtGguL7YajQJSSHlJJcVUsJ973ub9zMyZzQQWmt9nd/bM2Z1nz5kz57vPPnPmTI8e3epWt7rVrW51q1vd6la3nNMVV/QjOtcl6Va3ukVTL5P3Lrr+5h/5fH6on9x8Y++zVqZuxSERtgMqKyvLy8sLCnJyxj9c0E3ebnUrodSrV9++t9994uilRh+4/g6fL0Pged7t4Xmv4APw/f7ZLGC3rHSFGrYF03LGjx+VkjIIaPBgsEhJSam8wnjzf48wKRq+/OxVqVvdumCFiNvcEGtubT106FBfare6/g6/z+vmgFyupCSXCyZ4IeC/2ZDTSjnTpS88K46pXz+MWujWjgKsTZFIC/WgpJRyE/BG3CzyCMFbf2BamITaN2yFid5qVhYnbDBbsdgr55+VyE2OWDEvjEPH3Fk6dBFx7z4RC/kRUr1NgLuHbu+j/+D1dwQQdQFzgTwez1AgD8jwCP4bzYITRM506QvPimOqBG4tlbQapZTXrh3Qz6DFIu4MBmX6g547TcNM7Pvm4q7YF3EUxuML3XpJ19pgtWJ1xJx3VtxC1Jy8jnQlh/pjV3frXr363tX0i+pIBfJxTwQ5LA8k791Xa3fT934U8CJXFzq7SQi6I0aMgIuhHMf7/NdboteZLn3hWXFMlYao1YL3iSlTyvvROwJjnQIh3n3nD00Kw75v/qPrY1VMhcn0hczK4oQNVitWR8x5ZyXDEwzdakYIxsPFBw4X48I41B8dKQtNJJDb7HdhBaGP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);
  background-repeat: no-repeat;
  background-position-y: 0;
  background-size: 140em 7em;
  width: 7em;
  height: 7em;
  background-position: -14em center;
  font-size: .4em;
  margin-right: .5em;
  flex-flow: 1;
  flex-shrink: 1;

  &.icon-dir {
    background-position: 0 center;
  }

  &.icon-zip {
    background-position: -7em center;
  }

  &.icon-filelist {
    background-position: -21em center;
  }

  &.icon-rar,
  &.icon-7z,
  &.icon-tar,
  &.icon-gz {
    background-position: -28em center;
  }

  &.icon-xls,
  &.icon-xlsx {
    background-position: -35em center;
  }

  &.icon-doc,
  &.icon-docx {
    background-position: -42em center;
  }

  &.icon-ppt,
  &.icon-pptx {
    background-position: -49em center;
  }

  &.icon-vsd {
    background-position: -56em center;
  }

  &.icon-pdf {
    background-position: -63em center;
  }

  &.icon-txt,
  &.icon-md,
  &.icon-json,
  &.icon-htm,
  &.icon-xml,
  &.icon-html,
  &.icon-js,
  &.icon-css,
  &.icon-php,
  &.icon-jsp,
  &.icon-asp {
    background-position: -70em center;
  }

  &.icon-apk {
    background-position: -77em center;
  }

  &.icon-exe {
    background-position: -84em center;
  }

  &.icon-ipa {
    background-position: -91em center;
  }

  &.icon-mp4,
  &.icon-mov,
  &.icon-mpeg,
  &.icon-rm,
  &.icon-rmvb {
    background-position: -98em center;
  }

  &.icon-wav,
  &.icon-wmv,
  &.icon-mid,
  &.icon-mp3 {
    background-position: -105em center;
  }

  &.icon-bmp,
  &.icon-jpg,
  &.icon-jpeg,
  &.icon-png,
  &.icon-webp,
  &.icon-psd,
  &.icon-tiff,
  &.icon-tif,
  &.icon-eps,
  &.icon-raw,
  &.icon-svg,
  &.icon-ai,
  &.icon-avif,
  &.icon-apng,
  &.icon-wmf,
  &.icon-pcd,
  &.icon-gif {
    background-position: -112em center;
  }

}
</style>