数字公式优化

This commit is contained in:
2026-05-29 16:11:23 +08:00
parent 6288c3e2ea
commit 4b1a10b20c
11 changed files with 826 additions and 114 deletions

View File

@@ -33,6 +33,7 @@
"katex": "^0.16.21",
"mammoth": "^1.5.1",
"mathlive": "^0.104.0",
"mathml-to-latex": "^1.5.0",
"mavon-editor": "^2.6.17",
"multi-items-input": "^0.2.0",
"pizzip": "^3.1.7",

View File

@@ -8,14 +8,12 @@ window.MathJax = window.MathJax || {
displayMath: [['$$', '$$'], ['\\[', '\\]']]
},
a11y: {
// 启用 MathJax 可访问性功能,确保使用 aria-label
texHints: true,
screenReader: true,
mathml: {
enable: true,
},
label: {
// 公式的 aria-label 配置
enable: true,
texClass: 'MathJax-Label',
form: 'LaTeX'
@@ -24,85 +22,71 @@ window.MathJax = window.MathJax || {
svg: { fontCache: 'global' }
};
function stripLatexDelimiters(latex) {
return String(latex || '')
.trim()
.replace(/^\$\$/, '')
.replace(/\$\$$/, '')
.replace(/^\$/, '')
.replace(/\$$/, '')
.trim();
}
/** 块/行内统一用 $$ 渲染,保证公式大小一致;排版由 data-wrap + CSS 控制 */
function buildLatexContentForRender(latex) {
const raw = stripLatexDelimiters(latex);
if (!raw) return '';
return '$$' + raw + '$$';
}
function prepareWmathElement(element) {
const latexContent = element.getAttribute('data-latex');
if (!latexContent) return;
element.innerHTML = buildLatexContentForRender(latexContent);
}
// **定义全局渲染方法**
window.renderMathJax = function (tinymceId) {
if (window.MathJax && typeof window.MathJax.typesetPromise === "function") {
// console.log("正在渲染 MathJax 公式...");
// 如果提供了 TinyMCE 编辑器 ID
if (window.MathJax && typeof window.MathJax.typesetPromise === 'function') {
if (tinymceId) {
const editorInstance = window.tinymce.get(tinymceId); // 根据 ID 获取 TinyMCE 实例
const editorInstance = window.tinymce.get(tinymceId);
if (!editorInstance) return;
const editorBody = editorInstance.getBody();
// 获取所有 <wmath> 元素
const wmathElements = editorBody.querySelectorAll('wmath');
if (wmathElements.length > 0) {
wmathElements.forEach((element) => {
const latexContent = element.getAttribute('data-latex');
if (latexContent) {
// 将公式内容填入标签内部
element.innerHTML = latexContent;
// 使用 MathJax 渲染该元素
window.MathJax.typesetPromise([element]).catch((err) => {
console.warn("TinyMCE MathJax 渲染失败:", err);
});
}
prepareWmathElement(element);
window.MathJax.typesetPromise([element]).catch((err) => {
console.warn('TinyMCE MathJax 渲染失败:', err);
});
});
}
// 渲染 <math>MathML标签如果有的话
const mathElements = editorBody.querySelectorAll('math');
if (mathElements.length > 0) {
window.MathJax.typesetPromise(Array.from(mathElements)).catch((err) => {
console.warn("MathML 渲染失败:", err);
console.warn('MathML 渲染失败:', err);
});
}
}
else {
// 处理全局文档中的 <wmath> 标签
} else {
const wmathElements = document.querySelectorAll('wmath');
wmathElements.forEach((element) => {
// 检查 <wmath> 标签是否包含有效的 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);
});
}
prepareWmathElement(element);
window.MathJax.typesetPromise([element]).catch((err) => {
console.warn('MathJax 渲染失败:', err);
});
});
// 处理全局文档中的 <math> 标签MathML 内容)
const mathElements = document.querySelectorAll('math');
mathElements.forEach((element) => {
// 渲染 MathJax 公式
window.MathJax.typesetPromise([element]).catch((err) => {
console.warn("MathJax 渲染失败:", err);
console.warn('MathJax 渲染失败:', err);
});
});
}
} else {
console.warn("MathJax 未正确加载!");
console.warn('MathJax 未正确加载!');
}
}
};

