@tmagic/editor 1.8.0-beta.7 → 1.8.0-beta.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/dist/es/Editor.vue_vue_type_script_setup_true_lang.js +11 -3
  2. package/dist/es/components/CodeBlockEditor.vue_vue_type_script_setup_true_lang.js +3 -3
  3. package/dist/es/components/CodeParams.vue_vue_type_script_setup_true_lang.js +1 -1
  4. package/dist/es/components/CompareForm.vue_vue_type_script_setup_true_lang.js +24 -11
  5. package/dist/es/components/ContentMenu.vue_vue_type_script_setup_true_lang.js +1 -1
  6. package/dist/es/components/FloatingBox.vue_vue_type_script_setup_true_lang.js +2 -2
  7. package/dist/es/components/Icon.vue_vue_type_script_setup_true_lang.js +1 -1
  8. package/dist/es/components/Resizer.vue_vue_type_script_setup_true_lang.js +1 -1
  9. package/dist/es/components/ScrollBar.vue_vue_type_script_setup_true_lang.js +1 -1
  10. package/dist/es/components/ScrollViewer.vue_vue_type_script_setup_true_lang.js +1 -1
  11. package/dist/es/components/SearchInput.vue_vue_type_script_setup_true_lang.js +1 -1
  12. package/dist/es/components/SplitView.vue_vue_type_script_setup_true_lang.js +1 -1
  13. package/dist/es/components/ToolButton.vue_vue_type_script_setup_true_lang.js +1 -1
  14. package/dist/es/components/Tree.vue_vue_type_script_setup_true_lang.js +1 -1
  15. package/dist/es/components/TreeNode.vue_vue_type_script_setup_true_lang.js +1 -1
  16. package/dist/es/fields/Code.vue_vue_type_script_setup_true_lang.js +1 -1
  17. package/dist/es/fields/CodeLink.vue_vue_type_script_setup_true_lang.js +1 -1
  18. package/dist/es/fields/CodeSelect.vue_vue_type_script_setup_true_lang.js +1 -1
  19. package/dist/es/fields/CodeSelectCol.vue_vue_type_script_setup_true_lang.js +1 -1
  20. package/dist/es/fields/CondOpSelect.vue_vue_type_script_setup_true_lang.js +1 -1
  21. package/dist/es/fields/DataSourceFieldSelect/FieldSelect.vue_vue_type_script_setup_true_lang.js +3 -3
  22. package/dist/es/fields/DataSourceFieldSelect/Index.vue_vue_type_script_setup_true_lang.js +1 -1
  23. package/dist/es/fields/DataSourceFields.vue_vue_type_script_setup_true_lang.js +3 -3
  24. package/dist/es/fields/DataSourceInput.vue_vue_type_script_setup_true_lang.js +2 -2
  25. package/dist/es/fields/DataSourceMethodSelect.vue_vue_type_script_setup_true_name_true_lang.js +1 -1
  26. package/dist/es/fields/DataSourceMethods.vue_vue_type_script_setup_true_lang.js +1 -1
  27. package/dist/es/fields/DataSourceMocks.vue_vue_type_script_setup_true_lang.js +3 -3
  28. package/dist/es/fields/DataSourceSelect.vue_vue_type_script_setup_true_lang.js +1 -1
  29. package/dist/es/fields/DisplayConds.vue_vue_type_script_setup_true_lang.js +1 -1
  30. package/dist/es/fields/EventSelect.vue_vue_type_script_setup_true_lang.js +1 -1
  31. package/dist/es/fields/KeyValue.vue_vue_type_script_setup_true_lang.js +1 -1
  32. package/dist/es/fields/PageFragmentSelect.vue_vue_type_script_setup_true_lang.js +1 -1
  33. package/dist/es/fields/StyleSetter/Index.vue_vue_type_script_setup_true_lang.js +1 -1
  34. package/dist/es/fields/StyleSetter/components/BackgroundPosition.vue_vue_type_script_setup_true_lang.js +1 -1
  35. package/dist/es/fields/StyleSetter/components/Border.vue_vue_type_script_setup_true_lang.js +1 -1
  36. package/dist/es/fields/StyleSetter/components/Box.vue_vue_type_script_setup_true_lang.js +1 -1
  37. package/dist/es/fields/StyleSetter/icons/background-repeat/NoRepeat.js +1 -1
  38. package/dist/es/fields/StyleSetter/icons/background-repeat/Repeat.js +1 -1
  39. package/dist/es/fields/StyleSetter/icons/background-repeat/RepeatX.js +1 -1
  40. package/dist/es/fields/StyleSetter/icons/background-repeat/RepeatY.js +1 -1
  41. package/dist/es/fields/StyleSetter/icons/display/Block.js +1 -1
  42. package/dist/es/fields/StyleSetter/icons/display/Flex.js +1 -1
  43. package/dist/es/fields/StyleSetter/icons/display/Inline.js +1 -1
  44. package/dist/es/fields/StyleSetter/icons/display/InlineBlock.js +1 -1
  45. package/dist/es/fields/StyleSetter/icons/display/None.js +1 -1
  46. package/dist/es/fields/StyleSetter/icons/flex-direction/Column.js +1 -1
  47. package/dist/es/fields/StyleSetter/icons/flex-direction/ColumnReverse.js +1 -1
  48. package/dist/es/fields/StyleSetter/icons/flex-direction/Row.js +1 -1
  49. package/dist/es/fields/StyleSetter/icons/flex-direction/RowReverse.js +1 -1
  50. package/dist/es/fields/StyleSetter/icons/justify-content/Center.js +1 -1
  51. package/dist/es/fields/StyleSetter/icons/justify-content/FlexEnd.js +1 -1
  52. package/dist/es/fields/StyleSetter/icons/justify-content/FlexStart.js +1 -1
  53. package/dist/es/fields/StyleSetter/icons/justify-content/SpaceAround.js +1 -1
  54. package/dist/es/fields/StyleSetter/icons/justify-content/SpaceBetween.js +1 -1
  55. package/dist/es/fields/StyleSetter/icons/text-align/Center.js +1 -1
  56. package/dist/es/fields/StyleSetter/icons/text-align/Left.js +1 -1
  57. package/dist/es/fields/StyleSetter/icons/text-align/Right.js +1 -1
  58. package/dist/es/fields/StyleSetter/pro/Background.vue_vue_type_script_setup_true_lang.js +1 -1
  59. package/dist/es/fields/StyleSetter/pro/Border.vue_vue_type_script_setup_true_lang.js +1 -1
  60. package/dist/es/fields/StyleSetter/pro/Font.vue_vue_type_script_setup_true_lang.js +1 -1
  61. package/dist/es/fields/StyleSetter/pro/Layout.vue_vue_type_script_setup_true_lang.js +1 -1
  62. package/dist/es/fields/StyleSetter/pro/Position.vue_vue_type_script_setup_true_lang.js +1 -1
  63. package/dist/es/fields/StyleSetter/pro/Transform.vue_vue_type_script_setup_true_lang.js +1 -1
  64. package/dist/es/fields/UISelect.vue_vue_type_script_setup_true_lang.js +1 -1
  65. package/dist/es/icons/CenterIcon.vue_vue_type_script_setup_true_lang.js +1 -1
  66. package/dist/es/icons/CodeIcon.vue_vue_type_script_setup_true_lang.js +1 -1
  67. package/dist/es/icons/FolderMinusIcon.vue_vue_type_script_setup_true_lang.js +1 -1
  68. package/dist/es/index.js +8 -4
  69. package/dist/es/layouts/AddPageBox.vue_vue_type_script_setup_true_lang.js +1 -1
  70. package/dist/es/layouts/CodeEditor.vue_vue_type_script_setup_true_lang.js +1 -1
  71. package/dist/es/layouts/Framework.vue_vue_type_script_setup_true_lang.js +1 -1
  72. package/dist/es/layouts/NavMenu.vue_vue_type_script_setup_true_lang.js +3 -3
  73. package/dist/es/layouts/NavMenuColumn.vue_vue_type_script_setup_true_lang.js +1 -1
  74. package/dist/es/layouts/history-list/Bucket.vue_vue_type_script_setup_true_lang.js +1 -1
  75. package/dist/es/layouts/history-list/BucketTab.vue_vue_type_script_setup_true_lang.js +1 -1
  76. package/dist/es/layouts/history-list/GroupRow.vue_vue_type_script_setup_true_lang.js +39 -27
  77. package/dist/es/layouts/history-list/HistoryDiffDialog.vue_vue_type_script_setup_true_lang.js +10 -3
  78. package/dist/es/layouts/history-list/HistoryListPanel.vue_vue_type_script_setup_true_lang.js +167 -302
  79. package/dist/es/layouts/history-list/InitialRow.vue_vue_type_script_setup_true_lang.js +1 -1
  80. package/dist/es/layouts/history-list/PageTab.vue_vue_type_script_setup_true_lang.js +1 -1
  81. package/dist/es/layouts/history-list/composables.js +23 -55
  82. package/dist/es/layouts/history-list/useHistoryList.js +56 -0
  83. package/dist/es/layouts/history-list/useHistoryRevert.js +307 -0
  84. package/dist/es/layouts/page-bar/AddButton.vue_vue_type_script_setup_true_lang.js +1 -1
  85. package/dist/es/layouts/page-bar/PageBar.vue_vue_type_script_setup_true_lang.js +1 -1
  86. package/dist/es/layouts/page-bar/PageBarScrollContainer.vue_vue_type_script_setup_true_lang.js +1 -1
  87. package/dist/es/layouts/page-bar/PageList.vue_vue_type_script_setup_true_lang.js +1 -1
  88. package/dist/es/layouts/page-bar/Search.vue_vue_type_script_setup_true_lang.js +2 -2
  89. package/dist/es/layouts/props-panel/FormPanel.vue_vue_type_script_setup_true_lang.js +1 -1
  90. package/dist/es/layouts/props-panel/PropsPanel.vue_vue_type_script_setup_true_lang.js +1 -1
  91. package/dist/es/layouts/sidebar/ComponentListPanel.vue_vue_type_script_setup_true_lang.js +1 -1
  92. package/dist/es/layouts/sidebar/Sidebar.vue_vue_type_script_setup_true_lang.js +1 -1
  93. package/dist/es/layouts/sidebar/code-block/CodeBlockList.vue_vue_type_script_setup_true_lang.js +1 -1
  94. package/dist/es/layouts/sidebar/code-block/CodeBlockListPanel.vue_vue_type_script_setup_true_lang.js +1 -1
  95. package/dist/es/layouts/sidebar/data-source/DataSourceAddButton.vue_vue_type_script_setup_true_lang.js +1 -1
  96. package/dist/es/layouts/sidebar/data-source/DataSourceConfigPanel.vue_vue_type_script_setup_true_lang.js +3 -3
  97. package/dist/es/layouts/sidebar/data-source/DataSourceList.vue_vue_type_script_setup_true_lang.js +1 -1
  98. package/dist/es/layouts/sidebar/data-source/DataSourceListPanel.vue_vue_type_script_setup_true_lang.js +1 -1
  99. package/dist/es/layouts/sidebar/layer/LayerMenu.vue_vue_type_script_setup_true_lang.js +1 -1
  100. package/dist/es/layouts/sidebar/layer/LayerNodeTool.vue_vue_type_script_setup_true_lang.js +1 -1
  101. package/dist/es/layouts/sidebar/layer/LayerPanel.vue_vue_type_script_setup_true_lang.js +1 -1
  102. package/dist/es/layouts/workspace/Breadcrumb.vue_vue_type_script_setup_true_lang.js +1 -1
  103. package/dist/es/layouts/workspace/Workspace.vue_vue_type_script_setup_true_lang.js +1 -1
  104. package/dist/es/layouts/workspace/viewer/NodeListMenu.vue_vue_type_script_setup_true_lang.js +1 -1
  105. package/dist/es/layouts/workspace/viewer/Stage.vue_vue_type_script_setup_true_lang.js +1 -1
  106. package/dist/es/layouts/workspace/viewer/StageOverlay.vue_vue_type_script_setup_true_lang.js +1 -1
  107. package/dist/es/layouts/workspace/viewer/ViewerMenu.vue_vue_type_script_setup_true_lang.js +1 -1
  108. package/dist/es/services/codeBlock.js +32 -28
  109. package/dist/es/services/dataSource.js +43 -34
  110. package/dist/es/services/editor.js +31 -26
  111. package/dist/es/services/history.js +268 -384
  112. package/dist/es/style.css +12 -0
  113. package/dist/es/utils/dep/worker.js +1 -1
  114. package/dist/es/utils/history.js +35 -47
  115. package/dist/style.css +12 -0
  116. package/dist/tmagic-editor.umd.cjs +5524 -4807
  117. package/package.json +8 -8
  118. package/src/Editor.vue +10 -1
  119. package/src/components/CompareForm.vue +28 -7
  120. package/src/fields/DataSourceInput.vue +3 -3
  121. package/src/index.ts +4 -0
  122. package/src/layouts/NavMenu.vue +2 -2
  123. package/src/layouts/history-list/GroupRow.vue +10 -0
  124. package/src/layouts/history-list/HistoryDiffDialog.vue +8 -1
  125. package/src/layouts/history-list/HistoryListPanel.vue +64 -251
  126. package/src/layouts/history-list/PageTab.vue +4 -4
  127. package/src/layouts/history-list/composables.ts +52 -90
  128. package/src/layouts/history-list/useHistoryList.ts +60 -0
  129. package/src/layouts/history-list/useHistoryRevert.ts +406 -0
  130. package/src/services/codeBlock.ts +30 -29
  131. package/src/services/dataSource.ts +37 -37
  132. package/src/services/editor.ts +32 -29
  133. package/src/services/history.ts +340 -430
  134. package/src/theme/history-list-panel.scss +14 -0
  135. package/src/type.ts +185 -92
  136. package/src/utils/history.ts +52 -67
  137. package/types/index.d.ts +466 -297
