Files
Blizzard 4d9d1ac615 feat(desktop): 工业化升级 B —— 真桌面集成(Wails Go 桥 + 原生能力)
让它真正像桌面 App 而非套壳网页:启用闲置的 Wails Go 桥,接原生文件框/系统打开/
通知/无边框标题栏,且全部对浏览器预览(make web)优雅降级。

- app.go:SaveReportAs(原生"另存为"框 + 下载落盘)、OpenReport(下到临时目录 +
  系统默认应用打开 docx)、Notify(macOS osascript 通知)、download/openInSystem 跨平台
- main.go:macOS TitleBarHiddenInset —— 隐藏标题栏、内容铺满到顶、保留红绿灯交通灯
- lib/desktop.ts:window.go.main.App 运行时桥 + isDesktop/isMacDesktop 探测;
  saveReportAs/openReport/notify 在无 Wails(浏览器)时分别降级为 <a download>/新标签/Toast
- ReportView:桌面端「另存为 Word」(原生框) +「用系统打开」+ 完成弹系统通知;
  浏览器端保持「下载 Word」
- KbView:拖拽文件入库(HTML5 dataTransfer,两种模式通用)+ 拖拽高亮 +「选择文件」按钮
- TopBar:顶栏设为 Wails 可拖拽区(--wails-draggable),控件标 no-drag;
  macOS 桌面端左留白让位交通灯

验证:GOWORK=off wails build 打出 .app(绑定生成 + mac 标题栏);启动真实原生窗口
截图确认无边框标题栏 + 交通灯内嵌 + 顶栏可拖拽(见会话截图);浏览器(Preview)确认
window.go 不存在时降级正确(下载链路 + 拖拽占位)。tsc + vite build 通过。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 17:04:16 +08:00

107 lines
2.8 KiB
Go
Raw Permalink 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.
package main
import (
"context"
"fmt"
"io"
"net/http"
"os"
"os/exec"
"path/filepath"
goruntime "runtime"
wr "github.com/wailsapp/wails/v2/pkg/runtime"
)
// App 经 Wails 的 TS/Go 强绑定暴露给前端,承载只有桌面端能做的原生能力:
// 文件读写、系统"另存为"框、用系统默认应用打开、原生通知。
type App struct {
ctx context.Context
}
func NewApp() *App { return &App{} }
// startup 在 Wails 启动时注入运行时 ctx。
func (a *App) startup(ctx context.Context) { a.ctx = ctx }
// Ping 供前端探活 Go 桥是否就绪。
func (a *App) Ping() string { return "sundynix-desktop ok" }
// ReadLocalFile 读取本地文件内容(本地文件系统 I/O)。
func (a *App) ReadLocalFile(path string) (string, error) {
b, err := os.ReadFile(path)
if err != nil {
return "", err
}
return string(b), nil
}
// SaveReportAs 弹原生"另存为"对话框,把 url 指向的报告(.docx)下载到用户选定路径。
// 返回保存路径;用户取消则返回空串。
func (a *App) SaveReportAs(url, filename string) (string, error) {
if filename == "" {
filename = "report.docx"
}
path, err := wr.SaveFileDialog(a.ctx, wr.SaveDialogOptions{
Title: "保存报告",
DefaultFilename: filename,
Filters: []wr.FileFilter{{DisplayName: "Word 文档 (*.docx)", Pattern: "*.docx"}},
})
if err != nil || path == "" {
return "", err
}
if err := download(url, path); err != nil {
return "", err
}
return path, nil
}
// OpenReport 把报告下载到临时目录,并用系统默认应用(Word/Pages/WPS)打开。
func (a *App) OpenReport(url, filename string) error {
if filename == "" {
filename = "report.docx"
}
dst := filepath.Join(os.TempDir(), "sundynix-open-"+filename)
if err := download(url, dst); err != nil {
return err
}
return openInSystem(dst)
}
// Notify 弹一条系统通知(best-effortmacOS 用 osascript,其它平台暂静默)。
func (a *App) Notify(title, body string) {
if goruntime.GOOS == "darwin" {
script := fmt.Sprintf("display notification %q with title %q", body, title)
_ = exec.Command("osascript", "-e", script).Start()
}
}
func download(url, dst string) error {
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
return fmt.Errorf("下载失败 HTTP %d", resp.StatusCode)
}
f, err := os.Create(dst)
if err != nil {
return err
}
defer f.Close()
_, err = io.Copy(f, resp.Body)
return err
}
func openInSystem(path string) error {
switch goruntime.GOOS {
case "darwin":
return exec.Command("open", path).Start()
case "windows":
return exec.Command("rundll32", "url.dll,FileProtocolHandler", path).Start()
default:
return exec.Command("xdg-open", path).Start()
}
}