[vue-plugin-hiprint] 打印总结

工厂 saas 项目,出货需要打印「发货单」、「质保卡」等

核心需求:自动打印、选择打印机、自定义打印模板设计

方案考虑

  1. window.print()
    • 不能获取打印机列表,无法自动打印
  2. Lodop打印控件
    • 授权费用高
  3. vue-plugin-hiprint
    • 相比上面方案,自带模板设计,并且提供打印客户端,基本满足需求
    • 缺点:hiprint 是闭源的,自定义模板不够灵活(没有点击拖拽元素触发的事件等等)

最后选择使用 vue-plugin-hiprint 开发初版。

功能点实现

隐藏不需要的参数

js 复制代码
// 配置参数
hiprint.setConfig({
    text: {
        tabs: [], // 隐藏tabs分组
        supportOptions: [
            {
                name: 'title',
                hidden: true,
            },
            {
                name: 'fontSize',
                hidden: false,
            },
        ],
    },
});

查看所有参数:window.HIPRINT_CONFIG

注意:v0.0.56 无法隐藏参数的 tabs 分组,切到 v0.0.50

插入背景图片

背景图片只起参照作用,不参与打印

js 复制代码
const insertBg = () => {
    if (!state.currentTemplate.bgUrl) return
    const bgImg = document.querySelector('#bg-img')
    if (bgImg) {
        bgImg.style.opacity = state.currentTemplate.bgAph / 100
        bgImg.src = state.currentTemplate.bgUrl
    } else {
        const img = new Image()
        img.id = 'bg-img'
        img.style.display = 'block'
        img.style.width = '100%'
        img.style.height = '100%'
        img.style.position = 'relative'
        img.style.zIndex = '-1'
        img.style['-webkit-user-drag'] = 'none'
        img.style.opacity = state.currentTemplate.bgAph / 100
        img.src = state.currentTemplate.bgUrl
        img.onload = () => {
            const printPaper = document.querySelector('.hiprint-printPaper')
            printPaper.append(img)
        }
    }
}

打印预览

js 复制代码
<!-- 打印预览弹窗 -->
<el-dialog class="preview-dialog" v-model="previewVisible" title="打印预览" top="10vh" width="1600">
    <div class="preview-wrapper">
        <div class="preview"></div>
    </div>

    <template #footer>
        <span class="dialog-footer">
            <el-button @click="previewVisible = false">关闭</el-button>
        </span>
    </template>
</el-dialog>

// 打印预览
const previewVisible = ref(false)
const printPreview = () => {
    previewVisible.value = true
    const html = hiprintTemplate.getHtml(printData)
    // console.log('html: ', html)
    do {
        setTimeout(() => {
            $('.preview').empty()
            $('.preview').html(html)
        }, 200)
        return
    } while ($('.preview').length <= 0)
}

/* 不同模板 间隙 */
.preview .hiprint-printTemplate {
    background: #fff;
    border-bottom: 10px solid #ccc;
}

/* 批量打印 间隙 */
.preview .hiprint-printTemplate .hiprint-printPanel:not(:last-of-type) {
    border-bottom: 5px solid #ccc;
}

设置纸张大小和缩放

js 复制代码
// 设置纸张大小
const handleChangePageSize = () => {
    if (hiprintTemplate) {
        hiprintTemplate.setPaper(state.currentTemplate.pageWidth, state.currentTemplate.pageHeight)
    }
}

// 设置缩放
const handleChangeScale = () => {
    hiprintTemplate.zoom(state.currentTemplate.scale / 100, false)
}

插入分页符

js 复制代码
context.addPrintElementTypes('defaultModule', [
    new hiprint.PrintElementTypeGroup('辅助', [
        {
            title: '分页符',
            tid: 'defaultModule.pageBreak',
            type: 'hline',
            options: {
                width: 200,
                height: 10,
                borderStyle: 'dotted',
                borderColor: '#ff0000',
                showInPage: 'none', // 打印隐藏
                pageBreak: true, // 强制分页
            },
        },
    ]),
]);

// 针对始终隐藏的元素(分页符)
.alwaysHide {
    background-color: unset !important;
}

横向打印

js 复制代码
hiprintTemplate.print2(printData, {
    printer: '',
    title: '测试任务',
    landscape: true, // 横向打印
    pageSize: { // 纸张大小(必须)
        width: state.currentTemplate.pageWidth * 1000,
        height: state.currentTemplate.pageHeight * 1000,
    },
});

注意:

  • windows 系统可以直接修改打印机设置,而 mac 系统不行
  • 打印机驱动会影响打印效果,必须安装原生驱动!

获取打印机列表

js 复制代码
const getPrinterList = async () => {
    setInterval(() => {
        if (hiprint.hiwebSocket.opened) {
            state.printerList = hiprint.hiwebSocket?.getPrinterList() || [];
            console.log('打印机列表: ', state.printerList);
        } else {
            // 重新连接客户端
            hiprint.hiwebSocket.setHost("http://localhost:17521")
        }
    }, 1000);
}

扩展:可以通过本地存储记住上次选择的打印机

批量自动打印

js 复制代码
// 伪代码
const batchPrint = () => {
    selectRows.forEach((row: any) => {
        getOrderPrintData({ orderId: row.orderId }).then((res) => {
            if (res.success) {
                const printData = res.returnValue;
                console.log('printData', printData);

                const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

                const printQueue = async () => {
                    for (const item of printItemListFilter) {
                        // 每次打印延迟5s,防止过快导致打印机接收不到
                        // await delay(5000)

                        let hiprintTemplate = new hiprint.PrintTemplate({
                            template: JSON.parse(item.templateContent),
                        });

                        hiprintTemplate.print2(printData, { printer: item.printerName, title: '订单出货打印' });

                        // 更新打印状态
                        const updateStatus = async (isSuccess: boolean) => {

                        };

                        // 发送任务到打印机成功
                        hiprintTemplate.on('printSuccess', async function (e) {
                            await updateStatus(true);
                        });
                        // 发送任务到打印机失败
                        hiprintTemplate.on('printError', async function (e) {
                            await updateStatus(false);
                        });
                    }
                };

                printQueue();
            }
        });
    });
};

注意:mac 系统接收批量打印任务时必须要延迟,否则会丢包,而 winodws 系统不需要。当然为了保险和防止打印机过载工作,还是可以加上。

利用表格打印列表数据

js 复制代码
// 注意点:隐藏边框、单元格合并、无数据处理、函数转义
{
    tid: `customModule.productList`,
    title: '产品列表',
    type: 'table',
    options: {
        field: 'productList',
        fields: [
            { text: '产品名称', field: 'name' },
            { text: '左上', field: 'upLeft' }
            { text: '右上', field: 'upRight' }
            { text: '左下', field: 'downLeft' }
            { text: '右下', field: 'downRight' }
        ],
        
        // 隐藏边框
        tableHeaderRepeat: 'none',
        tableBorder: 'noBorder',
        tableBodyRowBorder: 'noBorder',
        tableBodyCellBorder: 'noBorder',

        tableBodyRowHeight: 40, // 行高
        width: 700, // 总宽
    },
    columns: [
        [
            {
                title: '产品名称',
                field: `name`,
                rowspan: 2,
                colspan: 1,
                align: 'center',
            },
            {
                title: '左上',
                field: `upLeft`,
                rowspan: 1,
                colspan: 1,
                align: 'center',
            },
            {
                title: '右上',
                field: `upRight`,
                rowspan: 1,
                colspan: 1,
                align: 'center',
            },
        ],
        [
            {
                title: '左下',
                field: `downLeft`,
                rowspan: 1,
                colspan: 1,
                align: 'center',
                // 函数都需要转义,防止保存时被 JSON.stringify() 过滤掉
                renderFormatter: function (value, row, colIndex, options, rowIndex) {
                    if (!row.name) return ''
                    return `
                        <div>
                            <div style="padding: 4px;border-right: 1px solid;text-align: right;border-bottom: 1px solid;">${
                                row.upLeft || '&nbsp;'
                            }</div>
                            <div style="padding: 4px;border-right: 1px solid;text-align: right;">${
                                row.downLeft || '&nbsp;'
                            }</div>
                        </div>
                    `
                }.toString(),
                styler2: function (value, row, index, options) {
                    return { padding: 0 }
                }.toString(),
            },
            {
                title: '右下',
                field: `downRight`,
                rowspan: 1,
                colspan: 1,
                align: 'center',
                renderFormatter: function (value, row, colIndex, options, rowIndex) {
                    if (!row.name) return ''
                    return `
                        <div>
                            <div style="padding: 4px;text-align: left;border-bottom: 1px solid;">${
                                row.upRight || '&nbsp;'
                            }</div>
                            <div style="padding: 4px;text-align: left;">${
                                row.downRight || '&nbsp;'
                            }</div>
                        </div>
                    `
                }.toString(),
                styler2: function (value, row, index, options) {
                    return { padding: 0 }
                }.toString(),
            },
        ],
    ],
}

局限:必须拖拽整个表格,无法将列单独拆开

关于打印客户端

mac 系统必须使用 x64 的安装包,否则打不开客户端

如果未连接到打印客户端并调用直接打印,会弹出 alert ,建议在触发打印的功能按钮上根据 hiprint.hiwebSocket.opened 做禁用和悬浮提示

可以将官网的安装包放到服务器上,方便用户直接下载

最后

参考学习

欢迎交流沟通。

相关推荐
小满zs2 分钟前
Zustand 第三章(状态简化)
前端·react.js
普宁彭于晏3 分钟前
元素水平垂直居中的方法
前端·css·笔记·css3
恋猫de小郭15 分钟前
为什么跨平台框架可以适配鸿蒙,它们的技术原理是什么?
android·前端·flutter
云浪18 分钟前
元素变形记:CSS 缩放函数全指南
前端·css
明似水33 分钟前
用 Melos 解决 Flutter Monorepo 的依赖冲突:一个真实案例
前端·javascript·flutter
独立开阀者_FwtCoder43 分钟前
stagewise:让AI与代码编辑器无缝连接
前端·javascript·github
清沫1 小时前
Cursor Rules 开发实践指南
前端·ai编程·cursor
江城开朗的豌豆1 小时前
JavaScript篇:对象派 vs 过程派:编程江湖的两种武功心法
前端·javascript·面试
不吃糖葫芦31 小时前
App使用webview套壳引入h5(二)—— app内访问h5,顶部被手机顶部菜单遮挡问题,保留顶部安全距离
前端·webview
江城开朗的豌豆1 小时前
JavaScript篇:字母侦探:如何快速统计字符串里谁才是'主角'?
前端·javascript·面试