From 69fd182755eebe2c230239cbc7aaca5a94259e2b Mon Sep 17 00:00:00 2001 From: liangxu Date: Mon, 20 Apr 2026 17:25:58 +0800 Subject: [PATCH] refactor(desktop): narrow home health board to expired/revoked accounts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Home 看板只保留已过期或已停用的账号,问题计数改用阻塞态 authState;新增 formatVerifiedAtLabel,统一校验通过时间的展示粒度。 Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/main/runtime-snapshot.ts | 5 ++- .../src/renderer/lib/formatters.ts | 15 ++++++++ .../src/renderer/views/AccountsView.vue | 4 +-- .../src/renderer/views/AiPlatformsView.vue | 4 +-- .../src/renderer/views/HomeView.vue | 35 ++++++++++--------- 5 files changed, 41 insertions(+), 22 deletions(-) diff --git a/apps/desktop-client/src/main/runtime-snapshot.ts b/apps/desktop-client/src/main/runtime-snapshot.ts index cdd35dc..669d553 100644 --- a/apps/desktop-client/src/main/runtime-snapshot.ts +++ b/apps/desktop-client/src/main/runtime-snapshot.ts @@ -114,6 +114,9 @@ function createLiveRuntimeSnapshot(controller: ReturnType + ["expired", "revoked", "challenge_required"].includes(item.authState), + ).length; return { generatedAt: now, @@ -138,7 +141,7 @@ function createLiveRuntimeSnapshot(controller: ReturnType item.status === "queued").length, issuesOpen: tasks.filter((item) => ["unknown", "failed"].includes(item.status)).length - + accounts.filter((item) => item.health !== "live").length, + + blockingAccountCount, healthCounts, }, clients, diff --git a/apps/desktop-client/src/renderer/lib/formatters.ts b/apps/desktop-client/src/renderer/lib/formatters.ts index 6ff7705..2154f6e 100644 --- a/apps/desktop-client/src/renderer/lib/formatters.ts +++ b/apps/desktop-client/src/renderer/lib/formatters.ts @@ -41,6 +41,21 @@ export function formatDateTime(value: number | null): string { }).format(value); } +export function formatVerifiedAtLabel(value: number | null): string { + if (!value) { + return "尚未完成校验"; + } + + const deltaMs = Math.abs(Date.now() - value); + if (deltaMs < 60_000) { + return "刚刚校验通过"; + } + if (deltaMs < 60 * 60_000) { + return `${Math.max(1, Math.round(deltaMs / 60_000))} 分钟前校验通过`; + } + return `${formatDateTime(value)} 校验通过`; +} + export function titleCaseToken(value: string): string { return value .split(/[_-]/g) diff --git a/apps/desktop-client/src/renderer/views/AccountsView.vue b/apps/desktop-client/src/renderer/views/AccountsView.vue index 186a8a7..691fa39 100644 --- a/apps/desktop-client/src/renderer/views/AccountsView.vue +++ b/apps/desktop-client/src/renderer/views/AccountsView.vue @@ -6,7 +6,7 @@ import StatusBadge from "../components/StatusBadge.vue"; import SurfaceCard from "../components/SurfaceCard.vue"; import { useDesktopRuntime } from "../composables/useDesktopRuntime"; import { showClientActionError } from "../lib/client-errors"; -import { formatDateTime, formatRelativeTime, titleCaseToken } from "../lib/formatters"; +import { formatDateTime, formatRelativeTime, formatVerifiedAtLabel, titleCaseToken } from "../lib/formatters"; import { desktopPublishMediaCatalog } from "../lib/media-catalog"; import type { RuntimeAccount } from "../types"; @@ -126,7 +126,7 @@ function sessionStateLabel(account: AccountRow): string { function verificationTimeLabel(account: AccountRow): string { if (account.lastVerifiedAt) { - return `${formatRelativeTime(account.lastVerifiedAt)}校验通过`; + return formatVerifiedAtLabel(account.lastVerifiedAt); } if (account.probeState === "probing") { return "正在校验"; diff --git a/apps/desktop-client/src/renderer/views/AiPlatformsView.vue b/apps/desktop-client/src/renderer/views/AiPlatformsView.vue index 683a8e4..31e0b6f 100644 --- a/apps/desktop-client/src/renderer/views/AiPlatformsView.vue +++ b/apps/desktop-client/src/renderer/views/AiPlatformsView.vue @@ -11,7 +11,7 @@ import { computed, ref } from "vue"; import { useDesktopRuntime } from "../composables/useDesktopRuntime"; import { showClientActionError } from "../lib/client-errors"; -import { formatDateTime, formatRelativeTime } from "../lib/formatters"; +import { formatDateTime, formatRelativeTime, formatVerifiedAtLabel } from "../lib/formatters"; import { desktopMonitoringMediaCatalog } from "../lib/media-catalog"; import type { RuntimeAccount } from "../types"; @@ -90,7 +90,7 @@ function authColor(account: AccountRow | null) { function verificationLabel(account: AccountRow): string { if (account.lastVerifiedAt) { - return `${formatRelativeTime(account.lastVerifiedAt)}校验通过`; + return formatVerifiedAtLabel(account.lastVerifiedAt); } if (account.probeState === "probing") { return "正在执行首轮校验"; diff --git a/apps/desktop-client/src/renderer/views/HomeView.vue b/apps/desktop-client/src/renderer/views/HomeView.vue index 9ed33bf..d96ad4a 100644 --- a/apps/desktop-client/src/renderer/views/HomeView.vue +++ b/apps/desktop-client/src/renderer/views/HomeView.vue @@ -5,7 +5,7 @@ import { DesktopOutlined, LinkOutlined, ClockCircleOutlined, AlertOutlined } fro import StatusBadge from "../components/StatusBadge.vue"; import { useDesktopRuntime } from "../composables/useDesktopRuntime"; import { formatClock, formatRelativeTime, titleCaseToken } from "../lib/formatters"; -import { healthTone, taskTone } from "../lib/runtime-ui"; +import { healthTone } from "../lib/runtime-ui"; function translatePlatform(platform: string) { const map: Record = { @@ -21,15 +21,14 @@ function translatePlatform(platform: string) { return map[platform?.toLowerCase()] || titleCaseToken(platform); } -function translateHealth(status: string) { - const map: Record = { - live: "正常", - expired: "已过期", - disconnected: "已断开", - risk_control: "风控中", - needs_captcha: "需验证码", - }; - return map[status?.toLowerCase()] || status; +function expiredAccountLabel(authState: string) { + switch (authState) { + case "revoked": + return "已停用"; + case "expired": + default: + return "已过期"; + } } function translateEventTitle(title: string) { @@ -84,7 +83,9 @@ const tasks = computed(() => snapshot.value?.tasks ?? []); const accounts = computed(() => snapshot.value?.accounts ?? []); const activity = computed(() => snapshot.value?.activity ?? []); -const riskyAccounts = computed(() => accounts.value.filter((item) => item.health !== "live")); +const expiredAccounts = computed(() => + accounts.value.filter((item) => ["expired", "revoked"].includes(item.authState)), +); const subsystemCards = computed(() => { const subsystems = snapshot.value?.subsystems ?? {}; @@ -158,23 +159,23 @@ const subsystemCards = computed(() => {

账号健康看板

-

把需要修复登录态、验证码或风险确认的账号单独拉出来。

+

这里只展示已经确认过期、需要重新授权的账号。

-
-
+
+
{{ account.displayName }} {{ translatePlatform(account.platform) }} · {{ formatUid(account.platformUid) }}
- - {{ formatRelativeTime(account.lastSyncAt) }} + + {{ formatRelativeTime(account.lastVerifiedAt ?? account.lastSyncAt) }}
-

当前没有发现异常账号,一切运行正常。

+

当前没有已过期账号。