View File

@@ -19,8 +19,8 @@ const service = axios.create({
// baseURL: 'https://submission.tmrjournals.com/', //正式 记得切换
// baseURL: 'http://www.tougao.com/', //测试本地 记得切换
// baseURL: 'http://192.168.110.110/tougao/public/index.php/',
// baseURL: '/api', //本地
baseURL: '/', //正式
baseURL: '/api', //本地
// baseURL: '/', //正式
});

View File

@@ -1626,6 +1626,166 @@ wmath {
position: fixed !important;
}
/* TinyMCE 行内公式输入框Latex2 */
.tinymce-inline-math-overlay {
position: fixed;
z-index: 100000;
overflow: visible;
}
.tinymce-inline-math-panel {
min-width: 360px;
max-width: min(560px, 92vw);
width: max-content;
background: #fff;
border: 1px solid #e8edf5;
border-radius: 10px;
box-shadow: 0 12px 32px rgba(15, 23, 42, 0.12), 0 2px 8px rgba(15, 23, 42, 0.06);
overflow: hidden;
}
.tinymce-inline-math-field-wrap {
padding: 8px 10px 6px;
background: linear-gradient(180deg, #fafbfd 0%, #f5f7fb 100%);
border-bottom: 1px solid #eef1f6;
max-height: 108px;
overflow: auto;
}
.tinymce-inline-math-overlay math-field {
display: block;
width: 100%;
min-height: 36px;
padding: 4px 8px;
box-sizing: border-box;
background: #fff;
border: 1px solid #dfe4ec !important;
border-radius: 8px;
box-shadow: inset 0 1px 2px rgba(15, 23, 42, 0.04);
}
.tinymce-inline-math-overlay math-field:focus-within {
border-color: #1654f7 !important;
box-shadow: 0 0 0 3px rgba(22, 84, 247, 0.1);
}
.tinymce-inline-math-overlay math-field::part(menu-toggle) {
display: none !important;
}
.tinymce-inline-math-footer {
display: flex;
flex-wrap: nowrap;
align-items: center;
justify-content: space-between;
gap: 10px;
padding: 6px 10px 8px;
background: #fff;
}
.tinymce-inline-math-tools {
display: flex;
flex-wrap: nowrap;
align-items: center;
gap: 6px;
flex: 0 1 auto;
min-width: 0;
white-space: nowrap;
}
.tinymce-inline-math-copy,
.tinymce-inline-math-wrap-toggle {
border: none;
background: transparent;
color: #1654f7;
font-size: 12px;
line-height: 18px;
padding: 2px 0;
cursor: pointer;
white-space: nowrap;
}
.tinymce-inline-math-copy:hover,
.tinymce-inline-math-wrap-toggle:hover,
.tinymce-inline-math-copy.is-copied {
color: #0f45d4;
}
.tinymce-inline-math-tool-divider {
display: inline-block;
width: 1px;
height: 14px;
background: #dcdfe6;
flex-shrink: 0;
}
.tinymce-inline-math-actions {
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
}
.tinymce-inline-math-confirm,
.tinymce-inline-math-cancel {
height: 28px;
padding: 0 12px;
border-radius: 5px;
font-size: 12px;
cursor: pointer;
line-height: 26px;
white-space: nowrap;
}
.tinymce-inline-math-confirm {
border: none;
background: #1654f7;
color: #fff;
}
.tinymce-inline-math-confirm:hover {
background: #0f45d4;
}
.tinymce-inline-math-cancel {
border: 1px solid #dcdfe6;
background: #fff;
color: #606266;
}
.tinymce-inline-math-cancel:hover {
border-color: #c0c4cc;
color: #303133;
}
.tinymce-wmath-context-menu {
position: fixed;
z-index: 100001;
min-width: 200px;
padding: 6px 0;
background: #fff;
border: 1px solid #e4e7ed;
border-radius: 6px;
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.12);
}
.tinymce-wmath-context-item {
display: block;
width: 100%;
padding: 8px 16px;
border: none;
background: transparent;
color: #303133;
font-size: 13px;
text-align: left;
cursor: pointer;
}
.tinymce-wmath-context-item:hover {
background: #f5f7fa;
color: #1654f7;
}
.sticky-header {
position: sticky;
top: 0;

View File

@@ -3,6 +3,9 @@ import Vue from 'vue';
import katex from 'katex';
import JSZip from 'jszip';
import mammoth from "mammoth";
import { MathfieldElement } from 'mathlive';
import 'mathlive/dist/mathlive-static.css';
import 'mathlive/dist/mathlive-fonts.css';
import { importWordDocumentWithMath as parseWordDocumentWithMath, parseHtmlToLatex as convertHtmlToLatex } from '@/utils/wordMathImport';
import api from '../../api/index.js';
import Common from '@/components/common/common'
@@ -26,6 +29,499 @@ const replaceNegativeSign = function (text) {
return text;
};
let activeInlineMathOverlay = null;
function escapeLatexForWmathAttr(latex) {
return String(latex || '')
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/</g, '&lt;');
}
function buildStoredLatex(rawLatex) {
const t = String(rawLatex || '').trim();
if (!t) return '';
if (/^\$\$[\s\S]+\$\$$/.test(t)) return t;
if (/^\$[\s\S]+\$$/.test(t)) return `$$${t.replace(/^\$|\$$/g, '')}$$`;
return `$$${t}$$`;
}
function resolveWmathWrapMode(latex) {
const t = String(latex || '')
.trim()
.replace(/^\$\$/, '')
.replace(/\$\$$/, '');
if (!t) return 'block';
if (/^\\begin\{/.test(t) || /\n\s*\n/.test(t)) return 'block';
if (/\\frac|\\dfrac|\\tfrac|\\sum|\\int|\\prod|\\lim|\\sqrt\{|\\displaystyle/.test(t)) {
return 'block';
}
return 'inline';
}
function createWmathElementHtml(rawLatex, uid, wrapMode) {
const storedLatex = buildStoredLatex(rawLatex);
const safeLatex = escapeLatexForWmathAttr(storedLatex);
const mode = normalizeWmathWrapMode(wrapMode);
return (
'<wmath contenteditable="false" data-id="' +
uid +
'" data-latex="' +
safeLatex +
'" data-wrap="' +
mode +
'">' +
storedLatex +
'</wmath>'
);
}
function parseLatexFromWmath(wmathElement) {
const raw = (wmathElement && wmathElement.getAttribute('data-latex')) || '';
return raw
.replace(/^\$\$/, '')
.replace(/\$\$$/, '')
.replace(/^\$/, '')
.replace(/\$$/, '')
.trim();
}
function getEventElementTarget(target) {
if (!target) return null;
if (target.nodeType === 3) return target.parentNode;
if (target.nodeType === 9) return null;
return target;
}
function findWmathAncestor(node, root) {
let el = getEventElementTarget(node);
while (el && el !== root) {
if (el.nodeType === 1 && el.nodeName && el.nodeName.toLowerCase() === 'wmath') {
return el;
}
el = el.parentNode;
}
return null;
}
function stripMceSelectedAttr(html) {
return String(html || '').replace(/\s*data-mce-selected="[^"]*"/gi, '');
}
function normalizeWmathWrapMode(wrap) {
return wrap === 'inline' ? 'inline' : 'block';
}
function getOppositeWrapLabel(wrapMode) {
const target = wrapMode === 'inline' ? 'Text above and below' : 'Inline with text';
return 'Change to ' + target;
}
function clearEditorMathSelection(ed) {
if (!ed || !ed.getBody) return;
const body = ed.getBody();
if (!body) return;
body.querySelectorAll('[data-mce-selected]').forEach((el) => {
el.removeAttribute('data-mce-selected');
});
try {
ed.selection.collapse(false);
} catch (err) {
// ignore
}
}
function closeWmathContextMenu() {
const menu = document.getElementById('tinymce-wmath-context-menu');
if (menu) menu.remove();
}
function copyTextToClipboard(text, onSuccess) {
const value = String(text || '').trim();
if (!value) return;
const done = typeof onSuccess === 'function' ? onSuccess : () => {};
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(value).then(done).catch(() => {
const ta = document.createElement('textarea');
ta.value = value;
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
document.body.removeChild(ta);
done();
});
} else {
const ta = document.createElement('textarea');
ta.value = value;
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
document.body.removeChild(ta);
done();
}
}
function showWmathContextMenu(ed, wmathElement, event) {
if (!wmathElement || !event) return;
if (document.getElementById('tinymce-inline-math-overlay')) return;
closeWmathContextMenu();
event.preventDefault();
event.stopPropagation();
const latex = parseLatexFromWmath(wmathElement);
const wrapMode = wmathElement.getAttribute('data-wrap') || 'block';
const uid = wmathElement.getAttribute('data-id') || 'wmath-' + Math.random().toString(36).substr(2, 9);
const wrapLabel = getOppositeWrapLabel(wrapMode);
const nextWrap = wrapMode === 'inline' ? 'block' : 'inline';
const menu = document.createElement('div');
menu.id = 'tinymce-wmath-context-menu';
menu.className = 'tinymce-wmath-context-menu';
menu.innerHTML =
'<button type="button" class="tinymce-wmath-context-item" data-action="copy">Copy LaTeX code</button>' +
'<button type="button" class="tinymce-wmath-context-item" data-action="wrap">' +
wrapLabel +
'</button>';
menu.style.left = Math.min(event.clientX, window.innerWidth - 220) + 'px';
menu.style.top = Math.min(event.clientY, window.innerHeight - 100) + 'px';
document.body.appendChild(menu);
const onMenuAction = (e) => {
const item = e.target.closest('[data-action]');
if (!item || !menu.contains(item)) return;
e.preventDefault();
e.stopPropagation();
const action = item.getAttribute('data-action');
if (action === 'copy') {
copyTextToClipboard(latex);
} else if (action === 'wrap' && latex) {
ed.dom.setOuterHTML(wmathElement, createWmathElementHtml(latex, uid, nextWrap));
setTimeout(() => {
if (typeof window.renderMathJax === 'function') {
window.renderMathJax(ed.id);
}
clearEditorMathSelection(ed);
}, 10);
}
closeWmathContextMenu();
document.removeEventListener('mousedown', onOutside, true);
};
const onOutside = (e) => {
if (menu.contains(e.target)) return;
closeWmathContextMenu();
document.removeEventListener('mousedown', onOutside, true);
};
menu.addEventListener('mousedown', (e) => e.stopPropagation());
menu.addEventListener('click', onMenuAction);
setTimeout(() => document.addEventListener('mousedown', onOutside, true), 0);
}
function openInlineMathEditor(ed, options) {
try {
openInlineMathEditorCore(ed, options);
} catch (err) {
console.error('openInlineMathEditor failed:', err);
closeInlineMathOverlay();
}
}
function openInlineMathEditorCore(ed, options) {
const opts = options || {};
const mode = opts.mode === 'edit' ? 'edit' : 'insert';
const wmathElement = opts.wmathElement || null;
closeInlineMathOverlay();
const iframe = ed.iframeElement;
const iframeDoc = ed.getDoc();
if (!iframe || !iframeDoc) return;
const placeholderId = 'math-ph-' + Date.now();
let uid = 'wmath-' + Math.random().toString(36).substr(2, 9);
let initialLatex = '';
let originalWmathHtml = null;
let preservedWrapMode = null;
let currentWrapMode = 'block';
let anchorRect = null;
if (mode === 'edit' && wmathElement) {
uid = wmathElement.getAttribute('data-id') || uid;
initialLatex = parseLatexFromWmath(wmathElement);
preservedWrapMode = wmathElement.getAttribute('data-wrap') || null;
originalWmathHtml = stripMceSelectedAttr(wmathElement.outerHTML);
anchorRect = wmathElement.getBoundingClientRect();
const phWidth = Math.max(Math.round(anchorRect.width), 4);
const phHeight = Math.max(Math.round(anchorRect.height), 4);
ed.dom.setOuterHTML(
wmathElement,
`<span id="${placeholderId}" class="math-placeholder" style="display:inline-block;min-width:${phWidth}px;min-height:${phHeight}px;vertical-align:middle;">&#8203;</span>`
);
} else {
ed.insertContent(
`<span id="${placeholderId}" class="math-placeholder" style="display:inline-block;min-width:4px;">&#8203;</span>`
);
}
const placeholder = ed.dom.get(placeholderId);
if (!placeholder) return;
currentWrapMode = normalizeWmathWrapMode(preservedWrapMode);
const phRect = placeholder.getBoundingClientRect();
const iframeRect = iframe.getBoundingClientRect();
const anchor = anchorRect || phRect;
const overlayTop = iframeRect.top + anchor.top;
const overlayLeft = iframeRect.left + anchor.left;
const overlayMinWidth = Math.min(Math.max(anchor.width, phRect.width, 360), 560);
let overlay = document.getElementById('tinymce-inline-math-overlay');
if (!overlay) {
overlay = document.createElement('div');
overlay.id = 'tinymce-inline-math-overlay';
overlay.className = 'tinymce-inline-math-overlay';
document.body.appendChild(overlay);
}
overlay.style.display = 'block';
overlay.style.top = overlayTop + 'px';
overlay.style.left = overlayLeft + 'px';
overlay.style.minWidth = overlayMinWidth + 'px';
overlay.innerHTML = `
<div class="tinymce-inline-math-panel">
<div class="tinymce-inline-math-field-wrap"></div>
<div class="tinymce-inline-math-footer">
<div class="tinymce-inline-math-tools">
<button type="button" class="tinymce-inline-math-copy">Copy LaTeX code</button>
<span class="tinymce-inline-math-tool-divider"></span>
<button type="button" class="tinymce-inline-math-wrap-toggle"></button>
</div>
<div class="tinymce-inline-math-actions">
<button type="button" class="tinymce-inline-math-cancel">Cancel</button>
<button type="button" class="tinymce-inline-math-confirm">OK</button>
</div>
</div>
</div>
`;
const fieldWrap = overlay.querySelector('.tinymce-inline-math-field-wrap');
const confirmBtn = overlay.querySelector('.tinymce-inline-math-confirm');
const cancelBtn = overlay.querySelector('.tinymce-inline-math-cancel');
const copyBtn = overlay.querySelector('.tinymce-inline-math-copy');
const wrapToggleBtn = overlay.querySelector('.tinymce-inline-math-wrap-toggle');
const updateWrapToggleLabel = () => {
wrapToggleBtn.textContent = getOppositeWrapLabel(currentWrapMode);
};
updateWrapToggleLabel();
wrapToggleBtn.addEventListener('mousedown', (e) => {
e.preventDefault();
e.stopPropagation();
});
wrapToggleBtn.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
currentWrapMode = currentWrapMode === 'inline' ? 'block' : 'inline';
updateWrapToggleLabel();
});
copyBtn.addEventListener('mousedown', (e) => {
e.preventDefault();
e.stopPropagation();
});
const mf = new MathfieldElement();
fieldWrap.appendChild(mf);
copyBtn.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
copyTextToClipboard(mf.value, () => {
copyBtn.textContent = 'Copied!';
copyBtn.classList.add('is-copied');
setTimeout(() => {
copyBtn.textContent = 'Copy LaTeX code';
copyBtn.classList.remove('is-copied');
}, 2000);
});
});
let mfInitialized = false;
const initMathfield = () => {
if (mfInitialized) return;
mfInitialized = true;
mf.virtualKeyboardMode = 'onfocus';
mf.placeholder = 'Type formula here.';
mf.menuItems = [];
try {
if (initialLatex) {
mf.value = initialLatex;
}
} catch (valueErr) {
console.warn('Mathfield set value failed:', valueErr);
}
requestAnimationFrame(() => {
mf.focus();
});
};
mf.addEventListener('mount', initMathfield, { once: true });
mf.addEventListener('contextmenu', (e) => {
e.preventDefault();
});
setTimeout(initMathfield, 0);
let finalized = false;
let ignoreOutsideClick = true;
const openedAt = Date.now();
const cleanupOutsideListeners = () => {
document.removeEventListener('mousedown', onDocMouseDown, true);
iframeDoc.removeEventListener('mousedown', onIframeMouseDown, true);
mf.removeEventListener('blur', onMfBlur);
};
const canCancelOutside = () => !ignoreOutsideClick && Date.now() - openedAt > 400;
const finalize = (save) => {
if (finalized) return;
finalized = true;
cleanupOutsideListeners();
const latex = (mf.value || '').trim();
closeInlineMathOverlay();
const ph = ed.dom.get(placeholderId);
if (!ph) return;
const afterUpdate = () => {
setTimeout(() => {
if (typeof window.renderMathJax === 'function') {
window.renderMathJax(ed.id);
}
clearEditorMathSelection(ed);
}, 10);
};
if (save && latex) {
ed.dom.setOuterHTML(ph, createWmathElementHtml(latex, uid, currentWrapMode));
afterUpdate();
} else if (mode === 'edit' && originalWmathHtml) {
ed.dom.setOuterHTML(ph, originalWmathHtml);
afterUpdate();
} else {
ed.dom.remove(ph);
clearEditorMathSelection(ed);
}
};
const onDocMouseDown = (e) => {
if (!canCancelOutside()) return;
if (overlay.contains(e.target)) return;
if (isMathliveUiTarget(e.target)) return;
finalize(false);
};
const onIframeMouseDown = (e) => {
if (!canCancelOutside()) return;
if (overlay.contains(e.target)) return;
if (isMathliveUiTarget(e.target)) return;
finalize(false);
};
const onMfBlur = () => {
setTimeout(() => {
if (finalized) return;
if (!canCancelOutside()) return;
const activeEl = document.activeElement;
const iframeActiveEl = iframeDoc.activeElement;
if (overlay.contains(activeEl)) return;
if (isMathliveUiTarget(activeEl) || isMathliveUiTarget(iframeActiveEl)) return;
finalize(false);
}, 150);
};
confirmBtn.addEventListener('mousedown', (e) => {
e.preventDefault();
e.stopPropagation();
});
confirmBtn.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
finalize(true);
});
cancelBtn.addEventListener('mousedown', (e) => {
e.preventDefault();
e.stopPropagation();
});
cancelBtn.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
finalize(false);
});
activeInlineMathOverlay = {
overlay,
iframeDoc,
mf,
finalize,
cleanupOutsideListeners
};
mf.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
e.preventDefault();
finalize(false);
}
});
mf.addEventListener('blur', onMfBlur);
setTimeout(() => {
ignoreOutsideClick = false;
document.addEventListener('mousedown', onDocMouseDown, true);
iframeDoc.addEventListener('mousedown', onIframeMouseDown, true);
}, 500);
}
function insertInlineMathWithMathlive(ed) {
openInlineMathEditor(ed, { mode: 'insert' });
}
function editInlineMathWithMathlive(ed, wmathElement) {
if (!wmathElement) return;
if (activeInlineMathOverlay) return;
openInlineMathEditor(ed, { mode: 'edit', wmathElement });
}
function isMathliveUiTarget(target) {
if (!target || !target.closest) return false;
return !!(
target.closest('#tinymce-inline-math-overlay') ||
target.closest('.ML__keyboard') ||
target.closest('.ML__virtual-keyboard') ||
target.closest('.MLK__plate') ||
target.closest('math-field')
);
}
function closeInlineMathOverlay() {
if (!activeInlineMathOverlay) return;
const { overlay, cleanupOutsideListeners } = activeInlineMathOverlay;
if (typeof cleanupOutsideListeners === 'function') {
cleanupOutsideListeners();
}
overlay.style.display = 'none';
overlay.innerHTML = '';
activeInlineMathOverlay = null;
}
// 首字母大写的方法
// const capitalizeFirstLetter = function (text) {
@@ -1837,6 +2333,19 @@ str = str.replace(regex, function (match, content, offset, fullString) {
},
insertInlineMathWithMathlive(ed) {
openInlineMathEditor(ed, { mode: 'insert' });
},
editInlineMathWithMathlive(ed, wmathElement) {
if (!wmathElement) return;
openInlineMathEditor(ed, { mode: 'edit', wmathElement });
},
findWmathFromTarget(target, root) {
return findWmathAncestor(target, root);
},
openWmathContextMenu(ed, wmathElement, event) {
showWmathContextMenu(ed, wmathElement, event);
},
initEditorButton(vueInstance, ed) {
ed.ui.registry.addMenuButton('customDropdown', {
@@ -2189,28 +2698,37 @@ str = str.replace(regex, function (match, content, offset, fullString) {
let latexEditorBookmark = null; // 用于记录插入点
let activeEditorId = null; // 当前激活的编辑器 ID
// 在编辑器工具栏中添加 "LateX" 按钮
// // 在编辑器工具栏中添加 "LateX" 按钮
// ed.ui.registry.addButton('LateX', {
// text: 'LateX', // 按钮文本
// onAction: function () {
// // 1. 获取当前光标位置
// const latexEditorBookmark = ed.selection.getBookmark(2); // 获取光标位置
// const editorId = ed.id; // 保存当前编辑器 ID
// // 2. 生成一个随机的 ID用于 wmath 标签
// const uid = 'wmath-' + Math.random().toString(36).substr(2, 9);
// // 3. 创建一个 <wmath> 标签并插入到光标处
// const wmathHtml = `<wmath contenteditable="false" data-id="${uid}" data-latex="" data-wrap="block"></wmath>`;
// ed.insertContent(wmathHtml); // 在光标位置插入 wmath 标签
// // 4. 打开公式编辑器窗口,并传递光标位置、编辑器 ID 和 wmathId
// const url = `/LateX?editorId=${editorId}&wmathId=${uid}`;
// // vueInstance.openLatexEditor({
// // editorId:editorId,
// // wmathId:uid,
// // });
// window.open(url, '_blank', 'width=1000,height=800,scrollbars=no,resizable=no');
// }
// });
// 行内公式编辑MathLive类似 Word「在此处键入公式」
ed.ui.registry.addButton('LateX', {
text: 'LateX', // 按钮文本
text: 'LateX',
tooltip: 'Type formula here',
onAction: function () {
// 1. 获取当前光标位置
const latexEditorBookmark = ed.selection.getBookmark(2); // 获取光标位置
const editorId = ed.id; // 保存当前编辑器 ID
// 2. 生成一个随机的 ID用于 wmath 标签
const uid = 'wmath-' + Math.random().toString(36).substr(2, 9);
// 3. 创建一个 <wmath> 标签并插入到光标处
const wmathHtml = `<wmath contenteditable="false" data-id="${uid}" data-latex="" data-wrap="block"></wmath>`;
ed.insertContent(wmathHtml); // 在光标位置插入 wmath 标签
// 4. 打开公式编辑器窗口,并传递光标位置、编辑器 ID 和 wmathId
const url = `/LateX?editorId=${editorId}&wmathId=${uid}`;
// vueInstance.openLatexEditor({
// editorId:editorId,
// wmathId:uid,
// });
window.open(url, '_blank', 'width=1000,height=800,scrollbars=no,resizable=no');
insertInlineMathWithMathlive(ed);
}
});

View File

@@ -2,14 +2,14 @@
//记得切换
//正式
const mediaUrl = '/public/';
const baseUrl = '/';
// const mediaUrl = '/public/';
// const baseUrl = '/';
//正式环境
// const mediaUrl = 'https://submission.tmrjournals.com/public/';
// // const mediaUrl = 'http://zmzm.tougao.dev.com/public/';
// const baseUrl = '/api'
const mediaUrl = 'https://submission.tmrjournals.com/public/';
// const mediaUrl = 'http://zmzm.tougao.dev.com/public/';
const baseUrl = '/api'
//测试环境

View File

@@ -2614,5 +2614,10 @@ export default {
wmath[data-wrap='inline'] {
display: inline-block !important;
width: auto !important;
vertical-align: middle;
}
wmath[data-wrap='inline'] mjx-container {
display: inline-block !important;
margin: 0 2px !important;
}
</style>

View File

@@ -641,6 +641,18 @@ export default {
font-weight: bold !important;
}
wmath {
pointer-events: auto !important;
cursor: pointer !important;
outline: none !important;
}
wmath[contenteditable="false"] {
cursor: pointer !important;
outline: none !important;
box-shadow: none !important;
}
@keyframes blueGlow {
0%,
100% {
@@ -731,35 +743,24 @@ export default {
e.preventDefault();
_this.pasteClipboardAsMath(ed);
});
var currentWmathElement = null;
ed.on('click', function (e) {
const wmathElement = e.target.closest('wmath');
const wmathElement = _this.$commonJS.findWmathFromTarget
? _this.$commonJS.findWmathFromTarget(e.target, ed.getBody())
: null;
if (wmathElement) {
currentWmathElement = wmathElement; // 保存当前点击的元素
e.preventDefault();
e.stopPropagation();
_this.$commonJS.editInlineMathWithMathlive(ed, wmathElement);
}
});
// 1. 提取内容:获取 LaTeX
const latexContentRaw = wmathElement.getAttribute('data-latex') || '';
const latexContent = latexContentRaw.replace(/\$/g, '').trim();
const encoded = encodeURIComponent(latexContent);
// 2. 提取状态:获取之前的 wrap 模式 👈 重要新增
const wrapMode = wmathElement.getAttribute('data-wrap') || 'block';
// 3. 处理唯一 ID
let wmathId = wmathElement.getAttribute('data-id');
if (!wmathId) {
wmathId = 'wmath-' + Math.random().toString(36).substr(2, 9);
wmathElement.setAttribute('data-id', wmathId);
}
const editorId = ed.id;
// 4. 打开窗口:在 URL 参数中增加 wrap 模式 👈 这样弹窗就知道该默认选哪个了
window.open(
`/LateX?id=${encoded}&wmathId=${wmathId}&editorId=${editorId}&wrap=${wrapMode}`,
'_blank',
'width=1000,height=800,scrollbars=no,resizable=no'
);
ed.on('contextmenu', function (e) {
const wmathElement = _this.$commonJS.findWmathFromTarget
? _this.$commonJS.findWmathFromTarget(e.target, ed.getBody())
: null;
if (wmathElement) {
_this.$commonJS.openWmathContextMenu(ed, wmathElement, e);
}
});

View File

@@ -240,4 +240,29 @@ export default {
.table-fade-enter-active, .table-fade-leave-active { transition: opacity 0.3s ease; }
.table-fade-enter, .table-fade-leave-to { opacity: 0; }
/* 表格预览:行内公式与文字同一行 */
.table-paper-view ::v-deep wmath[data-wrap='inline'] {
display: inline !important;
width: auto !important;
vertical-align: middle;
}
.table-paper-view ::v-deep wmath[data-wrap='inline'] mjx-container {
display: inline-block !important;
width: auto !important;
margin: 0 2px !important;
vertical-align: middle;
}
.table-paper-view ::v-deep wmath[data-wrap='inline'] mjx-container[display='true'] {
display: inline-block !important;
margin: 0 2px !important;
font-size: 12px !important;
}
.table-paper-view ::v-deep wmath[data-wrap='block'] {
display: block;
width: 100%;
}
</style>

View File

@@ -16,7 +16,7 @@
:value="value"
:typesettingType="typesettingType"
class="paste-area text-container"
:toolbar="!isAutomaticUpdate?['bold italic |customBlue removeBlue|LateX| myuppercase myuppercasea Line MoreSymbols|subscript superscript|clearButton|searchreplace']:['bold italic |customBlue removeBlue| myuppercase myuppercasea Line MoreSymbols|subscript superscript|clearButton|searchreplace']"
:toolbar="!isAutomaticUpdate?['bold italic |customBlue removeBlue|LateX Latex2| myuppercase myuppercasea Line MoreSymbols|subscript superscript|clearButton|searchreplace']:['bold italic |customBlue removeBlue|Latex2| myuppercase myuppercasea Line MoreSymbols|subscript superscript|clearButton|searchreplace']"
style="
/* white-space: pre-line; */
line-height: 12px;

View File

@@ -1,8 +1,26 @@
export const tableStyle = `
wmath[data-wrap="inline"] {
display: inline !important;
width: auto !important;
vertical-align: middle;
}
wmath[data-wrap="inline"] mjx-container {
display: inline-block !important;
width: auto !important;
margin: 0 2px !important;
vertical-align: middle;
font-size: 14px !important;
}
wmath[data-wrap="inline"] mjx-container[display="true"] {
display: inline-block !important;
margin: 0 2px !important;
}
wmath[data-wrap="block"] {
display: flex;
width: 100%;
}
wmath[data-wrap="block"] mjx-container {
font-size: 14px !important;
}
.word-img-placeholder {
background: #f0f0f0 ;
@@ -128,10 +146,10 @@ mjx-container {
font-size: 14px !important;
;
}
wmath{
wmath[data-wrap="block"] {
width: 100%;
display: block;
display: flex;
margin-bottom: 1em;
}
Info{
color:#ff8f25;