前端Vue框架实现html页面输出pdf(html2canvas,jspdf)

代码demo:

javascript 复制代码
<template>
    <el-dialog class="storageExportDialog" :fullscreen="true" title="" :visible.sync="visible" v-if="visible" width="600px">
        <div id="exportContainer" class="exportContainer">
            <div id="exportContent0" class="exportContent" ref="exportContent0">
                <div class="header">
                    <div class="left">{{ `Order: ${orderNmae}` }}</div>
                    <div class="right">{{ `1/${dataSlices.length+1}` }}</div>
                </div>
                <div class="data1">
                    <el-row>
                        <el-col :span="4" class="label">Wire End ID :</el-col>
                        <el-col :span="4" class="value">1</el-col>
                        <el-col :span="4" class="label">Wire ID :</el-col>
                        <el-col :span="4" class="value">2</el-col>
                        <el-col :span="4" class="label">Operator :</el-col>
                        <el-col :span="4" class="value">3</el-col>
                    </el-row>
                    <el-row>
                        <el-col :span="4" class="label">Wire End Description :</el-col>
                        <el-col :span="4" class="value">4</el-col>
                        <el-col :span="4" class="label">Wire Description :</el-col>
                        <el-col :span="4" class="value">5</el-col>
                        <el-col :span="4" class="label">Good :</el-col>
                        <el-col :span="4" class="value">6</el-col>
                    </el-row>
                    <el-row>
                        <el-col :span="4" class="label">Terminal ID :</el-col>
                        <el-col :span="4" class="value">7</el-col>
                        <el-col :span="4" class="label">Seal ID :</el-col>
                        <el-col :span="4" class="value">8</el-col>
                        <el-col :span="4" class="label">Bad :</el-col>
                        <el-col :span="4" class="value">9</el-col>
                    </el-row>
                    <el-row>
                        <el-col :span="4" class="label">Terminal Description : </el-col>
                        <el-col :span="4" class="value">10</el-col>
                        <el-col :span="4" class="label">Seal Description :</el-col>
                        <el-col :span="4" class="value">11</el-col>
                        <el-col :span="4" class="label">Total :</el-col>
                        <el-col :span="4" class="value">12</el-col>
                    </el-row>
                </div>
                <div class="data2">
                    <el-row>
                        <el-col :span="6" class="title">Tolerance</el-col>
                    </el-row>
                    <el-row>
                        <el-col :span="3" class="label">ATOL+: </el-col>
                        <el-col :span="3" class="value">5.0 %</el-col>
                        <el-col :span="3" class="label">STOL+: </el-col>
                        <el-col :span="3" class="value">10 %</el-col>
                        <el-col :span="3" class="label">ZONE 1:</el-col>
                        <el-col :span="3" class="value">20 pts</el-col>
                        <el-col :span="3" class="label">Z1TOL+:</el-col>
                        <el-col :span="3" class="value">25 %</el-col>
                    </el-row>
                    <el-row>
                        <el-col :span="3" class="label">ATOL-: </el-col>
                        <el-col :span="3" class="value">3.0 %</el-col>
                        <el-col :span="3" class="label">STOL-: </el-col>
                        <el-col :span="3" class="value">4 %</el-col>
                        <el-col :span="3" class="label">Filter: </el-col>
                        <el-col :span="3" class="value">35 %</el-col>
                        <el-col :span="3" class="label">Z1TOL-:</el-col>
                        <el-col :span="3" class="value">90 %</el-col>
                    </el-row>
                </div>
                <div class="data3">
                    <el-row>
                        <el-col :span="6" class="title">Tolerance</el-col>
                    </el-row>
                    <el-row>
                        <el-col :span="4" class="label">Crimp Height:</el-col>
                        <el-col :span="20" class="value">0.000 mm (+0.000 mm/-0.000 mm)</el-col>
                    </el-row>
                    <el-row>
                        <el-col :span="4" class="label">Crimp Height:</el-col>
                        <el-col :span="20" class="value">0.000 mm (+0.000 mm/-0.000 mm)</el-col>
                    </el-row>
                    <el-row>
                        <el-col :span="4" class="label">Min Pull Force:</el-col>
                        <el-col :span="20" class="value">0.0 N</el-col>
                    </el-row>
                </div>
                <div class="data4">
                    <el-row>
                        <el-col :span="6" class="title">Quality Verification</el-col>
                        <el-col :span="6">(Samples: 0)</el-col>
                    </el-row>
                    <el-row>
                        <el-col :span="1" class="label"></el-col>
                        <el-col :span="3" class="label">Crimp Height</el-col>
                        <el-col :span="3" class="label">Crimp Width</el-col>
                        <el-col :span="3" class="label">Pull Force</el-col>
                    </el-row>
                </div>
                <div class="data5">
                    <div class="chart1" ref="chart1"></div>
                    <div class="chart2" ref="chart2"></div>
                </div>
                <div class="data6">
                    <div class="chart3" ref="chart3"></div>
                </div>
                <div class="data7">
                    <div class="chart4" ref="chart4"></div>
                </div>
            </div>
            <div v-for="(slice, index) in dataSlices" :key="index" :id="`exportContent${index+1}`" class="exportContent" :ref="`exportContent${index+1}`">
                <div class="header">
                    <div class="left">{{ `Order: ${orderNmae}` }}</div>
                    <div class="right">{{ `${index+2}/${dataSlices.length+1}` }}</div>
                </div>
                <el-table class="dataTable" ref="dataTable" :data="slice" style="width: 100%;" stripe>
                    <el-table-column align="center" prop="index" label="No."></el-table-column>
                    <el-table-column align="center" prop="x" label="X Val"></el-table-column>
                    <el-table-column align="center" prop="date" label="Date"></el-table-column>
                    <el-table-column align="center" prop="time" label="Time"></el-table-column>
                </el-table>
            </div>
        </div>
        <div slot="footer" class="dialog-footer">
            <el-button type="success" @click="exportFn" icon="el-icon-printer" plain>导出</el-button>
            <el-button type="danger" @click="visible = false" icon="el-icon-close" plain>关闭</el-button>
        </div>
    </el-dialog>
</template>

<script>
import { getCurrentDateTime } from "@/utils/common.js"
import * as echarts from "echarts";
import html2canvas from 'html2canvas'
import jsPDF from 'jspdf'
export default {
    name: 'storageExport',
    data() {
        return {
            orderNmae: "test",
            chunkSize: 44, // 表格每页行数
            loading: null,
            chart1: null,
            chart2: null,
            chart3: null,
            chart4: null,
            visible: false,
            printTableData: [],
        }
    },
    watch: {
        visible() {
            if (this.visible == true) {
                this.initDataChart();
            } else {
                this.chart.dispose();
                this.chart = null;
            }
        }
    },
    computed: {
        dataSlices() {
            const slices = [];
            for (let i = 0; i < this.printTableData.length; i += this.chunkSize) {
                slices.push(this.printTableData.slice(i, i + this.chunkSize));
            }
            return slices;
        }
    },
    mounted() {
        for (let i = 1; i <= 100; i++) {
            this.printTableData.push({ index: i, date: "2024-09-09", time: "15:13:15", x: "1.5" },)
        }
    },
    beforeDestroy() {
    },
    methods: {
        initDataChart() {
            this.$nextTick(() => {
                this.chart1 = echarts.init(this.$refs['chart1']);
                this.chart2 = echarts.init(this.$refs['chart2']);
                this.chart3 = echarts.init(this.$refs['chart3']);
                this.chart4 = echarts.init(this.$refs['chart4']);
                let option = {
                    grid: {
                        left: '10%',
                        right: '10%',
                        bottom: '10%',
                        top: '10%',
                        containLabel: true
                    },
                    xAxis: {
                        type: 'category',
                        data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
                    },
                    yAxis: {
                        type: 'value'
                    },
                    series: [
                        {
                            data: [820, 932, 901, 934, 1290, 1330, 1320],
                            type: 'line',
                            smooth: true
                        }
                    ]
                };
                this.chart1.setOption(option);
                this.chart2.setOption(option);
                this.chart3.setOption(option);
                this.chart4.setOption(option);
            })
        },
        exportFn() {
            this.showLoading();
            console.log("导出");
            try {
                const pdf = new jsPDF('p', 'mm', 'a4');
                let imgs = [];
                let list = document.querySelectorAll('.exportContent');
                let addPromises = [];
                for (let i = 0; i < list.length; i++) {
                    let img = list[i];
                    let addPromise = new Promise((resolve, reject) => {
                        html2canvas(img, { scale: 2 }).then((canvas) => {
                            imgs.push(canvas)
                            resolve();
                        })
                    });
                    addPromises.push(addPromise);
                }
                Promise.all(addPromises).then(() => {
                    console.log("imgs", imgs);
                    imgs.forEach((img, index) => {
                        const imgData = img.toDataURL('image/webp');
                        pdf.addImage(imgData, 'WEBP', 0, 0, 210, 297);
                        if (index < imgs.length - 1) {
                            pdf.addPage();
                        }
                    })
                    this.savePdfFile(pdf);
                }).finally(() => {
                    this.closeLoading();
                })
            } catch (e) {
                console.log("导出失败", e);
                this.closeLoading();
            }
        },
        showLoading() {
            this.loading = this.$loading({
                lock: true,
                text: 'Loading',
                spinner: 'el-icon-loading',
                background: 'rgba(0, 0, 0, 0.5)'
            });
        },
        closeLoading() {
            this.loading.close();
        },
        savePdfFile(pdf) {
            const pdfBlob = pdf.output('blob');
            const pdfUrl = URL.createObjectURL(pdfBlob);
            const fileName = `${getCurrentDateTime()}.pdf`; // 自定义文件名
            const link = document.createElement('a');
            link.href = pdfUrl;
            link.download = fileName;
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
            let timer = setTimeout(() => {
                URL.revokeObjectURL(pdfUrl);
                clearTimeout(timer);
            }, 1000 * 10); // 10s 后释放 URL
        }
    }
}
</script>

