在编辑器中导入 .docx
使用 @tiptap-pro/extension-import-docx 编辑器扩展将 .docx 文件转换为 Tiptap JSON 非常简单,该扩展可直接集成到您的 Tiptap 编辑器中。
您也可以通过 REST API 导入。这两种方式使用相同的转换服务,并生成完全相同的内容输出。主要区别如下:
| 编辑器扩展 | REST API | |
|---|---|---|
| 运行位置 | 您的编辑器(客户端) | Tiptap 云端(服务器端) |
| 脚注和尾注 | 不会在回调中暴露 | 在响应中返回 |
| 页眉和页脚 | 通过 Pages 扩展自动应用 | 作为单独的数据字段返回 |
| 未知内容 | 按您的编辑器 schema 重写 | 原始 JSON,不进行 schema 过滤 |
如果您需要服务器端处理、访问脚注或尾注数据,或需要未过滤的原始 JSON,请选择 REST API。
安装 DOCX Import 扩展
Conversion 扩展发布在 Tiptap 的私有 npm 注册表中。请按照 私有注册表指南 集成这些扩展。
安装 Tiptap Import 扩展包:
npm i @tiptap-pro/extension-import-docx请确保您的编辑器包含处理 DOCX 内容所需的所有 Tiptap 扩展。完整的扩展列表、配置以及各自处理的内容,请参见 ConvertKit 页面。
所需扩展
安装 ConvertKit — 它捆绑了导入器所需的所有扩展:标准 nodes 和 marks、用于颜色/字体/大小/行高/背景的 TextStyleKit、预配置为 ['paragraph', 'heading'] 的 TextAlign、带有 multicolor: true 的 Highlight、带有 DOCX 裁剪属性的 Image、用于 DOCX 感知表格的 ConvertTableKit,以及 PageBreak:
import { ConvertKit } from '@tiptap-pro/extension-convert-kit'
new Editor({ extensions: [ConvertKit] })如果您需要将捆绑扩展中的某一个替换为自己的实现,可以通过 ConvertKit.configure({ slot: false }) 禁用任意单个 slot(例如,与 Pages extension 配合时,设置 table: false,并改为注册来自 @tiptap-pro/extension-pages-tablekit 的 TableKit)。完整的 slot 列表请参见 ConvertKit reference。
配置
将 Import 扩展添加到您的编辑器设置中。
import { ImportDocx } from '@tiptap-pro/extension-import-docx'
const editor = new Editor({
extensions: [
// 其他扩展 ...
ImportDocx.configure({
appId: 'your-app-id', // 您的 Convert App ID(请参见 Tiptap Cloud 设置)
token: 'your-jwt', // 用于身份验证的 JWT(请参见身份验证文档)
imageUploadConfig: {
url: 'https://your-image-upload-endpoint.com',
},
}),
// 其他扩展 ...
],
// 其他编辑器设置 ...
})| 属性 | 描述 |
|---|---|
appId | 您的 Tiptap Convert 应用的 ID(可在您 Tiptap 账户的 conversion settings 中找到) |
token | 由您的服务器为 Convert 服务生成的 JWT 身份验证令牌。(有关获取和使用这些凭据的详细信息,请参见 Authentication guide。) |
imageUploadConfig | 用于导入期间图片上传的结构化配置。详情请参见 图片上传配置。 |
verbose | 一个 string | number 配置属性,可帮助您控制导入过程中的诊断输出级别。这对于调试或更深入了解转换过程中发生的情况尤其有用。更多信息请参见 详细输出。 |
图片上传配置
imageUploadConfig 选项让您可以配置转换服务如何将 DOCX 文件中找到的图片上传到您的服务器。这取代了已弃用的 imageUploadCallbackUrl 字符串选项,并使您能够完全控制请求头、HTTP 方法和查询参数。
ImportDocx.configure({
appId: 'your-app-id',
token: 'your-jwt',
imageUploadConfig: {
url: 'https://your-server.com/upload-image',
headers: {
Authorization: 'Bearer your-upload-token',
'X-Custom-Header': 'custom-value',
},
method: 'PUT',
queryParams: {
bucket: 'images',
folder: 'docx-imports',
},
},
})| 属性 | 类型 | 必需 | 描述 |
|---|---|---|---|
url | string | 是 | 接收上传图片的端点 URL。必须是有效的 HTTP 或 HTTPS URL。 |
headers | HeadersInit | 否 | 转换服务在上传图片时转发的额外 HTTP 请求头。适用于身份验证(例如 Bearer token、API key)。接受普通对象、Headers 实例或键值对数组。 |
method | string | 否 | 用于上传图片的 HTTP 方法。若省略,则默认为转换服务自身的默认值。 |
queryParams | Record<string, string> | 否 | 由转换服务附加到上传 URL 的查询参数。 |
从 imageUploadCallbackUrl 迁移
imageUploadCallbackUrl 选项已弃用。请改用 imageUploadConfig:
// 之前(已弃用)
ImportDocx.configure({
imageUploadCallbackUrl: 'https://your-server.com/upload-image',
})
// 之后
ImportDocx.configure({
imageUploadConfig: {
url: 'https://your-server.com/upload-image',
},
})如果同时提供了 imageUploadConfig 和 imageUploadCallbackUrl,则以 imageUploadConfig 为准,并会输出一条控制台警告。
导入 DOCX 文件
配置好扩展后,您就可以导入用户选择的 DOCX 文件。
基础导入
最简单的方法是直接将文件传递给 importDocx 命令。在这里,它会用转换后的内容替换当前编辑器内容,并聚焦编辑器:
editor.chain().focus().importDocx({ file }).run()在大多数情况下,这一行代码就足以让用户导入 .docx 文件。该扩展会负责将文件发送到转换端点、获取转换后的 Tiptap JSON,并将其插入编辑器。
导入处理
为了在导入过程结束后拥有更多控制权,您可以使用 onImport 回调来处理转换结果。此回调会提供转换后的内容、发生的任何错误,以及一个名为 setEditorContent 的函数,用于将 context.content 中的内容插入编辑器。如果您不提供 onImport 回调,扩展会自动将内容插入编辑器,但您将无法处理错误或加载状态等其他事项。
editor
.chain()
.importDocx({
file,
onImport(context) {
// 判别联合:先处理失败分支。
if (context.error) {
showErrorToast({ message: context.error.message })
return
}
const { setEditorContent, content } = context
// 您可以在插入之前修改内容。
content.content?.push({
type: 'paragraph',
content: [{ type: 'text', text: 'Hello!' }],
})
// 例如,您可以更改应用程序的加载状态
isLoading = false
// 插入(可能已修改的)内容。将其传递给回调中提供的
// `setEditorContent` 函数,它会正确处理相关联动
//(包括在注册了 Pages 时自动应用页眉/页脚)。
setEditorContent(content)
},
})
.focus()
.run()在上面的示例中,我们控制的操作包括:
| 操作 | 描述 |
|---|---|
| 错误处理 | 如果转换失败,您可以显示提示消息或记录错误。 |
| 内容修改 | 您可以根据需要插入额外节点、移除某些节点,或以其他方式调整转换后的 Tiptap JSON。 |
| 编辑器插入 | 如果您想依赖扩展的默认插入行为(替换编辑器内容),可以调用回调中提供的 setEditorContent() 函数。如果您自行修改了内容,则必须手动使用 editor.commands.setContent(content) 进行设置。 |
页眉与页脚
当导入包含页眉和页脚的 .docx 文件时,如果安装了 Pages 扩展,导入扩展会自动检测并应用它们。没有 Pages,页眉/页脚数据仍会返回到你的 onImport 回调(见下面的手动处理)中,但没有地方渲染——请安装 Pages 以便它们在编辑器中可见。
自动处理
如果你的编辑器中注册了 Pages 扩展,在调用 setEditorContent() 时,页眉和页脚会自动应用:
editor
.chain()
.importDocx({
file,
onImport(context) {
if (context.error) {
console.error(context.error)
return
}
// 页眉和页脚会与正文内容一起自动应用
context.setEditorContent()
},
})
.focus()
.run()如果没有安装 Pages 扩展,页眉和页脚数据仍可在 onImport 回调中获取,但不会自动应用到编辑器中。
手动处理
onImport 回调提供所有页眉和页脚字段,供手动处理:
editor
.chain()
.importDocx({
file,
onImport(context) {
if (context.error) {
console.error(context.error)
return
}
// 直接访问页眉/页脚数据
const {
header, // 默认页眉(Tiptap JSON 或 null)
footer, // 默认页脚(Tiptap JSON 或 null)
headerFirstPage, // 首页页眉(Tiptap JSON 或 null)
footerFirstPage, // 首页页脚(Tiptap JSON 或 null)
headerOdd, // 奇数页页眉(Tiptap JSON 或 null)
footerOdd, // 奇数页页脚(Tiptap JSON 或 null)
headerEven, // 偶数页页眉(Tiptap JSON 或 null)
footerEven, // 偶数页页脚(Tiptap JSON 或 null)
} = context
// 设置正文内容,不自动应用页眉/页脚
editor.commands.setContent(context.content)
// 通过 Pages 扩展命令手动应用页眉和页脚
if (header) editor.commands.setHeader(header)
if (footer) editor.commands.setFooter(footer)
if (headerFirstPage || footerFirstPage) {
editor.commands.setDifferentFirstPage(true)
if (headerFirstPage) editor.commands.setHeaderFirstPage(headerFirstPage)
if (footerFirstPage) editor.commands.setFooterFirstPage(footerFirstPage)
}
if (headerOdd || headerEven || footerOdd || footerEven) {
editor.commands.setDifferentOddEven(true)
if (headerOdd) editor.commands.setHeaderOdd(headerOdd)
if (headerEven) editor.commands.setHeaderEven(headerEven)
if (footerOdd) editor.commands.setFooterOdd(footerOdd)
if (footerEven) editor.commands.setFooterEven(footerEven)
}
},
})
.focus()
.run()可用字段
| 字段 | 描述 |
|---|---|
header | 默认页眉内容,格式为 Tiptap JSON,或 null |
footer | 默认页脚内容,格式为 Tiptap JSON,或 null |
headerFirstPage | 首页页眉(当 Word 中启用“首页不同”时),或 null |
footerFirstPage | 首页页脚(当 Word 中启用“首页不同”时),或 null |
headerOdd | 奇数页页眉(当 Word 中启用“奇偶页不同”时),或 null |
footerOdd | 奇数页页脚(当 Word 中启用“奇偶页不同”时),或 null |
headerEven | 偶数页页眉(当 Word 中启用“奇偶页不同”时),或 null |
footerEven | 偶数页页脚(当 Word 中启用“奇偶页不同”时),或 null |
脚注与尾注
脚注和尾注在扩展的 onImport 回调中不可用
REST API 会在其响应中返回脚注和尾注数据(作为 footnotes 和 endnotes
字段),但编辑器扩展目前不会在 onImport 回调的
ImportContext 中提供这些内容。如果你需要脚注/尾注数据,请直接使用 REST
API,并从响应中提取 footnotes 和 endnotes 字段。
导入 API 可以从 DOCX 文件中提取脚注和尾注内容。文档正文中的内联引用表示为 footnoteReference 和 endnoteReference 节点,每个节点都有一个 noteId 属性。不过,这些节点需要一个自定义扩展才能在编辑器中渲染。
详细输出
DOCX 导入扩展提供了一个 verbose 配置属性,用于帮助你控制导入过程中诊断输出的级别。这对于调试或更深入地了解转换过程中发生了什么特别有用。
verbose 属性是一个位掩码数字,用于确定输出哪些类型的日志消息。该扩展使用以下级别:
| 值 | 级别 | 描述 |
|---|---|---|
| 1 | log | 一般信息日志 |
| 2 | warn | 警告 |
| 4 | error | 错误 |
Verbose 位掩码
你可以将各级别的值相加来组合它们。例如,verbose: 3 将同时启用
log(1)和 warn(2)消息。
详细输出除了 data 属性之外,还会再提供一个名为 logs 的属性,其中包含 info、warn 和 error 属性;它们各自都是数组,包含与对应详细级别相关的全部信息。
{
"data": {
"content": {
// Tiptap JSON
}
},
"logs": {
"info": [],
"warn": [
{
"message": "在媒体文件中未找到图像文件",
"fileName": "image1.gif",
"availableMediaFiles": []
}
],
"error": [
{
"message": "图像上传失败:一般错误",
"fileName": "image1.gif",
"url": "https://your-image-upload-endpoint.com",
"error": "无法连接。计算机是否可以访问该 url?",
"context": "uploadImage general error"
}
]
}
}支持与限制
有关管道各阶段支持哪些文档特性的详细说明,请参见支持的特性矩阵。