@@ -18,7 +18,9 @@
18
18
  <TMagicTabs v-model="activeTab" class="m-editor-history-list-tabs">
19
19
  <component
20
20
  :is="tabPaneComponent?.component || 'el-tab-pane'"
21
- v-bind="tabPaneComponent?.props({ name: 'page', label: `页面 (${pageGroups.length})` }) || {}"
21
+ v-bind="
22
+ tabPaneComponent?.props({ name: 'page', label: `${pageName} (${pageGroups.length})`, lazy: true }) || {}
23
+ "
22
24
  >
23
25
  <PageTab
24
26
  :list="pageGroupsDisplay"
@@ -37,7 +39,13 @@
37
39
  <component
38
40
  v-if="!disabledDataSource"
39
41
  :is="tabPaneComponent?.component || 'el-tab-pane'"
40
- v-bind="tabPaneComponent?.props({ name: 'data-source', label: `数据源 (${dataSourceGroups.length})` }) || {}"
42
+ v-bind="
43
+ tabPaneComponent?.props({
44
+ name: 'data-source',
45
+ label: `${dataSourceName} (${dataSourceGroups.length})`,
46
+ lazy: true,
47
+ }) || {}
48
+ "
41
49
  >
42
50
  <BucketTab
43
51
  :config="dataSourceConfig"
