<template>
  <div class="oss-filed">
    <n-upload
      action="#"
      :max="limit"
      :file-list="data.fileList"
      :default-upload="false"
      :show-remove-button="remBtn"
      :list-type="fileType == 'img' ? 'image-card' : 'text'"
      @change="handleChange"
      @before-upload="beforeUpload"
    >
      <n-button v-if="fileType != 'img'">
        <n-icon>
          <FileOutlined />
        </n-icon>
        选择{{ btnName }}
      </n-button>
      <div v-if="fileType == 'img'" class="oss-upload-text" />
    </n-upload>
    <n-button
      v-if="showUploadBtn"
      type="primary"
      :disabled="data.fileList.length === 0"
      style="margin-top: 16px"
      class="btn"
      :loading="loading"
      @click="startUpload"
    >
      <n-icon>
        <UploadOutlined />
      </n-icon>
      开始{{ btnName }}上传
    </n-button>
  </div>
</template>

<script setup name="UploadOss">
import OSS from 'ali-oss'
import { getOssAuth } from '@/api/common/upload.js'
import { ref, reactive, watch, onMounted, toRefs } from 'vue'
import { FileOutlined, UploadOutlined } from '@vicons/antd'

const props = defineProps({
  limit: {
    type: Number,
    default: 1
  },
  defaultList: {
    type: String,
    default: ''
  },
  fileType: {
    type: String,
    default: 'img'
  },
  btnName: {
    type: String,
    default: '文件'
  },
  remBtn: {
    type: Boolean,
    default: false
  },
  keyName: {
    type: String,
    default: 'src'
  },
  showUploadBtn: {
    type: Boolean,
    default: false
  }
})

const emit = defineEmits(['uploadStatus'])

const client = ref(null) // 上传实例
const loading = ref(false)
const data = reactive({
  ...props,
  client: null,
  fileList: [],
  hasUploadInfo: {}
})

onMounted(() => {
  if (props.defaultList) {
    const list = props.defaultList.split(',')
    data.fileList = list.map((item) => {
      const objectName = '/' + item.split('/').slice(3).join('/')
      const obj = {
        name: objectName,
        id: objectName,
        // type: 'image/png',
        url: item,
        objectName: objectName,
        status: 'finished'
      }
      data.hasUploadInfo[obj.id] = {
        id: objectName,
        objectName: objectName
      }
      return obj
    })
    emit('uploadStatus', 'upload')
  } else {
    data.fileList = []
  }
})

/* 监听默认文件列表 */
watch(() => props.defaultList, (value) => {
  if (value.length) {
    const list = value.split(',')
    const fileList = list.map((item) => {
      const objectName = '/' + item.split('/').slice(3).join('/')
      const obj = {
        name: objectName,
        id: objectName,
        type: 'image/png',
        url: item,
        objectName: objectName
      }
      data.hasUploadInfo[obj.id] = {
        id: objectName,
        objectName: objectName
      }
      return obj
    })
    data.fileList = fileList
    emit('uploadStatus', 'upload')
  } else {
    data.fileList = []
    // emit('uploadStatus', 'hasFile')
  }
})

/**
 * @description: 上传前的判断文件类型
 * @param {*} file 当前选择的文件
 * @return {*}
 */
const beforeUpload = ({ file }) => {
  if (props.fileType === 'img') {
    if (!file.type.match('image.*')) {
      $message.error('请选择正确的图片类型')
      /* 中断流程 */
      return false
    }
  }
  if (props.fileType === 'srt') {
    const type = ['srt', 'SRT']
    const temp = type.includes(file.name.split('.')[1])
    if (!temp) {
      $message.error('请选择SRT类型文件')
      /* 中断流程 */
      return false
    }
  }
  if (props.fileType === 'tif') {
    const type = ['tif', 'TIF']
    const temp = type.includes(file.name.split('.')[1])
    if (!temp) {
      $message.error('请选择tif类型文件')
      /* 中断流程 */
      return false
    }
  }
}

/**
 * @description: 文件发生变化时
 * @param {Array} fileList
 * @return {*}
 */
