react+quill封装的富文本编辑器(附带转接oss上传图片)

ps:老早之前写了一个simditor富文本,但是在实际的使用中bug很多,故重新寻觅了新的富文本,使用效果有待考量,但是功能自己感觉比simditor要好很多,可以自定义的内容比较开放。
###效果图
react+quill封装的富文本编辑器(附带转接oss上传图片)
###1.js文件 npm i quill 本文使用的版本是"quill": “^1.3.7”

import React, { Component } from 'react';
import Quill from "quill";
import $ from "jquery";
import config from '../../config.js';
import { Button, Upload } from 'antd';
require("quill/dist/quill.snow.css");
import styles from './QuillRichText.less';
import request from '../../utils/request';
import { getUid } from '../../utils/auth';
import moment from 'moment';
/**
 * 取值 let goods_desc = $(".detailContainer").find(".simditor-body").html();
 */
let ossConfig;
class QuillRichText extends Component {
    state = {
        textContent:''
    }
    componentDidMount = () => {
        this.initEditor();
    };

    //初始化编辑器
    initEditor = () => {
        let that = this;
        // Add fonts to whitelist
        /*----自定义字体 ----*/
        //1.需先引入需要展示的字体样式  然后加入到字体白名单里
        const Font = Quill.import('formats/font');
        // We do not add Aref Ruqaa since it is the default
        let fonts = [
        'SimSun',
        'SimHei',
        'Microsoft-YaHei',
        'KaiTi',
        'FangSong',
        'Arial',
        'Times-New-Roman',
        'monospace',
        'serif',
        'consolas'
        ];
        Font.whitelist = fonts; //将字体加入到白名单
        Quill.register(Font, true);
        let toolbarOptions = [
            ['bold', 'italic', 'underline'],        // toggled buttons
            [{ 'list': 'bullet' }],  
            ["link","image"],              // custom button values
            [{ 'size': ['small', false, 'large', 'huge'] }],  // custom dropdown
            [{ 'header': [1, 2, 3, 4, 5, 6, false] }],
            [{ 'color': [] }, { 'background': [] }],          // dropdown with defaults from theme
            [{ 'font': fonts},'video']
          ];
        //   "SimSun","SimHei","Microsoft-YaHei","KaiTi","FangSong","Arial","Times New Roman","sans-serif"
        let config = {
            debug: 'info',
            modules: {
                toolbar: toolbarOptions 
            },
            placeholder: '请输入内容...',
            readOnly: false,
            theme: 'snow'
        };
        this.editor = new Quill('#editor', config);// 初始化编辑器
        Quill.debug("error");//只开启error提示  默认的console还是会打印 没有找到更好的解决办法
        let toolbar = this.editor.getModule('toolbar');
        // console.log(this.props)
        this.editor.container.firstChild.innerHTML = this.props.value; //赋值富文本  此处用于父组件传值给子组件quill(主要用于编辑页面,只做添加页面此处可忽略)
        //点击quill的上传图片转接到oss上传的图标上
        toolbar.addHandler('image', () => {
            $('.editUpload .ant-upload').click()
        });
    };

