一 、在main.js引入全局JS
import JSZip from 'jszip';
export default {async extractWordTablesToArrays(file, callback) {const Zip = new JSZip();try {// 解压 Word 文档const zip = await Zip.loadAsync(file);const documentXml = await zip.file("word/document.xml").async("string");// 解析 XMLconst parser = new DOMParser();const documentDoc = parser.parseFromString(documentXml, "application/xml");// 获取命名空间const namespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";// 获取所有表格const tables = documentDoc.getElementsByTagNameNS(namespace, "tbl");const allTables = []; // 存储所有表格的二维数组if (!tables || tables.length === 0) {console.warn("未找到表格内容,请检查 XML 结构");return [];}for (const table of tables) {const rows = table.getElementsByTagNameNS(namespace, "tr");const tableArray = []; // 当前表格的二维数组for (const row of rows) {const cells = row.getElementsByTagNameNS(namespace, "tc");const rowArray = []; // 当前行的数据// 存储已合并的单元格let colSpanInfo = [];for (let i = 0; i < cells.length; i++) {const cell = cells[i];let cellText = "";// const paragraphs = cell.getElementsByTagNameNS(namespace, "p");const paragraphs = cell.getElementsByTagName("w:p");// 检查合并单元格属性const gridSpan = cell.getElementsByTagNameNS(namespace, "gridSpan")[0];const vMerge = cell.getElementsByTagNameNS(namespace, "vMerge")[0];// 获取合并信息let colspan = gridSpan ? parseInt(gridSpan.getAttribute("w:val"), 10) : 1;let rowspan = 1;if (vMerge && vMerge.getAttribute("w:val") === "restart") {rowspan = 2; // 假设合并两行} else if (vMerge && !vMerge.getAttribute("w:val")) {continue; // 如果是合并单元格的继续部分,则跳过}// 为当前单元格初始化计数器const currentLevelNumbers = {};for (const paragraph of paragraphs) {// 提取段落编号let listPrefix = "";// 初始化当前段落文本let paragraphText = listPrefix ? `${listPrefix} ` : "";const runs = paragraph.getElementsByTagName("w:r");for (const run of runs) {let textContent = "";const texts = run.getElementsByTagName("w:t");for (const text of texts) {textContent += text.textContent;}// 检查格式const rPr = run.getElementsByTagName("w:rPr")[0];let formattedText = textContent;if (rPr) {const bold = rPr.getElementsByTagName("w:b").length > 0;const italic = rPr.getElementsByTagName("w:i").length > 0;const vertAlignElement = rPr.getElementsByTagName("w:vertAlign")[0];if (bold) {formattedText = `<b>${formattedText}</b>`;}if (italic) {formattedText = `<i>${formattedText}</i>`;}if (vertAlignElement) {const vertAlign = vertAlignElement.getAttribute("w:val");if (vertAlign === "superscript") {formattedText = `<sup>${formattedText}</sup>`;} else if (vertAlign === "subscript") {formattedText = `<sub>${formattedText}</sub>`;}}}paragraphText += formattedText;}// 处理换行符const breaks = paragraph.getElementsByTagName("w:br");for (const br of breaks) {paragraphText += "<br>";}cellText += paragraphText; // 将段落文本添加到单元格文本}// 更新合并单元格的信息if (colSpanInfo[i]) {colspan = colSpanInfo[i].colspan;}// 保存当前单元格信息rowArray.push({text: cellText,colspan: colspan,rowspan: rowspan});// 记录跨列合并if (colspan > 1) {for (let j = 1; j < colspan; j++) {colSpanInfo[i + j] = { colspan: 0 }; // 用 0 填充后续的列合并}}}tableArray.push(rowArray); // 添加当前行到表格数组}allTables.push(tableArray); // 添加当前表格到所有表格数组}console.log("解析后的二维数组:", allTables);callback(allTables); // 返回处理后的 HTML} catch (error) {console.error("解析 Word 文件失败:", error);return [];}},getWordTablesThumbnails(tables, callback) {let combinedHtml = `<div style="display: flex; flex-wrap: wrap; gap: 16px; justify-content: start;">`;// 遍历每个表格,生成缩略图tables.forEach((table, index) => {let tableHtml = `<div style="border: 1px solid #ccc; padding: 8px; width: 200px; height: 150px; overflow: hidden; position: relative; cursor: pointer;"οnclick="document.getElementById('table-modal-${index}').style.display='block';"><div style="transform: scale(0.3); transform-origin: top left; position: absolute; width: 1000px;"><table border="1" style="border-collapse: collapse; width: 100%; text-align: center; table-layout: auto;">`;table.forEach((row) => {tableHtml += `<tr>`;// 遍历单元格row.forEach((cell) => {tableHtml += `<td colspan="${cell.colspan || 1}" rowspan="${cell.rowspan || 1}" style=""><span > ${cell.text}</span> </td>`;});tableHtml += `</tr>`;});tableHtml += `</table></div></div><!-- 模态框显示原始表格 --><div id="table-modal-${index}" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.7); z-index: 9999;"οnclick="this.style.display='none';"><div style="background: #fff; margin: 50px auto; height: 80%;padding: 20px; max-width: 80%;box-sizing:border-box;max-width: 80%; overflow: auto;">`;// 原始大小的表格内容tableHtml += `<table border="1" style="border-collapse: collapse; width: 100%; text-align: center; table-layout: auto;">`;table.forEach((row) => {tableHtml += `<tr>`;// 遍历单元格row.forEach((cell) => {tableHtml += `<td colspan="${cell.colspan || 1}" rowspan="${cell.rowspan || 1}" style=""><span > ${cell.text}</span> </td>`;});tableHtml += `</tr>`;});tableHtml += `</table></div></div>`;combinedHtml += tableHtml;});combinedHtml += `</div>`;const container = document.createElement("div");container.innerHTML = combinedHtml;callback(container.innerHTML);},},
二、页面中 点击图片上传本地文件
<template><div><img src="@/assets/img/word.png" alt="" style="width: 30px; height: 30px" @click="clickUpload" /><el-dialogappend-to-bodytitle="Add Academic Integrity Committee":visible.sync="addVisible"width="80%":close-on-click-modal="false"><div v-html="tablesHtml" class="wordTableHtml"></div></el-dialog></div>
</template>
<script>
export default {data() {return {tablesHtml: '',tables: [], // 保存解析后的表格数据addVisible: false};},components: {},methods: {addVisCancle() {this.addVisible = false;},clickUpload() {this.tables = [];var that = this;const input = document.createElement('input');input.type = 'file';input.accept = '.docx'; // 限制为 Word 文件input.addEventListener('change', function () {const file = input.files[0];if (file) {const reader = new FileReader();reader.onload = function (e) {that.$commonJS.extractWordTablesToArrays(file, function (wordTables) {console.log('tablesHtml at line 61:', wordTables);that.tables = wordTables;that.$commonJS.getWordTablesThumbnails(wordTables, function (html) {console.log('html at line 78:', html);that.tablesHtml = html;that.addVisible = true;});that.$emit('tables', that.tables, html);});};reader.readAsArrayBuffer(file);}});input.click();},}
};
</script>```以下样式看情况 因为我需要 上标 下标 粗体 字体 的样式
```css
<style scoped>
::v-deep .wordTableHtml b span {font-weight: bold !important;
}
::v-deep .wordTableHtml i span {font-style: italic !important;
}
::v-deep .wordTableHtml sub span {vertical-align: sub;
}
::v-deep .wordTableHtml sup span {vertical-align: super;
}
::v-deep .wordTableHtml sub {vertical-align: sub !important;
}
::v-deep .wordTableHtml sup {vertical-align: super !important;
}
::v-deep .wordTableHtml span[style*='vertical-align: super'] {vertical-align: super !important;
}
::v-deep .wordTableHtml span[style*='vertical-align: sub'] {vertical-align: sub !important;
}
::v-deep .wordTableHtml table {border: 0px !important;border-collapse: collapse; /* 去除单元格间隙 */width: auto;margin: 0 auto !important;table-layout: auto; /* 自动调整列宽 */text-align: left;font-family: 'Charis SIL' !important;font-size: 7.5pt !important;mso-font-kerning: 1pt !important;line-height: 10pt !important;mos-line-height: 10pt !important;
}
::v-deep .wordTableHtml table td,
.wordTableHtml table th::v-deep {padding: 5px;text-align: left !important;word-wrap: break-word; /* 长单词自动换行 */word-break: break-word;font-family: 'Charis SIL' !important;font-size: 7.5pt !important;mso-font-kerning: 1pt !important;line-height: 10pt !important;mos-line-height: 10pt !important;
}
::v-deep .wordTableHtml table tbody tr td {text-align: left !important;border-left: none !important;mso-border-left-alt: none !important;border-right: none !important;mso-border-right-alt: none !important;border-top: none;mso-border-top-alt: none !important;border-bottom: none !important;mso-border-bottom-alt: none !important;border: 1px dashed #dcdfe6 !important;border-left: 1px dashed #dcdfe6 !important;border-right: 1px dashed #dcdfe6 !important;word-break: keep-all !important;/* text-align: justify !important; */
}
::v-deep .wordTableHtml table tr td p {display: flex;text-align: left !important;align-items: center;margin: 0;font-family: 'Charis SIL' !important;font-size: 7.5pt !important;mso-font-kerning: 1pt !important;line-height: 10pt !important;mos-line-height: 10pt !important;
}
::v-deep .wordTableHtml table span {color: #000000;text-align: left !important;font-family: 'Charis SIL' !important;font-size: 7.5pt !important;mso-font-kerning: 1pt !important;line-height: 10pt !important;mos-line-height: 10pt !important;
}
::v-deep .wordTableHtml table .color-highlight {color: rgb(0, 130, 170) !important;font-family: 'Charis SIL' !important;font-size: 7.5pt !important;mso-font-kerning: 1pt !important;line-height: 10pt !important;mos-line-height: 10pt !important;
}
::v-deep .wordTableHtml table tr:first-child td {border-top: 1px solid #000 !important;border-bottom: 1px solid #000 !important;
}
::v-deep .wordTableHtml table tr:last-of-type td {border-bottom: 1px solid #000 !important;
}
</style>