Repository: arco-design/arco-design (ts)

Issue:
【Upload】limit属性拓展
- [x] I'm sure this does not appear in [the issue list of the repository](https://github.com/arco-design/arco-design/issues)

## Basic Info 

## What are the similar cases of this feature
无

## What problem does this feature solve?
目前设置limit的时候，如果上传的文件数量达到limit的值，上传按钮upload-trigger会自动隐藏。
业务中存在需要禁用而非隐藏的场景。目前只能通过监听fileList长度，设置disabled属性实现，而且需要在beforeUpload事件中自行处理多选事件，以防一次性多选，比较复杂。
希望能支持配置“在触及limit时，隐藏或者禁用上传按钮”的选项


<!-- generated by arco-issue. DO NOT REMOVE -->

Pull request description:
feat:   The limit parameter of the `Upload` component supports specifying that the upload node is disabled after the limit number is exceede
…umber is exceeded

<!--
  Thanks so much for your PR and contribution.

  Before submitting, please make sure to follow the Pull Request Guidelines: https://github.com/arco-design/arco-design/blob/main/CONTRIBUTING.md
-->

<!-- Put an `x` in "[ ]" to check a box) -->

## Types of changes

<!-- What types of changes does this PR introduce -->
- [x] New feature
- [ ] Bug fix
- [ ] Documentation change
- [ ] Coding style change
- [ ] Component style change
- [ ] Refactoring
- [ ] Performance improvement
- [ ] Test cases
- [ ] Continuous integration
- [ ] Typescript definition change
- [ ] Breaking change
- [ ] Others 

## Background and context

<!-- Explain what problem does the PR solve -->
<!-- Link to related open issues if applicable -->

## Solution

<!-- Describe how the problem is fixed in detail -->

## How is the change tested?

<!-- Unit tests should be added/updated for bug fixes and new features, if applicable -->
<!-- Please describe how you tested the change. E.g. Creating/updating unit tests or attaching a screenshot of how it works with your change -->

## Changelog

| Component | Changelog(CN) | Changelog(EN) | Related issues |
| --------- | ------------- | ------------- | -------------- |
| Upload          |        `Upload` 组件的 `limit` 参数支持指定超出限制数量后禁用上传节点      |       The limit parameter of the `Upload` component supports specifying that the upload node is disabled after the limit number is exceeded        |    Close #365             |
## Checklist:

- [ ] Test suite passes (`npm run test`)
- [ ] Provide changelog for relevant changes (e.g. bug fixes and new features) if applicable.
- [ ] Changes are submitted to the appropriate branch (e.g. features should be submitted to `feature` branch and others should be submitted to `main` branch)

## Other information

<!-- Please describe what other information that should be taken care of. E.g. describe the impact if introduce a breaking change -->


Patch:
diff --git a/components/Upload/README.en-US.md b/components/Upload/README.en-US.md
index 77118006..839d0a25 100644
--- a/components/Upload/README.en-US.md
+++ b/components/Upload/README.en-US.md
@@ -25,7 +25,7 @@ Upload file by selecting or dragging.
 |showUploadList|Whether to show upload list.It can be an object to customize the `previewIcon`, `removeIcon`,`fileIcon`, `reuploadIcon`, `cancelIcon`, `startIcon`, `errorIcon` and `fileName`|`boolean \| CustomIconType`|`true`|-|
 |autoUpload|Whether to automatically upload files after selecting them|`boolean`|`true`|-|
 |action|Uploading URL|`string`|`-`|-|
-|limit|maximum number of uploads allowed|`number`|`-`|-|
+|limit|maximum number of uploads allowed. Object type is supported in `2.28.0`|`number \| { maxCount: number; hideOnExceedLimit?: boolean }`|`-`|-|
 |disabled|Whether to disable|`boolean`|`-`|-|
 |drag|Whether to enable drag and drop upload|`boolean`|`-`|-|
 |multiple|Whether to allow multiple files to be selected|`boolean`|`-`|-|
diff --git a/components/Upload/README.zh-CN.md b/components/Upload/README.zh-CN.md
index 3d148da7..096270e1 100644
--- a/components/Upload/README.zh-CN.md
+++ b/components/Upload/README.zh-CN.md
@@ -25,7 +25,7 @@
 |showUploadList|是否展示上传文件列表。预览图标，删除图标，文件图标，重新上传图标，取消上传图标。|`boolean \| CustomIconType`|`true`|-|
 |autoUpload|是否选中文件后自动上传|`boolean`|`true`|-|
 |action|action|`string`|`-`|-|
-|limit|限制上传数量。超出后会隐藏|`number`|`-`|-|
+|limit|限制上传数量。默认超出后会隐藏上传节点。对象类型在 `2.28.0` 支持|`number \| { maxCount: number; hideOnExceedLimit?: boolean }`|`-`|-|
 |disabled|禁用|`boolean`|`-`|-|
 |drag|是否拖拽上传|`boolean`|`-`|-|
 |multiple|文件多选|`boolean`|`-`|-|
