重置项目

This commit is contained in:
2026-07-04 15:59:20 +08:00
parent 374e587f2d
commit 648a8b7f1c
224 changed files with 19700 additions and 9547 deletions
+125 -47
View File
@@ -21,6 +21,10 @@ import {
Radar,
ShieldCheck,
BellRing,
TrendingUp,
FileText,
Landmark,
Database,
} from 'lucide-react'
import { api } from '@/lib/api'
import { useCapabilities, useSettings } from '@/lib/useSharedQueries'
@@ -36,12 +40,15 @@ const STEPS = ['欢迎', '配置 Key', '能力探测', '完成'] as const
const BRAND = '#8B5CF6'
const HIGHLIGHTS = [
{ icon: LineChart, title: '看板与自选', desc: '实时行情、MA/MACD 指标、自定义自选列表', tint: 'text-accent' },
{ icon: ScanSearch, title: '策略选股', desc: '内置多套选股策略,一键扫描全市场命中', tint: 'text-bull' },
{ icon: Flame, title: '连板梯队', desc: '涨停板梯队、概念行业热度、市场情绪一览', tint: 'text-warning' },
{ icon: Radar, title: '实时监控', desc: '自定义条件 / 策略监控,触发即推送告警', tint: 'text-bear' },
{ icon: ShieldCheck, title: '回测验证', desc: '策略历史回测、因子分析,用数据说话', tint: 'text-accent' },
{ icon: BellRing, title: '本地优先', desc: '数据本地存储,隐私可控,断网仍可查阅', tint: 'text-bull' },
{ icon: LineChart, title: '看板与自选', desc: '市场全景看板、涨跌分布、情绪雷达,自定义自选列表', tint: 'text-accent' },
{ icon: ScanSearch, title: '策略选股', desc: '内置多套选股策略,一键扫描全市场命中标的', tint: 'text-bull' },
{ icon: TrendingUp, title: '个股分析', desc: 'AI 四维分析个股,关键价位、技术形态一目了然', tint: 'text-warning' },
{ icon: Flame, title: '连板梯队', desc: '涨停梯队、封板强度、炸板监控,情绪温度计', tint: 'text-warning' },
{ icon: Landmark, title: '概念行业', desc: '概念板块、行业维度的资金流向与热度排名', tint: 'text-accent' },
{ icon: FileText, title: '财务分析', desc: 'AI 解读财报,利润、资负、现金流、核心指标', tint: 'text-bear' },
{ icon: ShieldCheck, title: '回测验证', desc: '策略历史回测、因子分析,用数据验证逻辑', tint: 'text-accent' },
{ icon: Radar, title: '实时监控', desc: '自定义条件 / 策略监控,盘中触发即推送告警', tint: 'text-bear' },
{ icon: BellRing, title: '本地优先', desc: '数据本地存储,隐私可控,断网仍可查阅', tint: 'text-bull' },
]
export function Onboarding() {
@@ -101,7 +108,7 @@ export function Onboarding() {
className="shrink-0"
style={{ color: BRAND, filter: `drop-shadow(0 0 8px ${BRAND}55)` }}
/>
<span className="text-sm font-semibold tracking-tight">Stock Panel</span>
<span className="text-sm font-semibold tracking-tight">TickFlow Stock Panel</span>
</div>
{/* 步骤进度条 —— 胶囊式 */}
<div className="flex items-center gap-1.5">
@@ -172,27 +179,31 @@ function WelcomeStep({ onNext, onSkip }: { onNext: () => void; onSkip: () => voi
</motion.div>
<h1 className="mt-6 text-3xl font-bold text-foreground tracking-tight">
使 Stock Panel
使 TickFlow Stock Panel
</h1>
<p className="mt-3 text-sm text-secondary leading-relaxed max-w-md mx-auto">
A
,使
</p>
{/* 6 个特性卡片 */}
<div className="mt-8 grid grid-cols-2 sm:grid-cols-3 gap-3 text-left">
{/* 特性卡片 —— 3×3 网格,横向布局压缩高度 */}
<div className="mt-8 grid grid-cols-2 sm:grid-cols-3 gap-2.5 text-left">
{HIGHLIGHTS.map((h, i) => (
<motion.div
key={h.title}
initial={{ opacity: 0, y: 10 }}
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3, delay: 0.05 * i + 0.1 }}
transition={{ duration: 0.3, delay: 0.04 * i + 0.1 }}
whileHover={{ y: -2 }}
className="group rounded-card border border-border bg-surface/80 backdrop-blur-sm p-3.5 transition-colors hover:border-accent/30"
className="group flex items-start gap-2.5 rounded-card border border-border bg-surface/80 backdrop-blur-sm p-2.5 transition-colors hover:border-accent/30"
>
<h.icon className={`h-5 w-5 ${h.tint} transition-transform group-hover:scale-110`} />
<div className="mt-2 text-sm font-medium text-foreground">{h.title}</div>
<div className="mt-1 text-xs text-muted leading-relaxed">{h.desc}</div>
<div className="rounded-lg bg-elevated/50 p-1.5 shrink-0">
<h.icon className={`h-4 w-4 ${h.tint} transition-transform group-hover:scale-110`} />
</div>
<div className="min-w-0">
<div className="text-xs font-medium text-foreground">{h.title}</div>
<div className="mt-0.5 text-[11px] text-muted leading-snug line-clamp-2">{h.desc}</div>
</div>
</motion.div>
))}
</div>
@@ -216,7 +227,7 @@ function WelcomeStep({ onNext, onSkip }: { onNext: () => void; onSkip: () => voi
)
}
// ===== Step 1: 输入数据源 Key =====
// ===== Step 1: 输入 TickFlow Key =====
function KeyStep({ onNext, onSkip, onBack }: { onNext: () => void; onSkip: () => void; onBack: () => void }) {
const qc = useQueryClient()
@@ -240,7 +251,7 @@ function KeyStep({ onNext, onSkip, onBack }: { onNext: () => void; onSkip: () =>
},
})
// 已配置 key —— 免费档或付费档都算(只要不是无档 none)
// 已配置 key —— 免费档或付费档都算(只要不是 None)
const alreadyHasKey = settings.data?.mode !== 'none' && settings.data?.mode !== undefined
return (
@@ -249,26 +260,47 @@ function KeyStep({ onNext, onSkip, onBack }: { onNext: () => void; onSkip: () =>
<div className="rounded-lg bg-accent/10 p-2">
<ShieldCheck className="h-4 w-4 text-accent" />
</div>
<h2 className="text-xl font-bold text-foreground"> API Key</h2>
<h2 className="text-xl font-bold text-foreground"> TickFlow API Key</h2>
</div>
<p className="mt-2.5 text-sm text-secondary leading-relaxed">
Key 使 Key <span className="font-medium text-foreground"> </span>
使K; Key
TickFlow ,
,{' '}
<a
href="mailto:415333856@qq.com"
className="text-accent hover:underline font-medium"
>
415333856@qq.com
</a>
</p>
{/* 注册引导 */}
<div className="mt-5 rounded-card border border-border bg-surface/80 backdrop-blur-sm p-4 text-xs text-secondary leading-relaxed">
Key?{' '}
<a
href="https://tickflow.org/auth/register"
target="_blank"
rel="noreferrer"
className="text-accent hover:underline inline-flex items-baseline gap-0.5 font-medium"
>
<ExternalLink className="h-3 w-3 self-center" />
</a>{' '}
,使
{/* 档位对比说明 —— None 档 vs Free 档 */}
<div className="mt-4 grid grid-cols-1 sm:grid-cols-2 gap-2.5">
{/* None 档 —— 不配置时默认 */}
<div className="rounded-card border border-accent/20 bg-accent/[0.04] p-3">
<div className="flex items-center gap-1.5">
<span className="inline-flex h-[18px] items-center rounded px-1.5 text-[10px] font-bold font-mono bg-accent/15 text-accent/70">None</span>
<span className="text-xs font-medium text-foreground">()</span>
</div>
<ul className="mt-2 space-y-1 text-[11px] text-muted leading-relaxed">
<li>· K数据,</li>
<li>· , 1-2 </li>
<li>· </li>
</ul>
</div>
{/* Free 档 —— 免费注册即可获取 */}
<div className="rounded-card border border-accent/35 bg-accent/[0.08] p-3">
<div className="flex items-center gap-1.5">
<span className="inline-flex h-[18px] items-center rounded px-1.5 text-[10px] font-bold font-mono bg-accent/15 text-accent">Free</span>
<span className="text-xs font-medium text-foreground"></span>
<span className="inline-flex items-center rounded-full bg-accent px-1.5 py-0.5 text-[10px] font-bold text-white shadow-sm shadow-accent/30"></span>
</div>
<ul className="mt-2 space-y-1 text-[11px] text-secondary leading-relaxed">
<li>· ,</li>
<li>· K + </li>
<li>· </li>
</ul>
</div>
</div>
{/* Key 已配置提示 */}
@@ -282,6 +314,27 @@ function KeyStep({ onNext, onSkip, onBack }: { onNext: () => void; onSkip: () =>
</div>
)}
{/* 获取 Key 的说明 —— 黄框卡片 */}
<div className="mt-4 flex items-start gap-2 rounded-card border border-warning/40 bg-warning/10 px-3 py-2.5 text-xs text-foreground leading-relaxed">
<AlertCircle className="h-4 w-4 shrink-0 text-warning mt-px" />
<span>
Key {' '}
<a
href="https://tickflow.org/auth/register?ref=V3KDKGXPEA"
target="_blank"
rel="noreferrer"
className="text-warning hover:underline inline-flex items-baseline gap-0.5 font-medium"
>
tickflow.org
<ExternalLink className="h-3 w-3 self-center" />
</a>
<span className="block mt-1.5 text-foreground/70">
TickFlow ,
</span>
</span>
</div>
{/* 输入 */}
<form
onSubmit={(e) => {
@@ -293,7 +346,7 @@ function KeyStep({ onNext, onSkip, onBack }: { onNext: () => void; onSkip: () =>
<div className="relative">
<input
type={revealing ? 'text' : 'password'}
placeholder={alreadyHasKey ? '粘贴新 Key 替换当前' : '粘贴数据源 API Key'}
placeholder={alreadyHasKey ? '粘贴新 Key 替换当前' : '粘贴 TickFlow API Key'}
value={keyInput}
onChange={(e) => {
setKeyInput(e.target.value)
@@ -379,7 +432,7 @@ function ResultStep({ onNext, onBack }: { onNext: () => void; onBack: () => void
const settings = useSettings()
const caps = useCapabilities()
// 是否配置成功 —— 免费档(free)或付费档(api_key)都算;无档(none)算未配置
// 是否配置成功 —— 免费档(free)或付费档(api_key)都算;None算未配置
const hasKey = settings.data?.mode === 'free' || settings.data?.mode === 'api_key'
const capList = caps.data ? Object.entries(caps.data.capabilities) : []
@@ -438,10 +491,10 @@ function ResultStep({ onNext, onBack }: { onNext: () => void; onBack: () => void
<div className="mx-auto w-fit rounded-xl bg-elevated p-3">
<Zap className="h-6 w-6 text-warning" />
</div>
<div className="mt-3 text-sm font-medium text-foreground"></div>
<div className="mt-3 text-sm font-medium text-foreground"> None </div>
<p className="mt-2 text-xs text-muted leading-relaxed max-w-sm mx-auto">
Key,使K数据 Key ,
<span className="text-foreground font-medium"> </span>
Key,使 1 K数据 Key ,
<span className="text-foreground font-medium"> </span>
</p>
</div>
)}
@@ -470,10 +523,16 @@ function ResultStep({ onNext, onBack }: { onNext: () => void; onBack: () => void
// ===== Step 3: 完成 =====
function FinishStep({ onNext, onBack, pending }: { onNext: () => void; onBack: () => void; pending: boolean }) {
const settings = useSettings()
// 是否已配置 Key(free 或 api_key 都算,None 档算未配置)
const hasKey = settings.data?.mode === 'free' || settings.data?.mode === 'api_key'
// 首要行动:获取数据(不管配没配 Key, 新用户都需要先拉数据)
// 快速上手入口(精简为核心功能)
const tips = [
{ icon: ScanSearch, text: '在「选股」页用内置策略一键扫描全市场' },
{ icon: BellRing, text: '在「监控」页设置条件或策略告警,盘中实时推送' },
{ icon: ShieldCheck, text: '「回测」页用历史数据验证策略表现' },
{ icon: TrendingUp, text: '「个股分析」:输入代码,AI 四维分析 + 关键价位' },
{ icon: ScanSearch, text: '「选股」页:内置多套策略,一键扫描全市场' },
{ icon: ShieldCheck, text: '「回测」页:用历史数据验证策略表现,用数据说话' },
]
return (
@@ -500,18 +559,37 @@ function FinishStep({ onNext, onBack, pending }: { onNext: () => void; onBack: (
<h1 className="mt-6 text-2xl font-bold text-foreground">!</h1>
<p className="mt-2.5 text-sm text-secondary leading-relaxed max-w-md mx-auto">
,
<span className="text-foreground font-medium"> </span>
{hasKey
? 'Key 已生效,进入面板后系统会自动引导你获取行情数据,完成后即可使用全部功能。'
: '当前为 None 档,进入面板后系统会自动引导你获取历史日K数据(无需 Key),即可开始体验。'}
</p>
{/* 快速上手提示 */}
<div className="mt-6 space-y-2 text-left">
{/* 首要行动:获取数据 */}
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3, delay: 0.2 }}
className="mt-5 flex items-start gap-2.5 rounded-card border border-accent/30 bg-accent/[0.06] px-4 py-3 text-left"
>
<div className="rounded-lg bg-accent/15 p-1.5 shrink-0 mt-px">
<Database className="h-4 w-4 text-accent" />
</div>
<div className="min-w-0">
<div className="text-sm font-medium text-foreground">下一步:获取行情数据</div>
<p className="mt-1 text-xs text-secondary leading-relaxed">
, 1 A K( 5500 , 1-3 )
</p>
</div>
</motion.div>
{/* 快速上手入口 */}
<div className="mt-4 space-y-2 text-left">
{tips.map((t, i) => (
<motion.div
key={i}
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.3, delay: 0.1 * i + 0.2 }}
transition={{ duration: 0.3, delay: 0.1 * i + 0.3 }}
className="flex items-center gap-3 rounded-card border border-border bg-surface/80 backdrop-blur-sm px-3.5 py-2.5"
>
<div className="rounded-lg bg-accent/10 p-1.5 shrink-0">