feat(regime): 阶段×主线表格限高滚动 + 吸顶表头

段数多时(2y 范围 50+ 段)表格把页面拉得很长。改为 max-h-420px 内滚动,
表头 sticky 吸顶保证滚动时列名可见。

实现注意:
- sticky 必须加在 th 而非 tr (后者在 Chromium 不生效)
- 表格改 border-separate border-spacing-0 — Chromium 在 preflight 默认的
  border-collapse:collapse 下表格元素 sticky 完全失效 (computed 为 sticky
  但不粘, crbug.com/702927); separate 模式下 tr 边框不绘制, 行分隔线
  相应从 tr 挪到 td, 视觉不变

验证: tsc 通过; 实测容器 clientH=420/scrollH=1860 (页面少滚 ~1440px),
容器内滚动 1441px 后 th 与容器顶 delta=0 (吸顶生效), 截图确认列名行
固定、数据行正常。截图 gui-test-screenshots/regime-segments-*.png
This commit is contained in:
shy3130
2026-08-17 13:03:19 +08:00
parent 72c56963dd
commit 41e99b6974
3 changed files with 23 additions and 20 deletions
+23 -20
View File
@@ -648,42 +648,45 @@ export function Regime() {
<div className={cn(cardCls, 'p-3')}>
<SectionTitle icon={Layers} title="阶段 × 主线"
hint={`${segments.length} 段 · 主线按段内 top5 天数排序`} />
<div className="mt-2 overflow-x-auto">
<table className="w-full min-w-[760px] text-left text-[11px]">
{/* 最大高度内滚动: 段数多时不再拉长页面; 表头吸顶保证滚动时列名可见。
border-separate 是 sticky 前提 — Chromium 在 border-collapse:collapse
(preflight 默认) 下表格元素 sticky 失效; spacing-0 保持视觉不变。 */}
<div className="mt-2 max-h-[420px] overflow-auto">
<table className="w-full min-w-[760px] border-separate border-spacing-0 text-left text-[11px]">
<thead>
<tr className="border-b border-border text-[10px] text-muted">
<th className="py-1.5 pr-3 font-medium"></th>
<th className="py-1.5 pr-3 font-medium"></th>
<th className="py-1.5 pr-3 font-medium text-right"></th>
<th className="py-1.5 pr-3 font-medium text-right"></th>
<th className="py-1.5 pr-3 font-medium text-right">2+</th>
<th className="py-1.5 pr-3 font-medium text-right"></th>
<th className="py-1.5 pr-3 font-medium text-right"></th>
<th className="py-1.5 font-medium">线</th>
<tr className="text-[10px] text-muted">
<th className="sticky top-0 z-10 border-b border-border bg-surface py-1.5 pr-3 font-medium"></th>
<th className="sticky top-0 z-10 border-b border-border bg-surface py-1.5 pr-3 font-medium"></th>
<th className="sticky top-0 z-10 border-b border-border bg-surface py-1.5 pr-3 font-medium text-right"></th>
<th className="sticky top-0 z-10 border-b border-border bg-surface py-1.5 pr-3 font-medium text-right"></th>
<th className="sticky top-0 z-10 border-b border-border bg-surface py-1.5 pr-3 font-medium text-right">2+</th>
<th className="sticky top-0 z-10 border-b border-border bg-surface py-1.5 pr-3 font-medium text-right"></th>
<th className="sticky top-0 z-10 border-b border-border bg-surface py-1.5 pr-3 font-medium text-right"></th>
<th className="sticky top-0 z-10 border-b border-border bg-surface py-1.5 font-medium">线</th>
</tr>
</thead>
<tbody>
{[...segments].reverse().map((seg, i) => (
<tr key={`${seg.start}-${seg.phase}-${i}`} className="border-b border-border/50 last:border-0">
<td className="py-1.5 pr-3">
<tr key={`${seg.start}-${seg.phase}-${i}`}>
<td className="border-b border-border/50 py-1.5 pr-3">
<span className="rounded px-1.5 py-px text-[10px] font-semibold"
style={{ color: MARKET_PHASE_COLORS[seg.phase], backgroundColor: MARKET_PHASE_COLORS[seg.phase] + '20' }}>
{seg.label}
</span>
</td>
<td className="py-1.5 pr-3 font-mono text-[10px] text-secondary">
<td className="border-b border-border/50 py-1.5 pr-3 font-mono text-[10px] text-secondary">
{seg.start.slice(5)} ~ {seg.end.slice(5)}
</td>
<td className="py-1.5 pr-3 text-right font-mono">{seg.days}</td>
<td className="py-1.5 pr-3 text-right font-mono">{seg.avg_height}</td>
<td className="py-1.5 pr-3 text-right font-mono">{seg.avg_ge2}</td>
<td className="py-1.5 pr-3 text-right font-mono">
<td className="border-b border-border/50 py-1.5 pr-3 text-right font-mono">{seg.days}</td>
<td className="border-b border-border/50 py-1.5 pr-3 text-right font-mono">{seg.avg_height}</td>
<td className="border-b border-border/50 py-1.5 pr-3 text-right font-mono">{seg.avg_ge2}</td>
<td className="border-b border-border/50 py-1.5 pr-3 text-right font-mono">
{seg.avg_promo != null ? `${(seg.avg_promo * 100).toFixed(0)}%` : '—'}
</td>
<td className="py-1.5 pr-3 text-right font-mono">
<td className="border-b border-border/50 py-1.5 pr-3 text-right font-mono">
{seg.avg_seal_rate != null ? `${(seg.avg_seal_rate * 100).toFixed(0)}%` : '—'}
</td>
<td className="py-1.5">
<td className="border-b border-border/50 py-1.5">
<div className="flex flex-wrap gap-1">
{seg.top_mainlines.length > 0 ? seg.top_mainlines.map(m => (
<span key={m.member} className="rounded px-1.5 py-px text-[9px]"
Binary file not shown.

After

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB