From b4012dbbba868f87f37cef5891edb702d33cfb68 Mon Sep 17 00:00:00 2001 From: Blizzard Date: Fri, 17 Jul 2026 09:40:15 +0800 Subject: [PATCH] =?UTF-8?q?fix(desktop):=20=E6=8A=A5=E5=91=8A=20PDF=20?= =?UTF-8?q?=E5=AF=BC=E5=87=BA=E5=9C=A8=E5=A3=B3=E5=86=85=E6=94=B9=E8=B5=B0?= =?UTF-8?q?=E5=8E=9F=E7=94=9F=E6=A1=A5=20=E2=80=94=E2=80=94=20WKWebView=20?= =?UTF-8?q?=E6=8B=A6=E6=AD=BB=20window.open?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 实机验证抓到的:桌面壳内点 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 --- sundynix-desktop/app.go | 22 ++++++++++ .../sundynix/sundynix-desktop/app.ts | 9 +++++ sundynix-desktop/frontend/src/lib/desktop.ts | 40 +++++++++++-------- .../frontend/src/views/RunsView.tsx | 11 +++-- 4 files changed, 62 insertions(+), 20 deletions(-) diff --git a/sundynix-desktop/app.go b/sundynix-desktop/app.go index e6910c6..e0be32c 100644 --- a/sundynix-desktop/app.go +++ b/sundynix-desktop/app.go @@ -8,6 +8,7 @@ import ( "os/exec" "path/filepath" goruntime "runtime" + "strings" "time" "github.com/wailsapp/wails/v3/pkg/application" @@ -61,6 +62,27 @@ func (a *App) OpenReport(url, filename string) error { return openInSystem(dst) } +// PrintReportPage 把报告打印视图 HTML 落到临时文件,交系统默认浏览器打开(在那里 ⌘P → +// 存储为 PDF)。Wails 的 WKWebView 会把 window.open 拦成 null,前端弹打印窗那条路在壳内 +// 走不通;转交系统浏览器后「前端打印出 PDF、CJK 零字体依赖」的原有优势不变。返回落盘路径。 +func (a *App) PrintReportPage(filename, html string) (string, error) { + if filename == "" { + filename = "report" + } + // 文件名进过滤:主题可能含 / 之类的路径字符。 + safe := strings.Map(func(r rune) rune { + if strings.ContainsRune(`/\:*?"<>|`, r) { + return '_' + } + return r + }, filename) + dst := filepath.Join(os.TempDir(), "sundynix-print-"+safe+".html") + if err := os.WriteFile(dst, []byte(html), 0o600); err != nil { + return "", err + } + return dst, openInSystem(dst) +} + // Notify 弹一条系统通知(best-effort:macOS 用 osascript,其它平台暂静默)。 func (a *App) Notify(title, body string) { if goruntime.GOOS == "darwin" { diff --git a/sundynix-desktop/frontend/bindings/github.com/sundynix/sundynix-desktop/app.ts b/sundynix-desktop/frontend/bindings/github.com/sundynix/sundynix-desktop/app.ts index 6f4d332..b7a2469 100644 --- a/sundynix-desktop/frontend/bindings/github.com/sundynix/sundynix-desktop/app.ts +++ b/sundynix-desktop/frontend/bindings/github.com/sundynix/sundynix-desktop/app.ts @@ -33,6 +33,15 @@ export function Ping(): $CancellablePromise { return $Call.ByID(1003072145); } +/** + * PrintReportPage 把报告打印视图 HTML 落到临时文件,交系统默认浏览器打开(在那里 ⌘P → + * 存储为 PDF)。Wails 的 WKWebView 会把 window.open 拦成 null,前端弹打印窗那条路在壳内 + * 走不通;转交系统浏览器后「前端打印出 PDF、CJK 零字体依赖」的原有优势不变。返回落盘路径。 + */ +export function PrintReportPage(filename: string, html: string): $CancellablePromise { + return $Call.ByID(3564528865, filename, html); +} + /** * ReadLocalFile 读取本地文件内容(本地文件系统 I/O)。 */ diff --git a/sundynix-desktop/frontend/src/lib/desktop.ts b/sundynix-desktop/frontend/src/lib/desktop.ts index 4ca860d..d66e4c7 100644 --- a/sundynix-desktop/frontend/src/lib/desktop.ts +++ b/sundynix-desktop/frontend/src/lib/desktop.ts @@ -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 { + const html = + `${title}` + + `${bodyHtml}` + + `