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             `<div class="w-e-menu el-icon-s-open" data-title="格式刷">
 15             </div>`
 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 <template>
 2     <div class="editor-el">
 3         <div></div>
 4         <el-input v-show="false" v-model="value"></el-input>
 5     </div>
 6 </template>
 7 
 8 <script lang="ts">
 9 // 引入 wangEditor
10 import wangEditor from "wangeditor";
11 import { Component, Vue, Model, Emit, Watch } from "vue-property-decorator";
12 import "./formatBrush";
13 @Component({})
14 export default class servicesNoticeEdit extends Vue {
15     // v-model綁定值
16     @Model("valuechange", { type: String }) value!: String;
17     @Emit("valuechange") setValue() {}
18 
19     @Watch("value", { immediate: true }) updateValue(v: string) {
20         if (v) {
21             const me = this as any;
22             if (me.isUp) {
23                 // 這里通過標識符避免重復觸發事件
24                 me.isUp = false;
25             } else {
26                 me.$nextTick(() => {
27                     // 重新設置編輯器內容
28                     me.editor.txt.html(v);
29                 });
30             }
31         }
32     }
33     editor: any = null;
34     // 標識是否正在設置數據,避免重復觸發事件
35     isUp: Boolean = false;
36     mounted() {
37         const me = this as any,
38             // eslint-disable-next-line new-cap
39             editor = new wangEditor(me.$el.firstChild) as any;
40         // zIndex配置小點,避免影響其他組件
41         editor.config.zIndex = 100;
42         // 配置 onchange 回調函數,將數據同步到 vue 中
43         editor.config.onchange = (newHtml: string) => {
44             me.isUp = true;
45             me.setValue(newHtml);
46         };
47 
48         // 創建編輯器
49         editor.create();
50         me.editor = editor;
51     }
52     beforeDestroy() {
53         // 調用銷毀 API 對當前編輯器實例進行銷毀
54         this.editor.destroy();
55         this.editor = null;
56     }
57 }
58 </script>
59 <style scoped lang="scss">
60 ::v-deep.editor-el {
61     // 格式刷功能激活圖標樣式
62     .w-e-menu.w-e-active {
63         color: red;
64     }
65 }
66 </style>

效果大概是這樣

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM