先看效果:
上代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="print pickingPrint" id="pickingPrint">
</div>
</body>
<style>
* {
margin: 0;
padding: 0;
}
/* 详细设置:https://developer.mozilla.org/en-US/docs/Web/CSS/@page */
@media print {
@page {
margin: 0mm;
size: a4 landscape;
msn-header: none;
msn-footer: none;
}
.noPrint {
display: none;
}
}
.pickingPrint {
margin: 0;
font-size: 14px;
box-sizing: border-box;
}
.pickingPrint h3 {
font-size: 26px;
text-align: center;
font-weight: normal;
height: 35px;
}
.pickingPrint .flex {
display: flex;
}
.pickingPrint .flex1 {
flex: 1;
}
.pickingPrint .just-right {
justify-content: flex-end;
}
.pickingPrint .align-center {
align-items: center;
}
.pickingPrint .flex-wrap {
flex-wrap: wrap;
}
.pickingPrint .head {
padding: 15px 0;
white-space: nowrap;
line-height: 1.5em;
height: 72px;
box-sizing: border-box;
}
.pickingPrint .headCode {
padding-left: 8%;
position: relative;
}
.pickingPrint .headCode .qrCode {
position: absolute;
top: 0;
left: 0;
}
.pickingPrint .tableHead {
height: 27px;
}
.pickingPrint .head .item1 {
width: 35%;
}
.pickingPrint .head .item2 {
width: 28.5%;
padding-left: 5.5%;
}
.pickingPrint .head .item3 {
width: 21%;
}
.pickingPrint .headCode .item1 {
width: 35%;
}
.pickingPrint .headCode .item2 {
padding-left: 0;
width: 30%
}
.pickingPrint .headCode .item3 {
width: 30%;
}
.pickingPrint .table .item {
padding: 4px 2px;
box-sizing: border-box;
align-items: center;
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
}
.pickingPrint .table .item1 {
width: 12%;
}
.pickingPrint .table .item2 {
width: 12%;
}
.pickingPrint .table .item3 {
flex: 1;
}
.pickingPrint .table .item4 {
width: 10%;
}
.pickingPrint .table .item5 {
width: 12%;
}
.pickingPrint .table .item6 {
width: 10%;
}
.pickingPrint .table .item7 {
width: 7%;
}
.pickingPrint .table .item8 {
width: 8%;
}
.pickingPrint .table .item .number~.number {
border-top: 1px solid #eee;
}
.pickingPrint .tableBox {
border: 1px solid #999;
border-bottom: 0;
font-size: 12px;
box-sizing: border-box;
}
.pickingPrint .tableBox .table {
border-bottom: 1px dashed #999;
}
.pickingPrint .tableBox .table:last-child {
border-bottom-style: solid;
}
.pickingPrint .page {
height: 19px;
font-size: 14px;
align-items: center;
}
.pickingPrint .break {
page-break-after: always
}
</style>
<script>
var tmpNode = document.createElement("DIV");
tmpNode.style.cssText = "width:1in;height:1in;position:absolute;left:0px;top:0px;z-index:99;visibility:hidden";
document.body.appendChild(tmpNode);
const xdpi = tmpNode.offsetWidth;
const ydpi = tmpNode.offsetHeight;
tmpNode.parentNode.removeChild(tmpNode);
const widthValue = Math.floor(xdpi / 2.54 * 21);
const heightValue = ydpi / 2.54 * 29.7;
document.querySelector("#pickingPrint").style.width = `${heightValue}px`;
const date = new Date();
const zoneOffset = date.getTimezoneOffset();
let offsetHour = Math.abs(zoneOffset / 60);
let offsetHourString = "";
if (zoneOffset <= 0) {
offsetHourString = `+${offsetHour}`
}
if (zoneOffset > 0) {
offsetHourString = `-${offsetHour}`
}
const lanSet = {
cn: {
title: '标题',
number: "字段",
typeNumber: "字段",
totalNumber: "字段",
printedBy: "字段",
printTime: "字段",
printingTimes: "字段",
barCode: "字段",
sku: "字段",
goodName: '字段',
specification: "字段",
brand: "字段",
classification: "字段",
quantity: "字段",
gift: "字段",
page: "字段"
}
};
renderPage(0, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], 'cn');
function renderPage(index, list, lan) {
const char1 = `${lanSet[lan].number}: 字段`;
const char2 = `${lanSet[lan].typeNumber}:1`;
const char3 = `${lanSet[lan].totalNumber}:1`;
const char4 = `${lanSet[lan].printedBy}:管理员账号`;
const char5 = `${lanSet[lan].printTime}: ${date.toLocaleDateString() + " " + date.toLocaleTimeString()} ${offsetHourString}`;
const char6 = `${lanSet[lan].printingTimes}: ${index + 1}`;
const char7 = `OWCU/${lanSet[lan].barCode}`;
const char8 = `sku/${lanSet[lan].sku}`;
const char9 = `${lanSet[lan].goodName}`;
const char10 = `${lanSet[lan].specification}`;
const char11 = `${lanSet[lan].brand}`;
const char12 = `${lanSet[lan].classification}`;
const char13 = `${lanSet[lan].quantity}`;
const char14 = `${lanSet[lan].gift}`;
const pageItem = document.createElement("div");
pageItem.style.padding = `${ydpi / 2.54}px`;
pageItem.innerHTML = `
<h3>${lanSet[lan].title}</h3>
<div class="flex flex-wrap head">
<div class="item1">${char1}</div>
<div class="item2">${char2}</div>
<div class="item3">${char3}</div>
<div class="item1">${char4}</div>
<div class="item2">${char5}</div>
<div class="item3">${char6}</div>
</div>
<div class="flex table tableHead">
<div class="item item1">${char7}</div>
<div class="item item2">${char8}</div>
<div class="item item3">${char9}</div>
<div class="item item4">${char10}</div>
<div class="item item5">${char11}</div>
<div class="item item6">${char12}</div>
<div class="item item7">${char13}</div>
<div class="item item8">${char14}</div>
</div>
<div class="tableBox">
</div>
<div class="flex just-right page">${index + 1}/<span class="totalPage"></span>${lanSet[lan].page}</div>
`;
if (index > 0) {
pageItem.classList.add("break");
}
document.querySelector("#pickingPrint").appendChild(pageItem);
document.querySelectorAll("#pickingPrint .totalPage").forEach(totalPage => {
totalPage.innerText = index + 1;
});
renderItem(0);
console.log(widthValue * (index + 1), "1111");
function renderItem(itemIndex) {
const item = list[itemIndex];
const tableItem = document.createElement("div");
tableItem.className = "flex table";
tableItem.innerHTML = `
<div class="item item1">
<div class="number">字段</div>
<div class="number">字段</div>
</div>
<div class="item item2">
<div class="number">字段</div>
<div class="number">字段</div>
</div>
<div class="item item3">字段</div>
<div class="item item4">字段</div>
<div class="item item5">字段</div>
<div class="item item6">字段</div>
<div class="item item7">${itemIndex}</div>
<div class="item item8">字段</div>
`;
document.querySelectorAll("#pickingPrint .tableBox")[index].appendChild(tableItem);
if (pageItem.offsetHeight > widthValue) {
//有下一页
document.querySelectorAll("#pickingPrint .tableBox")[index].removeChild(tableItem);
if (list.slice(itemIndex, list.length).length > 0) {
renderPage(index + 1, list.slice(itemIndex, list.length), lan);
} else {
window.print();
}
} else {
//没有下一页
if (list[itemIndex + 1]) {
renderItem(itemIndex + 1);
} else {
// 没有数据了
window.print();
}
}
}
}
</script>
</html>
核心点:
1、计算A4纸张在宿主屏幕中的实际大小,计算方法:设置一个1英寸的元素,根据实际大小算出实际占用像素,然后根据宽高像素值反推A4纸大小。
2、根据元素动态创建高度,实现分页,分页样式: page-break-after: always
3、设置打印样式:@media print,详细参数见备注链接,我将页面大小设置成了auto,因为我前面已经精准算出了分页符的位置,所以打印时候会自动分页。也就是每页都会分成A4纸的大小。
拓展:
电商类的标签打印,也可以使用这个方案,比如打印100*100的物流面单。可以动态插入一条打印样式
@media print { @page { size:${width}px ${height}px; }}
彩蛋:
当前页面直接打印,一个小魔法。
@media print {
body { height: auto; }
body>:not(.printArea) { display: none;}
#printBody {height: auto !important;}
}
#printBody {
width: 100%;
height: 0;
overflow: hidden;
}