财务分析移除 Expert 套餐能力门控

serve 只读模式,财务数据仅来自 local 同步,
不再依赖 TickFlow Expert 能力。

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-07-06 16:04:47 +08:00
parent f408362c91
commit 7a96d51f8a
+6 -38
View File
@@ -2,8 +2,7 @@ import { useState, useEffect } from 'react'
import { RefreshCw, Lock, Loader2, X, Search, FileText, Database, CheckCircle2, Hourglass, Lightbulb, ExternalLink } from 'lucide-react' import { RefreshCw, Lock, Loader2, X, Search, FileText, Database, CheckCircle2, Hourglass, Lightbulb, ExternalLink } from 'lucide-react'
import { PageHeader } from '@/components/PageHeader' import { PageHeader } from '@/components/PageHeader'
import { EmptyState } from '@/components/EmptyState' import { EmptyState } from '@/components/EmptyState'
import { useCapabilities } from '@/lib/useSharedQueries' import { useFinancialStatus } from '@/lib/useFinancials'
import { useFinancialStatus, useFinancialSync } from '@/lib/useFinancials'
import { StockFinancialSearch } from '@/components/financials/StockFinancialSearch' import { StockFinancialSearch } from '@/components/financials/StockFinancialSearch'
import { StockFinancialDetail } from '@/components/financials/StockFinancialDetail' import { StockFinancialDetail } from '@/components/financials/StockFinancialDetail'
import { ReportHistoryPanel } from '@/components/financials/ReportHistoryPanel' import { ReportHistoryPanel } from '@/components/financials/ReportHistoryPanel'
@@ -27,42 +26,19 @@ const TABLE_ICON: Record<string, typeof FileText> = {
} }
export function Financials() { export function Financials() {
const { data: caps } = useCapabilities()
const hasFinancial = caps?.capabilities?.['financial'] != null
const { data: status, isLoading } = useFinancialStatus() const { data: status, isLoading } = useFinancialStatus()
const syncMut = useFinancialSync()
// 同步进行中 = 服务端真值(status.syncing)或本地乐观态(请求已发出待确认)。
// 乐观窗口:点击后到 invalidate 触发的 refetch 返回之间,status.syncing 暂为 false,
// 用 syncMut.isPending 覆盖,让按钮立即置灰、避免重复点击。
// 后端 trigger() 返回时 syncing 已为 true,refetch 到达后 status.syncing 接管。
const syncing = (status?.syncing ?? false) || syncMut.isPending
// 本次同步开始时间戳(ms): 用于判断每张表的 last_sync 是否属于本次同步
// (后端每张表完成即更新 last_sync, 前端轮询时对比时间戳得到精确进度)
const [syncStartedAt, setSyncStartedAt] = useState<number | null>(null)
// 单表同步时记录表名 (null = 全量同步), 用于区分卡片状态
const [syncSingleTable, setSyncSingleTable] = useState<string | null>(null)
// 同步自然结束(服务端 syncing 由 true→false):清空本次同步记录。
// 这是可靠的收尾时机 —— 不依赖 mutation 的 onSettled(它现在瞬间触发,会误清)。
useEffect(() => {
if (!syncing && syncStartedAt !== null) {
setSyncStartedAt(null)
setSyncSingleTable(null)
}
}, [syncing, syncStartedAt])
// 选中的个股(模糊搜索结果);null 时显示搜索引导
const [selected, setSelected] = useState<{ symbol: string; name: string } | null>(null)
const { last: lastStock, remember: rememberStock } = useLastStock('financials') const { last: lastStock, remember: rememberStock } = useLastStock('financials')
const [selected, setSelected] = useState<{ symbol: string; name: string } | null>(null)
const pick = (symbol: string, name: string) => { const pick = (symbol: string, name: string) => {
setSelected({ symbol, name }) setSelected({ symbol, name })
rememberStock(symbol, name) rememberStock(symbol, name)
} }
// 无 Expert 能力时: 等 status 加载完, 判断是否有从 local 同步来的数据 // 等 status 加载完, 判断是否有从 local 同步来的数据
if (!hasFinancial) {
if (isLoading) { if (isLoading) {
return ( return (
<> <>
<PageHeader title="财务分析" subtitle="利润表 / 资负表 / 现金流 / 关键指标 / AI分析 · Expert" /> <PageHeader title="财务分析" subtitle="利润表 / 资负表 / 现金流 / 关键指标 / AI分析" />
<div className="flex items-center justify-center py-16"> <div className="flex items-center justify-center py-16">
<Loader2 className="h-5 w-5 animate-spin text-muted" /> <Loader2 className="h-5 w-5 animate-spin text-muted" />
</div> </div>
@@ -70,20 +46,12 @@ export function Financials() {
) )
} }
if (!status?.available) { if (!status?.available) {
// 既无 Expert 能力, 也无同步数据 → 锁定页 // 无同步数据
return ( return (
<> <>
<PageHeader title="财务分析" subtitle="利润表 / 资负表 / 现金流 / 关键指标 / AI分析 · Expert" /> <PageHeader title="财务分析" subtitle="利润表 / 资负表 / 现金流 / 关键指标 / AI分析" />
<div className="px-8 py-10"> <div className="px-8 py-10">
<div className="mx-auto max-w-md rounded-card border border-warning/30 bg-warning/[0.04] p-8 text-center"> <div className="mx-auto max-w-md rounded-card border border-warning/30 bg-warning/[0.04] p-8 text-center">
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-warning/10">
<Lock className="h-6 w-6 text-warning" />
</div>
<h3 className="mt-4 text-base font-semibold text-foreground"> Expert </h3>
<p className="mt-2 text-xs leading-relaxed text-secondary">
Expert
</p>
<div className="mt-5 rounded-btn border border-accent/25 bg-accent/[0.05] px-3.5 py-3 text-left">
<div className="flex items-center gap-1.5 text-xs font-medium text-accent"> <div className="flex items-center gap-1.5 text-xs font-medium text-accent">
<Lightbulb className="h-3.5 w-3.5 shrink-0" /> <Lightbulb className="h-3.5 w-3.5 shrink-0" />