多平台打印

8 阅读3分钟

数据格式示例

// 抖音打印
let request1 = {
    cmd: "print",
    requestID: `dy-print-${Date.now()}${i}`,
    version: "1.0",
    task: {
      taskID: `dy-print-${Date.now()}${i}`,
      printer: form.expressPrinter,
      preview: false,
      documents: [
        {
          docNo: element.trackNo, //实际打印业务的单号
          documentID: (
            Math.floor(Math.random() * (999999999 - 100000000 + 1)) + 100000000
          ).toString(), //面单号,可选
          copy: 1, //打印两份,默认1份
          contents: [
            {
              params: res.params,
              signature: element.sign,
              encryptedData: element.printData, //实体打印机打印后是明文,虚拟打印机打印后是收件人脱敏信息
              templateURL: res.templateURL,
            },
            {
              //自定义区模板,如果没有,就不要传此对象
              data: res.data,
              templateURL: res.diyTemplateURL,
            },
          ],
        },
      ],
      config: {
        paperSize: {
          width: form.templateDiy == 0 ? 76 : form.width,
          height: form.templateDiy == 0 ? 130 : form.height,
        },
      },
    },
  };

// 拼多多打印
let payload = {
  cmd: "print",
  requestID: `pdd-print-${Date.now()}${i}`,
  version: "1.0",
  task: {
    documents: [
      {
        contents: [
          {
            encryptedData: data.encryptedData,
            signature: data.signature,
            templateUrl: data.templateUrl,
            userid: res.shopId,
            ver: data.ver,
          },
          // 如果是用系统模版的话这下面的对象可以不用
          {
            data: res.data,
            templateURL: res.diyTemplateURL,
          },
        ],
        documentID: `${Date.now()}${i}`,
      },
    ],
    printer: form.expressPrinter,
    taskID: `pdd-print-${Date.now()}${i}`,
    // preview: true,
    // previewType: "image",
  },
};

// 京东打印
payload = {
  orderType: "PRINT",
  key: `jd-print-${Date.now()}`,
  parameters: {
    printName: form.expressPrinter,
    printData: [data.perPrintData],
    // offsetTop: "10mm",
    // offsetLeft: "10mm",
    tempUrl: res.templateURL,
    // 如果是用系统模版的话这下面的customTempUrl可以不用
    customTempUrl: res.diyTemplateURL,
    customData: [
      {
        ...res.data,
      },
    ],
  },
};

// 淘宝打印
payload = {
  cmd: "print",
  requestID: `tb-print-${Date.now()}${i}`,
  version: "1.0",
  task: {
    documents: [
      {
        contents: [
          {
            encryptedData: data.encryptedData,
            signature: data.signature,
            templateURL: data.templateURL,
            userid: res.shopId,
            ver: data.ver,
          },
          // 如果是用系统模版的话这下面的对象可以不用,下面是自定义的模版
          {
            data: res.data,
            templateURL: res.diyTemplateURL,
          },
        ],
        documentID: `${Date.now()}${i}`,
      },
    ],
    printer: form.expressPrinter,
    taskID: `tb-print-${Date.now()}${i}`,
  },
};

打印代码

/**
 * 四平台WebSocket打印工具
 *
 * 平台地址:
 * - 抖音(DY): ws://127.0.0.1:13888
 * - 拼多多(PDD): ws://127.0.0.1:5000
 * - 京东(JD): ws://127.0.0.1:9113
 * - 淘宝(TB): ws://127.0.0.1:13528
 *
 * ===================== 使用示例 =====================
 *
 * // 1. 连接抖音打印组件
 * import { dyPrint } from '@/utils/printUtils'
 * await dyPrint.connect()
 *
 * // 2. 获取打印机列表
 * const printers = dyPrint.getPrinters()
 *
 * // 3. 监听打印结果
 * dyPrint.on('print:success', (data) => {
 *   console.log('打印成功', data)
 * })
 *
 * // 4. 发送打印请求
 * dyPrint.print({ ... })
 *
 * // 5. 同时连接所有平台
 * import { connectAll } from '@/utils/printUtils'
 * await connectAll()
 * ===================================================
 */

import { ElNotification } from "element-plus";

// 平台配置
const PLATFORM_URLS = {
  DY: "ws://127.0.0.1:13888", // 抖音
  PDD: "ws://127.0.0.1:5000", // 拼多多
  JD: "ws://127.0.0.1:9113", // 京东
  TB: "ws://127.0.0.1:13528", // 淘宝
};

const PLATFORM_NAMES = {
  DY: "抖音",
  PDD: "拼多多",
  JD: "京东",
  TB: "淘宝",
};

type Platform = "DY" | "PDD" | "JD" | "TB";

// 存储四个平台的WebSocket连接
const sockets: Record<Platform, WebSocket | null> = {
  DY: null,
  PDD: null,
  JD: null,
  TB: null,
};

// 存储打印机列表
const printers: Record<Platform, any[]> = {
  DY: [],
  PDD: [],
  JD: [],
  TB: [],
};

// 存储事件回调
const callbacks: Record<Platform, Record<string, ((data: any) => void)[]>> = {
  DY: {},
  PDD: {},
  JD: {},
  TB: {},
};

// 重连定时器
const reconnectTimers: Record<Platform, any> = {
  DY: null,
  PDD: null,
  JD: null,
  TB: null,
};

// 心跳定时器
const heartbeatTimers: Record<Platform, any> = {
  DY: null,
  PDD: null,
  JD: null,
  TB: null,
};

// 错误提示是否已显示
const errorShown: Record<Platform, boolean> = {
  DY: false,
  PDD: false,
  JD: false,
  TB: false,
};

/**
 * 连接指定平台的WebSocket
 */
function connect(platform: Platform): Promise<void> {
  return new Promise((resolve, reject) => {
    const url = PLATFORM_URLS[platform];

    // 已连接则直接返回
    if (sockets[platform]?.readyState === WebSocket.OPEN) {
      resolve();
      return;
    }

    try {
      const socket = new WebSocket(url);
      sockets[platform] = socket;

      // 连接成功
      socket.onopen = () => {
        console.log(`[${platform}] 连接成功`);
        errorShown[platform] = false;
        startHeartbeat(platform);
        // 请求打印机列表
        requestPrinters(platform);
        trigger(platform, "connected", true);
        resolve();
      };

      // 接收消息
      socket.onmessage = (event) => {
        try {
          const data = JSON.parse(event.data);
          handleMessage(platform, data);
        } catch (e) {
          console.error(`[${platform}] 解析消息失败`, e);
        }
      };

      // 连接错误
      socket.onerror = () => {
        console.error(`[${platform}] 连接错误`);
        showError(platform);
        trigger(platform, "connected", false);
        reject(new Error("连接失败"));
      };

      // 连接关闭
      socket.onclose = () => {
        console.log(`[${platform}] 连接关闭`);
        stopHeartbeat(platform);
        trigger(platform, "connected", false);
        // 3秒后自动重连
        scheduleReconnect(platform);
      };
    } catch (error) {
      reject(error);
    }
  });
}

/**
 * 断开连接
 */
function disconnect(platform: Platform) {
  stopHeartbeat(platform);
  clearTimeout(reconnectTimers[platform]);
  if (sockets[platform]) {
    sockets[platform]!.close();
    sockets[platform] = null;
  }
}

/**
 * 发送消息
 */
function send(platform: Platform, data: any) {
  const socket = sockets[platform];
  if (socket?.readyState === WebSocket.OPEN) {
    socket.send(JSON.stringify(data));
  }
}

/**
 * 处理消息
 */
function handleMessage(platform: Platform, data: any) {
  trigger(platform, "message", data);

  // 打印机列表
  if (data.cmd === "getPrinters" || data.message === "get printers success") {
    if (data.printers?.length > 0) {
      printers[platform] = data.printers;
      trigger(platform, "printers", data.printers);
    }
    // JD特殊格式
    if (data.detailinfo?.printers?.length > 0) {
      printers[platform] = data.detailinfo.printers.map((p: string) => ({ name: p }));
      trigger(platform, "printers", printers[platform]);
    }
  }

  // 打印成功
  if (data.cmd === "notifyPrintResult" && data.status === "success") {
    trigger(platform, "print:success", data);
  }

  // 打印失败
  if (data.cmd === "notifyPrintResult" && data.status === "failed") {
    trigger(platform, "print:failed", data);
  }

  // 正在打印
  if (data.cmd === "print" && data.status === "success") {
    trigger(platform, "print:processing", data);
  }
}

/**
 * 请求打印机列表
 */
function requestPrinters(platform: Platform) {
  if (platform === "JD") {
    send(platform, { orderType: "GET_Printers" });
  } else {
    send(platform, {
      requestID: `${Date.now()}`,
      version: "1.0",
      cmd: "getPrinters",
    });
  }
}

/**
 * 启动心跳
 */
function startHeartbeat(platform: Platform) {
  stopHeartbeat(platform);
  heartbeatTimers[platform] = setInterval(() => {
    send(platform, { type: "heartbeat" });
  }, 30000);
}

/**
 * 停止心跳
 */
function stopHeartbeat(platform: Platform) {
  if (heartbeatTimers[platform]) {
    clearInterval(heartbeatTimers[platform]);
    heartbeatTimers[platform] = null;
  }
}

/**
 * 安排重连
 */
function scheduleReconnect(platform: Platform) {
  clearTimeout(reconnectTimers[platform]);
  reconnectTimers[platform] = setTimeout(() => {
    connect(platform).catch(() => {});
  }, 3000);
}

/**
 * 显示错误提示
 */
function showError(platform: Platform) {
  if (errorShown[platform]) return;
  errorShown[platform] = true;

  ElNotification({
    title: `${PLATFORM_NAMES[platform]}打印组件连接错误`,
    dangerouslyUseHTMLString: true,
    message: `
      <div>未检测到${PLATFORM_NAMES[platform]}打印组件,请检查是否已打开。</div>
      <div style="margin-top: 10px;">
        <div>1. 已安装并启动:请刷新页面后重试</div>
        <div>2. 未安装:请下载并安装${PLATFORM_NAMES[platform]}打印组件</div>
        <div>3. 连接地址:${PLATFORM_URLS[platform]}</div>
      </div>
    `,
    type: "error",
    duration: 5000,
    onClose: () => {
      errorShown[platform] = false;
    },
  });
}

/**
 * 触发事件
 */
function trigger(platform: Platform, event: string, data?: any) {
  const cbs = callbacks[platform][event] || [];
  cbs.forEach((cb) => cb(data));
}

/**
 * 注册事件
 */
function on(platform: Platform, event: string, cb: (data: any) => void) {
  if (!callbacks[platform][event]) {
    callbacks[platform][event] = [];
  }
  callbacks[platform][event].push(cb);
}

/**
 * 移除事件
 */
function off(platform: Platform, event: string, cb?: (data: any) => void) {
  if (!callbacks[platform][event]) return;
  if (cb) {
    const idx = callbacks[platform][event].indexOf(cb);
    if (idx > -1) callbacks[platform][event].splice(idx, 1);
  } else {
    callbacks[platform][event] = [];
  }
}

/**
 * 获取连接状态
 */
function isConnected(platform: Platform): boolean {
  return sockets[platform]?.readyState === WebSocket.OPEN;
}

/**
 * 获取打印机列表
 */
function getPrinters(platform: Platform): any[] {
  return printers[platform];
}

/**
 * 打印
 */
function print(platform: Platform, request: any) {
  send(platform, request);
}

// ========== 导出便捷方法 ==========

// 抖音打印
export const dyPrint = {
  connect: () => connect("DY"),
  disconnect: () => disconnect("DY"),
  isConnected: () => isConnected("DY"),
  getPrinters: () => getPrinters("DY"),
  print: (req: any) => print("DY", req),
  on: (event: string, cb: (data: any) => void) => on("DY", event, cb),
  off: (event: string, cb?: (data: any) => void) => off("DY", event, cb),
};

// 拼多多打印
export const pddPrint = {
  connect: () => connect("PDD"),
  disconnect: () => disconnect("PDD"),
  isConnected: () => isConnected("PDD"),
  getPrinters: () => getPrinters("PDD"),
  print: (req: any) => print("PDD", req),
  on: (event: string, cb: (data: any) => void) => on("PDD", event, cb),
  off: (event: string, cb?: (data: any) => void) => off("PDD", event, cb),
};

// 京东打印
export const jdPrint = {
  connect: () => connect("JD"),
  disconnect: () => disconnect("JD"),
  isConnected: () => isConnected("JD"),
  getPrinters: () => getPrinters("JD"),
  print: (req: any) => print("JD", req),
  on: (event: string, cb: (data: any) => void) => on("JD", event, cb),
  off: (event: string, cb?: (data: any) => void) => off("JD", event, cb),
};

// 淘宝打印
export const tbPrint = {
  connect: () => connect("TB"),
  disconnect: () => disconnect("TB"),
  isConnected: () => isConnected("TB"),
  getPrinters: () => getPrinters("TB"),
  print: (req: any) => print("TB", req),
  on: (event: string, cb: (data: any) => void) => on("TB", event, cb),
  off: (event: string, cb?: (data: any) => void) => off("TB", event, cb),
};

/**
 * 同时连接所有平台
 */
export async function connectAll() {
  const results = await Promise.allSettled([
    connect("DY"),
    connect("PDD"),
    connect("JD"),
    connect("TB"),
  ]);
  return {
    DY: results[0].status === "fulfilled",
    PDD: results[1].status === "fulfilled",
    JD: results[2].status === "fulfilled",
    TB: results[3].status === "fulfilled",
  };
}

/**
 * 断开所有连接
 */
export function disconnectAll() {
  disconnect("DY");
  disconnect("PDD");
  disconnect("JD");
  disconnect("TB");
}