react中braft-editor的基本使用方式

braft-editor的基本使用

项目需求

实现照片上传,富文本为空时的提示,官网详见Braft Editor

import React, { PureComponent, Fragment } from 'react';
import { connect } from 'dva';
import BraftEditor from 'braft-editor'
import 'braft-editor/dist/index.css'
import moment from 'moment';
import Link from 'umi/link';
import {
    Row,
    Col,
    Card,
    Button,
    message,
    Divider,
    Table,
    Modal,
    Form,
    Select,
    Input,
    notification
} from 'antd';
import styles from './createNotice.less';
import { router } from 'umi';

const FormItem = Form.Item;
const { Option } = Select;

/* eslint react/no-multi-comp:0 */
@connect(({ notice, loading }) => ({
    notice,
    loading: loading.models.notice,
}))
@Form.create()
class CreateNotice extends PureComponent {
    constructor(props) {
        super(props)
        this.state = {
            modalVisible: false,
            title: '',
            labelId: '',
            labelName: '',
            content: '',
            editorValue: '',
            editorState: BraftEditor.createEditorState(null)
        }
    }
    componentDidMount() {
        this.requLabel()
    }
    requLabel() {
        const { dispatch } = this.props
        dispatch({
            type: 'notice/fetchLabel'
        })
    }
    //消息提醒
    openNotification = (type, msg) => {
        notification[type]({
            message: msg,
        });
    };
    //返回
    goBack = () => {
        Modal.confirm({
            title: '提示',
            content: '返回将不保存已编辑信息,确定离开吗?',
            okText: '确定',
            cancelText: '取消',
            onOk: () => {
                router.go(-1)
            },
            onCancel: () => { }
        })
    }
    //富文本的值改变时触发
    handleChange = (editorState) => {
        const { form } = this.props
        this.setState({ editorState })
        form.setFieldsValue({
            content: editorState
        })

    }
    //label的select切换
    onChange = (values) => {
        this.setState({
            labelId: values.key,
            labelName: values.label
        })
    }
    //预览
    preView = () => {
        this.props.form.validateFields((error, values) => {
            // if (!error) {
            this.setState({
                modalVisible: true,
                title: values.title,
                content: values.content.toHTML()
            })
            // }
        })

    }
    //关闭
    handleOk = () => {
        this.setState({
            modalVisible: false
        })
    }
    //发布
    handleSubmit = (event) => {
        const { dispatch, form } = this.props;
        const { labelId, editorState } = this.state
        event.preventDefault()

        form.validateFields((error, values) => {
            if (error) {
                return
            }
            let edit = this.state.editorState.isEmpty() //用isEmpty判断是否为空
            if (edit) {
                this.openNotification('warn', '请输入内容')
                return
            }
            if (!error) {
                const submitData = {
                    title: values.title,
                    infoLabelId: labelId,
                    content: window.btoa(window.encodeURIComponent(values.content.toHTML())) // or values.content.toRAW()
                }

                Modal.confirm({
                    title: '提示',
                    content: '确认发布吗?',
                    okText: '确定',
                    cancelText: '取消',
                    onOk: () => {
                        dispatch({
                            type: 'notice/publish',
                            payload: submitData,
                            callback: res => {
                                if (res.success) {
                                    this.openNotification('success', '发布成功')
                                    router.go(-1)
                                } else {
                                    this.openNotification('error', '发布失败')

                                }
                            }
                        })
                    },
                    onCancel: () => { }
                })
            }
        })

    }
    //上传媒体
    uploadPic = (param) => {
    //也可以用fetch或者axios,用formData
        const token = localStorage.getItem('meiyun-operation-token')
        const serverURL = '/meiyun-resource/oss/endpoint/put-file'
        const xhr = new XMLHttpRequest
        const fd = new FormData()
        const successFn = (response) => {
            let url = JSON.parse(xhr.responseText).data.link
            // 文件上传到服务端成功后获取地址
            // 上传成功后调用param.success并传入上传后的文件地址
            param.success({
                url,
                meta: {
                    id: 'xxx',
                    title: 'xxx',
                    alt: 'xxx',
                    loop: true, // 指定音视频是否循环播放
                    autoPlay: true, // 指定音视频是否自动播放
                    controls: true, // 指定音视频是否显示控制栏
                    poster: 'http://xxx/xx.png', // 指定视频播放器的封面
                }
            })
        }

        const progressFn = (event) => {
            // 上传进度发生变化时调用param.progress
            param.progress(event.loaded / event.total * 100)
        }

        const errorFn = (response) => {
            // 上传发生错误时调用param.error
            param.error({
                msg: '上传失败'
            })
        }

        xhr.upload.addEventListener("progress", progressFn, false)
        xhr.addEventListener("load", successFn, false)
        xhr.addEventListener("error", errorFn, false)
        xhr.addEventListener("abort", errorFn, false)

        fd.append('file', param.file)
        xhr.open('POST', serverURL, true)
        xhr.setRequestHeader('Blade-Auth', 'Bearer ' + token);
        xhr.send(fd)
    }
    render() {
        const {
            form: { getFieldDecorator },
            notice: { labelData },
            loading,
        } = this.props;
        const { modalVisible, title, labelName, content } = this.state
        const formItemLayout = {
            labelCol: { span: 4 },
            wrapperCol: { span: 18 }
        }
        const controls = [
            'font-size',
            'font-family',
            'list-ol',
            'list-ul',
            'hr',
            'text-align', 'bold', 'italic', 'underline', 'text-color', 'separator', 'superscript',
            'subscript', 'separator', 'media', 'letter-spacing',
            'line-height',
            'clear',]
        return (
            
< 公告管理
{getFieldDecorator('title', { rules: [{ required: true, message: '请输入公告标题' }, { message: '公告标题不能输入<或>', pattern: new RegExp('^[^<\|^>]+$', 'g') }, { message: '公告标题不能超过30个字符', max: 30 }] })()} {getFieldDecorator('labelId')( )} {/* */} {getFieldDecorator('content', { rules: [{ required: true, message: '请输入正文内容' }], })( this.editorInstance = instance} className={styles.myEditor} controls={controls} onChange={this.handleChange} forceNewLine={true} placeholder="请输入正文内容" media={{ uploadFn: this.uploadPic }} />)}
{modalVisible && 关闭 ]} onOk={this.handleOk} onCancel={this.handleOk}>

{title}

{labelName}

}
) } } export default CreateNotice

