You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Nuxt 3中如何用无漏洞包将Vue组件导出为PDF?

Vue复杂嵌套组件导出PDF解决方案

问题背景

在risk.vue页面中引入了RiskGrid.vue组件,该组件包含300+硬编码网格项、大量自定义样式,且嵌套RiskGridTable.vue子组件。需要通过按钮触发导出功能,将该组件完整导出为包含标题、描述和网格内容的PDF文件。此前尝试html2pdf、vue-html2pdf、jspdf等工具,要么存在已知漏洞,要么导出效果不符合预期。

代码结构示例

risk.vue

<TabPanel value="0" class="tab-panel">
    <div ref="rizikSazetakRef">
        <RiskGrid v-if="vrstaIzracuna == 'Djelatnost'" :tip="'RZ'" class="rizik-sazetak" />
        <span v-else>
            <font-awesome-icon icon="info-circle" style="margin-right: 5px;" />
            Nije odabrana vrsta izračuna
        </span>
    </div>
</TabPanel>
<button class="action-icon" @click="downloadRizikSazetak">
    <font-awesome-icon icon="download" size="lg" />
</button>

<script setup>
const downloadRizikSazetak = () => {
    // 待实现导出逻辑
};
</script>

RiskGrid.vue(片段)

<template>
    <div class="rizik-sazetak">
        <div v-if="showPopup" class="success-popup">
            <font-awesome-icon :icon="'circle-exclamation'" />
            <span>{{ message }}</span>
            <div class="progress-bar" />
        </div>
        <div class="sazetak">
            <div ref="rizikSazetakRef" class="grid-table">
                <!-- 300+网格项代码 -->
                <div class="grid-item vertical lightgreen db-l">Wind changes</div>
                <div class="grid-item table-header">First-al</div>
                <div class="grid-item table-header">First-bl</div>
                <div class="grid-item table-header">First-cl</div>
                <div class="grid-item table-header">First-dl</div>
                <div class="grid-item table-item nb-b">
                    <RiskGridTable :pozicija="12" :data="structuredData['12']" class="sazetak-table" />
                </div>
                <!-- 更多网格项代码 -->
            </div>
            <LegendaBoja v-if="structuredData" class="legenda" />
        </div>
    </div>
</template>

<style scoped>
/* 大量自定义样式 */
.vertical {
    writing-mode: sideways-lr;
    text-orientation: upright;
    text-align: center;
    text-wrap: wrap;
    font-size: 14px;
    padding: 5px;
    border-right: none;
    border-left: none;
    border-bottom: none;
}

.grid-item:nth-child(57) {
    grid-row: 7;
    grid-column: 12;
}

.grid-item:nth-child(58) {
    grid-row-start: 4;
    grid-column-start: 13;
    grid-row-end: 8;
    grid-column-end: 20;
}

.grid-item:nth-child(59) {
    grid-column: 20;
    grid-row: 4 / 8;
}
/* 更多样式代码 */
</style>

RiskGridTable.vue(片段)

<template>
    <div>
        <div v-if="data" class="grid">
            <div class="_grid-item first-div" :class="getColorClass(data[0].history)">
                <span class="first" :style="getFontSize(data[0].history)">
                    {{ data[0].history }}
                </span>
            </div>
            <div class="_grid-item db-s" :class="getColorClass(data[0].p0_4_5)" :style="getFontSize(data[0].p0_4_5)">
                {{ data[0].p0_4_5 }}
            </div>
            <div class="_grid-item" :class="getColorClass(data[0].p0_8_5)" :style="getFontSize(data[0].p0_8_5)">
                {{ data[0].p0_8_5 }}
            </div>
            <!-- 更多子网格项代码 -->
        </div>
    </div>
</template>

解决方案:html2canvas + jsPDF组合方案

选择无已知高危漏洞的稳定版本,手动组合实现导出,可控性更强。

步骤1:安装依赖

npm install html2canvas@1.4.1 jspdf@2.5.1

步骤2:实现导出函数

修改risk.vue的<script setup>部分:

<script setup>
import { ref } from 'vue';
import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';

const rizikSazetakRef = ref(null);

const downloadRizikSazetak = async () => {
    if (!rizikSazetakRef.value) return;

    // 克隆目标元素,避免干扰原页面渲染
    const cloneElement = rizikSazetakRef.value.cloneNode(true);
    document.body.appendChild(cloneElement);

    try {
        // 生成Canvas,提高分辨率避免PDF模糊
        const canvas = await html2canvas(cloneElement, {
            scale: 2,
            useCORS: true,
            logging: false,
        });

        // 初始化PDF,设置A4尺寸
        const pdf = new jsPDF('p', 'mm', 'a4');
        const imgWidth = 210;
        const pageHeight = 297;
        const imgHeight = (canvas.height * imgWidth) / canvas.width;
        let position = 30; // 预留标题、描述空间

        // 添加自定义标题和描述
        pdf.setFontSize(16);
        pdf.text('风险网格报告', 10, 15);
        pdf.setFontSize(12);
        pdf.text('报告描述内容', 10, 25);

        // 添加网格内容到PDF
        pdf.addImage(canvas, 'PNG', 0, position, imgWidth, imgHeight);
        let remainingHeight = imgHeight - (pageHeight - position);

        // 处理分页逻辑
        while (remainingHeight > 0) {
            position = remainingHeight - imgHeight;
            pdf.addPage();
            pdf.addImage(canvas, 'PNG', 0, position, imgWidth, imgHeight);
            remainingHeight -= pageHeight;
        }

        // 下载PDF文件
        pdf.save('风险网格报告.pdf');
    } catch (error) {
        console.error('PDF导出失败:', error);
    } finally {
        // 清理克隆的临时元素
        document.body.removeChild(cloneElement);
    }
};
</script>

关键注意事项

  1. 样式兼容性:Vue的scoped样式会自动添加元素属性选择器,克隆元素时会保留该属性,因此样式可正常渲染。若仍有样式丢失,可将组件核心样式移至全局样式文件。
  2. 分辨率优化:设置scale:2可提升Canvas分辨率,避免导出的PDF模糊。
  3. 分页处理:代码中自动处理超长内容的分页,确保所有网格项都能完整导出。
  4. 漏洞规避:指定安装无已知高危漏洞的版本,避免使用老旧依赖包。

内容的提问来源于stack exchange,提问作者Antonio Klepo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 04:58:10