diff --git a/.trellis/tasks/archive/2026-09/09-01-sector-radar-dual-ranking/check.jsonl b/.trellis/tasks/archive/2026-09/09-01-sector-radar-dual-ranking/check.jsonl new file mode 100644 index 0000000..36f18a4 --- /dev/null +++ b/.trellis/tasks/archive/2026-09/09-01-sector-radar-dual-ranking/check.jsonl @@ -0,0 +1,5 @@ +{"file": ".trellis/spec/frontend/index.md", "reason": "核对 feature 边界、URL 状态和质量门禁。"} +{"file": ".trellis/spec/frontend/component-guidelines.md", "reason": "核对 Tab 语义、表格列对齐、滚动、状态提示和可访问性。"} +{"file": ".trellis/spec/frontend/hook-guidelines.md", "reason": "核对双榜 query key、取消信号、分页停止条件和错误状态归属。"} +{"file": ".trellis/spec/frontend/quality-guidelines.md", "reason": "核对测试覆盖以及 format、lint、typecheck、test、build 结果。"} +{"file": "docs/research/onechartlab-sector-capital-radar.md", "reason": "确认未把原站未知的评分、涨跌幅或在榜天数伪造成已实现字段。"} diff --git a/.trellis/tasks/archive/2026-09/09-01-sector-radar-dual-ranking/design.md b/.trellis/tasks/archive/2026-09/09-01-sector-radar-dual-ranking/design.md new file mode 100644 index 0000000..4c981e8 --- /dev/null +++ b/.trellis/tasks/archive/2026-09/09-01-sector-radar-dual-ranking/design.md @@ -0,0 +1,39 @@ +# 板块资金雷达双榜交互技术设计 + +## 范围与边界 + +本轮只修改 `zhixing-web` 的板块资金雷达页面、相关 query 组合和测试,不改变 FastAPI 参数、响应模型、数据库表或指标构建逻辑。后端现有 `GET /api/v1/sector-radar/rankings` 已支持 `side=top` 与 `side=bottom`,前端用两条独立无限查询组合双榜;两侧仍共享交易日、板块类型、指标视角、排名变化参数、搜索词和 `page_size`。 + +路由继续兼容既有 `side` 查询参数,但双榜页面不再用它决定可见榜单,也不提供“榜单范围”控件。旧链接无论携带 `side=all/top/bottom` 都进入同一双榜视图,避免为本轮视觉迭代扩大路由类型迁移范围。 + +## Tab 与工具栏 + +板块类型和指标视角改为 feature 内的语义化按钮 Tab,使用 `role="tablist"`、`role="tab"` 与 `aria-selected`,沿用现有 `updateSearch` 写回 URL,并在切换时重置 `page=1`。不新增共享 Tabs primitive或第三方依赖;交易日、排名变化指标和对比区间继续使用现有 Select,搜索继续使用 Input。 + +指标 Tab 顺序按目标截图组织为波段资金率、单日资金率、单日净额、排名变化。排名变化激活时,附属的“变化指标”和“对比区间”控件紧凑显示,不改变主 Tab 层级。 + +## 双查询与滚动数据流 + +页面从同一基础筛选分别构造 `side="top"` 和 `side="bottom"` 的 `RadarRankingsQuery`,调用两次现有 `useSectorRadarRankings`。现有 query key 已包含 `side` 且排除 `page`,因此两侧缓存和分页相互独立,无需修改后端契约。 + +每侧分别展平 `InfiniteData.pages`,按数组索引把强榜第 N 行和弱榜第 N 行配成一个视觉行。任一侧行数不足时对应单元格留空,另一侧继续正常展示。唯一滚动容器接近底部时,只为仍有下一页、未请求中且未处于加载更多错误的侧调用 `fetchNextPage`;请求级互斥覆盖同一滚动周期,避免连续事件重复取数。两侧都没有下一页时停止。 + +加载更多失败按侧记录并保留已加载行;提示区说明强榜或弱榜哪一侧失败,并只重试失败侧。首次请求、后台刷新、刷新失败、部分发布、无发布和搜索无匹配继续保持明确分支,不用一侧的下一页错误覆盖另一侧现有数据。 + +## 镜像表格结构 + +使用一张语义化 `` 和显式列宽布局承载左右镜像双榜。第一层表头分为“资金进攻榜 TOP 10%”、当前指标和“BOTTOM 10% 资金撤离榜”;第二层列从左到右为: + +`排名 / 板块 | 排名百分位 | 样本 | 资金覆盖率 | 质量 | 流入 | 流出 | 质量 | 资金覆盖率 | 样本 | 排名百分位 | 排名 / 板块` + +右侧文本和列标题采用与阅读方向匹配的对齐方式,中间两列增加稳定分隔线。表头继续 sticky,表格保留足够 `min-width`,窄视口通过单一横向滚动容器查看,不压缩成多行或错位结构。 + +普通资金指标使用 `metric_value`:`CNY_100M` 以亿元格式化,`ratio` 以百分比格式化;强榜显示显式流入方向,弱榜显示显式流出方向,并使用现有主题中的红/绿语义和低饱和背景条增强对比。排名变化视角中间列改为“排名上升 / 排名下降”,值使用 `rank_change`,参考指标不伪装成独立流入/流出字段。 + +## 延期字段 + +涨跌幅、加权评分和在榜天数明确延期。当前 ranking row 只有单一 `metric_value`、排名、百分位、样本、覆盖率、质量与 1—5 日 `rank_change`;虽然数据源局部出现涨跌幅,聚合和排名表并未持久化该字段,加权评分公式也未公开,在榜天数没有历史轨迹契约。后续讨论必须先定义三项字段的业务语义、point-in-time 输入、公式、历史深度、异常处理与迁移兼容,不能在本轮 UI 中用现有字段代替或推算。 + +## 兼容、验证与回滚 + +本轮不改 HTTP 与数据库,可回滚为原单查询表格而不影响数据。验证包括 query 双实例参数、Tab 的 URL 更新、双榜配对、两侧独立续页、一侧结束或失败时另一侧继续、表头/行列数一致、正负格式化、可访问性、完整前端质量门禁和真实浏览器桌面布局检查。 diff --git a/.trellis/tasks/archive/2026-09/09-01-sector-radar-dual-ranking/implement.jsonl b/.trellis/tasks/archive/2026-09/09-01-sector-radar-dual-ranking/implement.jsonl new file mode 100644 index 0000000..8957900 --- /dev/null +++ b/.trellis/tasks/archive/2026-09/09-01-sector-radar-dual-ranking/implement.jsonl @@ -0,0 +1,5 @@ +{"file": ".trellis/spec/frontend/index.md", "reason": "确认本轮属于 sector-radar feature,遵守前端分层、URL 状态与质量门禁。"} +{"file": ".trellis/spec/frontend/component-guidelines.md", "reason": "Tab、镜像表格、状态提示和滚动容器需遵守现有组件、Tailwind 与可访问性约定。"} +{"file": ".trellis/spec/frontend/hook-guidelines.md", "reason": "双榜继续由 TanStack Query 管理两侧独立无限查询、缓存和取消信号。"} +{"file": ".trellis/spec/frontend/quality-guidelines.md", "reason": "实现需补齐用户可见行为测试并通过完整前端质量命令。"} +{"file": "docs/research/onechartlab-sector-capital-radar.md", "reason": "提供原站四种指标、TOP/BOTTOM 分层、公开字段和未知评分公式的证据边界。"} diff --git a/.trellis/tasks/archive/2026-09/09-01-sector-radar-dual-ranking/implement.md b/.trellis/tasks/archive/2026-09/09-01-sector-radar-dual-ranking/implement.md new file mode 100644 index 0000000..d57706d --- /dev/null +++ b/.trellis/tasks/archive/2026-09/09-01-sector-radar-dual-ranking/implement.md @@ -0,0 +1,33 @@ +# 板块资金雷达双榜交互执行计划 + +## 执行清单 + +- [ ] 更新页面筛选区,将板块类型和指标视角改为语义化 Tab;移除榜单范围 Select,保留交易日、搜索和排名变化附属控制,并验证 URL 状态重置。 +- [ ] 从共享筛选构造强榜与弱榜两条无限查询,分别展平分页结果,组合双侧加载、刷新、错误和重试状态。 +- [ ] 将单侧 `RadarTable` 重构为左右镜像双榜表格,建立稳定列宽、双层 sticky 表头、中间流入/流出(排名变化时为排名升降)以及双侧空位规则。 +- [ ] 为净额、比例和排名变化实现方向明确的格式化与低饱和条形表达,保留质量、覆盖率和样本等知行已有字段,不实现延期字段。 +- [ ] 更新页面行为测试,覆盖 Tab、双查询参数、行配对、双侧分页、一侧提前结束、一侧加载失败重试、格式化语义以及旧分页/榜单范围控件消失。 +- [ ] 运行相关 Vitest、`pnpm format:check`、`pnpm lint`、`pnpm typecheck`、`pnpm test`、`pnpm build` 和 `git diff --check`,随后在真实浏览器中检查桌面布局、滚动加载和控制台。 + +## 重点文件 + +- `zhixing-web/src/features/sector-radar/pages/sector-radar-page.tsx` +- `zhixing-web/src/features/sector-radar/pages/sector-radar-page.test.tsx` +- 如双侧组合需要抽取 query helper,再最小修改 `zhixing-web/src/features/sector-radar/api/sector-radar.query.ts` 及其测试;不修改后端、迁移和共享 Pagination。 + +## 验证命令 + +```bash +cd zhixing-web +pnpm exec vitest run src/features/sector-radar/api/sector-radar.query.test.ts src/features/sector-radar/pages/sector-radar-page.test.tsx +pnpm check +pnpm build +cd .. +git diff --check +``` + +## 回滚点与风险 + +双榜由两条独立 HTTP 请求组成,不保证同一数据库事务快照;两条请求使用同一交易日与查询条件,且排名发布不可变时结果一致。若后续服务允许发布过程中变更,需要再引入 publication id 固定或后端双榜原子响应,本轮不扩大契约。 + +镜像表格列数多,桌面窄视口不可避免横向滚动;验收重点是列宽与表头稳定、文本不换行和单一滚动容器,而不是压缩所有列到任意宽度。 diff --git a/.trellis/tasks/archive/2026-09/09-01-sector-radar-dual-ranking/prd.md b/.trellis/tasks/archive/2026-09/09-01-sector-radar-dual-ranking/prd.md new file mode 100644 index 0000000..380e85a --- /dev/null +++ b/.trellis/tasks/archive/2026-09/09-01-sector-radar-dual-ranking/prd.md @@ -0,0 +1,47 @@ +# 板块资金雷达双榜交互对齐 + +## Goal + +把知行项目的板块资金雷达从“筛选器 + 单边普通表格”进一步对齐 OneChartLab 原站的核心浏览方式,让用户在同一屏内通过 Tab 切换板块类型和排序指标,并横向对照强榜与弱榜的流入、流出表现。 + +用户价值是减少筛选与翻页操作,在一个连续滚动榜单中直接比较资金进入端和撤离端,同时保持现有知行独立指标及数据质量边界。 + +## Background + +- 当前页面已经支持服务端分页上的无限滚动,但仍以 Select 选择板块类型、指标视角和榜单范围,并只展示单侧榜单。 +- 用户提供的目标截图显示:搜索框后使用“概念 / 行业”Tab,右侧使用指标 Tab;主体表格同时呈现左侧强榜和右侧弱榜,中间用“流入 / 流出”两列形成视觉对照。 +- 本轮不使用 `lark-design-prototype`,以用户截图、现有项目视觉体系和实际数据契约为准。 + +## Requirements + +- 将“概念板块 / 行业板块”从 Select 改为可直接点击的 Tab,并与 URL 状态、数据查询及刷新行为保持同步。 +- 将指标视角改为 Tab 交互,至少覆盖现有的波段资金率、单日资金率、单日净额和排名变化;排名变化所需的指标与对比区间仍需有紧凑的附属控制。 +- 强榜与弱榜必须在同一个连续滚动列表中成对展示,不再要求用户通过“榜单范围”筛选器切换。 +- 表头结构、左右方向和列对齐参照目标截图:左侧为资金进攻榜,右侧为资金撤离榜,中间明确区分流入和流出;强榜从左向中间阅读,弱榜从中间向右阅读。 +- 双榜外围只使用知行已有且可验证的排名百分位、样本、资金覆盖率和质量字段;左右按镜像顺序排列,并以固定列宽或等价布局保证表头和行数据对齐。 +- 流入和流出数值使用方向明确、可比较的正负与色彩表达,并保持文本语义和可访问性,不只依赖颜色区分。 +- 波段资金率、单日资金率和单日净额视角的中间列使用“流入 / 流出”;排名变化视角使用与排名升降一致的表头和数值语义,不把排名变化伪装成资金流量。 +- 保留表头吸附、连续滚动加载、首次加载、后台刷新、部分发布、加载更多失败重试、空数据和致命错误等现有行为。 +- 不新增与目标交互无关的大块说明卡、指标墙或辅助面板。 + +## Acceptance Criteria + +- [ ] 用户可通过“概念 / 行业”Tab 切换板块类型,激活状态清晰且 URL 查询状态同步更新。 +- [ ] 用户可通过指标 Tab 切换波段资金率、单日资金率、单日净额和排名变化,当前指标与中间流入/流出表头一致。 +- [ ] 每个可见数据行同时展示一个强榜板块和一个弱榜板块,左右榜单分别保持各自排名顺序。 +- [ ] 表头和数据列在桌面视口下稳定对齐,左右镜像列均展示排名/板块、排名百分位、样本、资金覆盖率和质量,中间流入/流出区域与两侧之间有明确边界,横向滚动时结构不塌陷。 +- [ ] 中间指标值按当前视角正确格式化:净额以亿元显示,比例以百分比显示,排名变化以升降名次数显示;强弱方向有显式正负号或文字语义。 +- [ ] 滚动接近底部时强榜与弱榜继续加载下一批,任一侧提前结束时另一侧仍可继续展示,直到两侧都加载完成。 +- [ ] 加载更多失败时已加载的两侧数据不丢失,并提供可操作的重试入口。 +- [ ] 不再显示“榜单范围”Select、底部分页器或大块纯描述卡。 +- [ ] 相关前后端契约测试、前端行为测试、格式检查、lint、类型检查、完整测试和构建通过,并完成真实浏览器交互与布局检查。 + +## Out of Scope + +- 复刻原站未在知行数据契约中存在的股票勾选、加权评分或自选功能。 +- 更改板块指标公式、排序定义、数据构建任务或历史数据口径。 +- 引入与本轮交互无关的新页面模块。 + +## Deferred Items + +- 原站外围的“涨跌幅、加权评分、在榜天数”本轮不实现。当前知行排名持久化与 HTTP row 均没有这三个字段,其中加权评分的公开证据不足以确认公式;后续需分别讨论指标语义、输入时点、计算公式、持久化和历史兼容后再立项。 diff --git a/.trellis/tasks/archive/2026-09/09-01-sector-radar-dual-ranking/task.json b/.trellis/tasks/archive/2026-09/09-01-sector-radar-dual-ranking/task.json new file mode 100644 index 0000000..884e147 --- /dev/null +++ b/.trellis/tasks/archive/2026-09/09-01-sector-radar-dual-ranking/task.json @@ -0,0 +1,26 @@ +{ + "id": "sector-radar-dual-ranking", + "name": "sector-radar-dual-ranking", + "title": "板块资金雷达双榜交互对齐", + "description": "", + "status": "completed", + "dev_type": null, + "scope": null, + "package": null, + "priority": "P2", + "creator": "yuxuanhui", + "assignee": "yuxuanhui", + "createdAt": "2026-09-01", + "completedAt": "2026-09-01", + "branch": null, + "base_branch": "main", + "worktree_path": null, + "commit": null, + "pr_url": null, + "subtasks": [], + "children": [], + "parent": null, + "relatedFiles": [], + "notes": "", + "meta": {} +} \ No newline at end of file