useUploadImgs.js 5.75 KB
import { ref } from 'vue'
import { uploadImages } from '@/common/utils/upload'

/**
 * 图片上传组合式函数(兼容非TS环境,纯数组格式)
 * @param config 上传配置
 * @returns 上传相关方法和状态
 */
export function useUploadImgs(config) {
    // 默认配置
    const defaultConfig = {
        maxCount: 3,
        uploadText: '选择图片',
        sizeType: ['compressed'],
        ...config
    }

    // 核心修复:初始化为纯数组,且格式适配u-upload
    const imgList = ref([])

    /**
     * 删除图片
     */
    const deleteImg = (event) => {
        // 确保index是有效数字
        const index = Number(event.index)
        if (isNaN(index) || index < 0 || index >= imgList.value.length) return

        imgList.value.splice(index, 1)
        // 删除后重新校验
        defaultConfig.formRef.value?.validateField(defaultConfig.fieldName)
        uni.showToast({ title: '图片删除成功', icon: 'success' })
    }

    /**
     * 上传图片(适配u-upload的file格式)
     */
    const uploadImgs = async (event) => {
        // 核心修复:标准化file数据格式
        const rawFile = event.file || {}
        const fileList = Array.isArray(rawFile) ? rawFile : [rawFile]

        // 过滤无效文件
        const validFiles = fileList.filter(file => file && file.url)
        if (validFiles.length === 0) {
            uni.showToast({ title: '请选择有效图片', icon: 'none' })
            return
        }

        const targetImgList = imgList.value

        // 校验最大数量
        if (targetImgList.length + validFiles.length > defaultConfig.maxCount) {
            uni.showToast({ title: `最多只能上传${defaultConfig.maxCount}张图片`, icon: 'none' })
            return
        }

        // 核心修复:转换为u-upload兼容的格式
        const filePaths = validFiles.map(item => item.url)
        const tempItems = validFiles.map(item => ({
            url: item.url,
            status: 'uploading',
            message: '上传中',
            name: item.name || '',
            size: item.size || 0
        }))
        const startIndex = targetImgList.length
        targetImgList.push(...tempItems)

        try {
            const uploadResultUrls = await uploadImages({
                filePaths: filePaths,
                ignoreError: true
            })

            // 更新上传成功的图片
            uploadResultUrls.forEach((url, index) => {
                if (targetImgList[startIndex + index]) {
                    targetImgList.splice(startIndex + index, 1, {
                        url: url,
                        status: 'success',
                        message: '',
                        name: validFiles[index].name || '',
                        size: validFiles[index].size || 0
                    })
                }
            })

            // 处理上传失败的图片
            if (uploadResultUrls.length < validFiles.length) {
                const failCount = validFiles.length - uploadResultUrls.length
                for (let i = uploadResultUrls.length; i < validFiles.length; i++) {
                    if (targetImgList[startIndex + i]) {
                        targetImgList.splice(startIndex + i, 1, {
                            url: validFiles[i].url,
                            status: 'failed',
                            message: '上传失败',
                            name: validFiles[i].name || '',
                            size: validFiles[i].size || 0
                        })
                    }
                }
                uni.showToast({ title: `成功上传${uploadResultUrls.length}张,失败${failCount}张`, icon: 'none' })
            } else {
                uni.showToast({ title: `成功上传${validFiles.length}张图片`, icon: 'success' })
            }

            // 上传完成后校验
            defaultConfig.formRef.value?.validateField(defaultConfig.fieldName)
        } catch (err) {
            console.error(`【${defaultConfig.fieldName}】图片上传失败:`, err)
            // 标记所有上传失败
            for (let i = 0; i < validFiles.length; i++) {
                if (targetImgList[startIndex + i]) {
                    targetImgList.splice(startIndex + i, 1, {
                        url: validFiles[i].url,
                        status: 'failed',
                        message: '上传失败',
                        name: validFiles[i].name || '',
                        size: validFiles[i].size || 0
                    })
                }
            }
            uni.showToast({ title: '图片上传失败,请重试', icon: 'none' })
            // 上传失败后校验
            defaultConfig.formRef.value?.validateField(defaultConfig.fieldName)
        }
    }

    /**
     * 获取成功上传的图片URL列表
     */
    const getSuccessImgUrls = () => {
        return imgList.value.filter(item => item.status === 'success').map(item => item.url)
    }

    /**
     * 图片校验规则(供表单使用)
     */
    const imgValidateRule = {
        required: true,
        message: `请上传${defaultConfig.uploadText.replace('选择', '')}`,
        trigger: 'change',
        validator: (rule, value, callback) => {
            const hasSuccessImg = imgList.value.some(item => item.status === 'success')
            hasSuccessImg ? callback() : callback(new Error(`请上传至少1${defaultConfig.uploadText.replace('选择', '')}`))
        }
    }

    return {
        imgList: imgList.value, // 核心修复:返回纯数组(解除响应式代理)
        rawImgList: imgList,    // 保留响应式引用(内部使用)
        uploadImgs,
        deleteImg,
        getSuccessImgUrls,
        imgValidateRule,
        uploadConfig: defaultConfig
    }
}