<template>
  <div class="cus-form-column-upload-com"
       :class="{
         'cus-form-column-upload-com_view': isViewCpt,
         'cus-form-column-upload-com_list': layout === 'list',
         'cus-form-column-upload-com_card': layout === 'card',
         'cus-form-column-upload-com_limit-no-upload': isLimitCpt && limitNoUpload !== false
       }"
       v-loading="loading"
       :element-loading-background="elementLoadingBackground">
    <el-upload
        class="el-upload-com"
        ref="ref_upload"
        v-bind="$attrs"
        :file-list="paramVal"
        action="#"
        :list-type="layout === 'card' ? 'picture-card' : 'text'"
        :http-request="handleRequest"
        :before-upload="handleBeforeUpload"
        :accept="acceptTypeCpt"
        :limit="limit > 0 ? limit : null"
        :on-remove="handleRemove"
        :disabled="isLimitCpt"
    >
      <el-tooltip :content="acceptTooltipCpt" placement="top" popper-class="upload-tooltip-popper">
        <template v-if="layout === 'card'">
          <div class="upload-layout-card __hover" :class="{'limit-disabled': isLimitCpt}">
            <SvgIcon name="add" color="#0062E9" size="24"/>
            <div>选择{{acceptTypeFormatCpt}}</div>
            <div v-if="limit > 0">(最多{{ limit }}个)</div>
          </div>
        </template>
        <template v-else>
          <div class="upload-layout-list_button" :class="{'limit-disabled': isLimitCpt}">
            <SvgIcon name="add" color="#ffffff" size="14"/>
            选择{{acceptTypeFormatCpt}}
            <template v-if="limit > 0">(最多{{ limit }}个)</template>
          </div>
        </template>
      </el-tooltip>
      <template #file="{file}">
        <template v-if="layout === 'card'">
          <el-tooltip placement="top" :content="file[nameKey] ?? file[urlKey]">
            <div class="upload-layout-card_item" :class="{'item-view': isViewCpt || !delRule(file)}" @mouseenter="file.hover = true" @mouseleave="file.hover = false">
              <template v-if="validImgByUrl(file[urlKey])">
                <img class="img __hover" :src="$util.proxyNginxUrl(file[urlKey])" @click="viewImg(file[urlKey])"/>
              </template>
              <template v-else-if="validVideoByUrl(file[urlKey])">
                <video class="video __hover" controls :src="$util.proxyNginxUrl(file[urlKey])"/>
              </template>
              <template v-else>
                <img class="file __hover" :src="getFileImgByUrl(file[urlKey])" @click="downloadFileByUrl(file[urlKey], file[nameKey])"/>
              </template>
              <img class="del __hover" src="@/assets/images/cus/file-del.png" v-if="file.hover && !isViewCpt && delRule(file)" @click.stop="ref_upload.handleRemove(file)"/>
            </div>
          </el-tooltip>
        </template>
        <template v-else>
          <div class="upload-layout-list_item __hover" :class="{'item-view': isViewCpt || !delRule(file)}" @click="validImgByUrl(file[urlKey]) ? viewImg(file[urlKey]) : downloadFileByUrl(file[urlKey], file[nameKey])">
            <img class="file-type-img" :src="getFileImgByUrl(file[urlKey])"/>
            <CusEllipsis v-if="file[nameKey] ?? file[urlKey]" class="label" :value="file[nameKey] ?? file[urlKey]"/>
            <SvgIcon v-if="!isViewCpt && delRule(file)" class="close" name="close_2" size="12" @click.stop="ref_upload.handleRemove(file)"/>
          </div>
        </template>
      </template>
    </el-upload>
    <el-image-viewer
        v-if="currentImg.show"
        :zoom-rate="2"
        :max-scale="10"
        :min-scale="0.2"
        :hide-on-click-modal="true"
        :url-list="[currentImg.url]"
        :initial-index="0"
        @close="imageClose"
    />
  </div>
</template>

<script lang="ts">
import {
  defineComponent,
  computed,
  onMounted,
  ref,
  reactive,
  watch,
  getCurrentInstance,
  ComponentInternalInstance,
  toRefs,
  nextTick, onBeforeMount, inject
} from 'vue'
import {useStore} from 'vuex'
import {useRouter, useRoute} from 'vue-router'
import {ElMessage} from "element-plus";
import FileDefaultImg from '@/assets/images/file-type/file-type_default.png'
import FilePDFImg from '@/assets/images/file-type/file-type_pdf.png'
import FilePPTImg from '@/assets/images/file-type/file-type_ppt.png'
import FileRARImg from '@/assets/images/file-type/file-type_rar.png'
import FileTXTImg from '@/assets/images/file-type/file-type_txt.png'
import FileWORDImg from '@/assets/images/file-type/file-type_word.png'
import FileEXCELImg from '@/assets/images/file-type/file-type_excel.png'

