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

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 (84) hide show
  1. package/dist/es/Editor.vue_vue_type_script_setup_true_lang.js +9 -0
  2. package/dist/es/components/CompareForm.vue_vue_type_script_setup_true_lang.js +46 -28
  3. package/dist/es/editorProps.js +2 -0
  4. package/dist/es/fields/CodeLink.vue_vue_type_script_setup_true_lang.js +2 -5
  5. package/dist/es/hooks/use-code-block-edit.js +6 -3
  6. package/dist/es/hooks/use-data-source-edit.js +5 -2
  7. package/dist/es/hooks/use-stage.js +8 -4
  8. package/dist/es/index.js +3 -1
  9. package/dist/es/layouts/CodeEditor.vue_vue_type_script_setup_true_lang.js +2 -5
  10. package/dist/es/layouts/NavMenu.vue_vue_type_script_setup_true_lang.js +1 -1
  11. package/dist/es/layouts/history-list/Bucket.vue_vue_type_script_setup_true_lang.js +37 -14
  12. package/dist/es/layouts/history-list/BucketTab.js +5 -0
  13. package/dist/es/layouts/history-list/{CodeBlockTab.vue_vue_type_script_setup_true_lang.js → BucketTab.vue_vue_type_script_setup_true_lang.js} +30 -16
  14. package/dist/es/layouts/history-list/GroupRow.vue_vue_type_script_setup_true_lang.js +91 -60
  15. package/dist/es/layouts/history-list/HistoryDiffDialog.vue_vue_type_script_setup_true_lang.js +82 -30
  16. package/dist/es/layouts/history-list/HistoryListPanel.vue_vue_type_script_setup_true_lang.js +140 -66
  17. package/dist/es/layouts/history-list/InitialRow.vue_vue_type_script_setup_true_lang.js +15 -9
  18. package/dist/es/layouts/history-list/PageTab.vue_vue_type_script_setup_true_lang.js +15 -6
  19. package/dist/es/layouts/history-list/composables.js +72 -2
  20. package/dist/es/layouts/props-panel/PropsPanel.vue_vue_type_script_setup_true_lang.js +5 -1
  21. package/dist/es/layouts/sidebar/ComponentListPanel.vue_vue_type_script_setup_true_lang.js +1 -1
  22. package/dist/es/layouts/sidebar/code-block/CodeBlockList.vue_vue_type_script_setup_true_lang.js +3 -3
  23. package/dist/es/layouts/sidebar/code-block/CodeBlockListPanel.vue_vue_type_script_setup_true_lang.js +1 -1
  24. package/dist/es/layouts/sidebar/code-block/useContentMenu.js +1 -1
  25. package/dist/es/layouts/sidebar/data-source/DataSourceListPanel.vue_vue_type_script_setup_true_lang.js +1 -1
  26. package/dist/es/layouts/sidebar/data-source/useContentMenu.js +1 -1
  27. package/dist/es/layouts/sidebar/layer/LayerMenu.vue_vue_type_script_setup_true_lang.js +5 -5
  28. package/dist/es/layouts/sidebar/layer/LayerNodeTool.vue_vue_type_script_setup_true_lang.js +1 -1
  29. package/dist/es/layouts/workspace/viewer/Stage.vue_vue_type_script_setup_true_lang.js +1 -1
  30. package/dist/es/layouts/workspace/viewer/ViewerMenu.vue_vue_type_script_setup_true_lang.js +8 -8
  31. package/dist/es/services/codeBlock.js +25 -10
  32. package/dist/es/services/dataSource.js +24 -10
  33. package/dist/es/services/editor.js +98 -46
  34. package/dist/es/services/history.js +7 -2
  35. package/dist/es/services/keybinding.js +3 -3
  36. package/dist/es/style.css +60 -16
  37. package/dist/es/utils/content-menu.js +17 -9
  38. package/dist/style.css +60 -16
  39. package/dist/tmagic-editor.umd.cjs +795 -443
  40. package/package.json +7 -7
  41. package/src/Editor.vue +8 -0
  42. package/src/components/CompareForm.vue +50 -19
  43. package/src/editorProps.ts +7 -0
  44. package/src/fields/CodeLink.vue +2 -5
  45. package/src/hooks/use-code-block-edit.ts +4 -3
  46. package/src/hooks/use-data-source-edit.ts +2 -2
  47. package/src/hooks/use-stage.ts +4 -3
  48. package/src/index.ts +2 -0
  49. package/src/layouts/CodeEditor.vue +2 -5
  50. package/src/layouts/NavMenu.vue +1 -1
  51. package/src/layouts/history-list/Bucket.vue +58 -29
  52. package/src/layouts/history-list/BucketTab.vue +80 -0
  53. package/src/layouts/history-list/GroupRow.vue +110 -58
  54. package/src/layouts/history-list/HistoryDiffDialog.vue +53 -33
  55. package/src/layouts/history-list/HistoryListPanel.vue +165 -52
  56. package/src/layouts/history-list/InitialRow.vue +17 -6
  57. package/src/layouts/history-list/PageTab.vue +25 -7
  58. package/src/layouts/history-list/composables.ts +92 -1
  59. package/src/layouts/props-panel/PropsPanel.vue +5 -1
  60. package/src/layouts/sidebar/ComponentListPanel.vue +9 -5
  61. package/src/layouts/sidebar/code-block/CodeBlockList.vue +5 -5
  62. package/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +1 -1
  63. package/src/layouts/sidebar/code-block/useContentMenu.ts +1 -1
  64. package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +1 -1
  65. package/src/layouts/sidebar/data-source/useContentMenu.ts +1 -1
  66. package/src/layouts/sidebar/layer/LayerMenu.vue +19 -11
  67. package/src/layouts/sidebar/layer/LayerNodeTool.vue +7 -4
  68. package/src/layouts/workspace/viewer/Stage.vue +1 -1
  69. package/src/layouts/workspace/viewer/ViewerMenu.vue +8 -8
  70. package/src/services/codeBlock.ts +33 -9
  71. package/src/services/dataSource.ts +30 -8
  72. package/src/services/editor.ts +111 -34
  73. package/src/services/history.ts +10 -0
  74. package/src/services/keybinding.ts +3 -3
  75. package/src/theme/history-list-panel.scss +67 -14
  76. package/src/theme/props-panel.scss +3 -3
  77. package/src/type.ts +146 -0
  78. package/src/utils/content-menu.ts +18 -9
  79. package/types/index.d.ts +387 -156
  80. package/dist/es/layouts/history-list/CodeBlockTab.js +0 -5
  81. package/dist/es/layouts/history-list/DataSourceTab.js +0 -5
  82. package/dist/es/layouts/history-list/DataSourceTab.vue_vue_type_script_setup_true_lang.js +0 -62
  83. package/src/layouts/history-list/CodeBlockTab.vue +0 -61
  84. package/src/layouts/history-list/DataSourceTab.vue +0 -61
@@ -4331,12 +4331,15 @@
4331
4331
  await (0, vue.nextTick)();
4332
4332
  codeBlockEditorRef.value?.show();
4333
4333
  };
4334
- const deleteCode = async (key) => {
4335
- codeBlockService.deleteCodeDslByIds([key]);
4334
+ const deleteCode = async (key, { historySource } = {}) => {
4335
+ codeBlockService.deleteCodeDslByIds([key], { historySource });
4336
4336
  };
4337
4337
  const submitCodeBlockHandler = async (values, eventData) => {
4338
4338
  if (!codeId.value) return;
4339
- await codeBlockService.setCodeDslById(codeId.value, values, { changeRecords: eventData?.changeRecords });
4339
+ await codeBlockService.setCodeDslById(codeId.value, values, {
4340
+ changeRecords: eventData?.changeRecords,
4341
+ historySource: "props"
4342
+ });
4340
4343
  codeBlockEditorRef.value?.hide();
4341
4344
  };
4342
4345
  return {
@@ -5191,6 +5194,7 @@
5191
5194
  push(state, pageId) {
5192
5195
  const undoRedo = this.getUndoRedo(pageId);
5193
5196
  if (!undoRedo) return null;
5197
+ if (state.timestamp === void 0) state.timestamp = Date.now();
5194
5198
  undoRedo.pushElement(state);
5195
5199
  if (pageId === void 0 || `${pageId}` === `${this.state.pageId}`) this.emit("change", state);
5196
5200
  return state;
@@ -5211,7 +5215,9 @@
5211
5215
  oldContent: payload.oldContent ? cloneDeep$1(payload.oldContent) : null,
5212
5216
  newContent: payload.newContent ? cloneDeep$1(payload.newContent) : null,
5213
5217
  changeRecords: payload.changeRecords?.length ? cloneDeep$1(payload.changeRecords) : void 0,
5214
- historyDescription: payload.historyDescription
5218
+ historyDescription: payload.historyDescription,
5219
+ source: payload.source,
5220
+ timestamp: Date.now()
5215
5221
  };
5216
5222
  this.getCodeBlockUndoRedo(codeBlockId).pushElement(step);
5217
5223
  this.emit("code-block-history-change", codeBlockId, step);
@@ -5228,7 +5234,9 @@
5228
5234
  oldSchema: payload.oldSchema ? cloneDeep$1(payload.oldSchema) : null,
5229
5235
  newSchema: payload.newSchema ? cloneDeep$1(payload.newSchema) : null,
5230
5236
  changeRecords: payload.changeRecords?.length ? cloneDeep$1(payload.changeRecords) : void 0,
5231
- historyDescription: payload.historyDescription
5237
+ historyDescription: payload.historyDescription,
5238
+ source: payload.source,
5239
+ timestamp: Date.now()
5232
5240
  };
5233
5241
  this.getDataSourceUndoRedo(dataSourceId).pushElement(step);
5234
5242
  this.emit("data-source-history-change", dataSourceId, step);
@@ -6327,7 +6335,7 @@
6327
6335
  * @param options.doNotPushHistory 是否不写入历史记录(默认 false)
6328
6336
  * @returns 添加后的节点
6329
6337
  */
6330
- async add(addNode, parent, { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false, historyDescription } = {}) {
6338
+ async add(addNode, parent, { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false, historyDescription, historySource } = {}) {
6331
6339
  this.captureSelectionBeforeOp();
6332
6340
  const stage = this.get("stage");
6333
6341
  const addNodes = [];
@@ -6361,16 +6369,21 @@
6361
6369
  if (!((0, _tmagic_utils.isPage)(newNodes[0]) || (0, _tmagic_utils.isPageFragment)(newNodes[0]))) {
6362
6370
  const pageForOp = this.getNodeInfo(newNodes[0].id, false).page;
6363
6371
  if (!doNotPushHistory) this.pushOpHistory("add", {
6364
- nodes: newNodes.map((n) => cloneDeep$1((0, vue.toRaw)(n))),
6365
- parentId: (this.getParentById(newNodes[0].id, false) ?? this.get("root")).id,
6366
- indexMap: Object.fromEntries(newNodes.map((n) => {
6367
- const p = this.getParentById(n.id, false);
6368
- return [n.id, p ? getNodeIndex(n.id, p) : -1];
6369
- }))
6370
- }, {
6371
- name: pageForOp?.name || "",
6372
- id: pageForOp.id
6373
- }, historyDescription);
6372
+ extra: {
6373
+ nodes: newNodes.map((n) => cloneDeep$1((0, vue.toRaw)(n))),
6374
+ parentId: (this.getParentById(newNodes[0].id, false) ?? this.get("root")).id,
6375
+ indexMap: Object.fromEntries(newNodes.map((n) => {
6376
+ const p = this.getParentById(n.id, false);
6377
+ return [n.id, p ? getNodeIndex(n.id, p) : -1];
6378
+ }))
6379
+ },
6380
+ pageData: {
6381
+ name: pageForOp?.name || "",
6382
+ id: pageForOp.id
6383
+ },
6384
+ historyDescription,
6385
+ source: historySource
6386
+ });
6374
6387
  else this.selectionBeforeOp = null;
6375
6388
  }
6376
6389
  this.emit("add", newNodes);
@@ -6437,7 +6450,7 @@
6437
6450
  * @param options.doNotSwitchPage 删除后是否不切换当前页面(默认 false;删除页面 / 页面片段时为 true 会跳过自动切换到首个剩余页面)
6438
6451
  * @param options.doNotPushHistory 是否不写入历史记录(默认 false)
6439
6452
  */
6440
- async remove(nodeOrNodeList, { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false, historyDescription } = {}) {
6453
+ async remove(nodeOrNodeList, { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false, historyDescription, historySource } = {}) {
6441
6454
  this.captureSelectionBeforeOp();
6442
6455
  const nodes = Array.isArray(nodeOrNodeList) ? nodeOrNodeList : [nodeOrNodeList];
6443
6456
  const removedItems = [];
@@ -6461,7 +6474,12 @@
6461
6474
  doNotSelect,
6462
6475
  doNotSwitchPage
6463
6476
  })));
6464
- if (removedItems.length > 0 && pageForOp) if (!doNotPushHistory) this.pushOpHistory("remove", { removedItems }, pageForOp, historyDescription);
6477
+ if (removedItems.length > 0 && pageForOp) if (!doNotPushHistory) this.pushOpHistory("remove", {
6478
+ extra: { removedItems },
6479
+ pageData: pageForOp,
6480
+ historyDescription,
6481
+ source: historySource
6482
+ });
6465
6483
  else this.selectionBeforeOp = null;
6466
6484
  this.emit("remove", nodes);
6467
6485
  }
@@ -6522,7 +6540,7 @@
6522
6540
  */
6523
6541
  async update(config, data = {}) {
6524
6542
  this.captureSelectionBeforeOp();
6525
- const { doNotPushHistory = false, changeRecordList, changeRecords, historyDescription } = data;
6543
+ const { doNotPushHistory = false, changeRecordList, changeRecords, historyDescription, historySource } = data;
6526
6544
  const nodes = Array.isArray(config) ? config : [config];
6527
6545
  const updateData = await Promise.all(nodes.map((node, index) => {
6528
6546
  const recordsForNode = changeRecordList ? changeRecordList[index] ?? [] : changeRecords ?? [];
@@ -6531,14 +6549,19 @@
6531
6549
  if (updateData[0].oldNode?.type !== _tmagic_core.NodeType.ROOT) {
6532
6550
  if (this.get("nodes").length) if (!doNotPushHistory) {
6533
6551
  const pageForOp = this.getNodeInfo(nodes[0].id, false).page;
6534
- this.pushOpHistory("update", { updatedItems: updateData.map((d) => ({
6535
- oldNode: cloneDeep$1(d.oldNode),
6536
- newNode: cloneDeep$1((0, vue.toRaw)(d.newNode)),
6537
- changeRecords: d.changeRecords?.length ? cloneDeep$1(d.changeRecords) : void 0
6538
- })) }, {
6539
- name: pageForOp?.name || "",
6540
- id: pageForOp.id
6541
- }, historyDescription);
6552
+ this.pushOpHistory("update", {
6553
+ extra: { updatedItems: updateData.map((d) => ({
6554
+ oldNode: cloneDeep$1(d.oldNode),
6555
+ newNode: cloneDeep$1((0, vue.toRaw)(d.newNode)),
6556
+ changeRecords: d.changeRecords?.length ? cloneDeep$1(d.changeRecords) : void 0
6557
+ })) },
6558
+ pageData: {
6559
+ name: pageForOp?.name || "",
6560
+ id: pageForOp.id
6561
+ },
6562
+ historyDescription,
6563
+ source: historySource
6564
+ });
6542
6565
  } else this.selectionBeforeOp = null;
6543
6566
  }
6544
6567
  this.emit("update", updateData);
@@ -6554,7 +6577,7 @@
6554
6577
  * @param options.doNotPushHistory 是否不写入历史记录(默认 false)
6555
6578
  * @returns void
6556
6579
  */
6557
- async sort(id1, id2, { doNotSelect = false, doNotPushHistory = false } = {}) {
6580
+ async sort(id1, id2, { doNotSelect = false, doNotPushHistory = false, historySource } = {}) {
6558
6581
  this.captureSelectionBeforeOp();
6559
6582
  const root = this.get("root");
6560
6583
  if (!root) throw new Error("root为空");
@@ -6566,7 +6589,10 @@
6566
6589
  if (index2 < 0) return;
6567
6590
  const index1 = parent.items.findIndex((node) => `${node.id}` === `${id1}`);
6568
6591
  parent.items.splice(index2, 0, ...parent.items.splice(index1, 1));
6569
- await this.update(parent, { doNotPushHistory });
6592
+ await this.update(parent, {
6593
+ doNotPushHistory,
6594
+ historySource
6595
+ });
6570
6596
  if (!doNotSelect) await this.select(node);
6571
6597
  this.get("stage")?.update({
6572
6598
  config: cloneDeep$1(node),
@@ -6602,7 +6628,7 @@
6602
6628
  * @param options.doNotPushHistory 是否不写入历史记录(默认 false)
6603
6629
  * @returns 添加后的组件节点配置
6604
6630
  */
6605
- async paste(position = {}, collectorOptions, { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false } = {}) {
6631
+ async paste(position = {}, collectorOptions, { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false, historyDescription, historySource } = {}) {
6606
6632
  const config = storage_default.getItem(COPY_STORAGE_KEY);
6607
6633
  if (!Array.isArray(config)) return;
6608
6634
  const node = this.get("node");
@@ -6616,7 +6642,9 @@
6616
6642
  return this.add(pasteConfigs, parent, {
6617
6643
  doNotSelect,
6618
6644
  doNotSwitchPage,
6619
- doNotPushHistory
6645
+ doNotPushHistory,
6646
+ historyDescription,
6647
+ historySource
6620
6648
  });
6621
6649
  }
6622
6650
  async doPaste(config, position = {}) {
@@ -6644,11 +6672,15 @@
6644
6672
  * @param options.doNotPushHistory 是否不写入历史记录(默认 false)
6645
6673
  * @returns 当前组件节点配置
6646
6674
  */
6647
- async alignCenter(config, { doNotSelect = false, doNotPushHistory = false } = {}) {
6675
+ async alignCenter(config, { doNotSelect = false, doNotPushHistory = false, historyDescription, historySource } = {}) {
6648
6676
  const nodes = Array.isArray(config) ? config : [config];
6649
6677
  const stage = this.get("stage");
6650
6678
  const newNodes = await Promise.all(nodes.map((node) => this.doAlignCenter(node)));
6651
- const newNode = await this.update(newNodes, { doNotPushHistory });
6679
+ const newNode = await this.update(newNodes, {
6680
+ doNotPushHistory,
6681
+ historyDescription,
6682
+ historySource
6683
+ });
6652
6684
  if (!doNotSelect) if (newNodes.length > 1) await stage?.multiSelect(newNodes.map((node) => node.id));
6653
6685
  else await stage?.select(newNodes[0].id);
6654
6686
  return newNode;
@@ -6659,7 +6691,7 @@
6659
6691
  * @param options 可选配置
6660
6692
  * @param options.doNotPushHistory 是否不写入历史记录(默认 false)
6661
6693
  */
6662
- async moveLayer(offset, { doNotPushHistory = false } = {}) {
6694
+ async moveLayer(offset, { doNotPushHistory = false, historyDescription, historySource } = {}) {
6663
6695
  this.captureSelectionBeforeOp();
6664
6696
  const root = this.get("root");
6665
6697
  if (!root) throw new Error("root为空");
@@ -6684,12 +6716,17 @@
6684
6716
  this.addModifiedNodeId(parent.id);
6685
6717
  if (!doNotPushHistory) {
6686
6718
  const pageForOp = this.getNodeInfo(node.id, false).page;
6687
- this.pushOpHistory("update", { updatedItems: [{
6688
- oldNode: oldParent,
6689
- newNode: cloneDeep$1((0, vue.toRaw)(parent))
6690
- }] }, {
6691
- name: pageForOp?.name || "",
6692
- id: pageForOp.id
6719
+ this.pushOpHistory("update", {
6720
+ extra: { updatedItems: [{
6721
+ oldNode: oldParent,
6722
+ newNode: cloneDeep$1((0, vue.toRaw)(parent))
6723
+ }] },
6724
+ pageData: {
6725
+ name: pageForOp?.name || "",
6726
+ id: pageForOp.id
6727
+ },
6728
+ historyDescription,
6729
+ source: historySource
6693
6730
  });
6694
6731
  } else this.selectionBeforeOp = null;
6695
6732
  this.emit("move-layer", offset);
@@ -6708,7 +6745,7 @@
6708
6745
  * @param options.doNotSwitchPage 移动后是否不切换当前页面(默认 false;目标容器位于其它页面时为 true 会跳过自动选中以避免页面切换)
6709
6746
  * @param options.doNotPushHistory 是否不写入历史记录(默认 false)
6710
6747
  */
6711
- async moveToContainer(config, targetId, { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false } = {}) {
6748
+ async moveToContainer(config, targetId, { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false, historyDescription, historySource } = {}) {
6712
6749
  const isBatch = Array.isArray(config);
6713
6750
  const configs = (isBatch ? config : [config]).filter((item) => !((0, _tmagic_utils.isPage)(item) || (0, _tmagic_utils.isPageFragment)(item)));
6714
6751
  if (configs.length === 0) throw new Error("没有可移动的节点");
@@ -6756,11 +6793,16 @@
6756
6793
  name: "",
6757
6794
  id: target.id
6758
6795
  };
6759
- this.pushOpHistory("update", { updatedItems }, historyPage);
6796
+ this.pushOpHistory("update", {
6797
+ extra: { updatedItems },
6798
+ pageData: historyPage,
6799
+ historyDescription,
6800
+ source: historySource
6801
+ });
6760
6802
  } else this.selectionBeforeOp = null;
6761
6803
  return isBatch ? newConfigs : newConfigs[0];
6762
6804
  }
6763
- async dragTo(config, targetParent, targetIndex, { doNotPushHistory = false } = {}) {
6805
+ async dragTo(config, targetParent, targetIndex, { doNotPushHistory = false, historyDescription, historySource } = {}) {
6764
6806
  this.captureSelectionBeforeOp();
6765
6807
  if (!targetParent || !Array.isArray(targetParent.items)) return;
6766
6808
  const configs = Array.isArray(config) ? config : [config];
@@ -6802,9 +6844,14 @@
6802
6844
  }
6803
6845
  if (!doNotPushHistory) {
6804
6846
  const pageForOp = this.getNodeInfo(configs[0].id, false).page;
6805
- this.pushOpHistory("update", { updatedItems }, {
6806
- name: pageForOp?.name || "",
6807
- id: pageForOp.id
6847
+ this.pushOpHistory("update", {
6848
+ extra: { updatedItems },
6849
+ pageData: {
6850
+ name: pageForOp?.name || "",
6851
+ id: pageForOp.id
6852
+ },
6853
+ historyDescription,
6854
+ source: historySource
6808
6855
  });
6809
6856
  } else this.selectionBeforeOp = null;
6810
6857
  this.emit("drag-to", {
@@ -6852,6 +6899,10 @@
6852
6899
  if (!entry?.applied) return null;
6853
6900
  const { step } = entry;
6854
6901
  if (!this.get("root")) return null;
6902
+ if (step.opType === "update") {
6903
+ const items = step.updatedItems ?? [];
6904
+ if (!items.length || !items.every((item) => item.changeRecords?.length)) return null;
6905
+ }
6855
6906
  let revertedStep = null;
6856
6907
  const captureRevert = (s) => {
6857
6908
  revertedStep = s;
@@ -6861,7 +6912,8 @@
6861
6912
  const opts = {
6862
6913
  doNotSelect: true,
6863
6914
  doNotSwitchPage: true,
6864
- historyDescription
6915
+ historyDescription,
6916
+ historySource: "rollback"
6865
6917
  };
6866
6918
  try {
6867
6919
  switch (step.opType) {
@@ -6897,7 +6949,10 @@
6897
6949
  }
6898
6950
  return cloneDeep$1(oldNode);
6899
6951
  });
6900
- if (configs.length) await this.update(configs, { historyDescription });
6952
+ if (configs.length) await this.update(configs, {
6953
+ historyDescription,
6954
+ historySource: "rollback"
6955
+ });
6901
6956
  break;
6902
6957
  }
6903
6958
  }
@@ -6931,7 +6986,7 @@
6931
6986
  }
6932
6987
  return cursor;
6933
6988
  }
6934
- async move(left, top, { doNotPushHistory = false } = {}) {
6989
+ async move(left, top, { doNotPushHistory = false, historyDescription, historySource } = {}) {
6935
6990
  const node = (0, vue.toRaw)(this.get("node"));
6936
6991
  if (!node || (0, _tmagic_utils.isPage)(node)) return;
6937
6992
  const newStyle = calcMoveStyle(node.style || {}, left, top);
@@ -6940,7 +6995,11 @@
6940
6995
  id: node.id,
6941
6996
  type: node.type,
6942
6997
  style: newStyle
6943
- }, { doNotPushHistory });
6998
+ }, {
6999
+ doNotPushHistory,
7000
+ historyDescription,
7001
+ historySource
7002
+ });
6944
7003
  }
6945
7004
  resetState() {
6946
7005
  this.set("root", null);
@@ -6980,7 +7039,7 @@
6980
7039
  if (this.selectionBeforeOp) return;
6981
7040
  this.selectionBeforeOp = this.get("nodes").map((n) => n.id);
6982
7041
  }
6983
- pushOpHistory(opType, extra, pageData, historyDescription) {
7042
+ pushOpHistory(opType, { extra, pageData, historyDescription, source }) {
6984
7043
  const step = {
6985
7044
  data: pageData,
6986
7045
  opType,
@@ -6990,6 +7049,7 @@
6990
7049
  ...extra
6991
7050
  };
6992
7051
  if (historyDescription) step.historyDescription = historyDescription;
7052
+ if (source) step.source = source;
6993
7053
  history_default.push(step, pageData.id);
6994
7054
  this.selectionBeforeOp = null;
6995
7055
  }
@@ -7258,7 +7318,8 @@
7258
7318
  guidesOptions: stageOptions.guidesOptions,
7259
7319
  disabledMultiSelect: stageOptions.disabledMultiSelect,
7260
7320
  alwaysMultiSelect: stageOptions.alwaysMultiSelect,
7261
- disabledRule: stageOptions.disabledRule
7321
+ disabledRule: stageOptions.disabledRule,
7322
+ disabledFlashTip: stageOptions.disabledFlashTip
7262
7323
  });
7263
7324
  (0, vue.watch)(() => editor_default.get("disabledMultiSelect"), (disabledMultiSelect) => {
7264
7325
  if (disabledMultiSelect) stage.disableMultiSelect();
@@ -7315,14 +7376,17 @@
7315
7376
  changeRecordList.push(buildChangeRecords(style, "style"));
7316
7377
  });
7317
7378
  if (configs.length === 0) return;
7318
- editor_default.update(configs, { changeRecordList });
7379
+ editor_default.update(configs, {
7380
+ changeRecordList,
7381
+ historySource: "stage"
7382
+ });
7319
7383
  });
7320
7384
  stage.on("sort", (ev) => {
7321
- editor_default.sort(ev.src, ev.dist);
7385
+ editor_default.sort(ev.src, ev.dist, { historySource: "stage" });
7322
7386
  });
7323
7387
  stage.on("remove", (ev) => {
7324
7388
  const nodes = ev.data.map(({ el }) => editor_default.getNodeById((0, _tmagic_utils.getIdFromEl)()(el) || ""));
7325
- editor_default.remove(nodes.filter((node) => Boolean(node)));
7389
+ editor_default.remove(nodes.filter((node) => Boolean(node)), { historySource: "stage" });
7326
7390
  });
7327
7391
  stage.on("select-parent", () => {
7328
7392
  const parent = editor_default.get("parent");
@@ -8355,7 +8419,7 @@
8355
8419
  var SplitView_default = SplitView_vue_vue_type_script_setup_true_lang_default;
8356
8420
  //#endregion
8357
8421
  //#region packages/editor/src/components/Icon.vue?vue&type=script&setup=true&lang.ts
8358
- var _hoisted_1$72 = ["src"];
8422
+ var _hoisted_1$71 = ["src"];
8359
8423
  var Icon_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ (0, vue.defineComponent)({
8360
8424
  name: "MEditorIcon",
8361
8425
  __name: "Icon",
@@ -8373,7 +8437,7 @@
8373
8437
  key: 1,
8374
8438
  class: "magic-editor-icon"
8375
8439
  }, {
8376
- default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("img", { src: __props.icon }, null, 8, _hoisted_1$72)]),
8440
+ default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("img", { src: __props.icon }, null, 8, _hoisted_1$71)]),
8377
8441
  _: 1
8378
8442
  })) : typeof __props.icon === "string" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("i", {
8379
8443
  key: 2,
@@ -8393,11 +8457,11 @@
8393
8457
  var Icon_default = Icon_vue_vue_type_script_setup_true_lang_default;
8394
8458
  //#endregion
8395
8459
  //#region packages/editor/src/components/ToolButton.vue?vue&type=script&setup=true&lang.ts
8396
- var _hoisted_1$71 = {
8460
+ var _hoisted_1$70 = {
8397
8461
  key: 1,
8398
8462
  class: "menu-item-text"
8399
8463
  };
8400
- var _hoisted_2$29 = { class: "el-dropdown-link menubar-menu-button" };
8464
+ var _hoisted_2$28 = { class: "el-dropdown-link menubar-menu-button" };
8401
8465
  var ToolButton_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ (0, vue.defineComponent)({
8402
8466
  name: "MEditorToolButton",
8403
8467
  __name: "ToolButton",
@@ -8456,7 +8520,7 @@
8456
8520
  }, [__props.data.type === "divider" ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicDivider), {
8457
8521
  key: 0,
8458
8522
  direction: __props.data.direction || "vertical"
8459
- }, null, 8, ["direction"])) : __props.data.type === "text" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$71, (0, vue.toDisplayString)(__props.data.text), 1)) : __props.data.type === "button" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 2 }, [__props.data.tooltip ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicTooltip), {
8523
+ }, null, 8, ["direction"])) : __props.data.type === "text" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$70, (0, vue.toDisplayString)(__props.data.text), 1)) : __props.data.type === "button" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 2 }, [__props.data.tooltip ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicTooltip), {
8460
8524
  key: 0,
8461
8525
  effect: "dark",
8462
8526
  placement: "bottom-start",
@@ -8511,7 +8575,7 @@
8511
8575
  }), 128))]),
8512
8576
  _: 1
8513
8577
  })) : (0, vue.createCommentVNode)("v-if", true)]),
8514
- default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("span", _hoisted_2$29, [(0, vue.createTextVNode)((0, vue.toDisplayString)(__props.data.text), 1), (0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicIcon), { class: "el-icon--right" }, {
8578
+ default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("span", _hoisted_2$28, [(0, vue.createTextVNode)((0, vue.toDisplayString)(__props.data.text), 1), (0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicIcon), { class: "el-icon--right" }, {
8515
8579
  default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_element_plus_icons_vue.ArrowDown))]),
8516
8580
  _: 1
8517
8581
  })])]),
@@ -8525,12 +8589,12 @@
8525
8589
  var ToolButton_default = ToolButton_vue_vue_type_script_setup_true_lang_default;
8526
8590
  //#endregion
8527
8591
  //#region packages/editor/src/layouts/page-bar/AddButton.vue?vue&type=script&setup=true&lang.ts
8528
- var _hoisted_1$70 = {
8592
+ var _hoisted_1$69 = {
8529
8593
  key: 0,
8530
8594
  id: "m-editor-page-bar-add-icon",
8531
8595
  class: "m-editor-page-bar-item m-editor-page-bar-item-icon"
8532
8596
  };
8533
- var _hoisted_2$28 = {
8597
+ var _hoisted_2$27 = {
8534
8598
  key: 1,
8535
8599
  style: { "width": "21px" }
8536
8600
  };
@@ -8551,7 +8615,7 @@
8551
8615
  editorService.add(pageConfig);
8552
8616
  };
8553
8617
  return (_ctx, _cache) => {
8554
- return showAddPageButton.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$70, [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicPopover), { "popper-class": "data-source-list-panel-add-menu" }, {
8618
+ return showAddPageButton.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$69, [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicPopover), { "popper-class": "data-source-list-panel-add-menu" }, {
8555
8619
  reference: (0, vue.withCtx)(() => [(0, vue.createVNode)(Icon_default, { icon: (0, vue.unref)(_element_plus_icons_vue.Plus) }, null, 8, ["icon"])]),
8556
8620
  default: (0, vue.withCtx)(() => [(0, vue.createVNode)(ToolButton_default, { data: {
8557
8621
  type: "button",
@@ -8567,7 +8631,7 @@
8567
8631
  }
8568
8632
  } }, null, 8, ["data"])]),
8569
8633
  _: 1
8570
- })])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_2$28));
8634
+ })])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_2$27));
8571
8635
  };
8572
8636
  }
8573
8637
  });
@@ -8576,11 +8640,11 @@
8576
8640
  var AddButton_default = AddButton_vue_vue_type_script_setup_true_lang_default;
8577
8641
  //#endregion
8578
8642
  //#region packages/editor/src/layouts/page-bar/PageBarScrollContainer.vue?vue&type=script&setup=true&lang.ts
8579
- var _hoisted_1$69 = {
8643
+ var _hoisted_1$68 = {
8580
8644
  class: "m-editor-page-bar",
8581
8645
  ref: "pageBar"
8582
8646
  };
8583
- var _hoisted_2$27 = {
8647
+ var _hoisted_2$26 = {
8584
8648
  key: 0,
8585
8649
  class: "m-editor-page-bar-items",
8586
8650
  ref: "itemsContainer"
@@ -8670,9 +8734,9 @@
8670
8734
  }
8671
8735
  });
8672
8736
  return (_ctx, _cache) => {
8673
- return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$69, [
8737
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$68, [
8674
8738
  (0, vue.renderSlot)(_ctx.$slots, "prepend"),
8675
- __props.length ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_2$27, [(0, vue.renderSlot)(_ctx.$slots, "default")], 512)) : (0, vue.createCommentVNode)("v-if", true),
8739
+ __props.length ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_2$26, [(0, vue.renderSlot)(_ctx.$slots, "default")], 512)) : (0, vue.createCommentVNode)("v-if", true),
8676
8740
  canScroll.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
8677
8741
  key: 1,
8678
8742
  class: "m-editor-page-bar-item m-editor-page-bar-item-icon m-editor-page-bar-item-left-icon",
@@ -8692,12 +8756,12 @@
8692
8756
  var PageBarScrollContainer_default = PageBarScrollContainer_vue_vue_type_script_setup_true_lang_default;
8693
8757
  //#endregion
8694
8758
  //#region packages/editor/src/layouts/page-bar/PageList.vue?vue&type=script&setup=true&lang.ts
8695
- var _hoisted_1$68 = {
8759
+ var _hoisted_1$67 = {
8696
8760
  key: 0,
8697
8761
  id: "m-editor-page-bar-list-icon",
8698
8762
  class: "m-editor-page-bar-item m-editor-page-bar-item-icon"
8699
8763
  };
8700
- var _hoisted_2$26 = { class: "page-bar-popover-wrapper" };
8764
+ var _hoisted_2$25 = { class: "page-bar-popover-wrapper" };
8701
8765
  var _hoisted_3$10 = { class: "page-bar-popover-inner" };
8702
8766
  var PageList_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ (0, vue.defineComponent)({
8703
8767
  name: "MEditorPageList",
@@ -8711,7 +8775,7 @@
8711
8775
  await editorService.select(id);
8712
8776
  };
8713
8777
  return (_ctx, _cache) => {
8714
- return showPageListButton.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$68, [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicPopover), {
8778
+ return showPageListButton.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$67, [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicPopover), {
8715
8779
  "popper-class": "page-bar-popover",
8716
8780
  placement: "top",
8717
8781
  trigger: "hover",
@@ -8722,7 +8786,7 @@
8722
8786
  default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_element_plus_icons_vue.Files))]),
8723
8787
  _: 1
8724
8788
  })]),
