fix(desktop): 报告 PDF 导出在壳内改走原生桥 —— WKWebView 拦死 window.open

实机验证抓到的:桌面壳内点 PDF 报「打印窗口被拦截」——Wails v3 的 WKWebView
把 window.open 拦成 null,前端弹打印窗那条路在壳内根本走不通(浏览器预览没事)。

- app.go 加 PrintReportPage(filename, html):打印视图 HTML 落临时文件(文件名
  过滤路径字符),openInSystem 交系统默认浏览器打开,页面 onload 自动唤起打印框,
  用户直接「存储为 PDF」。CJK 零字体依赖的原有优势不变。
- desktop.ts printReportHtml 改 async:inWails 走原生桥,浏览器维持 window.open;
  RunsView 调用点随之 async + 错误透 toast。
- 绑定重新生成(注意要 `wails3 generate bindings -ts`,裸跑默认吐 JS 且要
  GOWORK=off,否则 go.work 干扰找不到 Service)。

验证:go test/tsc/68 例 vitest 全绿;壳内被拦是用户实机复现的。
⚠️ 原生桥新路径(临时文件→浏览器→打印框)用户尚未实机点验,重新打的包已就位,
下次跑报告顺手验。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Blizzard
2026-07-17 09:40:15 +08:00
parent 7c67256f87
commit b4012dbbba
4 changed files with 62 additions and 20 deletions
+23 -17
View File
@@ -41,26 +41,32 @@ export function notify(title: string, body: string): void {
if (inWails()) void App.Notify(title, body);
}
// printReportHtml:把已渲染的报告 HTML 在打印视图里出 PDF(浏览器/Webview 的"打印→存为 PDF")。
// printReportHtml:把已渲染的报告 HTML 在打印视图里出 PDF"打印→存为 PDF")。
// 走前端打印是为了让中文(CJK)零字体依赖即可正确排版——后端 PDF 需内嵌 CJK 字体,较重。
export function printReportHtml(title: string, bodyHtml: string): boolean {
// 桌面壳内 WKWebView 会把 window.open 拦成 null(实机验过),改走原生桥:
// HTML 落临时文件 → 系统默认浏览器打开 → 那里 ⌘P 出 PDF(自动唤起打印框)。
export async function printReportHtml(title: string, bodyHtml: string): Promise<boolean> {
const html =
`<!doctype html><html><head><meta charset="utf-8"><title>${title}</title>` +
`<style>` +
`*{box-sizing:border-box}` +
`body{font-family:-apple-system,system-ui,'PingFang SC','Microsoft YaHei',sans-serif;line-height:1.75;color:#111;max-width:760px;margin:36px auto;padding:0 28px}` +
`h1{font-size:24px;margin:0 0 16px}h2{font-size:18px;margin:24px 0 8px}h3{font-size:15px}` +
`p{margin:8px 0}ul,ol{margin:8px 0;padding-left:22px}` +
`code,pre{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;background:#f4f4f5;border-radius:4px}` +
`pre{padding:12px;overflow:auto}code{padding:1px 4px}` +
`blockquote{margin:8px 0;padding-left:12px;border-left:3px solid #ddd;color:#555}` +
`@media print{body{margin:0}}` +
`</style></head><body>${bodyHtml}` +
`<script>window.onload=function(){window.focus();window.print();};<\/script>` +
`</body></html>`;
if (inWails()) {
await App.PrintReportPage(title, html);
return true;
}
const w = window.open("", "_blank", "width=840,height=1024");
if (!w) return false; // 被弹窗拦截
w.document.write(
`<!doctype html><html><head><meta charset="utf-8"><title>${title}</title>` +
`<style>` +
`*{box-sizing:border-box}` +
`body{font-family:-apple-system,system-ui,'PingFang SC','Microsoft YaHei',sans-serif;line-height:1.75;color:#111;max-width:760px;margin:36px auto;padding:0 28px}` +
`h1{font-size:24px;margin:0 0 16px}h2{font-size:18px;margin:24px 0 8px}h3{font-size:15px}` +
`p{margin:8px 0}ul,ol{margin:8px 0;padding-left:22px}` +
`code,pre{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;background:#f4f4f5;border-radius:4px}` +
`pre{padding:12px;overflow:auto}code{padding:1px 4px}` +
`blockquote{margin:8px 0;padding-left:12px;border-left:3px solid #ddd;color:#555}` +
`@media print{body{margin:0}}` +
`</style></head><body>${bodyHtml}` +
`<script>window.onload=function(){window.focus();window.print();};<\/script>` +
`</body></html>`,
);
w.document.write(html);
w.document.close();
return true;
}