官方文檔:https://www.kancloud.cn/liuwave/quill/1434140
一、內建方法:
1、先安裝依賴 :進入到項目目錄下,執行 npm install vue-quill-editor --save 或者cnpm安裝。
2、main.js引入:
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);
3、頁面使用:
<template>
<div class="edit_container">
<quill-editor v-model="content"></quill-editor>
</div>
</template>
<script>
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 {
name: "fuwenben",
components: {
quillEditor
},
data() {
return {
content: `<p></p><p><br></p><ol><li><strong><em>Or drag/paste an image here.</em></strong></li><li><strong><em>rerew</em></strong></li><li><strong><em>rtrete</em></strong></li><li><strong><em>tytrytr</em></strong></li><li><strong><em>uytu</em></strong></li></ol>`
};
}
};
</script>
二、回顯:原樣回顯使用v-html,隻展示文字可以比對下去除不需要的标簽樣式之類的。
1、如我資料庫建立一張表,
CREATE TABLE `t_user_content` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`account` varchar(255) DEFAULT NULL,
`content` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
2、後端:使用者可以在頁面輸入簡介,資料庫存在則更新,否則插入:
@RequestMapping("/getContext")
public ResponseMessage getContext(){
JWTUserDTO jwtUser = (JWTUserDTO) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
UserContext userContext = userService.getContext(jwtUser.getUserName());
return ResponseMessage.success(userContext);
}
@RequestMapping("/addContext")
public ResponseMessage addContext(@RequestBody UserContext userContext){
JWTUserDTO jwtUser = (JWTUserDTO) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
userContext.setUserAccount(jwtUser.getUserName());
userService.addContext(userContext);
return ResponseMessage.success();
}
@Override
public void addContext(UserContext userContext) {
String userAccount = userContext.getUserAccount();
int count = userDao.getContextByUserAccount(userAccount);
if(count >0){
//更新
userDao.updateContext(userContext);
}else{
userDao.insertContext(userContext);
}
}
@Override
public UserContext getContext(String userName) {
return userDao.getContext(userName);
}
<select id="getContextByUserAccount" resultType="int">
select count(1) from t_user_content where account = #{userAccount}
</select>
<update id="updateContext">
update t_user_content set content = #{userContext.content}
where account =#{userContext.userAccount}
</update>
<insert id="insertContext">
insert into t_user_content(account,content)
values (#{userContext.userAccount},#{userContext.content})
</insert>
<select id="getContext" resultType="com.demo.dto.UserContext">
select account userAccount,content
from t_user_content where account =#{userName} limit 1
</select>
3、前端:
<template>
<div class="edit_container">
簡介
<quill-editor
v-model="content"
ref="myQuillEditor"
:options="editorOption"
@blur="onEditorBlur($event)"
@focus="onEditorFocus($event)"
@change="onEditorChange($event)"
></quill-editor>
<!-- 從資料庫讀取展示 -->
<p>
<button @click="submit()">送出</button>
</p>
</div>
</template>
<script>
import axios from "axios";
import { quillEditor } from "vue-quill-editor"; //調用編輯器
import "quill/dist/quill.core.css";
import "quill/dist/quill.snow.css";
import "quill/dist/quill.bubble.css";
import { getContext, addContext } from "@/api/user";
export default {
name: "fuwenben",
components: {
quillEditor
},
data() {
return {
content: `<p></p><p><br></p><ol><li><strong><em>Or drag/paste an image here.</em></strong></li><li><strong><em>rerew</em></strong></li><li><strong><em>rtrete</em></strong></li><li><strong><em>tytrytr</em></strong></li><li><strong><em>uytu</em></strong></li></ol>`,
editorOption: {}
};
},
methods: {
onEditorReady(editor) {
// 準備編輯器
},
onEditorBlur() {}, // 失去焦點事件
onEditorFocus() {}, // 獲得焦點事件
onEditorChange() {}, // 内容改變事件
init() {
getContext().then(res => {
this.content = res.data.content;
});
},
submit() {
addContext({ content: this.content }).then(res => {});
}
},
computed: {
editor() {
return this.$refs.myQuillEditor.quill;
}
},
created() {
this.init();
}
};
</script>
頁面效果:
如我編輯以上内容,點選儲存,資料庫存儲如下:可以看到圖檔被轉碼成base64了
重新整理頁面,擷取的編輯框格式正确。
4、說明:回顯問題,可以原樣回顯(在富文本框或者其他地方),也可以隻提取純文字回顯。
<div>
<!--回顯文本-->
<span class="first-marquee">
{{ contentText }}
</span>
<!--原樣回顯-->
<div class="title-popover" v-html="content" v-show="titleShow"></div>
</div>
getContent() {
this.$api[...]({ }).then(data => {
if (data) {
this.content= data.content;
let reg = /<[^>]+>/gi;
this.contentText = this.content.replace(reg, '');
this.contentText = this.contentText.replace(/ /g, '');
}
});
},
三、長度限制:可以使用quill自帶的方法。其中,圖檔會被當成一個字元,換行也會被當成一個字元,當長度超過最大長度時,輸入不了了,光标不可以再往後移動;複制進去的文本,長度超出的會自動删除
<el-form-item label="内容:" prop="messageContent" :required="true">
<quill-editor
class="ql-editor-class"
v-model="formValidate.messageContent"
:options="editorOption"
@change="onEditorChange($event)"
></quill-editor>
<span class="wordNumber">{{ TiLength }}/{{ maxLength }}</span>
</el-form-item>
data(){
return{
TiLength: 0,
maxLength: 100,
}
}
//富文本框内容長度限制
onEditorChange(e) {
e.quill.deleteText(this.maxLength, 4);
if (this.formValidate.content == '') {
this.TiLength = 0;
} else {
this.TiLength = e.quill.getLength() - 1;
}
},
效果:
四、工具欄自定義:quill支援自定義工具欄,可以删除一些不需要的配置,也可以修改某些配置。
<quill-editor
class="ql-editor-class"
v-model="formValidate.messageContent"
:options="editorOption"
></quill-editor>
之後按照規則覆寫editorOption即可。
editorOption: {//配置工具欄
modules: {
toolbar: [
['bold', 'italic', 'underline', 'strike'], // 加粗,斜體,下劃線,删除線
['blockquote', 'code-block'], //引用,代碼塊
[{ 'header': 1 }, { 'header': 2 }], // 幾級标題
[{ 'list': 'ordered'}, { 'list': 'bullet' }], // 有序清單,無序清單
[{ 'script': 'sub'}, { 'script': 'super' }], // 下角标,上角标
[{ 'indent': '-1'}, { 'indent': '+1' }], // 縮進
[{ 'direction': 'rtl' }], // 文字輸入方向
[{ 'size': ['45px','60px','90px'] }], // 字型大小
[{ 'header': [1, 2, 3, 4, 5, 6, false] }],// 标題
[{ 'color': [] }, { 'background': [] }], // 顔色選擇
[{ 'font': ['SimSun', 'SimHei','Microsoft-YaHei','KaiTi','FangSong','Arial'] }],// 字型
[{ 'align': [] }], // 居中
['clean'] // 清除樣式
]
}
// 背景顔色 - background
// 加粗- bold
// 顔色 - color
// 字型 - font
// 内聯代碼 - code
// 斜體 - italic
// 連結 - link
// 大小 - size
// 删除線 - strike
// 上标/下标 - script
// 下劃線 - underline
// 引用- blockquote
// 标題 - header
// 縮進 - indent
// 清單 - list
// 文本對齊 - align
// 文本方向 - direction
// 代碼塊 - code-block
// 公式 - formula
// 圖檔 - image
// 視訊 - video
// 清除字型樣式- clean
}
}
如我去掉了視訊、清楚樣式等幾個工具,且把原來的字型
拓展了一下:
代碼如下:
<!-- @format -->
<template>
<div>
<div class="container">
<el-form ref="formValidate" :model="formValidate" :rules="ruleValidate" label-position="left" label-width="100px">
<el-form-item label="内容:" prop="messageContent" :required="true">
<quill-editor
class="ql-editor-class"
v-model="formValidate.messageContent"
:options="editorOption"
></quill-editor>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
import { quillEditor, Quill } from 'vue-quill-editor';
import 'quill/dist/quill.core.css';
import 'quill/dist/quill.snow.css';
import 'quill/dist/quill.bubble.css';
//設定字型大小
let fontSizeStyle = Quill.import('attributors/style/size'); //引入這個後會把樣式寫在style上
fontSizeStyle.whitelist = ['12px', '14px', '16px'];
Quill.register(fontSizeStyle, true);
//設定字型樣式
let Font = Quill.import('attributors/style/font'); //引入這個後會把樣式寫在style上
let fonts = [false, 'SimSun', 'SimHei', 'Microsoft-YaHei', 'KaiTi', 'FangSong', 'Arial'];
Font.whitelist = fonts; //将字型加入到白名單
Quill.register(Font, true);
export default {
data() {
return {
// 表單資料
formValidate: {
messageContent: '',
},
// 表單驗證
ruleValidate: {
messageContent: [{ required: true, message: '消息内容不能為空!' }]
},
//富文本框自定義工具欄
editorOption: {
modules: {
toolbar: [
['bold', 'italic', 'underline'], // 加粗,斜體,下劃線
[{ header: 1 }, { header: 2 }], // 幾級标題
[{ list: 'ordered' }, { list: 'bullet' }], // 有序清單,無序清單
[{ script: 'sub' }, { script: 'super' }],
[{ indent: '-1' }, { indent: '+1' }], // 縮進
[{ direction: 'rtl' }], // 文字輸入方向
[{ color: [] }, { background: [] }], // 顔色選擇
[{ size: ['12px', '14px', '16px']}], // 字型大小
[{ header: [1, 2, 3, 4, 5, 6, false] }], // 标題
[{ font: ['SimSun', 'SimHei', 'Microsoft-YaHei', 'KaiTi', 'FangSong', 'Arial'] }], // 字型
[{ align: [] }], // 對齊方式
['image', 'link'] //圖檔,連結
]
}
},
};
},
}
<!--富文本框-->
<style scoped>
.quill-editor >>> .ql-container {
min-height: 300px;
}
/** 縮小工具欄下拉框的間距*/
.quill-editor >>> .ql-snow .ql-picker-options .ql-picker-item {
cursor: pointer;
display: block;
padding-bottom: 0px;
padding-top: 0px;
line-height: 30px;
}
/*
這裡一定要寫上,是用來把相關改變的配置在工具欄正常顯示
如果不寫,字型樣式的下拉會重複顯示Sans Serif,字型大小的下拉會重複顯示Normal
*/
.quill-editor >>> .ql-picker.ql-font .ql-picker-label[data-value='SimSun']::before,
.quill-editor >>> .ql-picker.ql-font .ql-picker-item[data-value='SimSun']::before {
content: '宋體';
font-family: 'SimSun' !important;
}
.quill-editor >>> .ql-picker.ql-font .ql-picker-label[data-value='SimHei']::before,
.quill-editor >>> .ql-picker.ql-font .ql-picker-item[data-value='SimHei']::before {
content: '黑體';
font-family: 'SimHei';
}
.quill-editor >>> .ql-picker.ql-font .ql-picker-label[data-value='Microsoft-YaHei']::before,
.quill-editor >>> .ql-picker.ql-font .ql-picker-item[data-value='Microsoft-YaHei']::before {
content: '微軟雅黑';
font-family: 'Microsoft YaHei';
}
.quill-editor >>> .ql-picker.ql-font .ql-picker-label[data-value='KaiTi']::before,
.quill-editor >>> .ql-picker.ql-font .ql-picker-item[data-value='KaiTi']::before {
content: '楷體';
font-family: 'KaiTi' !important;
}
.quill-editor >>> .ql-picker.ql-font .ql-picker-label[data-value='FangSong']::before,
.quill-editor >>> .ql-picker.ql-font .ql-picker-item[data-value='FangSong']::before {
content: '仿宋';
font-family: 'FangSong';
}
.quill-editor >>> .ql-snow .ql-picker.ql-size .ql-picker-label[data-value='12px']::before,
.quill-editor >>> .ql-snow .ql-picker.ql-size .ql-picker-item[data-value='12px']::before {
content: '12px';
}
.quill-editor >>> .ql-snow .ql-picker.ql-size .ql-picker-label[data-value='14px']::before,
.quill-editor >>> .ql-snow .ql-picker.ql-size .ql-picker-item[data-value='14px']::before {
content: '14px';
}
.quill-editor >>> .ql-snow .ql-picker.ql-size .ql-picker-label[data-value='16px']::before,
.quill-editor >>> .ql-snow .ql-picker.ql-size .ql-picker-item[data-value='16px']::before {
content: '16px';
}
</style>
五、自定義圖檔上傳:quill自己的圖檔上傳,會被轉成base64,當圖檔過大的時候會導緻字段過長、渲染過慢。方法是禁止quill的圖檔上傳功能,點選圖檔工具時觸發自定義圖檔上傳元件點選事件,然後調用自定義的接口上傳并擷取url,将url寫入到富文本框中即可。隻要圖檔位址正确,在富文本框中會顯示圖檔,而内容實際為url,長度也隻占一個字元。
<div style="display:none" ref="articalImg" class="articalImg">
<span>
<label class="button text-overflow" for="messageFile">
<span>上傳圖檔<i class="el-icon-plus"></i></span>
</label>
<form id="messageFileForm" class="upload-form">
<input
ref="messageFile"
@change="uploadFile($event)"
type="file"
id="messageFile"
style="position:absolute;clip:rect(0 0 0 0);"
accept="image/png"
class="upload-label"
/>
</form>
</span>
</div>
<quill-editor
class="ql-editor-class"
ref="myQuillEditor"
v-model="formValidate.messageContent"
:options="editorOption"
@change="onEditorChange($event)"
></quill-editor>
<span class="word-number">{{ TiLength }}/{{ contentMaxLength }}</span>
<script>
import { quillEditor, Quill } from 'vue-quill-editor';
import 'quill/dist/quill.core.css';
import 'quill/dist/quill.snow.css';
import 'quill/dist/quill.bubble.css';
//設定字型大小
let fontSizeStyle = Quill.import('attributors/style/size'); //引入這個後會把樣式寫在style上
fontSizeStyle.whitelist = [
'14px',
'16px',
'18px',
'20px',
'28px',
'36px',
'42px'
];
Quill.register(fontSizeStyle, true);
//設定字型樣式
let Font = Quill.import('attributors/style/font'); //引入這個後會把樣式寫在style上
let fonts = [false, 'SimSun', 'SimHei', 'Microsoft-YaHei', 'KaiTi', 'FangSong', 'Arial'];
Font.whitelist = fonts; //将字型加入到白名單
Quill.register(Font, true);
//工具欄自定義
const toolbarOptions = [
['bold', 'italic', 'underline'], // 加粗,斜體,下劃線
[{ header: 1 }, { header: 2 }], // 幾級标題
[{ list: 'ordered' }, { list: 'bullet' }], // 有序清單,無序清單
[{ indent: '-1' }, { indent: '+1' }], // 縮進
[{ direction: 'rtl' }], // 文字輸入方向
[{ color: [] }, { background: [] }], // 顔色選擇
[
{
size: ['14px', '16px', '18px', '20px', '28px', '36px', '42px']
}
], // 字型大小
[{ header: [1, 2, 3, 4, 5, 6, false] }], // 标題
[{ font: ['SimSun', 'SimHei', 'Microsoft-YaHei', 'KaiTi', 'FangSong', 'Arial'] }], // 字型
[{ align: [] }], // 對齊方式
['image', 'link'] //圖檔,連結
];
export default {
name: 'sendMessage',
mixins: [BreadCrumb],
data() {
return {
// 表單資料
formValidate: {
//内容
messageContent: ''
},
//富文本框自定義工具欄
editorOption: {
modules: {
toolbar: {
container: toolbarOptions, // 工具欄
handlers: {
image: function(value) {
if (value) {
// this.$refs.articalImg.click()
document.querySelector('.articalImg .upload-label').click();
} else {
this.quill.format('image', false);
}
}
}
}
}
},
TiLength: 0,
contentMaxLength: 1000
};
},
methods: {
//富文本框内容長度限制
onEditorChange(e) {
e.quill.deleteText(this.contentMaxLength, 4);
if (this.formValidate.content == '') {
this.TiLength = 0;
} else {
this.TiLength = e.quill.getLength() - 1;
}
},
uploadFile(ev) {
var fileList = ev.target.files;
let file = fileList[0];
let formData = new FormData();
formData.append('file', file);
//清空本次選擇的檔案
this.$refs.messageFile.value = '';
this.$api['file/upload'](formData, {
headers: {
'Content-Type': 'multipart/form-data'
},
timeout: 300000
}).then(data => {
if (data) {
//插入到富文本框中
let quill = this.$refs.myQuillEditor.quill;
console.info(quill);
// 擷取光标所在位置
let length = quill.getSelection().index;
// 插入圖檔,res為伺服器傳回的圖檔連結位址
quill.insertEmbed(
length,
'image',
data.url
);
// 調整光标到最後
quill.setSelection(length + 1);
}
});
}
}
};
</script>
如圖: