React wangEditor5 使用说明
1、支持包安装
yarn add @wangeditor/editor
# 或者 npm install @wangeditor/editor --saveyarn add @wangeditor/editor-for-react
# 或者 npm install @wangeditor/editor-for-react --save
2、使用
import '@wangeditor/editor/dist/css/style.css' // 引入 cssimport { useState, useEffect } from 'react'
import { Editor, Toolbar } from '@wangeditor/editor-for-react'
import { IDomEditor, IEditorConfig, IToolbarConfig } from '@wangeditor/editor'type InsertImgType = (url: string, alt: string, href: string) => void;
type InsertVideoType = (url: string, poster?: string) => void;const MyEditor: FunctionComponent = () => {// editor 实例const [editor, setEditor] = useState<IDomEditor | null>(null);// 编辑器内容const [html, setHtml] = useState('<p>hello</p>')// 模拟 ajax 请求,异步设置 htmluseEffect(() => {setTimeout(() => {setHtml('<p>hello world</p>')}, 1500)}, [])// 工具栏配置const toolbarConfig: Partial<IToolbarConfig> = {excludeKeys: ['group-video']};// 编辑器配置const editorConfig: Partial<IEditorConfig> = {placeholder: '请输入内容...',readOnly: false,MENU_CONF: {uploadImage: {// 自定义上传 -- 图片customUpload: (file: File, insertFn: InsertImgType) => {if(file.type.startsWith('image/')) {// file 即选中的文件// 自己实现上传,并得到图片 url alt href// 最后插入图片insertFn(url, alt, href)} else {// 错误提示}}},uploadVideo: {// 自定义上传 -- 视频customUpload: (file: File, insertFn: InsertVideoType) => {// file 即选中的文件// 自己实现上传,并得到视频 url poster// 最后插入视频insertFn(url, poster)}}}}useEffect(() => {// 修改弹窗位置为编译器居中editor?.on('modalOrPanelShow', modalOrPanel => {if (modalOrPanel.type !== 'modal') returnconst { $elem } = modalOrPanel; // modal elementconst width = $elem.width();const height = $elem.height();// set modal position z-index$elem.css({left: '50%',top: '50%',bottom: 'auto', // 需要修改底部间距,不然会受组件自身计算影响marginLeft: `-${width / 2}px`,marginTop: `-${height / 2}px`,zIndex: 1000});});// 及时销毁 editor ,重要!return () => {if (editor == null) returneditor.destroy()setEditor(null)}}, [editor])return (<><div style={{ border: '1px solid #ccc', zIndex: 100}}><Toolbareditor={editor}defaultConfig={toolbarConfig}mode="default"style={{ borderBottom: '1px solid #ccc' }}/><EditordefaultConfig={editorConfig}value={html}onCreated={setEditor}onChange={editor => setHtml(editor.getHtml())}mode="default"style={{ height: '500px', overflowY: 'hidden' }}/></div></>)
}export default MyEditor;
3、自定义菜单
1. 添加自定义菜单弹窗
import { DomEditor, IDomEditor, IModalMenu, SlateNode, SlateTransforms, t } from '@wangeditor/editor';
import { DOMElement } from '@wangeditor/editor/dist/editor/src/utils/dom';
import { genModalButtonElems, genModalInputElems } from './utils';class EditImageSize implements IModalMenu {showModal: boolean;modalWidth: number;title: string;iconSvg?: string;hotkey?: string;alwaysEnable?: boolean;tag: string;width?: number;private $content: DOMElement | null = null;private getSelectedImageNode(editor: IDomEditor): SlateNode | null {return DomEditor.getSelectedNodeByType(editor, 'image')}constructor() {this.title = t('videoModule.editSize');// this.iconSvg = '<svg >...</svg>';this.tag = 'button';this.showModal = true;this.modalWidth = 320;}// 菜单是否需要激活(如选中加粗文本,“加粗”菜单会激活),用不到则返回 falseisActive(): boolean {// 任何时候,都不用激活 menureturn false}// 获取菜单执行时的 value ,用不到则返回空 字符串或 falsegetValue(): string | boolean {// 插入菜单,不需要 valuereturn ''}// 菜单是否需要禁用(如选中 H1 ,“引用”菜单被禁用),用不到则返回 falseisDisabled(editor: IDomEditor): boolean {if (editor.selection == null) return trueconst videoNode = this.getSelectedImageNode(editor)if (videoNode == null) {// 选区未处于 image node ,则禁用return true}return false}// 点击菜单时触发的函数exec() {// 点击菜单时,弹出 modal 之前,不需要执行其他代码// 此处空着即可}// 弹出框 modal 的定位:1. 返回某一个 SlateNode; 2. 返回 null (根据当前选区自动定位)getModalPositionNode(editor: IDomEditor): SlateNode | null {return this.getSelectedImageNode(editor);}// 定义 modal 内部的 DOM ElementgetModalContentElem(editor: IDomEditor): DOMElement {const $content = this.$content || document.createElement('div');const [inputWidthContainerElem, inputWidthElem] = genModalInputElems(t('videoModule.width'),`input-width-${Math.random().toString(36).slice(2)}`,'auto');const [inputHeightContainerElem, inputHeightElem] = genModalInputElems(t('videoModule.height'),`input-height-${Math.random().toString(36).slice(2)}`,'auto');const buttonContainerElem = genModalButtonElems(`button-${Math.random().toString(36).slice(2)}`,t('videoModule.ok'));$content.append(inputWidthContainerElem);$content.append(inputHeightContainerElem);$content.append(buttonContainerElem);const imageNode = this.getSelectedImageNode(editor) as unknown as HTMLElement;// 绑定事件(第一次渲染时绑定,不要重复绑定)if (this.$content == null) {buttonContainerElem.onclick = () => {const width = Number(inputWidthElem.value);const height = Number(inputHeightElem.value);console.log(editor, isNaN(width) ? inputWidthElem.value : width ? width +'px' : 'auto', isNaN(height) ? inputHeightElem.value : height ? height +'px' : 'auto')editor.restoreSelection();// 修改尺寸SlateTransforms.setNodes(editor,{style: {width: isNaN(width) ? inputWidthElem.value : width ? width +'px' : 'auto',height: isNaN(height) ? inputHeightElem.value : height ? height +'px' : 'auto',}} as any,{match: n => DomEditor.checkNodeType(n, 'image'),})editor.hidePanelOrModal(); // 隐藏 modal}}if (imageNode == null) return $content;// 初始化 input 值const { width = 'auto', height = 'auto' } = imageNode.style;inputWidthElem.value = width || 'auto';inputHeightElem.value = height || 'auto';setTimeout(() => {inputWidthElem.focus()});return $content // 返回 DOM Element 类型// PS:也可以把 $content 缓存下来,这样不用每次重复创建、重复绑定事件,优化性能}
}export const EditImageSizeConf = {key: 'editImageSize', // 定义 menu key :要保证唯一、不重复(重要)factory() {return new EditImageSize() // 把 `YourMenuClass` 替换为你菜单的 class},
}
公用工具utils
// 生成输入框
export const genModalInputElems = (label: string, id: string, val: string): [HTMLLabelElement, HTMLInputElement] => {const $label = document.createElement('label');$label.className = 'babel-container';const $span = document.createElement('span');$span.textContent = label;const $input = document.createElement('input');$input.type = 'text';$input.id = id;$input.value = val;$label.append($span);$label.append($input);return [$label, $input];
};// 生成按钮
export const genModalButtonElems = (id: string, text: string) => {const $content = document.createElement('div');$content.className = 'button-container';const $button = document.createElement('button');$button.id = id;$button.textContent = text;$content.append($button);return $content;
};
2. 注册自定义菜单
// 注册自定义菜单useEffect(() => {try {Boot.registerMenu(EditImageSizeConf);} catch (e) {}}, [])
3. 挂载到工具栏
// 工具栏配置const toolbarConfig: Partial<IToolbarConfig> = {insertKeys: {index: 5, // 插入的位置,基于当前的 toolbarKeyskeys: ['editImageSize']}}
4. 挂载到组件hover菜单
// 编辑器配置const editorConfig: Partial<IEditorConfig> = {hoverbarKeys: {image: {menuKeys: ['editImageSize'] // 注意:要保留原有的菜单需加上之前的菜单key}}}
相关文章:
React wangEditor5 使用说明
1、支持包安装 yarn add wangeditor/editor # 或者 npm install wangeditor/editor --saveyarn add wangeditor/editor-for-react # 或者 npm install wangeditor/editor-for-react --save2、使用 import wangeditor/editor/dist/css/style.css // 引入 cssimport { useState…...
vue 实现数字验证码功能
需求:写了一个 手机发送验证码后 输入固定验证码的功能 封装成一个组件,如下: <template><div class"conts"><div class"box"><div class"code_list"><div :class"[ code_item, hideIndex 0 ? co…...
【计算机网络】HTTP协议详解(举例解释,超级详细)
文章目录 一、HTTP协议简单介绍 1、1 什么是HTTP协议 1、2 再次理解“协议” 二、HTTP请求 2、1 HTTP的工作过程 2、1、1 demo代码 2、2 URL 介绍 2、2、1 urlencode 和 urldecode 2、3 HTTP 请求格式 三、HTTP响应 3、1 响应demo 3、2 HTTP 响应格式 四、HTTP 请求和响应中的…...
PCB放置过孔技巧
合理的放置过孔能有效的节约面积。 我们根据嘉立创的pcb工艺能力中写出单双面板最小过孔为0.3mm(内径)/0.5mm(外径) 设置过孔尺寸外直径为24mil(0.61mm))内直径为12mil(0.305mm) 嘉立创PCB工艺加工能力范围说明-嘉立…...
淘宝商品详情接口数据采集用于上货,无货源选品上货,采集淘宝天猫商品详情数据
淘宝商品详情接口数据采集可用于上货。先通过关键字搜索接口,抓取到批量的商品ID,再将商品ID传入商品详情数据采集接口的请求参数中,从而达到批量抓取商品详情数据的功能。 接口名称:item_get,获取商品详情数据&#…...
DoS和DDos攻攻击
介绍 DDoS 和 DoS 攻击是我们最常见的网络攻击之一,而且历史相当悠久,算是很经典的两种攻击方式,但它们实际上是如何运作的呢? 虽然两者基本上都能够让工作停摆,但其中有很大的差异,接下来我们将逐一说明&a…...
Python实时采集Windows CPU\MEMORY\HDD使用率
文章目录 安装psutil库在Python脚本中导入psutil库获取CPU当前使用率,并打印结果获取内存当前使用率,并打印结果获取磁盘当前使用情况,并打印结果推荐阅读 要通过Python实时采集Windows性能计数器的数据,你可以使用psutil库。psut…...
【改造中序遍历算法】1038. 从二叉搜索树到更大和树
1038. 从二叉搜索树到更大和树 解题思路 改造中序遍历算法先遍历右子树 然后累加当前节点的值 再遍历左子树 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode…...
克服网络安全压力:如何掌控无限的云数据
管理云中的数字风险比以往任何时候都更加重要。数字化转型引发的云数据呈指数级增长,为安全分析师创造了一个更大的威胁环境。随着威胁行为者继续危害组织最敏感的数据,这一挑战将会加剧。 预计未来五年全球网络犯罪成本将激增,从 2022 年的…...
【数据结构和算法】--N叉树中,返回某些目标节点到根节点的所有路径
目录 一、前言二、具体实现及拓展2.1、递归-目标节点到根节点的路径数据2.2、list转换为tree结构2.3、tree转换为list结构 一、前言 这么多年工作经历中,“数据结构和算法”真的是超重要,工作中很多业务都能抽象成某种数据结构问题。下面是项目中遇到的…...
进程和线程的区别 线程之间共享的资源
线程和进程都是操作系统中的执行单位,但它们在以下几个方面存在区别: 相同处: 1.执行环境:线程和进程都有自己的执行上下文,包括程序计数器、寄存器和栈,可以独立执行指令。 2.并发性:线程和进…...
基于Matlab实现logistic方法(源码+数据)
Logistic回归是一种常用的分类算法,适用于二分类问题。本文将介绍如何使用Matlab实现Logistic回归方法,并通过一个示例演示其应用。 文章目录 引言实现步骤1. 数据准备2. 特征缩放3. 模型训练4. 模型评估 源码数据下载 引言 Logistic回归是一种广泛应用…...
leetCode 121. 买卖股票的最佳时机 贪心算法
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。 返回你可以从这笔交易中获取的最大利润。…...
《Oracle系列》Oracle 索引使用情况查看
查询用户的索引 select index_name,table_name,tablespace_name,index_type,uniqueness,statusfrom dba_indexeswhere owner <用户名>;查询用户的索引列 select index_name,table_name,column_name,index_owner,table_ownerfrom dba_ind_columnswhere table_owner &l…...
解决Invalid bound statement (not found)错误~
报错如下所示: 找了好久,刚开始以为是名称哪里写的有问题,但仔细检查了好多遍都不是 最后发现了问题如下所示: UserMapper里面的内容被我修改了,但classes中的内容还是原来的内容,所以才导致了编译器报错n…...
基于SpringBoot的反诈宣传平台设计与实现(源码+lw+部署文档+讲解等)
文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序(小蔡coding)有保障的售后福利 代码参考源码获取 前言 💗博主介绍:✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…...
【改进哈里鹰算法(NCHHO)】使用混沌和非线性控制参数来提高哈里鹰算法的优化性能,解决车联网相关的路由问题(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
【C语言】宏定义
🚩 WRITE IN FRONT🚩 🔎 介绍:"謓泽"正在路上朝着"攻城狮"方向"前进四"🔎🏅 荣誉:2021|2022年度博客之星物联网与嵌入式开发TOP5|TOP4、2021|2222年获评百大博…...
库存三层模型概述
库存分层 (1)电商库存体系分为三层:销售层、调度层、仓库层。 库存三层模型:销售库存,调度层属于订单领域-履约。实物库存属于库存领域 WMS的库存跟调度层是一致的。 但是销售库存跟调度层可能不一致,因为…...
SNERT预备队招新CTF体验赛-Web(SWCTF)
目录 1、F12 2、robots 3、game1-喂青蛙 4、game 2 - flap bird 5、game 3 - Clash 6、Get&Post 7、sql (1)手工注入 (2)工具注入 8、命令执行漏洞 9、文件上传漏洞 10、文件泄露 11、php反序列化漏洞 12、PHP绕…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...
ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
Go语言多线程问题
打印零与奇偶数(leetcode 1116) 方法1:使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...
【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...
Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)
引言 在人工智能飞速发展的今天,大语言模型(Large Language Models, LLMs)已成为技术领域的焦点。从智能写作到代码生成,LLM 的应用场景不断扩展,深刻改变了我们的工作和生活方式。然而,理解这些模型的内部…...
代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...
