多个表格需要,pdf需要的格式与原本展示的表格样式不同
1.创建一个新的表格,设置pdf需要的样式,用vue的h函数放入dom中
2.excel用xlxs插件直接传入新建el-table的dom,直接导出
3.pdf导出类似excel黑色边框白底黑字的文件,把el-table改成需要的样式,
由于table内部的表格是由td,tr绘制的所以表头和表格本身必须使用插槽写div给固定的表格宽度和高度配合
:row-style="rowStyle"
:cell-style="cellStyle"
:header-cell-style="headerCellStyle"
4.导pdf使用jspdf插件,只能接受图片格式(base64/png、jpg)等,使用html2Canvas先把dom转成canvas在转成图片
5.pdf文件的尺寸和分页需要根据需求设置,主要方法是
创建pdf文件对象
let pdf = new jsPDF('p', 'mm', 'a4');
为pdf页添加图片内容
pdf.addImage(
testImage,
'JPEG',
10,
10,
190,
Math.min(a4h, (190 * page.height) / page.width),
);
新增一页
pdf.addPage();
demo
exportPDFExcel.js
导出方法调出弹窗,调用addTable方法dom创建成功后处理导出事件
import { ElMessageBox, ElButton, ElLoading, ElTable } from 'element-plus'
import { h, nextTick } from 'vue'
import { request } from "@/utils/request";
import Menu from "./creatTable.js";
import { saveAs } from "file-saver";
import XLSX from "xlsx";
import { removeTable } from "./creatTable";
import html2Canvas from 'html2canvas';
import jsPDF from 'jspdf';
let fileName = ''
let fileType = ''
let mesBox = null
let loading = null
//导选择创口,pdfexcel
export function exportMessageBox(params, name, columns) {
ElMessageBox({
title: '请选择导出格式',
message: h('p', null, [
h('div', {
class: 'el-button el-button--primary el-button--default', innerHTML: 'Excel', onClick: () => {
getData(params, name, columns, 'xlsx')
}
}),
h('div', {
class: 'el-button el-button--primary el-button--default', innerHTML: 'PDF', onClick: () => {
getData(params, name, columns, 'pdf')
}
})
]),
'showConfirmButton': false
})
}
function getData(params, name, columns, type) {
//关闭弹窗,打开loading
mesBox = document.querySelector(".el-message-box__headerbtn");
mesBox.click();
loading = ElLoading.service({
text: "正在下载数据,请稍候",
background: "rgba(0, 0, 0, 0.7)",
});
fileName = name
fileType = type
//业务操作,调接口抽数据
request.get(params.url, params).then((response) => {
if (response.code === 200) {
let list = response.rows;
//创建table,传递列和数据组
Menu.addTable(list, columns)
}
});
}
export function downFile(params) {
//构建文件名
const currentDate = new Date();
const year = currentDate.getFullYear();
const month = String(currentDate.getMonth() + 1).padStart(2, "0");
const day = String(currentDate.getDate()).padStart(2, "0");
const hours = String(currentDate.getHours()).padStart(2, "0");
const minutes = String(currentDate.getMinutes()).padStart(2, "0");
const seconds = String(currentDate.getSeconds()).padStart(2, "0");
const formattedDate = `${year}_${month}_${day}_${hours}_${minutes}_${seconds}`;
const txt = `${fileName}${formattedDate}.${fileType}`
nextTick(() => {
//获取到dom
const table = document.querySelector("#table_export_content_one")
if (fileType === 'xlsx') {
//导出excel,直接传dom给XLSX用其方法
const workbook = XLSX.utils.table_to_book(
table,
{
raw: true, //有的是日期、小数等格式,直接乱码#。所以这里直接保留原始字符串
}
);
const wbout = XLSX.write(workbook, {
bookType: "xlsx",
bookSST: true,
type: "array",
});
saveAs(
new Blob([wbout], {
type: "application/octet-stream",
}),
txt
);
removeTable()
loading.close()
}
else {
downPDF()
}
});
}
//导出pdf
function downPDF() {
const table = document.querySelector("#table_export_content_one")
let title = fileName
//表格整体转canvas
html2Canvas(table, {
removeContainer: true,
useCORS: true,
}).then((canvas) => {
let pdf = new jsPDF('p', 'mm', 'a4'); //A4纸,纵向
//a4w和a4h在列少的情况可以直接配210*297,列多横板分页需要配成表格宽度*一页需要放的表格数,高度类比
let ctx = canvas.getContext('2d'),
a4w = 154 * 8,
a4h = 25 * 100, //A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277
renderedWidth = 0
let kss = true
while (renderedWidth <= canvas.width) {
let renderedHeight = 0
while (renderedHeight <= canvas.height) {
let page = document.createElement('canvas');
page.width = Math.min(a4w, canvas.width - renderedWidth);
page.height = Math.min(a4h, canvas.height - renderedHeight); //可能内容不足一页
//getImage获取一页的选区转成图片,参数为x,y,width,height
page
.getContext('2d')
.putImageData(
ctx.getImageData(
renderedWidth,
renderedHeight,
Math.min(a4w, canvas.width - renderedWidth),
Math.min(a4h, canvas.height - renderedHeight),
),
0,
0,
);
const testImage = page.toDataURL('image/jpeg', 1.0)
if(!kss){
const allImage = canvas.toDataURL('image/jpeg', 1.0)
const a = document.createElement("a");
a.download = '0123456.PNG';
a.href = allImage;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
kss=true
}
//将获取的该页image加入到pdf中
pdf.addImage(
testImage,
'JPEG',
10,
10,
190,
Math.min(a4h, (190 * page.height) / page.width),
); //添加图像到页面,保留10mm边距
renderedHeight += a4h;
if (renderedHeight < canvas.height) {
pdf.addPage(); //如果后面还有内容,添加一个空页
}
}
renderedWidth += a4w;
if (renderedWidth < canvas.width) {
pdf.addPage(); //如果后面还有内容,添加一个空页
}
}
pdf.save(title + '.pdf');
//移除临时创建的table,
removeTable()
loading.close()
})
}
function formatJson(filterVal, list) {
return list.map((v) =>
filterVal.map((j) => {
// 进行日期格式化
const arr = j.split(".");
if (arr.length === 1) {
return v[j];
} else if (arr.length === 2 && eval("v." + arr[0])) {
return eval("v." + j);
} else if (
arr.length === 3 &&
eval("v." + arr[0]) &&
eval("v." + arr[0] + "." + arr[1])
) {
return eval("v." + j);
}
return "";
})
);
}
creatTable.js
创建table方法和移除table方法
import { createVNode, render ,h, nextTick} from "vue";
import tableElement from "./index.vue";
import {downFile} from './exportPDFExcel'
// 定义一个div容器
const div = document.createElement("div");
document.body.appendChild(div);
export default {
addTable(list, columnsProp,) {
const vnode = createVNode(tableElement, { list, columnsProp });
render(vnode, div);
nextTick(()=>{
downFile()
})
},
};
export const removeTable = function () {
render(null, div);
};
index.vue
虚拟构建的table的dom内容,和参数处理
<template>
<div class="export_box_table">
<div class="export_table">
<el-table
:row-style="rowStyle"
:cell-style="cellStyle"
:header-cell-style="headerCellStyle"
id="table_export_content_one"
:data="test"
isShowDynamicColumn
>
<el-table-column
v-for="item in columns"
:label="item.label"
:prop="item.prop"
>
<template #header="scope">
<div :style="{ width: cellHeight + 'px' }">
{{ item.label }}
</div>
</template>
<template #default="scope">
<div
:style="{
'background-color': getStyle1(scope.row, item),
width: cellHeight + 'px',
color: '#000',
}"
>
44444
</div>
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
<script setup>
import dciTableColumn from "@/views/admin/dci/components/dciTableColumn.vue";
import { ElTable, ElTableColumn } from "element-plus";
import { computed } from "vue";
const { proxy } = getCurrentInstance();
const columns = ref([]);
const cellHeight = ref(150);
const tableWidth = computed(() => {
return (cellHeight.value+26) * (columns.value.length) + "px";
});
//测试数据
let testObj =
{
createBy: null,
createTime: "2023-11-20 16:29:57",
instPowerT: 0,
instPowerMw: 0,
instCoal: 0,
instGas: 0,
instWater: 0,
instElec: 0,
instHeat: 301.73,
}
let test = new Array(200).fill(testObj);
//测试结束
defineProps({
list: {
type: Array,
default() {
return [];
},
},
columnsProp: {
type: Array,
default() {
return [];
},
},
});
initTable();
function initTable() {
columns.value = [];
proxy.columnsProp.forEach((item, index) => {
if (item.props) {
initItem(item.props);
columns.value.push(item.props);
}
});
}
function rowStyle() {
return {
// "background-color": "rgba(2,5,2,0.2)",
// "background-color": "rgba(7,29,68,1)",
color: "#000",
};
}
function cellStyle() {
let hei = "2px";
let style = {
border: "1px solid #000",
// "background-color": "rgba(7,29,68,1)",
"background-color": "#fff",
padding: "0px",
height: hei,
"line-height": hei,
};
return style;
}
function headerCellStyle() {
return {
"background-color": "#fff!important",
width: "250px",
border: "1px solid #000",
color: "#000",
height: "32px!important",
"font-size": "14px",
padding: "0px",
"line-height": "32px",
};
}
function initItem(item) {
item.magnification = item.magnification ? item.magnification : 1;
item.suffix = item.suffix ? item.suffix : "";
item.color = item.color ? item.color : "";
item.type = item.type ? item.type : "";
item.tooltip = item.tooltip ? item.tooltip : "";
item.chuhui = item.chuhui ? item.chuhui : "";
item.ziduanName = item.ziduanName ? item.ziduanName : "";
item.ziduanValue = item.ziduanValue ? item.ziduanValue : "";
item.ziduanColor = item.ziduanColor ? item.ziduanColor : "";
item.isPhone = item.isPhone ? item.isPhone : false;
item.time = item.time ? item.time : false;
item.fixedNumber = item.fixedNumber ? item.fixedNumber : 1;
item.export = item.export ? item.export : false;
}
</script>
<style scoped>
.export_box_table{
width: 800px;
overflow: auto;
/* position: fixed; */
height: 500px;
/* z-index: 500; */
/* top: 0px; */
background-color: rgb(0, 255, 179);
}
.export_table {
width: fit-content;
height: 500px;
background-color: red;
/* position: absolute;
top: 0px;
left: 0px; */
overflow: auto;
}
#table_export_content_one {
width: v-bind(tableWidth);
}
</style>
<style lang="scss" scoped>
.column_visible {
position: absolute;
z-index: 9;
left: 100px;
top: -0px;
}
</style>
<style>
.hover_row_pointer:hover {
cursor: pointer;
}
</style>