    //富文本图片上传至oss上
    beforeGoodsUpload=(file)=> {
        let files = [];
        this.setState({
            fileList: [...files]
        })
         return false;
    }
    //图片oss上传   此处大致讲解  不明白的看我之前发布的关于oss上传的文章
    uploadGoodsPic(index,file){
        const UID = getUid();
        const isJPG = file.file.type === 'image/jpeg';
        const isPNG = file.file.type === 'image/png';
        const isLt2M = file.file.size / 1024 / 1024 < 5;
        if (!isJPG && !isPNG) {
            message.error('只能上传jpg|png|jpeg');
            return;
        }else{
            if (!isLt2M) {
                message.error('图片尺寸必须小于 5MB!');
                return;
            }
        }
        let param_first = {
            uid:UID
        };
        
        /**** oss上传****/
        request(`*********`, {  //获取oss上传所需的参数接口  后端提供
                method: 'POST',
                headers: config.headers,
                body:config.parseJson(param_first)
        }).then((res) => {
            ossConfig = {
                policy:res.data.policy,
                OSSAccessKeyId:res.data.accessid,
                callback:res.data.callback,
                signature:res.data.signature,
                url:res.data.host,
                dir:res.data.dir,
            };
            const photo = file.fileList[0].originFileObj;  // 获取图片对象
            let pointIndex = photo.name.lastIndexOf('.');
            let suffixName = photo['name'].substring(pointIndex);//后缀名
            const photoName = moment().format('YYYYMMDDHHmmss') + (Math.floor(Math.random() * 3665668)) +  suffixName;  // 原图片的名称
            // const photoName = photo.name;  // 原图片的名称
            const key = ossConfig.dir + UID  + photoName;  // 存储到oss的图片名称 自己定,必须确保唯一性,不然会覆盖oss中原有的文件
            const policy = ossConfig.policy; // 服务器端同事调oss的API,通过接口返回给前端的 policy
            const OSSAccessKeyId = ossConfig.OSSAccessKeyId;  // 服务器端同事调oss的API,通过接口返回给前端的 OSSAccessKeyId
            const callback = ossConfig.callback;  // 服务器端同事调oss的API,通过接口返回给前端的 callback。这个是需要 oss 触发这个回调来通知服务器操作结果。
            const signature = ossConfig.signature;  // 服务器端同事调oss的API,通过接口返回给前端的 signature。这个就是签名,最关键的。
            const url = ossConfig.url;
            const dir = ossConfig.dir;
            // biu一下,提交给oss
            let param = {
                name:photoName,
                key,
                policy:policy,
                OSSAccessKeyId:OSSAccessKeyId,
                success_action_status:'200',
                callback:callback,
                signature:signature,
                file:photo
            };
            const formData = new FormData();
            Object.keys(param).forEach((key) => {
              formData.append(key, param[key]);
            });
            request(url, {
                method: 'POST',
                headers: {
                },
                body: formData,
            }).then((res) => {
                //把上传到oss上的图片赋值给quill编辑器
                let quill = this.editor;
                let range = quill.selection.savedRange.index;
                if(range || range == 0){
                    quill.insertEmbed(range,'image',res.data.data.full_url);
                }
                quill.setSelection(quill.getSelection().index+1);
            })
        });
    }

    render() {
        let placeholder = ''
        const { value,pla } = this.props;
        // pla ? placeholder = pla : placeholder = '请输入内容'
        return (
            <div className="quillRich">
                <div id="editor" ref="editor">
                </div>
                {/* 此处上传隐藏 只做上传使用 */}
                <Upload 
                    name="goods" 
                    listType="picture-card" 
                    showUploadList="false" 
                    className="editUpload"
                    action=""
                    beforeUpload={this.beforeGoodsUpload}
                    onChange={this.uploadGoodsPic.bind(this,0)}
                    headers={config.headerAuth}
                    showUploadList={false}
                    accept=".png,.jpg,.jpeg"
                    >  
                </Upload>
            </div>
        );
    }
}

export default QuillRichText;

