前端el-table导出pdf

本文介绍了如何在前端利用html2Canvas和JsPDF插件,将el-table转换为图片并进一步转化为PDF文件。通过点击按钮触发打印事件,实现了表格的PDF导出功能。

1、首先来看下效果图

 2、使用技术:html2Canvas、JsPDF插件,mode_modules下载两个插件

npm  i  html2Canvas  JsPDF

其中,html2Canvas 负责把html转为图片,JsPDF负责把图片转为pdf文件

3、点击按钮触发打印事件

//点击按钮触发打印事件
button @click='htmlToPdf(节点ID',文件名')'>print<button>

//传入打印节点,文件名,然后调用打印事件
htmlToPdf(domName, title) {
  this.downloadPDF(domName, title)
},







//主要代码
downloadPDF(domName, title) {
  window.pageYoffset = 0;
  document.documentElement.scrollTop = 0;
  document.body.scrollTop = 0;

  // 水印
  const can = this.waterMark()

    const el = document.getElementById(domName);
    return new Promise((resolve, reject) => {
      const ele = document.getElementById(domName);
  
      let eleW = ele.offsetWidth+20; // 获得该容器的宽
      //   let eleH = ele.offsetHeight // 获得该容器的高
      let eleH = ele.offsetHeight; // 获得该容器的高
      let eleOffsetTop = ele.offsetTop; // 获得该容器到文档顶部的距离
      let eleOffsetLeft = ele.offsetLeft; // 获得该容器到文档最左的距离
      var canvas = document.createElement("canvas");
      let scaleBy = window.devicePixelRatio > 1 ? window.devicePixelRatio : 1;
      canvas.width = eleW * 2; // 将画布宽&&高放大两倍
      canvas.height = eleH * 2;
      var context = canvas.getContext("2d");
      context.scale(2, 2);
      //  context.translate(-offsetLeft - abs, -offsetTop);
      var opts = {
        scale: 2,//解决pdf不清晰问题
        // canvas: canvas,
        width: eleW,
        height: eleH,
        // logging :true,
        useCORS: true,
        background: "#FFF",
        allowTaint: false,
        // dpi: 300,
        dpi: window.devicePixelRatio * 2,
        // scale: window.devicePixelRatio *
<el-table v-loading="loading" :data="recordList" @selection-change="handleSelectionChange"> <el-table-column type="selection" width="55" align="center" /> <el-table-column label="服务单号" align="center" prop="seviceRecno" /> <el-table-column label="服务请求ID" align="center" prop="serviceRequestId" /> <el-table-column label="工程师ID" align="center" prop="engineerId" /> <el-table-column label="操作类型" align="center" prop="actionType" /> <el-table-column label="操作描述" align="center" prop="actionDescription" /> <el-table-column label="使用零件" align="center" prop="partsUsed" /> <el-table-column label="耗时(小时)" align="center" prop="hoursSpent" /> <el-table-column label="下一步计划" align="center" prop="nextStep" /> <el-table-column label="经验教训" align="center" prop="lessonLearn" /> <el-table-column label="纸质记录" align="center" prop="paperRecord" /> <el-table-column label="记录时间" align="center" prop="recordDate" width="180"> <template slot-scope="scope"> <span>{{ parseTime(scope.row.recordDate, '{y}-{m}-{d} {h}:{m}:{s}') }}</span> </template> </el-table-column> <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> <template slot-scope="scope"> <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['mes:record:edit']" >修改</el-button> <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['mes:record:remove']" >删除</el-button> </template> </el-table-column> </el-table>其中纸质记录可点击,点击后通过获取recordAdrees,打开文件
10-12
<template> <div class="app-container"> <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px"> <el-form-item> <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> </el-form-item> </el-form> <el-row :gutter="10" class="mb8"> <el-col :span="1.5"> <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" v-hasPermi="['business:medicalExamination:export']" >导出</el-button> </el-col> <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> </el-row> <el-table v-loading="loading" :data="medicalExaminationList" @selection-change="handleSelectionChange" border> <el-table-column label="厂区" align="center" prop="site" :min-width="100"/> <el-table-column label="工号" align="center" prop="cpf01" :min-width="100"> <template slot-scope="scope"> <a @click="handleLastCard(scope.row.cpf01)">{{ scope.row.cpf01 }}</a> </template> </el-table-column> <el-table-column label="姓名" align="center" prop="name" /> <el-table-column label="性别" align="center" prop="sex" /> <el-table-column label="年龄" align="center" prop="age" /> <el-table-column label="员工类别" align="center" prop="salaryType" /> <el-table-column label="厂处" align="center" prop="dptname03" /> <el-table-column label="体检日期" align="center"> <template slot-scope="scope"> <span>{{ parseTime(scope.row.examinationDate, '{y}-{m}-{d} {h}:{i}:{s}') }}</span> </template> </el-table-column> <el-table-column label="高压(mmHg)" align="center" prop="high" /> <el-table-column label="低压(mmHg)" align="center" prop="low" /> <el-table-column label="体检报告" align="center" class-name="small-padding fixed-width"> <template slot-scope="scope"> <el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)" v-hasPermi="['business:medicalExamination:download']" >下载PDF</el-button> </template> </el-table-column> <el-table-column label="入职日期" align="center"> <template slot-scope="scope"> <span>{{ parseTime(scope.row.cpfud13, '{y}-{m}-{d}') }}</span> </template> </el-table-column> <!-- <el-table-column label="身高(cm)" align="center" prop="height" />--> <!-- <el-table-column label="体重(kg)" align="center" prop="weight" />--> <!-- <el-table-column label="BMI" align="center" prop="bmi" />--> <!-- <el-table-column label="高压(mmHg)" align="center" prop="high" />--> <!-- <el-table-column label="低压(mmHg)" align="center" prop="low" />--> <!-- <el-table-column label="心率(次/分)" align="center" prop="rate" />--> <!-- <el-table-column label="血氧饱和度(%)" align="center" prop="sp" />--> <!-- <el-table-column label="体温(℃)" align="center" prop="tiwen" />--> <!-- <el-table-column label="脂肪量(kg)" align="center" prop="zfl" />--> <!-- <el-table-column label="脂肪率(%)" align="center" prop="zflv" />--> <!-- <el-table-column label="体水分量(kg)" align="center" prop="tsfl" />--> <!-- <el-table-column label="体水分率(%)" align="center" prop="tsflv" />--> <!-- <el-table-column label="肌肉量(kg)" align="center" prop="jrl" />--> <!-- <el-table-column label="肌肉率(%)" align="center" prop="jrlv" />--> <!-- <el-table-column label="蛋白质(kg)" align="center" prop="dbz" />--> <!-- <el-table-column label="蛋白质率(%)" align="center" prop="dbzlv" />--> <!-- <el-table-column label="细胞内液量(kg)" align="center" prop="xbnyl" />--> <!-- <el-table-column label="细胞内液率(%)" align="center" prop="xbnylv" />--> <!-- <el-table-column label="细胞外液量(kg)" align="center" prop="xbwyl" />--> <!-- <el-table-column label="细胞外液率(%)" align="center" prop="xbwylv" />--> <!-- <el-table-column label="基础代谢(kcal)" align="center" prop="jcdx" />--> <!-- <el-table-column label="骨盐(kg)" align="center" prop="gy" />--> <!-- <el-table-column label="去脂体重(kg)" align="center" prop="qztz" />--> <!-- <el-table-column label="骨量(kg)" align="center" prop="gl" />--> <!-- <el-table-column label="内脏脂肪(级)" align="center" prop="nzzf" />--> <!-- <el-table-column label="阻抗(Ω)" align="center" prop="zukang" />--> <!-- <el-table-column label="体表面积(平方米)" align="center" prop="tbmj" />--> <!-- <el-table-column label="超重百分比(%)" align="center" prop="czbfb" />--> <!-- <el-table-column label="骨骼肌量(kg)" align="center" prop="ggjl" />--> <!-- <el-table-column label="骨骼肌率(%)" align="center" prop="ggjlv" />--> <!-- <el-table-column label="皮下脂肪量(kg)" align="center" prop="pxzf" />--> <!-- <el-table-column label="皮下脂肪率(%)" align="center" prop="pxzflv" />--> <!-- <el-table-column label="最大心率(次/分钟)" align="center" prop="maxrate" />--> </el-table> <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" /> <!-- 添加或修改体检一体机对话框 --> <el-dialog :title="title" :visible.sync="open" width="800px" append-to-body> <el-table v-loading="loading" :data="cardList" border :max-height="550"> <el-table-column label="姓名" align="center" prop="emp_no" :width="150"/> <el-table-column label="工号" align="center" prop="emp_name" :width="150"/> <el-table-column label="刷卡时间" align="center"> <template slot-scope="scope"> <span>{{ parseTime(scope.row.event_time, '{y}-{m}-{d} {h}:{i}:{s}') }}</span> </template> </el-table-column> <el-table-column label="刷卡位置" align="center" prop="event_location" /> </el-table> </el-dialog> </div> </template> <script> import { listMedicalExamination, getMedicalExamination, delMedicalExamination, addMedicalExamination, updateMedicalExamination } from "@/api/business/medicalExamination"; import * as https from "https"; import axios from "axios"; export default { name: "MedicalExamination", data() { return { // 遮罩层 loading: true, // 选中数组 ids: [], // 非单个禁用 single: true, // 非多个禁用 multiple: true, // 显示搜索条件 showSearch: true, // 总条数 total: 0, // 体检一体机表格数据 medicalExaminationList: [], cardList : [], // 弹出层标题 title: "", // 是否显示弹出层 open: false, // 查询参数 queryParams: { pageNum: 1, pageSize: 10, name: null, sex: null, age: null, cpf01: null, }, // 表单参数 form: {}, // 表单校验 rules: { } }; }, created() { this.getList(); }, methods: { /** 查询体检一体机列表 */ getList() { this.loading = true; listMedicalExamination(this.queryParams).then(response => { this.medicalExaminationList = response.rows; this.total = response.total; this.loading = false; }); }, // 取消按钮 cancel() { this.open = false; this.reset(); }, // 表单重置 reset() { this.form = { id: null, name: null, sex: null, age: null, cpf01: null, examinationDate: null, height: null, weight: null, bmi: null, lxtz: null, mbtz: null, high: null, low: null, rate: null, sp: null, tiwen: null, zfl: null, zflv: null, tsfl: null, tsflv: null, jrl: null, jrlv: null, dbz: null, dbzlv: null, xbnyl: null, xbnylv: null, xbwyl: null, xbwylv: null, jcdx: null, gy: null, qztz: null, gl: null, nzzf: null, zukang: null, tbmj: null, czbfb: null, ggjl: null, ggjlv: null, pxzf: null, pxzflv: null, maxrate: null, createTime: null, createBy: null, site: null }; this.resetForm("form"); }, /** 搜索按钮操作 */ handleQuery() { this.queryParams.pageNum = 1; this.getList(); }, /** 重置按钮操作 */ resetQuery() { this.resetForm("queryForm"); this.handleQuery(); }, // 多选框选中数据 handleSelectionChange(selection) { this.ids = selection.map(item => item.id) this.single = selection.length!==1 this.multiple = !selection.length }, /** 新增按钮操作 */ handleAdd() { this.reset(); this.open = true; this.title = "添加体检一体机"; }, /** 下载PDF操作 */ handleDownload(row) { }, handleLastCard(cpf01){ this.open = true; this.getCardData(cpf01) }, async getCardData(cpf01) { try { const empNo = cpf01; const eventTime = this.formatDate(new Date()); // 使用当前日期 const endUrl = 'doris%2Fehs_special_case_track'; const response = await axios.get( `https://ehs.luxsan-ict.com/prod-api/business/ehs-api-caller/getApiData?endUrl=${endUrl}&empNo=${empNo}&eventTime=${eventTime}` ); if (response.data.error === 0) { this.cardList = response.data.data; } else { alert("请求失败: " + response.data.message); } } catch (error) { console.error("接口请求错误:", error); alert("网络请求失败,请检查接口是否可用"); } finally { this.loading = false; } }, formatDate(date) { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); return `${year}-${month}-${day}`; }, /** 提交按钮 */ submitForm() { this.$refs["form"].validate(valid => { if (valid) { if (this.form.id != null) { updateMedicalExamination(this.form).then(response => { this.$modal.msgSuccess("修改成功"); this.open = false; this.getList(); }); } else { addMedicalExamination(this.form).then(response => { this.$modal.msgSuccess("新增成功"); this.open = false; this.getList(); }); } } }); }, /** 删除按钮操作 */ handleDelete(row) { const ids = row.id || this.ids; this.$modal.confirm('是否确认删除体检一体机编号为"' + ids + '"的数据项?').then(function() { return delMedicalExamination(ids); }).then(() => { this.getList(); this.$modal.msgSuccess("删除成功"); }).catch(() => {}); }, /** 导出按钮操作 */ handleExport() { this.download('business/medicalExamination/export', { ...this.queryParams }, `medicalExamination_${new Date().getTime()}.xlsx`) } } }; </script> 刷卡时间倒叙排序
最新发布
12-02
<think>好的,我现在需要帮助用户解决关于el-table多表头导出的问题。首先,我得回忆一下el-tableElement UI中的一个表格组件,多表头通常是通过嵌套的el-table-column实现的。用户的问题是如何导出这种复杂结构的表格到文件,比如Excel。 首先,用户的需求是导出多级表头,可能需要保持表头的层级结构。常见的导出方法可能有几种:使用第三方库如xlsx或exceljs,或者Element UI自带的导出功能,不过自带的可能不支持多表头。需要确认Element UI的官方文档是否支持多表头导出。 接下来,考虑到用户可能已经尝试过官方方法但遇到问题,所以需要提供自定义的导出方案。这时候,可能需要手动构建表头的数据结构。例如,遍历el-table的列信息,特别是多级表头的情况,需要递归处理columns,收集每一层的标题,并合并单元格,以确保导出的Excel表头正确显示层级。 然后,用户可能需要将数据与表头对应起来。比如,处理多级表头下的数据行,确保数据列与表头匹配。可能需要处理列的子节点,递归获取所有叶子节点,以确定数据字段。 另外,使用xlsx库的话,需要生成一个二维数组,其中第一层是表头的多行,下面的是数据。例如,如果表头有两层,那么导出的表头部分可能需要两行,第一行是父标题,跨列合并,第二行是子标题。 还要考虑单元格合并的问题,使用xlsx的merges属性来设置合并区域。比如,父标题可能需要横向合并多个子标题的单元格。 可能的挑战包括如何正确遍历多级表头结构,生成对应的表头行,以及处理数据的对应关系。此外,用户可能需要处理一些边界情况,例如表头中有复杂的嵌套,或者某些列没有子列等。 还需要考虑代码示例的结构,可能需要提供一个函数,递归处理columns,生成表头数组,然后将数据部分转换为二维数组,最后使用xlsx的utils和writeFile方法生成文件。 最后,要提醒用户可能需要注意的事项,比如样式处理、性能问题(大数据量时的导出速度),以及可能的浏览器兼容性问题。另外,如果用户需要更复杂的样式,可能需要使用其他库如exceljs,它提供更多的格式控制选项。</think>在Element UI中实现多表头导出需要结合第三方库处理复杂结构,以下是两种常用方案: 一、使用xlsx库实现(推荐) ```javascript import XLSX from 'xlsx' function exportMultiHeaderExcel() { // 1. 获取表格DOM const table = document.querySelector('.el-table') // 2. 递归处理多级表头 function processHeaders(columns, maxLevel = 3) { const headerRows = Array.from({ length: maxLevel }, () => []) function traverse(columns, level = 0, parentSpan = 1) { columns.forEach(col => { const cell = { v: col.label, t: 's', s: { alignment: { horizontal: 'center' } } } if (col.children) { const colspan = traverse(col.children, level + 1, col.children.length) cell.s = { ...cell.s, alignment: { horizontal: 'center' } } headerRows[level].push(Object.assign({}, cell, { colspan })) } else { headerRows[level].push(cell) // 填充下层空白 for (let i = level + 1; i < maxLevel; i++) { headerRows[i].push({ v: '', t: 's' }) } } }) return columns.some(c => c.children) ? columns.length : parentSpan } traverse(columns) return headerRows } // 3. 获取表头数据(需要根据实际结构调整) const columns = this.$refs.table.columns // 需要根据实际表格引用调整 const headerData = processHeaders(columns) // 4. 准备数据体 const bodyData = this.tableData.map(row => columns.filter(col => !col.children).map(col => row[col.property]) ) // 5. 合并数据 const exportData = [...headerData, ...bodyData] // 6. 创建工作表 const ws = XLSX.utils.aoa_to_sheet(exportData) // 7. 设置合并(示例) ws['!merges'] = [ { s: { r: 0, c: 0 }, e: { r: 0, c: 2 } }, // 合并第一行前三个单元格 { s: { r: 1, c: 3 }, e: { r: 1, c: 5 } } // 根据实际表头结构调整 ] // 8. 创建工作簿并导出 const wb = XLSX.utils.book_new() XLSX.utils.book_append_sheet(wb, ws, 'Sheet1') XLSX.writeFile(wb, 'multi-header.xlsx') } ``` 二、使用exceljs实现(支持更复杂样式) ```javascript import ExcelJS from 'exceljs' async function exportWithExcelJS() { const workbook = new ExcelJS.Workbook() const worksheet = workbook.addWorksheet('Sheet1') // 处理多级表头 const buildHeaders = (columns, startRow) => { let maxDepth = 0 function traverse(col, row, parent) { const cell = worksheet.getCell(row, col.colIndex) cell.value = col.label cell.alignment = { vertical: 'middle', horizontal: 'center' } if (col.children) { col.children.forEach(child => { child.colIndex = col.colIndex + col.children.indexOf(child) traverse(child, row + 1, col) }) worksheet.mergeCells( row, col.colIndex, row, col.colIndex + col.children.length - 1 ) maxDepth = Math.max(maxDepth, row + 1) } else { worksheet.mergeCells( row, col.colIndex, maxDepth, col.colIndex ) } } columns.forEach((col, index) => { col.colIndex = index + 1 traverse(col, startRow) }) } // 生成表头 buildHeaders(this.$refs.table.columns, 1) // 添加数据行... // 导出文件 const buffer = await workbook.xlsx.writeBuffer() const blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }) saveAs(blob, 'multi-header.xlsx') } ``` 注意事项: 1. 表头结构处理: - 需要递归遍历columns配置 - 注意跨列合并(colspan)的计算 - 保持层级结构对应关系 2. 数据对应: - 需要处理多级表头下的数据字段映射 - 建议使用column.property作为字段标识 3. 样式优化: - 合并单元格时要考虑边框连续性 - 建议设置固定行高(20-30px为宜) - 添加表头背景色区分层级 4. 性能优化: - 大数据量建议分页导出 - 使用web worker防止界面卡顿 - 添加进度提示 扩展方案: 1. 服务端导出:将表格配置和JSON数据发送到后端处理 2. 使用PDF导出:适用于需要保持精确布局的场景 3. 前端缓存:对频繁使用的表格进行导出模板缓存 实际使用时需要根据具体表头结构调整合并策略和遍历逻辑,建议先通过console.log输出表头结构进行分析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值