fix SCUM player online freshness display

This commit is contained in:
npc0-hue
2026-08-27 10:12:23 +08:00
parent 9022cf0bea
commit 2937ece1bc
5 changed files with 21 additions and 8 deletions
@@ -181,16 +181,17 @@ function renderSurfaceBody(e: ReactLike["createElement"], pageKey: string, data:
function playersSurface(e: ReactLike["createElement"], data: SCUMSurfaceData, input: SCUMPageContext, view: ViewState) {
const search = view.playerSearch.trim().toLowerCase();
const players = data.players.filter((player) => matchesText(player, search, "displayName", "playerName", "gamePlayerId", "playerId", "steamId", "squadName") && (view.playerStatus === "all" || (view.playerStatus === "online") === playerOnline(player)));
const onlineCount = data.players.filter(playerOnline).length;
const selectedPlayer = data.players.find((player) => playerKey(player) === view.playerPanel.playerId);
return e("div", { className: "console-record-list scum-surface" },
e("div", { className: "resource-filter-bar scum-filter-bar" },
labeledField(e, "搜索用户", e("input", { type: "search", value: view.playerSearch, "aria-label": "搜索用户", placeholder: "名称 / Steam ID / 队伍", onChange: (event: InputEvent) => view.setPlayerSearch(inputValue(event)) })),
labeledField(e, "在线状态", e("select", { value: view.playerStatus, "aria-label": "在线状态", onChange: (event: InputEvent) => view.setPlayerStatus(inputValue(event)) }, e("option", { value: "all" }, "全部状态"), e("option", { value: "online" }, "在线"), e("option", { value: "offline" }, "离线/未知"))),
e("span", { className: "page-status" }, `${players.length} / ${data.players.length} 用户`)
e("span", { className: "scum-online-summary" }, "在线用户 ", e("strong", null, `${onlineCount} / ${data.players.length}`)),
players.length !== data.players.length ? e("span", { className: "page-status" }, `当前显示 ${players.length}`) : null
),
e("div", { className: "provider-table-wrap scum-table-wrap" },
e("table", { className: "resource-table scum-user-table" },
e("caption", { className: "provider-id" }, "SCUM 用户真实记录"),
e("thead", null, e("tr", null, ["用户名", "队伍", "状态", "最后活动", "操作"].map((label) => e("th", { key: label, scope: "col" }, label)))),
e("tbody", null, players.length ? players.map((player, index) => playerTableRow(e, player, index, data, input, view)) : e("tr", null, e("td", { colSpan: 5 }, e("p", { className: "page-status" }, "没有符合筛选条件的真实用户记录。"))))
)
@@ -578,7 +579,9 @@ function field(row: RecordMap | undefined, ...keys: string[]): unknown { if (!ro
function normalizeKey(value: string): string { return value.replace(/[_-]/g, "").toLowerCase(); }
function textField(row: RecordMap | undefined, ...keys: string[]): string { const value = field(row, ...keys); return value === undefined || value === null ? "" : String(value); }
function boolField(row: RecordMap, ...keys: string[]): boolean { const value = field(row, ...keys); return value === true || value === 1 || value === "1" || value === "true"; }
function playerOnline(player: RecordMap): boolean { const status = textField(player, "status").toLowerCase(); return boolField(player, "online") || ["online", "active", "connected"].includes(status); }
const playerActivityKeys = ["lastSeenAt", "lastLoginObservedAt", "onlineObservedAt", "updatedAt"];
const playerOnlineWindowMs = 5 * 60 * 1000;
function playerOnline(player: RecordMap): boolean { const status = textField(player, "status").toLowerCase(); const active = boolField(player, "online") || ["online", "active", "connected"].includes(status); if (!active) return false; const lastActivity = field(player, ...playerActivityKeys); if (lastActivity === undefined || lastActivity === null || lastActivity === "") return true; const timestamp = Date.parse(String(lastActivity)); return Number.isNaN(timestamp) || Date.now() - timestamp <= playerOnlineWindowMs; }
function numField(row: RecordMap, ...keys: string[]): string { const value = field(row, ...keys); return value === undefined || value === null || value === "" ? "--" : String(value); }
function idOf(row: RecordMap | undefined, fallback: string): string { return textField(row, "_recordKey", "id", "gamePlayerId", "squadId", "vehicleId", "flagId", "eventId", "code") || fallback; }
function freshness(row: RecordMap): string { const fresh = field(row, "freshness"); return isRecord(fresh) ? textField(fresh, "status") || "unknown" : textField(row, "freshnessStatus", "updatedAt") || "unknown"; }