[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 做禁用和悬浮提示

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

最后

参考学习

欢迎交流沟通。

相关推荐
brzhang3 小时前
我操,终于有人把 AI 大佬们 PUA 程序员的套路给讲明白了!
前端·后端·架构
止观止3 小时前
React虚拟DOM的进化之路
前端·react.js·前端框架·reactjs·react
goms4 小时前
前端项目集成lint-staged
前端·vue·lint-staged
谢尔登4 小时前
【React Natve】NetworkError 和 TouchableOpacity 组件
前端·react.js·前端框架
Lin Hsüeh-ch'in4 小时前
如何彻底禁用 Chrome 自动更新
前端·chrome
augenstern4166 小时前
HTML面试题
前端·html
张可6 小时前
一个KMP/CMP项目的组织结构和集成方式
android·前端·kotlin
G等你下课6 小时前
React 路由懒加载入门:提升首屏性能的第一步
前端·react.js·前端框架
蓝婷儿7 小时前
每天一个前端小知识 Day 27 - WebGL / WebGPU 数据可视化引擎设计与实践
前端·信息可视化·webgl
然我7 小时前
面试官:如何判断元素是否出现过?我:三种哈希方法任你选
前端·javascript·算法