8725
- default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("div", _hoisted_2$26, [(0, vue.createElementVNode)("div", _hoisted_3$10, [(0, vue.renderSlot)(_ctx.$slots, "page-list-popover", { list: __props.list }, () => [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.list, (item, index) => {
8789
+ default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("div", _hoisted_2$25, [(0, vue.createElementVNode)("div", _hoisted_3$10, [(0, vue.renderSlot)(_ctx.$slots, "page-list-popover", { list: __props.list }, () => [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.list, (item, index) => {
8726
8790
  return (0, vue.openBlock)(), (0, vue.createBlock)(ToolButton_default, {
8727
8791
  data: {
8728
8792
  type: "button",
@@ -8743,7 +8807,7 @@
8743
8807
  var PageList_default = PageList_vue_vue_type_script_setup_true_lang_default;
8744
8808
  //#endregion
8745
8809
  //#region packages/editor/src/layouts/page-bar/Search.vue?vue&type=script&setup=true&lang.ts
8746
- var _hoisted_1$67 = { class: "m-editor-page-bar-item m-editor-page-bar-item-icon m-editor-page-bar-search" };
8810
+ var _hoisted_1$66 = { class: "m-editor-page-bar-item m-editor-page-bar-item-icon m-editor-page-bar-search" };
8747
8811
  var Search_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ (0, vue.defineComponent)({
8748
8812
  __name: "Search",
8749
8813
  props: {
@@ -8776,7 +8840,7 @@
8776
8840
  emit("search", values);
8777
8841
  };
8778
8842
  return (_ctx, _cache) => {
8779
- return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$67, [(0, vue.createVNode)(Icon_default, {
8843
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$66, [(0, vue.createVNode)(Icon_default, {
8780
8844
  icon: (0, vue.unref)(_element_plus_icons_vue.Search),
8781
8845
  onClick: _cache[0] || (_cache[0] = ($event) => visible.value = !visible.value),
8782
8846
  class: (0, vue.normalizeClass)({ "icon-active": visible.value })
@@ -8800,8 +8864,8 @@
8800
8864
  var Search_default = Search_vue_vue_type_script_setup_true_lang_default;
8801
8865
  //#endregion
8802
8866
  //#region packages/editor/src/layouts/page-bar/PageBar.vue?vue&type=script&setup=true&lang.ts
8803
- var _hoisted_1$66 = { class: "m-editor-page-bar-tabs" };
8804
- var _hoisted_2$25 = ["data-page-id", "onClick"];
8867
+ var _hoisted_1$65 = { class: "m-editor-page-bar-tabs" };
8868
+ var _hoisted_2$24 = ["data-page-id", "onClick"];
8805
8869
  var _hoisted_3$9 = { class: "m-editor-page-bar-title" };
8806
8870
  var _hoisted_4$8 = ["title"];
8807
8871
  var PageBar_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ (0, vue.defineComponent)({
@@ -8868,7 +8932,7 @@
8868
8932
  }
8869
8933
  });
8870
8934
  return (_ctx, _cache) => {
8871
- return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$66, [(0, vue.createVNode)(PageBarScrollContainer_default, {
8935
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$65, [(0, vue.createVNode)(PageBarScrollContainer_default, {
8872
8936
  ref: "pageBarScrollContainer",
8873
8937
  "page-bar-sort-options": __props.pageBarSortOptions,
8874
8938
  length: list.value.length
@@ -8915,7 +8979,7 @@
8915
8979
  handler: () => remove(item)
8916
8980
  } }, null, 8, ["data"])])])]),
8917
8981
  _: 2
8918
- }, 1024)], 10, _hoisted_2$25);
8982
+ }, 1024)], 10, _hoisted_2$24);
8919
8983
  }), 128))]),
8920
8984
  _: 3
8921
8985
  }, 8, ["page-bar-sort-options", "length"])]);
@@ -8927,8 +8991,8 @@
8927
8991
  var PageBar_default = PageBar_vue_vue_type_script_setup_true_lang_default;
8928
8992
  //#endregion
8929
8993
  //#region packages/editor/src/layouts/AddPageBox.vue?vue&type=script&setup=true&lang.ts
8930
- var _hoisted_1$65 = { class: "m-editor-empty-panel" };
8931
- var _hoisted_2$24 = { class: "m-editor-empty-content" };
8994
+ var _hoisted_1$64 = { class: "m-editor-empty-panel" };
8995
+ var _hoisted_2$23 = { class: "m-editor-empty-content" };
8932
8996
  var AddPageBox_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ (0, vue.defineComponent)({
8933
8997
  name: "MEditorAddPageBox",
8934
8998
  __name: "AddPageBox",
@@ -8945,7 +9009,7 @@
8945
9009
  });
8946
9010
  };
8947
9011
  return (_ctx, _cache) => {
8948
- return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$65, [(0, vue.createElementVNode)("div", _hoisted_2$24, [(0, vue.createElementVNode)("div", {
9012
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$64, [(0, vue.createElementVNode)("div", _hoisted_2$23, [(0, vue.createElementVNode)("div", {
8949
9013
  class: "m-editor-empty-button",
8950
9014
  onClick: _cache[0] || (_cache[0] = ($event) => clickHandler((0, vue.unref)(_tmagic_core.NodeType).PAGE))
8951
9015
  }, [(0, vue.createElementVNode)("div", null, [(0, vue.createVNode)(Icon_default, { icon: (0, vue.unref)(_element_plus_icons_vue.Plus) }, null, 8, ["icon"])]), _cache[2] || (_cache[2] = (0, vue.createElementVNode)("p", null, "新增页面", -1))]), !__props.disabledPageFragment ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
@@ -8961,8 +9025,8 @@
8961
9025
  var AddPageBox_default = AddPageBox_vue_vue_type_script_setup_true_lang_default;
8962
9026
  //#endregion
8963
9027
  //#region packages/editor/src/layouts/CodeEditor.vue?vue&type=script&setup=true&lang.ts
8964
- var _hoisted_1$64 = { class: "magic-code-editor" };
8965
- var _hoisted_2$23 = {
9028
+ var _hoisted_1$63 = { class: "magic-code-editor" };
9029
+ var _hoisted_2$22 = {
8966
9030
  ref: "codeEditor",
8967
9031
  class: "magic-code-editor-content"
8968
9032
  };
@@ -9048,10 +9112,7 @@
9048
9112
  const toString = (v, language) => {
9049
9113
  let value;
9050
9114
  if (typeof v !== "string") if (language === "json") value = JSON.stringify(v, null, 2);
9051
- else value = (0, serialize_javascript.default)(v, {
9052
- space: 2,
9053
- unsafe: true
9054
- }).replace(/"(\w+)":\s/g, "$1: ");
9115
+ else value = serializeConfig(v);
9055
9116
  else value = v;
9056
9117
  if (language === "javascript" && value.startsWith("{") && value.endsWith("}")) value = `(${value})`;
9057
9118
  return value;
@@ -9202,7 +9263,7 @@
9202
9263
  }
9203
9264
  });
9204
9265
  return (_ctx, _cache) => {
9205
- return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$64, [((0, vue.openBlock)(), (0, vue.createBlock)(vue.Teleport, {
9266
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$63, [((0, vue.openBlock)(), (0, vue.createBlock)(vue.Teleport, {
9206
9267
  to: "body",
9207
9268
  disabled: !fullScreen.value
9208
9269
  }, [(0, vue.createElementVNode)("div", {
@@ -9220,7 +9281,7 @@
9220
9281
  }, {
9221
9282
  default: (0, vue.withCtx)(() => [(0, vue.createVNode)(Icon_default, { icon: (0, vue.unref)(_element_plus_icons_vue.FullScreen) }, null, 8, ["icon"])]),
9222
9283
  _: 1
9223
- })) : (0, vue.createCommentVNode)("v-if", true), (0, vue.createElementVNode)("div", _hoisted_2$23, null, 512)], 6)], 8, ["disabled"]))]);
9284
+ })) : (0, vue.createCommentVNode)("v-if", true), (0, vue.createElementVNode)("div", _hoisted_2$22, null, 512)], 6)], 8, ["disabled"]))]);
9224
9285
  };
9225
9286
  }
9226
9287
  });
@@ -9229,7 +9290,7 @@
9229
9290
  var CodeEditor_default = CodeEditor_vue_vue_type_script_setup_true_lang_default;
9230
9291
  //#endregion
9231
9292
  //#region packages/editor/src/layouts/Framework.vue?vue&type=script&setup=true&lang.ts
9232
- var _hoisted_1$63 = {
9293
+ var _hoisted_1$62 = {
9233
9294
  class: "m-editor",
9234
9295
  ref: "content",
9235
9296
  style: { "min-width": "900px" }
@@ -9293,7 +9354,7 @@
9293
9354
  }
9294
9355
  };
9295
9356
  return (_ctx, _cache) => {
9296
- return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$63, [
9357
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$62, [
9297
9358
  (0, vue.renderSlot)(_ctx.$slots, "header"),
9298
9359
  (0, vue.renderSlot)(_ctx.$slots, "nav"),
9299
9360
  (0, vue.renderSlot)(_ctx.$slots, "content-before"),
@@ -9371,7 +9432,10 @@
9371
9432
  */
9372
9433
  var useHistoryList = () => {
9373
9434
  const { historyService } = useServices();
9374
- /** 折叠状态:key 形如 `pg-${groupIdx}` / `ds-${id}-${groupIdx}` / `cb-${id}-${groupIdx}`。 */
9435
+ /**
9436
+ * 折叠状态:key 形如 `pg-${组内首步 index}` / `ds-${id}-${组内首步 index}` / `cb-${id}-${组内首步 index}`。
9437
+ * 用组内首步的稳定 index(而非展示位置)作为 key,确保历史数据更新后已展开的分组状态保持不变。
9438
+ */
9375
9439
  const expanded = (0, vue.reactive)({});
9376
9440
  const toggleGroup = (key) => {
9377
9441
  expanded[key] = !expanded[key];
@@ -9408,6 +9472,20 @@
9408
9472
  codeBlockGroupsByTarget: (0, vue.computed)(() => groupByTarget(codeBlockGroups.value))
9409
9473
  };
9410
9474
  };
9475
+ /**
9476
+ * 历史面板的时间展示:
9477
+ * - 当天的记录只显示 `HH:mm:ss`;
9478
+ * - 跨天的记录显示 `MM-DD HH:mm:ss`。
9479
+ * 无时间戳(旧数据 / 未写入)时返回空串,UI 据此不渲染时间。
9480
+ */
9481
+ var formatHistoryTime = (timestamp) => {
9482
+ if (!timestamp) return "";
9483
+ return `${(0, _tmagic_form.datetimeFormatter)(new Date(timestamp), "", "YYYY-MM-DD") === (0, _tmagic_form.datetimeFormatter)(/* @__PURE__ */ new Date(), "", "YYYY-MM-DD") ? (0, _tmagic_form.datetimeFormatter)(new Date(timestamp), "", "HH:mm:ss") : (0, _tmagic_form.datetimeFormatter)(new Date(timestamp), "", "MM-DD HH:mm:ss")}`;
9484
+ };
9485
+ /** 完整时间(含年份与秒),用于 title 悬浮提示。无时间戳时返回空串。 */
9486
+ var formatHistoryFullTime = (timestamp) => timestamp ? `${(0, _tmagic_form.datetimeFormatter)(new Date(timestamp), "", "YYYY-MM-DD HH:mm:ss")}` : "";
9487
+ /** 取一组历史步骤里最后一步(最近一次)的时间戳,用于组头部展示。 */
9488
+ var groupTimestamp = (group) => group.steps[group.steps.length - 1]?.step.timestamp;
9411
9489
  var opLabel = (op) => {
9412
9490
  switch (op) {
9413
9491
  case "add": return "新增";
@@ -9415,6 +9493,28 @@
9415
9493
  default: return "修改";
9416
9494
  }
9417
9495
  };
9496
+ /** 内置操作途径的中文文案;自定义来源直接回显原值,未知 / 缺省返回空串(UI 据此不渲染)。 */
9497
+ var HISTORY_SOURCE_LABELS = {
9498
+ stage: "画布",
9499
+ tree: "树面板",
9500
+ "component-panel": "组件面板",
9501
+ props: "配置面板",
9502
+ code: "源码",
9503
+ "stage-contextmenu": "画布菜单",
9504
+ "tree-contextmenu": "树菜单",
9505
+ toolbar: "工具栏",
9506
+ shortcut: "快捷键",
9507
+ rollback: "回滚",
9508
+ api: "接口",
9509
+ ai: "AI",
9510
+ unknown: "未知"
9511
+ };
9512
+ /** 操作途径文案:用于历史面板展示「画布 / 树面板 / 配置面板…」标签。 */
9513
+ var sourceLabel = (source = "unknown") => {
9514
+ return HISTORY_SOURCE_LABELS[source] ?? `${source}`;
9515
+ };
9516
+ /** 取一组历史步骤里最后一步(最近一次)的操作途径,用于组头部展示。 */
9517
+ var groupSource = (group) => group.steps[group.steps.length - 1]?.step.source;
9418
9518
  var nameOf = (node) => node?.name || node?.type || `${node?.id ?? ""}`;
9419
9519
  /**
9420
9520
  * 默认描述里展示「名称 (id: xxx)」,便于区分同名实体。
@@ -9510,32 +9610,59 @@
9510
9610
  const target = labelWithId(group.steps[group.steps.length - 1].step.newContent?.name || group.steps[0].step.oldContent?.name, group.id);
9511
9611
  return pathList ? `修改 ${target} · ${pathList}${paths.size > 3 ? "…" : ""}` : `修改 ${target}`;
9512
9612
  };
9613
+ /**
9614
+ * 页面 step 是否支持「回滚」(类 git revert):
9615
+ * - 新增 / 删除:不依赖 changeRecords,反向应用即删除 / 加回,始终可回滚;
9616
+ * - 更新:必须每个被更新节点都带有 changeRecords,才支持按 propPath 局部反向 patch。
9617
+ * 缺失 changeRecords 的更新只能整节点替换,会冲掉该节点后续的无关变更,因此不支持回滚。
9618
+ */
9619
+ var isPageStepRevertable = (step) => {
9620
+ if (step.opType !== "update") return true;
9621
+ const items = step.updatedItems ?? [];
9622
+ if (!items.length) return false;
9623
+ return items.every((item) => Boolean(item.changeRecords?.length));
9624
+ };
9625
+ /**
9626
+ * 数据源 step 是否支持「回滚」:
9627
+ * - 新增(oldSchema=null)/ 删除(newSchema=null):不依赖 changeRecords,始终可回滚;
9628
+ * - 更新(前后 schema 都存在):必须有 changeRecords 才支持局部反向 patch,否则不支持回滚。
9629
+ */
9630
+ var isDataSourceStepRevertable = (step) => {
9631
+ if (step.oldSchema === null || step.newSchema === null) return true;
9632
+ return Boolean(step.changeRecords?.length);
9633
+ };
9634
+ /**
9635
+ * 代码块 step 是否支持「回滚」:
9636
+ * - 新增(oldContent=null)/ 删除(newContent=null):不依赖 changeRecords,始终可回滚;
9637
+ * - 更新(前后 content 都存在):必须有 changeRecords 才支持局部反向 patch,否则不支持回滚。
9638
+ */
9639
+ var isCodeBlockStepRevertable = (step) => {
9640
+ if (step.oldContent === null || step.newContent === null) return true;
9641
+ return Boolean(step.changeRecords?.length);
9642
+ };
9513
9643
  //#endregion
9514
9644
  //#region packages/editor/src/layouts/history-list/GroupRow.vue?vue&type=script&setup=true&lang.ts
9515
- var _hoisted_1$62 = ["title"];
9516
- var _hoisted_2$22 = ["title"];
9645
+ var _hoisted_1$61 = ["title"];
9646
+ var _hoisted_2$21 = ["title"];
9517
9647
  var _hoisted_3$8 = { class: "m-editor-history-list-item-desc" };
9518
- var _hoisted_4$7 = {
9519
- key: 0,
9520
- class: "m-editor-history-list-item-current"
9521
- };
9522
- var _hoisted_5$3 = {
9648
+ var _hoisted_4$7 = ["title"];
9649
+ var _hoisted_5$3 = ["title"];
9650
+ var _hoisted_6$3 = {
9523
9651
  key: 2,
9524
9652
  class: "m-editor-history-list-item-merge"
9525
9653
  };
9526
- var _hoisted_6$3 = {
9654
+ var _hoisted_7$1 = {
9527
9655
  key: 0,
9528
9656
  class: "m-editor-history-list-substeps"
9529
9657
  };
9530
- var _hoisted_7 = ["title", "onClick"];
9531
- var _hoisted_8 = { class: "m-editor-history-list-item-index" };
9532
- var _hoisted_9 = { class: "m-editor-history-list-substep-desc" };
9533
- var _hoisted_10 = {
9534
- key: 0,
9535
- class: "m-editor-history-list-item-current"
9536
- };
9537
- var _hoisted_11 = ["onClick"];
9538
- var _hoisted_12 = ["onClick"];
9658
+ var _hoisted_8 = ["title"];
9659
+ var _hoisted_9 = { class: "m-editor-history-list-item-index" };
9660
+ var _hoisted_10 = { class: "m-editor-history-list-substep-desc" };
9661
+ var _hoisted_11 = ["title"];
9662
+ var _hoisted_12 = ["title"];
9663
+ var _hoisted_13 = ["onClick"];
9664
+ var _hoisted_14 = ["onClick"];
9665
+ var _hoisted_15 = ["onClick"];
9539
9666
  var GroupRow_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ (0, vue.defineComponent)({
9540
9667
  name: "MEditorHistoryListGroupRow",
9541
9668
  __name: "GroupRow",
@@ -9545,10 +9672,20 @@
9545
9672
  merged: { type: Boolean },
9546
9673
  opType: {},
9547
9674
  desc: {},
9675
+ source: {},
9676
+ time: {},
9677
+ timeTitle: {},
9548
9678
  stepCount: {},
9549
9679
  subSteps: {},
9550
9680
  expanded: { type: Boolean },
9551
- isCurrent: { type: Boolean }
9681
+ isCurrent: {
9682
+ type: Boolean,
9683
+ default: false
9684
+ },
9685
+ gotoEnabled: {
9686
+ type: Boolean,
9687
+ default: true
9688
+ }
9552
9689
  },
9553
9690
  emits: [
9554
9691
  "toggle",
@@ -9559,33 +9696,29 @@
9559
9696
  setup(__props, { emit: __emit }) {
9560
9697
  const props = __props;
9561
9698
  const emit = __emit;
9562
- /** 单步组:头部可点击 goto;合并组:头部可点击切换展开。当前组(isCurrent)的单步组头部不可点击。 */
9563
- const isHeadClickable = (0, vue.computed)(() => {
9564
- if (props.merged) return true;
9565
- return !props.isCurrent;
9566
- });
9699
+ /**
9700
+ * 仅合并组头部可点击(切换展开 / 收起);
9701
+ * 单步组的跳转改由头部的「回退」按钮触发,整行不再可点击。
9702
+ */
9703
+ const isHeadClickable = (0, vue.computed)(() => props.merged);
9567
9704
  const headTitle = (0, vue.computed)(() => {
9568
9705
  if (props.merged) return props.expanded ? "点击收起子步" : "点击展开子步";
9569
9706
  if (props.isCurrent) return "当前所在记录";
9570
- return "点击跳转到该记录";
9707
+ return "";
9571
9708
  });
9572
9709
  /**
9573
- * 头部点击行为分流:
9574
- * - 合并组:仅切换展开 / 收起,不触发 goto;
9575
- * - 单步组:跳转到该唯一步骤;当前组忽略点击。
9710
+ * 头部点击行为:仅合并组切换展开 / 收起;单步组不再响应整行点击。
9576
9711
  */
9577
9712
  const onHeadClick = () => {
9578
- if (props.merged) {
9579
- emit("toggle", props.groupKey);
9580
- return;
9581
- }
9582
- if (props.isCurrent) return;
9583
- if (!props.subSteps.length) return;
9584
- emit("goto", props.subSteps[0].index);
9713
+ if (props.merged) emit("toggle", props.groupKey);
9585
9714
  };
9586
- const onSubStepClick = (s) => {
9587
- if (s.isCurrent) return;
9588
- emit("goto", s.index);
9715
+ const onGotoClick = (index) => {
9716
+ if (!props.gotoEnabled) return;
9717
+ emit("goto", index);
9718
+ };
9719
+ const subStepTitle = (s) => {
9720
+ if (s.isCurrent) return "当前所在记录";
9721
+ return "";
9589
9722
  };
9590
9723
  /** 单步组头部是否展示"查看差异"入口:要求该唯一子步本身可对比。 */
9591
9724
  const headDiffable = (0, vue.computed)(() => !props.merged && Boolean(props.subSteps[0]?.diffable));
@@ -9635,54 +9768,82 @@
9635
9768
  (0, vue.createElementVNode)("span", {
9636
9769
  class: "m-editor-history-list-item-index",
9637
9770
  title: headIndexTitle.value
9638
- }, (0, vue.toDisplayString)(headIndexLabel.value), 9, _hoisted_2$22),
9771
+ }, (0, vue.toDisplayString)(headIndexLabel.value), 9, _hoisted_2$21),
9639
9772
  (0, vue.createElementVNode)("span", { class: (0, vue.normalizeClass)(["m-editor-history-list-item-op", `op-${__props.opType}`]) }, (0, vue.toDisplayString)((0, vue.unref)(opLabel)(__props.opType)), 3),
9640
9773
  (0, vue.createElementVNode)("span", _hoisted_3$8, (0, vue.toDisplayString)(__props.desc), 1),
9641
- __props.isCurrent ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_4$7, "当前")) : (0, vue.createCommentVNode)("v-if", true),
9642
- !__props.merged && headDiffable.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
9774
+ !__props.merged && (0, vue.unref)(sourceLabel)(__props.source) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
9775
+ key: 0,
9776
+ class: "m-editor-history-list-item-source",
9777
+ title: `操作途径:${(0, vue.unref)(sourceLabel)(__props.source)}`
9778
+ }, (0, vue.toDisplayString)((0, vue.unref)(sourceLabel)(__props.source)), 9, _hoisted_4$7)) : (0, vue.createCommentVNode)("v-if", true),
9779
+ !__props.merged && __props.time ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
9643
9780
  key: 1,
9644
- class: "m-editor-history-list-item-diff",
9645
- title: "查看修改差异",
9646
- onClick: _cache[0] || (_cache[0] = (0, vue.withModifiers)(($event) => onDiffClick(__props.subSteps[0].index), ["stop"]))
9647
- }, "查看差异")) : (0, vue.createCommentVNode)("v-if", true),
9648
- __props.merged ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_5$3, "合并 " + (0, vue.toDisplayString)(__props.stepCount) + " 步", 1)) : (0, vue.createCommentVNode)("v-if", true),
9781
+ class: "m-editor-history-list-item-time",
9782
+ title: __props.timeTitle || __props.time
9783
+ }, (0, vue.toDisplayString)(__props.time), 9, _hoisted_5$3)) : (0, vue.createCommentVNode)("v-if", true),
9784
+ __props.merged ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_6$3, "合并 " + (0, vue.toDisplayString)(__props.stepCount) + " 步", 1)) : (0, vue.createCommentVNode)("v-if", true),
9649
9785
  !__props.merged && headRevertable.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
