import { ref, type Ref } from 'vue' import { uploadImages } from '@/common/utils/upload' import type { UniFormRef } from '@/uni_modules/uview-plus/types' // 定义上传配置类型 export interface UploadImgConfig { maxCount?: number // 最大上传数量,默认3 uploadText?: string // 上传按钮文案 sizeType?: UniApp.UploadFileOption['sizeType'] // 图片压缩类型 formRef: Ref // 表单ref,用于校验 fieldName: string // 表单校验字段名(如problemImgs) } // 定义图片项类型 export interface UploadImgItem { url: string status: 'uploading' | 'success' | 'failed' message: string [key: string]: any // 兼容其他字段 } /** * 图片上传组合式函数(支持多实例复用) * @param config 上传配置 * @returns 上传相关方法和状态 */ export function useUploadImgs(config: UploadImgConfig) { // 默认配置 const defaultConfig = { maxCount: 3, uploadText: '选择图片', sizeType: ['compressed'] as UniApp.UploadFileOption['sizeType'], ...config } // 图片列表 const imgList = ref([]) /** * 删除图片 */ const deleteImg = (event: { index: number }) => { imgList.value.splice(event.index, 1) // 删除后重新校验 defaultConfig.formRef.value?.validateField(defaultConfig.fieldName) uni.showToast({ title: '图片删除成功', icon: 'success' }) } /** * 上传图片 */ const uploadImgs = async (event: { file: UniApp.ChooseImageSuccessCallbackResult | UniApp.ChooseImageSuccessCallbackResult[] }) => { const fileList = Array.isArray(event.file) ? event.file : [event.file] const targetImgList = imgList.value // 过滤超出最大数量的图片 if (targetImgList.length + fileList.length > defaultConfig.maxCount) { uni.showToast({ title: `最多只能上传${defaultConfig.maxCount}张图片`, icon: 'none' }) return } const filePaths = fileList.map(item => item.url) const tempItems = fileList.map(item => ({ ...item, status: 'uploading' as const, message: '上传中' })) 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, { ...fileList[index], status: 'success' as const, message: '', url: url }) } }) // 处理上传失败的图片 if (uploadResultUrls.length < fileList.length) { const failCount = fileList.length - uploadResultUrls.length for (let i = uploadResultUrls.length; i < fileList.length; i++) { if (targetImgList[startIndex + i]) { targetImgList.splice(startIndex + i, 1, { ...fileList[i], status: 'failed' as const, message: '上传失败' }) } } uni.showToast({ title: `成功上传${uploadResultUrls.length}张,失败${failCount}张`, icon: 'none' }) } else { uni.showToast({ title: `成功上传${fileList.length}张图片`, icon: 'success' }) } // 上传完成后校验 defaultConfig.formRef.value?.validateField(defaultConfig.fieldName) } catch (err) { console.error(`【${defaultConfig.fieldName}】图片上传失败:`, err) // 标记所有上传失败 for (let i = 0; i < fileList.length; i++) { if (targetImgList[startIndex + i]) { targetImgList.splice(startIndex + i, 1, { ...fileList[i], status: 'failed' as const, message: '上传失败' }) } } 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: any, value: any, callback: (error?: Error) => void) => { const hasSuccessImg = imgList.value.some(item => item.status === 'success') hasSuccessImg ? callback() : callback(new Error(`请上传至少1张${defaultConfig.uploadText.replace('选择', '')}`)) } } return { imgList, uploadImgs, deleteImg, getSuccessImgUrls, imgValidateRule, uploadConfig: defaultConfig } }