export default defineComponent({
  name: '',
  components: {},
  props: {
    param: {},
    label: {},
    limit: {
      default: 0
    },
    type: {default: 'all', validator(val: string) {
      return ['all', 'img', 'file'].includes(val)
    }},
    layout: {default: 'list', validator(val: string) {
      return ['list', 'card'].includes(val)
    }},
    acceptType: {default: '', type: String},
    acceptMax: {default: 0, type: Number},
    acceptFunc: {default: () => (options) => {}},
    view: {default: null},
    delRule: {default: () => () => false},
    cardWidth: {default: '147px'},
    cardHeight: {default: '128px'},
    urlKey: {default: 'url'},
    nameKey: {default: 'name'},
    limitNoUpload: {default: false}
  },
  setup(props, { emit }) {
    const store = useStore();
    const router = useRouter();
    const route = useRoute();
    const that = (getCurrentInstance() as ComponentInternalInstance).appContext.config.globalProperties
    const state = reactive({
      paramVal: <any>props.param,
      loading: false,
      elementLoadingBackground: inject('element-loading-background', null),
      currentImg: {
        show: false,
        url: ''
      },
      formView: inject('form-view', false),
    })
    watch(() => state.paramVal, (n) => {
      emit('emitParam', n)
    })
    watch(() => props.param, (n) => {
      state.paramVal = n
    })
    const isViewCpt = computed(() => {
      return that.$util.isValue(props.view) ? props.view : state.formView
    })
    const ref_upload = ref()
    const validImgByUrl = (url) => {
      if (url) {
        const imgList = ['bmp', 'jpg','jpeg', 'png', 'tif', 'gif', 'pcx', 'tga', 'exif', 'fpx', 'svg', 'psd', 'cdr', 'pcd', 'dxf', 'ufo', 'eps', 'ai', 'raw', 'WMF', 'webp', 'avif', 'apng', 'jfif'];
        //进行图片匹配
        let result = [...imgList.map(v => v.toLowerCase()), ...imgList.map(v => v.toUpperCase())].some((t) => url.includes('.' + t)) || url.includes('/ngx/proxy')  || url.includes('/pic?');
        return result
      }
      return false
    }
    const validVideoByUrl = (url) => {
      if (url) {
        const videoList = ['avi', 'mp4', 'mov', 'wmv', 'flv', 'mkv', 'mpeg', '3gp'];
        //进行图片匹配
        let result = [...videoList.map(v => v.toLowerCase()), ...videoList.map(v => v.toUpperCase())].some((t) => url.includes('.' + t));
        return result
      }
      return false
    }
    const acceptTypeCpt = computed(() => {
      let str = ''
      if (that.$util.isValue(props.acceptType)) {
        str += props.acceptType
      } else {
        if (props.type === 'all') {
          str += store.state.app.globalConfig.imgType
          str += ',' + store.state.app.globalConfig.fileType
        } else if (props.type === 'img') {
          str += store.state.app.globalConfig.imgType
        } else if (props.type === 'file') {
          str += store.state.app.globalConfig.fileType
        }
      }
      return str
    })
    const acceptTypeFormatCpt = computed(() => {
      let str = ''
      if (that.$util.isValue(props.acceptType)) {
        str += `自定义文件`
      } else {
        if (props.type === 'all') {
          str += `图片、文件`
        } else if (props.type === 'img') {
          str += `图片`
        } else if (props.type === 'file') {
          str += `文件`
        }
      }
      return str
    })
    const acceptTooltipCpt = computed(() => {
      let str = ''
      if (that.$util.isValue(props.acceptType)) {
        str += `只支持大小在0~${props.acceptMax}M内，格式为${props.acceptType}的自定义文件`
      } else {
        if (props.type === 'all') {
          str += `只支持大小在0~${store.state.app.globalConfig.imgMax}M内，格式为${store.state.app.globalConfig.imgType}的图片，`
          str += `或大小在0~${store.state.app.globalConfig.fileMax}M内，格式为${store.state.app.globalConfig.fileType}的文件`
        } else if (props.type === 'img') {
          str += `只支持大小在0~${store.state.app.globalConfig.imgMax}M内，格式为${store.state.app.globalConfig.imgType}的图片`
        } else if (props.type === 'file') {
          str += `只支持大小在0~${store.state.app.globalConfig.fileMax}M内，格式为${store.state.app.globalConfig.fileType}的文件`
        }
      }
      if (isLimitCpt.value) {
        str = `最多上传${props.limit}个${acceptTypeFormatCpt.value}`
      }
      return str
    })
    const isLimitCpt = computed(() => {
      return props.limit > 0 && props.limit === state.paramVal.length
    })
    const handleBeforeUpload = (file) => {
      if (that.$util.isValue(props.acceptType)) {
        if (file.size / (1024 * 1024) > props.acceptMax) {
          ElMessage.warning(`自定义文件大小不可超过${props.acceptMax}M`)
          return false
        } else if (!props.acceptType.split(',').some(v => file.name.includes(v))) {
          ElMessage.warning(`自定义文件类型仅支持${props.acceptType}`)
          return false
        }
      } else {
        if (props.type === 'img') {
          if (file.size / (1024 * 1024) > store.state.app.globalConfig.imgMax) {
            ElMessage.warning(`图片大小不可超过${store.state.app.globalConfig.imgMax}M`)
            return false
          } else if (!store.state.app.globalConfig.imgType.split(',').some(v => file.name.includes(v))) {
            ElMessage.warning(`图片类型仅支持${store.state.app.globalConfig.imgType}`)
            return false
          }
        } else if (props.type === 'file') {
          if (file.size / (1024 * 1024) > store.state.app.globalConfig.fileMax) {
            ElMessage.warning(`文件大小不可超过${store.state.app.globalConfig.fileMax}M`)
            return false
          } else if (!store.state.app.globalConfig.fileType.split(',').some(v => file.name.includes(v))) {
            ElMessage.warning(`文件类型仅支持${store.state.app.globalConfig.fileType}`)
            return false
          }
        } else if (props.type === 'all') {
          if (!acceptTypeCpt.value.split(',').some(v => file.name.includes(v))) {
            ElMessage.warning(`文件或图片类型仅支持${acceptTypeCpt.value}`)
            return false
          } else {
            if (store.state.app.globalConfig.imgType.split(',').some(v => file.name.includes(v))) {
              if (file.size / (1024 * 1024) > store.state.app.globalConfig.imgMax) {
                ElMessage.warning(`图片大小不可超过${store.state.app.globalConfig.imgMax}M`)
                return false
              }
            } else if (store.state.app.globalConfig.fileType.split(',').some(v => file.name.includes(v))) {
              if (file.size / (1024 * 1024) > store.state.app.globalConfig.fileMax) {
                ElMessage.warning(`文件大小不可超过${store.state.app.globalConfig.fileMax}M`)
                return false
              }
            }
          }
        }
      }
      return file
    }
    const handleRequest = async (options) => {
      state.loading = true
      if (that.$util.isValue(props.acceptType)) {
        const result: any = await props.acceptFunc(options)
        if (result?.[props.urlKey] && result?.[props.nameKey]) {
          state.paramVal = [...state.paramVal, result]
        } else {
          state.paramVal = [...state.paramVal]
        }
        state.loading = false
      } else {
        if (store.state.app.globalConfig.imgType.split(',').some(v => options.file.name.includes(v))) {
          const formData = new FormData();
          formData.append("file", options.file);
          that.$api.uploadPicOnly(formData).then(res => {
            if (res?.code === 200) {
              state.paramVal = [...state.paramVal, {
                [props.urlKey]: res.imgPath,
                [props.nameKey]: res.fileName
              }]
            }
            state.loading = false
          }).catch(() => {
            state.loading = false
          })
        } else if (store.state.app.globalConfig.fileType.split(',').some(v => options.file.name.includes(v))) {
          const formData = new FormData();
          formData.append("file", options.file);
          that.$api.uploadFile(formData).then(res => {
            if (res?.code === 200) {
              state.paramVal = [...state.paramVal, {
                [props.urlKey]: res.url,
                [props.nameKey]: res.fileName
              }]
            }
            state.loading = false
          }).catch(() => {
            state.loading = false
          })
        }
      }
    }
    const fileTypeMapper = new Map([
      ['doc', FileWORDImg],
      ['docx', FileWORDImg],
      ['ppt', FilePPTImg],
      ['pptx', FilePPTImg],
      ['pdf', FilePDFImg],
      ['xls', FileEXCELImg],
      ['xlsx', FileEXCELImg],
      ['txt', FileTXTImg],
      ['rar', FileRARImg],
      ['zip', FileRARImg],
    ])
    const getFileImgByUrl = (url) => {
      if (url) {
        const regex = /\.([^.]*)$/;
        const match = url.match(regex);
        const t = match ? match[1] : '';
        if (t && fileTypeMapper.has(t)) {
          return fileTypeMapper.get(t)
        }
      }
      return FileDefaultImg
    }
    const handleRemove = (file, files) => {
      state.paramVal = files
    }
    const downloadFileByUrl = (url, name) => {
      ElMessage.info('开始下载！')
      const xhr = new XMLHttpRequest();
      xhr.open('GET', url, true);
      xhr.responseType = 'blob';
      xhr.onload = function () {
        if (xhr.status === 200) {
          const blob = xhr.response;
          const url = window.URL.createObjectURL(blob);
          const a = document.createElement('a');
          a.href = url;
          a.download = name || 'default';
          a.click();
          ElMessage.success('下载成功！')
          window.URL.revokeObjectURL(url);
        }
      };
      xhr.send();
    }
    const viewImg = (url) => {
      state.currentImg.url = url
      state.currentImg.show = true
    }
    const imageClose = () => {
      state.currentImg.show = false
      state.currentImg.url = ''
    }
    onMounted(() => {
      state.loading = true
      store.dispatch('app/LOAD_GLOBAL_CONFIG').then(() => {
        state.loading = false
      })
    })
    return {
      ...toRefs(state),
      acceptTypeCpt,
      handleBeforeUpload,
      handleRequest,
      handleRemove,
      validImgByUrl,
      validVideoByUrl,
      acceptTooltipCpt,
      acceptTypeFormatCpt,
      getFileImgByUrl,
      ref_upload,
      downloadFileByUrl,
      isLimitCpt,
      viewImg,
      imageClose,
      isViewCpt
    }
  },
})
</script>

