wangEditor缩进采用的是padding-left:2em,没有首行缩进功能,因此个人根据官方说明配置了一下,移除了原来的缩进改成了首行缩进。
普通粘贴时基本好用,但是无格式文本粘贴还是只能是全局缩进。
希望大家也能帮助优化一下。
// 引入 wangEditor
import WangEditor from 'wangeditor'
const { $, DropListMenu } = WangEditor
// 第一,菜单 class ,Button 菜单继承 BtnMenu class
class AlertMenu extends DropListMenu {
constructor (editor) {
// data-title属性表示当鼠标悬停在该按钮上时提示该按钮的功能简述
const $elem = $(
'<div class="w-e-menu" data-title="首行缩进"><i class="w-e-icon-indent-increase"></i></div>'
)
// droplist 配置
const dropListConf = {
width: 100,
title: '首行缩进',
type: 'list',
list: [
{ $elem: $('<p><i class="w-e-icon-indent-increase w-e-drop-list-item"></i>增加缩进</p>'), value: '2em' },
{ $elem: $('<p><i class="w-e-icon-indent-decrease w-e-drop-list-item"></i>减少缩进</p>'), value: '' }
],
// droplist 每个 item 的点击事件
clickHandler: (value) => {
// value 参数即 dropListConf.list 中配置的 value
this.command(value)
}
}
super($elem, editor, dropListConf)
}
command (value) {
// 设置标题
// 判断是否有子元素 有一个官方提供的结束节点不准确的Bug
if (this.editor.selection.getSelectionContainerElem().elems[0].children.length > 0) {
// 无格式文本父元素是p,要把第一行单独拿出来缩进
let fir = 0
// 判断是否进入选区
var start = false
// 提前算出<br>标签的个数
let isAllBr = 0
// 判断是否全部是<br> 即无格式文本
let isVali = false
// 提前算出<br>标签的个数
for (let e of this.editor.selection.getSelectionContainerElem().elems[0].children) {
if (e.tagName === 'BR') {
isAllBr++
}
}
// 如果选区内子元素的个数与<br>元素个数相同即可判断为无格式文本
if (isAllBr === this.editor.selection.getSelectionContainerElem().elems[0].children.length) isVali = true
// 对无格式文本进行缩进处理
if (isVali) {
for (let e of this.editor.selection.getSelectionContainerElem().elems[0].children) {
if (e.tagName === "BR") {
if (value === '2em') {
fir += 1
if (fir === 1) {
this.editor.selection.getSelectionContainerElem().elems[0].style.textIndent = value
}
// 先把<br>换成span元素并增加缩进 再在span元素前再加上<br> indentP是因为原来用的是P元素
const indentP = document.createElement("span")
indentP.style.display = "inline-block"
indentP.style.width = value
indentP.className = 'indentP'
const theBr = document.createElement("br")
this.editor.selection.getSelectionContainerElem().elems[0].replaceChild(indentP, e)
this.editor.selection.getSelectionContainerElem().elems[0].insertBefore(theBr, indentP)
}
}
}
} else {
let elseIndex = 0
// 正常文本 以及无格式文本取消缩进逻辑
for (let e of this.editor.selection.getSelectionContainerElem().elems[0].children) {
// 如果是无格式文本
if (e.className === "indentP") {
elseIndex++
if (elseIndex === 1) {
this.editor.selection.getSelectionContainerElem().elems[0].style.textIndent = value
}
this.editor.selection.getSelectionContainerElem().elems[0].removeChild(e)
continue
}
// 如果是普通文本 选区的开始节点
if (e === this.editor.selection.getSelectionStartElem().elems[0]) {
start = true
}
// 如果已是选中节点,则开始进行缩进
if (start) {
e.style.textIndent = value
}
// 选区的结束节点 跳出循环
if (e === this.editor.selection.getSelectionEndElem().elems[0]) {
break
}
}
}
} else {
this.editor.selection.getSelectionContainerElem().elems[0].style.textIndent = value
}
}
// 删除这段代码会报错
tryChangeActive () {
// const reg = /^h/i
// const cmdValue = this.editor.cmd.queryCommandValue('indent')
// if (reg.test(cmdValue)) {
// // 选区处于标题内,激活菜单
// this.active()
// } else {
// // 否则,取消激活
// this.unActive()
// }
// this.active()
}
}
// 菜单 key ,各个菜单不能重复
const menuKey = 'alertMenuKey'
// 注册菜单
WangEditor.registerMenu(menuKey, AlertMenu)
完整代码 组件editor.vue
<template>
<div class="editor">
<div id="demo1"></div>
</div>
</template>
<script>
// 引入 wangEditor
import WangEditor from 'wangeditor'
const { $, DropListMenu } = WangEditor
// 第一,菜单 class ,Button 菜单继承 BtnMenu class
class AlertMenu extends DropListMenu {
constructor (editor) {
// data-title属性表示当鼠标悬停在该按钮上时提示该按钮的功能简述
const $elem = $(
'<div class="w-e-menu" data-title="首行缩进"><i class="w-e-icon-indent-increase"></i></div>'
)
// droplist 配置
const dropListConf = {
width: 100,
title: '首行缩进',
type: 'list',
list: [
{ $elem: $('<p><i class="w-e-icon-indent-increase w-e-drop-list-item"></i>增加缩进</p>'), value: '2em' },
{ $elem: $('<p><i class="w-e-icon-indent-decrease w-e-drop-list-item"></i>减少缩进</p>'), value: '' }
],
// droplist 每个 item 的点击事件
clickHandler: (value) => {
// value 参数即 dropListConf.list 中配置的 value
this.command(value)
}
}
super($elem, editor, dropListConf)
}
command (value) {
// 设置标题
// 判断是否有子元素 有一个官方提供的结束节点不准确的Bug
if (this.editor.selection.getSelectionContainerElem().elems[0].children.length > 0) {
let fir = 0
var start = false
let isAllBr = 0
let isVali = false
// 判断是否全部是BR 即无格式文本
for (let e of this.editor.selection.getSelectionContainerElem().elems[0].children) {
if (e.tagName === 'BR') {
isAllBr++
}
}
if (isAllBr === this.editor.selection.getSelectionContainerElem().elems[0].children.length) isVali = true
// 对无格式文本进行缩进处理
// 如果是<br>
// 选中节点无法选到br 进行和取消的逻辑
// 判断是否已是选中节点
if (isVali) {
for (let e of this.editor.selection.getSelectionContainerElem().elems[0].children) {
if (e.tagName === "BR") {
if (value === '2em') {
fir += 1
if (fir === 1) {
this.editor.selection.getSelectionContainerElem().elems[0].style.textIndent = value
}
const indentP = document.createElement("span")
indentP.style.display = "inline-block"
indentP.style.width = value
indentP.className = 'indentP'
const theBr = document.createElement("br")
this.editor.selection.getSelectionContainerElem().elems[0].replaceChild(indentP, e)
this.editor.selection.getSelectionContainerElem().elems[0].insertBefore(theBr, indentP)
}
}
}
} else {
let elseIndex = 0
// 正常文本 以及无格式文本取消缩进逻辑
for (let e of this.editor.selection.getSelectionContainerElem().elems[0].children) {
// 如果是无格式文本
if (e.className === "indentP") {
elseIndex++
if (elseIndex === 1) {
this.editor.selection.getSelectionContainerElem().elems[0].style.textIndent = value
}
this.editor.selection.getSelectionContainerElem().elems[0].removeChild(e)
continue
}
// 选区的开始节点
if (e === this.editor.selection.getSelectionStartElem().elems[0]) {
start = true
}
// 如果已是选中节点,则开始进行缩进
if (start) {
e.style.textIndent = value
}
// 选区的结束节点 跳出循环
if (e === this.editor.selection.getSelectionEndElem().elems[0]) {
break
}
}
}
} else {
this.editor.selection.getSelectionContainerElem().elems[0].style.textIndent = value
}
}
// 删除这段代码会报错
tryChangeActive () {
// const reg = /^h/i
// const cmdValue = this.editor.cmd.queryCommandValue('indent')
// if (reg.test(cmdValue)) {
// // 选区处于标题内,激活菜单
// this.active()
// } else {
// // 否则,取消激活
// this.unActive()
// }
// this.active()
}
}
// 菜单 key ,各个菜单不能重复
const menuKey = 'alertMenuKey'
// 注册菜单
WangEditor.registerMenu(menuKey, AlertMenu)
export default {
name: 'editor',
data () {
return {
editor: null,
editorData: ''
}
},
model: {
prop: 'content',
event: 'change'
},
props: {
content: {
type: String,
default: ''
}
},
mounted () {
const editor = new WangEditor(`#demo1`)
// 配置 onchange 回调函数,将数据同步到 vue 中
editor.config.onchange = (newHtml) => {
this.editorData = newHtml
this.$emit('change', newHtml)
}
editor.config.excludeMenus = [
'code',
'indent'
]
editor.config.pasteFilter = true
editor.config.pasteText = true
editor.config.pasteIgnoreImg = true
editor.config.uploadImgMaxSize = 2 * 1024 * 1024 // 限制大小为2M
editor.config.zIndex = 1
editor.config.height = 466
editor.config.showLinkImg = false
editor.config.showLinkVideo = false
editor.config.uploadFileName = 'file'
editor.config.uploadImgServer = this.baseURL + 'CommonPage/FileUploadHandler.ashx?m=' + Math.random()
editor.config.uploadVideoServer = this.baseURL + 'CommonPage/FileUploadHandler.ashx?m=' + Math.random()
editor.config.uploadVideoMaxSize = 1 * 1024 * 1024 * 1024 // 1024m
editor.config.uploadVideoAccept = ['mp4']
editor.config.uploadImgHooks = {
// 图片上传并返回结果,但图片插入错误时触发
fail: function (xhr, editor, result) {
// console.log(result)
},
success: function (xhr) {
// 图片上传并返回结果,图片插入成功之后触发
},
customInsert: function (insertImg, result) {
// 图片上传并返回结果,自定义插入图片的事件(而不是编辑器自动插入图片!!!)
// insertImg 是插入图片的函数,editor 是编辑器对象,result 是服务器端返回的结果
// 举例:假如上传图片成功后,服务器端返回的是 {url:'....'} 这种格式,即可这样插入图片:
let url = '../' + result.ResPath
insertImg(url)
// result 必须是一个 JSON 格式字符串!!!否则报错
}
}
editor.config.uploadVideoHooks = {
fail: function (xhr, editor, result) {
// console.log(result)
},
success: function (xhr) {
// 图片上传并返回结果,图片插入成功之后触发
},
customInsert: function (insertVideoFn, result) {
// 图片上传并返回结果,自定义插入图片的事件(而不是编辑器自动插入图片!!!)
// insertImg 是插入图片的函数,editor 是编辑器对象,result 是服务器端返回的结果
// 举例:假如上传图片成功后,服务器端返回的是 {url:'....'} 这种格式,即可这样插入图片:
let url = '../' + result.ResPath
insertVideoFn(url)
// result 必须是一个 JSON 格式字符串!!!否则报错
}
}
editor.config.customAlert = function (s, t) {
switch (t) {
case 'success':
this.$message.success(s)
break
case 'info':
this.$message.info(s)
break
case 'warning':
this.$message.warning(s)
break
case 'error':
this.$message.error(s)
break
default:
this.$message.info(s)
break
}
}
// 创建编辑器
editor.create()
this.editor = editor
},
methods: {
getEditorData () {
// 通过代码获取编辑器内容
// let data = this.editor.txt.html()
},
clearContent () {
this.editor.txt.clear()
},
beforeDestroy () {
// 调用销毁 API 对当前编辑器实例进行销毁
this.editor.destroy()
this.editor = null
},
setHtml (content) {
this.editor.txt.html(content)
}
},
beforeDestroy () {
// 销毁编辑器
this.editor.destroy()
this.editor = null
}
}
</script>
<style lang="less" >
.editor {
font-size: 1em;
width: 100%;
margin: auto;
tr, table td, table th {
height: 2em;
// min-height: 30px;
}
// table {
// border-top: 1px solid #ccc;
// border-left: 1px solid #ccc;
// }
// table td,
// table th {
// border-bottom: 1px solid #ccc;
// border-right: 1px solid #ccc;
// padding: 3px 5px;
// }
// table th {
// border-bottom: 2px solid #ccc;
// text-align: center;
// }
.w-e-text::-webkit-scrollbar {
width: 6px !important;
height: 6px !important;
}
.w-e-text::-webkit-scrollbar-thumb {
border-radius: 5px !important;
box-shadow: 0 0 2px #fff !important;
background-color: #ddd !important;
}
.w-e-text::-webkit-scrollbar-track{
border-radius: 0;
box-shadow: inset 0 0 2px #fff;
}
}
</style>