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 排版

相关推荐
CrissChan23 分钟前
Pycharm 函数注释
java·前端·pycharm
小小小小宇1 小时前
Vue.nextTick()笔记
前端
小约翰仓鼠2 小时前
vue3子组件获取并修改父组件的值
前端·javascript·vue.js
Lin Hsüeh-ch'in2 小时前
Vue 学习路线图(从零到实战)
前端·vue.js·学习
烛阴3 小时前
bignumber.js深度解析:驾驭任意精度计算的终极武器
前端·javascript·后端
计蒙不吃鱼3 小时前
一篇文章实现Android图片拼接并保存至相册
android·java·前端
全职计算机毕业设计3 小时前
基于Java Web的校园失物招领平台设计与实现
java·开发语言·前端
啊~哈4 小时前
vue3+elementplus表格表头加图标及文字提示
前端·javascript·vue.js
小小小小宇4 小时前
前端小tips
前端