Skip to content

通用控制

本页按功能组织三类文档(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 / pdf;未知项 / 空数组会让整次保存失败(不静默丢产物)。

后端(遍历产物集合逐 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:truebutton 是用户所点按钮(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 提供)。无需额外判断是否安装。

下一步

  • Word 特有功能 —— 模板填充、文档比对、修订 / 批注
  • Excel / PPT —— 各自打开模式与当前能力范围
  • 参考 —— 配置项 / 端点 / 类型枚举全表

面向集成方的产品技术文档 · 不含实现细节