vue-quill-editor 全屏设置
components/Editor/fullScreen.js
import Vue from "vue";
const domList = [
{
dom: `<svg t="1637824425355" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10463"><path d="M243.2 780.8v-179.2H153.6v179.2c0 49.28 40.32 89.6 89.6 89.6h179.2v-89.6H243.2zM780.8 153.6h-179.2v89.6h179.2v179.2h89.6V243.2c0-49.28-40.32-89.6-89.6-89.6zM243.2 243.2h179.2V153.6H243.2c-49.28 0-89.6 40.32-89.6 89.6v179.2h89.6V243.2z m537.6 537.6h-179.2v89.6h179.2c49.28 0 89.6-40.32 89.6-89.6v-179.2h-89.6v179.2z" p-id="10464" fill="#000000"></path></svg>`,
tit: "最大化",
id: "maxId",
display: "block",
},
{
dom: `<svg t="1637824296192" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6336"><path d="M341.065143 910.189714v-146.285714c0-53.686857-43.885714-97.572571-97.572572-97.572571h-146.285714a48.786286 48.786286 0 0 0 0 97.499428h146.285714v146.285714a48.786286 48.786286 0 1 0 97.499429 0z m-292.571429-617.910857c0 26.916571 21.796571 48.786286 48.713143 48.786286h146.285714c53.686857 0 97.572571-43.885714 97.572572-97.572572v-146.285714a48.786286 48.786286 0 0 0-97.499429 0v146.285714h-146.285714a48.786286 48.786286 0 0 0-48.786286 48.786286z m910.409143 0a48.786286 48.786286 0 0 0-48.713143-48.786286h-146.285714v-146.285714a48.786286 48.786286 0 1 0-97.499429 0v146.285714c0 53.686857 43.885714 97.572571 97.499429 97.572572h146.285714a48.786286 48.786286 0 0 0 48.713143-48.786286z m0 422.765714a48.786286 48.786286 0 0 0-48.713143-48.713142h-146.285714c-53.686857 0-97.572571 43.885714-97.572571 97.572571v146.285714a48.786286 48.786286 0 1 0 97.499428 0v-146.285714h146.285714a48.786286 48.786286 0 0 0 48.786286-48.786286z" fill="#000000" p-id="6337"></path></svg>`,
tit: "还原",
id: "minId",
display: "none",
},
];
/**@ bing
*2021-11-25 15:58:21
* v-maxWindow: 只针对 vue-quill-editor组件 最大化最小化
*/
Vue.directive("maxWindow", {
//属性名称maxWindow,前面加v- 使用
bind(el, binding, vnode, oldVnode) {
setTimeout(() => {
let dialogHeaderEl = el.querySelector(".ql-toolbar");
domList.map((item) => {
let dom = document.createElement("span");
dom.className = "ql-formats";
dom.innerHTML = `<button title="${item.tit}" style="display:${item.display}" id="${item.id}" type="button" class="ql-max">${item.dom}</button>`;
dialogHeaderEl.appendChild(dom);
});
//最大化
document.getElementById("maxId").onclick = () => {
el.style.width = 100 + "vw";
el.style.height = 100 + "vh";
el.style.position = "fixed";
el.style.top = 0;
el.style.left = 0;
el.style.zIndex = 9999;
el.style.background = "white";
document.getElementById("maxId").style.display = "none";
document.getElementById("minId").style.display = "block";
};
//最小化
document.getElementById("minId").onclick = () => {
el.style.width = "auto";
el.style.height = "auto";
el.style.position = "inherit";
document.getElementById("maxId").style.display = "block";
document.getElementById("minId").style.display = "none";
};
}, 0);
},
});
组件
components/Editor/newEditor.vue
<template>
<!--富文本编辑器-->
<div class="app-container customEditor">
<div class="avatar">
<!-- 图片上传组件辅助-->
<el-upload id="quill-upload" class="avatar-uploader" :action="serverUrl" ref="annexUpload" name="file" :limit="2"
list-type="picture" :on-exceed="handleExceed" :on-error="handleError" :on-success="uploadAttachment"
:file-list="uploadList" :with-credentials="true" :auto-upload="true" :headers="headers">
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>
</el-upload>
<!-- 视频上传组件辅助-->
<el-upload class="video-uploader" :action="serverUrl" name="video" accept=".mp4,.3gp" :headers="headers"
:show-file-list="false" :on-success="uploadSuccessVideo" :on-error="handleError" :before-upload="beforeUpload">
</el-upload>
</div>
<el-row v-loading="quillUpdateImg">
<div v-if="!disableFlag">
<quill-editor v-if="loaded" :disabled="disableFlag" v-model="detailContent" ref="myQuillEditor"
:options="editorOption" @focus="onEditorFocus($event)" @change="onEditorChange($event)" v-maxWindow>
</quill-editor>
</div>
<div v-else>
<div style="display: flex;">
<span @click="showViewer = !showViewer" style="float: right;cursor: pointer;margin-right: 20px;">
<i :class="!showViewer ? 'el-icon-arrow-up' : 'el-icon-arrow-down'" class="arrow"></i>
</span>
</div>
<viewer v-if="showViewer" :images="[]" class="viewer" ref="viewer">
<div v-html="detailContent"></div>
</viewer>
</div>
</el-row>
</div>
</template>
<script>
//需要传真的token 不需要传值可以删除
import {
getToken
} from '@/utils/auth'
import 'viewerjs/dist/viewer.css'
import Viewer from 'v-viewer'
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
//npm install quill
import Quill from 'quill'
//npm install vue-quill-editor
import {
quillEditor
} from 'vue-quill-editor'
//npm install quill-image-resize-module quill-image-drop-module --save 调节图片大小
//点击预览图片npm install v-viewer --save
//点击预览图片 在main.js中需要设置
//点击预览图片 import Viewer from 'v-viewer'
//点击预览图片 import 'viewerjs/dist/viewer.css'
Vue.use(Viewer)
import {
ImageDrop
} from 'quill-image-drop-module'
import ImageResize from 'quill-image-resize-module'
Quill.register('modules/imageDrop', ImageDrop)
Quill.register('modules/imageResize', ImageResize)
//全屏设置
import maxWindow from './fullscreen.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": "清除字体样式",
"ql-upload": "文件",
"ql-max": "窗口设置"
};
//获取富文本所以图片的id和被删除图片id
// const getRemovImgId = (ids, html) => {
// let dom = document.createElement('DIV')
// dom.innerHTML = html
// const imgDom = dom.getElementsByTagName('img')
// const url = window.location.host
// const arr=[]
// for (let i = 0; i < imgDom.length; i++) {
// if (imgDom[i].src.indexOf(url) > -1) {
// let reg = new RegExp('(^|&)id=([^&]*)(&|$)')
// let r = imgDom[i].src.split('?')[1].match(reg)
// let id=unescape(r[2])
// ids.splice(ids.indexOf(id),1)
// arr.push(id)
// }
// }
// return {
// removeIds:ids,
// imgIds:arr
// }
// }
export default {
name: 'QuillEditorForm',
components: {
quillEditor,
Viewer
},
props: ['entity', 'disableFlag', 'value'],
data() {
return {
imgIds: [],//所有图片id
removeIds:[],//被删除图片id
quillUpdateImg: false, // 根据图片上传状态来确定是否显示loading动画,刚开始是false,不显示
serverUrl: window.SITE_CONFIG['apiURL'] + "/common/upload", // 上传的图片服务器地址, // 这里写你要上传的图片服务器地址
headers: {
Authorization: 'Bearer ' + getToken()
}, // //需要传真的token 不需要传值可以删除 有的图片服务器要求请求头需要有token之类的参数,写在这里
uploadList: [],
activeNames: ['1'],
detailContent: '', // 富文本内容
initFlag: true,
// 富文本编辑器配置
editorOption: {
placeholder: '',
theme: 'snow', // or 'bubble'
modules: {
//粘贴
clipboard: {
matchers: []
},
imageDrop: {
loading: true,
name: 'file',
action: window.SITE_CONFIG['apiURL'] + "/common/upload",
response: (res) => {
console.log('拖拽', res)
return res.info
}
},
imageResize: { //调节图片大小
displayStyles: {
backgroundColor: 'black',
border: 'none',
color: 'white'
},
modules: ['Resize', 'DisplaySize', 'Toolbar']
},
toolbar: {
container: [
[], // 预留图片上传
['blockquote', 'code-block'], // 引用,代码块
[{
'list': 'ordered'
}, {
'list': 'bullet'
}], // 列表
[{
'header': [1, 2, 3,4,5,6, false]
}], // 几级标题(1~6)
[{
'header': 1
}, {
'header': 2
}], // 标题,键值对的形式;1、2表示字体大小
[{
'size': ['small', false, 'large']
}], // 字体大小('small', false, 'large', 'huge')
[{
'color': []
}, {
'background': []
}],
['bold', 'italic', 'underline'], // 加粗,斜体,下划线,删除线('strike')
[{
'script': 'sub'
}, {
'script': 'super'
}], // 上下标
[{
'indent': '-1'
}, {
'indent': '+1'
}], // 缩进
[{
'direction': 'rtl'
}], // 文本方向
[{ 'font': [] }], // 字体
// [{ 'align': [] }], // 对齐方式
['clean'], // 清除字体样式
['link', 'video']
], // 工具栏
handlers: {
image: (value) => {
if (value) {
this.$refs.annexUpload.$el.querySelector('#quill-upload input').click()
} else {
this.Quill.format('image', false)
}
},
video: function(value) {
//当点击视频上传时,value会变为true
if (value) {
// 触发上传
document.querySelector(".video-uploader input").click();
} else {
this.quill.format("video", false);
}
},
}
}
}
},
quillInstance: null,
loaded: true,
showViewer: true
}
},
watch: {
value: {
handler(val) {
this.detailContent = val
this.$forceUpdate()
},
deep: true,
immediate: true
},
detailContent: {
handler(val) {
if (val && this.initFlag && this.$refs.myQuillEditor && this.$refs.myQuillEditor.quill) {
// 初始化页面,取消focus状态
this.$refs.myQuillEditor.quill.enable(false)
this.detailContent = val
this.$emit('input', val)
setTimeout(() => {
this.$refs.myQuillEditor.quill.enable(true)
})
this.initFlag = false
} else {
this.detailContent = val
this.$emit('input', val)
}
},
deep: true,
immediate: true
},
disableFlag: {
handler(val) {
if (!val) {
this.editorOption.modules.toolbar.container[0] = ['image']
}
this.loaded = false
this.$nextTick(() => {
this.loaded = true
this.$nextTick(() => {
if (!this.$refs.myQuillEditor) {
return
}
// 自定义粘贴图片功能
this.quillInstance = this.$refs.myQuillEditor.quill
this.quillInstance.root.removeEventListener('paste', this.handlePaste)
this.quillInstance.root.addEventListener('paste', this.handlePaste, false)
})
})
},
immediate: true
}
},
mounted() {
//设置文字提示
this.setTitleText()
},
methods: {
//设置文字提示
setTitleText() {
setTimeout(function() {
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]];
});
}, 500)
},
// 用户自动获取焦点,不设置focus状态
onEditorFocus(e) {
this.initFlag = false
},
//图片粘贴
handlePaste(evt) {
if (evt.clipboardData && evt.clipboardData.files && evt.clipboardData.files.length) {
evt.preventDefault();
[].forEach.call(evt.clipboardData.files, file => {
if (!file.type.match(/^image\/(gif|jpe?g|a?png|bmp)/i)) {
return
}
this.uploadToServer(file, (res) => {
// 获取光标内容
var range = this.quillInstance.getSelection()
if (range) {
this.uploadAttachment(res, file, null)
// 在当前光标位置插入图片
// this.$refs.myQuillEditor.quill.insertEmbed(range.index, 'image', res.url)
// 将光标移动到图片后面
this.$refs.myQuillEditor.quill.setSelection(range.index + 1)
}
})
})
}
},
//上传图片
uploadToServer(file, callback) {
var xhr = new XMLHttpRequest()
var formData = new FormData()
formData.append('file', file)
console.log(this.serverUrl)
// headers //需要传真的token 不需要传值可以删除
let str = 'Bearer ' + getToken()
xhr.open('post', this.serverUrl)
xhr.setRequestHeader('Authorization', str)
xhr.withCredentials = true
xhr.responseType = 'json'
xhr.send(formData)
xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status === 200) {
callback(xhr.response)
}
}
},
//上传视频
uploadSuccessVideo(res, file) {
// res为文件服务器返回的数据
// 获取富文本组件实例
let quill = this.$refs.myQuillEditor.quill;
// 如果上传成功
if (res.code === 200) {
// 使用getSelection来获取光标所在位置
let length = quill.getSelection().index;
// 插入“video”或者“image” 第三个参数为服务器端返回的地址
quill.insertEmbed(length, "video", res.data);
// 调整光标到最后
quill.setSelection(length + 1);
console.log(this.content)
} else {
this.$message.error("视频插入失败");
}
// loading动画消失
this.quillUpdateImg = false;
},
//上传判断
handleExceed(files, fileList) {
this.$message.warning(`当前限制一次性上传最多 2 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`)
},
// 富文本图片上传前
beforeUpload() {
// 显示loading动画
this.quillUpdateImg = true;
},
//上传失败
handleError(err, file, fileList) {
this.$message.warning(`文件上传失败,请尝试重新上传,文件失败原因为: ${err}`)
},
//上传成功
uploadAttachment(response, file, fileList) {
// 保存文件信息
if (response.code === 200) {
// 获取富文本组件实例
let quill = this.$refs.myQuillEditor.quill
// 获取光标所在位置
let length = quill.getSelection().index
// 插入图片 res.info为服务器返回的图片地址
let imgurl = window.SITE_CONFIG['apiURL'] + response.fileName
quill.insertEmbed(length, 'image', imgurl)
quill.setSelection(length + 1)
let fileType = null
if (file.raw && file.raw.type) {
fileType = file.raw.type
} else {
fileType = file.type
}
let params = {}
params = {
// 保存文件上传的参数
}
this.$message.success('上传成功')
} else {
this.$message.error(response.message)
}
// 清空文件列表
this.uploadList = []
},
//改变时传值
onEditorChange(e) {
this.$emit('change', e.html)
//删除多余图片
// const ids=getRemovImgId(this.imgIds,e.html)
// this.imgIds=ids.imgIds
// this.removeIds=[...this.removeIds,...ids.removeIds]
// console.log(this.imgIds)
// console.log(this.removeIds)
}
}
}
</script>
<style lang="scss">
.customEditor .quill-editor {
.ql-toolbar.ql-snow .ql-picker-label {
display: flex !important;
}
}
.avatar {
display: none;
}
::v-deep .ql-disabled {
background-color: #F5F7FA;
border-color: #E4E7ED;
color: #7f8185;
cursor: not-allowed;
}
.viewer {
border: 1px solid #ebeef5;
padding: 12px 15px;
min-width: 657px;
max-width: 700px;
max-height: 600px;
overflow: auto;
}
.arrow {
margin: 0 8px 0 auto;
transition: transform .3s;
font-weight: 300;
}
</style>
在页面中引用
最后需要在vue.config.js chainWebpack里面设置
chainWebpack(config) {
//富文本可编辑 配置
config.plugin('provide').use(webpack.ProvidePlugin, [{
'window.Quill': 'quill'
}])
}