需求一 图片上传
就是要一个富文本编辑器,然后有图片上传功能,因为vue-quill-editor是将图片转为base64编码,所以当图片比较大时,提交后台时参数过长,导致提交失败。
将图片先上传至服务器,再将图片链接插入到富文本中
图片上传的话可以使用element或者iview,这里我以iview举例
图片上传区域要隐藏,自定义vue-quill-editor的图片上传,点击图片上传时调用iview或者element的图片上传,上传成功后在富文本编辑器中显示图片
零、安装使用
npm install vue-quill-editor --save
main.js
import VueQuillEditor from 'vue-quill-editor'
Vue.use(VueQuillEditor);
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
一、自定义vue-quill-editor图片上传
html:
<quill-editor
v-model="content"
:options="editorOption"
ref="QuillEditor">
</quill-editor>
<script>
// 工具栏配置
const toolbarOptions = [
['bold', 'italic', 'underline', 'strike'], // toggled buttons
['blockquote', 'code-block'],
[{'header': 1}, {'header': 2}], // custom button values
[{'list': 'ordered'}, {'list': 'bullet'}],
[{'script': 'sub'}, {'script': 'super'}], // superscript/subscript
[{'indent': '-1'}, {'indent': '+1'}], // outdent/indent
[{'direction': 'rtl'}], // text direction
[{'size': ['small', false, 'large', 'huge']}], // custom dropdown
[{'header': [1, 2, 3, 4, 5, 6, false]}],
[{'color': []}, {'background': []}], // dropdown with defaults from theme
[{'font': []}],
[{'align': []}],
['link', 'image', 'video'],
['clean'] // remove formatting button
export default {
data () {
return {
content: '',
editorOption: {
modules: {
toolbar: {
container: toolbarOptions, // 工具栏
handlers: {
'image': function (value) {
if (value) {
alert('自定义图片')
} else {
this.quill.format('image', false);
</script>
二、调用element或iview图片上传组件
html:
<Upload
:show-upload-list="false"
:on-success="handleSuccess"
:format="['jpg','jpeg','png','gif']"
:max-size="2048"
multiple
action="/file/upload"
<Button icon="ios-cloud-upload-outline" ></Button>
</Upload>
<quill-editor
v-model="content"
:options="editorOption"
ref="QuillEditor">
</quill-editor>
.ivu-upload {
display: none;
data () {
return {
content: '',
editorOption: {
modules: {
toolbar: {
container: toolbarOptions, // 工具栏
handlers: {
'image': function (value) {
if (value) {
// 调用iview图片上传
document.querySelector('.ivu-upload .ivu-btn').click()
} else {
this.quill.format('image', false);
methods: {
handleSuccess (res) {
// 获取富文本组件实例
let quill = this.$refs.QuillEditor.quill
// 如果上传成功
if (res) {
// 获取光标所在位置
let length = quill.getSelection().index;
// 插入图片,res为服务器返回的图片链接地址
quill.insertEmbed(length, 'image', res)
// 调整光标到最后
quill.setSelection(length + 1)
} else {
// 提示信息,需引入Message
Message.error('图片插入失败')
三、假如需要多个富文本编辑器
可能不止一处地方用到,比如添加完成后还有编辑功能,那就复制一份文件上传和富文本编辑:两个富文本用不同的ref标记,在各自配置中调用各自的文件上传;文件上传成功也使用不同的方法名称,里面调用各自的富文本编辑器。
重点:富文本和文件上传不管使用类名还是什么方式区分的,这两处地方都要和之前区分开。
需求二 文件上传
和图片上传相同,不同的是上传文件。解决的思路也相同:在vue-quill-editor中自定义按钮,点击使用iView的文件上传,然后将地址赋值给a标签的href属性,插入到富文本光标处。
一、自定义编辑器附件上传
我想通过download属性自定义文件下载名称,但是两种方式都失败了,可以忽略相关代码。以下是为富文本自定义插入a链接
import { Quill } from 'vue-quill-editor';
// 自定义插入a链接
var Link = Quill.import('formats/link');
class FileBlot extends Link { // 继承Link Blot
static create(value) {
let node = undefined
if (value&&!value.href){ // 适应原本的Link Blot
node = super.create(value);
else{ // 自定义Link Blot
node = super.create(value.href);
// node.setAttribute('download', value.innerText); // 左键点击即下载
node.innerText = value.innerText;
node.download = value.innerText;
return node;
FileBlot.blotName = 'link';
FileBlot.tagName = 'A';
Quill.register(FileBlot);
配置工具栏,添加了一个upload
,其余不需要的都可以去掉
<script>
// 自定义插入a链接
// ...
// 工具栏配置
const toolbarOptions = [
['bold', 'italic', 'underline', 'strike'], // toggled buttons
['blockquote', 'code-block'],
[{'header': 1}, {'header': 2}], // custom button values
[{'list': 'ordered'}, {'list': 'bullet'}],
[{'script': 'sub'}, {'script': 'super'}], // superscript/subscript
[{'indent': '-1'}, {'indent': '+1'}], // outdent/indent
[{'direction': 'rtl'}], // text direction
[{'size': ['small', false, 'large', 'huge']}], // custom dropdown
[{'header': [1, 2, 3, 4, 5, 6, false]}],
[{'color': []}, {'background': []}], // dropdown with defaults from theme
[{'font': []}],
[{'align': []}],
['link', 'image', 'upload'],
['clean'] // remove formatting button
export default {
data () {
return {
content: '',
editorOption: {
modules: {
toolbar: {
container: toolbarOptions, // 工具栏
handlers: {
'image': ((value) => {
if (value) {
alert('自定义图片')
} else {
this.quill.format('image', false);
'upload': ((value) => {
if (value) {
alert('自定义文件上传')
</script>
自定义文件上传的图标样式
.ql-snow.ql-toolbar .ql-upload{
background: url("../assets/images/icon-upload.svg");
background-size: 16px 16px;
background-position: center center;
background-repeat:no-repeat;
/*background: red;*/
二、调用element或iview上传组件
为两个图片上传分别定义了类名,以做调用时的区分。
<!-- 上传图片 -->
<Upload
:show-upload-list="false"
:on-success="handleSuccess"
:format="messageAllData.imgFileType"
:before-upload="handleBeforeUpload"
type="drag"
:action="api.imgManage"
class="uploadImage">
</Upload>
<!-- 上传文件 -->
<Upload :show-upload-list="false"
:on-success="handleFileSuccess"
:before-upload="handleFileBeforeUpload"
type="drag"
:action="api.imgManage"
class="uploadFile">
</Upload>
修改工具栏配置,当点击富文本时,调用相应的上传组件
handlers: {
'image': (value => {
if (value) {
document.querySelector('.uploadImage input').click()
}else {
this.quill.format('image', false);
'upload': (value => {
if (value) {
document.querySelector('.uploadFile input').click()
这两个文件上传都要隐藏
.uploadImage,
.uploadFile{
width: 0;
height: 0;
display: none;
下面是插入图片和文件的方法
<quill-editor
v-model="content"
:options="editorOption"
ref="QuillEditor">
</quill-editor>
methods: {
// 图片
handleSuccess (res, file) {
let quill = this.$refs.QuillEditor.quill
let length = quill.getSelection().index;
quill.insertEmbed(length, 'image', res)
quill.setSelection(length + 1)
// 文件
handleFileSuccess (res, file) {
let fileNameLength = file.name.length
// 插入链接
let quill = this.$refs.QuillEditor.quill
let length = quill.getSelection().index;
quill.insertEmbed(length, 'link', {href:res, innerText:file.name}, "api")
quill.setSelection(length + fileNameLength)
bug及优化
一、回车光标不显示
不知道为什么,百度都搜不到,好像只有我出现了这个问题,最后通过监听回车,手动换行并在换行后加了一个空格,因为没有内容的时候光标不显示,然后把光标向前调一个位置,移到空格前面。
const bindings = {
custom: {
key: 13,
handler: function(range, context) {
this.quill.insertText(range.index, '\n ');
setTimeout(() => {
let nowRange = this.quill.getSelection().index - 1
this.quill.setSelection(nowRange)
}, 0)
export default {
data () {
return {
content: '',
editorOption: {
modules: {
keyboard: {
bindings: bindings
toolbar: {
// ...
二、给菜单栏添加中文标题title
// 标题
const titleConfig = {
'ql-bold':'加粗',
'ql-color':'颜色',
'ql-font':'字体',
'ql-code':'插入代码',
'ql-italic':'斜体',
'ql-link':'添加链接',
'ql-background':'背景颜色',
'ql-size':'字体大小',
'ql-strike':'删除线',
'ql-script':'上标/下标',
'ql-underline':'下划线',
'ql-blockquote':'引用',
'ql-header':'标题',
'ql-indent':'缩进',
'ql-list':'列表',
'ql-align':'文本对齐',
'ql-direction':'文本方向',
'ql-code-block':'代码块',
'ql-formula':'公式',
'ql-image':'图片',
'ql-video':'视频',
'ql-clean':'清除字体样式',
'ql-upload':'文件'
methods: {
addQuillTitle () {
const oToolBar = document.querySelector('.ql-toolbar'),
aButton = oToolBar.querySelectorAll('button'),
aSelect = oToolBar.querySelectorAll('select');
aButton.forEach(function(item){
if(item.className === 'ql-script'){
item.value === 'sub' ? item.title = '下标': item.title = '上标';
}else if(item.className === 'ql-indent'){
item.value === '+1' ? item.title ='向右缩进': item.title ='向左缩进';
}else{
item.title = titleConfig[item.classList[0]];
aSelect.forEach(function(item){
item.parentNode.title = titleConfig[item.classList[0]];
mounted () {
this.addQuillTitle()
有个需要注意的地方,按上面的方法使用后,确实有效,但是字体颜色和背景颜色的提示都变成了背景颜色,然后修改了标题栏的配置,提示才彼此对应。
const toolbarOptions = [
[{'color': []}, {'background': []}],
const toolbarOptions = [
[{'color': []}],
[{'background': []}],
文档及其他参考
官方文档参考vue-quill-editor
图片及文件的验证参考
图片样式限制宽高和边距就可以了,在富文本编辑器里面可以回车换行;
可以另写一个style标签
里面写上样式,也可以在原有的样式中通过深度选择器来写样式
<div v-html="editorContent" class="richTextStyle"></div>
<style>
/*富文本图片样式 */
.richTextStyle img {
max-width: 720px;
margin:10px;
/*富文本文字溢出不换行样式 */
.richTextStyle p, .richTextStyle sup, .richTextStyle strong,
.richTextStyle em, .richTextStyle u,
.richTextStyle s, .richTextStyle blockquote,
.richTextStyle h1, .richTextStyle h2, .richTextStyle h3,
.richTextStyle h4, .richTextStyle h5, .richTextStyle h6,
.richTextStyle li, .richTextStyle sub, .richTextStyle a {
word-break: break-all;
</style>