diff --git a/dist.zip b/dist.zip new file mode 100644 index 0000000..799c1e2 Binary files /dev/null and b/dist.zip differ diff --git a/npminstall-debug.log b/npminstall-debug.log index 8d0f56e..4dc5b2f 100644 --- a/npminstall-debug.log +++ b/npminstall-debug.log @@ -3,7 +3,14 @@ registry: 'https://registry.npmmirror.com', pkgs: [ { - name: 'spellchecker', + name: 'tinymce', + version: 'latest', + type: 'tag', + alias: undefined, + arg: [Result] + }, + { + name: 'tinymce-kityformula-editor', version: 'latest', type: 'tag', alias: undefined, @@ -15,7 +22,7 @@ cacheDir: 'C:\\Users\\Administrator\\.npminstall_tarball', env: { npm_config_registry: 'https://registry.npmmirror.com', - npm_config_argv: '{"remain":[],"cooked":["--fix-bug-versions","--china","--userconfig=C:\\\\Users\\\\Administrator\\\\.cnpmrc","--disturl=https://cdn.npmmirror.com/binaries/node","--registry=https://registry.npmmirror.com","spellchecker"],"original":["--fix-bug-versions","--china","--userconfig=C:\\\\Users\\\\Administrator\\\\.cnpmrc","--disturl=https://cdn.npmmirror.com/binaries/node","--registry=https://registry.npmmirror.com","spellchecker"]}', + npm_config_argv: '{"remain":[],"cooked":["--fix-bug-versions","--china","--userconfig=C:\\\\Users\\\\Administrator\\\\.cnpmrc","--disturl=https://cdn.npmmirror.com/binaries/node","--registry=https://registry.npmmirror.com","tinymce","tinymce-kityformula-editor"],"original":["--fix-bug-versions","--china","--userconfig=C:\\\\Users\\\\Administrator\\\\.cnpmrc","--disturl=https://cdn.npmmirror.com/binaries/node","--registry=https://registry.npmmirror.com","tinymce","tinymce-kityformula-editor"]}', npm_config_user_agent: 'npminstall/7.12.0 npm/? node/v16.14.1 win32 x64', npm_config_cache: 'C:\\Users\\Administrator\\.npminstall_tarball', NODE: 'C:\\Program Files\\nodejs\\node.exe', @@ -119,7 +126,10 @@ }, fsevents: { host: 'https://cdn.npmmirror.com/binaries/fsevents' }, nodejieba: { host: 'https://cdn.npmmirror.com/binaries/nodejieba' }, - canvas: { host: 'https://cdn.npmmirror.com/binaries/canvas' }, + canvas: { + host: 'https://cdn.npmmirror.com/binaries/canvas', + remote_path: 'v{version}' + }, 'skia-canvas': { host: 'https://cdn.npmmirror.com/binaries/skia-canvas' }, 'flow-bin': { replaceHost: 'https://github.com/facebook/flow/releases/download/v', diff --git a/package.json b/package.json index 358bd98..1e17ced 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "html2canvas": "^1.4.1", "install": "^0.13.0", "jszip": "^3.10.1", + "katex": "^0.16.21", "mammoth": "^1.5.1", "mathlive": "^0.104.0", "mavon-editor": "^2.6.17", diff --git a/public/index.html b/public/index.html index f588847..b185a6f 100644 --- a/public/index.html +++ b/public/index.html @@ -15,7 +15,8 @@
- + + diff --git a/public/js/global-math.js b/public/js/global-math.js new file mode 100644 index 0000000..6e5aaf4 --- /dev/null +++ b/public/js/global-math.js @@ -0,0 +1,110 @@ +// 确保 MathJax 配置正确 +window.MathJax = window.MathJax || { + loader: { + load: ['[tex]/ams', '[tex]/newcommand'] + }, + tex: { + inlineMath: [['$', '$'], ['\\(', '\\)']], + displayMath: [['$$', '$$'], ['\\[', '\\]']] + }, + a11y: { + // 启用 MathJax 可访问性功能,确保使用 aria-label + texHints: true, + screenReader: true, + mathml: { + enable: true, + }, + label: { + // 公式的 aria-label 配置 + enable: true, + texClass: 'MathJax-Label', + form: 'LaTeX' + } + }, + svg: { fontCache: 'global' } +}; + +// **定义全局渲染方法** +window.renderMathJax = function (tinymceId) { + // if (window.MathJax && typeof window.MathJax.typesetPromise === "function") { + // console.log("正在渲染 MathJax 公式..."); + + // // 如果提供了 TinyMCE 编辑器 ID + // if (tinymceId) { + // const editorInstance = window.tinymce.get(tinymceId); // 根据 ID 获取编辑器实例 + // if (!editorInstance) { + // return; + // } + + // // 获取指定的 TinyMCE 编辑器中的内容 + // const editorBody = editorInstance.getBody(); + + // // 渲染 editorBody 中所有 标签,并使用 data-latex 的值 + // const wmathElements = editorBody.querySelectorAll('wmath'); + // wmathElements.forEach((element) => { + // // 检查 标签是否包含有效的 data-latex 属性值 + // const latexContent = element.getAttribute('data-latex'); + // if (latexContent) { + // // 将元素的内部内容替换为 data-latex 的值 + // element.innerHTML = latexContent; + + // // 渲染 MathJax 公式 + // window.MathJax.typesetPromise([element]).catch((err) => { + // console.warn("TinyMCE MathJax 渲染失败:", err); + // }); + // } + // }); + + // // 渲染 editorBody 中所有 标签(MathML 内容) + // const mathElements = editorBody.querySelectorAll('math'); + // mathElements.forEach((element) => { + // // 渲染 MathJax 公式 + // window.MathJax.typesetPromise([element]).catch((err) => { + // console.warn("MathJax 渲染失败:", err); + // }); + // }); + + // } else { + // // 处理全局文档中的 标签 + // const wmathElements = document.querySelectorAll('wmath'); + // wmathElements.forEach((element) => { + // // 检查 标签是否包含有效的 data-latex 属性值 + // const latexContent = element.getAttribute('data-latex'); + // if (latexContent) { + // // 将元素的内部内容替换为 data-latex 的值 + // element.innerHTML = latexContent; + + // // 渲染 MathJax 公式 + // window.MathJax.typesetPromise([element]).catch((err) => { + // console.warn("MathJax 渲染失败:", err); + // }); + // } + // }); + + // // 处理全局文档中的 标签(MathML 内容) + // const mathElements = document.querySelectorAll('math'); + // mathElements.forEach((element) => { + // // 渲染 MathJax 公式 + // window.MathJax.typesetPromise([element]).catch((err) => { + // console.warn("MathJax 渲染失败:", err); + // }); + // }); + // } + + // } else { + // console.warn("MathJax 未正确加载!"); + // } +} + + + + + + + + + + + + + diff --git a/src/assets/css/main.css b/src/assets/css/main.css index f53d356..49dd4e7 100644 --- a/src/assets/css/main.css +++ b/src/assets/css/main.css @@ -1258,7 +1258,7 @@ a { /* 自动调整列宽 */ text-align: left; font-family: 'Charis SIL' !important; - font-size: 12px !important; + font-size: 14px !important; mso-font-kerning: 1pt !important; line-height: 20px !important; mos-line-height: 20px !important; @@ -1274,7 +1274,7 @@ a { /* 长单词自动换行 */ word-break: normal; font-family: 'Charis SIL' !important; - font-size: 12px !important; + font-size: 14px !important; mso-font-kerning: 1pt !important; line-height: 20px !important; mos-line-height: 20px !important; @@ -1303,7 +1303,7 @@ a { margin: 0; font-family: 'Charis SIL' !important; - font-size: 12px !important; + font-size: 14px !important; mso-font-kerning: 1pt !important; line-height: 20px !important; mos-line-height: 20px !important; @@ -1313,7 +1313,7 @@ a { color: #000000; text-align: left !important; font-family: 'Charis SIL' !important; - font-size: 12px !important; + font-size: 14px !important; mso-font-kerning: 1pt !important; line-height: 20px !important; mos-line-height: 20px !important; @@ -1322,7 +1322,7 @@ a { .word-container table .color-highlight { color: rgb(0, 130, 170) !important; font-family: 'Charis SIL' !important; - font-size: 12px !important; + font-size: 14px !important; mso-font-kerning: 1pt !important; line-height: 20px !important; mos-line-height: 20px !important; @@ -1433,4 +1433,12 @@ a { /* 设置字体颜色 */ text-decoration: line-through !important; /* 设置字体颜色 */ -} \ No newline at end of file +} +mjx-container { + font-size: 14px !important; + } + wmath{ + width: 100%; + display: block;display: flex; + } + \ No newline at end of file diff --git a/src/common/js/commonJS.js b/src/common/js/commonJS.js index fc20718..77040c9 100644 --- a/src/common/js/commonJS.js +++ b/src/common/js/commonJS.js @@ -1,4 +1,5 @@ import Vue from 'vue'; +import katex from 'katex'; import JSZip from 'jszip'; import Common from '@/components/common/common' import Tiff from 'tiff.js'; @@ -18,6 +19,90 @@ const capitalizeFirstLetter = function (text) { }); }; export default { + extractLatexFromMathJax() { + // 获取所有 MathJax 渲染的公式容器 + const mathContainers = document.querySelectorAll('mjx-container'); + + mathContainers.forEach(container => { + // 查找每个渲染公式对应的 MathML 部分 + const mathElement = container.querySelector('mjx-math'); + console.log('mathElement at line 28:', mathElement) + + if (mathElement) { + // 获取 MathJax 渲染的公式对象 + const jax = window.MathJax.getJaxFor(mathElement); + console.log('jax at line 32:', jax) + + if (jax) { + // 使用 MathJax API 获取公式的 LaTeX 代码 + const latex = jax.getLiteral(); // 获取 LaTeX 表达式 + console.log('提取到的 LaTeX 公式:', latex); // 输出 LaTeX 代码 + } else { + console.warn('MathJax 对象未找到'); + } + } + }); + }, + + + + + replaceWMathContent(inputHtml, callback) { + // 使用正则表达式查找所有 标签,并提取 data-latex 的内容 + var str = inputHtml.replace(/[^<]*<\/wmath>/g, function(match, latexContent) { + // 返回 标签,内容替换为 data-latex 的值 + return `${latexContent}`; + }); + + // 调用回调函数并传递处理后的结果 + callback(str); + + // 输出结果到控制台 + console.log('Processed HTML:', str); + } + + + , + // **解析 MathJax 公式,获取 LaTeX** + async extractMathJaxLatex(cell, callback) { + console.log('cell at line 67:', cell) + return new Promise((resolve, reject) => { + // Step 1: First, process the math content and extract LaTeX from tags + let updatedContent = cell.innerHTML; // Start with the cell's inner HTML + console.log('cell content at the start:', updatedContent); + + // Find all elements + const wmathElements = cell.querySelectorAll('wmath'); + wmathElements.forEach((element) => { + // Get the LaTeX content from the data-latex attribute + const latexContent = element.getAttribute('data-latex'); + console.log('LaTeX content from data-latex:', latexContent); + + // Replace the tag with its LaTeX content wrapped in $$...$$ + updatedContent = updatedContent.replace(element.outerHTML, `${latexContent}`); + }); + + console.log('updatedContent after processing wmath tags:', updatedContent); + + // Step 2: Now extract content without the outer tags + updatedContent = this.extractContentWithoutOuterSpan(updatedContent); + console.log('updatedContent after extractContentWithoutOuterSpan:', updatedContent); + + // Step 3: Call the callback function with the final updated content + // callback(updatedContent); + + // Resolve the promise with the final content + resolve(updatedContent); + }); + } + + + , + renderLatex(latexString) { + return katex.renderToString(latexString, { + throwOnError: false + }); + }, decodeHtml(html) { var txt = document.createElement('textarea'); txt.innerHTML = html; @@ -25,9 +110,15 @@ export default { }, //去掉最外层自定义的span标签 extractContentWithoutOuterSpan(cell) { + console.log('cell at line 90:', cell) var str = '' + if(!cell){ + return '' + } // 获取单元格的 HTML 内容 - let htmlContent = cell.innerHTML.trim(); + let htmlContent = cell.trim(); + + console.log('htmlContent at line 94:', htmlContent) str = this.transformHtmlString(htmlContent) @@ -68,9 +159,10 @@ export default { - + console.log('str at line 141:', str) // 如果没有 标签,直接返回原始 HTML 内容 return str; + }, async extractPastedWordTablesToArrays(pastedHtml, callback) { try { @@ -236,31 +328,31 @@ export default { rowspan = 1; // 初始化 rowspan let nextRowIdx = rowIndex + 1; let maxRowspan = rows.length - rowIndex; // 确保 rowspan 不会超过剩余行数 - + while (nextRowIdx < rows.length) { const nextRowCells = rows[nextRowIdx].getElementsByTagNameNS(namespace, "tc"); // console.log(`🔍 检查下一行单元格 at row ${nextRowIdx}, col ${cellIndex}:`, nextRowCells); - + if (nextRowCells.length > cellIndex) { const nextCell = nextRowCells[cellIndex]; - + if (!nextCell) { // console.warn(`⚠️ nextCell 未定义 at row ${nextRowIdx}, col ${cellIndex}`); break; } - + const nextVMerge = nextCell.getElementsByTagNameNS(namespace, "vMerge")[0]; // console.log(`🔍 检查 nextVMerge at row ${nextRowIdx}, col ${cellIndex}:`, nextVMerge); - + // **如果 nextVMerge 为空,则不应继续增长 rowspan** if (!nextVMerge) { // console.log(`⚠️ nextVMerge 为空 at row ${nextRowIdx}, col ${cellIndex} - 停止扩展`); break; } - + // **解析 nextVMerge 的值** const vMergeVal = nextVMerge.getAttribute("w:val"); - + if (!vMergeVal || vMergeVal === "continue") { if (rowspan < maxRowspan) { // 限制 rowspan 最大值 rowspan++; @@ -286,7 +378,7 @@ export default { continue; } } - + console.log('rowspan at line 265:', rowspan) const currentLevelNumbers = {}; for (const paragraph of paragraphs) { @@ -621,11 +713,23 @@ export default { // inputHtml = inputHtml.replace(/(<[^>]+) style="[^"]*"/g, '$1'); // 移除style属性 // inputHtml = inputHtml.replace(/(<[^>]+) class="[^"]*"/g, '$1'); // 移除class属性 - inputHtml = inputHtml.replace(/<([a-zA-Z0-9]+)[^>]*>/g, '<$1>'); // 删除标签上的所有属性 + // inputHtml = inputHtml.replace(/<([a-zA-Z0-9]+)[^>]*>/g, '<$1>'); // 删除标签上的所有属性 + inputHtml = inputHtml.replace(/<([a-zA-Z0-9]+)([^>]*)>/g, function (match, tag, attributes) { + // 使用正则表达式删除属性(保留 data-latex) + let updatedAttributes = attributes.replace(/\s([a-zA-Z0-9-]+)(="[^"]*")?/g, function (attrMatch, attrName) { + // 只保留 data-latex 属性,其他属性删除 + if (attrName === "data-latex") { + return attrMatch; + } + return ''; // 删除其他属性 + }); + // 返回标签,保留 data-latex 属性 + return `<${tag}${updatedAttributes}>`; + }); // 2. 删除所有不需要的标签 (除 `strong`, `em`, `sub`, `sup`, `b`, `i` 外的所有标签) - inputHtml = inputHtml.replace(/<(?!\/?(strong|em|sub|sup|b|i|blue))[^>]+>/g, ''); // 删除不需要的标签 + inputHtml = inputHtml.replace(/<(?!\/?(strong|em|sub|sup|b|i|blue|wmath))[^>]+>/g, ''); // 删除不需要的标签 // 3. 如果有 `` 和 `` 标签,去掉内部样式并保留内容 inputHtml = inputHtml.replace(/]*>/g, '').replace(/<\/span>/g, ''); // 去除span标签 @@ -645,62 +749,74 @@ export default { let tempDiv = document.createElement('div'); tempDiv.innerHTML = content; // 解析 HTML 内容 let paragraphs = tempDiv.querySelectorAll("p"); // 选取所有

作为数据项 - + // 2️⃣ 将

内容转换为数组,并处理内容 let parsedData = Array.from(paragraphs).map(p => { let text = p.innerHTML.trim(); // 获取内容,去除两端空格 - + text= this.transformHtmlString(text) // 3️⃣ **正确移除 (Word 复制的无效标签)** - text = text.replace(/<\/?o:p[^>]*>/g, ""); - + text = text.replace(/<\/?o:p[^>]*>/g, ""); + // 4️⃣ **移除所有 style="..."** text = text.replace(/\s*style="[^"]*"/gi, ""); - + // 5️⃣ **修正标签替换** text = text.replace(//gi, "").replace(/<\/strong>/gi, ""); text = text.replace(//gi, "").replace(/<\/em>/gi, ""); - + // 6️⃣ **移除空的 span、b、i 标签** text = text.replace(/\s*<\/span>/gi, ""); text = text.replace(/\s*<\/b>/gi, ""); text = text.replace(/\s*<\/i>/gi, ""); - + // 7️⃣ **确保不移除半个标签(修复匹配规则)** text = text.replace(/<[^\/>]+>\s*<\/[^>]+>/gi, match => { return match.trim() === "" ? "" : match; }); - + // 8️⃣ **返回最终内容** return text.trim() === "" ? "" : text; }); - + console.log(parsedData); // 输出数组,方便调试 return parsedData; } - + , - parseTableToArray(tableString, callback) { + async parseTableToArray(tableString, callback) { const parser = new DOMParser(); const doc = parser.parseFromString(tableString, 'text/html'); const rows = doc.querySelectorAll('table tr'); // 获取所有的行() - callback(Array.from(rows).map(row => { - const cells = row.querySelectorAll('th, td'); // 获取每个行中的单元格(包括 和 ) - return Array.from(cells).map(cell => ({ + // 使用 Promise 来处理异步的 MathJax 解析 + const result = await Promise.all( + Array.from(rows).map(async (row) => { + const cells = row.querySelectorAll('th, td'); // 获取每个行中的单元格(包括 和 ) + return await Promise.all( + Array.from(cells).map(async (cell) => { + const text = await this.extractMathJaxLatex(cell); + return { + text, + colspan: cell.getAttribute('colspan') ? parseInt(cell.getAttribute('colspan')) : 1, // 提取 colspan,默认值为 1 + rowspan: cell.getAttribute('rowspan') ? parseInt(cell.getAttribute('rowspan')) : 1 // 提取 rowspan,默认值为 1 + }; + }) + ); + }) + ); + console.log('result at line 78611:', result) + callback(result) + // 返回处理后的数组 - text: this.extractContentWithoutOuterSpan(cell), - colspan: cell.getAttribute('colspan') ? parseInt(cell.getAttribute('colspan')) : 1, // 提取 colspan,默认值为 1 - rowspan: cell.getAttribute('rowspan') ? parseInt(cell.getAttribute('rowspan')) : 1 // 提取 rowspan,默认值为 1 - })); - })); }, + parseNumbering(numberingDoc) { const numberingMap = new Map(); if (!numberingDoc) return numberingMap; @@ -1834,19 +1950,14 @@ export default { }); ed.ui.registry.addButton('LateX', { text: 'LateX', // 按钮文本 - className: 'custom-button-blue', // 添加自定义类 + // className: 'custom-button-blue', // 添加自定义类 // shortcut: "Ctrl+J", onAction: function () { - // 在选中的文本周围包裹 标签 - // var selectedText = ed.selection.getContent(); - // console.log('selectedText at line 529:', selectedText); - // if (selectedText) { - // var wrappedText = `${selectedText}`; - // ed.selection.setContent(wrappedText); - // } else { - // this.$message.error('请选择要添加蓝色的文本'); - // } - } + window.open('/LateX?id=4477', '_blank', 'width=600,height=400'); + + // 在新页面中插入 MathLive 编辑器 + // formulaWindow.document.write(``); + } }); ed.ui.registry.addButton('myuppercase', { diff --git a/src/components/common/Home.vue b/src/components/common/Home.vue index 97382ae..7515b4b 100644 --- a/src/components/common/Home.vue +++ b/src/components/common/Home.vue @@ -1,31 +1,42 @@