<template>
    <div class="app-upload">
        <el-upload ref="uploadRef" :accept="acceptTypes.join(',')" :file-list="fileList" :limit="limit"
            :multiple="limit > 1" :action="uploadUrl" :before-upload="onBeforeUpload" @success="onSuccess"
            @remove="onRemove" @preview="onPreview" @exceed="onExceed">
            <template #tip>
                <div class="el-upload__tip">
                    <slot name="tip"></slot>
                </div>
            </template>
            <slot>
                <el-button type="primary">上传</el-button>
            </slot>
        </el-upload>
        <el-image-viewer :url-list="imageList.map((e)=>e.filePath)" :initial-index="imageIndex" @close="onViewerClose"
            v-if="showViewer" teleported />
    </div>
</template>

<script lang="ts" setup>
import { shallowRef, shallowReactive, computed, PropType, toRaw, onMounted } from 'vue'
import { ElMessage, UploadProps, UploadUserFile, UploadRawFile, UploadFile, UploadFiles, UploadInstance, genFileId } from 'element-plus'
import { getFileUrl } from '@/filters'
import service from '@/services'

const props = defineProps({
    fileTypes: {
        type: Array as PropType<readonly ('image' | 'pdf' | 'word' | 'excel')[]>,
        default: () => ([])
    },
    limit: {
        type: Number,
        default: 1
    },
    maxSize: {
        type: Number,
        default: 0
    },
    typeMessage: {
        type: String,
        default: '请选择正确的文件类型'
    }
})

const emit = defineEmits(['change'])

const { uploadUrl } = service.config
const uploadRef = shallowRef<UploadInstance>()
const showViewer = shallowRef(false)
const fileList = shallowRef<UploadUserFile[]>([])
const acceptTypes = shallowReactive<string[]>([]) // 接受上传的文件类型
const uploadTypes = shallowReactive<string[]>([]) // 允许上传的文件类型
const imageIndex = shallowRef(0)
const imageTypes = ['image/png', 'image/jpeg']

// 预览图列表
const imageList = computed(() => {
    const result: { uid: number, filePath: string }[] = []
    fileList.value.forEach(({ raw, uid, response }) => {
        if (raw && uid && response) {
            // 判断是否图片类型
            if (imageTypes.includes(raw.type)) {
                (response as { filePath: string }[]).forEach((e) => {
                    result.push({
                        uid,
                        filePath: getFileUrl(e.filePath)
                    })
                })
            }
        }
    })
    return result
})

const onChange = () => {
    const files = fileList.value.reduce((res, { response }) => {
        const list = (response ?? []) as unknown[]
        list.forEach((e) => {
            res.push(toRaw(e))
        })
        return res
    }, [] as unknown[])
    emit('change', props.limit > 1 ? files : files[0] ?? {})
}

// 当超出限制时的回调
const onExceed = (files: File[]) => {
    if (props.limit === 1) {
        const el = uploadRef.value
        if (el) {
            const rawFile = files[0] as UploadRawFile
            if (uploadTypes.includes(rawFile.type)) {
                rawFile.uid = genFileId()
                el.clearFiles()
                el.handleStart(rawFile)
                el.submit()
            } else {
                ElMessage.warning(props.typeMessage)
            }
        }
    } else {
        ElMessage.warning(`最多只能上传${props.limit}个文件`)
    }
}

// 上传之前判断文件类型
const onBeforeUpload = (rawFile: UploadRawFile) => {
    if (uploadTypes.includes(rawFile.type)) {
        return true
    }
    ElMessage.warning(props.typeMessage)
    return false
}

// 上传成功的回调
const onSuccess = (response: unknown, uploadFile: UploadFile, uploadFiles: UploadFiles) => {
    fileList.value = uploadFiles
    onChange()
}

const onRemove = (uploadFile: UploadFile, uploadFiles: UploadFiles) => {
    fileList.value = uploadFiles
    onChange()
}

// 打开预览图
const onPreview: UploadProps['onPreview'] = ({ uid, raw }: UploadFile) => {
    if (imageList.value.length) {
        // 判断是否图片类型
        if (imageTypes.includes(raw?.type ?? '')) {
            const index = imageList.value.findIndex((e) => e.uid === uid)
            if (index > -1) {
                imageIndex.value = index
                showViewer.value = true
            }
        }
    }
}

const onViewerClose = () => {
    showViewer.value = false
}

onMounted(() => {
    props.fileTypes.forEach((value) => {
        switch (value) {
            case 'image': {
                acceptTypes.push('.jpg', '.jpeg', '.png')
                uploadTypes.push(...imageTypes)
                break
            }
            case 'pdf': {
                acceptTypes.push('.pdf')
                uploadTypes.push('application/pdf')
                break
            }
            case 'word': {
                acceptTypes.push('.doc', '.docx')
                uploadTypes.push('application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document')
                break
            }
            case 'excel': {
                acceptTypes.push('.xls', '.xlsx')
                uploadTypes.push('application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
                break
            }
        }
    })
})
</script>

<style lang="less">
@import './index.less';
</style>