@@ -8,13 +8,12 @@ import {
|
||||
RefreshCw,
|
||||
Repeat,
|
||||
Search,
|
||||
Settings2,
|
||||
TrendingDown,
|
||||
TrendingUp,
|
||||
} from 'lucide-react'
|
||||
import { PageHeader } from '@/components/PageHeader'
|
||||
import { EmptyState } from '@/components/EmptyState'
|
||||
import { AnalysisConfigDialog, type AnalysisFieldConfig } from '@/components/analysis-shared'
|
||||
import { type AnalysisFieldConfig } from '@/components/analysis-shared'
|
||||
import { StockPreviewDialog } from '@/components/StockPreviewDialog'
|
||||
import { RpsRotationDialog } from '@/components/RpsRotationDialog'
|
||||
import { api, type MarketSnapshotRow } from '@/lib/api'
|
||||
@@ -68,9 +67,7 @@ function loadConfig(): AnalysisFieldConfig {
|
||||
return storage.conceptAnalysisConfig.get({}) as AnalysisFieldConfig
|
||||
}
|
||||
|
||||
function saveConfig(c: AnalysisFieldConfig) {
|
||||
storage.conceptAnalysisConfig.set(c)
|
||||
}
|
||||
|
||||
|
||||
function pickBestConfig(
|
||||
configs: { id: string; label: string; description?: string; fields: { name: string; label: string }[] }[],
|
||||
@@ -234,8 +231,7 @@ function statSort(mode: SortMode) {
|
||||
}
|
||||
|
||||
export function ConceptAnalysis() {
|
||||
const [fieldConfig, setFieldConfig] = useState<AnalysisFieldConfig>(loadConfig)
|
||||
const [showConfig, setShowConfig] = useState(false)
|
||||
const [fieldConfig] = useState<AnalysisFieldConfig>(loadConfig)
|
||||
const [search, setSearch] = useState('')
|
||||
const [selectedKey, setSelectedKey] = useState<string | null>(null)
|
||||
const [sortMode, setSortMode] = useState<SortMode>('heat')
|
||||
@@ -301,12 +297,6 @@ export function ConceptAnalysis() {
|
||||
return set.size
|
||||
}, [stats])
|
||||
|
||||
const handleSaveConfig = (c: AnalysisFieldConfig) => {
|
||||
setFieldConfig(c)
|
||||
saveConfig(c)
|
||||
setSelectedKey(null)
|
||||
}
|
||||
|
||||
if (configsQuery.isLoading) {
|
||||
return <div className="flex h-full items-center justify-center"><RefreshCw className="h-5 w-5 animate-spin text-muted" /></div>
|
||||
}
|
||||
@@ -316,19 +306,9 @@ export function ConceptAnalysis() {
|
||||
return (
|
||||
<>
|
||||
<div className="flex h-full flex-col">
|
||||
<PageHeader
|
||||
title="概念分析"
|
||||
right={
|
||||
<button onClick={() => setShowConfig(true)} className="p-1.5 text-muted hover:bg-surface hover:text-accent" title="配置数据源">
|
||||
<Settings2 className="h-4 w-4" />
|
||||
</button>
|
||||
}
|
||||
/>
|
||||
<PageHeader title="概念分析" />
|
||||
<EmptyState icon={Layers3} title="暂无概念数据" hint="请通过 local→serve 同步概念数据" />
|
||||
</div>
|
||||
<AnimatePresence>
|
||||
{showConfig && <AnalysisConfigDialog currentConfig={fieldConfig} onSave={handleSaveConfig} onClose={() => setShowConfig(false)} />}
|
||||
</AnimatePresence>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -348,9 +328,6 @@ export function ConceptAnalysis() {
|
||||
>
|
||||
<Repeat className="h-3.5 w-3.5" />涨幅RPS轮动分析
|
||||
</button>
|
||||
<button onClick={() => setShowConfig(true)} className="p-1.5 text-muted hover:bg-surface hover:text-accent" title="配置数据源">
|
||||
<Settings2 className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
@@ -388,9 +365,7 @@ export function ConceptAnalysis() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AnimatePresence>
|
||||
{showConfig && <AnalysisConfigDialog currentConfig={fieldConfig} onSave={handleSaveConfig} onClose={() => setShowConfig(false)} />}
|
||||
</AnimatePresence>
|
||||
|
||||
|
||||
{previewSymbol && (
|
||||
<StockPreviewDialog
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
import { useMemo, useState, type ReactNode } from 'react'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { AnimatePresence } from 'framer-motion'
|
||||
import {
|
||||
Activity,
|
||||
Crown,
|
||||
Layers3,
|
||||
RefreshCw,
|
||||
Search,
|
||||
Settings2,
|
||||
TrendingDown,
|
||||
TrendingUp,
|
||||
} from 'lucide-react'
|
||||
import { PageHeader } from '@/components/PageHeader'
|
||||
import { EmptyState } from '@/components/EmptyState'
|
||||
import { AnalysisConfigDialog, DimensionHeatmap, type AnalysisFieldConfig } from '@/components/analysis-shared'
|
||||
import { DimensionHeatmap, type AnalysisFieldConfig } from '@/components/analysis-shared'
|
||||
import { StockPreviewDialog } from '@/components/StockPreviewDialog'
|
||||
import { api, type MarketSnapshotRow } from '@/lib/api'
|
||||
import { QK } from '@/lib/queryKeys'
|
||||
@@ -68,9 +66,7 @@ interface IndustryStat {
|
||||
function loadConfig(): AnalysisFieldConfig {
|
||||
return storage.industryAnalysisConfig.get({}) as AnalysisFieldConfig
|
||||
}
|
||||
function saveConfig(c: AnalysisFieldConfig) {
|
||||
storage.industryAnalysisConfig.set(c)
|
||||
}
|
||||
|
||||
|
||||
// ===== 自动选取最佳数据源 =====
|
||||
|
||||
@@ -267,8 +263,7 @@ function groupByIndustryLevel(groups: DimensionGroup[], level: IndustryLevel): D
|
||||
// ===== 主页面 =====
|
||||
|
||||
export function IndustryAnalysis() {
|
||||
const [fieldConfig, setFieldConfig] = useState<AnalysisFieldConfig>(loadConfig)
|
||||
const [showConfig, setShowConfig] = useState(false)
|
||||
const [fieldConfig] = useState<AnalysisFieldConfig>(loadConfig)
|
||||
const [search, setSearch] = useState('')
|
||||
const [selectedKey, setSelectedKey] = useState<string | null>(null)
|
||||
const [sortMode, setSortMode] = useState<SortMode>('heat')
|
||||
@@ -349,12 +344,6 @@ export function IndustryAnalysis() {
|
||||
return map
|
||||
}, [marketMap])
|
||||
|
||||
const handleSaveConfig = (c: AnalysisFieldConfig) => {
|
||||
setFieldConfig(c)
|
||||
saveConfig(c)
|
||||
setSelectedKey(null)
|
||||
}
|
||||
|
||||
if (configsQuery.isLoading) {
|
||||
return <div className="flex h-full items-center justify-center"><RefreshCw className="h-5 w-5 animate-spin text-muted" /></div>
|
||||
}
|
||||
@@ -364,19 +353,10 @@ export function IndustryAnalysis() {
|
||||
return (
|
||||
<>
|
||||
<div className="flex h-full flex-col">
|
||||
<PageHeader
|
||||
title="行业分析"
|
||||
right={
|
||||
<button onClick={() => setShowConfig(true)} className="p-1.5 text-muted hover:bg-surface hover:text-accent" title="配置数据源">
|
||||
<Settings2 className="h-4 w-4" />
|
||||
</button>
|
||||
}
|
||||
/>
|
||||
<PageHeader title="行业分析" />
|
||||
<EmptyState icon={Layers3} title="暂无行业数据" hint="请通过 local→serve 同步行业数据" />
|
||||
</div>
|
||||
<AnimatePresence>
|
||||
{showConfig && <AnalysisConfigDialog currentConfig={fieldConfig} onSave={handleSaveConfig} onClose={() => setShowConfig(false)} showHierarchyLevel />}
|
||||
</AnimatePresence>
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -388,13 +368,6 @@ export function IndustryAnalysis() {
|
||||
<PageHeader
|
||||
title="行业分析"
|
||||
subtitle={`${industryLevelLabel} · ${marketQuery.data?.as_of ?? rowsQuery.data?.date ?? '最新'} · ${stats.length} 个行业 · ${totalSymbols} 只标的`}
|
||||
right={
|
||||
<div className="flex items-center gap-1">
|
||||
<button onClick={() => setShowConfig(true)} className="p-1.5 text-muted hover:bg-surface hover:text-accent" title="配置数据源">
|
||||
<Settings2 className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="min-h-full bg-[radial-gradient(circle_at_12%_0%,rgba(245,158,11,0.12),transparent_28%),radial-gradient(circle_at_85%_8%,rgba(244,63,94,0.08),transparent_28%)] px-6 py-5">
|
||||
@@ -441,9 +414,7 @@ export function IndustryAnalysis() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AnimatePresence>
|
||||
{showConfig && <AnalysisConfigDialog currentConfig={fieldConfig} onSave={handleSaveConfig} onClose={() => setShowConfig(false)} showHierarchyLevel />}
|
||||
</AnimatePresence>
|
||||
|
||||
|
||||
{previewSymbol && (
|
||||
<StockPreviewDialog
|
||||
|
||||
Reference in New Issue
Block a user