9650
9786
  key: 3,
9651
9787
  class: "m-editor-history-list-item-revert",
9652
9788
  title: "将该步骤的修改作为一次新操作反向应用(不影响后续历史)",
9653
- onClick: _cache[1] || (_cache[1] = (0, vue.withModifiers)(($event) => onRevertClick(__props.subSteps[0].index), ["stop"]))
9789
+ onClick: _cache[0] || (_cache[0] = (0, vue.withModifiers)(($event) => onRevertClick(__props.subSteps[0].index), ["stop"]))
9654
9790
  }, "回滚")) : (0, vue.createCommentVNode)("v-if", true),
9655
- __props.merged ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
9791
+ !__props.merged && __props.gotoEnabled && !__props.isCurrent && __props.subSteps.length ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
9656
9792
  key: 4,
9793
+ class: "m-editor-history-list-item-goto",
9794
+ title: "回到该记录",
9795
+ onClick: _cache[1] || (_cache[1] = (0, vue.withModifiers)(($event) => onGotoClick(__props.subSteps[0].index), ["stop"]))
9796
+ }, "回到")) : (0, vue.createCommentVNode)("v-if", true),
9797
+ !__props.merged && headDiffable.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
9798
+ key: 5,
9799
+ class: "m-editor-history-list-item-diff",
9800
+ title: "查看修改差异",
9801
+ onClick: _cache[2] || (_cache[2] = (0, vue.withModifiers)(($event) => onDiffClick(__props.subSteps[0].index), ["stop"]))
9802
+ }, "查看差异")) : (0, vue.createCommentVNode)("v-if", true),
9803
+ __props.merged ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
9804
+ key: 6,
9657
9805
  class: (0, vue.normalizeClass)(["m-editor-history-list-group-toggle", { "is-expanded": __props.expanded }])
9658
9806
  }, "▾", 2)) : (0, vue.createCommentVNode)("v-if", true)
9659
- ], 10, _hoisted_1$62), __props.merged && __props.expanded ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("ul", _hoisted_6$3, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(subStepsDisplay.value, (s) => {
9807
+ ], 10, _hoisted_1$61), __props.merged && __props.expanded ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("ul", _hoisted_7$1, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(subStepsDisplay.value, (s) => {
9660
9808
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("li", {
9661
9809
  key: s.index,
9662
9810
  class: (0, vue.normalizeClass)({
9663
9811
  "is-undone": !s.applied,
9664
- "is-current": s.isCurrent,
9665
- "is-clickable": !s.isCurrent
9812
+ "is-current": s.isCurrent
9666
9813
  }),
9667
- title: s.isCurrent ? "当前所在记录" : "点击跳转到该记录",
9668
- onClick: ($event) => onSubStepClick(s)
9814
+ title: subStepTitle(s)
9669
9815
  }, [
9670
- (0, vue.createElementVNode)("span", _hoisted_8, "#" + (0, vue.toDisplayString)(s.index + 1), 1),
9671
- (0, vue.createElementVNode)("span", _hoisted_9, (0, vue.toDisplayString)(s.desc), 1),
9672
- s.isCurrent ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_10, "当前")) : (0, vue.createCommentVNode)("v-if", true),
9673
- s.diffable ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
9816
+ (0, vue.createElementVNode)("span", _hoisted_9, "#" + (0, vue.toDisplayString)(s.index + 1), 1),
9817
+ (0, vue.createElementVNode)("span", _hoisted_10, (0, vue.toDisplayString)(s.desc), 1),
9818
+ (0, vue.unref)(sourceLabel)(s.source) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
9819
+ key: 0,
9820
+ class: "m-editor-history-list-item-source",
9821
+ title: `操作途径:${(0, vue.unref)(sourceLabel)(s.source)}`
9822
+ }, (0, vue.toDisplayString)((0, vue.unref)(sourceLabel)(s.source)), 9, _hoisted_11)) : (0, vue.createCommentVNode)("v-if", true),
9823
+ s.time ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
9674
9824
  key: 1,
9675
- class: "m-editor-history-list-item-diff",
9676
- title: "查看修改差异",
9677
- onClick: (0, vue.withModifiers)(($event) => onDiffClick(s.index), ["stop"])
9678
- }, "查看差异", 8, _hoisted_11)) : (0, vue.createCommentVNode)("v-if", true),
9825
+ class: "m-editor-history-list-item-time",
9826
+ title: s.timeTitle || s.time
9827
+ }, (0, vue.toDisplayString)(s.time), 9, _hoisted_12)) : (0, vue.createCommentVNode)("v-if", true),
9679
9828
  s.revertable ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
9680
9829
  key: 2,
9681
9830
  class: "m-editor-history-list-item-revert",
9682
9831
  title: "将该步骤的修改作为一次新操作反向应用(不影响后续历史)",
9683
9832
  onClick: (0, vue.withModifiers)(($event) => onRevertClick(s.index), ["stop"])
9684
- }, "回滚", 8, _hoisted_12)) : (0, vue.createCommentVNode)("v-if", true)
9685
- ], 10, _hoisted_7);
9833
+ }, "回滚", 8, _hoisted_13)) : (0, vue.createCommentVNode)("v-if", true),
9834
+ __props.gotoEnabled && !s.isCurrent ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
9835
+ key: 3,
9836
+ class: "m-editor-history-list-item-goto",
9837
+ title: "回到该记录",
9838
+ onClick: (0, vue.withModifiers)(($event) => onGotoClick(s.index), ["stop"])
9839
+ }, "回到", 8, _hoisted_14)) : (0, vue.createCommentVNode)("v-if", true),
9840
+ s.diffable ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
9841
+ key: 4,
9842
+ class: "m-editor-history-list-item-diff",
9843
+ title: "查看修改差异",
9844
+ onClick: (0, vue.withModifiers)(($event) => onDiffClick(s.index), ["stop"])
9845
+ }, "查看差异", 8, _hoisted_15)) : (0, vue.createCommentVNode)("v-if", true)
9846
+ ], 10, _hoisted_8);
9686
9847
  }), 128))])) : (0, vue.createCommentVNode)("v-if", true)], 2);
9687
9848
  };
9688
9849
  }
@@ -9692,15 +9853,17 @@
9692
9853
  var GroupRow_default = GroupRow_vue_vue_type_script_setup_true_lang_default;
9693
9854
  //#endregion
9694
9855
  //#region packages/editor/src/layouts/history-list/InitialRow.vue?vue&type=script&setup=true&lang.ts
9695
- var _hoisted_1$61 = ["title"];
9696
- var _hoisted_2$21 = {
9697
- key: 0,
9698
- class: "m-editor-history-list-item-current"
9699
- };
9856
+ var _hoisted_1$60 = ["title"];
9700
9857
  var InitialRow_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ (0, vue.defineComponent)({
9701
9858
  name: "MEditorHistoryListInitialRow",
9702
9859
  __name: "InitialRow",
9703
- props: { isCurrent: { type: Boolean } },
9860
+ props: {
9861
+ isCurrent: { type: Boolean },
9862
+ gotoEnabled: {
9863
+ type: Boolean,
9864
+ default: true
9865
+ }
9866
+ },
9704
9867
  emits: ["goto-initial"],
9705
9868
  setup(__props, { emit: __emit }) {
9706
9869
  /**
@@ -9722,8 +9885,7 @@
9722
9885
  "is-current": __props.isCurrent,
9723
9886
  "is-clickable": !__props.isCurrent
9724
9887
  }]),
9725
- title: __props.isCurrent ? "当前已回到未修改的初始状态" : "点击回到未修改的初始状态",
9726
- onClick
9888
+ title: __props.isCurrent ? "当前已回到未修改的初始状态" : "点击回到未修改的初始状态"
9727
9889
  }, [
9728
9890
  _cache[0] || (_cache[0] = (0, vue.createElementVNode)("span", {
9729
9891
  class: "m-editor-history-list-item-index",
@@ -9731,8 +9893,13 @@
9731
9893
  }, "#0", -1)),
9732
9894
  _cache[1] || (_cache[1] = (0, vue.createElementVNode)("span", { class: "m-editor-history-list-item-op op-initial" }, "初始", -1)),
9733
9895
  _cache[2] || (_cache[2] = (0, vue.createElementVNode)("span", { class: "m-editor-history-list-item-desc" }, "未修改的初始状态", -1)),
9734
- __props.isCurrent ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_2$21, "当前")) : (0, vue.createCommentVNode)("v-if", true)
9735
- ], 10, _hoisted_1$61);
9896
+ __props.gotoEnabled && !__props.isCurrent ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
9897
+ key: 0,
9898
+ class: "m-editor-history-list-item-goto",
9899
+ title: "回到该记录",
9900
+ onClick: (0, vue.withModifiers)(onClick, ["stop"])
9901
+ }, "回到")) : (0, vue.createCommentVNode)("v-if", true)
9902
+ ], 10, _hoisted_1$60);
9736
9903
  };
9737
9904
  }
9738
9905
  });
@@ -9741,7 +9908,7 @@
9741
9908
  var InitialRow_default = InitialRow_vue_vue_type_script_setup_true_lang_default;
9742
9909
  //#endregion
9743
9910
  //#region packages/editor/src/layouts/history-list/Bucket.vue?vue&type=script&setup=true&lang.ts
9744
- var _hoisted_1$60 = { class: "m-editor-history-list-bucket" };
9911
+ var _hoisted_1$59 = { class: "m-editor-history-list-bucket" };
9745
9912
  var _hoisted_2$20 = { class: "m-editor-history-list-bucket-title" };
9746
9913
  var _hoisted_3$7 = { class: "m-editor-history-list-bucket-count" };
9747
9914
  var _hoisted_4$6 = { class: "m-editor-history-list-ul" };
@@ -9752,11 +9919,20 @@
9752
9919
  title: {},
9753
9920
  bucketId: {},
9754
9921
  prefix: {},
9922
+ showInitial: {
9923
+ type: Boolean,
9924
+ default: true
9925
+ },
9755
9926
  groups: {},
9756
- describeGroup: { type: Function },
9757
- describeStep: { type: Function },
9758
- isStepDiffable: { type: Function },
9759
- expanded: {}
9927
+ describeGroup: {},
9928
+ describeStep: {},
9929
+ isStepDiffable: {},
9930
+ isStepRevertable: {},
9931
+ expanded: {},
9932
+ gotoEnabled: {
9933
+ type: Boolean,
9934
+ default: true
9935
+ }
9760
9936
  },
9761
9937
  emits: [
9762
9938
  "toggle",
@@ -9770,19 +9946,22 @@
9770
9946
  /** 该 bucket 是否处于初始状态(栈 cursor=0),等价于全部 group 都未 applied。 */
9771
9947
  const isInitial = (0, vue.computed)(() => props.groups.length > 0 && props.groups.every((g) => !g.applied));
9772
9948
  return (_ctx, _cache) => {
9773
- return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$60, [(0, vue.createElementVNode)("div", _hoisted_2$20, [
9949
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$59, [(0, vue.createElementVNode)("div", _hoisted_2$20, [
9774
9950
  (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)(__props.title), 1),
9775
9951
  (0, vue.createElementVNode)("code", null, (0, vue.toDisplayString)(String(__props.bucketId)), 1),
9776
9952
  (0, vue.createElementVNode)("span", _hoisted_3$7, (0, vue.toDisplayString)(__props.groups.length) + " 组", 1)
9777
9953
  ]), (0, vue.createElementVNode)("ul", _hoisted_4$6, [
9778
- ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.groups, (group, gIdx) => {
9954
+ ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.groups, (group) => {
9779
9955
  return (0, vue.openBlock)(), (0, vue.createBlock)(GroupRow_default, {
9780
- key: `${__props.prefix}-${__props.bucketId}-${gIdx}`,
9781
- "group-key": `${__props.prefix}-${__props.bucketId}-${gIdx}`,
9956
+ key: `${__props.prefix}-${__props.bucketId}-${group.steps[0]?.index}`,
9957
+ "group-key": `${__props.prefix}-${__props.bucketId}-${group.steps[0]?.index}`,
9782
9958
  applied: group.applied,
9783
9959
  merged: group.steps.length > 1,
9784
9960
  "op-type": group.opType,
9785
9961
  desc: __props.describeGroup(group),
9962
+ source: (0, vue.unref)(groupSource)(group),
9963
+ time: (0, vue.unref)(formatHistoryTime)((0, vue.unref)(groupTimestamp)(group)),
9964
+ "time-title": (0, vue.unref)(formatHistoryFullTime)((0, vue.unref)(groupTimestamp)(group)),
9786
9965
  "step-count": group.steps.length,
9787
9966
  "sub-steps": group.steps.map((s) => ({
9788
9967
  index: s.index,
@@ -9790,10 +9969,14 @@
9790
9969
  isCurrent: s.isCurrent,
9791
9970
  desc: __props.describeStep(s.step),
9792
9971
  diffable: __props.isStepDiffable ? __props.isStepDiffable(s.step) : false,
9793
- revertable: s.applied
9972
+ revertable: s.applied && (__props.isStepRevertable ? __props.isStepRevertable(s.step) : true),
9973
+ source: s.step.source,
9974
+ time: (0, vue.unref)(formatHistoryTime)(s.step.timestamp),
9975
+ timeTitle: (0, vue.unref)(formatHistoryFullTime)(s.step.timestamp)
9794
9976
  })),
9795
9977
  "is-current": group.isCurrent,
9796
- expanded: !!__props.expanded[`${__props.prefix}-${__props.bucketId}-${gIdx}`],
9978
+ expanded: !!__props.expanded[`${__props.prefix}-${__props.bucketId}-${group.steps[0]?.index}`],
9979
+ "goto-enabled": __props.gotoEnabled,
9797
9980
  onToggle: _cache[0] || (_cache[0] = (key) => _ctx.$emit("toggle", key)),
9798
9981
  onGoto: _cache[1] || (_cache[1] = (index) => _ctx.$emit("goto", __props.bucketId, index)),
9799
9982
  onDiffStep: _cache[2] || (_cache[2] = (index) => _ctx.$emit("diff-step", __props.bucketId, index)),
@@ -9804,17 +9987,23 @@
9804
9987
  "merged",
9805
9988
  "op-type",
9806
9989
  "desc",
9990
+ "source",
9991
+ "time",
9992
+ "time-title",
9807
9993
  "step-count",
9808
9994
  "sub-steps",
9809
9995
  "is-current",
9810
- "expanded"
9996
+ "expanded",
9997
+ "goto-enabled"
9811
9998
  ]);
9812
9999
  }), 128)),
9813
- (0, vue.createCommentVNode)("\n 初始状态项:永远位于该 bucket 列表底部(同样按倒序展示,最底部 = 最早状态)。\n 当 bucket 内所有 group 都未 applied 时即为当前位置。\n "),
9814
- (0, vue.createVNode)(InitialRow_default, {
10000
+ (0, vue.createCommentVNode)("\n 初始状态项:永远位于该 bucket 列表底部(同样按倒序展示,最底部 = 最早状态)。\n 当 bucket 内所有 group 都未 applied 时即为当前位置。\n showInitial=false 时不展示(用于没有\"撤销到初始状态\"语义的自定义历史,如业务模块历史)。\n "),
10001
+ __props.showInitial !== false ? ((0, vue.openBlock)(), (0, vue.createBlock)(InitialRow_default, {
10002
+ key: 0,
9815
10003
  "is-current": isInitial.value,
10004
+ "goto-enabled": __props.gotoEnabled,
9816
10005
  onGotoInitial: _cache[4] || (_cache[4] = ($event) => _ctx.$emit("goto-initial", __props.bucketId))
9817
- }, null, 8, ["is-current"])
10006
+ }, null, 8, ["is-current", "goto-enabled"])) : (0, vue.createCommentVNode)("v-if", true)
9818
10007
  ])]);
9819
10008
  };
9820
10009
  }
@@ -9823,77 +10012,27 @@
9823
10012
  //#region packages/editor/src/layouts/history-list/Bucket.vue
9824
10013
  var Bucket_default = Bucket_vue_vue_type_script_setup_true_lang_default;
9825
10014
  //#endregion
9826
- //#region packages/editor/src/layouts/history-list/CodeBlockTab.vue?vue&type=script&setup=true&lang.ts
9827
- var _hoisted_1$59 = {
9828
- key: 0,
9829
- class: "m-editor-history-list-empty"
9830
- };
9831
- var CodeBlockTab_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ (0, vue.defineComponent)({
9832
- name: "MEditorHistoryListCodeBlockTab",
9833
- __name: "CodeBlockTab",
9834
- props: {
9835
- buckets: {},
9836
- expanded: {}
9837
- },
9838
- emits: [
9839
- "toggle",
9840
- "goto",
9841
- "goto-initial",
9842
- "diff-step",
9843
- "revert-step"
9844
- ],
9845
- setup(__props) {
9846
- /** 仅 update(前后 content 都存在)时可查看差异。 */
9847
- const isCodeBlockStepDiffable = (step) => Boolean(step.oldContent && step.newContent);
9848
- return (_ctx, _cache) => {
9849
- return !__props.buckets.length ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$59, "暂无操作记录")) : ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicScrollbar), {
9850
- key: 1,
9851
- "max-height": "360px"
9852
- }, {
9853
- default: (0, vue.withCtx)(() => [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.buckets, (bucket) => {
9854
- return (0, vue.openBlock)(), (0, vue.createBlock)(Bucket_default, {
9855
- key: `cb-${bucket.id}`,
9856
- title: "代码块",
9857
- "bucket-id": bucket.id,
9858
- prefix: "cb",
9859
- groups: bucket.groups,
9860
- "describe-group": (0, vue.unref)(describeCodeBlockGroup),
9861
- "describe-step": (0, vue.unref)(describeCodeBlockStep),
9862
- "is-step-diffable": isCodeBlockStepDiffable,
9863
- expanded: __props.expanded,
9864
- onToggle: _cache[0] || (_cache[0] = (key) => _ctx.$emit("toggle", key)),
9865
- onGoto: _cache[1] || (_cache[1] = (id, index) => _ctx.$emit("goto", id, index)),
9866
- onGotoInitial: _cache[2] || (_cache[2] = (id) => _ctx.$emit("goto-initial", id)),
9867
- onDiffStep: _cache[3] || (_cache[3] = (id, index) => _ctx.$emit("diff-step", id, index)),
9868
- onRevertStep: _cache[4] || (_cache[4] = (id, index) => _ctx.$emit("revert-step", id, index))
9869
- }, null, 8, [
9870
- "bucket-id",
9871
- "groups",
9872
- "describe-group",
9873
- "describe-step",
9874
- "expanded"
9875
- ]);
9876
- }), 128))]),
9877
- _: 1
9878
- }));
9879
- };
9880
- }
9881
- });
9882
- //#endregion
9883
- //#region packages/editor/src/layouts/history-list/CodeBlockTab.vue
9884
- var CodeBlockTab_default = CodeBlockTab_vue_vue_type_script_setup_true_lang_default;
9885
- //#endregion
9886
- //#region packages/editor/src/layouts/history-list/DataSourceTab.vue?vue&type=script&setup=true&lang.ts
10015
+ //#region packages/editor/src/layouts/history-list/BucketTab.vue?vue&type=script&setup=true&lang.ts
9887
10016
  var _hoisted_1$58 = {
9888
10017
  key: 0,
9889
10018
  class: "m-editor-history-list-empty"
9890
10019
  };