使用braft-editor踩坑记,引用 braft-utils有错误

最近接到一个需求,需要支持在文本输入框支持图片粘贴上传,但是在我们这边管理页面,对于用户提的一些问题显示又不支持 Matkdown。

所以选择 braft-editor 来实现,发现提供一些配置项,因为我这边不需要那些加粗,下划线等等按钮,只需要上传图片,粘贴然后配合 COS 存链接就好了。

遇到的问题

首先我这个是 React 项目,其它项目不太清楚,然后使用 yarn。

在 utils 官方仓库中,有相关 issues,链接在下方:

引用 braft-utils 有错误 #500

其中也有人提及了一些解决方案,但是并没有解决问题,一直报错:

TS7016: Could not find a declaration file for module ‘braft-utils’. ‘xxx/node_modules/braft-utils/dist/index.js’ implicitly has an ‘any’ type.

Try npm i --save-dev @types/braft-utils if it exists or add a new declaration (.d.ts) file containing declare module 'braft-utils';

看这个报错信息,有提示,用 npm 安装那个依赖,我已经试过了,并没有效果,不存在那个依赖包。

解决方式

直接少废话,以下是解决方式:

yarn add braft-finder
yarn add braft-utils
yarn add draft-js-multidecorators
yarn add draftjs-utils

然后在你当前需要引入的文件那,同级目录底下创建一个名为 xxx.d.ts 文件,放入以下定义:

declare module 'braft-utils';
declare module 'braft-finder';

弄完之后记得重新 yarn dev ,之后就会出现如下页面,完美解决。

弄完这个问题,还就那个焦头烂额的,不过总算没有 bug 了,在这里记录一下,以免大家踩坑。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

你可能感兴趣的:(react中braft-editor的基本使用方式)