1.安装
vue-quill-editor github地址:https://github.com/surmon-china/vue-quill-editor
官方文档地址:https://www.npmjs.com/package/vue-quill-editor
基于Quill、适用于Vue2的富文本编辑器,支持服务端渲染和单页应用
npm install vue-quill-editor --save
安装图片拖动和变化大小的插件,需要在vue.config.js引入webpack中配置 不配置会出错,具体链接(https://github.com/kensnyder/quill-image-resize-module/issues/54)
plugins: [
new webpack.ProvidePlugin({
'window.Quill': 'quill/dist/quill.js',
'Quill': 'quill/dist/quill.js',
}),
]
2.局部引入
import Vue from 'vue'
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
import { quillEditor } from 'vue-quill-editor'
import Quill from 'quill'
import { ImageDrop } from 'quill-image-drop-module' // 图片拖动插件
import ImageResize from 'quill-image-resize-module' // 图片设置大小插件
Quill.register('modules/imageDrop', ImageDrop) // 图片拖动插件
Quill.register('modules/imageResize', ImageResize) // 图片设置大小插件
// 鼠标移入显示title文字
import { addQuillTitle } from '@/assets/quill-title.js'
Vue.use(quillEditor)
// 图片上传接口
import { richTextImgUpload } from '@/api/uploadFIle'
3.工具栏配置
// 工具栏配置
const toolbarOptions = [
['bold', 'italic', 'underline', 'strike'], // 加粗,斜体,下划线,删除线
['blockquote', 'code-block'], // 引用,代码块
[{ header: 1 }, { header: 2 }], // 标题,键值对的形式;1、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'], // 清除字体样式
['image', 'video'] // 上传图片、上传视频
]
/* 富文本编辑图片上传配置*/
const uploadConfig = {
action: '', // 必填参数 图片上传地址
methods: 'POST', // 必填参数 图片上传方式
token: '', // 可选参数 如果需要token验证,假设你的token有存放在sessionStorage
name: 'img', // 必填参数 文件的参数名
size: 500, // 可选参数 图片大小,单位为Kb, 1M = 1024Kb
accept: 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon' // 可选 可上传的图片格式
}
4.data中初始化配置
data() {
return {
content: '',
videoOrimg:true,
editorOption: {
placeholder: '请输入...',
modules: {
imageResize: {}, //图片拖动设置大小
imageDrop: true, // 图片拖动
toolbar: {
container: toolbarOptions, //工具栏
handlers: {
image: (value) => {
if (value) {
// 这是是为了获取你当前点击的那个富文本框
this.videoOrimg = true
document.querySelectorAll('.upload-demo input')[0].click()
} else {
this.quill.format('image', false)
}
},
video: (value) => {
if (value) {
// 这是是为了获取你当前点击的那个富文本框
this.videoOrimg = false
document.querySelectorAll('.upload-demo input')[0].click()
} else {
this.quill.format('image', false)
}
}
}
}
},
theme: 'snow'
}
}
},
5.模板中使用
<template>
<div>
<el-upload
class="upload-demo"
action="#"
name="file"
:http-request="requestUpload"
:before-upload="beforeAvatarUpload"
:on-success="handleSuccess"
multiple
/>
<quill-editor ref="toref" v-model="content" :options="editorOption" />
</div>
</template>
6.图片上传(视频上传同理)
因为是前后端分离的,富文本上传图片要单独上传,在content中替换为后端返回图片地址。
methods: {
// 覆盖默认的上传行为
requestUpload(params) {
const formData = new FormData()
formData.append('img', params.file)
// 图片
if(this.videoOrimg){
richTextImgUpload(formData).then((res) => {
params.onSuccess(res)
})
}else{
richTextVideoUpload(formData).then((res) => {
params.onSuccess(res)
})
}
},
// 判断图片后缀
beforeAvatarUpload(file) {
console.log(file,'dasda')
const isJPG = file.type === 'image/jpeg' || 'image/png'
const isVideo = file.type === 'video/mp4'
const isLt2M = file.size / 1024 / 1024 < 10
// 图片
if(this.videoOrimg){
if (!isJPG) {
this.$message.error('上传图片只能是 JPG , PNG格式!')
}
if (!isLt2M) {
this.$message.error('上传图片大小不能超过 10MB!')
}
return isJPG && isLt2M
}else{
if(!isVideo){
this.$message.error('上传视频只能是 MP4格式!')
}
return isVideo
}
},
// 上传成功
handleSuccess(res, file) {
if (res.error == 0) {
// 动态添加 ref 通过 eval () 去执行
const toeval = this.$refs.toref.quill
// eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码。
// 获取光标所在位置
const length = toeval.selection.savedRange.index
// 插入图片,res为服务器返回的图片链接地址
// 图片
if(this.videoOrimg){
toeval.insertEmbed(
length,
'image',
process.env.VUE_APP_BASE_API_ORIGIN + res.data.imgscr
)
}else{
toeval.insertEmbed(
length,
'video',
process.env.VUE_APP_BASE_API_ORIGIN + res.data.imgscr
)
}
// 调整光标到最后
toeval.setSelection(length + 1)
}
},
// 将内容上传给父组件
postContent() {
this.$emit('getContent', this.content)
},
},
7.quill-title.js内容
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':'清除字体样式'
};
export function 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]];
});
}
8.完整代码
<template>
<div>
<el-upload
class="upload-demo"
action="#"
name="file"
:http-request="requestUpload"
:before-upload="beforeAvatarUpload"
:on-success="handleSuccess"
multiple
/>
<quill-editor ref="toref" v-model="content" :options="editorOption" />
</div>
</template>
<script>
import Vue from 'vue'
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
import { quillEditor } from 'vue-quill-editor'
import Quill from 'quill'
import { ImageDrop } from 'quill-image-drop-module' // 图片拖动插件
import ImageResize from 'quill-image-resize-module' // 图片设置大小插件
Quill.register('modules/imageDrop', ImageDrop) // 图片拖动插件
Quill.register('modules/imageResize', ImageResize) // 图片设置大小插件
// 鼠标移入显示title文字
import { addQuillTitle } from '@/assets/quill-title.js'
Vue.use(quillEditor)
// 图片上传接口
import { richTextImgUpload, richTextVideoUpload } from '@/api/uploadFIle'
// 工具栏配置
const toolbarOptions = [
['bold', 'italic', 'underline', 'strike'], // 加粗,斜体,下划线,删除线
['blockquote', 'code-block'], // 引用,代码块
[{ header: 1 }, { header: 2 }], // 标题,键值对的形式;1、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'], // 清除字体样式
['image', 'video'] // 上传图片、上传视频
]
/* 富文本编辑图片上传配置*/
const uploadConfig = {
action: '', // 必填参数 图片上传地址
methods: 'POST', // 必填参数 图片上传方式
token: '', // 可选参数 如果需要token验证,假设你的token有存放在sessionStorage
name: 'img', // 必填参数 文件的参数名
size: 500, // 可选参数 图片大小,单位为Kb, 1M = 1024Kb
accept: 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon' // 可选 可上传的图片格式
}
export default {
components: {
quillEditor
},
props: {
contents:String,
default:''
},
data() {
return {
content: '',
videoOrimg:true,
editorOption: {
placeholder: '请输入...',
modules: {
imageResize: {}, //图片拖动设置大小
imageDrop: true, // 图片拖动
toolbar: {
container: toolbarOptions, //工具栏
handlers: {
image: (value) => {
if (value) {
// 这是是为了获取你当前点击的那个富文本框
this.videoOrimg = true
document.querySelectorAll('.upload-demo input')[0].click()
} else {
this.quill.format('image', false)
}
},
video: (value) => {
if (value) {
// 这是是为了获取你当前点击的那个富文本框
this.videoOrimg = false
document.querySelectorAll('.upload-demo input')[0].click()
} else {
this.quill.format('image', false)
}
}
}
}
},
theme: 'snow'
}
}
},
mounted() {
addQuillTitle()
},
methods: {
// 覆盖默认的上传行为
requestUpload(params) {
const formData = new FormData()
formData.append('img', params.file)
// 图片
if(this.videoOrimg){
richTextImgUpload(formData).then((res) => {
params.onSuccess(res)
})
}else{
richTextVideoUpload(formData).then((res) => {
params.onSuccess(res)
})
}
},
// 判断图片后缀
beforeAvatarUpload(file) {
console.log(file,'dasda')
const isJPG = file.type === 'image/jpeg' || 'image/png'
const isVideo = file.type === 'video/mp4'
const isLt2M = file.size / 1024 / 1024 < 10
// 图片
if(this.videoOrimg){
if (!isJPG) {
this.$message.error('上传图片只能是 JPG , PNG格式!')
}
if (!isLt2M) {
this.$message.error('上传图片大小不能超过 10MB!')
}
return isJPG && isLt2M
}else{
if(!isVideo){
this.$message.error('上传视频只能是 MP4格式!')
}
return isVideo
}
},
// 上传成功
handleSuccess(res, file) {
if (res.error == 0) {
// 动态添加 ref 通过 eval () 去执行
const toeval = this.$refs.toref.quill
// eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码。
// 获取光标所在位置
const length = toeval.selection.savedRange.index
// 插入图片,res为服务器返回的图片链接地址
// 图片
if(this.videoOrimg){
toeval.insertEmbed(
length,
'image',
process.env.VUE_APP_BASE_API_ORIGIN + res.data.imgscr
)
}else{
toeval.insertEmbed(
length,
'video',
process.env.VUE_APP_BASE_API_ORIGIN + res.data.imgscr
)
}
// 调整光标到最后
toeval.setSelection(length + 1)
}
},
// 将内容上传给父组件
postContent() {
this.$emit('getContent', this.content)
},
},
watch: {
contents:{
immediate: true, // 这句重要
handler (val) {
this.content = this.contents
}
}
}
}
</script>
<style scoped>
.upload-demo {
display: none;
}
</style>
9.预览