欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 锐评 > Vue3结合vue-plugin-hiprint实现自定义打印模板设计与布局

Vue3结合vue-plugin-hiprint实现自定义打印模板设计与布局

2024/10/25 4:19:54 来源:https://blog.csdn.net/Da_zhenzai/article/details/143211419  浏览:    关键词:Vue3结合vue-plugin-hiprint实现自定义打印模板设计与布局

简介

在现代Web应用开发中,打印功能是不可或缺的一部分,尤其是在需要输出标准化文档的场景下。本文将详细介绍如何在Vue3项目中利用vue-plugin-hiprint插件实现一个可定制的打印模板设计器,并通过具体示例来展示其配置与使用方法。

技术栈

本项目基于以下技术栈:

  • Vue3
  • TypeScript (Ts)
  • Element Plus
  • Vite

插件安装

首先通过npm安装vue-plugin-hiprint插件:

npm install vue-plugin-hiprint

main.ts引入组件

在项目的入口文件main.ts中引入hiPrintPlugin并注册到Vue应用实例上。同时引入全局CSS(从node_modules/vue-plugin-hiprint/dist/目录下复制一份print-lock.css到静态资源目录下)。

import { createApp } from 'vue';
import App from './App.vue';
// 引入全局CSS  
import './assets/public/print-lock.css'  
import { hiPrintPlugin } from 'vue-plugin-hiprint';hiPrintPlugin.disAutoConnect(); // 取消自动连接直接打印客户端
const app = createApp(App);
app.use(hiPrintPlugin, '$pluginName');

CSS样式配置

为了确保打印效果符合预期,在原有的print-lock.css基础上进行了扩展和调整,以适应不同的浏览器环境,尤其是Firefox浏览器中可能出现的打印重叠问题。如下是个人的iconfont.css,等会主组件要用,这里面的SVG字体样式后台评论区将提供:

@font-face {font-family: "iconfont"; /* Project id 3559670 */src: url("iconfont.woff2?t=1667531544868") format("woff2"),url("iconfont.woff?t=1667531544868") format("woff"),url("iconfont.ttf?t=1667531544868") format("truetype");
}.iconfont {font-family: "iconfont" !important;font-size: 16px;font-style: normal;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;
}.sv-edit-data:before {content: "\e655";
}.sv-shimmer:before {content: "\e6d6";
}.sv-origin:before {content: "\e6ac";
}.sv-zIndex:before {content: "\e603";
}.sv-structure:before {content: "\ec6f";
}.sv-list:before {content: "\e742";
}.sv-grid:before {content: "\e849";
}.sv-flow:before {content: "\e611";
}.sv-switch:before {content: "\e6f6";
}.sv-theme:before {content: "\e644";
}.sv-element:before {content: "\e615";
}.sv-pdf:before {content: "\e67a";
}.sv-browser:before {content: "\e726";
}.sv-font-big:before {content: "\eb04";
}.sv-font-small:before {content: "\eb05";
}.sv-font-bold:before {content: "\ec83";
}.sv-font-tiny:before {content: "\e6c1";
}.sv-options:before {content: "\e607";
}.sv-close:before {content: "\e646";
}.sv-clone:before {content: "\ec7a";
}.sv-cut:before {content: "\e643";
}.sv-preview:before {content: "\e61c";
}.sv-zoom-in:before {content: "\e60f";
}.sv-zoom-out:before {content: "\e610";
}.sv-edit:before {content: "\e6b9";
}.sv-paste:before {content: "\e6c0";
}.sv-copy:before {content: "\e6c2";
}.sv-unlock:before {content: "\e6e7";
}.sv-lock:before {content: "\e6e8";
}.sv-zIndex-plus:before {content: "\e715";
}.sv-zIndex-minus:before {content: "\e716";
}.sv-zIndex-top:before {content: "\e71f";
}.sv-sigh:before {content: "\e724";
}.sv-ask:before {content: "\e725";
}.sv-dev-code:before {content: "\e733";
}.sv-bug:before {content: "\e73f";
}.sv-zIndex-bottom:before {content: "\e71d";
}.sv-new:before {content: "\e64d";
}.sv-clear:before {content: "\e62d";
}.sv-base:before {content: "\e7d0";
}.sv-export:before {content: "\eabf";
}.sv-import:before {content: "\eac0";
}.sv-add:before {content: "\eaf3";
}.sv-printer:before {content: "\eabe";
}.sv-save:before {content: "\eabd";
}.sv-more:before {content: "\e625";
}.sv-menu:before {content: "\e628";
}.sv-nav-right:before {content: "\e629";
}.sv-nav-up:before {content: "\e62a";
}.sv-nav-left:before {content: "\e62b";
}.sv-nav-down:before {content: "\e62c";
}.sv-setting:before {content: "\e62e";
}.sv-delete:before {content: "\e630";
}.sv-undo:before {content: "\e631";
}.sv-redo:before {content: "\e632";
}.sv-refresh:before {content: "\e634";
}.sv-history:before {content: "\e635";
}.sv-html:before {content: "\e633";
}.sv-longText:before {content: "\e64c";
}.sv-table:before {content: "\ec15";
}.sv-qrcode:before {content: "\e642";
}.sv-image:before {content: "\e8ba";
}.sv-barcode:before {content: "\eb64";
}.sv-text:before {content: "\e60b";
}.sv-vline:before {content: "\e63a";
}.sv-oval:before {content: "\eb99";
}.sv-rect:before {content: "\e620";
}.sv-hline:before {content: "\e60a";
}.sv-print-c:before {content: "\e602";
}.sv-print:before {content: "\e601";
}.sv-c:before {content: "\e600";
}.sv-vertical:before {content: "\e706";
}.sv-distributeHor:before {content: "\e707";
}.sv-right:before {content: "\e708";
}.sv-left:before {content: "\e709";
}.sv-distributeVer:before {content: "\e70f";
}.sv-bottom:before {content: "\e710";
}.sv-top:before {content: "\e711";
}.sv-horizontal:before {content: "\e712";
}.sv-rotate:before {content: "\e66f";
}.sv-butongbu:before {content: "\e636";
}.sv-synchronization:before {content: "\e676";
}
/* 重写全局 hiprint 样式 */
.hiprint-headerLine,
.hiprint-footerLine {border-color: purple !important;
}.hiprint-headerLine:hover,
.hiprint-footerLine:hover {border-top: 3px dashed purple !important;
}.hiprint-headerLine:hover:before {content: "页眉线";left: calc(50% - 18px);position: relative;background: #ffff;top: -14px;color: purple;font-size: 12px;
}.hiprint-footerLine:hover:before {content: "页脚线";left: calc(50% - 18px);position: relative;color: purple;background: #ffff;top: -14px;font-size: 12px;
}
/* 区域 */
.left {background: white;border-radius: 4px;border: 1px solid #d9d9d9;padding: 10px 0;box-shadow: 2px 2px 2px 0px rgb(128 0 128 / 20%);overflow: auto;
}
.center {margin: 0 10px;background: white;border-radius: 4px;border: 1px solid #d9d9d9;padding: 20px;box-shadow: 2px 2px 2px 0px rgb(128 0 128 / 20%);overflow: auto;
}
.right {background: white;border-radius: 4px;border: 1px solid #d9d9d9;padding: 10px 0;box-shadow: 2px 2px 2px 0px rgb(128 0 128 / 20%);overflow: auto;
}
/* 左侧拖拽元素样式 */
.title {font-size: 16px;font-weight: 500;width: 100%;margin: 10px 0 0 24px;
}
.item {display: flex;flex-direction: column;align-items: center;background: white;padding: 4px 10px;margin: 10px 8px 4px 8px;width: 38%;min-height: 60px;border-radius: 4px;box-shadow: 2px 2px 2px 2px rgba(171, 171, 171, 0.2);
}
.item .iconfont {font-size: 1.5rem;
}
.item span {font-size: 14px;
}/* scrollbar */
::-webkit-scrollbar {height: 4px;width: 4px;
}
::-webkit-scrollbar-corner {height: 4px;width: 4px;
}
::-webkit-scrollbar-thumb {background: purple;border-radius: 2px;background-image: -webkit-linear-gradient(45deg,rgba(255, 255, 255, 0.2) 25%,transparent 25%,transparent 50%,rgba(255, 255, 255, 0.2) 50%,rgba(255, 255, 255, 0.2) 75%,transparent 75%,transparent);
}
::-webkit-scrollbar-thumb:hover {background: purple;
}/* flex */
.flex-row {display: flex;
}
.flex-col {display: flex;flex-direction: column;
}
.flex-wrap {flex-wrap: wrap;
}
.align-center {align-items: center;
}
.justify-center {justify-content: center;
}.flex-1 {flex: 1;
}
.flex-2 {flex: 2;
}
.flex-3 {flex: 3;
}
.flex-4 {flex: 4;
}
.flex-5 {flex: 5;
}.ml-10 {margin-left: 10px;
}
.mr-10 {margin-right: 10px;
}
.mt-10 {margin-top: 10px;
}
.mb-10 {margin-bottom: 10px;
}button:hover {opacity: 1;
}
button i {font-size: 16px !important;
}
.circle,
.circle-4 {border-radius: 4px !important;
}
.circle-10 {border-radius: 10px !important;
}/* modal */
.modal {padding: 0;margin: 0;
}
.modal .mask {position: fixed;top: 0;right: 0;bottom: 0;left: 0;z-index: 1000;height: 100%;background-color: #00000073;
}
.modal .wrap {position: fixed;top: 0;right: 0;bottom: 0;left: 0;z-index: 1000;overflow: auto;background-color: #00000073;outline: 0;
}
.modal .wrap .box {position: relative;margin: 10% auto;width: 40%;background: #fff;border-radius: 4px;z-index: 1001;box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);transition: all 0.3s ease;
}
.modal-box__header {padding: 10px 14px;border-bottom: 1px solid #e9e9e9;
}
.modal-box__footer {text-align: end;
}
.modal-box__footer button {min-width: 100px;
}
.modal-box__footer button:not(:last-child) {margin-right: 10px;
}

