<script lang="ts" setup>
import { ElMessage } from 'element-plus'
import { Plus } from '@element-plus/icons-vue'
import type { UploadProps, UploadUserFile, UploadFile, UploadFiles, UploadInstance } from 'element-plus'
import md5 from 'blueimp-md5'
import axios from 'axios'
import { getSignature } from '@/api/base'

interface Props {
  modelValue: string | { name: string; url: string }[]
  prefix?: string
  size?: number
  beforeUpload?: UploadProps['beforeUpload']
  onChange?: (uploadFile: UploadFile, uploadFiles: UploadFiles, uploadRef?: UploadInstance) => void
}

const props = withDefaults(defineProps<Props>(), {
  prefix: 'upload/saas-learn/'
})

const emit = defineEmits(['update:modelValue', 'success'])

const uploadRef = ref<UploadInstance>()

const fileList = ref<UploadUserFile[]>([])

watch(
  () => props.modelValue,
  value => {
    fileList.value = Array.isArray(value) ? value.map(item => ({ ...item })) : []
  }
)

const showFileList = computed(() => {
  return Array.isArray(props.modelValue)
})

// 自定义上传
const handleHttpRequest: UploadProps['httpRequest'] = async xhr => {
  const name = xhr.file.name
  const key = `${props.prefix}${md5(name + Date.now())}.${name.split('.').pop()}`
  const signature: Record<string, any> = await getSignature()
  const params = {
    key,
    host: signature.host,
    OSSAccessKeyId: signature.accessid,
    policy: signature.policy,
    signature: signature.signature,
    success_action_status: '200',
    url: `${signature.host}/${key}`,
    file: xhr.file
  }
  return axios
    .post(params.host || 'https://webapp-pub.ezijing.com', Object.assign(params, xhr.data), {
      headers: { 'Content-Type': 'multipart/form-data' },
      onUploadProgress(progress: any) {
        progress.percent = progress.total > 0 ? (progress.loaded / progress.total) * 100 : 0
        xhr.onProgress(progress)
      }
    })
    .then(() => {
      return params
    })
}

// 文件改变
const handleChange: UploadProps['onChange'] = (uploadFile, uploadFiles) => {
  props.onChange && props.onChange(uploadFile, uploadFiles, uploadRef.value)
}

// 上传之前
const handleBeforeUpload: UploadProps['beforeUpload'] = file => {
  if (props.size && file.size > props.size) {
    ElMessage.error(`文件大小不能超过${props.size / 1024 / 1024}M`)
    return false
  }
  if (props.beforeUpload) {
    return props.beforeUpload(file)
  }
}

// 上传限制
const handleExceed: UploadProps['onExceed'] = () => {
  ElMessage.warning('文件超出个数限制')
}

// 上传成功
const handleSuccess: UploadProps['onSuccess'] = (response, uploadFile: any, uploadFiles) => {
  if (!uploadFiles.every(item => item.status === 'success')) return
  uploadFile.type = uploadFile.raw?.type
  uploadFile.url = response.url
  const value = showFileList.value
    ? uploadFiles.map((item: any) => {
        return {
          name: item.name,
          url: item.url,
          size: item.size || item.raw?.size,
          type: item.type || item.raw?.type
        }
      })
    : response.url
  emit('update:modelValue', value)
  emit('success', uploadFile, uploadFiles)
}

// 删除
const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
  const value = showFileList.value ? uploadFiles : ''
  emit('update:modelValue', value)
}

// 预览
const handlePreview: UploadProps['onPreview'] = uploadFile => {
  window.open(uploadFile.url)
}
</script>

<template>
  <el-upload
    action="https://webapp-pub.ezijing.com"
    :show-file-list="showFileList"
    :http-request="handleHttpRequest"
    :before-upload="handleBeforeUpload"
    :on-change="handleChange"
    :on-exceed="handleExceed"
    :on-remove="handleRemove"
    :on-preview="handlePreview"
    :on-success="handleSuccess"
    :file-list="fileList"
    class="uploader"
    ref="uploadRef">
    <slot>
      <template v-if="showFileList">
        <template v-if="$attrs['list-type'] === 'picture-card'">
          <el-icon><Plus /></el-icon>
        </template>
        <template v-else>
          <el-button size="default" round>点击上传</el-button>
        </template>
      </template>
      <div class="avatar-uploader" v-else>
        <el-image :src="(modelValue as string)" fit="contain" v-if="modelValue" />
        <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
      </div>
    </slot>
    <template #tip>
      <div class="el-upload__tip"><slot name="tip"></slot></div>
    </template>
  </el-upload>
</template>

<style lang="scss">
.uploader {
  flex: 1;
  overflow: hidden;
}
.avatar-uploader {
  width: 178px;
  height: 178px;
  border: 1px dashed var(--el-border-color);
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition: var(--el-transition-duration-fast);
  .el-image {
    width: 100%;
    height: 100%;
  }
}
.avatar-uploader:hover {
  border-color: var(--el-color-primary);
}
.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 100%;
  height: 100%;
  text-align: center;
}
</style>