9891
- var DataSourceTab_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ (0, vue.defineComponent)({
9892
- name: "MEditorHistoryListDataSourceTab",
9893
- __name: "DataSourceTab",
10020
+ var BucketTab_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ (0, vue.defineComponent)({
10021
+ name: "MEditorHistoryListBucketTab",
10022
+ __name: "BucketTab",
9894
10023
  props: {
10024
+ title: {},
10025
+ prefix: {},
9895
10026
  buckets: {},
9896
- expanded: {}
10027
+ describeGroup: {},
10028
+ describeStep: {},
10029
+ isStepDiffable: {},
10030
+ isStepRevertable: {},
10031
+ expanded: {},
10032
+ gotoEnabled: {
10033
+ type: Boolean,
10034
+ default: true
10035
+ }
9897
10036
  },
9898
10037
  emits: [
9899
10038
  "toggle",
@@ -9903,8 +10042,6 @@
9903
10042
  "revert-step"
9904
10043
  ],
9905
10044
  setup(__props) {
9906
- /** 仅 update(前后 schema 都存在)时可查看差异。 */
9907
- const isDataSourceStepDiffable = (step) => Boolean(step.oldSchema && step.newSchema);
9908
10045
  return (_ctx, _cache) => {
9909
10046
  return !__props.buckets.length ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$58, "暂无操作记录")) : ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicScrollbar), {
9910
10047
  key: 1,
@@ -9912,26 +10049,33 @@
9912
10049
  }, {
9913
10050
  default: (0, vue.withCtx)(() => [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.buckets, (bucket) => {
9914
10051
  return (0, vue.openBlock)(), (0, vue.createBlock)(Bucket_default, {
9915
- key: `ds-${bucket.id}`,
9916
- title: "数据源",
10052
+ key: `${__props.prefix}-${bucket.id}`,
10053
+ title: __props.title,
9917
10054
  "bucket-id": bucket.id,
9918
- prefix: "ds",
10055
+ prefix: __props.prefix,
9919
10056
  groups: bucket.groups,
9920
- "describe-group": (0, vue.unref)(describeDataSourceGroup),
9921
- "describe-step": (0, vue.unref)(describeDataSourceStep),
9922
- "is-step-diffable": isDataSourceStepDiffable,
10057
+ "describe-group": __props.describeGroup,
10058
+ "describe-step": __props.describeStep,
10059
+ "is-step-diffable": __props.isStepDiffable,
10060
+ "is-step-revertable": __props.isStepRevertable,
9923
10061
  expanded: __props.expanded,
10062
+ "goto-enabled": __props.gotoEnabled,
9924
10063
  onToggle: _cache[0] || (_cache[0] = (key) => _ctx.$emit("toggle", key)),
9925
10064
  onGoto: _cache[1] || (_cache[1] = (id, index) => _ctx.$emit("goto", id, index)),
9926
10065
  onGotoInitial: _cache[2] || (_cache[2] = (id) => _ctx.$emit("goto-initial", id)),
9927
10066
  onDiffStep: _cache[3] || (_cache[3] = (id, index) => _ctx.$emit("diff-step", id, index)),
9928
10067
  onRevertStep: _cache[4] || (_cache[4] = (id, index) => _ctx.$emit("revert-step", id, index))
9929
10068
  }, null, 8, [
10069
+ "title",
9930
10070
  "bucket-id",
10071
+ "prefix",
9931
10072
  "groups",
9932
10073
  "describe-group",
9933
10074
  "describe-step",
9934
- "expanded"
10075
+ "is-step-diffable",
10076
+ "is-step-revertable",
10077
+ "expanded",
10078
+ "goto-enabled"
9935
10079
  ]);
9936
10080
  }), 128))]),
9937
10081
  _: 1
@@ -9940,8 +10084,8 @@
9940
10084
  }
9941
10085
  });
9942
10086
  //#endregion
9943
- //#region packages/editor/src/layouts/history-list/DataSourceTab.vue
9944
- var DataSourceTab_default = DataSourceTab_vue_vue_type_script_setup_true_lang_default;
10087
+ //#region packages/editor/src/layouts/history-list/BucketTab.vue
10088
+ var BucketTab_default = BucketTab_vue_vue_type_script_setup_true_lang_default;
9945
10089
  //#endregion
9946
10090
  //#region packages/editor/src/components/CompareForm.vue?vue&type=script&setup=true&lang.ts
9947
10091
  var CompareForm_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ (0, vue.defineComponent)({
@@ -9955,15 +10099,10 @@
9955
10099
  dataSourceType: {},
9956
10100
  labelWidth: { default: "120px" },
9957
10101
  height: {},
9958
- extendState: {}
10102
+ extendState: {},
10103
+ loadConfig: {}
9959
10104
  },
9960
10105
  setup(__props, { expose: __expose }) {
9961
- /**
9962
- * 对比类型:
9963
- * - node: 节点组件,按 `type` 从 propsService 获取属性表单配置
9964
- * - data-source: 数据源,按 `type`(base/http/...) 从 dataSourceService 获取数据源表单配置
9965
- * - code-block: 数据源代码块,使用内置的代码块表单配置
9966
- */
9967
10106
  const props = __props;
9968
10107
  const { propsService, dataSourceService, codeBlockService, editorService } = useServices();
9969
10108
  const services = useServices();
@@ -10023,34 +10162,57 @@
10023
10162
  }
10024
10163
  return !isEqual(curValue, lastValue);
10025
10164
  };
10026
- const loadConfig = async () => {
10165
+ const removeStyleDisplayConfig = (formConfig) => formConfig.map((item) => {
10166
+ if (!("type" in item)) return item;
10167
+ if (item?.type !== "tab" || !Array.isArray(item.items)) return item;
10168
+ return {
10169
+ ...item,
10170
+ items: item.items.map((tabPane) => {
10171
+ if (tabPane?.title !== "样式" || !Array.isArray(tabPane.items)) return tabPane;
10172
+ return {
10173
+ ...tabPane,
10174
+ display: true
10175
+ };
10176
+ })
10177
+ };
10178
+ });
10179
+ /**
10180
+ * 内置的默认 FormConfig 加载逻辑:按 `category` 从对应 service / 工具取配置。
10181
+ * 作为 ctx.defaultLoadConfig 透传给自定义 `loadConfig`,方便复用与二次加工。
10182
+ */
10183
+ const defaultLoadConfig = async () => {
10027
10184
  switch (props.category) {
10028
10185
  case "node":
10029
- if (!props.type) {
10030
- config.value = [];
10031
- return;
10032
- }
10033
- config.value = await propsService.getPropsConfig(props.type);
10034
- break;
10035
- case "data-source":
10036
- config.value = dataSourceService.getFormConfig(props.type || "base");
10037
- break;
10038
- case "code-block":
10039
- config.value = getCodeBlockFormConfig({
10040
- paramColConfig: codeBlockService.getParamsColConfig(),
10041
- isDataSource: () => Boolean(props.dataSourceType),
10042
- dataSourceType: () => props.dataSourceType,
10043
- codeOptions,
10044
- editable: false
10045
- });
10046
- break;
10047
- default: config.value = [];
10186
+ if (!props.type) return [];
10187
+ return removeStyleDisplayConfig(await propsService.getPropsConfig(props.type));
10188
+ case "data-source": return dataSourceService.getFormConfig(props.type || "base");
10189
+ case "code-block": return getCodeBlockFormConfig({
10190
+ paramColConfig: codeBlockService.getParamsColConfig(),
10191
+ isDataSource: () => Boolean(props.dataSourceType),
10192
+ dataSourceType: () => props.dataSourceType,
10193
+ codeOptions,
10194
+ editable: false
10195
+ });
10196
+ default: return [];
10197
+ }
10198
+ };
10199
+ const loadConfig = async () => {
10200
+ if (props.loadConfig) {
10201
+ config.value = await props.loadConfig({
10202
+ category: props.category,
10203
+ type: props.type,
10204
+ dataSourceType: props.dataSourceType,
10205
+ defaultLoadConfig
10206
+ });
10207
+ return;
10048
10208
  }
10209
+ config.value = await defaultLoadConfig();
10049
10210
  };
10050
10211
  (0, vue.watch)([
10051
10212
  () => props.category,
10052
10213
  () => props.type,
10053
- () => props.dataSourceType
10214
+ () => props.dataSourceType,
10215
+ () => props.loadConfig
10054
10216
  ], () => {
10055
10217
  loadConfig();
10056
10218
  }, { immediate: true });
@@ -10111,20 +10273,37 @@
10111
10273
  key: 0,
10112
10274
  class: "m-editor-history-diff-dialog-body"
10113
10275
  };
10114
- var _hoisted_2$19 = { class: "m-editor-history-diff-dialog-header" };
10115
- var _hoisted_3$6 = { class: "m-editor-history-diff-dialog-target" };
10116
- var _hoisted_4$5 = { class: "m-editor-history-diff-dialog-controls" };
10117
- var _hoisted_5$2 = { class: "m-editor-history-diff-dialog-legend" };
10118
- var _hoisted_6$2 = {
10276
+ var _hoisted_2$19 = {
10277
+ key: 0,
10278
+ class: "m-editor-history-diff-dialog-notice"
10279
+ };
10280
+ var _hoisted_3$6 = { class: "m-editor-history-diff-dialog-header" };
10281
+ var _hoisted_4$5 = { class: "m-editor-history-diff-dialog-target" };
10282
+ var _hoisted_5$2 = { class: "m-editor-history-diff-dialog-controls" };
10283
+ var _hoisted_6$2 = { class: "m-editor-history-diff-dialog-legend" };
10284
+ var _hoisted_7 = {
10119
10285
  key: 0,
10120
10286
  class: "m-editor-history-diff-dialog-tip"
10121
10287
  };
10122
10288
  var HistoryDiffDialog_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ (0, vue.defineComponent)({
10123
10289
  name: "MEditorHistoryDiffDialog",
10124
10290
  __name: "HistoryDiffDialog",
10125
- props: { extendState: { type: Function } },
10126
- setup(__props, { expose: __expose }) {
10127
- /** 差异对话框的入参 */
10291
+ props: {
10292
+ extendState: {},
10293
+ loadConfig: {},
10294
+ width: { default: "900px" },
10295
+ onConfirm: {},
10296
+ selfDiffFieldTypes: {}
10297
+ },
10298
+ emits: ["close"],
10299
+ setup(__props, { expose: __expose, emit: __emit }) {
10300
+ const props = __props;
10301
+ const emit = __emit;
10302
+ /**
10303
+ * 差异对比模式:
10304
+ * - before:该步骤修改前 vs 该步骤修改后(默认行为,体现这一步带来的变化)
10305
+ * - current:该步骤修改后 vs 当前最新值(用于查看「该步骤之后是否还被改过」)
10306
+ */
10128
10307
  const visible = (0, vue.ref)(false);
10129
10308
  const payload = (0, vue.ref)(null);
10130
10309
  const mode = (0, vue.ref)("before");
@@ -10146,6 +10325,7 @@
10146
10325
  scrollBeyondLastLine: false,
10147
10326
  hideUnchangedRegions: { enabled: true }
10148
10327
  };
10328
+ const dialogTitle = (0, vue.computed)(() => props.onConfirm ? "确认回滚" : "查看修改差异");
10149
10329
  const hasCurrent = (0, vue.computed)(() => payload.value?.currentValue !== void 0 && payload.value?.currentValue !== null);
10150
10330
  /** 左侧(旧/参照)值 */
10151
10331
  const leftValue = (0, vue.computed)(() => {
@@ -10166,13 +10346,20 @@
10166
10346
  if (mode.value !== "current" || !payload.value) return false;
10167
10347
  return isEqual(payload.value.value, payload.value.currentValue);
10168
10348
  });
10349
+ const onConfirmClick = () => {
10350
+ const cb = props.onConfirm;
10351
+ cb?.();
10352
+ visible.value = false;
10353
+ };
10169
10354
  const targetText = (0, vue.computed)(() => {
10170
10355
  if (!payload.value) return "";
10171
- const prefix = {
10356
+ const categoryText = {
10172
10357
  node: "节点",
10173
10358
  "data-source": "数据源",
10174
10359
  "code-block": "代码块"
10175
- }[payload.value.category] || "";
10360
+ };
10361
+ const { category } = payload.value;
10362
+ const prefix = category ? categoryText[category] : "";
10176
10363
  const label = payload.value.targetLabel || payload.value.type || "";
10177
10364
  const { id } = payload.value;
10178
10365
  return [prefix, id !== void 0 && id !== "" ? `${label}(${id})` : label].filter(Boolean).join(":");
@@ -10189,6 +10376,9 @@
10189
10376
  (0, vue.watch)(visible, (v) => {
10190
10377
  if (!v) payload.value = null;
10191
10378
  });
10379
+ const onClose = () => {
10380
+ emit("close");
10381
+ };
10192
10382
  __expose({
10193
10383
  open,
10194
10384
  close
@@ -10196,33 +10386,49 @@
10196
10386
  return (_ctx, _cache) => {
10197
10387
  return (0, vue.openBlock)(), (0, vue.createBlock)(vue.Teleport, { to: "body" }, [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicDialog), {
10198
10388
  modelValue: visible.value,
10199
- "onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => visible.value = $event),
10389
+ "onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => visible.value = $event),
10200
10390
  class: "m-editor-history-diff-dialog",
10201
- title: "查看修改差异",
10202
- width: "900px",
10391
+ title: dialogTitle.value,
10203
10392
  top: "5vh",
10204
10393
  "destroy-on-close": "",
10205
- "append-to-body": ""
10394
+ "append-to-body": "",
10395
+ width: __props.width,
10396
+ onClose
10206
10397
  }, {
10207
- footer: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), {
10398
+ footer: (0, vue.withCtx)(() => [__props.onConfirm ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), {
10208
10399
  size: "small",
10209
10400
  onClick: _cache[2] || (_cache[2] = ($event) => visible.value = false)
10210
10401
  }, {
10211
- default: (0, vue.withCtx)(() => [..._cache[9] || (_cache[9] = [(0, vue.createTextVNode)("关闭", -1)])]),
10402
+ default: (0, vue.withCtx)(() => [..._cache[10] || (_cache[10] = [(0, vue.createTextVNode)("取消", -1)])]),
10212
10403
  _: 1
10213
- })]),
10404
+ }), (0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), {
10405
+ size: "small",
10406
+ type: "primary",
10407
+ onClick: onConfirmClick
10408
+ }, {
10409
+ default: (0, vue.withCtx)(() => [..._cache[11] || (_cache[11] = [(0, vue.createTextVNode)("确定回滚", -1)])]),
10410
+ _: 1
10411
+ })], 64)) : ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicButton), {
10412
+ key: 1,
10413
+ size: "small",
10414
+ onClick: _cache[3] || (_cache[3] = ($event) => visible.value = false)
10415
+ }, {
10416
+ default: (0, vue.withCtx)(() => [..._cache[12] || (_cache[12] = [(0, vue.createTextVNode)("关闭", -1)])]),
10417
+ _: 1
10418
+ }))]),
10214
10419
  default: (0, vue.withCtx)(() => [payload.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$57, [
10215
- (0, vue.createElementVNode)("div", _hoisted_2$19, [(0, vue.createElementVNode)("span", _hoisted_3$6, (0, vue.toDisplayString)(targetText.value), 1), (0, vue.createElementVNode)("div", _hoisted_4$5, [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicRadioGroup), {
10420
+ __props.onConfirm ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_2$19, "仅回滚有差异的字段")) : (0, vue.createCommentVNode)("v-if", true),
10421
+ (0, vue.createElementVNode)("div", _hoisted_3$6, [(0, vue.createElementVNode)("span", _hoisted_4$5, (0, vue.toDisplayString)(targetText.value), 1), (0, vue.createElementVNode)("div", _hoisted_5$2, [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicRadioGroup), {
10216
10422
  modelValue: viewMode.value,
10217
10423
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => viewMode.value = $event),
10218
10424
  size: "small",
10219
10425
  class: "m-editor-history-diff-dialog-view"
10220
10426
  }, {
10221
10427
  default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicRadioButton), { value: "form" }, {
10222
- default: (0, vue.withCtx)(() => [..._cache[4] || (_cache[4] = [(0, vue.createTextVNode)("表单对比", -1)])]),
10428
+ default: (0, vue.withCtx)(() => [..._cache[5] || (_cache[5] = [(0, vue.createTextVNode)("表单对比", -1)])]),
10223
10429
  _: 1
10224
10430
  }), (0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicRadioButton), { value: "code" }, {
10225
- default: (0, vue.withCtx)(() => [..._cache[5] || (_cache[5] = [(0, vue.createTextVNode)("源码对比", -1)])]),
10431
+ default: (0, vue.withCtx)(() => [..._cache[6] || (_cache[6] = [(0, vue.createTextVNode)("源码对比", -1)])]),
10226
10432
  _: 1
10227
10433
  })]),
10228
10434
  _: 1
@@ -10233,18 +10439,18 @@
10233
10439
  class: "m-editor-history-diff-dialog-mode"
10234
10440
  }, {
10235
10441
  default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicRadioButton), { value: "before" }, {
10236
- default: (0, vue.withCtx)(() => [..._cache[6] || (_cache[6] = [(0, vue.createTextVNode)("与修改前对比", -1)])]),
10442
+ default: (0, vue.withCtx)(() => [..._cache[7] || (_cache[7] = [(0, vue.createTextVNode)("与修改前对比", -1)])]),
10237
10443
  _: 1
10238
10444
  }), (0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicRadioButton), {
10239
10445
  value: "current",
10240
10446
  disabled: !hasCurrent.value
10241
10447
  }, {
10242
- default: (0, vue.withCtx)(() => [..._cache[7] || (_cache[7] = [(0, vue.createTextVNode)("与当前对比", -1)])]),
10448
+ default: (0, vue.withCtx)(() => [..._cache[8] || (_cache[8] = [(0, vue.createTextVNode)("与当前对比", -1)])]),
10243
10449
  _: 1
10244
10450
  }, 8, ["disabled"])]),
