react富文本編輯器react-quill的使用

關(guān)于寫此文的目的:

第一次接觸富文本編輯器街夭,尤其是react-quill這種,百度不到的各種坑,踩了無數(shù)的坑終于搞明白了捎泻。

1.安裝

npm i react-quill --save
需要用到emjo的話還需要npm i quillEmoji --save
安裝完之后頁面引進(jìn)

import ReactQuill, { Quill } from 'react-quill';
import 'react-quill/dist/quill.snow.css';
import quillEmoji from 'quill-emoji';
import "quill-emoji/dist/quill-emoji.css"; //這個不引入的話會出現(xiàn)emoji框一直在輸入框下面的情況
import { ImageDrop } from './plugin/quill-image-drop-module'; //講圖片拖進(jìn)文本框,可以直接安裝quill-image-drop-module诡曙;但由于我的webpack版本過低臀叙,無法兼容es6,所以把插件拿出來了
//注冊ToolbarEmoji价卤,將在工具欄出現(xiàn)emoji劝萤;注冊TextAreaEmoji,將在文本輸入框處出現(xiàn)emoji慎璧。VideoBlot是我自定義的視頻組件床嫌,后面會講,
const { EmojiBlot, ShortNameEmoji, ToolbarEmoji, TextAreaEmoji } = quillEmoji;
Quill.register({
  'formats/emoji': EmojiBlot,
  'formats/video': VideoBlot,
  'modules/emoji-shortname': ShortNameEmoji,
  'modules/emoji-toolbar': ToolbarEmoji,
  'modules/emoji-textarea': TextAreaEmoji,
  // 'modules/ImageExtend': ImageExtend, //拖拽圖片擴(kuò)展組件
  'modules/ImageDrop': ImageDrop, //復(fù)制粘貼組件
}, true);

next胸私。

初始化富文本實例厌处,我寫在constructor里,module也是寫在這里邊

constructor(props) { super(props); this.reactQuillRef = null; }

富文本組件react-quill參數(shù)

<ReactQuill
      ref={(el) => { this.reactQuillRef = el }}
      defaultValue={postRichText}
       key="1"
       id="textDiv1" theme="snow" modules={this.modules}  />

