# 选股执行状态抽屉与紧凑布局技术设计 ## 1. Boundaries 本任务的产品边界在 `zhixing-web`: - `SelectionResultsPage` 继续拥有执行状态、当前结果和局部抽屉开关状态。 - `features/selection/components` 新增执行状态抽屉与未完成评估表格,组件只消费 `SelectionFailure` 等 feature 类型。 - `PageLayout` 负责收紧跨页面的内容节奏;选股页面负责工作台内部的密度和滚动边界。 - `shared/ui/dialog.tsx` 作为底层弹层 primitive 复用,不新增后端或全局状态。 不改变 API adapter、query key、TanStack Query 状态和路由 search 参数。 ## 2. Component Shape 建议的组件关系: ```text SelectionResultsPage ├─ PageLayout │ ├─ ExecutionToolbar │ ├─ ResultMetrics(onOpenExecutionStatus) │ └─ ResultState │ └─ SelectionResultsWorkbench └─ ExecutionStatusDrawer ├─ status summary └─ IncompleteEvaluationTable ``` `FailureList` 从 `ResultState` 移除。`ResultMetrics` 的执行状态入口通过回调通知页面,不把 Dialog 状态放进 Zustand,也不把失败列表复制到本地 state。 抽屉使用 `Dialog` 的受控 `open/onOpenChange`,通过 feature 局部 class 将 popup 定位到右侧、设置 `width: min(600px, 100vw)`、`height: 100svh` 和紧凑边框圆角。这样保留 Base UI 的焦点陷阱、Escape 和遮罩语义,同时避免为一次性业务交互新增浅层 shared primitive。 ## 3. Data Flow ```text SelectionResults.failures │ ├─ ResultMetrics: failure count/status trigger └─ ExecutionStatusDrawer: read-only props └─ IncompleteEvaluationTable: rows ``` 抽屉不请求数据、不轮询、不修改 URL。抽屉展示的每一行直接对应 `SelectionFailure`,状态和原因只做前端文案格式化;缺失原因显示 `—`。 ## 4. Layout and Density ### Desktop - `AppLayout → main → PageLayout → ResultState → workbench` 保持连续的 `flex: 1`、`min-height: 0` 高度链。 - 操作区、指标区、筛选栏、分页均使用 `shrink-0`。 - 主从工作台使用剩余空间;表格 `overflow-auto`,分页留在面板底部。 - 页面 gutter 由当前 24px 收紧为 16px 级别;内部 panel/filter/detail 使用 8–16px 级别的间距。 ### Mobile - 保留日期、策略、执行按钮的语义和触控尺寸,不为了压缩而降低到桌面 32px。 - 内容容器采用紧凑 gutter 和较小 section gap;页面恢复自然滚动。 - 抽屉宽度随视口收缩,不产生横向溢出;表格必要时只在抽屉内部横向处理长文本,优先让原因换行。 ## 5. Compatibility and Rollback - 旧结果结构仍可直接渲染;只有失败列表的呈现位置改变。 - 若 Drawer 样式影响 Base UI Dialog,可回退为同一受控 Dialog 的居中呈现,不影响数据流和页面其他交互。 - 若高度链修复造成无结果/错误状态被压缩,保留这些状态的最小可读高度,并只让成功结果工作台承担剩余高度。 ## 6. Verification Strategy - 页面测试:抽屉打开、表格内容、空表状态、关闭和焦点入口;确认主页面不再直接渲染失败卡片。 - 质量门禁:Prettier、ESLint、TypeScript、Vitest、Vite build。 - 浏览器:使用可控 mock 结果验证 1440×900、1280×800、390×844、375×812;检查抽屉宽度、分页底部位置、数据区滚动和无水平溢出。