菜鸟打印组件系列-vue3快速接入

文章目录

  • 前言
  • 1. 相关名词或语句
  • 2. CAINIAO打印组件能力
  • 3. 安装与下载
  • 4. vue3集成步骤
      • 4.1 使用pina 创建websoket相关处理的模块。
      • 4.2 创建本地自定义模板(要打印的模板以及样式)
      • 4.3 结合el-table ,实现批量打印
  • 总结


前言

文章主要记录不注册商家,直接使用菜鸟打印组件提供的自定义模板的能力,来实现打印的。案例是基于vue3+vite+element-plus ,注册商家的使用可以参考官方案例。


1. 相关名词或语句

  • CAINIAO打印组件 (菜鸟打印组件)
  • cnPrinter(菜鸟打印组件)
  • ISV(第三方软件服务商)
  • 不干胶标签纸(快递单或则商品标签贴物品 )
  • 面单(指快递单等)
  • DPI 水平 (DPI(Dots Per Inch,每英寸点数),图像每英寸(1 英寸 = 25.4 毫米)长度内的像素点数)
  • 垂直偏移量 (设置打印内容在面单的位置)

2. CAINIAO打印组件能力

1.支持面单使用模板,这里的模板要注册商家号获取与管理。
2.支持全定义模板.
3. 兼容能力:支持windows 与 macOS(但不支持14以上)
4. 不支持独立部署,不支持离线使用(打印的数据都要上传到菜鸟的云服务器处理后,返回渲染后的面单,注意国外windows服务器传输比较慢)
5. 面对国内用户

3. 安装与下载

🚀CAINIAO打印组件官网 下载插件;
💪引用自官方 打印组件商家操作手册
在这里插入图片描述

4. vue3集成步骤

4.1 使用pina 创建websoket相关处理的模块。

菜鸟打印组件是通过websoket的方式发送指令给打印机的。这里使用pina 创建websoket相关处理的模块。
其中用的是 13528 端口。也就是 ws://127.0.0.1:13528 , 注意端口是否被占用。

下面的代码我让AI生产的,然后稍微改了一下

import { defineStore } from 'pinia'
import { printStatusApi } from '@/api/xxx'
// export const WS_URL = 'ws://127.0.0.1:13528'
import { RefNumEnum, WS_URL } from '@/utils/constant'
import { ElMessageBox } from 'element-plus'

export const useWebSocketStore = defineStore('websocket', {
  state: () => ({
    ws: null, // 全局存储一个 websoket ,避免重复创建
    isConnected: false, // 判断菜鸟组件是否以及启动
    reconnectInterval: 5000,
    reconnectTimer: null,
    callbackEventObj: {},
    isShowErrBox: false
  }),
  actions: {
    connect(cb: any, callbackType: any) {
      this.callbackEventObj[callbackType] = cb // 打印成功或则失败的回调函数,场景不同用callbackType 来判断对于的回调
      if (this.ws && this.ws.readyState === 1)  return
      this.ws = new WebSocket(WS_URL)
      this.ws.onopen = () => {
        console.log('WebSocket connected')
        this.isConnected = true
      }
      this.ws.onmessage = (event) => {
        console.log('WebSocket message received:', event.data)
        // 处理收到的消息
        this.subscribe(JSON.parse(event.data || ''), cb)
      }
      this.ws.onclose = () => {
        console.log('WebSocket disconnected')
        this.isConnected = false
        this.reconnect(cb, callbackType)
      }
      this.ws.onerror = (error) => {
        console.error('WebSocket error:', error)
        this.reconnect(cb, callbackType)
      }
    },
    send(message) {
      if (this.isConnected) {
        this.ws.send(message)
      } else {
        console.warn('WebSocket is not connected')
      }
    },
    close() {
      if (this.ws) {
        this.ws.close()
        this.ws = null
      }
    },
    subscribe(data: any, callBackFn: any) {
      switch (data.cmd) {
        case 'getPrinters':
          console.log('获取打印机列表', data.printers)
          if (data.printers) {
            data.printers = data.printers.filter((i: any) => {
              return i.status == 'enable'
            })
          }
          // 缓存起来
          localStorage.setItem('PRINTERS', JSON.stringify(data.printers || []))
          break
        case 'getPrinterConfig':
          console.log('获取打印机配置')
          console.log(data.printer)
          break
        case 'print':
          console.log('发送打印')
         // console.log(data)
          if (data?.status === 'failed' && !this.isShowErrBox) {
            this.isShowErrBox = true
            ElMessageBox({
              title: 'Warning',
              message: `${data?.msg}`,
              confirmButtonText: 'I know',
              callback: () => {
                this.isShowErrBox = false
              }
            })
          }
          break
        case 'notifyPrintResult':
          console.log('打印通知----',data)
          if (['printed', 'failed'].includes(data.taskStatus)) {
            // 打印成功/失败 回调记录状态Api接口
            this.printStatus(data)
          }
          break
        case 'setPrinterConfig':
          console.log('设置打印机配置')
          console.log(data)
          break
      }
    },
    // 这里的data是发送给菜鸟组件的数据按文档要求来,一般有requestID,TaskID等,把自定义的拼接进去
    async printStatus(data: any) {
      // 传参
      let curId = data?.requestID.split('#') && data?.requestID.split('#')[0]
      await  printStatusApi(curId )
      this.callbackEventObj[markType]()
    },
    // 这里的重连可以设置一个次数后,停止重连
    reconnect(cb, callbackType) {
      if (!this.reconnectTimer) {
        console.log(`WebSocket will reconnect in ${this.reconnectInterval} ms`)
        this.reconnectTimer = setTimeout(() => {
          console.log('WebSocket reconnecting...')
          this.connect(cb, callbackType)
          this.reconnectTimer = null
        }, this.reconnectInterval)
      }
    },
    stopReconnect() {
      if (this.reconnectTimer) {
        clearTimeout(this.reconnectTimer)
        this.reconnectTimer = null
      }
    }
  }
})

