React 中使用富文本编辑器 Braft Editor ,并集成上传图片功能

Braft Editor 是基于draft-js开发的富文本编辑器,适用于 React 框架。

1. 安装

使用npm

npm install braft-editor --save

使用yarn

yarn add braft-editor

2. 基本使用

import React, { Component } from 'react'
import BraftEditor from 'braft-editor'
import 'braft-editor/dist/index.css'

export default class Main extends Component {

  state = {
    editorState: BraftEditor.createEditorState('<p>初始值</p>'), // 设置编辑器初始内容
    outputHTML: '<p></p>' // 编辑器输出内容
  }

  componentDidMount () {
    this.setState({
      editorState: BraftEditor.createEditorState('<p>hello,<b>world!</b><p>')
    })
  }

  handleChange = (editorState) => {
    this.setState({
      editorState: editorState,
      outputHTML: editorState.toHTML()
    }, () => {
      console.log(editorState)
      console.log(this.state.outputHTML)
    })
  }

  render () {
    const { editorState, outputHTML } = this.state

    return (
      <div>
        <div className="editor-wrapper">
          <BraftEditor
            value={editorState}
            onChange={this.handleChange}
          />
        </div>
      </div>
    )
  }
}

2. 自定义内置控件

Braft Editor使用 controls 属性指定需要展示的控件;使用contentStyle调整编辑区域的高度。

  render () {
      const controls = [
      'undo', 'redo', 'separator',
      'font-size', 'line-height', 'letter-spacing', 'separator',
      'text-color', 'bold', 'italic', 'underline', 'strike-through', 'separator',
      'superscript', 'subscript', 'remove-styles', 'emoji', 'separator', 'text-indent', 'text-align', 'separator',
      'headings', 'list-ul', 'list-ol', 'blockquote', 'code', 'separator',
      'link', 'separator', 'hr', 'separator',
      'media',
      'separator',
      'clear'
    ]

    return (
      <div className="editor-wrapper">
        <BraftEditor
          controls={controls}
          contentinset 0 1px 3px rgba(0,0,0,.1)'}}
        />
      </div>
    )

  }

3. 集成 Ant Design 上传组件

Braft Editor上传图片会默认转成base64, 下面将使用Ant Design 的上传组件改成上传到服务器的方法:

  • 使用ContentUtils来将上传后的图片插入到编辑器,安装:
yarn add braft-utils
  • 使用component类型的extendControls将Upload组件集成到编辑器工具栏
import React, { Component } from 'react'
import BraftEditor from 'braft-editor'
import 'braft-editor/dist/index.css'

export default class Main extends Component {

  state = {
    editorState: BraftEditor.createEditorState(null)
  }

  componentDidMount () {
    this.setState({
      editorState: BraftEditor.createEditorState('<p>hello,<b>world!</b><p>')
    })
  }

  onOk = () => {
    this.props.form.validateFields((err, fieldsValue) => {
      if (!err) {
        console.log(fieldsValue)
      }
    })
  }

  beforeUpload = file => {
    this.props.form.setFieldsValue({
      content: ContentUtils.insertMedias(this.props.form.getFieldValue('content'), [{
        type: 'IMAGE',
        url: imgUrl, // imgUrl 为上传成功后 后台返回的url地址
      }])
    return false
  }


  render () {
     const {
      form: { getFieldDecorator },
    } = this.props

    const { editorState } = this.state

    const formItemLayout = {
      labelCol: {
        sm: { span: 4 },
      },
      wrapperCol: {
        sm: { span: 20 },
      },
    }


    const controls = [
      'undo', 'redo', 'separator',
      'font-size', 'line-height', 'letter-spacing', 'separator',
      'text-color', 'bold', 'italic', 'underline', 'strike-through', 'separator',
      'superscript', 'subscript', 'remove-styles', 'emoji', 'separator', 'text-indent', 'text-align', 'separator',
      'headings', 'list-ul', 'list-ol', 'blockquote', 'code', 'separator',
      'link', 'separator', 'hr', 'separator',
      // 'media',
      'separator',
      'clear'
    ]

    const extendControls = [
      {
        key: 'antd-uploader',
        type: 'component',
        component: (
          <Upload
            accept="image/*"
            showUploadList={false}
            beforeUpload={this.beforeUpload}
          >
            {/* 这里的按钮最好加上type="button",以避免在表单容器中触发表单提交,用Antd的Button组件则无需如此 */}
            <button type="button" className="control-item button upload-button" data-title="插入图片">
              <Icon type="picture" theme="filled" />
            </button>
          </Upload>
        )
      }
    ]

    return (
      <div>
        <div className="editor-wrapper">
          <Form {...formItemLayout}>
            <Form.Item label="标题">
              {getFieldDecorator(`title`, {
                initialValue: '',
                rules: [{ required: true, message: '请输入标题' }],
              })(
                <Input placeholder="请输入" allowClear style={{ width: 300 }} />
              )}
            </Form.Item>
            <Form.Item label="内容">
              {getFieldDecorator(`content`, {
                validateTrigger: 'onBlur',
                initialValue: editorState,
                rules: [{ required: true, message: '请输入内容' }],
              })(
                <BraftEditor
                  className={styles.editorStyle}
                  contentinset 0 1px 3px rgba(0,0,0,.1)' }}
                  controls={controls}
                  extendControls={extendControls}
                  placeholder="请输入正文内容"
                />
              )}
            </Form.Item>
            <Form.Item>
              <Button type="primary" onClick={this.onOk}>
                确定
              </Button>
            </Form.Item>
          </Form>

        </div>
      </div>
    )
  }
}