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

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

最后

参考学习

欢迎交流沟通。

相关推荐
喵叔哟18 分钟前
重构代码之取消临时字段
java·前端·重构
还是大剑师兰特1 小时前
D3的竞品有哪些,D3的优势,D3和echarts的对比
前端·javascript·echarts
王解1 小时前
【深度解析】CSS工程化全攻略(1)
前端·css
一只小白菜~1 小时前
web浏览器环境下使用window.open()打开PDF文件不是预览,而是下载文件?
前端·javascript·pdf·windowopen预览pdf
方才coding1 小时前
1小时构建Vue3知识体系之vue的生命周期函数
前端·javascript·vue.js
man20171 小时前
【2024最新】基于springboot+vue的闲一品交易平台lw+ppt
vue.js·spring boot·后端
阿征学IT1 小时前
vue过滤器初步使用
前端·javascript·vue.js
王哲晓1 小时前
第四十五章 Vue之Vuex模块化创建(module)
前端·javascript·vue.js
丶21361 小时前
【WEB】深入理解 CORS(跨域资源共享):原理、配置与常见问题
前端·架构·web
发现你走远了1 小时前
『VUE』25. 组件事件与v-model(详细图文注释)
前端·javascript·vue.js