react quill 富文本 使用

首先 npm i quill 安装 (目前使用的 “quill”: “^1.3.7” 版)

components 文件下创建 QuillRichText 文件 > index.js

import React, { Component } from 'react';
import Quill from "quill";
require("quill/dist/quill.snow.css");
import moment from 'moment';
import PropTypes from 'prop-types';
import { connect } from 'dva';

// 图片伸缩  需要安装 
// import ImageResize from "quill-image-resize-module";
// Quill.register("modules/imageResize", ImageResize);

import { AppConstants } from "../../constants/AppConstants";
import Prefix from "../../constants/Prefix";

/*富文本编辑图片上传配置   */
const uploadConfig = {
    action: Prefix("UPLOAD") + "/file/common/upload/v1", // 必填参数 图片上传地址 
    methods: "POST", // 必填参数 图片上传方式
    token: AppConstants.TOKEN, // 可选参数 如果需要token验证 
    name: "file", // 必填参数 文件的参数名
    size: 500, // 可选参数   图片大小,单位为Kb, 1M = 1024Kb
    accept: "image/png, image/gif, image/jpeg, image/bmp, image/x-icon" // 可选 可上传的图片格式
  };

/* 头部功能显示 */
const titleConfig = {
    "ql-bold": "加粗",
    "ql-color": "字体颜色",
    "ql-font": "字体",
    "ql-code": "插入代码",
    "ql-italic": "斜体",
    "ql-link": "添加链接",
    "ql-background": "背景颜色",
    "ql-size": "字体大小",
    "ql-strike": "删除线",
    "ql-script": "上标/下标",
    "ql-underline": "下划线",
    "ql-blockquote": "引用",
    "ql-header": "标题",
    "ql-indent": "缩进",
    "ql-list": "列表",
    "ql-align": "文本对齐",
    "ql-direction": "文本方向",
    "ql-code-block": "代码块",
    "ql-formula": "公式",
    "ql-image": "图片",
    "ql-video": "视频",
    "ql-clean": "清除字体样式"
  };
  
  /*图片上传   */
 const handlers = {
    image: function image() {
      var self = this;
      var fileInput = this.container.querySelector("input.ql-image[type=file]");
      if (fileInput === null) {
        fileInput = document.createElement("input");
        fileInput.setAttribute("type", "file");
        // 设置图片参数名
        if (uploadConfig.name) {
          fileInput.setAttribute("name", uploadConfig.name);
        }
        // 可设置上传图片的格式
        fileInput.setAttribute("accept", uploadConfig.accept);
        fileInput.classList.add("ql-image");
        // 监听选择文件
        fileInput.addEventListener("change", function() {
          // 创建formData
          var formData = new FormData();
          formData.append(uploadConfig.name, fileInput.files[0]);
  
          formData.append("uploadType", 7);
        
          // 图片上传
          var xhr = new XMLHttpRequest();
          xhr.open(uploadConfig.methods, uploadConfig.action, true);
          xhr.setRequestHeader("token", AppConstants.TOKEN);          
          // 上传数据成功,会触发
          xhr.onload = function(e) {
            if (xhr.status === 200) {
              var res = JSON.parse(xhr.responseText);
              let length = self.quill.getSelection(true).index;
              //图片上传成功后,服务器返回的图片链接。
              self.quill.insertEmbed(length, "image", res.data.url);
              self.quill.setSelection(length + 1);
            }
            fileInput.value = "";
          };
          // 开始上传数据
          xhr.upload.onloadstart = function(e) {
            fileInput.value = "";
          };
          // 当发生网络异常的时候会触发,如果上传数据的过程还未结束
          xhr.upload.onerror = function(e) {};
          // 上传数据完成(成功或者失败)时会触发
          xhr.upload.onloadend = function(e) {
            // console.log('上传结束')
          };
          xhr.send(formData);
        });
        this.container.appendChild(fileInput);
      }
      fileInput.click();
    }
  };

接上面,我这里的需求是可新建文章、查看 且编辑

details 是点击详情 父组件 请求后的内容

@connect(({ systemNotice }) => ({
    details: systemNotice.details,
  }))

class QuillRichText extends Component {

      constructor(props) {
        super(props);
        this.state = {
          isUpdate: false,
          value:''
        };   
      }

    componentDidMount = () => {
        this.initEditor();
        this.addQuillTitle();
    };

    componentWillUnmount() {
        const { dispatch } = this.props;
        dispatch({
          type: 'systemNotice/updateState',
          payload: {
            details: {
              title: '',
              content: '',
            },
          },
        });
      }

