鲸鱼选股 (XuanGu) 前端页面布局规范
版本:1.2 | 更新日期:2026-10-09
设计参考:shadcn/ui · Ant Design 5.x · Tailwind CSS 4
一、设计原则
1.1 核心理念
| 原则 | 说明 |
|---|---|
| 一致性 | 相同类型的元素在所有页面保持统一的视觉表现和交互行为 |
| 层次感 | 通过间距、字号、色彩建立清晰的信息层级 |
| 呼吸感 | 适当留白,避免信息过载,提升可读性 |
| 响应式 | 宽屏优先:充分适配 1280px ~ 2560px+ 显示器;移动端(小于 768px)按第九节降级 |
| 可访问性 | 符合 WCAG 2.1 AA 标准,支持键盘导航与屏幕阅读器 |
1.2 设计系统架构
CSS Custom Properties (globals.css)
↓ 设计令牌(颜色、间距、圆角、阴影)
Tailwind Config (tailwind.config.js)
↓ 令牌映射为工具类
shadcn/ui Primitives (src/components/ui/)
↓ 基础组件(Button, Card, Table...)
Layout Primitives (src/components/layout/)
↓ 布局组件(TitleBar, Sidebar, RightPanel)
Page Compositions (src/pages/)
↓ 页面组合
二、设计令牌 (Design Tokens)
2.1 色彩系统
语义色(CSS Custom Properties)
| 令牌 | 用途 | 亮色模式 | 暗色模式 |
|---|---|---|---|
--background | 页面背景 | 220 20% 97% | 222 30% 9% |
--card | 卡片表面 | 0 0% 100% | 222 24% 13% |
--primary | 主色调(可动态切换) | 228 86% 63% | 228 78% 62% |
--muted-foreground | 次要文本 | 220 10% 46% | 220 15% 55% |
--border | 边框 | 220 13% 91% | 222 20% 18% |
--up | 涨(A 股红涨) | 354 78% 47% | 0 72% 55% |
--down | 跌(A 股绿跌) | 146 76% 33% | 142 70% 45% |
--success | 成功 | 142 72% 40% | 142 70% 45% |
--warning | 警告 | 38 92% 50% | 38 88% 55% |
--destructive | 危险/错误 | 0 72% 51% | 0 62% 50% |
色彩使用规则
| 场景 | 色彩 | 说明 |
|---|---|---|
| 主操作按钮 | bg-primary | 仅用于页面最重要的 1~2 个操作 |
| 次要操作 | bg-secondary 或 border | 取消、返回等 |
| 危险操作 | bg-destructive | 删除、清空等不可逆操作 |
| 成功状态 | text-success / bg-success/10 | 完成、通过 |
| 涨跌标识 | text-up / text-down | 严格遵循 A 股红涨绿跌 |
2.2 间距系统 (Spacing Scale)
参考 Ant Design 的 8px 栅格系统,结合 Tailwind 默认值:
| 语义名 | Tailwind | 像素值 | 用途 |
|---|---|---|---|
xs | gap-1 / space-y-1 | 4px | 紧凑元素内部间距(徽章内、图标与文字) |
sm | gap-2 / space-y-2 | 8px | 相关元素间距(表单项、按钮组) |
md | gap-3 / space-y-3 | 12px | 卡片内部区块间距 |
base | gap-4 / space-y-4 | 16px | 标准区块间距 |
lg | gap-5 / space-y-5 | 20px | 页面主要分区间距 |
xl | gap-6 / space-y-6 | 24px | 顶级模块间距 |
2xl | gap-8 / space-y-8 | 32px | 大区块分隔(首屏) |
统一规则:
- 页面内边距:
p-5(20px)标准页 /p-6(24px)详情页 - 区块间距:默认
space-y-5,卡片内space-y-3 - 表单元素间距:
gap-3(12px)水平 /space-y-4(16px)垂直
2.3 圆角系统 (Border Radius)
| 元素类型 | Tailwind | 像素值 | 说明 |
|---|---|---|---|
| 大容器/卡片 | rounded-2xl / rounded-lg | 16px | 页面级容器、Card 组件 |
| 控件/按钮/输入框 | rounded-control | 9px | 交互控件统一圆角 |
| 徽章/标签 | rounded-md | 6px | Badge、Tag |
| 小图标容器 | rounded-md | 6px | 24px 以下图标 |
| 头像/圆形按钮 | rounded-full | 50% | 用户头像、FAB |
规则:glass-card 统一使用 rounded-2xl,不再使用 rounded-xl。
2.4 阴影系统 (Shadows)
| 层级 | Tailwind | 用途 |
|---|---|---|
| 无阴影 | — | 扁平元素、内嵌区域 |
shadow-xs | 微阴影 | 分割线替代、轻微抬升 |
shadow-sm | 小阴影 | 卡片默认状态 |
shadow-md | 中阴影 | 卡片悬停、下拉菜单 |
shadow-lg | 大阴影 | 模态框、浮动面板 |
shadow-mac | macOS 风格 | 主要浮动层(Popover、Dialog) |
2.5 字体系统 (Typography)
| 层级 | Tailwind | 字号/行高 | 用途 |
|---|---|---|---|
| Display | text-2xl font-bold | 24px / 32px | 页面大标题(极少使用) |
| H1 页面标题 | text-base font-semibold tracking-tight | 16px / 24px | PageHeader 标题(紧凑化) |
| H2 区块标题 | text-lg font-semibold | 18px / 28px | Card 标题、区块标题 |
| H3 子标题 | text-base font-medium | 16px / 24px | 子区块标题 |
| Body 正文 | text-sm | 14px / 22px | 默认正文(Tailwind default) |
| Body 小正文 | text-2sm | 13px / 20px | 次要正文 |
| Caption 说明 | text-xs | 12px / 16px | 说明文字、时间戳 |
| Overline 标注 | text-[10px] uppercase tracking-wider | 10px | 表头、标签 |
数字字体:所有数字使用 font-variant-numeric: tabular-nums(.nums 类),确保对齐。
三、布局系统
3.1 页面分类
所有页面归为 5 类,每类规定标准结构:
| 类型 | 适用场景 | 标准外层结构 |
|---|---|---|
| A. 标准内容页 | 信息展示、列表、报告 | 见 3.2 |
| B. 工作台页 | 筛选 + 可滚动主体 | 见 3.3 |
| C. 详情页 | 单标的/单记录详情 | 见 3.4 |
| D. 双面板页 | 主内容 + 辅助面板 | 见 3.5 |
| E. 全屏沉浸页 | 图表密集、特殊交互 | 见 3.6 |
3.2 标准内容页 (Type A)
适用于:Backtest、NlPick、Notifications、GlobalMarket 等信息展示页。
<div className="h-full overflow-y-auto scrollbar-thin">
<div className="mx-auto w-[98%] max-w-[2200px] space-y-5 p-5">
<PageHeader icon={Icon} title="页面标题" description="说明" />
{/* 内容区块 */}
<Card className="rounded-2xl p-5">
{/* ... */}
</Card>
</div>
</div>
规格:
- 外层:
h-full overflow-y-auto scrollbar-thin - 内容区:
mx-auto w-[98%] max-w-[2200px] space-y-5 p-5 - 卡片:
rounded-2xl p-5
3.3 工作台页 (Type B)
适用于:Screener、SmartPick、FactorPick、PatternSelect、EarningsWindow、ChokepointSelect 等需要固定头部的页面。
标准 flush 模式(无筛选栏或筛选栏在滚动区内)
<div className="flex h-full flex-col overflow-hidden">
{/* PageHeader 直接置顶,自带 border-b */}
<PageHeader icon={Icon} title="页面标题" description="说明" />
{/* 可滚动主体 */}
<div className="flex-1 overflow-y-auto scrollbar-thin">
<div className="mx-auto w-[98%] max-w-[2200px] space-y-4 p-5">
{/* 内容 */}
</div>
</div>
</div>
带固定筛选栏模式
<div className="flex h-full flex-col overflow-hidden">
{/* 固定头部:PageHeader + FilterBar */}
<div className="flex-shrink-0 border-b border-border bg-card">
<PageHeader icon={Icon} title="页面标题" />
<FilterBar>
{/* 筛选控件 */}
</FilterBar>
</div>
{/* 可滚动主体 */}
<div className="flex-1 overflow-y-auto scrollbar-thin">
<div className="mx-auto w-[98%] max-w-[2200px] space-y-4 p-5">
{/* 内容 */}
</div>
</div>
</div>
规格:
- 外层:
flex h-full flex-col overflow-hidden(页面不自带滚动,由内部容器处理) - PageHeader 直接置顶(flush),无需额外
flex-shrink-0包裹(组件自带border-b) - 带固定筛选栏时:
flex-shrink-0 border-b border-border bg-card包裹 PageHeader + FilterBar - 主体滚动:
flex-1 overflow-y-auto scrollbar-thin - 内容区:
w-[98%] max-w-[2200px] space-y-4 p-5
3.4 详情页 (Type C)
适用于:FinancialDetail、EtfDetail、SectorDetail 等单记录详情页。
<div className="h-full overflow-y-auto scrollbar-thin">
<div className="mx-auto w-[98%] max-w-[2200px] space-y-5 p-6">
<DetailBackButton />
{/* 详情头部 */}
<div className="space-y-2">
<h1 className="text-2xl font-bold tracking-tight">标题</h1>
<p className="text-sm text-muted-foreground">描述</p>
</div>
{/* 详情内容 */}
<Card className="rounded-2xl p-6">
{/* ... */}
</Card>
</div>
</div>
规格:
- 内容区:
p-6(详情页留白更多) - 卡片:
p-6 - 标题:
text-2xl font-bold
3.5 双面板页 (Type D)
适用于:Analysis、TrendDiscovery、RealtimeMarket 等主辅信息并排页。
标准 flush 模式(PageHeader 置顶)
<div className="flex h-full flex-col overflow-hidden">
<PageHeader icon={Icon} title="页面标题" description="说明" />
<div className="flex-1 overflow-hidden">
<div className="flex h-full flex-row">
{/* 主内容区 */}
<div className="flex min-h-0 flex-1 flex-col overflow-y-auto scrollbar-thin p-5">
{/* 主内容 */}
</div>
{/* 辅助面板 */}
<RightPanel resizable collapsible defaultWidth={420}>
{/* 辅助信息 */}
</RightPanel>
</div>
</div>
</div>
规格:
- PageHeader:flush 置顶,与 Type B 一致
- 主内容:
flex min-h-0 flex-1 flex-col overflow-y-auto scrollbar-thin p-5 - 辅助面板:
RightPanel组件,宽度 320~720px 可调
3.6 全屏沉浸页 (Type E)
适用于:MarketTreemap、CrowdingDetail 等图表密集页。
<div className="flex h-full flex-col overflow-hidden">
{/* 精简头部 */}
<div className="flex-shrink-0 flex items-center justify-between border-b border-border px-5 py-3">
<h1 className="text-lg font-semibold">标题</h1>
<div className="flex items-center gap-2">
{/* 操作 */}
</div>
</div>
{/* 全屏内容 */}
<div className="flex-1 overflow-hidden">
{/* 图表/可视化内容 */}
</div>
</div>
规格:
- 头部精简:
px-5 py-3,不使用 PageHeader - 内容区:
flex-1 overflow-hidden,无内边距
3.7 内容宽度规范
| 场景 | 宽度类名 | 说明 |
|---|---|---|
| 标准页面 | w-[98%] max-w-[2200px] | 充分适配宽屏 |
| 阅读/文章 | w-[96%] max-w-5xl | NewsDetail 等长文阅读 |
| 全屏图表 | w-full | MarketTreemap 等 |
规则:
- 最大宽度从 1920px 提升到 2200px,适配 2K/4K 显示器
- 宽度百分比从 96% 提升到 98%,减少两侧空白
四、组件规范
4.1 页面头部 (PageHeader)
所有非详情页 必须 使用 PageHeader 组件,且 必须置顶齐平(紧贴应用 header 与侧边栏,无间距)。
<div className="flex h-full flex-col overflow-hidden">
<PageHeader
icon={TrendingUp} // Lucide 图标
title="智能选股" // 必填
description="基于多因子模型的量化选股" // 可选
actions={ // 右侧操作区
<Button size="sm">新建策略</Button>
}
meta={ // 元信息(标题右侧,可选)
<span className="text-[11px] text-muted-foreground">
更新于 5 分钟前
</span>
}
/>
{/* 可滚动主体 */}
<div className="flex-1 overflow-y-auto scrollbar-thin">
<div className="mx-auto w-[98%] max-w-[2200px] space-y-4 p-5">
{/* 内容 */}
</div>
</div>
</div>
视觉规格:
| 元素 | 样式 |
|---|---|
| 容器 | px-6 py-2.5 border-b border-border |
| 图标容器 | h-8 w-8 rounded-control bg-accent text-primary flex items-center justify-center |
| 图标 | h-4 w-4 |
| 标题 | text-base font-semibold tracking-tight |
| 描述 | text-[11px] text-muted-foreground truncate mt-0 |
| 元信息 | text-[11px] text-muted-foreground(标题右侧) |
| 操作区 | flex items-center gap-2 |
| 整体间距 | gap-2(各层级) |
布局规则:
- 外层容器:
flex h-full flex-col overflow-hidden - PageHeader 直接作为第一个子元素(无卡片样式、无额外 padding)
- 滚动容器:
flex-1 overflow-y-auto scrollbar-thin - 内容区:
mx-auto w-[98%] max-w-[2200px] space-y-4 p-5
禁止:
- 手写内联页面头部样式
- 将 PageHeader 放在滚动容器内部(必须置顶齐平)
- 使用
text-xl font-bold替代text-base font-semibold - 图标不使用容器直接放置
- 外层容器缺少
overflow-hidden(会导致滚动穿透)
已统一页面:
- 规则选股 (SmartPick)
- 量化选股 (FactorPick)
- 形态选股 (PatternSelect)
- 财报选股 (EarningsWindow)
- 条件选股 (Screener)
- 卡脖子选股 (ChokepointSelect)
- 今日信号 (TacticSignalsDashboard)
- 自选股 (Favorites)
- 监控中心 (MonitorCenter)
- 持仓中心 (PositionCenter)
- 可转债 (ConvertibleBond)
- 热门股票 (HotTrending)
- 数据可信度 (DataTrustCenter)
- AI 使用统计 (AiUsage)
- 持仓体检 (PortfolioInspection)
- 掌握度 (Mastery)
- 社区 (Community)
- 商品中心 (CommodityCenter)
- 另类数据源 (AltSources)
4.2 卡片 (Card)
标准卡片
<Card className="rounded-2xl p-5">
<CardHeader>
<CardTitle>标题</CardTitle>
<CardDescription>描述</CardDescription>
</CardHeader>
<CardContent className="p-5 pt-0">
{/* 内容 */}
</CardContent>
</Card>
紧凑卡片(KPI、统计)
<Card className="rounded-2xl p-4">
<div className="flex items-center justify-between">
<span className="text-xs text-muted-foreground">标签</span>
<Icon className="h-4 w-4 text-muted-foreground" />
</div>
<div className="mt-2 text-2xl font-bold nums">1,234</div>
<div className="mt-1 text-xs text-up">+2.5%</div>
</Card>
玻璃态卡片
<div className="glass-card rounded-2xl p-5">
{/* 内容 */}
</div>
规则:
- 标准卡片:
p-5 - 紧凑卡片:
p-4 - 宽松卡片(详情页):
p-6 - 玻璃态:统一
rounded-2xl - 同一页面内,同类型卡片内边距必须一致
4.3 表格 (Table)
选型指南
| 数据量 | 推荐组件 | 配置 |
|---|---|---|
| < 50 行 | DataTable | 标准模式 |
| 50~500 行 | DataTable | dense 紧凑模式 |
| > 500 行 | VirtualTable | 虚拟滚动 |
| 只读展示 < 20 行 | Table 原语 | 轻量 |
禁止:原生 <table> HTML。
标准表格样式
<DataTable
columns={columns}
data={data}
// 标准模式(默认)
// 或 dense 紧凑模式
/>
统一规格:
| 属性 | 标准模式 | 紧凑模式 |
|---|---|---|
| 表头背景 | bg-secondary/50 | 同左 |
| 表头字重 | font-medium text-xs text-muted-foreground | 同左 |
| 表头高度 | h-9(36px) | h-8(32px) |
| 单元格内边距 | px-3 py-2(12px × 8px) | px-2 py-1.5(8px × 6px) |
| 行高 | h-10(40px) | h-8(32px) |
| 行悬停 | hover:bg-secondary/60 | 同左 |
| 行边框 | border-b border-border/60 | 同左 |
| 交替行 | 默认关闭 | 默认关闭 |
表头样式
<TableHead className="bg-secondary/50 font-medium text-xs text-muted-foreground uppercase tracking-wider">
列名
</TableHead>
规则:
- 表头使用大写字母(
uppercase) - 字间距加宽(
tracking-wider) - 数字列右对齐(
text-right) - 金额列使用
.nums类确保数字对齐
结果集表(DbAdmin / SQL 工作台,借鉴 dbx)
数据库管理页的结果集浏览(src/pages/DbAdmin/ResultTable.tsx,SQL 工作台与表数据 tab 共用)不适用上述 DataTable 规范:列多、行长、需要看列类型与排序态,按数据库客户端密度设计,不要按通用规范"统一"它。
| 属性 | 规格 |
|---|---|
| 表头多行式 | 最多三行按需渲染:列名行(text-xs font-semibold text-foreground + 排序方块 size-4,未排灰色双箭头,激活 bg-primary 白箭头)/ 类型行(font-mono text-[10px],按类型族着色,见 shared.ts::TYPE_TEXT_CLASS)/ 注释行(text-[10px] text-muted-foreground,非等宽——中文在 mono 下发虚;长注释 truncate + hover 由 tooltip 补全)。缺哪行省哪行:无类型无注释时退化为单行 29px,全齐约 57px |
| 注释放行判据 | 统一走 shared.ts::columnComment():trim 后非空、且不等于列名才显示(中文字段名表里「注释 == 列名」很常见,重复印一遍纯属噪音)。表头 / tooltip / 列设置面板 / 转置视图四处同源,不各写一遍 |
| 来源 | 注释来自 POST /dbadmin/query 的表元数据补齐(仅简单 SELECT 的指名列表/星号;别名/UNION 等无源列查询不返回 type/comment,表头如实退化) |
| 表头底色 | bg-muted(比表体 bg-background 略深),sticky top-0 吸顶 |
| 列间分隔 | border-r border-border/60 形成竖线栅格 |
| 行高 | h-7(28px),单元格 font-mono text-xs,数值列 text-right(ResultColumn.numeric 或 isNumericType(col.type) 判定,不按值猜) |
| 布局契约 | 结果区 flex min-h-0 flex-col 铺满剩余高度,滚动发生在表格容器内部(sticky 表头才有意义);结果面板自身不滚动 |
4.4 筛选栏 (FilterBar)
<FilterBar title="筛选条件">
<FilterSelect
label="市场"
options={marketOptions}
value={market}
onChange={setMarket}
/>
<FilterSelect
label="行业"
options={industryOptions}
value={industry}
onChange={setIndustry}
/>
<FilterBar.Divider />
<FilterSelect
label="排序"
options={sortOptions}
value={sort}
onChange={setSort}
/>
</FilterBar>
统一规格:
| 属性 | 值 |
|---|---|
| 容器内边距 | px-6 py-2.5 |
| 控件间距 | gap-2(8px) |
| 控件高度(标准) | h-7(28px) |
| 控件高度(紧凑) | h-6(24px) |
| 标签字号 | text-xs text-muted-foreground |
规则:
- 标准场景使用
h-7(sm) - 空间紧凑时使用
h-6(xs) - 不使用其他高度值
4.5 按钮 (Button)
变体 (Variants)
| 变体 | 用途 | 示例 |
|---|---|---|
default | 主操作 | 提交、保存 |
secondary | 次要操作 | 取消、返回 |
outline | 边框按钮 | 更多操作 |
ghost | 幽灵按钮 | 工具栏图标按钮 |
destructive | 危险操作 | 删除 |
link | 链接样式 | 跳转 |
尺寸 (Sizes)
| 尺寸 | 高度 | 用途 |
|---|---|---|
sm | h-8(32px) | 标准按钮 |
default | h-9(36px) | 表单提交 |
lg | h-10(40px) | 主要 CTA |
icon | h-8 w-8 | 图标按钮 |
规则:
- 同一操作区,按钮尺寸保持一致
- 主操作使用
default变体,每页不超过 2 个 - 图标按钮使用
ghost+icon尺寸
4.6 徽章 (Badge)
变体
| 变体 | 用途 | 样式 |
|---|---|---|
default | 默认标签 | bg-primary text-primary-foreground |
secondary | 次要标签 | bg-secondary text-secondary-foreground |
outline | 边框标签 | border text-foreground |
success | 成功 | bg-success/10 text-success |
warning | 警告 | bg-warning/10 text-warning |
destructive | 错误 | bg-destructive/10 text-destructive |
up | 涨 | bg-up/10 text-up |
down | 跌 | bg-down/10 text-down |
规格:
- 圆角:
rounded-md(6px) - 内边距:
px-2 py-0.5 - 字号:
text-xs
4.7 表单 (Form)
布局
<form className="space-y-4">
<div className="space-y-2">
<Label htmlFor="field">字段名</Label>
<Input id="field" placeholder="请输入" />
</div>
<div className="flex items-center gap-3">
<Button type="submit">提交</Button>
<Button type="button" variant="secondary">取消</Button>
</div>
</form>
规格:
- 表单项间距:
space-y-4(16px) - 标签与输入框间距:
space-y-2(8px) - 按钮组间距:
gap-3(12px)
输入框 (Input)
| 属性 | 值 |
|---|---|
| 高度 | h-9(36px) |
| 内边距 | px-3 py-2 |
| 圆角 | rounded-control(9px) |
| 边框 | border border-input |
| 焦点 | focus:ring-2 focus:ring-primary/20 |
日期选择 (DatePicker / DateRangePicker)
日期输入一律用日期选择器(禁止裸 <input> 或手输 YYYY-MM-DD 文本输入),
视觉对齐 shadcn v4 date-picker(Popover + Calendar,月/年下拉标题;项目未引入
react-day-picker 依赖,日历由自研 ui/calendar.tsx 实现):
- 单日:
<DatePicker>(src/components/ui/date-picker.tsx) - 区间:
<DateRangePicker>(src/components/ui/date-range-picker.tsx,presets可加「近 N 天」快捷侧栏)
| 属性 | 说明 |
|---|---|
value / from+to | YYYY-MM-DD 字符串或 null(null 语义由调用方定:如涨停板=恢复自动、回溯面板=回到最新) |
onChange | 单日 (date: string | null)(再点已选日期=取消选择);区间 (from, to) |
min / max | 可选日期边界(如回溯面板限定最早~最新快照日) |
clearable | 单日弹层底部「清空」按钮(仅 value 非空时显示) |
className | 透传触发按钮;默认紧凑档 h-8 text-xs(区间为 h-7 text-[11px]) |
尺寸档位:筛选条/工具栏用默认紧凑档;表单内与相邻 Input 同高同宽
(如 className="h-auto w-full rounded-xl px-3 py-2 text-sm",rounded-xl 在
级联中晚于 Button 基类 rounded-control,直接写即可覆盖,无需 !)。
4.8 板块分类卡片骨架 (SectorClassCard)
板块中心多种分类视图(东财行业+概念 / 申万一二级 / 同花顺行业)共享统一卡片骨架 src/components/sector/SectorClassCard.tsx,通过插槽注入差异数据。
<SectorClassCard
name="半导体"
badge="行业"
code="BK0491"
pct={2.35}
rightMetric={<>成交 123.45亿</>}
action={<StarIcon />} // 操作区(与 chevron 互斥)
// chevron // 或详情跳转箭头
onClick={() => navigate(...)}
>
{/* 扩展区:涨跌家数 / 价格位置分位条 / 成交占比等 */}
</SectorClassCard>
视觉规格:
| 元素 | 样式 |
|---|---|
| 容器 | glass-card rounded-2xl p-3.5 |
| 悬停 | hover:-translate-y-0.5 hover:border-primary/40 hover:shadow-mac |
| 名称 | text-sm font-semibold |
| 徽标 | text-[9px] bordered label (rounded border border-border/60 bg-muted/30 px-1 py-0.5) |
| 代码 | text-[10px] tabular-nums |
| 涨跌幅 | text-xl font-bold leading-none tabular-nums + pctTone 语义色 |
| 右侧指标 | text-[11px] tabular-nums text-muted-foreground |
| 扩展区 | border-t border-border/40 pt-2 space-y-1.5 |
规则:
- 新增板块分类视图必须使用
SectorClassCard骨架,不另起内联布局 - 同花顺行业因 master-detail 交互保留列表行(
ThsIndustryPanel.RankRow),但字号/间距/徽标与骨架对齐 action与chevron互斥:收藏场景用action,详情跳转用chevron
五、页面类型对照表
5.1 全量页面分类
| 页面 | 路由 | 类型 | 当前问题 | 优化动作 |
|---|---|---|---|---|
| Home | / | E | 自定义布局 | 保持 |
| Analysis | /analysis/:code | D | — | 保持 |
| SmartPick | /rule-pick | B | — | ✅ 已添加 PageHeader(挂可信度徽章 + 数据体检入口卡) |
| Screener | /screener | B | — | ✅ 已添加 PageHeader |
| Favorites | /favorites | B | 原生 table | ✅ 已添加 PageHeader;待迁移 DataTable |
| MarketCenter | /market-center | B | 原生 table + 手写虚拟滚动 | 待迁移 VirtualTable |
| EtfCenter | /etf-center | B | 原生 table | 待迁移 DataTable |
| Zhangtingban | /zhangtingban | A | — | 保持(Type A 标准内容页) |
| FactorPick | /factor-pick | B | — | ✅ 已添加 PageHeader(flush 置顶) |
| PatternSelect | /patterns | B | — | ✅ 已添加 PageHeader(flush 置顶) |
| EarningsWindow | /earnings | B | — | ✅ 已添加 PageHeader(flush 置顶) |
| Backtest | /backtest | A | — | 保持 |
| NlPick | /nl-pick | A | — | 保持 |
| GlobalMarket | /global-market | A | — | ✅ 已改为 2200px |
| DataTrustCenter | /data-trust | A | — | ✅ 新建页,Type A 规格(p-5 / 2200px);5 区块:可信度评分 / 数据源目录 / 源状态 / 新鲜度 / 降级时间线 / 主动探测 |
| Notifications | /notifications | A | — | 保持 |
| LimitUpReason | /limit-up-reason | A | — | 保持 |
| NewsRadar | /news-radar | A | — | 保持 |
| HotTrending | /hot-trending | B | — | ✅ 已添加 PageHeader |
| MonitorCenter | /monitor-center | B | — | ✅ 已添加 PageHeader |
| ThesisTracker | /thesis | B | — | 保持 |
| CatalystCalendar | /catalyst | B | — | 保持 |
| TradeJournal | /trade-journal | A | — | 保持 |
| ConvertibleBond | /bonds | A | — | 保持 |
| Debate | /rule-pick/debate | A | 卡片内边距不一致(p-4/p-5 混用) | 统一 p-5 |
| ChokepointSelect | /chokepoint | B | — | ✅ 已添加 PageHeader(flush 置顶) |
| TrendDiscovery | /trend-discovery | D | — | ✅ 已添加 PageHeader(flush 置顶) |
| RealtimeMarket | /realtime | D | — | 保持 |
| MorningNote | /morning-note | 自定义 | 双面板布局 | 保持 |
| DailyReview | /daily-review | 自定义 | 双面板布局 | 保持 |
| DimensionAnalysis | /dimension/:dimKey | 自定义 | 卡片内边距不一致(p-4/p-6 混用) | 统一 |
| SectorCenter | /sector-center | A | — | ✅ 板块分类卡片统一为 SectorClassCard 骨架(东财行业+概念 / 申万一二级 / 同花顺行业) |
| GlobalMacro | /global-macro | 薄包装 | — | 保持 |
| PolicyRates | /policy-rates | 薄包装 | — | 保持 |
| TacticSignals | /tactics | B | — | ✅ 已添加 PageHeader(flush 置顶) |
| PositionCenter | /position-center | C | — | ✅ 已添加 PageHeader |
| PatternSimilarity | /pattern-similarity | C | — | 保持 |
| FinancialDetail | /analysis/:code/finance | C | — | ✅ 已统一 p-6 |
| EtfDetail | /etf/:code | C | — | ✅ 已统一 p-6 |
| IndexDetail | /index-detail/:code | C | — | ✅ 已统一 p-6 |
| IndexConstituents | /index-constituents/:code | C | — | 保持 |
| SectorDetail | /sector-center/:code | C | — | 保持 |
| ConceptDetail | /sector-center/concept/:code | C | — | 保持 |
| IndustryDetail | /sector-center/sw/:system/:code | C | — | 保持 |
| NewsDetail | /news/:source/:newsKey | C | — | 保持 |
| StockModuleDetail | /analysis/:code/module/:module | C | — | ✅ 已统一 p-6 |
| Checklist | /checklist/* | C | — | 保持 |
| SmartPickDetail | /rule-pick/:strategyId | B | — | 保持 |
| LimitLadder | /limit-ladder | A | — | 保持 |
| CrowdingDetail | /crowding-detail | E | — | ✅ 已统一 p-6 |
| AnnualHeatmapDetail | /annual-heatmap-detail | E | — | ✅ 已统一 p-6 |
| MarketTreemap | /market-treemap | E | — | 保持 |
| Auction | /auction | A | — | 保持 |
| AIChat | /ai-chat | 自定义 | 聊天界面 | 保持 |
| DataManager | /datamgr | 管理 | — | 保持 |
| DbAdmin | /dbadmin | 管理 | — | 保持 |
| TaskCenter | /tasks | 管理 | — | 保持 |
| Settings | /settings/* | 管理 | — | 保持 |
5.2 优化优先级
| 优先级 | 任务 | 影响页面数 | 工作量 |
|---|---|---|---|
| P0 | 原生 <table> 迁移到 DataTable/VirtualTable | 3 | 中 |
| P1 | 添加 PageHeader 到缺失页面 | ~10 | 小 |
| P1 | 统一卡片内边距(消除同页多种 padding) | 3 | 小 |
| P2 | 统一页面内边距(p-5 / p-6) | 全部 | 中 |
| P2 | 统一区块间距(space-y-5) | 全部 | 小 |
| P2 | 内容宽度从 1920px 提升到 2200px | 全部 | 小 |
| P3 | glass-card 圆角统一为 rounded-2xl | ~15 | 小 |
六、实施计划
Phase 1:基础设施(1~2 天)
| 任务 | 文件 | 说明 |
|---|---|---|
| 1.1 扩展 PageHeader 组件 | src/components/ui/page-header.tsx | 增加更多 slots |
| 1.2 统一 Table 原语样式 | src/components/ui/table.tsx | 表头、行高、悬停 |
| 1.3 统一 DataTable 样式 | src/components/ui/data-table.tsx | 标准/紧凑模式 |
| 1.4 更新内容宽度常量 | src/components/ui/detail-page.tsx | 1920px → 2200px |
Phase 2:页面迁移(3~5 天)
| 优先级 | 页面 | 改动 | 状态 |
|---|---|---|---|
| P0 | MarketCenter | 原生 table → VirtualTable | ⏳ 待办 |
| P0 | EtfCenter | 原生 table → DataTable | ⏳ 待办 |
| P0 | Favorites | 原生 table → DataTable | ⏳ 待办(PageHeader 已完成) |
| P1 | Screener, SmartPick, HotTrending, MonitorCenter, PositionCenter | 添加 PageHeader | ✅ 已完成 |
| P1 | FactorPick, PatternSelect, EarningsWindow, ChokepointSelect, TacticSignals | 添加 PageHeader(flush 置顶) | ✅ 已完成 |
| P1 | Zhangtingban | 添加 PageHeader | ⏳ 待办 |
| P1 | DimensionAnalysis, Debate | 统一卡片内边距 | ⏳ 待办 |
| P2 | FinancialDetail, StockModuleDetail, IndexDetail, EtfDetail, CrowdingDetail, AnnualHeatmapDetail | 统一 p-6 + space-y-4 | ✅ 已完成 |
| P2 | GlobalMarket | max-w-[1600px] → max-w-[2200px] | ✅ 已完成 |
| P2 | 所有标准内容页 | 统一 p-5 + space-y-5 | ⏳ 待办 |
| P3 | 所有 glass-card | rounded-xl → rounded-2xl | ⏳ 待办 |
Phase 3:验证(1 天)
| 任务 | 说明 |
|---|---|
| 视觉走查 | 逐页面对比,确认一致性 |
| 响应式测试 | 1280px / 1920px / 2560px 三档 |
| 更新文档 | 将规范写入 AGENTS.md |
七、速查表
┌─────────────────────────────────────────────────────────────────┐
│ 页面布局规范速查 │
├─────────────────────────────────────────────────────────────────┤
│ 内容宽度 w-[98%] max-w-[2200px] │
│ 页面内边距 p-5(标准)/ p-6(详情页) │
│ 区块间距 space-y-5(默认)/ space-y-3(卡片内) │
│ 页面头部 PageHeader 组件(必须) │
│ 卡片圆角 rounded-2xl(glass-card 统一) │
│ 卡片内边距 p-5(标准)/ p-4(紧凑)/ p-6(宽松) │
│ 表格单元格 px-3 py-2(标准)/ px-2 py-1.5(紧凑) │
│ 表格行高 h-10(标准)/ h-8(紧凑) │
│ 筛选栏 FilterBar + FilterSelect,gap-2 │
│ 控件圆角 rounded-control(9px) │
│ 控件高度 h-9(输入框)/ h-7(FilterSelect)/ h-8(Button) │
│ 移动端断点 md: 以下(<768px),JS 用 useIsMobile() │
│ 移动端标题 长标题(≥7字)且右侧有内容 → short 模式 │
│ 移动端表格 overflow-x-auto + min-w-[520px] 横向滚动 │
│ 移动端抽屉 fixed w-80 max-w-[85vw] + translate-x 过渡 │
└─────────────────────────────────────────────────────────────────┘
八、参考资源
| 资源 | 链接 | 说明 |
|---|---|---|
| shadcn/ui | https://ui.shadcn.com | 组件设计参考 |
| Ant Design 5.x | https://ant.design | 间距、排版参考 |
| Tailwind CSS | https://tailwindcss.com | 工具类文档 |
| Tailwind CSS 4 | https://tailwindcss.com/docs | 本项目使用版本 |
九、移动端适配
适用视口:小于 768px(手机竖屏为主,参考 390×844,内容宽 ≈358px)。 桌面优先的布局规范在手机上按本节降级;只改视觉与布局,不改变数据流与功能逻辑。
9.1 断点约定
| 手段 | 值 | 说明 |
|---|---|---|
| CSS 断点 | md:(768px)以下视为移动端 | Tailwind 默认断点,统一以 md 为界,不引入自定义断点 |
| JS 判定 | useIsMobile()(src/hooks/useIsMobile.ts) | matchMedia (max-width: 767px) + 事件驱动;轻量场景可用同文件 useMediaQuery(query) |
| 参考视口 | 390×844 | 手机竖屏;设计时按内容宽 ≈358px 校验 |
选择规则:
- 纯样式差异(宽度、列数、间距)优先用
md:前缀——无 JS、无二次渲染、SSR 友好; - 需要结构差异(挂载/卸载、双布局只渲染其一、条件传参)才用
useIsMobile(); - 两个分支必须互斥且各只挂载一份,避免重复请求与重复监听。
9.2 卡片标题 short 模式
共享组件 SectionTitle(src/components/market/dashboard/primitives.tsx)与
DataModuleHeader(src/components/ui/data-module-header.tsx)支持 short?: string:
<SectionTitle title="指数年度涨跌热力图" short="年度涨跌热力图" />
<DataModuleHeader title="阶段涨跌幅" short="阶段涨跌" hint="近 5 日区间收益" />
- 组件内部自行调用
useIsMobile():移动端渲染short,桌面端渲染title,调用方不做任何判断; short由人工甄选核心语义,不是自动截断(例:阶段涨跌幅→阶段涨跌、指数年度涨跌热力图→年度涨跌热力图)。
甄选规则(需同时满足 1+2):
| 条件 | 说明 |
|---|---|
| ① 标题 ≥7 个显示字符 | 短标题(≤6 字)移动端放得下,不动 |
| ② 与右侧内容共享一行 | 右侧有 hint/action/extra 挤压标题时才降字;独占整行的静态 h2/h3 跳过,即使 9~12 字 |
| — 动态标题跳过 | {card.name}、{activeTab.label} 等运行时取值无法人工甄选 |
9.3 网格与表格
- KPI/统计网格降列:
grid-cols-2 sm:grid-cols-3 md:grid-cols-4(移动端两列起步,桌面恢复完整列数)。 - 宽表格不缩列、不换行:外包
overflow-x-auto,给表格容器显式最小宽度(min-w-[520px]/min-w-[640px],按表格实际列数定),移动端横向滚动查看。 - 🔴 禁止在移动端隐藏表格列来「适配」——列的取舍是信息设计问题,横向滚动是布局问题,后者才是移动端的正解。
9.4 抽屉模式(移动端侧栏)
参考实现:src/pages/MorningNote.tsx(配置面板)。
{/* 触发按钮:仅移动端显示 */}
<Button variant="outline" size="icon" className="h-8 w-8 md:hidden"
onClick={() => setConfigOpen(true)}>
<Settings2 className="h-3.5 w-3.5" />
</Button>
{/* 遮罩 */}
{configOpen && (
<div
className="fixed inset-x-0 bottom-0 top-[56px] z-40 bg-black/40 backdrop-blur-sm md:hidden"
onClick={() => setConfigOpen(false)}
/>
)}
{/* 抽屉面板:fixed + transform 过渡 */}
<aside
className={cn(
"fixed bottom-0 left-0 top-[56px] z-50 flex w-80 max-w-[85vw] flex-col border-r border-border bg-card transition-transform duration-300 ease-out md:hidden",
configOpen ? "translate-x-0" : "-translate-x-full"
)}
>
{configPanel}
</aside>
规格:
| 元素 | 样式 |
|---|---|
| 面板 | fixed bottom-0 left-0 top-[56px] z-50 w-80 max-w-[85vw] + transition-transform duration-300 |
| 遮罩 | z-40 bg-black/40 backdrop-blur-sm,点击关闭 |
| 开合 | translate-x-0 / -translate-x-full 切换,关着也要挂载(保留过渡动画) |
| 滚动锁定 | 打开时锁定背景滚动,关闭恢复 |
| 桌面端 | 同一内容走原侧栏布局,与 md:hidden 分支互斥 |
9.5 搜索框与下拉面板
- 标题栏搜索框宽度分档:
w-28 sm:w-48 xl:w-56 2xl:w-64 3xl:w-72(移动端收窄,给图标区让位)。 - StockSearch 下拉面板移动端不走窄面板:改视口级全宽
fixed inset-x-2 top-[60px],桌面仍absolute right-0 mt-2 w-[340px] max-w-[calc(100vw-2rem)]。 - fixed 面板仍渲染在
containerRef内,外点关闭的contains判定不受影响。 - 下拉仅在有内容时渲染(历史/关键词/结果三者其一非空),空关键词聚焦不弹空面板。
9.6 其他
| 场景 | 规则 |
|---|---|
| K线图高度 | 移动端 280px(桌面保持原有更大高度),useIsMobile() 条件传参 |
| Dialog | 移动端 max-w-[calc(100vw-2rem)],不超视口 |
| 双布局页面 | Analysis 等页面移动端/桌面两套布局只挂载其一 |
| 触控目标 | 可点击控件 ≥ 32px(h-8),避免纯 24px 图标热区 |
本规范由 AI 辅助生成,经人工审核后生效。如有疑问或建议,请联系开发团队。