word表格 转换html 并导出.docx和图片(vue)

一、复制word表格转换成html代码 ,在页面中显示 并且能导出

1、导出使用了htmlDocx插件

javascript 复制代码
//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进行改变 )

javascript 复制代码
<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内容时 如果内容出现上标下标要进行特殊处理不然会被覆盖

相关推荐
Suppose24 分钟前
[Vue]template相关
vue.js
cnsxjean1 小时前
Vue教程|搭建vue项目|Vue-CLI2.x 模板脚手架
javascript·vue.js·ui·前端框架·npm
MarisolHu1 小时前
前端学习笔记-Vue篇-02
前端·vue.js·笔记·学习
小周同学_丶2 小时前
解决el-select数据量过大的3种方法
前端·vue.js·elementui
NoneCoder3 小时前
HTML5系列(9)-- Web Components
前端·html·html5
花之亡灵3 小时前
(笔记)vue3引入Element-plus
前端·javascript·vue.js
不会玩技术的技术girl4 小时前
利用HTML5获取商品详情:打造高效电商体验
前端·html·html5
以对_5 小时前
【el-table】表格后端排序
前端·javascript·vue.js
北城笑笑5 小时前
Vue 90 ,Element 13 ,Vue + Element UI 中 el-switch 使用小细节解析,避免入坑(获取后端的数据类型自动转变)
前端·javascript·vue.js·elementui
前端Hardy6 小时前
HTML&CSS 奇幻森林:小熊的甜蜜蛋糕派对大冒险
前端·javascript·css·html