This commit is contained in:
2026-01-30 15:15:31 +08:00
parent 1ad05f016a
commit beee2ec54b
11 changed files with 255 additions and 193 deletions

View File

@@ -150,49 +150,49 @@ export default {
replaceWMathContent(inputHtml, callback) {
// 正则逻辑:匹配整个 wmath 标签,并捕获内部所有的属性字符串
var str = inputHtml.replace(/<wmath ([^>]+)>[^<]*<\/wmath>/g, function (match, allProps) {
// 1. 从所有属性中提取 data-latex 的值
const latexMatch = allProps.match(/data-latex="([^"]+)"/);
const latexContent = latexMatch ? latexMatch[1] : '';
// 2. 从所有属性中提取 data-wrap 的值
const wrapMatch = allProps.match(/data-wrap="([^"]+)"/);
const wrapAttr = wrapMatch ? ` data-wrap="${wrapMatch[1]}"` : '';
// 3. 重新组装:只保留 data-latex 和 data-wrap
// 注意:这里去掉了多余的 prefix/suffix确保标签“干净”
return `<wmath${wrapAttr} data-latex="${latexContent}">${latexContent}</wmath>`;
});
callback(str);
} ,
},
// **解析 MathJax 公式,获取 LaTeX**
async extractMathJaxLatex(cell, callback) {
return new Promise((resolve, reject) => {
// Step 1: 获取初始 HTML
let updatedContent = cell.innerHTML;
// 查找所有 <wmath> 元素
const wmathElements = cell.querySelectorAll('wmath');
wmathElements.forEach((element) => {
// 1. 提取 latex 内容
const latexContent = element.getAttribute('data-latex') || '';
// 2. 提取 wrap 模式 (只提取,不包裹)
const wrapMode = element.getAttribute('data-wrap') || 'block';
// 3. 重新组装标签:只保留属性,标签内部只放原始 latex 文本
// 这里不加 $ 或 $$,保持数据的原始性
const newWmathTag = `<wmath data-wrap="${wrapMode}" data-latex="${latexContent}">${latexContent}</wmath>`;
// 4. 执行替换
updatedContent = updatedContent.replace(element.outerHTML, newWmathTag);
});
// Step 2: 提取去掉外层 span 的内容
updatedContent = this.extractContentWithoutOuterSpan(updatedContent);
// Step 3: Resolve 结果
resolve(updatedContent);
});
@@ -350,6 +350,21 @@ export default {
console.error("❌ 找不到 word/document.xml无法解析 Word 文件");
return;
}
const relsFile = zip.file("word/_rels/document.xml.rels");
let imageRelMap = {};
if (relsFile) {
const relsXml = await relsFile.async("string");
const relDoc = new DOMParser().parseFromString(relsXml, "application/xml");
const rels = relDoc.getElementsByTagName("Relationship");
for (let r = 0; r < rels.length; r++) {
const id = rels[r].getAttribute("Id");
const target = rels[r].getAttribute("Target");
if (target && target.includes("media/")) {
// 补全路径,通常 rels 里的路径是相对 word 文件夹的
imageRelMap[id] = `word/${target}`;
}
}
}
const documentXml = await documentFile.async("string");
const parser = new DOMParser();
const documentDoc = parser.parseFromString(documentXml, "application/xml");
@@ -458,14 +473,44 @@ export default {
const drawings = run.getElementsByTagName("w:drawing");
for (let d = 0; d < drawings.length; d++) {
const drawing = drawings[d];
// 使用命名空间提取 a:blip
// --- 1. 提取宽高 (EMUs 转 PX) ---
// Word 存储尺寸的地方通常在 wp:extent
const extent = drawing.getElementsByTagName("wp:extent")[0];
let styleStr = "";
if (extent) {
const cx = parseInt(extent.getAttribute("cx"));
const cy = parseInt(extent.getAttribute("cy"));
// 9525 是 EMU 到像素的换算比例
const widthPx = Math.round(cx / 9525);
const heightPx = Math.round(cy / 9525);
styleStr = `width="${widthPx}" height="${heightPx}"`;
}
const blips = drawing.getElementsByTagNameNS("http://schemas.openxmlformats.org/drawingml/2006/main", "blip");
for (let b = 0; b < blips.length; b++) {
const blip = blips[b];
const embedId = blip.getAttribute("r:embed");
if (embedId) {
textContent += `<img data-embed="${embedId}"/>`;
if (embedId && imageRelMap[embedId]) {
// const imagePath = imageRelMap[embedId];
// const imgFile = zip.file(imagePath);
// if (imgFile) {
// const fileSize = imgFile._data.uncompressedSize;
// // 依然保留 1MB 限制,保护浏览器不卡死
// if (fileSize <= 1048576) {
// const base64 = await imgFile.async("base64");
// const ext = imagePath.split('.').pop().toLowerCase();
// // <img src="data:image/${ext};base64,${base64}" ${styleStr} />
// // --- 2. 直接拼接带宽高的 img 标签 ---
// textContent += ``;
// } else {
// // console.warn(`跳过大图片: ${imagePath}, 大小: ${(fileSize / 1024 / 1024).toFixed(2)}MB`);
// textContent += ``;
// }
// }
}
}
}
@@ -2062,12 +2107,12 @@ export default {
// 插入自定义表格到编辑器中
ed.setContent('');
const customCallback = ed.getParam('clear_custom_action');
if (typeof customCallback === 'function') {
customCallback(ed, vueInstance);
} else {
// 3. 如果没有自定义逻辑,执行默认逻辑
vueInstance.$emit('onClear');
}
if (typeof customCallback === 'function') {
customCallback(ed, vueInstance);
} else {
// 3. 如果没有自定义逻辑,执行默认逻辑
vueInstance.$emit('onClear');
}
}
});
@@ -2079,11 +2124,11 @@ export default {
onAction: function () {
// 必须获取带 HTML 的内容,否则里面的 em/i 标签在拼接前就丢了
var selectedText = ed.selection.getContent({ format: 'html' });
if (selectedText) {
// 这就是你想要的:直接外层套一个 blue
var wrappedText = `<blue>${selectedText}</blue>`;
// 使用 setContent 强行回写
ed.selection.setContent(wrappedText);
}
@@ -2166,29 +2211,29 @@ export default {
ed.ui.registry.addMenuButton('MoreSymbols', {
text: '···', // 按钮显示的三个点
tooltip: 'More Special Characters',
onAction: () => {}, // 菜单主按钮点击通常不执行操作,由子菜单执行
onAction: () => { }, // 菜单主按钮点击通常不执行操作,由子菜单执行
fetch: (callback) => {
const items = [
{
type: 'menuitem',
text: 'en-dash (短划线)',//短划线
onAction: () => ed.insertContent('')
},
{
type: 'menuitem',
text: 'minus sign (减号)',//减号
onAction: () => ed.insertContent('')
},
{
type: 'menuitem',
text: 'hyphen (连接符)',
onAction: () => ed.insertContent('-')
},
];
callback(items);
const items = [
{
type: 'menuitem',
text: 'en-dash (短划线)',//短划线
onAction: () => ed.insertContent('')
},
{
type: 'menuitem',
text: 'minus sign (减号)',//减号
onAction: () => ed.insertContent('')
},
{
type: 'menuitem',
text: 'hyphen (连接符)',
onAction: () => ed.insertContent('-')
},
];
callback(items);
}
});
});
ed.ui.registry.addButton('removeBlue', {
text: 'Blue', // 按钮文本