<!--
 * @Author: zhaojinfeng 121016171@qq.com
 * @Date: 2023-08-16 17:33:41
 * @LastEditors: Hefeng 1057605508@qq.com
 * @LastEditTime: 2023-09-15 12:51:29
 * @FilePath: \th-components-vue3\packages\upload-list\index.vue
 * @Description:
 *
-->
<template>
  <el-upload
    v-model:file-list="userFiles"
    :class="{ 'user-files': listType === 'picture-card' && userFiles.length >= limit }"
    :accept="accept"
    :disabled="disabled"
    :before-upload="beforeUpload"
    :http-request="httpRequest"
    :list-type="listType"
    :limit="limit"
    @preview="preview"
    @success="success"
    @remove="remove"
  >
    <slot>
      <el-icon v-if="listType === 'picture-card'">
        <Plus />
      </el-icon>
      <el-button v-if="listType !== 'picture-card' && userFiles.length < limit" type="primary" plain :disabled="disabled">
        点击上传
      </el-button>
    </slot>
  </el-upload>
</template>

<script lang="ts" setup name="ThUploadList">
import { Plus } from '@element-plus/icons-vue'
import type { UploadFile, UploadFiles, UploadRawFile, UploadRequestOptions, UploadUserFile } from 'element-plus'

const props = withDefaults(
  defineProps<{
    /** 可上传的文件类型 */
    accept?: string
    /** 文件集合 */
    fileList?: FileVO[]
    /** 文件上传的请求函数 */
    request: (formData: FormData, args?: any) => Promise<FileVO>
    /** 自定义文件上传路径前缀 */
    filePath?: string
    /** 禁用组件 */
    disabled?: boolean
    /** 最大上传大小（Mb） */
    maxsize?: number
    /** 文件列表的类型 */
    listType?: 'text' | 'picture' | 'picture-card'
    /** 允许上传文件的最大数量 */
    limit?: number
  }>(), {
    fileList: () => [],
    maxsize: 100,
    limit: Infinity,
  },
)

const emit = defineEmits<{
  (event: 'update:file-list', file: FileVO[]): void
  (event: 'preview', file: UploadFile): void
}>()

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

function beforeUpload(rawFile: UploadRawFile) {
  if (rawFile.size > props.maxsize * 1024 * 1024) {
    const err = `文件大小不能超过${props.maxsize}Mb`
    ElMessage.error(err)
    return false
  }
  return true
}
async function httpRequest({ file, onProgress, onSuccess, onError }: UploadRequestOptions) {
  const formData = new FormData()
  formData.append('file', file)
  if (props.filePath)
    formData.append('filePath', props.filePath)
  try {
    const res = await props.request(formData, {
      onUploadProgress(e: ProgressEvent) {
        onProgress({ ...e, percent: Math.floor(e.loaded * 100 / e.total) })
      },
    })
    onSuccess(res)
  }
  catch (err) {
    onError(err)
  }
}

function preview(uploadFile: UploadFile) {
  emit('preview', uploadFile)
}

const { pause, resume } = watchPausable(
  () => props.fileList, (fileList) => {
    userFiles.value = fileList.map(file => ({
      ...file,
      status: 'success',
      percentage: 100,
      uid: file.id,
    }))
  },
  { immediate: true },
)

function success(response: FileVO, uploadFile: UploadFile, uploadFiles: UploadFiles) {
  // 将返回响应合并到文件上
  Object.assign(uploadFile, response)
  // 手动更新，暂停监听，避免因props.fileList更新再次更新uploadFile
  pause()
  emit('update:file-list', uploadFiles)
  resume()
}

function remove(_uploadFile: UploadFile, uploadFiles: UploadFiles) {
  userFiles.value = uploadFiles
  // 手动更新，暂停监听，避免因props.fileList更新再次更新uploadFile
  pause()
  emit('update:file-list', uploadFiles)
  resume()
}
</script>

<style>
.user-files .el-upload--picture-card{
  display: none;
 }
</style>
