行业包文档管理 行业包文档管理
首页
  • 系统快速上手
  • Git分支管理规则及协作流程说明
  • 系统账号密码说明
  • 架构说明

    • 系统整体架构说明
    • QMS-SPC系统架构说明
  • 后端基础类库说明
  • PC端前端所有组件说明 (opens new window)
  • 数据模型设计与说明
  • 后端新建微服务配置
  • 后端华为OpenApis开发
  • 事务集成
  • PC端前端从0开始

    • 本地部署运行
    • 创建前端工程
    • PC端组件库安装使用
    • 按钮权限基础包
    • 业务集成二开
    • 交付人员二开
  • 移动端前端从0开始

    • 移动端打包APK
    • 移动端公共组件
    • 移动端公共库
    • 移动端蓝牙打印
  • 前端公共

    • 前端整体架构
    • 前端注意事项
  • 公有云部署流程
  • iDME私有化部署流程
  • 许可授权流程
  • 各方案推荐配置清单
  • PC端前端二开流程
  • 移动端底座二开流程
  • 更新日志及规范规范
常见问题
Source (opens new window)
首页
  • 系统快速上手
  • Git分支管理规则及协作流程说明
  • 系统账号密码说明
  • 架构说明

    • 系统整体架构说明
    • QMS-SPC系统架构说明
  • 后端基础类库说明
  • PC端前端所有组件说明 (opens new window)
  • 数据模型设计与说明
  • 后端新建微服务配置
  • 后端华为OpenApis开发
  • 事务集成
  • PC端前端从0开始

    • 本地部署运行
    • 创建前端工程
    • PC端组件库安装使用
    • 按钮权限基础包
    • 业务集成二开
    • 交付人员二开
  • 移动端前端从0开始

    • 移动端打包APK
    • 移动端公共组件
    • 移动端公共库
    • 移动端蓝牙打印
  • 前端公共

    • 前端整体架构
    • 前端注意事项
  • 公有云部署流程
  • iDME私有化部署流程
  • 许可授权流程
  • 各方案推荐配置清单
  • PC端前端二开流程
  • 移动端底座二开流程
  • 更新日志及规范规范
