PageHeader
page-header组合返回、面包屑、标题、标签和操作区做出页头
用法
极简(仅标题 + 操作)
最常见的列表页头:左侧标题,右侧主操作按钮。
用户管理
tsx
<PageHeader
title="用户管理"
extra={<Button variant="solid" size="sm">新建用户</Button>}
/>面包屑 + 副标题 + 标签
breadcrumb 渲染在标题上方,subTitle 与 tags 贴在标题右侧。
商品列表
管理在售与下架商品128 在售
tsx
<PageHeader
breadcrumb={<Breadcrumb items={[{ label: "首页", href: "/" }, { label: "商品列表" }]} />}
title="商品列表"
subTitle="管理在售与下架商品"
tags={<Chip tone="brand" variant="soft" size="sm">128 在售</Chip>}
/>详情页(返回 + Tabs 页脚 + 分隔线)
传入 onBack 渲染返回箭头;footer 常放 Tabs;bordered 在底部加分隔线。
订单 #20260603-8821
共 6 件商品进行中已付款
tsx
<PageHeader
onBack={() => router.back()}
breadcrumb={<Breadcrumb items={items} />}
title="订单 #20260603-8821"
subTitle="共 6 件商品"
tags={<Chip tone="brand" variant="soft" size="sm">进行中</Chip>}
extra={<><Button variant="ghost" size="sm">导出</Button><Button variant="solid" size="sm">编辑</Button></>}
footer={
<Tabs defaultValue="detail">
<TabsList>
<TabsTab value="detail">详情</TabsTab>
<TabsTab value="items">商品</TabsTab>
<TabsTab value="logistics">物流</TabsTab>
</TabsList>
</Tabs>
}
bordered
/>元信息行
meta 是标题下那串用分隔符串起来的事实值;空项自动跳过,因此某项缺值时不会留下孤零零一个分隔符。
张三
在保
- 330106…512
- 男
- 3 段社保
- 2 家公司
- 最近参保单位:杭州美风科技有限公司
tsx
<PageHeader
title="张三"
meta={[
"330106…512",
"男",
segments && `${segments} 段社保`, // 无值时整项消失,不留孤点
companyCount ? `${companyCount} 家公司` : null,
"最近参保单位:杭州美风科技有限公司",
]}
/>何时用
详情页 / 管理页顶部的页头骨架:统一安放返回箭头、面包屑、主标题、状态标签、右侧操作区和底部 Tabs。各槽位直接 dogfood 传入瑚琏 Breadcrumb / Chip / Tabs;只需要面包屑导航本身用 Breadcrumb,PageHeader 是把整块页头排版好。
导入
ts
import { PageHeader } from "@hulianui/ui"Props
| 名称 | 类型 | 默认 | 说明 |
|---|---|---|---|
| backLabel | string | "返回" | 返回按钮的无障碍标签。 |
| bordered | boolean | false | 是否在页头底部渲染分隔线(复用 <Separator/>)。 |
| metaSeparator | ReactNode | "·" | meta 各项之间的分隔符,装饰位自动 aria-hidden。 |
| titleAs | ElementType | "h1" | 标题渲染成哪个标签。层级归页面决定;只让出标签,字号不跟着标签变(恒为 20px/28px)。 |
另继承HTMLAttributes<HTMLElement>(除title,因其类型被改为 ReactNode)。
Events
| 事件 | 类型 | 说明 |
|---|---|---|
| onBack | () => void | 提供则在标题左侧渲染返回箭头按钮,点击触发该回调(带回调 → 消费侧为 client)。 |
Slots
| 插槽 | 类型 | 说明 |
|---|---|---|
| title* | ReactNode | 主标题。 |
| subTitle | ReactNode | 副标题,内联于标题右侧,中性弱化色。 |
| breadcrumb | ReactNode | 面包屑区(标题行上方),传入瑚琏 <Breadcrumb/>。 |
| tags | ReactNode | 状态标签区(贴标题右侧),传入 <Chip/>/<Badge/> 等。 |
| meta | ReactNode[] | 元信息行:标题下面那串用 metaSeparator 串起来的事实值。分隔符由组件插在项与项之间,空项自动跳过。 |
| extra | ReactNode | 右侧操作区(按钮组等),窄屏(视口 < 640px)自动换行到标题下方;宽屏下标题再长也不换行,长标题该截断就截断。 |
| footer | ReactNode | 底部附加区,常放 <Tabs/>。 |
禁忌 / 坑
meta是一串并列的事实值,别拿它当别的槽用:一句话说明用subTitle,状态标记用tags,Tabs 之类的整块内容用footer。meta里的空项(null/undefined/false/"")自动跳过,分隔符只插在留下来的项之间,所以不必在调用点先filter(Boolean)。数字0是事实值(「0 家公司」),不算空。- 元信息行渲染为
<ul>/<li>,分隔符是独立的aria-hidden装饰位——读屏读到的是列表项而不是被中点粘住的长串文本。别再用span + span::before { content: "·" }自己拼点。 - 从
span + span::before { content: "·" }迁过来时要逐项核对,不能照搬:那条选择器的真实语义是「渲染出来的相邻 `<span>` 之间才插点」,所以那一行里混了按钮 / 图标 / 链接(渲染成<button>/<svg>/<a>)的位置,现网本来就没有点。而meta的「一项」是数组项,不看它渲染成什么标签,一律在项与项之间插分隔符。把[证件号, <CopyButton/>]这类混排原样搬过来,会凭空多出一个分隔符——这是真实的视觉回归,不是本组件的 bug(hulianui/hulian#247)。 - `extra` 换行看视口,不看标题长度(#263)。左列是
flex: 1 1 0,宽屏下标题再长也不会把操作区挤到第二行;视口 < 640px 才让位换行。同源问题在 Card 的CardHeader上先暴露:那边卡片宽度由布局决定、与视口无关,所以连这一档窄屏换行都没有。 titleAs只让出标签,不让出字号:换成h2之后字号仍是 20px/28px。要改字号在className上用后代选择器(className落在外层<header>),别在title里再套一层子元素用工具类把父元素的字号顶掉——那是一个标题上两条打架的字号声明。title为 ReactNode,与HTMLAttributes.title?: string冲突,类型已Omit<"title">——别再往 DOM 透传字符串 title。- 默认返回标签跟随
ConfigProvider,backLabel显式覆盖;组件因此是 client 组件,服务端组件仍可导入并渲染它。
相关
Tabs · Breadcrumb · Pagination · Anchor · Affix · BackTop
Playground
订单 #20260603-8821
共 6 件商品进行中已付款
<PageHeader
onBack={() => router.back()}
breadcrumb={<Breadcrumb items={items} />}
title="订单 #20260603-8821"
subTitle="共 6 件商品"
tags={<Chip tone="brand" variant="soft">进行中</Chip>}
extra={<Button>编辑</Button>}
footer={<Tabs>…</Tabs>}
bordered
/>