diff --git a/apps/admin-web/src/i18n/messages/en-US.ts b/apps/admin-web/src/i18n/messages/en-US.ts index b4b5e4b..f89735a 100644 --- a/apps/admin-web/src/i18n/messages/en-US.ts +++ b/apps/admin-web/src/i18n/messages/en-US.ts @@ -82,6 +82,7 @@ const enUS = { tracking: 'Tracking', trackingDetail: 'Data Details', trackingMarkedArticles: 'External Article Marks', + aiAccounts: 'AI Account Management', contentManagement: 'Content Management', knowledge: 'Knowledge Base', images: 'Image Management', @@ -270,6 +271,10 @@ const enUS = { description: 'Manage manually marked external article URLs so they count in cited articles and citation rankings.', }, + aiAccounts: { + title: 'AI Account Management', + description: 'Review AI model accounts, authorization state, and desktop runtime nodes.', + }, trackingQuestion: { title: 'Question Detail', description: diff --git a/apps/admin-web/src/i18n/messages/zh-CN.ts b/apps/admin-web/src/i18n/messages/zh-CN.ts index 786f2cc..78cfd40 100644 --- a/apps/admin-web/src/i18n/messages/zh-CN.ts +++ b/apps/admin-web/src/i18n/messages/zh-CN.ts @@ -92,6 +92,7 @@ const zhCN = { tracking: '数据追踪', trackingDetail: '数据详情', trackingMarkedArticles: '外部文章标记', + aiAccounts: 'AI 账号管理', contentManagement: '内容管理', knowledge: '知识库', images: '图片管理', @@ -269,6 +270,10 @@ const zhCN = { title: '外部文章标记', description: '维护用户手动标记的外部文章 URL,让它们参与引用文章和引用排行统计。', }, + aiAccounts: { + title: 'AI 账号管理', + description: '查看 AI 模型账号、授权状态和桌面运行节点。', + }, trackingQuestion: { title: '问题详情', description: '按问题查看大模型回答、引用来源、引用分析和回答引用。', @@ -564,7 +569,8 @@ const zhCN = { backToQuestions: '返回问题列表', citationRanking: 'Citation Ranking', citationRankingTitle: '引用排行', - citationRankingHint: '按发布外链、投稿外链和手动标记 URL 归因外部文章在模型引用来源中的出现次数与占比。', + citationRankingHint: + '按发布外链、投稿外链和手动标记 URL 归因外部文章在模型引用来源中的出现次数与占比。', citationWindow7: '近 7 天', citationWindow30: '近 30 天', citationWindowLabel: '近 {days} 天', @@ -697,7 +703,8 @@ const zhCN = { }, trackingMarkedArticles: { title: '外部文章标记', - description: '把无法自动对接的官网、媒体或第三方页面标记为外部文章,系统会在 6 个月内用于引用归因。', + description: + '把无法自动对接的官网、媒体或第三方页面标记为外部文章,系统会在 6 个月内用于引用归因。', createAction: '标记文章', createTitle: '标记外部文章', editTitle: '编辑外部文章', diff --git a/apps/admin-web/src/layouts/AppShell.vue b/apps/admin-web/src/layouts/AppShell.vue index 03d2f83..f0ada71 100644 --- a/apps/admin-web/src/layouts/AppShell.vue +++ b/apps/admin-web/src/layouts/AppShell.vue @@ -1,5 +1,6 @@ + + + + diff --git a/apps/admin-web/src/views/TrackingView.vue b/apps/admin-web/src/views/TrackingView.vue index 62fdab6..0691e8e 100644 --- a/apps/admin-web/src/views/TrackingView.vue +++ b/apps/admin-web/src/views/TrackingView.vue @@ -9,7 +9,6 @@ import { SearchOutlined, } from '@ant-design/icons-vue' import type { - DesktopAccountInfo, MonitoringCitedArticle, MonitoringHotQuestion, MonitoringPlatformAuthorizationStatus, @@ -25,12 +24,10 @@ import { useRoute, useRouter } from 'vue-router' import ArticleDetailDrawer from '@/components/ArticleDetailDrawer.vue' import PageHero from '@/components/PageHero.vue' -import { monitoringApi, tenantAccountsApi } from '@/lib/api' -import { resolveAccountCheckedAt, resolveAccountHealth } from '@/lib/desktop-account-runtime' +import { monitoringApi } from '@/lib/api' import { formatDateTime } from '@/lib/display' import { formatError } from '@/lib/errors' import { - isMonitoringPlatformId, normalizeMonitoringPlatformFilter, normalizeMonitoringPlatformId, } from '@/lib/monitoring-platforms' @@ -62,19 +59,6 @@ type CitationRankingRow = { glyph: string } -type AIPlatformStatusCard = { - id: string - label: string - shortName: string - accent: string - description: string - account: DesktopAccountInfo | null - isClientOnline: boolean - sampleStatus: string - lastSampledAt: string | null - actualSampleCount: number -} - function formatTrackingBusinessDate(value: Date): string { const parts = new Intl.DateTimeFormat('en-US', { timeZone: trackingBusinessTimeZone, @@ -366,11 +350,6 @@ const citationSummaryQuery = useQuery({ }), }) -const aiAccountsQuery = useQuery({ - queryKey: ['tracking', 'ai-platform-accounts'], - queryFn: () => tenantAccountsApi.list(), -}) - const collectNowMutation = useMutation({ mutationFn: async () => { if (!selectedBrandId.value) { @@ -475,61 +454,6 @@ const hotQuestionsPageInfo = computed(() => { const hotQuestionsTotal = computed(() => hotQuestionsPageInfo.value?.total ?? 0) -const aiPlatformStatusCards = computed(() => { - const accountGroups = new Map() - for (const account of aiAccountsQuery.data.value ?? []) { - const platformId = normalizeMonitoringPlatformId(account.platform) - if (!isMonitoringPlatformId(platformId) || account.deleted_at) { - continue - } - const list = accountGroups.get(platformId) ?? [] - list.push(account) - accountGroups.set(platformId, list) - } - - const breakdownMap = new Map( - (dashboardQuery.data.value?.platform_breakdown ?? []).map((item) => [ - normalizeMonitoringPlatformId(item.ai_platform_id), - item, - ]), - ) - const onlineAuthorizedPlatformIDs = new Set( - (dashboardQuery.data.value?.runtime.online_authorized_monitoring_platform_ids ?? []) - .map((item) => normalizeMonitoringPlatformId(item)) - .filter((item): item is string => Boolean(item)), - ) - - return aiPlatformCatalog.map((platform) => { - const matchedAccounts = [...(accountGroups.get(platform.id) ?? [])].sort((left, right) => { - const leftOnline = left.client_online === true ? 1 : 0 - const rightOnline = right.client_online === true ? 1 : 0 - if (leftOnline !== rightOnline) { - return rightOnline - leftOnline - } - const leftAt = Date.parse(resolveAccountCheckedAt(left) ?? '') || 0 - const rightAt = Date.parse(resolveAccountCheckedAt(right) ?? '') || 0 - return rightAt - leftAt - }) - const breakdown = breakdownMap.get(platform.id) - - return { - id: platform.id, - label: platform.label, - shortName: platform.shortName, - accent: platform.accent, - description: platform.description, - account: matchedAccounts[0] ?? null, - isClientOnline: - onlineAuthorizedPlatformIDs.has(platform.id) || - (!dashboardQuery.data.value && - matchedAccounts.some((account) => account.client_online === true)), - sampleStatus: breakdown?.platform_sample_status ?? 'pending', - lastSampledAt: breakdown?.last_sampled_at ?? null, - actualSampleCount: breakdown?.actual_sample_count ?? 0, - } - }) -}) - const selectedMetricCards = computed(() => { const overview = dashboardQuery.data.value?.overview return [ @@ -820,48 +744,6 @@ function normalizeCitationWindowDays(value: unknown): number { return numeric === 30 ? 30 : 7 } -function accountAuthLabel(account: DesktopAccountInfo | null): string { - if (!account) { - return '未绑定' - } - - switch (resolveAccountHealth(account)) { - case 'live': - return '授权正常' - case 'captcha': - return '待处理' - case 'expired': - return '授权过期' - default: - return '风险观察' - } -} - -function accountAuthColor(account: DesktopAccountInfo | null): string { - if (!account) { - return 'default' - } - - switch (resolveAccountHealth(account)) { - case 'live': - return 'green' - case 'captcha': - return 'gold' - case 'expired': - return 'red' - default: - return 'orange' - } -} - -function desktopStatusLabel(account: DesktopAccountInfo | null, isClientOnline: boolean): string { - if (!account?.client_id) { - return '未绑定桌面端' - } - - return isClientOnline ? '客户端在线' : '客户端离线' -} - function formatCount(value: number | null | undefined): string { if (value === null || value === undefined) { return '--' @@ -1075,7 +957,9 @@ function disableTrackingDate(current: dayjs.Dayjs): boolean { ]" > - + + ↓ + {{ (formatDelta(card.value, card.previous) ?? '').replace(/[+-]/, '') }} @@ -1130,80 +1014,6 @@ function disableTrackingDate(current: dayjs.Dayjs): boolean { - -
-
-

AI 平台状态

-
-
- -
-
-
-
- - {{ platform.shortName }} - -
- {{ platform.label }} -

{{ platform.description }}

-
-
- - - {{ accountAuthLabel(platform.account) }} - -
- -
-
- 绑定状态 - {{ platform.account ? '已绑定' : '未绑定' }} -
-
- 客户端 - {{ desktopStatusLabel(platform.account, platform.isClientOnline) }} -
-
- 采样状态 - - {{ statusLabel(platform.sampleStatus) }} - -
-
- 最近采样 - - {{ platform.lastSampledAt ? formatDateTime(platform.lastSampledAt) : '--' }} - -
-
- 样本数 - {{ formatCount(platform.actualSampleCount) }} -
-
- 账号 - {{ platform.account.display_name }} -
-
-
-
-
-
@@ -1579,105 +1389,6 @@ function disableTrackingDate(current: dayjs.Dayjs): boolean { grid-template-columns: minmax(0, 1fr); } -.tracking-panel--ai-platforms { - overflow: hidden; -} - -.ai-platform-status-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); - gap: 16px; - margin-top: 12px; -} - -.ai-platform-status-card { - display: flex; - flex-direction: column; - gap: 14px; - padding: 16px; - border-radius: 16px; - border: 1px solid #e2e8f0; - background: #ffffff; - transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); - box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.02); -} - -.ai-platform-status-card:hover { - border-color: var(--platform-accent) !important; - box-shadow: 0 10px 20px -8px var(--platform-accent); - transform: translateY(-2px); -} - -.ai-platform-status-card__head { - display: flex; - justify-content: space-between; - gap: 12px; - align-items: flex-start; -} - -.ai-platform-status-card__identity { - display: flex; - gap: 12px; - align-items: flex-start; -} - -.ai-platform-status-card__identity strong { - display: block; - color: #0f172a; - font-size: 15px; - font-weight: 600; - line-height: 1.2; -} - -.ai-platform-status-card__identity p { - margin: 4px 0 0; - color: #64748b; - line-height: 1.5; - font-size: 12px; -} - -.ai-platform-status-card__badge { - width: 36px; - height: 36px; - display: inline-flex; - align-items: center; - justify-content: center; - border-radius: 10px; - border: 1px solid transparent; - font-size: 14px; - font-weight: 700; - flex-shrink: 0; -} - -.ai-platform-status-card__meta { - display: flex; - flex-direction: column; - gap: 10px; -} - -.ai-platform-status-card__row { - display: flex; - justify-content: space-between; - gap: 16px; - align-items: center; - padding: 8px 12px; - border-radius: 8px; - background: #f8fafc; - border: 1px solid #f1f5f9; -} - -.ai-platform-status-card__row span { - color: #64748b; - font-size: 12px; -} - -.ai-platform-status-card__row strong { - color: #0f172a; - font-size: 13px; - text-align: right; - word-break: break-all; -} - .tracking-citation-section { display: flex; flex-direction: column; @@ -1715,7 +1426,10 @@ function disableTrackingDate(current: dayjs.Dayjs): boolean { border-radius: 16px; background: #ffffff; border: 1px solid rgba(226, 232, 240, 0.8) !important; - box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.02), 0 10px 15px -3px rgba(0, 0, 0, 0.03), 0 0 0 1px rgba(0, 0, 0, 0.01); + box-shadow: + 0 4px 6px -1px rgba(0, 0, 0, 0.02), + 0 10px 15px -3px rgba(0, 0, 0, 0.03), + 0 0 0 1px rgba(0, 0, 0, 0.01); } .tracking-panel--citation { @@ -1845,7 +1559,9 @@ function disableTrackingDate(current: dayjs.Dayjs): boolean { .metric-card:hover { border-color: #3b82f6; transform: translateY(-2px); - box-shadow: 0 10px 15px -3px rgba(59, 130, 246, 0.05), 0 4px 6px -2px rgba(59, 130, 246, 0.02); + box-shadow: + 0 10px 15px -3px rgba(59, 130, 246, 0.05), + 0 4px 6px -2px rgba(59, 130, 246, 0.02); } .metric-card__label {