diff --git a/src/upload/_example/request-method.jsx b/src/upload/_example/request-method.jsx new file mode 100644 index 000000000..3d682628e --- /dev/null +++ b/src/upload/_example/request-method.jsx @@ -0,0 +1,67 @@ +import React, { useCallback, useState } from 'react'; +import { Radio, Upload } from 'tdesign-react'; + +const RequestMethod = () => { + const [files, setFiles] = useState([]); + const [uploadMethod, setUploadMethod] = useState('requestSuccessMethod'); + + // customize upload `file`, if success, return url + const requestSuccessMethod = useCallback( + (file) => + new Promise((resolve) => { + // set file.percent for mock upload progress + // eslint-disable-next-line no-param-reassign + file.percent = 0; + let timer = setTimeout(() => { + // resolve 参数为关键代码 + resolve({ + status: 'success', + response: { url: 'https://tdesign.gtimg.com/site/avatar.jpg' }, + }); + // eslint-disable-next-line no-param-reassign + file.percent = 100; + clearTimeout(timer); + timer = null; + }, 520); + }), + [], + ); + + // customize upload `file`, if fail, return error message + const requestFailMethod = useCallback( + () => + new Promise((resolve) => { + // resolve 参数为关键代码 + resolve({ + status: 'fail', + error: 'for some reason, upload fail', + }); + }), + [], + ); + + const onChangeUploadMethod = useCallback((value) => { + setUploadMethod(value); + setFiles([]); + }, []); + + return ( +
+
+ + 上传成功示例 + 上传失败示例 + +
+ + +
+ ); +}; + +export default RequestMethod; diff --git a/src/upload/type.ts b/src/upload/type.ts index 214dfb873..8507d0663 100644 --- a/src/upload/type.ts +++ b/src/upload/type.ts @@ -258,8 +258,8 @@ export interface UploadRemoveContext { } export interface SuccessContext { - e: ProgressEvent; + e?: ProgressEvent; file: UploadFile; - fileList: UploadFile[]; + fileList?: UploadFile[]; response: any; } diff --git a/src/upload/upload.tsx b/src/upload/upload.tsx index 446f9a1e8..de05d9278 100644 --- a/src/upload/upload.tsx +++ b/src/upload/upload.tsx @@ -13,7 +13,14 @@ import FlowList from './themes/flow-list/index'; import BooleanRender from './boolean-render'; import { finishUpload, isSingleFile, updateFileList } from './util'; import { FlowRemoveContext, TdUploadFile, UploadProps } from './types'; -import { ProgressContext, SuccessContext, TdUploadProps, UploadFile, UploadRemoveContext } from './type'; +import { + ProgressContext, + RequestMethodResponse, + SuccessContext, + TdUploadProps, + UploadFile, + UploadRemoveContext, +} from './type'; import useDefaultValue from './hooks/useDefaultValue'; const urlCreator = window.webkitURL || window.URL; @@ -45,6 +52,7 @@ const Upload: React.ForwardRefRenderFunction = (props, ref onRemove, onDragenter, onDragleave, + requestMethod, files: fileList = [], } = useDefaultValue, UploadProps>(props, []); @@ -92,7 +100,7 @@ const Upload: React.ForwardRefRenderFunction = (props, ref }; const onError = useCallback( - (options: { event: ProgressEvent; file: TdUploadFile; response?: any }) => { + (options: { event?: ProgressEvent; file: TdUploadFile; response?: any }) => { const { event, file, response } = options; file.status = 'fail'; let res = response; @@ -156,19 +164,41 @@ const Upload: React.ForwardRefRenderFunction = (props, ref [fileList, onChange, onProgress], ); + const handleRequestMethod = useCallback( + (file: UploadFile) => { + if (typeof requestMethod !== 'function') { + console.warn('TDesign Upload Warn: `requestMethod` must be a function.'); + return; + } + requestMethod(file).then((res: RequestMethodResponse) => { + if (!handleRequestMethodResponse(res)) return; + if (res.status === 'success') { + return handleSuccess({ file, response: res.response }); + } + if (res.status === 'fail') { + const r = res.response || {}; + onError({ file, response: { ...r, error: res.error } }); + } + }); + }, + [handleSuccess, onError, requestMethod], + ); const upload = useCallback( - (uploadFile: TdUploadFile): Promise => { + async (uploadFile: TdUploadFile): Promise => { const file = { ...uploadFile }; if (file.status !== 'waiting') { return; } - if (!action) { - console.error('TDesign Upload Error: action is required.'); + if (!action && !requestMethod) { + console.error('TDesign Upload Error: action or requestMethod is required.'); return; } setErrorMsg(''); // eslint-disable-next-line no-param-reassign file.status = 'progress'; + if (requestMethod) { + return handleRequestMethod(file); + } request({ action, data, @@ -181,9 +211,41 @@ const Upload: React.ForwardRefRenderFunction = (props, ref onSuccess: handleSuccess, }); }, - [action, data, handleProgress, handleSuccess, headers, name, onError, withCredentials], + [ + action, + data, + handleProgress, + handleRequestMethod, + handleSuccess, + headers, + name, + onError, + requestMethod, + withCredentials, + ], ); + function handleRequestMethodResponse(res: RequestMethodResponse) { + if (!res) { + console.error('TDesign Upload Error: `requestMethodResponse` is required.'); + return false; + } + if (!res.status) { + console.error( + 'TDesign Upload Error: `requestMethodResponse.status` is missing, which value is `success` or `fail`', + ); + return false; + } + if (!['success', 'fail'].includes(res.status)) { + console.error('TDesign Upload Error: `requestMethodResponse.status` must be `success` or `fail`'); + return false; + } + if (res.status === 'success' && (!res.response || !res.response.url)) { + console.warn('TDesign Upload Warn: `requestMethodResponse.response.url` is required, when `status` is `success`'); + } + return true; + } + const formatFiles = (files: File[] = []): TdUploadFile[] => files.map((fileRaw) => { const file = typeof format === 'function' ? format(fileRaw) : fileRaw;