10245
10451
  _: 1
10246
10452
  }, 8, ["modelValue"])])]),
10247
- (0, vue.createElementVNode)("div", _hoisted_5$2, [
10453
+ (0, vue.createElementVNode)("div", _hoisted_6$2, [
10248
10454
  (0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicTag), {
10249
10455
  size: "small",
10250
10456
  type: "danger"
@@ -10252,7 +10458,7 @@
10252
10458
  default: (0, vue.withCtx)(() => [(0, vue.createTextVNode)((0, vue.toDisplayString)(leftLabel.value), 1)]),
10253
10459
  _: 1
10254
10460
  }),
10255
- _cache[8] || (_cache[8] = (0, vue.createElementVNode)("span", { class: "m-editor-history-diff-dialog-arrow" }, "→", -1)),
10461
+ _cache[9] || (_cache[9] = (0, vue.createElementVNode)("span", { class: "m-editor-history-diff-dialog-arrow" }, "→", -1)),
10256
10462
  (0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicTag), {
10257
10463
  size: "small",
10258
10464
  type: "success"
@@ -10260,16 +10466,18 @@
10260
10466
  default: (0, vue.withCtx)(() => [(0, vue.createTextVNode)((0, vue.toDisplayString)(rightLabel.value), 1)]),
10261
10467
  _: 1
10262
10468
  }),
10263
- mode.value === "current" && isSameAsCurrent.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_6$2, " 当前值与该步修改后一致,无差异 ")) : (0, vue.createCommentVNode)("v-if", true)
10469
+ mode.value === "current" && isSameAsCurrent.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_7, " 当前值与该步修改后一致,无差异 ")) : (0, vue.createCommentVNode)("v-if", true)
10264
10470
  ]),
10265
10471
  viewMode.value === "form" ? ((0, vue.openBlock)(), (0, vue.createBlock)(CompareForm_default, {
10266
- key: 0,
10472
+ key: 1,
10267
10473
  category: payload.value.category,
10268
10474
  type: payload.value.type,
10269
10475
  "data-source-type": payload.value.dataSourceType,
10270
10476
  value: rightValue.value,
10271
10477
  "last-value": leftValue.value,
10272
10478
  "extend-state": __props.extendState,
10479
+ "load-config": __props.loadConfig,
10480
+ "self-diff-field-types": __props.selfDiffFieldTypes,
10273
10481
  height: "70vh"
10274
10482
  }, null, 8, [
10275
10483
  "category",
@@ -10277,9 +10485,11 @@
10277
10485
  "data-source-type",
10278
10486
  "value",
10279
10487
  "last-value",
10280
- "extend-state"
10488
+ "extend-state",
10489
+ "load-config",
10490
+ "self-diff-field-types"
10281
10491
  ])) : ((0, vue.openBlock)(), (0, vue.createBlock)(CodeEditor_default, {
10282
- key: 1,
10492
+ key: 2,
10283
10493
  type: "diff",
10284
10494
  language: "json",
10285
10495
  "init-values": leftValue.value,
@@ -10290,7 +10500,11 @@
10290
10500
  }, null, 8, ["init-values", "modified-values"]))
10291
10501
  ])) : (0, vue.createCommentVNode)("v-if", true)]),
10292
10502
  _: 1
10293
- }, 8, ["modelValue"])]);
10503
+ }, 8, [
10504
+ "modelValue",
10505
+ "title",
10506
+ "width"
10507
+ ])]);
10294
10508
  };
10295
10509
  }
10296
10510
  });
@@ -10344,14 +10558,17 @@
10344
10558
  "max-height": "360px"
10345
10559
  }, {
10346
10560
  default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("ul", _hoisted_2$18, [
10347
- ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.list, (group, gIdx) => {
10561
+ ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.list, (group) => {
10348
10562
  return (0, vue.openBlock)(), (0, vue.createBlock)(GroupRow_default, {
10349
- key: `pg-${gIdx}`,
10350
- "group-key": `pg-${gIdx}`,
10563
+ key: `pg-${group.steps[0]?.index}`,
10564
+ "group-key": `pg-${group.steps[0]?.index}`,
10351
10565
  applied: group.applied,
10352
10566
  merged: group.steps.length > 1,
10353
10567
  "op-type": group.opType,
10354
10568
  desc: (0, vue.unref)(describePageGroup)(group),
10569
+ source: (0, vue.unref)(groupSource)(group),
10570
+ time: (0, vue.unref)(formatHistoryTime)((0, vue.unref)(groupTimestamp)(group)),
10571
+ "time-title": (0, vue.unref)(formatHistoryFullTime)((0, vue.unref)(groupTimestamp)(group)),
10355
10572
  "step-count": group.steps.length,
10356
10573
  "sub-steps": group.steps.map((s) => ({
10357
10574
  index: s.index,
@@ -10359,10 +10576,13 @@
10359
10576
  isCurrent: s.isCurrent,
10360
10577
  desc: (0, vue.unref)(describePageStep)(s.step),
10361
10578
  diffable: isPageStepDiffable(s.step),
10362
- revertable: s.applied
10579
+ revertable: s.applied && (0, vue.unref)(isPageStepRevertable)(s.step),
10580
+ source: s.step.source,
10581
+ time: (0, vue.unref)(formatHistoryTime)(s.step.timestamp),
10582
+ timeTitle: (0, vue.unref)(formatHistoryFullTime)(s.step.timestamp)
10363
10583
  })),
10364
10584
  "is-current": group.isCurrent,
10365
- expanded: !!__props.expanded[`pg-${gIdx}`],
10585
+ expanded: !!__props.expanded[`pg-${group.steps[0]?.index}`],
10366
10586
  onToggle: _cache[0] || (_cache[0] = (key) => _ctx.$emit("toggle", key)),
10367
10587
  onGoto: _cache[1] || (_cache[1] = (index) => _ctx.$emit("goto", index)),
10368
10588
  onDiffStep: _cache[2] || (_cache[2] = (index) => _ctx.$emit("diff-step", index)),
@@ -10373,6 +10593,9 @@
10373
10593
  "merged",
10374
10594
  "op-type",
10375
10595
  "desc",
10596
+ "source",
10597
+ "time",
10598
+ "time-title",
10376
10599
  "step-count",
10377
10600
  "sub-steps",
10378
10601
  "is-current",
@@ -10417,7 +10640,8 @@
10417
10640
  * 此外每条 step 上提供"查看差异"入口(仅在前后值都存在的 update 步骤显示),
10418
10641
  * 点击后弹出 HistoryDiffDialog,使用 CompareForm 组件以表单形式展示新旧值差异。
10419
10642
  *
10420
- * 各 tab 的内容拆分为独立的 SFCPageTab / DataSourceTab / CodeBlockTab),
10643
+ * 各 tab 的内容拆分为独立的 SFC:页面用 PageTab,数据源 / 代码块复用通用的 BucketTab
10644
+ * (通过 title / prefix / describe* / isStepDiffable 注入差异)。
10421
10645
  * 共享的描述生成与折叠状态在 composables.ts 中维护。
10422
10646
  */
10423
10647
  const ClockIcon = (0, vue.markRaw)(_element_plus_icons_vue.Clock);
@@ -10426,7 +10650,23 @@
10426
10650
  /** 面板显隐受控:reference 图标点击切换,右上角关闭按钮置为 false。 */
10427
10651
  const visible = (0, vue.ref)(false);
10428
10652
  const tabPaneComponent = (0, _tmagic_design.getDesignConfig)("components")?.tabPane;
10429
- const { editorService, dataSourceService, codeBlockService, historyService } = useServices();
10653
+ /**
10654
+ * 业务方自定义的扩展 tab,由 Editor 顶层通过 `historyListExtraTabs` 注入。
10655
+ * 追加在内置「页面 / 数据源 / 代码块」三个 tab 之后,未提供时为空数组。
10656
+ */
10657
+ const extraTabs = (0, vue.inject)("historyListExtraTabs", []);
10658
+ /** label 支持字符串或函数,函数形式便于展示动态数量等内容。 */
10659
+ const resolveTabLabel = (tab) => typeof tab.label === "function" ? tab.label() : tab.label;
10660
+ const { editorService, dataSourceService, codeBlockService, historyService, propsService } = useServices();
10661
+ /**
10662
+ * 数据源 / 代码块功能可被业务方通过 `disabledDataSource` / `disabledCodeBlock` 禁用,
10663
+ * 禁用后对应的历史记录 tab 不再展示。若当前激活的 tab 恰好被禁用,则回退到「页面」tab。
10664
+ */
10665
+ const disabledDataSource = (0, vue.computed)(() => propsService.getDisabledDataSource());
10666
+ const disabledCodeBlock = (0, vue.computed)(() => propsService.getDisabledCodeBlock());
10667
+ (0, vue.watch)([disabledDataSource, disabledCodeBlock], ([dsDisabled, cbDisabled]) => {
10668
+ if (activeTab.value === "data-source" && dsDisabled || activeTab.value === "code-block" && cbDisabled) activeTab.value = "page";
10669
+ });
10430
10670
  /**
10431
10671
  * 通过 inject 拿到 Editor 顶层注入的 `extendFormState`,转交给 HistoryDiffDialog
10432
10672
  * 内部的 CompareForm,使差异对比表单的 filterFunction 能拿到完整的业务上下文。
@@ -10434,6 +10674,10 @@
10434
10674
  */
10435
10675
  const extendFormState = (0, vue.inject)("extendFormState", void 0);
10436
10676
  const { expanded, toggleGroup, pageGroups, dataSourceGroups, codeBlockGroups, pageGroupsDisplay, dataSourceGroupsByTarget, codeBlockGroupsByTarget } = useHistoryList();
10677
+ /** 数据源 step 仅 update(前后 schema 都存在)时可查看差异。 */
10678
+ const isDataSourceStepDiffable = (step) => Boolean(step.oldSchema && step.newSchema);
10679
+ /** 代码块 step 仅 update(前后 content 都存在)时可查看差异。 */
10680
+ const isCodeBlockStepDiffable = (step) => Boolean(step.oldContent && step.newContent);
10437
10681
  /** 把"目标 step 索引"翻译成"目标 cursor"(已应用步骤数量)。 */
10438
10682
  const indexToCursor = (index) => index + 1;
10439
10683
  const onPageGoto = (index) => {
@@ -10458,36 +10702,24 @@
10458
10702
  const onCodeBlockGotoInitial = (id) => {
10459
10703
  codeBlockService.goto(id, 0);
10460
10704
  };
10461
- /**
10462
- * 「回滚」入口:把目标历史步骤的修改作为一次新操作反向应用(类 git revert),
10463
- * 不破坏原有栈结构。各 service 内部完成反向 + 入栈,并自带描述用于面板展示。
10464
- */
10465
- const onPageRevert = (index) => {
10466
- editorService.revertPageStep(index);
10467
- };
10468
- const onDataSourceRevert = (id, index) => {
10469
- dataSourceService.revert(id, index);
10470
- };
10471
- const onCodeBlockRevert = (id, index) => {
10472
- codeBlockService.revert(id, index);
10473
- };
10474
10705
  const diffDialogRef = (0, vue.useTemplateRef)("diffDialog");
10475
10706
  /**
10476
- * 页面 step 差异:仅 update 单节点修改可对比,传入旧/新节点。
10707
+ * 构造页面 step 的差异弹窗入参:仅 update 单节点修改可对比,传入旧/新节点。
10477
10708
  * 节点类型 `type` 优先取 newNode.type,再回退 oldNode.type。
10478
10709
  * `currentValue` 取自 editorService 中该节点当前实际值,用于支持「与当前对比」。
10710
+ * 无可对比内容(如多节点 / add / remove)时返回 null。
10479
10711
  */
10480
- const onPageDiff = (index) => {
10712
+ const buildPageDiffPayload = (index) => {
10481
10713
  const groups = historyService.getPageHistoryGroups();
10482
10714
  for (const group of groups) {
10483
10715
  const entry = group.steps.find((s) => s.index === index);
10484
10716
  if (!entry) continue;
10485
10717
  const item = entry.step.updatedItems?.[0];
10486
- if (!item?.oldNode || !item?.newNode) return;
10718
+ if (!item?.oldNode || !item?.newNode) return null;
10487
10719
  const type = item.newNode.type || item.oldNode.type || "";
10488
10720
  const nodeId = item.newNode.id ?? item.oldNode.id;
10489
10721
  const currentNode = nodeId !== void 0 ? editorService.getNodeById(nodeId) : null;
10490
- diffDialogRef.value?.open({
10722
+ return {
10491
10723
  category: "node",
10492
10724
  type,
10493
10725
  lastValue: item.oldNode,
@@ -10495,50 +10727,87 @@
10495
10727
  currentValue: currentNode || null,
10496
10728
  targetLabel: item.newNode.name || item.oldNode.name || type,
10497
10729
  id: nodeId
10498
- });
10499
- return;
10730
+ };
10500
10731
  }
10732
+ return null;
10501
10733
  };
10502
- const onDataSourceDiff = (id, index) => {
10503
- const groups = historyService.getDataSourceHistoryGroups();
10734
+ /**
10735
+ * 在指定分组列表中按 id / index 查找命中的 step,命中后交由 build 构造差异弹窗入参。
10736
+ * 用于统一数据源、代码块两类历史的查找逻辑。
10737
+ */
10738
+ const findGroupStep = (groups, id, index, build) => {
10504
10739
  for (const group of groups) {
10505
10740
  if (group.id !== id) continue;
10506
10741
  const entry = group.steps.find((s) => s.index === index);
10507
10742
  if (!entry) continue;
10508
- const { oldSchema, newSchema } = entry.step;
10509
- if (!oldSchema || !newSchema) return;
10510
- const currentSchema = dataSourceService.getDataSourceById(`${id}`);
10511
- diffDialogRef.value?.open({
10512
- category: "data-source",
10513
- type: newSchema.type || oldSchema.type || "base",
10514
- lastValue: oldSchema,
10515
- value: newSchema,
10516
- currentValue: currentSchema || null,
10517
- targetLabel: newSchema.title || oldSchema.title || `${id}`,
10518
- id
10519
- });
10520
- return;
10743
+ return build(entry.step);
10521
10744
  }
10745
+ return null;
10746
+ };
10747
+ const buildDataSourceDiffPayload = (id, index) => findGroupStep(historyService.getDataSourceHistoryGroups(), id, index, ({ oldSchema, newSchema }) => {
10748
+ if (!oldSchema || !newSchema) return null;
10749
+ const currentSchema = dataSourceService.getDataSourceById(`${id}`);
10750
+ return {
10751
+ category: "data-source",
10752
+ type: newSchema.type || oldSchema.type || "base",
10753
+ lastValue: oldSchema,
10754
+ value: newSchema,
10755
+ currentValue: currentSchema || null,
10756
+ targetLabel: newSchema.title || oldSchema.title || `${id}`,
10757
+ id
10758
+ };
10759
+ });
10760
+ const buildCodeBlockDiffPayload = (id, index) => findGroupStep(historyService.getCodeBlockHistoryGroups(), id, index, ({ oldContent, newContent }) => {
10761
+ if (!oldContent || !newContent) return null;
10762
+ return {
10763
+ category: "code-block",
10764
+ lastValue: oldContent,
10765
+ value: newContent,
10766
+ currentValue: codeBlockService.getCodeContentById(id) || null,
10767
+ targetLabel: newContent.name || oldContent.name || `${id}`,
10768
+ id
10769
+ };
10770
+ });
10771
+ const onPageDiff = (index) => {
10772
+ const payload = buildPageDiffPayload(index);
10773
+ if (payload) diffDialogRef.value?.open(payload);
10774
+ };
10775
+ const onDataSourceDiff = (id, index) => {
10776
+ const payload = buildDataSourceDiffPayload(id, index);
10777
+ if (payload) diffDialogRef.value?.open(payload);
10522
10778
  };
10523
10779
  const onCodeBlockDiff = (id, index) => {
10524
- const groups = historyService.getCodeBlockHistoryGroups();
10525
- for (const group of groups) {
10526
- if (group.id !== id) continue;
10527
- const entry = group.steps.find((s) => s.index === index);
10528
- if (!entry) continue;
10529
- const { oldContent, newContent } = entry.step;
10530
- if (!oldContent || !newContent) return;
10531
- const currentContent = codeBlockService.getCodeContentById(id);
10532
- diffDialogRef.value?.open({
10533
- category: "code-block",
10534
- lastValue: oldContent,
10535
- value: newContent,
10536
- currentValue: currentContent || null,
10537
- targetLabel: newContent.name || oldContent.name || `${id}`,
10538
- id
10539
- });
10540
- return;
10541
- }
10780
+ const payload = buildCodeBlockDiffPayload(id, index);
10781
+ if (payload) diffDialogRef.value?.open(payload);
10782
+ };
10783
+ const onConfirmRevert = (0, vue.shallowRef)();
10784
+ /**
10785
+ * 「回滚」入口:把目标历史步骤的修改作为一次新操作反向应用(类 git revert),
10786
+ * 不破坏原有栈结构。各 service 内部完成反向 + 入栈,并自带描述用于面板展示。
10787
+ *
10788
+ * 交互:先弹出该步骤的差异弹窗供用户确认,点击「确定回滚」后再真正执行回滚;
10789
+ * 对没有可对比内容的步骤(如 add / remove / 多节点更新)则直接回滚。
10790
+ */
10791
+ const onPageRevert = (index) => {
10792
+ const payload = buildPageDiffPayload(index);
10793
+ onConfirmRevert.value = () => editorService.revertPageStep(index);
10794
+ if (payload) diffDialogRef.value?.open({ ...payload });
10795
+ else onConfirmRevert.value();
10796
+ };
10797
+ const onDataSourceRevert = (id, index) => {
10798
+ const payload = buildDataSourceDiffPayload(id, index);
10799
+ onConfirmRevert.value = () => dataSourceService.revert(id, index);
10800
+ if (payload) diffDialogRef.value?.open({ ...payload });
10801
+ else onConfirmRevert.value();
10802
+ };
10803
+ const onCodeBlockRevert = (id, index) => {
10804
+ const payload = buildCodeBlockDiffPayload(id, index);
10805
+ onConfirmRevert.value = () => codeBlockService.revert(id, index);
10806
+ if (payload) diffDialogRef.value?.open({ ...payload });
10807
+ else onConfirmRevert.value();
10808
+ };
10809
+ const onDiffDialogClose = () => {
10810
+ onConfirmRevert.value = void 0;
10542
10811
  };
10543
10812
  return (_ctx, _cache) => {
10544
10813
  return (0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, null, [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicPopover), {
@@ -10603,13 +10872,19 @@
10603
10872
  ])]),
10604
10873
  _: 1
10605
10874
  }, 16)),
10606
- ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)((0, vue.unref)(tabPaneComponent)?.component || "el-tab-pane"), (0, vue.normalizeProps)((0, vue.guardReactiveProps)((0, vue.unref)(tabPaneComponent)?.props({
10875
+ !disabledDataSource.value ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)((0, vue.unref)(tabPaneComponent)?.component || "el-tab-pane"), (0, vue.normalizeProps)((0, vue.mergeProps)({ key: 0 }, (0, vue.unref)(tabPaneComponent)?.props({
10607
10876
  name: "data-source",
10608
10877
  label: `数据源 (${(0, vue.unref)(dataSourceGroups).length})`
10609
10878
  }) || {})), {
10610
- default: (0, vue.withCtx)(() => [(0, vue.createVNode)(DataSourceTab_default, {
10879
+ default: (0, vue.withCtx)(() => [(0, vue.createVNode)(BucketTab_default, {
10880
+ title: "数据源",
10881
+ prefix: "ds",
10611
10882
  buckets: (0, vue.unref)(dataSourceGroupsByTarget),
10612
10883
  expanded: (0, vue.unref)(expanded),
10884
+ "describe-group": (0, vue.unref)(describeDataSourceGroup),
10885
+ "describe-step": (0, vue.unref)(describeDataSourceStep),
10886
+ "is-step-diffable": isDataSourceStepDiffable,
10887
+ "is-step-revertable": (0, vue.unref)(isDataSourceStepRevertable),
10613
10888
  onToggle: (0, vue.unref)(toggleGroup),
10614
10889
  onGoto: onDataSourceGoto,
10615
10890
  onGotoInitial: onDataSourceGotoInitial,
@@ -10618,17 +10893,26 @@
10618
10893
  }, null, 8, [
10619
10894
  "buckets",
10620
10895
  "expanded",
10896
+ "describe-group",
10897
+ "describe-step",
10898
+ "is-step-revertable",
10621
10899
  "onToggle"
10622
10900
  ])]),
10623
10901
  _: 1
10624
- }, 16)),
10625
- ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)((0, vue.unref)(tabPaneComponent)?.component || "el-tab-pane"), (0, vue.normalizeProps)((0, vue.guardReactiveProps)((0, vue.unref)(tabPaneComponent)?.props({
10902
+ }, 16)) : (0, vue.createCommentVNode)("v-if", true),
10903
+ !disabledCodeBlock.value ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)((0, vue.unref)(tabPaneComponent)?.component || "el-tab-pane"), (0, vue.normalizeProps)((0, vue.mergeProps)({ key: 1 }, (0, vue.unref)(tabPaneComponent)?.props({
10626
10904
  name: "code-block",
10627
10905
  label: `代码块 (${(0, vue.unref)(codeBlockGroups).length})`
10628
10906
  }) || {})), {
10629
- default: (0, vue.withCtx)(() => [(0, vue.createVNode)(CodeBlockTab_default, {
10907
+ default: (0, vue.withCtx)(() => [(0, vue.createVNode)(BucketTab_default, {
10908
+ title: "代码块",
10909
+ prefix: "cb",
10630
10910
  buckets: (0, vue.unref)(codeBlockGroupsByTarget),
10631
10911
  expanded: (0, vue.unref)(expanded),
10912
+ "describe-group": (0, vue.unref)(describeCodeBlockGroup),
10913
+ "describe-step": (0, vue.unref)(describeCodeBlockStep),
10914
+ "is-step-diffable": isCodeBlockStepDiffable,
10915
+ "is-step-revertable": (0, vue.unref)(isCodeBlockStepRevertable),
10632
10916
  onToggle: (0, vue.unref)(toggleGroup),
10633
10917
  onGoto: onCodeBlockGoto,
10634
10918
  onGotoInitial: onCodeBlockGotoInitial,
@@ -10637,18 +10921,32 @@
10637
10921
  }, null, 8, [
10638
10922
  "buckets",
10639
10923
  "expanded",
10924
+ "describe-group",
10925
+ "describe-step",
10926
+ "is-step-revertable",
10640
10927
  "onToggle"
10641
10928
  ])]),
10642
10929
  _: 1
10643
- }, 16))
10930
+ }, 16)) : (0, vue.createCommentVNode)("v-if", true),
10931
+ ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)((0, vue.unref)(extraTabs), (tab) => {
10932
+ return (0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)((0, vue.unref)(tabPaneComponent)?.component || "el-tab-pane"), (0, vue.mergeProps)({ key: tab.name }, { ref_for: true }, (0, vue.unref)(tabPaneComponent)?.props({
10933
+ name: tab.name,
10934
+ label: resolveTabLabel(tab)
10935
+ }) || {}), {
10936
+ default: (0, vue.withCtx)(() => [((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)(tab.component), (0, vue.mergeProps)({ ref_for: true }, tab.props || {}, (0, vue.toHandlers)(tab.listeners || {})), null, 16))]),
10937
+ _: 2
10938
+ }, 1040);
10939
+ }), 128))
10644
10940
  ]),