    //初始化编辑器
    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
        const fonts = [
        'SimSun',
        'SimHei',
        // 'Microsoft-YaHei',
        'KaiTi',
        'FangSong',
        'Arial',
        // 'Times-New-Roman',
        'monospace',
        'serif',
        'consolas'
        ];
        Font.whitelist = fonts; //将字体加入到白名单
        Quill.register(Font, true);
        const toolbarOptions = [
            ['bold', 'italic', 'underline', "strike"],        // toggled buttons
            [{ 'list': 'bullet' }],  
            [{ 'align': [] }],
            ["image"],              // 可添加 "link"                   
            [{ 'color': [] }, { 'background': [] }],          // dropdown with defaults from theme
            [{ 'size': ['small', false, 'large', 'huge'] }],  // custom dropdown
            [{ 'header': [1, 2, 3, 4, 5, 6, false] }],
            [{ 'font': fonts}],     // 可添加 'video'
            
          ];
        //   "SimSun","SimHei","Microsoft-YaHei","KaiTi","FangSong","Arial","Times New Roman","sans-serif"
        const config = {
            debug: 'info',
            modules: {
                toolbar: {
                    container: toolbarOptions ,
                    handlers: handlers, // 事件重写
                }
            },          
            placeholder: '请输入内容...',
            readOnly: false,
            theme: 'snow'
        };
        this.editor = new Quill('#editor', config);// 初始化编辑器
        Quill.debug("error");//只开启error提示  默认的console还是会打印 没有找到更好的解决办法
        const toolbar = this.editor.getModule('toolbar');
        const { details } = this.props;  
        this.editor.container.firstChild.innerHTML = details.content; //赋值富文本  此处用于父组件传值给子组件quill(主要用于编辑页面,只做添加页面此处可忽略)
        this.editor.on('text-change', this.handleChange.bind(this));
    };

   /*鼠标移入头部功能区域 显示功能提示   */
    addQuillTitle = () => {
        const oToolBar = document.querySelector(".ql-toolbar"),
          aButton = oToolBar.querySelectorAll("button"),
          aSelect = oToolBar.querySelectorAll("select"),
          aSpan = oToolBar.querySelectorAll("span");
        aButton.forEach(item => {
          if (item.className === "ql-script") {
            item.value === "sub" ? (item.title = "下标") : (item.title = "上标");
          } else if (item.className === "ql-indent") {
            item.value === "+1"
              ? (item.title = "向右缩进")
              : (item.title = "向左缩进");
          } else if (item.className === "ql-list") {
            item.value === "ordered"
              ? (item.title = "有序列表")
              : (item.title = "无序列表");
          } else if (item.className === "ql-header") {
            item.value === "1" ? (item.title = "标题H1") : (item.title = "标题H2");
          } else {
            item.title = titleConfig[item.classList[0]];
          }
        });
        aSelect.forEach(item => {
          if (item.className != "ql-color" && item.className != "ql-background") {
            item.parentNode.title = titleConfig[item.classList[0]];
          }
        });
        aSpan.forEach(item => {
          if (item.classList[0] === "ql-color") {
            item.title = titleConfig[item.classList[0]];
          } else if (item.classList[0] === "ql-background") {
            item.title = titleConfig[item.classList[0]];
          }
        });
      }

    handleChange () {
        const { details } = this.props;  
        this.setState({
            value: this.editor.root.innerHTML,
            isUpdate: true,
        });   
        details.content = this.editor.root.innerHTML;
    }

    render() {
        const { value }  = this.state;
        return (
            <div className="quillRich">
                <div id="editor" ref="editor"  
                 value={ value } 
                >
                </div>
            </div>
        );
    }
}

export default QuillRichText;

调用的

....
import QuillRichText from '../../../components/QuillRichText';
....

@connect(({ user, systemNotice, loading }) => ({
  details: systemNotice.details,
}))

export default class NoticeView extends Component {
 // 从列表页进入的
   constructor(props) {
    super(props);
    const { id, type } = props.match.params;
    this.id = +id;
    this.type = type;
    this.state = {};
  }

// 通过   this.type 判断是编辑还是新增, 编辑根据 id 请求数据后保存 至 Models

//  点击确认获取编辑数据、提交 省略

 render() {
     const { details } = this.props;
         let isTitel;
	    if (this.type !== 'add') {
	      isTitel = '编辑公告';
	    } else {
	      isTitel = '新增公告';
	    }
    if (loading && this.type !== 'add') return  <Loading></Loading>
	  return (
	  	//......省略
	  		<div> {isTitel} </div>
            <QuillRichText
              theme="snow"
              className="ql-editor"
              value={details.content}          
            />
            
         //......省略
	  )
   }
}


_quill.less

.ql-toolbar{
  width: 100%!important;
  line-height: initial !important;
}

#editor,.ql-editor {
  height: 400px;
  em{
    font-style: revert;
  }
}

.ql-editor.ql-blank::before {
  font-style: revert !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";
}


/* 自定义字体 */


/*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";
}



列表进入 新增 或编辑
在这里插入图片描述

新增
在这里插入图片描述

编辑

部分内容 摘自网络 ------侵权立即删除

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React Quill是一个React组件,用于在网页中实现富文本编辑器的功能。要实现预览功能,你可以使用React Quill提供的方法和事件。 首先,确保你已经安装了React Quill,并正确地引入它。 接下来,你可以创建一个React组件,使用React Quill来渲染富文本编辑器。在这个组件中,你可以通过监听Quill编辑器的内容变化事件,来更新预览区域的内容。 下面是一个简单的示例代码: ```jsx import React, { useState } from 'react'; import ReactQuill from 'react-quill'; import 'react-quill/dist/quill.snow.css'; const RichTextEditor = () => { const [content, setContent] = useState(''); const handleContentChange = (value) => { setContent(value); }; return ( <div> <ReactQuill value={content} onChange={handleContentChange} /> <h3>预览:</h3> <div dangerouslySetInnerHTML={{ __html: content }}></div> </div> ); }; export default RichTextEditor; ``` 在上面的代码中,我们创建了一个名为RichTextEditor的组件。它使用useState钩子来保存编辑器的内容,并通过handleContentChange函数来更新内容。 在组件的返回部分,我们渲染了一个ReactQuill组件,并将其值设置为content变量,并通过onChange事件处理函数来更新content变量。 同时,我们还在页面上添加了一个预览区域,可以通过dangerouslySetInnerHTML属性将content的值作为HTML内容进行渲染。 这样,当用户在编辑器中输入内容时,预览区域会实时更新展示编辑器的内容。 希望这个示例能帮助到你!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值