tt-help-cli-ycl 1.3.95 → 1.3.97

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -167,6 +167,7 @@ function renderStats() {
167
167
  flashEl("statTarget", d.targetUsers, { full: true });
168
168
  flashEl("statUserUpdateTasks", d.userUpdateTasks || 0, { full: true });
169
169
  flashEl("statRawJobs", d.rawJobs || 0);
170
+ flashEl("statTags", d.tagCount || 0, { full: true });
170
171
  // 同步子页面 stats
171
172
  const pendingTotal = document.getElementById("pendingStatTotal");
172
173
  if (pendingTotal) pendingTotal.textContent = formatStatNum(d.totalUsers);
@@ -659,16 +660,21 @@ function openLocationModal(uniqueId, currentLocation) {
659
660
  overlay = document.createElement("div");
660
661
  overlay.id = "locationModalOverlay";
661
662
  overlay.className = "modal-overlay";
663
+ const safeId = escapeJsString(uniqueId);
662
664
  const options = TARGET_LOCATIONS.map(
663
665
  (loc) =>
664
- `<button class="loc-option ${loc === currentLocation ? "active" : ""}" onclick="selectLocation('${uniqueId}','${loc}')">${loc}</button>`,
666
+ `<button class="loc-option ${loc === currentLocation ? "active" : ""}" onclick="selectLocation('${safeId}','${loc}')">${loc}</button>`,
665
667
  ).join("");
666
668
  overlay.innerHTML = `
667
669
  <div class="modal" style="max-width:420px">
668
670
  <h3>修改用户国家</h3>
669
- <div class="hint">用户: @${uniqueId},当前国家: ${currentLocation}</div>
671
+ <div class="hint">用户: @${safeId},当前国家: ${currentLocation}</div>
670
672
  <div class="loc-grid">${options}</div>
671
- <div class="btn-row" style="margin-top:16px">
673
+ <div class="custom-loc-row">
674
+ <input type="text" id="customLocationInput" class="custom-loc-input" placeholder="或手动输入国家代码,如 UK" maxlength="10" onkeydown="if(event.key==='Enter')confirmCustomLocation('${safeId}')">
675
+ </div>
676
+ <div class="btn-row" style="margin-top:12px">
677
+ <button class="btn-submit" onclick="confirmCustomLocation('${safeId}')">确认</button>
672
678
  <button class="btn-cancel" onclick="closeLocationModal()">取消</button>
673
679
  </div>
674
680
  </div>
@@ -677,6 +683,21 @@ function openLocationModal(uniqueId, currentLocation) {
677
683
  overlay.addEventListener("click", (e) => {
678
684
  if (e.target === overlay) closeLocationModal();
679
685
  });
686
+ // 自动聚焦输入框
687
+ setTimeout(() => {
688
+ const input = document.getElementById("customLocationInput");
689
+ if (input) input.focus();
690
+ }, 100);
691
+ }
692
+
693
+ function confirmCustomLocation(uniqueId) {
694
+ const input = document.getElementById("customLocationInput");
695
+ const val = input ? input.value.trim().toUpperCase() : "";
696
+ if (!val) {
697
+ showToast("请输入国家代码", true);
698
+ return;
699
+ }
700
+ selectLocation(uniqueId, val);
680
701
  }
681
702
 
682
703
  function closeLocationModal() {
@@ -726,6 +747,56 @@ async function selectLocation(uniqueId, location) {
726
747
  }
727
748
  }
728
749
 
750
+ async function confirmNonSeller(uniqueId) {
751
+ if (
752
+ !confirm(
753
+ `确定要将 @${uniqueId} 标记为非商家吗?\n这将把 ta 的商家标识(ttSeller)设为 false。`,
754
+ )
755
+ ) {
756
+ return;
757
+ }
758
+ showLoading("正在更新...");
759
+ try {
760
+ const res = await fetch(
761
+ `/api/user-non-seller/${encodeURIComponent(uniqueId)}`,
762
+ {
763
+ method: "PUT",
764
+ headers: { "Content-Type": "application/json" },
765
+ },
766
+ );
767
+ const data = await res.json();
768
+ if (data.error) {
769
+ showToast(data.error, true);
770
+ return;
771
+ }
772
+ showToast(`@${uniqueId} 已标记为非商家`);
773
+
774
+ // 从当前列表中移除该用户
775
+ currentTargetUsers = currentTargetUsers.filter(
776
+ (u) => u.uniqueId !== uniqueId,
777
+ );
778
+ currentTargetTotal = Math.max(0, currentTargetTotal - 1);
779
+ renderTargetTable();
780
+
781
+ // 同时更新统计数据
782
+ const statEl = document.getElementById("targetPageStatTotal");
783
+ if (statEl)
784
+ statEl.textContent = formatStatNum(currentTargetTotal, { full: true });
785
+ // 更新主页面统计
786
+ if (currentStats) {
787
+ currentStats.targetUsers = Math.max(
788
+ 0,
789
+ (currentStats.targetUsers || 0) - 1,
790
+ );
791
+ flashEl("statTarget", currentStats.targetUsers, { full: true });
792
+ }
793
+ } catch (e) {
794
+ showToast("更新失败: " + e.message, true);
795
+ } finally {
796
+ hideLoading();
797
+ }
798
+ }
799
+
729
800
  async function submitAddUsers() {
730
801
  const ta = document.getElementById("modalUserInput");
731
802
  const raw = ta.value.trim();
@@ -967,6 +1038,8 @@ function handleRoute() {
967
1038
  showRawPage();
968
1039
  } else if (hash === "#target") {
969
1040
  showTargetPage();
1041
+ } else if (hash === "#tags") {
1042
+ showTagsPage();
970
1043
  } else {
971
1044
  showMainPage();
972
1045
  }
@@ -994,6 +1067,7 @@ function showPendingPage() {
994
1067
  document.getElementById("userUpdatePage").classList.remove("active");
995
1068
  document.getElementById("rawPage").classList.remove("active");
996
1069
  document.getElementById("targetPage").classList.remove("active");
1070
+ document.getElementById("tagsPage").classList.remove("active");
997
1071
  fetchPendingByCountry();
998
1072
  fetchAttachStuckByCountry();
999
1073
  }
@@ -1004,6 +1078,7 @@ function showUserUpdatePage() {
1004
1078
  document.getElementById("userUpdatePage").classList.add("active");
1005
1079
  document.getElementById("rawPage").classList.remove("active");
1006
1080
  document.getElementById("targetPage").classList.remove("active");
1081
+ document.getElementById("tagsPage").classList.remove("active");
1007
1082
  fetchUserUpdateByCountry();
1008
1083
  fetchAttachStuckByCountry();
1009
1084
  }
@@ -1021,6 +1096,7 @@ function showRawPage() {
1021
1096
  document.getElementById("userUpdatePage").classList.remove("active");
1022
1097
  document.getElementById("rawPage").classList.add("active");
1023
1098
  document.getElementById("targetPage").classList.remove("active");
1099
+ document.getElementById("tagsPage").classList.remove("active");
1024
1100
  fetchRawByCountry();
1025
1101
  fetchRawJobs();
1026
1102
  }
@@ -1031,18 +1107,419 @@ function showMainPage() {
1031
1107
  document.getElementById("userUpdatePage").classList.remove("active");
1032
1108
  document.getElementById("rawPage").classList.remove("active");
1033
1109
  document.getElementById("targetPage").classList.remove("active");
1110
+ document.getElementById("tagsPage").classList.remove("active");
1034
1111
  }
1035
1112
 
1036
1113
  function navigateToTarget() {
1037
1114
  window.location.hash = "#target";
1038
1115
  }
1039
1116
 
1117
+ function navigateToTags() {
1118
+ window.location.hash = "#tags";
1119
+ }
1120
+
1121
+ // ========== 添加关键词弹窗 ==========
1122
+
1123
+ function openAddTagModal() {
1124
+ let overlay = document.getElementById("addTagModalOverlay");
1125
+ if (overlay) return;
1126
+ overlay = document.createElement("div");
1127
+ overlay.id = "addTagModalOverlay";
1128
+ overlay.className = "modal-overlay";
1129
+ overlay.innerHTML = `
1130
+ <div class="modal">
1131
+ <h3>添加关键词</h3>
1132
+ <div class="hint">每行一个关键词,不带 # 号。新关键词默认待打分状态。</div>
1133
+ <textarea id="addTagModalInput" placeholder="例如:&#10;fashion&#10;beauty&#10;skincare&#10;travel" style="height:160px"></textarea>
1134
+ <div class="preview" id="addTagModalPreview"></div>
1135
+ <div class="form-row">
1136
+ <label style="color:#aaa;font-size:12px">选择国家(可多选):</label>
1137
+ <div id="addTagCountryCheckboxes" style="display:flex;flex-wrap:wrap;gap:6px;margin-top:6px">
1138
+ </div>
1139
+ </div>
1140
+ <div class="btn-row">
1141
+ <button class="btn-cancel" onclick="closeAddTagModal()">取消</button>
1142
+ <button class="btn-submit" onclick="submitAddTags()">确认添加</button>
1143
+ </div>
1144
+ </div>
1145
+ `;
1146
+ document.body.appendChild(overlay);
1147
+ overlay.addEventListener("click", (e) => {
1148
+ if (e.target === overlay) closeAddTagModal();
1149
+ });
1150
+
1151
+ // 渲染国家复选框
1152
+ const countryGrid = document.getElementById("addTagCountryCheckboxes");
1153
+ const TARGET_LOCATIONS = [
1154
+ "AT",
1155
+ "BE",
1156
+ "CZ",
1157
+ "DE",
1158
+ "ES",
1159
+ "FR",
1160
+ "GR",
1161
+ "HU",
1162
+ "IE",
1163
+ "IT",
1164
+ "NL",
1165
+ "PL",
1166
+ "PT",
1167
+ ];
1168
+ countryGrid.innerHTML = TARGET_LOCATIONS.map(
1169
+ (c) =>
1170
+ `<label class="checkbox-label"><input type="checkbox" value="${c}" checked> ${c}</label>`,
1171
+ ).join("");
1172
+
1173
+ const ta = document.getElementById("addTagModalInput");
1174
+ ta.focus();
1175
+ ta.addEventListener("input", () => updateAddTagPreview());
1176
+ ta.addEventListener("keydown", (e) => {
1177
+ if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
1178
+ e.preventDefault();
1179
+ submitAddTags();
1180
+ }
1181
+ });
1182
+ document.addEventListener(
1183
+ "keydown",
1184
+ (window.addTagEscHandler = (e) => {
1185
+ if (e.key === "Escape") closeAddTagModal();
1186
+ }),
1187
+ );
1188
+ }
1189
+
1190
+ function closeAddTagModal() {
1191
+ const overlay = document.getElementById("addTagModalOverlay");
1192
+ if (overlay) overlay.remove();
1193
+ if (window.addTagEscHandler) {
1194
+ document.removeEventListener("keydown", window.addTagEscHandler);
1195
+ delete window.addTagEscHandler;
1196
+ }
1197
+ }
1198
+
1199
+ function updateAddTagPreview() {
1200
+ const ta = document.getElementById("addTagModalInput");
1201
+ const preview = document.getElementById("addTagModalPreview");
1202
+ if (!ta || !preview) return;
1203
+ const tags = ta.value
1204
+ .split("\n")
1205
+ .map((s) => s.trim().replace(/^#+/, ""))
1206
+ .filter(Boolean);
1207
+ preview.textContent = tags.length ? `共 ${tags.length} 个关键词` : "";
1208
+ }
1209
+
1210
+ async function submitAddTags() {
1211
+ const ta = document.getElementById("addTagModalInput");
1212
+ const raw = ta ? ta.value.trim() : "";
1213
+ if (!raw) {
1214
+ showToast("请输入关键词", true);
1215
+ return;
1216
+ }
1217
+
1218
+ const tags = raw
1219
+ .split("\n")
1220
+ .map((s) => s.trim().replace(/^#+/, ""))
1221
+ .filter(Boolean);
1222
+ if (tags.length === 0) {
1223
+ showToast("请输入有效关键词", true);
1224
+ return;
1225
+ }
1226
+
1227
+ // 获取选中的国家
1228
+ const checkedBoxes = document.querySelectorAll(
1229
+ "#addTagCountryCheckboxes input[type=checkbox]:checked",
1230
+ );
1231
+ const countries = Array.from(checkedBoxes).map((cb) => cb.value);
1232
+ if (countries.length === 0) {
1233
+ showToast("请至少选择一个国家", true);
1234
+ return;
1235
+ }
1236
+
1237
+ showLoading("正在添加关键词...");
1238
+ try {
1239
+ const res = await fetch("/api/tags/batch-add", {
1240
+ method: "POST",
1241
+ headers: { "Content-Type": "application/json" },
1242
+ body: JSON.stringify({ tags, countries }),
1243
+ });
1244
+ const data = await res.json();
1245
+ if (data.error) {
1246
+ showToast(data.error, true);
1247
+ return;
1248
+ }
1249
+ closeAddTagModal();
1250
+ showToast(`已添加 ${data.added} 个关键词`);
1251
+ loadTagsPage();
1252
+ } catch (e) {
1253
+ showToast("添加失败: " + e.message, true);
1254
+ } finally {
1255
+ hideLoading();
1256
+ }
1257
+ }
1258
+
1259
+ // ========== 关键词页面 ==========
1260
+
1261
+ let currentTags = [];
1262
+ let currentTagsFilter = "all";
1263
+ let currentTagsCountry = "";
1264
+ let currentTagsSort = { key: null, asc: true };
1265
+ let currentTagsTotal = 0;
1266
+ let currentTagsLoading = false;
1267
+ let currentTagsAllLoaded = false;
1268
+ let currentTagsSeq = 0;
1269
+ const TAGS_PAGE_SIZE = 200;
1270
+ let cachedTagCountries = [];
1271
+
1272
+ function showTagsPage() {
1273
+ document.getElementById("mainPage").classList.add("hidden");
1274
+ document.getElementById("pendingPage").classList.remove("active");
1275
+ document.getElementById("userUpdatePage").classList.remove("active");
1276
+ document.getElementById("rawPage").classList.remove("active");
1277
+ document.getElementById("targetPage").classList.remove("active");
1278
+ document.getElementById("tagsPage").classList.add("active");
1279
+ showLoading("正在加载关键词...");
1280
+ Promise.all([loadTagStats(), loadTagsPage()]).finally(hideLoading);
1281
+ }
1282
+
1283
+ function setTagsFilter(filter) {
1284
+ currentTagsFilter = filter;
1285
+ currentTags = [];
1286
+ currentTagsAllLoaded = false;
1287
+ document
1288
+ .querySelectorAll("#tagsPage .controls button[data-tags-filter]")
1289
+ .forEach((b) => {
1290
+ b.classList.toggle("active", b.dataset.tagsFilter === filter);
1291
+ });
1292
+ loadTagsPage();
1293
+ }
1294
+
1295
+ async function loadTagStats() {
1296
+ try {
1297
+ const res = await fetch("/api/tags/stats");
1298
+ const stats = await res.json();
1299
+ if (!stats) return;
1300
+ document.getElementById("tagsPageStatTotal").textContent = stats.total || 0;
1301
+ flashEl("tagsPageStatProductive", stats.productive || 0);
1302
+ flashEl("tagsPageStatDead", stats.dead || 0);
1303
+ flashEl("tagsPageStatNew", (stats.new || 0) + (stats.scoring || 0));
1304
+ cachedTagCountries = stats.countries || [];
1305
+ // 初始化国家下拉
1306
+ renderTagsCountryFilter();
1307
+ } catch (e) {
1308
+ console.error("获取关键词统计失败:", e);
1309
+ }
1310
+ }
1311
+
1312
+ function renderTagsCountryFilter() {
1313
+ const sel = document.getElementById("tagsCountryFilter");
1314
+ if (!sel) return;
1315
+ const val = sel.value;
1316
+ sel.innerHTML =
1317
+ '<option value="">全部国家</option>' +
1318
+ cachedTagCountries
1319
+ .map(
1320
+ (c) =>
1321
+ `<option value="${c}"${val === c ? " selected" : ""}>${c}</option>`,
1322
+ )
1323
+ .join("");
1324
+ }
1325
+
1326
+ async function loadTagsPage(append = false) {
1327
+ if (append && (currentTagsAllLoaded || currentTagsLoading)) return;
1328
+ if (append) currentTagsLoading = true;
1329
+
1330
+ const seq = ++currentTagsSeq;
1331
+ const country = document.getElementById("tagsCountryFilter").value;
1332
+ const offset = append ? currentTags.length : 0;
1333
+
1334
+ try {
1335
+ let url = `/api/tags?limit=${TAGS_PAGE_SIZE}&offset=${offset}`;
1336
+ if (currentTagsFilter !== "all") url += `&status=${currentTagsFilter}`;
1337
+ if (country) url += `&country=${encodeURIComponent(country)}`;
1338
+
1339
+ const res = await fetch(url);
1340
+ const data = await res.json();
1341
+
1342
+ if (seq !== currentTagsSeq) return; // 旧请求丢弃
1343
+
1344
+ if (append) {
1345
+ currentTags = currentTags.concat(data.tags || []);
1346
+ } else {
1347
+ currentTags = data.tags || [];
1348
+ currentTagsTotal = data.total || 0;
1349
+ }
1350
+ currentTagsAllLoaded = currentTags.length >= currentTagsTotal;
1351
+
1352
+ renderTagsTable();
1353
+ } catch (e) {
1354
+ console.error("加载关键词失败:", e);
1355
+ } finally {
1356
+ if (append) currentTagsLoading = false;
1357
+ }
1358
+ }
1359
+
1360
+ function renderTagsTable() {
1361
+ const el = document.getElementById("tagsTable");
1362
+ const moreHint = document.getElementById("tagsMoreHint");
1363
+ let display = [...currentTags];
1364
+
1365
+ // 搜索过滤(本地二次过滤)
1366
+ const searchVal =
1367
+ document.getElementById("tagsSearchInput")?.value.trim().toLowerCase() ||
1368
+ "";
1369
+ if (searchVal) {
1370
+ display = display.filter((t) => t.tag.toLowerCase().includes(searchVal));
1371
+ }
1372
+
1373
+ // 本地排序
1374
+ if (currentTagsSort.key) {
1375
+ display.sort((a, b) => {
1376
+ let va = a[currentTagsSort.key];
1377
+ let vb = b[currentTagsSort.key];
1378
+ if (va == null && vb == null) return 0;
1379
+ if (va == null) return 1;
1380
+ if (vb == null) return -1;
1381
+ if (typeof va === "number" && typeof vb === "number") {
1382
+ return currentTagsSort.asc ? va - vb : vb - va;
1383
+ }
1384
+ va = String(va).toLowerCase();
1385
+ vb = String(vb).toLowerCase();
1386
+ return currentTagsSort.asc ? va.localeCompare(vb) : vb.localeCompare(va);
1387
+ });
1388
+ }
1389
+
1390
+ if (display.length === 0) {
1391
+ el.innerHTML =
1392
+ '<tr><td colspan="11" style="text-align:center;color:#888;padding:24px">暂无数据</td></tr>';
1393
+ if (moreHint) moreHint.style.display = "none";
1394
+ return;
1395
+ }
1396
+
1397
+ const statusLabels = {
1398
+ new: '<span class="tag pending">待打分</span>',
1399
+ scoring: '<span class="tag processing">打分中</span>',
1400
+ productive: '<span class="tag seller">有效</span>',
1401
+ dead: '<span class="tag no-video">无效</span>',
1402
+ };
1403
+ const sourceLabels = {
1404
+ llm: "LLM",
1405
+ manual: "手动",
1406
+ };
1407
+
1408
+ el.innerHTML = display
1409
+ .map((t, i) => {
1410
+ const countries = (t.countries || []).join(", ") || "-";
1411
+ const statusTag =
1412
+ statusLabels[t.status] ||
1413
+ `<span class="tag pending">${t.status}</span>`;
1414
+ const source = sourceLabels[t.source] || t.source || "-";
1415
+ const created = t.created_at || "-";
1416
+ const score = t.score != null ? t.score.toFixed(1) : "-";
1417
+ const authorCount = t.author_count ?? "-";
1418
+ const totalPosts = t.total_posts ?? "-";
1419
+ const matchedAuthors = t.matched_authors ?? "-";
1420
+ const pushedUsers = t.pushed_users ?? "-";
1421
+
1422
+ return `<tr>
1423
+ <td style="color:#9ca3af;font-size:12px;text-align:center">${i + 1}</td>
1424
+ <td style="font-weight:600;color:#e0e0e0">#${escapeHtml(t.tag)}</td>
1425
+ <td style="color:${t.score >= 5 ? "#22c55e" : t.score >= 3 ? "#facc15" : "#888"}">${score}</td>
1426
+ <td>${authorCount}</td>
1427
+ <td>${totalPosts}</td>
1428
+ <td>${matchedAuthors}</td>
1429
+ <td>${pushedUsers}</td>
1430
+ <td style="font-size:11px">${countries}</td>
1431
+ <td>${statusTag}</td>
1432
+ <td style="font-size:11px;color:#888">${source}</td>
1433
+ <td style="font-size:11px;color:#888">${created}</td>
1434
+ </tr>`;
1435
+ })
1436
+ .join("");
1437
+
1438
+ if (moreHint) {
1439
+ moreHint.style.display = "";
1440
+ if (currentTagsAllLoaded) {
1441
+ moreHint.innerHTML = `已全部加载`;
1442
+ moreHint.style.color = "#9ca3af";
1443
+ moreHint.style.cursor = "default";
1444
+ } else if (currentTagsLoading) {
1445
+ moreHint.innerHTML = `加载中...`;
1446
+ moreHint.style.color = "#9ca3af";
1447
+ moreHint.style.cursor = "wait";
1448
+ } else {
1449
+ moreHint.innerHTML = `<u style="color:#3b82f6">点击加载更多</u> ↓`;
1450
+ moreHint.style.color = "#6b7280";
1451
+ moreHint.style.cursor = "pointer";
1452
+ }
1453
+ }
1454
+ }
1455
+
1456
+ // ====== 关键词页面事件绑定 ======
1457
+
1458
+ // 刷新
1459
+ document
1460
+ .getElementById("refreshTagsBtn")
1461
+ .addEventListener("click", async () => {
1462
+ showLoading("正在刷新...");
1463
+ try {
1464
+ await Promise.all([loadTagStats(), loadTagsPage()]);
1465
+ showToast("刷新完成");
1466
+ } catch (e) {
1467
+ showToast("刷新失败: " + e.message, true);
1468
+ } finally {
1469
+ hideLoading();
1470
+ }
1471
+ });
1472
+
1473
+ // 国家筛选(事件委托,避免替换 innerHTML 后绑定丢失)
1474
+ document.getElementById("tagsPage").addEventListener("change", (e) => {
1475
+ if (e.target && e.target.id === "tagsCountryFilter") {
1476
+ currentTagsCountry = e.target.value;
1477
+ showLoading("正在加载...");
1478
+ loadTagsPage().finally(hideLoading);
1479
+ }
1480
+ });
1481
+
1482
+ // 搜索防抖
1483
+ let tagsSearchTimer = null;
1484
+ document.getElementById("tagsSearchInput").addEventListener("input", () => {
1485
+ if (tagsSearchTimer) clearTimeout(tagsSearchTimer);
1486
+ tagsSearchTimer = setTimeout(() => {
1487
+ showLoading("正在搜索...");
1488
+ loadTagsPage().finally(hideLoading);
1489
+ }, 300);
1490
+ });
1491
+
1492
+ // 加载更多
1493
+ function loadMoreTags() {
1494
+ loadTagsPage(true);
1495
+ }
1496
+
1497
+ // 排序
1498
+ document.querySelectorAll(".sortable-tag").forEach((th) => {
1499
+ th.addEventListener("click", () => {
1500
+ const key = th.dataset.sort;
1501
+ if (currentTagsSort.key === key) {
1502
+ currentTagsSort.asc = !currentTagsSort.asc;
1503
+ } else {
1504
+ currentTagsSort.key = key;
1505
+ currentTagsSort.asc = true;
1506
+ }
1507
+ th.querySelectorAll(".sort-icon").forEach(
1508
+ (icon) => (icon.textContent = "↕"),
1509
+ );
1510
+ const icon = th.querySelector(".sort-icon");
1511
+ if (icon) icon.textContent = currentTagsSort.asc ? "↑" : "↓";
1512
+ renderTagsTable();
1513
+ });
1514
+ });
1515
+
1040
1516
  function showTargetPage() {
1041
1517
  document.getElementById("mainPage").classList.add("hidden");
1042
1518
  document.getElementById("pendingPage").classList.remove("active");
1043
1519
  document.getElementById("userUpdatePage").classList.remove("active");
1044
1520
  document.getElementById("rawPage").classList.remove("active");
1045
1521
  document.getElementById("targetPage").classList.add("active");
1522
+ document.getElementById("tagsPage").classList.remove("active");
1046
1523
  showLoading("正在加载目标商家数据...");
1047
1524
  fetchTargetByCountry();
1048
1525
  // 同步统计
@@ -1209,7 +1686,7 @@ function renderTargetTable() {
1209
1686
 
1210
1687
  if (displayUsers.length === 0) {
1211
1688
  el.innerHTML =
1212
- '<tr><td colspan="10" style="text-align:center;color:#888;padding:24px">暂无数据</td></tr>';
1689
+ '<tr><td colspan="11" style="text-align:center;color:#888;padding:24px">暂无数据</td></tr>';
1213
1690
  if (moreHint) {
1214
1691
  moreHint.style.display = "none";
1215
1692
  }
@@ -1224,17 +1701,20 @@ function renderTargetTable() {
1224
1701
  const fans = u.followerCount != null ? formatNum(u.followerCount) : "-";
1225
1702
  const videos = u.videoCount != null ? u.videoCount : "-";
1226
1703
  const userLocation = u.locationCreated || "-";
1227
- const confirmedLocation = u.confirmedLocation
1228
- ? u.confirmedLocation === u.locationCreated
1229
- ? `<span style="color:#22c55e">${u.confirmedLocation} ✓</span>`
1230
- : `<span style="color:#ef4444">${u.confirmedLocation} ✗</span>`
1231
- : "-";
1704
+ // 如果用户手工修改过国家,确认国家列显示"已修正"
1705
+ const confirmedLocation = u.modifiedAt
1706
+ ? `<span style="color:#f59e0b;font-weight:600">已修正</span>`
1707
+ : u.confirmedLocation
1708
+ ? u.confirmedLocation === u.locationCreated
1709
+ ? `<span style="color:#22c55e">${u.confirmedLocation} ✓</span>`
1710
+ : `<span style="color:#ef4444">${u.confirmedLocation} ✗</span>`
1711
+ : "-";
1232
1712
  const latestVideo = u.latestVideoTime
1233
1713
  ? formatTime(u.latestVideoTime * 1000)
1234
1714
  : "-";
1235
1715
  const refreshTime = u.refreshTime ? formatTime(u.refreshTime) : "-";
1236
1716
  const topPlayCount =
1237
- u.topVideoPlayCount != null && u.topVideoPlayCount > 0
1717
+ u.topVideoPlayCount != null && u.topVideoPlayCount >= 1000
1238
1718
  ? formatNum(u.topVideoPlayCount)
1239
1719
  : "-";
1240
1720
  const topPlayCountCell = u.topVideoHref
@@ -1270,6 +1750,9 @@ function renderTargetTable() {
1270
1750
  ${topPlayCountCell}
1271
1751
  <td data-label="最近发布" style="font-size:11px;color:#888">${latestVideo}</td>
1272
1752
  <td data-label="最近刷新" style="font-size:11px;color:#888">${refreshTime}</td>
1753
+ <td data-label="操作">
1754
+ <button class="btn-non-seller" onclick="confirmNonSeller('${escapeJsString(u.uniqueId)}')" title="将此用户标记为非商家">设为非商家</button>
1755
+ </td>
1273
1756
  </tr>`;
1274
1757
  })
1275
1758
  .join("");