// cmsp 是一个 Windows 桌面工具:把货憨憨 ERP 里指定 Shopee 店铺的商品, // 按商品主图到淘宝搜同款,抓取同款商品详情页的介绍视频,下载到本地, // 再批量上传回货憨憨对应商品,使 Shopee 商品页刷新后能看到视频。 // // 从哪里开始读: // // main.go 这里,负责启动窗口 // app.go 暴露给前端的方法清单 // internal/config/ 参数设置的读写与校验 // internal/store/ SQLite,所有状态的唯一事实来源 // internal/logx/ 带脱敏的日志 // frontend/src/ 界面 // // 完整说明见 Wiki:https://git.ilapage.cn/chengma/cmsp/wiki package main import ( "embed" "log" "os" "path/filepath" "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" "github.com/wailsapp/wails/v2/pkg/options/assetserver" "github.com/wailsapp/wails/v2/pkg/options/windows" ) // 把编译好的前端打包进可执行文件,这样发给同事就是单个 exe, // 不用再带一堆网页文件。 // //go:embed all:frontend/dist var assets embed.FS func main() { app := NewApp() // 窗口尺寸。这里有个容易踩的坑,改之前先读完: // // Width/Height 是逻辑像素,Windows 会按系统缩放放大。 // 在 125% 缩放的 1920x1080 笔记本上,1440x900 会变成 // 1800x1125 物理像素,比工作区(1920x1030)还高, // 窗口被居中后标题栏和最大化/关闭按钮会跑到屏幕外面, // 使用者会以为"程序没有标题栏"。 // // 所以这里不写死原型尺寸,改成启动即最大化: // 不管同事的屏幕多大、缩放多少,都能用满可用空间。 // 下面的 Width/Height 只是还原窗口后的大小,取一个 // 在 1366x768 笔记本上也放得下的值。 // // MinWidth 设 1024 是底线:再窄工具栏上行的六个筛选控件 // 会挤在一起。界面本身会换行,不会错乱。 err := wails.Run(&options.App{ Title: "cmsp — Shopee 商品视频工具", WindowStartState: options.Maximised, Width: 1280, Height: 760, MinWidth: 1024, MinHeight: 600, AssetServer: &assetserver.Options{ Assets: assets, }, OnStartup: app.startup, OnShutdown: app.shutdown, // 打开浏览器默认右键菜单,这样在商品主图弹窗上右键才有「图片另存为」。 // // 代价:整个界面右键都会出现浏览器菜单,其中包含「检查」, // 同事可能误点开发者工具。权衡后仍然打开,因为另存图片是实际需要, // 误点开发者工具关掉即可,没有破坏性。 // 弹窗里另外放了一个「另存为」按钮,WebView2 的原生菜单在个别机器上 // 不给图片选项时仍然可用。 EnableDefaultContextMenu: true, // Bind 里的对象会被生成成 JavaScript 给前端调用。 // 新增可供前端调用的方法,就写成 App 的公开方法,不用改这里。 Bind: []any{ app, }, Windows: &windows.Options{ DisableWindowIcon: false, }, }) if err != nil { // 走到这里说明窗口没能创建,界面上什么都看不到, // 所以要同时写到控制台和文件,否则同事只会看到程序一闪而过。 log.Println("启动失败:", err) writeStartupError(err) } } // writeStartupError 把启动失败的原因写到程序目录下的文件里。 // // 双击 exe 启动时没有控制台,报错会一闪而过。留个文件, // 同事能直接把它发给维护者。 func writeStartupError(runErr error) { exe, err := os.Executable() if err != nil { return } path := filepath.Join(filepath.Dir(exe), "启动失败.txt") _ = writeTextFile(path, "cmsp 启动失败:\n"+runErr.Error()+"\n") } // writeTextFile 用 UTF-8 写一个文本文件。 // // 单独抽出来是因为 app.go 导出日志时也要用。 func writeTextFile(path, content string) error { return os.WriteFile(path, []byte(content), 0o600) }