10645
10941
  _: 1
10646
10942
  }, 8, ["modelValue"])])]),
10647
10943
  _: 1
10648
10944
  }, 8, ["visible"]), (0, vue.createVNode)(HistoryDiffDialog_default, {
10649
10945
  ref: "diffDialog",
10650
- "extend-state": (0, vue.unref)(extendFormState)
10651
- }, null, 8, ["extend-state"])], 64);
10946
+ "extend-state": (0, vue.unref)(extendFormState),
10947
+ "on-confirm": onConfirmRevert.value,
10948
+ onClose: onDiffDialogClose
10949
+ }, null, 8, ["extend-state", "on-confirm"])], 64);
10652
10950
  };
10653
10951
  }
10654
10952
  });
@@ -10895,7 +11193,7 @@
10895
11193
  disabled: () => editorService.get("node")?.type === _tmagic_core.NodeType.PAGE,
10896
11194
  handler: () => {
10897
11195
  const node = editorService.get("node");
10898
- node && editorService.remove(node);
11196
+ node && editorService.remove(node, { historySource: "toolbar" });
10899
11197
  }
10900
11198
  });
10901
11199
  break;
@@ -11259,7 +11557,11 @@
11259
11557
  if (record.propPath?.startsWith("style") && record.value === "") (0, _tmagic_utils.setValueByKeyPath)(record.propPath, record.value, newValue);
11260
11558
  });
11261
11559
  }
11262
- editorService.update(newValue, { changeRecords: eventData?.changeRecords });
11560
+ const historySource = eventData ? "props" : "code";
11561
+ editorService.update(newValue, {
11562
+ changeRecords: eventData?.changeRecords,
11563
+ historySource
11564
+ });
11263
11565
  } catch (e) {
11264
11566
  emit("submit-error", e);
11265
11567
  }
@@ -12228,7 +12530,7 @@
12228
12530
  const editCode = (id) => {
12229
12531
  emit("edit", id);
12230
12532
  };
12231
- const deleteCode = async (id) => {
12533
+ const deleteCode = async (id, { historySource } = {}) => {
12232
12534
  const currentCode = codeList.value.find((codeItem) => codeItem.id === id);
12233
12535
  const existBinds = Boolean(currentCode?.items?.length);
12234
12536
  const undeleteableList = codeBlockService.getUndeletableList() || [];
@@ -12238,7 +12540,7 @@
12238
12540
  cancelButtonText: "取消",
12239
12541
  type: "warning"
12240
12542
  });
12241
- emit("remove", id);
12543
+ emit("remove", id, { historySource });
12242
12544
  } else if (typeof props.customError === "function") props.customError(id, existBinds ? CodeDeleteErrorType.BIND : CodeDeleteErrorType.UNDELETEABLE);
12243
12545
  else if (existBinds) _tmagic_design.tMagicMessage.error("代码块存在绑定关系,不可删除");
12244
12546
  else _tmagic_design.tMagicMessage.error("代码块不可删除");
@@ -12288,7 +12590,7 @@
12288
12590
  default: (0, vue.withCtx)(() => [(0, vue.createVNode)(Icon_default, {
12289
12591
  icon: (0, vue.unref)(_element_plus_icons_vue.Close),
12290
12592
  class: "edit-icon",
12291
- onClick: (0, vue.withModifiers)(($event) => deleteCode(`${data.key}`), ["stop"])
12593
+ onClick: (0, vue.withModifiers)(($event) => deleteCode(`${data.key}`, { historySource: "tree" }), ["stop"])
12292
12594
  }, null, 8, ["icon", "onClick"])]),
12293
12595
  _: 2
12294
12596
  }, 1024)) : (0, vue.createCommentVNode)("v-if", true),
@@ -12336,7 +12638,7 @@
12336
12638
  const codeBlock = codeBlockService.getCodeContentById(selectId);
12337
12639
  if (!codeBlock) return;
12338
12640
  const newCodeId = await codeBlockService.getUniqueId();
12339
- codeBlockService.setCodeDslById(newCodeId, cloneDeep$1(codeBlock));
12641
+ codeBlockService.setCodeDslById(newCodeId, cloneDeep$1(codeBlock), { historySource: "tree-contextmenu" });
12340
12642
  }
12341
12643
  },
12342
12644
  {
@@ -12398,7 +12700,7 @@
12398
12700
  if (codeBlockListRef.value) for (const [, status] of codeBlockListRef.value.nodeStatusMap.entries()) status.selected = false;
12399
12701
  };
12400
12702
  const { nodeContentMenuHandler, menuData: contentMenuData, contentMenuHideHandler } = useContentMenu$1((id) => {
12401
- codeBlockListRef.value?.deleteCode(id);
12703
+ codeBlockListRef.value?.deleteCode(id, { historySource: "tree-contextmenu" });
12402
12704
  });
12403
12705
  const menuData = (0, vue.computed)(() => props.customContentMenu(contentMenuData, "code-block"));
12404
12706
  return (_ctx, _cache) => {
@@ -12486,8 +12788,11 @@
12486
12788
  editDialog.value.show();
12487
12789
  };
12488
12790
  const submitDataSourceHandler = (value, eventData) => {
12489
- if (value.id) dataSourceService.update(value, { changeRecords: eventData.changeRecords });
12490
- else dataSourceService.add(value);
12791
+ if (value.id) dataSourceService.update(value, {
12792
+ changeRecords: eventData.changeRecords,
12793
+ historySource: "props"
12794
+ });
12795
+ else dataSourceService.add(value, { historySource: "props" });
12491
12796
  editDialog.value?.hide();
12492
12797
  };
12493
12798
  return {
@@ -12820,7 +13125,7 @@
12820
13125
  if (!selectId) return;
12821
13126
  const ds = dataSourceService.getDataSourceById(selectId);
12822
13127
  if (!ds) return;
12823
- dataSourceService.add(cloneDeep$1(ds));
13128
+ dataSourceService.add(cloneDeep$1(ds), { historySource: "tree-contextmenu" });
12824
13129
  }
12825
13130
  },
12826
13131
  {
@@ -12897,7 +13202,7 @@
12897
13202
  cancelButtonText: "取消",
12898
13203
  type: "warning"
12899
13204
  });
12900
- dataSourceService.remove(id);
13205
+ dataSourceService.remove(id, { historySource: "tree-contextmenu" });
12901
13206
  };
12902
13207
  const dataSourceListRef = (0, vue.useTemplateRef)("dataSourceList");
12903
13208
  const filterTextChangeHandler = (val) => {
@@ -13004,7 +13309,12 @@
13004
13309
  var FolderMinusIcon_default = FolderMinusIcon_vue_vue_type_script_setup_true_lang_default;
13005
13310
  //#endregion
13006
13311
  //#region packages/editor/src/utils/content-menu.ts
13007
- var useDeleteMenu = () => ({
13312
+ /**
13313
+ * 共享的右键菜单项构造器(画布 ViewerMenu 与图层树 LayerMenu 共用)。
13314
+ * `historySource` 用于标记本次操作的途径,调用方按所在面板传入:
13315
+ * 画布传 `'stage-contextmenu'`,树形面板传 `'tree-contextmenu'`。
13316
+ */
13317
+ var useDeleteMenu = (historySource) => ({
13008
13318
  type: "button",
13009
13319
  text: "删除",
13010
13320
  icon: _element_plus_icons_vue.Delete,
@@ -13014,7 +13324,7 @@
13014
13324
  },
13015
13325
  handler: ({ editorService }) => {
13016
13326
  const nodes = editorService.get("nodes");
13017
- nodes && editorService.remove(nodes);
13327
+ nodes && editorService.remove(nodes, { historySource });
13018
13328
  }
13019
13329
  });
13020
13330
  var useCopyMenu = () => ({
@@ -13026,7 +13336,7 @@
13026
13336
  nodes && editorService?.copy(nodes);
13027
13337
  }
13028
13338
  });
13029
- var usePasteMenu = (menu) => ({
13339
+ var usePasteMenu = (historySource, menu) => ({
13030
13340
  type: "button",
13031
13341
  text: "粘贴",
13032
13342
  icon: (0, vue.markRaw)(_element_plus_icons_vue.DocumentCopy),
@@ -13043,17 +13353,20 @@
13043
13353
  editorService.paste({
13044
13354
  left: initialLeft,
13045
13355
  top: initialTop
13046
- });
13047
- } else editorService.paste();
13356
+ }, void 0, { historySource });
13357
+ } else editorService.paste(void 0, void 0, { historySource });
13048
13358
  }
13049
13359
  });
13050
- var moveTo = async (id, { editorService }) => {
13360
+ var moveTo = async (id, { editorService }, historySource) => {
13051
13361
  const nodes = editorService.get("nodes") || [];
13052
13362
  const parent = editorService.getNodeById(id);
13053
13363
  if (!parent || nodes.length === 0) return;
13054
- await editorService.moveToContainer((0, _tmagic_core.cloneDeep)(nodes), parent.id, { doNotSwitchPage: true });
13364
+ await editorService.moveToContainer((0, _tmagic_core.cloneDeep)(nodes), parent.id, {
13365
+ doNotSwitchPage: true,
13366
+ historySource
13367
+ });
13055
13368
  };
13056
- var useMoveToMenu = ({ editorService }) => {
13369
+ var useMoveToMenu = ({ editorService }, historySource) => {
13057
13370
  return {
13058
13371
  type: "button",
13059
13372
  text: "移动至",
@@ -13066,7 +13379,7 @@
13066
13379
  text: `${page.name}(${page.id})`,
13067
13380
  type: "button",
13068
13381
  handler: (services) => {
13069
- moveTo(page.id, services);
13382
+ moveTo(page.id, services, historySource);
13070
13383
  }
13071
13384
  }))
13072
13385
  };
@@ -13099,7 +13412,7 @@
13099
13412
  name: component.text,
13100
13413
  type: component.type,
13101
13414
  ...component.data || {}
13102
- });
13415
+ }, void 0, { historySource: "tree-contextmenu" });
13103
13416
  }
13104
13417
  }));
13105
13418
  const getSubMenuData = (0, vue.computed)(() => {
@@ -13108,7 +13421,7 @@
13108
13421
  type: "button",
13109
13422
  icon: _element_plus_icons_vue.Files,
13110
13423
  handler: () => {
13111
- editorService.add({ type: "tab-pane" });
13424
+ editorService.add({ type: "tab-pane" }, void 0, { historySource: "tree-contextmenu" });
13112
13425
  }
13113
13426
  }];
13114
13427
  if (node.value?.items) return componentList.value.reduce((subMenuData, group, index) => subMenuData.concat(createMenuItems(group), index < componentList.value.length - 1 ? [{
@@ -13135,9 +13448,9 @@
13135
13448
  items: getSubMenuData.value
13136
13449
  },
13137
13450
  useCopyMenu(),
13138
- usePasteMenu(),
13139
- useDeleteMenu(),
13140
- useMoveToMenu(services),
13451
+ usePasteMenu("tree-contextmenu"),
13452
+ useDeleteMenu("tree-contextmenu"),
13453
+ useMoveToMenu(services, "tree-contextmenu"),
13141
13454
  ...props.layerContentMenu
13142
13455
  ], "layer"));
13143
13456
  const show = (e) => {
@@ -13168,7 +13481,7 @@
13168
13481
  editorService.update({
13169
13482
  id: props.data.id,
13170
13483
  visible
13171
- });
13484
+ }, { historySource: "tree" });
13172
13485
  };
13173
13486
  return (_ctx, _cache) => {
13174
13487
  return __props.data.type !== "page" ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicButton), {
@@ -13784,7 +14097,7 @@
13784
14097
  name: text,
13785
14098
  type,
13786
14099
  ...data
13787
- });
14100
+ }, void 0, { historySource: "component-panel" });
13788
14101
  };
