使用页眉和页脚扩展您的 DOCX 导出功能
@tiptap-pro/extension-export-docx 扩展内置支持自定义导出文档的页眉和页脚。
添加此扩展后,您可以通过以下附加属性配置您的 ExportDocx:
页眉配置
headers 对象允许您自定义导出 DOCX 文档的页眉:
| 属性 | 类型 | 描述 |
|---|---|---|
evenAndOddHeaders | boolean | 是否为奇数页和偶数页使用不同的页眉 |
differentFirstPage | boolean | 是否首页使用不同的页眉。当为 true 时,首页使用 first 值,取代 default。 |
default | Header | (() => Promise<Header>) | 每页的标准默认页眉,或当激活 evenAndOddHeaders 选项时用于奇数页的页眉。可以是 Header 对象或返回 Header 的异步函数 |
first | Header | (() => Promise<Header>) | 首页的页眉,仅在 differentFirstPage 设置为 true 时使用。可以是 Header 对象或返回 Header 的异步函数 |
even | Header | (() => Promise<Header>) | 当激活 evenAndOddHeaders 选项时偶数页的页眉。可以是 Header 对象或返回 Header 的异步函数 |
页脚配置
footers 对象允许您自定义导出 DOCX 文档的页脚:
| 属性 | 类型 | 描述 |
|---|---|---|
evenAndOddFooters | boolean | 是否为奇数页和偶数页使用不同的页脚 |
differentFirstPage | boolean | 是否首页使用不同的页脚。当为 true 时,首页使用 first 值,取代 default。 |
default | Footer | (() => Promise<Footer>) | 每页的标准默认页脚,或当激活 evenAndOddFooters 选项时用于奇数页的页脚。可以是 Footer 对象或返回 Footer 的异步函数 |
first | Footer | (() => Promise<Footer>) | 首页的页脚,仅在 differentFirstPage 设置为 true 时使用。可以是 Footer 对象或返回 Footer 的异步函数 |
even | Footer | (() => Promise<Footer>) | 当激活 evenAndOddFooters 选项时偶数页的页脚。可以是 Footer 对象或返回 Footer 的异步函数 |
使用示例
扩展配置
通过 ExportDocx.configure() 方法配置页眉和页脚。您可以直接使用 Docx 命名空间中的对象,如 Docx.Header 和 Docx.Footer,以获得完全自定义的控制:
关于页眉和页脚对象的说明
Header、Footer、Paragraph 和 TextRun 对象通过 @tiptap-pro/extension-export-docx 导出的 Docx 命名空间访问。您可以使用任何有效内容自定义它们,包括段落、文本运行等,只要是在 DOCX 页眉或页脚中可用的标准元素。
import { ExportDocx, Docx } from '@tiptap-pro/extension-export-docx'
const editor = new Editor({
extensions: [
// ... 其他扩展
ExportDocx.configure({
onCompleteExport: result => {
// 处理导出结果
const blob = new Blob([result], {
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
})
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = 'export.docx'
a.click()
URL.revokeObjectURL(url)
},
headers: {
evenAndOddHeaders: true,
default: new Docx.Header({
children: [
new Docx.Paragraph({
children: [
new Docx.TextRun({
text: "默认页眉",
bold: true,
}),
],
}),
],
}),
first: new Docx.Header({
children: [
new Docx.Paragraph({
children: [
new Docx.TextRun({
text: "首页页眉",
size: 24,
bold: true,
}),
],
}),
],
}),
even: new Docx.Header({
children: [
new Docx.Paragraph({
children: [
new Docx.TextRun({
text: "偶数页页眉",
}),
],
}),
],
}),
},
footers: {
default: new Docx.Footer({
children: [
new Docx.Paragraph({
children: [
new Docx.TextRun({
text: "默认页脚",
}),
],
}),
],
}),
},
}),
],
})
// 触发导出
editor.commands.exportDocx()使用辅助函数(替代方法)
为方便处理 Tiptap 风格内容,您可以使用 convertHeader 和 convertFooter 辅助函数,它们会自动处理 mark 转换、链接和其他 Tiptap 特性。
import { ExportDocx } from '@tiptap-pro/extension-export-docx'
const editor = new Editor({
extensions: [
// ... 其他扩展
ExportDocx.configure({
onCompleteExport: result => {
// 处理导出结果
const blob = new Blob([result], {
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
})
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = 'export.docx'
a.click()
URL.revokeObjectURL(url)
},
headers: {
evenAndOddHeaders: true,
default: async () =>
convertHeader({
node: {
type: 'paragraph',
content: [
{
type: 'text',
text: '页眉',
marks: [{ type: 'textStyle', attrs: { color: 'red' } }],
},
],
},
}),
first: async () =>
convertHeader({
node: {
type: 'paragraph',
content: [
{
type: 'text',
text: '首页页眉',
marks: [{ type: 'bold' }],
},
],
},
}),
even: async () =>
convertHeader({
node: {
type: 'paragraph',
content: [
{
type: 'text',
text: '偶数页页眉',
marks: [{ type: 'textStyle', attrs: { color: 'blue' } }],
},
],
},
}),
},
footers: {
default: async () =>
convertFooter({
node: {
type: 'paragraph',
content: [
{
type: 'text',
text: '页脚',
marks: [{ type: 'textStyle', attrs: { color: 'red' } }],
},
],
},
}),
first: async () =>
convertFooter({
node: {
type: 'paragraph',
content: [
{
type: 'text',
text: '首页页脚',
marks: [{ type: 'bold' }],
},
],
},
}),
even: async () =>
convertFooter({
node: {
type: 'paragraph',
content: [
{
type: 'text',
text: '偶数页页脚',
marks: [{ type: 'textStyle', attrs: { color: 'blue' } }],
},
],
},
}),
},
}),
],
})
// 触发导出
editor.commands.exportDocx()convertHeader 和 convertFooter 辅助函数期望传入一个 Tiptap 节点,这里为一个段落,它们会自动处理:
- Mark 转换(加粗、斜体、颜色等)
- 链接转换
- 文本样式和格式
- 复杂的 Tiptap 节点结构
这使得使用熟悉的 Tiptap JSON 结构创建丰富的页眉和页脚变得更简单。
重要提示!
请注意,如果您使用 convertHeader 和 convertFooter 辅助函数,则需要使用异步箭头函数,因为这些辅助工具函数内部调用了异步函数 convertParagraph,其异步性是为了实现图像的解析处理。
高级示例
使用异步函数的动态页眉和页脚
您也可以提供返回 Header 或 Footer 对象的异步函数。这对于动态内容生成非常有用,例如获取用户数据、格式化当前日期或处理图像。
异步函数的优点
使用异步函数定义页眉和页脚,可以实现强大的用例,例如:从 API 获取用户信息,包含当前时间戳或动态日期,处理和嵌入图像,计算文档统计信息,从数据库或外部服务检索数据。
import { ExportDocx, Docx } from '@tiptap-pro/extension-export-docx'
// 获取用户信息的函数
async function getCurrentUser() {
// 这里可以是 API 调用、数据库查询等
return {
name: 'John Doe',
department: '工程部',
email: 'john.doe@company.com'
}
}
const editor = new Editor({
extensions: [
// ... 其他扩展
ExportDocx.configure({
onCompleteExport: result => {
// 处理导出结果
const blob = new Blob([result], {
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
})
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = 'export.docx'
a.click()
URL.revokeObjectURL(url)
},
headers: {
default: async () => {
const user = await getCurrentUser()
const currentDate = new Date().toLocaleDateString()
return new Docx.Header({
children: [
new Docx.Paragraph({
children: [
new Docx.TextRun({
text: `文档由 ${user.name} (${user.department}) 于 ${currentDate} 准备`,
size: 20,
}),
],
}),
],
})
},
},
footers: {
default: async () => {
const user = await getCurrentUser()
return new Docx.Footer({
children: [
new Docx.Paragraph({
children: [
new Docx.TextRun({
text: `联系方式: ${user.email} | 生成时间: ${new Date().toISOString()}`,
size: 16,
italics: true,
}),
],
}),
],
})
},
},
}),
],
})
// 触发导出
editor.commands.exportDocx()异步函数的生命周期
提供的异步函数将在整个文档转换完成后、构建文档之前调用。
静态和动态页眉/页脚混合使用
您可以将静态页眉/页脚和动态页眉/页脚结合使用于不同页面:
import { ExportDocx, Docx } from '@tiptap-pro/extension-export-docx'
const editor = new Editor({
extensions: [
// ... 其他扩展
ExportDocx.configure({
onCompleteExport: result => {
// 处理导出结果
const blob = new Blob([result], {
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
})
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = 'export.docx'
a.click()
URL.revokeObjectURL(url)
},
headers: {
// 静态首页页眉
first: new Docx.Header({
children: [
new Docx.Paragraph({
children: [
new Docx.TextRun({
text: "公司机密报告",
size: 24,
bold: true,
}),
],
}),
],
}),
// 后续页的动态默认页眉
default: async () => {
const reportData = await fetchReportMetadata()
return new Docx.Header({
children: [
new Docx.Paragraph({
children: [
new Docx.TextRun({
text: `${reportData.title} - 生成于 ${reportData.timestamp}`,
size: 18,
}),
],
}),
],
})
},
},
footers: {
default: async () => {
const stats = await getDocumentStats()
return new Docx.Footer({
children: [
new Docx.Paragraph({
children: [
new Docx.TextRun({
text: `页数: ${stats.pages} | 字数: ${stats.words}`,
size: 14,
}),
],
}),
],
})
},
},
}),
],
})
async function fetchReportMetadata() {
// 模拟 API 调用
return {
title: '季度业务报告',
timestamp: new Date().toLocaleDateString()
}
}
async function getDocumentStats() {
// 模拟文档分析
return {
pages: 10,
words: 2500
}
}页码
导出支持两种方式来生成实时页码字段:
- 普通文本页眉和页脚中的
{page}/{total}标记——可与从 Pages 扩展 中提取的内容自动配合使用,也可与传递给headers/footers的普通文本字符串一起使用。导出会将每个标记转换为实时的PAGE/NUMPAGES字段,因此 Word 会渲染出实际的当前页码和总页数。 Docx.PageNumberAPI——当你使用new Docx.Header()/new Docx.Footer()手动构建页眉/页脚内容时,或者当你需要在字段周围加入超出纯标记之外的格式时,这是必需的。convertHeader/convertFooter辅助函数目前不会处理Docx.PageNumber引用——请直接传入Docx实例。
通过 {page} / {total} 标记显示页码
任何普通文本页眉或页脚(包括通过 Pages 扩展自动提取的内容,例如 editor.commands.setHeader('Page {page} of {total}'))在导出的 DOCX 中都会渲染为实时页码字段:
import { ExportDocx } from '@tiptap-pro/extension-export-docx'
const editor = new Editor({
extensions: [
// ... 其他扩展
ExportDocx.configure({
onCompleteExport: result => {
const blob = new Blob([result], {
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
})
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = 'export.docx'
a.click()
URL.revokeObjectURL(url)
},
// 普通文本页眉/页脚——`{page}` 和 `{total}` 会在导出时转换为
// 实时的 `PAGE` / `NUMPAGES` 字段。
headers: {
default: 'Confidential — Page {page} of {total}',
},
footers: {
default: 'Page {page} of {total}',
},
}),
],
])
// 触发导出
editor.commands.exportDocx()当页眉/页脚是从 Pages 扩展中自动提取时,也会自动执行相同的转换,因此通过 editor.commands.setFooter('Page {page} of {total}') 设置的页脚在导出时会包含实时页码,无需任何额外配置。
基本页码
import { ExportDocx, Docx } from '@tiptap-pro/extension-export-docx'
const editor = new Editor({
extensions: [
// ... 其他扩展
ExportDocx.configure({
onCompleteExport: result => {
// 处理导出结果
const blob = new Blob([result], {
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
})
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = 'export.docx'
a.click()
URL.revokeObjectURL(url)
},
headers: {
default: new Docx.Header({
children: [
new Docx.Paragraph({
children: [
new Docx.TextRun({
children: ['第 ', Docx.PageNumber.CURRENT, ' 页'],
}),
],
}),
],
}),
},
footers: {
default: new Docx.Footer({
children: [
new Docx.Paragraph({
children: [
new Docx.TextRun({
children: ['第 ', Docx.PageNumber.CURRENT, ' 页'],
}),
],
}),
],
}),
},
}),
],
])
// 触发导出
editor.commands.exportDocx()显示当前页码和总页数
import { ExportDocx, Docx } from '@tiptap-pro/extension-export-docx'
const editor = new Editor({
extensions: [
// ... 其他扩展
ExportDocx.configure({
onCompleteExport: result => {
// 处理导出结果
const blob = new Blob([result], {
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
})
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = 'export.docx'
a.click()
URL.revokeObjectURL(url)
},
footers: {
default: new Docx.Footer({
children: [
new Docx.Paragraph({
children: [
new Docx.TextRun({
children: [
'第 ',
Docx.PageNumber.CURRENT,
' 页,共 ',
Docx.PageNumber.TOTAL_PAGES,
' 页',
],
}),
],
}),
],
}),
},
}),
],
])
// 触发导出
editor.commands.exportDocx()