tmagic-editor/docs/api/editor/historyServiceMethods.md
roymondchen 0f42989ca3 refactor(editor): 统一历史栈结构,支持扩展历史类型
将 pageSteps/codeBlockState/dataSourceState 三套独立历史栈收敛为统一的 steps 结构
(按 stepType 分桶),并新增 registerStepType/setStepName/getStepName 支持自定义
扩展历史类型。同步重构 history 相关服务、组件、工具方法、测试与文档。
2026-06-23 20:14:41 +08:00

290 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# historyService方法
## reset
- **详情:**
重置全部历史记录:清空 `state.steps` 下的页面 / 代码块 / 数据源 / 扩展类型全部栈(保留已注册的扩展类型键)。
## resetState
- **详情:**
同 [`reset`](#reset),清空 `state.steps` 下全部栈。
::: tip
历史服务不再维护「当前活动页」状态(已移除 `state.pageId` / `state.canUndo` / `state.canRedo`)。
活动页由 `editorService` 维护,撤销 / 重做 / 读取页面历史时请显式传入 pageId。
是否可撤销 / 重做请改用 [`canUndo`](#canundo) / [`canRedo`](#canredo)。
:::
## registerStepType
- **参数:**
- `{string} stepType` 自定义历史类型标识(勿与内置 `page` / `codeBlock` / `dataSource` 重名)
- `{Object} options` 可选
- `{string} event` push / undo / redo 后派发的事件名;缺省为 `${stepType}-history-change`
- `{string} name` 历史面板中的展示名称tab / 分组标题等);缺省回退到 stepType 本身
- **详情:**
注册一个扩展历史类型,使其可与内置 `page` / `codeBlock` / `dataSource` 一样走统一的
[`push`](#push) / [`undo`](#undo) / [`redo`](#redo)(按 id 分栈、独立 undo/redo
注册后该类型的栈存放在 `historyService.state.steps[stepType]`,展示名称存放在 `historyService.state.stepNames[stepType]`
## getStepName
- **参数:**
- `{HistoryStepType} stepType` 历史类型
- **返回:**
- `{string}` 该类型的展示名称(用于历史面板 tab / 分组标题等);未登记时回退到 stepType 本身
- **详情:**
读取指定历史类型的展示名称。内置 `page` / `codeBlock` / `dataSource` 默认分别为「页面 / 代码块 / 数据源」。
## setStepName
- **参数:**
- `{HistoryStepType} stepType` 历史类型
- `{string} name` 展示名称
- **详情:**
设置指定历史类型的展示名称(写入 `historyService.state.stepNames`,历史面板会响应式刷新)。
内置 `page` / `codeBlock` / `dataSource` 也可在此覆盖默认中文名。
## push
- **参数:**
- `{HistoryStepType} stepType` 历史类型,内置 `'page'` / `'codeBlock'` / `'dataSource'`,并支持通过 [`registerStepType`](#registersteptype) 扩展
- `{StepValue | BaseStepValue} step` 已构造好的历史记录(缺省自动补全 `uuid` / `timestamp`
- `{Id} id` 必填;目标栈 id`page` 为 pageId其余类型为对应资源 id
::: details 查看 StepValue 及关联类型定义
<<< @/../packages/editor/src/type.ts#StepValue{ts}
<<< @/../packages/editor/src/type.ts#BaseStepValue{ts}
<<< @/../packages/editor/src/type.ts#StepExtra{ts}
<<< @/../packages/editor/src/type.ts#HistoryOpType{ts}
<<< @/../packages/editor/src/type.ts#HistoryOpSource{ts}
<<< @/../packages/schema/src/index.ts#Id{ts}
<<< @/../packages/schema/src/index.ts#MNode{ts}
<<< @/../packages/form-schema/src/base.ts#ChangeRecord{ts}
:::
- **返回**
- `{StepValue | BaseStepValue | null}` 入栈失败未传 / 无效 id时返回 `null`
- **详情**
添加一条历史记录统一入口所有类型`page` / `codeBlock` / `dataSource` / 扩展行为完全一致 `stepType` 选择目标栈类型 `id`必填选择具体栈按需建栈后入栈并派发对应的历史变更事件`page` `change`其余如 `code-block-history-change` / `data-source-history-change`回调签名统一为 `(id, step)`
跨页 / 跨资源操作如把节点搬到其它页必须显式传入目标 id`codeBlock` / `dataSource` step 通常由 `createStackStep` 等工具按 `oldValue` / `newValue` 构造后传入
::: tip
`opType: 'update'` 的每个 diff 项上可携带 `changeRecords`用于撤销 / 重做时仅按
`propPath` 局部更新对应字段避免整节点替换冲掉同节点上的其它无关变更不带
`changeRecords` 时退化为整节点替换 `sort` / `moveLayer` / 拖动等纯快照场景)。
`step` 上的 `historyDescription` / `source` 仅用于历史面板展示与埋点不影响 undo/redo 行为
入栈时会为每条记录自动生成唯一标识 `uuid`调用方未指定时可用于精确引用 / 定位某一条历史记录
若需要在执行 DSL 操作后拿到本次写入记录的 `uuid`可使用 editorService / dataSourceService /
codeBlockService 提供的 `*AndGetHistoryId` 方法参见
[editorService 历史记录 uuid 与 \*AndGetHistoryId](./editorServiceMethods.md#历史记录-uuid-与-andgethistoryid)
:::
## undo
- **参数**
- `{HistoryStepType} stepType` 历史类型
- `{Id} id` 必填目标栈 id`page` pageId其余类型为对应资源 id
- **返回**
- `{StepValue | BaseStepValue | null}`
- **详情**
撤销指定历史栈的最近一次变更所有类型行为一致 `stepType` + `id` 定位栈不会越过 index 0 initial 基线所有类型同等适用 [`setMarker`](#setmarker)仅在确有可撤销 step 时派发对应的历史变更事件`page` `change`回调签名 `(id, step)`)。
`page` 类型 `opType: 'update'` diff 项的 `changeRecords` 存在会按 `propPath` `oldSchema` 取值做局部回滚否则用 `oldSchema` 整节点替换`codeBlock` / `dataSource` 拿到 step 后由调用方写回对应 service本方法不会自动回放)。
## redo
- **参数**
- `{HistoryStepType} stepType` 历史类型
- `{Id} id` 必填目标栈 id`page` pageId其余类型为对应资源 id
- **返回**
- `{StepValue | BaseStepValue | null}`
- **详情**
恢复指定历史栈到下一步语义与 [`undo`](#undo) 对称`page` 类型 `opType: 'update'` diff 项的 `changeRecords` 存在会按 `propPath` `newSchema` 取值做局部重做否则用 `newSchema` 整节点替换
## canUndo
- **参数**
- `{HistoryStepType} stepType` 历史类型
- `{Id} id` 可选目标栈 id缺省 / 无效时返回 `false`
- **返回**
- `{boolean}`
- **详情**
指定历史栈当前是否可撤销游标高于 index 0 initial 基线底线)。适用于所有类型`page` / `codeBlock` / `dataSource` / 扩展)。
## canRedo
- **参数**
- `{HistoryStepType} stepType` 历史类型
- `{Id} id` 可选目标栈 id缺省 / 无效时返回 `false`
- **返回**
- `{boolean}`
- **详情**
指定历史栈当前是否可重做适用于所有类型`page` / `codeBlock` / `dataSource` / 扩展)。
## setMarker
- **参数**
- `{HistoryStepType} stepType` 历史类型
- `{Id} id` 目标栈 id`page` pageId其余类型为对应资源 id
- `{Object} options` 可选`name` / `description` / `source`用于基线的展示信息
- **返回**
- `{StepValue | null}` 已存在基线时返回原基线栈非空无基线 id 无效时返回 `null`
- **详情**
为指定历史栈种入一条 `opType: 'initial'` 初始基线记录作为该栈 index 0 的固定底线它是真实入栈并随栈持久化的 step但被钉为撤销 / 回滚的下限`undo` / `goto` / `revert` 都不会越过它所有类型含扩展类型均可设置基线仅当目标栈为空时种入
## getMarker
- **参数**
- `{HistoryStepType} stepType` 历史类型
- `{Id} id` 可选目标栈 id缺省 / 无效时返回 `undefined`
- **返回**
- `{StepValue | undefined}`
- **详情**
读取指定历史栈的初始基线 step index 0 `opType: 'initial'`不存在时返回 `undefined`
## markSaved
- **参数**
- `{HistoryStepType} stepType` 历史类型内置另有 `'codeBlock'` / `'dataSource'`并支持扩展仅在传入 `id` 时生效
- `{Id} id` 可选目标栈 id缺省表示标记全部类型全部栈
- **详情**
标记历史记录为已保存」(把对应栈当前游标所在的记录标记为 `saved = true`)。统一入口
- **缺省 `id`**标记整份 DSL 已保存」——把所有类型所有栈当前游标所在的记录都标记为已保存此时 `stepType` 不生效触发 `mark-saved` 事件且 `{ kind: 'all' }`通常在 DSL 整体落库成功后调用
- **传入 `id`**仅标记 `stepType` 下该 id 对应的栈触发 `mark-saved` 事件且 `{ kind: stepType, id }` `{ kind: 'page', id }` / `{ kind: 'codeBlock', id }` / `{ kind: 'dataSource', id }`)。
同一栈内任意时刻最多保留一条已保存记录标记前会清除该栈内全部旧标记某个栈处于全部已撤销」(cursor 0时不会留下已保存记录 IndexedDB 恢复时其游标会回到 0配合 [`restoreFromIndexedDB`](#restorefromindexeddb) 把游标恢复到此处
## clear
- **参数**
- `{HistoryStepType} stepType` 历史类型内置另有 `'codeBlock'` / `'dataSource'`并支持扩展
- `{Id} id` 可选目标栈 id缺省表示清空 `stepType` 下的全部栈
- **详情**
清空历史记录栈统一入口所有类型page / codeBlock / dataSource / 扩展行为一致
- **传入 `id`**仅清空 `stepType` 下该 id 对应的栈
- **缺省 `id`**清空 `stepType` 下的全部栈
仅删除撤销/重做记录不会改动 DSL / 代码块 / 数据源本身清空时会**保留各栈原有的 initial 基线**文案 / 来源 [`setMarker`](#setmarker)无基线时清空成空栈清空后触发 `clear` 事件签名 `(id, stepType)`)。
## saveToIndexedDB
- **参数**
- `{HistoryPersistOptions} options` 可选
::: details 查看 HistoryPersistOptions / PersistedHistoryState 类型定义
<<< @/../packages/editor/src/type.ts#HistoryPersistOptions{ts}
<<< @/../packages/editor/src/type.ts#PersistedHistoryState{ts}
<<< @/../packages/editor/src/utils/undo-redo.ts#SerializedUndoRedo{ts}
:::
- **返回**
- `{Promise<PersistedHistoryState>}` 写入成功的快照对象
- **详情**
把当前内存中的全部历史栈页面 / 代码块 / 数据源 / 扩展类型连同各自游标容量序列化后写入本地 IndexedDB
- 最终库名为 `${dbName}-${当前 DSL app id}`按应用隔离
- `key` 用于在同一 store 下区分不同记录缺省为 `default`
- 历史记录里可能包含函数代码块内容 / 节点事件等内部使用 `serialize-javascript` 序列化为字符串后写入恢复时再用 `parseDSL` 还原因此可安全持久化函数 / `Map`
- 不支持 IndexedDB 的环境 SSR reject
写入成功后触发 `save-to-indexed-db` 事件
::: warning
`beforeunload` / `pagehide` 阶段浏览器不会等待异步 IndexedDB 事务提交单纯依赖卸载时写入可能丢失最近一次编辑建议在历史变更时防抖即调用本方法持久化确保刷新后能完整恢复
:::
## restoreFromIndexedDB
- **参数**
- `{HistoryPersistOptions} options` 可选
- **返回**
- `{Promise<PersistedHistoryState | null>}` 找不到记录时返回 `null`
- **详情**
从本地 IndexedDB 读取此前保存的历史快照并重建全部撤销/重做栈
- 每个栈都会按 `listMaxSize` 裁剪并还原游标
- 若某个栈存在已保存记录 `markSaved`其游标会被定位到最近一条已保存记录之后使恢复后的状态与落库的 DSL 对齐
- 会整体覆盖当前内存中的历史状态活动页由 `editorService` 维护不在此恢复
- 找不到对应记录时返回 `null` 且不改动当前状态不支持 IndexedDB 的环境会 reject
成功后触发 `restore-from-indexed-db` 事件
## findStepLocationByUuid
- **参数**
- `{HistoryStepType} stepType` 历史类型
- `{string} uuid` 目标历史记录的 uuid
- `{Id} id` 可选目标栈 id
- **返回**
- `{ { id: Id; index: number } | null }` 找到时返回所属栈 id 与步骤索引找不到时返回 `null`
- **详情**
按历史记录 uuid 在指定历史类型的栈中查找其所属 id 与索引统一入口
- **传入 `id`**仅在该 id 对应的单个栈中查找如页面历史按活动页查看传入 pageId
- **缺省 `id`**遍历该类型下全部栈查找代码块 / 数据源等按全部资源分桶的场景)。
uuid 回滚等需要把 uuid 映射回 `(id, index)` 的场景使用 [editorService.revertPageStepById](./editorServiceMethods.md#revertpagestepbyid) / [codeBlockService.revertById](./codeBlockServiceMethods.md#revertbyid) / [dataSourceService.revertById](./dataSourceServiceMethods.md#revertbyid) 内部均通过本方法定位步骤
## destroy
- **详情**
销毁