页面页眉和页脚
你可以调整页面布局的一些方面,以更好地满足你的需求。主要选项有 header、footer、headerHeight 和 footerHeight。
Imported from DOCX
当你导入 .docx 文件并使用 DOCX 导入
扩展 时,文档中的页眉和页脚会自动应用到 Pages 中——包括首页不同以及奇偶页不同的变体。无需额外配置。
页眉内容
- 将在页面页眉中渲染的内容
- 默认值:
''– 空字符串
初始配置(字符串)
Pages.configure({
header: 'Awesome Tiptap header', // 字符串!
})初始配置(HTML)
Pages.configure({
header: () => '<bold>Awesome Tiptap header</bold>', // 返回 HTML 或字符串的函数!
})页眉内容编辑命令
editor.commands.setHeader('My awesome header') // 字符串!
// 或者
editor.commands.Header((page, total) => `${page} of ${total}`) // 返回 HTML 或字符串的函数!页眉替换模板
当 header 属性使用字符串时,提供了两个便捷的模板替换:
{page}:被替换为当前页码。{total}:被替换为文档的总页数。
header 属性也可以接受一个函数,传递相同的信息,这次通过函数参数提供:
Pages.configure({
header: {
type: 'doc',
content: [
{
type: 'paragraph',
content: [{ type: 'text', text: 'Document Title', marks: [{ type: 'bold' }] }],
},
],
},
})页眉高度
- 控制每页顶部页眉区域的高度。
- 默认值:
50(像素)
两种方式都可以设置内容:
使用 configure 的初始设置:
Pages.configure({
header: 'My Document Title',
footer: 'Page {page} of {total}',
})使用 commands 的运行时更新:
// 编辑器初始化后更新页眉
editor.commands.setHeader('Updated Header')页眉配置
默认页眉
header 选项设置每页页眉区域显示的内容。
初始配置:
Pages.configure({
headerHeight: 80, // 让页眉区域更高
})页眉高度编辑命令
editor.commands.setHeaderHeight(30) // 更小的页眉!页脚内容
- 将在页面页脚中渲染的内容
- 默认值:
{page}– 带有页码模板替换的字符串
初始配置(字符串)
Pages.configure({
footer: 'Awesome Tiptap footer', // 字符串!
})编辑器命令:
// 覆盖页眉上边距
editor.commands.setHeaderTopMargin(40)
// 清除覆盖并回退到当前格式的默认值
//(该格式顶部边距的 50%)
editor.commands.resetHeaderTopMargin()Input validation
setHeaderTopMargin 会拒绝负值和 NaN——在这些情况下,该命令会返回 false 并记录警告。
页脚配置
默认页脚
footer 选项设置每页页脚区域显示的内容。
初始配置:
Pages.configure({
footer: () => '<bold>Awesome Tiptap footer</bold>', // 返回 HTML 或字符串的函数!
})页脚内容编辑命令
editor.commands.setFooter('My awesome footer') // 字符串!
// 或者
editor.commands.setFooter((page, total) => `${page} of ${total}`) // 返回 HTML 或字符串的函数!页脚替换模板
当 footer 属性使用字符串时,提供了两个便捷的模板替换:
{page}:被替换为当前页码。{total}:被替换为文档的总页数。
footer 属性也可以接受一个函数,传递相同的信息,这次通过函数参数提供:
Pages.configure({
footer: (page, total) => `${page} of ${total}` // 示例,显示:"1 of 10"
})编辑器命令:
// 覆盖页脚下边距
editor.commands.setFooterBottomMargin(40)
// 清除覆盖并回退到当前格式的默认值
//(该格式底部边距的 50%)
editor.commands.resetFooterBottomMargin()Input validation
setFooterBottomMargin 会拒绝负值和 NaN——在这些情况下,该命令会返回 false 并记录警告。
首页不同
editor.commands.setFooterHeight(30) // 更小的页脚!注意
setDifferentFirstPage() 命令可同时启用首页不同的页眉和页脚,行为类似 Microsoft Word。
页面边距可以通过使用自定义页面格式进行自定义。内置格式具有固定边距,但你可以创建自己的带有自定义边距的格式:
Pages.configure({
header: 'Default Header',
footer: 'Page {page}',
differentFirstPage: true,
headerFirstPage: 'Title Page',
footerFirstPage: '', // 首页无页脚
})命令
// 启用首页不同(同时影响页眉和页脚)
editor.commands.setDifferentFirstPage(true)
// 设置首页页眉和页脚内容
editor.commands.setHeaderFirstPage('Welcome to My Document')
editor.commands.setFooterFirstPage('')奇偶页不同
启用奇数页(1、3、5……)和偶数页(2、4、6……)不同的页眉和/或页脚,类似于微软 Word 的“奇偶页不同”选项。
注意
setDifferentOddEven() 命令可同时启用奇偶页不同的页眉和页脚,行为类似 Microsoft Word。
配置
Pages.configure({
differentOddEven: true,
headerOdd: '章节标题 – 奇数页',
headerEven: '书名 – 偶数页',
footerOdd: '第 {page} 页',
footerEven: '第 {page} 页',
})命令
// 启用奇偶页不同(同时影响页眉和页脚)
editor.commands.setDifferentOddEven(true)
// 设置奇数页和偶数页页眉
editor.commands.setHeaderOdd('奇数页页眉')
editor.commands.setHeaderEven('偶数页页眉')
// 设置奇数页和偶数页页脚
editor.commands.setFooterOdd('第 {page} 页')
editor.commands.setFooterEven('第 {page} 页')首页与奇偶页同时启用
当同时启用 differentFirstPage 和 differentOddEven 时,首页页眉/页脚优先于奇偶页设置,同时页 1 使用首页设置。
优先顺序:
- 首页(如果启用
differentFirstPage)- 适用于第 1 页 - 奇偶页(如果启用
differentOddEven)- 适用于第 2 页及之后 - 默认页眉/页脚 - 后备
Pages.configure({
header: 'Default Header', // 后备
differentFirstPage: true,
headerFirstPage: 'Title Page', // 第 1 页
differentOddEven: true,
headerOdd: 'Odd Page Header', // 第 3, 5, 7 页……
headerEven: 'Even Page Header', // 第 2, 4, 6 页……
})可编辑的页眉和页脚
用户可以通过双击页眉或页脚直接编辑它们。这将打开一个功能完备的 Tiptap 编辑器,允许丰富文本编辑。
自定义扩展
页眉/页脚编辑器使用与你在主编辑器中配置的相同扩展。为了匹配 schema(这样相同的 mark、节点和表格行为都能在页眉和页脚中工作),请通过 headerFooterExtensions 传入 ConvertKit 和 TableKit:
import { ConvertKit } from '@tiptap-pro/extension-convert-kit'
import { TableKit } from '@tiptap-pro/extension-pages-tablekit'
Pages.configure({
header: 'My header',
headerFooterExtensions: [ConvertKit.configure({ table: false }), TableKit],
})任何 Tiptap 扩展都可以通过 headerFooterExtensions 选项添加到页眉/页脚编辑器中。与主编辑器的扩展栈保持一致,可以让文档及其页眉页脚之间的 schema、键盘快捷键和渲染效果保持统一。
激活的编辑器状态
当用户双击页眉或页脚进行编辑时,扩展会通过存储暴露当前激活的编辑器。这使你能够构建与页眉/页脚编辑器配合使用的自定义工具栏。
存储属性:
activeEditor– 当前打开的页眉或页脚编辑器对应的 Tiptap Editor 实例(或null)activeEditorType–'header'、'footer'或nullactivePageNumber– 正在编辑的页码(或null)headerEditorOn/headerEditorOff– 订阅/取消订阅页眉覆盖层内部 Tiptap 编辑器上的事件footerEditorOn/footerEditorOff– 订阅/取消订阅页脚覆盖层内部 Tiptap 编辑器上的事件
该扩展还会监听页眉和页脚内部编辑器发出的 focus 事件。当用户聚焦到已打开的页眉或页脚编辑器时,activeEditor、activeEditorType 和 activePageNumber 会从当前激活的覆盖层中刷新。
监听页眉/页脚编辑器事件
页眉和页脚各自拥有独立的编辑器。你可以通过 editor.storage.pages 订阅这些编辑器的事件。
import { useEffect, useState } from 'react'
function HeaderFooterStatus({ editor }) {
const [activeEditorType, setActiveEditorType] = useState(null)
const [activePageNumber, setActivePageNumber] = useState(null)
useEffect(() => {
if (!editor) return
const syncActiveEditorState = () => {
const { activeEditorType, activePageNumber } = editor.storage.pages
setActiveEditorType(activeEditorType)
setActivePageNumber(activePageNumber)
}
editor.storage.pages.headerEditorOn?.('focus', syncActiveEditorState)
editor.storage.pages.footerEditorOn?.('focus', syncActiveEditorState)
return () => {
editor.storage.pages.headerEditorOff?.('focus', syncActiveEditorState)
editor.storage.pages.footerEditorOff?.('focus', syncActiveEditorState)
}
}, [editor])
if (!activeEditorType || !activePageNumber) {
return <span>正在编辑文档</span>
}
return (
<span>
正在编辑第 {activePageNumber} 页的 {activeEditorType}
</span>
)
}你也可以将相同的辅助方法用于其他 Tiptap 编辑器事件,例如 update、selectionUpdate、blur 或 transaction。
构建自定义工具栏
下面是一个 React 组件示例,它为主编辑器和页眉/页脚编辑器提供一个统一的工具栏:
import { useEditor, useEditorState, EditorContent } from '@tiptap/react'
import { useEffect, useState } from 'react'
function DocumentEditor() {
const [headerFooterEditor, setHeaderFooterEditor] = useState(null)
const [activeEditorType, setActiveEditorType] = useState(null)
const [activePageNumber, setActivePageNumber] = useState(null)
const editor = useEditor({
extensions: [
ConvertKit.configure({ table: false }),
TableKit,
Pages.configure({
header: '文档页眉',
footer: '第 {page} 页',
}),
],
})
// 监听页眉/页脚编辑器的激活变化
useEffect(() => {
if (!editor) return
const syncActiveHeaderFooterEditor = () => {
const { activeEditor, activeEditorType, activePageNumber } = editor.storage.pages
setHeaderFooterEditor(activeEditor)
setActiveEditorType(activeEditorType)
setActivePageNumber(activePageNumber)
}
editor.on('update', syncActiveHeaderFooterEditor)
editor.storage.pages.headerEditorOn?.('focus', syncActiveHeaderFooterEditor)
editor.storage.pages.footerEditorOn?.('focus', syncActiveHeaderFooterEditor)
return () => {
editor.off('update', syncActiveHeaderFooterEditor)
editor.storage.pages.headerEditorOff?.('focus', syncActiveHeaderFooterEditor)
editor.storage.pages.footerEditorOff?.('focus', syncActiveHeaderFooterEditor)
}
}, [editor])
// 使用激活的编辑器(页眉/页脚或主编辑器)
const activeEditor = activeEditorType ? headerFooterEditor : editor
const { isBoldActive } = useEditorState({
editor: activeEditor,
selector: ({ editor: e }) => ({
isBoldActive: e?.isActive('bold') ?? false,
}),
})
return (
<div>
<div className="toolbar">
<span>
Editing:{' '}
{activeEditorType && activePageNumber
? `${activeEditorType} on page ${activePageNumber}`
: 'Document'}
</span>
<button
className={isBoldActive ? 'is-active' : ''}
onClick={() => activeEditor?.chain().focus().toggleBold().run()}
>
加粗
</button>
</div>
<EditorContent editor={editor} />
</div>
)
}访问页眉和页脚内容
用户通过页眉或页脚编辑器修改内容后,内容会存储在扩展的存储区。这对于保存文档或导出为 DOCX 等格式非常有用。
HTML 内容
访问编辑后的 HTML 内容:
// 默认页眉/页脚
const headerHTML = editor.storage.pages.headerHTML
const footerHTML = editor.storage.pages.footerHTML
// 首页
const headerFirstPageHTML = editor.storage.pages.headerFirstPageHTML
const footerFirstPageHTML = editor.storage.pages.footerFirstPageHTML
// 奇偶页
const headerOddHTML = editor.storage.pages.headerOddHTML
const headerEvenHTML = editor.storage.pages.headerEvenHTML
const footerOddHTML = editor.storage.pages.footerOddHTML
const footerEvenHTML = editor.storage.pages.footerEvenHTML注意
这些存储值在用户通过相应的页眉/页脚编辑器编辑内容之前均为 null。编辑之前,使用配置中的原始模板。
用于 DOCX 导出的 JSON 内容
对于 DOCX 导出,使用 JSON 版本,它们提供结构化的 Tiptap 文档格式:
// 默认页眉/页脚
const headerJSON = editor.storage.pages.headerJSON
const footerJSON = editor.storage.pages.footerJSON
// 首页
const headerFirstPageJSON = editor.storage.pages.headerFirstPageJSON
const footerFirstPageJSON = editor.storage.pages.footerFirstPageJSON
// 奇偶页
const headerOddJSON = editor.storage.pages.headerOddJSON
const headerEvenJSON = editor.storage.pages.headerEvenJSON
const footerOddJSON = editor.storage.pages.footerOddJSON
const footerEvenJSON = editor.storage.pages.footerEvenJSON注意
这些存储值在用户通过相应的页眉/页脚编辑器编辑内容之前均为 null。编辑之前,使用配置中的原始模板。
保存页眉和页脚内容
保存页眉和页脚内容是你的责任。保存文档时从存储区获取内容,加载时再恢复。
保存所有页眉和页脚内容:
function getHeaderFooterContent(editor) {
const { pages } = editor.storage
return {
// 默认页眉/页脚
headerHTML: pages.headerHTML,
headerJSON: pages.headerJSON,
footerHTML: pages.footerHTML,
footerJSON: pages.footerJSON,
// 首页
headerFirstPageHTML: pages.headerFirstPageHTML,
headerFirstPageJSON: pages.headerFirstPageJSON,
footerFirstPageHTML: pages.footerFirstPageHTML,
footerFirstPageJSON: pages.footerFirstPageJSON,
// 奇偶页
headerOddHTML: pages.headerOddHTML,
headerOddJSON: pages.headerOddJSON,
headerEvenHTML: pages.headerEvenHTML,
headerEvenJSON: pages.headerEvenJSON,
footerOddHTML: pages.footerOddHTML,
footerOddJSON: pages.footerOddJSON,
footerEvenHTML: pages.footerEvenHTML,
footerEvenJSON: pages.footerEvenJSON,
}
}
// 保存到后端
const headerFooterData = getHeaderFooterContent(editor)
await saveDocument({ content: editor.getJSON(), headerFooter: headerFooterData })编程式打开和关闭编辑器
你可以通过编程方式打开和关闭页眉和页脚编辑器。这对于集成自定义 UI 元素、构建导航界面或响应用户操作非常有用。
打开编辑器
使用 openHeaderEditor 和 openFooterEditor 命令为指定页面打开编辑器:
// 打开第 1 页的页眉编辑器
editor.commands.openHeaderEditor({ pageNumber: 1 })
// 打开第 3 页的页脚编辑器
editor.commands.openFooterEditor({ pageNumber: 3 })这些命令在成功打开编辑器时返回 true,页面不存在时返回 false。
注意
打开一个编辑器时会自动关闭之前打开的任何页眉或页脚编辑器。主文档编辑器在页眉或页脚编辑器打开时不可编辑。
使用场景:
- 构建页码导航 UI,让用户直接跳转编辑指定页的页眉或页脚
- 在工具栏中创建“跳转到页眉/页脚”按钮
- 实现快捷键打开页眉或页脚
- 响应外部事件时编程打开编辑器
示例:页面导航组件
function PageNavigation({ editor }) {
const pageCount = editor.storage.pages.getPageCount?.() ?? 1
return (
<div>
{Array.from({ length: pageCount }, (_, i) => (
<div key={i + 1}>
<span>第 {i + 1} 页</span>
<button onClick={() => editor.commands.openHeaderEditor({ pageNumber: i + 1 })}>
编辑页眉
</button>
<button onClick={() => editor.commands.openFooterEditor({ pageNumber: i + 1 })}>
编辑页脚
</button>
</div>
))}
</div>
)
}关闭编辑器
使用关闭命令关闭页眉或页脚编辑器:
// 关闭当前打开的任何编辑器
editor.commands.closeHeaderFooterEditors()
// 关闭特定编辑器
editor.commands.closeHeaderEditor()
editor.commands.closeFooterEditor()防止双击关闭编辑器
默认情况下,在打开的页眉或页脚编辑器外双击会关闭编辑器。你可以使用回调选项防止此行为,这在有工具栏按钮或其他 UI 元素时很有用,避免用户双击时编辑器意外关闭。
配置
提供三种回调选项:
onDblClickHeaderFooterPreventClose- 适用于页眉和页脚(回退)onDblClickHeaderPreventClose- 仅适用于页眉(优先于回退)onDblClickFooterPreventClose- 仅适用于页脚(优先于回退)
每个回调函数接收 MouseEvent,返回 true 表示阻止关闭,false 允许关闭。
示例:点击工具栏时防止关闭
Pages.configure({
header: '我的页眉',
footer: '第 {page} 页',
onDblClickHeaderFooterPreventClose: (event) => {
// 如果点击在工具栏内,保持编辑器打开
const toolbar = document.querySelector('.my-toolbar')
return toolbar?.contains(event.target)
},
})示例:对页眉和页脚采用不同行为
Pages.configure({
header: '我的页眉',
footer: '第 {page} 页',
// 双击页眉外部永不关闭
onDblClickHeaderPreventClose: () => true,
// 仍允许双击页脚外部关闭
onDblClickFooterPreventClose: () => false,
})重点强调颜色
可以自定义页眉和页脚编辑器覆盖层的视觉样式,调整强调色包括:
- 光标颜色 — 编辑器中的文本光标颜色
- 工具栏边框 — 编辑工具栏的顶部边框(页眉)或底部边框(页脚)
- 标签背景 — “页眉” 或 “页脚” 标签背景色
配置
在扩展配置时设置强调色:
Pages.configure({
header: '我的页眉',
footer: '第 {page} 页',
// 同时设置页眉和页脚的颜色
accentColor: '#3b82f6',
})也可以分别设置页眉和页脚的强调色:
Pages.configure({
header: '我的页眉',
footer: '第 {page} 页',
headerAccentColor: '#3b82f6', // 页眉的蓝色
footerAccentColor: '#10b981', // 页脚的绿色
})运行时更新
使用命令动态修改强调色:
// 同时更新页眉和页脚颜色
editor.commands.setAccentColor('#8b5cf6')
// 仅更新页眉颜色
editor.commands.setHeaderAccentColor('#3b82f6')
// 仅更新页脚颜色
editor.commands.setFooterAccentColor('#ef4444')CSS 颜色值
强调色选项支持任何有效的 CSS 颜色值,包括十六进制(#3b82f6)、rgb(rgb(59, 130, 246))、hsl(hsl(217, 91%, 60%))、oklch 或 CSS 变量(var(--primary-color))。
限制
编辑功能不能禁用
双击编辑行为是内置于扩展中的,无法禁用。所有有权访问编辑器的用户都可以编辑页眉和页脚。
页眉和页脚编辑器样式
页眉/页脚编辑器使用内置样式。但你可以通过 accentColor、headerAccentColor 和 footerAccentColor 选项自定义强调色。
协作功能
页眉和页脚编辑器会与主文档一起参与协作。请像为任何 Pages 编辑器一样设置协作提供器——无需额外连接即可让页眉和页脚进入同一会话。完整设置流程请参见 向 Pages 添加协作。
页眉/页脚编辑器使用内置样式,但你可通过 accentColor、headerAccentColor 和 footerAccentColor 选项自定义强调色。
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
header | string | JSONContent | '' | 默认页眉内容 |
footer | string | JSONContent | '' | 默认页脚内容 |
headerTopMargin | number | 顶部边距的 50% | 页眉内容距离页面顶部的距离 |
footerBottomMargin | number | 底部边距的 50% | 页脚内容距离页面底部的距离 |
differentFirstPage | boolean | false | 是否启用首页不同的页眉和页脚 |
headerFirstPage | PagesHeaderFooter | '' | 首页页眉内容 |
footerFirstPage | PagesHeaderFooter | '' | 首页页脚内容 |
differentOddEven | boolean | false | 是否启用奇偶页不同的页眉和页脚 |
headerOdd | PagesHeaderFooter | '' | 奇数页页眉内容 |
headerEven | PagesHeaderFooter | '' | 偶数页页眉内容 |
footerOdd | PagesHeaderFooter | '' | 奇数页页脚内容 |
footerEven | PagesHeaderFooter | '' | 偶数页页脚内容 |
headerFooterExtensions | Extension[] | StarterKit | 页眉/页脚编辑器使用的扩展 |
onDblClickHeaderFooterPreventClose | (event: MouseEvent) => boolean | undefined | 双击外部时阻止关闭页眉/页脚编辑器的回调 |
onDblClickHeaderPreventClose | (event: MouseEvent) => boolean | undefined | 双击外部时阻止关闭页眉编辑器的回调 |
onDblClickFooterPreventClose | (event: MouseEvent) => boolean | undefined | 双击外部时阻止关闭页脚编辑器的回调 |
accentColor | string | '#6366f1' | 页眉/页脚编辑器覆盖层的强调色 |
headerAccentColor | string | accentColor 值 | 页眉编辑器覆盖层的强调色 |
footerAccentColor | string | accentColor 值 | 页脚编辑器覆盖层的强调色 |
页眉和页脚编辑不支持协作同步。它们分别是独立的 Tiptap 实例,与主文档的协作会话无关。
| Command | Parameters | Description |
|---|---|---|
setHeader | header: PagesHeaderFooter | 设置默认页眉内容 |
setFooter | footer: PagesHeaderFooter | 设置默认页脚内容 |
setHeaderTopMargin | margin: number | 设置页眉顶部边距(像素)。拒绝负值和 NaN。 |
resetHeaderTopMargin | none | 清除页眉顶部边距覆盖;回退到当前格式的默认值(格式顶部边距的 50%)。 |
setFooterBottomMargin | margin: number | 设置页脚底部边距(像素)。拒绝负值和 NaN。 |
resetFooterBottomMargin | none | 清除页脚底部边距覆盖;回退到当前格式的默认值(格式底部边距的 50%)。 |
setDifferentFirstPage | enabled: boolean | 切换首页不同(页眉 + 页脚) |
setHeaderFirstPage | header: PagesHeaderFooter | 设置首页页眉内容 |
setFooterFirstPage | footer: PagesHeaderFooter | 设置首页页脚内容 |
setDifferentOddEven | enabled: boolean | 切换奇偶页不同(页眉 + 页脚) |
setHeaderOdd | header: PagesHeaderFooter | 设置奇数页页眉内容 |
setHeaderEven | header: PagesHeaderFooter | 设置偶数页页眉内容 |
setFooterOdd | footer: PagesHeaderFooter | 设置奇数页页脚内容 |
setFooterEven | footer: PagesHeaderFooter | 设置偶数页页脚内容 |
openHeaderEditor | { pageNumber: number } | 为特定页面打开页眉编辑器(从 1 开始编号) |
openFooterEditor | { pageNumber: number } | 为特定页面打开页脚编辑器(从 1 开始编号) |
closeHeaderFooterEditors | none | 关闭当前打开的页眉/页脚编辑器 |
closeHeaderEditor | none | 如果页眉编辑器已打开,则将其关闭 |
closeFooterEditor | none | 如果页脚编辑器已打开,则将其关闭 |
setAccentColor | color: string | 为两个编辑器覆盖层设置强调色 |
setHeaderAccentColor | color: string | 为页眉编辑器覆盖层设置强调色 |
setFooterAccentColor | color: string | 为页脚编辑器覆盖层设置强调色 |