重置项目
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
import { useMemo, useState, type ReactNode } from 'react'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { AnimatePresence } from 'framer-motion'
|
||||
import {
|
||||
Activity,
|
||||
Crown,
|
||||
Database,
|
||||
Layers3,
|
||||
RefreshCw,
|
||||
Repeat,
|
||||
Search,
|
||||
Settings2,
|
||||
TrendingDown,
|
||||
@@ -14,8 +14,9 @@ import {
|
||||
} from 'lucide-react'
|
||||
import { PageHeader } from '@/components/PageHeader'
|
||||
import { EmptyState } from '@/components/EmptyState'
|
||||
import { AnalysisConfigDialog, type AnalysisFieldConfig } from '@/components/analysis-shared'
|
||||
import { AnalysisConfigDialog, PresetFetchState, type AnalysisFieldConfig } from '@/components/analysis-shared'
|
||||
import { StockPreviewDialog } from '@/components/StockPreviewDialog'
|
||||
import { RpsRotationDialog } from '@/components/RpsRotationDialog'
|
||||
import { api, type MarketSnapshotRow } from '@/lib/api'
|
||||
import { QK } from '@/lib/queryKeys'
|
||||
import { storage } from '@/lib/storage'
|
||||
@@ -240,6 +241,7 @@ export function ConceptAnalysis() {
|
||||
const [sortMode, setSortMode] = useState<SortMode>('heat')
|
||||
const [previewSymbol, setPreviewSymbol] = useState<string | null>(null)
|
||||
const [previewName, setPreviewName] = useState<string>('')
|
||||
const [showRps, setShowRps] = useState(false)
|
||||
|
||||
const configsQuery = useQuery({ queryKey: QK.extData, queryFn: api.extDataList })
|
||||
const availableConfigs = configsQuery.data?.items ?? []
|
||||
@@ -256,6 +258,21 @@ export function ConceptAnalysis() {
|
||||
enabled: !!activeConfigId,
|
||||
})
|
||||
|
||||
// 内置概念预设 (ext_gn_ths) 手动获取数据
|
||||
const PRESET_CONCEPT_ID = 'ext_gn_ths'
|
||||
const queryClient = useQueryClient()
|
||||
const fetchMutation = useMutation({
|
||||
mutationFn: () => api.extDataPresetFetch(PRESET_CONCEPT_ID),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: QK.extData })
|
||||
queryClient.invalidateQueries({ queryKey: QK.extDataRows(PRESET_CONCEPT_ID, undefined, PAGE_LIMIT) })
|
||||
},
|
||||
})
|
||||
// 是否处于「内置概念预设存在但无数据」状态 → 显示获取按钮
|
||||
const needsConceptFetch =
|
||||
!!activeConfig && activeConfig.id === PRESET_CONCEPT_ID &&
|
||||
!rowsQuery.isLoading && (rowsQuery.data?.total ?? 0) === 0
|
||||
|
||||
const marketQuery = useQuery({
|
||||
queryKey: QK.marketSnapshot,
|
||||
queryFn: api.marketSnapshot,
|
||||
@@ -310,18 +327,30 @@ export function ConceptAnalysis() {
|
||||
}
|
||||
|
||||
if (!activeConfig) {
|
||||
// 极端情况: 无任何概念配置。仍提供一键获取内置概念数据入口
|
||||
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>
|
||||
}
|
||||
/>
|
||||
<EmptyState icon={Database} title="暂无概念数据" hint={'请先在"数据"页面创建包含概念/题材字段的扩展数据源'} />
|
||||
</div>
|
||||
<>
|
||||
<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>
|
||||
}
|
||||
/>
|
||||
<PresetFetchState
|
||||
title="暂无概念数据"
|
||||
hint="从同花顺获取概念分类数据后即可使用概念分析"
|
||||
isLoading={fetchMutation.isPending}
|
||||
error={fetchMutation.error}
|
||||
onFetch={() => fetchMutation.mutate()}
|
||||
/>
|
||||
</div>
|
||||
<AnimatePresence>
|
||||
{showConfig && <AnalysisConfigDialog currentConfig={fieldConfig} onSave={handleSaveConfig} onClose={() => setShowConfig(false)} />}
|
||||
</AnimatePresence>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -332,6 +361,14 @@ export function ConceptAnalysis() {
|
||||
subtitle={`${marketQuery.data?.as_of ?? rowsQuery.data?.date ?? '最新'} · ${stats.length} 个概念 · ${totalSymbols} 只标的`}
|
||||
right={
|
||||
<div className="flex items-center gap-1">
|
||||
{/* RPS 轮动: 打开涨幅轮动矩阵对话框 */}
|
||||
<button
|
||||
onClick={() => setShowRps(true)}
|
||||
className="inline-flex items-center gap-1 rounded-btn border border-amber-400/40 bg-amber-400/15 px-2.5 py-1.5 text-[11px] text-amber-400 font-medium transition-colors hover:bg-amber-400/25 hover:border-amber-400/60"
|
||||
title="概念涨幅轮动矩阵"
|
||||
>
|
||||
<Repeat className="h-3.5 w-3.5" />涨幅RPS轮动分析
|
||||
</button>
|
||||
<button
|
||||
onClick={() => { rowsQuery.refetch(); marketQuery.refetch() }}
|
||||
disabled={rowsQuery.isFetching || marketQuery.isFetching}
|
||||
@@ -374,6 +411,14 @@ export function ConceptAnalysis() {
|
||||
</div>
|
||||
) : rowsQuery.isLoading ? (
|
||||
<div className="rounded-2xl border border-border bg-surface px-6 py-16 text-center text-sm text-muted">正在计算概念强度...</div>
|
||||
) : needsConceptFetch ? (
|
||||
<PresetFetchState
|
||||
title="未获取概念数据"
|
||||
hint="内置概念数据源已就绪,点击下方按钮从同花顺获取概念分类数据"
|
||||
isLoading={fetchMutation.isPending}
|
||||
error={fetchMutation.error}
|
||||
onFetch={() => fetchMutation.mutate()}
|
||||
/>
|
||||
) : (
|
||||
<EmptyState icon={Layers3} title="未匹配到概念数据" hint={resolved.hint || '请检查扩展数据是否包含概念/题材相关字段'} />
|
||||
)}
|
||||
@@ -391,6 +436,10 @@ export function ConceptAnalysis() {
|
||||
onClose={() => { setPreviewSymbol(null); setPreviewName('') }}
|
||||
/>
|
||||
)}
|
||||
|
||||
<AnimatePresence>
|
||||
{showRps && <RpsRotationDialog onClose={() => setShowRps(false)} />}
|
||||
</AnimatePresence>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user