# 上传文件到服务器端通用组件UploadPanel与上传按钮组件UploadButton

组件来源: 办公系统v3 - common组件

## 1.功能说明

可通过插入UploadPanel或者UploadButton组件，实现本地文件上传到远程（minio等等）。UploadButton需配合UploadPanel使用，UploadPanel需要进行适当封装后使用，封装方法与UploadButton类似。

## 2.组件依赖

### (1)UploadPanel组件依赖

- react

- antd

- axios

- 通用组件DragModal

### (2)UploadButton组件依赖

- react

- antd

- 通用组件UploadPanel

## 3.依赖结构体

```tsx
export interface UploadFileLocale {
    file: RcFile,
    callbackFn: (param: FileUploadParam) => Promise<any> //具体上传的回调函数
}

//上传文件上下文的接口
export interface UploadFileState {
    files: UploadFileLocale[]
    visible: boolean
    startUpload: (files: UploadFileLocale[]) => void
    onEnd: (value: number) => void
    count: number // 用于记录上传了多少个文件
}

export interface FileUploadParam {
    md5: string,
    suffix: string,
    filePath: string,
    name: string,
    type?: number,
    replaceId?: number,
    fileSize: number,
}
```

## 4.组件接口

### (1)UploadPanel组件接口

```tsx
interface IProps{
    //通过md5判断文件是否存在的api,如果存在则返回文件对象（文件对象可自己定义），不存在返回null
    checkFileExists: (md5: string, params?: {}) => Promise<AxiosResponse<any>>     
    //通过关键字key查询文件上传地址url（关键字可以为文件名等等）
    getUploadUrl: (key: string, params?: {}) => Promise<AxiosResponse<any>>
    uploadContext: React.Context<UploadFileState>   //上传文件上下文
}
```

### (2)UploadButton组件接口

```tsx
interface IProps {
    //上下文服务
    uploadFileContext: React.Context<UploadFileState>
    //上传之后的回调函数
    uploadCallbackFn: (uploadFile: FileUploadParam) => Promise<any>
    //按钮名称
    title: string
}
```

## 5.使用方法

### (1)UploadPanel组件使用方法

1) 在项目最外层插入UploadPanel组件与对应上下文

下面例子为添加到App.tsx中

```tsx
import React from 'react';
import UploadPanel, { UploadFileState, useUploadService } from './common/upload-panel/upload-panel';

export const UploadFileContext = React.createContext<UploadFileState>(null);

function App() {
    //此处可添加其他内容
    return (
        <UploadFileContext.Provider value={useUploadService()}>
            <UploadPanel checkFileExists={...} getUploadUrl={...} uploadContext={UploadFileContext}/>
            {/*需要的api请自行定义；此处可添加其他内容*/}
        </UploadFileContext.Provider>
        )
}

export default App;
```

2) 在需要使用下载功能的子组件中，引入App.tsx中定义的UploadFileContext，并

```tsx
import { UploadFileContext } from '@/App';
import { FileUploadParam, UploadFileLocale, UploadFileState } from '@/common/upload-panel/upload-panel';
import { Button, Upload } from "antd";
import { ArrowUpOutlined } from "@ant-design/icons";
import { useContext, useEffect, useState } from "react";
export default function X() {
    //引入上下文
    const uploadFileContext = useContext(UploadFileContext);

    //上传文件的回调函数
    const uploadCallbackFn = (uploadFile: FileUploadParam) => {
        return new Promise(resolve => {
            //自定义回调内容
        })
    }

    //上传文件后将文件储存至此
    let [uploadFileList, setUploadFileList] = useState([]);

    //当上传文件列表发生改变时，调用上传文件api开始上传
    useEffect(() => {
        if (uploadFileList.length === 0) return;
        let files: UploadFileLocale[] = uploadFileList.map(item => {
            return {
                file: item,
                callbackFn: props.uploadCallbackFn
            }
        })
        uploadFileContext.startUpload(files); //上传的关键
    }, [uploadFileList])

    //在执行antd本身组件Upload的上传文件功能之前所做的事
    //这里需要禁止antd本身组件Upload的上传行为，因此必须返回false
    const beforeUpload = (file, fileList) => {
        if (uploadFileList.length === 0) {
            setUploadFileList([...fileList]);
        }else{
            if(file){
                setUploadFileList([file])
            }
        }
        //必须返回false，以阻止antd组件自身上传行为
        return false;
    }

    return (
        <div className="main-X-container">
            <Upload
            name="file"
            beforeUpload={beforeUpload}
            showUploadList={false}                
            >
                <Button icon={<ArrowUpOutlined />} title={"上传"} size="small" type="dashed"></Button>
            </Upload>
        </div>
    )
}
```

### (2)UploadButton组件使用方法

第一步与UploadPanel一致，需要在最外层添加UploadPanel组件，第二步中，可引入上下文后直接调用UploadButton

```tsx
import { message, Space } from 'antd';
import { useState } from 'react';
import { FileUploadParam } from '@/common/upload-panel/upload-panel';
import { UploadFileContext } from '@/App';
import { DownloadFile } from '@/common/download-panel/download-panel';
import UploadButton from '@/common/upload-panel/upload-button';

export default function X() {
    //上传文件的回调函数，上传调取api的逻辑放在此处
    const uploadCallbackFn = (uploadFile: FileUploadParam) => {
        return new Promise(resolve => {
            //自定义回调内容
        })
    }

    return (
        <div className="main-X-container">
            <UploadButton uploadFileContext={UploadFileContext} uploadCallbackFn={uploadCallbackFn}/>
        </div>
    )
}
```
