# MImageCropper 头像照片裁切

- 来源：[E10 UI 组件库](https://www.e-cology.com.cn/ui/mobile/image-cropper/freepass)
- 平台：移动端
- 维护者：屈正巍
- 设计稿：http://pd.e-cology.com.cn/pd/E10/interface/e10xiaoguotu/PC/sjl/zpsz/
- Issue：http://gitlab.ecology10.cn/FRONTEND/weapp-ui/-/issues?state=all&search=[ImageCropper]

## 代码演示

### 基础用法

基础用法

```jsx
import React from 'react'
import { MImageCropper } from '@weapp/ui'
import imgSrc from "../../../../assets/images/dKbkpPXKfvZzWCM.png"

interface StateType {
  src: string
}

export default class ImageCropperDemo extends React.Component<any, StateType> {
  cropper: any = null
  constructor(props: any) {
    super(props)
    this.state = {
      src: imgSrc,
    }
  }

  onChange = (src: string) => this.setState({ src })
  onConfirm = (img: string, cropData?: any, photo?: string, photoData?: any) => {
    console.info('img', img)
    console.info('cropData', cropData)
    console.info('photo', photo)
    console.info('photoData', photoData)
  }

  render() {
    const { src } = this.state
    return (
      <MImageCropper
        type={'avatarAndPhoto'}
        src={src}
        onChange={this.onChange}
        onConfirm={this.onConfirm}
        uploadParams={{
          uploadUrl: '/api/file/module/upload',//'/api/hrm/profile/upload',
          uploadParams: { id: `${+new Date()}`, size: 10, module: 'hrm' },
          limitType: 'jpg,png,jpeg,gif,bmp',
          maxSize: 10,
        }}
      />
    )
  }
}
```

### 设置头像与照片初始化src

设置头像与照片初始化src

```jsx
import React from 'react'
import { MImageCropper } from '@weapp/ui'
import imgSrc from "../../../../assets/images/dKbkpPXKfvZzWCM.png"
interface StateType {
  src: string
}

export default class ImageCropperDemo extends React.Component<any, StateType> {
  cropper: any = null
  constructor(props: any) {
    super(props)
    this.state = {
      src: imgSrc,
    }
  }

  onChange = (src: string) => this.setState({ src })
  onSuccess = (uploadData: any, response?: any) => {
    console.info('uploadData', uploadData)
    console.info('response', response)
  }
  onError = (uploadData: any, response?: any) => {
    console.info('uploadData', uploadData)
    console.info('response', response)
  }
  onConfirm = (img: string, cropData?: any, photo?: string, photoData?: any) => {
    console.info('img', img)
    console.info('cropData', cropData)
    console.info('photo', photo)
    console.info('photoData', photoData)
  }

  render() {
    const { src } = this.state
    return (
      <MImageCropper
        type={'avatarAndPhoto'}
        src={src}
        onChange={this.onChange}
        onConfirm={this.onConfirm}
        defaultShowCrop={false}
        defaultPreviewSrc={imgSrc}
        defaultPhotoPreviewSrc={'/build/ui/demo/assets/photo-view/1.jpg'}
        uploadParams={{
          uploadUrl: '/api/file/module/upload',//'/api/hrm/profile/upload',
          uploadParams: { id: `${+new Date()}`, size: 10, module: 'hrm' },
          limitType: 'jpg,png,jpeg,gif,bmp',
          maxSize: 10,
          onSuccess: this.onSuccess,
          onerror: this.onError
        }}
      />
    )
  }
}
```

### 路由模式

路由模式

```jsx
import React from 'react'
import { withRouter } from 'react-router-dom';
import { MImageCropper, MDialog, utils } from '@weapp/ui'
import imgSrc from "../../../../assets/images/dKbkpPXKfvZzWCM.png"
const { formatParentPath } = utils;

const photographSrc = imgSrc;

interface Props {
  history?: any
}

interface StateType {
  src: string
  defaultPreviewSrc?: string
  defaultPhotoPreviewSrc?: string
}

// @ts-ignore
@withRouter
export default class ImageCropperDemo extends React.Component<Props, StateType> {
  cropper: any = null
  constructor(props: any) {
    super(props)
    this.state = {
      src: photographSrc,
      defaultPreviewSrc: '',
      defaultPhotoPreviewSrc: ''
    }
  }
  onChange = (src: string) => this.setState({ src })
  onSuccess = (uploadData: any, response?: any) => {
    console.info('uploadData', uploadData)
    console.info('response', response)
  }
  onConfirm = (img: string, cropData?: any, photo?: string, photoData?: any) => {
    console.info('img', img)
    console.info('cropData', cropData)
    console.info('photo', photo)
    console.info('photoData', photoData)
  }

  linkToAvatarSetting = () => {
    const parentPath = formatParentPath(this.props as any);
    this.props.history.push(`${parentPath}/avatarSetting`);
    this.setState({ src: "/papi/file/preview?type=imgs&fileId=829649134716354560" })
    setTimeout(() => {
      this.setState({
        defaultPreviewSrc: imgSrc,
        defaultPhotoPreviewSrc: '/build/ui/demo/assets/photo-view/1.jpg'
      })
    }, 1000);
  }

  render() {
    const parentPath: string = formatParentPath(this.props as any);
    const { src, defaultPreviewSrc, defaultPhotoPreviewSrc } = this.state
    return (
      <div>
        <h2>路由模式</h2>
        <div onClick={this.linkToAvatarSetting}>
          <img src={photographSrc} />
        </div>
        <MDialog
          path={`${parentPath}/avatarSetting`}
          destroyOnClose
          isRouteLayout={true}
        // onAfterClose={this.props.onClose}
        >
          <MImageCropper
            type={'avatarAndPhoto'}
            src={src}
            onChange={this.onChange}
            onConfirm={this.onConfirm}
            uploadParams={{
              uploadUrl: '/api/file/module/upload',//'/api/hrm/profile/upload',
              uploadParams: { id: `${+new Date()}`, size: 10, module: 'hrm' },
              limitType: 'jpg,png,jpeg,gif,bmp',
              maxSize: 10,
            }}
            defaultShowCrop={false}
            // defaultPhotoPreviewSrc={'/build/ui/demo/assets/photo-view/1.jpg'}
            defaultPreviewSrc={defaultPreviewSrc}
            defaultPhotoPreviewSrc={defaultPhotoPreviewSrc}
          />
        </MDialog>
      </div>
    )
  }
}
```

### 直接进入预览页面

直接进入预览页面

```jsx
import React from 'react'
import { withRouter } from 'react-router-dom';
import { MImageCropper, MDialog, utils } from '@weapp/ui'
import imgSrc from "../../../../assets/images/dKbkpPXKfvZzWCM.png"
const { formatParentPath } = utils;

const photographSrc = imgSrc;

interface Props {
  history?: any
}

interface StateType {
  src: string
  defaultPreviewSrc?: string
  defaultPhotoPreviewSrc?: string
}

// @ts-ignore
@withRouter
export default class ImageCropperDemo extends React.Component<Props, StateType> {
  cropper: any = null
  constructor(props: any) {
    super(props)
    this.state = {
      src: imgSrc,
      defaultPreviewSrc: '',
      defaultPhotoPreviewSrc: ''
    }
  }

  onChange = (src: string) => this.setState({ src })
  onConfirm = (img: string, cropData?: any, photo?: string, photoData?: any) => {
    console.info('img', img)
    console.info('cropData', cropData)
    console.info('photo', photo)
    console.info('photoData', photoData)
  }
  linkToAvatarSetting = () => {
    const parentPath = formatParentPath(this.props as any);
    this.props.history.push(`${parentPath}/avatarSetting`);
    setTimeout(() => {
      this.setState({
        defaultPreviewSrc: imgSrc,
        defaultPhotoPreviewSrc: '/build/ui/demo/assets/photo-view/1.jpg'
      })
    }, 1000);
  }

  render() {
    const parentPath: string = formatParentPath(this.props as any);
    const { src, defaultPreviewSrc, defaultPhotoPreviewSrc } = this.state
    return (
      <div>
        <h2>路由模式</h2>
        <div onClick={this.linkToAvatarSetting}>
          <img src={photographSrc} />
        </div>
        <MDialog
          path={`${parentPath}/avatarSetting`}
          destroyOnClose
          isRouteLayout={true}
          forceRender
        // onAfterClose={this.props.onClose}
        >
          <MImageCropper
            type={'avatarAndPhoto'}
            src={src}
            onChange={this.onChange}
            onConfirm={this.onConfirm}
            firstToPreview
            uploadParams={{
              uploadUrl: '/api/file/module/upload',//'/api/hrm/profile/upload',
              uploadParams: { id: `${+new Date()}`, size: 10, module: 'hrm' },
              limitType: 'jpg,png,jpeg,gif,bmp',
              maxSize: 10,
            }}
            defaultShowCrop={false}
            // defaultPhotoPreviewSrc={'/build/ui/demo/assets/photo-view/1.jpg'}
            defaultPreviewSrc={defaultPreviewSrc}
            defaultPhotoPreviewSrc={defaultPhotoPreviewSrc}
          />
        </MDialog>
        <br />
      </div>
    )
  }
}
```

### src失效渲染空白情况

src失效

```jsx
import React from 'react'
import { withRouter } from 'react-router-dom';
import { MImageCropper, MDialog, utils } from '@weapp/ui'
import imgSrc from "../../../../assets/images/dKbkpPXKfvZzWCM.png"
const { formatParentPath } = utils;

const photographSrc = imgSrc;

interface Props {
  history?: any
}

interface StateType {
  src: string
}

// @ts-ignore
@withRouter
export default class ImageCropperDemo extends React.Component<Props, StateType> {
  cropper: any = null
  constructor(props: any) {
    super(props)
    this.state = {
      src: '',
    }
  }

  onChange = (src: string) => this.setState({ src })
  onConfirm = (img: string, cropData?: any, photo?: string, photoData?: any) => {
    console.info('img', img)
    console.info('cropData', cropData)
    console.info('photo', photo)
    console.info('photoData', photoData)
  }
  linkToAvatarSetting = () => {
    const parentPath = formatParentPath(this.props as any);
    this.props.history.push(`${parentPath}/avatarSetting`);
    // this.setState({ src: "/papi/file/preview?type=imgs&fileId=829649134716354560" })
  }

  render() {
    const parentPath: string = formatParentPath(this.props as any);
    const { src } = this.state
    return (
      <div>
        <h2>路由模式</h2>
        <div onClick={this.linkToAvatarSetting}>
          <img src={photographSrc} />
        </div>
        <MDialog
          path={`${parentPath}/avatarSetting`}
          destroyOnClose
          isRouteLayout={true}
        // onAfterClose={this.props.onClose}
        >
          <MImageCropper
            type={'avatarAndPhoto'}
            src={src}
            onChange={this.onChange}
            onConfirm={this.onConfirm}
            firstToPreview
            hasLoading={false}
            uploadParams={{
              uploadUrl: '/api/file/module/upload',//'/api/hrm/profile/upload',
              uploadParams: { size: 10, module: 'hrm' },
              limitType: 'jpg,png,jpeg,gif,bmp',
              maxSize: 10,
            }}
          />
        </MDialog>
        <br />
      </div>
    )
  }
}
```

## API

### 注意：

```
1.当图片加载失败时，进入裁切组件会直接跳转至预览页面，且必须要上传图片后才可以进行裁切（进入到裁切页面）
2.如果通过firstToPreview参数设置直接进入到预览界面，请结合 【设置头像与照片初始化src】案例，设置预览的初始化src，否则会因为裁切框没有生成导致裁切出现意外情况
```

### 参数说明如下：

| 参数 | 说明 | 类型 | 可选 | 默认 |
| --- | --- | --- | --- | --- |
| src | 图片链接地址 | string |  |  |
| defaultSrc | 默认展示图片地址 | string |  |  |
| uploadParams | 上传按钮相关参数 | UploadProps | - |  |
| accept | 文件上传类型 | string |  |  |
| onDelete | 【删除照片】按钮的回调 | (success: Function) => void | - |  |
| onScaleLarge | 【放大照片】按钮的回调 | () => void | - |  |
| onScaleSmall | 【缩小照片】按钮的回调 | () => void | - |  |
| onRotate | 【旋转照片】按钮的回调 | () => void | - |  |
| preview | 用于展示的 dom 类名 | string |  |  |
| previewStyle | 用于展示的 dom 样式 | CSSProperties |  |  |
| photoPreviewStyle | 用于展示照片的 dom 样式 | CSSProperties |  |  |
| previewText | 预览区顶部自定义文案 | ReactNode |  |  |
| cropperText | 裁剪区顶部自定义文案 | ReactNode |  |  |
| cropperStyle | 用于展示的 dom 样式 | CSSProperties(最小宽度为 200px) |  |  |
| onConfirm | Dialog【确定】按钮的回调 | (img: string, cropData?: Cropper.Data, photo?: string, photoData?: Cropper.Data, allData?: any) => void |  |  |
| getCropper | 获取组件内置的对象 | (type: `cropper`\|`file`, value: any) => void; |  |  |
| emptyShow | 无图的默认展示 | ReactNode |  |  |
| aspectRatio | 裁剪框宽高比例 | number |  | 1 / 1 |
| dragMode | 拖拽方式 | `crop` \| `move` \| `none` |  | `crop` |
| onChange | 选择文件后 src 改变回调 | (src: string) => void |  |  |
| showDelete | 是否展示"删除照片"按钮 | boolean |  | true |
| onBeforeDelete | 删除按钮点击前拦截 | () => Promise |  |  |
| onBeforeSelect | 选择文件按钮点击前拦截 | () => Promise |  |  |
| onBeforeUpload | 上传事件拦截 | (uploadParams: any) => Promise |  |  |
| cropperOption | Cropper 配置项,参考文档 | Cropper.Options |  |  |
| errorDisplayImg | 接口上传失败是否回显图片 | boolean |  | true(回显) |
| hasLoading | errorDisplayImg=true 是否添加 loading 状态 | string |  | true |
| defaultShowCrop | 是否默认显示裁剪框 | boolean |  | true |
| ref | 获取 dom 元素 | any |  |  |
| rotateTo | 图片旋转角度 | number |  |  |
| defaultPreviewSrc | 显示默认预览框框内容（defaultShowCrop 为 false 时生效） | string |  |  |
| defaultPhotoPreviewSrc | 显示照片默认预览框框内容（defaultShowCrop 为 false 时生效） | string |  |  |
| onCancel | 取消事件回调 | () => void |  |  |
| onClose | 关闭事件回调 | () => void |  |  |
| dialogProps | 对话框相关属性 | DialogProps |  |  |
| uploadFileMaxSize | 上传文件大小限制（以MB为单位） | number |  | 2 |
| firstToPreview | 首次进入是否直接进到预览页面 | boolean |  |  |
| onSuccess | 上传成功回调 | (uploadData: any, response?: any) => void |  |  |
| onError | 上传失败回调 | (uploadData: any, response?: any) => void |  |  |
| base64Type | 默认裁切出来的base64图片文件类型（png的体积较大，请慎重选择） | `png` \| `jpeg` \| `webp` |  | `webp` |
> 所有拦截事件 resolve 传入 false 时将不会执行原事件
> 
> 手动上传裁剪后的图片： ref.current.handleUpload()
