优化备注与封禁
This commit is contained in:
+35
-13
@@ -1,4 +1,5 @@
|
|||||||
import './style.css';
|
import './style.css';
|
||||||
|
import { ClipboardGetText } from '../wailsjs/runtime/runtime.js';
|
||||||
|
|
||||||
const storageKey = 'hfb-upload-settings';
|
const storageKey = 'hfb-upload-settings';
|
||||||
|
|
||||||
@@ -9,7 +10,7 @@ app.innerHTML = `
|
|||||||
<section class="topbar">
|
<section class="topbar">
|
||||||
<div>
|
<div>
|
||||||
<h1>哈夫币外部上传</h1>
|
<h1>哈夫币外部上传</h1>
|
||||||
<p>粘贴账号数据,预览确认后上传到服务器。</p>
|
<p>粘贴账号数据,确认后上传到服务器。</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="status" id="status">准备就绪</div>
|
<div class="status" id="status">准备就绪</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -41,7 +42,7 @@ app.innerHTML = `
|
|||||||
打开文件
|
打开文件
|
||||||
<input id="fileInput" type="file" accept=".txt,.log,.md,.json" hidden />
|
<input id="fileInput" type="file" accept=".txt,.log,.md,.json" hidden />
|
||||||
</label>
|
</label>
|
||||||
<button class="button secondary" id="previewBtn">预览 JSON</button>
|
<button class="button secondary" id="pasteBtn">粘贴剪贴板</button>
|
||||||
<button class="button primary" id="uploadBtn">上传</button>
|
<button class="button primary" id="uploadBtn">上传</button>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -54,7 +55,7 @@ app.innerHTML = `
|
|||||||
<section class="panel result-panel">
|
<section class="panel result-panel">
|
||||||
<div class="panel-title">结果</div>
|
<div class="panel-title">结果</div>
|
||||||
<pre id="result">暂无结果
|
<pre id="result">暂无结果
|
||||||
点击“预览 JSON”或“上传”后,这里会显示多行结果。</pre>
|
点击“上传”后,这里会显示多行结果。</pre>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
@@ -69,7 +70,7 @@ const elements = {
|
|||||||
data: document.querySelector('#data'),
|
data: document.querySelector('#data'),
|
||||||
result: document.querySelector('#result'),
|
result: document.querySelector('#result'),
|
||||||
fileInput: document.querySelector('#fileInput'),
|
fileInput: document.querySelector('#fileInput'),
|
||||||
previewBtn: document.querySelector('#previewBtn'),
|
pasteBtn: document.querySelector('#pasteBtn'),
|
||||||
uploadBtn: document.querySelector('#uploadBtn'),
|
uploadBtn: document.querySelector('#uploadBtn'),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -83,7 +84,7 @@ function setStatus(text, tone = 'neutral') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setBusy(busy) {
|
function setBusy(busy) {
|
||||||
elements.previewBtn.disabled = busy;
|
elements.pasteBtn.disabled = busy;
|
||||||
elements.uploadBtn.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() {
|
async function init() {
|
||||||
const api = backend();
|
const api = backend();
|
||||||
if (!api) {
|
if (!api) {
|
||||||
@@ -140,7 +156,7 @@ elements.fileInput.addEventListener('change', async (event) => {
|
|||||||
if (!file) return;
|
if (!file) return;
|
||||||
try {
|
try {
|
||||||
elements.data.value = await file.text();
|
elements.data.value = await file.text();
|
||||||
elements.result.textContent = '文件已载入,可以预览或上传。';
|
elements.result.textContent = '文件已载入,可以上传。';
|
||||||
setStatus('已载入文件', 'ok');
|
setStatus('已载入文件', 'ok');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setStatus('读取文件失败', 'error');
|
setStatus('读取文件失败', 'error');
|
||||||
@@ -150,17 +166,23 @@ elements.fileInput.addEventListener('change', async (event) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
elements.previewBtn.addEventListener('click', async () => {
|
elements.pasteBtn.addEventListener('click', async () => {
|
||||||
const api = backend();
|
|
||||||
setBusy(true);
|
setBusy(true);
|
||||||
setStatus('正在生成预览...', 'neutral');
|
setStatus('正在读取剪贴板...', 'neutral');
|
||||||
saveSettings();
|
|
||||||
try {
|
try {
|
||||||
elements.result.textContent = await api.Preview(collectForm());
|
const text = await readClipboardText();
|
||||||
setStatus('预览已生成', 'ok');
|
if (!text || !text.trim()) {
|
||||||
|
setStatus('剪贴板为空', 'error');
|
||||||
|
elements.result.textContent = '剪贴板没有可用的文本内容。';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
elements.data.value = text;
|
||||||
|
elements.result.textContent = '已从剪贴板填入账号数据,可以上传。';
|
||||||
|
setStatus('已粘贴剪贴板', 'ok');
|
||||||
|
elements.data.focus();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
elements.result.textContent = String(err);
|
elements.result.textContent = String(err);
|
||||||
setStatus('预览失败', 'error');
|
setStatus('读取剪贴板失败', 'error');
|
||||||
} finally {
|
} finally {
|
||||||
setBusy(false);
|
setBusy(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
|
||||||
|
// 使用项目专属端口,避免多项目同时开发时与 Vite 默认 5173 冲突
|
||||||
|
export default defineConfig({
|
||||||
|
server: {
|
||||||
|
host: 'localhost',
|
||||||
|
port: 5188,
|
||||||
|
strictPort: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
+53
-2
@@ -43,7 +43,7 @@ func ParseUploadText(text string) (ParsedUpload, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return ParsedUpload{}, err
|
return ParsedUpload{}, err
|
||||||
}
|
}
|
||||||
account.SecretKD, err = parseRequiredFloat(values, "绝密KD", "绝密kd")
|
account.SecretKD, err = parseOptionalFloat(values, "绝密KD", "绝密kd")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ParsedUpload{}, err
|
return ParsedUpload{}, err
|
||||||
}
|
}
|
||||||
@@ -51,10 +51,12 @@ func ParseUploadText(text string) (ParsedUpload, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return ParsedUpload{}, err
|
return ParsedUpload{}, err
|
||||||
}
|
}
|
||||||
account.BanRecord = optionalDefault(firstValue(values, "封禁记录"), "无")
|
banRaw := firstValue(values, "封禁记录")
|
||||||
|
account.BanRecord = normalizeBanFlag(banRaw)
|
||||||
account.CommonRegion = firstValue(values, "常用地区", "常用区域")
|
account.CommonRegion = firstValue(values, "常用地区", "常用区域")
|
||||||
account.ContactPhone = firstValue(values, "联系电话", "手机号", "电话")
|
account.ContactPhone = firstValue(values, "联系电话", "手机号", "电话")
|
||||||
account.OwnerOnlineTime = firstValue(values, "号主在线时间", "在线时间")
|
account.OwnerOnlineTime = firstValue(values, "号主在线时间", "在线时间")
|
||||||
|
account.Remark = composeRemark(firstValue(values, "备注", "说明"), banRaw)
|
||||||
|
|
||||||
account.Currency.HafuCoin, err = parseRequiredFloat(values, "哈夫币纯币", "哈夫币")
|
account.Currency.HafuCoin, err = parseRequiredFloat(values, "哈夫币纯币", "哈夫币")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -134,6 +136,43 @@ func optionalDefault(value, fallback string) string {
|
|||||||
return strings.TrimSpace(value)
|
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) {
|
func parseRequiredInt(values map[string]string, labels ...string) (int, error) {
|
||||||
value := firstValue(values, labels...)
|
value := firstValue(values, labels...)
|
||||||
if value == "" {
|
if value == "" {
|
||||||
@@ -170,6 +209,18 @@ func parseRequiredFloat(values map[string]string, labels ...string) (float64, er
|
|||||||
return number, nil
|
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) {
|
func parseRequiredRatio(values map[string]string, labels ...string) (float64, error) {
|
||||||
value := firstValue(values, labels...)
|
value := firstValue(values, labels...)
|
||||||
if value == "" {
|
if value == "" {
|
||||||
|
|||||||
@@ -66,3 +66,51 @@ func TestParseUploadTextMissingRequiredField(t *testing.T) {
|
|||||||
t.Fatal("ParseUploadText() expected error")
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ type ExternalAccountData struct {
|
|||||||
CommonRegion string `json:"commonRegion"`
|
CommonRegion string `json:"commonRegion"`
|
||||||
ContactPhone string `json:"contactPhone"`
|
ContactPhone string `json:"contactPhone"`
|
||||||
OwnerOnlineTime string `json:"ownerOnlineTime"`
|
OwnerOnlineTime string `json:"ownerOnlineTime"`
|
||||||
|
Remark string `json:"remark,omitempty"`
|
||||||
Currency ExternalUploadCurrency `json:"currency"`
|
Currency ExternalUploadCurrency `json:"currency"`
|
||||||
DailyConsumption ExternalDailyConsumption `json:"dailyConsumption"`
|
DailyConsumption ExternalDailyConsumption `json:"dailyConsumption"`
|
||||||
Inventory ExternalUploadInventory `json:"inventory"`
|
Inventory ExternalUploadInventory `json:"inventory"`
|
||||||
|
|||||||
+2
-1
@@ -5,7 +5,8 @@
|
|||||||
"frontend:install": "npm install",
|
"frontend:install": "npm install",
|
||||||
"frontend:build": "npm run build",
|
"frontend:build": "npm run build",
|
||||||
"frontend:dev:watcher": "npm run dev",
|
"frontend:dev:watcher": "npm run dev",
|
||||||
"frontend:dev:serverUrl": "auto",
|
"frontend:dev:serverUrl": "http://localhost:5188",
|
||||||
|
"devServer": "localhost:34188",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "yml",
|
"name": "yml",
|
||||||
"email": ""
|
"email": ""
|
||||||
|
|||||||
Reference in New Issue
Block a user