文章目录
- 📋前言
- 🎯关于 Print.js
- 🧩PDF 打印
- 🧩网页(HTML)打印
- 🧩图像打印
- 🧩JSON 打印
- 🧩下载并安装使用
- 🧩相关配置
- 🎯 Vue3 中使用 Print.js 实战
- 🎯Vue3 + Nodejs + Print.js 模拟打印实战案例
- 🧩启动 Nodejs 服务
- 🧩启动 Vue 项目
- 📝最后
- 🔥文末送书
- 🧩编辑推荐
- 🧩内容介绍
- 🧩作者介绍
- 🔥参与方式
📋前言
今天久违的更新一下关于 Vue 的文章了,本篇文章是基于 Vue3 + Node.js + ElementPlus 的实战项目分享,实战内容包括有打印插件 Print.js 的使用,以及关于 ElementPlus 中的 el-table 与 el-pagination 的深入使用。本次项目以文章(axios 实战进阶练习——基于 Vue3 + Node.js + ElementPlus 实现的联系人列表管理后台)中的项目为模板进行改动和编写。
🎯关于 Print.js
在使用 Print.js 插件之前,我们可以通过下面的链接先了解和认识一下这个 JavaScript 插件。
官方地址:Print.js - Javascript library for HTML elements, PDF and image files printing.
GitHub地址:GitHub - crabbly/Printjs: A tiny javascript library to help printing from the web.
如何认识和快速上手 Print.js,我们可以从官网的内容开始阅读,在官网中有很详细的介绍和使用例子,虽然是英文版的。Print.js 打印插件包括了 PDF 打印、HTML 打印、JSON 打印、图像打印等,这篇文章主要分享的 PDF 打印的实战,其中在使用过 Print.js 之后,对于这几种打印的类型,其实都是差不多的,主要区别还是参数配置问题。接下来就简单介绍一下这几种打印的类型。
🧩PDF 打印
Print.js 主要是为了帮助我们直接在应用程序中打印 PDF 文件,无需离开界面,也不使用嵌入。对于用户不需要打开或下载 PDF 文件,而只需要打印它们的独特情况。
例如,当用户请求打印在服务器端生成的报表时,这很有用的一种情况。这些报告以 PDF 文件的形式发回。在打印这些文件之前,无需打开它们。Print.js 提供了一种在我们的应用程序中打印这些文件的快速方法。
❗注意:PDF 文件必须从托管应用的同一网域提供。Print.js 在打印文件之前使用 iframe 加载文件,因此,它受到同源策略的限制。这有助于防止跨站点脚本 (XSS) 攻击。(关于这点也是实际项目中出现的一个问题)
<button type="button" onclick="printJS('docs/printjs.pdf')">
Print PDF
</button>
<button type="button" onclick="printJS({printable:'docs/xx_large_printjs.pdf', type:'pdf', showModal:true})">
Print PDF with Message
</button>
<button type="button" onclick="printJS({printable: base64, type: 'pdf', base64: true})">
Print PDF with Message
</button>
🧩网页(HTML)打印
有时我们只想打印HTML页面的选定部分,这可能很棘手。使用 Print.js,我们可以轻松传递要打印的元素的 id。该元素可以是任何标记,只要它具有唯一的 id。图书馆将尝试非常接近它在屏幕上的外观进行打印,同时,它将为其创建打印机友好的格式。
<form method="post" action="#" id="printJS-form">
...
</form>
<button type="button" onclick="printJS('printJS-form', 'html')">
Print Form
</button>
<button type="button" onclick="printJS({ printable: 'printJS-form', type: 'html', header: 'PrintJS - Form Element Selection' })">
Print Form with Header
</button>
🧩图像打印
Print.js 可用于通过传递图像 URL 快速打印页面上的任何图像。当您使用低分辨率版本的图像在屏幕上有多个图像时,这可能很有用。当用户尝试打印所选图像时,您可以将高分辨率 url 传递给 Print.js。
<img src="images/print-01.jpg" />
printJS('images/print-01-highres.jpg', 'image')
printJS({printable: 'images/print-01-highres.jpg', type: 'image', header: 'My cool image header'})
printJS({
printable: ['images/print-01-highres.jpg', 'images/print-02-highres.jpg', 'images/print-03-highres.jpg'],
type: 'image',
header: 'Multiple Images',
imageStyle: 'width:50%;margin-bottom:20px;'
})
🧩JSON 打印
打印动态数据或 JavaScript 对象数组的简单快捷方法。
someJSONdata = [
{
name: 'John Doe',
email: 'john@doe.com',
phone: '111-111-1111'
},
{
name: 'Barry Allen',
email: 'barry@flash.com',
phone: '222-222-2222'
},
{
name: 'Cool Dude',
email: 'cool@dude.com',
phone: '333-333-3333'
}
]
<button type="button" onclick="printJS({printable: someJSONdata, properties: ['name', 'email', 'phone'], type: 'json'})">
Print JSON Data
</button>
我们可以通过传递一些自定义 css 来设置数据网格的样式:
<button type="button" onclick="printJS({
printable: someJSONdata,
properties: ['name', 'email', 'phone'],
type: 'json',
gridHeaderStyle: 'color: red; border: 2px solid #3971A5;',
gridStyle: 'border: 2px solid #3971A5;'
})">
Print JSON Data
</button>
我们可以自定义发送对象数组的表头文本:
<button type="button" onclick="printJS({
printable: someJSONdata,
properties: [
{ field: 'name', displayName: 'Full Name'},
{ field: 'email', displayName: 'E-mail'},
{ field: 'phone', displayName: 'Phone'}
],
type: 'json'
})">
Print with custom table header text
</button>
JSON、HTML 和 Image print 可以接收原始 HTML 标头:
<button type="button" onclick="printJS({
printable: someJSONdata,
type: 'json',
properties: ['name', 'email', 'phone'],
header: '<h3 class="custom-h3">My custom header</h3>',
style: '.custom-h3 { color: red; }'
})">
Print header raw html
</button>
🧩下载并安装使用
可以从 GitHub 版本下载最新版本的 Print.js。(链接在上面提及过)
使用 npm 进行安装,请执行以下操作:
npm install print-js --save
使用 yarn 安装:
yarn add print-js
通过 npm 或 yarn 安装时,将库导入到项目中:
import print from 'print-js'
也可以使用在线的 CDN:
https://printjs-4de6.kxcdn.com/print.min.js
https://printjs-4de6.kxcdn.com/print.min.css
🧩相关配置
Print.js 将接受一个对象作为参数,下面表格的参数则为该对象的属性,通过控制属性值来控制打印。
参数 | 默认值 | 说明 |
---|---|---|
printable | null | 文档来源:pdf或图像的url,html元素的id或json数据的对象 |
type | 可打印类型。可用的打印选项包括:pdf,html,image,json和raw-html。 | |
header | null | 用于HTML,Image或JSON打印的可选标头。它将放在页面顶部。此属性将接受文本或原始HTML |
headerStyle | ‘font-weight:300;’ | 要应用于标题文本的可选标题样式 |
maxWidth | 800 | 最大文档宽度(像素)。根据需要更改此项。在打印HTML,图像或JSON时使用。 |
css | null | 这允许我们传递一个或多个应该应用于正在打印的html的css文件URL。值可以是包含单个URL的字符串,也可以是包含多个URL的数组。 |
style | null | 这允许我们传递一个字符串,该字符串应该应用于正在打印的html。 |
scanStyles | true | 设置为false时,库不会处理应用于正在打印的html的样式。使用css参数时很有用。 |
targetStyle | null | 默认情况下,在打印HTML元素时,库仅处理某些样式。此选项允许您传递要处理的样式数组。例如:[‘padding-top’,‘border-bottom’] |
targetStyles | null | 与targetStyle相同,这将处理任何一系列样式。例如:[‘border’,‘padding’],将包括’border-bottom’,‘border-top’,‘border-left’,‘border-right’,‘padding-top’等。你也可以传递[’*']来处理所有样式 |
ignoreElements | [] | 接受打印父html元素时应忽略的html的id数组。 |
properties | null | 在打印JSON时使用。这些是对象属性名称。 |
gridHeaderStyle | ‘font-weight:bold;’ | 打印JSON数据时网格标题的可选样式。 |
gridStyle | ‘border: 1px solid lightgray; margin-bottom: -1px;’ | 打印JSON数据时网格行的可选样式 |
repeatTableHeader | true | 在打印JSON数据时使用。设置为时false,数据表标题仅显示在第一页中。 |
showModal | null | 启用此选项可在检索或处理大型PDF文件时显示用户反馈 |
modalMessage | ‘Retrieving Document…’ | 当向用户显示的消息showModal被设定为true。 |
onLoadingStart | null | 加载PDF时要执行的功能 |
onLoadingEnd | null | 加载PDF后要执行的功能 |
documentTitle | ‘Document’ | 打印html,image或json时,它将显示为文档标题。如果用户尝试将打印作业保存为pdf文件,它也将是文档的名称。 |
fallbackPrintable | null | 打印pdf时,如果浏览器不兼容(检查浏览器兼容性表),库将在新选项卡中打开pdf。这允许您传递要打开的不同pdf文档,而不是传递给printable的原始文档。如果您在备用pdf文件中注入javascript,这可能很有用。 |
onPdfOpen | null | 打印pdf时,如果浏览器不兼容(检查浏览器兼容性表),库将在新选项卡中打开pdf。可以在此处传递回调函数,这将在发生这种情况时执行。在您想要处理打印流程,更新用户界面等的某些情况下,它可能很有用。 |
onPrintDialogClose | null | 关闭浏览器打印对话框后执行回调功能 |
onError | error => throw error | 发生错误时要执行的回调函数。 |
base64 | false | 在打印作为base64数据传递的PDF文档时使用 |
honorMarginPadding(不建议使用) | true | 这用于保留或删除正在打印的元素的填充和边距。有时这些样式设置在屏幕上很棒,但在打印时看起来很糟糕。您可以通过将其设置为false来删除它。 |
honorColor(不建议使用) | false | 要以彩色打印文本,请将此属性设置为true。默认情况下,所有文本都将以黑色打印。 |
font(不建议使用) | ‘TimesNewRoman’ | 打印HTML或JSON时使用的字体 |
font_size(不建议使用) | ‘12pt’ | 打印HTML或JSON时使用的字体大小 |
imageStyle (不建议使用) | ‘width:100%;’ | 打印图像时使用。接受包含要应用于每个图像的自定义样式的字符串。 |
frameId | null | print.js会将要打印的内容复制到一个新的Frame中,此参数是frame的id值 |
🎯 Vue3 中使用 Print.js 实战
接下来,我们通过业务需求和代码解析来分享一下在 Vue3 中使用 Print.js 的实战。首先我们来讲一下需求,非常简单,就是某个表格或列表的打印按钮,然后页面调起打印窗口,打印完成就删除这条打印数据。不往深入的讲,我们不谈数据来源,以及如何添加数据到打印列表,我只要知道点击这个按钮可以打印就行了,这里我们就用 Nodejs 来写一个返回 PDF 的接口,写死一条 PDF 的数据,来模拟用户添加打印数据到打印列表,实现打印功能。
接下来我们通过代码来看看如何使用 Print.js,首先是要安装 Print.js ,上面也提及到了这个 npm 命令,这里就不重复了。安装完成之后,我们要在项目中导入 Print.js,如下图。
我们可以先简单写个按钮来测试一下,是否可以使用正常使用这个插件。
<button type="button" onclick="printJS('https://xxx.com/P020210715514554764187.pdf')">Print PDF</button>
上面这段代码,是打印 PDF 的一个简单例子,上面我们也说到,打印 PDF 文件时,PDF 文件必须从托管应用的同一网域提供,受到同源策略的限制。这有助于防止跨站点脚本 (XSS) 攻击。所以我们要确保这个 PDF 与项目是在同一个域(比如说 PDF 和项目都在 https://xxx.com 这个域名的服务器上面),如果不一样的话会出现跨域的情况。
上面图片是官网的 example 的例子,点击 Print PDF 那个按钮后,会弹出打印的窗口如上图所示。下面则是我在本地项目使用 Print.js 的例子截图。
我们可以看到出现了跨域的情况,是因为这个在线的 PDF (这里就不提供这个 PDF 链接,读者可以用自己的在线 PDF 来测试)跟我本地跑的 Vue 项目不是同一个网域。因此我们在后台返回获取到的 PDF 路径要跟运行的项目是同一个网域。假设把 PDF 路径上传到 OSS(对象存储服务),然后项目部署在其他的服务器什么,这种情况下,是可以进行打印访问的,但是前提是在 OSS 上面设置了允许跨域。接下来看一下这段实战中的代码片段,以及其功能的解析。
const printdelete = (row) => {
ElMessage({
message: "正在加入打印",
grouping: true,
type: "success",
});
printJS(IMG.value + row.path);
console.log(IMG.value + row.path);
const params = {
id: row.id,
};
instance
.post("/xxxapi/print/delete?id=" + params.id)
.then((res) => {
console.log(res);
if (res.status === 200) {
console.log("已添加到打印!");
ElMessage({
message: "已添加到打印!",
grouping: true,
type: "success",
});
} else {
ElMessage({
message: "打印失败",
grouping: true,
type: "error",
});
}
})
.catch((err) => {
ElMessage({
message: err,
grouping: true,
type: "error",
});
});
};
这段代码就是打印实战的代码,总体思路就是,通过后端返回的 PDF 路径,然后拼接好 OSS 的路径,在之后使用 printJS 进行打印,最后打印成功后调用删除打印列表的接口,删除刚刚打印完的打印列表数据,其他就是是一些交互操作,打印前后的消息栏。
接下来我们具体看到代码,从上往下,首先这个函数接收一个参数 row,这个参数的来源是 el-table 中每一行的数据信息,比如说打印列表有打印文件的名称,发起人,打印文件的创建时间等等,其中不在列表显示的 PDF 路径是参数 row 中尤为重要的数据。我们可以看到代码中 printJS(IMG.value + row.path); 的这段代码,其中 IMG.value 是 OSS 的路径,然后 row.path 就是刚刚所说的在列表没有显示的 PDF 路径,二者拼接起来就是一个完整打印路径,通过调用 printJS 即可实现打印。接着往下看,调起打印窗口,打印完成,接下来就是删除这条打印完的打印数据,通过请求删除的接口(这段代码 .post(“/xxxapi/print/delete?id=” + params.id)),其中代码中的 instance 是指封装的 axios.create,这样写方便后续多次调用 axios.create。
总体流程就是在函数内部,首先使用ElMessage方法显示一个成功的消息,提示正在加入打印。然后使用 printJS 方法打印 IMG.value + row.path ,并在控制台输出该路径。接下来,创建一个包含 row.id 的 params 对象。接着,使用 instance 发送一个 POST 请求到 /xxxapi/print/delete?id= 加上 params.id 作为参数的 URL。如果请求成功(状态码为 200),在控制台输出响应结果,并显示一个成功的消息。否则,显示一个失败的消息。如果发生错误,会在控制台输出错误,并显示一个错误的消息。
🎯Vue3 + Nodejs + Print.js 模拟打印实战案例
通过上面的基础学习和实战分享,我们对 Print.js 有了一定的了解,通过上手实例和项目分享的内容也可以快速 Print.js 了。接下来分享一个简单的实战案例,来巩固和总结一下前面文章提及的内容。
首先,我们使用 Nodejs 写一个服务,把本地的 PDF 部署在 http://localhost:8080/ 上面,在使用 Print.js 确保 PDF 和 Vue 项目都在同一个网域下,避免出现跨域的情况。接下来,我们一起来看一下 Nodejs 的代码。
const express = require('express');
const app = express();
const path = require('path');
const fs = require('fs');
const {
createProxyMiddleware
} = require('http-proxy-middleware');
// 设置端口号
const port = 8080;
// 加载静态资源
// app.use(express.static(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname)));
// 处理下载请求
app.get('/api/getPdfLink', (req, res) => {
// 构造文件绝对路径
// const filePath = path.join(__dirname, 'P020221111501862950279.pdf');
const pdfPath = 'http://localhost:8080/P020221111501862950279.pdf';
console.log(`File path: ${pdfPath}`);
// 返回文件路径
res.send({
link: pdfPath
});
});
// 反向代理到 Vue 项目
app.use(
'/',
createProxyMiddleware({
target: 'http://localhost:8080',
changeOrigin: true,
})
);
// 启动服务
app.listen(port, () => {
console.log(`Server listening at http://localhost:${port}/api/getPdfLink`);
});
首先创建 app.js 作为项目的入口文件,后续只要 node app 即可运行服务,然后安装所需的配置文件,如下命令。
npm install express // 安装 Express 框架
因为Vue 项目已经占用了 8080 端口,为了确保 Nodejs 服务也监听在同一个端口上,以保持相同的域名,我们可以尝试使用反向代理来实现这一目的,安装命令如下。
npm install http-proxy-middleware // 安装反向代理
安装好这些配置后,我们看具体代码的功能。这个程序的主要目的就是写一个接口返回 PDF 路径,然后前端调用这个接口获取到 PDF 的路径,然后实现打印功能。接下来简单分析一下这段代码。
- 设置端口号为 8080:
const port = 8080;
- 加载静态资源:
app.use(express.static(path.join(__dirname)));
。这里使用 Express 的 express.static 中间件来加载静态资源。path.join(__dirname) 表示将当前目录作为静态资源所在的根目录。这意味着 PDF 文件应该放在服务器启动的根目录中,如下图。
- /api/getPdfLink 接口:当客户端通过 /api/getPdfLink 路径发送 GET 请求时,服务器会返回一个包含 PDF 文件路径的 JSON 响应。在这个例子中,PDF 文件的路径是硬编码的,为 http://localhost:8080/P020221111501862950279.pdf。
app.get('/api/getPdfLink', (req, res) => {
const pdfPath = 'http://localhost:8080/P020221111501862950279.pdf';
console.log(`File path: ${pdfPath}`);
res.send({ link: pdfPath });
});
- 反向代理到 Vue 项目:这部分代码将所有对根路径 / 的请求反向代理到目标地址为 http://localhost:8080 的服务器。这通常用于将 Express 服务器和 Vue 项目集成在一起,实现前后端的联合开发。
app.use(
'/',
createProxyMiddleware({
target: 'http://localhost:8080',
changeOrigin: true,
})
);
- 启动服务器:最后,通过调用 app.listen 方法,启动服务器并监听指定的端口号。
app.listen(port, () => {
console.log(`Server listening at http://localhost:${port}/api/getPdfLink`);
});
🧩启动 Nodejs 服务
接下来通过 node app 启动服务,并且测试一下接口是否可以获取到数据。
直接在浏览器访问 http://localhost:8080/api/getPdfLink。
然后再访问 http://localhost:8080/P020221111501862950279.pdf。
到此 Nodejs 服务启动没用问题,接下来我们运行一下 Vue 项目。
🧩启动 Vue 项目
npm run serve 直接启动项目。
页面非常简单,就只有一个按钮。通过下图控制台输出的结果,可以说明成功调用接口,返回了参数。
然后我们点击这个按钮,试一下能不能成功调起打印窗口呢。
最后成功调起的了打印窗口,运行成功,到此项目的主流程已经完成了。下面是这个页面的代码。
<template>
<div class="contact-list" id="#printJS-HTML'">
<!-- 标题 -->
<el-row justify="center">
<button type="button" @click="printPDF">Print PDF</button>
</el-row>
</div>
</template>
<script setup>
// import { ElMessage, ElMessageBox } from "element-plus";
import printJS from "print-js";
import { ref, onMounted } from "vue";
import axios from "axios";
import Http from "@/service/http";
// axios默认数据
// const instance = axios.create({
// baseURL: "http://localhost:8080",
// timeout: 10000,
// });
// 在组件挂载完毕后调用 refreshList 函数
onMounted(() => {
refreshList();
});
const pdflink = ref();
const refreshList = async () => {
let res = await Http.getPdfLink();
pdflink.value = res.link;
console.log(res);
console.log(res.link);
};
const printPDF = () => {
printJS(pdflink.value);
};
</script>
<style scoped>
.contact-list {
max-width: 800px;
margin: auto;
padding: 20px;
}
</style>
如果需要完整代码或者项目的读者,可以私信我或者添加我的联系方式获取,如收到消息会第一时间回复。
📝最后
到此就是 Vue 实战篇——打印插件 Print.js 的使用(Vue3 + Nodejs + Print.js 实战)以及 el-table 与 el-pagination 的深入使用(上)的全部内容了,通过这篇文章,我们可以多学一招,学会使用 Print.js 插件打印 PDF 文件。以及通过 Vue3 + Nodejs + Print.js 实战,我们可以加深对该插件的熟悉程度,其实该实战的难点是 Nodejs 服务的编写,但这不是我们所要深究的,这只是解决跨域问题的一个前提,最主要是体验和上手过一遍打印的流程。通过接口获取到 PDF 文件的路径,然后在项目中使用 Print.js 插件的 printJS 方法,把获取到的路径传入这个方法,然后调起打印窗口,最后完成打印。
除此之外,关于该项目的实战延申,还有关于 el-table 与 el-pagination 的内容,这些内容会在《Vue3 开发实战分享——打印插件 Print.js 的使用(Vue3 + Nodejs + Print.js 实战)以及 el-table 与 el-pagination 的深入使用(下)》介绍和分享,敬请期待。
🔥文末送书
🧩编辑推荐
(1)实用性强:本书的案例是基于真实的企业级项目需求而开发的,读者可通过学习本 书,掌握实际项目开发中的技巧和方法。
(2)实用性强:本书的案例是基于真实的企业级项目需求而开发的,读者可通过学习本 书,掌握实际项目开发中的技巧和方法。
(3)互动性强:本书赠送配套的微课视频约164集,读者可通过观看视频更好地理解代码的实现过程和实现方法。
🧩内容介绍
本书是一本实用性很强的Vue.js 3实战项目书。书中结合实际项目场景,构建了一个完整的企业级应用。全书共分13章,内容包含项目概述、Vue3项目管理、登录管理、后台主框架、图库管理、管理员管理、用户管理、商品管理、订单管理、优惠券管理、商品评论管理、分销管理和公告管理,并且讲解了这些模块的实际应用方法。同时,本书还介绍了如何使用Vite、Axios、Vue Router、Vuex等流行工具和库,以提高开发效率、提升用户体验。
🧩作者介绍
袁龙,从事Web开发、教学培训等业务,创建“锦匠特效”和“锦匠课堂”两大Web前端工具类网站,为数万前端开发者提供高效率的工作方式,轻松实现网页动画特效,目前是51CTO、CSDN等在线教育平台讲师。著有《Vue.js核心技术解析与uni-app跨平台开发实战》《Node.js从基础到项目实践(视频教学版) 》等多部著作。
🔥参与方式
《 Vue.js 3企业级项目开发实战》免费包邮送出 3 本!
抽奖方式:评论区随机抽取 3 位小伙伴免费送出!
参与方式:关注博主、点赞、收藏、评论区评论 “人生苦短,我学Vue!” (切记要点赞+收藏,否则抽奖无效,每个人最多评论三次!)
活动截止时间:2023-10-21 20:00:00
当当自营店购买链接:http://product.dangdang.com/29621812.html