From 6b49231d67a656de65037c5a776b2443939d09f4 Mon Sep 17 00:00:00 2001 From: shy3130 <415333856@qq.com> Date: Sun, 30 Aug 2026 19:05:46 +0800 Subject: [PATCH] =?UTF-8?q?feat(onboarding):=20=E7=AC=AC3=E6=AD=A5?= =?UTF-8?q?=E7=94=B1=E3=80=8C=E8=83=BD=E5=8A=9B=E6=8E=A2=E6=B5=8B=E3=80=8D?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E3=80=8C=E8=83=BD=E5=8A=9B=E8=B7=AF=E7=94=B1?= =?UTF-8?q?=E6=A3=80=E6=B5=8B=E3=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 以能力路由矩阵呈现(7 数据集逐行: 生效源+可用性+候选提示), 替代以 TickFlow 档位为中心的旧视图, 多源下矩阵即真相 - 不可用行针对性提示: 有候选给「可切换候选: X」; TickFlow 未配 Key 给「配置 Key 后可解锁」; 底部汇总 N/M 可用与 None 档说明 - 步骤名/注释同步改为能力路由; 移除不再使用的 TickFlow 档位视图代码 - 浏览器实测: 7 行矩阵渲染正确, 可用性/候选/汇总与当前环境一致 --- frontend/src/pages/Onboarding.tsx | 189 +++++++++++------------------- 1 file changed, 70 insertions(+), 119 deletions(-) diff --git a/frontend/src/pages/Onboarding.tsx b/frontend/src/pages/Onboarding.tsx index 3ae1d5f..25921fe 100644 --- a/frontend/src/pages/Onboarding.tsx +++ b/frontend/src/pages/Onboarding.tsx @@ -12,7 +12,6 @@ import { LineChart, ScanSearch, Flame, - Zap, Radar, ShieldCheck, BellRing, @@ -23,18 +22,18 @@ import { Plus, Puzzle, KeyRound, + Route, } from 'lucide-react' import { api } from '@/lib/api' -import { useCapabilities, usePreferences, useSettings } from '@/lib/useSharedQueries' +import { usePreferences, useSettings } from '@/lib/useSharedQueries' import { QK } from '@/lib/queryKeys' -import { CAP_LABELS } from '@/lib/capability-labels' import { Logo } from '@/components/Logo' import { DataSourceEditor } from '@/pages/settings/DataSourceEditor' // ===== 引导页:5 步向导 ===== -// 0. 声明 1. 欢迎 2. 输入 Key(可跳过) 3. 能力探测结果 4. 完成 → 写标记 → 进面板 +// 0. 声明 1. 欢迎 2. 数据源与 Key 3. 能力路由检测 4. 完成 → 写标记 → 进面板 -const STEPS = ['声明', '欢迎', '数据源', '能力探测', '完成'] as const +const STEPS = ['声明', '欢迎', '数据源', '能力路由', '完成'] as const const BRAND = '#8B5CF6' @@ -643,149 +642,96 @@ function DataSourceStep({ onNext, onBack }: { onNext: () => void; onBack: () => ) } -// ===== Step 3: 能力探测结果 ===== -// 按当前实际选中的数据源分流: -// - TickFlow: 提示第三方源性质 + 可配 Key、按 Key 匹配档位, 展示档位与能力探测 -// - 其他源: 弱化 TickFlow (不展示其档位/能力), 只汇总所选源的数据集覆盖与回落规则 +// ===== Step 3: 能力路由检测结果 ===== +// 以能力路由矩阵呈现: 每个标准化数据集一行, 展示当前生效源与可用性, +// 不再以 TickFlow 档位为中心 —— 多源下各数据集独立路由, 矩阵即真相。 function ResultStep({ onNext, onBack }: { onNext: () => void; onBack: () => void }) { const settings = useSettings() - const caps = useCapabilities() const prefs = usePreferences() - const sources = useQuery({ - queryKey: QK.dataSources, - queryFn: api.dataSources, - staleTime: 60_000, + const matrix = useQuery({ + queryKey: QK.capabilityMatrix, + queryFn: api.capabilityMatrix, + staleTime: 30_000, }) const activeName = prefs.data?.daily_data_provider || 'tickflow' const isTickflow = activeName === 'tickflow' - const sourceItem = [ - ...(sources.data?.builtin ?? []), - ...(sources.data?.plugins ?? []), - ...(sources.data?.custom ?? []), - ].find(s => s.name === activeName) - + const routes = matrix.data?.capabilities ?? [] // 是否配置成功 —— 免费档(free)或付费档(api_key)都算;None 档算未配置 const hasKey = settings.data?.mode === 'free' || settings.data?.mode === 'api_key' - const capList = caps.data ? Object.entries(caps.data.capabilities) : [] + const usableCount = routes.filter(r => r.usable).length return (
- +
-

能力探测结果

+

能力路由检测

+

+ 每个标准化数据集独立路由:下方是当前生效源与实际可用性,随时可在 + 设置 → 数据源 + 按数据集改选候选源。 +

- {prefs.isLoading ? ( + {matrix.isLoading ? (
- 正在读取当前数据源… + 正在检测能力路由…
- ) : isTickflow ? ( + ) : routes.length === 0 ? ( +
+ 暂未获取到能力路由矩阵,可稍后在 设置 → 数据源 中重新检测。 +
+ ) : ( <> - {/* TickFlow 源说明: 点明第三方性质 + Key/档位关系 */} -
- -
- 当前选择了 TickFlow 第三方数据源 - 。实时行情、监控等能力与订阅档位由 TickFlow Key 决定:可在 - 设置 → 账户 - 配置 Key,系统会根据 Key 自动匹配档位;未配置 Key 时按 None 档运行,仅保留内置历史数据能力。 -
-
- - {hasKey ? ( - <> -

- Key 已生效,以下是你当前可用的全部能力。后续可在 - 设置 → 账户 - 中重新检测或更换 Key。 -

- -
-
- 订阅档位 - - {caps.data?.label ?? settings.data?.tier_label ?? '—'} +
+ {routes.map(r => ( +
+
+ {r.usable ? ( + + ) : ( + + )} + {r.label} + + {r.usable ? '可用' : '不可用'}
- - {caps.isLoading ? ( -
- - 正在探测能力… +
+ 生效源:{r.effective_display} + · + {r.desc} +
+ {!r.usable && ( +
+ {r.candidates.length > 0 + ? `可切换候选:${r.candidates.map(c => c.display).join(' / ')}` + : isTickflow && !hasKey + ? '配置 TickFlow Key 后可解锁' + : '暂无可用候选,可在设置中排查该源'}
- ) : capList.length > 0 ? ( -
- {capList.slice(0, 8).map(([cap]) => { - const meta = CAP_LABELS[cap] - return ( -
- - {meta?.name ?? cap} -
- ) - })} - {capList.length > 8 && ( -
…等共 {capList.length} 项
- )} -
- ) : ( -
暂未探测到能力
)}
- - ) : ( -
-
- -
-
将以 None 档继续
-

- 当前未配置有效 Key,仍可使用看板、选股、回测等功能 —— 进入看板后可直接获取近 1 年历史日K数据。配置 Key 后可解锁实时行情监控等能力,随时在 - 设置 → 账户 填写。 -

-
- )} - - ) : ( - /* 其他数据源: 不展示 TickFlow 档位/能力探测, 汇总所选源的数据集覆盖 */ -
-
- 当前数据源 - - - {(sourceItem?.display_name ?? activeName).replace(/(.*?)|\(.*?\)/g, '').trim() || sourceItem?.display_name || activeName} - - - {sources.data?.custom?.some(s => s.name === activeName) ? '自有' : '第三方'} - + ))} +
+ +
+ + {usableCount}/{routes.length} 个数据集可用 + {isTickflow && !hasKey && ' · 未配置 TickFlow Key,按 None 档运行,历史日K不受影响'} +
- -
- {(sourceItem?.datasets?.length ?? 0) > 0 ? ( - sourceItem!.datasets.map(ds => ( - - {DATASET_LABELS[ds] ?? ds} - - )) - ) : ( - 未声明数据集 - )} -
-

- 行情能力由所选数据源决定:以上数据集由该源提供,未覆盖的数据集自动回落内置源,无需额外配置。 -

- -
- TickFlow 的 Key 与档位探测仅在选择 TickFlow 作为数据源时展示;如需切换,前往 - 设置 → 数据源 。 -
-
+ )} {/* 底部操作 */} @@ -809,6 +755,11 @@ function ResultStep({ onNext, onBack }: { onNext: () => void; onBack: () => void ) } +/** 指向设置数据源的行内提示 (能力路由可随时调整) */ +function LinkMark() { + return 调整: 设置 → 数据源 +} + // ===== Step 4: 完成 ===== function FinishStep({ onNext, onBack, pending }: { onNext: () => void; onBack: () => void; pending: boolean }) {