npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vue3-clodop

v0.1.0

Published

C-Lodop 云打印 Vue 3 插件 - 提供类型安全的响应式打印能力

Readme

vue3-clodop

C-Lodop 云打印 Vue 3 插件 — 类型安全的 Builder 模式封装,提供响应式组合式 API。

目录


安装

npm install vue3-clodop

需要本机安装并启动 C-Lodop 云打印服务(默认端口 8000 / 18000)。

快速开始

1. 注册插件

// main.ts
import { createApp } from 'vue';
import App from './App.vue';
import { CLodopPlugin } from 'vue3-clodop';

const app = createApp(App);

app.use(CLodopPlugin, {
  timeout: 10000,
  debug: true,
  // license: { companyName: '', license: '' },
});

app.mount('#app');

2. 在组件中使用

<script setup lang="ts">
import { useCLodop, Orientation, BarcodeType } from 'vue3-clodop';

const { printer, isReady, isLoading, error, version, reload } = useCLodop();

function doPrint() {
  if (!printer.value) return;
  printer.value
    .init('订单打印')
    .setPageSize({ orientation: Orientation.Portrait, width: 2100, height: 2970, name: 'A4' })
    .addText({ top: 50, left: 50, width: 500, height: 40, content: '你好,世界' })
    .setItemStyle(0, 'FontSize', 18)
    .addBarcode({ top: 120, left: 50, width: 200, height: 200, type: BarcodeType.QRCode, value: 'https://vuejs.org' })
    .preview();
}
</script>

<template>
  <div v-if="isLoading">正在连接打印服务...</div>
  <div v-else-if="error">{{ error.message }}</div>
  <button v-else :disabled="!isReady" @click="doPrint">打印预览</button>
</template>

插件配置

CLodopPluginoptions 参数类型为 CLodopPluginOptions

| 属性 | 类型 | 默认值 | 说明 | |------|------|--------|------| | scriptUrls | string[] | HTTP: localhost:8000/18000HTTPS: localhost:8443/18443 | C-Lodop 脚本地址列表,按优先级降级 | | timeout | number | 10000 | 脚本加载超时(毫秒) | | autoLoad | boolean | true | 是否在插件安装时自动加载脚本 | | license | LicenseOptions | — | 许可证配置 | | onReady | (clodop) => void | — | 加载成功回调 | | onError | (error) => void | — | 加载失败回调 | | debug | boolean | false | 启用调试日志 |


组合式函数 useCLodop

import { useCLodop } from 'vue3-clodop';

const {
  printer,     // ShallowRef<CLodopPrinter | null>
  rawCLodop,   // ShallowRef<CLodopRaw | null>
  status,      // Ref<CLodopStatus>
  isReady,     // Ref<boolean>
  isLoading,   // Ref<boolean>
  error,       // Ref<Error | null>
  version,     // Ref<string>
  reload,      // () => Promise<void>
} = useCLodop(options?);

返回值

| 属性 | 类型 | 说明 | |------|------|------| | printer | ShallowRef<CLodopPrinter \| null> | 打印类实例(使用 shallowRef 避免 Proxy 干扰 CLODOP 内部序列化) | | rawCLodop | ShallowRef<CLodopRaw \| null> | 原始 CLODOP 对象 | | status | Ref<CLodopStatus> | 连接状态:idle / loading / ready / error | | isReady | Ref<boolean> | 是否已就绪 | | isLoading | Ref<boolean> | 是否正在加载 | | error | Ref<Error \| null> | 错误信息 | | version | Ref<string> | C-Lodop 版本号 | | reload | () => Promise<void> | 手动重新加载 |

局部选项(覆盖插件配置)

| 属性 | 类型 | 说明 | |------|------|------| | scriptUrls | string[] | 自定义脚本 URL | | timeout | number | 超时时间 | | license | LicenseOptions | 许可证 | | autoLoad | boolean | 是否自动加载(默认 true) | | debug | boolean | 调试日志 |


CLodopPrinter API

所有返回 this 的方法支持链式调用(Builder 模式)。

属性访问

| 属性 | 类型 | 对应原始 API | 说明 | |------|------|-------------|------| | raw | CLodopRaw | — | 获取原始 CLODOP 实例 | | version | string | CLODOP.VERSION | C-Lodop 版本号 | | cVersion | string | CLODOP.CVERSION | C-Lodop 扩展版本号 | | lastResult | any | CLODOP.Result | 最后一次操作结果 | | lastItemIndex | number | — | 最后添加的打印项序号(从 1 开始) |

连接状态

| 属性/方法 | 类型 | 对应原始 API | 说明 | |-----------|------|-------------|------| | isSocketOpened | boolean (getter) | CLODOP.SocketOpened | WebSocket 是否已连接 | | isSocketEnabled | boolean (getter) | CLODOP.SocketEnable | WebSocket 是否启用 | | isLocal | boolean (getter) | CLODOP.blIslocal | 是否本地模式 | | isBusy | boolean (getter) | CLODOP.blWorking | 是否正在执行任务 | | hostURI | string (getter) | CLODOP.strHostURI | 服务地址 | | isIdle() | boolean | !CLODOP.blWorking | 是否空闲 | | reconnectWebSocket() | void | CLODOP.OpenWebSocket() | 手动触发 WebSocket 重连 |

打印机管理

| 方法 | 返回值 | 对应原始 API | 说明 | |------|--------|-------------|------| | getPrinterCount() | number | GET_PRINTER_COUNT() | 打印机数量 | | getPrinterName(index) | string | GET_PRINTER_NAME(index) | 按序号获取打印机名称 | | getPrinterProperty(index, property) | string | GET_PRINTER_NAME('index:property') | 获取打印机指定属性 | | getPrinters() | PrinterInfo[] | CLODOP.Printers | 所有打印机详情列表 | | getDefaultPrinter() | PrinterInfo \| undefined | CLODOP.Printers | 默认打印机信息 | | findPrinter(keyword) | PrinterInfo \| undefined | CLODOP.Printers | 按名称模糊搜索打印机 | | getPageSizes(printerIndex?) | string[] | GET_PAGESIZES_LIST() | 获取指定打印机支持的纸张列表 | | fillPrinterSelect(element) | this | Create_Printer_List() | 将打印机填充到 <select> | | fillPageSizeSelect(element, printerIndex?) | this | Create_PageSize_List() | 将纸张列表填充到 <select> |

初始化

| 方法 | 返回值 | 对应原始 API | 说明 | |------|--------|-------------|------| | init(taskName?) | this | PRINT_INIT(taskName) | 初始化打印任务 | | initWithMargin(top, left, width, height, taskName?) | this | PRINT_INITA(top, left, width, height, taskName) | 初始化打印任务(带纸张边距) |

页面设置

| 方法 | 返回值 | 对应原始 API | 说明 | |------|--------|-------------|------| | setPageSize(options) | this | SET_PRINT_PAGESIZE(orient, width, height, name) | 设置纸张大小 | | setPrinter(nameOrIndex) | this | SET_PRINTER_INDEX(nameOrIndex) | 设置当前打印机 | | setFallbackPrinter(nameOrIndex) | this | SET_PRINTER_INDEXA(nameOrIndex) | 设置备用打印机 | | setCopies(copies) | this | SET_PRINT_COPIES(copies) | 设置打印份数 | | setLicenses(options) | this | SET_LICENSES(company, license, licenseA, licenseB) | 设置许可证 |

添加打印项

每次调用 add* 方法后 lastItemIndex 自动 +1,可立即用 setItemStyle(0, ...) 设置当前项样式。

| 方法 | 参数类型 | 对应原始 API | 说明 | |------|---------|-------------|------| | addText(options) | TextItemOptions | ADD_PRINT_TEXT / ADD_PRINT_TEXTA | 添加文本(有 name 时用 A 版本) | | addHtml(options) | HtmlItemOptions | ADD_PRINT_HTML / ADD_PRINT_HTMLA | 添加 HTML(自动分页) | | addHtm(options) | HtmlItemOptions | ADD_PRINT_HTM | 添加 HTML(不自动分页,超出截断) | | addImage(options) | ImageItemOptions | ADD_PRINT_IMAGE | 添加图片(自动识别 URL / Base64 / HTML / 本地路径) | | addImageFromUrl(url, rect, fetchOptions?) | string, Rect, RequestInit? | fetch + ADD_PRINT_IMAGE | 下载远程图片转 data URI 后添加(异步,解决跨域) | | addImageFromFile(file, rect) | File, Rect | FileReader + ADD_PRINT_IMAGE | 读取 File 对象转 data URI 后添加(异步) | | addBarcode(options) | BarcodeItemOptions | ADD_PRINT_BARCODE / ADD_PRINT_BARCODEA | 添加条码/二维码 | | addRect(options) | RectItemOptions | ADD_PRINT_RECT / ADD_PRINT_RECTA | 添加矩形(有 color 时用 A 版本) | | addEllipse(options) | RectItemOptions | ADD_PRINT_ELLIPSE / ADD_PRINT_ELLIPSEA | 添加椭圆 | | addLine(options) | LineItemOptions | ADD_PRINT_LINE | 添加线条(两点坐标) | | addDnLine(options) | DiagonalLineItemOptions | ADD_PRINT_DNLINE / ADD_PRINT_DNLINEA | 添加下对角线(左上→右下) | | addUpLine(options) | DiagonalLineItemOptions | ADD_PRINT_UPLINE / ADD_PRINT_UPLINEA | 添加上对角线(左下→右上) | | addShape(options) | ShapeItemOptions | ADD_PRINT_SHAPE | 添加通用图形 | | addTable(options) | TableItemOptions | ADD_PRINT_TABLE | 添加表格(HTML 内容) | | addTableUrl(options) | UrlItemOptions | ADD_PRINT_TBURL | 添加表格(URL 方式) | | addUrl(options) | UrlItemOptions | ADD_PRINT_URL | 添加 URL 页面 | | addPdf(options) | PdfItemOptions | ADD_PRINT_PDF | 添加 PDF(本地路径或 Base64) | | addPdfFromUrl(url, rect) | string, Rect | fetch + ADD_PRINT_PDF | 下载远程 PDF 转 Base64 后添加(异步) | | addPdfFromFile(file, rect) | File, Rect | FileReader + ADD_PRINT_PDF | 读取 File 对象转 Base64 后添加(异步) | | addChart(options) | ChartItemOptions | ADD_PRINT_CHART | 添加图表 |

样式设置

| 方法 | 参数 | 对应原始 API | 说明 | |------|------|-------------|------| | setDefaultStyle(styleName, value) | string, any | SET_PRINT_STYLE | 设置默认样式(影响后续所有项) | | setItemStyle(itemNo, key, value) | number\|string, string, any | SET_PRINT_STYLEA | 设置指定项样式(0=最后一项) | | setItemStyles(itemNo, styles) | number\|string, ItemStyleOptions | SET_PRINT_STYLEA (批量) | 批量设置样式 | | setTextStyle(itemNo, style) | number\|string, TextStyleOptions | SET_PRINT_TEXT_STYLE / SET_PRINT_TEXT_STYLEA | 快捷文本样式设置 | | setItemProperty(itemNo, pageType, hOrient, vOrient) | number\|string, number, number, number | SET_PRINT_PROPERTY | 设置页面属性(页眉/页脚) |

setItemStyles 属性映射表

setItemStyles 接受 ItemStyleOptions 对象,内部自动映射为 SET_PRINT_STYLEA 的 key/value 调用:

| JS 属性 | CLODOP Key | 类型 | 说明 | |---------|------------|------|------| | fontName | FontName | string | 字体名称 | | fontSize | FontSize | number | 字号 | | bold | Bold | boolean \| number | 加粗 | | italic | Italic | boolean \| number | 斜体 | | underline | Underline | boolean \| number | 下划线 | | alignment | Alignment | Alignment | 对齐方式 | | fontColor | FontColor | string \| number | 字体颜色 | | itemType | ItemType | ItemPageType | 项类型(页眉/页脚) | | hOrient | HOrient | HorizontalOrient | 水平位置 | | vOrient | VOrient | VerticalOrient | 垂直位置 | | disabled | Disabled | boolean \| number | 是否禁用 | | angle | Angle | number | 旋转角度 | | autoWrap | AutoWrap | boolean | 自动换行 | | leftMargin | LeftMargin | number | 左内边距 | | rightMargin | RightMargin | number | 右内边距 | | topMargin | TopMargin | number | 上内边距 | | bottomMargin | BottomMargin | number | 下内边距 | | penStyle | PenStyle | PenStyle | 线条样式 | | penWidth | PenWidth | number | 线条宽度 | | stretch | Stretch | number | 拉伸模式 | | readOnly | ReadOnly | boolean \| number | 只读 | | linked | Linked | string | 关联 | | contentVName | ContentVName | string | 内容变量名 | | linePosition | LinePosition | number | 线条位置 | | qrCodeVersion | QRCodeVersion | number | QR 码版本 | | qrCodeErrorLevel | QRCodeErrorLevel | string | QR 码纠错等级 |

打印模式

| 方法 | 参数 | 对应原始 API | 说明 | |------|------|-------------|------| | setMode(modeType, modeValue) | string, any | SET_PRINT_MODE | 设置打印模式 | | setPageRange(start, end) | number, number | SET_PRINT_MODE('PRINT_START_PAGE/END_PAGE') | 设置打印页码范围 | | setPreviewWindow(options) | PreviewWindowOptions | SET_PREVIEW_WINDOW | 设置预览窗口参数 | | setPreviewMode(value) | any | SET_PREVIEW_MODE | 设置预览模式 | | setShowMode(type, value) | string, any | SET_SHOW_MODE | 设置显示模式 | | setSaveMode(type, value) | string, any | SET_SAVE_MODE | 设置保存模式 | | setBackgroundImage(content) | string | ADD_PRINT_SETUP_BKIMG | 设置背景图 | | addProgramData(value) | any | ADD_PRINT_DATA('ProgramData', value) | 添加程序数据 |

PrintModeType 常用值

SET_PRINT_MODE 是通用键值透传接口。modeType 转小写后存入 PageData,随打印数据一起发送到 C-Lodop 服务端。print.js 客户端仅特殊处理 NOCLEAR_AFTER_PRINT(同步属性)、含 WINDOW_DEF/CONTROL_PRINTER 的类型(立即发送),其余均为服务端解析。

| 枚举值 | 原始字符串 | 来源 | 说明 | |--------|-----------|------|------| | PrintModeType.NoClearAfterPrint | NOCLEAR_AFTER_PRINT | print.js:1141 | 打印后不清除数据(客户端同步设置属性) | | PrintModeType.PrintStartPage | PRINT_START_PAGE | print.js:1899 | 起始页码(PRINTAOK 内部调用) | | PrintModeType.PrintEndPage | PRINT_END_PAGE | print.js:1901 | 结束页码(PRINTAOK 内部调用) |

以上为 print.js 源码中有据可查的值。其他 modeType 由 C-Lodop 服务端解析,可直接传字符串:printer.setMode('DOUBLE_SIDED_PRINT', 1)

分页

| 方法 | 返回值 | 对应原始 API | 说明 | |------|--------|-------------|------| | newPage() | this | NEWPAGE() | 强制分页(继承当前页面设置) | | newPageA() | this | NEWPAGEA() | 强制分页(独立页码序列) |

输出操作(同步)

| 方法 | 返回值 | 对应原始 API | 说明 | |------|--------|-------------|------| | print() | string \| null | PRINT() | 直接打印(静默) | | preview(target?, width?, height?, option?) | string \| null | PREVIEW(target, width, height, option) | 打印预览 | | printWithDialog() | string \| null | PRINTA() | 弹出打印对话框 | | printSilent() | string \| null | PRINTB() | 后台静默打印 | | printDesign() | string \| null | PRINT_DESIGN() | 打印设计(仅本地) | | printSetup() | string \| null | PRINT_SETUP() | 打印维护/设置(仅本地) | | selectPrinter() | boolean | SELECT_PRINTER(false) | 弹出打印机选择框(仅选择) | | selectPrinterAndPrint() | boolean | SELECT_PRINTER(true) | 弹出打印机选择框并打印 | | printAfterSelect(printerIndex, copies, startPage?, endPage?) | void | PRINTAOK(...) | 选择后执行打印 | | saveToFile(fileName) | string \| null | SAVE_TO_FILE(fileName) | 保存为文件(仅本地) |

输出操作(异步)

所有异步方法返回 Promise<TaskResult>,其中 TaskResult = { taskId: string; value: any }

| 方法 | 对应原始 API | 说明 | |------|-------------|------| | printAsync() | PRINT() + On_Return | 直接打印 | | previewAsync(target?, width?, height?, option?) | PREVIEW() + On_Return | 打印预览 | | printWithDialogAsync() | PRINTA() + On_Return | 弹出打印对话框 | | printSilentAsync() | PRINTB() + On_Return | 后台静默打印 | | printDesignAsync() | PRINT_DESIGN() + On_Return | 打印设计 | | printSetupAsync() | PRINT_SETUP() + On_Return | 打印维护/设置 | | saveToFileAsync(fileName) | SAVE_TO_FILE() + On_Return | 保存为文件 | | sendRawDataAsync(rawData) | SEND_PRINT_RAWDATA() + On_Return | 发送原始打印指令 | | writeFileAsync(fileName, text, mode?) | WRITE_FILE_TEXT() + On_Return | 写入本地文件 | | getFileTimeAsync(fileName) | GET_FILE_TIME() + On_Return | 获取文件修改时间 | | writePortAsync(portName, data) | WRITE_PORT_DATA() + On_Return | 向端口写入数据 | | readPortAsync(portName) | READ_PORT_DATA() + On_Return | 从端口读取数据 | | getSystemInfoAsync(infoType) | GET_SYSTEM_INFO() + On_Return | 获取系统信息 | | getDialogValueAsync(type, preValue) | GET_DIALOG_VALUE() + On_Return | 弹出对话框获取输入 | | getValueAsync(valueType, valueIndex) | GET_VALUE() + On_Return | 获取打印参数值 | | doActionAsync(actionName, actionValue) | DO_ACTION() + On_Return | 执行动作 | | formatAsync(type, value) | FORMAT() + On_Return | 通用格式化 | | getIniFileNameAsync(taskName) | GET_PRINT_INIFFNAME() + On_Return | 获取 INI 配置文件名 | | readFileAsync(fileName) | GET_FILE_TEXT() + On_Return | 读取文件 | | fileExistsAsync(fileName) | IS_FILE_EXIST() + On_Return | 检查文件是否存在 |

回调注册

| 方法 | 返回值 | 对应原始 API | 说明 | |------|--------|-------------|------| | onReturn(callback) | this | On_Return + On_Return_Remain=true | 注册打印结果回调(持久监听) | | onBroadcast(callback) | this | On_Broadcast + On_Broadcast_Remain=true | 注册广播消息回调 | | offReturn() | this | On_Return=null | 移除打印结果回调 | | offBroadcast() | this | On_Broadcast=null | 移除广播消息回调 |

文件操作

仅限 C-Lodop 本地服务。

| 方法 | 返回值 | 对应原始 API | 说明 | |------|--------|-------------|------| | readFile(fileName) | string \| null | GET_FILE_TEXT(fileName) | 读取本地文件内容 | | writeFile(fileName, text, mode?) | string \| null | WRITE_FILE_TEXT(mode, fileName, text) | 写入本地文件 | | fileExists(fileName) | string \| null | IS_FILE_EXIST(fileName) | 检查文件是否存在 | | getFileTime(fileName) | string \| null | GET_FILE_TIME(fileName) | 获取文件修改时间 |

端口操作

| 方法 | 返回值 | 对应原始 API | 说明 | |------|--------|-------------|------| | writePort(portName, data) | string \| null | WRITE_PORT_DATA(portName, data) | 向端口写入数据 | | readPort(portName) | string \| null | READ_PORT_DATA(portName) | 从端口读取数据 |

工具方法

| 方法 | 返回值 | 对应原始 API | 说明 | |------|--------|-------------|------| | formatDate(format, value) | any | FORMAT('Time:' + format, value) | 格式化日期 | | format(type, value) | any | FORMAT(type, value) | 通用格式化 | | sendRawData(rawData) | string \| null | SEND_PRINT_RAWDATA(rawData) | 发送原始打印指令(ESC/POS 等) | | getSystemInfo(infoType) | string \| null | GET_SYSTEM_INFO(infoType) | 获取系统信息 | | getDialogValue(type, preValue) | string \| null | GET_DIALOG_VALUE(type, preValue) | 弹出对话框获取用户输入 | | getValue(valueType, valueIndex) | string \| null | GET_VALUE(valueType, valueIndex) | 获取打印参数值 | | getIniFileName(taskName) | string \| null | GET_PRINT_INIFFNAME(taskName) | 获取打印任务 INI 配置文件名 | | doAction(actionName, actionValue) | string \| null | DO_ACTION(actionName, actionValue) | 执行动作 |

PDF / 二进制工具函数

独立导出的工具函数,不依赖 CLodopPrinter 实例。

PDF 相关

| 函数 | 参数 | 返回值 | 说明 | |------|------|--------|------| | fetchPdfAsBase64(url, fetchOptions?) | string, RequestInit? | Promise<string> | 下载 PDF 转 Base64(需同源或已配 CORS) | | blobToBase64(blob) | Blob | Promise<string> | Blob 转 Base64(适合后端 API 返回的文件流) | | readFileAsBase64(file) | File | Promise<string> | 读取本地 File 对象转 Base64 |

图片相关

| 函数 | 参数 | 返回值 | 说明 | |------|------|--------|------| | fetchImageAsDataUri(url, fetchOptions?) | string, RequestInit? | Promise<string> | 下载图片转 data:image/xxx;base64,...(自动推断 MIME) | | readFileAsDataUri(file) | File | Promise<string> | 本地 File 转 data URI |

通用

| 函数 | 参数 | 返回值 | 说明 | |------|------|--------|------| | arrayBufferToBase64(data) | Uint8Array | string | 二进制数据转 Base64(底层工具) |

以上基于 C-Lodop 官方示例 demoDownloadPDF / demoOpenLocalPDFfile / demoGetBASE64 的逻辑。

AO 桥接打印

用于通过 AO(ActiveX Object)桥接方式连接远程打印机。

| 方法 | 返回值 | 对应原始 API | 说明 | |------|--------|-------------|------| | setBridgeIndex(bridgeValue) | boolean | SET_BRIDGE_INDEX(value) | 设置 AO 桥接打印机 | | getAOPrintersList(driver, listName?, split?) | string[] | Get_AOPrinters_List(...) | 获取 AO 打印机列表 | | getAOBridgesList(driver, listName?, split?) | string[] | Get_AOBridges_List(...) | 获取 AO 桥接列表 | | getAOBridgeList(listName?, split?) | string[] | Get_AOBridge_List(...) | 获取默认驱动的桥接列表 | | getAOBridgeSubPrintersList(bridge, listName?, split?, driver?) | string[] | Get_AOBridge_SubPrinters_List(...) | 获取桥接下的子打印机列表 | | getAOBridgeSubPrintersPageSizeList(bridge, subPrinter, split?, driver?) | string[] | Get_AOBridge_SubPrinters_PageSize_List(...) | 获取桥接子打印机支持的纸张 |

静态方法

| 方法 | 返回值 | 说明 | |------|--------|------| | CLodopPrinter.isAvailable(checkSocket?) | boolean | 检查 C-Lodop 服务是否可用 | | CLodopPrinter.onCLodopOpened(callback) | void | 注册全局 On_CLodop_Opened 回调 |


枚举参考

Orientation — 纸张方向

| 枚举值 | 值 | 说明 | |--------|---|------| | Portrait | 1 | 纵向 | | Landscape | 2 | 横向 | | PortraitFixed | 3 | 纵向,固定纸张 |

PenStyle — 线条样式

| 枚举值 | 值 | 说明 | |--------|---|------| | Solid | 0 | 实线 | | Dash | 1 | 虚线 | | Dot | 2 | 点线 | | DashDot | 3 | 点划线 | | DashDotDot | 4 | 双点划线 |

Alignment — 文本对齐

| 枚举值 | 值 | 说明 | |--------|---|------| | Left | 1 | 左对齐 | | Center | 2 | 居中 | | Right | 3 | 右对齐 |

BarcodeType — 条码类型

| 枚举值 | 值 | 说明 | |--------|---|------| | Code128A | 128A | 128A 编码 | | Code128B | 128B | 128B 编码 | | Code128C | 128C | 128C 编码(纯数字) | | Code128Auto | 128Auto | 128 自动编码 | | EAN8 | EAN8 | EAN-8 | | EAN13 | EAN13 | EAN-13(商品条码) | | EAN128A | EAN128A | EAN-128A | | EAN128B | EAN128B | EAN-128B | | EAN128C | EAN128C | EAN-128C | | Code39 | 39 | Code 39 | | Code39Extended | 39Extended | Code 39 扩展 | | Interleaved25 | 2_5interleaved | 交叉 25 码 | | Code93 | 93 | Code 93 | | Code93Extended | 93Extended | Code 93 扩展 | | Codabar | Codabar | Codabar | | QRCode | QRCode | QR 二维码 | | PDF417 | PDF417 | PDF417 | | DataMatrix | DataMatrix | DataMatrix |

ShapeType — 图形类型

| 枚举值 | 值 | 说明 | |--------|---|------| | UpLine | 0 | 上对角线(左下→右上) | | DownLine | 1 | 下对角线(左上→右下) | | Rectangle | 2 | 矩形 | | Ellipse | 3 | 椭圆 |

ItemPageType — 打印项页面类型

| 枚举值 | 值 | 说明 | |--------|---|------| | Normal | 0 | 普通项 | | HeaderFooter | 1 | 页眉页脚 | | Header | 2 | 页眉 | | Footer | 3 | 页脚 |

HorizontalOrient — 水平对齐

| 枚举值 | 值 | 说明 | |--------|---|------| | Left | 0 | 左 | | Center | 1 | 居中 | | Right | 2 | 右 | | Full | 3 | 铺满 |

VerticalOrient — 垂直对齐

| 枚举值 | 值 | 说明 | |--------|---|------| | Top | 0 | 上 | | Center | 1 | 居中 | | Bottom | 2 | 下 | | Full | 3 | 铺满 |

PreviewDispMode — 预览显示模式

| 枚举值 | 值 | 说明 | |--------|---|------| | FullScreen | 0 | 全屏 | | Windowed | 1 | 窗口模式 | | Auto | 2 | 自适应 |

FileWriteMode — 文件写入模式

| 枚举值 | 值 | 说明 | |--------|---|------| | Overwrite | 1 | 覆盖 | | Append | 2 | 追加 |

CLodopStatus — 连接状态

| 枚举值 | 值 | 说明 | |--------|---|------| | Idle | idle | 未初始化 | | Loading | loading | 正在加载 | | Ready | ready | 已就绪 | | Error | error | 加载失败 |


类型/接口参考

Rect — 位置和尺寸基础接口

interface Rect {
  top: number | string;     // 距顶部距离(px 或 '10%')
  left: number | string;    // 距左侧距离
  width: number | string;   // 宽度
  height: number | string;  // 高度
}

TextItemOptions

interface TextItemOptions extends Rect {
  content: string;   // 文本内容
  name?: string;     // 打印项名称(有值时调用 A 版本 API)
}

HtmlItemOptions

interface HtmlItemOptions extends Rect {
  content: string;   // HTML 内容
  name?: string;
}

ImageItemOptions

interface ImageItemOptions extends Rect {
  content: string;
  // content 自动识别格式(addImage 内部处理):
  // - "data:image/jpg;base64,..."  → Base64 图片,生成 <img src="data:...">
  // - "http(s)://..."              → URL 图片,生成 <img src="..." crossOrigin="anonymous">
  // - "<img ..."                   → HTML 标签直接透传
  // - 其他(如 "C:\test.jpg")      → 本地文件路径,直接传给 ADD_PRINT_IMAGE
}

Base64 格式要点data:image/jpg;base64,XXXX 中,逗号前是编码描述,逗号后是内容。 image/ 后的格式标识(如 jpg必须与生成编码时的实际图片格式一致

支持的图片格式:bmp、jpg、jpeg、gif、png、ico、emf、wmf

跨域说明addImage 传入 URL 时自动加 crossOrigin="anonymous", 但 C-Lodop 服务端渲染 <img> 时仍可能受限。 对于第三方跨域图片,推荐使用 addImageFromUrl + 后端代理彻底绕过 CORS。

BarcodeItemOptions

interface BarcodeItemOptions extends Rect {
  type: BarcodeType | string;  // 条码类型
  value: string;               // 条码值
  name?: string;
}

RectItemOptions

interface RectItemOptions extends Rect {
  penStyle?: PenStyle;         // 线条样式(默认 Solid)
  penWidth?: number;           // 线条宽度(默认 1)
  color?: string | number;     // 颜色(有值时调用 A 版本 API)
}

LineItemOptions

interface LineItemOptions {
  top1: number | string;       // 起点 Y
  left1: number | string;      // 起点 X
  top2: number | string;       // 终点 Y
  left2: number | string;      // 终点 X
  penStyle?: PenStyle;
  penWidth?: number;
}

DiagonalLineItemOptions

interface DiagonalLineItemOptions extends Rect {
  penStyle?: PenStyle;
  penWidth?: number;
  color?: string | number;
}

ShapeItemOptions

interface ShapeItemOptions extends Rect {
  shapeType: ShapeType;        // 图形类型
  penStyle?: PenStyle;
  penWidth?: number;
  color?: string | number;
}

PageSizeOptions

interface PageSizeOptions {
  orientation?: Orientation;   // 纸张方向
  width?: number;              // 宽度(单位:0.1mm,2100 = 210mm)
  height?: number;             // 高度(单位:0.1mm,2970 = 297mm)
  name?: string;               // 纸张名称(如 'A4',优先于宽高)
}

PreviewWindowOptions

interface PreviewWindowOptions {
  dispMode?: PreviewDispMode | number;  // 显示模式
  toolMode?: number;                    // 工具栏模式(按位标志)
  directPrint?: boolean;                // 是否默认直接打印
  width?: number;                       // 窗口宽度
  height?: number;                      // 窗口高度
  buttonCaption?: string;               // 打印按钮文字
}

TextStyleOptions

interface TextStyleOptions {
  fontName?: string;
  fontSize?: number;
  bold?: boolean;
  italic?: boolean;
  underline?: boolean;
  alignment?: Alignment;
  color?: string | number;     // 有值时调用 A 版本 API
}

ItemStyleOptions

interface ItemStyleOptions {
  fontName?: string;
  fontSize?: number;
  bold?: number | boolean;
  italic?: number | boolean;
  underline?: number | boolean;
  alignment?: Alignment;
  fontColor?: string | number;
  itemType?: ItemPageType;
  hOrient?: HorizontalOrient;
  vOrient?: VerticalOrient;
  disabled?: number | boolean;
  angle?: number;
  autoWrap?: boolean;
  leftMargin?: number;
  rightMargin?: number;
  topMargin?: number;
  bottomMargin?: number;
  penStyle?: PenStyle;
  penWidth?: number;
  stretch?: number;
  readOnly?: number | boolean;
  linked?: string;
  contentVName?: string;
  linePosition?: number;
  qrCodeVersion?: number;
  qrCodeErrorLevel?: string;
  [key: string]: any;          // 支持任意自定义属性
}

PrinterInfo

interface PrinterInfo {
  index: number;           // 打印机序号
  name: string;            // 打印机名称
  driverName: string;      // 驱动名称
  portName: string;        // 端口名称
  orientation: number;     // 默认方向
  paperSize: number;       // 纸张代码
  paperLength: number;     // 纸张长度(0.1mm)
  paperWidth: number;      // 纸张宽度(0.1mm)
  copies: number;          // 默认份数
  printQuality: number;    // 打印质量(DPI)
  isColor: boolean;        // 是否彩色
  isDuplex: boolean;       // 是否支持双面
  formName: string;        // 当前纸张名称
  pageSizes: string[];     // 支持的纸张列表
  isDefault: boolean;      // 是否为默认打印机
}

LicenseOptions

interface LicenseOptions {
  companyName: string;
  license: string;
  licenseA?: string;
  licenseB?: string;
}

TaskResult

interface TaskResult {
  taskId: string;   // 任务 ID
  value: any;       // 返回值
}

脚本加载器

loader.ts 导出的底层加载函数,通常通过插件或 useCLodop 自动调用:

| 函数 | 返回值 | 说明 | |------|--------|------| | loadCLodopScript(urls?, timeout?) | Promise<CLodopRaw> | 从多个 URL 依次尝试加载脚本 | | unloadCLodopScript() | void | 卸载已加载的脚本(用于重新加载) | | getDefaultUrls() | string[] | 获取默认脚本 URL(自动区分 HTTP/HTTPS) | | checkServiceAvailable(host?, port?, timeout?) | Promise<boolean> | 检查 C-Lodop 服务端口连通性 | | getLoadState() | LoadState | 获取当前加载状态 | | isHttpsEnv() | boolean | 检测当前页面是否 HTTPS |


完整示例

商品标签打印

import { useCLodop, Orientation, BarcodeType, PenStyle } from 'vue3-clodop';

const { printer, isReady } = useCLodop();

function printLabel(product: { name: string; price: number; sku: string }) {
  if (!printer.value) return;

  printer.value
    .init('商品标签')
    .setPageSize({ orientation: Orientation.Portrait, width: 1000, height: 500 })
    .addRect({ top: 5, left: 5, width: '96%', height: '92%', penStyle: PenStyle.Solid, penWidth: 1 })
    .addText({ top: 10, left: 15, width: 180, height: 20, content: `商品:${product.name}` })
    .setItemStyles(0, { fontSize: 11, bold: true })
    .addText({ top: 35, left: 15, width: 100, height: 18, content: `价格:${product.price.toFixed(2)}` })
    .addBarcode({ top: 60, left: 15, width: 180, height: 50, type: BarcodeType.Code128Auto, value: product.sku })
    .addBarcode({ top: 15, left: 215, width: 120, height: 120, type: BarcodeType.QRCode, value: product.sku })
    .preview();
}

异步打印并等待结果

async function printAndWait() {
  if (!printer.value) return;

  printer.value
    .init('报表')
    .setPageSize({ orientation: Orientation.Landscape, width: 2970, height: 2100, name: 'A4' })
    .addHtml({ top: 20, left: 20, width: '100%', height: '100%', content: reportHtml });

  try {
    const result = await printer.value.printAsync();
    console.log(`打印完成: taskId=${result.taskId}, value=${result.value}`);
  } catch (e) {
    console.error('打印失败:', e);
  }
}

多页打印

printer.value
  .init('多页报告')
  .setPageSize({ orientation: Orientation.Portrait, width: 2100, height: 2970, name: 'A4' })
  .addText({ top: 50, left: 50, width: 500, height: 40, content: '第一页' })
  .newPage()
  .addText({ top: 50, left: 50, width: 500, height: 40, content: '第二页' })
  .setPageRange(1, 2)
  .preview();

PDF 文件打印

两层限制

  1. C-Lodop 限制ADD_PRINT_PDF(print.js:1487-1491)仅接受本地路径或 Base64 字符串,远程 URL 直接传入会空白页
  2. 浏览器 CORS 限制fetchPdfAsBase64 使用浏览器 fetch API,受同源策略约束。第三方跨域 URL(目标服务器未设 Access-Control-Allow-Origin 头)会被浏览器拦截

解决方案对照表

| 场景 | 方案 | |------|------| | 本地文件路径 | addPdf({ content: 'C:\\...' }) | | 同源 / 已配 CORS 的 URL | fetchPdfAsBase64(url)addPdfFromUrl(url, rect) | | 第三方跨域 URL | 后端代理中转 → fetchPdfAsBase64('/api/proxy/pdf?url=...') | | 后端 API 返回 Blob | blobToBase64(blob)addPdf({ content: base64 }) | | <input type="file"> | readFileAsBase64(file)addPdfFromFile(file, rect) |

import { fetchPdfAsBase64, readFileAsBase64, blobToBase64 } from 'vue3-clodop';

// 方式一:本地文件路径(仅限本地 C-Lodop 服务)
printer.value
  .init('PDF 打印')
  .setPageSize({ orientation: Orientation.Portrait, width: 2100, height: 2970, name: 'A4' })
  .addPdf({ top: 0, left: 0, width: '100%', height: '100%', content: 'C:\\Documents\\report.pdf' })
  .preview();

// 方式二:同源或已配 CORS 的远程 URL
async function printRemotePdf(url: string) {
  const base64 = await fetchPdfAsBase64(url);
  printer.value!
    .init('远程 PDF')
    .setPageSize({ orientation: Orientation.Portrait, width: 2100, height: 2970, name: 'A4' })
    .addPdf({ top: 0, left: 0, width: '100%', height: '100%', content: base64 })
    .preview();
}

// 方式三:跨域 PDF — 通过后端代理中转(推荐)
async function printCrossOriginPdf(thirdPartyUrl: string) {
  // 后端代理接口示例:GET /api/proxy/pdf?url=xxx → 返回 PDF 文件流
  const base64 = await fetchPdfAsBase64(`/api/proxy/pdf?url=${encodeURIComponent(thirdPartyUrl)}`);
  printer.value!
    .init('跨域 PDF')
    .addPdf({ top: 0, left: 0, width: '100%', height: '100%', content: base64 })
    .preview();
}

// 方式四:后端 API 直接返回 Blob(如 axios responseType: 'blob')
async function printBlobPdf(orderId: string) {
  const resp = await fetch(`/api/orders/${orderId}/pdf`);
  const blob = await resp.blob();
  const base64 = await blobToBase64(blob);
  printer.value!
    .init(`订单${orderId}`)
    .addPdf({ top: 0, left: 0, width: '100%', height: '100%', content: base64 })
    .preview();
}

// 方式五:addPdfFromUrl 异步链式调用
async function printRemotePdfChained(url: string) {
  const p = printer.value!
    .init('远程 PDF')
    .setPageSize({ orientation: Orientation.Portrait, width: 2100, height: 2970, name: 'A4' });
  await p.addPdfFromUrl(url, { top: 0, left: 0, width: '100%', height: '100%' });
  p.preview();
}

// 方式六:<input type="file"> 本地文件选择
async function printFromFileInput(file: File) {
  const base64 = await readFileAsBase64(file);
  printer.value!
    .init('本地文件 PDF')
    .addPdf({ top: 0, left: 0, width: '100%', height: '100%', content: base64 })
    .preview();
}

图片打印

两层限制(与 PDF 类似):

  1. C-Lodop 限制ADD_PRINT_IMAGE(print.js:1459)接受 HTML <img> 标签或本地路径
  2. 浏览器 CORS 限制:跨域 <img> 可能被 C-Lodop 服务端拒绝加载

addImage 自动处理:传入 URL 时自动生成 <img crossOrigin="anonymous">, 但对于严格跨域场景,推荐 addImageFromUrl + 后端代理彻底绕过。

解决方案对照表

| 场景 | 方案 | |------|------| | 同域 URL | addImage({ content: 'http://localhost:8000/photo.jpg' }) | | Base64 data URI | addImage({ content: 'data:image/png;base64,...' }) | | 自定义 HTML | addImage({ content: '<img src="..." crossOrigin="anonymous" width="200px" />' }) | | 本地文件路径 | addImage({ content: 'C:\\Photos\\banner.png' }) | | 第三方跨域 URL | addImageFromUrl('/api/proxy/image?url=...', rect) | | <input type="file"> | addImageFromFile(file, rect) | | 后端返回 Blob | fetchImageAsDataUrireadFileAsDataUriaddImage |

import { fetchImageAsDataUri, readFileAsDataUri } from 'vue3-clodop';

// 方式一:URL 图片(addImage 自动生成 <img crossOrigin="anonymous">)
printer.value
  .init('图片打印')
  .setPageSize({ orientation: Orientation.Portrait, width: 2100, height: 2970, name: 'A4' })
  .addImage({ top: 50, left: 50, width: 500, height: 375, content: 'https://example.com/photo.jpg' })
  .preview();
// 等效于: ADD_PRINT_IMAGE(50,50,500,375, '<img border="0" src="https://..." crossOrigin="anonymous" width="500px" height="375px" />')

// 方式二:Base64 图片(格式标识须与实际一致)
const canvas = document.getElementById('myCanvas') as HTMLCanvasElement;
const dataUri = canvas.toDataURL('image/png');  // data:image/png;base64,...
printer.value
  .init('Canvas 截图打印')
  .addImage({ top: 20, left: 20, width: '96%', height: '94%', content: dataUri })
  .preview();

// 方式三:本地文件路径
printer.value
  .init('本地图片')
  .addImage({ top: 0, left: 0, width: '100%', height: '100%', content: 'C:\\Photos\\banner.png' })
  .preview();

// 方式四:自定义 HTML img 标签(完全控制属性)
printer.value
  .init('自定义 img')
  .addImage({
    top: 60, left: 50, width: 300, height: 300,
    content: '<img border="0" src="http://127.0.0.1:18000/CLodopDemos/PrintSample8.jpg" crossOrigin="anonymous" width="200px" height="200px"/>',
  })
  .preview();

// 方式五:跨域图片 — 通过后端代理 + addImageFromUrl(推荐)
async function printCrossOriginImage(thirdPartyUrl: string) {
  const proxyUrl = `/api/proxy/image?url=${encodeURIComponent(thirdPartyUrl)}`;
  const p = printer.value!.init('跨域图片');
  await p.addImageFromUrl(proxyUrl, { top: 50, left: 50, width: 500, height: 375 });
  p.preview();
}

// 方式六:<input type="file"> 选择本地图片
async function printFromFileInput(file: File) {
  const p = printer.value!.init('本地文件图片');
  await p.addImageFromFile(file, { top: 50, left: 50, width: 500, height: 400 });
  p.preview();
}

// 方式七:后端 API 返回图片 Blob → 手动转 data URI
async function printImageFromApi(url: string) {
  const dataUri = await fetchImageAsDataUri(url);
  // dataUri = "data:image/jpeg;base64,/9j/4AAQ..."(MIME 自动推断)
  printer.value!.init('API 图片')
    .addImage({ top: 50, left: 50, width: 500, height: 375, content: dataUri })
    .preview();
}

// 方式八:证件照排版打印(多张图片网格)
function printIdPhotos(photoBase64: string) {
  const p = printer.value!;
  p.init('证件照打印')
   .setPageSize({ orientation: Orientation.Portrait, width: 2100, height: 2970, name: 'A4' });

  const photoW = 250, photoH = 350, gap = 20;
  const cols = 4, rows = 6;
  const startX = 100, startY = 80;

  for (let row = 0; row < rows; row++) {
    for (let col = 0; col < cols; col++) {
      const x = startX + col * (photoW + gap);
      const y = startY + row * (photoH + gap);
      p.addImage({ top: y, left: x, width: photoW, height: photoH, content: photoBase64 });
    }
  }
  p.preview();
}

对角线与交叉表格

import { PenStyle } from 'vue3-clodop';

// 绘制带斜线表头的表格
printer.value
  .init('斜线表头')
  .setPageSize({ orientation: Orientation.Portrait, width: 2100, height: 2970, name: 'A4' })
  // 外边框
  .addRect({ top: 50, left: 50, width: 500, height: 300, penStyle: PenStyle.Solid, penWidth: 2 })
  // 斜线表头单元格
  .addRect({ top: 50, left: 50, width: 120, height: 60, penStyle: PenStyle.Solid, penWidth: 1 })
  .addDnLine({ top: 50, left: 50, width: 120, height: 60, penStyle: PenStyle.Solid, penWidth: 1 })
  // 斜线左下角文字(行标题)
  .addText({ top: 80, left: 55, width: 50, height: 20, content: '姓名' })
  .setItemStyle(0, 'FontSize', 9)
  // 斜线右上角文字(列标题)
  .addText({ top: 55, left: 100, width: 60, height: 20, content: '科目' })
  .setItemStyle(0, 'FontSize', 9)
  // 列标题
  .addText({ top: 65, left: 180, width: 80, height: 30, content: '语文' })
  .addText({ top: 65, left: 270, width: 80, height: 30, content: '数学' })
  .addText({ top: 65, left: 360, width: 80, height: 30, content: '英语' })
  // 横线分隔
  .addLine({ top1: 110, left1: 50, top2: 110, left2: 550, penStyle: PenStyle.Solid, penWidth: 1 })
  .addLine({ top1: 170, left1: 50, top2: 170, left2: 550, penStyle: PenStyle.Solid, penWidth: 1 })
  // 竖线分隔
  .addLine({ top1: 50, left1: 170, top2: 350, left2: 170, penStyle: PenStyle.Solid, penWidth: 1 })
  .addLine({ top1: 50, left1: 260, top2: 350, left2: 260, penStyle: PenStyle.Solid, penWidth: 1 })
  .addLine({ top1: 50, left1: 350, top2: 350, left2: 350, penStyle: PenStyle.Solid, penWidth: 1 })
  .preview();

套打(在预印表单上精确定位打印)

// 套打的关键:精确控制每个打印项的 top/left 坐标,配合预印表单
printer.value
  .init('快递面单套打')
  .setPageSize({ orientation: Orientation.Portrait, width: 1000, height: 1800 })
  .setPrinter('针式打印机')
  // 寄件人信息(坐标需根据实际面单调整)
  .addText({ top: 120, left: 80, width: 300, height: 25, content: '张三' })
  .setItemStyle(0, 'FontSize', 10)
  .addText({ top: 150, left: 80, width: 400, height: 25, content: '13800138000' })
  .addText({ top: 180, left: 80, width: 400, height: 40, content: '上海市浦东新区陆家嘴环路 1000 号' })
  .setItemStyles(0, { fontSize: 9, autoWrap: true })
  // 收件人信息(大号字体突出显示)
  .addText({ top: 300, left: 80, width: 300, height: 35, content: '李四' })
  .setItemStyles(0, { fontSize: 14, bold: true })
  .addText({ top: 340, left: 80, width: 400, height: 25, content: '18900189000' })
  .setItemStyle(0, 'FontSize', 12)
  .addText({ top: 370, left: 80, width: 400, height: 50, content: '北京市朝阳区建国路 88 号 SOHO 现代城 A 座 2001' })
  .setItemStyles(0, { fontSize: 11, autoWrap: true })
  // 大头笔区域(分拣码)
  .addText({ top: 450, left: 100, width: 200, height: 60, content: '010-朝阳' })
  .setItemStyles(0, { fontSize: 28, bold: true })
  // 快递单号条码
  .addBarcode({ top: 550, left: 80, width: 350, height: 80, type: BarcodeType.Code128Auto, value: 'SF1234567890123' })
  .addText({ top: 635, left: 80, width: 350, height: 20, content: 'SF1234567890123' })
  .setItemStyles(0, { fontSize: 10, alignment: 2 })
  .print(); // 套打通常直接打印,不预览

ESC/POS 热敏小票打印

// 通过 sendRawData 直接发送 ESC/POS 指令到小票打印机
function printReceipt(items: Array<{ name: string; qty: number; price: number }>) {
  const ESC = '\x1B';
  const GS = '\x1D';
  const LF = '\x0A';

  let cmds = '';
  cmds += ESC + '@';                       // 初始化
  cmds += ESC + 'a' + '\x01';              // 居中
  cmds += GS + '!' + '\x11';               // 放大一倍
  cmds += '我的小店' + LF;
  cmds += GS + '!' + '\x00';               // 恢复正常大小
  cmds += ESC + 'a' + '\x00';              // 左对齐
  cmds += '================================' + LF;
  cmds += '商品名称        数量    金额' + LF;
  cmds += '--------------------------------' + LF;

  let total = 0;
  for (const item of items) {
    const amount = item.qty * item.price;
    total += amount;
    const line = item.name.padEnd(16) +
                 String(item.qty).padStart(4) +
                 amount.toFixed(2).padStart(8);
    cmds += line + LF;
  }

  cmds += '================================' + LF;
  cmds += ESC + 'a' + '\x02';              // 右对齐
  cmds += GS + '!' + '\x01';               // 字体加宽
  cmds += `合计: ${total.toFixed(2)}` + LF;
  cmds += GS + '!' + '\x00';               // 恢复
  cmds += ESC + 'a' + '\x00';              // 左对齐
  cmds += LF;
  cmds += ESC + 'a' + '\x01';              // 居中
  cmds += '谢谢惠顾,欢迎下次光临!' + LF;
  cmds += LF + LF + LF;
  cmds += GS + 'V' + '\x00';               // 切纸

  printer.value!
    .init('小票')
    .setPrinter('POS-58')
    .sendRawData(cmds);
}

图形绘制综合示例

import { PenStyle, ShapeType } from 'vue3-clodop';

printer.value
  .init('图形综合示例')
  .setPageSize({ orientation: Orientation.Portrait, width: 2100, height: 2970, name: 'A4' })
  // 标题
  .addText({ top: 30, left: 50, width: 500, height: 40, content: '图形绘制综合演示' })
  .setItemStyles(0, { fontSize: 20, bold: true, fontColor: '#1e293b' })

  // 实线矩形
  .addRect({ top: 100, left: 50, width: 200, height: 120, penStyle: PenStyle.Solid, penWidth: 2 })
  .addText({ top: 150, left: 90, width: 120, height: 20, content: '实线矩形' })
  .setItemStyle(0, 'FontSize', 10)

  // 虚线矩形(带颜色)
  .addRect({ top: 100, left: 300, width: 200, height: 120, penStyle: PenStyle.Dash, penWidth: 1, color: '#4f46e5' })
  .addText({ top: 150, left: 340, width: 120, height: 20, content: '虚线矩形' })
  .setItemStyles(0, { fontSize: 10, fontColor: '#4f46e5' })

  // 椭圆
  .addEllipse({ top: 260, left: 50, width: 200, height: 120, penStyle: PenStyle.Solid, penWidth: 2, color: '#10b981' })
  .addText({ top: 310, left: 110, width: 80, height: 20, content: '椭圆' })
  .setItemStyle(0, 'FontSize', 10)

  // 通用图形 — 下对角线
  .addShape({ shapeType: ShapeType.DownLine, top: 260, left: 300, width: 200, height: 120, penStyle: PenStyle.Solid, penWidth: 2, color: '#ef4444' })
  .addText({ top: 390, left: 340, width: 120, height: 20, content: 'Shape 下对角线' })
  .setItemStyle(0, 'FontSize', 10)

  // 专用对角线 API — X 形交叉
  .addRect({ top: 420, left: 50, width: 200, height: 200, penStyle: PenStyle.Solid, penWidth: 1 })
  .addDnLine({ top: 420, left: 50, width: 200, height: 200, penStyle: PenStyle.Solid, penWidth: 2, color: '#4f46e5' })
  .addUpLine({ top: 420, left: 50, width: 200, height: 200, penStyle: PenStyle.Solid, penWidth: 2, color: '#10b981' })
  .addText({ top: 505, left: 100, width: 100, height: 20, content: 'X 交叉' })
  .setItemStyle(0, 'FontSize', 10)

  // 各种线型演示
  .addLine({ top1: 440, left1: 300, top2: 440, left2: 550, penStyle: PenStyle.Solid, penWidth: 2 })
  .addText({ top: 445, left: 560, width: 60, height: 15, content: '实线' })
  .setItemStyle(0, 'FontSize', 8)

  .addLine({ top1: 475, left1: 300, top2: 475, left2: 550, penStyle: PenStyle.Dash, penWidth: 2 })
  .addText({ top: 480, left: 560, width: 60, height: 15, content: '虚线' })
  .setItemStyle(0, 'FontSize', 8)

  .addLine({ top1: 510, left1: 300, top2: 510, left2: 550, penStyle: PenStyle.Dot, penWidth: 2 })
  .addText({ top: 515, left: 560, width: 60, height: 15, content: '点线' })
  .setItemStyle(0, 'FontSize', 8)

  .addLine({ top1: 545, left1: 300, top2: 545, left2: 550, penStyle: PenStyle.DashDot, penWidth: 2 })
  .addText({ top: 550, left: 560, width: 60, height: 15, content: '点划线' })
  .setItemStyle(0, 'FontSize', 8)

  .addLine({ top1: 580, left1: 300, top2: 580, left2: 550, penStyle: PenStyle.DashDotDot, penWidth: 2 })
  .addText({ top: 585, left: 560, width: 80, height: 15, content: '双点划线' })
  .setItemStyle(0, 'FontSize', 8)
  .preview();

双面打印

printer.value
  .init('双面打印')
  .setPageSize({ orientation: Orientation.Portrait, width: 2100, height: 2970, name: 'A4' })
  .setMode('DOUBLE_SIDED_PRINT', 1)      // 开启双面打印
  .addText({ top: 50, left: 50, width: 500, height: 40, content: '正面内容' })
  .newPage()
  .addText({ top: 50, left: 50, width: 500, height: 40, content: '背面内容' })
  .newPage()
  .addText({ top: 50, left: 50, width: 500, height: 40, content: '第二张正面' })
  .newPage()
  .addText({ top: 50, left: 50, width: 500, height: 40, content: '第二张背面' })
  .print();

页眉页脚

import { ItemPageType, HorizontalOrient, VerticalOrient } from 'vue3-clodop';

printer.value
  .init('带页眉页脚的文档')
  .setPageSize({ orientation: Orientation.Portrait, width: 2100, height: 2970, name: 'A4' })
  // 页眉 — 每页顶部自动重复
  .addText({ top: 15, left: 50, width: 500, height: 20, content: '公司机密文件 — 内部资料' })
  .setItemStyles(0, { fontSize: 9, fontColor: '#999', itemType: ItemPageType.Header })
  .addLine({ top1: 38, left1: 50, top2: 38, left2: 550, penStyle: PenStyle.Solid, penWidth: 1 })
  .setItemStyle(0, 'ItemType', ItemPageType.Header)
  // 页脚 — 每页底部自动重复,#页号# 会被替换为实际页码
  .addText({ top: '285mm', left: 50, width: 500, height: 20, content: '第 #页号# 页 / 共 #总页数# 页' })
  .setItemStyles(0, { fontSize: 9, fontColor: '#999', alignment: 2, itemType: ItemPageType.Footer })
  // 正文
  .addText({ top: 60, left: 50, width: 500, height: 40, content: '第一页正文内容' })
  .setItemStyle(0, 'FontSize', 14)
  .newPage()
  .addText({ top: 60, left: 50, width: 500, height: 40, content: '第二页正文内容' })
  .setItemStyle(0, 'FontSize', 14)
  .preview();

水印效果

printer.value
  .init('水印文档')
  .setPageSize({ orientation: Orientation.Portrait, width: 2100, height: 2970, name: 'A4' })
  // 水印文字 — 旋转 45 度、半透明灰色、大号字体
  .addText({ top: 350, left: 80, width: 500, height: 60, content: '仅供内部使用' })
  .setItemStyles(0, {
    fontSize: 40,
    fontColor: '#dddddd',
    bold: true,
    angle: -45,
    itemType: ItemPageType.HeaderFooter,  // 每页重复
  })
  // 正常内容
  .addText({ top: 50, left: 50, width: 500, height: 40, content: '合同文档正文' })
  .setItemStyle(0, 'FontSize', 14)
  .addHtml({ top: 100, left: 50, width: '90%', height: '80%', content: contractHtml })
  .preview();

批量连续打印(不清除数据)

import { PrintModeType } from 'vue3-clodop';

async function batchPrint(orders: Array<{ id: string; content: string }>) {
  if (!printer.value) return;

  for (let i = 0; i < orders.length; i++) {
    const order = orders[i];
    printer.value
      .init(`订单-${order.id}`)
      .setPageSize({ orientation: Orientation.Portrait, width: 2100, height: 2970, name: 'A4' })
      .addText({ top: 50, left: 50, width: 500, height: 30, content: `订单号: ${order.id}` })
      .setItemStyles(0, { fontSize: 16, bold: true })
      .addHtml({ top: 100, left: 50, width: '90%', height: '80%', content: order.content });

    // 使用异步打印,等每份完成后再打下一份
    const result = await printer.value.printAsync();
    console.log(`订单 ${order.id} 打印完成: ${result.value}`);
  }
}

自定义预览窗口

import { PreviewDispMode } from 'vue3-clodop';

printer.value
  .init('自定义预览')
  .setPageSize({ orientation: Orientation.Portrait, width: 2100, height: 2970, name: 'A4' })
  .setPreviewWindow({
    dispMode: PreviewDispMode.Windowed,   // 窗口模式(非全屏)
    toolMode: 0,                          // 显示全部工具栏
    directPrint: false,                   // 不默认直接打印
    width: 900,                           // 窗口宽度
    height: 700,                          // 窗口高度
    buttonCaption: '确认打印',             // 打印按钮文字
  })
  .setMode('AUTO_CLOSE_PREWINDOW', 1)     // 打印后自动关闭预览窗口
  .addText({ top: 50, left: 50, width: 500, height: 40, content: '自定义预览窗口示例' })
  .preview();

保存为文件(PDF / 图片)

// 保存为 PDF 文件
printer.value
  .init('导出 PDF')
  .setPageSize({ orientation: Orientation.Portrait, width: 2100, height: 2970, name: 'A4' })
  .addText({ top: 50, left: 50, width: 500, height: 40, content: '这份文档将被保存为 PDF' })
  .setItemStyle(0, 'FontSize', 16)
  .addHtml({ top: 120, left: 50, width: '90%', height: '80%', content: reportHtml })
  .saveToFile('C:\\Documents\\report.pdf');

// 保存为图片并设置格式
printer.value
  .init('导出图片')
  .setPageSize({ orientation: Orientation.Portrait, width: 2100, height: 2970, name: 'A4' })
  .setSaveMode('SAVEAS_IMGFILETYPE', 'jpg')  // 指定图片格式
  .addText({ top: 50, left: 50, width: 500, height: 40, content: '这份文档将被保存为图片' })
  .saveToFile('C:\\Documents\\page.jpg');

// 异步保存并等待结果
const result = await printer.value
  .init('异步导出')
  .addHtml({ top: 0, left: 0, width: '100%', height: '100%', content: reportHtml })
  .saveToFileAsync('C:\\Documents\\output.pdf');
console.log(`保存完成: ${result.value}`);

串口/端口通信

// 向 COM 串口写入数据(适用于电子秤、扫码枪等设备)
printer.value!.writePort('COM1', 'R');  // 发送读取指令

// 异步读取串口返回数据
const portResult = await printer.value!.readPortAsync('COM1');
console.log('串口返回:', portResult.value);

// 获取系统信息
const machineId = printer.value!.getSystemInfo('MachineID');
console.log('机器码:', machineId);

文件对话框选择文件

// 弹出文件选择对话框
const filePath = await printer.value!.getDialogValueAsync('ChooseFile', '');
console.log('用户选择的文件:', filePath.value);

// 弹出保存对话框
const savePath = await printer.value!.getDialogValueAsync('SaveFile', 'output.pdf');
console.log('用户选择的保存路径:', savePath.value);

// 弹出文件夹选择
const folder = await printer.value!.getDialogValueAsync('ChooseFolder', '');
console.log('用户选择的文件夹:', folder.value);

连接状态监控

import { CLodopPrinter } from 'vue3-clodop';

// 静态方法 — 检查服务是否可用
if (CLodopPrinter.isAvailable()) {
  console.log('C-Lodop 服务已启动');
}

// 含 WebSocket 连接检查
if (CLodopPrinter.isAvailable(true)) {
  console.log('WebSocket 已连接,可使用全部功能');
}

// 实例方法 — 查看详细状态
if (printer.value) {
  console.log('版本:', printer.value.version);
  console.log('扩展版本:', printer.value.cVersion);
  console.log('WebSocket:', printer.value.isSocketOpened ? '已连接' : '未连接');
  console.log('空闲:', printer.value.isIdle() ? '是' : '否');
  console.log('服务地址:', printer.value.hostURI);

  // 手动重连 WebSocket
  if (!printer.value.isSocketOpened) {
    printer.value.reconnectWebSocket();
  }
}

// 注册全局就绪回调
CLodopPrinter.onCLodopOpened((clodop) => {
  console.log('C-Lodop WebSocket 已连接,版本:', clodop.VERSION);
});