<style scoped lang="scss">
.cus-form-column-upload-com {
  $uploadWidth: v-bind(cardWidth);
  $uploadHeight: v-bind(cardHeight);
  width: 100%;
  position: relative;
  .el-upload-com {
    :deep(.el-upload) {
      .upload-layout-list_button {
        height: 32px;
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 0 18px;
        background-color: #0062E9;
        border-radius: 4px;
        font-size: 14px;
        font-family: Microsoft YaHei;
        font-weight: 400;
        color: #FFFFFF;
        .svg-icon {
          margin-right: 7px;
        }
        &.limit-disabled {
          cursor: no-drop;
          opacity: 0.5;
        }
      }

      .upload-layout-card {
        width: 100%;
        height: 100%;
        display: flex;
        flex-direction: column;
        line-height: 1;
        align-items: center;
        justify-content: center;
        background-color: rgba(46, 129, 255, 0.04);
        .svg-icon {
          margin-bottom: 10px;
          opacity: 0.5;
        }
        > div {
          font-size: 14px;
          font-family: PingFang SC-Regular, PingFang SC;
          font-weight: 400;
          color: rgba(0,98,233,0.5);
        }
        &.limit-disabled {
          cursor: no-drop;
          opacity: 0.5;
        }
      }
    }
    :deep(.el-upload-list) {
      margin: 0;
      .el-upload-list__item {
        transition: none;
        .upload-layout-list_item {
          display: flex;
          align-items: center;
          padding: 0 6px;
          .file-type-img {
            width: 14px;
            margin: 0 6px 0 6px;
          }
          .label {
            width: calc(100% - 6px - 6px - 14px - 30px);
          }
          .close {
            margin-left: auto;
          }
          &.item-view {
            .label {
              width: calc(100% - 6px - 6px - 14px);
            }
          }
        }
        .upload-layout-card_item {
          width: 100%;
          height: 100%;
          position: relative;
          .img {
            width: 100%;
            height: 100%;
          }
          .video {
            width: 100%;
            height: 100%;
            object-fit: fill;
          }
          .file {
            width: 100%;
            height: 100%;
          }
          .del {
            position: absolute;
            top: 0;
            right: 0;
            z-index: 2;
            width: 20px;
            height: 20px;
          }
        }
      }
    }
  }
  &.cus-form-column-upload-com_view, &.cus-form-column-upload-com_limit-no-upload {
    .el-upload-com {
      :deep(.el-upload) {
        display: none;
      }
    }
  }
  &.cus-form-column-upload-com_card {
    .el-upload-com {
      :deep(.el-upload) {
        width: $uploadWidth;
        height: $uploadHeight;
      }
      :deep(.el-upload-list) {
        .el-upload-list__item {
          width: $uploadWidth;
          height: $uploadHeight;
        }
      }
    }
  }
}
</style>
