☰
SpringBoot+Vue3 企业打印架构:审批单、业务凭证与条码标签如何统一设计
2026/10/8 2:08:12 网站建设 项目流程

SpringBoot+Vue3 企业打印架构:审批单、业务凭证与条码标签如何统一设计

🌐文档地址:https://ruoyioffice.com
👇 文章底部获取源码和演示地址 👇
💬 :17156169080(获取产品咨询)

仓库收到一批设备,管理员需要打印入库凭证,给每台设备贴标签,还要保存一份能在下个月重新查看的电子材料。三个动作使用同一批业务数据,却不能简单共用一个“打印当前页面”按钮。纸张、版式、设备、历史口径和结果确认不同,决定了企业打印实际上是一套输出架构。

▲ 同一份业务内容可以服务多个输出通道。统一的是数据含义和模板治理,不是强行用同一种技术驱动所有设备。

一、为什么打印功能容易在项目后期变成返工中心

最初的需求通常很简单:“把详情页打印出来。”于是前端加一个按钮,调用浏览器打印。直到真实业务开始使用,问题才逐渐出现:左侧菜单也被打出来;第二页没有表头;标签尺寸不对;二维码还未绘制完成;旧单据重打时套用了新模板;点击取消打印,系统却把它记成了“已成功”。

这些问题不都属于 CSS。它们来自几种不同的对象被混在一起:**业务内容是什么、如何排版、交给谁输出、系统究竟确认了什么。**如果设计之初没有区分,后面每个模块都会各自补丁,最后形成互不兼容的打印孤岛。

需求核心约束不宜直接套用的做法
审批单与业务凭证主表、明细、意见、分页直接打印整个管理页面
资产条码标签毫米尺寸、码制、设备校准缩小一张 A4 页面代替标签模板
电子归档材料固定版式、可重现、权限把当前页面 URL 当作归档文件
历史重打当时数据与当时模板永远读取当前主数据重新渲染

本文采用“资产业务产生凭证与标签”作为主线,同时借助流程打印代码说明跨业务数据结构。浏览器打印和资产 C-Lodop 双通道是现有工程能力;统一打印任务、不可变归档快照及端到端回执,是在此基础上提出的设计扩展。

二、先看一笔业务怎样从屏幕走向纸面

1. 从业务详情选择打印对象,而不是从页面 HTML 抓字段

资产卡片已经有业务身份、分类、使用组织和标识码。打印时,用户实际选择的是一组业务对象,而不是某个 DOM 节点。后端应按这些对象重新获取允许输出的数据,形成专用打印响应。

▲ 真实资产详情页面。详情用于确认打印对象;打印接口仍需独立校验对象范围和字段权限,不能因为按钮可见就跳过授权。

这种做法能避免两类问题。第一,页面为了交互可能只加载部分明细,而打印需要完整内容。第二,列表列配置、主题和隐藏字段会影响 DOM,却不应改变凭证中的正式数据。

业务响应与打印响应可以共享底层查询和转换逻辑,但不要让打印模板直接访问任意数据库字段。可输出字段应有稳定标识、展示名称、类型和格式规则。后续把“使用部门”改成页面上的简称,不应让模板中的字段路径全部失效。

2. 模板管理的是输出规则,不只是几段 HTML

资产标签模板包含纸张或标签尺寸、行列布局、码制、字段配置以及打印通道。用户选择 A4 不干胶模板与标签机模板,本质上是在选择不同的物理约束,而不只是选择不同配色。

▲ 真实模板编辑界面。模板应把字段、尺寸和输出通道放在一起治理;修改模板时不需要修改业务单据本身。

模板可分为身份与版本两层。模板身份表达“资产标签标准版”,版本表达某次发布后的具体布局。草稿允许调整,发布后要能被打印任务稳定引用。是否立即让所有新打印使用新版,是业务策略;历史任务怎样重现则是另一条规则。

还要区分业务值和显示值。资产名称是业务数据;名称过长时换行、截断或缩小字体,是模板规则。金额数值与大写格式、日期值与打印日期格式,同样不应混为一谈。否则同一业务内容经过不同模板后,可能看起来像产生了不同事实。

3. 预览应体现目标版式,而不是假装设备已经成功出纸

标签预览页可以展示当前模板、份数、偏移和标签内容。它让实施人员在没有真正发送打印任务前发现字段缺失、溢出和排列问题,是很有价值的验收入口。

