diff --git a/packages/core/client/src/schema-component/antd/preview/Preview.tsx b/packages/core/client/src/schema-component/antd/preview/Preview.tsx index 948eadea3107..df2c32beba61 100644 --- a/packages/core/client/src/schema-component/antd/preview/Preview.tsx +++ b/packages/core/client/src/schema-component/antd/preview/Preview.tsx @@ -1,10 +1,9 @@ import { DeleteOutlined, DownloadOutlined, PlusOutlined } from '@ant-design/icons'; import { connect, mapReadPretty } from '@formily/react'; -import { Upload as AntdUpload, Button, Progress, Space } from 'antd'; +import { Upload as AntdUpload, Button, Progress, Space, UploadFile } from 'antd'; import cls from 'classnames'; -import { css } from '@emotion/css'; import { saveAs } from 'file-saver'; -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import Lightbox from 'react-image-lightbox'; import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app @@ -33,12 +32,15 @@ export const FileSelector = (props: Props) => { const [photoIndex, setPhotoIndex] = useState(0); const [visible, setVisible] = useState(false); const { t } = useTranslation(); + const internalFileList = useRef([]); // 兼容旧版本 const showSelectButton = selectFile === undefined && quickUpload === undefined; useEffect(() => { - setFileList(toFileList(value)); + const fileList = toFileList(value); + setFileList(fileList); + internalFileList.current = fileList; }, [value]); const handleRemove = (file) => { @@ -70,7 +72,7 @@ export const FileSelector = (props: Props) => { } }; return ( -
+
@@ -114,6 +116,7 @@ export const FileSelector = (props: Props) => { icon={} onClick={() => { handleRemove(file); + internalFileList.current = internalFileList.current.filter((item) => item.uid !== file.uid); }} /> )} @@ -166,9 +169,14 @@ export const FileSelector = (props: Props) => { showUploadList={false} onRemove={handleRemove} onChange={(info) => { + // info.fileList 有 BUG,会导致上传状态一直是 uploading + // 所以这里仿照 antd 源码,自己维护一个 fileList + const list = updateFileList(info.file, internalFileList.current); + internalFileList.current = list; + // 如果不在这里 setFileList 的话,会导致 onChange 只会执行一次 - setFileList([...info.fileList]); - uploadProps.onChange?.(info); + setFileList(toFileList(list)); + uploadProps.onChange?.({ fileList: list }); }} >
{ }; export default Preview; + +function updateFileList(file: UploadFile, fileList: (UploadFile | Readonly)[]) { + const nextFileList = [...fileList]; + const fileIndex = nextFileList.findIndex(({ uid }) => uid === file.uid); + if (fileIndex === -1) { + nextFileList.push(file); + } else { + nextFileList[fileIndex] = file; + } + return nextFileList; +} diff --git a/packages/core/client/src/schema-component/antd/upload/Upload.tsx b/packages/core/client/src/schema-component/antd/upload/Upload.tsx index 03b270f64ea8..a1fef5fce973 100644 --- a/packages/core/client/src/schema-component/antd/upload/Upload.tsx +++ b/packages/core/client/src/schema-component/antd/upload/Upload.tsx @@ -1,11 +1,10 @@ import { DeleteOutlined, DownloadOutlined, InboxOutlined, LoadingOutlined, PlusOutlined } from '@ant-design/icons'; import { usePrefixCls } from '@formily/antd/esm/__builtins__'; import { connect, mapProps, mapReadPretty } from '@formily/react'; -import { Upload as AntdUpload, Button, Progress, Space, Modal } from 'antd'; +import { Upload as AntdUpload, Button, Modal, Progress, Space, UploadFile } from 'antd'; import cls from 'classnames'; -import { css } from '@emotion/css'; import { saveAs } from 'file-saver'; -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import Lightbox from 'react-image-lightbox'; import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app @@ -33,12 +32,16 @@ Upload.Attachment = connect((props: UploadProps) => { const [visible, setVisible] = useState(false); const [fileType, setFileType] = useState<'image' | 'pdf'>(); const { t } = useTranslation(); + const internalFileList = useRef([]); + function closeIFrameModal() { setVisible(false); } useEffect(() => { if (sync) { - setFileList(toFileList(value)); + const fileList = toFileList(value); + setFileList(fileList); + internalFileList.current = fileList; } }, [value, sync]); const uploadProps = useUploadProps({ ...props }); @@ -55,7 +58,7 @@ Upload.Attachment = connect((props: UploadProps) => { setFileType('image'); setVisible(true); setFileIndex(index); - } else if(isPdf(file.extname)) { + } else if (isPdf(file.extname)) { setVisible(true); setFileIndex(index); setFileType('pdf'); @@ -64,7 +67,7 @@ Upload.Attachment = connect((props: UploadProps) => { } }; return ( -
+
@@ -115,6 +118,9 @@ Upload.Attachment = connect((props: UploadProps) => { } const index = prevFileList.indexOf(file); prevFileList.splice(index, 1); + internalFileList.current = internalFileList.current.filter( + (item) => item.uid !== file.uid, + ); onChange(toValue([...prevFileList])); return [...prevFileList]; }); @@ -141,12 +147,17 @@ Upload.Attachment = connect((props: UploadProps) => { listType={'picture-card'} fileList={fileList} onChange={(info) => { + // info.fileList 有 BUG,会导致上传状态一直是 uploading + // 所以这里仿照 antd 源码,自己维护一个 fileList + const list = updateFileList(info.file, internalFileList.current); + internalFileList.current = list; + setSync(false); if (multiple) { if (info.file.status === 'done') { - onChange(toValue(info.fileList)); + onChange(toValue(list)); } - setFileList(info.fileList.map(toItem)); + setFileList(list.map(toItem)); } else { if (info.file.status === 'done') { // TODO(BUG): object 的联动有问题,不响应,折中的办法先置空再赋值 @@ -198,7 +209,7 @@ Upload.Attachment = connect((props: UploadProps) => { ]} /> )} - + {visible && fileType === 'pdf' && ( { footer={[ , - + , ]} width={'85vw'} centered={true} > -
- + background: 'white', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + overflowY: 'auto', + }} + > +
)} @@ -305,3 +320,14 @@ Upload.DraggerV2 = connect( ); export default Upload; + +function updateFileList(file: UploadFile, fileList: (UploadFile | Readonly)[]) { + const nextFileList = [...fileList]; + const fileIndex = nextFileList.findIndex(({ uid }) => uid === file.uid); + if (fileIndex === -1) { + nextFileList.push(file); + } else { + nextFileList[fileIndex] = file; + } + return nextFileList; +} diff --git a/packages/core/client/src/schema-component/antd/upload/shared.ts b/packages/core/client/src/schema-component/antd/upload/shared.ts index 7bfe51c8ab7b..a283a5e94086 100644 --- a/packages/core/client/src/schema-component/antd/upload/shared.ts +++ b/packages/core/client/src/schema-component/antd/upload/shared.ts @@ -2,7 +2,6 @@ import { Field } from '@formily/core'; import { useField } from '@formily/react'; import { reaction } from '@formily/reactive'; import { isArr, isValid, toArr as toArray } from '@formily/shared'; -import { UploadChangeParam } from 'antd/es/upload'; import { UploadFile } from 'antd/es/upload/interface'; import { useEffect } from 'react'; import { useAPIClient } from '../../../api-client'; @@ -167,7 +166,7 @@ export const useUploadValidator = (serviceErrorMessage = 'Upload Service Error') export function useUploadProps({ serviceErrorMessage, ...props }: T) { useUploadValidator(serviceErrorMessage); - const onChange = (param: UploadChangeParam) => { + const onChange = (param: { fileList: any[] }) => { props.onChange?.(normalizeFileList([...param.fileList])); }; @@ -212,7 +211,10 @@ export function useUploadProps({ serviceEr export const toItem = (file) => { if (file?.response?.data) { - file = file.response.data; + file = { + uid: file.uid, + ...file.response.data, + }; } return { ...file, diff --git a/packages/core/client/src/schema-component/antd/upload/style.less b/packages/core/client/src/schema-component/antd/upload/style.less index 41468d990f36..a4b225c47131 100644 --- a/packages/core/client/src/schema-component/antd/upload/style.less +++ b/packages/core/client/src/schema-component/antd/upload/style.less @@ -40,7 +40,7 @@ background: rgba(0, 0, 0, 0.5); } .ant-upload-list-picture-card-container { - // margin-bottom: 28px; + margin-bottom: 28px; } }