WangEditor5-源码模式

wangEditor官网https://www.wangeditor.com/v5/toolbar-config.html

yarn add @wangeditor/editor
yarn add @wangeditor/editor-for-react

1、创建工具栏类

import { IButtonMenu, IDomEditor } from '@wangeditor/editor';
import { source } from '../icon';
class SourceMenu implements IButtonMenu {
    title: string; // 按钮标题,没有图标的情况则显示该标题,有图标则为tips
    tag: string; // 自定义菜单类型
    active: boolean; // 选中状态
    /**
     * 图标这里我的图标是在阿里妈妈适量图标库获取的https://www.iconfont.cn/
     * 搜索对应的图标赋值svg代码放到方法返回即可
     * icon/index.ts源码
     * export const source = () => `<svg>...</svg>`
     */
    iconSvg: string; 
    constructor() {
        this.title = '源码' // 自定义菜单标题
        this.iconSvg = source() // 图标
        this.tag = 'button'
        this.active = false
    }

    /**
     * 获取编辑器内容源码
     * @param editor
     */
    getValue(editor: IDomEditor): string | boolean {
        return editor.getHtml();
    }

    /**
     * 菜单是否需要激活,当切换为源码时菜单激活
     * @param editor
     * @param active 激活状态
     */
    isActive(editor: IDomEditor, active?: boolean): boolean {
        return this.active;
    }

    /**
     * 菜单是否需要禁用(如选中 H1 ,“引用”菜单被禁用),用不到则返回 false
     * @param editor
     */
    isDisabled(editor: IDomEditor): boolean {
        return false;
    }

    /**
     * 点击菜单时触发的函数
     * @param editor
     * @param value
     */
    exec(editor: IDomEditor, value: string | boolean) {
        this.active = !this.active;
        if (this.isDisabled(editor)) return
        editor.emit('clickSource', this.active);
    }
}
export default SourceMenu;

2、工具栏方法实现

import SourceMenu from "./sources";
// @ts-ignore 代码格式化工具
import prettier from 'prettier/standalone';
// @ts-ignore 格式化html
import parserHtml from 'prettier/parser-html';

/**
 * 在编辑器中得到的html源码是没有格式的html字符串
 * 所以需要格式化展示代码
 * 格式化html代码
 * @param code
 */
export const parserHtmlCode = (code: string): string => {
    try {
        return prettier.format(code, {
            parser: 'html',
            plugins: [parserHtml],
            // 格式化的标签不换行 例如span标签等>格式化后会换行
            htmlWhitespaceSensitivity: 'ignore'
        });
    } catch (e) {
        console.error('格式化代码错误', e);
        return code;
    }
}

/**
 * 将编辑器html转换为代码块内容
 * @param html
 */
export const parseEditorCode = (html: string) => {
    let code = html
        .replace(/&nbsp;/g, '')
        .replace(new RegExp('<p><br></p>', 'g'), '');
    let data = parserHtmlCode(code).trim();
    let textCode = data
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/ /g, "&nbsp;");
    return `<pre><code class="language-html">${textCode}</code></pre>`;
}

/**
 * 将代码块转换为编辑器html
 * @param preCode 代码块
 */
export const parseCodeEditor = (preCode: string) => {
    // 转码
    let data = encodeURI(preCode);
    // 将&nbsp;转换为空格
    data = data.replace(/%C2%A0/g,'%20');
    // 解码
    data = decodeURI(data);
    let htmlStr = data
        .replace('<pre><code class="language-html">', '')
        .replace('</code></pre>', '')
        .replace(/&lt;/ig, "<")
        .replace(/&gt;/ig, ">");
    return htmlStr
        .replace(new RegExp('\\n', 'g'), '')
        .replace(new RegExp('<p><br></p>', 'g'), '')
        .trim();
}

export const sourceConf = { 
	// 工具栏中的唯一key
	key: 'source', 
	// 组件
	factory: () => new SourceMenu() 
};

3、页面具体代码

在组件初始化时注册

import { IDomEditor, Boot } from '@wangeditor/editor';
import { Editor, Toolbar } from '@wangeditor/editor-for-react';
import '@wangeditor/editor/dist/css/style.css';
import React, {useEffect, useState} from 'react';
import {IWangEditorProps, parseCodeEditor, parseEditorCode, sourceConf} from "./props";

const WangEditor = ({ value = '', mode = 'default', onChange, toolbarConfig = {}, showCode = false }: any) => {
	// 如果组件引用时加了源码模式showCode={true},则添加工具栏
    showCode && arr.push('source');
    /***********自定义变量-------开始*************/
    // editor 实例
    const [editor, setEditor] = useState<IDomEditor | null>(null);
    // 编辑器内容
    const [html, setHtml] = useState<string>(value || '');
    /***********自定义变量-------结束*************/

    /***********生命周期函数-------开始*************/
    // 及时销毁 editor
    useEffect(() => {
        if (editor?.isFocused()) {
            editor?.blur();
        }
        return () => {
            // eslint-disable-next-line eqeqeq
            if (editor == null) return;
            editor.destroy();
            setEditor(null);
        };
    }, [editor]);

    const onCreated = (editor: IDomEditor) => {
    	/*
    	 * 在组件创建时初始化注册菜单,注意菜单不可以重复注册否则会报异常
    	 * Unhandled Rejection (Error): Duplicated key 'source' in menu items
    	 */
        if (!editor.getAllMenuKeys().includes('source')) {
            Boot.registerMenu(sourceConf);
        }
        setEditor(editor);
        // 源码菜单点击监听事件
        editor?.on('clickSource', (active) => clickSource(active, editor));
    }

    /**
     * 源码点击
     * @param active 菜单是否点击
     * @param editor 编辑器内容
     */
    const clickSource = (active: boolean, editor: IDomEditor) => {
        let value = editor.getHtml();
        // 先将编辑器内容清空
        editor.clear();
        if (active) {
            // 将html代码转换为html代码块 dangerouslyInsertHtml是插入html不是重置html,如果使用setHtml也会报节点异常
            editor.dangerouslyInsertHtml(parseEditorCode(value));
        } else {
            // 将html代码块转换为editor的html
            editor.dangerouslyInsertHtml(parseCodeEditor(value));
            value = parseCodeEditor(value);
        }
    }

    /***********生命周期函数-------结束*************/
    return (
        <div>
            <Toolbar
                editor={editor}
                defaultConfig={{
                    ...toolbarConfig,
                    // 添加自定义菜单
                    insertKeys: {
                        index: editor?.getAllMenuKeys().length || 0,
                        keys: arr,
                    }
                }}
                mode={mode}
            />
            <Editor value={html} onCreated={onCreated} />
        </div>
    );
};

export default WangEditor;

4、效果图

预览模式

在这里插入图片描述

源码模式

在这里插入图片描述

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿࿆杰࿆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值