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,大小非常小,非常实用

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

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

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

     

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

  • 相关阅读:
    .NET 回归
    Smart ORM v0.4.1开发计划
    计算4的1万次方的结果
    GMS Modem 短信收发组件
    计划任务的编成实现
    告别2007,展望2008
    Smart ORM v0.3发布(完全面向对象的轻量级ORM工具)
    Java中委托事件模型 (转)
    刚刚开通了博客,写点东西
    ASP.NET弹出对话框(转)
  • 原文地址:https://www.cnblogs.com/katydids/p/12676111.html
Copyright © 2011-2022 走看看