
不简说
2023/04/14阅读:132主题:自定义主题1
细思极恐,是我太过单纯。这种动态生成打印模板,没点心思通不了!
不知道标题有没有让你眼前一亮😄
❝「日常工作中、难免遇到各种离谱的需求。本篇以【vue-plugin-hiprint】打印插件为引,和码友们
❞分享
一下,如何去分析并实现
各种需求。」
思考🤔
日常中,需求
≈(约等于) 需要实现的功能/效果
。 既然是功能/效果
,那我们肯定需要先分析啦!
那我们应该从哪些方面进行分析呢?
比如:
-
输出
的到底是什么
(要得到的功能/效果);-
比如 浏览器读取文件
-
-
是否需要 硬件(底层)
支持;-
比如 浏览器
支持不支持访问文件
;
-
-
是否已有 案例
(分析目前技术栈能不能做到);-
比如 xxx有这个功能
; -
或者 能实现
到哪一步
;
-
-
如果 以上两点都不支持
,那么我们就需要考虑:-
哪些技术方便实现
(类似功能/效果); -
需要
哪些硬件(底层)
支持; -
可能
遇到的问题
; -
时间/人员
安排(需要现学否?);
-
以下内容以打印模板为例子🌰,希望各位码友有所感悟
。效果如下图:

本篇源码地址: https://github.com/CcSimple/vue-plugin-hiprint-start
1.前言⁉️
本篇以一名刚接触
「vue-plugin-hiprint」插件的小伙伴视野来阐述。
「需求」:把「多个」标签打印模板
(「模板中包含多个元素,模板可拖拽调整」),铺满 A4 纸
打印。也可能
是其他纸张
。
「已知」:该插件支持多面板
、多模板
、批量打印
;当前标签模板大小
、A4纸大小
、目前返回的打印数据
是一维数组
;该插件默认是没有自动铺满
功能的。
根据需求
和已知条件
,我们试着分析一下
:
-
需求到底
要的是输出什么
内容;(目前插件能实现到哪一步
); -
熟悉
该插件
的实现逻辑(能做些什么
,原理是什么
); -
可能存在
的一些问题
;(插件的不足之处
);
👌🏻进入正题,老规矩
先列个目录
:
-
分析
打印输出的效果
; -
剖析
插件模板打印渲染原理
; -
实现
铺满 A4 纸的需求
; -
思考🤔
这样实现存在的问题
;
分析 打印 输出的效果
已知
有两个标签打印模板
,如下图:


这两个模板
的内容(包含多个打印元素)
是可以微调
的。想要铺满A4纸
,意思就是这A4纸
可以容纳多个这两种模板
。分析嘛,先随便画个草图:

我们把左侧模板
和右侧模板
看作一个整体
,大概输出的效果
就是这个样子。
至于模板
中更多的打印元素
,我们就需要进一步分析
该插件的渲染原理
了。比如它是怎么定位
的、怎么排序
等等。
剖析 插件 模板打印渲染原理
打开在线demo,作为一名前端开发者
,一些基础的调试操作肯定是需要熟悉
的!先看看渲染的DOM
:
模板对象的DOM:

模板内元素的DOM:

如上图,我们可以清楚,打印模板
设置的纸张大小
,它的单位
是mm
,而模板内元素
的单位
是pt
,且元素
都是absolute
定位。
「1.所以我们在处理的时候
,需要注意单位直接的转换
。」
再🤔思考一下模板内
的打印元素
怎么办呢?

如上图,假设
是这样填充到 A4 纸内
,那元素A
和元素B
的定位
也是必然需要跟着调整。
「2.所以我们在处理的时候
,需要处理模板内的所有元素
。」
至于打印怎么填充的数据
,可以看我的另一篇文章
:【vue-plugin-hiprint】使用-打印篇
还是言简意赅
的絮叨
一下吧:
填充数据,就是当元素设置了【字段名】
,比如 name
,我们在调用 预览:getHtml
、浏览器打印:print
、客户端直接打印:print2
等方法的时候,把这个name
当做key
,传入对应的数据
;如:
let printData = {name:"这是打印填充的数据"};
hiprintTemplate.print(printData);
如果是批量打印
,那么仅需要把这个printData包装成数组
;如:
let printData = [{name:"数据1"},{name:"数据2"}];
hiprintTemplate.print(printData);
这样就实现了批量打印
。这仅仅
是这是在一个模板
上!
分析
「只是」一个「思考的过程」。
❝「光想是没有意义的,实践才是硬道理。有问题再思考🤔就好了」
❞
实现 铺满A4纸 需求
首先定义两个模板对象
,并把它显示出来
,以方便调试/分析
。如图:

