跳到主要内容

打印功能实现

概述

在微搭应用开发中,打印功能是许多业务场景(如订单管理、仓储物流、零售收银等)的核心需求。本文将详细介绍如何在微搭应用中实现打印机调用,包括网页打印、云打印服务集成以及小程序打印方案。

适用场景

  • 订单打印:电商订单、外卖订单、配送单打印
  • 票据打印:收据、发票、凭证打印
  • 标签打印:商品标签、条码标签、物流标签
  • 报表打印:业务报表、统计报表、审批单据

实现方案

方案一:网页打印(Web 应用)

适用于微搭 Web 应用,通过浏览器的原生打印 API 实现打印功能。

1. 基础打印实现

创建一个自定义方法 printContent

export default async function ({ event, data }) {
// 触发浏览器打印对话框
window.print();
}

在按钮的点击事件中调用该方法即可触发打印。

2. 打印指定区域内容

如果只想打印页面中的特定区域,可以使用以下方法:

export default async function ({ event, data }) {
// 1. 获取要打印的内容元素
const printArea = document.getElementById('print-area');

if (!printArea) {
$w.utils.showToast({
title: "打印区域未找到",
icon: "error",
duration: 2000,
});
return;
}

// 2. 保存原始页面内容
const originalContent = document.body.innerHTML;

// 3. 替换页面内容为打印区域内容
document.body.innerHTML = printArea.innerHTML;

// 4. 触发打印
window.print();

// 5. 恢复原始页面内容
document.body.innerHTML = originalContent;

// 6. 重新加载页面(可选,用于恢复页面交互)
window.location.reload();
}

3. 使用打印样式优化

在应用的自定义 CSS 中添加打印专用样式:

/* 打印时隐藏不需要的元素 */
@media print {
/* 隐藏导航栏、按钮等非打印元素 */
.no-print,
.navbar,
.sidebar,
button {
display: none !important;
}

/* 打印区域样式优化 */
.print-area {
width: 100%;
padding: 20px;
font-size: 14px;
}

/* 强制分页 */
.page-break {
page-break-after: always;
}

/* 避免元素被截断 */
.no-break {
page-break-inside: avoid;
}
}

在需要打印的容器组件上添加 print-area 类名:

在微搭中的操作步骤:

  1. 选中需要打印的容器组件(如容器、表单容器等)
  2. 在右侧属性面板中,点击「组件配置」→「样式」
  3. 找到「className」配置项,输入 print-area
  4. 同时设置组件的 ID 为 print-area(在「基础」配置中设置)

4. 高级打印功能

使用第三方库 print-js 实现更强大的打印功能:

步骤 1:引入 print-js 库

在应用设置中添加外部依赖:

https://printjs-4de6.kxcdn.com/print.min.js
https://printjs-4de6.kxcdn.com/print.min.css

步骤 2:实现打印方法

export default async function ({ event, data }) {
try {
// 打印 HTML 元素
printJS({
printable: 'print-area', // 元素 ID
type: 'html',
targetStyles: ['*'], // 保留所有样式
style: `
.print-area {
font-family: Arial, sans-serif;
font-size: 14px;
line-height: 1.6;
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
`,
});
} catch (error) {
console.error('打印失败:', error);
$w.utils.showToast({
title: "打印失败,请重试",
icon: "error",
duration: 2000,
});
}
}

打印 PDF 文件:

export default async function ({ event, data }) {
// 假设 data.pdfUrl 是 PDF 文件的链接
const pdfUrl = data.pdfUrl || 'https://example.com/document.pdf';

printJS({
printable: pdfUrl,
type: 'pdf',
showModal: true, // 显示加载弹窗
});
}

打印图片:

export default async function ({ event, data }) {
const imageUrl = data.imageUrl || 'https://example.com/image.jpg';

printJS({
printable: imageUrl,
type: 'image',
header: '商品标签', // 可选的标题
});
}

打印 JSON 数据表格:

export default async function ({ event, data }) {
const tableData = [
{ name: '产品A', quantity: 10, price: 100 },
{ name: '产品B', quantity: 5, price: 200 },
{ name: '产品C', quantity: 8, price: 150 },
];

printJS({
printable: tableData,
type: 'json',
properties: ['name', 'quantity', 'price'], // 要显示的字段
header: '<h3>产品清单</h3>', // 表格标题
gridHeaderStyle: 'color: #000; border: 1px solid #ddd; padding: 8px;',
gridStyle: 'border: 1px solid #ddd; padding: 8px;',
});
}

方案二:云打印服务(推荐)

通过集成云打印服务(如飞鹅云打印、易联云等),实现远程打印和批量打印。

以飞鹅云打印为例

步骤 1:注册飞鹅云打印账号

访问 飞鹅云打印官网 注册账号,添加打印机设备。

步骤 2:创建云函数

在云开发控制台创建云函数 printOrder

const cloud = require('@cloudbase/node-sdk');
const crypto = require('crypto');
const axios = require('axios');

// 飞鹅云打印配置
const FEI_E_CONFIG = {
user: 'your_feie_user', // 飞鹅云用户名
ukey: 'your_feie_ukey', // 飞鹅云 UKEY
sn: 'your_printer_sn', // 打印机编号
};

// 生成飞鹅云签名
function generateSign(params) {
const keys = Object.keys(params).sort();
const str = keys.map(key => `${key}=${params[key]}`).join('&') + FEI_E_CONFIG.ukey;
return crypto.createHash('sha1').update(str).digest('hex');
}

exports.main = async (event, context) => {
const { content, times = 1 } = event;

try {
// 构建打印内容(飞鹅云格式)
const printContent = `
<CB>订单详情</CB><BR>
<C>--------------------------------</C><BR>
订单号:${content.orderNo}<BR>
下单时间:${content.orderTime}<BR>
<C>--------------------------------</C><BR>
<B>商品清单</B><BR>
${content.items.map(item =>
`${item.name} x${item.quantity}${item.price}<BR>`
).join('')}
<C>--------------------------------</C><BR>
<RIGHT>总计:<BOLD>¥${content.totalAmount}</BOLD></RIGHT><BR>
<C>================================</C><BR>
<C>感谢您的购买!</C><BR>
<QR>https://example.com/order/${content.orderNo}</QR>
`.trim();

// 调用飞鹅云打印 API
const params = {
user: FEI_E_CONFIG.user,
stime: Math.floor(Date.now() / 1000),
sig: '',
apiname: 'Open_printMsg',
sn: FEI_E_CONFIG.sn,
content: printContent,
times: times,
};

params.sig = generateSign(params);

const response = await axios.post(
'http://api.feieyun.cn/Api/Open/',
new URLSearchParams(params)
);

console.log('打印结果:', response.data);

return {
success: true,
data: response.data,
};
} catch (error) {
console.error('打印失败:', error);
return {
success: false,
error: error.message,
};
}
};

步骤 3:在微搭应用中调用

创建打印按钮的点击事件方法:

export default async function ({ event, data }) {
try {
// 1. 获取订单数据
const orderData = {
orderNo: 'ORD202603200001',
orderTime: new Date().toLocaleString(),
items: [
{ name: '商品A', quantity: 2, price: 50 },
{ name: '商品B', quantity: 1, price: 100 },
],
totalAmount: 200,
};

// 2. 调用云函数打印
const result = await $w.cloud.callFunction({
name: 'printOrder',
data: {
content: orderData,
times: 1, // 打印份数
},
});

if (result.success) {
$w.utils.showToast({
title: "打印成功",
icon: "success",
duration: 2000,
});
} else {
throw new Error(result.error);
}
} catch (error) {
console.error('打印失败:', error);
$w.utils.showToast({
title: "打印失败: " + error.message,
icon: "error",
duration: 3000,
});
}
}

飞鹅云打印标签说明

标签说明示例
<BR>换行商品名称<BR>
<CB>文本</CB>居中加粗<CB>标题</CB>
<C>文本</C>居中<C>内容</C>
<B>文本</B>加粗<B>重要</B>
<BOLD>文本</BOLD>加粗(同 B)<BOLD>金额</BOLD>
<RIGHT>文本</RIGHT>右对齐<RIGHT>¥100</RIGHT>
<QR>内容</QR>二维码<QR>订单链接</QR>
<BARCODE>内容</BARCODE>条形码<BARCODE>12345</BARCODE>
<CUT>切纸<CUT>

方案三:小程序打印

微信小程序环境下可以通过蓝牙连接打印机或使用云打印服务。

1. 蓝牙打印机连接

export default async function ({ event, data }) {
try {
// 1. 初始化蓝牙模块
await wx.openBluetoothAdapter();

// 2. 开始搜索蓝牙设备
await wx.startBluetoothDevicesDiscovery({
allowDuplicatesKey: false,
});

$w.utils.showToast({
title: "正在搜索打印机...",
icon: "loading",
duration: 2000,
});

// 3. 监听蓝牙设备发现
wx.onBluetoothDeviceFound((res) => {
console.log('发现设备:', res.devices);
// 根据设备名称或 UUID 过滤打印机
const printers = res.devices.filter(device =>
device.name && device.name.includes('Printer')
);

if (printers.length > 0) {
// 连接第一个找到的打印机
connectPrinter(printers[0].deviceId);
}
});
} catch (error) {
console.error('蓝牙初始化失败:', error);
$w.utils.showToast({
title: "蓝牙初始化失败",
icon: "error",
duration: 2000,
});
}
}

// 连接打印机
async function connectPrinter(deviceId) {
try {
await wx.createBLEConnection({ deviceId });

$w.utils.showToast({
title: "打印机连接成功",
icon: "success",
duration: 2000,
});

// 连接成功后可以发送打印指令
// ...
} catch (error) {
console.error('连接打印机失败:', error);
}
}

2. 使用云打印服务

小程序中同样可以调用方案二中的云函数实现云打印。


最佳实践

1. 打印前预览

在打印前提供预览功能,让用户确认打印内容:

export default async function ({ event, data }) {
// 显示打印预览弹窗
$w.utils.showModal({
title: '打印预览',
content: '确认打印此订单吗?',
success: (res) => {
if (res.confirm) {
// 用户确认后执行打印
window.print();
}
},
});
}

2. 批量打印

实现批量打印多个订单:

export default async function ({ event, data }) {
const selectedOrders = $w.dataList1.selectedItems; // 获取选中的订单

if (!selectedOrders || selectedOrders.length === 0) {
$w.utils.showToast({
title: "请先选择要打印的订单",
icon: "error",
duration: 2000,
});
return;
}

// 批量调用云函数打印
const promises = selectedOrders.map(order =>
$w.cloud.callFunction({
name: 'printOrder',
data: { content: order },
})
);

try {
await Promise.all(promises);
$w.utils.showToast({
title: `成功打印 ${selectedOrders.length} 个订单`,
icon: "success",
duration: 2000,
});
} catch (error) {
console.error('批量打印失败:', error);
$w.utils.showToast({
title: "部分订单打印失败",
icon: "error",
duration: 2000,
});
}
}

3. 打印历史记录

将打印记录保存到数据库:

export default async function ({ event, data }) {
const app = await $w.cloud.getCloudInstance();
const db = app.database();

try {
// 打印前保存记录
await db.collection('print_history').add({
orderNo: data.orderNo,
printTime: new Date(),
printBy: $w.auth.currentUser.uid,
status: 'pending',
});

// 执行打印
const result = await $w.cloud.callFunction({
name: 'printOrder',
data: { content: data },
});

// 更新打印状态
await db.collection('print_history')
.where({ orderNo: data.orderNo })
.update({
status: result.success ? 'success' : 'failed',
result: result.data,
});
} catch (error) {
console.error('保存打印记录失败:', error);
}
}

4. 错误处理

完善的错误处理机制:

export default async function ({ event, data }) {
try {
// 验证打印数据
if (!data || !data.orderNo) {
throw new Error('订单数据不完整');
}

// 检查打印机状态(云打印)
const printerStatus = await checkPrinterStatus();
if (!printerStatus.online) {
throw new Error('打印机离线,请检查设备状态');
}

// 执行打印
const result = await $w.cloud.callFunction({
name: 'printOrder',
data: { content: data },
});

if (!result.success) {
throw new Error(result.error || '打印失败');
}

$w.utils.showToast({
title: "打印成功",
icon: "success",
duration: 2000,
});
} catch (error) {
console.error('打印错误:', error);

// 根据错误类型提供不同的提示
let errorMessage = '打印失败,请重试';
if (error.message.includes('离线')) {
errorMessage = '打印机离线,请检查设备';
} else if (error.message.includes('数据')) {
errorMessage = '订单数据异常,请刷新后重试';
}

$w.utils.showToast({
title: errorMessage,
icon: "error",
duration: 3000,
});
}
}

// 检查打印机状态
async function checkPrinterStatus() {
// 调用云函数查询打印机状态
const result = await $w.cloud.callFunction({
name: 'checkPrinter',
data: {},
});
return result.data;
}

常见问题

Q1: 网页打印时样式丢失怎么办?

A: 使用 @media print 定义专门的打印样式,并在 print-js 中使用 targetStyles: ['*'] 保留所有样式。

Q2: 如何实现静默打印(无需弹出对话框)?

A: 浏览器出于安全考虑,无法直接实现静默打印。可以使用云打印服务(如飞鹅云)实现远程静默打印。

Q3: 小程序如何连接网络打印机?

A: 小程序无法直接连接网络打印机,建议使用云打印服务或通过服务器中转。

Q4: 打印内容出现乱码怎么办?

A: 检查字符编码设置,确保使用 UTF-8 编码。对于云打印服务,查看对应文档的编码要求。

Q5: 如何实现打印队列管理?

A: 在云函数中维护打印队列,使用数据库记录打印任务状态,通过定时任务轮询处理。