{"version":3,"file":"useUpload.mjs","names":[],"sources":["../../../../src/components/upload/src/useUpload.ts"],"sourcesContent":["import { computed, inject, onMounted, ref, useModel, watch } from \"vue\";\nimport { ElMessage, ElNotification, formContextKey, formItemContextKey, genFileId, uploadProps } from \"element-plus\";\nimport { Decimal } from \"decimal.js\";\nimport { uploadUtil } from \"../utils/upload\";\nimport type {\n\tFormContext,\n\tFormItemContext,\n\tUploadFile,\n\tUploadFiles,\n\tUploadProps,\n\tUploadRawFile,\n\tUploadRequestOptions,\n\tUploadUserFile,\n} from \"element-plus\";\nimport type { ComputedRef, Ref, WritableComputedRef } from \"vue\";\n\ninterface UploadComposable {\n\tfileList: WritableComputedRef<UploadUserFile[]>;\n\tloading: Ref<boolean>;\n\tformContext: FormContext | undefined;\n\tformItemContext: FormItemContext | undefined;\n\tmaxSizeMB: ComputedRef<Decimal>;\n\thandleValue: () => void;\n\thandleHttpRequest: (options: UploadRequestOptions) => Promise<void>;\n\thandleOnSuccess: (fileUrl: string, uploadFile: UploadFile, uploadFiles: UploadFiles) => void;\n\thandleOnError: (error: Error, uploadFile: UploadFile, uploadFiles: UploadFiles) => void;\n\thandleOnRemove: (uploadFile: UploadFile, uploadFiles: UploadFiles) => void;\n\thandleOnExceed: (files: File[], uploadFiles: UploadUserFile[]) => void;\n\thandleOnUpload: (file: UploadFile | UploadRawFile) => boolean;\n\thandleOnChange: (uploadFile: UploadFile, uploadFiles: UploadFiles) => void;\n}\n\n/**\n * 创建上传组件共享的文件状态和操作方法。\n *\n * @param componentName - 组件名称。\n * @param fileTypeName - 文件类型名称。\n * @param props - 上传组件的 Props。\n * @param emit - 上传组件的事件触发器。\n * @param options - 文件大小和类型校验配置。\n * @returns 文件列表、加载状态和上传操作方法。\n */\nexport const useUpload = <T extends string | string[]>(\n\tcomponentName: string,\n\tfileTypeName: string,\n\tprops: Partial<UploadProps> & {\n\t\tmodelValue?: T | null;\n\t},\n\temit: ((event: \"update:fileList\", value: UploadUserFile[]) => void) & ((event: \"update:modelValue\", value: T | null) => void),\n\tdata?: {\n\t\tmaxSize?: string | number;\n\t\tuploadApi?: (formData: FormData) => Promise<string>;\n\t\tuploadUrl?: string;\n\t}\n): UploadComposable => {\n\tconst fileListModel = useModel(props, \"fileList\");\n\tconst fileList = computed<UploadUserFile[]>({\n\t\tget: () => fileListModel.value ?? [],\n\t\tset: (value) => {\n\t\t\tfileListModel.value = value;\n\t\t},\n\t});\n\n\tconst loading = ref(false);\n\tlet activeUploadCount = 0;\n\n\t// 获取 el-form 组件上下文\n\tconst formContext = inject(formContextKey, undefined);\n\t// 获取 el-form-item 组件上下文\n\tconst formItemContext = inject(formItemContextKey, undefined);\n\n\tconst mbNum = new Decimal(1024);\n\tconst maxSizeKB = computed(() => new Decimal(typeof data?.maxSize === \"number\" ? data.maxSize : Number(data?.maxSize)));\n\tconst maxSizeMB = computed(() => maxSizeKB.value.div(mbNum));\n\n\tonMounted(() => {\n\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated -- 需要识别 Element Plus 2.x 注入的默认请求实现。\n\t\tif (!props.disabled && props.autoUpload && !data?.uploadApi && !data?.uploadUrl && props.httpRequest === uploadProps.httpRequest.default) {\n\t\t\tconsole.error(`[Fast:${componentName}]`, \"uploadApi 和 uploadUrl 至少需要提供一个。\");\n\t\t}\n\t});\n\n\tconst handleValue = (files: UploadUserFile[] = fileList.value): void => {\n\t\tlet value: T | null;\n\t\tif (files.length > 0) {\n\t\t\tif (props.multiple === true) {\n\t\t\t\tvalue = files.flatMap((item) => (item.url ? [item.url] : [])) as T;\n\t\t\t} else {\n\t\t\t\tconst fileUrl = files[0]?.url;\n\t\t\t\tif (!fileUrl) return;\n\t\t\t\tvalue = fileUrl as T;\n\t\t\t}\n\t\t} else {\n\t\t\tvalue = props.multiple === true ? ([] as unknown as T) : null;\n\t\t}\n\t\temit(\"update:modelValue\", value);\n\t};\n\n\tconst handleHttpRequest = async (options: UploadRequestOptions): Promise<void> => {\n\t\tconst propsData = options.data;\n\t\tif (!data?.uploadApi && !data?.uploadUrl) {\n\t\t\tconst error = new Error(`上传${fileTypeName}接口 uploadApi 或地址 uploadUrl 不能为空。`);\n\t\t\tElMessage.error(`上传${fileTypeName}Api或地址不能为空`);\n\t\t\tconsole.error(`[Fast:${componentName}]`, error.message);\n\t\t\tthrow error;\n\t\t}\n\t\tactiveUploadCount++;\n\t\tloading.value = true;\n\t\ttry {\n\t\t\tlet fileUrl: string;\n\t\t\tif (data.uploadApi) {\n\t\t\t\tfileUrl = await uploadUtil.uploadFileByApi(data.uploadApi, options.file, options.filename, propsData);\n\t\t\t} else {\n\t\t\t\tconst headers =\n\t\t\t\t\toptions.headers instanceof Headers\n\t\t\t\t\t\t? options.headers\n\t\t\t\t\t\t: new Headers(\n\t\t\t\t\t\t\t\tObject.entries(options.headers).flatMap(([key, value]) =>\n\t\t\t\t\t\t\t\t\tvalue === null || value === undefined ? [] : [[key, String(value)] as [string, string]]\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t);\n\t\t\t\tfileUrl = await uploadUtil.uploadFile(data.uploadUrl ?? options.action, options.file, options.filename, propsData, {\n\t\t\t\t\theaders,\n\t\t\t\t\tmethod: options.method,\n\t\t\t\t\twithCredentials: options.withCredentials,\n\t\t\t\t});\n\t\t\t}\n\t\t\toptions.onSuccess(fileUrl);\n\t\t} finally {\n\t\t\tactiveUploadCount = Math.max(activeUploadCount - 1, 0);\n\t\t\tloading.value = activeUploadCount > 0;\n\t\t}\n\t};\n\n\tconst handleOnSuccess = (fileUrl: string, uploadFile: UploadFile, uploadFiles: UploadFiles): void => {\n\t\tif (!fileUrl) return;\n\t\tuploadFile.url = fileUrl;\n\t\tif (!props.multiple && uploadFiles.length > 1) {\n\t\t\tuploadFiles.splice(0, uploadFiles.length, uploadFile);\n\t\t}\n\t\thandleValue(uploadFiles);\n\t\t// 调用 el-form 内部的校验方法（可自动校验）\n\t\tif (formItemContext?.prop) formContext?.validateField([formItemContext.prop]);\n\t\tElMessage.success(\"上传成功\");\n\t\tprops.onSuccess?.(fileUrl, uploadFile, uploadFiles);\n\t};\n\n\tconst handleOnError = (error: Error, uploadFile: UploadFile, uploadFiles: UploadFiles): void => {\n\t\tElNotification({\n\t\t\tmessage: `【${uploadFile.name}】${fileTypeName}上传失败，请您重新上传`,\n\t\t\ttype: \"error\",\n\t\t});\n\t\tprops.onError?.(error, uploadFile, uploadFiles);\n\t};\n\n\tconst handleOnRemove = (uploadFile: UploadFile, uploadFiles: UploadFiles): void => {\n\t\thandleValue(uploadFiles);\n\t\tprops.onRemove?.(uploadFile, uploadFiles);\n\t};\n\n\tconst handleOnExceed = (files: File[], uploadFiles: UploadUserFile[]): void => {\n\t\tElMessage.warning(`最多只能上传 ${props.limit} 个${fileTypeName}，请移除后再进行上传`);\n\t\tprops.onExceed?.(files, uploadFiles);\n\t};\n\n\tconst handleOnUpload = (file: UploadFile | UploadRawFile): boolean => {\n\t\tconst fileSizeKB = new Decimal(file.size ?? 0).div(mbNum);\n\n\t\tif (fileSizeKB.greaterThan(maxSizeKB.value)) {\n\t\t\tconsole.warn(`[Fast:${componentName}]`, `【${file.name}】${fileTypeName}上传大小不能超过 ${maxSizeMB.value.toString()}MB。`);\n\t\t\tElMessage.warning(`【${file.name}】${fileTypeName}上传大小不能超过 ${maxSizeMB.value.toString()}MB`);\n\t\t\treturn false;\n\t\t}\n\n\t\tconst rawFile = \"type\" in file ? file : file.raw;\n\t\tif (!rawFile) return false;\n\t\tif (!uploadUtil.matchesAccept(rawFile, props.accept)) {\n\t\t\tconst uploadFileNames = uploadUtil.detectFileType(props.accept ?? \"\");\n\t\t\tconsole.warn(`[Fast:${componentName}]`, `只允许上传【${uploadFileNames}】格式的${fileTypeName}。`);\n\t\t\tElMessage.warning(`只允许上传【${uploadFileNames}】格式的${fileTypeName}`);\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t};\n\n\tconst handleOnChange = (uploadFile: UploadFile, uploadFiles: UploadFiles): void => {\n\t\tif (props.autoUpload === false && uploadFile.status === \"ready\" && !handleOnUpload(uploadFile)) {\n\t\t\tfileList.value = uploadFiles.filter((item) => item.uid !== uploadFile.uid);\n\t\t\treturn;\n\t\t}\n\t\tprops.onChange?.(uploadFile, uploadFiles);\n\t};\n\n\t/**\n\t * 监听 v-model 绑定数据\n\t */\n\twatch(\n\t\t() => props.modelValue,\n\t\t(newValue) => {\n\t\t\tif (newValue) {\n\t\t\t\tif (Array.isArray(newValue)) {\n\t\t\t\t\tfileList.value = newValue.map((m) => {\n\t\t\t\t\t\tconst find = fileList.value.find((f) => f.url === m);\n\t\t\t\t\t\tconst urlWithoutParams = m.split(/[?#]/u)[0] ?? \"\";\n\t\t\t\t\t\tconst fileName = urlWithoutParams.slice(urlWithoutParams.lastIndexOf(\"/\") + 1);\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\tfind ?? {\n\t\t\t\t\t\t\t\tname: fileName,\n\t\t\t\t\t\t\t\tstatus: \"success\",\n\t\t\t\t\t\t\t\tuid: genFileId(),\n\t\t\t\t\t\t\t\turl: m,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t);\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tconst find = fileList.value.find((f) => f.url === newValue);\n\t\t\t\t\tconst urlWithoutParams = newValue.split(/[?#]/u)[0] ?? \"\";\n\t\t\t\t\tconst fileName = urlWithoutParams.slice(urlWithoutParams.lastIndexOf(\"/\") + 1);\n\t\t\t\t\tfileList.value = [\n\t\t\t\t\t\tfind ?? {\n\t\t\t\t\t\t\tname: fileName,\n\t\t\t\t\t\t\tstatus: \"success\",\n\t\t\t\t\t\t\tuid: genFileId(),\n\t\t\t\t\t\t\turl: newValue,\n\t\t\t\t\t\t},\n\t\t\t\t\t];\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfileList.value = [];\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\timmediate: true,\n\t\t}\n\t);\n\n\treturn {\n\t\tfileList,\n\t\tloading,\n\t\tformContext,\n\t\tformItemContext,\n\t\tmaxSizeMB,\n\t\thandleValue,\n\t\thandleHttpRequest,\n\t\thandleOnSuccess,\n\t\thandleOnError,\n\t\thandleOnRemove,\n\t\thandleOnExceed,\n\t\thandleOnUpload,\n\t\thandleOnChange,\n\t};\n};\n"],"mappings":"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"}