提交
This commit is contained in:
215
src/common/js/commonJS.js
Normal file
215
src/common/js/commonJS.js
Normal file
@@ -0,0 +1,215 @@
|
||||
export default {
|
||||
// 提取 Word 文件中的表格
|
||||
extractTablesFromWord(arrayBuffer, callback) {
|
||||
const zip = new JSZip();
|
||||
zip.loadAsync(arrayBuffer)
|
||||
.then((zip) => zip.file('word/document.xml').async('string'))
|
||||
.then((docXml) => {
|
||||
const parser = new DOMParser();
|
||||
const xmlDoc = parser.parseFromString(docXml, 'text/xml');
|
||||
const tables = xmlDoc.getElementsByTagName('w:tbl'); // 查找 Word 表格标签
|
||||
let html = '';
|
||||
const wordTables = []
|
||||
for (let table of tables) {
|
||||
var str = this.convertTableToHtml(table);
|
||||
const container = document.createElement('div');
|
||||
container.innerHTML = str;
|
||||
wordTables.push({
|
||||
html: this.updateTableStyles(container),
|
||||
orientation: 'portrait' // 默认纵向
|
||||
});
|
||||
|
||||
html += str;
|
||||
}
|
||||
|
||||
if (!html)
|
||||
html = '<p>未检测到表格内容。</p>';
|
||||
|
||||
|
||||
callback( Array.from(wordTables).map((table) => table.html).join(''), wordTables);
|
||||
})
|
||||
.catch(function (err) {
|
||||
console.error('解析 Word 文件出错:', err);
|
||||
callback('<p>文件解析失败,请检查文件格式。</p>');
|
||||
});
|
||||
},
|
||||
//更新传入所有表格样式
|
||||
updateTableStyles(container,type,setTopBottomBorder) {
|
||||
var typesettingType=type?type:1
|
||||
const tables = container.querySelectorAll('table');
|
||||
tables.forEach((table) => {
|
||||
table.setAttribute(
|
||||
'style',
|
||||
`width: ${typesettingType == 1 ? '17.18cm' : '25.88cm'
|
||||
};border: none; margin: 0 auto !important;border-collapse: collapse; `
|
||||
);
|
||||
const cells = table.querySelectorAll('td');
|
||||
cells.forEach((td) => {
|
||||
if (/^-?\d+(\.\d+)?$/.test(td.textContent.trim())) {
|
||||
this.replaceNegativeSign(td);
|
||||
}
|
||||
// 检查当前 td 是否包含上下标
|
||||
if (!this.containsSupOrSub(td)) {
|
||||
// 递归处理单元格内的所有子节点
|
||||
td.childNodes.forEach((node) => this.capitalizeFirstLetter(node));
|
||||
// 替换 <a> 标签为其内部文本
|
||||
td.querySelectorAll('a').forEach((a) => a.replaceWith(document.createTextNode(a.textContent)));
|
||||
}
|
||||
const childElements = td.querySelectorAll('*');
|
||||
// 遍历每个子元素
|
||||
childElements.forEach((element) => {
|
||||
// 如果元素的文本内容匹配正则表达式
|
||||
if (/\[\d+(?:,\d+)*\]/g.test(element.textContent)) {
|
||||
element.classList.add('color-highlight');
|
||||
element.style.color = 'rgb(0,130,170)';
|
||||
}
|
||||
});
|
||||
});
|
||||
if(setTopBottomBorder){
|
||||
const firstRowTdElements = container.querySelectorAll('tr:first-child td'); // 获取第一个 <tr> 中的所有 <td> 元素
|
||||
firstRowTdElements.forEach((td) => {
|
||||
const currentStyle = td.getAttribute('style');
|
||||
if (currentStyle) {
|
||||
td.setAttribute(
|
||||
'style',
|
||||
currentStyle +
|
||||
';border-top:1.0000pt solid #000 !important;mso-border-top-alt:0.5000pt solid #000 !important;border-bottom:1.0000pt solid #000 !important;mso-border-bottom-alt:0.5000pt solid #000 !important;'
|
||||
);
|
||||
} else {
|
||||
td.setAttribute(
|
||||
'style',
|
||||
'border-top:1.0000pt solid #000 !important;mso-border-top-alt:0.5000pt solid #000 !important;border-bottom:1.0000pt solid #000 !important;mso-border-bottom-alt:0.5000pt solid #000 !important;'
|
||||
);
|
||||
}
|
||||
});
|
||||
const firstRowTdElementsLast = container.querySelectorAll('tr:last-of-type td');
|
||||
firstRowTdElementsLast.forEach((td) => {
|
||||
// 获取当前的 style 属性(如果有)
|
||||
const currentStyle = td.getAttribute('style');
|
||||
// 如果已有 style 属性,则追加边框样式;如果没有 style 属性,则设置新的 style
|
||||
if (currentStyle) {
|
||||
td.setAttribute(
|
||||
'style',
|
||||
currentStyle +
|
||||
';border-bottom:1.0000pt solid #000 !important;mso-border-bottom-alt:0.5000pt solid #000 !important;'
|
||||
);
|
||||
} else {
|
||||
td.setAttribute(
|
||||
'style',
|
||||
'border-bottom:1.0000pt solid #000 !important;mso-border-bottom-alt:0.5000pt solid #000 !important;'
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
return Array.from(tables).map((table) => table.outerHTML).join('');
|
||||
|
||||
},
|
||||
// 将 XML 表格转换为 HTML
|
||||
convertTableToHtml(tableNode) {
|
||||
const rows = tableNode.getElementsByTagName('w:tr');
|
||||
let html = '<table border="1" style="border-collapse: collapse;">';
|
||||
for (let row of rows) {
|
||||
html += '<tr>';
|
||||
const cells = row.getElementsByTagName('w:tc');
|
||||
for (let cell of cells) {
|
||||
let cellHtml = '';
|
||||
const paragraphs = cell.getElementsByTagName('w:p'); // 获取单元格内段落
|
||||
|
||||
for (let paragraph of paragraphs) {
|
||||
const texts = paragraph.getElementsByTagName('w:r'); // 获取段落内的文本和样式
|
||||
for (let run of texts) {
|
||||
const textNode = run.getElementsByTagName('w:t')[0];
|
||||
if (textNode) {
|
||||
const style = this.getStyleFromRun(run); // 提取样式
|
||||
cellHtml += `<span style="${style}">${textNode.textContent}</span>`;
|
||||
}
|
||||
}
|
||||
cellHtml += '<br>'; // 段落换行
|
||||
}
|
||||
|
||||
html += `<td>${cellHtml}</td>`;
|
||||
}
|
||||
html += '</tr>';
|
||||
}
|
||||
html += '</table>';
|
||||
|
||||
return html;
|
||||
},
|
||||
|
||||
// 提取 w:r 节点中的样式并转换为 CSS
|
||||
getStyleFromRun(run) {
|
||||
const styleNode = run.getElementsByTagName('w:rPr')[0];
|
||||
let style = '';
|
||||
if (styleNode) {
|
||||
// 加粗
|
||||
if (styleNode.getElementsByTagName('w:b').length > 0) {
|
||||
style += 'font-weight: bold;';
|
||||
}
|
||||
// 斜体
|
||||
if (styleNode.getElementsByTagName('w:i').length > 0) {
|
||||
style += 'font-style: italic;';
|
||||
}
|
||||
// 上标或下标
|
||||
const vertAlign = styleNode.getElementsByTagName('w:vertAlign')[0];
|
||||
if (vertAlign) {
|
||||
const alignVal = vertAlign.getAttribute('w:val');
|
||||
if (alignVal === 'superscript') {
|
||||
style += 'vertical-align: super; font-size: smaller;';
|
||||
} else if (alignVal === 'subscript') {
|
||||
style += 'vertical-align: sub; font-size: smaller;';
|
||||
}
|
||||
}
|
||||
// 字体颜色
|
||||
const colorNode = styleNode.getElementsByTagName('w:color')[0];
|
||||
if (colorNode) {
|
||||
const colorVal = colorNode.getAttribute('w:val');
|
||||
style += `color: #${colorVal};`;
|
||||
}
|
||||
}
|
||||
|
||||
return style;
|
||||
},
|
||||
replaceNegativeSign(node) {
|
||||
if (node.nodeType === Node.TEXT_NODE) {
|
||||
// 如果是文本节点,替换负号
|
||||
node.nodeValue = node.nodeValue.replace(/^-(?=\d)/, '−');
|
||||
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
this.applyToChildNodes(node, (child) => this.replaceNegativeSign(child));
|
||||
}
|
||||
},
|
||||
|
||||
capitalizeFirstLetter(node) {
|
||||
if (node.nodeType === Node.TEXT_NODE) {
|
||||
// 如果是文本节点,只处理第一个非空字符
|
||||
node.nodeValue = node.nodeValue.replace(/^\s*([a-zA-Z])/, (match, firstLetter) => firstLetter.toUpperCase());
|
||||
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
this.applyToChildNodes(node, (child) => this.capitalizeFirstLetter(child));
|
||||
}
|
||||
},
|
||||
|
||||
applyToChildNodes(node, fn) {
|
||||
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
node.childNodes.forEach(fn);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
containsSupOrSub(element) {
|
||||
// 如果当前节点是元素节点
|
||||
if (element.nodeType === 1) {
|
||||
// 如果是 <sup> 或 <sub> 标签,返回 true
|
||||
if (element.tagName === 'SUP' || element.tagName === 'SUB') {
|
||||
return true;
|
||||
}
|
||||
// 否则,递归检查子节点
|
||||
return Array.from(element.childNodes).some((child) => this.containsSupOrSub(child));
|
||||
}
|
||||
// 如果不是元素节点(如文本节点),返回 false
|
||||
return false;
|
||||
},
|
||||
|
||||
// 通用递归方法
|
||||
|
||||
};
|
||||
Reference in New Issue
Block a user