▲ 真实标签预览页面。本次仅查看预览,未向物理打印机发送任务;界面预览正确也不等于设备已经完成出纸。

在现有前端中,切换模板会重新加载打印数据;仅预览时不会要求累加打印次数,正式点击打印时才按配置记录。这个区别值得保留,因为预览通常要反复进行,不能把每次调版都当成业务打印。

不过,“点击打印才计数”仍然不等于“设备成功才计数”。浏览器弹出打印对话框后,用户可以取消;标签服务接收任务后,设备也可能缺纸。记录名称如果直接叫“成功打印次数”,就给出了超出系统证据的承诺。

三、统一架构应该统一到哪一层

一个实用的边界是把系统分成业务数据提供、模板描述、输出适配和任务记录四部分。前两部分尽可能统一,输出适配允许差异,任务记录则必须明确各通道能提供何种证据。

▲ 建议逻辑模型:打印任务引用具体业务对象和模板版本,输出记录描述实际通道及结果证据。统一任务与版本快照属于扩展设计。

对审批单,已有BpmProcessPrintServiceImpl会加载历史流程实例、实例绑定的流程定义、已完成任务和相关人员信息,再组织printSchema、fieldData、tableData。这说明“打印用数据”可以成为独立协议,而不是散落在 Vue 模板里的若干字符串替换。

下面根据已有服务整理数据组装主线,省略用户映射与具体字段构造。它展示职责分工,不是可以直接替代完整服务的代码。

