diff --git a/apps/desktop-client/src/renderer/views/AccountsView.vue b/apps/desktop-client/src/renderer/views/AccountsView.vue index 8651e5a..d8dd2dd 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, formatVerifiedAtLabel, titleCaseToken } from "../lib/formatters"; +import { formatDateTime, formatRelativeTime, formatVerifiedAtLabel } from "../lib/formatters"; import { desktopPublishMediaCatalog } from "../lib/media-catalog"; import type { RuntimeAccount } from "../types"; @@ -18,6 +18,7 @@ const searchQuery = ref(""); const bindPendingPlatformId = ref(null); const consolePendingAccountId = ref(null); const unbindPendingAccountId = ref(null); +const reauthorizePendingAccountId = ref(null); const probePendingAccountIds = ref(new Set()); const actionSuccess = ref(null); @@ -59,8 +60,8 @@ function platformMeta(platform: string) { return ( desktopPublishMediaCatalog.find((item) => item.id === platform) ?? { id: platform, - label: titleCaseToken(platform), - shortName: titleCaseToken(platform).slice(0, 1), + label: "未知平台", + shortName: "未", accent: "#64748b", loginUrl: null, logoUrl: null, @@ -150,6 +151,15 @@ function sessionStateLabel(account: AccountRow): string { } function verificationTimeLabel(account: AccountRow): string { + switch (account.authState) { + case "expired": + return "需要重新授权"; + case "revoked": + return "已停用"; + case "challenge_required": + return account.authReason === "risk_control" ? "触发风控,需处理" : "需要人工验证"; + } + if (account.lastVerifiedAt) { return formatVerifiedAtLabel(account.lastVerifiedAt); } @@ -162,6 +172,16 @@ function verificationTimeLabel(account: AccountRow): string { return "尚未校验"; } +function accountRecoveryActionLabel(account: AccountRow): string { + return authState(account) === "expired" ? "重新授权" : "重新校验"; +} + +function isRecoveryActionPending(account: AccountRow): boolean { + return authState(account) === "expired" + ? reauthorizePendingAccountId.value === account.id || bindPendingPlatformId.value === account.platform + : isProbePending(account); +} + const filteredAccounts = computed(() => { const keyword = searchQuery.value.trim().toLowerCase(); @@ -272,6 +292,26 @@ function isProbePending(account: AccountRow): boolean { ); } +async function recoverAccount(account: AccountRow) { + if (authState(account) !== "expired") { + await verifyAccount(account); + return; + } + + reauthorizePendingAccountId.value = account.id; + actionSuccess.value = null; + + try { + const nextAccount = await window.desktopBridge.app.bindPublishAccount(account.platform); + actionSuccess.value = `${nextAccount.display_name} 已重新授权`; + await refresh(); + } catch (error) { + showClientActionError("bind-account", error); + } finally { + reauthorizePendingAccountId.value = null; + } +} + const tableVirtual = computed(() => filteredAccounts.value.length > 20); const tableScroll = computed(() => (tableVirtual.value ? { x: 960, y: 640 } : { x: 960 })); @@ -290,7 +330,7 @@ const tableColumns = [
-

MEDIA ACCOUNT MANAGEMENT

+

媒体授权中心

媒体账号管理

@@ -320,7 +360,7 @@ const tableColumns = [
- +

选择平台进行授权

@@ -348,7 +388,6 @@ const tableColumns = [

{{ platform.count > 0 ? `${platform.count} 个账号` : "未绑定" }}

-
- +
@@ -394,7 +433,7 @@ const tableColumns = [

授权账号列表

- +
所有平台 @@ -487,9 +526,12 @@ const tableColumns = [ - - - + + + @@ -517,7 +559,7 @@ const tableColumns = [ max-width: 1400px; } -/* Hero Section */ +/* 顶部信息区 */ .hero-card { background: #ffffff; border-radius: 20px; @@ -565,7 +607,7 @@ const tableColumns = [ font-weight: 600; } -/* Stats Strip */ +/* 统计条 */ .stats-strip { display: flex; padding: 24px 40px; @@ -595,7 +637,7 @@ const tableColumns = [ font-feature-settings: "tnum"; } -/* Content Section */ +/* 内容区 */ .content-section { display: flex; flex-direction: column; @@ -626,7 +668,7 @@ const tableColumns = [ gap: 20px; } -/* Modern Card Layout */ +/* 卡片布局 */ .modern-card { display: flex; flex-direction: column; @@ -659,7 +701,7 @@ const tableColumns = [ display: flex; justify-content: space-between; align-items: flex-start; - margin-bottom: 8px; /* Reduced because content goes into footer */ + margin-bottom: 8px; } .brand-info { @@ -726,7 +768,7 @@ const tableColumns = [ height: 34px; } -/* Modern Enterprise Table Styles */ +/* 表格样式 */ :deep(.modern-table) { padding: 0 32px 32px; } @@ -842,7 +884,7 @@ const tableColumns = [ align-items: center; } -/* Responsiveness */ +/* 响应式 */ @media (max-width: 1024px) { .hero-header { flex-direction: column; diff --git a/apps/desktop-client/src/renderer/views/AiPlatformsView.vue b/apps/desktop-client/src/renderer/views/AiPlatformsView.vue index cb6e43c..9199dc5 100644 --- a/apps/desktop-client/src/renderer/views/AiPlatformsView.vue +++ b/apps/desktop-client/src/renderer/views/AiPlatformsView.vue @@ -95,7 +95,15 @@ function authColor(account: AccountRow | null) { } function accountActionAlert(account: AccountRow | null): string | null { - if (!account || account.authState !== "challenge_required") { + if (!account) { + return null; + } + + if (account.authState === "expired") { + return "当前账号授权已过期,相关任务会暂停执行。请点击“重新授权”完成登录后再继续。"; + } + + if (account.authState !== "challenge_required") { return null; } @@ -111,6 +119,15 @@ function accountActionAlert(account: AccountRow | null): string | null { } function verificationLabel(account: AccountRow): string { + switch (account.authState) { + case "expired": + return "需要重新授权"; + case "revoked": + return "已停用"; + case "challenge_required": + return account.authReason === "risk_control" ? "触发风控,需处理" : "需要人工验证"; + } + if (account.lastVerifiedAt) { return formatVerifiedAtLabel(account.lastVerifiedAt); } @@ -141,6 +158,10 @@ function sessionLabel(account: AccountRow): string { } } +function isReauthorizePending(account: AccountRow): boolean { + return bindPendingPlatformId.value === account.platform; +} + function onlineLabel(account: AccountRow): string { return account.online ? "客户端在线" : "客户端离线"; } @@ -307,6 +328,16 @@ async function unbindPlatform(account: AccountRow) {
+ + + + +