4.2 创建本地自定义模板(要打印的模板以及样式)

自定义的模板,是菜鸟组件定义模板语言。需要传到菜鸟的服务器处理后,渲染成真正的面单,这个文件放在public文件夹下,不然vite打包的时候会处理,导致打印出错。

<?xml version="1.0" encoding="UTF-8"?>
<page
        xmlns="http://cloudprint.cainiao.com/print"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://cloudprint.cainiao.com/print http://cloudprint-docs-resource.oss-cn-shanghai.aliyuncs.com/lpml_schema.xsd"
        xmlns:editor="http://cloudprint.cainiao.com/schema/editor"
        width="210" height="297"
           >
		<layout orientation="vertical" left="72" top= "6" width="60" height="20" style="zIndex:1;">
               <text  
                style="fontSize:18;align:center;">
                <![CDATA[xxxxxxxxxxxxxxxx]]>
               </text>
		</layout>
            <layout left= "8" top= "22" width="80" height="36" >
               <barcode width= "80" height= "36" value= "<%=_data.xx%>" type= "code128a" style= "hideText:false" />
            </layout>
              <layout orientation="vertical" left="108" top= "22" width="90" height="14" style="zIndex:4;">
               <text  
                style="fontSize:12;align:left;">
                <![CDATA[xxxxxx: <%=_data.xx%> ]]>
               </text>
		</layout>
              <layout left="108" top= "28" width="80" height="36" >
                <text  
                style="fontSize:12;align:left;">
                <![CDATA[xxxxxx: <%=_data.xx%> ]]>
               </text>
            </layout>
              <layout left="108" top= "34" width="120" height="36" >
                <text  
                style="fontSize:12;align:left;">
                <![CDATA[xxxxxxx: <%=_data.xx%> ]]>
               </text>
            </layout>
              <layout left="108" top= "40" width="80" height="36" >
                <text  
                style="fontSize:12;align:left;">
                <![CDATA[xxxxx: <%=_data.x%> ]]>
               </text>
            </layout>
            <layout left="108" top= "46" width="80" height="36" >
                <text  
                style="fontSize:12;align:left;">
                <![CDATA[Print Date: <%=_data.xxx%>]]>
               </text>
            </layout>
            <layout left="108" top= "52" width="80" height="36" >
                <text  
                style="fontSize:12;align:left;">
                <![CDATA[xxx: ____________ ]]>
               </text>
            </layout>
            <layout  id="1550220042465924" width="96" height="20.13" left="2" top="65.77"  style="zIndex:4;">
                  <table width="200">  
                        <tr >
                              <th width="38"> 
                                    <text  style="fontSize:10;align:center;">
                                                      <![CDATA[xxx]]>
                                    </text>
                               </th>
                              <th width="26"> 
                               <text  style="fontSize:10;align:center;">
                                                <![CDATA[xxx]]>
                               </text>
                               </th>
                               <th width="31"> 
                                    <text  style="fontSize:10;align:center;">
                                                      <![CDATA[xx]]>
                                    </text>
                              </th>
                              <th width="36"> 
                                    <text  style="fontSize:10;align:center;">
                                                      <![CDATA[xx]]>
                                    </text>
                               </th>
                              <th width="13"> 
                                    <text  style="fontSize:10;align:center;">
                                                      <![CDATA[xx]]>
                                    </text>
                               </th>
                              <th width="13"> 
                                    <text  style="fontSize:10;align:center;">
                                                      <![CDATA[xxx]]>
                                    </text>
                               </th>
                        </tr>
               
                      <% for(i=0;i<_data.arr.length;i++) { %>
                        <tr>
                              <td> 
                                    <text  style="fontSize:10;align:center;">
                                          <![CDATA[<%=_data.arr[i].x%>]]>
                                    </text>
                              </td>
                              <td>
                                    <text   style="fontSize:10;align:center;">
                                                <![CDATA[<%=_data.arr[i].x%>]]>
                                          </text>
                              </td>
                              <td>
                                    <text  style="fontSize:10;align:center;">
                                                <![CDATA[<%=_data.arr[i].x%>]]>
                                          </text>
                              </td>
                              <td>
                                    <text   style="fontSize:10;align:center;">
                                                <![CDATA[<%=_data.arr[i].xx%>]]>
                                          </text>
                              </td>
                              <td>
                                    <text   style="fontSize:10;align:center;">
                                                <![CDATA[<%=_data.arr[i].x%>]]>
                                          </text>
                              </td>
                              <td>
                                    <text style="fontSize:10;align:center;">
                                    </text>
                              </td>
                        </tr>
                      <%}%>
                  </table>
            </layout>
             <footer height="20" >
                  <layout width="100" height="10" left="10" top="1"  style="overflow:visible;">
                    <pageIndex format="Page currentPageNumber of totalPageNumber" style="fontSize:10;"></pageIndex>
                  </layout>
            </footer>
           
