qrcode-zip-filesaver,下载zip包

Web端A4纸排版+打印+二维码生成+ZIP包下载(Vue实战版)

本文基于Vue实现Web端完整的A4纸排版布局、批量/单页打印、动态生成二维码、二维码图片打包为ZIP压缩包下载的功能,整合 vue-qrcode(二维码生成)、JSZip(ZIP打包)、FileSaver.js(文件下载)三大核心库,适配A4纸张物理尺寸,兼顾打印与下载场景。

一、核心功能与依赖库

1. 实现功能

  • A4纸张精准排版(210×297mm),支持分页展示二维码+文本内容;
  • 单页/全部内容打印(屏蔽打印无关元素);
  • 动态生成带自定义名称的二维码;
  • 单页/全部二维码图片打包为ZIP压缩包下载。

2. 依赖库(需提前安装)

库名称 作用 安装命令 官方仓库
vue-qrcode 生成二维码(Vue组件版) npm install vue-qrcode --save https://github.com/fengyuanchen/vue-qrcode.git
JSZip 压缩文件为ZIP包 npm install jszip --save https://github.com/Stuk/jszip
FileSaver.js 前端下载文件(兼容多浏览器) npm install file-saver --save https://github.com/eligrey/FileSaver.js
(备选)qrcodejs 原生JS二维码生成 npm install qrcodejs2 --save https://github.com/davidshimjs/qrcodejs

二、完整Vue组件代码

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
<template>
<div class="print-box">
<!-- A4打印内容区域:按页数渲染,支持单页/全部显示 -->
<div
class="print-bd"
v-for="pageNum in totalPages"
:key="pageNum"
v-show="currentPage === 0 ? true : (currentPage === pageNum)"
>
<!-- 每页内的二维码+文本条目 -->
<div
v-for="itemNum in (pageNum < totalPages ? pageSize : lastPageSize)"
:key="itemNum"
>
<!-- 生成二维码:绑定ref用于后续获取DOM,传递值和自定义名称 -->
<qrcode
:ref="`qrcode-${(pageNum-1)*pageSize + itemNum}`"
value="https://fengyuanchen.github.io/vue-qrcode"
:data-name="`张某-${(pageNum-1)*pageSize + itemNum}`"
tag="img" <!-- 输出为img标签,方便获取base64 -->
></qrcode>
<!-- 文本说明 -->
<div>张某 {{ (pageNum-1)*pageSize + itemNum }}</div>
</div>
</div>

<!-- 操作按钮区(打印/下载,打印时隐藏) -->
<div class="print-btn">
<div>
<!-- 全部操作按钮 -->
<div class="handle-all">
<div class="btn" @click="printAll">全部打印</div>
<div class="btn" @click="downloadZipAll">全部下载</div>
</div>
<!-- 单页操作按钮 -->
<div class="handle-page">
第<input
type="number"
maxlength="1000"
minlength="1"
v-model="currentPage"
min="1"
:max="totalPages"
/>页
<div class="btn" @click="printCurrentPage">打印</div>
<div class="btn" @click="downloadZipCurrentPage">下载</div>
</div>
</div>
</div>
</div>
</template>

<script>
import { $http } from '../../assets/js/api'; // 项目自有API,可忽略
import JSZip from 'jszip';
import { saveAs } from 'file-saver';
import Qrcode from 'vue-qrcode'; // 引入二维码组件

export default {
name: 'A4PrintQrcode',
components: { Qrcode }, // 注册二维码组件
data() {
return {
totalItems: 46, // 总二维码条目数
pageSize: 15, // 每页显示条目数
totalPages: 0, // 总页数
lastPageSize: 0, // 最后一页条目数
currentPage: 0 // 当前选中页(0=全部,≥1=指定页)
};
},
mounted() {
// 计算总页数和最后一页条目数
this.lastPageSize = this.totalItems % this.pageSize || this.pageSize;
this.totalPages = Math.floor(this.totalItems / this.pageSize) + (this.lastPageSize ? 1 : 0);
},
methods: {
/**
* 单页二维码打包为ZIP下载
*/
downloadZipCurrentPage() {
if (!this.currentPage || this.currentPage < 1 || this.currentPage > this.totalPages) {
alert('请输入有效的页码!');
return;
}

const zip = new JSZip(); // 初始化ZIP实例(注意:不兼容Win10 IE11)
zip.file("说明.txt", "该压缩包包含当前页的学生二维码图片\n"); // 新增说明文件
const imgFolder = zip.folder("二维码图片"); // 创建图片子文件夹

// 计算当前页的条目范围
const startNum = (this.currentPage - 1) * this.pageSize + 1;
const endNum = this.currentPage === this.totalPages
? (this.currentPage - 1) * this.pageSize + this.lastPageSize
: this.currentPage * this.pageSize;

// 遍历当前页的二维码DOM,打包为图片
Object.keys(this.$refs).forEach(refKey => {
if (refKey.startsWith('qrcode-')) {
const itemNum = parseInt(refKey.replace('qrcode-', ''));
// 仅处理当前页的条目
if (itemNum >= startNum && itemNum <= endNum) {
const qrcodeEl = this.$refs[refKey][0].$el; // 获取二维码DOM
const imgName = qrcodeEl.dataset.name; // 获取自定义图片名称
// 提取base64数据(去除前缀,仅保留编码部分)
const base64Data = qrcodeEl.toDataURL().split(',')[1];
// 将图片添加到ZIP(base64格式)
imgFolder.file(`${imgName}.jpg`, base64Data, { base64: true });
}
}
});

// 生成ZIP并下载
zip.generateAsync({ type: "blob" })
.then(content => {
saveAs(content, `第${this.currentPage}页-学生二维码.zip`);
})
.catch(err => {
console.error('ZIP打包失败:', err);
alert('下载失败,请重试!');
});
},

/**
* 全部二维码打包为ZIP下载
*/
downloadZipAll() {
const zip = new JSZip();
zip.file("说明.txt", "该压缩包包含所有学生二维码图片\n");
const imgFolder = zip.folder("二维码图片");

// 遍历所有二维码DOM,打包为图片
Object.keys(this.$refs).forEach(refKey => {
if (refKey.startsWith('qrcode-')) {
const qrcodeEl = this.$refs[refKey][0].$el;
const imgName = qrcodeEl.dataset.name;
const base64Data = qrcodeEl.toDataURL().split(',')[1];
imgFolder.file(`${imgName}.jpg`, base64Data, { base64: true });
}
});

// 生成ZIP并下载
zip.generateAsync({ type: "blob" })
.then(content => {
saveAs(content, "全部学生二维码.zip");
})
.catch(err => {
console.error('ZIP打包失败:', err);
alert('下载失败,请重试!');
});
},

/**
* 打印全部内容
*/
printAll() {
this.currentPage = 0; // 显示全部内容
// 延迟执行,确保DOM渲染完成
setTimeout(() => window.print(), 201);
},

/**
* 打印当前页内容
*/
printCurrentPage() {
if (!this.currentPage || this.currentPage < 1 || this.currentPage > this.totalPages) {
alert('请输入有效的页码!');
return;
}
window.print(); // 触发浏览器打印对话框
}
}
};
</script>