常见问题
Source (opens new window)
  • 移动端蓝牙打印

    • 概述
      • 核心依赖
        • 基本使用流程
          • 1. 进入页面时注册
          • 2. 页面挂载时连接打印机
          • 3. 使用时调用打印
          • Demo 1: 使用 CPCL 构建器直接构建
          • Demo 2: 使用模板渲染(后端模板)
          • Demo 3: 使用页面级工具函数
          • Demo 4: 获取模板 API 文件示例
          • Demo 5: 页面级打印工具函数示例
          • 4. 离开页面时注销
        • 常见问题处理
          • 1. 连接失败处理
          • 2. 打印失败处理
          • 3. 重试机制
        • 开发注意事项
          • 参考示例项目
          • 前端手册
          • 移动端前端从0开始
          sie
          2026-09-11
          目录

          移动端蓝牙打印

          移动端蓝牙打印功能开发指南

          # 概述

          本指南介绍如何在 uni-app 移动端项目中集成和使用蓝牙打印功能。蓝牙打印功能基于 SPP(Serial Port Profile) 协议实现,适用于支持 SPP 的蓝牙打印机(如芝柯 XT453 等)。

          # 核心依赖

          // 主要依赖的工具类
          import SPPPrintHelper from "@/utils/bluetooth/spp-print-helper.js"; // 蓝牙打印辅助类
          import CPCLBuilder from "@/utils/bluetooth/builder/cpcl-builder.js"; // CPCL 指令构建器
          import { renderCpclTemplate } from "@/utils/bluetooth/utils.js"; // 模板渲染工具
          
          1
          2
          3
          4

          # 基本使用流程

          # 1. 进入页面时注册

          在页面的 created 生命周期中初始化蓝牙打印机实例:

          export default {
            name: "your-page",
            mixins: [mixin], // 必须引入 mixin
            data() {
              return {
                sppHelper: null, // SPP 蓝牙打印辅助实例
                printTemplate: "", // 打印模板内容
              };
            },
            created() {
              console.log("初始化 SPP 蓝牙打印辅助实例");
              // 初始化 SPP 蓝牙打印辅助实例
              this.sppHelper = new SPPPrintHelper();
              this.sppHelper.init();
              
              // 在 created 中只做初始化,不立即连接
              // 连接放到 mounted 中后台执行,避免阻塞页面首帧渲染
              
              // 获取打印模板(如果需要)
              this.getPrintTemplate();
            },
          };
          
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22

          # 2. 页面挂载时连接打印机

          在 mounted 生命周期中后台静默连接默认打印机:

          mounted() {
            // 页面挂载完成后再后台静默连接默认打印机,避免阻塞页面进入
            if (this.sppHelper) {
              console.log("后台静默连接默认打印机");
              this.sppHelper.connectInBackground({
                timeout: [`<nowiki>`](http://<nowiki>)5000,
                onError: (err) => {
                  $toast.toast("自动连接默认打印机失败:" + err.message);
                  console.error("[your-page] auto connect default err :>> ", err);
                },
              });
              
              // 注册打印机断开回调,收到系统 ACL 断开广播时提示用户
              if (this.sppHelper.printer) {
                this.sppHelper.printer.onDisconnect = (address) => {
                  console.warn("[sppTool] printer disconnected:", address);
                  $toast.toast("蓝牙打印机已断开连接");
                };
              }
            }
          }
          
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21

          # 3. 使用时调用打印

          在业务逻辑中调用打印功能。以下是几种常见的使用场景示例:

          # Demo 1: 使用 CPCL 构建器直接构建

          // 顶部导入依赖
          import CPCLBuilder from "@/utils/bluetooth/builder/cpcl-builder.js";
          
          export default {
            // ... 其他配置
            methods: {
              /**
               * 使用 CPCL 构建器直接构建打印内容
               */
              async printWithBuilder() {
                try {
                  // 1. 创建构建器实例
                  const builder = new CPCLBuilder();
                  
                  // 2. 初始化指令(必须放在开头)
                  builder.init(420); // 设置标签高度为 420 点
                  
                  // 3. 添加页面元素
                  builder.addBox(0, 0, 520, 350); // 添加矩形框
                  builder.addText("物料编码: " + this.partCode, 55, 0, 10, 10);
                  builder.setAlignment("center");
                  builder.addBarcode("128", this.barcodeNo, 10, 50);
                  
                  // 4. 添加打印指令(必须放在最后)
                  builder.print();
                  
                  // 5. 构建 CPCL 指令字符串
                  const cpclText = builder.build();
                  
                  // 6. 发送到打印机
                  await this.sppHelper.printCPCL(cpclText);
                  $toast.success("打印成功");
                  
                } catch (err) {
                  console.error("[your-page] print error :>> ", err);
                  $toast.toast("打印失败:" + (err.message || err));
                }
              },
            }
          };
          
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25
          26
          27
          28
          29
          30
          31
          32
          33
          34
          35
          36
          37
          38
          39
          40

          # Demo 2: 使用模板渲染(后端模板)

          // 顶部导入依赖
          import { renderCpclTemplate } from "@/utils/bluetooth/utils.js";
          import printTemplateInfoBluetoothApi from './api/printTemplateInfoBluetooth.js';
          
          export default {
            // ... 其他配置
            methods: {
              /**
               * 使用后端模板进行渲染打印
               */
              async printWithBackendTemplate() {
                try {
                  // 1. 准备打印数据
                  const data = {
                    barcodeNo: this.barcodeNo,
                    partCode: this.partCode,
                    partName: this.partName,
                    qty: this.qty,
                    // ... 其他业务字段
                  };
                  
                  // 2. 渲染模板(template 从后端 API 获取)
                  const cpclText = renderCpclTemplate(this.printTemplate, data);
                  console.log('渲染后的 CPCL:', cpclText);
                  
                  // 3. 发送到打印机
                  await this.sppHelper.printCPCL(cpclText);
                  $toast.success("打印成功");
                  
                } catch (err) {
                  console.error("[your-page] print error :>> ", err);
                  $toast.toast("打印失败:" + (err.message || err));
                }
              },
              
              /**
               * 获取后端模板(通常在 created/mounted 中调用)
               */
              async getPrintTemplate() {
                try {
                  
                  const res = await printTemplateInfoBluetoothApi.getByCode("your-template-code");
                  if (res?.code === 0) {
                    this.printTemplate = res?.data?.content || "";
                  }
                } catch (err) {
                  console.error("getPrintTemplate err >>> ", err);
                  this.catchErr(err);
                }
              },
            }
          };
          
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25
          26
          27
          28
          29
          30
          31
          32
          33
          34
          35
          36
          37
          38
          39
          40
          41
          42
          43
          44
          45
          46
          47
          48
          49
          50
          51
          52

          # Demo 3: 使用页面级工具函数

          // 顶部导入依赖
          import { barcodePrintTemplate } from "./utils/print.js";
          
          export default {
            // ... 其他配置
            methods: {
              /**
               * 使用页面级工具函数打印
               */
              async printWithPageUtils() {
                try {
                  // 1. 准备打印数据
                  const data = {
                    barcodeNo: this.barcodeNo,
                    partCode: this.partCode,
                    partName: this.partName,
                  };
                  
                  // 2. 调用工具函数生成 CPCL 指令
                  const cpclText = barcodePrintTemplate(data);
                  
                  // 3. 发送到打印机
                  await this.sppHelper.printCPCL(cpclText);
                  $toast.success("打印成功");
                  
                } catch (err) {
                  console.error("[your-page] print error :>> ", err);
                  this.catchErr(err);
                }
              },
            }
          };
          
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25
          26
          27
          28
          29
          30
          31
          32

          # Demo 4: 获取模板 API 文件示例

          // api/printTemplateInfoBluetooth.js
          import request from "@/api/index";
          
          const basePath = "/sys/v1/print-template-info-bluetooth";
          
          export default {
            /**
             * 根据编码获取打印模板信息
             * @param {string} code 模板编码
             * @returns {Promise<{code: number, msg: string, data: {content: string}}>}
             */
            getByCode(code) {
              return request({
                url: `${basePath}/get-by-code?code=${code}`,
                method: "GET",
              });
            },
          };
          
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18

          # Demo 5: 页面级打印工具函数示例

          // utils/print.js
          import CPCLBuilder from "@/utils/bluetooth/builder/cpcl-builder.js";
          
          /**
           * 条码打印模板
           * @param {object} data 参数
           * @param {string} data.barcodeNo 条码
           * @param {string} data.partCode 物料编码
           * @param {string} data.partName 物料名称
           * @param {number} opts.height - 打印高度,默认420
           * @returns {string} CPCL 指令字符串
           */
          export function barcodePrintTemplate(data, opts) {
            const builder = new CPCLBuilder();
          
            const totalWidth = 520;
            const totalHeight = 350;
            const row1Height = 116;
          
            // 初始化命令(必须放在开头)
            builder.init(opts?.height || 420);
            // 方框
            builder.addBox(0, 0, totalWidth, totalHeight);
            
            // 添加内容
            builder.addText(`物料编码: ${data.partCode}`, 55, 0, 4, 4);
            builder.addText(`物料名称: ${data.partName}`, 55, 0, 260, 4);
            
            builder.setAlignment("center");
            builder.addBarcode("128", data.barcodeNo, 4, row1Height + 4);
            builder.addText(data.barcodeNo, 55, 4, row1Height + 50 + 8);
          
            // 打印指令(必须放在最后)
            builder.print();
          
            return builder.build();
          }
          
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25
          26
          27
          28
          29
          30
          31
          32
          33
          34
          35
          36
          37

          重要提示:所有导入语句(import)都应该放在 Vue 组件的顶部,而不是在方法内部。

          # 4. 离开页面时注销

          在页面的 $onUnload 生命周期中断开蓝牙连接:

          export default {
            name: "your-page",
            mixins: [mixin], // 必须引入 mixin
            methods: {
              /**
               * 页面卸载生命周期,同 uni-app 的 onUnload,兼容 H5 和 APK
               */
              $onUnload() {
                // 页面离开时断开 SPP 蓝牙连接,释放资源
                if (this.sppHelper) {
                  console.log("断开 SPP 蓝牙连接");
                  this.sppHelper.disconnect().catch((err) => {
                    console.error("SPP disconnect err :>> ", err);
                  });
                }
              },
            }
          }
          
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18

          # 常见问题处理

          # 1. 连接失败处理

          // 连接时的错误处理
          this.sppHelper.connectInBackground({
            timeout: 5000,
            onError: (err) => {
              // 可以提示用户或记录日志
              console.warn("连接打印机失败:", err.message);
              $toast.toast("打印机连接失败,请检查蓝牙和设备");
            },
            onSuccess: () => {
              console.log("打印机连接成功");
            },
          });
          
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12

          # 2. 打印失败处理

          try {
            await this.sppHelper.printCPCL(cpclText);
            $toast.success("打印成功");
          } catch (err) {
            console.error("打印失败:", err);
            
            // 根据错误类型给予用户提示
            if (err.message.includes("蓝牙未开启")) {
              $toast.toast("请开启手机蓝牙");
            } else if (err.message.includes("未配对")) {
              $toast.toast("打印机未配对,请先配对设备");
            } else if (err.message.includes("连接超时")) {
              $toast.toast("打印机连接超时,请检查设备是否开启");
            } else if (err.message.includes("断开连接")) {
              $toast.toast("打印机已断开连接");
            } else {
              $toast.toast("打印失败:" + err.message);
            }
          }
          
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19

          # 3. 重试机制

          /**
           * 带重试的打印方法
           * @param {string} cpclText CPCL 指令
           * @param {number} maxRetries 最大重试次数,默认 2
           */
          async printWithRetry(cpclText, maxRetries = 2) {
            let retries = 0;
            
            while (retries <= maxRetries) {
              try {
                await this.sppHelper.printCPCL(cpclText);
                return true; // 成功
              } catch (err) {
                retries++;
                console.warn(`打印失败,第 ${retries} 次重试:`, err.message);
                
                if (retries > maxRetries) {
                  throw err; // 超出重试次数,抛出错误
                }
                
                // 短暂延迟后重试
                await new Promise(resolve => setTimeout(resolve, 1000));
              }
            }
          }
          
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25

          # 开发注意事项

          1. 权限要求:确保 App 具有蓝牙权限
          2. 环境限制:蓝牙打印功能仅支持 APP-PLUS 环境(打包后的 APK)
          3. 默认设备:需要先通过系统设置添加并设置默认打印机
          4. 连接管理:遵循 "进入注册 → 使用时连接 → 离开注销" 的生命周期管理
          5. 错误处理:所有打印操作都应包含 try-catch 错误处理
          6. 性能优化:避免在页面进入时立即连接,使用 connectInBackground 后台连接
          7. 资源释放:页面离开时必须调用 disconnect() 释放蓝牙连接

          # 参考示例项目

          • LES条码拆分:src/pages/barcode/barcode-split/
          • LES标准拣货:src/pages/outbound/pick-goods/

          这两个项目展示了完整的蓝牙打印功能实现,包括模板管理、错误处理和生命周期管理。

          在线编辑 (opens new window)
          最近更新
          01
          交付人员二开
          09-16
          02
          业务集成二开
          09-16
          03
          事务集成
          07-09
          更多文章>
          Theme by Vdoing | Copyright © 2023-2026 SIE
          • 跟随系统
          • 浅色模式
          • 深色模式
          • 阅读模式