"打印精灵"扩展小程序Extension开发指南


打印精灵的Web插件, 桌面版, Center版均支持扩展小程序, 开发建议使用桌面版.

目录结构

extension-name/
├── extension.json     # 扩展配置文件(必需)
├── entry.html         # 入口 HTML 文件
├── app.js             # 前端应用代码
├── styles.css         # 样式文件
├── icon.png           # 扩展图标(根目录)
└── server/            # 服务端脚本
    ├── script_1.js
    └── script_2.js

extension.json 配置

 1{
 2  "id": "extension-id",
 3  "name": "扩展名称",
 4  "desc": "扩展描述",
 5  "icon": "icon.png",
 6  "entry": "entry.html",
 7  "version": "x.x.x",
 8  "config": {
 9    "key1": "value1",   //缺省配置项目
10    "key2": "value2"
11  }
12}

前端 API

前端API包括: 配置API, 核心功能API, 服务端脚本调用API三类, 前端程序通过这三类API可以实现复杂的业务程序.

配置 API

1// 读取配置
2const cfg = await (await fetch(`/app-ext/getcfg?app=${extensionId}`)).json()
3
4// 保存配置
5await fetch(`/app-ext/setcfg?app=${extensionId}`, {
6    method: 'POST',
7    headers: { 'Content-Type': 'application/json' },
8    body: JSON.stringify({ key: value })
9})

核心功能API

API 方法 说明
/api/v2/get-printer-list GET 获取打印机列表
/api/v2/print POST 打印标签
/api/v2/close POST 关闭打印机(生成文件)
/utils/thumb GET 标签预览
 1// 获取打印机列表
 2const { printers } = await (await fetch(`/api/v2/get-printer-list`)).json()
 3// printers: [{ name, type, act }, ...]
 4
 5// 打印标签
 6await fetch("/api/v2/print", {
 7    method: "POST",
 8    headers: { "Content-Type": "application/json" },
 9    body: JSON.stringify({
10        cmd: "PrintLabel",
11        data: { tpid: label_id, vars: record },
12        opts: { printer: printerName }
13    })
14})
15
16// 关闭打印机并生成文件
17await fetch("/api/v2/close", {
18    method: "POST",
19    headers: { "Content-Type": "application/json" },
20    body: JSON.stringify({ printer: "Microsoft Print to PDF" })
21})
22
23// 标签预览
24const vars = encodeURIComponent(JSON.stringify(record))
25const previewUrl = `/utils/thumb?id=${templateId}&vars=${vars}`

服务端脚本调用

对于无法通过核心API实现的功能, 可以通过服务端脚本实现, 在前端调用服务端脚本的API如下

1// GET 请求
2const result = await fetch(`/app-ext-run/${extensionId}/script.js?key=value`)
3
4// POST 请求  
5const result = await fetch(`/app-ext-run/${extensionId}/script.js`, {
6    method: 'POST',
7    headers: { 'Content-Type': 'application/json' },
8    body: JSON.stringify({ data })
9})

服务端 API

打印精灵扩展框架支持用JS开发服务端脚步程序, 并提供了一系列服务端API用于功能开发

上下文对象 ctx

属性/方法 说明
ctx.request.query URL 查询参数
ctx.request.body POST 请求体
ctx.cfg 扩展配置
ctx.json(data) 返回 JSON 响应
ctx.type(mime) 设置响应类型
ctx.data(buffer) 返回二进制数据
ctx.file(path) 返回文件

DB操作

 1// 打开 DB 表
 2const tbl = DB.Open(excelPath[, "SheetName"])
 3
 4// 查询 - 获取第一条
 5const result = tbl.Query().Where("列名", "=", value).First()
 6
 7// 查询 - 获取所有匹配
 8const list = tbl.Query().Where("列名", "=", value).Find()
 9
10// 关闭表
11db.close(excelPath, "SheetName")

打印/预览函数

1// 生成预览图
2rc = preview(labelId, vars, { printer: "preview", dpmm: "8" })
3ctx.type("png")
4ctx.data(rc.data)
5
6// 打印标签
7rc = print(labelId, vars, { printer: printerName })

完整示例

entry.html

 1<!DOCTYPE html>
 2<html>
 3<head>
 4  <link rel="stylesheet" href="styles.css">
 5</head>
 6<body>
 7  <div id="root"></div>
 8  <script src="/js/preact-bundle.js"></script>
 9  <script type="module" src="app.js"></script>
10</body>
11</html>

app.js

 1const { Preact, html } = window;
 2const { useState, useEffect, render } = Preact;
 3
 4function Setup(props) {
 5    const [excel, setExcel] = useState();
 6    const [labelId, setLabelId] = useState();
 7    const [printer, setPrinter] = useState("");
 8    const [printers, setPrinters] = useState([]);
 9
10    useEffect(() => {
11        // 加载打印机列表
12        fetch(`/api/v2/get-printer-list`)
13            .then(r => r.json())
14            .then(rc => setPrinters(rc.printers || []));
15        
16        // 加载已保存的配置
17        fetch(`/app-ext/getcfg?app=myextension`)
18            .then(r => r.json())
19            .then(cfg => {
20                setExcel(cfg.excel);
21                setLabelId(cfg.label_id);
22                setPrinter(cfg.printer || "");
23            });
24    }, []);
25
26    const saveSetup = async () => {
27        await fetch(`/app-ext/setcfg?app=myextension`, {
28            method: 'POST',
29            headers: { 'Content-Type': 'application/json' },
30            body: JSON.stringify({ excel, label_id: labelId, printer })
31        });
32        props.onSetup();
33    };
34
35    return html`
36        <section>
37            <label>数据文件</label>
38            <input value=${excel} onChange=${e => setExcel(e.target.value)} />
39            
40            <label>标签ID</label>
41            <input value=${labelId} onChange=${e => setLabelId(e.target.value)} />
42            
43            <label>打印机</label>
44            <select value=${printer} onChange=${e => setPrinter(e.target.value)}>
45                ${printers.map(p => html`<option value=${p.name}>${p.name}</option>`)}
46            </select>
47            
48            <button onClick=${saveSetup}>保存</button>
49        </section>
50    `;
51}
52
53function Main(props) {
54    const [msg, setMsg] = useState("");
55
56    const printLabel = async (id) => {
57        const res = await fetch(`/app-ext-run/myextension/print.js`, {
58            method: 'POST',
59            headers: { 'Content-Type': 'application/json' },
60            body: JSON.stringify({ id })
61        });
62        const rc = await res.json();
63        setMsg(rc.rc === "OK" ? "打印成功" : rc.msg);
64    };
65
66    return html`
67        <div>
68            <button onClick=${() => printLabel("ABC123")}>打印</button>
69            <p>${msg}</p>
70        </div>
71    `;
72}
73
74function App() {
75    const [inSetup, setInSetup] = useState(true);
76    return html`
77        <main>
78            <h1>我的扩展</h1>
79            ${inSetup 
80                ? html`<${Setup} onSetup=${() => setInSetup(false)} />`
81                : html`<${Main} />`
82            }
83        </main>
84    `;
85}
86
87render(html`<${App} />`, document.getElementById('root'));

server/print.js

 1var body = ctx.request.body;
 2try {
 3    let { excel, label_id, printer } = ctx.cfg;
 4    
 5    let tbl = db.open(excel, "Sheet1");
 6    let vars = tbl.Query().Where("设备编码", "=", body.id).First();
 7    
 8    print(label_id, vars, { printer: printer });
 9    ctx.json({ rc: "OK" });
10} catch (e) {
11    ctx.json({ rc: "ERR", msg: "" + e });
12}

server/preview.js

 1var id = ctx.request.query.id;
 2try {
 3    let { excel, label_id } = ctx.cfg;
 4    
 5    let tbl = db.open(excel, "Sheet1");
 6    let vars = tbl.Query().Where("设备编码", "=", id).First();
 7    
 8    rc = preview(label_id, vars, { printer: "preview", dpmm: "8" });
 9    ctx.type("png");
10    ctx.data(rc.data);
11} catch (e) {
12    ctx.file("err.png");
13}

server/close.js

1try {
2    let { excel } = ctx.cfg;
3    if (excel) {
4        db.close(excel, "Sheet1");
5    }
6    ctx.json({ rc: "OK" });
7} catch (e) {
8    ctx.json({ rc: "ERR", msg: "" + e });
9}

API 汇总

API 方法 说明
/app-ext/getcfg?app={id} GET 获取扩展配置
/app-ext/setcfg?app={id} POST 保存扩展配置
/app-ext-run/{id}/{script}.js GET/POST 调用服务端脚本
/api/v2/get-printer-list GET 获取打印机列表
/api/v2/print POST 打印标签
/api/v2/close POST 关闭打印机
/utils/thumb?id={tplId}&vars={json} GET 标签预览图

最佳实践

  1. 路径规范: 服务端脚本路径为 /app-ext-run/{extensionId}/{script}.js
  2. 配置管理: 使用 ctx.cfg 获取配置,配置通过 /app-ext/setcfg 保存
  3. 错误处理: 服务端统一返回 { rc: "OK"/"ERR", msg: "..." }
  4. 资源释放: 操作完成后调用 db.close() 释放 Excel 文件
  5. Preact 框架: 前端使用 Preact,通过 /js/preact-bundle.js 引入

Leave Your Message

login