publicBpmProcessPrintDataRespVObuildPrintData(StringinstanceId){varinstance=processInstanceService.getHistoricProcessInstance(instanceId);if(instance==null)throwexception(PROCESS_INSTANCE_NOT_EXISTS);vardefinition=processDefinitionService.getProcessDefinitionInfo(instance.getProcessDefinitionId());varform=definition!=null&&definition.getFormId()!=null?formService.getForm(definition.getFormId()):null;vartasks=taskService.getFinishedTaskListByProcessInstanceIdWithoutCancel(instanceId);// 实际实现还组装发起人、部门及审批人信息。varresult=newBpmProcessPrintDataRespVO();result.setPrintSchema(buildPrintSchema(definition,form));result.setFieldData(buildFieldData(instance,definition,form));result.setTableData(buildTableData(instance,definition,form));// 审批轨迹由实际 Convert 逻辑统一转换,而非模板直接查任务表。returnresult;}

这种协议的关键不是 DTO 名称,而是它让模板知道哪些字段可用、哪些是多行明细、哪些是流程轨迹。模板设计器可以基于字段定义生成选择项,输出适配器则基于确定的数据渲染,双方都不必理解整个业务服务。

同时不能忽略字段级安全。用户可以查看单据,不代表可以导出所有敏感字段;某个审批节点隐藏的字段,也不应通过打印接口绕过限制。打印、预览、生成 PDF、下载归档文件,都属于数据出口,必须分别检查访问边界。

四、A4、PDF 和标签机,应该怎样选通道

浏览器打印:优先解决常规纸张与交互预览

浏览器通道适合用户能确认纸张、打印机和份数的常规办公场景。页面需要独立的打印区域和打印样式,隐藏导航、操作按钮等交互元素。对于长明细,需要明确表头重复、行拆分、页边距和签字区的处理。

现有资产标签实现没有在数据加载后立即调用打印,而是等待 Vue 更新和两帧绘制。条码使用 canvas 时,这个等待能减少“已经切到新数据,却打印了旧图”的风险。

asyncfunctionwaitForPrintReady(){awaitnextTick();awaitnewPromise<void>((resolve)=>{requestAnimationFrame(()=>requestAnimationFrame(()=>resolve()));});}asyncfunctionhandleBrowserPrint(){if(labels.value.length===0)return;printing.value=true;try{awaitloadPrintData(true);awaitwaitForPrintReady();document.body.classList.add(PRINT_BODY_CLASS);window.addEventListener('afterprint',removePrintMode,{once:true});window.print();}finally{printing.value=false;}}

这是对当前前端控制流的摘录,省略了空数据时的提示。对于外部字体、远程图片或异步图表,两帧并不构成通用的资源完成证明,还应等待对应资源真正就绪。afterprint用于恢复页面状态,不应单独作为物理打印成功凭证。

PDF:重点是固定版式与可重现,而不是文件扩展名

浏览器“另存为 PDF”和服务端生成 PDF 是两种实现路径。前者依赖用户操作与客户端环境;后者便于集中生成和归档,但需要管理字体、分页、渲染资源、任务超时以及文件权限。不能因为浏览器具备保存 PDF 的入口,就宣称系统已经具备服务端归档流水线。

选择时可以先问:这份材料是否需要以后按同样内容重新取得?如果只是临时下载,当前数据渲染可能足够;如果需要证明当时输出了什么,就应保存实际文件或足以重现的不可变数据和模板版本。

“可重现”也有程度之分。只保存业务 ID,能重新查询当前数据;保存字段快照和模板版本,能重建当时语义;保存最终 PDF 和内容摘要,才能直接比对当时文件。不同要求对应不同成本,不必所有单据都采用最高等级,但必须向业务方说清楚。

标签机:尺寸与设备指令是第一等约束

资产标签实现通过printChannel区分浏览器与 C-Lodop 通道。标签机适配会把毫米尺寸、条码区域、字段位置、字体和偏移转换为打印指令;特定模板还可以携带原始设备指令。

因此,统一模板协议应该保留通道能力差异,而不是假装每个模板都能无损转换。网页 CSS、标签指令和 PDF 排版分别有自己的布局能力。设计器可显示“支持的输出通道”,让用户在发布模板之前就知道限制。

对于未经安装或无法检测的本地打印服务,产品应给出清晰状态,并保留可用的浏览器输出路径。不要在后台静默失败后仍提示打印成功,也不要为了截图演示绕过设备检测。本文不假设每个客户都拥有标签机。

五、一次打印任务,要留下哪些可解释的记录

打印任务不是一条“已打印=true”。至少要分清请求已创建、内容已生成、已提交通道、设备已确认,以及人工确认等不同证据等级。不是所有通道都能到达最后一种状态。

▲ 建议时序:先确定内容与模板,再交给具体通道;结果状态只能根据实际可获取的回执推进。

对于用户连续点击或网络重试,可以在创建任务时使用请求标识防重。但业务上的“重打”是一个新的意图,不应永远被幂等键拦住。前一次请求的重试复用原任务;用户明确重新打印,则创建新任务并关联原任务,记录原因和发起人。

下面是任务创建的扩展设计伪代码。唯一约束与“同键同参数”的校验是设计前提,不能仅靠先查后插防止并发重复。

publicPrintJobrequestPrint(PrintCommandcommand,Subjectsubject){permission.requirePrint(subject,command.businessId());Stringdigest=commandDigest.of(command);PrintJobexisting=jobs.findByRequestId(subject.tenantId(),command.requestId());if(existing!=null){if(!existing.payloadDigest().equals(digest)){thrownewIllegalArgumentException("同一请求标识不能携带不同参数");}returnexisting;}TemplateVersiontemplate=templates.requirePublished(command.templateVersionId());PrintPayloadpayload=dataProvider.authorizedPayload(command,subject);PrintJobcandidate=PrintJob.created(command.requestId(),subject,template,payload);candidate.setPayloadDigest(digest);// 唯一键冲突时读取胜出记录,并再次核对 digest。returnjobs.insertOrGetMatching(candidate);}

任务审计还应避免过度记录。包含人员信息、合同金额或内部审批意见的打印内容,不宜直接写入普通应用日志。日志可以记录任务 ID、业务 ID、通道和错误类别,敏感快照存放在受控区域,并设置与业务相匹配的保留时间。

打印任务跨越浏览器、本地服务和设备时,通常不存在一个覆盖全部步骤的数据库事务。超时后也不能简单认定“没有打印”。可以把状态保留为待核实,让用户检查设备和纸张,再选择重试或新建重打任务。诚实的状态比虚假的成功提示更有价值。

六、历史重打,为什么会牵涉到业务快照

假设资产的使用部门已经从研发部变成测试部,用户重新打印两个月前的交接凭证。这里至少有两种合理需求:打印“当时发生的交接事实”,或者打印“当前资产最新情况”。两者都可能正确,但必须选择其中一种,不能由代码碰巧读取哪张表决定。

建议在产品上使用不同名称,例如“查看原凭证”和“按当前数据重新生成”。原凭证优先使用当时保存的文件;重新生成则明确显示数据时间、模板版本与新生成时间。这样既能服务业务,又不会把新文件伪装成原始记录。

同理,审批流程中的人员名称、部门名称和手写签名,也可能随时间变化。引用历史流程定义解决的是定义身份,并不自动冻结所有主数据。需要哪些字段快照,应根据凭证用途逐项选择,而不是想当然地把“使用历史实例”理解为“全部内容永远不变”。

模板升级时也要考虑兼容。新模板要求一个旧单据从未保存的字段,系统不应默默填空并继续盖上“完整凭证”。可以回退到旧模板、提示缺少数据,或允许生成带说明的补充材料。缺失信息应被看见,而不是被漂亮排版掩盖。

七、实施验收:把打印问题提前到交付之前

软件预览只能完成一部分验收。常规 A4 至少要验证长名称、多页明细、空字段和中文字体;标签至少要验证尺寸、边距、实际扫码,以及更换设备后的偏移。对于没有设备的环境,可以完成内容和模板检查,但不能给出“实机扫码通过”的结论。

验收场景应检查的行为证据要求
切模板后立即打印使用新模板与新条码页面渲染就绪及输出样张
用户取消浏览器打印不宣称设备出纸成功请求与结果状态分开
详情更新后重新预览按明确策略刷新数据显示数据时间或快照身份
历史模板已升级原凭证仍可解释模板版本与原文件可追溯
连续点击与超时重试不意外重复创建同一任务请求幂等与重打关联
无权访问某项数据预览和下载同样受限服务端拒绝而非仅隐藏按钮

还可以准备一组固定的版式样本:一页、两页、超长说明、空明细、特殊符号、不同份数。模板每次变更后,用同一组数据重新渲染并比较,能比人工临时找一张单据更早发现回归。

如果要做视觉比对,不宜把所有像素差异都判成失败。生成时间、页码和动态任务号需要单独处理;真正应稳定的是字段位置、分页边界、条码内容及关键文字。验收目标应对应业务风险,而不是追求没有解释的截图相似度。

八、从现有双通道,逐步走向统一打印服务

第一阶段可以保留各模块现有打印入口,先统一打印数据协议和权限校验。这样对业务页面影响小,也能减少模板直接访问内部字段的问题。

第二阶段集中模板身份、版本与通道能力。浏览器 A4、标签机和后续 PDF 渲染可以分别接入,仍然保留各自的设备约束。统一平台不应变成限制最强通道只能使用最弱功能。

第三阶段再增加任务幂等、重打关联、归档文件和留存治理。对于只需要临时标签的场景,不必强制保存完整 PDF;对于需要长期核对的正式凭证,则应保存更强的证据。

在系统中体验时,可以先查看资产详情,再进入标签模板与标签预览,观察业务内容、模板和通道的分工;流程打印服务则可作为主表、明细与审批信息统一组装的参考。本文没有新增打印服务,也没有执行实机打印,所有扩展方案都应按需要实施。

常见问题

能不能所有打印都统一用浏览器实现?

可以把浏览器作为常规办公默认通道,但精确标签、受控设备指令、后台批量生成和长期归档可能有不同要求。统一内容和模板治理,比强制统一物理输出技术更稳妥。

点击打印后,为什么不能直接把状态改成成功?

按钮点击只能证明用户发起了动作,不能证明设备完成出纸。系统应按通道实际能提供的证据命名状态;浏览器关闭打印界面也不能单独证明成功。

有 PDF 文件,是否就实现了归档?

还没有。需要确定文件身份、访问权限、业务关联、生成依据和保留策略。一个随当前数据重新生成的下载文件,与保留当时结果的归档文件含义不同。

模板变更会不会影响旧单据?

取决于打印时引用的是当前模板还是确定的版本,以及数据是否保留了必要快照。系统必须明确选择,不能把历史一致性交给默认查询行为。

结语

企业打印真正需要统一的,是业务内容、模板身份、访问边界与结果证据。A4、PDF 和标签机可以使用不同通道,但用户应该始终知道自己打印了什么、依据哪一版、系统确认到了哪一步。

RuoYi Office 的流程打印数据组装与资产双通道实现,为这套设计提供了实际工程起点。沿着这些边界逐步建设,比每个业务页面各写一套打印逻辑,更容易交付,也更容易长期维护。

如果这篇对你有用,点个「在看」或收藏。

🌐演示地址:https://ruoyioffice.com/web
📦GitHub 源码:https://github.com/yuqing2026/ruoyi-office
📦Gitee 源码:https://gitee.com/yqzy1688/ruoyi-office
💬微信:17156169080(获取产品咨询)

打开演示地址直接查看系统。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询