<script setup lang="ts">
import { ElMessage } from 'element-plus'
import type { ImageUploadProps } from './props'
import { imageUploadEmits } from './props'
import type { UploadItem } from './types'
import { uuid } from '@/utils/utils'
import axios, { type AxiosProgressEvent } from 'axios'

const emits = defineEmits(imageUploadEmits)

const props = withDefaults(defineProps<ImageUploadProps>(), {
  fileName: 'file',
  autoUpload: true,
  preview: true,
  limit: 9,
  fileSize: 5,
  iconSize: 28
})

const images = ref<UploadItem[]>([])

// 是否可上传
const isUpload = computed<boolean>(() => {
  return (
    !props.disabled &&
    !(typeof props.limit === 'number' && props.limit > 0 && images.value != null && images.value.length >= props.limit)
  )
})

// 检查图片是否全部上传完毕
const checkUpload = () => {
  return images.value.length == 0 || (images.value.length && !images.value.some(x => x.status != 'success'))
}

// 预览图片列表
const previewList = computed(() => {
  if (!props.preview) {
    return []
  }
  return images.value?.map(x => x.url) || []
})

/* 选择文件 */
const onUpload = (file: File) => {
  if (!isUpload.value || props.disabled) {
    return false
  }
  if (!file.type.startsWith('image')) {
    ElMessage.error('只能选择图片')
    return false
  }
  if (file.size / 1024 / 1024 > props.fileSize) {
    ElMessage.error(`大小不能超过 ${props.fileSize}MB`)
    return false
  }

  const item: UploadItem = {
    key: uuid(),
    name: file.name,
    status: void 0,
    progress: 0,
    file
  }
  item.url = window.URL.createObjectURL(file)

  images.value.push(item)
  // 是否自动上传
  if (props.autoUpload) {
    // 自动上传最后一个文件
    uploadItem(images.value.at(-1) as UploadItem)
  }
  return false
}

/* 上传文件 */
const uploadItem = (item: UploadItem) => {
  if (!props.action && typeof props.uploadFunction != 'function') {
    console.log('请传入action路径或者uploadFunction上传方法')
    return false
  }
  // 如果配置了上传路径，TODO：awint
  if (props.action) {
    const formData = new FormData()
    formData.append(props.fileName, item.file as File)
    axios
      .post(props.action, formData, {
        onUploadProgress: (e: AxiosProgressEvent) => {
          if (e.total != null) {
            item.progress = (e.loaded / e.total) * 100
          }
        }
      })
      .then(res => {
        item.status = 'success'
        item.url = res.data
      })
      .catch(() => {
        item.status = 'danger'
      })
  } else {
    // 自定义方法上传
    if (typeof props.uploadFunction != 'function') return false
    ;(props.uploadFunction as Function)(item)
  }
}

/* 手动上传文件 */
const submit = () => {
  if (images.value.length) {
    images.value.forEach(item => {
      item.status != 'success' && uploadItem(item)
    })
  }
}

/* 删除图片 */
const onRemove = (index: number) => {
  emits('remove', images.value[index])
  images.value.splice(index, 1)
}

/* 重新上传 */
const onRetry = (index: number) => {
  uploadItem(images.value[index])
}

/* 修改modelValue */
const updateModelValue = (items: any) => {
  emits('update:modelValue', items.map((x: UploadItem) => x.url).join(',') || '')
}

watch(
  () => props.modelValue,
  () => {
    // 判断modelValue存不存在，存在就是成功，不存在就没有
    if (props.modelValue && props.modelValue != undefined) {
      // 传进来的数据转换成内部需要数据
      const formatData = typeof props.modelValue === 'string' ? props.modelValue.split(',').filter(x => x) : []
      const datas: UploadItem[] = formatData.map((x: string) => {
        return {
          key: uuid(),
          url: x,
          status: 'success',
          name: x.split('/').pop()
        }
      })
      images.value = datas
    }
  },
  { immediate: true }
)