<style scoped lang="scss">
// 容器样式:适配A4纸张,黑色背景区分打印区域
.print-box {
height: auto;
position: relative;
padding-top: 12px;
padding-bottom: 72px; // 给底部按钮留空间
background: #000;
line-height: 0;
}

// A4打印区域:严格匹配A4物理尺寸(210×297mm)
.print-bd {
box-sizing: border-box;
padding: 10mm 15mm 20mm 15mm; // 页边距(可按需调整)
width: 210mm; // A4宽度(固定)
height: 297mm; // A4高度(固定)
background-color: #fff;
margin: 0 auto; // 居中显示

> div {
width: 60mm; // 每个二维码条目宽度
text-align: center;
line-height: 1;
display: block;
float: left; // 横向排列

> img {
width: 80%; // 二维码图片缩放
}
> div {
line-height: 1;
font-size: 14px;
color: #666;
margin: 1mm 0 4mm; // 文本与二维码间距
}
}
}

// 操作按钮区:固定在底部,打印时隐藏
.print-btn {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 60px;
background-color: #f8f8f8;

> div {
height: 100%;
width: 210mm; // 与A4区域同宽,居中
margin: 0 auto;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}

.handle-all, .handle-page {
display: flex;
flex-direction: row;
align-items: center;
}

.btn {
cursor: pointer;
line-height: 1;
margin: 10px 5px;
border: 1px solid #ccc;
padding: 4px 8px;
transition: all .5s;

&:hover {
border-color: #c4261d;
color: #fff;
background-color: #c4261d;
}
}
}

// 打印专属样式:屏蔽无关元素,重置布局
@media print {
.print-btn { display: none; } // 隐藏操作按钮
.print-box { padding: 0; } // 重置容器内边距
}
</style>

三、关键逻辑解析

1. A4纸张排版

  • 核心:使用 mm(毫米)作为尺寸单位(width: 210mm; height: 297mm),避免像素(px)因设备DPI差异导致的布局偏差;
  • 分页:根据总条目数和每页条数自动计算总页数,通过 v-show 控制单页/全部内容的显示。

2. 二维码生成

  • 基于 vue-qrcode 组件,通过 ref 绑定每个二维码DOM,方便后续获取base64数据;
  • 自定义属性 data-name 为每个二维码标记唯一名称(如“张某-1”),用于ZIP包内的图片命名。

3. 打印功能

  • 调用浏览器原生 window.print() 方法,通过 setTimeout 延迟执行,确保DOM渲染完成;
  • @media print 媒体查询:屏蔽打印按钮等无关元素,仅保留A4打印区域,保证打印效果纯净。

4. ZIP打包下载

  • JSZip 初始化ZIP实例,创建子文件夹分类存放图片;
  • 提取二维码DOM的 toDataURL() 生成base64数据,去除前缀后以base64格式写入ZIP;
  • FileSaver.jssaveAs 方法实现ZIP包前端下载,支持自定义文件名。

四、参考资料

  1. 二维码生成:https://www.jianshu.com/p/85088be3c860
  2. ZIP打包下载:https://www.cnblogs.com/sxww-zyt/p/12674568.html

总结

  1. A4排版核心:使用 mm 单位匹配纸张物理尺寸,@media print 控制打印样式;
  2. 二维码处理:通过 ref 绑定DOM,提取base64数据用于ZIP打包;
  3. ZIP下载注意:JSZip 不兼容Win10 IE11,需注意浏览器适配;
  4. 打印优化:延迟执行 window.print(),确保分页内容完全渲染。

qrcode-zip-filesaver,下载zip包
https://cszy.top/20200820-zip-filesaver/
作者
csorz
发布于
2020年8月20日
许可协议