前言:vue中不少項目都須要用到富文本編輯器,在使用了ueditor和tinymce後,發現並不理想。因此果斷使用vue-quill-editor來實現。
wangEditor(國產,基於javascript和css開發的web富文本編輯器,開源免費)優點:輕量簡介,最重要的是開源且中文文檔齊全。缺點:更新不及時。沒有強大的開發團隊支撐。 UEditor(百度)優點:插件多,基本曼度各類需求,由百度web前端研發部開發。缺點:插件提交較大,網頁加載速度相對就慢了些。使用複雜。屬於先後端不分離插件。在使用時須要配置後端的一些東西,使用不便。
Kindeditor () 優點:文檔齊全,爲中文,閱讀方便。缺點:圖片上傳存在問題,上傳歷史過多,會所有加載,致使瀏覽器卡頓。 補充:Tinymce也是一款不錯的富文本編輯器,種植,各有優點和劣勢,關鍵是選擇一款最適合的就好。由於筆者在開發vue,因此直接使用vue-quill-editor較爲方便些。具體看狀況使用。
npm install vue-quill-editor -s
import VueQuillEditor from 'vue-quill-editor' import 'quill/dist/quill.core.css' import 'quill/dist/quill.snow.css' import 'quill/dist/quill.bubble.css' Vue.use(VueQuillEditor);
須要注意的是toolbar的配置1. 只須要填寫功能名的加粗 - bold;斜體 - italic下劃線 - underline刪除線 - strike引用- blockquote代碼塊 - code-block公式 - formula圖片 - image視頻 - video清除字體樣式- clean這一類的引用 直接['name','name']這種格式就行了2. 須要有默認值的標題 - header [{ 'header': 1 }, { 'header': 2 }] 值字體大小 列表 - list [{ 'list': 'ordered'}, { 'list': 'bullet' }], 值ordered,bullet 上標/下標 - script [{ 'script': 'sub'}, { 'script': 'super' }], 值sub,super 縮進 - indent [{ 'indent': '-1'}, { 'indent': '+1' }], 值-1,+1等 文本方向 - direction [{'direction':'rtl'}]
<template> <quill-editor class="editor" ref="myTextEditor" v-model="content" :options="editorOption" @blur="onEditorBlur($event)" @focus="onEditorFocus($event)" @ready="onEditorReady($event)" @change="onEditorChange($event)"> </quill-editor> </template> <script> export default { data () { return { content: null, editorOption: { modules: { toolbar: [ ["bold", "italic", "underline", "strike"], // 加粗 斜體 下劃線 刪除線 ["blockquote", "code-block"], // 引用 代碼塊 [{ header: 1 }, { header: 2 }], // 一、2 級標題 [{ list: "ordered" }, { list: "bullet" }], // 有序、無序列表 [{ script: "sub" }, { script: "super" }], // 上標/下標 [{ indent: "-1" }, { indent: "+1" }], // 縮進 // [{'direction': 'rtl'}], // 文本方向 [{ size: ["small", false, "large", "huge"] }], // 字體大小 [{ header: [1, 2, 3, 4, 5, 6, false] }], // 標題 [{ color: [] }, { background: [] }], // 字體顏色、字體背景顏色 [{ font: [] }], // 字體種類 [{ align: [] }], // 對齊方式 ["clean"], // 清除文本格式 ["link", "image", "video"] // 連接、圖片、視頻 ], //工具菜單欄配置 }, placeholder: '請在這裏添加產品描述', //提示 readyOnly: false, //是否只讀 theme: 'snow', //主題 snow/bubble syntax: true, //語法檢測 } } }, methods: { // 失去焦點 onEditorBlur(editor) {}, // 得到焦點 onEditorFocus(editor) {}, // 開始 onEditorReady(editor) {}, // 值發生變化 onEditorChange(editor) { this.content = editor.html; console.log(editor); }, }, computed: { editor() { return this.$refs.myTextEditor.quillEditor; } }, mounted() { // console.log('this is my editor',this.editor); } } </script>
<style> .editor { line-height: normal !important; height: 800px; } .ql-snow .ql-tooltip[data-mode=link]::before { content: "請輸入連接地址:"; } .ql-snow .ql-tooltip.ql-editing a.ql-action::after { border-right: 0px; content: '保存'; padding-right: 0px; } .ql-snow .ql-tooltip[data-mode=video]::before { content: "請輸入視頻地址:"; } .ql-snow .ql-picker.ql-size .ql-picker-label::before, .ql-snow .ql-picker.ql-size .ql-picker-item::before { content: '14px'; } .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: '10px'; } .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: '18px'; } .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: '32px'; } .ql-snow .ql-picker.ql-header .ql-picker-label::before, .ql-snow .ql-picker.ql-header .ql-picker-item::before { content: '文本'; } .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: '標題1'; } .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: '標題2'; } .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: '標題3'; } .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: '標題4'; } .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: '標題5'; } .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: '標題6'; } .ql-snow .ql-picker.ql-font .ql-picker-label::before, .ql-snow .ql-picker.ql-font .ql-picker-item::before { content: '標準字體'; } .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=serif]::before, .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=serif]::before { content: '襯線字體'; } .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=monospace]::before, .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before { content: '等寬字體'; } </style>
由於圖片這塊,大多狀況下,咱們都不須要base64格式的,因此咱們須要將圖片經過OSS換取網絡路徑而後發送給後端和回顯。
editorOption: { modules: { toolbar: { handlers: { image: function(value) { if (value) { // 觸發input框選擇圖片文件 document.querySelector(".avatar-uploader input").click();//自定義元素的點擊事件 } else { this.quill.format("image", false); } }, // link: function(value) { // if (value) { // var href = prompt('請輸入url'); // this.quill.format("link", href); // } else { // this.quill.format("link", false); // } // }, } } } },
uploadSuccess(res, file) { // res爲圖片服務器返回的數據 // 獲取富文本組件實例 let quill = this.$refs.myQuillEditor.quill; // 若是上傳成功 if (res.code == 200) { // 獲取光標所在位置 let length = quill.getSelection().index; // 插入圖片 res.url爲服務器返回的圖片地址 quill.insertEmbed(length, "image", res.url); // 調整光標到最後 quill.setSelection(length + 1); } else { this.$message.error("圖片插入失敗"); } // loading動畫消失 this.quillUpdateImg = false; },
<template> <div> <!-- 圖片上傳組件輔助--> <el-upload class="avatar-uploader" :action="serverUrl" name="file" :headers="header" :show-file-list="false" list-type="picture" :multiple="false" :on-success="uploadSuccess" :on-error="uploadError" :before-upload="beforeUpload"> </el-upload> <quill-editor class="editor" v-model="content" ref="myQuillEditor" :options="editorOption" @blur="onEditorBlur($event)" @focus="onEditorFocus($event)" @change="onEditorChange($event)"> </quill-editor> </div> </template> <script> // 工具欄配置 const toolbarOptions = [ ["bold", "italic", "underline", "strike"], // 加粗 斜體 下劃線 刪除線 ["blockquote", "code-block"], // 引用 代碼塊 [{ header: 1 }, { header: 2 }], // 一、2 級標題 [{ list: "ordered" }, { list: "bullet" }], // 有序、無序列表 [{ script: "sub" }, { script: "super" }], // 上標/下標 [{ indent: "-1" }, { indent: "+1" }], // 縮進 // [{'direction': 'rtl'}], // 文本方向 [{ size: ["small", false, "large", "huge"] }], // 字體大小 [{ header: [1, 2, 3, 4, 5, 6, false] }], // 標題 [{ color: [] }, { background: [] }], // 字體顏色、字體背景顏色 [{ font: [] }], // 字體種類 [{ align: [] }], // 對齊方式 ["clean"], // 清除文本格式 ["link", "image", "video"] // 連接、圖片、視頻 ]; import { quillEditor } from "vue-quill-editor"; import "quill/dist/quill.core.css"; import "quill/dist/quill.snow.css"; import "quill/dist/quill.bubble.css"; export default { props: { /*編輯器的內容*/ value: { type: String }, /*圖片大小*/ maxSize: { type: Number, default: 4000 //kb } }, components: { quillEditor }, data() { return { content: this.value, quillUpdateImg: false, // 根據圖片上傳狀態來肯定是否顯示loading動畫,剛開始是false,不顯示 editorOption: { theme: "snow", // or 'bubble' placeholder: "您想說點什麼?", modules: { toolbar: { container: toolbarOptions, // container: "#toolbar", handlers: { image: function(value) { if (value) { // 觸發input框選擇圖片文件 document.querySelector(".avatar-uploader input").click(); } else { this.quill.format("image", false); } }, // link: function(value) { // if (value) { // var href = prompt('請輸入url'); // this.quill.format("link", href); // } else { // this.quill.format("link", false); // } // }, } } } }, serverUrl: "https://testihospitalapi.ebaiyihui.com/oss/api/file/store/v1/saveFile", // 這裏寫你要上傳的圖片服務器地址 header: { // token: sessionStorage.token } // 有的圖片服務器要求請求頭須要有token }; }, methods: { onEditorBlur() { //失去焦點事件 }, onEditorFocus() { //得到焦點事件 }, onEditorChange() { //內容改變事件 this.$emit("input", this.content); }, // 富文本圖片上傳前 beforeUpload() { // 顯示loading動畫 this.quillUpdateImg = true; }, uploadSuccess(res, file) { // res爲圖片服務器返回的數據 // 獲取富文本組件實例 let quill = this.$refs.myQuillEditor.quill; // 若是上傳成功 if (res.code == 200) { // 獲取光標所在位置 let length = quill.getSelection().index; // 插入圖片 res.url爲服務器返回的圖片地址 quill.insertEmbed(length, "image", res.result.url); // 調整光標到最後 quill.setSelection(length + 1); } else { this.$message.error("圖片插入失敗"); } // loading動畫消失 this.quillUpdateImg = false; }, // 富文本圖片上傳失敗 uploadError() { // loading動畫消失 this.quillUpdateImg = false; this.$message.error("圖片插入失敗"); } } }; </script> <style> .editor { line-height: normal !important; height: 800px; } .ql-snow .ql-tooltip[data-mode=link]::before { content: "請輸入連接地址:"; } .ql-snow .ql-tooltip.ql-editing a.ql-action::after { border-right: 0px; content: '保存'; padding-right: 0px; } .ql-snow .ql-tooltip[data-mode=video]::before { content: "請輸入視頻地址:"; } .ql-snow .ql-picker.ql-size .ql-picker-label::before, .ql-snow .ql-picker.ql-size .ql-picker-item::before { content: '14px'; } .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: '10px'; } .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: '18px'; } .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: '32px'; } .ql-snow .ql-picker.ql-header .ql-picker-label::before, .ql-snow .ql-picker.ql-header .ql-picker-item::before { content: '文本'; } .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: '標題1'; } .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: '標題2'; } .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: '標題3'; } .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: '標題4'; } .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: '標題5'; } .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: '標題6'; } .ql-snow .ql-picker.ql-font .ql-picker-label::before, .ql-snow .ql-picker.ql-font .ql-picker-item::before { content: '標準字體'; } .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=serif]::before, .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=serif]::before { content: '襯線字體'; } .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=monospace]::before, .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before { content: '等寬字體'; } </style> 引入: <template> <Editor v-model="article.content"/> </template> <script> import Editor from './quillEditor' export default { components: { Editor }, data() { return { article: { content: '', } } } } </script> <style> </style>