使

使用Vue调用TSC打印机实现打印标签

漫步杂记 4 月前

在我的项目中,我最初使用 Java 调用 TSC 打印机打印标签,但由于后端程序部署在阿里云的云服务器,无法与本地的 TSC 打印机建立连接,因此决定切换到使用前端 Vue 实现打印功能。以下是一个详细的步骤指南,帮助你在 Vue 项目中实现 TSC 打印机的调用。

要在 Vue 项目中实现与 TSC 打印机的联调,可以借助 TSC PrintServer 软件。TSC PrintServer 是一个中间件,可以将打印机连接到网络,通过 HTTP WebSocket 请求发送打印命令。

TSC 打印机 厂家官网软件下载地址:资料下载 | TSC Printers (chinatsc.cn)

直达下载地址:https://fs.chinatsc.cn/system/files/tsc-printserver_setup_v1006.zip

  1. 下载 TSC PrintServer
    • 下载并安装 TSC PrintServer 软件后,找到安装目录中的 Example 文件夹。
  2. 拷贝文件
    • 将 Example\assert\js 文件夹下的 base64.js 和 TSC.js 文件拷贝到 Vue 项目的 src/utils/print/ 文件夹中。

将TSC.js文件略微调整

import Base64 from "@/utils/print/base64";
var functions_inorder = [];
//======================================================================================================================
//@description	清空数据
function init() {
  functions_inorder = [];
}
//======================================================================================================================
//@description	连接打印机USB
//@param {String} usbPrinter:	usb路徑(USBPath),若参数为空字串{"openport_usb" :""}则自动连接找到的第一台打印机
function openport_usb(usbPrinter) {
  functions_inorder.push({ openport_usb: usbPrinter });
}
//======================================================================================================================
//@description  连接打印机网络
//@param {String} ip:	打印机IP地址
//@param {String} port:	打印机网络端口
function openport_net(ip, port) {
  functions_inorder.push({ openport_net: ip + "," + port });
}
//======================================================================================================================
//@description  中断打印机连线
function closeport() {
  functions_inorder.push({ closeport: "" });
}
//======================================================================================================================
//@description	清除打印机Buffer
function clearbuffer() {
  functions_inorder.push({ clearbuffer: "" });
}
//======================================================================================================================
//@description	设定标签资讯
//@param {String} width_mm:		标签宽度(单位mm)
//@param {String} height_mm:	标签高度(单位mm)
//@param {String} speed:		打印速度
//@param {String} density:		打印浓度
//@param {String} sensor:		標籤種類["0":GAP, "1":BLINE]
//@param {String} gap_height:	间隙高度(单位mm)
//@param {String} gap_offset:	间隙偏移(单位mm)
function setup(width_mm, height_mm, speed, density, sensor, gap_height, gap_offset) {
  functions_inorder.push({
    setup:
      width_mm +
      "," +
      height_mm +
      "," +
      speed +
      "," +
      density +
      "," +
      sensor +
      "," +
      gap_height +
      "," +
      gap_offset,
  });
}
//======================================================================================================================
//@description	打印条码
//@param {String} x:			条码x轴座标(单位dot)
//@param {String} y:			条码y轴座标(单位dot)
//@param {String} type:			条码类型["128", "128M", "EAN128", "25", "25C", "39", "39C", "93", "EAN13", "EAN13+2", "EAN13+5", "EAN8", "EAN8+2", "EAN8+5", "CODA", "POST", "UPCA", "UPCA+2", "UPCA+5", "UPCE", "UPCE+2", "UPCE+5", "CPOST", "MSI", "MSIC", "PLESSEY", "ITF14", "EAN14", "11", "TELEPEN", "TELEPENN", "PLANET", "CODE49", "DPI", "DPL"]
//@param {String} height:		条码高度
//@param {String} readable:		条码可读性["0":无, "1":置左, "2":置中, "3":置右]
//@param {String} rotation:		条码旋转["0":无, "90":90度, "180":180度, "270":270度]
//@param {String} narrow:		条码窄元件宽度[详见TSPL手册BARCODE条目]
//@param {String} wide:			条码宽元件宽度[详见TSPL手册BARCODE条目]
//@param {String} code:			条码内容
function barcode(x, y, type, height, readable, rotation, narrow, wide, code) {
  if (code.charAt(0) == "@")
    functions_inorder.push({
      barcode:
        x +
        "," +
        y +
        ',"' +
        type +
        '",' +
        height +
        "," +
        readable +
        "," +
        rotation +
        "," +
        narrow +
        "," +
        wide +
        "," +
        code,
    });
  else
    functions_inorder.push({
      barcode:
        x +
        "," +
        y +
        ',"' +
        type +
        '",' +
        height +
        "," +
        readable +
        "," +
        rotation +
        "," +
        narrow +
        "," +
        wide +
        ',"' +
        code +
        '"',
    });
}
//======================================================================================================================
//@description	打印文字
//@param {String} x:		文字x轴座标(单位dot)
//@param {String} y:		文字y轴座标(单位dot)
//@param {String} fonttype:	内建字型名称["0", "1", "2", "3", "4", "5", "6", "7", "8", "ROMAN.TTF"] 或 下载字型名称
//@param {String} rotation:	文字旋转["0":无, "90":90度, "180":180度, "270":270度]
//@param {String} xmul:		文字x轴放大率
//@param {String} ymul:		文字y轴放大率
//@param {String} text:		打印文字
function printerfont(x, y, fonttype, rotation, xmul, ymul, text) {
  if (text.charAt(0) == "@")
    functions_inorder.push({
      printerfont:
        x +
        "," +
        y +
        ',"' +
        fonttype +
        '",' +
        rotation +
        "," +
        xmul +
        "," +
        ymul +
        "," +
        text,
    });
  else
    functions_inorder.push({
      printerfont:
        x +
        "," +
        y +
        ',"' +
        fonttype +
        '",' +
        rotation +
        "," +
        xmul +
        "," +
        ymul +
        ',"' +
        text +
        '"',
    });
}
//======================================================================================================================
//@description	发送指令到打印机
//@param {String} cmd:	指令
function sendcommand(cmd) {
  functions_inorder.push({ sendcommand: cmd });
}
//======================================================================================================================
//@description	发送指令到打印机,加上换行符号(\r\n)
//@param {String} cmd:	指令
function sendcommand_crlf(cmd) {
  functions_inorder.push({ sendcommand_crlf: cmd });
}
//======================================================================================================================
//@description	打印标签
//@param {String} sets:		份数
//@param {String} copies:	张数
function printlabel(sets, copies) {
  functions_inorder.push({ printlabel: sets + "," + copies });
  return functions_inorder;
}
//======================================================================================================================
//@description	下载PCX
//@param {String} file_path:	档案路径
//@param {String} image_name:	保存名称
function downloadpcx(file_path, image_name) {
  functions_inorder.push({ downloadpcx: file_path + "," + image_name });
}
//======================================================================================================================
//@description	跳过下一张标签
function formfeed() {
  functions_inorder.push({ formfeed: "" });
}
//======================================================================================================================
//@description	关闭backfeed功能
function nobackfeed() {
  functions_inorder.push({ nobackfeed: "" });
}
//======================================================================================================================
//@description	打印文字
//@param {String} x:				文字x轴座标(单位dot)
//@param {String} y:				文字y轴座标(单位dot)
//@param {String} fontheight:		文字大小
//@param {String} rotation:			文字旋转["0":无, "90":90度, "180":180度, "270":270度]
//@param {String} fontstyle:		文字样式["0":无, "1":斜体, "2":粗体, "3":斜体+粗体]
//@param {String} fontunderline:	文字底线["0":无, "1":底线]
//@param {String} szFaceName:		字型名称[例:"Arial"]
//@param {String} text:				打印文字
function windowsfont(x, y, fontheight, rotation, fontstyle, fontunderline, szFaceName, text){
  functions_inorder.push({
    windowsfont:
      x +
      "," +
      y +
      "," +
      fontheight +
      "," +
      rotation +
      "," +
      fontstyle +
      "," +
      fontunderline +
      "," +
      szFaceName +
      "," +
      text,
  });
}
//======================================================================================================================
//@description	发送位元组数据到打印机
//@param {String} cmd:	指令
function sendUint8Array(uint8_arr) {
  functions_inorder.push({ sendUint8Array: Base64.fromUint8Array(uint8_arr) });
  // console.log("uint8_arr", uint8_arr);
  // console.log("functions_inorder", functions_inorder);
}
//======================================================================================================================
//@description	返回打印机状态
function printerstatus() {
  functions_inorder.push({ printerstatus: "" });
}
//======================================================================================================================
//@description	返回打印机名称
function printername() {
  functions_inorder.push({ printername: "" });
}
//======================================================================================================================
//@description	返回打印机序号
function printerserial() {
  functions_inorder.push({ printerserial: "" });
}
export default {
  printerserial,
  printername,
  printerstatus,
  sendUint8Array,
  windowsfont,
  functions_inorder,
  nobackfeed,
  formfeed,
  barcode,
  downloadpcx,
  printlabel,
  sendcommand_crlf,
  sendcommand,
  printerfont,
  setup,
  clearbuffer,
  closeport,
  openport_net,
  openport_usb,
  init,
};

接下来在业务界面index.vue调用打印机代码,我是根据选中的id调用了后端的查询单据详情接口,单个或批量的查询选中的数据,在成功返回的方法里面循环遍历结果数据,并调用PrintCode(this.usbPath, x);方法实现标签打印。

<script>
import printApi from "@/utils/print/TSC";
var websocket;
export default {
  data() {
    return {
      usbPath: {},
    };
  },
  created() {
    this.getList();
  },
  methods: {
    /** 打印按钮操作 */
    handlePrint(row) {
      this.webSocketConnect()
        .then((res) => {
          this.usbPath.usb_printers = res;
          const ids = row.id || this.ids;
          getPrintInfo(ids).then((response) => {
            response.rows.forEach((x, y) => {
              this.PrintCode(this.usbPath, x);
            });
          });
          this.$modal.msgSuccess("执行打印了");
        })
        .catch((err) => {
          this.$modal.msgError("获取打印机失败");
        });
    },
    PrintCode(usbPath, obj) {
      const u8 = new Uint8Array(2);
      u8[0] = 13;
      u8[1] = 10;
      printApi.init();
      // 需要携带打印参数(打印机路径)
      printApi.openport_usb(usbPath.usb_printers);
      printApi.sendUint8Array(u8);
      printApi.clearbuffer();
      //--------------------------
      //xxxxx这里就是标签内容书写区域
      //--------------------------
      printApi.setup("50", "30", "5", "15", "0", "22", "0");
      printApi.sendcommand_crlf('QRCODE 345,35,Q,5,A,0,M2,S7,"https://shanghai.chinaxxxxxx.com/jishu_index.html?id=' + obj.id + '"');
      printApi.printerfont("270","250","MSYH.TTF","180","7","7","" + obj.center + "");
      printApi.printerfont("250", "210", "MSYH.TTF", "180", "7", "7", "待检");
      printApi.printerfont("170", "210", "MSYH.TTF", "180", "7", "7", "在检");
      printApi.printerfont("90", "210", "MSYH.TTF", "180", "7", "7", "已检");
      printApi.sendcommand_crlf("BOX 250,170,190,130,2");
      printApi.sendcommand_crlf("BOX 170,170,110,130,2");
      printApi.sendcommand_crlf("BOX 90,170,30,130,2");
      printApi.printerfont("230", "160", "MSYH.TTF", "180", "7", "7", "√");
      printApi.barcode("330","50","128","50","0","90","1","1","" + obj.center + "");
      printApi.sendcommand_crlf('PUTBMP 40,40,"LOGO.BMP"');

      //@description	打印标签
      //@param {String} sets:		份数
      //@param {String} copies:	张数
      // 抛出打印的参数
      let printData = printApi.printlabel(1, 1);
      // console.log("printData", printData);
      let data = {};
      data.functions_inorder = printData;
      this.webSocketSend(data);
      // return printData;
    },
    /**连接*/
    webSocketConnect() {
      return new Promise((resolve, reject) => {
        websocket = new WebSocket("ws://127.0.0.1:8888");
        websocket.onopen = function (e) {
          //加载时获取 usb 列表
          var obj = { usb_list: [] };
          websocket.send(JSON.stringify(obj));
        };
        websocket.onmessage = (event) => {
          if (event.data === "Finished") {
          } else {
            try {
              const obj = JSON.parse(event.data);
              if ("usb_list" in obj) {
                if (obj.usb_list.length === 0) {
                  this.$modal.msgError("找不到打印机");
                } else {
                  resolve(obj.usb_list[0].USBPath);
                }
              }
              if ("Function_Failed" in obj) {
                alert(`Failed: ${obj.Function_Failed}`);
              }
              if ("printerstatus" in obj) {
                if (obj.printerstatus.length != 0) {
                  alert(`Printer Status:\r\n${obj.printerstatus.join("\r\n")}`);
                }
              }
              if ("printername" in obj) {
                alert(`Printer Name: ${obj.printername}\r\n`);
              }
              if ("printerserial" in obj) {
                alert(`Printer Serial: ${obj.printerserial}\r\n`);
              }
              if ("about" in obj) {
                alert(`TSC PrintServer\r\nVersion: ${obj.about}\r\n`);
              }
            } catch (e) {
              reject(new Error(`Invalid return value--${e}`));
            }
          }
        };
        websocket.onclose = function (event) {
          if (event.code != 1000) {
            var reason = "";
            if (event.code == 1001)
              reason = "端点正在“消失”,例如服务器出现故障或浏览器已离开页面";
            else if (event.code == 1002)
              reason = "终结点由于协议错误而终止连接";
            else if (event.code == 1003)
              reason =
                "端点正在终止连接,因为它收到了一种它无法接受的数据类型(例如,如果端点收到二进制消息,则仅理解文本数据可能会发送此数据)。";
            else if (event.code == 1004)
              reason = "保留。具体含义可能会在将来定义";
            else if (event.code == 1005) reason = "实际上不存在状态代码";
            else if (event.code == 1006)
              reason = "连接异常,例如,没有发送或接收 Close 控制帧";
            else if (event.code == 1007)
              reason =
                "终结点正在终止连接,因为它在消息中接收到的数据与消息类型不一致(例如,文本消息中的非 UTF-8 [https://www.rfc-editor.org/rfc/rfc3629] 数据)";
            else if (event.code == 1008)
              reason =
                "终结点正在终止连接,因为它收到了“违反其策略”的消息。如果没有其他合理的理由,或者需要隐藏有关策略的具体细节,则会给出此原因";
            else if (event.code == 1009)
              reason = "终结点正在终止连接,因为它收到的消息太大而无法处理";
            else if (event.code == 1010)
              reason =
                "客户端)正在终止连接,因为它期望服务器协商一个或多个扩展,但服务器未在 WebSocket 握手的响应消息中返回它们。<br /> 具体来说,需要的扩展是:" +
                event.reason;
            else if (event.code == 1011)
              reason =
                "服务器正在终止连接,因为它遇到了阻止其完成请求的意外情况。";
            else if (event.code == 1015)
              reason =
                "由于无法执行 TLS 握手(例如,无法验证服务器证书),连接已关闭";
            else reason = "未知原因";
            reject(
              new Error(`WebSocket closed with code ${event.code}: ${reason}`)
            );
          } else {
            resolve();
          }
        };

        websocket.onerror = function (error) {
          reject(new Error(`WebSocket error: ${error}`));
        };
      });
    },
    /**websocket的close事件*/
    webSocketClose() {
      websocket.close();
    },
    /**websocket发送消息*/
    webSocketSend(obj) {
      websocket.send(JSON.stringify(obj));
    },
  },
};
</script>

最后双击启动TSC PrintServer.exe程序,本地打开WebSocket监听,在页面点击打印按钮,就可以打印标签了。

评论(0)

发布评论

相关文章