###2.less文件 覆盖quill一些自带的样式

    .ql-toolbar{
        width: 100%!important;
        border: none!important;
        border-bottom:1px solid #E4E4E4!important;
    }
    #editor,.ql-editor{
        border: none!important;
        max-height: 440px!important;
    }
    .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=SimSun]::before,
    .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimSun]::before {
        content: "宋体";
        font-family: "SimSun";
    }
    .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=SimHei]::before,
    .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimHei]::before {
        content: "黑体";
        font-family: "SimHei";
    }
    .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Microsoft-YaHei]::before,
    .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Microsoft-YaHei]::before {
        content: "微软雅黑";
        font-family: "Microsoft YaHei";
    }
    .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=KaiTi]::before,
    .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=KaiTi]::before {
        content: "楷体";
        font-family: "KaiTi";
    }
    .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=FangSong]::before,
    .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=FangSong]::before {
        content: "仿宋";
        font-family: "FangSong";
    }
    .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Arial]::before,
    .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Arial]::before {
        content: "Arial";
        font-family: "Arial";
    }
    .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Times-New-Roman]::before,
    .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Times-New-Roman]::before {
        content: "Times New Roman";
        font-family: "Times New Roman";
    }
    .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=sans-serif]::before,
    .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=sans-serif]::before {
        content: "sans-serif";
        font-family: "sans-serif";
    }
    
    .ql-font-SimSun {
        font-family: "SimSun";
    }
    .ql-font-SimHei {
        font-family: "SimHei";
    }
    .ql-font-Microsoft-YaHei {
        font-family: "Microsoft YaHei";
    }
    .ql-font-KaiTi {
        font-family: "KaiTi";
    }
    .ql-font-FangSong {
        font-family: "FangSong";
    }
    .ql-font-Arial {
        font-family: "Arial";
    }
    .ql-font-Times-New-Roman {
        font-family: "Times New Roman";
    }
    .ql-font-sans-serif {
        font-family: "sans-serif";
    }
    .ql-snow .ql-tooltip[data-mode=link]::before{
        content: "\94FE\63A5";
    }
    .ql-snow .ql-tooltip.ql-editing a.ql-action::after{
        content: "\786E\5B9A";
    }
    .ql-snow .ql-picker.ql-size .ql-picker-label[data-value=small]::before, 
    .ql-snow .ql-picker.ql-size .ql-picker-item[data-value=small]::before{
        content: "\5C0F\53F7\5B57\4F53";
    }
    .ql-snow .ql-picker.ql-size .ql-picker-label::before,
    .ql-snow .ql-picker.ql-size .ql-picker-item::before{
        content: "\6B63\5E38\5927\5C0F";
    }
    .ql-snow .ql-picker.ql-size .ql-picker-label[data-value=large]::before,
    .ql-snow .ql-picker.ql-size .ql-picker-item[data-value=large]::before{
        content: "\5927\53F7\5B57\4F53";
    }
    .ql-snow .ql-picker.ql-size .ql-picker-label[data-value=huge]::before, 
    .ql-snow .ql-picker.ql-size .ql-picker-item[data-value=huge]::before{
        content: "\8D85\5927\5B57\4F53";
    }
    .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before, 
    .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before{
        content: "\6807\9898\0020\0031";
    }
    .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before, 
    .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before{
        content: "\6807\9898\0020\0032";
    }
    .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before, 
    .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before{
        content: "\6807\9898\0020\0033";
    }
    .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before, 
    .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before{
        content: "\6807\9898\0020\0034";
    }
    .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before, 
    .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before{
        content: "\6807\9898\0020\0035";
    }
    .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before, 
    .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before{
        content: "\6807\9898\0020\0036";
    }
    .ql-snow .ql-picker.ql-header .ql-picker-label::before, 
    .ql-snow .ql-picker.ql-header .ql-picker-item::before{
        content: "\666E\901A\6587\672C";
    }
    .ql-snow .ql-tooltip::before{
        content: "\8BBF\95EE\94FE\63A5";
    }
    .ql-snow .ql-tooltip a.ql-action::after{
        content: "\7F16\8F91";
    }
    .ql-snow .ql-tooltip a.ql-remove::before{
        content: "\79FB\9664\000A";
    }
    .ql-snow .ql-tooltip[data-mode=video]::before{
        content: "\89C6\9891\94FE\63A5";
    }
}

###3.自定义字体 font.less文件

/*font汉化*/
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=SimSun]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimSun]::before {
  content: "宋体";
  font-family: "SimSun";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=SimHei]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimHei]::before {
  content: "黑体";
  font-family: "SimHei";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Microsoft-YaHei]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Microsoft-YaHei]::before {
  content: "微软雅黑";
  font-family: "Microsoft YaHei";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=KaiTi]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=KaiTi]::before {
  content: "楷体";
  font-family: "KaiTi";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=FangSong]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=FangSong]::before {
  content: "仿宋";
  font-family: "FangSong";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Arial]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Arial]::before {
  content: "Arial";
  font-family: "Arial";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Times-New-Roman]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Times-New-Roman]::before {
  content: "Times New Roman";
  font-family: "Times New Roman";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=sans-serif]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=sans-serif]::before {
  content: "sans-serif";
  font-family: "sans-serif";
}

.ql-font-SimSun {
  font-family: "SimSun";
}
.ql-font-SimHei {
  font-family: "SimHei";
}
.ql-font-Microsoft-YaHei {
  font-family: "Microsoft YaHei";
}
.ql-font-KaiTi {
  font-family: "KaiTi";
}
.ql-font-FangSong {
  font-family: "FangSong";
}
.ql-font-Arial {
  font-family: "Arial";
}
.ql-font-Times-New-Roman {
  font-family: "Times New Roman";
}
.ql-font-sans-serif {
  font-family: "sans-serif";
}
上一篇:QL SERVER 和DELPHI的DATATYPE 对照


下一篇:P1516 青蛙的约会