这里就不贴代码了,相信你看我的前几篇文章,已经很清楚怎么定义模板了。
根据前面
的分析
,合并去生成新的模板
,需要计算模板
能够容纳多少
个这样的模板
,而且还需要去动态处理
模板内的元素定位
。
👌「上重点代码」🚀:
-
能容纳多少个小的模板
:
// A4纸张大小
const { height, width } = {
width: 210,
height: 296.6
}
// 70:小模板的高 105:小模板的宽
// 垂直方向, 可以容纳多少个模板
const ver = parseInt(height / 70);
// 水平方向, 可以容纳多少个模板
const hor = parseInt(width / 105);
// 那么可想而知,新模板,一共可以容纳 ver * hor 个小模板。
-
定义新的模板对象,并填充小模板
// 新的模板,宽高单位是 mm
let template = {
panels: [
{
index: 0,
width,
height,
// 我们实际需要打印的元素,都存放在这个数组里
printElements: [],
},
],
};
// 根据道垂直方向、水平方向 循环添加打印元素
// 记录新模板有多少个整体, 以便于处理返回的数据!
// ?思考🤔 这里为什么不用 ver * hor
let limitCount = 0;
// top: 记录 打印元素 top值(注意单位); 提示:相当于记录上次填充到哪儿了
for (let v = 1, top = 0; v <= ver; v++) {
// left: 记录 打印元素 left值
for (let h = 1, left = 0; h <= hor; h++) {
// 这里就需要获取到小模板内的所有打印元素了,然后更新它的定位
// 为了更好理解, 我这里拆分来写
// 获取 模板1 的 打印元素, 及 [左][右] 中的 [左]
if (h == 1) {
let printElements1 = hiprintTemplateMap[1].getJson().panels[0].printElements;
printElements1 = printElements1.map((item) => {
// 偏移量计算
item.options.top += top;
item.options.left += left;
// !! 元素的字段名 肯定不能重复呀! 所以需要特殊处理!!!
if (item.options.field) {
// 及变成 字段名 + 第 v 行 第 h 列
// 如: qrcode 变成 qrcode_1_1 (第 1 行 的 第 1 列)
item.options.field += `_${v}_${h}`;
}
return item;
});
template.panels[0].printElements = template.panels[0].printElements.concat(printElements1);
}
// 3.2. 获取 模板2 的 打印元素, 及 [左][右] 中的 [右]
if (h == 2) {
let printElements2 = hiprintTemplateMap[2].getJson().panels[0].printElements;
printElements2 = printElements2.map((item) => {
// 偏移量计算
item.options.top += top;
item.options.left += left;
// !! 元素的字段名 肯定不能重复呀! 所以需要特殊处理!!!
if (item.options.field) {
// 及变成 字段名 + 第 v 行 第 h 列
// 如: qrcode 变成 qrcode_1_1 (第 1 行 的 第 1 列)
item.options.field += `_${v}_${h}`;
}
return item;
});
template.panels[0].printElements = template.panels[0].printElements.concat(printElements2);
}
// 3.3. 计算 下一列 模板的 left 值 (单位转换)
left += hinnn.mm.toPt(105);
// 记录整体个数
limitCount++;
}
// 3.4 计算下一行 模板的 top 值 (单位转换)
top += hinnn.mm.toPt(70);
}
❝「思考🤔为什么不直接使用
❞ver * hor
得到总共能容纳多少个小模板」
-
处理新模板需要的打印数据
printData = []; // 清空数据
const len = dataList.length;
let keys = Object.keys(dataList[0]); // 这里假设 每个模板数据格式一样, 取对象的 key
// 需要多少个这样的面板(批量打印), 才能打印全部的数据
let needPanel = Math.ceil(len / limitCount);
// curIndex:记录已经处理了多少数据
for (let p = 0, curIndex = 0; p < needPanel; p++) {
// 当前面板的打印数据
for (let v = 1; v <= ver; v++) {
for (let h = 1; h <= hor; h++) {
// 数据已处理完
if (curIndex >= len) {
break;
}
keys.forEach((key) => {
panelPrintData[`${key}_${v}_${h}`] = dataList[curIndex][key];
});
curIndex++;
}
}
printData.push(panelPrintData);
}
❝「整体下来,就是和上一步对应,处理生成
❞打印数据
的key
」
至此,动态生成模板功能实现。
思考🤔 存在的问题
-
如果
多个的小模板不是统一大小
,能不能实现这个功能? -
打印数据不是刚好
充满整个纸张,有多余不必要的内容
又该怎么处理?
日常工作中,可能随时会遇到这种类似的问题
,「分析」与「实践」是必然的。
「不要排斥、需要实践!试着去动动手、动动脑分析吧!」
总结
-
在 实践分析
的过程中
,我不仅进一步学会了如何去使用这个插件
,而且更一步清楚了它的原理
。 -
在 分析中
,不断去思考、实践、试错
;会发现其实实现起来也不复杂
,重要
的还是思路
,以及
对一些原理的掌握
。 -
同时 在记录这篇文章的时候
,又学会了使用ScreenToGif
工具等等。
「分析、实践、试错!不要怕,各位码友们!」
欢迎各位码友转发
及留言反馈
,觉得不错就点个赞
再走咯~
作者介绍
