TinyMCE富文本編輯器在Vue中的使用方式
簡(jiǎn)介
公司的后臺(tái)管理系統(tǒng)要做一個(gè)文檔編輯器,最開始選型是 toast-ui/editor ,但是同事用起來指出功能太少、不好用、沒有字體功能。。。
好好好,本著為人民服務(wù)的精神,我重新篩選編輯器組件,最終選定了TinyMCE這個(gè)編輯器。
一、TinyMCE文檔
在開發(fā)過程中,免不了要站在巨人的肩膀上。我參考了莫若卿大佬的TinyMCE中文文檔,大家可以參考。
順便加上官方文檔,英文的看著費(fèi)勁 TinyMCE官方文檔
二、實(shí)現(xiàn)成果
無圖無真相!這里我就把實(shí)現(xiàn)前后的效果圖先放出來,大家如果需要的話,再往后看。
1、toast-ui/editor效果
2、TinyMCE效果
對(duì)比兩個(gè)編輯器,可以發(fā)現(xiàn)TinyMCE多了很多功能,包括字體大小,字體風(fēng)格,行間距等等。
三、引用TinyMCE的方法
對(duì)比完兩個(gè)編輯器的優(yōu)劣,直接跳過toast-ui/editor,只介紹TinyMCE的實(shí)現(xiàn)。
1、引入TinyMCE
由于我是在vue中引用,跟莫若卿大佬的不太一樣,所以我介紹一下我的用法。
(1)、首先,我引用了vue-tinymce,這是提供給 vue 開發(fā)者使用的 TinyMCE 組件。
- 1.安裝組件:
npm install @packy-tang/vue-tinymce
- 2.引入:
import VueTinymce from “@packy-tang/vue-tinymce” Vue.use(VueTinymce)
- 3.使用:
<vue-tinymce ref="tinymce" v-model="content" :setting="setting" />
import tinymce from './components/tinymce/' import VueTinymce from './components/vue-tinymce' Vue.prototype.$tinymce = tinymce Vue.use(VueTinymce)
(2)、由于我改了插件中的一些方法,所以我把TinyMCE整個(gè)插件放到了代碼了,當(dāng)作組件使用。
廢話不多說,上代碼
<template> <vue-tinymce ref="tinymce" v-model="content" :setting="setting" /> </template> <script> //樣式 import './skins/content/default/content.min.css' import './skins/ui/oxide/skin.min.css' import './skins/ui/oxide/content.min.css' //主題 import './themes/silver' //插件 import './plugins/link' //鏈接插件 import './plugins/image' //圖片插件 import './plugins/imagetools' import './plugins/media' //媒體插件 import './plugins/table' //表格插件 import './plugins/lists' //列表插件 import './plugins/quickbars' //快速欄插件 import './plugins/fullscreen' //全屏插件 import './plugins/lineheight' import './plugins/uploadimage' import './plugins/charmap' import './plugins/codesample' import './plugins/upfile' import './plugins/code' /** * 注: * 5.3.x版本需要額外引進(jìn)圖標(biāo),沒有所有按鈕就會(huì)顯示not found */ import './icons/default/icons' //本地化 import './plugins/zh_CN.js' import { uploadFile } from '@/api/document/doc' export default { name: 'tinymceEditor', props: { initialValue: { type: String, default: '' } }, watch: { initialValue (newValue) { this.content = newValue }, }, data(){ return { tinymceHtml: '', resVideo: null, uploaded: false, content: '', setting: { menubar: false, toolbar: "undo redo | fullscreen | formatselect alignleft aligncenter alignright alignjustify | upfile link unlink code | numlist bullist lineheight | image media table | fontselect fontsizeselect forecolor backcolor | bold italic underline strikethrough charmap | indent outdent | superscript subscript | removeformat |", toolbar_drawer: "sliding", quickbars_selection_toolbar: "removeformat | bold italic underline strikethrough | fontsizeselect forecolor backcolor", plugins: "code lineheight link image upfile imagetools media table lists fullscreen quickbars charmap", language: 'zh_CN', height: 500, deprecation_warnings: false, images_upload_handler: (blob, callback) => { this.upload(blob, url => { callback(url) }) }, file_picker_types: 'media', file_picker_callback: (callback, value, meta) => { //當(dāng)點(diǎn)擊meidia圖標(biāo)上傳時(shí),判斷meta.filetype == 'media'有必要,因?yàn)閒ile_picker_callback是media(媒體)、image(圖片)、file(文件)的共同入口 if (meta.filetype == 'media'){ let filetype = '.mp4' //創(chuàng)建一個(gè)隱藏的type=file的文件選擇input let input = document.createElement('input'); input.setAttribute('type', 'file'); input.setAttribute('accept', filetype); let that = this; input.onchange = function() { let file = this.files[0]; that.uploadImg(file, url => { callback(url) }); } //觸發(fā)點(diǎn)擊 input.click(); } }, file_callback: (file, callback) => { // 自定義處理文件操作部分 this.uploadFile(file, url => { callback(url) }) } }, } }, methods: { upload (file, callback) { const formData = new FormData() if (file.size / 1024 / 1024 > 50) { this.$message.error('圖片大小應(yīng)小于50M') return false } formData.append('file', file.blob(), file.name) uploadFile(formData).then(res => { const imageSrc = res.data.objectUrl callback(imageSrc) }); }, uploadFile (file, callback) { const fileName = file.name; const fileSize = (file.size / 1024 / 1024).toFixed(1); if (fileSize > 200) { Message.error("文件大小應(yīng)小于200M"); return false; } const formData = new FormData(); formData.append("file", file, fileName); var timestamp = new Date().getTime(); formData.append("batchKey", timestamp); axios.post(`/xxx/xxx/uploadFile`, formData).then(res => { const imageSrc = res.data.objectUrl callback(imageSrc) }); }, async uploadImg (file, callback) { let loadText = document.createElement('span'); let fileName = file.name; const fileSize = (file.size / 1024 / 1024).toFixed(1); if (fileSize > 200) { Message.error("文件大小應(yīng)小于200M"); return false; } const formData = new FormData(); formData.append("file", file, fileName); var timestamp = new Date().getTime(); formData.append("batchKey", timestamp); // 上傳信息顯示為位置 const supMessagePos = document.getElementsByClassName('tox-dialog__footer')[0] const messagePos = document.getElementsByClassName('tox-dialog__footer-start')[0] var config = { onUploadProgress: progressEvent => { var complete = ((progressEvent.loaded / progressEvent.total) * 100) | 0; if (complete == 100) { loadText.innerHTML = "加載完成,等待上傳..."; supMessagePos.insertBefore(loadText, messagePos) } else { loadText.innerHTML = "正在加載... " + complete + " %"; supMessagePos.insertBefore(loadText, messagePos) } } }; await axios.post(`/xxx/xxx/uploadFile`, formData, config).then(res => { if (res.data) { loadText.innerHTML = "上傳完成!" callback(res.data.objectKey, { title: fileName }) supMessagePos.insertBefore(loadText, messagePos) // myWebSocket.closeSocket(); } }) } } } </script> <style> .tox-statusbar__branding { display: none; } </style>
2、說明
我這里加上了上傳圖片、上傳文件和上傳視頻的方法,不需要的可以省略。
“setting”里的“toolbar”和"plugins"是控制編輯器中的插件功能的。toolbar代表上方引入的功能按鈕,plugins列舉需要引入的插件。
“images_upload_handler”這個(gè)屬性是上傳圖片的回調(diào),加上之后上傳圖片的組件會(huì)增加一個(gè)“上傳”按鈕,方便很多。
實(shí)現(xiàn)效果如下:
“file_picker_callback”屬性也是上傳回調(diào),與上邊那個(gè)類似,不過可以配置。
我添加了判斷 meta.filetype == 'media'
,因?yàn)?ldquo;file_picker_callback”是media(媒體)、image(圖片)、file(文件)的共同入口,我只需要上傳視頻,所以我加了判斷。
加完后的效果如下:
"file_callback"屬性比較特別,它是“upfile”插件專屬的屬性,可以添加上傳文檔的回調(diào)。我因?yàn)樾枰蟼鱬df文件,所以引入了這個(gè)插件到編輯器中。
3、其他配置項(xiàng)
(1)、在光標(biāo)位置顯示快速工具欄
quickbars_insert_toolbar
([插入]快捷工具欄)
這個(gè)功能是當(dāng)你的光標(biāo)在【空的一行】時(shí),顯示可以快速插入的工具項(xiàng)。
setting: { quickbars_insert_toolbar: 'quickimage quicktable' }
如果不需要,可以把這一項(xiàng)設(shè)置為空。
setting: { quickbars_insert_toolbar: '' }
(2)、選中文字后,顯示快速工具欄
quickbars_selection_toolbar
([選擇]快捷工具欄)
這個(gè)功能是當(dāng)你選中文字后,顯示可以對(duì)文字操作的工具項(xiàng)。
setting: { quickbars_selection_toolbar: "removeformat | bold italic underline strikethrough | fontsizeselect forecolor backcolor" }
(3)、向編輯器粘貼圖片后,自動(dòng)將圖片從base64格式轉(zhuǎn)換為url地址
urlconverter_callback
(粘貼圖片后,不自動(dòng)上傳,而是使用base64編碼)
前兩天同事問了我這個(gè)問題,我順便加在這里分享一下。
這個(gè)功能是為了解決向編輯器粘貼圖片的問題。正常通過插件上傳圖片時(shí),會(huì)自動(dòng)處理成url格式,通過ctrl+v粘貼的圖片則不處理。
在“setting”里加上以下這段代碼就可以解決。
setting: { urlconverter_callback: (url, node, onSave, name) => { if (node === 'img' && url.startsWith('blob:')) { tinymce.activeEditor && tinymce.activeEditor.uploadImages() } return url }, }
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Avue實(shí)現(xiàn)動(dòng)態(tài)查詢與數(shù)據(jù)展示的示例代碼
Avue是一個(gè)基于Vue.js的前端框架,它是由阿里云開發(fā)的一款企業(yè)級(jí)UI組件庫(kù),旨在提供一套全面、易用且高性能的界面解決方案本文介紹了Avue實(shí)現(xiàn)動(dòng)態(tài)查詢與數(shù)據(jù)展示的示例,需要的朋友可以參考下2024-08-08vue動(dòng)態(tài)配置模板 ''component is''代碼
這篇文章主要介紹了vue動(dòng)態(tài)配置模板 'component is'代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-07-07vue3配置代理實(shí)現(xiàn)axios請(qǐng)求本地接口返回PG庫(kù)數(shù)據(jù)
這篇文章主要為大家詳細(xì)介紹了vue3配置代理實(shí)現(xiàn)axios請(qǐng)求本地接口返回PG庫(kù)數(shù)據(jù)的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下2025-03-03Vue.set()動(dòng)態(tài)的新增與修改數(shù)據(jù),觸發(fā)視圖更新的方法
今天小編就為大家分享一篇Vue.set()動(dòng)態(tài)的新增與修改數(shù)據(jù),觸發(fā)視圖更新的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-09-09vue3中關(guān)于路由hash與History的設(shè)置
這篇文章主要介紹了vue3中關(guān)于路由hash與History的設(shè)置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04vue3實(shí)現(xiàn)自定義導(dǎo)航菜單的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何使用vue3實(shí)現(xiàn)自定義導(dǎo)航菜單,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-11-11