</page>

4.3 结合el-table ,实现批量打印

下面是在列表页面上使用上述模块,实现可以支持多选table的项,批量打印,记录打印的状态后刷新页面的核心代码,自行理解后根据自身业务或则交互使用。

import { useWebSocketStore } from '@/store/modules/websocket'
const useWebSocket = useWebSocketStore()
// variable.selectedAr 是勾选的数据
const doPrint = async function () {
  if (variable.selectedArr && variable.selectedArr.length === 0) {
    return ElMessage.warning('请选择需要打印的数据')
  }
  // 使用for of 是为了可以串行,也就是支持 await 放回数据后再接下去执行,同是支持 break 中断循环。
  for (const dataItem of variable.selectedArr) {
    const { data } = await detailApi({id:'123'})
    // 注意这个模板需要放在 public 里面
    const templateURL = `${window.location.origin}/cnPrinter/xxx.template`
    //打印对象   https://open.taobao.com/doc.htm?docId=107014&docType=1 根据官网调整
    const jsonObject: any ={
	"cmd": "print",
	"requestID": "123458976",
	"version": "1.0",
	"task": {
		"taskID": "7293666",
		"preview": false,
		"printer": "", // 为空字符串时,取系统默认打印机
		"documents": [{
			"documentID": "0123456789",
			"contents": [
			{
				"data": {
					"value": "测试字段值需要配合自定义区变量名"
				},
				"templateURL": `${templateURL}`
			}]
		}]
	}
}
    //发送给打印机
    useWebSocket.send(JSON.stringify(jsonObject))
  }
  handleSearch()
}

const callBackFunction = () => {}
onMounted(() => {
  useWebSocket.connect(callBackFunction,pageType)
})


总结

