zoukankan      html  css  js  c++  java
  • 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,
                 '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,大小非常小,非常实用

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

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

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

     

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

  • 相关阅读:
    POJ 1703 Find them, Catch them
    POJ 2236 Wireless Network
    POJ 2010 Moo University
    POJ 2184 Cow Exhibition
    POJ 3280 Cheapest Palindrome
    POJ 3009 Curling 2.0
    POJ 3669 Meteor Shower
    POJ 2718 Smallest Difference
    POJ 3187 Backward Digit Sums
    POJ 3050 Hopscotch
  • 原文地址:https://www.cnblogs.com/katydids/p/12676111.html
Copyright © 2011-2022 走看看