首页 > 其他分享 >【React工作记录八十七】React+antDesign实现上传图片功能(类组件)

【React工作记录八十七】React+antDesign实现上传图片功能(类组件)

时间:2023-06-05 18:32:08浏览次数:39  
标签:八十七 String imgList param React && props antDesign size

前言

大家好 我是歌谣 今天继续给大家带来工作中实战部分的一些代码的封装和认识

需求实现

1可以支持上传最多九张图片
2图片支持预览 替换 删除
3支持自定义上传文件大小 格式 未上传提示

实现效果

【React工作记录八十七】React+antDesign实现上传图片功能(类组件)_ide

子组件封装

UploadImage组件

* @Description: 公共上传图片
 * @param {Array} type 图片格式。默认类型为png,jpeg
 * @param {Number} size 图片限制大小 默认大小为8M
 * @param {String} errorTypeMsg 图片格式错误文字提示
 * @param {String} errorTypeMsg 图片大小错误文字提示
 * @param {Function} handleFetchUrl 选中图片后回调函数
 * @param {String} uploadUrl 上传图片请求的url,默认为admin/fileUpload
 * @param {String} iconText 按钮文字
 * @param {Object} style 样式
 * @param {String} value 当前图片地址
 
 */
import React, { Component } from 'react';
import { Tooltip, Upload, Icon, message, Modal, Divider, Spin } from 'antd';
import styles from './index.less';
import { fileUpload } from '@/services/common/upload';
 
export default class UploadImage extends Component {
  constructor(props) {
    super(props);
    this.state = {
      visible: false,
      shadow: false,
      loading: false,
    };
  }
 
  componentWillMount() {
    this.props.onRef && this.props.onRef(this);
  }
 
  /**
   * @description: 上传前格式大小等校验
   */
  beforeUpload = (file) => {
    /**
     * @description: 父组件传入的参数
     * @param {Array} type 图片格式。默认类型为png,jpeg
     * @param {Number} size 图片限制大小 默认大小为8M
     * @param {String} errorTypeMsg 图片格式错误文字提示
     * @param {String} errorSizeMsg 图片大小错误文字提示
     * @param {Function} checkSize 文件大小校验方式
     */
    let {
      type = ['image/jpeg', 'image/png', 'image/gif'],
      size,
      errorTypeMsg,
      errorSizeMsg,
      checkSize,
    } = this.props;
    size ? (size = size) : (size = 8);
    if (checkSize) {
      size = checkSize(file.type);
    }
    const isJpgOrPng = type.includes(file.type);
    if (!isJpgOrPng) {
      message.error(
        errorTypeMsg || '图片/视频格式错误!请上传jpeg/png/gif格式图片或avi/mp4格式视频'
      );
    }
    const isLt8M = file.size < size * 1024 * 1024;
    if (!isLt8M) {
      message.error(errorSizeMsg || '图片/视频大小限制' + size + 'M!');
    }
    return isJpgOrPng && isLt8M;
  };
 
  handleUpload = ({ file }) => {
    /**
     * @description:
     * @param {Function} handleFetchUrl 选中图片后回调函数
     * @param {String} uploadUrl 上传图片请求的url,默认为admin/fileUpload
     */
    const { dispatch, uploadUrl, handleFetchUrl } = this.props;
    const formData = new FormData();
    formData.append('file', file);
    formData.append('fileCode', 'PIC');
    this.setState({ loading: true });
    const upload = async (record) => {
      let upUrl = uploadUrl || fileUpload;
      try {
        const response = await upUrl(formData);
        if (response.returnCode === 0) {
          if (handleFetchUrl && typeof handleFetchUrl === 'function') {
            handleFetchUrl(response.data, file.type);
          }
          message.success('上传成功');
          //上传成功的回调
          this.props.handleUploadImage && this.props.handleUploadImage();
        }
        this.setState({ loading: false });
      } catch (error) {
        console.log(error, 'error');
        this.setState({ loading: false });
      }
    };
    upload();
    // dispatch({
    //   type: uploadUrl || 'admin/fileUpload',
    //   payload: formData,
    //   callback: (response) => {
    //     if (response.returnCode === 0) {
    //       if (handleFetchUrl && typeof handleFetchUrl === 'function') {
    //         handleFetchUrl(response.data, file.type);
    //       }
    //       message.success('上传成功');
    //     }
    //   },
    // });
  };
 
