1.安装模块
yarn add react-cropper lrz
2.分装组件
src/components/ImgCropper/index.tsx
/** * 上传logo组件 */ import * as React from 'react'; import { Upload, Icon, message, Modal, Button } from 'antd'; // 图片剪裁 import Cropper from 'react-cropper'; // 图片压缩 // import lrz from 'lrz'; // base64 转 Blob import { b64toBlob } from '@utils/b64toBlob'; import { BaseUrl } from '@utils/constants'; import 'cropperjs/dist/cropper.css'; import axios from 'axios'; import './index.less'; const lrz = require('lrz'); interface IProps { onSuccess?: Function | any; onChange?: Function | any; title: string; aspectRatio?: number; } interface IState { srcCropper: any; visible: boolean; confirmLoading: boolean; } class ImgCropper extends React.Component<IProps, IState>{ constructor(props: IProps){ super(props); this.state = { srcCropper: '', visible: false, confirmLoading: false } } beforeUpload = (file: any) => { const isLt10M = file.size / 1024 / 1024 < 10; if (!isLt10M) { // 添加文件限制 message.error({ content: '文件大小不能超过10M' }); return false; } const reader = new FileReader(); reader.readAsDataURL(file); // 开始读取文件 // 因为读取文件需要时间,所以要在回调函数中使用读取的结果 reader.onload = (e: any) => { this.setState({ visible: true, srcCropper: e.target.result, // cropper的图片路径 }); }; return false; }; saveImg = () => { this.setState({ confirmLoading: true, }); // 通过refs读取到Cropper实例,并读取到裁剪之后的图片(base64) const cropper: any = this.refs.cropper; const url = cropper.getCroppedCanvas().toDataURL(); // 此处使用了lrz组件对裁剪之后的图片进行压缩,lrz的API十分简单,quality是指对压缩图片的品质,一般0.6或者0.7即可 lrz(url, { quality: 0.6 }).then((results: any) => { const { onSuccess, onChange } = this.props; const fd = new FormData(); // 由于后台接口参数需要一个文件名,所有根据当前时间生成文件名 const imgName = `${new Date().getTime()}.png`; // 将base64转化成二进制流 fd.append('file', b64toBlob(results.base64), imgName); // 发送请求 axios.post(`${BaseUrl}/tools/saveAvatar`, fd).then((res) => { const { data={} } = res; if(data.code === 200){ onSuccess(data.data.file); onChange && onChange(data.data.file); message.success(data.message || '上传成功'); } }).catch((err) => { message.error('上传失败'); }).finally(() => this.onCloseModal()) }); }; // 取消 onCloseModal = () => { this.setState({ visible: false, confirmLoading: false }) } render() { // 考虑靠组件复用,裁剪Modal的标题作为属性从组件外部传递进来 const { title, aspectRatio=1 } = this.props; /** * srcCropper:cropper组件内部图片的url * visible:裁剪Modal的显示属性 * confirmLoading:图片上传过程中Modal的Loading效果 * */ const { srcCropper, visible, confirmLoading } = this.state; return ( <div> <Upload beforeUpload={this.beforeUpload} showUploadList={false}> <Button> <Icon type="upload" /> 选择图片 </Button> </Upload> <Modal title={title} visible={visible} onOk={this.saveImg} onCancel={this.onCloseModal} okText="确认上传" cancelText="取消" confirmLoading={confirmLoading} > {/* <div className="previewHeader"> {srcCropper ? ( <div className="previewOutter"> <div className="uploadCrop previewContainer" /> <div className="uploadCropcir previewContainer" /> </div> ) : ( '' )} </div> */} {srcCropper ? ( <Cropper ref="cropper" style={{ height: 400, '100%' }} // 预览图的容器 preview=".previewContainer" guides // 固定图片裁剪比例(正方形) aspectRatio={aspectRatio} // 要裁剪的图片的路径 src={srcCropper} /> ) : ( '' )} </Modal> </div> ); } } export default ImgCropper;
3.页面调用
<Form.Item label="头像"> {getFieldDecorator('avatar', { rules: [{ required: true, message: '请上传头像' }], initialValue: detail['avatar'] || undefined })(<ImgCropper title="上传头像" onSuccess={this.handleSuccess} />)} {(avatarImg || detail['avatar']) && <img className="avatar-img" src={avatarImg || detail['avatar']} alt="头像" /> || null} </Form.Item>
.