工具欄modules的定義基本屬性如下:

 this.modules = {
      toolbar: {
        container: [
          [{ 'size': ['small', false, 'large', 'huge'] }], //字體設(shè)置
          // [{ 'header': [1, 2, 3, 4, 5, 6, false] }], //標(biāo)題字號岁疼,不能設(shè)置單個字大小
          ['bold', 'italic', 'underline', 'strike'],  
          [{ 'list': 'ordered' }, { 'list': 'bullet' }, { 'indent': '-1' }, { 'indent': '+1' }],
          ['link', 'image'], // a鏈接和圖片的顯示
          [{ 'align': [] }],
          [{
            'background': ['rgb(  0,   0,   0)', 'rgb(230,   0,   0)', 'rgb(255, 153,   0)',
              'rgb(255, 255,   0)', 'rgb(  0, 138,   0)', 'rgb(  0, 102, 204)',
              'rgb(153,  51, 255)', 'rgb(255, 255, 255)', 'rgb(250, 204, 204)',
              'rgb(255, 235, 204)', 'rgb(255, 255, 204)', 'rgb(204, 232, 204)',
              'rgb(204, 224, 245)', 'rgb(235, 214, 255)', 'rgb(187, 187, 187)',
              'rgb(240, 102, 102)', 'rgb(255, 194, 102)', 'rgb(255, 255, 102)',
              'rgb(102, 185, 102)', 'rgb(102, 163, 224)', 'rgb(194, 133, 255)',
              'rgb(136, 136, 136)', 'rgb(161,   0,   0)', 'rgb(178, 107,   0)',
              'rgb(178, 178,   0)', 'rgb(  0,  97,   0)', 'rgb(  0,  71, 178)',
              'rgb(107,  36, 178)', 'rgb( 68,  68,  68)', 'rgb( 92,   0,   0)',
              'rgb(102,  61,   0)', 'rgb(102, 102,   0)', 'rgb(  0,  55,   0)',
              'rgb(  0,  41, 102)', 'rgb( 61,  20,  10)']
          }],
          [{
            'color': ['rgb(  0,   0,   0)', 'rgb(230,   0,   0)', 'rgb(255, 153,   0)',
              'rgb(255, 255,   0)', 'rgb(  0, 138,   0)', 'rgb(  0, 102, 204)',
              'rgb(153,  51, 255)', 'rgb(255, 255, 255)', 'rgb(250, 204, 204)',
              'rgb(255, 235, 204)', 'rgb(255, 255, 204)', 'rgb(204, 232, 204)',
              'rgb(204, 224, 245)', 'rgb(235, 214, 255)', 'rgb(187, 187, 187)',
              'rgb(240, 102, 102)', 'rgb(255, 194, 102)', 'rgb(255, 255, 102)',
              'rgb(102, 185, 102)', 'rgb(102, 163, 224)', 'rgb(194, 133, 255)',
              'rgb(136, 136, 136)', 'rgb(161,   0,   0)', 'rgb(178, 107,   0)',
              'rgb(178, 178,   0)', 'rgb(  0,  97,   0)', 'rgb(  0,  71, 178)',
              'rgb(107,  36, 178)', 'rgb( 68,  68,  68)', 'rgb( 92,   0,   0)',
              'rgb(102,  61,   0)', 'rgb(102, 102,   0)', 'rgb(  0,  55,   0)',
              'rgb(  0,  41, 102)', 'rgb( 61,  20,  10)']
          }],
          ['clean'], //清空
          ['emoji'], //emoji表情阔涉,設(shè)置了才能顯示
          ['video2'], //我自定義的視頻圖標(biāo),和插件提供的不一樣捷绒,所以設(shè)置為video2
        ],
        handlers: {
          'image': this.imageHandler.bind(this), //點擊圖片標(biāo)志會調(diào)用的方法
          'video2': this.showVideoModal.bind(this),
        },
      },
      // ImageExtend: {
      //   loading: true,
      //   name: 'img',
      //   action: RES_URL + "connector?isRelativePath=true",
      //   response: res => FILE_URL + res.info.url
      // },
      ImageDrop: true,
      'emoji-toolbar': true,  //是否展示出來
      "emoji-textarea": false, //我不需要emoji展示在文本框所以設(shè)置為false
      "emoji-shortname": true, 
    }

想要的最終效果如下圖:


image.png

到這一步洒敏,界面是能正常顯示了,但功能不完善疙驾,next

3.功能的開發(fā)

1.上傳本地圖片到服務(wù)器凶伙,代碼如下:

//這是點擊圖片圖標(biāo)觸發(fā)的事件
imageHandler() {
    const input = document.createElement('input')
    input.setAttribute('type', 'file')
    input.setAttribute('accept', 'image/*')
    input.setAttribute('multiple', 'multiple')
    input.click()
    const that = this;
    input.onchange = async () => {
      Array.from(input.files).forEach(item => {
      //業(yè)務(wù)需求安裝了壓縮圖片的插件,可忽略
        new Compressor(item, {
          quality: 0.8,
          convertSize: 1024 * 1024 * 8,
          success(result) {
          //很很很很重要的一步
            const formData = new FormData();
            formData.append('file', result, result.name);
            Axios({
              method: 'post',
              data: formData,
              url: config.RES_URL + 'connector?isRelativePath=true'它碎,//圖片上傳的接口
            }).then(res => {
              if (res.data.success) {
                let quill = that.reactQuillRef.getEditor();//獲取到編輯器本身
                const cursorPosition = quill.getSelection().index;//獲取當(dāng)前光標(biāo)位置
                const link = config.RES_URL + res.data.info.url;
                quill.insertEmbed(cursorPosition, "image", link);//插入圖片
                quill.setSelection(cursorPosition + 1);//光標(biāo)位置加1
              }
            })
          },
        });
      })
    }
  }

