From f996ec9bae3c067ed441d188d15661d6e7e8eb1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A1=97=E8=A7=92=E5=B0=8F=E6=9E=97?= <1013335014@qq.com> Date: Tue, 4 Jun 2024 11:45:31 +0800 Subject: [PATCH] =?UTF-8?q?Feat=EF=BC=9A=E6=94=AF=E6=8C=81=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E5=9C=A8=E5=AF=8C=E6=96=87=E6=9C=AC=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E6=A1=86=E4=B8=AD=E7=BC=96=E8=BE=91=E6=95=B0=E5=AD=A6=E5=85=AC?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/constants/defaultOptions.js | 8 +- simple-mind-map/src/plugins/Formula.js | 77 +++++++++++++++++++ simple-mind-map/src/plugins/RichText.js | 12 ++- 3 files changed, 94 insertions(+), 3 deletions(-) diff --git a/simple-mind-map/src/constants/defaultOptions.js b/simple-mind-map/src/constants/defaultOptions.js index aa14f6a7..8892f628 100644 --- a/simple-mind-map/src/constants/defaultOptions.js +++ b/simple-mind-map/src/constants/defaultOptions.js @@ -332,5 +332,11 @@ export const defaultOpt = { // 添加附加的节点前置内容,前置内容指和文本同一行的区域中的前置内容,不包括节点图片部分 createNodePrefixContent: null, // 添加附加的节点后置内容,后置内容指和文本同一行的区域中的后置内容,不包括节点图片部分 - createNodePostfixContent: null + createNodePostfixContent: null, + // 是否开启在富文本编辑框中直接编辑数学公式 + enableEditFormulaInRichTextEdit: true, + // 转换富文本内容,当进入富文本编辑时,可以通过该参数传递一个函数,函数接收文本内容,需要返回你处理后的文本内容 + transformRichTextOnEnterEdit: null, + // 可以传递一个函数,即将结束富文本编辑前会执行该函数,函数接收richText实例,所以你可以在此时机更新quill文档数据 + beforeHideRichTextEdit: null } diff --git a/simple-mind-map/src/plugins/Formula.js b/simple-mind-map/src/plugins/Formula.js index 2e031d55..5d628de3 100644 --- a/simple-mind-map/src/plugins/Formula.js +++ b/simple-mind-map/src/plugins/Formula.js @@ -10,9 +10,18 @@ class Formula { this.opt = opt this.mindMap = opt.mindMap window.katex = katex + this.init() this.extendQuill() } + init() { + if (this.mindMap.opt.enableEditFormulaInRichTextEdit) { + this.mindMap.opt.transformRichTextOnEnterEdit = + this.latexRichToText.bind(this) + this.mindMap.opt.beforeHideRichTextEdit = this.formatLatex.bind(this) + } + } + // 获取katex配置 getKatexConfig() { const config = { @@ -59,6 +68,74 @@ class Formula { richTextPlugin.setTextStyleIfNotRichText(richTextPlugin.node) richTextPlugin.hideEditText([node]) } + + // 将公式富文本转换为公式源码 + latexRichToText(nodeText) { + if (nodeText.indexOf('class="ql-formula"') !== -1) { + const parser = new DOMParser() + const doc = parser.parseFromString(nodeText, 'text/html') + const els = doc.getElementsByClassName('ql-formula') + for (const el of els) + nodeText = nodeText.replace( + el.outerHTML, + `\$${el + .getAttribute('data-value') + .replaceAll('&', '&') + .replaceAll('<', '<') + .replaceAll('>', '>')}\$` + ) + } + return nodeText + } + + // 使用格式化的 latex 字符串内容更新 quill 内容:输入 $*****$ + formatLatex(richText) { + const contents = richText.quill.getContents() + const ops = contents.ops + let mod = false + for (let i = ops.length - 1; i >= 0; i--) { + const op = ops[i] + const insert = op.insert + if (insert && typeof insert !== 'object' && insert !== '\n') { + if (/\$.+?\$/g.test(insert)) { + const m = [...insert.matchAll(/\$.+?\$/g)] + const arr = insert.split(/\$.+?\$/g) + for (let j = m.length - 1; j >= 0; j--) { + const exp = m[j]?.[0].slice(1, -1) ?? null // $...$ 之间的表达式 + if (exp !== null && exp.trim().length > 0) { + const isLegal = this.checkFormulaIsLegal(exp) + if (isLegal) { + arr.splice(j + 1, 0, { insert: { formula: exp } }) // 添加到对应位置之后 + mod = true + } else { + arr.splice(j + 1, 0, '') + } + } else arr.splice(j + 1, 0, '') // 表达式为空时,占位 + } + while (arr.length > 0) { + let v = arr.pop() + if (typeof v === 'string') { + if (v.length < 1) continue + v = { insert: v } + } + v['attributes'] = ops[i]['attributes'] + ops.splice(i + 1, 0, v) + } + ops.splice(i, 1) // 删除原来的字符串 + } + } + } + if (mod) richText.quill.setContents(contents) + } + + checkFormulaIsLegal(str) { + try { + katex.renderToString(str) + return true + } catch (e) { + return false + } + } } Formula.instanceName = 'formula' diff --git a/simple-mind-map/src/plugins/RichText.js b/simple-mind-map/src/plugins/RichText.js index eb55c6e1..d3a5fcc3 100644 --- a/simple-mind-map/src/plugins/RichText.js +++ b/simple-mind-map/src/plugins/RichText.js @@ -171,7 +171,8 @@ class RichText { customInnerElsAppendTo, nodeTextEditZIndex, textAutoWrapWidth, - selectTextOnEnterEditText + selectTextOnEnterEditText, + transformRichTextOnEnterEdit } = this.mindMap.opt this.node = node this.isInserting = isInserting @@ -241,7 +242,10 @@ class RichText { } } // 节点文本内容 - const nodeText = node.getData('text') + let nodeText = node.getData('text') + if (typeof transformRichTextOnEnterEdit === 'function') { + nodeText = transformRichTextOnEnterEdit(nodeText) + } // 是否是空文本 const isEmptyText = isUndef(nodeText) // 是否是非空的非富文本 @@ -325,6 +329,10 @@ class RichText { if (!this.showTextEdit) { return } + const { beforeHideRichTextEdit } = this.mindMap.opt + if (typeof beforeHideRichTextEdit === 'function') { + beforeHideRichTextEdit(this) + } let html = this.getEditText() let list = nodes && nodes.length > 0 ? nodes : this.mindMap.renderer.activeNodeList