fix(upload): fix the upload status not being updated if the upload was successful (#2133)

* fix(Upload): fix style

* fix(Upload): fix uploading

* Revert "fix(Upload): fix uploading"

This reverts commit 45984cd59fab38b8e6fb3f49930b29acc8699b4f.

* refactor: add key

* fix: fix the upload status not being updated if the upload was
successful

* fix(FileSelector): fix uploading

* fix: fix key
This commit is contained in:
被雨水过滤的空气-Rairn 2023-06-28 15:38:03 +08:00 committed by GitHub
parent ece7cb76c2
commit d71fed0471
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 87 additions and 40 deletions

View File

@ -1,10 +1,9 @@
import { DeleteOutlined, DownloadOutlined, PlusOutlined } from '@ant-design/icons'; import { DeleteOutlined, DownloadOutlined, PlusOutlined } from '@ant-design/icons';
import { connect, mapReadPretty } from '@formily/react'; 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 cls from 'classnames';
import { css } from '@emotion/css';
import { saveAs } from 'file-saver'; import { saveAs } from 'file-saver';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import Lightbox from 'react-image-lightbox'; import Lightbox from 'react-image-lightbox';
import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app 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 [photoIndex, setPhotoIndex] = useState(0);
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const { t } = useTranslation(); const { t } = useTranslation();
const internalFileList = useRef([]);
// 兼容旧版本 // 兼容旧版本
const showSelectButton = selectFile === undefined && quickUpload === undefined; const showSelectButton = selectFile === undefined && quickUpload === undefined;
useEffect(() => { useEffect(() => {
setFileList(toFileList(value)); const fileList = toFileList(value);
setFileList(fileList);
internalFileList.current = fileList;
}, [value]); }, [value]);
const handleRemove = (file) => { const handleRemove = (file) => {
@ -70,7 +72,7 @@ export const FileSelector = (props: Props) => {
} }
}; };
return ( return (
<div key={file.id} className={'ant-upload-list-picture-card-container'}> <div key={file.uid || file.id} className={'ant-upload-list-picture-card-container'}>
<div className="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture-card"> <div className="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture-card">
<div className={'ant-upload-list-item-info'}> <div className={'ant-upload-list-item-info'}>
<span className="ant-upload-span"> <span className="ant-upload-span">
@ -114,6 +116,7 @@ export const FileSelector = (props: Props) => {
icon={<DeleteOutlined />} icon={<DeleteOutlined />}
onClick={() => { onClick={() => {
handleRemove(file); handleRemove(file);
internalFileList.current = internalFileList.current.filter((item) => item.uid !== file.uid);
}} }}
/> />
)} )}
@ -166,9 +169,14 @@ export const FileSelector = (props: Props) => {
showUploadList={false} showUploadList={false}
onRemove={handleRemove} onRemove={handleRemove}
onChange={(info) => { onChange={(info) => {
// info.fileList 有 BUG会导致上传状态一直是 uploading
// 所以这里仿照 antd 源码,自己维护一个 fileList
const list = updateFileList(info.file, internalFileList.current);
internalFileList.current = list;
// 如果不在这里 setFileList 的话,会导致 onChange 只会执行一次 // 如果不在这里 setFileList 的话,会导致 onChange 只会执行一次
setFileList([...info.fileList]); setFileList(toFileList(list));
uploadProps.onChange?.(info); uploadProps.onChange?.({ fileList: list });
}} }}
> >
<div <div
@ -250,3 +258,14 @@ export const FileSelector = (props: Props) => {
}; };
export default Preview; export default Preview;
function updateFileList(file: UploadFile, fileList: (UploadFile | Readonly<UploadFile>)[]) {
const nextFileList = [...fileList];
const fileIndex = nextFileList.findIndex(({ uid }) => uid === file.uid);
if (fileIndex === -1) {
nextFileList.push(file);
} else {
nextFileList[fileIndex] = file;
}
return nextFileList;
}

View File