  /**
   * @description: 改变visible
   * @param {*}
   */
  handleSetVisible = (val, event) => {
    this.setState({
      visible: val,
    });
    if (event) {
      event.stopPropagation();
    }
  };
 
  /**
   * @description: 改变image上的阴影显示
   */
  setShadow = (val) => {
    this.setState({
      shadow: val,
    });
  };
 
  /**
   * @description: 删除图片
   */
  handleDeleteImg = (event) => {
    const { dispatch, uploadUrl, handleFetchUrl } = this.props;
    handleFetchUrl('');
    if (event) {
      //函数删除的回调
      this.props.onHandleDelete && this.props.onHandleDelete();
      event.stopPropagation();
    }
  };
 
  render() {
    /**
     * @description:
     * @param {String} iconText 按钮文字
     * @param {Object} style 样式
     * @param {String} value 内容
     * @param {String} fileType 文件类型  img/video
     * @param {String} backgroundImg 背景图,如果是video,缩略图中显示背景图,弹窗大图中显示video
     * @param {String} showDelete 是否显示删除按钮,showDelete=delete 不显示
     * @param {Number} size 图片大小单位M,默认8
     */
    const {
      iconText,
      style,
      value,
      fileType,
      backgroundImg,
      disabled,
      infoText,
      showDelete,
      size,
      errorSizeMsg,
      errorTypeMsg,
    } = this.props;
    console.log(iconText)
    const { visible, shadow, loading } = this.state;
    return (
      <div className={styles.ContentBox}>
        <Spin spinning={loading}>
          <Upload
            listType="picture-card"
            showUploadList={false}
            customRequest={this.handleUpload}
            beforeUpload={this.beforeUpload}
            disabled={disabled}
          >
            {value ? (
              <div
                className={styles.imgContent}
                style={{
                  width: 150,
                  height: 150,
                  display: 'flex',
                  flexDirection: 'column',
                  alignItems: 'center',
                  justifyContent: 'center',
                  ...style,
                }}
                onm ouseOver={() => this.setShadow(true)}
                onm ouseLeave={() => this.setShadow(false)}
              >
                {(fileType === 'img' || !fileType) && (
                  <img
                    alt={iconText}
                    src={value}
                    style={{
                      maxWidth: (style && style.width && style.width) || 150,
                      maxHeight: (style && style.height && style.height) || 150,
                    }}
                  />
                )}
                {fileType === 'video' && (
                  <img
                    alt={iconText}
                    src={backgroundImg}
                    style={{
                      maxWidth: (style && style.width && style.width) || 150,
                      maxHeight: (style && style.height && style.height) || 150,
                    }}
                  />
                )}
                {shadow && (
                  <div className={styles.imgShadow}>
                    <div
                      className={styles.shadowDiv}
                      onClick={(event) => {
                        event.stopPropagation();
                      }}
                    />
                    <div className={styles.shadowDiv}>
                      <Tooltip title="放大">
                        <Icon
                          type="zoom-in"
                          onClick={(event) => {
                            this.handleSetVisible(true, event);
                          }}
                        />
                      </Tooltip>
                      <Divider type="vertical" style={{ width: 2 }} />
                      <Tooltip title="替换">
                        <Icon type="upload" />
                      </Tooltip>
 
                      {!disabled && showDelete != 'delete' && (
                        <>
                          <Divider type="vertical" style={{ width: 2 }} />
                          <Tooltip title="删除">
                            <Icon
                              type="delete"
                              onClick={(event) => {
                                this.handleDeleteImg(event);
                              }}
                            />
                          </Tooltip>
                        </>
                      )}
                    </div>
                    <div
                      className={styles.shadowDiv}
                      onClick={(event) => {
                        event.stopPropagation();
                      }}
                    />
                  </div>
                )}
              </div>
            ) : (
              <div
                style={{
                  width: 150,
                  height: 150,
                  display: 'flex',
                  flexDirection: 'column',
                  alignItems: 'center',
                  justifyContent: 'center',
                  ...style,
                }}
              >
                <Icon type="plus" />
                <div className="ant-upload-text" 
style={{ marginTop: 14, whiteSpace: 'normal' }}>
                  {iconText || '上传图片'}
                </div>
              </div>
            )}
          </Upload>
          <div style={{ color: '#666', 
fontSize: '12px', lineHeight: '12px', textAlign: 'center' }}>
            {infoText ? infoText : ''}
          </div>
          <Modal
            maskClosable={false}
            maskClosable={false}
            visible={visible}
            footer={null}
            onCancel={() => this.handleSetVisible(false)}
          >
            {(fileType === 'img' || !fileType) && (
              <img
                alt={iconText}
                style={{
                  width: 476,
                }}
                src={value}
              />
            )}
            {fileType === 'video' && (
              <video
                alt={iconText}
                style={{
                  width: 476,
                }}
                src={value}
                controls
              />
            )}
          </Modal>
        </Spin>
      </div>
    );
  }
}

