基于React的富文本编辑器-Braft Editor使用

antd 是基于 Ant Design 设计规范实现的 高质量 React 组件库,我们倾向于只提供符合该规范且带有视觉展现的 UI 组件,也尽量不重复造轮子。

如果要在React项目中使用富文本编辑器,官方推荐使用react-quill 与 braft-editor。
详细点击这里

这篇文章主要介绍Braft Editor与Antd的结合使用。

在Ant Design表单中使用

功能要点

  • 使用BraftEditor.createEditorState创建editorState
  • 使用editorState.toHTML()实时获取html
  • 使用editorState.isEmpty()进行空值校验

注意事项

  • 编辑器组件的数据格式为ediorState,为此在调用setFieldsValue时和在提交之前,需要进行相应的转换(toHTML())
  • 进行空值校验的话,需要自定义validator,并通过value.isEmpty()来校验,value就是一个editorState
  • 编辑器组件的验证时机需要改成onBlur,以避免不期望的验证提示和不必要的性能开销
  • 编辑器的value属性必须是一个editorState对象
  • 实际使用时请避免在onChange中直接toHTML,配合节流函数或者在合适的时机使用更恰当

编辑器演示

WX20210517-111254@2x.png

示例源码

import BaseCmp from '@components/BaseCmp.js'
import { connect } from 'react-redux'
import {
    RLInput, RLButton, RLForm, RLFormItem
} from '@components/index.js'
import { DatePicker } from 'antd'
import { createRef } from 'react'
import BraftEditor from 'braft-editor'
import actionInfoManage from '@actions/infoManage/actionInfoManage.js'
import { dealTime, dealDateTime } from '@/libs/utils.js'
import moment from 'moment'
import locale from 'antd/es/date-picker/locale/zh_CN'


class CmpInfoEdit extends BaseCmp {
    constructor(props) {
        super(props)
        if (props.infoId) {
            this.infoId = props.infoId
        }
        this.state = {
            infoListInfo: {
                title: '',
                start_time: '',
                content: BraftEditor.createEditorState(null)
            }
        }
        this.form = createRef()
    }
    componentDidMount() {
        if (this.infoId) {   // 编辑
            this.getInfoDetail(this.infoId)
        } else {
            this.setState({
                infoListInfo: {
                    ...this.state.infoListInfo
                }
            })
        }
    }

    // 资讯详情
    getInfoDetail = (id) => {
        actionInfoManage.getInfoDetail({id}).then(res => {
            if (res.code === 200) {
                const data = res.data
                let userList = data.invite_uids
                this.setState({
                    userList,
                    infoListInfo: {
                        ...data,
                        start_time: moment(dealTime(data.start_time, 'YYYY-MM-DD HH:mm')),
                        content: BraftEditor.createEditorState(data.content),
                    }
                }, () => {
                    // 给表单重置值
                    this.form && this.form.setFieldsValue(this.state.infoListInfo)
                })
            } else {
                this.showToast({ type: 'error', content: res.msg })
            }
        })
    }

    editFailed = (res) => {
        this.showToast({ content: '您有必填项未填写', type: 'success' })
    }

    editConfirm = (values) => {
        console.log('onFinish', values)
        this.setState({
            addLoading: true
        })
        let { start_time } = values
        const { title, content } = this.state.infoListInfo
        const params = {
            title,
            start_time: dealDateTime(start_time.format('YYYY-MM-DD HH:mm')),
            content: content.toHTML()
        }
        if (this.infoId) {   // 编辑
            actionInfoManage.infoEdit({ ...params, id: this.infoId }).then(res => {
                if (res.code === 200) {
                    this.showToast({ content: '编辑成功', type: 'success' })
                    this.props.changePage('list')
                } else {
                    this.showToast({ type: 'error', content: res.msg })
                }
            }).finally(() => {
                this.setState({
                    addLoading: false
                })
            })
        } else {   // 创建
            actionInfoManage.infoAdd(params).then(res => {
                if (res.code === 200) {
                    this.showToast({ type: 'success', content: '创建成功' })
                    this.props.changePage('list')
                } else {
                    this.showToast({ type: 'error', content: res.msg })
                }
            }).finally(() => {
                this.setState({
                    addLoading: false
                })
            })
        }
    }

