前端学习案例-ant design带你封装一个上传图片组件

简介: 前端学习案例-ant design带你封装一个上传图片组件

需求实现

1可以支持上传最多九张图片

2图片支持预览 替换 删除

3支持自定义上传文件大小 格式 未上传提示

实现效果

image.png

image.png

image.png

 


 


 

代码封装

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,
                }}
                onMouseOver={() => this.setShadow(true)}
                onMouseLeave={() => 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>


相关文章
|
4天前
|
前端开发
从零开始学习前端开发:HTML基础
HTML是前端开发的基础,它是网页内容的结构化描述语言。本文将介绍HTML的基本语法、标签和元素,以及如何使用它们来创建简单的网页。
|
2天前
|
前端开发 JavaScript
从零开始学习前端开发
前端开发是一门非常受欢迎的技术,它可以让我们在网页上展示出美观、交互式的内容。但是对于初学者来说,前端开发可能是一门比较难入手的技术。本文将会从基础概念开始介绍前端开发,并深入了解HTML、CSS和JavaScript的使用及其应用。
|
5天前
|
设计模式 存储 前端开发
JS的几种设计模式,Web前端基础三剑客学习知识分享,前端零基础开发
JS的几种设计模式,Web前端基础三剑客学习知识分享,前端零基础开发
|
5天前
|
前端开发 JavaScript Java
npm与Maven:前端与后端构建工具深度对比学习
npm与Maven:前端与后端构建工具深度对比学习
|
6天前
|
前端开发 JavaScript Java
浅谈企业级前端应用中的组件概念和具体的应用
浅谈企业级前端应用中的组件概念和具体的应用
11 1
|
6天前
|
前端开发 JavaScript 关系型数据库
前端网页手册(1)--基础学习
前端网页手册(1)--基础学习
5 1
|
6天前
|
移动开发 前端开发
ruoyi-nbcio-plus的Vue3前端升级组件后出现的问题(二)
ruoyi-nbcio-plus的Vue3前端升级组件后出现的问题(二)
12 1
|
6天前
|
移动开发 前端开发 JavaScript
ruoyi-nbcio-plus的Vue3前端升级组件后出现的问题(一)
ruoyi-nbcio-plus的Vue3前端升级组件后出现的问题(一)
18 2
|
6天前
|
前端开发
前端进度条组件NProgress
前端进度条组件NProgress
22 1
前端进度条组件NProgress
|
4天前
|
移动开发 前端开发 JavaScript
10款精美的web前端源码的特效,2024年最新面试题+笔记+项目实战
10款精美的web前端源码的特效,2024年最新面试题+笔记+项目实战