@@ -55,7 +63,13 @@
55
63
  <component
56
64
  v-if="!disabledCodeBlock"
57
65
  :is="tabPaneComponent?.component || 'el-tab-pane'"
58
- v-bind="tabPaneComponent?.props({ name: 'code-block', label: `代码块 (${codeBlockGroups.length})` }) || {}"
66
+ v-bind="
67
+ tabPaneComponent?.props({
68
+ name: 'code-block',
69
+ label: `${codeBlockName} (${codeBlockGroups.length})`,
70
+ lazy: true,
71
+ }) || {}
72
+ "
59
73
  >
60
74
  <BucketTab
61
75
  :config="codeBlockConfig"
@@ -74,7 +88,7 @@
74
88
  v-for="tab in extraTabs"
75
89
  :key="tab.name"
76
90
  :is="tabPaneComponent?.component || 'el-tab-pane'"
77
- v-bind="tabPaneComponent?.props({ name: tab.name, label: resolveTabLabel(tab) }) || {}"
91
+ v-bind="tabPaneComponent?.props({ name: tab.name, label: resolveTabLabel(tab), lazy: true }) || {}"
78
92
  >
79
93
  <component :is="tab.component" v-bind="tab.props || {}" v-on="tab.listeners || {}" />
80
94
  </component>