到這里已經(jīng)能實現(xiàn)本地圖片上傳到服務(wù)器的需求了
2.視頻自定義

 //上傳視頻處理
  addVideoItem = (img, url) => {
    let quill = this.reactQuillRef.getEditor();//獲取到編輯器本身
    let cursorPosition = quill.selection.savedRange.index
    quill.insertEmbed(cursorPosition, 'Video', {
      url,
      controls: 'controls',
      poster: img,
      width: '100%',
      controlslist: 'nodownload noremoteplayback',
      oncontextmenu: 'return false'
    })
    // 光標(biāo)不加1的話視頻刪不掉
    quill.setSelection(cursorPosition + 1);//光標(biāo)位置加1
    this.setState({
      upVideoShow: false
    })
  }

這段代碼功能實現(xiàn)的前提是函荣,我剛開始引入的自定義視頻組件,創(chuàng)建視頻標(biāo)簽扳肛,代碼如下:


const Quill = require('quill');

const BlockEmbed = Quill.import('blots/block/embed')
export class VideoBlot extends BlockEmbed {
  static create(value) {
    let node = super.create()
    node.setAttribute('src', value.url)
    node.setAttribute('controls', value.controls)
    node.setAttribute('width', value.width)
    node.setAttribute('poster', value.poster)
    node.setAttribute('controlslist', 'nodownload noremoteplayback')
    node.setAttribute('oncontextmenu', 'return false')
    return node;
  }
  // 富文本初始化取參數(shù)傻挂,如果有編輯富文本的功能的話,這段代碼就需要加上
  static value(node) {
    return {
      url: node.getAttribute('src'),
      controls: node.getAttribute('controls'),
      width: node.getAttribute('width'),
      poster: node.getAttribute('poster'),
      controlslist: node.getAttribute('controlslist'),
      oncontextmenu: node.getAttribute('oncontextmenu')
    };
  }
}
VideoBlot.blotName = 'Video';
VideoBlot.tagName = 'video';
VideoBlot.className = 'ql-video';

完結(jié)
踩坑無數(shù)搞了兩個星期才弄的明明白白挖息,隔了一個月再來寫當(dāng)初怎么艱難開始的也都忘了金拒,寫不出來什么了。如有遇到什么問題歡迎留言

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末套腹,一起剝皮案震驚了整個濱河市绪抛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌电禀,老刑警劉巖幢码,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異尖飞,居然都是意外死亡症副,警方通過查閱死者的電腦和手機(jī)店雅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贞铣,“玉大人闹啦,你說我怎么就攤上這事≡樱” “怎么了窍奋?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長圣勒。 經(jīng)常有香客問我费变,道長,這世上最難降的妖魔是什么圣贸? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任挚歧,我火速辦了婚禮,結(jié)果婚禮上吁峻,老公的妹妹穿的比我還像新娘滑负。我一直安慰自己,他們只是感情好用含,可當(dāng)我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布矮慕。 她就那樣靜靜地躺著,像睡著了一般啄骇。 火紅的嫁衣襯著肌膚如雪痴鳄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天缸夹,我揣著相機(jī)與錄音痪寻,去河邊找鬼。 笑死虽惭,一個胖子當(dāng)著我的面吹牛橡类,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播芽唇,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼顾画,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了匆笤?” 一聲冷哼從身側(cè)響起研侣,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎疚膊,沒想到半個月后义辕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡寓盗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年灌砖,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片傀蚌。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡基显,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出善炫,到底是詐尸還是另有隱情撩幽,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布箩艺,位于F島的核電站窜醉,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏艺谆。R本人自食惡果不足惜榨惰,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望静汤。 院中可真熱鬧琅催,春花似錦、人聲如沸虫给。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽抹估。三九已至缠黍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間药蜻,已是汗流浹背瓷式。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留谷暮,地道東北人蒿往。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像湿弦,于是被迫代替她去往敵國和親瓤漏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,762評論 2 345