<!--
 * @Description: 上传组件
-->

<template>
  <div @click="selectFile">
    <input
      ref="inputFileRef"
      type="file"
      :accept="accept"
      :multiple="multiple"
      @change="handleChange($event)"
      hidden
    />
    <slot name="uploader">
      <div class="uploader">
        <el-icon size="20"><Plus /></el-icon>
      </div>
    </slot>
  </div>
</template>

<script name="Upload-uploader" lang="ts" setup>
  //#region import
  import {
    Plus
  } from '@element-plus/icons-vue'
  import type { NewFile, Status } from '../types'
  import {toRefs,computed, ref} from 'vue'
  //#endregion

  //#region Vue 组件参数
  const emit = defineEmits(['update:modelValue', 'oversize'])

  const props = withDefaults(
    defineProps<{
      mode?: 'normal' | 'thumbnail' // 显示模式：常规/缩略图
      modelValue: Array<NewFile>
      accept?: string // 文件格式
      multiple?: boolean // 是否支持文件多选
      maxSize?: number // 限制文件大小
      maxCount?: number // 最大上传文件数量
      beforeRead?: (file: File[]) => boolean
      afterRead?: (newFile: NewFile[]) => void
      checkFormat?: (file: File[]) => Promise<File[]>
      compress?: (file: NewFile[]) => Promise<NewFile[]>
      customRequest?: (file: NewFile) => Promise<NewFile>
    }>(),
    {
      mode: 'normal',
      accept: '*/*',
      multiple: false,
      maxSize: Infinity,
      maxCount: Infinity
    }
  )

  const {
    mode,
    modelValue,
    accept,
    multiple,
    maxSize,
    maxCount,
    beforeRead,
    afterRead,
    checkFormat,
    compress,
    customRequest
  } = toRefs(props)

  // 双向数据绑定
  const fileList = computed({
    get() {
      return modelValue.value
    },
    set(newValue) {
      emit('update:modelValue', newValue)
    }
  })
  //#endregion

  const inputFileRef = ref<HTMLElement | null>(null)

  // 选择文件
  const selectFile = () => inputFileRef.value?.click()

  // 监测文件变化
  const handleChange = async ({ target }: any) => {
    const inputElement = target as any
    if (inputElement?.files === null || inputElement?.files.length === 0) return

    // // 上传数量限制
    // let files: File[] = []
    // if (maxCount.value <= fileList.value.length) return
    // else {
    //   files = [...inputElement.files].slice(
    //     0,
    //     maxCount.value - fileList.value.length
    //   )
    // }

    let files: File[] = [...inputElement.files]

    // 读取文件信息
    let newFileList: NewFile[] = await readFile(files)
    newFileList.forEach((item) => fileList.value.push(item))

    // 文件读取完毕后
    if (customRequest.value) {
      const handleAfterRead = customRequest.value
      const newFileListRef = ref(newFileList)
      newFileListRef.value.forEach(item => handleAfterRead({file: item} as any))
    }

    // 将input置空，这样来触发下次如果重名文件不调用onchange方法问题
    inputElement.value = ''
  }

  // 限制上传大小
  const oversize = (files: File[]): File[] => {
    const excludeFiles: File[] = files.filter(
      (item) => item.size > maxSize.value
    )
    excludeFiles.length > 0 && emit('oversize', excludeFiles)
    return files.filter((item) => item.size <= maxSize.value)
  }

  // 读取文件信息并添加新信息
  const readFile = (files: File[]): Promise<NewFile[]> => {
    return new Promise((resolve) => {
      let tasks: Array<Promise<NewFile>> = []
      files.forEach((file) => {
        tasks.push(
          new Promise((resolve) => {
            resolve({
                id: Math.random().toString(36).slice(-10),
                name: file.name,
                percentage: 0,
                type: file.type,
                file: file as File,
                suffix: file?.name.slice(
                  file?.name.lastIndexOf('.'),
                  file?.name.length
                ).toLowerCase(),
                status: 'uploading' as Status
              })
          })
        )
      })

      Promise.all(tasks)
        .then((res) => {
          resolve(res)
        })
        .catch((err) => {
          throw new Error(err)
        })
    })
  }
</script>

<style lang="scss" scoped>
.uploader {
  width: 100px;
  height: 100px;
  border: 1px dashed rgb(224, 224, 230);
  border-radius: 4px;
  cursor: pointer;
  transition: border-color 0.2s ease-in-out;
  display: flex;
  justify-content: center;
  align-items: center;

  &:hover {
    border-color: #46299D;
  }
}
</style>
