公共 API 约定
本文说明 office-sdk 的入口、实例生命周期、通用类型、事件模型和颜色参数。各产品组件方法见 DOCX、XLSX、PPTX。
入口
公开入口位于部署后的静态资源目录。以下示例假设 lib/ 被映射为 /office-sdk/:
<link rel="stylesheet" href="/office-sdk/style.css" />
<script type="module">
import "/office-sdk/preload.js";
import OfficeSdk from "/office-sdk/UI.js";
</script>
preload 是可选入口,用来提前请求关键 JavaScript 运行时、worker 入口和文档应用模块。它不会预取全部 .wasm 二进制或所有语言部署文件;其余资源会在首次打开文档时按需加载。文档详情页、预览页等打开文档的页面推荐引入;普通列表页可以等用户点击打开后再加载。
运行环境前置条件
打开文档依赖以下浏览器能力:
- ES Module 和动态
import() - Web Worker、SharedWorker 和 WebAssembly
- IndexedDB
Promise、Blob、ArrayBuffer
SharedWorker 和 IndexedDB 是当前打开流程的必需能力,缺少任意一项都会导致 render() 失败。还应确认浏览器隐私策略、无痕模式、企业安全策略或 WebView 容器没有禁用这些能力。
SDK worker 默认通过 new SharedWorker(workerUrl) 直接创建,因此建议将完整 lib/ 与宿主页面同源部署。仅配置 CORS 不能保证跨域 SharedWorker 可用,详见 CSP 部署指南。
configureLicense
configureLicense(...) 用于配置福昕 Office SDK 授权信息。调用 openfile(...) 前必须先调用一次,否则 SDK 无法完成 license 校验。
签名:
function configureLicense(options: OfficeSdkLicenseOptions): void;
type OfficeSdkLicenseOptions = {
licenseKey: string;
licenseSN: string;
enginePath?: string;
};
示例:
OfficeSdk.configureLicense({
licenseSN: "<SN=... 后面的值>",
licenseKey: "<Sign=... 后面的值>"
});
| 字段 | 必填 | 说明 |
|---|---|---|
licenseSN | 是 | 授权信息中 SN= 后面的值。 |
licenseKey | 是 | 授权信息中 Sign= 后面的值。 |
enginePath | 否 | 授权引擎资源目录,默认使用 SDK 产物中的 license/gsdk/。如果部署时调整了目录结构,需要显式传入该目录 URL,并确保以 / 结尾。 |
部署时必须保留 license/OfficeSDK.js、license/gsdk/officesdk_license.js、license/gsdk/officesdk_license.wasm 和 license/gsdk/officesdk_license.wasm.brotli。SDK 会自动读取当前页面 window.location.hostname 作为授权校验域名,不需要也不支持手动传入 domains。授权会同时校验当前域名和文档模块权限:docx 对应 Word,xlsx 对应 Excel,pptx 对应 PPT。
openfile
签名:
function openfile(
fileData: OfficeWidgetFileData,
options?: OfficeSdkOpenOptions | null
): Promise<OfficeWidgetOpenResult>;
最小示例:
OfficeSdk.configureLicense({
licenseSN: "<SN=... 后面的值>",
licenseKey: "<Sign=... 后面的值>"
});
const { docType, widget } = await OfficeSdk.openfile({
docId: `local-${Date.now()}`,
fileName: file.name,
file
});
const app = await widget.mount("#office-container").render();
带用户、UI 和打开模式:
const { widget } = await OfficeSdk.openfile(
{
docId: "contract-001",
fileName: "contract.docx",
file
},
{
userData: {
userId: "u1",
clientId: "web-001",
nickName: "Alice",
avatar: "https://example.com/avatar.png",
opts: { color: "#2F80ED" }
},
uiOptions: {
showTopBar: true,
showBottomBar: true
},
mode: {
readOnly: false,
lang: "zh-CN"
}
}
);
const app = await widget.mount(document.getElementById("office-container")!).render();
createfile
创建新的 Office 文件:
type OfficeWidgetCreateDocType = 1 | 2 | 3;
function createfile(
docType: OfficeWidgetCreateDocType,
options?: OfficeSdkOpenOptions | null
): Promise<OfficeWidgetOpenResult>;
参数说明:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
docType | OfficeWidgetCreateDocType | 是 | 需要创建的 Office 文档类型标识。 |
options | OfficeSdkOpenOptions | null | 否 | 用户信息、界面配置、语言及只读模式等创建选项。 |
文档类型标识:
| 标识值 | 文档类别 | 文件格式 | 扩展名 |
|---|---|---|---|
1 | Word 文档 | Office Open XML Wordprocessing Document | .docx |
2 | Excel 工作簿 | Office Open XML Spreadsheet | .xlsx |
3 | PowerPoint 演示文稿 | Office Open XML Presentation | .pptx |
const { widget } = await OfficeSdk.createfile(1, {
mode: { lang: "zh-CN" }
});
const app = await widget.mount("#office-container").render();
每次调用都会生成独立的 docId。options、返回值和 widget 生命周期与 openfile(...) 相同。
fileData
type OfficeWidgetFileData = {
docId?: string;
fileName: string;
file: File | Blob | ArrayBuffer | ArrayBufferView | string;
};
| 字段 | 必填 | 说明 |
|---|---|---|
docId | 否 | 文档 ID,用于缓存和实例生命周期标识。不传时 SDK 会根据文件信息生成。 |
fileName | 是 | 文件名,用扩展名识别文档类型。当前 openfile(...) 支持 doc、docx、xls、xlsx、pptx。 |
file | 是 | 文件内容或浏览器可访问的文件地址。支持 File/Blob、ArrayBuffer、TypedArray,以及 blob:、data:、http(s):、file:、以 / 开头的绝对路径、以 ./ 或 ../ 开头的相对路径。普通字符串(如 files/a.docx)不会被识别为 URL。不能传 null 或 undefined。 |
URL 打开示例:
const { widget } = await OfficeSdk.openfile({
docId: "report-xlsx",
fileName: "report.xlsx",
file: "https://example.com/files/report.xlsx"
});
await widget.mount("#office-container").render();
options
type OfficeSdkOpenOptions = {
userData?: OfficeSdkUser | null;
uiOptions?: OfficeSdkUiOptions;
mode?: OfficeSdkOpenMode;
};
type OfficeSdkUser = {
clientId?: string;
userId?: string;
nickName?: string;
avatar?: string;
opts?: Record<string, unknown>;
};
type OfficeSdkUiOptions = {
showTopBar?: boolean;
showBottomBar?: boolean;
};
type OfficeSdkOpenMode = {
readOnly?: boolean;
lang?: "zh-CN" | "en-US" | (string & {});
};
| 字段 | 说明 |
|---|---|
userData | 当前用户信息。不传时会补默认用户:nickName: "Local User"、avatar: ""、opts.color: "#2F80ED"。 |
uiOptions.showTopBar | 是否显示顶部工具栏。不传时使用 SDK 默认布局。 |
uiOptions.showBottomBar | 是否显示底部状态栏。不传时使用 SDK 默认布局。 |
mode.readOnly | 是否以只读模式打开。true 时 SDK 会在文档加载后设置只读状态。 |
mode.lang | 语言,例如 zh-CN、en-US。不传时依次回退到 window.lang、<html lang>、浏览器语言。 |
返回值
type OfficeWidgetOpenResult = {
docType: number;
widget: OfficeWidgetApp;
};
type OfficeWidgetApp = {
mount(target: string | HTMLElement): OfficeWidgetApp;
render(): Promise<OfficeRenderedApp>;
close(): Promise<void>;
readonly docId: string;
readonly docType: number;
readonly docTypeName: string | null;
};
docType 取值:
| 值 | 类型 |
|---|---|
1 | Word / DOCX |
2 | Excel / XLSX |
3 | PowerPoint / PPTX |
widget.mount(target) 支持:
widget.mount("#office-container");
widget.mount("office-container");
widget.mount(document.getElementById("office-container")!);
字符串以 #、. 或 [ 开头时按 CSS 选择器处理;其他字符串按 DOM id 处理。不要传入以标签名或复杂组合开头的选择器,例如 "main .office-container"。
widget.render() 会等待文档 ready,成功后返回当前文档的公开 app API 对象。后续 API 均按 app.Component.method(...) 调用。
挂载成功后,widget.docTypeName 为 "WORD"、"EXCEL" 或 "PPT";挂载完成前为 null。
widget 生命周期
widget.close() 用于关闭当前文档实例并释放运行时资源。
| 方法 | 签名 | 说明 |
|---|---|---|
close | close(): Promise<void> | 关闭当前 widget,释放文档实例、内部事件监听和挂载资源。 |
建议在以下场景调用:
- 页面路由切换或业务组件卸载前
- 用户重新打开另一个文件前
- 不再需要当前文档 viewer 时
close() 返回 Promise<void>,调用方应 await 完成后再复用同一个容器打开新文档。
关闭示例:
let currentWidget: Awaited<ReturnType<typeof OfficeSdk.openfile>>["widget"] | null = null;
async function openFile(file: File) {
await currentWidget?.close();
const { widget } = await OfficeSdk.openfile({
docId: `local-${Date.now()}`,
fileName: file.name,
file
});
const app = await widget.mount("#office-container").render();
currentWidget = widget;
return app;
}
错误处理与排查
openfile(...) 和 widget.render() 是异步方法;widget.mount(...) 是同步链式调用。建议使用 try...catch 包住打开与渲染流程,并把原始错误记录到日志中。
try {
const { widget } = await OfficeSdk.openfile({
fileName: file.name,
file
});
await widget.mount("#office-container").render();
} catch (error) {
console.error("[office-sdk] open failed", error);
}
外部示例中的 shared/officeWidget.js 提供了 classifyError(error),用于把常见打开失败转换成适合示例页面展示的 kind、title 和 detail。它是示例 helper,不是 UI.js 的公开导出,也不是 SDK API 合约;业务工程可以参考其实现方式,但不应依赖这些 kind 作为 SDK 返回值。
如需查看示例页面的错误展示分类,请参考外部分发包中的 shared/README.md。API Reference 只约定 SDK 方法、参数、返回值和实例生命周期。
app 组件
render() 返回的 app 是当前文档类型的公开 API 对象。常见组件:
| 文档类型 | 组件 |
|---|---|
| DOCX | Document、Paragraph、Table、Selection、Finder、UndoRedo |
| XLSX | Document、Workbook、Worksheet、Selection、Finder、Cursor、UndoRedo |
| PPTX | Document、Paragraph、TextBox、Player、Selection、Viewer、UndoRedo |
示例:
app.Document.setZoom(120);
app.UndoRedo.undo();
事件模型
公开 API 使用组件级事件入口。
组件事件
Document、Selection、Finder、UndoRedo 等组件继承通用事件方法:
type EventStatus =
| "ok"
| "no such event"
| "duplicated event callback"
| "no such callback";
component.addEventListener(eventName, handler): EventStatus;
component.removeEventListener(eventName, handler): EventStatus;
使用方式:
function onSelectionChange(info) {
console.log(info);
}
const status = app.Selection.addEventListener("SELECTION_CHANGE", onSelectionChange);
if (status !== "ok") {
console.warn("listen failed:", status);
}
// 解绑时必须传入同一个 handler 函数
const removeStatus = app.Selection.removeEventListener("SELECTION_CHANGE", onSelectionChange);
事件属于哪个组件,以各产品页的“组件事件”表为准。常见归属如下:
| 场景 | 组件 | 示例事件 |
|---|---|---|
| 文档加载、导出、缩放、页码变化 | app.Document | DOCUMENT_EXPORT_READY、DOCX_END_LOADING、XLSX_WORKSHEET_CHANGE、PPTX_SLIDES_CHANGED |
| 选区变化 | app.Selection | SELECTION_CHANGE |
| 查找面板请求 | app.Finder | DOCX_OPEN_FIND_UI、XLSX_OPEN_FIND_UI |
| 撤销重做状态变化 | app.UndoRedo | UNDO_REDO_STATE_CHANGE |
传入不属于当前组件的事件名会返回 "no such event"。组件事件不接收 options 参数;如果只需要触发一次,可以在回调中主动解绑:
function onExportReady() {
app.Document.removeEventListener("DOCUMENT_EXPORT_READY", onExportReady);
console.log("export ready");
}
app.Document.addEventListener("DOCUMENT_EXPORT_READY", onExportReady);
颜色参数
DOCX 和 XLSX 常用 RGB 对象:
{ r: 255, g: 0, b: 0 }
RGB 对象只接收 r、g、b 三个通道,当前不支持 a/alpha 字段。
DOCX 还支持主题色对象:
{ name: "accent1", theme: 4, tint: 0, shade: 0 }
XLSX Selection.setFontColor、Selection.setFillColor 和边框颜色直接接收 RGB、主题色、索引色或 rgbHex 对象:
{ r: 255, g: 0, b: 0 }
{ theme: 4, tint: 0 }
{ indexed: 10 }
{ rgbHex: "FFFF0000" }
rgbHex 可使用 6 位 RRGGBB 或 8 位 AARRGGBB;只有 8 位 rgbHex 可以携带 alpha。它与不支持透明度的 RGB 对象不是同一种参数形式。
如果业务层使用的是色盘 UI 原始值,例如 { hex: "#FF0000", type: "standard", name: "red" },需要先转换成上面的 RGB 或主题色对象再传给 XLSX Selection.* 样式 API。
PPTX 字体颜色支持字符串或带 hex 的对象:
"#4472C4"
"rgb(68, 114, 196)"
{ hex: "#4472C4", type: "theme", name: "accent1" }
当前实现推荐使用 { hex: "#RRGGBB" } 对象。字符串解析在任一颜色通道为 0 时存在限制,不应依赖 "#FF0000"、"rgb(0, 0, 0)" 等字符串形式。
调色板类 API 返回以下结构:
{
theme: unknown[];
standard: unknown[];
}
常见约束
- 样式类 API 多数依赖当前选区。没有有效选区时不会生效。
- 导出方法是异步方法,建议始终
await。 exportPdf(...)需要安全上下文(例如 HTTPS 或 localhost)、window.queryLocalFonts能力和本地字体访问权限。不满足条件时当前实现会直接结束,不执行 PDF 导出。- DOCX 的
Table.*需要当前光标或选区位于表格上下文中。 - XLSX 的
Selection.*面向当前活动单元格或选区。 - PPTX 的
Paragraph.*与TextBox.*依赖当前选中的文本框或文本选区。