官网地址:https://vueup.github.io/vue-quill/
效果图
1、安装
npm install @vueup/vue-quill@alpha --save
npm install quill-image-extend-module --save
npm install quill-image-resize-module -- save
2、在vue.config.js中添加配置,否则quill-image-resize-module会出现Cannot read property ‘imports‘ of undefined报错问题
var webpack = require('webpack');
module.exports = {
configureWebpack: {
plugins: [
new webpack.ProvidePlugin({
'window.Quill': 'quill/dist/quill.js',
'Quill': 'quill/dist/quill.js'
}),
]
}
}
3、创建quillTool.js(用于添加超链接、视频)
import { Quill } from '@vueup/vue-quill'
// 源码中是import直接倒入,这里要用Quill.import引入
const BlockEmbed = Quill.import('blots/block/embed')
const Link = Quill.import('formats/link')
const ATTRIBUTES = ['height', 'width']
class quillTool extends BlockEmbed {
static create(value) {
const node = super.create(value)
// 添加video标签所需的属性
node.setAttribute('controls', 'controls')
node.setAttribute('type', 'video/mp4')
node.setAttribute('src', this.sanitize(value))
return node
}
static formats(domNode) {
return ATTRIBUTES.reduce((formats, attribute) => {
if (domNode.hasAttribute(attribute)) {
formats[attribute] = domNode.getAttribute(attribute)
}
return formats
}, {})
}
static sanitize(url) {
return Link.sanitize(url)
}
static value(domNode) {
return domNode.getAttribute('src')
}
format(name, value) {
if (ATTRIBUTES.indexOf(name) > -1) {
if (value) {
this.domNode.setAttribute(name, value)
} else {
this.domNode.removeAttribute(name)
}
} else {
super.format(name, value)
}
}
html() {
const { video } = this.value()
return `<a href="${video}">${video}</a>`
}
}
quillTool.blotName = 'video' // 这里不用改,楼主不用iframe,直接替换掉原来,如果需要也可以保留原来的,这里用个新的blot
quillTool.className = 'ql-video'
quillTool.tagName = 'video' // 用video标签替换iframe
export default quillTool
4、完整代码
<template>
<QuillEditor :options="editorOption" contentType="html" ref="QuillEditor" class="ql-editor" />
</template>
<script>
const QEIheaders = {
n: Math.ceil(Math.random() * 10),
t: Math.floor(new Date().getTime() / 1000),
c: axios.defaults.headers['authKey'] ? axios.defaults.headers['authKey'] : 'undefined',
s: '',
}
QEIheaders.s = md5(`c=${QEIheaders.c}&t=${QEIheaders.t}&n=${QEIheaders.n}&key=yourKey`);
import { QuillEditor, Quill } from '@vueup/vue-quill'
import { ImageExtend, QuillWatch } from 'quill-image-extend-module'
import ImageResize from 'quill-image-resize-module';
import quillTool from '@/utils/quillTool'
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
import md5 from 'js-md5'
import axios from 'axios'
const fontSize = ['12px', '13px', '14px', '15px', '16px', '18px', '20px', '24px', '28px', '32px', '36px'];
Quill.imports['attributors/style/size'].whitelist = fontSize;
Quill.register(quillTool, true)
Quill.register(Quill.imports['attributors/style/size'], true);
Quill.register('modules/imageResize', ImageResize);
Quill.register('modules/ImageExtend', ImageExtend)
// 工具栏配置
const toolbarOptions = [
['bold', 'italic', 'underline', 'strike'], // 加粗 斜体 下划线 删除线
["blockquote", "code-block"], // 引用
[{ list: 'ordered' }, { list: 'bullet' }], // 有序、无序列表
[{ script: "sub" }, { script: "super" }], // 上标/下标
[{ indent: '-1' }, { indent: '+1' }], // 缩进
[{ direction: 'rtl' }], // 文本方向
[{ size: fontSize }], // 字体大小
[{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题
[{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色
[{ font: [] }], // 字体种类
[{ align: [] }], // 对齐方式
['clean'], // 清除文本格式
['link', 'image', 'video'] // 链接、图片、视频
]
export default {
mixins: [ tool ],
***ponents: { QuillEditor },
data() {
return {
editorOption: {
theme: 'snow',
placeholder: '请输入',
modules: {
imageResize: {}, // 声明imageResize模块,必须的!
ImageExtend: {
name: 'file_name', // 参数名
action: window.BASE_URL + '/upload/image', // 服务器地址,如果为空则采用base64插入图片
headers: xhr => { // 设置请求头参数
for(let item of Object.keys(QEIheaders)){
xhr.setRequestHeader(item, QEIheaders[item])
}
},
response: res => {
return `res.data.filepath`
},
size: 8, // 图片不能超过8M
sizeError: () => {
this.$message.error('粘贴图片大小不能超过8MB!')
}
},
toolbar: {
container: toolbarOptions,
handlers: {
image: function(value) {
QuillWatch.emit(this.quill.id)
},
link: function(value) {
if (value) {
var href = prompt('请输入链接地址:')
this.quill.format('link', href)
} else {
this.quill.format('link', false)
}
},
video: function(value) {
if (value) {
var href = prompt('请输入视频链接:')
this.quill.format('video', href)
} else {
this.quill.format('video', false)
}
}
}
}
}
},
}
},
mounted(){
// 解决富文本编辑器自动获取焦点问题
//document.querySelector('.FSDcontent').scrollTo(0,0) // 设置相关容器滚动到顶部
this.$refs.QuillEditor.getQuill().enable(false) // 禁用
this.$nextTick(() => {
this.$refs.QuillEditor.getQuill().enable(true) // 启用
},100)
},
}
</script>
<style>
.ql-snow .ql-container {
height: 300px;
line-height: normal;
width: auto;
}
.ql-snow span.ql-size {
max-width: 80px !important;
}
.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"] {
left: 0 !important;
}
.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="12px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="12px"]::before {
content: "12px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="13px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="13px"]::before {
content: "13px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="14px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="14px"]::before {
content: "14px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="16px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="16px"]::before {
content: "16px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="17px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="17px"]::before {
content: "17px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="18px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="18px"]::before {
content: "18px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="19px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="19px"]::before {
content: "19px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="20px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="20px"]::before {
content: "20px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="21px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="21px"]::before {
content: "21px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="22px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="22px"]::before {
content: "22px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="23px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="23px"]::before {
content: "23px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="24px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="24px"]::before {
content: "24px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="25px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="25px"]::before {
content: "25px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="26px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="26px"]::before {
content: "26px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="27px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="27px"]::before {
content: "27px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="28px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="28px"]::before {
content: "28px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="29px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="29px"]::before {
content: "29px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="30px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="30px"]::before {
content: "30px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="31px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="31px"]::before {
content: "31px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="32px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="32px"]::before {
content: "32px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="33px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="33px"]::before {
content: "33px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="34px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="34px"]::before {
content: "34px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="35px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="35px"]::before {
content: "35px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="36px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="36px"]::before {
content: "36px";
}
.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>