WangEditor 自定义图片插件开发

WangEditor 插件开发

官网上有完整的流程 自定义扩展新功能 | wangEditor

我这里开发的是一个图片组件。图片需要从资源库里面选择

注册新菜单

自定义扩展新功能 | wangEditor

typescript 复制代码
import type { IButtonMenu, IDomEditor } from '@wangeditor/editor';

export default class ImageMenu implements IButtonMenu {
    title = '图片'

    tag = 'button'

    // eslint-disable-next-line max-len
    iconSvg = '<svg viewBox="0 0 1024 1024" > <path d="M959.877 128l0.123 0.123v767.775l-0.123 0.122H64.102l-0.122-0.122V128.123l0.122-0.123h895.775zM960 64H64C28.795 64 0 92.795 0 128v768c0 35.205 28.795 64 64 64h896c35.205 0 64-28.795 64-64V128c0-35.205-28.795-64-64-64zM832 288.01c0 53.023-42.988 96.01-96.01 96.01s-96.01-42.987-96.01-96.01S682.967 192 735.99 192 832 234.988 832 288.01zM896 832H128V704l224.01-384 256 320h64l224.01-192z" > </path></svg >';

    getValue() {
        return false;
    }

    isActive() {
        return false; // or true
    }

    isDisabled() {
        return false; // or true
    }

    exec(editor: IDomEditor) {
        editor.emit('imageMenuClick');
    }
}

exec 为什么用emit 事件。

图片是从资源库选择的, 但是选择的时候会动态的改变可选择的图片类型, 所以需要在封装的编辑器组件里面读取props 里面的值。那exec 方法要获取这个值的话, 就只能在组件的mounted 里面注册。 这就会产生重复注册的问题。所有通过emit事件触发。 然后编辑器组件监听这个事件,唤起资源库选择

typescript 复制代码
this.editor!.on('imageMenuClick', () => {
            this.dialogVisible.imageSourceChoose = true;
      });

exec 非emit 处理参考 github.com/wangeditor-... 大概意思是把编辑器传入弹窗组件

劫持编辑器事件和操作

自定义扩展新功能 | wangEditor

定义新元素

自定义扩展新功能 | wangEditor

复制代码
typescript 复制代码
import { IDomEditor } from '@wangeditor/editor';

function withCustomImage<T extends IDomEditor>(editor: T): T {
    const { isInline, isVoid } = editor;

    const newEditor = editor;

    newEditor.isInline = (elem: any) => {
        const { type } = elem;
        if (type === 'img') {
            return true;
        }
        return isInline(elem);
    };

    newEditor.isVoid = (elem: any) => {
        const { type } = elem;
        if (type === 'img') {
            return true;
        }

        return isVoid(elem);
    };

    return newEditor;
}

export default withCustomImage;

上面没什么好说的, 就是定义这个元素是不是inline节点数据结构 | wangEditor , void节点数据结构 | wangEditor

typescript 复制代码
import { IEditorImageElement } from '@/models/WangEditor/Image';
import { SlateElement, DomEditor, IDomEditor } from '@wangeditor/editor';
import { h, VNode } from 'snabbdom';

function renderCustomImage(elem: SlateElement, children: VNode[] | null, editor: IDomEditor) {
    const { src, cssClass } = elem as IEditorImageElement;

    const isSelected = DomEditor.isNodeSelected(editor, elem);

    const classObj: Record<string, boolean> = {};
    cssClass.forEach((className) => {
        classObj[className] = true;
    });

    const vnode = h('img', {
        props: {
            src,
            contentEditable: false,
        },
        style: {
            marginLeft: '3px',
            marginRight: '3px',
            border: isSelected
                ? '2px solid var(--w-e-textarea-selected-border-color)' // wangEditor 提供了 css var https://www.wangeditor.com/v5/theme.html
                : '2px solid transparent',
        },
        className: cssClass,
    });

    return vnode;
}

const renderElemConf = {
    type: 'img',
    renderElem: renderCustomImage,
};

export default renderElemConf;

必须要加上margin,不然inline元素插入以后 光标无法选择到元素的后面。

把新元素转换为 HTML

typescript 复制代码
import { IEditorImageElement } from '@/models/WangEditor/Image';
import { SlateElement } from '@wangeditor/editor';

function customImageToHtml(elem: SlateElement): string {
    const { src, cssClass } = elem as IEditorImageElement;

    return `<img data-w-e-type="img" data-w-e-is-void data-w-e-is-inline class="${cssClass.join(' ')}" src="${src}"/>`;
}

const elemToHtmlConf = {
    type: 'img', // 节点 type ,重要!!!
    elemToHtml: customImageToHtml,
};

export default elemToHtmlConf;

html 上的 attr 是必须的, 用来判断的

解析新元素 HTML 到编辑器

typescript 复制代码
import { IEditorImageElement } from '@/models/WangEditor/Image';
import { DOMElement } from '@wangeditor/editor/dist/editor/src/utils/dom';

function parseHtml(elem: DOMElement) {
    const src = elem.getAttribute('src');
    const classNames = elem.getAttribute('class');
    return {
        type: 'img',
        src,
        cssClass: classNames ? classNames.split(' ') : [],
        children: [{ text: '' }],
    } as IEditorImageElement;
}

const parseHtmlConf = {
    selector: 'img[data-w-e-type="img"]',
    parseElemHtml: parseHtml,
};

export default parseHtmlConf;

注意点

  1. void 元素必须插入一个 { text: ''}
  2. 插入元素到原光标位置需要**restoreSelection**() 。为了书写体验, 可以用editor.move(2) 移动到下一个光标
  3. 插入非inline 元素, 必须判断下一行是不是有可编辑区域,不然无法编辑, 参考github.com/wangeditor-...

本文使用 markdown.com.cn 排版

相关推荐
EasyNTS几秒前
无插件H5播放器EasyPlayer.js网页web无插件播放器vue和react详细介绍
前端·javascript·vue.js
guokanglun24 分钟前
Vue.js动态组件使用
前端·javascript·vue.js
-seventy-37 分钟前
Ajax 与 Vue 框架应用点——随笔谈
前端
我认不到你1 小时前
antd proFromSelect 懒加载+模糊查询
前端·javascript·react.js·typescript
集成显卡1 小时前
axios平替!用浏览器自带的fetch处理AJAX(兼容表单/JSON/文件上传)
前端·ajax·json
焚琴煮鹤的熊熊野火1 小时前
前端垂直居中的多种实现方式及应用分析
前端
我是苏苏1 小时前
C# Main函数中调用异步方法
前端·javascript·c#
转角羊儿2 小时前
uni-app文章列表制作⑧
前端·javascript·uni-app
大G哥2 小时前
python 数据类型----可变数据类型
linux·服务器·开发语言·前端·python
hong_zc2 小时前
初始 html
前端·html