<template>
  <div>
    <div class="container clearfix">
      <!-- 图片显示框 -->
      <template v-if="currentImg.length">
        <div class="imgList" v-for="(item, index) in fileList" :key="item">
          <div class="imgContainer" @mouseenter="mouseEnter(index)" @mouseleave="mouseLeave(index)">
            <img :src="currentImg[index]" class="imgCover" />
            <span v-if="maskShow[index]" class="mask">
              <EyeOutlined v-if="false" @click="handlePreview(item, index)" />
              <DeleteOutlined v-if="!disabled" @click="deleteImg(index)" />
            </span>
          </div>
        </div>
      </template>
      <!-- 图片显示框 /-->
      <!-- 上传图片框/内置了input -->
      <a-spin :spinning="spinning">
        <a-upload
          v-if="count === 1 ? 'true' : fileList.length < count"
          list-type="picture-card"
          :before-upload="beforeUpload"
          @change="handleChange"
          :multiple="count != 1"
          :disabled="disabled"
          :customRequest="selfUpload"
          :showUploadList="false">
          <!-- :disabled="count == 1 && fileUuid ? true : false" -->
          <div ref="uploadRef">
            <template v-if="isLt5M && isJpgOrPngOrJpeg">
              <UploadOutlined :style="{ fontSize: '14px' }" />
              <div class="ant-upload-text">上传图片</div>
            </template>
            <template v-else>
              <LoadingOutlined />
              <div class="ant-upload-text">校验失败</div>
            </template>
          </div>
        </a-upload>
      </a-spin>
      <!-- 上传图片框/内置input /-->
    </div>
    <!-- 预览图片弹窗 -->
    <ns-modal :visible="previewVisible" :footer="null" @cancel="handleCancel">
      <img v-if="previewVisible" alt="example" style="width: 100%" :src="previewImage" />
    </ns-modal>
    <!-- 预览图片弹窗 /-->
    <!-- 错误消息提示 -->
    <div class="err-msg" v-if="!isJpgOrPngOrJpeg">
      <p>{{ fileName }} 文件上传失败</p>
      <p :style="{ color: 'red' }">请选择{{ fileType.join(',') }}的图片</p>
    </div>
    <div class="err-msg" v-if="isJpgOrPngOrJpeg ? !isLt5M : ''">
      <p>{{ fileName }} 文件上传失败</p>
      <p :style="{ color: 'red' }">请选择{{ maxSize / 1024 / 1024 }}M内的图片</p>
    </div>
    <ns-modal
      title="图片裁剪"
      style="min-width: 980px"
      :width="980"
      :visible="cropPreview"
      @cancel="cropperViewChange(false)">
      <a-row :gutter="[40]">
        <a-col :span="16" :style="{ height: '350px' }">
          <vueCropper
            ref="cropperRef"
            :img="options.img"
            :info="true"
            :infoTrue="options.infoTrue"
            :original="options.original"
            :outputType="options.outputType"
            :auto-crop="options.autoCrop"
            :fixed-box="options.fixedBox"
            :auto-crop-width="options.autoCropWidth"
            :auto-crop-height="options.autoCropHeight"
            :fixedNumber="options.fixedNumber"
            :fixed="options.fixed"
            :centerBox="options.centerBox"
            @realTime="realTime" />
        </a-col>
        <a-col :span="8">
          <div style="margin-bottom: 10px">预览区域</div>
          <div style="margin: auto; width: 280px; overflow: auto">
            <div v-html="previews.html"></div>
          </div>
        </a-col>
      </a-row>

      <template #footer>
        <a-button @click="cropperViewChange(false)">取消</a-button>
        <a-button type="primary" @click="rotateLeft">左旋转</a-button>
        <a-button type="primary" @click="rotateRight">右旋转</a-button>
        <a-button type="primary" @click="refreshCrop">复位</a-button>
        <a-button type="primary" @click="uploadAgain">重新上传</a-button>
        <a-button type="primary" @click="getCropSource">确定</a-button>
      </template>
    </ns-modal>

    <!-- 错误消息提示 /-->
  </div>
