場景
系統中經常會用到富文本編輯器,比如新增通知和公告功能,並且需要添加上傳圖片。

vue-quill-editor官網:
https://www.npmjs.com/package/vue-quill-editor
注:
博客:
https://blog.csdn.net/badao_liumang_qizhi
關注公眾號
霸道的程序猿
獲取編程相關電子書、教程推送與免費下載。
實現
安裝vue-quil-editor
npm install vue-quill-editor --save
引用封裝組件
這里沒有使用全局引用,要將這個富文本編輯器做成一個組件,方便引用。
所以在項目下的components下新建Editor目錄,在此目錄下新建index.vue,用來封裝並暴露富文本編輯器組件
引入組件需要添加如下代碼,包括ccss文件也是必須要引入的
import { quillEditor } from "vue-quill-editor";
import "quill/dist/quill.core.css";
import "quill/dist/quill.snow.css";
import "quill/dist/quill.bubble.css";
然后在template中添加富文本組件
<quill-editor class="editor" v-model="content" ref="quillEditor" :options="editorOption" @blur="onEditorBlur($event)" @focus="onEditorFocus($event)" @change="onEditorChange($event)" ></quill-editor> </div>
設置其菜單欄通過
:options="editorOption"
對應的菜單欄的內容定義
editorOption: { placeholder: "", theme: "snow", // or 'bubble' placeholder: "請輸入內容", modules: { toolbar: { container: toolbarOptions, handlers: { image: function(value) { if (value) { // 觸發input框選擇圖片文件 document.querySelector(".quill-img input").click(); } else { this.quill.format("image", false); } } } } } },
注意這里的設置img菜單的點擊事件會觸發一個function
在方法中根據class屬性找到選擇器,此選擇器是使用的ElementUI的el-upload,會觸發其點擊事件
el-upload的代碼如下
<el-upload class="avatar-uploader quill-img" :action="uploadImgUrl" name="file" :headers="headers" :show-file-list="false" :on-success="quillImgSuccess" :on-error="uploadError" :before-upload="quillImgBefore" accept='.jpg,.jpeg,.png,.gif' ></el-upload>
使用el-upload進行文件上傳到SpringBoot后台並存儲到服務器上,以及前端回顯照片的路徑參照如下博客:
https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/107979828
繼續看富文本組件,其內容的綁定通過
v-model="content"
來實現,就是組件傳遞到數據庫中存儲的內容
存儲內容示例:

所以需要提前聲明content屬性
data() { return { content: this.value,
並且將當前控件的value賦值給content,因為這里是封裝的組件,在調用組件時會將數據庫中的數據進行回顯
<Editor v-model="form.noticeContent" />
然后就能在組件中this.value來獲取並賦值給富文本組件綁定的content
為了實現監視這兩個的改變而改變,所以添加watch函數
watch: { value: function() { this.content = this.value; } },
然后就是設置其一些事件
@blur="onEditorBlur($event)" @focus="onEditorFocus($event)" @change="onEditorChange($event)"
如果需要對這些事件進行操作可在下面進行重寫
onEditorBlur() { //失去焦點事件 }, onEditorFocus() { //獲得焦點事件 }, onEditorChange() { //內容改變事件 this.$emit("input", this.content); },
上面關聯了標題欄中的圖片的點擊事件會執行el-upload的點擊事件。
那么在el-upload組件中
:action="uploadImgUrl"
設置其上傳的url
:headers="headers"
綁定請求頭
headers: { Authorization: 'Bearer ' + getToken() }
請求頭里攜帶token,請求頭的設置參考如下
https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/108345222
再設置它的一些事件
:on-success="quillImgSuccess" :on-error="uploadError" :before-upload="quillImgBefore"
對上傳前,上傳成功和失敗的事件進行重寫
// 富文本圖片上傳前 quillImgBefore(file) { let fileType = file.type; if(fileType === 'image/jpeg' || fileType === 'image/png'){ return true; }else { this.$message.error('請插入圖片類型文件(jpg/jpeg/png)'); return false; } }, quillImgSuccess(res, file) { // res為圖片服務器返回的數據 // 獲取富文本組件實例 let quill = this.$refs.quillEditor.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("圖片插入失敗"); } }, // 富文本圖片上傳失敗 uploadError() { // loading動畫消失 this.$message.error("圖片插入失敗"); }
注意這里的圖片上傳成功是事件中,圖片上傳對應的后台SpringBoot接口
@PostMapping("/common/upload") public AjaxResult uploadFile(MultipartFile file) throws Exception { try { // 上傳文件路徑 String filePath = RuoYiConfig.getUploadPath(); // 上傳並返回新文件名稱 String fileName = FileUploadUtils.upload(filePath, file); String url = serverConfig.getUrl() + fileName; AjaxResult ajax = AjaxResult.success(); ajax.put("fileName", fileName); ajax.put("url", url); return ajax; } catch (Exception e) { return AjaxResult.error(e.getMessage()); } }
在此接口中
String filePath = RuoYiConfig.getUploadPath();
是在application.yml中獲取配置的文件上傳的路徑
然后調用了文件上傳工具類的上傳方法
String fileName = FileUploadUtils.upload(filePath, file);
最終后台接口返回的url是一個在服務器上絕對路徑的圖片地址,比如
http://localhost:8080/profile/upload/2020/09/02/e070fd1a26dca6c00acf6db1bc467905.png
然后
// 獲取光標所在位置 let length = quill.getSelection().index; // 插入圖片 res.url為服務器返回的圖片地址 quill.insertEmbed(length, "image", res.url); // 調整光標到最后 quill.setSelection(length + 1);
調用
quill.insertEmbed(length, "image", res.url);
就能實現在富文本中插入照片,插入成功后的富文本編輯器的內容中就會增加
<img src="http://localhost:8080/profile/upload/2020/09/02/e070fd1a26dca6c00acf6db1bc467905.png ">
富文本編輯器封裝組件的完整代碼
<template>
<div>
<!-- 圖片上傳組件輔助 -->
<el-upload
class="avatar-uploader quill-img"
:action="uploadImgUrl"
name="file"
:headers="headers"
:show-file-list="false"
:on-success="quillImgSuccess"
:on-error="uploadError"
:before-upload="quillImgBefore"
accept='.jpg,.jpeg,.png,.gif'
></el-upload>
<!-- 富文本組件 -->
<quill-editor
class="editor"
v-model="content"
ref="quillEditor"
:options="editorOption"
@blur="onEditorBlur($event)"
@focus="onEditorFocus($event)"
@change="onEditorChange($event)"
></quill-editor>
</div>
</template>
<script>
import { getToken } from '@/utils/auth'
// 工具欄配置
const toolbarOptions = [
["bold", "italic", "underline", "strike"], // 加粗 斜體 下划線 刪除線
["blockquote", "code-block"], // 引用 代碼塊
[{ list: "ordered" }, { list: "bullet" }], // 有序、無序列表
[{ indent: "-1" }, { indent: "+1" }], // 縮進
[{ size: ["small", false, "large", "huge"] }], // 字體大小
[{ header: [1, 2, 3, 4, 5, 6, false] }], // 標題
[{ color: [] }, { background: [] }], // 字體顏色、字體背景顏色
[{ 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,
uploadImgUrl: "",
editorOption: {
placeholder: "",
theme: "snow", // or 'bubble'
placeholder: "請輸入內容",
modules: {
toolbar: {
container: toolbarOptions,
handlers: {
image: function(value) {
if (value) {
// 觸發input框選擇圖片文件
document.querySelector(".quill-img input").click();
} else {
this.quill.format("image", false);
}
}
}
}
}
},
uploadImgUrl: process.env.VUE_APP_BASE_API + "/common/upload", // 上傳的圖片服務器地址
headers: {
Authorization: 'Bearer ' + getToken()
}
};
},
watch: {
value: function() {
this.content = this.value;
}
},
methods: {
onEditorBlur() {
//失去焦點事件
},
onEditorFocus() {
//獲得焦點事件
},
onEditorChange() {
//內容改變事件
this.$emit("input", this.content);
},
// 富文本圖片上傳前
quillImgBefore(file) {
let fileType = file.type;
if(fileType === 'image/jpeg' || fileType === 'image/png'){
return true;
}else {
this.$message.error('請插入圖片類型文件(jpg/jpeg/png)');
return false;
}
},
quillImgSuccess(res, file) {
// res為圖片服務器返回的數據
// 獲取富文本組件實例
let quill = this.$refs.quillEditor.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("圖片插入失敗");
}
},
// 富文本圖片上傳失敗
uploadError() {
// loading動畫消失
this.$message.error("圖片插入失敗");
}
}
};
</script>
<style>
.editor {
line-height: normal !important;
height: 192px;
}
.quill-img {
display: none;
}
.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>
封裝完組件后就是在需要用到的頁面進行引用,比如這里是在通知和公告的新增和編輯頁面使用。
來到此vue頁面,首先在頁面中引入該組件
import Editor from '@/components/Editor' ;
然后聲明該組件
export default { name: "Notice", components: { Editor },
此頁面為發布通知和公告的頁面,所以在新增和編輯時的位置添加該組件
<el-col :span="24"> <el-form-item label="內容"> <Editor v-model="form.noticeContent" /> </el-form-item> </el-col>
這里的form.noticeContent就是在點擊編輯頁面時從后台數據庫中查詢的保存的富文本編輯器的內容。
設計一個公告的數據庫,添加存儲內容的字段

這樣就能實現富文本編輯器的內容的存儲和回顯以及帶照片的上傳。