diff --git a/components/Upload/interface.tsx b/components/Upload/interface.tsx
index cc617a6a..2bfaeab3 100644
--- a/components/Upload/interface.tsx
+++ b/components/Upload/interface.tsx
@@ -106,10 +106,10 @@ export interface UploadProps {
    */
   action?: string;
   /**
-   * @zh 限制上传数量。超出后会隐藏
-   * @en maximum number of uploads allowed
+   * @zh 限制上传数量。默认超出后会隐藏上传节点。对象类型在 `2.28.0` 支持
+   * @en maximum number of uploads allowed. Object type is supported in `2.28.0`
    */
-  limit?: number;
+  limit?: number | { maxCount: number; hideOnExceedLimit?: boolean };
   /**
    * @zh 禁用
    * @en Whether to disable
@@ -290,6 +290,8 @@ export type UploadItem = {
 
 export interface UploaderProps extends UploadProps {
   prefixCls?: string;
+  limit?: number;
+  hide?: boolean;
   onFileStatusChange?: (file: UploadItem) => void;
 }
 
diff --git a/components/Upload/upload.tsx b/components/Upload/upload.tsx
index 57a8460c..f09a24b4 100644
--- a/components/Upload/upload.tsx
+++ b/components/Upload/upload.tsx
@@ -156,7 +156,6 @@ const Upload: React.ForwardRefRenderFunction<UploadInstance, PropsWithChildren<U
   });
 
   const {
-    disabled,
     listType,
     className,
     style,
@@ -168,10 +167,18 @@ const Upload: React.ForwardRefRenderFunction<UploadInstance, PropsWithChildren<U
   } = props;
 
   const fileList = getFileList(uploadState.current);
+  const limit = isNumber(props.limit)
+    ? { hideOnExceedLimit: true, maxCount: props.limit }
+    : { hideOnExceedLimit: true, ...props.limit };
+
+  const exceedLimit = limit.maxCount && limit.maxCount <= fileList.length;
+  const disabledUploadDom =
+    'disabled' in props ? props.disabled : !limit.hideOnExceedLimit && exceedLimit;
 
   const uploadDom = (
     <div
       {...omit(rest, [
+        'disabled',
         'directory',
         'onReupload',
         'defaultFileList',
@@ -203,8 +210,8 @@ const Upload: React.ForwardRefRenderFunction<UploadInstance, PropsWithChildren<U
         {
           [`${prefixCls}-type-${listType}`]: listType,
           [`${prefixCls}-drag`]: props.drag,
-          [`${prefixCls}-disabled`]: props.disabled,
-          [`${prefixCls}-hide`]: isNumber(props.limit) && props.limit <= fileList.length,
+          [`${prefixCls}-disabled`]: disabledUploadDom,
+          [`${prefixCls}-hide`]: limit.hideOnExceedLimit && exceedLimit,
         },
         className
       )}
@@ -213,6 +220,9 @@ const Upload: React.ForwardRefRenderFunction<UploadInstance, PropsWithChildren<U
       <Uploader
         ref={uploaderRef}
         {...props}
+        limit={limit.maxCount}
+        hide={limit.hideOnExceedLimit && exceedLimit}
+        disabled={disabledUploadDom}
         prefixCls={prefixCls}
         fileList={fileList}
         onProgress={(file: UploadItem, e: ProgressEvent) => {
@@ -251,7 +261,7 @@ const Upload: React.ForwardRefRenderFunction<UploadInstance, PropsWithChildren<U
         <UploadList
           progressProps={progressProps}
           showUploadList={showUploadList}
-          disabled={disabled}
+          disabled={props.disabled}
           listType={listType}
           fileList={fileList}
           renderUploadItem={renderUploadItem}
diff --git a/components/Upload/uploader.tsx b/components/Upload/uploader.tsx
index 761c299e..efbc0d3f 100644
--- a/components/Upload/uploader.tsx
+++ b/components/Upload/uploader.tsx
@@ -141,7 +141,7 @@ class Uploader extends React.Component<UploaderProps, UploaderState> {
 
   handleFiles = (files: File[]) => {
     const { limit, fileList, onExceedLimit, autoUpload } = this.props;
-    if (isNumber(limit) && (limit as number) < fileList.length + files.length) {
+    if (isNumber(limit) && limit < fileList.length + files.length) {
       return onExceedLimit && onExceedLimit(files, fileList);
     }
     const asyncUpload = (file: File, index: number) => {
@@ -199,9 +199,8 @@ class Uploader extends React.Component<UploaderProps, UploaderState> {
       disabled,
       drag,
       listType,
-      fileList,
+      hide,
       directory,
-      limit,
     } = this.props;
 
     return (
@@ -225,7 +224,7 @@ class Uploader extends React.Component<UploaderProps, UploaderState> {
         />
         <CSSTransition
           key="trigger-node"
-          in={!(isNumber(limit) && limit <= fileList.length)}
+          in={!hide}
           timeout={100}
           unmountOnExit
           classNames="fadeIn"


Test patch:
diff --git a/components/Upload/__test__/apis.test.tsx b/components/Upload/__test__/apis.test.tsx
index 155a9261..9b0d060e 100644
--- a/components/Upload/__test__/apis.test.tsx
+++ b/components/Upload/__test__/apis.test.tsx
@@ -200,4 +200,43 @@ describe('Upload api callbacks', function () {
       expect(fileList).toEqual([{ ...defaultFileList[1], percent: 100 }]);
     });
   });
+
+  it('onExceedLimit should be fired', async function () {
+    const mockFn = jest.fn();
+    await getWrapper({
+      onExceedLimit: mockFn,
+      limit: 1,
+      defaultFileList: [
+        {
+          name: '2',
+          uid: '2',
+          status: 'done',
+        },
+      ],
+    });
+
+    expect(mockFn.mock.calls.length).toBe(1);
+  });
+
+  it('limit hide ', async function () {
+    const wrapper = await getWrapper({
+      limit: 1,
+    });
+
+    await sleep(200);
+    wrapper.update();
+
+    expect(wrapper.find('.arco-upload-trigger')).toHaveLength(0);
+  });
+
+  it('limit disabled ', async function () {
+    const wrapper = await getWrapper({
+      limit: { maxCount: 1, hideOnExceedLimit: false },
+    });
+
+    await sleep(200);
+    wrapper.update();
+    expect(wrapper.find('.arco-upload-trigger')).toHaveLength(1);
+    expect(wrapper.find('TriggerNode').props().disabled).toBe(true);
+  });
 });