@tmagic/editor 1.8.0-beta.4 → 1.8.0-beta.6

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 (76) hide show
  1. package/dist/es/components/CompareForm.vue_vue_type_script_setup_true_lang.js +1 -1
  2. package/dist/es/components/ToolButton.vue_vue_type_script_setup_true_lang.js +6 -6
  3. package/dist/es/fields/CodeSelectCol.vue_vue_type_script_setup_true_lang.js +4 -1
  4. package/dist/es/fields/DataSourceFieldSelect/FieldSelect.vue_vue_type_script_setup_true_lang.js +8 -2
  5. package/dist/es/fields/DataSourceFields.vue_vue_type_script_setup_true_lang.js +25 -8
  6. package/dist/es/fields/DataSourceMethodSelect.vue_vue_type_script_setup_true_name_true_lang.js +6 -4
  7. package/dist/es/fields/DataSourceMethods.vue_vue_type_script_setup_true_lang.js +25 -12
  8. package/dist/es/fields/StyleSetter/components/Border.vue_vue_type_script_setup_true_lang.js +27 -5
  9. package/dist/es/index.js +6 -3
  10. package/dist/es/initService.js +1 -1
  11. package/dist/es/layouts/Framework.vue_vue_type_script_setup_true_lang.js +1 -1
  12. package/dist/es/layouts/history-list/Bucket.vue_vue_type_script_setup_true_lang.js +19 -55
  13. package/dist/es/layouts/history-list/BucketTab.vue_vue_type_script_setup_true_lang.js +22 -39
  14. package/dist/es/layouts/history-list/GroupRow.vue_vue_type_script_setup_true_lang.js +149 -103
  15. package/dist/es/layouts/history-list/HistoryDiffDialog.vue_vue_type_script_setup_true_lang.js +32 -8
  16. package/dist/es/layouts/history-list/HistoryListPanel.vue_vue_type_script_setup_true_lang.js +335 -213
  17. package/dist/es/layouts/history-list/InitialRow.vue_vue_type_script_setup_true_lang.js +36 -7
  18. package/dist/es/layouts/history-list/PageTab.vue_vue_type_script_setup_true_lang.js +47 -60
  19. package/dist/es/layouts/history-list/composables.js +73 -32
  20. package/dist/es/layouts/page-bar/PageBar.vue_vue_type_script_setup_true_lang.js +4 -2
  21. package/dist/es/layouts/sidebar/Sidebar.vue_vue_type_script_setup_true_lang.js +5 -1
  22. package/dist/es/layouts/sidebar/data-source/DataSourceConfigPanel.vue_vue_type_script_setup_true_lang.js +16 -3
  23. package/dist/es/layouts/sidebar/data-source/DataSourceListPanel.vue_vue_type_script_setup_true_lang.js +23 -1
  24. package/dist/es/services/codeBlock.js +96 -37
  25. package/dist/es/services/dataSource.js +70 -26
  26. package/dist/es/services/editor.js +266 -104
  27. package/dist/es/services/history.js +271 -212
  28. package/dist/es/services/ui.js +3 -0
  29. package/dist/es/style.css +53 -7
  30. package/dist/es/utils/data-source/index.js +2 -0
  31. package/dist/es/utils/editor.js +103 -49
  32. package/dist/es/utils/history.js +232 -0
  33. package/dist/es/utils/indexed-db.js +86 -0
  34. package/dist/es/utils/undo-redo.js +60 -1
  35. package/dist/style.css +53 -7
  36. package/dist/tmagic-editor.umd.cjs +2064 -1000
  37. package/package.json +7 -7
  38. package/src/components/CompareForm.vue +3 -1
  39. package/src/components/ToolButton.vue +2 -2
  40. package/src/fields/CodeSelectCol.vue +7 -1
  41. package/src/fields/DataSourceFieldSelect/FieldSelect.vue +16 -2
  42. package/src/fields/DataSourceFields.vue +37 -8
  43. package/src/fields/DataSourceMethodSelect.vue +9 -4
  44. package/src/fields/DataSourceMethods.vue +42 -21
  45. package/src/fields/StyleSetter/components/Border.vue +15 -6
  46. package/src/index.ts +1 -0
  47. package/src/initService.ts +1 -1
  48. package/src/layouts/Framework.vue +1 -1
  49. package/src/layouts/history-list/Bucket.vue +34 -71
  50. package/src/layouts/history-list/BucketTab.vue +46 -54
  51. package/src/layouts/history-list/GroupRow.vue +146 -111
  52. package/src/layouts/history-list/HistoryDiffDialog.vue +94 -68
  53. package/src/layouts/history-list/HistoryListPanel.vue +298 -115
  54. package/src/layouts/history-list/InitialRow.vue +28 -9
  55. package/src/layouts/history-list/PageTab.vue +57 -51
  56. package/src/layouts/history-list/composables.ts +157 -36
  57. package/src/layouts/page-bar/PageBar.vue +4 -2
  58. package/src/layouts/sidebar/Sidebar.vue +6 -1
  59. package/src/layouts/sidebar/data-source/DataSourceConfigPanel.vue +30 -2
  60. package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +26 -1
  61. package/src/services/codeBlock.ts +113 -37
  62. package/src/services/dataSource.ts +94 -40
  63. package/src/services/editor.ts +376 -136
  64. package/src/services/history.ts +306 -209
  65. package/src/services/ui.ts +7 -0
  66. package/src/theme/history-list-panel.scss +72 -5
  67. package/src/theme/page-bar.scss +0 -4
  68. package/src/theme/style-setter/border.scss +4 -1
  69. package/src/type.ts +183 -64
  70. package/src/utils/data-source/index.ts +2 -0
  71. package/src/utils/editor.ts +166 -60
  72. package/src/utils/history.ts +308 -0
  73. package/src/utils/index.ts +2 -0
  74. package/src/utils/indexed-db.ts +122 -0
  75. package/src/utils/undo-redo.ts +88 -0
  76. package/types/index.d.ts +813 -302
