一、复制word表格转换成html代码 ,在页面中显示 并且能导出
1、导出使用了htmlDocx插件
//1、使用html-docx-js 插件
npm install html-docx-js --save
npm install html2canvas
//2、在页面中引入
import htmlDocx from 'html-docx-js/dist/html-docx.js';
import html2canvas from "html2canvas";
2、开始写代码 (以下代码包含生成word以及生成图片)
特别注意,关于页面大小(A4,页面边距是无法改变的 我多次尝试 没什么效果 我的方法是定义好表格宽高 人工对生成的word进行改变 )
新发现原来是可以改页面大小的需要修改html-docx-js源代码 请看另一篇
<template>
<div>
<div>
<el-select v-model="typesettingType">
<el-option v-for="item in typesettingTypeOptions" :key="item.value" :label="item.label" :value="item.value"> </el-option>
</el-select>
<span>请在下面区域粘贴 Word 表格</span>
</div>
<div class="controls" style="overflow: hidden">
请在此区域粘贴 Word 表格(typesettingType 为页面方向 1为竖向 <span> </span>
<div
style="margin: 0 auto; box-sizing: border-box"
class="paste-area"
:style="`width: ${typesettingType == 1 ? 210 : 297}mm !important;
padding:25.4mm 19.1mm`"
contenteditable="true"
id="content"
ref="content"
@paste="handlePaste"
></div>
</div>
<el-button @click="exportToWord">导出为 Word</el-button>
<el-button @click="downloadImage">导出为 图片</el-button>
</div>
</template>
<script>
import htmlDocx from 'html-docx-js/dist/html-docx.js';
import html2canvas from 'html2canvas';
export default {
data() {
return {
updatedHtml: '',
typesettingType: 1,
typesettingTypeOptions: [
{ label: '竖向 A4', value: 1, pageWidth: 210 },
{ label: '横向 A4', value: 2, pageWidth: 297 }
]
};
},
mounted() {
this.typesettingType = 1;
},
methods: {
//下载图片
downloadImage() {
const container = this.$refs.content;
const outputDiv = container.querySelector('table');
// 使用 html2canvas 将内容转为图片
html2canvas(outputDiv, {
scale: 1, // 提高图片的分辨率,默认为1,设置为2 3 可以使图片更清晰最多好像到4
logging: false, // 禁用日志输出
useCORS: true, // 允许跨域图像
allowTaint: true // 允许污染 canvas,解决图片链接不可用问题
})
// 清空现有内容,显示图片
.then((canvas) => {
// 创建一个图片对象
const imgData = canvas.toDataURL('image/png');
// 创建一个链接并下载图片
const link = document.createElement('a');
link.href = imgData;
link.download = 'image.png';
link.click();
});
},
async handlePaste(event) {
setTimeout(() => {
var content = this.$refs.content;
console.log('content at line 51:', content);
if (content) {
const updatedHtml = this.setHtmlWord();
this.updatedHtml = updatedHtml.match(/<table[\s\S]*?<\/table>/i)[0];
this.$refs.content.innerHTML = this.updatedHtml;
console.log('updatedHtml at line 114:', this.updatedHtml);
}
}, 1000);
},
//判断内容是否有上下标
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;
},
setHtmlWord() {
var setHtmlWord = this.$refs.content.outerHTML;
const html = setHtmlWord
.replace(
/<(table)([^>]*style="([^"]*)")/g, // 匹配有 style 属性的 <table>
(match, p1, p2, p3) => {
const existingStyle = p3;
// 如果现有样式不为空,则在原样式后追加新的样式
const updatedStyle = existingStyle
? existingStyle +
`; width: ${
this.typesettingType == 1 ? '488' : '736'
}pt !important;`
: `width: ${
this.typesettingType == 1 ? '488' : '736'
}pt !important;`;
var str = `<table${p2.replace(`style="${existingStyle}"`, `style="${updatedStyle}"`)}`;
return str;
}
)
.replace(/style="style=;/g, 'style="')
.replace(/;;/g, ';')
.replace(/\n/g, '<br>');
const container = document.createElement('div');
container.innerHTML = html;
const AllTd = container.querySelectorAll('table td'); // 获取 <tr> 中的所有 <td> 元素
// 遍历所有 <td> 元素,添加上下边框样式
AllTd.forEach((td) => {
const currentStyle = td.getAttribute('style');
if (currentStyle) {
td.setAttribute(
'style',
currentStyle +
';color:#000000 !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;'
);
} else {
td.setAttribute(
'style',
'color:#000000 !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;'
);
}
});
const firstRowTdElements = container.querySelectorAll('tr:first-child td'); // 获取第一个 <tr> 中的所有 <td> 元素
// 遍历所有 <td> 元素,添加上下边框样式
firstRowTdElements.forEach((td) => {
const currentStyle = td.getAttribute('style');
if (currentStyle) {
td.setAttribute(
'style',
currentStyle +
';color:#000000 !important; 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',
'color:#000000 !important;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');
// 遍历所有 <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;'
);
}
});
// 获取修改后的 HTML 内容
const updatedHtml = container.innerHTML;
return updatedHtml;
},
//导出word
exportToWord() {
const tableHtml = `
<html xmlns:w="urn:schemas-microsoft-com:office:word">
<body>
<div align="center">
${this.$refs.content.innerHTML}
</div>
</body>
</html>`;
console.log('tableHtml at line 150:', tableHtml);
const converted = htmlDocx.asBlob(tableHtml); // 将 HTML 转换为 Word Blob
// 触发文件下载
const link = document.createElement('a');
link.href = URL.createObjectURL(converted);
link.download = 'table.docx';
link.click();
}
}
};
</script>
<style scoped>
table {
border-collapse: collapse;
width: 100%;
margin: 20px 0;
}
th,
td {
border: 1px dashed #dcdfe6;
padding: 8px;
text-align: center;
position: relative;
}
table {
border-top: 2px solid #000;
border-bottom: 1px solid #000;
}
th {
border-bottom: 1px solid #000;
}
th input,
td input {
width: 100%;
border: none;
outline: none;
text-align: center;
}
th input,
td input ::placeholder {
color: #aaa !important;
}
.controls {
margin: 0 0 20px;
}
.drag-handle {
cursor: move;
}
::v-deep .paste-area {
height: auto; /* A4纸高度 */
background: white; /* 纸张背景 */
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* 添加阴影模拟纸张浮起 */
border: 1px solid #ddd; /* 模拟纸张边框 */
/* padding: 25.4mm 19.1mm; //内边距 */
box-sizing: border-box; /* 确保内边距不会影响整体尺寸 */
transform-origin: top left;
}
::v-deep .paste-area table {
/* border-top: 2px solid #000 !important; */
/* border-bottom: 1px solid #000 !important; */
margin-left: 0 !important;
margin-right: 0 !important;
margin: 0 auto !important;
}
::v-deep .paste-area table td {
border-top: none !important;
border-bottom: none !important;
border: 1px dashed #dcdfe6 !important;
/* display: flex;
align-items: center; */
}
::v-deep .paste-area table td p {
display: flex;
align-items: center;
}
::v-deep .paste-area table .MsoNormal {
max-width: 200px !important; /* 限制容器宽度 */
word-wrap: break-word !important;
overflow-wrap: break-word !important;
}
.text-container {
position: relative;
padding: 20px;
border: 1px solid #ccc;
margin: 20px;
font-size: 16px;
line-height: 1.5;
}
</style>
在处理td内容时 如果内容出现上标下标要进行特殊处理不然会被覆盖