</template>
<script lang="ts">
  import {
    UploadOutlined,
    LoadingOutlined,
    EyeOutlined,
    DeleteOutlined,
  } from '@ant-design/icons-vue';
  import { defineComponent, ref, computed, reactive, watch } from 'vue';
  import { http } from '/nerv-lib/util/http';
  import { NsMessage } from '../../message';
  import VueCropper from 'vue-cropper/src/vue-cropper.vue';
  interface FileItem {
    uid: string;
    type: string;
    size: number;
    name?: string;
    status?: string;
    response?: string;
    percent?: number;
    url?: string;
    preview?: string;
    originFileObj?: any;
  }
  interface FileInfo {
    file: FileItem;
    fileList: FileItem[];
  }

  interface Options {
    img: string | ArrayBuffer | null; // 裁剪图片的地址
    info: true; // 裁剪框的大小信息
    outputSize: number; // 裁剪生成图片的质量 [1至0.1]
    outputType: 'jpeg'; // 裁剪生成图片的格式
    canScale: boolean; // 图片是否允许滚轮缩放
    autoCrop: boolean; // 是否默认生成截图框
    autoCropWidth: number; // 默认生成截图框宽度
    autoCropHeight: number; // 默认生成截图框高度
    fixedBox: boolean; // 固定截图框大小 不允许改变
    fixed: boolean; // 是否开启截图框宽高固定比例
    fixedNumber: Array<number>; // 截图框的宽高比例  需要配合centerBox一起使用才能生效
    full: boolean; // 是否输出原图比例的截图
    canMoveBox: boolean; // 截图框能否拖动
    original: boolean; // 上传图片按照原始比例渲染
    centerBox: boolean; // 截图框是否被限制在图片里面
    infoTrue: boolean; // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
  }
  // 转base64
  function getBase64(file: File) {
    return new Promise((resolve, reject) => {
      // console.log(file);
      const reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onload = () => resolve(reader.result);
      reader.onerror = (error) => reject(error);
    });
  }
  export default defineComponent({
    name: 'NsUpload',
    components: {
      UploadOutlined,
      LoadingOutlined,
      DeleteOutlined,
      EyeOutlined,
      VueCropper,
    },
    props: {
      // 上传的地址
      url: {
        type: String,
        require: true,
      },
      // 上传的图片大小
      maxSize: {
        type: Number,
        default: 5242880,
      },
      // 上传的图片类型
      fileType: {
        type: Array,
        default: () => {
          return ['jpg', 'png', 'jpeg'];
        },
      },
      // 展示图片数量
      count: {
        type: Number,
        default: 1,
      },
      // 是否禁用
      disabled: {
        type: Boolean,
        default: false,
      },
      // 上传文件类型，0-证书，1-图片，2-身份证件
      uploadType: {
        type: Number,
        default: 1,
      },
      baseImageUrl: {
        type: [String, Object],
      },
      compatibilityUuid: {
        type: [String, Object],
      },
      // 图片归类所需
      params: {
        type: [Object],
      },
      // 是否两个都emit
      whetherTwo: {
        type: Boolean,
        default: false,
      },
      cropperProps: Object,
    },
    emits: ['change'],
    setup(props, { emit }) {
      console.log(props);

      const previewVisible = ref<boolean>(false);
      const cropPreview = ref<boolean>(false);
      const isLt5M = ref<boolean>(true);
      const isJpgOrPngOrJpeg = ref<boolean>(true);
      const previewImage = ref<string | undefined>('');
      const fileList = ref<FileItem[]>([]);
      const fileUuid = ref<string>('');
      const uuidList = ref([]);
      const uuidString = ref('');
      const currentImg = ref<string[]>([]);
      const isIntImg = ref(false);
      const cropperRef: Ref<any> = ref(null);
      const previews: Ref<any> = ref({});
      // 裁剪之后的数据
      const realTime = (data: any) => {
        previews.value = data;
      };
      const options: UnwrapNestedRefs<Options> = reactive({
        img: '', // 需要剪裁的图片
        autoCrop: true, // 是否默认生成截图框
        autoCropWidth: 280, // 默认生成截图框的宽度
        autoCropHeight: 156, // 默认生成截图框的长度
        fixedBox: false, // 是否固定截图框的大小 不允许改变
        info: true, // 裁剪框的大小信息
        outputSize: 0.5, // 裁剪生成图片的质量 [1至0.1]
        outputType: 'jpeg', // 裁剪生成图片的格式
        canScale: false, // 图片是否允许滚轮缩放
        fixed: false, // 是否开启截图框宽高固定比例
        fixedNumber: [16, 9], // 截图框的宽高比例 需要配合centerBox一起使用才能生效
        full: true, // 是否输出原图比例的截图
        canMoveBox: false, // 截图框能否拖动
        original: false, // 上传图片按照原始比例渲染
        centerBox: false, // 截图框是否被限制在图片里面
        infoTrue: true, // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
        ...props.cropperProps,
      });
      const spinning = ref(false);
      const uploadRef = ref();
      const imgName = ref();
      const filterValue = (value) => {
        if (typeof value == 'object') {
          let arr = [];
          value.forEach((item) => {
            if (item.includes('ParkPic/')) {
              arr.push(item.slice(item.lastIndexOf('/') + 1));
            } else {
              arr.push(item);
            }
          });
          return arr;
        } else {
          let str = '';
          if (value) {
            if (value.includes('ParkPic/')) {
              str = value.slice(value.lastIndexOf('/') + 1);
            } else {
              str = value;
            }
          }

          return str;
        }
      };
      if (props.baseImageUrl) {
        isIntImg.value = true;
        if (typeof props.baseImageUrl == 'object') {
          uuidList.value = props.compatibilityUuid;
          // currentImg.value.concat(props.baseImageUrl);
          props.baseImageUrl.map((item, index) => {
            console.log(item);
            fileList.value.push(item);
            currentImg.value.push(item);
            // previewImage.value = props.baseImageUrl[index];
          });
          // currentImg.value.unshift(...props.baseImageUrl);
          emit(
            'change',
            props.whetherTwo
              ? [filterValue(fileList.value), filterValue(uuidList.value)]
              : filterValue(fileList.value),
          );
        } else {
          console.log(props);

          fileList.value.push({});
          uuidString.value = props.compatibilityUuid;
          fileUuid.value = props.baseImageUrl;
          currentImg.value.unshift(props.baseImageUrl);
          previewImage.value = props.baseImageUrl;
          emit(
            'change',
            props.whetherTwo
              ? [filterValue(fileUuid.value), filterValue(uuidString.value)]
              : filterValue(fileUuid.value),
          );
        }
      }

      const fileName = ref<string | undefined>('');
      const maskShow = ref<boolean[]>([]);
      const acceptType = computed(() =>
        props.fileType.map((item: String) => {
          return 'image/' + item;
        }),
      );
      watch(
        () => props.baseImageUrl,
        (e) => {
          if (e) {
            isIntImg.value = true;
            if (typeof e == 'object') {
              e.map((item, index) => {
                fileList.value.push(item);
                currentImg.value.push(item);
              });
            } else {
              fileList.value.push({});
              currentImg.value.unshift(e);
              previewImage.value = e;
            }
          }
        },
      );
      const beforeUpload = (file: FileItem) => {
        // 上传出错后，下次上传图片前，重置为true，让图片可以上传
        isLt5M.value = true;
        isJpgOrPngOrJpeg.value = true;
        // 限制图片格式,服务器不支持gif图片
        isJpgOrPngOrJpeg.value = acceptType.value.includes(file.type);
        // 如果大于指定的大小，显示错误信息
        if (file.size > props.maxSize) {
          isLt5M.value = false;
        }
        fileName.value = file.name;
        return isLt5M.value && isJpgOrPngOrJpeg.value;
      };
      const handleChange = ({ fileList: newFileList }: FileInfo) => {
        // 单图上传
        if (props.count === 1) {
          // 删除图片时，newFileList.length = 0
          // 图片大小不符合规范时，!isLt5M.value为true
          if (!isLt5M.value || !isJpgOrPngOrJpeg.value || newFileList.length - 1 < 0) {
            // 让图片不显示，也不上传
            fileList.value = [];
          } else {
            // 添加\更换图片
            // newFileList[newFileList.length - 1]的目的是为了只显示最新一张图片
            fileList.value = [newFileList[newFileList.length - 1]];
          }
        } else {
          if (!isLt5M.value || !isJpgOrPngOrJpeg.value) {
          } else {
            fileList.value.push(newFileList);
          }
        }
      };
      const refreshCrop = () => {
        cropperRef.value.refresh();
      };
      const rotateRight = () => {
        cropperRef.value.rotateRight();
      };
      const rotateLeft = () => {
        cropperRef.value.rotateLeft();
      };
      const cropperViewChange = (visible = false) => {
        cropPreview.value = visible;
      };
      const uploadAgain = () => {
        console.log(uploadRef.value);

        uploadRef.value.click();
      };

      const getCropSource = () => {
        cropperRef.value.getCropBlob(async (data: BlobPart) => {
          let file = new window.File([data], imgName.value);
          console.log(file);

          if (props.count !== 1) {
            currentImg.value.push(await getBase64(file));
          } else {
            currentImg.value = [await getBase64(file)];
          }

          const formData = new FormData();
          formData.append('file', file);
          formData.append('uploadType', props.uploadType);
          Object.keys(props.params).map((item) => {
            formData.append(item, props.params[item]);
          });

          request(formData).then(cropperViewChange(false));
        });
      };
      const selfUpload = async ({ file }) => {
        imgName.value = file.name;
        options.img = await getBase64(file);
        options.outputType = file.type.split('/')[1] == 'jpg' ? 'jpeg' : file.type.split('/')[1];
        cropperViewChange(true);
      };

      const request = (formData) => {
        const config = {
          headers: {
            'Content-Type': 'multipart/form-data',
          },
          // params: params,
        };
        spinning.value = true;
        return http
          .post(props.url, formData, config)
          .then((res) => {
            if (props.count > 1) {
              fileList.value.push(res.data.httpUrl);
              if (!uuidList.value) {
                uuidList.value = [];
              }
              uuidList.value.push(res.data.fileUuid);
              // fileList.value.push('/api/parking_merchant/objs/ParkPic/' + res.data.fileUuid);
              fileList.value.forEach((item, index) => {
                if (typeof item == 'object') {
                  fileList.value.splice(index, 1);
                }
              });
              uuidList.value.forEach((item, index) => {
                if (typeof item == 'object') {
                  uuidList.value.splice(index, 1);
                }
              });
              // props.whetherTwo;
              emit(
                'change',
                props.whetherTwo
                  ? [filterValue(fileList.value), filterValue(uuidList.value)]
                  : filterValue(fileList.value),
              );
            } else {
              fileUuid.value = res.data.httpUrl || res.data.picUuid || res.data.fileUuid;
              uuidString.value = res.data.fileUuid;
              emit(
                'change',
                props.whetherTwo
                  ? [filterValue(fileUuid.value), filterValue(uuidString.value)]
                  : filterValue(fileUuid.value),
              );
            }
            spinning.value = false;
          })
          .catch(() => {
            fileList.value.forEach((item, index) => {
              if (typeof item == 'object') {
                fileList.value.splice(fileList.value.length - 1, 1);
              }
            });
            NsMessage.error('上传失败，请重试');
            spinning.value = false;
          });
      };
      const handlePreview = async (item, index: number) => {
        if (props.count > 1) {
          previewImage.value = fileList.value[index];
        } else {
          previewImage.value = fileUuid.value;
        }
        previewVisible.value = true;
      };
      const deleteImg = (index: number) => {
        currentImg.value.splice(index, 1);
        fileList.value.splice(index, 1);
        uuidList.value && uuidList.value.splice(index, 1);
        uuidString.value = '';
        fileUuid.value = '';
        isIntImg.value = false;
        if (props.count == 1) {
          emit(
            'change',
            props.whetherTwo
              ? [filterValue(fileUuid.value), filterValue(uuidString.value)]
              : filterValue(fileUuid.value),
          );
        } else {
          emit(
            'change',
            props.whetherTwo
              ? [filterValue(fileList.value), filterValue(uuidList.value)]
              : filterValue(fileList.value),
          );
        }
      };
      const handleCancel = () => {
        previewVisible.value = false;
      };
      const mouseEnter = (index: number) => {
        maskShow.value[index] = true;
      };
      const mouseLeave = (index: number) => {
        maskShow.value[index] = false;
      };
      return {
        previewVisible,
        previewImage,
        fileList,
        isLt5M,
        isJpgOrPngOrJpeg,
        fileName,
        currentImg,
        maskShow,
        fileUuid,
        selfUpload,
        handleCancel,
        handlePreview,
        handleChange,
        beforeUpload,
        mouseEnter,
        mouseLeave,
        deleteImg,
        spinning,
        options,
        cropperRef,
        realTime,
        previews,
        cropPreview,
        rotateRight,
        refreshCrop,
        rotateLeft,
        cropperViewChange,
        getCropSource,
        uploadRef,
        uploadAgain,
      };
    },
  });
</script>
<style lang="less" scoped>
  :deep(.ant-upload-picture-card-wrapper) {
    width: unset !important;
  }
  :deep(.ant-upload-picture-card-wrapper .ant-upload.ant-upload-select-picture-card) {
    margin: 0;
    width: 64px !important;
    height: 64px !important;
    border: 1px solid #d9d9d9;
  }
  :deep(.ant-upload-picture-card-wrapper .ant-upload.ant-upload-select-picture-card:hover) {
    border-color: #d9d9d9;
  }
  :deep(.ant-upload-select-picture-card i) {
    font-size: 32px;
    color: #999;
  }
  :deep(.ant-upload-select-picture-card .ant-upload-text) {
    color: #666;
    font-size: 12px;
  }
  :deep(.ant-upload-picture-card-wrapper .ant-upload-list-picture-card-container) {
    width: 88px;
    height: 64px !important;
  }
  :deep(.ant-upload-picture-card-wrapper .ant-upload-list-picture-card .ant-upload-list-item) {
    margin: 0;
    padding: 0;
    width: 64px !important;
    height: 64px !important;
  }
  .title,
  .err-msg {
    text-align: left;
  }
  .err-msg p {
    margin: 0;
  }
  .container {
    display: flex;
    justify-content: flex-start;
    flex-wrap: wrap;
  }
  .imgList {
    display: flex;
  }
  .imgContainer {
    margin-right: 16px;
    border: 1px solid #d9d9d9;
    position: relative;
    margin-bottom: 8px;
  }
  .imgCover {
    width: 64px;
    height: 64px;
    object-fit: contain;
  }
  .imgContainer .mask {
    position: absolute;
    top: 0;
    left: 0;
    width: 64px !important;
    height: 64px !important;
    background: rgba(0, 0, 0, 0.5);
    text-align: center;
    line-height: 64px !important;
  }
  .mask .anticon-eye {
    color: white;
    margin-right: 18px;
  }
  .mask .anticon-eye:hover {
    color: #00acff;
    margin-right: 18px;
  }
  .mask .anticon-delete {
    color: white;
  }
  .mask .anticon-delete:hover {
    color: #00acff;
  }
</style>
