Files
cmsp/main.go
T
QiuSWandClaude Opus 5 46378db86b feat: 商品主图可点击查看原图,支持右键另存为
- 表格主图列的缩略图可点击,弹出原图弹窗,鼠标指针改为 zoom-in
- main.go 打开 EnableDefaultContextMenu,这样在弹窗图片上右键才有
  「图片另存为」。代价是整个界面右键都会出现浏览器菜单,其中包含
  「检查」,同事可能误点开发者工具;权衡后仍然打开,因为另存图片是
  实际需要,误点开发者工具关掉即可,没有破坏性。同时移除 main.go 中
  已不再成立的「关掉右键检查」注释。
- 弹窗另配一个「另存为」按钮:WebView2 的原生右键菜单在个别机器上
  不给图片选项,这条路径不依赖浏览器菜单
- app.go 新增 SaveImageAs:只接受 http/https,避免变成可读取本地
  任意文件的入口;30 秒超时、20 MB 上限、空响应判失败
- 原图限制在 70vh 内并可滚动,避免大图撑出屏幕

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-03 10:24:18 +08:00

109 lines
4.0 KiB
Go
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.
// 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)
}