diff --git a/apps/desktop-client/src/renderer/views/HomeView.vue b/apps/desktop-client/src/renderer/views/HomeView.vue index 9c6fa68..60e1eae 100644 --- a/apps/desktop-client/src/renderer/views/HomeView.vue +++ b/apps/desktop-client/src/renderer/views/HomeView.vue @@ -4,7 +4,7 @@ import { DesktopOutlined, LinkOutlined, ClockCircleOutlined, AlertOutlined } fro import StatusBadge from "../components/StatusBadge.vue"; import { useDesktopRuntime } from "../composables/useDesktopRuntime"; -import { formatClock, formatRelativeTime } from "../lib/formatters"; +import { formatRelativeTime } from "../lib/formatters"; import { translateDesktopPlatform } from "../lib/media-catalog"; import { healthTone } from "../lib/runtime-ui"; @@ -24,6 +24,32 @@ function blockedAccountLabel(account: { authState: string; authReason: string | } } +function translateTaskStatus(status: string) { + const map: Record = { + failed: "执行失败", + unknown: "结果未知", + }; + return map[status?.toLowerCase()] || status; +} + +function formatIssueDetail(issue: RuntimeIssueItem) { + if (issue.detail) { + return issue.detail; + } + return issue.kind === "task" ? "任务执行结果需要人工确认。" : "账号授权状态需要重新处理。"; +} + +type RuntimeIssueItem = { + id: string; + kind: "task" | "account"; + title: string; + meta: string; + detail: string; + badgeLabel: string; + badgeTone: "danger" | "warn"; + at: number; +}; + function translateEventTitle(title: string) { const map: Record = { "Accounts Synced": "本地账号同步", @@ -70,7 +96,7 @@ function formatUid(uid: string) { return (uid || "").replace(/^(?:platform:)+/gi, ""); } -const { snapshot, loading, refresh } = useDesktopRuntime(); +const { snapshot } = useDesktopRuntime(); const tasks = computed(() => snapshot.value?.tasks ?? []); const accounts = computed(() => snapshot.value?.accounts ?? []); @@ -79,6 +105,41 @@ const activity = computed(() => snapshot.value?.activity ?? []); const blockedAccounts = computed(() => accounts.value.filter((item) => ["expired", "revoked", "challenge_required"].includes(item.authState)), ); +const blockedTasks = computed(() => + tasks.value.filter((item) => ["failed", "unknown"].includes(item.status)), +); +const issueBreakdown = computed(() => ({ + tasks: blockedTasks.value.length, + accounts: blockedAccounts.value.length, + total: blockedTasks.value.length + blockedAccounts.value.length, +})); +const issueItems = computed(() => { + const taskIssues = blockedTasks.value.map((task) => ({ + id: `task-${task.id}`, + kind: "task" as const, + title: task.title || task.jobId, + meta: `${task.accountName} · ${translatePlatform(task.platform)}`, + detail: task.summary, + badgeLabel: translateTaskStatus(task.status), + badgeTone: "danger" as const, + at: task.updatedAt, + })); + const accountIssues = blockedAccounts.value.map((account) => ({ + id: `account-${account.id}`, + kind: "account" as const, + title: account.displayName, + meta: `${translatePlatform(account.platform)} · ${formatUid(account.platformUid)}`, + detail: + account.authReason === "risk_control" + ? "平台触发风控,需要人工验证后恢复任务执行。" + : "授权状态不可用,需要重新登录或确认账号状态。", + badgeLabel: blockedAccountLabel(account), + badgeTone: healthTone(account.health) === "warn" ? "warn" as const : "danger" as const, + at: account.lastVerifiedAt ?? account.lastSyncAt, + })); + + return [...taskIssues, ...accountIssues].sort((left, right) => right.at - left.at); +});