AI 帮你画模板、点一下就云打印:sv-print 这三个月都干了啥

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 更新日志

相关推荐
Amos_Web1 小时前
Rspack 源码解析(二十六):CssPlugin 与 CssChunkingPlugin 如何保证 CSS 顺序并切分 chunk
前端
孙浩君1 小时前
0行手写代码,我用AI做了一个打工人模拟器(含3D霓虹城市漫游)
前端·后端
夏天要喝冰可乐1 小时前
用 Codex 连接 Blender,我做了一块可以走进楼宇的大屏
前端·three.js
Dawson Zhu1 小时前
【AI Infra小白入门⑦】Transformer架构工程拆解:从Attention到MLA的优化密码
人工智能·语言模型·架构·aigc·agi
醒醒1111 小时前
从微信小程序到全端:一个安全扫描器的演进之路(v0.5.0 → v0.6.0)
前端·安全
Csvn1 小时前
灰度发布:让变更只影响 1% 用户(R02)
人工智能·aigc·agent
seconp1 小时前
计算机毕设做什么项目?整理了 80 个 Web、小程序、AI 与数据方向
前端·小程序·课程设计
大明二代2 小时前
从零构建 Mastra RAG MVP:DeepSeek、SiliconFlow 与 PostgreSQL/pgvector 实战
javascript·数据库·人工智能