外观
通用控制
本页按功能组织三类文档(Word / Excel / PPT)通用的集成写法。每个功能给出用途 + 后端代码 + 前端代码的最小片段;完整的配置项、端点、类型清单收在参考。
一条铁律:打开模式由服务端裁决。可编辑 / 只读 / Excel / PPT 等是文档级业务决策,由业务后端在 host-page 路由里用
AtkonofficeOpenMode挑定;浏览器只透传params。所以「不同打开方式」大多是后端换一个枚举 + 前端指到对应路由,而非前端传标志位。
打开编辑
最常规的可编辑打开。后端用 webOpen 传文档定位符 + NormalEdit 模式,前端 open 指到该路由。
后端(host-page 路由):
java
@GetMapping(value = "/atkonoffice/edit", produces = MediaType.TEXT_HTML_VALUE)
public String edit(@RequestParam String docId, HttpServletRequest request) {
String savePath = "/atkonoffice/doc/save?docId=" + docId; // 相对路径,origin 由 SDK 补全
return new AtkonofficeCtrl(request)
.webOpen(docId, AtkonofficeOpenMode.NormalEdit, "张三")
.setSaveFilePage(savePath)
.getHostPage();
}前端:
js
atkonoffice.open({
pageApi: '/atkonoffice/edit',
params: { docId: 'report.docx', user: '张三' },
})只读打开
用于审阅、留痕、展示,防误改。与「打开编辑」只差后端一个枚举——把 NormalEdit 换成 ReadOnly,通常单独挂一条只读路由,且可不设保存地址。
后端:
java
@GetMapping(value = "/atkonoffice/view", produces = MediaType.TEXT_HTML_VALUE)
public String view(@RequestParam String docId, HttpServletRequest request) {
return new AtkonofficeCtrl(request)
.webOpen(docId, AtkonofficeOpenMode.ReadOnly, "张三") // 只读
.getHostPage(); // 不设 savePath = 不保存
}前端:
js
atkonoffice.open({ pageApi: '/atkonoffice/view', params: { docId: 'report.docx' } })Word 另有「仅修订
RevisionOnly」「仅批注CommentOnly」两种受控模式,见 Word 特有功能。Excel / PPT 的只读见各自页。全部打开模式枚举见参考 · 类型。
新建空白文档
让终端用户从零撰写。后端用 webCreate(family, user) 打开一份 SDK 自带的空白模板——会绕过解析器,故未配文档解析器也能「新建」,只有保存时才需要自有存储。
后端:
java
@GetMapping(value = "/atkonoffice/create", produces = MediaType.TEXT_HTML_VALUE)
public String create(@RequestParam String type, // "word" / "excel" / "ppt"
@RequestParam String newDocId, HttpServletRequest request) {
String savePath = "/atkonoffice/doc/save?docId=" + newDocId;
return new AtkonofficeCtrl(request)
.webCreate(DocFamily.fromWireType(type), "张三") // 新建对应类型空白文档
.setSaveFilePage(savePath)
.getHostPage();
}前端:
js
atkonoffice.open({
pageApi: '/atkonoffice/create',
params: { type: 'word', newDocId: 'draft-001.docx' },
})
DocFamily三个常量:WORD/EXCEL/PPT,见参考 · 类型。
保存与回写
保存的字节经 multipart POST 回业务 save 路由,用 AtkonofficeFileSaver.getFileBytes() 取字节落盘。大多数集成不需要前端主动触发保存——用户点 Office 原生保存按钮即走后端 save 路由。前端 session.* 只在需要编程式保存 / 改保存目标时用到。
后端(save 路由,与开始一致):
java
byte[] bytes = saver.getFileBytes();
yourStorage.save(docId, bytes);
saver.setResult("{\"ok\":true}");前端(编辑页内,可选的编程式保存):
js
// 原子口(推荐):设址与保存同帧到达,无「先设址后保存」两步竞态
atkonoffice.session.save({ savePath: '/atkonoffice/doc/save?docId=report.docx' })
// 声明一次保存产出一组产物(原格式 + PDF),一次原子上传(详见下节「多产物保存」)
atkonoffice.session.save({ formats: ['source', 'pdf'] })
// 附带业务字段(作为 multipart 表单字段 POST 回后端,后端用 saver.getFormField 取)
atkonoffice.session.save({ savePath: '/atkonoffice/doc/save', newDocId: 'draft-001.docx' })savePath必须是相对路径(以/起始、禁://),客户端把它接在本次打开所用的后端地址(完整值,含反向代理 / 多实例部署带的路径前缀)之后;formats声明本次保存产出哪些产物(缺省只存原格式source),与savePath正交(前者管有什么、后者管去哪)——详见多产物保存;session.setSavePath(path)可改后续所有保存(含 Office 原生按钮)的默认目标(有状态便利口);- 客户端外普通浏览器里
session.*全是无害 no-op,同一份页面代码可安全书写。
多产物保存
一次保存可把当前文档物化成一组产物——原格式文件(source)+ 可选派生件(目前内置 pdf)。客户端逐个生产后恰发一次原子 multipart POST:要么全部落盘、要么都不落(任一产物生产失败即整次保存失败、不留半份、不覆盖原件)。同步导出含最新未保存编辑,仅普通编辑态可用(Word / Excel / PPT 三类)。
前端(编辑页内):
js
// 一次保存产出原格式 + PDF 两份产物;纯 ['pdf'] 也合法(只导出 PDF、不动原件)
atkonoffice.session.save({ formats: ['source', 'pdf'] })
formats缺省(不传)= 只存原格式source,与普通保存完全一致。词表当前只有source/
后端(遍历产物集合逐 role 落盘,派生件按扩展名派生兄弟文件名、不覆盖原件):
java
saver.getFiles().forEach((role, artifact) -> {
String targetId = "source".equals(role)
? docId // 主产物 → 原 docId
: deriveSibling(docId, artifact.getExtName()); // 派生件 → 兄弟文件名(如 report.pdf)
yourStorage.save(targetId, artifact.getBytes());
});
saver.setResult("{\"ok\":true}");每个带文件名的 part 都是一个产物(part 名 = role);便捷口
getFileBytes()/getFileName()取的是主产物source。完成后documentSaved事件的saveTypes列出本次产出的产物 role(如["source","pdf"])。
生命周期事件回流
文档的关键节点回流到业务页,用于编排上下游流程(触发审批、归档、通知等)。只读通知,不能取消保存(要拦截见下一节)。
前端(外层业务页):
js
atkonoffice
.on('documentOpened', (e) => console.log('已打开', e.doc_id))
.on('documentBeforeSave', (e) => console.log('即将保存', e.doc_id)) // 通知式
.on('documentSaved', (e) => console.log('已保存', e.doc_id, e.ok, e.saveTypes))
// 编辑器窗口已不在(正常关闭 / 异常消失):复位页面的「编辑中」状态
.on('shellClosed', (e) => resetEditingUi(e.reason))
// 本页与服务器的连接断了 / 恢复了:给用户一条准确的横幅提示
.on('channelLost', (e) => showBanner(e.reason === 'rejected'
? '连接已断开,请重新打开文档。'
: '连接暂时中断,正在后台恢复,恢复后自动接上。'))
.on('channelRestored', () => hideBanner())
// 运行期异常:按稳定错误码分流,不要匹配 message 文案(它会随版本调整措辞)
.on('error', (err) => {
if (err.terminal) { // ws_gave_up:通道永久丢失
showBanner('连接已断开,请重新打开文档。') // 只订上面状态事件的页面也必须接这一条
} else if (err.code === 'ws_backoff_exhausted') { // 已转低频重试,后端恢复后自愈
showBanner('连接暂时中断,正在后台重试。')
} else if (err.category === 'config') { // 接入 / 部署配置问题:重试无用
console.error('接入配置有误', err.code)
} else { // 词表开放:必须保留兜底
console.error(err.code ?? err)
}
})发起打开阶段的失败不走 on('error'),由 open() 自己 reject(同一套带 code 的错误对象):
js
try {
await atkonoffice.open({ pageApi: '/atkonoffice/edit', params: { docId } })
} catch (err) {
// 联调期最常撞的两条:launch_request_failed(请求没发出去:后端没起 / 断网 / 跨域被拦)、
// launch_response_not_json(网关 / 反代返了 HTML 错误页)
console.error('打开失败', err.code, err.message)
}事件与负载字段清单见参考 · 类型,错误码词表与分流纪律见参考 · 错误对象。
也可在后端注册回调函数名(
setAfterDocumentOpened/setBeforeDocumentSaved/setAfterDocumentSaved),事件到达浏览器时由浏览器端 SDK 调用业务页里的同名函数——见参考 · 配置。两条路任选。
保存前 · 关闭前拦截
与只读的 on('documentBeforeSave') 不同,这里可阻断保存 / 关闭,用于业务侧前置校验或确认。仅在编辑页内(session.*)可用。
前端(编辑页内):
js
atkonoffice.session.onBeforeSave(() => {
return myValidate() // 返回 false / reject → 取消保存;true / undefined / 抛错 / 超时 → 放行
})
atkonoffice.session.onBeforeClose(() => confirm('确定关闭?'))弹出提示与确认框
在编辑页内弹出一个原生提示框,显示在正在编辑的文档之上、不被文档遮挡,用于信息提示、操作确认或错误告知(如「提交前确认」)。仅编辑页内(session.*)可用。
前端(编辑页内):
js
// 弹一个「是 / 否」确认框,等用户选择
const r = await atkonoffice.session.showMessageBox({
type: 'confirm', // info / confirm / warning / error(决定图标)
title: '提交确认',
text: '确定要提交这份文档吗?',
buttons: 'yesNo', // ok / okCancel / yesNo / yesNoCancel
})
if (r.ok && r.button === 'yes') submitApproval()
// 与「保存前拦截」配合,做「保存前先确认」
atkonoffice.session.onBeforeSave(async () => {
const r = await atkonoffice.session.showMessageBox({
type: 'confirm', text: '确定保存当前修改?', buttons: 'okCancel',
})
return r.ok && r.button === 'ok' // 用户点「取消」→ 返回 false 阻断保存
})- 返回
Promise,解析为{ ok, button }:ok:true时button是用户所点按钮(ok/cancel/yes/no); type/buttons均可省略(默认info/ok);- 永不 reject——客户端外的普通浏览器、非法参数、保存进行中等场景解析为
{ ok: false }、不弹框、不抛错,同一份页面代码可安全书写。
自定义工具条按钮
在编辑窗口上追加业务按钮(如「提交审批」「下一步」),把业务动作直接放到用户面前。按钮物理样式统一、业务页 CSS 够不到。仅编辑页内可用。
前端(编辑页内):
js
const handle = atkonoffice.session.addCustomToolButton('提交审批', 0, () => submitApproval())
// iconIndex:1..N 渲染内置图标,0 / 省略 / 越界 = 纯文字
handle?.remove() // 移除按钮(客户端外普通浏览器返回 null)
atkonoffice.session.setCustomToolbarVisible(false) // 整体显隐已配置的自定义工具条编辑界面定制
调整编辑窗口的呈现——收敛为简洁 viewer、控制缩放视图、定制客户端窗口外观。这些在后端 AtkonofficeCtrl 上链式配置,打开时应用;未调用则保持默认、零影响。
后端(常用几项):
java
return new AtkonofficeCtrl(request)
.webOpen(docId, AtkonofficeOpenMode.ReadOnly, user)
.setOfficeToolbars(false) // 隐藏 Office 原生功能区 → 简洁查看
.setZoomPercent(120) // 缩放
.setWindowTitle("合同审阅") // 客户端窗口标题
.setWindowTheme(AtkonofficeTheme.SYSTEM) // 窗口外观主题
.getHostPage();全部外观 setter(缩放 / 视图 / 复制限制 / 窗口边框主题标题等十余项)见参考 · 配置。
业务参数透传与分流
open({ params }) 的键会全量透传进 pageApi 端点 query,可用于业务分流。模型键:docId(打开 / 保存所指文档)、user(终端用户显示名)、compareDocId(Word 比对,见 Word);其余(如 bizType)为自定义业务键。
推荐做法:一功能一路由。把不同打开方式拆成多条 host-page 路由(普通编辑 / 只读 / Excel / 新建…),每条各自挑打开模式,前端用 open({ pageApi }) 指到对应路由:
js
// 编辑
atkonoffice.open({ pageApi: '/atkonoffice/edit', params: { docId, bizType: 'contract' } })
// 只读
atkonoffice.open({ pageApi: '/atkonoffice/view', params: { docId } })后端据透传的自定义键分流:
java
@GetMapping("/atkonoffice/edit")
public String edit(@RequestParam String docId,
@RequestParam(required = false) String bizType, // 自定义键,原样到达
HttpServletRequest request) {
boolean lockContract = "contract".equals(bizType);
return new AtkonofficeCtrl(request)
.webOpen(docId, lockContract ? AtkonofficeOpenMode.CommentOnly
: AtkonofficeOpenMode.NormalEdit, "张三")
.setSaveFilePage(savePath).getHostPage();
}用自带登录的业务页承载编辑器
若已有自带登录校验的业务页面要直接承载编辑器,用 open({ pageUrl }) 让客户端导航到它(而非用 pageApi 走 SDK 内建宿主页)。这类页面无需为接入改造。
js
atkonoffice.open({
pageUrl: `${location.origin}/biz/editor?docId=report.docx`, // 你自己业务页的真实地址
storage: { token: localStorage.getItem('token') }, // 注入编辑页 localStorage,让已有登录守卫在客户端内通过
})storage双用途:序列化进数据面请求头做后端鉴权,并注入编辑页localStorage;headers/cookie也可应用到数据面请求,见参考 · 类型。
承载页里同样可用
atkonoffice.session.*(保存、工具条、拦截、word.*)在编辑页内定制。
装机检测与引导
未安装客户端时的检测与引导由 open() 内部自管——已安装静默拉起,未安装弹内建引导(引导页地址由后端 atkonoffice.launch.client-download-url 提供)。无需额外判断是否安装。