<template>
  <div class="upload-box">
    <div class="img-box">
      <div v-for="(item, index) in fileList" class="items">
        <img v-if="item.filePath" :src="item.filePath" class="upload-image" />
        <div class="upload-handle" @click.stop>
          <div class="handle-icon" @click="handleEdit(index)" v-if="!self_disabled">
            <n-icon size="20">
              <EditOutlined />
            </n-icon>
          </div>
          <div class="handle-icon" @click="onPreview(index)">
            <n-icon size="20">
              <ZoomInOutlined />
            </n-icon>
          </div>
          <div class="handle-icon" @click="handleRemove(index)" v-if="!self_disabled">
            <n-icon size="20">
              <DeleteOutlined />
            </n-icon>
          </div>
        </div>
      </div>
      <n-upload
        action="#"
        :id="uuid"
        class="upload-demo my-upload-images"
        :multiple="multiple"
        ref="uploadRef"
        :style="{ width: width, height: height }"
        :disabled="loading ? true : self_disabled || fileList.length >= props.limit"
        @before-upload="beforeUpload"
        @change="handleUploadChange"
        :max="limit"
        v-model:file-list="fileList"
        :show-file-list="false"
        v-if="!self_disabled"
        :accept="props.fileType"
      >
        <n-icon size="40" color="#666666" v-if="!loading">
          <PlusOutlined />
        </n-icon>
        <n-progress type="circle" v-else :percentage="progress" />
      </n-upload>
    </div>
    <div class="n-upload__tip">
      <slot name="tip"></slot><span v-if="limit">,最多{{ limit }}张</span>
    </div>
  </div>
</template>