@@ -1,7 +1,8 @@
1
1
  import { CODE_DRAFT_STORAGE_KEY } from "../type.js";
2
2
  import BaseService from "./BaseService.js";
3
- import history_default from "./history.js";
4
3
  import { getEditorConfig } from "../utils/config.js";
4
+ import { describeRevertStep, getLastPushedHistoryIds } from "../utils/history.js";
5
+ import history_default from "./history.js";
5
6
  import storage_default, { Protocol } from "./storage.js";
6
7
  import { COPY_CODE_STORAGE_KEY } from "../utils/editor.js";
7
8
  import editor_default from "./editor.js";
@@ -20,17 +21,6 @@ var canUsePluginMethods = {
20
21
  ],
21
22
  sync: ["setCodeDslByIdSync"]
22
23
  };
23
- /**
24
- * 「回滚」生成的新 step 简短描述。仅 service 层使用。
25
- */
26
- var describeRevertCodeBlockStep = (step) => {
27
- const { oldContent, newContent, changeRecords, id } = step;
28
- if (oldContent === null && newContent) return `撤回新增 ${newContent.name || newContent.id || id}`;
29
- if (oldContent && newContent === null) return `还原已删除的 ${oldContent.name || oldContent.id || id}`;
30
- const name = newContent?.name || oldContent?.name || `${id}`;
31
- const propPath = changeRecords?.[0]?.propPath;
32
- return propPath ? `还原 ${name} · ${propPath}` : `还原 ${name}`;
33
- };
34
24
  var CodeBlock = class extends BaseService {
35
25
  state = reactive({
36
26
  codeDsl: null,
@@ -39,6 +29,16 @@ var CodeBlock = class extends BaseService {
39
29
  undeletableList: [],
40
30
  paramsColConfig: void 0
41
31
  });
32
+ /**
33
+ * 最近一次写入历史栈的代码块历史记录 uuid(单条写入场景:新增 / 更新)。
34
+ * 供 setCodeDslById(Sync)AndGetHistoryId 取回,普通方法不读取它。
35
+ */
36
+ lastPushedHistoryId = null;
37
+ /**
38
+ * deleteCodeDslByIds 一次删除多个代码块时,按写入顺序收集的历史记录 uuid 列表。
39
+ * 在 deleteCodeDslByIds 入口处重置,供 deleteCodeDslByIdsAndGetHistoryId 取回。
40
+ */
41
+ lastDeletedHistoryIds = [];
42
42
  constructor() {
43
43
  super([...canUsePluginMethods.async.map((methodName) => ({
44
44
  name: methodName,
@@ -121,13 +121,13 @@ var CodeBlock = class extends BaseService {
121
121
  ...codeConfigProcessed
122
122
  };
123
123
  const newContent = cloneDeep$1(codeDsl[id]);
124
- if (!doNotPushHistory) history_default.pushCodeBlock(id, {
124
+ if (!doNotPushHistory) this.lastPushedHistoryId = history_default.pushCodeBlock(id, {
125
125
  oldContent,
126
126
  newContent,
127
127
  changeRecords,
128
128
  historyDescription,
129
129
  source: historySource
130
- });
130
+ })?.uuid ?? null;
131
131
  this.emit("addOrUpdate", id, codeDsl[id]);
132
132
  }
133
133
  /**
@@ -210,18 +210,59 @@ var CodeBlock = class extends BaseService {
210
210
  async deleteCodeDslByIds(codeIds, { doNotPushHistory = false, historyDescription, historySource } = {}) {
211
211
  const currentDsl = await this.getCodeDsl();
212
212
  if (!currentDsl) return;
213
+ this.lastDeletedHistoryIds = [];
213
214
  codeIds.forEach((id) => {
214
215
  const oldContent = currentDsl[id] ? cloneDeep$1(currentDsl[id]) : null;
215
216
  delete currentDsl[id];
216
- if (oldContent && !doNotPushHistory) history_default.pushCodeBlock(id, {
217
- oldContent,
218
- newContent: null,
219
- historyDescription,
220
- source: historySource
221
- });
217
+ if (oldContent && !doNotPushHistory) {
218
+ const uuid = history_default.pushCodeBlock(id, {
219
+ oldContent,
220
+ newContent: null,
221
+ historyDescription,
222
+ source: historySource
223
+ })?.uuid;
224
+ if (uuid) this.lastDeletedHistoryIds.push(uuid);
225
+ }
222
226
  this.emit("remove", id);
223
227
  });
224
228
  }
229
+ /**
230
+ * 下列 *AndGetHistoryId 方法与对应的写入方法行为完全一致,
231
+ * 返回值在 {@link DslOpWithHistoryIdsResult} 中同时包含原操作结果与本次写入的历史 uuid 列表,
232
+ * 可用于精确引用 / 定位该条历史记录(埋点、revert、跨端同步等)。
233
+ *
234
+ * 当本次操作未写入历史(doNotPushHistory 为 true、或无对应记录)时 historyIds 为 `[]`。
235
+ */
236
+ /** 等价于 {@link setCodeDslById},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
237
+ async setCodeDslByIdAndGetHistoryId(id, codeConfig, options = {}) {
238
+ this.lastPushedHistoryId = null;
239
+ await this.setCodeDslById(id, codeConfig, options);
240
+ return {
241
+ result: void 0,
242
+ historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
243
+ };
244
+ }
245
+ /** 等价于 {@link setCodeDslByIdSync},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
246
+ setCodeDslByIdSyncAndGetHistoryId(id, codeConfig, force = true, options = {}) {
247
+ this.lastPushedHistoryId = null;
248
+ this.setCodeDslByIdSync(id, codeConfig, force, options);
249
+ return {
250
+ result: void 0,
251
+ historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
252
+ };
253
+ }
254
+ /**
255
+ * 等价于 {@link deleteCodeDslByIds},并额外返回本次写入的全部历史记录 uuid(按删除顺序)。
256
+ * 一次删除多个代码块会产生多条历史记录;未写入任何历史时 historyIds 为 `[]`。
257
+ */
258
+ async deleteCodeDslByIdsAndGetHistoryId(codeIds, options = {}) {
259
+ this.lastDeletedHistoryIds = [];
260
+ await this.deleteCodeDslByIds(codeIds, options);
261
+ return {
262
+ result: void 0,
263
+ historyIds: [...this.lastDeletedHistoryIds]
264
+ };
265
+ }
225
266
  setParamsColConfig(config) {
226
267
  this.state.paramsColConfig = config;
227
268
  }
@@ -297,11 +338,27 @@ var CodeBlock = class extends BaseService {
297
338
  async revert(id, index) {
298
339
  const entry = history_default.getCodeBlockStepList(id)[index];
299
340
  if (!entry?.applied) return null;
300
- if (entry.step.oldContent && entry.step.newContent && !entry.step.changeRecords?.length) return null;
301
- const description = `回滚 #${index + 1}: ${describeRevertCodeBlockStep(entry.step)}`;
341
+ const { oldSchema, newSchema, changeRecords } = entry.step.diff?.[0] ?? {};
342
+ if (oldSchema && newSchema && !changeRecords?.length) return null;
343
+ const description = `回滚 #${index + 1}: ${describeRevertStep(entry.step.id, entry.step.diff?.[0], (s) => s.name)}`;
302
344
  return await this.applyRevertStep(entry.step, description);
303
345
  }
304
346
  /**
347
+ * 通过历史记录 uuid 回滚代码块历史步骤,语义同 {@link revert},
348
+ * 仅无需调用方再传 codeBlockId 与 index:内部会按 uuid 在全部代码块栈中定位对应步骤后再回滚。
349
+ * 按数组顺序依次回滚,返回与入参同序的结果列表(某项失败时为 `null`)。
350
+ *
351
+ * @param uuids 目标历史记录的 uuid 列表,通常由 {@link setCodeDslByIdAndGetHistoryId} 等方法返回的 `historyIds`
352
+ */
353
+ async revertById(uuids) {
354
+ const results = [];
355
+ for (const uuid of uuids) {
356
+ const location = history_default.findCodeBlockStepLocationByUuid(uuid);
357
+ results.push(location ? await this.revert(location.id, location.index) : null);
358
+ }
359
+ return results;
360
+ }
361
+ /**
305
362
  * 生成代码块唯一id
306
363
  * @returns {Id} 代码块唯一id
307
364
  */
@@ -366,22 +423,23 @@ var CodeBlock = class extends BaseService {
366
423
  * 差异仅在于通过公开的 setCodeDslByIdSync / deleteCodeDslByIds 触发 push。
367
424
  */
368
425
  async applyRevertStep(step, historyDescription) {
369
- const { id, oldContent, newContent, changeRecords } = step;
370
- if (oldContent === null && newContent) {
426
+ const { id } = step;
427
+ const { oldSchema, newSchema, changeRecords } = step.diff?.[0] ?? {};
428
+ if (!oldSchema && newSchema) {
371
429
  await this.deleteCodeDslByIds([id], {
372
430
  historyDescription,
373
431
  historySource: "rollback"
374
432
  });
375
433
  return history_default.getCodeBlockStepList(id).slice(-1)[0]?.step ?? null;
376
434
  }
377
- if (oldContent && newContent === null) {
378
- this.setCodeDslByIdSync(id, cloneDeep$1(oldContent), true, {
435
+ if (oldSchema && !newSchema) {
436
+ this.setCodeDslByIdSync(id, cloneDeep$1(oldSchema), true, {
379
437
  historyDescription,
380
438
  historySource: "rollback"
381
439
  });
382
440
  return history_default.getCodeBlockStepList(id).slice(-1)[0]?.step ?? null;
383
441
  }
384
- if (!oldContent || !newContent) return null;
442
+ if (!oldSchema || !newSchema) return null;
385
443
  if (changeRecords?.length) {
386
444
  const current = this.getCodeContentById(id);
387
445
  if (!current) return null;
@@ -392,17 +450,17 @@ var CodeBlock = class extends BaseService {
392
450
  fallbackToFullReplace = true;
393
451
  break;
394
452
  }
395
- const value = cloneDeep$1(getValueByKeyPath(record.propPath, oldContent));
453
+ const value = cloneDeep$1(getValueByKeyPath(record.propPath, oldSchema));
396
454
  setValueByKeyPath(record.propPath, value, patched);
397
455
  }
398
- this.setCodeDslByIdSync(id, fallbackToFullReplace ? cloneDeep$1(oldContent) : patched, true, {
456
+ this.setCodeDslByIdSync(id, fallbackToFullReplace ? cloneDeep$1(oldSchema) : patched, true, {
399
457
  changeRecords,
400
458
  historyDescription,
401
459
  historySource: "rollback"
402
460
  });
403
461
  return history_default.getCodeBlockStepList(id).slice(-1)[0]?.step ?? null;
404
462
  }
405
- this.setCodeDslByIdSync(id, cloneDeep$1(oldContent), true, {
463
+ this.setCodeDslByIdSync(id, cloneDeep$1(oldSchema), true, {
406
464
  historyDescription,
407
465
  historySource: "rollback"
408
466
  });
@@ -423,19 +481,20 @@ var CodeBlock = class extends BaseService {
423
481
  * @param reverse true=撤销,false=重做
424
482
  */
425
483
  async applyHistoryStep(step, reverse) {
426
- const { id, oldContent, newContent, changeRecords } = step;
427
- if (oldContent === null && newContent) {
484
+ const { id } = step;
485
+ const { oldSchema, newSchema, changeRecords } = step.diff?.[0] ?? {};
486
+ if (!oldSchema && newSchema) {
428
487
  if (reverse) await this.deleteCodeDslByIds([id], { doNotPushHistory: true });
429
- else this.setCodeDslByIdSync(id, cloneDeep$1(newContent), true, { doNotPushHistory: true });
488
+ else this.setCodeDslByIdSync(id, cloneDeep$1(newSchema), true, { doNotPushHistory: true });
430
489
  return;
431
490
  }
432
- if (oldContent && newContent === null) {
433
- if (reverse) this.setCodeDslByIdSync(id, cloneDeep$1(oldContent), true, { doNotPushHistory: true });
491
+ if (oldSchema && !newSchema) {
492
+ if (reverse) this.setCodeDslByIdSync(id, cloneDeep$1(oldSchema), true, { doNotPushHistory: true });
434
493
  else await this.deleteCodeDslByIds([id], { doNotPushHistory: true });
435
494
  return;
436
495
  }
437
- if (!oldContent || !newContent) return;
438
- const sourceForValues = reverse ? oldContent : newContent;
496
+ if (!oldSchema || !newSchema) return;
497
+ const sourceForValues = reverse ? oldSchema : newSchema;
439
498
  if (changeRecords?.length) {
440
499
  const current = this.getCodeContentById(id);
441
500
  if (!current) return;
@@ -1,4 +1,5 @@
1
1
  import BaseService from "./BaseService.js";
2
+ import { describeRevertStep, getLastPushedHistoryIds } from "../utils/history.js";
2
3
  import history_default from "./history.js";
3
4
  import storage_default, { Protocol } from "./storage.js";
4
5
  import { COPY_DS_STORAGE_KEY } from "../utils/editor.js";
@@ -26,18 +27,6 @@ var canUsePluginMethods = {
26
27
  "createId"
27
28
  ]
28
29
  };
29
- /**
30
- * 「回滚」生成的新 step 简短描述。
31
- * 仅在 service 层使用,避免依赖 UI 层 composables。
32
- */
33
- var describeRevertDataSourceStep = (step) => {
34
- const { oldSchema, newSchema, changeRecords, id } = step;
35
- if (oldSchema === null && newSchema) return `撤回新增 ${newSchema.title || newSchema.id || id}`;
36
- if (oldSchema && newSchema === null) return `还原已删除的 ${oldSchema.title || oldSchema.id || id}`;
37
- const title = newSchema?.title || oldSchema?.title || `${id}`;
38
- const propPath = changeRecords?.[0]?.propPath;
39
- return propPath ? `还原 ${title} · ${propPath}` : `还原 ${title}`;
40
- };
41
30
  var DataSource = class extends BaseService {
42
31
  state = reactive({
43
32
  datasourceTypeList: [],
@@ -48,6 +37,12 @@ var DataSource = class extends BaseService {
48
37
  events: {},
49
38
  methods: {}
50
39
  });
40
+ /**
41
+ * 最近一次写入历史栈的数据源历史记录 uuid。
42
+ * 供 *AndGetHistoryId 系列方法在调用 add / update / remove 后取回本次产生的历史记录 id;
43
+ * 普通方法不读取它,调用前由 *AndGetHistoryId 重置为 null。
44
+ */
45
+ lastPushedHistoryId = null;
51
46
  constructor() {
52
47
  super(canUsePluginMethods.sync.map((methodName) => ({
53
48
  name: methodName,
@@ -97,12 +92,12 @@ var DataSource = class extends BaseService {
97
92
  id: config.id && !this.getDataSourceById(config.id) ? config.id : this.createId()
98
93
  };
99
94
  this.get("dataSources").push(newConfig);
100
- if (!doNotPushHistory) history_default.pushDataSource(newConfig.id, {
95
+ if (!doNotPushHistory) this.lastPushedHistoryId = history_default.pushDataSource(newConfig.id, {
101
96
  oldSchema: null,
102
97
  newSchema: newConfig,
103
98
  historyDescription,
104
99
  source: historySource
105
- });
100
+ })?.uuid ?? null;
106
101
  this.emit("add", newConfig);
107
102
  return newConfig;
108
103
  }
@@ -120,13 +115,13 @@ var DataSource = class extends BaseService {
120
115
  const oldConfig = dataSources[index];
121
116
  const newConfig = cloneDeep$1(config);
122
117
  dataSources[index] = newConfig;
123
- if (!doNotPushHistory) history_default.pushDataSource(newConfig.id, {
118
+ if (!doNotPushHistory) this.lastPushedHistoryId = history_default.pushDataSource(newConfig.id, {
124
119
  oldSchema: oldConfig ? cloneDeep$1(oldConfig) : null,
125
120
  newSchema: newConfig,
126
121
  changeRecords,
127
122
  historyDescription,
128
123
  source: historySource
129
- });
124
+ })?.uuid ?? null;
130
125
  this.emit("update", newConfig, {
131
126
  oldConfig,
132
127
  changeRecords
@@ -145,15 +140,47 @@ var DataSource = class extends BaseService {
145
140
  const index = dataSources.findIndex((ds) => ds.id === id);
146
141
  const oldConfig = index !== -1 ? dataSources[index] : null;
147
142
  dataSources.splice(index, 1);
148
- if (oldConfig && !doNotPushHistory) history_default.pushDataSource(id, {
143
+ if (oldConfig && !doNotPushHistory) this.lastPushedHistoryId = history_default.pushDataSource(id, {
149
144
  oldSchema: cloneDeep$1(oldConfig),
150
145
  newSchema: null,
151
146
  historyDescription,
152
147
  source: historySource
153
- });
148
+ })?.uuid ?? null;
154
149
  this.emit("remove", id);
155
150
  }
156
151
  /**
152
+ * 下列 *AndGetHistoryId 方法与对应的 add / update / remove 行为完全一致,
153
+ * 返回值在 {@link DslOpWithHistoryIdsResult} 中同时包含原操作结果与本次写入历史栈的 uuid 列表({@link DataSourceStepValue.uuid}),
154
+ * 可用于精确引用 / 定位该条历史记录(埋点、revert、跨端同步等)。
155
+ *
156
+ * 当本次操作未写入历史(doNotPushHistory 为 true、或无对应记录)时 historyIds 为 `[]`。
157
+ */
158
+ /** 等价于 {@link add},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
159
+ addAndGetHistoryId(config, options = {}) {
160
+ this.lastPushedHistoryId = null;
161
+ return {
162
+ result: this.add(config, options),
163
+ historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
164
+ };
165
+ }
166
+ /** 等价于 {@link update},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
167
+ updateAndGetHistoryId(config, options = {}) {
168
+ this.lastPushedHistoryId = null;
169
+ return {
170
+ result: this.update(config, options),
171
+ historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
172
+ };
173
+ }
174
+ /** 等价于 {@link remove},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
175
+ removeAndGetHistoryId(id, options = {}) {
176
+ this.lastPushedHistoryId = null;
177
+ this.remove(id, options);
178
+ return {
179
+ result: void 0,
180
+ historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
181
+ };
182
+ }
183
+ /**
157
184
  * 撤销指定数据源的最近一次变更。
158
185
  *
159
186
  * 内部走 add / update / remove,因此会自动触发 dataSourceService 的事件,
@@ -221,10 +248,25 @@ var DataSource = class extends BaseService {
221
248
  revert(id, index) {
222
249
  const entry = history_default.getDataSourceStepList(id)[index];
223
250
  if (!entry?.applied) return null;
224
- if (entry.step.oldSchema && entry.step.newSchema && !entry.step.changeRecords?.length) return null;
225
- const description = `回滚 #${index + 1}: ${describeRevertDataSourceStep(entry.step)}`;
251
+ const { oldSchema, newSchema, changeRecords } = entry.step.diff?.[0] ?? {};
252
+ if (oldSchema && newSchema && !changeRecords?.length) return null;
253
+ const description = `回滚 #${index + 1}: ${describeRevertStep(entry.step.id, entry.step.diff?.[0], (s) => s.title)}`;
226
254
  return this.applyRevertStep(entry.step, description);
227
255
  }
256
+ /**
257
+ * 通过历史记录 uuid 回滚数据源历史步骤,语义同 {@link revert},
258
+ * 仅无需调用方再传 dataSourceId 与 index:内部会按 uuid 在全部数据源栈中定位对应步骤后再回滚。
259
+ * 按数组顺序依次回滚,返回与入参同序的结果列表(某项失败时为 `null`)。
260
+ *
261
+ * @param uuids 目标历史记录的 uuid 列表,通常由 {@link addAndGetHistoryId} 等方法返回的 `historyIds`
262
+ */
263
+ revertById(uuids) {
264
+ return uuids.map((uuid) => {
265
+ const location = history_default.findDataSourceStepLocationByUuid(uuid);
266
+ if (!location) return null;
267
+ return this.revert(location.id, location.index);
268
+ });
269
+ }
228
270
  createId() {
229
271
  return `ds_${guid()}`;
230
272
  }
@@ -283,15 +325,16 @@ var DataSource = class extends BaseService {
283
325
  * 同构,差异仅在于走对应的公共 add / update / remove 而不是带 doNotPushHistory 的版本。
284
326
  */
285
327
  applyRevertStep(step, historyDescription) {
286
- const { id, oldSchema, newSchema, changeRecords } = step;
287
- if (oldSchema === null && newSchema) {
328
+ const { id } = step;
329
+ const { oldSchema, newSchema, changeRecords } = step.diff?.[0] ?? {};
330
+ if (!oldSchema && newSchema) {
288
331
  this.remove(`${id}`, {
289
332
  historyDescription,
290
333
  historySource: "rollback"
291
334
  });
292
335
  return history_default.getDataSourceStepList(id).slice(-1)[0]?.step ?? null;
293
336
  }
294
- if (oldSchema && newSchema === null) {
337
+ if (oldSchema && !newSchema) {
295
338
  this.add(cloneDeep$1(oldSchema), {
296
339
  historyDescription,
297
340
  historySource: "rollback"
@@ -340,13 +383,14 @@ var DataSource = class extends BaseService {
340
383
  * @param reverse true=撤销,false=重做
341
384
  */
342
385
  applyHistoryStep(step, reverse) {
343
- const { id, oldSchema, newSchema, changeRecords } = step;
344
- if (oldSchema === null && newSchema) {
386
+ const { id } = step;
387
+ const { oldSchema, newSchema, changeRecords } = step.diff?.[0] ?? {};
388
+ if (!oldSchema && newSchema) {
345
389
  if (reverse) this.remove(`${id}`, { doNotPushHistory: true });
346
390
  else this.add(cloneDeep$1(newSchema), { doNotPushHistory: true });
347
391
  return;
348
392
  }
349
- if (oldSchema && newSchema === null) {
393
+ if (oldSchema && !newSchema) {
350
394
  if (reverse) this.add(cloneDeep$1(oldSchema), { doNotPushHistory: true });
351
395
  else this.remove(`${id}`, { doNotPushHistory: true });
352
396
  return;