13789
14102
  const dragstartHandler = ({ text, type, data = {} }, e) => {
13790
14103
  e.dataTransfer?.setData("text/json", (0, serialize_javascript.default)({
@@ -14589,11 +14902,11 @@
14589
14902
  display: () => canCenter.value,
14590
14903
  handler: () => {
14591
14904
  if (!nodes.value) return;
14592
- editorService.alignCenter(nodes.value);
14905
+ editorService.alignCenter(nodes.value, { historySource: "stage-contextmenu" });
14593
14906
  }
14594
14907
  },
14595
14908
  useCopyMenu(),
14596
- usePasteMenu(menuRef),
14909
+ usePasteMenu("stage-contextmenu", menuRef),
14597
14910
  {
14598
14911
  type: "divider",
14599
14912
  direction: "horizontal",
@@ -14608,7 +14921,7 @@
14608
14921
  icon: (0, vue.markRaw)(_element_plus_icons_vue.Top),
14609
14922
  display: () => !(0, _tmagic_utils.isPage)(node.value) && !(0, _tmagic_utils.isPageFragment)(node.value) && !props.isMultiSelect,
14610
14923
  handler: () => {
14611
- editorService.moveLayer(1);
14924
+ editorService.moveLayer(1, { historySource: "stage-contextmenu" });
14612
14925
  }
14613
14926
  },
14614
14927
  {
@@ -14617,7 +14930,7 @@
14617
14930
  icon: (0, vue.markRaw)(_element_plus_icons_vue.Bottom),
14618
14931
  display: () => !(0, _tmagic_utils.isPage)(node.value) && !(0, _tmagic_utils.isPageFragment)(node.value) && !props.isMultiSelect,
14619
14932
  handler: () => {
14620
- editorService.moveLayer(-1);
14933
+ editorService.moveLayer(-1, { historySource: "stage-contextmenu" });
14621
14934
  }
14622
14935
  },
14623
14936
  {
@@ -14626,7 +14939,7 @@
14626
14939
  icon: (0, vue.markRaw)(_element_plus_icons_vue.Top),
14627
14940
  display: () => !(0, _tmagic_utils.isPage)(node.value) && !(0, _tmagic_utils.isPageFragment)(node.value) && !props.isMultiSelect,
14628
14941
  handler: () => {
14629
- editorService.moveLayer(LayerOffset.TOP);
14942
+ editorService.moveLayer(LayerOffset.TOP, { historySource: "stage-contextmenu" });
14630
14943
  }
14631
14944
  },
14632
14945
  {
@@ -14635,16 +14948,16 @@
14635
14948
  icon: (0, vue.markRaw)(_element_plus_icons_vue.Bottom),
14636
14949
  display: () => !(0, _tmagic_utils.isPage)(node.value) && !(0, _tmagic_utils.isPageFragment)(node.value) && !props.isMultiSelect,
14637
14950
  handler: () => {
14638
- editorService.moveLayer(LayerOffset.BOTTOM);
14951
+ editorService.moveLayer(LayerOffset.BOTTOM, { historySource: "stage-contextmenu" });
14639
14952
  }
14640
14953
  },
14641
- useMoveToMenu(services),
14954
+ useMoveToMenu(services, "stage-contextmenu"),
14642
14955
  {
14643
14956
  type: "divider",
14644
14957
  direction: "horizontal",
14645
14958
  display: () => !(0, _tmagic_utils.isPage)(node.value) && !(0, _tmagic_utils.isPageFragment)(node.value) && !props.isMultiSelect
14646
14959
  },
14647
- useDeleteMenu(),
14960
+ useDeleteMenu("stage-contextmenu"),
14648
14961
  {
14649
14962
  type: "divider",
14650
14963
  direction: "horizontal"
@@ -14901,7 +15214,7 @@
14901
15214
  left: (0, _tmagic_utils.calcValueByFontsize)(doc, left / zoom.value)
14902
15215
  };
14903
15216
  config.data.inputEvent = e;
14904
- editorService.add(config.data, parent);
15217
+ editorService.add(config.data, parent, { historySource: "component-panel" });
14905
15218
  }
14906
15219
  };
14907
15220
  return (_ctx, _cache) => {
@@ -15189,10 +15502,12 @@
15189
15502
  * @param options.doNotPushHistory 是否不写入历史记录(默认 false)
15190
15503
  * @returns {void}
15191
15504
  */
15192
- async setCodeDslById(id, codeConfig, { changeRecords, doNotPushHistory = false } = {}) {
15505
+ async setCodeDslById(id, codeConfig, { changeRecords, doNotPushHistory = false, historyDescription, historySource } = {}) {
15193
15506
  this.setCodeDslByIdSync(id, codeConfig, true, {
15194
15507
  changeRecords,
15195
- doNotPushHistory
15508
+ doNotPushHistory,
15509
+ historyDescription,
15510
+ historySource
15196
15511
  });
15197
15512
  }
15198
15513
  /**
@@ -15207,7 +15522,7 @@
15207
15522
  * @param options.historyDescription 入栈时附带的人类可读描述,用于历史面板展示
15208
15523
  * @returns {void}
15209
15524
  */
15210
- setCodeDslByIdSync(id, codeConfig, force = true, { changeRecords, doNotPushHistory = false, historyDescription } = {}) {
15525
+ setCodeDslByIdSync(id, codeConfig, force = true, { changeRecords, doNotPushHistory = false, historyDescription, historySource } = {}) {
15211
15526
  const codeDsl = this.getCodeDsl();
15212
15527
  if (!codeDsl) throw new Error("dsl中没有codeBlocks");
15213
15528
  if (codeDsl[id] && !force) return;
@@ -15226,7 +15541,8 @@
15226
15541
  oldContent,
15227
15542
  newContent,
15228
15543
  changeRecords,
15229
- historyDescription
15544
+ historyDescription,
15545
+ source: historySource
15230
15546
  });
15231
15547
  this.emit("addOrUpdate", id, codeDsl[id]);
15232
15548
  }
@@ -15307,7 +15623,7 @@
15307
15623
  * @param options 可选配置
15308
15624
  * @param options.doNotPushHistory 是否不写入历史记录(默认 false)
15309
15625
  */
15310
- async deleteCodeDslByIds(codeIds, { doNotPushHistory = false, historyDescription } = {}) {
15626
+ async deleteCodeDslByIds(codeIds, { doNotPushHistory = false, historyDescription, historySource } = {}) {
15311
15627
  const currentDsl = await this.getCodeDsl();
15312
15628
  if (!currentDsl) return;
15313
15629
  codeIds.forEach((id) => {
@@ -15316,7 +15632,8 @@
15316
15632
  if (oldContent && !doNotPushHistory) history_default.pushCodeBlock(id, {
15317
15633
  oldContent,
15318
15634
  newContent: null,
15319
- historyDescription
15635
+ historyDescription,
15636
+ source: historySource
15320
15637
  });
15321
15638
  this.emit("remove", id);
15322
15639
  });
@@ -15396,6 +15713,7 @@
15396
15713
  async revert(id, index) {
15397
15714
  const entry = history_default.getCodeBlockStepList(id)[index];
15398
15715
  if (!entry?.applied) return null;
15716
+ if (entry.step.oldContent && entry.step.newContent && !entry.step.changeRecords?.length) return null;
15399
15717
  const description = `回滚 #${index + 1}: ${describeRevertCodeBlockStep(entry.step)}`;
15400
15718
  return await this.applyRevertStep(entry.step, description);
15401
15719
  }
@@ -15466,11 +15784,17 @@
15466
15784
  async applyRevertStep(step, historyDescription) {
15467
15785
  const { id, oldContent, newContent, changeRecords } = step;
15468
15786
  if (oldContent === null && newContent) {
15469
- await this.deleteCodeDslByIds([id], { historyDescription });
15787
+ await this.deleteCodeDslByIds([id], {
15788
+ historyDescription,
15789
+ historySource: "rollback"
15790
+ });
15470
15791
  return history_default.getCodeBlockStepList(id).slice(-1)[0]?.step ?? null;
15471
15792
  }
15472
15793
  if (oldContent && newContent === null) {
15473
- this.setCodeDslByIdSync(id, cloneDeep$1(oldContent), true, { historyDescription });
15794
+ this.setCodeDslByIdSync(id, cloneDeep$1(oldContent), true, {
15795
+ historyDescription,
15796
+ historySource: "rollback"
15797
+ });
15474
15798
  return history_default.getCodeBlockStepList(id).slice(-1)[0]?.step ?? null;
15475
15799
  }
15476
15800
  if (!oldContent || !newContent) return null;
@@ -15489,11 +15813,15 @@
15489
15813
  }
15490
15814
  this.setCodeDslByIdSync(id, fallbackToFullReplace ? cloneDeep$1(oldContent) : patched, true, {
15491
15815
  changeRecords,
15492
- historyDescription
15816
+ historyDescription,
15817
+ historySource: "rollback"
15493
15818
  });
15494
15819
  return history_default.getCodeBlockStepList(id).slice(-1)[0]?.step ?? null;
15495
15820
  }
15496
- this.setCodeDslByIdSync(id, cloneDeep$1(oldContent), true, { historyDescription });
15821
+ this.setCodeDslByIdSync(id, cloneDeep$1(oldContent), true, {
15822
+ historyDescription,
15823
+ historySource: "rollback"
15824
+ });
15497
15825
  return history_default.getCodeBlockStepList(id).slice(-1)[0]?.step ?? null;
15498
15826
  }
15499
15827
  /**
@@ -15657,7 +15985,7 @@
15657
15985
  * @param options.doNotPushHistory 是否不写入历史记录(默认 false)
15658
15986
  * @param options.historyDescription 入栈时附带的人类可读描述,用于历史面板展示
15659
15987
  */
15660
- add(config, { doNotPushHistory = false, historyDescription } = {}) {
15988
+ add(config, { doNotPushHistory = false, historyDescription, historySource } = {}) {
15661
15989
  const newConfig = {
15662
15990
  ...config,
15663
15991
  id: config.id && !this.getDataSourceById(config.id) ? config.id : this.createId()
@@ -15666,7 +15994,8 @@
15666
15994
  if (!doNotPushHistory) history_default.pushDataSource(newConfig.id, {
15667
15995
  oldSchema: null,
15668
15996
  newSchema: newConfig,
15669
- historyDescription
15997
+ historyDescription,
15998
+ source: historySource
15670
15999
  });
15671
16000
  this.emit("add", newConfig);
15672
16001
  return newConfig;
@@ -15679,7 +16008,7 @@
15679
16008
  * @param data.doNotPushHistory 是否不写入历史记录(默认 false)
15680
16009
  * @param data.historyDescription 入栈时附带的人类可读描述,用于历史面板展示
15681
16010
  */
15682
- update(config, { changeRecords = [], doNotPushHistory = false, historyDescription } = {}) {
16011
+ update(config, { changeRecords = [], doNotPushHistory = false, historyDescription, historySource } = {}) {
15683
16012
  const dataSources = this.get("dataSources");
15684
16013
  const index = dataSources.findIndex((ds) => ds.id === config.id);
15685
16014
  const oldConfig = dataSources[index];
@@ -15689,7 +16018,8 @@
15689
16018
  oldSchema: oldConfig ? cloneDeep$1(oldConfig) : null,
15690
16019
  newSchema: newConfig,
15691
16020
  changeRecords,
15692
- historyDescription
16021
+ historyDescription,
16022
+ source: historySource
15693
16023
  });
15694
16024
  this.emit("update", newConfig, {
15695
16025
  oldConfig,
@@ -15704,7 +16034,7 @@
15704
16034
  * @param options.doNotPushHistory 是否不写入历史记录(默认 false)
15705
16035
  * @param options.historyDescription 入栈时附带的人类可读描述,用于历史面板展示
15706
16036
  */
15707
- remove(id, { doNotPushHistory = false, historyDescription } = {}) {
16037
+ remove(id, { doNotPushHistory = false, historyDescription, historySource } = {}) {
15708
16038
  const dataSources = this.get("dataSources");
15709
16039
  const index = dataSources.findIndex((ds) => ds.id === id);
15710
16040
  const oldConfig = index !== -1 ? dataSources[index] : null;
@@ -15712,7 +16042,8 @@
15712
16042
  if (oldConfig && !doNotPushHistory) history_default.pushDataSource(id, {
15713
16043
  oldSchema: cloneDeep$1(oldConfig),
15714
16044
  newSchema: null,
15715
- historyDescription
16045
+ historyDescription,
16046
+ source: historySource
15716
16047
  });
15717
16048
  this.emit("remove", id);
15718
16049
  }
@@ -15784,6 +16115,7 @@
15784
16115
  revert(id, index) {
15785
16116
  const entry = history_default.getDataSourceStepList(id)[index];
15786
16117
  if (!entry?.applied) return null;
16118
+ if (entry.step.oldSchema && entry.step.newSchema && !entry.step.changeRecords?.length) return null;
15787
16119
  const description = `回滚 #${index + 1}: ${describeRevertDataSourceStep(entry.step)}`;
15788
16120
  return this.applyRevertStep(entry.step, description);
15789
16121
  }
@@ -15847,11 +16179,17 @@
15847
16179
  applyRevertStep(step, historyDescription) {
15848
16180
  const { id, oldSchema, newSchema, changeRecords } = step;
15849
16181
  if (oldSchema === null && newSchema) {
15850
- this.remove(`${id}`, { historyDescription });
16182
+ this.remove(`${id}`, {
16183
+ historyDescription,
16184
+ historySource: "rollback"
16185
+ });
15851
16186
  return history_default.getDataSourceStepList(id).slice(-1)[0]?.step ?? null;
15852
16187
  }
15853
16188
  if (oldSchema && newSchema === null) {
15854
- this.add(cloneDeep$1(oldSchema), { historyDescription });
16189
+ this.add(cloneDeep$1(oldSchema), {
16190
+ historyDescription,
16191
+ historySource: "rollback"
16192
+ });
15855
16193
  return history_default.getDataSourceStepList(id).slice(-1)[0]?.step ?? null;
15856
16194
  }
15857
16195
  if (!oldSchema || !newSchema) return null;
@@ -15870,11 +16208,15 @@
15870
16208
  }
15871
16209
  this.update(fallbackToFullReplace ? cloneDeep$1(oldSchema) : patched, {
15872
16210
  changeRecords,
15873
- historyDescription
16211
+ historyDescription,
16212
+ historySource: "rollback"
15874
16213
  });
15875
16214
  return history_default.getDataSourceStepList(id).slice(-1)[0]?.step ?? null;
15876
16215
  }
15877
- this.update(cloneDeep$1(oldSchema), { historyDescription });
16216
+ this.update(cloneDeep$1(oldSchema), {
16217
+ historyDescription,
16218
+ historySource: "rollback"
16219
+ });
15878
16220
  return history_default.getDataSourceStepList(id).slice(-1)[0]?.step ?? null;
15879
16221
  }
15880
16222
  /**
@@ -16191,7 +16533,7 @@
16191
16533
  [KeyBindingCommand.DELETE_NODE]: () => {
16192
16534
  const nodes = editor_default.get("nodes");
16193
16535
  if (!nodes || (0, _tmagic_utils.isPage)(nodes[0]) || (0, _tmagic_utils.isPageFragment)(nodes[0])) return;
16194
- editor_default.remove(nodes);
16536
+ editor_default.remove(nodes, { historySource: "shortcut" });
16195
16537
  },
16196
16538
  [KeyBindingCommand.COPY_NODE]: () => {
16197
16539
  const nodes = editor_default.get("nodes");
@@ -16201,13 +16543,13 @@
16201
16543
  const nodes = editor_default.get("nodes");
16202
16544
  if (!nodes || (0, _tmagic_utils.isPage)(nodes[0]) || (0, _tmagic_utils.isPageFragment)(nodes[0])) return;
16203
16545
  editor_default.copy(nodes);
16204
- editor_default.remove(nodes);
16546
+ editor_default.remove(nodes, { historySource: "shortcut" });
16205
16547
  },
16206
16548
  [KeyBindingCommand.PASTE_NODE]: () => {
16207
16549
  editor_default.get("nodes") && editor_default.paste({
16208
16550
  offsetX: 10,
16209
16551
  offsetY: 10
16210
- });
16552
+ }, void 0, { historySource: "shortcut" });
16211
16553
  },
16212
16554
  [KeyBindingCommand.UNDO]: () => {
16213
16555
  editor_default.undo();
@@ -16518,6 +16860,7 @@
16518
16860
  disabledMultiSelect: false,
16519
16861
  alwaysMultiSelect: false,
16520
16862
  disabledPageFragment: false,
16863
+ disabledFlashTip: false,
16521
16864
  disabledStageOverlay: false,
16522
16865
  containerHighlightClassName: _tmagic_stage.CONTAINER_HIGHLIGHT_CLASS_NAME,
16523
16866
  containerHighlightDuration: 800,
@@ -16527,6 +16870,7 @@
16527
16870
  disabledCodeBlock: false,
16528
16871
  componentGroupList: () => [],
16529
16872
  datasourceList: () => [],
16873
+ historyListExtraTabs: () => [],
16530
16874
  menu: () => ({
16531
16875
  left: [],
16532
16876
  right: []
@@ -16967,6 +17311,7 @@
16967
17311
  disabledMultiSelect: { type: Boolean },
16968
17312
  alwaysMultiSelect: { type: Boolean },
16969
17313
  disabledPageFragment: { type: Boolean },
17314
+ disabledFlashTip: { type: Boolean },
16970
17315
  disabledStageOverlay: { type: Boolean },
16971
17316
  disabledShowSrc: { type: Boolean },
16972
17317
  disabledDataSource: { type: Boolean },
@@ -16983,6 +17328,7 @@
16983
17328
  beforeDblclick: { type: Function },
16984
17329
  beforeLayerNodeDblclick: { type: Function },
16985
17330
  extendFormState: { type: Function },
17331
+ historyListExtraTabs: {},
16986
17332
  pageBarSortOptions: {},
16987
17333
  pageFilterFunction: { type: Function }
16988
17334
  }, defaultEditorProps),
@@ -17032,6 +17378,7 @@
17032
17378
  guidesOptions: props.guidesOptions,
17033
17379
  disabledMultiSelect: props.disabledMultiSelect,
17034
17380
  alwaysMultiSelect: props.alwaysMultiSelect,
17381
+ disabledFlashTip: props.disabledFlashTip,
17035
17382
  beforeDblclick: props.beforeDblclick
17036
17383
  };
17037
17384
  stageOverlay_default.set("stageOptions", stageOptions);
@@ -17044,6 +17391,12 @@
17044
17391
  * 与 PropsPanel 通过 `:extend-state` 显式传入的方式保持等价。
17045
17392
  */
17046
17393
  (0, vue.provide)("extendFormState", props.extendFormState);
17394
+ /**
17395
+ * 把历史记录面板的自定义扩展 tab 提供给深层的 HistoryListPanel(它挂在 NavMenu 中,
17396
+ * 以 markRaw component 形式渲染,无法直接通过 props 透传)。业务方可借此在历史记录
17397
+ * 面板内追加自定义模块的历史 tab。
17398
+ */
17399
+ (0, vue.provide)("historyListExtraTabs", props.historyListExtraTabs);
17047
17400
  (0, vue.provide)("eventBus", new events.EventEmitter());
17048
17401
  const propsPanelMountedHandler = (e) => {
17049
17402
  emit("props-panel-mounted", e);
@@ -21552,10 +21905,7 @@
21552
21905
  });
21553
21906
  const modelValue = (0, vue.reactive)({ form: { [props.name]: "" } });
21554
21907
  (0, vue.watch)(() => props.model[props.name], (value) => {
21555
- modelValue.form = { [props.name]: (0, serialize_javascript.default)(value, {
21556
- space: 2,
21557
- unsafe: true
21558
- }).replace(/"(\w+)":\s/g, "$1: ") };
21908
+ modelValue.form = { [props.name]: serializeConfig(value) };
21559
21909
  }, { immediate: true });
21560
21910
  const changeHandler = (v) => {
21561
21911
  if (!props.name || !props.model) return;
@@ -21814,6 +22164,8 @@
21814
22164
  exports.Fixed2Other = Fixed2Other;
21815
22165
  exports.FloatingBox = FloatingBox_default;
21816
22166
  exports.H_GUIDE_LINE_STORAGE_KEY = H_GUIDE_LINE_STORAGE_KEY;
22167
+ exports.HistoryDiffDialog = HistoryDiffDialog_default;
22168
+ exports.HistoryListBucket = Bucket_default;
21817
22169
  exports.Icon = Icon_default;
21818
22170
  exports.IdleTask = IdleTask;
21819
22171
  exports.KeyBindingCommand = KeyBindingCommand;