拖拽组件

为了提供更加丰富的设计体验,可以通过自定义拖拽组件来增强打印模板设计器的功能性。例如,这里customProvider.js:可以添加个人业务(追溯业务)相关的元素,如文本、条形码、二维码等。

import { hiprint } from "vue-plugin-hiprint";
export const iCustomProvider = function (options) {console.log(options.moduleList.value);var addElementTypes = function (context) {context.removePrintElementTypes("providerModule1");context.addPrintElementTypes("providerModule1", [new hiprint.PrintElementTypeGroup("追溯业务", [// options.config,...options.moduleList.value.map(item => ({tid: item.defaultModule,title: item.title,data: item.title,type: item.type,options: {field: item.field,height: 14,testData: "默认",fontSize: 12,fontWeight: '500',textAlign: 'center',textContentVerticalAlign: 'middle',...(item.textType ? { textType: item.textType } : {}),},})),{tid: "providerModule1.text",title: "文本",data: "文本",type: "text",options: {field: "customText",testData: "文本",height: 14,fontSize: 12,fontWeight: "500",textAlign: "left",textContentVerticalAlign: "middle",},},{tid: "providerModule1.barcode",title: "条形码",data: "XS888888888",type: "text",options: {field: "barcode",testData: "XS888888888",height: 32,fontSize: 12,lineHeight: 18,textAlign: "left",textType: "barcode",},},{tid: "providerModule1.qrcode",title: "二维码",data: "XS888888888",type: "qrcode",options: {field: "qrcode",testData: "XS888888888",height: 32,fontSize: 12,lineHeight: 18,textType: "qrcode",},},]),]);};return {addElementTypes: addElementTypes,};
};

主组件调用

在主组件中,我们使用el-drawer来显示打印模板的设计界面,引入上述我们创建好的customProvider.js和。此界面包括纸张尺寸的选择或自定义输入,以及打印、导出模板为JSON格式的功能按钮。

<el-drawer v-model="drawer" title="创建模板" size="100%" @opened="buildDesigner"><!-- 纸张尺寸选择区域 --><el-button-group><el-button v-for="(value, type) in paperTypes" :key="type" :type="getButtonType(type)" @click="setPaper(type, value)">{{ type }}</el-button><!-- 自定义纸张尺寸输入框 --><el-popover v-model="paperPopVisible" title="设置纸张宽高(mm)" :width="240" trigger="click"><!-- 输入框 --><el-input type="number" v-model="paperWidth" placeholder="宽(mm)"></el-input><el-input type="number" v-model="paperHeight" placeholder="高(mm)"></el-input><el-button type="primary" @click="otherPaper">确定</el-button></el-popover></el-button-group><!-- 功能按钮 --><button @click.stop="print"><i class="iconfont sv-printer" />浏览器打印</button><button @click.stop="exportJson"><i class="iconfont sv-export" />保存模板</button><!-- 其他元素 --><div id="hiprint-printTemplate"></div>
</el-drawer>
<script setup name="Template" lang="ts">
import { hiprint } from 'vue-plugin-hiprint';
import { iCustomProvider } from './customProvider'
// ...
// 当前纸张
const curPaper = ref({type: 'other',width: 80,height: 60});
// 纸张类型
const paperTypes = ref({'A3': {width: 420,height: 296.6},'A4': {width: 210,height: 296.6},'A5': {width: 210,height: 147.6},'B3': {width: 500,height: 352.6},'B4': {width: 250,height: 352.6},'B5': {width: 250,height: 175.6}});
// 自定义纸张
const paperPopVisible = ref(false);
const paperWidth = ref('80');
const paperHeight = ref('60');
// 计算按钮类型
const getButtonType = (type) => {return curPaper.value.type === type ? 'primary' : 'default';
};
const drawer = ref(false);
/*** 构建左侧可拖拽元素* 注意: 可拖拽元素必须在 hiprint.init() 之后调用* 而且 必须包含 class="ep-draggable-item" 否则无法拖拽进设计器*/
const buildLeftElement = () => {// eslint-disable-next-line no-undef// hiprint.PrintElementTypeManager.buildByHtml($(".ep-draggable-item"));$("#provider-container1").empty(); // 先清空, 避免重复构建// eslint-disable-next-line no-undefhiprint.PrintElementTypeManager.build($("#provider-container1"), "providerModule1");
};
/*** 构建设计器* 注意: 必须要在 onMounted 中去构建* 因为都是把元素挂载到对应容器中, 必须要先找到该容器*/
let hiprintTemplate;
const buildDesigner = () => {//构建拖拽元素buildLeftElement();// eslint-disable-next-line no-undef$("#hiprint-printTemplate").empty(); // 先清空, 避免重复构建hiprintTemplate = new hiprint.PrintTemplate({settingContainer: "#PrintElementOptionSetting", // 元素参数容器});hiprintTemplate.design("#hiprint-printTemplate");if (data.form.templateJson) {mergeTemplate(data.form.templateJson);}
};/*** 浏览器打印*/
const print = () => {// 打印数据,key 对应 元素的 字段名let printData = { name: "CcSimple" };// 参数: 打印时设置 左偏移量,上偏移量let options = { leftOffset: -1, topOffset: -1 };// 扩展let ext = {callback: () => {console.log("浏览器打印窗口已打开");},styleHandler: () => {// 重写 文本 打印样式return "<style>.hiprint-printElement-text{color:red !important;}</style>";},};// 调用浏览器打印hiprintTemplate.print(printData, options, ext);
};
/*** 导出模板 json* 必须确保 hiprintTemplate 已成功创建*/
const exportJson = () => {const jsonIns = hiprintTemplate.getJson();data.form.templateJson = JSON.stringify(jsonIns);drawer.value = false;validateTemplateJson();
};
/*** 导出模板 json tid* 仅导出 options, 不导出 printElementType* 必须确保 hiprintTemplate 已成功创建*/
const exportJsonTid = () => {const jsonIns = hiprintTemplate.getJsonTid();console.log(jsonIns);alert("导出成功! 请查看控制台输出");
};
/*** 更新出新的模板内容*/
const mergeTemplate = (jsonIn: string) => {if (hiprintTemplate) {try {hiprintTemplate.update(JSON.parse(jsonIn))} catch (e) {ElMessage.error(`更新失败: ${e}`)}}
};
/*** 设置纸张大小* @param type [A3, A4, A5, B3, B4, B5, other]* @param value {width,height} mm*/
const setPaper = (type, value) => {try {// 更新当前纸张信息curPaper.value = {type: type, width: value.width, height: value.height};// 设置打印模板的纸张尺寸hiprintTemplate.setPaper(value.width, value.height);} catch (error) {ElMessage.error(`操作失败: ${error}`);}
};// 触发模板数据的验证
const validateTemplateJson = () => {if (templateFormRef.value) {templateFormRef.value.validateField('templateJson').catch(() => { });}
};
/** 查询打印模板列表 */
const getList = async () => {loading.value = true;const res = await listTemplate(queryParams.value);templateList.value = res.rows;total.value = res.total;loading.value = false;
}
/** 获取模板组件数据 **/
let moduleList = ref([]);
const templateModules = async () => {try {const res = await getTemplateModule();moduleList.value = res.data.style;} catch (error) {console.error('Failed to fetch template module:', error);}
};onMounted(async () => {getList();await templateModules();hiprint.init({providers: [iCustomProvider({ moduleList: moduleList })]});buildDesigner();
});
</script>
<style>
@import '@/assets/css/iconfont.css';
</style>

展示效果

在这里插入图片描述

注意事项

  1. 纸张尺寸自定义:在设置纸张尺寸时,需确保输入的数值符合实际打印设备支持的范围。
  2. 打印样式兼容性:不同浏览器对打印样式的处理可能略有差异,特别是在处理页面布局时,需要对特定浏览器进行针对性优化。
  3. 自定义元素类型:当添加自定义元素类型时,需确保每个元素的属性值正确无误,特别是对于条形码和二维码这样的复杂元素。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com