Skip to content

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 必填;目标栈 id(page 为 pageId,其余类型为对应资源 id)
    查看 StepValue 及关联类型定义
    ts
    /**
     * 页面节点历史记录条目(`diff` 内容为 {@link MNode})。结构已与代码块 / 数据源统一收敛到
     * {@link BaseStepValue}:关联 id 见 `data.id`,选区等上下文见 `extra`。
     */
    export type StepValue = BaseStepValue<MNode, StepExtra>;
    ts
    /**
     * 历史记录条目公共字段,被 {@link StepValue} / {@link CodeBlockStepValue} / {@link DataSourceStepValue} 复用。
     *
     * 泛型 `T` 为 `diff` 中变化内容的快照类型(页面节点 `MNode` / 代码块 `CodeBlockContent` / 数据源 `DataSourceSchema`)。
     */
    export interface BaseStepValue<T = unknown, U extends Record<string, any> = {}> {
      /**
       * 历史记录唯一标识(uuid)。入栈时自动写入(若调用方未指定),
       * 用于精确定位 / 引用某一条历史记录(如 revert、埋点、跨端同步等)。
       * 注意与 `data.id`(关联的页面 / 代码块 / 数据源 id)区分。
       */
      uuid: string;
      /**
       * 关联目标信息:`id` 为关联的页面 / 代码块 / 数据源等资源 id(也是历史栈的分组 key),
       * `name` 为展示名。所有历史类型统一携带。
       */
      data: { name: string; id: Id };
      /** 操作类型:新增 / 删除 / 更新(三类历史记录统一携带)。 */
      opType: HistoryOpType;
      /**
       * 本次变更的内容(统一 diff 表达),每项见 {@link StepDiffItem}
       * 页面节点(add/remove 多节点、update 多节点)会有多项,代码块 / 数据源通常只有一项。
       */
      diff: StepDiffItem<T>[];
      /**
       * 调用方可选传入的人类可读描述(如「调整按钮颜色」),用于历史面板展示。
       * 不影响 undo/redo 行为;缺省时面板会根据节点 / propPath 自动生成描述。
       */
      historyDescription?: string;
      /**
       * 操作途径:标记本次变更由哪条交互入口触发,取值见 {@link HistoryOpSource}
       * (画布 / 树面板 / 组件面板 / 配置面板 / 源码编辑器 / 右键菜单 / 工具栏 / 快捷键 / 回滚 / 接口 等)。
       * 仅用于历史面板展示与业务埋点,不影响 undo/redo 行为;缺省时面板视为「未知」。
       */
      source?: HistoryOpSource;
      /**
       * 入栈时间戳(毫秒)。入栈时自动写入(若调用方未指定),仅用于历史面板展示。
       */
      timestamp?: number;
      /**
       * 是否为「已保存」记录:DSL 落库(如保存到后端 / 本地)时由 historyService.markSaved 标记。
       * 同一栈内任意时刻最多只有一条记录为 true;从 IndexedDB 恢复时游标会被定位到最近一条已保存记录之后。
       */
      saved?: boolean;
      /**
       * 是否为「整体设置 root」(set root)产生的记录(由 {@link Editor.pushRootDiffHistory} 写入)。
       * 用于「连续 set root 合并」:当某页栈最新一条已是 root 记录时,下一条 set root 会替换它而非新增,
       * 避免源码反复保存 / 外部重设 DSL 时堆积多条 root 记录。
       */
      rootStep?: boolean;
      /** 操作人 */
      operator?: string;
      /** 扩展信息 */
      extra?: U;
    }
    ts
    /**
     * 历史记录的扩展上下文({@link BaseStepValue.extra})。
     * 内置字段供 `page` 类型在撤销 / 重做时恢复选区与受影响节点;扩展类型可自由附加其它键。
     */
    export interface StepExtra {
      /** 操作前选中的节点 ID,用于撤销后恢复选择状态(page 类型) */
      selectedBefore?: Id[];
      /** 操作后选中的节点 ID,用于重做后恢复选择状态(page 类型) */
      selectedAfter?: Id[];
      /** 本次操作涉及的节点 id 集合(page 类型) */
      modifiedNodeIds?: Map<Id, Id>;
      [key: string]: any;
    }
    ts
    /**
     * 历史记录操作类型:
     * - `add` / `remove` / `update`:普通可撤销/重做的节点变更;
     * - `initial`:页面「未修改的初始状态」基线(设置 root 时生成),作为页面栈 index 0 的固定底线 step。
     *   该 step 不可被撤销/回滚(cursor 不会低于它),仅用于历史面板底部的初始行展示。
     */
    export type HistoryOpType = 'add' | 'remove' | 'update' | 'initial';
    ts
    /**
     * 历史记录的「操作途径」——标记本次变更由哪条交互入口触发,仅用于历史面板展示 / 业务埋点,
     * 不影响 undo/redo 行为。缺省(未传)时 UI 视为「未知」。
     *
     * - `stage`:画布(拖拽 / 缩放 / 排序等舞台直接操作)
     * - `tree`:树形面板(图层 / 数据源 / 代码块等树形结构里的拖拽 / 菜单操作)
     * - `component-panel`:组件面板(左侧组件列表点击 / 拖拽新增组件)
     * - `props`:配置面板表单(属性表单字段编辑)
     * - `code`:源码编辑器(配置面板「源码」面板里直接编辑 JSON/代码后保存)
     * - `stage-contextmenu`:画布右键菜单(舞台上节点的右键上下文菜单)
     * - `tree-contextmenu`:树面板右键菜单(图层 / 数据源 / 代码块等树形列表上的右键上下文菜单)
     * - `toolbar`:工具栏菜单(顶部导航工具栏按钮)
     * - `shortcut`:键盘快捷键
     * - `rollback`:历史回滚(历史面板里对某条历史「回滚」,反向应用为一条新记录,类 git revert)
     * - `api`:代码 / 接口调用(程序化触发)
     * - `ai`:AI 生成 / 智能助手触发的变更
     * - `unknown`:未知来源
     *
     * 通过 `(string & {})` 允许业务侧扩展自定义途径字符串,同时保留内置值的自动补全。
     */
    export type HistoryOpSource =
      | 'initial'
      | 'stage'
      | 'tree'
      | 'component-panel'
      | 'props'
      | 'code'
      | 'root-code'
      | 'stage-contextmenu'
      | 'tree-contextmenu'
      | 'toolbar'
      | 'shortcut'
      | 'rollback'
      | 'api'
      | 'ai'
      // 同步
      | 'sync'
      | 'unknown'
      | (string & {});
    ts
    export type Id = string | number;
    ts
    export type MNode = MComponent | MContainer | MIteratorContainer | MPage | MApp | MPageFragment;
    ts
    export interface ChangeRecord {
      propPath?: string;
      value: any;
    }
  • 返回:

    • {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 必填;目标栈 id(page 为 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 必填;目标栈 id(page 为 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 目标栈 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 把游标恢复到此处。

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 可选
    查看 HistoryPersistOptions / PersistedHistoryState 类型定义
    ts
    /** historyService 持久化相关 API 的可选配置。 */
    export interface HistoryPersistOptions {
      /** IndexedDB 数据库名,默认 `tmagic-editor`(最终库名会拼上当前 DSL app id)。 */
      dbName?: string;
      /** objectStore 名,默认 `history`。 */
      storeName?: string;
      /** 记录 key,用于区分不同活动页 / 项目,默认 `default`。 */
      key?: IDBValidKey;
      /**
       * 显式指定用于库名隔离的 DSL app id。
       * 缺省时回退到当前 editorService 的 `root.id`;在「先恢复历史再 set root」场景下 root 尚未设置,
       * 需由调用方(如从待加载 DSL 取 id)显式传入,否则会读 / 写到未按 app 隔离的默认库。
       */
      appId?: Id;
    }
    ts
    /**
     * 历史记录的可持久化快照。由 historyService.saveToIndexedDB 写入 IndexedDB,
     * 再由 historyService.restoreFromIndexedDB 读出并重建各 UndoRedo 栈。
     */
    export interface PersistedHistoryState {
      /** 快照结构版本号,便于后续兼容升级。 */
      version: number;
      /**
       * 全部历史栈的序列化快照,按「类型 -> id」两级分组,与 {@link HistorySteps} 对应。
       * 内置 `page` / `codeBlock` / `dataSource`,并包含业务注册的扩展类型。
       */
      steps: {
        page: Record<Id, SerializedUndoRedo<StepValue>>;
        codeBlock: Record<Id, SerializedUndoRedo<CodeBlockStepValue>>;
        dataSource: Record<Id, SerializedUndoRedo<DataSourceStepValue>>;
        [stepType: string]: Record<Id, SerializedUndoRedo<any>>;
      };
      /** 保存时间戳(毫秒)。 */
      savedAt: number;
    }
    ts
    /**
     * UndoRedo 栈的可序列化快照,用于持久化(如写入 IndexedDB)后再还原。
     */
    export interface SerializedUndoRedo<T = any> {
      /** 栈内全部元素(按时间正序,索引 0 为最早一步)。 */
      elementList: T[];
      /** 游标位置(已应用步骤数量)。 */
      listCursor: number;
      /** 栈容量上限。 */
      listMaxSize: number;
    }
  • 返回:

    • {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

  • 详情:

    销毁

Powered by 腾讯视频会员平台技术中心