<template>
  <div
    class="rest-select-file"
    :class="isDrag ? 'drag' : ''"
    :style="{ width, height }"
    @dragenter="ondragenter"
    @dragover="ondragover"
    @dragleave="ondragleave"
    @drop="ondrop"
  >
    <div class="logo-box">
      <div class="logo">
        <icon-solar-cloud-upload-linear></icon-solar-cloud-upload-linear>
      </div>
      <span class="text" style="padding: 0 6px; text-align: center">{{ hint }}</span>
    </div>
    <input id="file-input" class="file-input" type="file" :accept="accept.toString()" :multiple="newLimit > 1" @click="inputClick" @change="selectFileChange" />
  </div>
</template>

<script setup lang="ts">
  import IconSolarCloudUploadLinear from '~icons/solar/cloud-upload-linear';

  const props = defineProps({
    /** 宽度(要带单位) */
    width: { type: String, default: '100px' },
    /** 高度(要带单位) */
    height: { type: String, default: '100px' },
    /** 提示文字 */
    hint: { type: String, default: '点击上传' },
    /** 最多上传几张图片 */
    limit: { type: Number, default: 1 },
    /** 限制可以选择的文件类型(根据后缀限制) 如：['.png', 'jpg']  */
    accept: { type: Array as PropType<`.${string}`[]>, default: () => ['.png', '.jpg'] },
    /** 选择图片完成后的回调 */
    afterSelectFile: { type: Function as PropType<(data: File[]) => File[] | Promise<File[]>>, default: undefined },
  });

  const emit = defineEmits<{
    (e: 'change', data: File[]): void;
  }>();
  const manualSelectFileOp = {
    resolve: undefined as ((value: File[]) => void) | undefined,
    reject: undefined as ((value: Error) => void) | undefined,
  };

  const newLimit = ref<number>(props.limit);
  watch(
    () => props.limit,
    (val) => {
      newLimit.value = val;
    }
  );

  /** 解决地二次不能选择相同文件 */
  function inputClick(event: Event) {
    const inputDom = event.target as HTMLInputElement;
    inputDom.value = '';
  }

  /**
   * 选择文件完成
   */
  async function selectFileChange(event: Event) {
    const inputDom = event.target as HTMLInputElement;
    const files = inputDom.files ? [...inputDom.files] : [];
    await filterFile(files);
    newLimit.value = props.limit; // 恢复为 props.limit
  }

  /** 是否正在拖拽 */
  const isDrag = ref(false);
  /**
   * 被鼠标拖动的对象进入其容器范围内时触发此事件
   */
  function ondragenter(event: DragEvent) {
    event.preventDefault();
    event.stopPropagation();
    isDrag.value = true;
    if (event.dataTransfer) {
      event.dataTransfer.dropEffect = 'copy'; // 兼容某些三方应用,如圈点
    }
    return false;
  }

  /**
   * 当某被拖动的对象在容器范围内拖动时触发此事件
   */
  function ondragover(event: DragEvent) {
    event.preventDefault();
    event.stopPropagation();
    isDrag.value = true;
    return false;
  }

  /**
   * 当被鼠标拖动的对象离开其容器范围内时触发此事件
   */
  function ondragleave(event: DragEvent) {
    event.preventDefault();
    event.stopPropagation();
    isDrag.value = false;

    return false;
  }

  /**
   * 在一个拖动过程中,释放鼠标键时触发此事件
   */
  function ondrop(event: DragEvent) {
    event.preventDefault();
    event.stopPropagation();
    isDrag.value = false;
    const files = event.dataTransfer?.files ? [...event.dataTransfer!.files] : [];
    filterFile(files);
  }

  /**
   * 筛选合规的文件
   */
  async function filterFile(files: File[]): Promise<File[]> {
    try {
      let newFiles = files;
      if (typeof props.afterSelectFile === 'function' && files) {
        try {
          newFiles = await props.afterSelectFile(files);
        } catch (error: any) {
          newFiles = [];
        }
      }
      newFiles = newFiles.filter((item) => {
        const suffix = item.name.split('.').pop() || '';
        return props.accept.includes(`.${suffix}`);
      });
      newFiles.length = newLimit.value;

      if (typeof manualSelectFileOp.resolve === 'function') {
        // 手动选择时
        typeof manualSelectFileOp.resolve === 'function' && manualSelectFileOp.resolve(newFiles);
        manualSelectFileOp.resolve = undefined;
        manualSelectFileOp.reject = undefined;
      } else {
        emit('change', newFiles);
      }
      return Promise.resolve(newFiles);
    } catch (error: any) {
      typeof manualSelectFileOp.reject === 'function' && manualSelectFileOp.reject(error); // 手动选择时
      manualSelectFileOp.resolve = undefined;
      manualSelectFileOp.reject = undefined;
      return Promise.reject(error);
    }
  }

  defineExpose({
    /** 选择文件 */
    selectFile(limit = 1) {
      return new Promise<File[]>((resolve, reject) => {
        manualSelectFileOp.resolve = resolve;
        manualSelectFileOp.reject = reject;
        if (typeof limit === 'number' && limit >= 1) {
          newLimit.value = limit;
        }
        nextTick(() => {
          document.getElementById('file-input')?.click();
        });
      });
    },
  });
</script>

<style lang="scss" scoped>
  .rest-select-file {
    position: relative;
    user-select: none;
    background: var(--el-fill-color-lighter);
    border: 1px dashed var(--el-border-color-darker);
    border-radius: 6px;

    &:hover {
      border: 1px dashed var(--el-color-primary);
    }

    &.drag {
      background: var(--el-color-primary-light-9);
      border: 1px dashed var(--el-color-primary);
    }

    .logo-box {
      @include flex(column, nowrap, center, center, center);
      width: 100%;
      height: 100%;

      .logo {
        width: 40px;
        height: 40px;
        font-size: 30px;
        color: $c-text-disabled;
      }

      .text {
        font-size: 12px;
        line-height: 12px;
        color: $c-text-disabled;
      }
    }

    .file-input {
      position: absolute;
      top: 0;
      left: 0;
      z-index: 1;
      width: 100%;
      height: 100%;
      cursor: pointer;
      opacity: 0;
    }
  }
</style>
