改成wails

This commit is contained in:
yml2213
2026-07-23 14:04:52 +08:00
parent d0efcedcc5
commit a9ecad38f7
24 changed files with 2220 additions and 496 deletions
-318
View File
@@ -1,318 +0,0 @@
package app
import (
"context"
"encoding/json"
"fmt"
"image/color"
"io"
"os"
"strings"
"time"
"fyne.io/fyne/v2"
fyneapp "fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"updata_hfb/internal/hfb"
)
const sampleText = `上号方式:QQ扫码
哈夫币纯币:156
段位:铂金
等级:60
保险格数:9
每日消耗:10
体力:7
负重:7
AWM子弹数量:80
6头数量:13
6甲数量:12
皮肤:暗星
绝密KD0.2
押金:350
号主在线时间:11:00 至 00:00
封禁记录:无
常用地区:海南
回收比例:1:47.0
回收租金:332
联系电话:15203616450`
func Run() {
a := fyneapp.NewWithID("com.hfb.updata")
w := a.NewWindow("哈夫币外部上传")
w.Resize(fyne.NewSize(980, 880))
prefs := a.Preferences()
serverEntry := widget.NewEntry()
serverEntry.SetText(prefStringWithFallback(prefs, "server", envDefault("HFB_SERVER", "http://127.0.0.1:8080")))
serverEntry.SetPlaceHolder("例如:http://127.0.0.1:8080")
uploaderEntry := widget.NewEntry()
uploaderEntry.SetText(prefStringWithFallback(prefs, "uploader", strings.TrimSpace(os.Getenv("HFB_UPLOADER_NAME"))))
uploaderEntry.SetPlaceHolder("后台上传管理员名称")
secretEntry := widget.NewEntry()
secretEntry.Password = true
secretEntry.SetText(prefStringWithFallback(prefs, "secret", strings.TrimSpace(os.Getenv("HFB_UPLOAD_SECRET"))))
secretEntry.SetPlaceHolder("未配置密钥可留空")
timeoutEntry := widget.NewEntry()
timeoutEntry.SetText(prefStringWithFallback(prefs, "timeout", "15s"))
timeoutEntry.SetPlaceHolder("例如:15s")
dataEntry := widget.NewMultiLineEntry()
dataEntry.Wrapping = fyne.TextWrapWord
dataEntry.SetMinRowsVisible(14)
dataEntry.SetPlaceHolder(sampleText)
resultView := newResultPanel(420)
resultView.SetText("```text\n暂无结果\n点击“预览 JSON”或“上传”后,这里会显示多行结果。\n```")
statusLabel := widget.NewLabel("准备就绪")
setResult := func(text string) {
resultView.SetText(formatResultText(text))
}
setBusy := func(uploadBtn *widget.Button, previewBtn *widget.Button, busy bool) {
if busy {
uploadBtn.Disable()
previewBtn.Disable()
statusLabel.SetText("正在上传,请稍等...")
return
}
uploadBtn.Enable()
previewBtn.Enable()
}
var uploadBtn *widget.Button
var previewBtn *widget.Button
loadBtn := widget.NewButton("打开文件", func() {
dialog.ShowFileOpen(func(reader fyne.URIReadCloser, err error) {
if err != nil {
dialog.ShowError(fmt.Errorf("打开文件失败:%w", err), w)
return
}
if reader == nil {
return
}
defer reader.Close()
content, err := io.ReadAll(reader)
if err != nil {
dialog.ShowError(fmt.Errorf("读取文件失败:%w", err), w)
return
}
dataEntry.SetText(string(content))
statusLabel.SetText("已载入文件")
}, w)
})
previewBtn = widget.NewButton("预览 JSON", func() {
req, err := buildRequest(uploaderEntry.Text, dataEntry.Text)
if err != nil {
statusLabel.SetText("解析失败")
dialog.ShowError(err, w)
return
}
out, err := hfb.MarshalPretty(req)
if err != nil {
statusLabel.SetText("生成 JSON 失败")
dialog.ShowError(err, w)
return
}
setResult(string(out))
statusLabel.SetText("预览已生成")
})
uploadBtn = widget.NewButton("上传", func() {
timeout, err := time.ParseDuration(strings.TrimSpace(timeoutEntry.Text))
if err != nil || timeout <= 0 {
dialog.ShowError(fmt.Errorf("请求超时时间不正确:%s", timeoutEntry.Text), w)
return
}
req, err := buildRequest(uploaderEntry.Text, dataEntry.Text)
if err != nil {
statusLabel.SetText("解析失败")
dialog.ShowError(err, w)
return
}
server := strings.TrimSpace(serverEntry.Text)
secret := strings.TrimSpace(secretEntry.Text)
setBusy(uploadBtn, previewBtn, true)
setResult("")
go func() {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
resp, err := hfb.Client{
Server: server,
Secret: secret,
Timeout: timeout,
}.Upload(ctx, req)
fyne.Do(func() {
setBusy(uploadBtn, previewBtn, false)
if err != nil {
statusLabel.SetText("上传失败")
setResult(err.Error())
dialog.ShowError(err, w)
return
}
statusLabel.SetText("上传成功")
setResult(resp)
dialog.ShowInformation("上传成功", "数据已经提交到服务器。", w)
})
}()
})
uploadBtn.Importance = widget.HighImportance
serverEntry.OnChanged = func(s string) { prefs.SetString("server", strings.TrimSpace(s)) }
uploaderEntry.OnChanged = func(s string) { prefs.SetString("uploader", strings.TrimSpace(s)) }
secretEntry.OnChanged = func(s string) { prefs.SetString("secret", strings.TrimSpace(s)) }
timeoutEntry.OnChanged = func(s string) { prefs.SetString("timeout", strings.TrimSpace(s)) }
configForm := widget.NewForm(
widget.NewFormItem("服务器", serverEntry),
widget.NewFormItem("上传人", uploaderEntry),
widget.NewFormItem("上传密钥", secretEntry),
widget.NewFormItem("超时时间", timeoutEntry),
)
configAccordion := widget.NewAccordion(
widget.NewAccordionItem("连接配置", configForm),
)
configAccordion.Close(0)
toolbar := container.NewHBox(loadBtn, previewBtn, uploadBtn)
content := container.NewBorder(
nil,
container.NewVBox(statusLabel),
nil,
nil,
container.NewVScroll(container.NewVBox(
configAccordion,
toolbar,
widget.NewLabel("账号数据"),
dataEntry,
widget.NewLabel("结果"),
resultView,
)),
)
w.SetContent(content)
w.ShowAndRun()
}
func buildRequest(uploaderName string, rawText string) (hfb.ExternalUploadRequest, error) {
if strings.TrimSpace(rawText) == "" {
return hfb.ExternalUploadRequest{}, fmt.Errorf("账号数据不能为空")
}
parsed, err := hfb.ParseUploadText(rawText)
if err != nil {
return hfb.ExternalUploadRequest{}, err
}
name := strings.TrimSpace(uploaderName)
if name == "" {
name = parsed.UploaderName
}
if name == "" {
return hfb.ExternalUploadRequest{}, fmt.Errorf("缺少上传人名称:请在“上传人”里填写,或在文本中增加“上传人:姓名”")
}
return hfb.NewExternalUploadRequest(name, parsed.Account, time.Now()), nil
}
func envDefault(key string, fallback string) string {
value := strings.TrimSpace(os.Getenv(key))
if value == "" {
return fallback
}
return value
}
func prefStringWithFallback(prefs fyne.Preferences, key, fallback string) string {
if prefs == nil {
return fallback
}
value := strings.TrimSpace(prefs.String(key))
if value != "" {
return value
}
return fallback
}
func formatResultText(text string) string {
text = strings.TrimSpace(text)
if text == "" {
return "暂无结果"
}
var payload any
if err := json.Unmarshal([]byte(text), &payload); err == nil {
if pretty, err := json.MarshalIndent(payload, "", " "); err == nil {
return "```json\n" + string(pretty) + "\n```"
}
}
return "```text\n" + text + "\n```"
}
type resultPanel struct {
widget.BaseWidget
scroll *container.Scroll
content *widget.RichText
minHeight float32
}
func newResultPanel(minHeight float32) *resultPanel {
panel := &resultPanel{
content: widget.NewRichTextFromMarkdown(""),
minHeight: minHeight,
}
panel.scroll = container.NewVScroll(panel.content)
panel.ExtendBaseWidget(panel)
return panel
}
func (p *resultPanel) SetText(text string) {
p.content.ParseMarkdown(text)
p.Refresh()
}
func (p *resultPanel) CreateRenderer() fyne.WidgetRenderer {
return &resultPanelRenderer{panel: p}
}
type resultPanelRenderer struct {
panel *resultPanel
}
func (r *resultPanelRenderer) Layout(size fyne.Size) {
r.panel.scroll.Resize(size)
r.panel.scroll.Move(fyne.NewPos(0, 0))
}
func (r *resultPanelRenderer) MinSize() fyne.Size {
size := r.panel.scroll.MinSize()
if size.Height < r.panel.minHeight {
size.Height = r.panel.minHeight
}
if size.Width < 1 {
size.Width = 1
}
return size
}
func (r *resultPanelRenderer) Refresh() {
r.panel.scroll.Refresh()
}
func (r *resultPanelRenderer) BackgroundColor() color.Color {
return theme.Color(theme.ColorNameBackground)
}
func (r *resultPanelRenderer) Objects() []fyne.CanvasObject {
return []fyne.CanvasObject{r.panel.scroll}
}
func (r *resultPanelRenderer) Destroy() {}