fileUpload为接口调用 export function fileUpload(params) { return request({ url: xxxxxxxx, method: 'form', params }); }

BaseUploadImage组件

* @Description: 公共上传图片
 * @param {Array} type 图片格式。默认类型为png,jpeg
 * @param {Number} size 图片限制大小 默认大小为8M
 * @param {String} errorTypeMsg 图片格式错误文字提示
 * @param {String} errorTypeMsg 图片大小错误文字提示
 * @param {Function} handleFetchUrl 选中图片后回调函数,目前只在单选中有,适配旧版本
 * @param {String} uploadUrl 上传图片请求的url,默认为admin/fileUpload
 * @param {String} iconText 按钮文字
 * @param {Object} style 样式
 * @param {String} value 当前图片地址
 * @param {Boolean} multiple 是否多选
 *  * *@param {String} showDelete 是否显示删除按钮,showDelete=0 不显示
 * * @param {Number} size 图片大小单位M
 
 */
import React, { Component } from 'react';
import { Tooltip, Upload, Icon, message, Modal, Divider } from 'antd';
import styles from './index.less';
import UploadImage from './upload.js';
 
export default class BaseUploadImage extends Component {
  constructor(props) {
    super(props);
    this.state = {
      imgList: (props.multiple ? props.value : [props.value]) || [''],
    };
  }
 
  componentWillMount() {
    this.props.onRef && this.props.onRef(this);
    window.up = this;
  }
 
  /**
   * @description: 父组件传入的默认值发生改变时,更改图片的默认显示
   * @param {*} nextProps 最新的props
   */
  componentWillReceiveProps(nextProps) {
    const {
      props: { multiple, maxCount = 9 },
    } = this;
    const { value } = nextProps;
    let imgList;
    if (nextProps.value !== this.props.value) {
      if (multiple) {
        imgList = value;
        if (value && value.length && value.length < maxCount) {
          imgList.push('');
        }
      } else {
        imgList = [value];
      }
      this.setState({
        imgList: imgList || [''],
      });
    }
  }
 