<script setup lang="ts">
  import { computed, ref } from 'vue';
  import { generateUUID } from '@/utils/auth';
  import type { UploadFileInfo } from 'naive-ui';
  import { PlusOutlined, EditOutlined, DeleteOutlined, ZoomInOutlined } from '@vicons/antd';
  import { useNotification } from 'naive-ui';
  import OSS from 'ali-oss';
  const uploadRef = ref();
  import { upload, getOssConfig } from '@/api/common';

  /**
   * 接受父组件参数
   */
  const props = defineProps({
    zIndex: {
      default: -1,
    },
    multiple: {
      type: Boolean,
      default: false,
    },
    btnTip: {
      type: String,
      default: '',
    },
    limit: {
      type: Number,
      default: undefined,
    },
    width: {
      type: String,
      default: '120px',
    },
    height: {
      type: String,
      default: '120px',
    },
    borderRadius: {
      type: String,
      default: '8px',
    },
    fileLists: {
      type: Array,
      required: true,
      default: [],
    },
    disabled: {
      default: false,
    },
    fileSize: {
      default: 200,
    },
    fileType: {
      default: '',
    },
    name: {
      default: '',
    },
  });
  // 生成组件唯一id
  const uuid = ref('id-' + generateUUID());

  const notification = useNotification();

  // 判断是否禁用上传和删除
  const self_disabled = computed(() => {
    return props.disabled;
  });

  /**
   * 文件列表
   */
  const fileList = computed({
    get: () => {
      return props.fileLists;
    },
    set: (val) => {
      emit('update:modelValue', fileList.value);
    },
  });

  /**
   * 定义参数
   */
  let editIndex = '';
  const emit = defineEmits(['upload']);
  const progress = ref(0);
  const loading = ref(false);

  /**
   * 本地接口上传
   * @param options 参数
   */
  const handleHttpUpload = async (options) => {
    loading.value = true;
    try {
      // 上传文件
      const formData = new FormData();
      formData.append('file', options.file.file);
      formData.append('name', props.name);
      const res = await upload(formData);
      if (props.multiple) {
        let list = JSON.parse(JSON.stringify(fileList.value));
        if (editIndex !== '') {
          list.splice(editIndex, 1, {
            url: res.fileUrl,
            name: res.originalName,
            filePath: res.fileUrl,
            fileName: res.originalName,
          });
          editIndex = '';
        } else {
          list.push({
            url: res.fileUrl,
            name: res.originalName,
            filePath: res.fileUrl,
            fileName: res.originalName,
          });
        }
        emit('upload', list, props.zIndex);
      } else {
        emit('upload', res, props.zIndex);
      }
    } catch (error) {
      notification.error({
        message: '温馨提示',
        duration: 2000,
        description: '上传文件失败！',
      });
      if (props.multiple) {
        emit('upload', fileList.value, props.zIndex);
      } else {
        uploadRef.value?.clear();
        emit('upload', '', props.zIndex);
      }
      options.onError(error as any);
    } finally {
      progress.value = 0;
      loading.value = false;
    }
  };

  /**
   * 阿里云OSS上传
   * @param options 参数
   */
  const handleHttpUploads = async (options: UploadRequestOptions) => {
    const configData = await getOssConfig();
    let date = new Date();
    let year = date.getFullYear();
    let month: string | number = date.getMonth() + 1;
    month = month < 10 ? '0' + month : month;
    let day: string | number = date.getDate();
    day = day < 10 ? '0' + day : day;
    let h: string | number = date.getHours();
    h = h < 10 ? '0' + h : h;
    let minute: string | number = date.getMinutes();
    minute = minute < 10 ? '0' + minute : minute;
    let second: string | number = date.getSeconds();
    second = second < 10 ? '0' + second : second;
    let time = h + '' + minute + '' + second;
    let fileName = time + '-' + options.file.name;
    fileName = fileName.replace(/[\+\_\,\!\|\~\`\(\)\#\$\%\^\&\*\{\}\:\;\"\<\>\?]/g, '');
    let filePath = props.name + '/' + year + '/' + month + '/' + day + '/' + fileName;
    loading.value = true;
    try {
      // 文件请求头
      const headers = {
        'Access-Control-Allow-Origin': '*',
      };
      const client = new OSS({
        // yourRegion填写Bucket所在地域。以华东1（杭州）为例，yourRegion填写为oss-cn-hangzhou。
        endpoint: configData.oss_endpoint,
        // 从STS服务获取的临时访问密钥（AccessKey ID和AccessKey Secret）。
        secure: true,
        region: configData.oss_region,
        accessKeyId: configData.oss_accessKeyId,
        accessKeySecret: configData.oss_accessKeySecret,
        // 填写Bucket名称。
        bucket: configData.oss_bucketName,
        timeout: 600000,
      });
      const res = await client.multipartUpload(filePath, options.file.file, {
        headers: headers,
        // 获取分片上传进度、断点和返回值。
        progress: (p, cpt, res) => {
          progress.value = parseFloat(parseFloat(p * 100).toFixed(2));
        },
        // 设置并发上传的分片数量。
        parallel: 4,
        // 设置分片大小。默认值为1 MB，最小值为100 KB。
        partSize: 1024 * 1024,
        // headers,
        mime: options.file.type,
      });
      // 文件地址拼接
      const fileUrl = configData.oss_domain + res.name;
      if (props.multiple) {
        let list = JSON.parse(JSON.stringify(fileList.value));
        if (editIndex !== '') {
          list.splice(editIndex, 1, {
            url: fileUrl,
            name: options.file.name,
            filePath: fileUrl,
            fileName: options.file.name,
          });
          editIndex = '';
        } else {
          list.push({
            url: fileUrl,
            name: options.file.name,
            filePath: fileUrl,
            fileName: options.file.name,
          });
        }
        emit('upload', list, props.zIndex);
      } else {
        emit('upload', fileUrl, options.file.name, props.zIndex);
      }
    } catch (error) {
      notification.error({
        message: '温馨提示',
        description: '上传文件失败！',
      });
      if (props.multiple) {
        emit('upload', props.fileList, props.zIndex);
      } else {
        uploadRef.value!.clearFiles();
        emit('upload', '', props.zIndex);
      }
    } finally {
      progress.value = 0;
      loading.value = false;
    }
  };

  /**
   * 执行文件上传
   * @param data 参数
   */
  const handleUploadChange = (data: { fileList: UploadFileInfo[] }) => {
    handleHttpUpload(data);
  };

  /**
   * 文件预览
   * @param index 参数
   */
  const onPreview = (index: any) => {
    window.open(fileList.value[index].filePath);
  };

  /**
   * 执行编辑
   * @param index 参数
   */
  const handleEdit = (index: any) => {
    editIndex = index;
    const dom = document.querySelector(`#${uuid.value} .n-upload-file-input`);
    dom && dom.dispatchEvent(new MouseEvent('click'));
  };

  /**
   * 执行删除
   * @param index 参数
   */
  const handleRemove = (index: any) => {
    editIndex = index;
    if (props.multiple) {
      emit(
        'upload',
        fileList.value.filter((f, i) => !(editIndex == i)),
        props.zIndex,
      );
    } else {
      uploadRef.value?.clear();
      emit('upload', '', props.zIndex);
    }
  };

  /**
   * 最大上传张数限制
   */
  const onExceed = () => {
    if (props.limit && fileList.value.length >= props.limit) {
      notification.warning({
        message: '温馨提示',
        duration: 2000,
        description: `最多支持上传${props.limit}张`,
      });
      return false;
    }
  };

  /**
   * @description 文件上传之前判断
   * @param rawFile 选择的文件
   * */
  const beforeUpload = (rawFile) => {
    console.log(rawFile);
    const imgSize = rawFile.file.file.size / 1024 / 1024 < props.fileSize;
    if (!imgSize) {
      notification.warning({
        message: '温馨提示',
        duration: 2000,
        description: `上传文件大小不能超过 ${props.fileSize}M！`,
      });
      return false;
    }
    if (props.fileType) {
      let fileType = props.fileType.replace(/\s/g, '');
      fileType.split('.').join('|');
      let fileIndex = rawFile.file.file.name.lastIndexOf('.');
      let substrName = rawFile.file.file.name.substr(fileIndex);
      if (fileType.indexOf(substrName) == -1) {
        notification.warning({
          message: '温馨提示',
          duration: 2000,
          description: '上传文件不符合所需的格式！',
        });
        return false;
      }
    }
    return true;
  };
</script>
<style scoped lang="less">
  .upload-box {
    position: relative;

    .img-box {
      display: flex;
      flex-wrap: wrap;
      > div {
        margin-right: 10px;
        margin-bottom: 10px;
        position: relative;

        &.items {
          width: v-bind(width);
          height: v-bind(height);
          border: 1px dashed #d9d9d9;
          border-radius: 4px;
          padding: 5px;
        }

        &:hover {
          .upload-handle {
            opacity: 1;
          }
        }
      }
      .upload-image {
        width: 100%;
        height: 100%;
        object-fit: contain;
      }

      .upload-handle {
        position: absolute;
        top: 0;
        right: 0;
        box-sizing: border-box;
        display: flex;
        align-items: center;
        justify-content: center;
        width: 100%;
        height: 100%;
        cursor: pointer;
        background: rgb(0 0 0 / 60%);
        opacity: 0;

        .handle-icon {
          display: flex;
          flex-direction: column;
          align-items: center;
          justify-content: center;
          padding: 0 6%;
          color: aliceblue;
          span {
            font-size: 85%;
            line-height: 85%;
          }
        }
      }
    }

    :deep(.n-upload) {
      position: relative;
      display: flex;
      align-items: center;
      justify-content: center;
      width: v-bind(width);
      height: v-bind(height);
      overflow: hidden;
      border-radius: 4px;
      border: 1px dashed #d9d9d9;
      cursor: pointer;
      .n-upload-trigger {
        width: 100%;
        height: 100%;
        display: flex;
        align-items: center;
        justify-content: center;
      }
    }
    .n-upload__tip {
      line-height: 18px;
      font-size: 12px;
      margin-top: 7px;
    }
  }
</style>
