react封装图片上传组件

支持表单受控和非受控使用,基于antd upload 进行的二次封装,

使用场景如下图:

 1.组件文件夹

 

 2. index.tsx贴代码

import React, { useEffect, useMemo, useState } from 'react';
import { ImageFilesWrapper } from './style';
import type { RcFile, UploadProps } from 'antd/es/upload';
import type { UploadFile } from 'antd/es/upload/interface';
import { message, Modal } from 'antd';
import icon from './img/uil_image_plus.svg';
import { uploadImageFileWithNoToken } from 'requests/fileUpload-requests';
import { addUriPrefixIfNeeded } from 'utils/requests/utils';  // 转化url
import { v4 } from 'uuid';
interface Iprops {
  onChange?: (data) => void;
  onRemove?: (data) => void;
  maxCount?: number; //可上传的图片张数
  value?: string[]; // 表单默认值
  multiple?: boolean;
}
interface Iflie {
  uid: string;
  status: string;
  url: string;
}

//图片上传
export default function ImageUpload(props: Iprops) {
  const { onChange, multiple, value, onRemove, maxCount } = props;
  // 处理 表单传入的值
  const defaultList = useMemo(() => {
    if (value?.length) {
      let list: Iflie[] = [];
      value?.map(v => {
        list.push({
          uid: v4(),
          status: 'done',
          url: addUriPrefixIfNeeded(v),
        });
      });
      return list;
    }
  }, [value]);

  const [fileList, setFileList] = useState<any>(defaultList || []);

  const [previewVisible, setPreviewVisible] = useState(false);
  const [previewImage, setPreviewImage] = useState('');
  const [previewTitle, setPreviewTitle] = useState('');

  const handleCancel = () => setPreviewVisible(false);
  const handleChange: UploadProps['onChange'] = async ({
    fileList: newFileList,
  }) => {
    setFileList(newFileList || []);
    let urlList: any = [];
    newFileList?.map(v => {
      if (v?.response?.fileId || v.url) {
        urlList.push(v?.response?.fileId || v.url);
      }
    });
    onChange?.(urlList);
  };

  const getBase64 = (file: RcFile): Promise<string> =>
    new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onload = () => resolve(reader.result as string);
      reader.onerror = error => reject(error);
    });

  const handlePreview = async (file: UploadFile) => {
    if (!file.url && !file.preview) {
      file.preview = await getBase64(file.originFileObj as RcFile);
    }
    setPreviewImage(file.url || (file.preview as string));
    setPreviewVisible(true);
    setPreviewTitle(
      file.name || file.url!.substring(file.url!.lastIndexOf('/') + 1),
    );
  };

  async function uploadHeadImg(info) {
    try {
      const res = await uploadImageFileWithNoToken(info.file);
      if (res) {
        message.info('上传成功');
        info.onSuccess(res, info.file); // 上传成功触发
      } else {
        message.error('上传失败');
      }
    } catch (error) {
      info.onError(error, undefined, info.file); // 上传失败触发
      // @ts-ignore
      message.error(error?.message || '上传失败');
      console.error(error);
    }
  }

  return (
    <>
      <ImageFilesWrapper
        listType="picture-card"
        fileList={fileList}
        onPreview={handlePreview}
        multiple={multiple}
        maxCount={maxCount}
        accept=".png,.jpg,.jpeg"
        beforeUpload={file => {
          if (!['image/png', 'image/jpg', 'image/jpeg'].includes(file.type)) {
            message.info('仅支持上传png/jpg/jpeg格式的图片');
          }
          return ['image/png', 'image/jpg', 'image/jpeg'].includes(file.type);
        }}
        onChange={handleChange}
        onRemove={(data: any) => {
          console.log(data, 'data,remove');
          if (data?.disabled) {
            return Promise.resolve(false);
          } else {
            return onRemove ? onRemove(data) : Promise.resolve(true);
          }
        }}
        customRequest={uploadHeadImg}
      >
        {((maxCount && fileList?.length < maxCount) || !maxCount) && (
          <div className="imgButton">
            <img src={icon} alt="" />
            <div>添加图片</div>
          </div>
        )}
      </ImageFilesWrapper>

      <Modal
        open={previewVisible}
        title={previewTitle}
        footer={null}
        onCancel={handleCancel}
      >
        <img alt="example" style={{ width: '100%' }} src={previewImage} />
      </Modal>
    </>
  );
}

3.样式代码

import { Upload } from 'antd';
import styled from 'styled-components/macro';

export const ImageFilesWrapper = styled(Upload)`
  text-align: left;
  .adm-image-uploader-cell {
    width: 60px;
    height: 60px;
    line-height: 60px;
  }
  .adm-image-uploader {
  }
  .upload-finsh {
    color: #0ebd73;
    margin-right: 10px;
  }
  .imgButton {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--primary-color);
    img {
      margin-right: 5px;
    }
  }
`;

4.页面表单使用

        <Form.Item
          label="人员头像"
          name="headerImg"
          rules={[
            {
              required: false,
            },
          ]}
          valuePropName="value"
          trigger="onChange"
        >
          <ImageUpload maxCount={1} />
        </Form.Item>

表单初始值赋:

 initialValues={{
          headerImg: infoToEdit?.avatarUrl ? [infoToEdit.avatarUrl] : undefined,
        }}

 

posted @ 2023-02-27 14:48  行走的蒲公英  阅读(776)  评论(0编辑  收藏  举报