<!--
 * @Author: xiejiang
 * @Date: 2024-11-05 10:14:56
 * @LastEditors: xiejiang
 * @LastEditTime: 2024-11-26 17:30:39
 * @Description: 文件上传
 * Copyright（c）2024 by 好老师教育科技有限公司 All right Reserved.
-->
<template>
  <div class="upload-box">
    <t-upload :theme="theme" v-model="_fileList" :multiple="multiple" :disabled="disabled" :size-limit="{
      size: fileSize, unit: 'MB', message: `文件大小不超过${fileSize} MB`
    }" :request-method="handleHttpUpload" :on-success="uploadSuccess" :on-fail="uploadError" :draggable="drag"
      :accept="accept">
      <slot name="trigger"></slot>
      <template #fileListDisplay>
        <div v-if="_fileList && _fileList.length" class="list-custom">
          <div v-for="(item, index) in _fileList" :key="index">
            <template v-if="item.show">
              <!-- 上传进度 -->
              <t-progress v-if="item.showPercentage && percentage" theme="line"
                :color="{ from: '#0052D9', to: '#00A870' }" :percentage="item.percentage" :status="'active'" />

              <template v-else>
                <!-- 图片预览 -->
                <ImageViewer v-if="item.type === 'image' && item.url" :is-del="true" :images="[item.url]"
                  @onDel="onFileDel(item)" />
                <p v-else>
                  <span class="mr-[8px]">{{ item.name }}</span>
                  <CloseCircleIcon class="cursor-pointer" @click="onFileDel(item)" />
                </p>
              </template>
            </template>
          </div>
        </div>
      </template>
    </t-upload>

    <!-- 自定义提示 -->
    <slot name="tip"></slot>
  </div>
</template>

<script setup lang="ts" name="upload">
import axios from 'axios'
import { generateUUID } from '@/utils'
import { isImageFile } from '@/utils/is'
import { signature } from '@/api/modules/upload'
import { NotifyPlugin } from 'tdesign-vue-next'
import type { UploadFile, RequestMethodResponse } from 'tdesign-vue-next'

interface UploadFileProps {
  fileList: UploadFile[]
  theme?: 'custom'
  accept?: string
  showImageFileName?: Boolean // 是否显示图
  max?: number // 最大支持多少上传
  unit?: 'B' | 'KB' | 'MB' | 'GB' // 文件大小单位
  multiple?: boolean // 是否支持持多文件上传
  drag?: boolean // 是否支持拖拽上传 ==> 非必传（默认为 true）
  disabled?: boolean // 是否禁用上传组件 ==> 非必传（默认为 false）
  fileSize?: number // 文件大小限制 ==> 非必传（默认为 5M）
  height?: string // 组件高度 ==> 非必传（默认为 150px）
  width?: string // 组件宽度 ==> 非必传（默认为 150px）
  isReplace?: boolean // 单张上传时， 是否覆盖上一张图片
  percentage?: boolean // 是否显示进度条
}

// 接受父组件参数
const props = withDefaults(defineProps<UploadFileProps>(), {
  fileList: () => [],
  drag: false,
  disabled: false,
  multiple: false,
  max: 0,
  fileSize: 5,
  isReplace: false,
  unit: 'MB',
  accept: '*',
  percentage: true,
  height: '150px',
  width: '150px'
})

const _fileList = ref<UploadFile[]>(props.fileList)

// 生成组件唯一id
const uuid = ref('id-' + generateUUID())

/**
 * @description 文件上传
 * @param response 上传响应结果
 * @param uploadFile 上传的文件
 * */
interface UploadEmits {
  (e: 'update:fileList', value: UploadFile[]): void
  (e: 'upload-success', value: UploadFile[]): void
}
const emit = defineEmits<UploadEmits>()

// 获取上传签名
const getSignature = (name: string) => {
  return signature(name, true)
}
interface SignatureData {
  accessId: string
  policy: string
  signature: string
  dir: string
  host: string
}
const handleHttpUpload = async (files: UploadFile): Promise<RequestMethodResponse> => {
  const file = files[0]
  let requestMethodResponse: RequestMethodResponse = { status: 'fail', error: '未知错误', response: {} }
  const fileName = uuid.value + file.name
  let signatureData = {} as SignatureData
  try {
    const { data } = await getSignature(fileName)
    signatureData = data
    const imageUrl = `${signatureData.host}/${signatureData.dir}/${fileName}`
    const item = _fileList.value.find(v => v.name === file.name)

    // 单张上传时， 是否覆盖上一张图片
    if (item) {
      if (props.isReplace && _fileList.value.length > 1) {
        _fileList.value = _fileList.value.slice(-1)
      }
    } else {
      // 添加文件到文件列表
      _fileList.value.push({
        url: imageUrl,
        name: file.name,
        show: true,
        status: 'success',
        showPercentage: true,
        percentage: 0,
        type: isImageFile(file.type as string) ? 'image' : 'file'
      })
    }
    emit('update:fileList', _fileList.value)

    // 自定义上传进度
    const fileObj = _fileList.value.find(v => v.name === file.name) as UploadFile
    const timer = setInterval(() => {
      if (fileObj.percentage <= 99) {
        fileObj.percentage++
        emit('update:fileList', _fileList.value)
      }
    }, 50)

    // 上传
    const formData = new FormData()
    formData.append('OSSAccessKeyId', signatureData.accessId)
    formData.append('policy', signatureData.policy)
    formData.append('Signature', signatureData.signature)
    formData.append('success_action_status', '200')
    formData.append('key', signatureData.dir! + '/' + fileName)
    formData.append('file', file.raw!)
    try {
      await axios({
        url: signatureData.host,
        method: 'post',
        headers: {
          'Content-Type': 'multipart/form-data'
        },
        data: formData,
      }).then(() => {
        _fileList.value.forEach(item => {
          if (item.name === file.name) {
            item.showPercentage = false
            clearInterval(timer)
          }
        })
        emit('update:fileList', _fileList.value)
        emit('upload-success', _fileList.value)
        requestMethodResponse = { status: 'success', response: { url: imageUrl, name: file.name } }
      })
    } catch (error) {
      requestMethodResponse = { status: 'fail', error: '上传失败', response: {} }
    }
  } catch (error) {
    requestMethodResponse = { status: 'fail', error: '获取签名失败', response: {} }
  }
  // 添加默认返回值
  return requestMethodResponse
}

// 删除上传文件
const onFileDel = (item: UploadFile) => {
  // 从文件列表中删除该文件
  _fileList.value = _fileList.value.filter(v => v.name !== item.name)
  emit('update:fileList', _fileList.value)
}

/**
 * @description 文件上传成功
 * */
const uploadSuccess = () => {
  NotifyPlugin.success({
    title: '温馨提示',
    content: '文件上传成功！'
  })
}

/**
 * @description 文件上传错误
 * */
const uploadError = () => {
  NotifyPlugin('error', {
    title: '温馨提示',
    content: '文件上传失败，请您重新上传！'
  })
}
</script>

<style scoped lang="less">
.upload-box {
  :deep(.t-upload__dragger) {
    padding: 0;
    border: none;
    /* stylelint-disable-next-line function-no-unknown */
    width: v-bind(width);
    /* stylelint-disable-next-line function-no-unknown */
    height: v-bind(height);
  }
}
</style>