本文一开始介绍了菜鸟打印组件,相关名词,以及在打印方面的相关能力和使用时的注意点。接着举例基于vue3+pina+elementPlus的项目通过步骤1.使用pina 创建websoket相关处理的模块,处理打印机与浏览器长连接的过程。步骤2.按照菜鸟要求的模板语言,调整自己的打印模板,并且放在public文件夹下,步骤3.在列表页其中使用el-table组件,引入websoket模块,连接菜鸟组件以及批量打印,实现简单的批量打印功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/118953.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Kubernetes Dashboard 用户名密码方式登录

Author&#xff1a;rab 前言 为了 K8s 集群安全&#xff0c;默认情况下 Dashboard 以 Token 的形式登录的&#xff0c;那如果我们想以用户名/密码的方式登录该怎么操作呢&#xff1f;其实只需要我们创建用户并进行 ClusterRoleBinding 绑定即可&#xff0c;接下来是具体的操作…

【Docker】Docker中 的AUFS、BTRFS、ZFS、存储池概念的详细讲解

前言 作者简介&#xff1a; 辭七七&#xff0c;目前大二&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; 七七的闲谈 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&…

Jupyter Notebook交互式开源笔记本工具

1、官网 http://jupyter.org/ 2、什么是Jupyter Notebook Jupyter Notebook一个交互式的开源笔记本工具&#xff0c;可以用于编写、运行、和共享代码、文本、图形等内容。 如下文本、代码、图形 支持多种编程语言&#xff0c;包括python、R和Julia等&#xff0c;可以走一个…

【elasticsearch+kibana基于windows docker安装】

创建网络&#xff1a;es和kibana容器互联 docker network create es-net加载镜像 docker pull elasticsearch:7.12.1运行 docker run -d --name es -p 9200:9200 -p 9300:9300 -e "discovery.typesingle-node" -e ES_JAVA_OPTS"-Xms512m -Xmx512m" -v $…

【ES专题】ElasticSearch搜索进阶

目录 前言阅读导航前置知识特别提醒笔记正文一、分词器详解1.1 基本概念1.2 分词发生的时期1.3 分词器的组成1.3.1 切词器&#xff1a;Tokenizer1.3.2 词项过滤器&#xff1a;Token Filter1.3.3 字符过滤器&#xff1a;Character Filter 1.4 倒排索引的数据结构 <font color…

Nacos本地修改编译源码2.2.3

下载Nacos源码 由于github访问速度慢&#xff0c;所以在gitee上下载 git clone https://gitee.com/mirrors/Nacos.git切换2.2.3版本 git checkout 2.2.3或者直接下载2.2.3的源码 本地编译 源码导入idea&#xff0c;然后编译 mvn -Dmaven.test.skiptrue -Drat.skiptrue c…

【owt】p2p client mfc 工程梳理

1年前构建的,已经搞不清楚了。所以梳理下,争取能用较新的webrtc版本做测试。最早肯定用这个测试跑通过 【owt】p2p Signaling Server 运行、与OWT-P2P-MFC 交互过程及信令分析官方的mfc客户端 估计是构造了多个不同的webrc版本的客户端

github使用教程

配置Git 首先在本地创建ssh key&#xff1b; $ ssh-keygen -t rsa -C "your_emailyouremail.com"后面的your_emailyouremail.com改为你在github上注册的邮箱&#xff0c;之后会要求确认路径和输入密码&#xff0c;我们这使用默认的一路回车就行。成功的话会在~/下生…

科技云报道:不卷自研大模型,金山办公如何创新生成式AI?

科技云报道原创。 过去大半年里&#xff0c;很多人对大模型的前景寄予厚望。主流观点认为&#xff0c;每个行业、每款产品都可以通过大模型“重做一遍”。 “重做一遍”听起来想象空间很大&#xff0c;但实际上多数大模型产品需要漫长的训练周期和海量资源投入&#xff0c;落…

unraid 安装并设置 zerotier 内网穿透安装 unraid 局域网内其他设备

Read Original 最近看了以下两个文章&#xff0c;感谢发布的各种精彩文章&#xff0c;让我受益匪浅。OPENWRT 的固件在设置了&#xff0c;【自动允许客户端 NAT】后&#xff0c;可以直接访问局域网其他设备&#xff0c;而我 unraid 部署 zerotier 后&#xff0c;只能访问 unra…

2023第一届OPENAIGC开发者大赛圆满收官获奖名单公示

11月4日&#xff0c;历时两个月的「2023第一届开放AIGC开发者大赛」在苏州国际博览中心圆满收官。本次大赛以“AI生成未来”为主题&#xff0c;旨在寻找并推动中国大模型、AIGC领域的创新人才和前沿技术应用。云集来自各大行业与领域的企业、高校等323支优秀团队&#xff0c;共…

力扣 141.环形链表和142.环形链表2

目录 1.环形链表Ⅰ解题思路2.环形链表Ⅰ代码实现3.环形链表Ⅱ解题思路4.环形链表Ⅱ代码实现 1.环形链表Ⅰ解题思路 利用快慢指针&#xff0c;快指针一次走两个&#xff0c;慢指针一次走一个&#xff0c;如果出现了快指针为空或者快指针的next为空的现象则说明不带环&#xff0…

将 UniLinks 与 Flutter 集成(安卓 AppLinks + iOS UniversalLinks)

让我们使用 Flutter Mobile 和 Flutter Web 集成 UniLinks。 一步一步的指导&#xff01; 我是 Pedro Dionsio&#xff0c;是葡萄牙 InspireIT 公司的 Flutter 开发人员&#xff0c;我写这个 UniLinks 教程的座右铭是&#xff1a; Firebase DynamicLinks 已被弃用&#xff0…

vivo 网络端口安全建设技术实践

作者&#xff1a;vivo 互联网安全团队 - Peng Qiankun 随着互联网业务的快速发展&#xff0c;网络攻击的频率和威胁性也在不断增加&#xff0c;端口是应用通信中的门户&#xff0c;它是数据进出应用的必经之路&#xff0c;因此端口安全也逐渐成为了企业内网的重要防线之一&…

【mmcv报错】ModuleNotFoundError: No module named ‘mmcv.runner

跑一个代码需要用到mmcv和mmseg 其中有两行代码&#xff1a; from mmcv.runner import load_checkpoint from mmseg.utils import get_root_logger我先是按照官方推荐的安装方法去安装了mmcv和mmseg pip install -U openmim mim install mmcv它会自动帮你安装mmengine 我的cu…

前后端交互常见的几种数据传输格式

目录 1. get请求 query string 2.form表单get请求 3..form表单post请求 4..json格式 5.总结 1. get请求 query string 前端通过get请求携带 query string&#xff08;键值对&#xff09; ,后端通过req.getParameter(key)方法获取数据。如果key不存在&#xff0c;获取到的就…

3款免费又好用的 Docker 可视化管理工具

前言 Docker提供了命令行工具&#xff08;Docker CLI&#xff09;来管理Docker容器、镜像、网络和数据卷等Docker组件。我们也可以使用可视化管理工具来更方便地查看和管理Docker容器、镜像、网络和数据卷等Docker组件。今天我们来介绍3款免费且好用的 Docker 可视化管理工具。…

一款好用的PDF转翻页电子书网站

​你是否曾经遇到过PDF文件无法翻页或者阅读不便的问题&#xff1f;今天给大家推荐一款好用的PDF转翻页电子书网站&#xff0c;让你轻松阅读PDF文件&#xff0c;不再烦恼翻页问题&#xff01; 一、网站介绍 这款FLBOOK在线制作电子杂志网站支持多种电子文件格式转换&#xff0…

vue3中router和route的区别(使用场景)

1.router router是用来对路由进行操作的&#xff1b; 多用于路由跳转、路由守卫、页面刷新、给路由文件添加路由路径或者移除路由路径等等 2.route route是用来获取路由信息的&#xff1b; 多用于获取路由路径、路由传参数据、路由文件配置的属性信息等等

大漠插件(二、Qt使用插件时注意事项)

本章目的 在上篇已经注册完毕大漠&#xff0c;那么怎么使用大漠来制作脚本&#xff0c;我选择了我最熟悉的Qt来开发&#xff0c;毕竟只是小软件&#xff0c;用脚本或者c都差不了多少。本章就是开发途中的一些坑。 本人开发环境是 win11 64、Qt 5.15.2安装了5.10.0的msvc2015 32…