watch(
  images,
  () => {
    updateModelValue(images.value)
  },
  {
    deep: true
  }
)

defineExpose({
  checkUpload,
  submit
})
</script>

<template>
  <div class="upload-container">
    <div class="upload-image" v-for="(item, index) in images" :key="item.key">
      <el-image :src="item.url" :preview-src-list="previewList" :initial-index="index" fit="cover"></el-image>
      <div v-if="!disabled" class="upload-remove" @click.stop="onRemove(index)">
        <el-icon size="14">
          <Close />
        </el-icon>
      </div>
      <div v-if="item.status === 'uploading' || item.status === 'danger'" class="upload-progress">
        <slot name="progress" :item="item">
          <div class="upload-text">
            {{ item.status == 'danger' ? '上传失败' : '上传中' }}
          </div>
          <el-progress
            :showText="false"
            v-bind="progressProps || {}"
            :percentage="item.progress"
            :status="item.status === 'danger' ? 'exception' : void 0"
          />
          <div v-if="!disabled">
            <div v-if="item.status === 'danger'" class="upload-retry" @click.stop="onRetry(index)">
              <el-icon>
                <Refresh />
              </el-icon>
            </div>
          </div>
        </slot>
      </div>
    </div>
    <div>
      <el-upload
        action=""
        accept="image/*"
        :multiple="multiple"
        :disabled="disabled"
        :show-file-list="false"
        :beforeUpload="onUpload"
        :drag="drag"
        v-if="isUpload"
      >
        <div class="upload-plus">
          <el-icon :size="iconSize">
            <Plus />
          </el-icon>
        </div>
      </el-upload>
    </div>
  </div>
</template>

<style lang="scss" scoped>
.upload-container {
  display: flex;
  flex-wrap: wrap;
  .upload-image {
    position: relative;
    width: 100px;
    height: 100px;
    border: 1px dashed #dcdfe6;
    border-radius: var(--el-border-radius-base);
    overflow: hidden;
    margin: 0px 8px 8px 0;
    cursor: pointer;
    .upload-remove {
      width: 20px;
      height: 20px;
      display: flex;
      justify-content: center;
      align-items: center;
      position: absolute;
      right: 0px;
      top: 0px;
      background-color: rgba($color: #000000, $alpha: 0.3);
      color: #fff;
      border-radius: 0px 0px 0px var(--el-border-radius-round);
      z-index: 9;
      &:hover {
        background-color: var(--el-color-danger);
      }
      .el-icon {
        margin-top: -5px;
        margin-left: 2px;
      }
    }
    .upload-progress {
      position: absolute;
      width: 100%;
      height: 100%;
      left: 0px;
      top: 0px;
      display: flex;
      flex-direction: column;
      justify-content: center;
      padding: 10px;
      background-color: rgba($color: #000000, $alpha: 0.4);
      color: #fff;
      box-sizing: border-box;
      .upload-text {
        text-align: center;
        margin-bottom: 3px;
        font-size: 12px;
      }
      .upload-retry {
        text-align: center;
        margin-top: 5px;
      }
    }
    .el-image {
      width: 100%;
      height: 100%;
    }
  }
  .upload-plus {
    width: 100px;
    height: 100px;
    display: flex;
    justify-content: center;
    align-items: center;
    color: var(--el-color-info);
    border: 1px dashed #dcdfe6;
    border-radius: var(--el-border-radius-base);
    &:not(.disabled):hover {
      border-color: var(--el-color-primary);
    }
  }
  :deep(.el-upload-dragger) {
    width: auto;
    height: auto;
    padding: 0px;
    margin: 0px;
    border-color: transparent;
    border-width: 0px;
    border-radius: 0px;
    background-color: transparent;
    &.is-dragover {
      outline: 1px dashed var(--el-color-primary);
      background-color: var(--el-color-primary-light-9);
    }
  }
}
</style>