    disabledDate = (current) => {
        return current && current < moment().startOf('day')
    }

    render() {
        return (
            <div className='page-info-edit'>
                <RLForm
                    ref={form => this.form = form}
                    labelCol={{ style: { width: 150, marginRight: 20, textAlign: 'right' } }}
                    labelAlign='left'
                    wrapperCol={{ style: { span: 24, marginRight: 30 } }}
                    onFinish={this.editConfirm}
                    onFinishFailed={this.editFailed}
                    initialValues={this.state.infoListInfo}
                    validateTrigger='onBlur'
                >
                    <RLFormItem
                        name='title'
                        label='资讯主题'
                        colon={false}
                        rules={[
                            {
                                required: true,
                                message: '请输入资讯主题'
                            }, {
                                max: 50,
                                message: '资讯主题最多50个字符'
                            }
                        ]}
                    >
                        <RLInput
                            placeholder='请输入资讯主题'
                            style={{ width: 360 }}
                            value={this.state.infoListInfo.title}
                            onChange={e => {
                                this.setState({
                                    infoListInfo: {
                                        ...this.state.infoListInfo,
                                        title: e.target.value
                                    }
                                })
                            }}
                        />
                    </RLFormItem>
                    <RLFormItem
                        name='start_time'
                        label='发布时间'
                        colon={false}
                        rules={[
                            {
                                required: true,
                                message: '请选择发布时间'
                            }
                        ]}
                    >
                        <DatePicker
                            allowClear
                            locale={locale}
                            showTime
                            disabledDate={this.disabledDate}
                            format={'YYYY-MM-DD HH:mm'}
                            placeholder='请选择日期时间'
                        />
                    </RLFormItem>
                    <RLFormItem
                        name='content'
                        label='正文内容'
                        colon={false}
                        rules={[
                            {
                                required: true,
                                validator: (rule, value) => {
                                    if (value.isEmpty()) {
                                        return Promise.reject('请输入正文内容')
                                    } else {
                                        return Promise.resolve()
                                    }
                                }
                            }
                        ]}
                    >
                        <BraftEditor
                            value={this.state.infoListInfo.content}
                            onChange={editorState => {
                                this.setState({
                                    infoListInfo: {
                                        ...this.state.infoListInfo,
                                        content: editorState
                                    }
                                })
                            }}
                            media={{
                                accepts: {
                                    image: 'image/jpeg,image/png',
                                    video: 'video/mp4',
                                    audio: 'audio/mpeg,audio/mp3',
                                },
                                uploadFn: (upload) => {},

                                // onChange(...rest) {
                                //     console.log('onChange---rest', rest)
                                // }
                            }}
                            style={{ border: '1px solid #d1d1d1', borderRadius: 3, background: '#fff' }}
                        />
                    </RLFormItem>

                    <RLFormItem>
                        <div style={{ display: 'flex', justifyContent: 'center' }}>
                            <RLButton
                                type="default"
                                label='取消'
                                width={80}
                                style={{ display: 'inline-block' }}
                                onClick={() => {
                                    this.props.changePage('list')
                                }}
                            />
                            <RLButton
                                type="primary"
                                htmlType="submit"
                                label={this.infoId ? '保存' : '创建'}
                                style={{ marginLeft: 40, display: 'inline-block' }}
                                loading={this.state.addLoading}
                                width={80}
                            />

                        </div>

                    </RLFormItem>
                </RLForm>
            </div>
        )
    }

}
export default connect((store, props) => {
    return {
        ...props
    }
})(CmpInfoEdit)
复制代码

空值校验

使用isEmpty()校验,rules中的代码如下:

rules={[
    {
        required: true,
        validator: (rule, value) => {
            if (value.isEmpty()) {
                return Promise.reject('请输入正文内容')
            } else {
                return Promise.resolve()
            }
        }
    }
]}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享