@ -1,11 +1,10 @@
import { DeleteOutlined, DownloadOutlined, InboxOutlined, LoadingOutlined, PlusOutlined } from '@ant-design/icons'; import { DeleteOutlined, DownloadOutlined, InboxOutlined, LoadingOutlined, PlusOutlined } from '@ant-design/icons';
import { usePrefixCls } from '@formily/antd/esm/__builtins__'; import { usePrefixCls } from '@formily/antd/esm/__builtins__';
import { connect, mapProps, mapReadPretty } from '@formily/react'; 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 cls from 'classnames';
import { css } from '@emotion/css';
import { saveAs } from 'file-saver'; import { saveAs } from 'file-saver';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import Lightbox from 'react-image-lightbox'; import Lightbox from 'react-image-lightbox';
import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app 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 [visible, setVisible] = useState(false);
const [fileType, setFileType] = useState<'image' | 'pdf'>(); const [fileType, setFileType] = useState<'image' | 'pdf'>();
const { t } = useTranslation(); const { t } = useTranslation();
const internalFileList = useRef([]);
function closeIFrameModal() { function closeIFrameModal() {
setVisible(false); setVisible(false);
} }
useEffect(() => { useEffect(() => {
if (sync) { if (sync) {
setFileList(toFileList(value)); const fileList = toFileList(value);
setFileList(fileList);
internalFileList.current = fileList;
} }
}, [value, sync]); }, [value, sync]);
const uploadProps = useUploadProps({ ...props }); const uploadProps = useUploadProps({ ...props });
@ -55,7 +58,7 @@ Upload.Attachment = connect((props: UploadProps) => {
setFileType('image'); setFileType('image');
setVisible(true); setVisible(true);
setFileIndex(index); setFileIndex(index);
} else if(isPdf(file.extname)) { } else if (isPdf(file.extname)) {
setVisible(true); setVisible(true);
setFileIndex(index); setFileIndex(index);
setFileType('pdf'); setFileType('pdf');
@ -64,7 +67,7 @@ Upload.Attachment = connect((props: UploadProps) => {
} }
}; };
return ( return (
<div className={'ant-upload-list-picture-card-container'}> <div key={file.uid || file.id} className={'ant-upload-list-picture-card-container'}>
<div className="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture-card"> <div className="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture-card">
<div className={'ant-upload-list-item-info'}> <div className={'ant-upload-list-item-info'}>
<span className="ant-upload-span"> <span className="ant-upload-span">
@ -115,6 +118,9 @@ Upload.Attachment = connect((props: UploadProps) => {
} }
const index = prevFileList.indexOf(file); const index = prevFileList.indexOf(file);
prevFileList.splice(index, 1); prevFileList.splice(index, 1);
internalFileList.current = internalFileList.current.filter(
(item) => item.uid !== file.uid,
);
onChange(toValue([...prevFileList])); onChange(toValue([...prevFileList]));
return [...prevFileList]; return [...prevFileList];
}); });
@ -141,12 +147,17 @@ Upload.Attachment = connect((props: UploadProps) => {
listType={'picture-card'} listType={'picture-card'}
fileList={fileList} fileList={fileList}
onChange={(info) => { onChange={(info) => {
// info.fileList 有 BUG会导致上传状态一直是 uploading
// 所以这里仿照 antd 源码,自己维护一个 fileList
const list = updateFileList(info.file, internalFileList.current);
internalFileList.current = list;
setSync(false); setSync(false);
if (multiple) { if (multiple) {
if (info.file.status === 'done') { if (info.file.status === 'done') {
onChange(toValue(info.fileList)); onChange(toValue(list));
} }
setFileList(info.fileList.map(toItem)); setFileList(list.map(toItem));
} else { } else {
if (info.file.status === 'done') { if (info.file.status === 'done') {
// TODO(BUG): object 的联动有问题,不响应,折中的办法先置空再赋值 // TODO(BUG): object 的联动有问题,不响应,折中的办法先置空再赋值
@ -198,7 +209,7 @@ Upload.Attachment = connect((props: UploadProps) => {
]} ]}
/> />
)} )}
{visible && fileType === 'pdf' && ( {visible && fileType === 'pdf' && (
<Modal <Modal
open={visible} open={visible}
@ -207,7 +218,7 @@ Upload.Attachment = connect((props: UploadProps) => {
footer={[ footer={[
<Button <Button
style={{ style={{
textTransform: 'capitalize' textTransform: 'capitalize',
}} }}
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
@ -218,31 +229,35 @@ Upload.Attachment = connect((props: UploadProps) => {
> >
{t('download')} {t('download')}
</Button>, </Button>,
<Button onClick={closeIFrameModal} style={{textTransform: 'capitalize'}}> <Button onClick={closeIFrameModal} style={{ textTransform: 'capitalize' }}>
{t('close')} {t('close')}
</Button> </Button>,
]} ]}
width={'85vw'} width={'85vw'}
centered={true} centered={true}
> >
<div style={{ <div
padding: '8px', style={{
maxWidth: '100%', padding: '8px',
maxHeight: 'calc(100vh - 256px)', maxWidth: '100%',
height: '90vh', maxHeight: 'calc(100vh - 256px)',
width: '100%', height: '90vh',
background: 'white',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
overflowY: 'auto'
}} >
<iframe src={images[fileIndex].url} style={{
width: '100%', width: '100%',
maxHeight: '90vh', background: 'white',
flex: '1 1 auto' display: 'flex',
}}> flexDirection: 'column',
</iframe> alignItems: 'center',
overflowY: 'auto',
}}
>
<iframe
src={images[fileIndex].url}
style={{
width: '100%',
maxHeight: '90vh',
flex: '1 1 auto',
}}
></iframe>
</div> </div>
</Modal> </Modal>
)} )}
@ -305,3 +320,14 @@ Upload.DraggerV2 = connect(
); );
export default Upload; export default Upload;
function updateFileList(file: UploadFile, fileList: (UploadFile | Readonly<UploadFile>)[]) {
const nextFileList = [...fileList];
const fileIndex = nextFileList.findIndex(({ uid }) => uid === file.uid);
if (fileIndex === -1) {
nextFileList.push(file);
} else {
nextFileList[fileIndex] = file;
}
return nextFileList;
}

View File

@ -2,7 +2,6 @@ import { Field } from '@formily/core';
import { useField } from '@formily/react'; import { useField } from '@formily/react';
import { reaction } from '@formily/reactive'; import { reaction } from '@formily/reactive';
import { isArr, isValid, toArr as toArray } from '@formily/shared'; import { isArr, isValid, toArr as toArray } from '@formily/shared';
import { UploadChangeParam } from 'antd/es/upload';
import { UploadFile } from 'antd/es/upload/interface'; import { UploadFile } from 'antd/es/upload/interface';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useAPIClient } from '../../../api-client'; import { useAPIClient } from '../../../api-client';
@ -167,7 +166,7 @@ export const useUploadValidator = (serviceErrorMessage = 'Upload Service Error')
export function useUploadProps<T extends IUploadProps = UploadProps>({ serviceErrorMessage, ...props }: T) { export function useUploadProps<T extends IUploadProps = UploadProps>({ serviceErrorMessage, ...props }: T) {
useUploadValidator(serviceErrorMessage); useUploadValidator(serviceErrorMessage);
const onChange = (param: UploadChangeParam<UploadFile>) => { const onChange = (param: { fileList: any[] }) => {
props.onChange?.(normalizeFileList([...param.fileList])); props.onChange?.(normalizeFileList([...param.fileList]));
}; };
@ -212,7 +211,10 @@ export function useUploadProps<T extends IUploadProps = UploadProps>({ serviceEr
export const toItem = (file) => { export const toItem = (file) => {
if (file?.response?.data) { if (file?.response?.data) {
file = file.response.data; file = {
uid: file.uid,
...file.response.data,
};
} }
return { return {
...file, ...file,

View File

@ -40,7 +40,7 @@
background: rgba(0, 0, 0, 0.5); background: rgba(0, 0, 0, 0.5);
} }
.ant-upload-list-picture-card-container { .ant-upload-list-picture-card-container {
// margin-bottom: 28px; margin-bottom: 28px;
} }
} }