diff --git a/frontend/src/main.js b/frontend/src/main.js index dde1a57..f2a8009 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -1,4 +1,5 @@ import './style.css'; +import { ClipboardGetText } from '../wailsjs/runtime/runtime.js'; const storageKey = 'hfb-upload-settings'; @@ -9,7 +10,7 @@ app.innerHTML = `

哈夫币外部上传

-

粘贴账号数据,预览确认后上传到服务器。

+

粘贴账号数据,确认后上传到服务器。

准备就绪
@@ -41,7 +42,7 @@ app.innerHTML = ` 打开文件 - + @@ -54,7 +55,7 @@ app.innerHTML = `
结果
暂无结果
-点击“预览 JSON”或“上传”后,这里会显示多行结果。
+点击“上传”后,这里会显示多行结果。
@@ -69,7 +70,7 @@ const elements = { data: document.querySelector('#data'), result: document.querySelector('#result'), fileInput: document.querySelector('#fileInput'), - previewBtn: document.querySelector('#previewBtn'), + pasteBtn: document.querySelector('#pasteBtn'), uploadBtn: document.querySelector('#uploadBtn'), }; @@ -83,7 +84,7 @@ function setStatus(text, tone = 'neutral') { } function setBusy(busy) { - elements.previewBtn.disabled = busy; + elements.pasteBtn.disabled = busy; elements.uploadBtn.disabled = busy; } @@ -115,6 +116,21 @@ function collectForm() { }; } +async function readClipboardText() { + try { + const text = await ClipboardGetText(); + if (typeof text === 'string' && text.trim() !== '') { + return text; + } + } catch { + // 回退到浏览器剪贴板 API + } + if (navigator.clipboard?.readText) { + return await navigator.clipboard.readText(); + } + throw new Error('无法读取剪贴板内容'); +} + async function init() { const api = backend(); if (!api) { @@ -140,7 +156,7 @@ elements.fileInput.addEventListener('change', async (event) => { if (!file) return; try { elements.data.value = await file.text(); - elements.result.textContent = '文件已载入,可以预览或上传。'; + elements.result.textContent = '文件已载入,可以上传。'; setStatus('已载入文件', 'ok'); } catch (err) { setStatus('读取文件失败', 'error'); @@ -150,17 +166,23 @@ elements.fileInput.addEventListener('change', async (event) => { } }); -elements.previewBtn.addEventListener('click', async () => { - const api = backend(); +elements.pasteBtn.addEventListener('click', async () => { setBusy(true); - setStatus('正在生成预览...', 'neutral'); - saveSettings(); + setStatus('正在读取剪贴板...', 'neutral'); try { - elements.result.textContent = await api.Preview(collectForm()); - setStatus('预览已生成', 'ok'); + const text = await readClipboardText(); + if (!text || !text.trim()) { + setStatus('剪贴板为空', 'error'); + elements.result.textContent = '剪贴板没有可用的文本内容。'; + return; + } + elements.data.value = text; + elements.result.textContent = '已从剪贴板填入账号数据,可以上传。'; + setStatus('已粘贴剪贴板', 'ok'); + elements.data.focus(); } catch (err) { elements.result.textContent = String(err); - setStatus('预览失败', 'error'); + setStatus('读取剪贴板失败', 'error'); } finally { setBusy(false); } diff --git a/frontend/vite.config.js b/frontend/vite.config.js new file mode 100644 index 0000000..2abf84d --- /dev/null +++ b/frontend/vite.config.js @@ -0,0 +1,10 @@ +import { defineConfig } from 'vite' + +// 使用项目专属端口,避免多项目同时开发时与 Vite 默认 5173 冲突 +export default defineConfig({ + server: { + host: 'localhost', + port: 5188, + strictPort: true, + }, +}) diff --git a/internal/hfb/parser.go b/internal/hfb/parser.go index da851fc..38a3b65 100644 --- a/internal/hfb/parser.go +++ b/internal/hfb/parser.go @@ -43,7 +43,7 @@ func ParseUploadText(text string) (ParsedUpload, error) { if err != nil { return ParsedUpload{}, err } - account.SecretKD, err = parseRequiredFloat(values, "绝密KD", "绝密kd") + account.SecretKD, err = parseOptionalFloat(values, "绝密KD", "绝密kd") if err != nil { return ParsedUpload{}, err } @@ -51,10 +51,12 @@ func ParseUploadText(text string) (ParsedUpload, error) { if err != nil { return ParsedUpload{}, err } - account.BanRecord = optionalDefault(firstValue(values, "封禁记录"), "无") + banRaw := firstValue(values, "封禁记录") + account.BanRecord = normalizeBanFlag(banRaw) account.CommonRegion = firstValue(values, "常用地区", "常用区域") account.ContactPhone = firstValue(values, "联系电话", "手机号", "电话") account.OwnerOnlineTime = firstValue(values, "号主在线时间", "在线时间") + account.Remark = composeRemark(firstValue(values, "备注", "说明"), banRaw) account.Currency.HafuCoin, err = parseRequiredFloat(values, "哈夫币纯币", "哈夫币") if err != nil { @@ -134,6 +136,43 @@ func optionalDefault(value, fallback string) string { return strings.TrimSpace(value) } +// normalizeBanFlag 只输出“有/无”,具体封禁历史放到备注。 +func normalizeBanFlag(value string) string { + value = strings.TrimSpace(value) + if value == "" || isNoBanRecord(value) { + return "无" + } + return "有" +} + +func isNoBanRecord(value string) bool { + value = strings.TrimSpace(value) + switch value { + case "无", "无封禁", "无封禁记录", "否", "没有", "none", "None", "NONE": + return true + default: + return false + } +} + +// composeRemark 合并原文备注与封禁历史详情。 +func composeRemark(remark, banRaw string) string { + remark = strings.TrimSpace(remark) + banDetail := strings.TrimSpace(banRaw) + if banDetail == "" || isNoBanRecord(banDetail) { + return remark + } + // 如果原文已经是“有/无”,不当作详情追加 + if banDetail == "有" || banDetail == "是" || banDetail == "有封禁" || banDetail == "有封禁记录" { + return remark + } + banLine := "封禁历史:" + banDetail + if remark == "" { + return banLine + } + return remark + "\n" + banLine +} + func parseRequiredInt(values map[string]string, labels ...string) (int, error) { value := firstValue(values, labels...) if value == "" { @@ -170,6 +209,18 @@ func parseRequiredFloat(values map[string]string, labels ...string) (float64, er return number, nil } +func parseOptionalFloat(values map[string]string, labels ...string) (float64, error) { + value := firstValue(values, labels...) + if value == "" { + return 0, nil + } + number, err := parseFloatText(value, false) + if err != nil { + return 0, fmt.Errorf("%s 数字格式不正确:%s", labels[0], value) + } + return number, nil +} + func parseRequiredRatio(values map[string]string, labels ...string) (float64, error) { value := firstValue(values, labels...) if value == "" { diff --git a/internal/hfb/parser_test.go b/internal/hfb/parser_test.go index 3cefecd..7857894 100644 --- a/internal/hfb/parser_test.go +++ b/internal/hfb/parser_test.go @@ -66,3 +66,51 @@ func TestParseUploadTextMissingRequiredField(t *testing.T) { t.Fatal("ParseUploadText() expected error") } } + +func TestParseUploadTextBanRecordAndRemark(t *testing.T) { + text := `上号方式:QQ扫码 +哈夫币纯币:156 +段位:铂金 +等级:60 +保险格数:9 +每日消耗:10 +体力:7 +负重:7 +AWM子弹数量:80 +6头数量:13 +6甲数量:12 +皮肤:暗星 +绝密KD:0.2 +押金:350 +号主在线时间:11:00 至 00:00 +封禁记录:2026/01/23封禁3天 2026/01/26封禁一天 +常用地区:海南 +回收比例:1:47.0 +回收租金:332 +联系电话:15203616450 +备注:有语音封禁` + parsed, err := ParseUploadText(text) + if err != nil { + t.Fatalf("ParseUploadText() error = %v", err) + } + if parsed.Account.BanRecord != "有" { + t.Fatalf("BanRecord = %q, want 有", parsed.Account.BanRecord) + } + wantRemark := "有语音封禁\n封禁历史:2026/01/23封禁3天 2026/01/26封禁一天" + if parsed.Account.Remark != wantRemark { + t.Fatalf("Remark = %q, want %q", parsed.Account.Remark, wantRemark) + } +} + +func TestParseUploadTextNoBanKeepsEmptyRemark(t *testing.T) { + parsed, err := ParseUploadText(sampleText) + if err != nil { + t.Fatalf("ParseUploadText() error = %v", err) + } + if parsed.Account.BanRecord != "无" { + t.Fatalf("BanRecord = %q, want 无", parsed.Account.BanRecord) + } + if parsed.Account.Remark != "" { + t.Fatalf("Remark = %q, want empty", parsed.Account.Remark) + } +} diff --git a/internal/hfb/types.go b/internal/hfb/types.go index 0bb305d..9e4051c 100644 --- a/internal/hfb/types.go +++ b/internal/hfb/types.go @@ -28,6 +28,7 @@ type ExternalAccountData struct { CommonRegion string `json:"commonRegion"` ContactPhone string `json:"contactPhone"` OwnerOnlineTime string `json:"ownerOnlineTime"` + Remark string `json:"remark,omitempty"` Currency ExternalUploadCurrency `json:"currency"` DailyConsumption ExternalDailyConsumption `json:"dailyConsumption"` Inventory ExternalUploadInventory `json:"inventory"` diff --git a/wails.json b/wails.json index 880599e..1abcafb 100644 --- a/wails.json +++ b/wails.json @@ -5,7 +5,8 @@ "frontend:install": "npm install", "frontend:build": "npm run build", "frontend:dev:watcher": "npm run dev", - "frontend:dev:serverUrl": "auto", + "frontend:dev:serverUrl": "http://localhost:5188", + "devServer": "localhost:34188", "author": { "name": "yml", "email": ""