鸿蒙PC Electron 打印服务实现详解
双重保障:优先使用浏览器原生,降级到 Electron API错误处理:完善的错误处理和日志记录样式优化:使用优化打印效果用户体验:友好的错误提示和加载状态。
·
鸿蒙PC Electron 打印服务实现详解
📋 目录
问题背景
在鸿蒙平台上开发 Electron 应用时,需要实现打印功能。打印功能是桌面应用的基础需求之一,但在跨平台开发中,不同平台的打印 API 和实现方式存在差异。
需求分析
- 基本打印功能:用户点击打印按钮后,能够打印当前页面内容
- 打印预览:显示系统打印对话框,允许用户选择打印机和打印选项
- 打印样式优化:打印时隐藏不必要的 UI 元素,优化打印效果
- 跨平台兼容:在鸿蒙平台上稳定运行,同时兼容其他平台
实现方案
方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
浏览器原生 window.print() |
简单直接,兼容性好 | 功能有限,无法自定义 | 简单打印需求 |
Electron webContents.print() |
功能强大,可配置选项多 | 在某些平台上可能不稳定 | 需要高级打印功能 |
| 鸿蒙原生打印 API | 平台深度集成 | 需要额外的适配层 | 需要平台特定功能 |
最终方案
采用双重保障方案:
- 优先使用浏览器原生 API:
window.print()- 在鸿蒙平台上更可靠 - 降级到 Electron API:
webContents.print()- 作为备选方案 - IPC 通信:通过 Electron IPC 在主进程和渲染进程之间通信
代码实现
1. 主进程实现(main.js)
在主进程中监听打印请求,并调用 Electron 的打印 API:
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');
// 禁用硬件加速(可选,解决某些打印兼容性问题)
app.disableHardwareAcceleration();
app.commandLine.appendSwitch('disable-gpu');
// 打印功能处理
ipcMain.on('print', (event) => {
console.log('收到打印请求');
const window = BrowserWindow.fromWebContents(event.sender);
if (!window) {
console.error('无法获取窗口对象');
return;
}
console.log('准备调用打印 API');
try {
// 尝试使用 Electron 的打印 API
if (window.webContents && typeof window.webContents.print === 'function') {
window.webContents.print({
silent: false, // 显示打印对话框
printBackground: true, // 打印背景色和图片
deviceName: '' // 使用默认打印机
}, (success, failureReason) => {
if (success) {
console.log('打印成功');
} else {
console.error('打印失败:', failureReason);
// 如果 Electron 打印失败,尝试使用浏览器原生打印
console.log('尝试使用浏览器原生打印');
window.webContents.executeJavaScript('window.print()').catch(err => {
console.error('浏览器原生打印也失败:', err);
});
}
});
} else {
console.warn('webContents.print 不可用,使用浏览器原生打印');
window.webContents.executeJavaScript('window.print()').catch(err => {
console.error('浏览器原生打印失败:', err);
});
}
} catch (error) {
console.error('打印过程中发生错误:', error);
// 降级方案:使用浏览器原生打印
try {
window.webContents.executeJavaScript('window.print()').catch(err => {
console.error('降级打印也失败:', err);
});
} catch (e) {
console.error('所有打印方案都失败:', e);
}
}
});
2. 预加载脚本(preload.js)
在预加载脚本中暴露安全的打印 API:
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
versions: {
chrome: process.versions.chrome,
node: process.versions.node,
electron: process.versions.electron
},
// 打印功能
print: () => {
ipcRenderer.send('print');
}
});
3. 渲染进程实现(index.html)
在页面中添加打印按钮和打印函数:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello World</title>
<style>
/* 打印样式 */
@media print {
body {
background: white !important;
color: black !important;
}
.button-container {
display: none !important;
}
h1 {
color: black !important;
}
}
</style>
</head>
<body>
<h1>Hello World</h1>
<div class="button-container">
<button onclick="printPage()">🖨️ 打印</button>
</div>
<script>
function printPage() {
console.log('点击打印按钮');
try {
// 方法1: 直接使用浏览器原生打印 API(在鸿蒙平台上更可靠)
if (typeof window.print === 'function') {
console.log('调用 window.print()');
window.print();
} else if (window.electronAPI && window.electronAPI.print) {
// 方法2: 使用 Electron IPC 打印
console.log('使用 Electron IPC 打印');
window.electronAPI.print();
} else {
alert('打印功能不可用');
}
} catch (error) {
console.error('打印失败:', error);
alert('打印功能出错: ' + error.message);
}
}
</script>
</body>
</html>
遇到的问题与解决方案
问题一:点击打印按钮没有反应
现象:点击打印按钮后,没有任何反应,打印对话框不弹出。
原因分析:
- Electron 的
webContents.print()API 在鸿蒙平台上可能不稳定 - IPC 通信可能存在问题
- 打印权限可能未正确配置
解决方案:
- 添加详细的调试日志:
function printPage() {
console.log('点击打印按钮');
// 添加调试信息
console.log('window.print 可用:', typeof window.print === 'function');
console.log('electronAPI.print 可用:', window.electronAPI && window.electronAPI.print);
// 执行打印
window.print();
}
- 优先使用浏览器原生 API:
// 直接使用 window.print(),这是最可靠的方式
function printPage() {
if (typeof window.print === 'function') {
window.print(); // 优先使用浏览器原生 API
} else if (window.electronAPI && window.electronAPI.print) {
window.electronAPI.print(); // 降级到 Electron API
}
}
- 禁用硬件加速(可选):
// 在 main.js 开头添加
app.disableHardwareAcceleration();
app.commandLine.appendSwitch('disable-gpu');
问题二:打印时包含不需要的元素
现象:打印时,按钮、导航栏等 UI 元素也被打印出来。
解决方案:使用 CSS @media print 媒体查询隐藏不需要的元素:
@media print {
/* 隐藏按钮容器 */
.button-container {
display: none !important;
}
/* 优化打印样式 */
body {
background: white !important;
color: black !important;
}
/* 确保文字可读 */
h1 {
color: black !important;
}
}
问题三:打印背景色和图片不显示
现象:打印时,背景色和背景图片不显示。
解决方案:在打印选项中启用 printBackground:
window.webContents.print({
silent: false,
printBackground: true, // 启用背景打印
deviceName: ''
}, callback);
打印样式优化
1. 基本打印样式
@media print {
/* 重置页面样式 */
* {
margin: 0;
padding: 0;
}
/* 优化页面布局 */
body {
background: white !important;
color: black !important;
font-size: 12pt;
line-height: 1.5;
}
/* 隐藏不需要的元素 */
.no-print {
display: none !important;
}
/* 分页控制 */
.page-break {
page-break-after: always;
}
/* 避免在元素中间分页 */
h1, h2, h3 {
page-break-after: avoid;
}
}
2. 打印布局优化
@media print {
/* 设置页面边距 */
@page {
margin: 2cm;
}
/* 优化表格打印 */
table {
border-collapse: collapse;
width: 100%;
}
/* 确保图片完整显示 */
img {
max-width: 100%;
height: auto;
}
}
3. 打印按钮样式
<!-- 使用 no-print 类标记不需要打印的元素 -->
<div class="button-container no-print">
<button onclick="printPage()">🖨️ 打印</button>
</div>
最佳实践
1. 错误处理
function printPage() {
try {
if (typeof window.print === 'function') {
window.print();
} else {
throw new Error('打印功能不可用');
}
} catch (error) {
console.error('打印失败:', error);
// 友好的错误提示
alert('打印功能暂时不可用,请稍后重试');
}
}
2. 用户体验优化
function printPage() {
// 显示加载提示
const loadingMsg = document.createElement('div');
loadingMsg.textContent = '正在准备打印...';
loadingMsg.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:#333;color:#fff;padding:20px;border-radius:5px;z-index:9999;';
document.body.appendChild(loadingMsg);
// 延迟执行打印,确保提示显示
setTimeout(() => {
window.print();
document.body.removeChild(loadingMsg);
}, 100);
}
3. 打印前数据准备
function printPage() {
// 打印前准备数据
preparePrintData();
// 执行打印
window.print();
// 打印后清理
cleanupAfterPrint();
}
function preparePrintData() {
// 例如:加载打印所需的数据
// 格式化数据
// 更新页面内容
}
function cleanupAfterPrint() {
// 清理临时数据
// 恢复页面状态
}
4. 打印配置选项
// Electron 打印选项配置
const printOptions = {
silent: false, // 显示打印对话框
printBackground: true, // 打印背景
deviceName: '', // 打印机名称(空表示默认)
color: true, // 彩色打印
margins: {
marginType: 'default' // 边距类型:default, none, printableArea, custom
},
landscape: false, // 横向打印
scaleFactor: 100, // 缩放比例
pagesPerSheet: 1, // 每张纸打印的页数
collate: true, // 自动分页
copies: 1 // 打印份数
};
window.webContents.print(printOptions, callback);
完整示例代码
main.js(完整版)
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');
// 禁用硬件加速(可选)
app.disableHardwareAcceleration();
app.commandLine.appendSwitch('disable-gpu');
let mainWindow;
function createWindow() {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: path.join(__dirname, 'preload.js'),
},
});
mainWindow.loadFile('index.html');
return mainWindow;
}
// 打印功能
ipcMain.on('print', (event) => {
const window = BrowserWindow.fromWebContents(event.sender);
if (!window) {
console.error('无法获取窗口对象');
return;
}
try {
if (window.webContents && typeof window.webContents.print === 'function') {
window.webContents.print({
silent: false,
printBackground: true,
deviceName: ''
}, (success, failureReason) => {
if (!success) {
console.error('打印失败:', failureReason);
// 降级到浏览器原生打印
window.webContents.executeJavaScript('window.print()');
}
});
} else {
window.webContents.executeJavaScript('window.print()');
}
} catch (error) {
console.error('打印错误:', error);
window.webContents.executeJavaScript('window.print()');
}
});
app.whenReady().then(() => {
createWindow();
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
preload.js(完整版)
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
versions: {
chrome: process.versions.chrome,
node: process.versions.node,
electron: process.versions.electron
},
print: () => {
ipcRenderer.send('print');
}
});
index.html(完整版)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>打印示例</title>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
}
.button-container {
margin: 20px 0;
}
button {
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
}
@media print {
.button-container {
display: none !important;
}
body {
background: white !important;
color: black !important;
}
}
</style>
</head>
<body>
<h1>打印示例页面</h1>
<p>这是要打印的内容。</p>
<div class="button-container">
<button onclick="printPage()">🖨️ 打印</button>
</div>
<script>
function printPage() {
try {
if (typeof window.print === 'function') {
window.print();
} else if (window.electronAPI && window.electronAPI.print) {
window.electronAPI.print();
} else {
alert('打印功能不可用');
}
} catch (error) {
console.error('打印失败:', error);
alert('打印功能出错: ' + error.message);
}
}
</script>
</body>
</html>
总结
实现要点
- 双重保障:优先使用浏览器原生
window.print(),降级到 Electron API - 错误处理:完善的错误处理和日志记录
- 样式优化:使用
@media print优化打印效果 - 用户体验:友好的错误提示和加载状态
注意事项
- 平台兼容性:在鸿蒙平台上,浏览器原生 API 更可靠
- 权限配置:确保应用有打印权限
- 性能优化:禁用硬件加速可能有助于解决某些兼容性问题
- 样式控制:合理使用 CSS 媒体查询控制打印样式
扩展方向
- PDF 导出:使用
webContents.printToPDF()导出 PDF - 打印预览:实现自定义打印预览功能
- 批量打印:支持批量打印多个页面
- 打印模板:实现可配置的打印模板系统
参考资料
更多推荐


所有评论(0)