> **本文回答**：`CadEvents` 枚举里有 10 个基础文档事件 `DocumentCreating/Created/Opening/Opened/Closing/Closed/Saving/Saved/Modified/Switched`，外加协作类（`DocumentUploading/Uploaded/Downloading/Downloaded/SyncStatusChanged`）和缓存类（`DocumentCaching/Cached/Restoring/Restored/CacheCleared`）。**当前版本真正派发的只有 `DocumentCreating/Created/Modified/Switched`**；`DocumentOpening/Opened/Closing/Closed/Saving/Saved` 及协作、缓存、分支、Patch、冲突类事件**没有任何派发点**（订阅永不触发）。阻止保存/关闭请在命令层用 `CommandStarting` 拦截 `QSAVE/SAVEAS/SAVESERVER/SAVELOCAL/CLOSE`；"打开/保存之后"用 `CommandEnded`。

## 一、基础事件清单

| 事件 | 说明 | 可取消 | 当前版本 |
|---|---|---|---|
| `DocumentCreating` | 新建前（`Engine.docs.add()`；打开文件时也会先建空文档） | 是 | ✓ 派发 |
| `DocumentCreated` | 新建后（打开文件时此刻内容尚未载入） | 否 | ✓ 派发 |
| `DocumentOpening` | 打开前 | 是 | ✗ 未派发 |
| `DocumentOpened` | 打开后 | 否 | ✗ 未派发 |
| `DocumentClosing` | 关闭前 | 是 | ✗ 未派发 |
| `DocumentClosed` | 关闭后 | 否 | ✗ 未派发 |
| `DocumentSaving` | 保存前 | 是 | ✗ 未派发 |
| `DocumentSaved` | 保存后 | 否 | ✗ 未派发 |
| `DocumentModified` | 文档从"未修改"变为"已修改"（`DBMOD` 0→1）时触发**一次** | 否 | ✓ 派发 |
| `DocumentSwitched` | 切换活动文档后（args `{previousDocument, currentDocument}`） | 否 | ✓ 派发 |

MCP 参考：`vjcad/doc/event/event-types.md`（清单）；派发点以发布包中实际 `emit(CadEvents.*)` 为准。

## 二、最小用法

```js
Engine.eventManager.on(CadEvents.DocumentCreated, (args) => {
  console.log('新文档:', args.document.name);   // 打开文件时内容此刻尚未载入
});

Engine.eventManager.on(CadEvents.DocumentSwitched, (args) => {
  console.log('切换到:', args.currentDocument.name);
});

// "打开 / 保存之后"：DocumentOpened / DocumentSaved 不派发，改看命令结束
// （用户取消也会触发 CommandEnded；success 只表示 main 没抛异常）
Engine.eventManager.on(CadEvents.CommandEnded, (args) => {
  if (['OPEN', 'OPENFROMSERVER', 'OPENFROMLOCAL'].includes(args.commandName)) console.log('打开命令结束');
  if (['QSAVE', 'SAVEAS', 'SAVESERVER', 'SAVELOCAL'].includes(args.commandName)) console.log('保存命令结束');
});

Engine.eventManager.on(CadEvents.DocumentModified, () => {
  scheduleAutoSave();   // 仅 clean→dirty 时触发一次
});
```

MCP 参考：`vjcad/example/event/05documentevents.js`、`vjcad/doc/event/event-usage.md`（官方示例里的 `DocumentOpened/DocumentSaved` 订阅在当前版本不会触发）。

## 三、阻止保存空文档

`DocumentSaving` 不派发，在命令启动前拦截：

```js
Engine.eventManager.on(CadEvents.CommandStarting, (args) => {
  const saveCmds = ['QSAVE', 'SAVEAS', 'SAVESERVER', 'SAVELOCAL'];
  if (saveCmds.includes(args.commandName) && Engine.getEntities(e => e.isAlive).length === 0) {
    args.cancel = true;
    alert('文档为空，无法保存');
  }
});
```

## 四、关闭确认

`DocumentClosing` 不派发；内置 `CLOSE` 命令自己已对未保存文档（`DBMOD === 1`）弹确认框。要追加自定义检查，同样拦截命令（注意：文档标签上的 × 按钮走 `DocumentTabs` 自己的关闭逻辑，不经过 `CLOSE` 命令，拦不到）：

```js
Engine.eventManager.on(CadEvents.CommandStarting, (args) => {
  if (args.commandName === 'CLOSE' && !myBusinessCheckPassed()) {
    args.cancel = true;
    writeMessage('<br/>业务校验未通过，禁止关闭');
  }
});
```

MCP 参考：`vjcad/doc/event/event-usage.md`（原文用 `DocumentClosing`，当前版本不触发）。

## 五、协作 / 同步事件（✗ 均未派发）

| 事件 | 说明 | 可取消 |
|---|---|---|
| `DocumentUploading` / `DocumentUploaded` | 上传到服务端 | 是 / 否 |
| `DocumentDownloading` / `DocumentDownloaded` | 从服务端下载 | 是 / 否 |
| `DocumentSyncStatusChanged` | 同步状态变化 | 否 |

以上只存在于枚举，当前版本没有派发点；需要同步状态时在 `SAVESERVER` 等命令的 `CommandEnded` 里自行查询。

MCP 参考：`vjcad/doc/event/event-types.md`。

## 六、本地缓存事件（✗ 均未派发）

| 事件 | 说明 | 可取消 |
|---|---|---|
| `DocumentCaching` / `DocumentCached` | 保存到本地缓存 | 是 / 否 |
| `DocumentRestoring` / `DocumentRestored` | 从本地缓存恢复 | 是 / 否 |
| `DocumentCacheCleared` | 缓存清理后 | 否 |

MCP 参考：`vjcad/doc/event/event-types.md`。

## 七、分支 / Patch / 冲突事件（✗ 均未派发）

| 事件 | 说明 |
|---|---|
| `BranchCreating/Created/Switching/Switched/Merging/Merged/Deleting/Deleted` | 分支生命周期 |
| `PatchCreating/Created/Applying/Applied/Reverting/Reverted` | Patch 生命周期 |
| `ConflictDetected/Resolving/Resolved/Skipped` | 冲突 |

> 这组事件目前只是枚举占位，订阅不会触发；协作 UI 提示请基于版本 API 的返回值（见 [version](../version/overview.md)）。

MCP 参考：`vjcad/doc/event/event-types.md`、[version](../version/overview.md)。

## 反例

- 订阅 `DocumentSaving / DocumentOpened / DocumentClosing` 等未派发事件，以为拦截/回调生效：永远不会触发。
- 用 `CommandStarting` 拦截保存但**不调用任何用户提示**：用户以为没存上但其实是被静默阻止。
- 在打开命令的 `CommandEnded` 里调耗时操作（5+ 秒）：阻塞 UI，建议异步触发。
- 拿 `DocumentModified` 统计"每次修改"：它只在 clean→dirty 时触发一次，直到保存（或撤销回保存点）清除 `DBMOD` 前不再触发；缩放（`setZoom/zoomIn/zoomOut`）还会直接置 `DBMOD=1` 而不派发，之后的编辑也不再触发。逐次变更请监听 `EntityModified / EntityAdded / EntitiesErased / UndoStackChanged`。

## 参考（官方文档 MCP）

- 文档：`vjcad/doc/event/event-types.md`、`vjcad/doc/event/event-usage.md`
- 示例：`vjcad/example/event/05documentevents.js`