@@ -91,9 +105,6 @@
91
105
  </TMagicTooltip>
92
106
  </template>
93
107
  </TMagicPopover>
94
-
95
- <HistoryDiffDialog ref="diffDialog" :extend-state="extendFormState" />
96
- <HistoryDiffDialog ref="confirmDialog" :is-confirm="true" :extend-state="extendFormState" />
97
108
  </template>
98
109
 
99
110
  <script lang="ts" setup>
@@ -118,18 +129,10 @@
118
129
  * (通过 title / prefix / describe* / isStepDiffable 注入差异)。
119
130
  * 共享的描述生成与折叠状态在 composables.ts 中维护。
120
131
  */
121
- import { computed, inject, markRaw, ref, useTemplateRef, watch } from 'vue';
132
+ import { computed, inject, markRaw, ref, watch } from 'vue';
122
133
  import { Clock, Close } from '@element-plus/icons-vue';
123
134
 
124
- import {
125
- getDesignConfig,
126
- TMagicButton,
127
- tMagicMessage,
128
- tMagicMessageBox,
129
- TMagicPopover,
130
- TMagicTabs,
131
- TMagicTooltip,
132
- } from '@tmagic/design';
135
+ import { getDesignConfig, TMagicButton, tMagicMessage, TMagicPopover, TMagicTabs, TMagicTooltip } from '@tmagic/design';
133
136
  import type { FormState } from '@tmagic/form';
134
137
 
135
138
  import MIcon from '@editor/components/Icon.vue';
@@ -138,15 +141,15 @@ import type {
138
141
  BaseStepValue,
139
142
  CodeBlockStepValue,
140
143
  DataSourceStepValue,
141
- DiffDialogPayload,
142
144
  HistoryBucketConfig,
143
145
  HistoryListExtraTab,
144
146
  } from '@editor/type';
145
147
 
146
148
  import BucketTab from './BucketTab.vue';
147
- import { describeStep, isSingleDiffStepRevertable, useHistoryList } from './composables';
148
- import HistoryDiffDialog from './HistoryDiffDialog.vue';
149
+ import { confirmHistoryAction, describeStep, isSingleDiffStepRevertable } from './composables';
149
150
  import PageTab from './PageTab.vue';
151
+ import { useHistoryList } from './useHistoryList';
152
+ import { useHistoryRevert } from './useHistoryRevert';
150
153
 
151
154
  defineOptions({
152
155
  name: 'MEditorHistoryListPanel',
@@ -173,8 +176,9 @@ const extraTabs = inject<HistoryListExtraTab[]>('historyListExtraTabs', []);
173
176
  /** label 支持字符串或函数,函数形式便于展示动态数量等内容。 */
174
177
  const resolveTabLabel = (tab: HistoryListExtraTab) => (typeof tab.label === 'function' ? tab.label() : tab.label);
175
178
 
179
+ const services = useServices();
176
180
  const { editorService, dataSourceService, codeBlockService, historyService, propsService, stageOverlayService } =
177
- useServices();
181
+ services;
178
182
 
179
183
  /**
180
184
  * 数据源 / 代码块功能可被业务方通过 `disabledDataSource` / `disabledCodeBlock` 禁用,
@@ -198,6 +202,7 @@ const extendFormState = inject<((_state: FormState) => Record<string, any> | Pro
198
202
  'extendFormState',
199
203
  undefined,
200
204
  );
205
+ const getPropsPanelFormState = inject<(() => FormState | undefined) | undefined>('getPropsPanelFormState', undefined);
201
206
 
202
207
  const {
203
208
  expanded,
@@ -214,7 +219,15 @@ const {
214
219
  * 当前活动页的「加载/初始」标记记录(设置 root 时生成),透传给 PageTab 的底部初始行展示。
215
220
  * 基于 historyService 的 reactive state 派生,活动页切换或标记写入后自动刷新。
216
221
  */
217
- const pageMarker = computed(() => historyService.getPageMarker());
222
+ const pageMarker = computed(() => historyService.getMarker('page', editorService.get('page')?.id));
223
+
224
+ /**
225
+ * 各历史类型的展示名称(页面 / 数据源 / 代码块),由 historyService.state.stepNames 配置,
226
+ * 业务方可通过 historyService.setStepName / registerStepType 覆盖。基于 reactive state 派生,配置变更后自动刷新。
227
+ */
228
+ const pageName = computed(() => historyService.getStepName('page'));
229
+ const dataSourceName = computed(() => historyService.getStepName('dataSource'));
230
+ const codeBlockName = computed(() => historyService.getStepName('codeBlock'));
218
231
 
219
232
  /** 代码块 step 仅 update(前后 content 都存在)时可查看差异。 */
220
233
  const isStepDiffable = (step: BaseStepValue) => Boolean(step.diff?.[0]?.oldSchema && step.diff?.[0]?.newSchema);
@@ -222,23 +235,26 @@ const isStepDiffable = (step: BaseStepValue) => Boolean(step.diff?.[0]?.oldSchem
222
235
  /**
223
236
  * 数据源 / 代码块两类 bucket 历史的整体渲染配置:把 title / prefix 与各自的描述、
224
237
  * 可差异、可回滚判定收敛为单一对象整体注入 BucketTab,组件内部按需读取。
238
+ * title / 描述回退名取自可配置的展示名称,故用 computed 使其随 stepNames 变更刷新。
225
239
  */
226
240
  // 数据源/代码块不做相邻合并,每组恒为单步,省略 describeGroup,由 toRowGroup 回退到 describeStep。
227
- const dataSourceConfig: HistoryBucketConfig<DataSourceStepValue> = {
228
- title: '数据源',
241
+ const dataSourceConfig = computed<HistoryBucketConfig<DataSourceStepValue>>(() => ({
242
+ title: dataSourceName.value,
229
243
  prefix: 'ds',
230
- describeStep: (step: DataSourceStepValue): string => describeStep(step, (schema) => schema?.title, '数据源'),
244
+ describeStep: (step: DataSourceStepValue): string =>
245
+ describeStep(step, (schema) => schema?.title, dataSourceName.value),
231
246
  isStepDiffable,
232
247
  isStepRevertable: isSingleDiffStepRevertable,
233
- };
248
+ }));
234
249
 
235
- const codeBlockConfig: HistoryBucketConfig<CodeBlockStepValue> = {
236
- title: '代码块',
250
+ const codeBlockConfig = computed<HistoryBucketConfig<CodeBlockStepValue>>(() => ({
251
+ title: codeBlockName.value,
237
252
  prefix: 'cb',
238
- describeStep: (step: CodeBlockStepValue): string => describeStep(step, (content) => content?.name, '代码块'),
253
+ describeStep: (step: CodeBlockStepValue): string =>
254
+ describeStep(step, (content) => content?.name, codeBlockName.value),
239
255
  isStepDiffable,
240
256
  isStepRevertable: isSingleDiffStepRevertable,
241
- };
257
+ }));
242
258
 
243
259
  /** 把"目标 step 索引"翻译成"目标 cursor"(已应用步骤数量)。 */
244
260
  const indexToCursor = (index: number) => index + 1;
@@ -254,7 +270,7 @@ const onPageGoto = (index: number) => {
254
270
  * - 该 step 涉及的节点都已不存在(如删除记录、被撤销的新增)时给出提示,不做选中。
255
271
  */
256
272
  const onPageSelect = async (index: number) => {
257
- const step = historyService.getPageStepList()[index]?.step;
273
+ const step = historyService.getStepList('page', editorService.get('page')?.id)[index]?.step;
258
274
  if (!step) return;
259
275
  const targetId = (step.diff ?? [])
260
276
  .map((item) => item.newSchema?.id ?? item.oldSchema?.id)
@@ -294,222 +310,13 @@ const onCodeBlockGotoInitial = (id: string | number) => {
294
310
  codeBlockService.goto(id, 0);
295
311
  };
296
312
 
297
- const diffDialogRef = useTemplateRef<InstanceType<typeof HistoryDiffDialog>>('diffDialog');
298
- const confirmDialogRef = useTemplateRef<InstanceType<typeof HistoryDiffDialog>>('confirmDialog');
299
-
300
- /**
301
- * 三类历史(页面 / 数据源 / 代码块)差异弹窗入参的构造差异,收敛为一份配置:
302
- * 仅「分组来源、当前值读取、类型 / 展示名提取」不同,定位 step、校验前后值、组装 payload 的流程共用。
303
- */
304
- interface DiffPayloadSource {
305
- /** 表单类别:节点 / 数据源 / 代码块。 */
306
- category: DiffDialogPayload['category'];
307
- /** 该类别按时间正序的历史分组列表(含已撤销)。 */
308
- groups: () => { id?: string | number; steps: { index: number; step: { diff?: any[] } }[] }[];
309
- /** 读取目标当前实际值,用于「与当前对比」;不存在时返回空即禁用对比。 */
310
- getCurrent: (_id: string | number) => Record<string, any> | null | undefined;
311
- /** 由新/旧快照提取展示名(含各自的兜底,如节点回退 type、数据源 / 代码块回退 id)。 */
312
- resolveLabel: (_newSchema: Record<string, any>, _oldSchema: Record<string, any>, _id: string | number) => string;
313
- /** 由新/旧快照提取类型;代码块无 type 字段则不传。 */
314
- resolveType?: (_newSchema: Record<string, any>, _oldSchema: Record<string, any>) => string;
315
- }
316
-
317
- /**
318
- * 构造差异弹窗入参:仅 update(前后值都存在)可对比。
319
- * - 页面(无 id):在全部分组中按 index 定位 step,目标 id 取自快照;
320
- * - 数据源 / 代码块(带 id):先匹配分组 id 再按 index 定位。
321
- * 无可对比内容(多节点 / add / remove)或定位不到时返回 null。
322
- */
323
- const buildDiffPayload = (source: DiffPayloadSource, index: number, id?: string | number): DiffDialogPayload | null => {
324
- for (const group of source.groups()) {
325
- if (id !== undefined && group.id !== id) continue;
326
- const step = group.steps.find((s) => s.index === index)?.step;
327
- if (!step) continue;
328
- const oldSchema = step.diff?.[0]?.oldSchema as Record<string, any> | undefined;
329
- const newSchema = step.diff?.[0]?.newSchema as Record<string, any> | undefined;
330
- if (!oldSchema || !newSchema) return null;
331
- const targetId = id ?? newSchema.id ?? oldSchema.id;
332
- const type = source.resolveType?.(newSchema, oldSchema);
333
- return {
334
- category: source.category,
335
- ...(type !== undefined ? { type } : {}),
336
- lastValue: oldSchema,
337
- value: newSchema,
338
- currentValue: (targetId !== undefined ? source.getCurrent(targetId) : null) || null,
339
- targetLabel: source.resolveLabel(newSchema, oldSchema, targetId),
340
- id: targetId,
341
- };
342
- }
343
- return null;
344
- };
345
-
346
- const buildPageDiffPayload = (index: number): DiffDialogPayload | null =>
347
- buildDiffPayload(
348
- {
349
- category: 'node',
350
- groups: () => historyService.getPageHistoryGroups(),
351
- getCurrent: (id) => editorService.getNodeById(id) as Record<string, any> | null,
352
- resolveType: (n, o) => n.type || o.type || '',
353
- resolveLabel: (n, o) => n.name || o.name || n.type || o.type || '',
354
- },
355
- index,
356
- );
357
-
358
- const buildDataSourceDiffPayload = (id: string | number, index: number): DiffDialogPayload | null =>
359
- buildDiffPayload(
360
- {
361
- category: 'data-source',
362
- groups: () => historyService.getDataSourceHistoryGroups(),
363
- getCurrent: (id) => dataSourceService.getDataSourceById(`${id}`) as Record<string, any> | null,
364
- resolveType: (n, o) => n.type || o.type || 'base',
365
- resolveLabel: (n, o, id) => n.title || o.title || `${id}`,
366
- },
367
- index,
368
- id,
369
- );
370
-
371
- const buildCodeBlockDiffPayload = (id: string | number, index: number): DiffDialogPayload | null =>
372
- buildDiffPayload(
373
- {
374
- category: 'code-block',
375
- groups: () => historyService.getCodeBlockHistoryGroups(),
376
- getCurrent: (id) => codeBlockService.getCodeContentById(id) as Record<string, any> | null,
377
- resolveLabel: (n, o, id) => n.name || o.name || `${id}`,
378
- },
379
- index,
380
- id,
381
- );
382
-
383
- const onPageDiff = (index: number) => {
384
- const payload = buildPageDiffPayload(index);
385
- if (payload) diffDialogRef.value?.open(payload);
386
- };
387
-
388
- const onDataSourceDiff = (id: string | number, index: number) => {
389
- const payload = buildDataSourceDiffPayload(id, index);
390
- if (payload) diffDialogRef.value?.open(payload);
391
- };
392
-
393
- const onCodeBlockDiff = (id: string | number, index: number) => {
394
- const payload = buildCodeBlockDiffPayload(id, index);
395
- if (payload) diffDialogRef.value?.open(payload);
396
- };
397
-
398
- /**
399
- * 「回滚」统一入口:把目标历史步骤的修改作为一次新操作反向应用(类 git revert),
400
- * 不破坏原有栈结构。各 service 内部完成反向 + 入栈,并自带描述用于面板展示。
401
- *
402
- * 交互:
403
- * - 可差异对比的步骤(单节点 / 单实体 update):弹出差异弹窗供用户确认,点「确定回滚」再执行;
404
- * - 无法对比的步骤(add / remove / 多节点更新,payload 为 null):弹出普通二次确认框,确认后执行。
405
- *
406
- * 页面 / 数据源 / 代码块三类回滚仅「差异入参构造」与「实际 revert 调用」不同,
407
- * 由调用方分别传入 payload 与 revert,公共的弹窗 / 确认流程在此收敛。
408
- */
409
- const runRevert = (payload: DiffDialogPayload | null): Promise<boolean> => {
410
- if (payload && confirmDialogRef.value) {
411
- return confirmDialogRef.value.confirm(payload);
412
- }
413
- return confirmRevert();
414
- };
415
-
416
- /**
417
- * 回滚前置校验:若该历史步骤回滚所依赖的目标数据已被删除,则无法回滚。
418
- * - update(把旧值写回):被修改的目标必须仍存在;
419
- * - 页面 remove(还原被删节点):被删节点的原父容器必须仍存在,否则无处插回;
420
- * add(回滚即删除)即使目标已不在,也已达成「删除」目的,不视为失败。
421
- *
422
- * 命中时弹出「回滚失败」提示并返回 true,调用方据此中止本次回滚。
423
- */
424
- const isPageRevertTargetMissing = (index: number): boolean => {
425
- const step = historyService.getPageStepList()[index]?.step;
426
- if (!step) return false;
427
- if (step.opType === 'update') {
428
- return (step.diff ?? []).some((item) => {
429
- const id = item.newSchema?.id ?? item.oldSchema?.id;
430
- return id !== undefined && !editorService.getNodeById(id, false);
431
- });
432
- }
433
- if (step.opType === 'remove') {
434
- return (step.diff ?? []).some(
435
- (item) => item.parentId !== undefined && !editorService.getNodeById(item.parentId, false),
436
- );
437
- }
438
- return false;
439
- };
440
-
441
- /** 数据源 update 步骤回滚时,对应数据源必须仍存在(已删除则无处写回旧值)。 */
442
- const isDataSourceRevertTargetMissing = (id: string | number, index: number): boolean => {
443
- const step = historyService.getDataSourceStepList(id)[index]?.step;
444
- return Boolean(step && step.opType === 'update' && !dataSourceService.getDataSourceById(`${id}`));
445
- };
446
-
447
- /** 代码块 update 步骤回滚时,对应代码块必须仍存在(已删除则无处写回旧值)。 */
448
- const isCodeBlockRevertTargetMissing = (id: string | number, index: number): boolean => {
449
- const step = historyService.getCodeBlockStepList(id)[index]?.step;
450
- return Boolean(step && step.opType === 'update' && !codeBlockService.getCodeContentById(id));
451
- };
452
-
453
- /** 目标数据已被删除、无法回滚时的统一提示。 */
454
- const showRevertTargetMissing = () => {
455
- tMagicMessage.error('回滚失败:该记录对应的数据已被删除');
456
- };
457
-
458
- const onPageRevert = (index: number) => {
459
- if (isPageRevertTargetMissing(index)) {
460
- showRevertTargetMissing();
461
- return Promise.resolve(null);
462
- }
463
- return runRevert(buildPageDiffPayload(index)).then((result) => (result ? editorService.revertPageStep(index) : null));
464
- };
465
-
466
- const onDataSourceRevert = (id: string | number, index: number) => {
467
- if (isDataSourceRevertTargetMissing(id, index)) {
468
- showRevertTargetMissing();
469
- return Promise.resolve(null);
470
- }
471
- return runRevert(buildDataSourceDiffPayload(id, index)).then((result) =>
472
- result ? dataSourceService.revert(id, index) : null,
473
- );
474
- };
475
-
476
- const onCodeBlockRevert = (id: string | number, index: number) => {
477
- if (isCodeBlockRevertTargetMissing(id, index)) {
478
- showRevertTargetMissing();
479
- return Promise.resolve(null);
480
- }
481
- return runRevert(buildCodeBlockDiffPayload(id, index)).then((result) =>
482
- result ? codeBlockService.revert(id, index) : null,
483
- );
484
- };
485
-
486
- /**
487
- * 「回滚」二次确认:新增 / 删除 / 多节点更新等无法做差异对比的步骤,
488
- * 不弹差异弹窗,改用一个普通确认框替代「确定回滚」按钮,避免点击后无任何提示直接执行。
489
- * 用户取消时返回 false,调用方据此中止回滚。
490
- */
491
- const confirmRevert = (): Promise<boolean> =>
492
- confirmDialog(
493
- '确定回滚该步骤吗?回滚会将该操作作为一条新记录反向应用(新增将被删除、删除将被还原),不影响后续历史记录。',
494
- );
495
-
496
313
  /**
497
- * 通用二次确认弹窗:清空历史 / 无法差异对比的回滚等会改变状态的操作,先弹出确认框,
498
- * 用户点击「确定」返回 true,取消(confirm reject)时返回 false 并静默忽略。
314
+ * 「单步回滚」与「查看差异」的完整逻辑收敛到 useHistoryRevert,面板与业务方共用:
315
+ * 二者均由 useHistoryRevert 内部按需动态挂载 HistoryDiffDialog,
316
+ * 业务方亦可直接 import useHistoryRevert(services) 调用,无需自行挂载任何弹窗。
499
317
  */
500
- const confirmDialog = async (message: string): Promise<boolean> => {
501
- try {
502
- await tMagicMessageBox.confirm(message, '提示', {
503
- confirmButtonText: '确定',
504
- cancelButtonText: '取消',
505
- type: 'warning',
506
- });
507
- return true;
508
- // eslint-disable-next-line no-unused-vars
509
- } catch (e) {
510
- return false;
511
- }
512
- };
318
+ const { onPageRevert, onDataSourceRevert, onCodeBlockRevert, onPageDiff, onDataSourceDiff, onCodeBlockDiff } =
319
+ useHistoryRevert(services, { extendState: extendFormState, getPropsPanelFormState });
513
320
 
514
321
  /**
515
322
  * 把内存中(已清空对应类别后的)历史状态重新写回 IndexedDB,
@@ -527,27 +334,33 @@ const syncIndexedDB = async () => {
527
334
 
528
335
  const onPageClear = async () => {
529
336
  if (
530
- await confirmDialog('确定清空当前页面的历史记录吗?清空后将无法撤销/重做之前的操作,本地保存的记录也会一并删除。')
337
+ await confirmHistoryAction(
338
+ '确定清空当前页面的历史记录吗?清空后将无法撤销/重做之前的操作,本地保存的记录也会一并删除。',
339
+ )
531
340
  ) {
532
- historyService.clearPage();
341
+ historyService.clear('page', editorService.get('page')?.id);
533
342
  await syncIndexedDB();
534
343
  }
535
344
  };
536
345
 
537
346
  const onDataSourceClear = async () => {
538
347
  if (
539
- await confirmDialog('确定清空数据源的历史记录吗?清空后将无法撤销/重做之前的操作,本地保存的记录也会一并删除。')
348
+ await confirmHistoryAction(
349
+ '确定清空数据源的历史记录吗?清空后将无法撤销/重做之前的操作,本地保存的记录也会一并删除。',
350
+ )
540
351
  ) {
541
- historyService.clearDataSource();
352
+ historyService.clear('dataSource');
542
353
  await syncIndexedDB();
543
354
  }
544
355
  };
545
356
 
546
357
  const onCodeBlockClear = async () => {
547
358
  if (
548
- await confirmDialog('确定清空代码块的历史记录吗?清空后将无法撤销/重做之前的操作,本地保存的记录也会一并删除。')
359
+ await confirmHistoryAction(
360
+ '确定清空代码块的历史记录吗?清空后将无法撤销/重做之前的操作,本地保存的记录也会一并删除。',
361
+ )
549
362
  ) {
550
- historyService.clearCodeBlock();
363
+ historyService.clear('codeBlock');
551
364
  await syncIndexedDB();
552
365
  }
553
366
  };
@@ -34,7 +34,7 @@ import { computed } from 'vue';
34
34
 
35
35
  import { TMagicScrollbar } from '@tmagic/design';
36
36
 
37
- import type { HistoryRowDescriptor, PageHistoryGroup, StepValue } from '@editor/type';
37
+ import type { HistoryGroup, HistoryRowDescriptor, StepValue } from '@editor/type';
38
38
 
39
39
  import type { HistoryRowGroup } from './composables';
40
40
  import {
@@ -53,7 +53,7 @@ defineOptions({
53
53
 
54
54
  const props = defineProps<{
55
55
  /** 当前活动页面的历史分组列表,已按时间倒序排好(最新一组在最前)。空数组时显示空态。 */
56
- list: PageHistoryGroup[];
56
+ list: HistoryGroup<StepValue>[];
57
57
  /**
58
58
  * 共享的折叠状态表(key -> 是否展开,缺省或 true 为展开、false 为收起),由顶层 panel 统一维护。
59
59
  * 本 tab 使用 `pg-${组内首步 index}` 作为 key——以稳定的 step 索引而非展示位置标识分组,
@@ -105,9 +105,9 @@ const descriptor: HistoryRowDescriptor<StepValue> = {
105
105
  isStepRevertable: isPageStepRevertable,
106
106
  };
107
107
 
108
- const rowKey = (group: PageHistoryGroup) => `pg-${group.steps[0]?.index}`;
108
+ const rowKey = (group: HistoryGroup<StepValue>) => `pg-${group.steps[0]?.index}`;
109
109
 
110
- const toRow = (group: PageHistoryGroup): HistoryRowGroup => toRowGroup(group, rowKey(group), descriptor);
110
+ const toRow = (group: HistoryGroup<StepValue>): HistoryRowGroup => toRowGroup(group, rowKey(group), descriptor);
111
111
 
112
112
  /**
113
113
  * 是否处于"初始状态"——即对应页面历史栈 cursor===0: