Files
qhmes/jeecgboot-vue3/src/views/print/template/utils/printDotBridge.ts

202 lines
5.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* PrintDot 本地桥接器Wails 客户端WebSocket 协议封装。
* 默认地址 ws://127.0.0.1:1122/ws ,与 PrintDot-Client 中 Bridge 默认端口一致。
*/
const LS_WS_URL = 'qhmes_print_dot_ws_url';
const LS_KEY = 'qhmes_print_dot_key';
export type PrintDotPrinter = { name: string; isDefault?: boolean };
export function getPrintDotBridgeConfig() {
return {
wsUrl: localStorage.getItem(LS_WS_URL) || 'ws://127.0.0.1:1122/ws',
key: localStorage.getItem(LS_KEY) || '',
};
}
export function setPrintDotBridgeConfig(wsUrl: string, key: string) {
localStorage.setItem(LS_WS_URL, (wsUrl || '').trim());
localStorage.setItem(LS_KEY, key ?? '');
}
function buildWsUrl(wsUrl: string, key: string) {
const base = (wsUrl || '').trim();
const k = (key || '').trim();
if (!k) {
return base;
}
const sep = base.includes('?') ? '&' : '?';
return `${base}${sep}key=${encodeURIComponent(k)}`;
}
/** 连接后服务端会先推送 printer_list本方法读取该列表后关闭连接 */
export function fetchPrintDotPrinters(timeoutMs = 8000): Promise<PrintDotPrinter[]> {
const { wsUrl, key } = getPrintDotBridgeConfig();
return new Promise((resolve, reject) => {
let done = false;
const ws = new WebSocket(buildWsUrl(wsUrl, key));
const timer = window.setTimeout(() => {
if (done) {
return;
}
done = true;
try {
ws.close();
} catch {
// ignore
}
reject(new Error('连接 PrintDot 超时,请确认客户端已启动且 WebSocket 地址正确'));
}, timeoutMs);
const finish = (fn: () => void) => {
if (done) {
return;
}
done = true;
window.clearTimeout(timer);
try {
ws.close();
} catch {
// ignore
}
fn();
};
ws.onmessage = (ev) => {
try {
const data = JSON.parse(ev.data as string);
if (data?.type === 'printer_list' && Array.isArray(data.data)) {
const list = data.data
.map((p: any) => ({
name: String(p?.name || '').trim(),
isDefault: p?.isDefault === true,
}))
.filter((p: PrintDotPrinter) => !!p.name);
finish(() => resolve(list));
}
} catch {
// ignore
}
};
ws.onerror = () => {
finish(() => reject(new Error('无法连接 PrintDot WebSocket请检查地址与客户端是否运行')));
};
});
}
/** 将 PrintDot 返回的英文错误转为带处理步骤的提示(便于运维排查) */
function enhancePrintDotErrorMessage(raw: string): string {
const m = String(raw || '').trim();
if (/SumatraPDF\.exe not found/i.test(m) || /SUMATRAPDF_PATH/i.test(m)) {
return `${m}。本地处理PrintDot 依赖 SumatraPDF 静默打印 PDF。请安装 Sumatra PDF 后任选其一:将 SumatraPDF.exe 放在 PrintDot 客户端 exe 同目录;或将 Sumatra 安装目录加入系统 PATH或设置用户/系统环境变量 SUMATRAPDF_PATH 指向 SumatraPDF.exe 的完整路径,然后重启 PrintDot。`;
}
return m;
}
/**
* 发送 PDF 打印任务content 为 Base64 PDF可为纯 Base64 或 data:application/pdf;base64, 前缀)
* 连接建立后服务端可能先推送 printer_list需忽略直至收到 status。
*/
export function printDotSendPdf(params: {
printer: string;
pdfBase64: string;
jobName?: string;
copies?: number;
timeoutMs?: number;
}): Promise<{ ok: boolean; message: string }> {
const { wsUrl, key } = getPrintDotBridgeConfig();
const timeout = params.timeoutMs ?? 180000;
return new Promise((resolve, reject) => {
let settled = false;
const ws = new WebSocket(buildWsUrl(wsUrl, key));
const timer = window.setTimeout(() => {
if (settled) {
return;
}
settled = true;
try {
ws.close();
} catch {
// ignore
}
reject(new Error('PrintDot 打印等待结果超时'));
}, timeout);
const finishOk = (ok: boolean, message: string) => {
if (settled) {
return;
}
settled = true;
window.clearTimeout(timer);
try {
ws.close();
} catch {
// ignore
}
resolve({ ok, message });
};
ws.onopen = () => {
let content = String(params.pdfBase64 || '').trim();
if (content.startsWith('data:')) {
const idx = content.indexOf(',');
if (idx !== -1) {
content = content.slice(idx + 1);
}
}
const payload: Record<string, unknown> = {
printer: String(params.printer || '').trim(),
content,
job: {
name: String(params.jobName || 'QH-MES').trim() || 'QH-MES',
copies: Math.max(1, Number(params.copies) || 1),
},
};
ws.send(JSON.stringify(payload));
};
ws.onmessage = (ev) => {
try {
const data = JSON.parse(ev.data as string);
if (data?.type === 'printer_list') {
return;
}
if (data?.status) {
const ok = data.status === 'success';
const rawMsg = String(data.message || '');
finishOk(ok, ok ? rawMsg : enhancePrintDotErrorMessage(rawMsg));
}
} catch {
finishOk(false, 'PrintDot 返回无法解析');
}
};
ws.onerror = () => {
if (settled) {
return;
}
settled = true;
window.clearTimeout(timer);
try {
ws.close();
} catch {
// ignore
}
reject(new Error('PrintDot WebSocket 错误'));
};
});
}
/** 将列表页的「系统默认」或空选择解析为桥接器返回的默认打印机名称 */
export function resolvePrintDotPrinterName(selectedValue: string, printers: PrintDotPrinter[]): string {
const s = String(selectedValue || '').trim();
if (s && s !== '__system_default__') {
return s;
}
const def = printers.find((p) => p.isDefault);
return def?.name || printers[0]?.name || '';
}