package app import ( "context" "fmt" "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/widget" "updata_hfb/internal/hfb" ) const sampleText = `上号方式:QQ扫码 哈夫币纯币:156 段位:铂金 等级:60 保险格数:9 每日消耗:10 体力:7 负重:7 AWM子弹数量:80 6头数量:13 6甲数量:12 皮肤:暗星 绝密KD:0.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 := widget.NewRichTextWithText("") resultView.Wrapping = fyne.TextWrapWord statusLabel := widget.NewLabel("准备就绪") setResult := func(text string) { resultView.ParseMarkdown("```json\n" + strings.TrimSpace(text) + "\n```") } 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("结果"), newMinHeightScroll(resultView, 420), )), ) 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 } type minHeightScroll struct { widget.BaseWidget scroll fyne.CanvasObject minHeight float32 } func newMinHeightScroll(content fyne.CanvasObject, minHeight float32) *minHeightScroll { panel := &minHeightScroll{ scroll: container.NewVScroll(content), minHeight: minHeight, } panel.ExtendBaseWidget(panel) return panel } func (p *minHeightScroll) CreateRenderer() fyne.WidgetRenderer { return widget.NewSimpleRenderer(p.scroll) } func (p *minHeightScroll) MinSize() fyne.Size { size := p.scroll.MinSize() if size.Height < p.minHeight { size.Height = p.minHeight } return size }