AI 帮你画模板、点一下就云打印:sv-print 这三个月都干了啥
哈喽哇!我是小不 ,不简说的不~
上一篇关于 sv-print 的文章已经是是七月初了,现在都十月中了。中间 sv-print 更了 6 次 ,版本号从 0.2.22 一路滚到 0.2.26,插件也越来越多啦~
我原本想数一下这三个月新增了多少东西,数到一半放弃了,太多了 😂
所以这期不按时间线写了,按模块写。
- 云服务、
- AI 设计、
- 数据源、
- 骑缝章、
- 矢量 PDF、
- Word 导出、
- 静默打印客户端纸张管理
......你用哪个就看哪个,不用从头翻。
一句话概括这三个月:
sv-print 从"一个打印设计器"变成了"一个能连云、能被 AI 帮我干活、能对接 SaaS 的打印设计器"。
一、新插件篇:这一波最大的变化
1. @sv-print/plugin-cloud 云服务插件 🌩️
这个是这三个月里分量最重的一个。它把 sv-print 从"孤岛"拉进了云端生态。
能干啥:
- 可插拔登录 :oauth / token / host / local 四种模式,面板直接切换,
autoLogin启动没登录就自动登录一次 - 服务端渲染:不用在浏览器里渲染,直接甩给服务端出 PDF
- 云打印 / 中转打印:打印任务下发到云端设备,本地机器只负责发指令
- 模板中心:模板存云端,多人协作、跨设备同步


js
import "@sv-print/plugin-cloud/dist/style.css";
import pluginCloud from "@sv-print/plugin-cloud";
const plugins = [
pluginCloud({
// host 缺省时走当前站点 /api
autoLogin: true, // 启动未登录时自动发起一次登录
auth: {
type: "oauth",
authorizeOrigin: "https://www.example.com",
clientId: "sv-print-demo",
// 生产推荐: client_secret 由宿主服务端持有
exchangeToken: async (code, state) => {
const res = await fetch("/my-backend/svprint/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ code, state }),
});
return (await res.json()).data; // { accessToken, ... }
},
},
}),
];
const cloud = designerUtils.cloud;
// 模板中心
cloud.templateCenter.show();
// 服务端渲染(同步) / 云打印 / 中转打印
await designerUtils.printTemplate.serverRender({ sync: true, type: "pdf" });
await designerUtils.printTemplate.cloudPrint({ device_id: "dev-xxx" });
✨ 10-18 这一版又补了几块:
云打印现在是三步走 ------选设备 → 选打印机 → 下发任务。以前是拿 device_id 直接打,现在有了 getDevicePrinters,能先看看这台设备上到底有几台打印机、哪台是默认:
js
const cloud = designerUtils.cloud;
const devices = await cloud.getDevices();
if (!devices.length) throw new Error("暂无云打印设备");
const device = devices[0];
const printers = await cloud.getDevicePrinters(device.device_id);
const printer =
printers.find((p) => p.isDefault && !p.disabled) ||
printers.find((p) => !p.disabled);
if (!printer) throw new Error("设备暂无可用打印机");
// 用当前设计器的模板 json 和打印数据
await cloud.cloudPrint({
device_id: device.device_id,
print_type: "template",
// template_data: templateJson, // 缺省取当前设计模板数据
// preview_data: { name: "张三" }, // 缺省取当前设计打印数据
print_options: { printer: printer.name, copies: 1, duplexMode: "simplex" },
});
⚠️ 这版还有个细节:非设计模式下也能用模板的云端方法 了,还有新建模板时会自动把云服务初始化好。你要是只在预览页塞了个
PrintTemplate,以前得自己手动 connect,现在省了。
2. @sv-print/plugin-view-ai AI 辅助设计 🤖
用自然语言生成模板 JSON 和打印数据。听着很玄学,实际用起来还行。
三种连接模式,可以同时配好、运行时切:
- provider:SDK 里塞 API Key,最省事,但 Key 会进前端包
- proxy:你后端代理一层,Key 留在服务端 ------ 生产环境推荐
- customService:完全自定义,你自己接什么模型都行
还有流式响应 、图片输入 (Ctrl+V 直接粘贴截图进去让它认)、AI 工具调用、提示管理器。

js
import "@sv-print/plugin-view-ai/dist/style.css";
import pluginViewAi from "@sv-print/plugin-view-ai";
const plugins = [
pluginViewAi({
// proxy 轻量代理模式: 服务端注入 API Key, 生产环境防泄露
proxy: {
proxyUrl: "https://my-server.com/api/ai/proxy",
targetBaseUrl: "https://api.openai.com/v1",
model: "gpt-4o",
// 从接口动态拉取模型列表 (优先级高于 models, 失败时回退)
modelsUrl: "https://my-server.com/api/ai/models",
},
showPromptManager: true, // 显示提示管理器
enableImageInput: true, // 支持 Ctrl+V 粘贴/上传/拖拽图片
defaultShow: false, // 默认不显示
icon: "sv-ai",
}),
];
🎯 实际业务场景:
新产品要给客户演示打印效果,以前得设计师出一个模板。现在让业务对着 AI 说"做一张带骑缝章的多页合同,页眉有公司名和 logo",出来的 JSON 直接进设计器微调一下就能用。
✨ 10-18 新增了 onApplied:
这个回调是"应用成功之后 "才触发,不是点一下确认就给你。以前你想在 AI 生成完之后同步点什么,只能在 onApply 里赌一把;现在可以确定模板和打印数据都落地了再干活:
js
import pluginViewAi from "@sv-print/plugin-view-ai";
const plugins = [
pluginViewAi({
// onApply 用于应用前拦截; onApplied 在用户确认且应用成功后触发
onApplied: async ({ templateJson, printData }) => {
if (printData != null) {
designerUtils.dataSource.setData("default", printData);
}
},
}),
];
说白了就是:AI 给你生成了打印数据,那字段树得跟着长出来对吧。这一行就把它串上了。
3. @sv-print/plugin-view-data-source 数据源插件 🗂️
多数据源字段树,拖拽或者点选就能把字段绑到元素上,打印的时候自动从数据源取数。

数据源支持三种取数方式,优先级从低到高:
data:直接给原始打印数据,用parseData解析成字段树request:配个 url +dataPath,点开懒加载fetcher:自定义函数,想怎么取怎么取
js
import "@sv-print/plugin-view-data-source/dist/style.css";
import dataSourcePlugin, { parseData } from "@sv-print/plugin-view-data-source";
const plugins = [
dataSourcePlugin({
sources: [
{
name: "default",
title: "默认数据源",
data: parseData(printData), // 解析打印数据为字段树
rawData: printData, // 原始打印数据
},
{
name: "user",
title: "用户数据",
defaultExpand: true, // 默认展开
// 接口方式获取字段
request: { url: "/api/user/fields", dataPath: "data.list" },
},
{
name: "order",
title: "订单数据",
defaultExpand: false, // 默认折叠, 点击后懒加载
// 自定义函数获取字段 (优先级最高)
fetcher: async () => {
const res = await fetch("/api/order/schema");
return (await res.json()).fields;
},
},
],
pickerMultiple: true, // 多选模式
pickerCloseOnSelect: false, // 多选模式下选后不关闭弹窗
onFieldSet: (field, element, optionName) => {
// 返回 false 可以拦截这次绑定
if (field.type === "object") return false;
const { type } = element.printElementType;
if (optionName === "field") {
// 表格/子模板要数组, 列字段名不能是数组
if (
["table", "box"].some((i) => type.includes(i)) &&
field.type !== "array"
) {
return false;
}
}
return true;
},
}),
];
✨ 10-18 新增:字段树右键能改能删了。
之前字段树只能看,现在右键可以直接改字段名、显示名称、类型。改的时候嵌套字段的路径会同步更新(不会出现改了父级子级路径还挂着老名字的情况),删的时候子字段一起带走。
更关键的是运行时 API 终于来了:
js
const dataSource = designerUtils.dataSource;
// 更新已有数据源配置, 保留未传配置, 清除缓存及手动字段覆盖
dataSource.updateSource("product", { title: "商品资料" });
// 替换原始数据并立即重建字段树, 清除旧取数配置及手动字段覆盖
dataSource.setData("product", { sku: "A", qty: 2 });
const productData = await dataSource.getRawData("product");
const allData = await dataSource.getRawData(); // 按打印规则合并所有数据源
这套 API 一出来,数据源就从"设计时配一次"变成"运行时能改"了。上面的
onApplied里的setData就是这么用的。
4. 元素插件三连:骑缝章、PDF 元素、元素工具集 🖋️
骑缝章 @sv-print/plugin-ele-seal
按实际渲染页数切分章图,逐页在纸边显示,拼合起来正好还原完整章图。
这个功能在纸质合同、凭证类打印里是真刚需
js
import pluginEleSeal from "@sv-print/plugin-ele-seal";
const plugins = [pluginEleSeal()];
// 注册后左侧"插件"分组出现"骑缝章"元素, 拖入模板即可
// 章图来源: field 打印数据字段 > src 直接配置的图片地址
// pageRange 盖章页范围: "all" | "first:3" | "1-3,5" | "odd" | "even"

PDF 元素 @sv-print/plugin-ele-pdf
把 PDF 文件当元素塞进模板里渲染,支持 url / base64 / dataURL 三种来源,流式分页、文字叠加层、页码范围。
叠加层支持 #{field} 和 #{pdfMeta.xxx} 占位符 ------ 意思是 PDF 页面上还能盖动态内容,比如在合同 PDF 上盖一个"已审核"水印章。
js
import pluginElePdf from "@sv-print/plugin-ele-pdf";
const plugins = [pluginElePdf()];
// 元素参数: pdfUrl 数据源 | pdfPageRange 页码范围如 "1,3,5-8"
// pdfOverlays 叠加层支持 #{field} 与 #{pdfMeta.xxx} 占位符

元素工具集 @sv-print/plugin-view-ele-tools
选中元素时元素上方浮一条工具栏,还能显示尺寸标签和微调按钮。
js
import pluginViewEleTools, {
getDefaultTools,
} from "@sv-print/plugin-view-ele-tools";
const plugins = [
pluginViewEleTools({
showSizeBox: true, // 元素底部显示尺寸标签 + 微调按钮
sizeBoxUnit: "mm",
sizeBoxStep: 5,
// 按元素类型自定义工具
resolveTools: (ctx) => {
if (ctx.elementType === "text") {
return [
...getDefaultTools(),
{
key: "my-extra",
title: "自定义",
icon: "svicon sv-font-big",
click: (ctx) => {
ctx.selectEls.forEach((el) =>
el.updateOption("fontSize", 20, false)
);
ctx.refreshPageStructure();
},
},
];
}
return null; // 使用内置默认
},
}),
];

讲真,尺寸标签这个我太喜欢了。手动调宽高全靠眼睛,标签一挂上毫米数,改起来心里有底。
5. 导出三连:矢量 PDF、Word、Excel 增强 📦
矢量 PDF @sv-print/plugin-api-pdf-vector
之前导出的 PDF 是"图片 PDF",放大就糊、文字选不中。这个插件导的是矢量 的:文字可选中、可搜索、可复制,矢量图形,字体子集嵌入。
js
import pluginApiPdfVector from "@sv-print/plugin-api-pdf-vector";
// 需一并安装 peerDependency: @imggion/html2realpdf
const plugins = [pluginApiPdfVector({})];
let printData = { name: "张三" };
// 1. 直接下载
await hiprintTemplate.toVectorPdf(printData, { name: "pdf名称" });
// 2. 获取 blob 自行处理
const res = await hiprintTemplate.toVectorPdf(printData, {
isDownload: false,
type: "blob", // blob、arraybuffer、bloburl、datauristring
onProgress: (cur, total, phase) => {
// phase: "snapshot" | "wasm" | "complete"
console.log("进度", phase, cur, total);
},
});

⚠️ 注意 peerDependency 那个
@imggion/html2realpdf,漏装直接跑不起来。别问我怎么知道的 🙃
Word 导出 @sv-print/plugin-api-word
导出可编辑的 docx。两种模式:
editable(默认):网格表格还原,文本/表格/图片在 Word 里原生可编辑positioned:浮动定位,所见即所得还原设计位置
js
import pluginApiWord from "@sv-print/plugin-api-word";
const plugins = [pluginApiWord()];
// editable(默认): 网格表格还原, 文本/表格/图片在 Word 中原生可编辑
template.toWord(printData, { mode: "editable", name: "出库单" });
// positioned: 浮动定位, 所见即所得还原设计位置
template.toWord(printData, { mode: "positioned" });
// 不自动下载, 拿到 Blob 自行上传
template.toWord(printData, { isDownload: false }).then((blob) => {
const form = new FormData();
form.append("file", blob, "report.docx");
return fetch("/api/upload", { method: "POST", body: form });
});

Excel 导出增强 @sv-print/plugin-api-excel
新增 customTypeResolver / onBeforeWrite / multiSheet / pageGap,文本重叠和换行效果也改了,还原度更高。
多 sheet 这个参数比较顶 ------ 之前一份报表导出一个 sheet,现在可以按业务拆多个。
骑缝章、PDF、Word 这几个渲染器资源,客户端里也能在线下载更新
客户端 2.0.9 把 pdfjs、elePdf、eleSeal、textAuto、formatter、eleW2html、eleBox、eleStable 都做成在线渲染器资源了,升级不用等客户端打包。
6. 新手引导 @sv-print/plugin-driver 🧭
新用户进来一脸懵,不知道元素怎么拖、面板是什么、预览在哪。这个插件把整套引导步骤配好了。
支持 cacheKey 做版本缓存(引导文案改了才重新走一遍)、onHighlightStarted / onDeselected 自定义高亮前后的动作、元素可以传函数动态找、还能 disableActiveInteraction 屏蔽高亮期间的交互。
js
import "@sv-print/plugin-driver/dist/style.css";
import pluginDriver from "@sv-print/plugin-driver";
// 重置滚动条位置:driver.js 执行时会居中显示。执行完后重置滚动条,避免样式错乱。
const restScroll = () => {
globalThis.$(`#paperBox`).scrollTop(0);
globalThis.$(`.svp-designer`).scrollTop(0);
globalThis.$(`#SVPrint`).scrollTop(0);
};
const plugins = [
pluginDriver({
// 缓存key,判断某个版本的 steps 是否需要执行
cacheKey: "driver:DesignGuidance-v1",
steps: [
{
element: ".svp-header",
popover: {
title: "这是菜单栏",
description: "支持插槽,也可以完全自定义哟!",
},
},
{
element: () => {
const previewBox = document.querySelector(".previewBox");
return previewBox.closest(".svp-modal-box");
},
onHighlightStarted: () => designerUtils.preview.show(),
onDeselected: () => designerUtils.preview.hide(),
popover: {
title: "这是预览弹窗",
description: "支持浏览器打印、静默打印、导出图片、导出pdf。",
},
},
{
element: "#dragBox-options",
side: "left",
popover: {
title: "属性区域",
description: "点面板看面板属性,点元素看元素属性。",
},
},
],
autoStart: true, // 是否自动开启引导, 默认 true
driverOptions: {
showProgress: true,
nextBtnText: "下一步",
prevBtnText: "上一步",
doneBtnText: "我知道了",
},
onComplete: () => {
restScroll();
console.log("onboarded");
},
onSkip: () => {
restScroll();
console.log("onSkip");
},
}),
];

上面那三条 steps 是我精简过的,实际内置了一整套(菜单栏、元素列表、画布、属性区、页面结构、历史记录、概览图、页眉页脚线......大概十七八步)。直接用现成的也行。
7. 参数修改 @sv-print/plugin-options-modify 弹窗式下拉
之前这个插件是把下拉框换成别的控件,这版内置了弹窗式单选/多选下拉。
选项特别多的时候,下拉框拉到天荒地老也找不完,现在直接弹窗,还能搜索、还能自定义添加、还能加校验。
js
import pluginOptionsModify, {
getSelectToMultiselectModifyHandler,
PopupSelectValueGetHandler,
PopupSelectValueSetHandler,
PopupSelectDestroyHandler,
} from "@sv-print/plugin-options-modify";
const plugins = [
pluginOptionsModify({
list: [
{
filter: (key, target) => {
// 忽略一些特殊参数
return (
!["watermarkOptions", "panelLayoutOptions", "dataType"].includes(
key
) &&
target.hasClass("hiprint-option-item") &&
target.find(".hiprint-option-item-field select").length > 0
);
},
modifyHandler: getSelectToMultiselectModifyHandler({
mode: "single", // "single" | "multiple"
showSearch: true, // 是否显示搜索框
allowCustom: true, // 是否允许自定义添加
// 显示顶部栏:选项数量超过5个时显示
shouldShowTopbar: (count, key) => count > 5,
// 自定义添加前的校验:只接受数字
beforeAddCustom: (v, key) => {
if (["fontSize", "lineHeight"].includes(key)) {
return !isNaN(Number(v)) || (alert("请输入数字"), false);
}
return true;
},
}),
setValueHandler: PopupSelectValueSetHandler,
getValueHandler: PopupSelectValueGetHandler,
destroyHandler: PopupSelectDestroyHandler,
},
],
}),
];
8. 国际化 @sv-print/plugin-i18n 继续补 🔤
- 支持自定义参数选择器列表(
optionsSelectorList),你的自定义参数也能翻译 - 元素 / 面板 / 设计器 三个层级都有回调:
onPanelSelected/onElementSelected/onDesigned - 新增回调延迟处理 :
onDesignedDelay/onSettingsDelay/onPreviewDelay - 设置弹窗、预览弹窗都接上了 hook(
onSettings/onPreview) - 处理了特殊 DOM(带
i/span子标签的 button 这种) missingKeyHandler能把缺的 key 收集起来,方便批量补翻译
延迟处理这个挺实用 ------ 有些 UI 组件是异步渲染的,翻译跑早了等于没翻,onDesignedDelay: 600 就能等它。
js
import pluginI18n from "@sv-print/plugin-i18n";
const plugins = [
pluginI18n({
resources: {
/* ... */
},
onDesignedDelay: 600, // 设计回调延迟(部分 UI 调整后再翻译)
onSettingsDelay: 0, // 设置回调延迟
onPreviewDelay: 0, // 预览回调延迟
onSettings: (opts) => {
/* opts.type: "show" 等 */
},
onPreview: (element) => {
/* ... */
},
}),
];
9. 编辑器 @sv-print/plugin-view-code-edit 📝
模板数据、打印数据、元素 json 都能用 CodeMirror 编辑了。缩进、换行、自定义补全、搜索面板全都能配,onReady 里还能 appendExtension 追加自己的扩展(监听文档变化、加快捷键),不覆盖原有配置。
js
import "@sv-print/plugin-view-code-edit/dist/style.css";
import pluginViewCodeEdit from "@sv-print/plugin-view-code-edit";
const plugins = [
pluginViewCodeEdit({
editorConfig: {
lineMaxLength: 120,
searchConfig: { caseSensitive: true, regexp: true },
tabSize: 2,
lineWrapping: true,
// 自定义补全项(会与内置 hinnn API 补全合并)
autocompletion: [
{ label: "myFunc", type: "function", info: "自定义函数" },
],
onReady: ({ view, state, appendExtension, EditorView }) => {
// 1. 监听文档变化
appendExtension(
EditorView.updateListener.of((update) => {
if (update.docChanged) {
console.log("内容变化:", update.state.doc.toString());
}
})
);
// 2. 追加快捷键
appendExtension(
keymap.of([
{
key: "Ctrl-s",
run: () => {
console.log("保存:", view.state.doc.toString());
return true;
},
},
])
);
},
},
}),
];

10. 格式化器 @sv-print/plugin-formatter 占位符匹配
${xxx} 现在可以直接执行对应 key 的格式化器了,而且能按正则匹配字段名自动套格式化。
js
import pluginFormatter from "@sv-print/plugin-formatter";
const plugins = [
pluginFormatter({
name: "格式化器",
funList: [
{ name: "金额", key: "money", fun: (v) => `¥ ${Number(v).toFixed(2)}` },
{ name: "布尔", key: "bool", fun: (v) => (v ? "是" : "否") },
{
name: "打印时间",
key: "datetime",
fun: (v) => new Date().toLocaleString(),
},
],
regexList: [
// 1. RegExp:对 key / fullKey 依次 test(),命中即匹配
{ regex: /(^|\.)(amount|price|total)$/i, key: "money" },
// 2. 字符串:与 key 或 fullKey 严格相等
{ regex: "isActive", key: "bool" },
// 3. 函数:回调 (key, value, fullKey) => boolean
{ regex: (key, value, fullKey) => key === "datetime", key: "datetime" },
],
}),
];
这条最妙的是第一条正则:
amount/price/total结尾的字段自动变金额格式。后端给什么字段名都不用管了。
二、设计器本体:不太起眼但很顶的那些
1. 设置弹窗 + onSettings hook + header 外部更新
7-30 加了设置弹窗(主题、自定义主题、语言、帮助、使用引导、快捷键),10-10 加了 onSettings hook 让插件能注入自己的 tab。
js
// 打开设置 // "basic" | "language" | "help" | "shortcut" | "about"
designerUtils.openSettings("basic");
designerUtils.closeSettings();
designerUtils.changeTheme("winter");
designerUtils.changeLanguage("繁体中文");
// 插件: 在设置弹窗注入自定义 tab
const myPlugin = {
name: "my-plugin",
hooks: [
{
hook: "onSettings",
name: "mySettingsTab",
run: (opts) => {
if (opts.type !== "tabs") return; // type: "tabs" | "show"
return {
key: "my-tab",
label: "我的设置",
icon: "sv-tool",
sort: 3, // 排序, 内置 tab 为 0~5
render: (el) => {
el.innerHTML = "<div>自定义设置内容</div>";
},
destroy: (el) => {},
};
},
},
],
leastHiprintVersion: "0.3.42",
};
header 也能从外部改了 ------ 元素列表和菜单列表都能动态增删,可以原地改也可以返回新数组自己排序:
js
designerUtils.header.updateMenuList((list) => {
list.splice(0, 0, {
icon: "sv-templates",
title: "模板中心",
click: (util) => {},
});
});
designerUtils.header.updateEleList((list) => {
/* ... */
});
设计器底部还多了客户端连接状态、云服务登录态、版本号(点击切换引擎版本)。

2. beforeGetHtml 这条 hook 线
7-30 加的,getHtml / print / print2 都能用,9-28 补了 data 参数和异步支持。
js
let printData = {};
await hiprintTemplate.getHtml(printData, {
beforeGetHtml: ({ template, html }) => {
// 做一些处理, 支持 promise
return html;
},
});
同批还有 beforePrint / beforePrint2 也是 data + 异步。能返回 Promise 这一点很重要 ------ 我以前想在打印前拉一次接口(拿最新的价格、库存),只能自己绕,现在直接 await。
3. 表格能力
- 表体行处理模式(新):缩小字体 / 不换行+省略 / 不换行,三选一。表格塞不下的时候终于不是"硬撑到溢出"了
- 右键菜单编辑表头(新):不用再改 columns 排序那一套
- 表格分页逻辑优化:首页空行、表格头处理、合并删除行、标题双击下拉选源
- 多选元素复制粘贴时按原有布局粘贴;旋转 ±90° 时客户端打印纸张的适配
- 显示规则 / 隐藏规则支持多选了
4. 交互细节
Alt + Wheel/Alt + +/-缩放画布,快捷键说明同步更新了- dragbox 支持
cache缓存拖拽后的位置,面板尺寸变化标尺跟着更新 showModal的 content 支持 html 字符串了(以前传 html 会被当纯文本显示)- 预览组件支持缩略图和样式配置
- 拖动方向支持键盘移动、框选移动,指定拖动方向时不处理吸附
- dragbox 调整宽高后会自动吸附贴边

5. CLI 支持 dev 环境
create-sv-print-plugin 这个 CLI 现在能创建 dev 环境了,写插件可以热调试,不用每次打包再装。
三、静默打印客户端 2.0.9
安装包:pan.quark.cn/s/428f36751...
1. 纸张管理(这个我觉得该单独吹一下)📄
打印机详情里加了纸张管理面板:纸张列表、搜索、刷新、自定义纸张尺寸 。新增三个接口 ------ paperSizes、validatePaper、customPaper。
⚠️ 单位先说清楚 :三个接口的宽高参数传的都是微米 (μm),
1mm = 1000μm。所以 A4 是210000 × 297000,不是210 × 297。返回结果里两个都给:widthMicrons/heightMicrons是微米,widthMm/heightMm是毫米,前端直接拿后者显示就完事。
js
// http 服务方式
// 纸张列表(单位: 微米, 同时返回 mm)
const list = await fetch(
`http://127.0.0.1:7071/printer/getPaperSizes?printerName=${encodeURIComponent(
"printerName"
)}`
).then((r) => r.json());
// => { ok: true, printerId, papers: [{ name, widthMm, heightMm, widthMicrons, heightMicrons, paperCode? }] }
// 校验自定义纸张尺寸是否受驱动支持
const validate = await fetch("http://127.0.0.1:7071/printer/validatePaper", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
printerName: "printerName",
widthMicrons: 210000,
heightMicrons: 297000,
}),
}).then((r) => r.json());
// => { ok: true, supported: true, orientation, reason?, validatedWidthMicrons?, validatedHeightMicrons? }
// 构造一个打印任务可用的自定义纸张描述并校验
const custom = await fetch("http://127.0.0.1:7071/printer/customPaper", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
printerName: "printerName",
widthMicrons: 80000,
heightMicrons: 60000,
}),
}).then((r) => r.json());
// => { ok: true, supported, custom: true, driverWidthMicrons?, driverHeightMicrons?, reason? }
💡 为什么这个值得吹?
自定义纸张这事儿的真相是:光告诉打印机"我要用 80×60mm"是没用的,得看驱动认不认 。所以 validatePaper 会回你 supported: true/false 和一个 reason,customPaper 还能返回驱动实际认下来的尺寸(driverWidthMicrons)。这才是真正能落地的信息,比"设置成功"四个字有用一百倍。

js
// socket / mqtt 方式 (监听发送共用同一个事件名)
socket.emit("paperSizes", "printerName");
socket.on("paperSizes", (res) => {
console.log(res.papers);
});
socket.emit("validatePaper", {
printerId: "printerName",
widthMicrons: 210000,
heightMicrons: 297000,
});
socket.on("validatePaper", (res) => {
console.log(res.supported);
});
socket.emit("customPaper", {
printerId: "printerName",
widthMicrons: 80000,
heightMicrons: 60000,
});
socket.on("customPaper", (res) => {
console.log(res);
});
2. 调试能力
openDevTools 可以打开指定打印窗口 / 模板渲染窗口的开发者工具,预览窗口也加了调试按钮。
js
// 打开打印窗口 / 模板渲染窗口 的开发者工具 (已打开则关闭)
await fetch("http://127.0.0.1:7071/printer/openDevTools", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ printerName: "printerName", isRenderer: false }),
});

排查渲染问题终于不用猜了,直接开 DevTools 看 DOM。
3. 打印日志能改能重打
打印日志加了刷新 按钮,数据用 CodeMirror 编辑器 查看,支持编辑数据后重打;打印机详情的数据也改成编辑器查看。
这个我给满分。以前打印效果不对,只能去日志里扒那一坨 JSON,改完还得手动重新发请求,现在改完直接重打。
4. print 接口现在会等任务跑完
js
// http 调用 print, 现在会等待队列任务完成并返回执行结果
const result = await fetch("http://127.0.0.1:7071/printer/print", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
printer: "printerName",
printType: "html",
html: "<h1>sv-print</h1>",
copies: 2,
landscape: false,
duplexMode: "longEdge",
// capture 打印可透传 snapdom 参数
captureType: "jpeg",
captureOptions: { dpr: 3, backgroundColor: "#fff" },
}),
}).then((r) => r.json());
// => { success: true, msg: '', data: ... }
以前是发出去就返回,我这边只能自己轮询日志。现在直接给执行结果,省事。
5. 其他
| 新增/优化 | 说明 |
|---|---|
| 云打印 SaaS | 客户端侧适配 |
| http 端口可配置 | 1024 ~ 65535,默认 7071 |
| 任务栏显示开关 | 配合下面这条一起用 |
| 关闭不退出应用 | 按 showInTaskbar 决定最小化到任务栏还是藏托盘,协议唤醒时自动弹窗 |
| 渲染窗口开关 | 关掉模板渲染窗口能省内存 ⚠️ 但关了之后传模板 json 打印就不可用了 |
| capture 打印 | 支持透传 captureOptions(snapdom 参数,默认 dpr: 2) |
| 插件资源升级 | 骑缝章、word、excel、组合框、表格、pdf、textAuto、formatter |
| 渲染器资源在线更新 | pdfjs、elePdf、eleSeal、textAuto、formatter、eleW2html、eleBox、eleStable |
| windows pdf 打印 | pdf-to-printer 升到 5.8.1,支持 paperKind |
⚠️ 升级提醒
::: tip 温馨提示 本次更新请记得更新 print-lock.css 打印样式 :::
还有一个要留神:如果为了省内存把"开启模板渲染窗口"关了,那"传模板 json 打印"就不可用了。两个开关是有依赖关系的,别为了省那点内存把功能关了。
写到最后
三个月,6 次更新,20 多个包。写完这篇我脑子是木的 😂
但有一说一,这波更新的方向我挺认的。以前 sv-print 是"设计器 + 渲染器",这三个月补的是它外面的那一圈:连云、连 AI、连数据源、连你后端的口子。这些都不是设计器本身该干的事,但恰恰是让它真能在生产落地的部分。
我自己的心法就一条:新东西先看它有没有把"我以前要自己造轮子的那个轮子"给造了。
这三个月里对我最有用的三个:
- 自定义纸张的
supported+reason------ 不用再猜驱动认不认了 - 打印日志编辑后重打 ------ 调试时间直接砍半
beforeGetHtml支持 async ------ 打印前拉接口这件事终于不用绕路
至于 AI 设计那个,坦白说我现在还在观望阶段。让它生成"标准快递面单"挺好用,让它生成"我们公司那个很特殊的、带骑缝章、四页、页眉页脚都要动态的合同"------它给的 JSON 还是得我改。所以我的用法是:让它把 70% 的体力活干掉,剩下 30% 我自己收拾。这个比例我觉得挺健康。
AI 这东西吧,讲真,好用是好用,别当它是你同事就行 😂
整理时间:2026-10-19 覆盖版本:sv-print@0.2.23 ~ 0.2.26,@sv-print/hiprint@0.3.37 ~ 0.3.42,静默打印客户端@2.0.9 原始更新日志:sv-print 更新日志