const handleChange = async({ fileList }) => {
  data.fileList = fileList.map((item) => {
    if (Object.keys(data.hasUploadInfo).includes(item.id)) {
      item.objectName = data.hasUploadInfo[item.id].objectName
    }
    return item
  })
  /* 判断是否存在文件 */
  const status = fileList.length ? 'ready' : 'no-file'
  emit('uploadStatus', status)
}

/**
 * @description: 上传文件
 * @return {*}
 */
const startUpload = () => {
  return new Promise((resolve, reject) => {
    loading.value = true
    /* 定义要上传文件 */
    const uploads = []
    /* 从待文件列表中获取没有上传过的文件 */
    const uloadList = data.fileList.filter((item) => {
      return !item.url
    })
    /* 如果待上传列表长度为0，则返回，否则获取上传鉴权 */
    if (!uloadList.length) {
      loading.value = false
      /* 如果没有要上传的文件信息则返回oss的文件名 */
      /* 不存在objectName的文件表示上传失败 */
      const objectList = data.fileList.map((item) => item.objectName || 'error')
      resolve(objectList)
    } else {
      getOssAuth().then(res => {
        /* 初始化oss上传客户端 */
        data.client = new OSS({
          timeout: 600000,
          region: 'oss-cn-shanghai',
          secure: true,
          accessKeyId: res.data.accessKeyId,
          accessKeySecret: res.data.accessKeySecret,
          stsToken: res.data.securityToken,
          bucket: 'ta-tech-image'
        })
        /* promise上传要上传的文件 */
        uloadList.forEach(item => {
          uploads.push(uploadOss(item))
        })
        Promise.all(uploads).then((response) => {
          /* 获取fileList内的文件存储名称 */
          const objectList = data.fileList.map((item) => item.objectName).filter(it => it)
          // resolve(objectList)
          resolve({ [props.keyName]: objectList.join() })
          emit('uploadStatus', 'success', { [props.keyName]: objectList.join() })
          loading.value = false
        }).catch((error) => {
          console.log('error', error)
          loading.value = false
          resolve(error)
        })
      })
    }
  })
}

/**
 * 上传到 OSS
 * @param {*} files
 */
const uploadOss = (files) => {
  const file = files.file
  return new Promise((resolve, reject) => {
    /* 生成随机文件名称 */
    const randomString = Math.random().toString(36).slice(2)
    const suffix = /\.[^\.]+/.exec(file.name)
    const timestamp = new Date().getTime()
    const objectName = `/imagedir/${randomString}_${timestamp}${suffix}`
    /* 上传文件至oss */
    data.client.put(objectName, file)
      .then((res) => {
        const index = data.fileList.findIndex(list => list.id === files.id)
        data.fileList[index].url = res.url
        data.fileList[index].objectName = objectName
        const id = data.fileList[index].id
        data.hasUploadInfo[id] = {
          id: objectName,
          objectName: objectName
        }
        resolve(objectName)
      })
      .catch((err) => {
        resolve('error')
        console.log(err)
      })
  })
}

defineExpose({ startUpload, ...toRefs(data) })

</script>

<style lang="scss" scoped>
.oss-filed {
  width: 100%;
  padding: 10px 20px;
}

.oss-upload-picture-card-wrapper {
  min-height: 122px;
  padding: 10px 0 0 10px;
}

.oss-upload-text {
  // width: 100px;
  // height: 100px;
  // cursor: pointer;
  // border: 1px solid #e1e1e1;
  // margin: 10px 0 0 10px;
  position: relative;
}

.oss-upload-text::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 30px;
  transform: translate(-50%, -50%);
  border-top: 2px solid #e1e1e1;
}

.oss-upload-text::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  height: 30px;
  transform: translate(-50%, -50%);
  border-left: 2px solid #e1e1e1;
}

.n-image {
  img {
    overflow: hidden;
  }
}

.oss-upload-type {
  text-align: center;
  font-size: 14px;
  color: rgb(64, 152, 252);
}

.btn {
  // width: 130px;
  margin-left: calc(50% - 65px);
  margin-top: 10px;
  margin-bottom: 30px;
}
</style>
