From 0169a50ef02040244b93d3c50afc03e9a3764e4e Mon Sep 17 00:00:00 2001 From: edram Date: Tue, 25 Nov 2025 13:30:34 +0800 Subject: [PATCH] =?UTF-8?q?feat(Uploader):=20=E5=8F=96=E6=B6=88=E5=86=85?= =?UTF-8?q?=E9=83=A8=E7=BB=B4=E6=8A=A4=E4=B8=8A=E4=BC=A0=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=EF=BC=8C=E9=80=9A=E8=BF=87=20value=20=E5=8F=97=E6=8E=A7?= =?UTF-8?q?=EF=BC=8C=E5=A4=96=E9=83=A8=E5=8F=AF=E4=BB=A5=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E5=92=8C=E6=8E=A7=E5=88=B6=E4=B8=8A=E4=BC=A0=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/uploader/PropsType.ts | 8 +- .../src/components/uploader/Uploader.tsx | 112 +++++++----------- 2 files changed, 42 insertions(+), 78 deletions(-) diff --git a/packages/react-vant/src/components/uploader/PropsType.ts b/packages/react-vant/src/components/uploader/PropsType.ts index 397253d68..6267228e3 100644 --- a/packages/react-vant/src/components/uploader/PropsType.ts +++ b/packages/react-vant/src/components/uploader/PropsType.ts @@ -16,17 +16,11 @@ export type UploaderValueItem = { thumbnail?: string /** 原始文件 */ file?: File + status?: TaskStatus } & Record type TaskStatus = 'pending' | 'failed' -export type UploaderTask = { - id: number - url?: string - status: TaskStatus - file: File -} - export type UploaderMaxSize = number | string | ((file: File) => boolean) export type UploaderBeforeRead = ( diff --git a/packages/react-vant/src/components/uploader/Uploader.tsx b/packages/react-vant/src/components/uploader/Uploader.tsx index 32b150571..508085fd6 100644 --- a/packages/react-vant/src/components/uploader/Uploader.tsx +++ b/packages/react-vant/src/components/uploader/Uploader.tsx @@ -1,20 +1,15 @@ -import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react' +import React, { forwardRef, useImperativeHandle, useRef } from 'react' import cls from 'clsx' import { Photograph } from '@react-vant/icons' // Utils import { getSizeStyle, extend, createNamespace } from '../utils' import { isOversize, filterFiles, readFileContent, isImageFile } from './utils' // Types -import { - UploaderInstance, - UploaderProps, - UploaderTask, - UploaderValueItem, -} from './PropsType' +import { UploaderInstance, UploaderProps, UploaderValueItem } from './PropsType' import { UploaderPreviewItem } from './UploaderPreviewItem' // Components import ImagePreview from '../image-preview' -import { useIsomorphicLayoutEffect, usePropsValue } from '../hooks' +import { usePropsValue } from '../hooks' import { mergeProps } from '../utils/get-default-props' const [bem] = createNamespace('uploader') @@ -40,20 +35,8 @@ const Uploader = forwardRef((p, ref) => { const imagePreview = useRef(null) const inputRef = useRef() - const [tasks, setTasks] = useState([]) - const idCountRef = useRef(0) - useIsomorphicLayoutEffect(() => { - if (!Array.isArray(value)) return - setTasks(prev => - prev.filter(task => { - if (task.url === undefined) return true - return !value.some(fileItem => fileItem.url === task.url) - }) - ) - }, [value]) - const { maxCount, maxSize, resultType, beforeRead } = props async function processFile(file: File, fileList: File[]) { @@ -100,51 +83,60 @@ const Uploader = forwardRef((p, ref) => { } } - const newTasks = files.map( + const newFiles = files.map( file => ({ - id: idCountRef.current++, + key: `${idCountRef.current++}`, status: 'pending', file, - } as UploaderTask) + } as UploaderValueItem) ) - setTasks(prev => [...prev, ...newTasks]) + setValue(prev => [...prev, ...newFiles]) await Promise.all( - newTasks.map(async currentTask => { + newFiles.map(async currentTask => { try { - let result = {} as UploaderValueItem + let result = { ...currentTask } as UploaderValueItem + const dataUrl = (await readFileContent( + currentTask.file, + resultType + )) as string + result.url = dataUrl + result.file = currentTask.file + result.key = currentTask.key + + setValue(prev => { + return prev.map(task => { + if (task.key === currentTask.key) { + return { + ...task, + ...result, + } + } + return task + }) + }) + if (props.upload) { result = await props.upload(currentTask.file) - } else { - const dataUrl = (await readFileContent( - currentTask.file, - resultType - )) as string - result.url = dataUrl - result.file = currentTask.file - result.key = currentTask.id } - setTasks(prev => { + setValue(prev => { return prev.map(task => { - if (task.id === currentTask.id) { + if (task.key === currentTask.key) { return { ...task, - url: result.url, + ...result, + status: undefined, } } return task }) }) - setValue(prev => { - const newVal = { ...result, file: currentTask.file } - return [...prev, newVal] - }) } catch (e) { - setTasks(prev => { + setValue(prev => { return prev.map(task => { - if (task.id === currentTask.id) { + if (task.key === currentTask.key) { return { ...task, status: 'failed', @@ -195,9 +187,11 @@ const Uploader = forwardRef((p, ref) => { isImage={props.isImageUrl?.(item)} url={item.thumbnail ?? item.url} imageFit={props.imageFit} - deletable={props.deletable} - previewSize={props.previewSize} + status={item.status} + statusTextRender={props.statusTextRender} + deletable={item.status !== 'pending' ? props.deletable : false} deleteRender={props.deleteRender} + previewSize={props.previewSize} previewCoverRender={() => props.previewCoverRender?.(item)} onClick={() => props.onClickPreview?.(item, index)} onDelete={async () => { @@ -212,28 +206,7 @@ const Uploader = forwardRef((p, ref) => { const renderPreviewList = () => { if (props.previewImage) { - return ( - <> - {value.map(renderPreviewItem)} - {tasks.map(task => { - if (task.status === 'failed') return null - return ( - { - setTasks(tasks.filter(x => x.id !== task.id)) - }} - /> - ) - })} - - ) + return <>{value.map(renderPreviewItem)} } return null } @@ -249,10 +222,7 @@ const Uploader = forwardRef((p, ref) => { } const renderUpload = () => { - if ( - props.showUpload && - (maxCount === 0 || value.length + tasks.length < maxCount) - ) { + if (props.showUpload && (maxCount === 0 || value.length < maxCount)) { const Input = props.readOnly ? null : (