<style lang="scss" scoped>
::v-deep.storageExportDialog {
    .el-dialog__footer {
        position: fixed;
        top: 0;
        right: 0;
        padding: 5px;
    }
}
::v-deep.exportContainer {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    .exportContent {
        width: 210mm;
        height: 297mm;
        border: 0.1px solid #ccc;
        overflow: scroll;
        .header {
            width: 100%;
            height: 10mm;
            display: flex;
            justify-content: space-between;
            align-items: center;
            border: 0.1px solid #ccc;
            .left {
                padding: 1mm;
                font-size: 18px;
                font-weight: bold;
            }
            .right {
                padding: 1mm;
                font-size: 12px;
            }
        }
        .data1 {
            width: 100%;
            height: 30mm;
            border: 0.1px solid #ccc;
            font-size: 12px;
            font-weight: bolder;
            .label {
                height: 7.5mm;
                line-height: 7.5mm;
                text-align: right;
            }
            .value {
                height: 7.5mm;
                line-height: 7.5mm;
                padding-left: 2mm;
                text-align: left;
            }
        }
        .data2 {
            width: 100%;
            height: 20mm;
            font-size: 12px;
            .title {
                height: 5mm;
                padding-left: 3mm;
                line-height: 5mm;
                font-weight: bolder;
            }
            .label {
                height: 7.5mm;
                line-height: 7.5mm;
                text-align: right;
            }
            .value {
                height: 7.5mm;
                line-height: 7.5mm;
                padding-left: 2mm;
                text-align: left;
            }
        }
        .data3 {
            width: 100%;
            height: 25mm;
            font-size: 12px;
            .title {
                height: 4mm;
                padding-left: 3mm;
                line-height: 4mm;
                font-weight: bolder;
            }
            .label {
                height: 7mm;
                line-height: 7mm;
                text-align: right;
            }
            .value {
                height: 7mm;
                line-height: 7mm;
                padding-left: 2mm;
                text-align: left;
            }
        }
        .data4 {
            width: 100%;
            height: 10mm;
            font-size: 12px;
            .title {
                height: 4mm;
                padding-left: 3mm;
                line-height: 4mm;
                font-weight: bolder;
            }
            .label {
                height: 6mm;
                line-height: 6mm;
                text-align: center;
            }
        }
        .data5 {
            height: 65mm;
            width: 100%;
            display: flex;
            border-bottom: 0.1px solid #ccc;
            .chart1,
            .chart2 {
                height: 65mm;
                width: 50%;
            }
        }
        .data6 {
            height: 65mm;
            width: 100%;
            .chart3 {
                height: 65mm;
                width: 100%;
            }
        }
        .data7 {
            height: 65mm;
            width: 100%;
            .chart4 {
                height: 65mm;
                width: 100%;
            }
        }
        .dataTable {
            .el-table__cell {
                padding: 0;
                font-size: 12px;
            }
        }
    }
}
</style>

页面效果:

导出效果:

相关推荐
GISer_Jing几秒前
2025前端面试热门题目——计算机网络篇
前端·计算机网络·面试
m0_748245522 分钟前
吉利前端、AI面试
前端·面试·职场和发展
理想不理想v14 分钟前
webpack最基础的配置
前端·webpack·node.js
pubuzhixing18 分钟前
开源白板新方案:Plait 同时支持 Angular 和 React 啦!
前端·开源·github
2401_8576009528 分钟前
SSM 与 Vue 共筑电脑测评系统:精准洞察电脑世界
前端·javascript·vue.js
2401_8576009528 分钟前
数字时代的医疗挂号变革:SSM+Vue 系统设计与实现之道
前端·javascript·vue.js
GDAL29 分钟前
vue入门教程:组件透传 Attributes
前端·javascript·vue.js
轻口味33 分钟前
Vue.js 核心概念:模板、指令、数据绑定
vue.js
2402_8575834939 分钟前
基于 SSM 框架的 Vue 电脑测评系统:照亮电脑品质之路
前端·javascript·vue.js
web150850966411 小时前
在uniapp Vue3版本中如何解决webH5网页浏览器跨域的问题
前端·uni-app