首页 > 其他分享 >react封装图片上传组件

react封装图片上传组件

时间:2023-02-27 14:55:32浏览次数:40  
标签:封装 url image react file error import 上传 const

支持表单受控和非受控使用,基于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,
        }}

 

标签:封装,url,image,react,file,error,import,上传,const
From: https://www.cnblogs.com/shyhuahua/p/17150368.html

相关文章

  • 上传gitlab代码后jenkins自动进行发布的配置
     1、安装​​GitLabPlugin​​​和​​GenericWebhookTriggerPlugin​​两个插件2、要在gitlab生成一个访问api的token 3、在jenkins的系统管理里找到下面界面进行输......
  • 滴滴前端高频react面试题汇总
    说说React组件开发中关于作用域的常见问题。在EMAScript5语法规范中,关于作用域的常见问题如下。(1)在map等方法的回调函数中,要绑定作用域this(通过bind方法)。(2)父组件传递......
  • 你要的react+ts最佳实践指南
    本文根据日常开发实践,参考优秀文章、文档,来说说TypeScript是如何较优雅的融入React项目的。温馨提示:日常开发中已全面拥抱函数式组件和ReactHooks,class类组件的写......
  • 腾讯前端必会react面试题合集
    React-Router的路由有几种模式?React-Router支持使用hash(对应HashRouter)和browser(对应BrowserRouter)两种路由规则,react-router-dom提供了BrowserRouter和HashRo......
  • 彻底搞懂React-hook链表构建原理
    写在前面的小结每一个hook函数都有对应的hook对象保存状态信息useContext是唯一一个不需要添加到hook链表的hook函数只有useEffect、useLayoutEffect以及us......
  • 从实现一个React到深度理解React框架核心原理
    前言这篇文章循序渐进地介绍实现以下几个概念,遵循本篇文章基本就能搞懂为啥需要fiber,为啥需要commit和phases、reconciliation阶段等原理。本篇文章又不完全和原文一致,这......
  • 校招前端高频react面试题合集
    了解redux吗?redux是一个应用数据流框架,主要解决了组件之间状态共享问题,原理是集中式管理,主要有三个核心方法:actionstorereduce工作流程view调用store的dispatch......
  • 前端一面常见react面试题(持续更新中)
    React组件中怎么做事件代理?它的原理是什么?React基于VirtualDOM实现了一个SyntheticEvent层(合成事件层),定义的事件处理器会接收到一个合成事件对象的实例,它符合W3C标准,且......
  • react生命周期
    componentDidMount/componentWillUnmountuseEffect(()=>{//挂载后的操作},[])useEffect(()=>{//添加事件监听return()=>{//取消事件监听}}......
  • react的jsx和React.createElement是什么关系?面试常问
    1、JSX在React17之前,我们写React代码的时候都会去引入React,并且自己的代码中没有用到,这是为什么呢?这是因为我们的JSX代码会被Babel编译为React.createElement,我们来......