react引入富文本编辑器TinyMCE

这周做了一个PC端的service后台需求,要求有富文本编辑器,插入图片、表格,字体字号背景色等等,

最后引入了富文本编辑器TinyMCE

对于TinyMCE的简介:

TinyMCE是一款易用、且功能强大的所见即所得的富文本编辑器。同类程序有:UEditor、Kindeditor、Simditor、CKEditor、wangEditor、Suneditor、froala等等。

TinyMCE的优势:

  • 开源可商用,基于LGPL2.1
  • 插件丰富,自带插件基本涵盖日常所需功能
  • 接口丰富,可扩展性强,有能力可以无限拓展功能
  • 界面好看,符合现代审美
  • 提供经典、内联、沉浸无干扰三种模式
  • 对标准支持优秀(自v5开始)
  • 多语言支持,官网可下载几十种语言。

 上图为需求中配置的富文本编辑器的内容,我的需求TinyMCE完全可以满足,

  TinyMCE 官网:www.tiny.cloud

   TinyMCE支持vue、react、angular 

 这次改动的工程使用的是react,

在需求中封装了一个适合我们的富文本编辑组件:

editor组件中的内容:

import React from 'react';
import './Editor.scss';

import {isDev, nginxPrefix} from '@/config';

import {Upload, Button, Icon, Popconfirm, Spin, message} from 'antd';

import _get from 'lodash/get';
import _uniqueId from 'lodash/uniqueId';

import PropTypes from 'prop-types';

/*
* @props string id? 标识符
* @props number height? 高度
* @props string defaultContent? 初始内容
* @props boolen disabled? 禁用
* @props function onDelete? 删除事件
* @props function onAdd? 添加事件
* @props object uploadConfig? 自定义上传配置
* @event function getEditorContent 获取编辑内容
* @event function setEditorContent 设置编辑内容
* @event function insertContent 插入编辑内容
*/
class Editor extends React.Component {
    constructor(props) {
        super(props);

        const tinymceId = `editor-tinymce-${this.props.id}-${_uniqueId()}-${new Date().getTime()}`;

        this.state = {
            // 编辑器ID
            tinymceId,
            // 编辑器实例
            editor: null
        };
    }

    componentDidMount() {
        const {height = 300, defaultContent = ''} = this.props;
        window.tinymce.init({
            selector: `#${this.state.tinymceId}`,
            language: 'zh_CN',
            height: height,
            min_height: 200,
            width: '100%',
            resize: true,
            default_link_target: '_blank',
            init_instance_callback: editor => {
                if (defaultContent) {
                    editor.setContent(defaultContent);
                }
            },
            paste_enable_default_filters: true,
            // paste_word_valid_elements: () => {
            //
            // },
            // 插件配置
            plugins: 'table image lists link paste',
            // 菜单配置
            menubar: 'file edit view insert format',
            // 工具栏配置
            /* eslint-disable */
            toolbar: 'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | outdent indent | blockquote | table | image link | forecolor backcolor | bullist numlist | removeformat'
            /* eslint-enable */
        }).then(([editor]) => this.setState({editor}));
    }

    componentWillUnmount() {
        // 在某些生命周期中,实例并未生成,没有卸载方法。(组件挂载阶段)
        // if (this.state.editor !== null) {
        //     this.state.editor.destroy();
        // }
        window.tinymce.get(this.state.tinymceId).destroy();
    }

    getEditorContent = () => {
        return this.state.editor.getContent();
    }

    setEditorContent = (content) => {
        window.tinymce.get(this.state.tinymceId).setContent(content);
    }

    insertContent = (content) => {
        try {
            this.state.editor.insertContent(content);
        } catch(e) {
            window.tinymce.get(this.state.tinymceId).insertContent(content);
        }
    }

    // 默认上传配置
    uploadConfig = {
        name: 'file',
        action: (isDev ? '' : nginxPrefix) + '/admin/common/uploadFile',
        headers: {
            authorization: 'authorization-text',
        },
        onChange: (info) => {
            if (info.file.status === 'done') {
                message.success('图片上传成功');
                this.state.editor.insertContent(
                    `<img src="${_get(info, 'file.response.data.result')}" >`
                );
            } else if (info.file.status === 'error') {
                message.error('图片上传失败');
            }
        },
        accept: '.jpg,.jpeg,.jpe,.png,.bmp'
    }

    render() {
        const {uploadConfig} = this.props;
        return (
            <Spin spinning={this.props.disabled} indicator={<Icon type="stop" style={{color: '#555'}} />}>
                <div className="editor-container">
                    <textarea id={this.state.tinymceId} />
                    <div className="btn-bar">
                        <Upload {...(uploadConfig ? uploadConfig : this.uploadConfig)}>
                            <Button><Icon type="upload" />添加本地图片</Button>
                        </Upload>
                        <span>
                            {
                                this.props.onAdd
                                &&
                                <Button icon="plus" shape="circle" onClick={this.props.onAdd} />
                            }
                            {
                                this.props.onDelete
                                &&
                                <Popconfirm
                                    title="无法撤回,确认删除?"
                                    onConfirm={this.props.onDelete}
                                    okText="确认"
                                    cancelText="取消"
                                    placement="leftBottom"
                                >
                                    <Button
                                        type="danger"
                                        icon="delete"
                                        shape="circle"
                                        style={{marginLeft: '4px'}}
                                        onClick={() => {
                                            // 当富文本编辑器中没有内容时,删除按钮不弹窗,直接调用删除方法
                                            const content = this.getEditorContent();

                                            if (!content) {
                                                this.props.onDelete();
                                            }
                                        }}
                                    />
                                </Popconfirm>
                            }
                        </span>
                    </div>
                </div>
            </Spin>
        );
    }
}

Editor.defaultProps = {
    id: 'no-props-id',
    height: 300,
    defaultContent: '',
    onDelete: null,
    onAdd: null,
    disabled: false,
    uploadConfig: null
};

Editor.propTypes = {
    id: PropTypes.string,
    height: PropTypes.number,
    defaultContent: PropTypes.string,
    onDelete: PropTypes.func,
    onAdd: PropTypes.func,
    disabled: PropTypes.bool
};

export default Editor;

组件最终提交的是一段HTML,图片只是一个URL,大小非常小,非常实用

这次的需求中富文本编辑框是录入案例,

当然有录入就有展示,对于展示也很简单,在展示的地方加了个图片放大的功能

展示的时候有个问题,就是图片的大小我们想控制的很小,这样整体的内容都可以看到,图片具体内容可以点击图片放大,

 

来来来,展示一下我男神图片点击放大之后的帅图:

猜你喜欢

转载自www.cnblogs.com/katydids/p/12676111.html