@@ -8,17 +8,14 @@ import { AiReportBubble } from '@/components/financials/AiReportBubble'
|
|||||||
import { StockAnalysisHost } from '@/components/stock-analysis/StockAnalysisHost'
|
import { StockAnalysisHost } from '@/components/stock-analysis/StockAnalysisHost'
|
||||||
import { StockAnalysisBubble } from '@/components/stock-analysis/StockAnalysisBubble'
|
import { StockAnalysisBubble } from '@/components/stock-analysis/StockAnalysisBubble'
|
||||||
import {
|
import {
|
||||||
useCapabilities,
|
|
||||||
useSettings,
|
useSettings,
|
||||||
usePreferences,
|
usePreferences,
|
||||||
useVersion,
|
useVersion,
|
||||||
} from '@/lib/useSharedQueries'
|
} from '@/lib/useSharedQueries'
|
||||||
import { QK } from '@/lib/queryKeys'
|
import { QK } from '@/lib/queryKeys'
|
||||||
import {
|
import {
|
||||||
ScanSearch,
|
|
||||||
FileText,
|
FileText,
|
||||||
Settings,
|
Settings,
|
||||||
Key,
|
|
||||||
LayoutDashboard,
|
LayoutDashboard,
|
||||||
Tags,
|
Tags,
|
||||||
TrendingUp,
|
TrendingUp,
|
||||||
@@ -38,7 +35,6 @@ const BRAND = '#8B5CF6'
|
|||||||
|
|
||||||
const nav = [
|
const nav = [
|
||||||
{ to: '/', label: '看板', icon: LayoutDashboard },
|
{ to: '/', label: '看板', icon: LayoutDashboard },
|
||||||
{ to: '/screener', label: '策略', icon: ScanSearch },
|
|
||||||
{ to: '/stock-analysis', label: '个股分析', icon: TrendingUp },
|
{ to: '/stock-analysis', label: '个股分析', icon: TrendingUp },
|
||||||
{ to: '/limit-ladder', label: '连板梯队', icon: Flame },
|
{ to: '/limit-ladder', label: '连板梯队', icon: Flame },
|
||||||
{ to: '/concept-analysis', label: '概念分析', icon: Layers3 },
|
{ to: '/concept-analysis', label: '概念分析', icon: Layers3 },
|
||||||
@@ -48,91 +44,6 @@ const nav = [
|
|||||||
{ to: '/indices', label: '指数', icon: BarChart3 },
|
{ to: '/indices', label: '指数', icon: BarChart3 },
|
||||||
] as const
|
] as const
|
||||||
|
|
||||||
// ===== 档位卡片 =====
|
|
||||||
function TierBadge({ label, hasKey }: { label: string; hasKey?: boolean }) {
|
|
||||||
const base = label.split(' ')[0].split('+')[0].toLowerCase()
|
|
||||||
const isNone = base === 'none'
|
|
||||||
|
|
||||||
const tierConfig: Record<string, {
|
|
||||||
desc: string
|
|
||||||
tagBg: React.CSSProperties
|
|
||||||
dotStyle: React.CSSProperties
|
|
||||||
labelTextStyle: React.CSSProperties
|
|
||||||
}> = {
|
|
||||||
none: {
|
|
||||||
desc: '未配置 Key · 仅历史日K',
|
|
||||||
tagBg: { background: 'rgba(113,113,122,0.15)' },
|
|
||||||
dotStyle: { background: '#52525b' },
|
|
||||||
labelTextStyle: { color: '#71717a' },
|
|
||||||
},
|
|
||||||
free: {
|
|
||||||
desc: '基础日K · 自选实时',
|
|
||||||
tagBg: { background: 'rgba(113,113,122,0.3)' },
|
|
||||||
dotStyle: { background: '#71717a' },
|
|
||||||
labelTextStyle: { color: '#a1a1aa' },
|
|
||||||
},
|
|
||||||
starter: {
|
|
||||||
desc: '批量同步 · 行情池',
|
|
||||||
tagBg: { background: 'rgba(59,130,246,0.2)' },
|
|
||||||
dotStyle: { background: '#3b82f6' },
|
|
||||||
labelTextStyle: { color: '#60a5fa' },
|
|
||||||
},
|
|
||||||
pro: {
|
|
||||||
desc: '分钟K · 实时行情 · 盘口',
|
|
||||||
tagBg: { background: 'linear-gradient(135deg, rgba(168,85,247,0.2), rgba(124,58,237,0.15))' },
|
|
||||||
dotStyle: { background: 'linear-gradient(135deg, #a855f7, #7c3aed)' },
|
|
||||||
labelTextStyle: { background: 'linear-gradient(135deg, #c084fc, #a855f7)', WebkitBackgroundClip: 'text', backgroundClip: 'text', color: 'transparent' },
|
|
||||||
},
|
|
||||||
expert: {
|
|
||||||
desc: 'WebSocket · 财务数据',
|
|
||||||
tagBg: { background: 'linear-gradient(135deg, rgba(59,130,246,0.2), rgba(168,85,247,0.2), rgba(245,158,11,0.2))' },
|
|
||||||
dotStyle: { background: 'linear-gradient(135deg, #3b82f6, #a855f7, #f59e0b)' },
|
|
||||||
labelTextStyle: { background: 'linear-gradient(135deg, #60a5fa, #c084fc, #fbbf24)', WebkitBackgroundClip: 'text', backgroundClip: 'text', color: 'transparent' },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const t = tierConfig[base] || tierConfig.none
|
|
||||||
// none 档显示英文「None」,无 label 时也显示「None」
|
|
||||||
const displayLabel = isNone ? 'None' : (label || 'None')
|
|
||||||
|
|
||||||
return (
|
|
||||||
<NavLink
|
|
||||||
to="/settings?tab=account"
|
|
||||||
className="mt-2.5 group block -mx-2.5"
|
|
||||||
title="API 设置"
|
|
||||||
>
|
|
||||||
<div className="relative overflow-hidden rounded-lg border border-blue-400/20 bg-gradient-to-br from-blue-500/[0.12] via-surface to-surface px-3 py-2 transition-all hover:border-blue-400/35 hover:from-blue-500/[0.16]">
|
|
||||||
<div className="absolute -right-5 -top-6 h-14 w-14 rounded-full bg-blue-500/10 blur-2xl" />
|
|
||||||
<div className="relative flex items-center gap-2">
|
|
||||||
<div className="flex h-6 w-6 items-center justify-center rounded-md bg-blue-400/10 text-blue-300 ring-1 ring-blue-400/20">
|
|
||||||
<Key className="h-3.5 w-3.5" />
|
|
||||||
</div>
|
|
||||||
<div className="min-w-0 flex-1">
|
|
||||||
<div className="flex items-center gap-1.5">
|
|
||||||
<span className="text-xs font-medium text-foreground">TickFlow</span>
|
|
||||||
<span
|
|
||||||
className="h-1.5 w-1.5 rounded-full"
|
|
||||||
style={{ ...t.dotStyle, ...(base === 'expert' ? { animation: 'pulse 2s infinite' } : {}) }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="mt-0.5 truncate text-[10px] leading-tight text-muted">
|
|
||||||
{isNone && !hasKey ? '配置 Key 解锁更多能力' : t.desc}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<span
|
|
||||||
className="inline-flex h-[18px] max-w-[68px] shrink-0 items-center overflow-hidden rounded px-1.5 text-[10px] font-bold font-mono leading-none"
|
|
||||||
style={t.tagBg}
|
|
||||||
>
|
|
||||||
<span className="truncate" style={t.labelTextStyle}>{displayLabel}</span>
|
|
||||||
</span>
|
|
||||||
<Settings className="h-3 w-3 shrink-0 text-muted group-hover:text-blue-300 transition-colors" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</NavLink>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function AIConfigBadge({ configured, model }: { configured?: boolean; model?: string }) {
|
function AIConfigBadge({ configured, model }: { configured?: boolean; model?: string }) {
|
||||||
return (
|
return (
|
||||||
<NavLink
|
<NavLink
|
||||||
@@ -164,7 +75,6 @@ function AIConfigBadge({ configured, model }: { configured?: boolean; model?: st
|
|||||||
|
|
||||||
export function Layout() {
|
export function Layout() {
|
||||||
// ===== 共享 hooks (替代内联 useQuery) =====
|
// ===== 共享 hooks (替代内联 useQuery) =====
|
||||||
const { data: caps } = useCapabilities()
|
|
||||||
const { data: settingsState } = useSettings()
|
const { data: settingsState } = useSettings()
|
||||||
const { data: versionData } = useVersion()
|
const { data: versionData } = useVersion()
|
||||||
const { data: prefs } = usePreferences()
|
const { data: prefs } = usePreferences()
|
||||||
@@ -226,10 +136,6 @@ export function Layout() {
|
|||||||
style={{ background: `linear-gradient(90deg, ${BRAND}88, transparent 80%)` }}
|
style={{ background: `linear-gradient(90deg, ${BRAND}88, transparent 80%)` }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TierBadge
|
|
||||||
label={caps?.label ?? ''}
|
|
||||||
hasKey={settingsState?.mode !== 'none'}
|
|
||||||
/>
|
|
||||||
<AIConfigBadge
|
<AIConfigBadge
|
||||||
configured={settingsState?.ai_configured ?? settingsState?.has_ai_key}
|
configured={settingsState?.ai_configured ?? settingsState?.has_ai_key}
|
||||||
model={settingsState?.ai_model}
|
model={settingsState?.ai_model}
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ interface NavEntry {
|
|||||||
|
|
||||||
const BUILTIN_PAGES: NavEntry[] = [
|
const BUILTIN_PAGES: NavEntry[] = [
|
||||||
{ id: '/', label: '看板', type: 'builtin', visible: true },
|
{ id: '/', label: '看板', type: 'builtin', visible: true },
|
||||||
{ id: '/screener', label: '策略', type: 'builtin', visible: true },
|
|
||||||
{ id: '/limit-ladder', label: '连板梯队', type: 'builtin', visible: true },
|
{ id: '/limit-ladder', label: '连板梯队', type: 'builtin', visible: true },
|
||||||
{ id: '/concept-analysis', label: '概念分析', type: 'builtin', visible: true },
|
{ id: '/concept-analysis', label: '概念分析', type: 'builtin', visible: true },
|
||||||
{ id: '/industry-analysis', label: '行业分析', type: 'builtin', visible: true },
|
{ id: '/industry-analysis', label: '行业分析', type: 'builtin', visible: true },
|
||||||
|
|||||||
Reference in New Issue
Block a user