vue 中 wangeditor 格式刷功能


去年我刚到公司的时候,旁边同事问我wangeditor怎么实现格式刷功能,我找了很多资料勉勉强强实现了一个,一直想写个文章记录下,拖到现在才写,真是老了呢。

新建一个js文件,暂且命名为formatBrush.js,直接上代码:

  1 // 参考资料
  2 // https://www.jianshu.com/p/13dca2711b6e
  3 // https://juejin.cn/post/6844903737161433102
  4 
  5 import wangEditor from "wangeditor";
  6 
  7 const { BtnMenu } = wangEditor;
  8 // 第一,菜单 class ,Button 菜单继承 BtnMenu class
  9 class FormatBrushMenu extends BtnMenu {
 10     constructor(editor) {
 11         // data-title属性表示当鼠标悬停在该按钮上时提示该按钮的功能简述
 12         // 图表直接用的element-ui的图标,请自行调整
 13         const $elem = wangEditor.$(
 14             `
15
` 16 ); 17 super($elem, editor); 18 const me = this; 19 me.editor = editor; 20 // 监听编辑器鼠标释放事件 21 editor.$textElem.on("mouseup", () => { 22 // 如果格式刷功能出于激活状态 23 if (me._active) { 24 // 延迟执行,避免获取不到正确的元素 25 setTimeout(() => { 26 // 复制格式刷样式 27 pasteStyle(editor); 28 // 取消格式刷激活样式 29 me.unActive(); 30 }, 100); 31 } 32 }); 33 } 34 // 菜单点击事件 35 clickHandler() { 36 const me = this; 37 const editor = me.editor; 38 if (me._active) { 39 // 已经在激活状态时取消激活 40 me.unActive(); 41 // 清空格式刷样式数据 42 editor.copyStyleList = [] 43 } else { 44 // 没有选中则终端 45 if (editor.selection.isSelectionEmpty()) return; 46 // 激活按钮 47 me.active(); 48 // 获取格式刷样式 49 const domToParse = 50 editor.selection.getSelectionContainerElem().elems[0]; 51 const copyStyleList = parseDom(domToParse); 52 // 保存格式刷样式 53 editor.copyStyleList = copyStyleList; 54 } 55 } 56 tryChangeActive() { } 57 } 58 59 // 菜单 key ,各个菜单不能重复 60 const menuKey = "formatBrush"; 61 62 // 注册菜单 63 wangEditor.registerMenu(menuKey, FormatBrushMenu); 64 65 // 复制选中dom的样式 66 function parseDom(dom) { 67 let targetDom = null; 68 const nodeArray = []; 69 70 getTargetDom(dom); 71 72 getAllStyle(targetDom); 73 74 function getTargetDom(dom) { 75 for (const i of dom.childNodes) { 76 if (i.nodeType === 3 && i.nodeValue && i.nodeValue.trim() !== "") { 77 targetDom = dom; 78 return; 79 } 80 } 81 getTargetDom(dom.children[0]); 82 } 83 84 function getAllStyle(dom) { 85 if (!dom) return; 86 const tagName = dom.tagName.toLowerCase(); 87 if (tagName === "p") { 88 nodeArray.push({ 89 tagName: "span", 90 attributes: Array.from(dom.attributes).map((i) => { 91 return { 92 name: i.name, 93 value: i.value 94 }; 95 }) 96 }); 97 return; 98 } else { 99 nodeArray.push({ 100 tagName: tagName, 101 attributes: Array.from(dom.attributes).map((i) => { 102 return { 103 name: i.name, 104 value: i.value 105 }; 106 }) 107 }); 108 getAllStyle(dom.parentNode); 109 } 110 } 111 return nodeArray; 112 } 113 114 function addStyle(text, nodeArray) { 115 let currentNode = null; 116 nodeArray.forEach((ele, index) => { 117 const node = document.createElement(ele.tagName); 118 for (const attr of ele.attributes) { 119 node.setAttribute(attr.name, attr.value); 120 } 121 if (index === 0) { 122 node.innerText = text; 123 currentNode = node; 124 } else { 125 node.appendChild(currentNode); 126 currentNode = node; 127 } 128 }); 129 return currentNode; 130 } 131 132 // 粘贴 133 function pasteStyle(editor) { 134 // 获取格式刷保存的样式 135 const copyStyleList = editor.copyStyleList; 136 // 有样式说明格式刷被激活 137 if (copyStyleList) { 138 // 获取当前选中内容 139 // 如果没选中也会执行,再次使用需要重新激活格式刷功能 140 const text = editor.selection.getSelectionText(); 141 const targetDom = addStyle(text, copyStyleList); 142 editor.cmd.do("insertHTML", targetDom.outerHTML); 143 // 清空格式刷样式 144 editor.copyStyleList = null; 145 } 146 }

vue组件代码如下,基于ts(没用ts的自己改改吧,话说vue3这种写法就不行了呀,组合式api还是好用):

 1 
 7 
 8 
59 

效果大概是这样