• 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>

    效果大概是这样

  • 相关阅读:
    1051 Wooden Sticks(贪心-3)
    97 等价交换(贪心-2)
    python文件操作
    python学习-day 2
    python学习-day 1
    Python 测试题目-1
    Python list和dict方法
    Python 字符串
    while循环语句
    Python if判断语句
  • 原文地址:https://www.cnblogs.com/mlzs/p/15967553.html
Copyright © 2020-2023  润新知