  /**
   * @description: 上传图片回调
   * @param {*} val 上传图片后服务器返回的值
   * @param {*} index 上传的图片index,多选时用
   */
  handleSetImg = (val, index) => {
    const { multiple, maxCount = 9 } = this.props;
    const { imgList } = this.state;
    if (multiple) {
      if (!val && imgList.length > 0) {
        if (imgList.length === maxCount) {
          imgList.push('');
        }
        imgList.splice(index, 1);
      } else {
        imgList[index] = val;
        if (maxCount > imgList.length) {
          imgList.push('');
        }
      }
      this.setState({
        imgList,
      });
    } else {
      this.setState({ imgList: [val] });
    }
  };
  handleChange = () => {
    this.props.handleDeleteImg && this.props.handleDeleteImg();
  };
  handleUploadImg = () => {
    console.log(22222);
    this.props.handleUpload && this.props.handleUpload();
  };
  /**
   * @description: 获取当前上传成功的图片
   * @return {*} 当前上传成功的图片
   */
  getActiveImg = () => {
    const { multiple, maxCount = 9 } = this.props;
    const { imgList } = this.state;
    if (multiple) {
      //因为多选的时候,如果没有上传到最大可上传限度,会多出一条数据,所以如果上传数量小于maxCount,需要删掉最尾的一条数据
      if (maxCount === imgList.length && imgList[imgList.length - 1]) {
        return imgList;
      } else {
        return imgList.slice(0, imgList.length - 1);
      }
    } else {
      return imgList && imgList[0];
    }
  };
 
  render() {
    /**
     * @description:
     * @param {Boolean} multiple 是否多选
     * @param {Number} maxCount 多选时,最多可以上传几个
     */
    const {
      multiple,
      maxCount = 9,
      iconText,
      style,
      fileType,
      backgroundImg,
      disabled,
      infoText,
      dispatch,
      showDelete,
      size,
      errorSizeMsg,
      errorTypeMsg,
    } = this.props;
    const { imgList } = this.state;
    return (
      <div className={styles.ContentBox}>
        {multiple && (imgList && typeof imgList === 'object' && imgList.length > 0) ? (
          imgList.map((item, index) => {
            console.log(item);
            return (
              maxCount >= index && (
                <UploadImage
                  onHandleDelete={() => {
                    this.handleChange();
                  }}
                  handleUploadImage={() => {
                    this.handleUploadImg();
                  }}
                  iconText={iconText}
                  style={style}
                  fileType={fileType}
                  backgroundImg={backgroundImg}
                  disabled={disabled}
                  infoText={infoText}
                  key={index}
                  dispatch={dispatch}
                  value={item}
                  showDelete={showDelete}
                  size={size}
                  errorTypeMsg={errorTypeMsg}
                  errorSizeMsg={errorSizeMsg}
                  handleFetchUrl={
                    this.props.handleFetchUrl || ((val) => this.handleSetImg(val, index))
                  }
                />
              )
            );
          })
        ) : (
          <UploadImage
            onHandleDelete={() => {
              this.handleChange();
            }}
            handleUploadImage={() => {
              this.handleUploadImg();
            }}
            iconText={iconText}
            style={style}
            fileType={fileType}
            backgroundImg={backgroundImg}
            disabled={disabled}
            infoText={infoText}
            dispatch={dispatch}
            value={imgList[0]}
            showDelete={showDelete}
            size={size}
            errorTypeMsg={errorTypeMsg}
            errorSizeMsg={errorSizeMsg}
            handleFetchUrl={this.props.handleFetchUrl || ((val) => this.handleSetImg(val))}
          />
        )}
      </div>
    );
  }
}

样式文件index.less

.ContentBox {
  display: inline-block;
  .imgContent {
    padding: 8px;
    position: relative;
    display: flex;
    justify-content: center;
    align-items: center;
    .imgShadow {
      width: 100%;
      height: 100%;
      position: absolute;
      background: rgba(0,0,0,0.5);
      .shadowDiv {
        height: 33.3333333%;
        display:flex;
        justify-content: space-around;
        color: #fff;
        align-items: center;
        font-size: 18px;
        padding: 0 10px;
      }
    }
  }
}

父组件引用

<Form.Item label="上传图片">
              <BaseUploadImage
                onRef={(ref) => {
                  this.upload = ref;
                }}
                value={this.state.themeImgPath}
                multiple
              />
            </Form.Item>

总结

我是歌谣 放弃很容易 但是坚持一定很酷 微信公众号关注前端小歌谣带你进入前端巅峰交流群

标签:八十七,String,imgList,param,React,&&,props,antDesign,size
From: https://blog.51cto.com/u_14476028/6418389

相关文章

  • 【React工作记录八十八】React+antDesign封装一个tab组件(类组件)
    前言我是歌谣放弃很容易但是坚持一定很酷喜欢我就一键三连哈微信公众号关注前端小歌谣在我们的工作生活中每次学习一个框架我们就不免要封装组件而具备封装一个完美组件的能力我称之为"优秀"简单的父子组件父组件<Geyao/>子组件importReact,{Component}from'react';......
  • React 动态菜单-不限级递归菜单树
    import{FC,useState}from"react";import{Layout,Menu}from'antd';import{Link}from'react-router-dom'import{getData}from"../../mock-data";const{Header,Content,Footer,Sider}=Layout;//菜单数据结构type......
  • react中reduce基本使用
    importReact,{useReducer}from'react';import'./App.css';constApp=()=>{constreduce=(state,action)=>{constactionFn={add:function(){return{...state,count:state.count+1}......
  • 【React工作记录八十六】React+Hook+ts+antDesignMobile实现input自动获取功能
    前言大家好我是歌谣今天继续处理工作遇到的问题目前要做的问题是移动端需要做一个进入页面获取焦点的功能实现演示核心代码input上面绑定refs<InputonChange={handleChange}ref={inputRef}placeholder="请输入条形编码或者扫码录入"/>定义状态constinputRef:any=useRef(nul......
  • AntDesign中a-menu的使用案例
    <template><divclass="nav-bar":class="{collapsed:collapsed}"><divclass="collapse-btn"@click="toggleCollapsed"><a-icon:type="collapsed?'menu-unfold':'......
  • 【由浅到深】聊聊 Vue 和 React 的区别,看看你在哪个段位
    大家好,我是沐华。最近部门招人,捞了一批简历至少都是5年以上的前端来面试,其中不乏360的,腾讯的,简历上写的基本都是熟练使用Vue2、Vue3、React并阅读源码对其实现原理有自己的理解,实际问起来却不免让人唏嘘比如:既然两个框架都用过,那能说一下你觉得这俩有什么区别吗毕竟是两个框架,不......
  • 在react里面刷新浏览器,不会触发componentWillUnmount事件
    今天遇见个小bug发现刷新浏览器,componentWillUnmount竟然不会触发。搜了一下,可能原因是浏览器刷新的时候,componentQillUnmout来不及触发,就被刷掉了。使用onbeforeunload事件可以完美解决这个问题。 ......
  • 【React工作记录八十三】React+Hook+ts+antDesign实现table行编辑功能
    前言大家好我是歌谣今天要说的是antdesign实现表格行编辑的功能考虑问题的时候我们需要多看官方的api开发开始紧接着我们对照着api进行开发首先加一个table<TableonChange={onTableChange}rowKey="id"......
  • react配置API请求代理
    需求当请求http://10.1.1.1:3131/v1/*接口时,需要代理到8181端口。如果只需要代理匹配到/v1路径的请求,可以在package.json中使用http-proxy-middleware进行自定义代理配置。以下是一个示例:首先,确保已经安装了http-proxy-middleware包。如果没有安装,可以使用以下命令进行安......
  • react初始化项目
    初始化基础的项目npminstall-gcreate-react-appcreate-react-appmy-appcdmy-appnpmstart箴言:因为这些东西是非常简单的。不要抱怨自己学不会,那是因为你没有足够用心。......