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

13 KiB
Raw Blame History

historyService方法

reset

  • 详情:

    重置全部历史记录:清空 state.steps 下的页面 / 代码块 / 数据源 / 扩展类型全部栈(保留已注册的扩展类型键)。

resetState

  • 详情:

    reset,清空 state.steps 下全部栈。

    ::: tip 历史服务不再维护「当前活动页」状态(已移除 state.pageId / state.canUndo / state.canRedo)。 活动页由 editorService 维护,撤销 / 重做 / 读取页面历史时请显式传入 pageId。 是否可撤销 / 重做请改用 canUndo / 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 / undo / 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 扩展
    • {StepValue | BaseStepValue} step 已构造好的历史记录(缺省自动补全 uuid / timestamp
    • {Id} id 必填;目标栈 idpage 为 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(必填)选择具体栈,按需建栈后入栈,并派发对应的历史变更事件(pagechange,其余如 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。 :::

undo

  • 参数:

    • {HistoryStepType} stepType 历史类型
    • {Id} id 必填;目标栈 idpage 为 pageId其余类型为对应资源 id
  • 返回:

    • {StepValue | BaseStepValue | null}
  • 详情:

    撤销指定历史栈的最近一次变更。所有类型行为一致:按 stepType + id 定位栈,不会越过 index 0 的 initial 基线(所有类型同等适用,见 setMarker),仅在确有可撤销 step 时派发对应的历史变更事件(pagechange,回调签名 (id, step))。

    page 类型 opType: 'update' 时,若 diff 项的 changeRecords 存在,会按 propPatholdSchema 取值做局部回滚;否则用 oldSchema 整节点替换。codeBlock / dataSource 拿到 step 后由调用方写回对应 service本方法不会自动回放

redo

  • 参数:

    • {HistoryStepType} stepType 历史类型
    • {Id} id 必填;目标栈 idpage 为 pageId其余类型为对应资源 id
  • 返回:

    • {StepValue | BaseStepValue | null}
  • 详情:

    恢复指定历史栈到下一步,语义与 undo 对称。page 类型 opType: 'update' 时,若 diff 项的 changeRecords 存在,会按 propPathnewSchema 取值做局部重做;否则用 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 目标栈 idpage 为 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 把游标恢复到此处。

clear

  • 参数:

    • {HistoryStepType} stepType 历史类型,内置另有 'codeBlock' / 'dataSource',并支持扩展
    • {Id} id 可选;目标栈 id。缺省表示清空 stepType 下的全部栈
  • 详情:

    清空历史记录栈。统一入口所有类型page / codeBlock / dataSource / 扩展)行为一致:

    • 传入 id:仅清空 stepType 下该 id 对应的栈;
    • 缺省 id:清空 stepType 下的全部栈。

    仅删除撤销/重做记录,不会改动 DSL / 代码块 / 数据源本身。清空时会保留各栈原有的 initial 基线(文案 / 来源,见 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 / codeBlockService.revertById / dataSourceService.revertById 内部均通过本方法定位步骤。

destroy

  • 详情:

    销毁