@tmagic/editor 1.8.0-beta.4 → 1.8.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/components/CompareForm.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/components/ToolButton.vue_vue_type_script_setup_true_lang.js +6 -6
- package/dist/es/fields/CodeSelectCol.vue_vue_type_script_setup_true_lang.js +4 -1
- package/dist/es/fields/DataSourceFieldSelect/FieldSelect.vue_vue_type_script_setup_true_lang.js +8 -2
- package/dist/es/fields/DataSourceFields.vue_vue_type_script_setup_true_lang.js +25 -8
- package/dist/es/fields/DataSourceMethodSelect.vue_vue_type_script_setup_true_name_true_lang.js +6 -4
- package/dist/es/fields/DataSourceMethods.vue_vue_type_script_setup_true_lang.js +25 -12
- package/dist/es/fields/StyleSetter/components/Border.vue_vue_type_script_setup_true_lang.js +27 -5
- package/dist/es/index.js +6 -3
- package/dist/es/initService.js +1 -1
- package/dist/es/layouts/Framework.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/layouts/history-list/Bucket.vue_vue_type_script_setup_true_lang.js +19 -55
- package/dist/es/layouts/history-list/BucketTab.vue_vue_type_script_setup_true_lang.js +22 -39
- package/dist/es/layouts/history-list/GroupRow.vue_vue_type_script_setup_true_lang.js +149 -103
- package/dist/es/layouts/history-list/HistoryDiffDialog.vue_vue_type_script_setup_true_lang.js +32 -8
- package/dist/es/layouts/history-list/HistoryListPanel.vue_vue_type_script_setup_true_lang.js +335 -213
- package/dist/es/layouts/history-list/InitialRow.vue_vue_type_script_setup_true_lang.js +36 -7
- package/dist/es/layouts/history-list/PageTab.vue_vue_type_script_setup_true_lang.js +47 -60
- package/dist/es/layouts/history-list/composables.js +73 -32
- package/dist/es/layouts/page-bar/PageBar.vue_vue_type_script_setup_true_lang.js +4 -2
- package/dist/es/layouts/sidebar/Sidebar.vue_vue_type_script_setup_true_lang.js +5 -1
- package/dist/es/layouts/sidebar/data-source/DataSourceConfigPanel.vue_vue_type_script_setup_true_lang.js +16 -3
- package/dist/es/layouts/sidebar/data-source/DataSourceListPanel.vue_vue_type_script_setup_true_lang.js +23 -1
- package/dist/es/services/codeBlock.js +96 -37
- package/dist/es/services/dataSource.js +70 -26
- package/dist/es/services/editor.js +266 -104
- package/dist/es/services/history.js +271 -212
- package/dist/es/services/ui.js +3 -0
- package/dist/es/style.css +53 -7
- package/dist/es/utils/data-source/index.js +2 -0
- package/dist/es/utils/editor.js +103 -49
- package/dist/es/utils/history.js +232 -0
- package/dist/es/utils/indexed-db.js +86 -0
- package/dist/es/utils/undo-redo.js +60 -1
- package/dist/style.css +53 -7
- package/dist/tmagic-editor.umd.cjs +2064 -1000
- package/package.json +7 -7
- package/src/components/CompareForm.vue +3 -1
- package/src/components/ToolButton.vue +2 -2
- package/src/fields/CodeSelectCol.vue +7 -1
- package/src/fields/DataSourceFieldSelect/FieldSelect.vue +16 -2
- package/src/fields/DataSourceFields.vue +37 -8
- package/src/fields/DataSourceMethodSelect.vue +9 -4
- package/src/fields/DataSourceMethods.vue +42 -21
- package/src/fields/StyleSetter/components/Border.vue +15 -6
- package/src/index.ts +1 -0
- package/src/initService.ts +1 -1
- package/src/layouts/Framework.vue +1 -1
- package/src/layouts/history-list/Bucket.vue +34 -71
- package/src/layouts/history-list/BucketTab.vue +46 -54
- package/src/layouts/history-list/GroupRow.vue +146 -111
- package/src/layouts/history-list/HistoryDiffDialog.vue +94 -68
- package/src/layouts/history-list/HistoryListPanel.vue +298 -115
- package/src/layouts/history-list/InitialRow.vue +28 -9
- package/src/layouts/history-list/PageTab.vue +57 -51
- package/src/layouts/history-list/composables.ts +157 -36
- package/src/layouts/page-bar/PageBar.vue +4 -2
- package/src/layouts/sidebar/Sidebar.vue +6 -1
- package/src/layouts/sidebar/data-source/DataSourceConfigPanel.vue +30 -2
- package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +26 -1
- package/src/services/codeBlock.ts +113 -37
- package/src/services/dataSource.ts +94 -40
- package/src/services/editor.ts +376 -136
- package/src/services/history.ts +306 -209
- package/src/services/ui.ts +7 -0
- package/src/theme/history-list-panel.scss +72 -5
- package/src/theme/page-bar.scss +0 -4
- package/src/theme/style-setter/border.scss +4 -1
- package/src/type.ts +183 -64
- package/src/utils/data-source/index.ts +2 -0
- package/src/utils/editor.ts +166 -60
- package/src/utils/history.ts +308 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/indexed-db.ts +122 -0
- package/src/utils/undo-redo.ts +88 -0
- package/types/index.d.ts +813 -302
|
@@ -1,46 +1,27 @@
|
|
|
1
1
|
import { Layout, canUsePluginMethods } from "../type.js";
|
|
2
2
|
import BaseService from "./BaseService.js";
|
|
3
3
|
import props_default from "./props.js";
|
|
4
|
+
import { getLastPushedHistoryIds } from "../utils/history.js";
|
|
4
5
|
import history_default from "./history.js";
|
|
5
6
|
import storage_default, { Protocol } from "./storage.js";
|
|
6
|
-
import { COPY_STORAGE_KEY, calcAlignCenterStyle, calcLayerTargetIndex, calcMoveStyle, classifyDragSources, collectRelatedNodes, editorNodeMergeCustomizer, fixNodePosition, getInitPositionStyle, getNodeIndex, getPageFragmentList, getPageList, moveItemsInContainer, resolveSelectedNode, setChildrenLayout, setLayout, toggleFixedPosition } from "../utils/editor.js";
|
|
7
|
+
import { COPY_STORAGE_KEY, calcAlignCenterStyle, calcLayerTargetIndex, calcMoveStyle, classifyDragSources, collectRelatedNodes, describeStepForRevert, editorNodeMergeCustomizer, fixNodePosition, getInitPositionStyle, getNodeIndex, getPageFragmentList, getPageList, moveItemsInContainer, resolveSelectedNode, setChildrenLayout, setLayout, toggleFixedPosition } from "../utils/editor.js";
|
|
7
8
|
import { beforePaste, getAddParent } from "../utils/operator.js";
|
|
8
9
|
import { isFixed } from "@tmagic/stage";
|
|
9
|
-
import { getNodeInfo, getNodePath, getValueByKeyPath, isPage, isPageFragment, setValueByKeyPath } from "@tmagic/utils";
|
|
10
|
+
import { getNodeInfo, getNodePath, getValueByKeyPath, guid, isPage, isPageFragment, setValueByKeyPath } from "@tmagic/utils";
|
|
10
11
|
import { NodeType } from "@tmagic/core";
|
|
11
12
|
import { reactive, toRaw } from "vue";
|
|
12
|
-
import { cloneDeep as cloneDeep$1, isObject, mergeWith, uniq } from "lodash-es";
|
|
13
|
+
import { cloneDeep as cloneDeep$1, isEmpty, isEqual, isObject, mergeWith, uniq } from "lodash-es";
|
|
13
14
|
//#region packages/editor/src/services/editor.ts
|
|
14
15
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
16
|
+
* 把「变更前后节点快照」列表归一成 update 类型的 {@link StepDiffItem} 列表,供 {@link StepValue.diff} 使用。
|
|
17
|
+
* `changeRecords` 来自 form 端的 propPath/value 列表,撤销/重做时只对这些 propPath 做局部更新;
|
|
18
|
+
* 缺省(未传 / 空数组)才退化为整节点替换。
|
|
17
19
|
*/
|
|
18
|
-
var
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const label = node?.name || node?.type || (node?.id !== void 0 ? `${node.id}` : "");
|
|
24
|
-
return `撤回新增 ${count} 个节点${count === 1 && label ? `(${label})` : ""}`;
|
|
25
|
-
}
|
|
26
|
-
case "remove": {
|
|
27
|
-
const count = step.removedItems?.length ?? 0;
|
|
28
|
-
const node = step.removedItems?.[0]?.node;
|
|
29
|
-
const label = node?.name || node?.type || (node?.id !== void 0 ? `${node.id}` : "");
|
|
30
|
-
return `还原已删除的 ${count} 个节点${count === 1 && label ? `(${label})` : ""}`;
|
|
31
|
-
}
|
|
32
|
-
default: {
|
|
33
|
-
const items = step.updatedItems ?? [];
|
|
34
|
-
if (items.length === 1) {
|
|
35
|
-
const { newNode, oldNode, changeRecords } = items[0];
|
|
36
|
-
const target = newNode?.name || newNode?.type || oldNode?.name || oldNode?.type || `${newNode?.id ?? ""}`;
|
|
37
|
-
const propPath = changeRecords?.[0]?.propPath;
|
|
38
|
-
return propPath ? `还原 ${target} · ${propPath}` : `还原 ${target}`;
|
|
39
|
-
}
|
|
40
|
-
return `还原 ${items.length} 个节点的修改`;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
};
|
|
20
|
+
var buildUpdateDiff = (items) => items.map(({ oldNode, newNode, changeRecords }) => ({
|
|
21
|
+
oldSchema: oldNode,
|
|
22
|
+
newSchema: newNode,
|
|
23
|
+
...changeRecords?.length ? { changeRecords } : {}
|
|
24
|
+
}));
|
|
44
25
|
var Editor = class extends BaseService {
|
|
45
26
|
state = reactive({
|
|
46
27
|
root: null,
|
|
@@ -58,6 +39,12 @@ var Editor = class extends BaseService {
|
|
|
58
39
|
alwaysMultiSelect: false
|
|
59
40
|
});
|
|
60
41
|
selectionBeforeOp = null;
|
|
42
|
+
/**
|
|
43
|
+
* 最近一次 pushOpHistory 写入的历史记录 uuid。
|
|
44
|
+
* 供 *AndGetHistoryId 系列方法在调用普通操作后取回本次产生的历史记录 id;
|
|
45
|
+
* 普通操作不会读取它,调用前由 *AndGetHistoryId 重置为 null。
|
|
46
|
+
*/
|
|
47
|
+
lastPushedHistoryId = null;
|
|
61
48
|
constructor() {
|
|
62
49
|
super(canUsePluginMethods.async.map((methodName) => ({
|
|
63
50
|
name: methodName,
|
|
@@ -72,8 +59,9 @@ var Editor = class extends BaseService {
|
|
|
72
59
|
* 设置当前指点节点配置
|
|
73
60
|
* @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength' | 'pageFragmentLength
|
|
74
61
|
* @param value MNode
|
|
62
|
+
* @param options.historySource 设置 root 时,本次变更写入历史记录的「操作来源」(仅 name === 'root' 时生效)
|
|
75
63
|
*/
|
|
76
|
-
set(name, value) {
|
|
64
|
+
set(name, value, options = {}) {
|
|
77
65
|
const preValue = this.state[name];
|
|
78
66
|
this.state[name] = value;
|
|
79
67
|
if (name === "nodes" && Array.isArray(value)) this.set("node", value[0]);
|
|
@@ -84,12 +72,19 @@ var Editor = class extends BaseService {
|
|
|
84
72
|
this.state.pageLength = getPageList(app).length || 0;
|
|
85
73
|
this.state.pageFragmentLength = getPageFragmentList(app).length || 0;
|
|
86
74
|
this.state.stageLoading = this.state.pageLength !== 0;
|
|
75
|
+
if (preValue && !isEmpty(preValue)) this.pushRootDiffHistory(preValue, app, options.historySource);
|
|
76
|
+
else app.items?.forEach((pageNode) => {
|
|
77
|
+
if (pageNode?.id !== void 0 && !history_default.getPageMarker(pageNode.id)) history_default.setPageMarker(pageNode.id, {
|
|
78
|
+
name: pageNode.name,
|
|
79
|
+
source: options.historySource
|
|
80
|
+
});
|
|
81
|
+
});
|
|
87
82
|
} else {
|
|
88
83
|
this.state.pageLength = 0;
|
|
89
84
|
this.state.pageFragmentLength = 0;
|
|
90
85
|
this.state.stageLoading = false;
|
|
91
86
|
}
|
|
92
|
-
this.emit("root-change", value, preValue);
|
|
87
|
+
this.emit("root-change", value, preValue, options);
|
|
93
88
|
}
|
|
94
89
|
}
|
|
95
90
|
/**
|
|
@@ -109,7 +104,25 @@ var Editor = class extends BaseService {
|
|
|
109
104
|
getNodeInfo(id, raw = true) {
|
|
110
105
|
let root = this.get("root");
|
|
111
106
|
if (raw) root = toRaw(root);
|
|
112
|
-
|
|
107
|
+
if (!root) return {
|
|
108
|
+
node: null,
|
|
109
|
+
parent: null,
|
|
110
|
+
page: null,
|
|
111
|
+
path: []
|
|
112
|
+
};
|
|
113
|
+
if (id === root.id) return {
|
|
114
|
+
node: root,
|
|
115
|
+
parent: null,
|
|
116
|
+
page: null,
|
|
117
|
+
path: []
|
|
118
|
+
};
|
|
119
|
+
const pageIdStr = `${this.get("page")?.id || ""}`;
|
|
120
|
+
const currentPageNode = root.items?.find((item) => `${item.id}` === pageIdStr);
|
|
121
|
+
if (currentPageNode && `${id}` !== pageIdStr) {
|
|
122
|
+
const info = getNodeInfo(id, currentPageNode);
|
|
123
|
+
if (info.node) return info;
|
|
124
|
+
}
|
|
125
|
+
return getNodeInfo(id, root, currentPageNode);
|
|
113
126
|
}
|
|
114
127
|
/**
|
|
115
128
|
* 根据ID获取指点节点配置
|
|
@@ -302,23 +315,26 @@ var Editor = class extends BaseService {
|
|
|
302
315
|
}
|
|
303
316
|
if (!(isPage(newNodes[0]) || isPageFragment(newNodes[0]))) {
|
|
304
317
|
const pageForOp = this.getNodeInfo(newNodes[0].id, false).page;
|
|
305
|
-
if (!doNotPushHistory)
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
indexMap: Object.fromEntries(newNodes.map((n) => {
|
|
318
|
+
if (!doNotPushHistory) {
|
|
319
|
+
const parentId = (this.getParentById(newNodes[0].id, false) ?? this.get("root")).id;
|
|
320
|
+
this.pushOpHistory("add", {
|
|
321
|
+
diff: newNodes.map((n) => {
|
|
310
322
|
const p = this.getParentById(n.id, false);
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
323
|
+
const idx = p ? getNodeIndex(n.id, p) : -1;
|
|
324
|
+
return {
|
|
325
|
+
newSchema: cloneDeep$1(toRaw(n)),
|
|
326
|
+
parentId,
|
|
327
|
+
index: typeof idx === "number" ? idx : -1
|
|
328
|
+
};
|
|
329
|
+
}),
|
|
330
|
+
pageData: {
|
|
331
|
+
name: pageForOp?.name || "",
|
|
332
|
+
id: pageForOp.id
|
|
333
|
+
},
|
|
334
|
+
historyDescription,
|
|
335
|
+
source: historySource
|
|
336
|
+
});
|
|
337
|
+
} else this.selectionBeforeOp = null;
|
|
322
338
|
}
|
|
323
339
|
this.emit("add", newNodes);
|
|
324
340
|
return Array.isArray(addNode) ? newNodes : newNodes[0];
|
|
@@ -398,7 +414,7 @@ var Editor = class extends BaseService {
|
|
|
398
414
|
};
|
|
399
415
|
const idx = getNodeIndex(curNode.id, parent);
|
|
400
416
|
removedItems.push({
|
|
401
|
-
|
|
417
|
+
oldSchema: cloneDeep$1(toRaw(curNode)),
|
|
402
418
|
parentId: parent.id,
|
|
403
419
|
index: typeof idx === "number" ? idx : -1
|
|
404
420
|
});
|
|
@@ -409,7 +425,7 @@ var Editor = class extends BaseService {
|
|
|
409
425
|
doNotSwitchPage
|
|
410
426
|
})));
|
|
411
427
|
if (removedItems.length > 0 && pageForOp) if (!doNotPushHistory) this.pushOpHistory("remove", {
|
|
412
|
-
|
|
428
|
+
diff: removedItems,
|
|
413
429
|
pageData: pageForOp,
|
|
414
430
|
historyDescription,
|
|
415
431
|
source: historySource
|
|
@@ -417,18 +433,17 @@ var Editor = class extends BaseService {
|
|
|
417
433
|
else this.selectionBeforeOp = null;
|
|
418
434
|
this.emit("remove", nodes);
|
|
419
435
|
}
|
|
420
|
-
async doUpdate(config, { changeRecords = [] } = {}) {
|
|
421
|
-
|
|
422
|
-
if (!root) throw new Error("root为空");
|
|
436
|
+
async doUpdate(config, { changeRecords = [], historySource } = {}) {
|
|
437
|
+
if (!this.get("root")) throw new Error("root为空");
|
|
423
438
|
if (!config?.id) throw new Error("没有配置或者配置缺少id值");
|
|
424
439
|
const info = this.getNodeInfo(config.id, false);
|
|
425
440
|
if (!info.node) throw new Error(`获取不到id为${config.id}的节点`);
|
|
426
441
|
const node = toRaw(info.node);
|
|
427
|
-
let newConfig = await toggleFixedPosition(toRaw(config), node,
|
|
442
|
+
let newConfig = await toggleFixedPosition(toRaw(config), node, info.path, this.getLayout);
|
|
428
443
|
newConfig = mergeWith(cloneDeep$1(node), newConfig, editorNodeMergeCustomizer);
|
|
429
444
|
if (!newConfig.type) throw new Error("配置缺少type值");
|
|
430
445
|
if (newConfig.type === NodeType.ROOT) {
|
|
431
|
-
this.set("root", newConfig);
|
|
446
|
+
this.set("root", newConfig, { historySource });
|
|
432
447
|
return {
|
|
433
448
|
oldNode: node,
|
|
434
449
|
newNode: newConfig,
|
|
@@ -478,17 +493,20 @@ var Editor = class extends BaseService {
|
|
|
478
493
|
const nodes = Array.isArray(config) ? config : [config];
|
|
479
494
|
const updateData = await Promise.all(nodes.map((node, index) => {
|
|
480
495
|
const recordsForNode = changeRecordList ? changeRecordList[index] ?? [] : changeRecords ?? [];
|
|
481
|
-
return this.doUpdate(node, {
|
|
496
|
+
return this.doUpdate(node, {
|
|
497
|
+
changeRecords: recordsForNode,
|
|
498
|
+
historySource
|
|
499
|
+
});
|
|
482
500
|
}));
|
|
483
501
|
if (updateData[0].oldNode?.type !== NodeType.ROOT) {
|
|
484
502
|
if (this.get("nodes").length) if (!doNotPushHistory) {
|
|
485
503
|
const pageForOp = this.getNodeInfo(nodes[0].id, false).page;
|
|
486
504
|
this.pushOpHistory("update", {
|
|
487
|
-
|
|
505
|
+
diff: buildUpdateDiff(updateData.map((d) => ({
|
|
488
506
|
oldNode: cloneDeep$1(d.oldNode),
|
|
489
|
-
newNode: cloneDeep$1(
|
|
507
|
+
newNode: cloneDeep$1(d.newNode),
|
|
490
508
|
changeRecords: d.changeRecords?.length ? cloneDeep$1(d.changeRecords) : void 0
|
|
491
|
-
}))
|
|
509
|
+
}))),
|
|
492
510
|
pageData: {
|
|
493
511
|
name: pageForOp?.name || "",
|
|
494
512
|
id: pageForOp.id
|
|
@@ -651,10 +669,10 @@ var Editor = class extends BaseService {
|
|
|
651
669
|
if (!doNotPushHistory) {
|
|
652
670
|
const pageForOp = this.getNodeInfo(node.id, false).page;
|
|
653
671
|
this.pushOpHistory("update", {
|
|
654
|
-
|
|
672
|
+
diff: buildUpdateDiff([{
|
|
655
673
|
oldNode: oldParent,
|
|
656
674
|
newNode: cloneDeep$1(toRaw(parent))
|
|
657
|
-
}]
|
|
675
|
+
}]),
|
|
658
676
|
pageData: {
|
|
659
677
|
name: pageForOp?.name || "",
|
|
660
678
|
id: pageForOp.id
|
|
@@ -728,7 +746,7 @@ var Editor = class extends BaseService {
|
|
|
728
746
|
id: target.id
|
|
729
747
|
};
|
|
730
748
|
this.pushOpHistory("update", {
|
|
731
|
-
|
|
749
|
+
diff: buildUpdateDiff(updatedItems),
|
|
732
750
|
pageData: historyPage,
|
|
733
751
|
historyDescription,
|
|
734
752
|
source: historySource
|
|
@@ -779,7 +797,7 @@ var Editor = class extends BaseService {
|
|
|
779
797
|
if (!doNotPushHistory) {
|
|
780
798
|
const pageForOp = this.getNodeInfo(configs[0].id, false).page;
|
|
781
799
|
this.pushOpHistory("update", {
|
|
782
|
-
|
|
800
|
+
diff: buildUpdateDiff(updatedItems),
|
|
783
801
|
pageData: {
|
|
784
802
|
name: pageForOp?.name || "",
|
|
785
803
|
id: pageForOp.id
|
|
@@ -795,6 +813,64 @@ var Editor = class extends BaseService {
|
|
|
795
813
|
});
|
|
796
814
|
}
|
|
797
815
|
/**
|
|
816
|
+
* 下列 *AndGetHistoryId 方法与对应的普通操作(add / remove / update ...)行为完全一致,
|
|
817
|
+
* 返回值在 {@link DslOpWithHistoryIdsResult} 中同时包含原操作结果与本次写入历史栈的 uuid 列表({@link StepValue.uuid}),
|
|
818
|
+
* 可用于精确引用 / 定位该条历史记录(埋点、revert、跨端同步等)。
|
|
819
|
+
*
|
|
820
|
+
* 当本次操作未写入历史(doNotPushHistory 为 true、或操作无实际变更 / 提前返回)时 historyIds 为 `[]`。
|
|
821
|
+
*/
|
|
822
|
+
/** 等价于 {@link add},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
|
|
823
|
+
async addAndGetHistoryId(addNode, parent, options = {}) {
|
|
824
|
+
this.lastPushedHistoryId = null;
|
|
825
|
+
return {
|
|
826
|
+
result: await this.add(addNode, parent, options),
|
|
827
|
+
historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
/** 等价于 {@link remove},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
|
|
831
|
+
async removeAndGetHistoryId(nodeOrNodeList, options = {}) {
|
|
832
|
+
this.lastPushedHistoryId = null;
|
|
833
|
+
await this.remove(nodeOrNodeList, options);
|
|
834
|
+
return {
|
|
835
|
+
result: void 0,
|
|
836
|
+
historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
/** 等价于 {@link update},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
|
|
840
|
+
async updateAndGetHistoryId(config, data = {}) {
|
|
841
|
+
this.lastPushedHistoryId = null;
|
|
842
|
+
return {
|
|
843
|
+
result: await this.update(config, data),
|
|
844
|
+
historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
/** 等价于 {@link moveLayer},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
|
|
848
|
+
async moveLayerAndGetHistoryId(offset, options = {}) {
|
|
849
|
+
this.lastPushedHistoryId = null;
|
|
850
|
+
await this.moveLayer(offset, options);
|
|
851
|
+
return {
|
|
852
|
+
result: void 0,
|
|
853
|
+
historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
/** 等价于 {@link moveToContainer},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
|
|
857
|
+
async moveToContainerAndGetHistoryId(config, targetId, options = {}) {
|
|
858
|
+
this.lastPushedHistoryId = null;
|
|
859
|
+
return {
|
|
860
|
+
result: await this.moveToContainer(config, targetId, options),
|
|
861
|
+
historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
/** 等价于 {@link dragTo},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
|
|
865
|
+
async dragToAndGetHistoryId(config, targetParent, targetIndex, options = {}) {
|
|
866
|
+
this.lastPushedHistoryId = null;
|
|
867
|
+
await this.dragTo(config, targetParent, targetIndex, options);
|
|
868
|
+
return {
|
|
869
|
+
result: void 0,
|
|
870
|
+
historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
798
874
|
* 撤销当前操作
|
|
799
875
|
* @returns 被撤销的操作
|
|
800
876
|
*/
|
|
@@ -832,9 +908,10 @@ var Editor = class extends BaseService {
|
|
|
832
908
|
const entry = history_default.getPageStepList()[index];
|
|
833
909
|
if (!entry?.applied) return null;
|
|
834
910
|
const { step } = entry;
|
|
911
|
+
if (step.opType === "initial") return null;
|
|
835
912
|
if (!this.get("root")) return null;
|
|
836
913
|
if (step.opType === "update") {
|
|
837
|
-
const items = step.
|
|
914
|
+
const items = step.diff ?? [];
|
|
838
915
|
if (!items.length || !items.every((item) => item.changeRecords?.length)) return null;
|
|
839
916
|
}
|
|
840
917
|
let revertedStep = null;
|
|
@@ -851,24 +928,26 @@ var Editor = class extends BaseService {
|
|
|
851
928
|
};
|
|
852
929
|
try {
|
|
853
930
|
switch (step.opType) {
|
|
854
|
-
case "add":
|
|
855
|
-
const
|
|
856
|
-
|
|
857
|
-
const existing = this.getNodeById(
|
|
931
|
+
case "add":
|
|
932
|
+
for (const { newSchema } of step.diff ?? []) {
|
|
933
|
+
if (!newSchema) continue;
|
|
934
|
+
const existing = this.getNodeById(newSchema.id, false);
|
|
858
935
|
if (existing) await this.remove(existing, opts);
|
|
859
936
|
}
|
|
860
937
|
break;
|
|
861
|
-
}
|
|
862
938
|
case "remove": {
|
|
863
|
-
const sorted = [...step.
|
|
864
|
-
for (const {
|
|
939
|
+
const sorted = [...step.diff ?? []].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
|
|
940
|
+
for (const { oldSchema, parentId } of sorted) {
|
|
941
|
+
if (!oldSchema || parentId === void 0) continue;
|
|
865
942
|
const parent = this.getNodeById(parentId, false);
|
|
866
|
-
if (parent) await this.add([cloneDeep$1(
|
|
943
|
+
if (parent) await this.add([cloneDeep$1(oldSchema)], parent, opts);
|
|
867
944
|
}
|
|
868
945
|
break;
|
|
869
946
|
}
|
|
870
947
|
case "update": {
|
|
871
|
-
const configs = (step.
|
|
948
|
+
const configs = (step.diff ?? []).filter((item) => item.oldSchema && item.newSchema).map(({ oldSchema, newSchema, changeRecords }) => {
|
|
949
|
+
const oldNode = oldSchema;
|
|
950
|
+
const newNode = newSchema;
|
|
872
951
|
if (changeRecords?.length) {
|
|
873
952
|
const patch = {
|
|
874
953
|
id: newNode.id,
|
|
@@ -898,6 +977,21 @@ var Editor = class extends BaseService {
|
|
|
898
977
|
return revertedStep;
|
|
899
978
|
}
|
|
900
979
|
/**
|
|
980
|
+
* 通过历史记录 uuid 回滚当前页面的历史步骤,语义与 {@link revertPageStep} 完全一致,
|
|
981
|
+
* 仅入参从 index 改为 uuid 列表({@link StepValue.uuid})。按数组顺序依次回滚,
|
|
982
|
+
* 返回与入参同序的结果列表(某项失败时为 `null`)。
|
|
983
|
+
*
|
|
984
|
+
* @param uuids 目标历史记录的 uuid 列表,通常由 *AndGetHistoryId 方法返回的 `historyIds`
|
|
985
|
+
*/
|
|
986
|
+
async revertPageStepById(uuids) {
|
|
987
|
+
const results = [];
|
|
988
|
+
for (const uuid of uuids) {
|
|
989
|
+
const index = history_default.getPageStepIndexByUuid(uuid);
|
|
990
|
+
results.push(index < 0 ? null : await this.revertPageStep(index));
|
|
991
|
+
}
|
|
992
|
+
return results;
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
901
995
|
* 跳转当前页面历史栈到指定游标位置。
|
|
902
996
|
*
|
|
903
997
|
* `targetCursor` 与 `UndoRedo.getCursor()` 同义:表示"已应用步骤数量",
|
|
@@ -973,19 +1067,83 @@ var Editor = class extends BaseService {
|
|
|
973
1067
|
if (this.selectionBeforeOp) return;
|
|
974
1068
|
this.selectionBeforeOp = this.get("nodes").map((n) => n.id);
|
|
975
1069
|
}
|
|
976
|
-
|
|
1070
|
+
/**
|
|
1071
|
+
* 比较「上一次 root」与「新 root」的页面 / 页面片,按页面粒度把整体替换拆成历史记录:
|
|
1072
|
+
* - 新旧都存在且内容变化的页面 → 一条 `update`(整页快照替换,无 changeRecords);
|
|
1073
|
+
* - 仅新 root 存在的页面 → 一条 `add`;
|
|
1074
|
+
* - 仅旧 root 存在的页面 → 一条 `remove`。
|
|
1075
|
+
*
|
|
1076
|
+
* 每条记录落到对应页面自己的历史栈(与普通节点操作一致),并标记来源 `source`。
|
|
1077
|
+
* 内容未变化的页面不产生记录,避免重复设置相同 DSL 时产生噪声。
|
|
1078
|
+
*/
|
|
1079
|
+
pushRootDiffHistory(preRoot, nextRoot, source) {
|
|
1080
|
+
const prevPages = preRoot?.items || [];
|
|
1081
|
+
const nextPages = nextRoot?.items || [];
|
|
1082
|
+
const prevMap = new Map(prevPages.map((p) => [`${p.id}`, p]));
|
|
1083
|
+
const nextMap = new Map(nextPages.map((p) => [`${p.id}`, p]));
|
|
1084
|
+
const indexInItems = (root, id) => (root.items ?? []).findIndex((item) => `${item.id}` === `${id}`);
|
|
1085
|
+
nextPages.forEach((nextPage) => {
|
|
1086
|
+
const prevPage = prevMap.get(`${nextPage.id}`);
|
|
1087
|
+
if (!prevPage) this.pushPageDiffStep("add", nextPage, {
|
|
1088
|
+
newSchema: cloneDeep$1(toRaw(nextPage)),
|
|
1089
|
+
parentId: nextRoot.id,
|
|
1090
|
+
index: indexInItems(nextRoot, nextPage.id)
|
|
1091
|
+
}, source);
|
|
1092
|
+
else if (!isEqual(toRaw(prevPage), toRaw(nextPage))) this.pushPageDiffStep("update", nextPage, {
|
|
1093
|
+
oldSchema: cloneDeep$1(toRaw(prevPage)),
|
|
1094
|
+
newSchema: cloneDeep$1(toRaw(nextPage))
|
|
1095
|
+
}, source);
|
|
1096
|
+
});
|
|
1097
|
+
prevPages.forEach((prevPage) => {
|
|
1098
|
+
if (!nextMap.has(`${prevPage.id}`)) this.pushPageDiffStep("remove", prevPage, {
|
|
1099
|
+
oldSchema: cloneDeep$1(toRaw(prevPage)),
|
|
1100
|
+
parentId: preRoot.id,
|
|
1101
|
+
index: indexInItems(preRoot, prevPage.id)
|
|
1102
|
+
}, source);
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
/**
|
|
1106
|
+
* 构造一条页面级「set root」历史记录(不携带选区 / modifiedNodeIds 上下文)并落到该页面自己的栈。
|
|
1107
|
+
*
|
|
1108
|
+
* 连续 set root 替换:若该页栈最新一条已是**同来源**的 set root 记录({@link StepValue.rootStep} 且 `source` 相同),
|
|
1109
|
+
* 则用本次记录**替换**它而非新增,避免源码反复保存 / 外部重设 DSL 时堆积多条 root 记录;
|
|
1110
|
+
* 来源不同则照常新增(initial 基线不是 rootStep,不在此列)。
|
|
1111
|
+
*/
|
|
1112
|
+
pushPageDiffStep(opType, page, diffItem, source) {
|
|
1113
|
+
const step = {
|
|
1114
|
+
uuid: guid(),
|
|
1115
|
+
data: {
|
|
1116
|
+
name: page.name || "",
|
|
1117
|
+
id: page.id
|
|
1118
|
+
},
|
|
1119
|
+
opType,
|
|
1120
|
+
selectedBefore: [],
|
|
1121
|
+
selectedAfter: [],
|
|
1122
|
+
modifiedNodeIds: /* @__PURE__ */ new Map(),
|
|
1123
|
+
diff: [diffItem],
|
|
1124
|
+
rootStep: true
|
|
1125
|
+
};
|
|
1126
|
+
if (source) step.source = source;
|
|
1127
|
+
const top = history_default.getCurrentPageStep(page.id);
|
|
1128
|
+
if (top?.rootStep && top.source === source) history_default.replaceCurrentPageStep(step, page.id);
|
|
1129
|
+
else history_default.push(step, page.id);
|
|
1130
|
+
}
|
|
1131
|
+
pushOpHistory(opType, { diff, pageData, historyDescription, source }) {
|
|
977
1132
|
const step = {
|
|
1133
|
+
uuid: guid(),
|
|
978
1134
|
data: pageData,
|
|
979
1135
|
opType,
|
|
980
1136
|
selectedBefore: this.selectionBeforeOp ?? [],
|
|
981
1137
|
selectedAfter: this.get("nodes").map((n) => n.id),
|
|
982
1138
|
modifiedNodeIds: new Map(this.get("modifiedNodeIds")),
|
|
983
|
-
|
|
1139
|
+
diff
|
|
984
1140
|
};
|
|
985
1141
|
if (historyDescription) step.historyDescription = historyDescription;
|
|
986
1142
|
if (source) step.source = source;
|
|
987
|
-
history_default.push(step, pageData.id);
|
|
1143
|
+
const historyId = history_default.push(step, pageData.id) ? step.uuid : null;
|
|
1144
|
+
this.lastPushedHistoryId = historyId;
|
|
988
1145
|
this.selectionBeforeOp = null;
|
|
1146
|
+
return historyId;
|
|
989
1147
|
}
|
|
990
1148
|
/**
|
|
991
1149
|
* 应用历史操作(撤销 / 重做)
|
|
@@ -1000,6 +1158,7 @@ var Editor = class extends BaseService {
|
|
|
1000
1158
|
* @param reverse true = 撤销,false = 重做
|
|
1001
1159
|
*/
|
|
1002
1160
|
async applyHistoryOp(step, reverse) {
|
|
1161
|
+
if (step.opType === "initial") return;
|
|
1003
1162
|
const root = this.get("root");
|
|
1004
1163
|
const stage = this.get("stage");
|
|
1005
1164
|
if (!root) return;
|
|
@@ -1010,56 +1169,59 @@ var Editor = class extends BaseService {
|
|
|
1010
1169
|
};
|
|
1011
1170
|
switch (step.opType) {
|
|
1012
1171
|
case "add": {
|
|
1013
|
-
const
|
|
1014
|
-
if (reverse) for (const
|
|
1015
|
-
|
|
1172
|
+
const items = step.diff ?? [];
|
|
1173
|
+
if (reverse) for (const { newSchema } of items) {
|
|
1174
|
+
if (!newSchema) continue;
|
|
1175
|
+
const existing = this.getNodeById(newSchema.id, false);
|
|
1016
1176
|
if (existing) await this.remove(existing, commonOpts);
|
|
1017
1177
|
}
|
|
1018
1178
|
else {
|
|
1019
|
-
const
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
if (parent.items)
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
});
|
|
1033
|
-
}
|
|
1179
|
+
const sorted = [...items].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
|
|
1180
|
+
for (const { newSchema, parentId, index } of sorted) {
|
|
1181
|
+
if (!newSchema || parentId === void 0) continue;
|
|
1182
|
+
const parent = this.getNodeById(parentId, false);
|
|
1183
|
+
if (parent?.items) {
|
|
1184
|
+
if (typeof index === "number" && index >= 0 && index < parent.items.length) parent.items.splice(index, 0, cloneDeep$1(newSchema));
|
|
1185
|
+
else parent.items.push(cloneDeep$1(newSchema));
|
|
1186
|
+
await stage?.add({
|
|
1187
|
+
config: cloneDeep$1(newSchema),
|
|
1188
|
+
parent: cloneDeep$1(parent),
|
|
1189
|
+
parentId: parent.id,
|
|
1190
|
+
root: cloneDeep$1(root)
|
|
1191
|
+
});
|
|
1034
1192
|
}
|
|
1035
1193
|
}
|
|
1036
1194
|
}
|
|
1037
1195
|
break;
|
|
1038
1196
|
}
|
|
1039
1197
|
case "remove": {
|
|
1040
|
-
const items = step.
|
|
1198
|
+
const items = step.diff ?? [];
|
|
1041
1199
|
if (reverse) {
|
|
1042
|
-
const sorted = [...items].sort((a, b) => a.index - b.index);
|
|
1043
|
-
for (const {
|
|
1200
|
+
const sorted = [...items].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
|
|
1201
|
+
for (const { oldSchema, parentId, index } of sorted) {
|
|
1202
|
+
if (!oldSchema || parentId === void 0) continue;
|
|
1044
1203
|
const parent = this.getNodeById(parentId, false);
|
|
1045
1204
|
if (parent?.items) {
|
|
1046
|
-
parent.items.splice(index, 0, cloneDeep$1(
|
|
1205
|
+
parent.items.splice(index ?? parent.items.length, 0, cloneDeep$1(oldSchema));
|
|
1047
1206
|
await stage?.add({
|
|
1048
|
-
config: cloneDeep$1(
|
|
1207
|
+
config: cloneDeep$1(oldSchema),
|
|
1049
1208
|
parent: cloneDeep$1(parent),
|
|
1050
1209
|
parentId,
|
|
1051
1210
|
root: cloneDeep$1(root)
|
|
1052
1211
|
});
|
|
1053
1212
|
}
|
|
1054
1213
|
}
|
|
1055
|
-
} else for (const {
|
|
1056
|
-
|
|
1214
|
+
} else for (const { oldSchema } of items) {
|
|
1215
|
+
if (!oldSchema) continue;
|
|
1216
|
+
const existing = this.getNodeById(oldSchema.id, false);
|
|
1057
1217
|
if (existing) await this.remove(existing, commonOpts);
|
|
1058
1218
|
}
|
|
1059
1219
|
break;
|
|
1060
1220
|
}
|
|
1061
1221
|
case "update": {
|
|
1062
|
-
const configs = (step.
|
|
1222
|
+
const configs = (step.diff ?? []).filter((item) => item.oldSchema && item.newSchema).map(({ oldSchema, newSchema, changeRecords }) => {
|
|
1223
|
+
const oldNode = oldSchema;
|
|
1224
|
+
const newNode = newSchema;
|
|
1063
1225
|
if (changeRecords?.length) {
|
|
1064
1226
|
const sourceForValues = reverse ? oldNode : newNode;
|
|
1065
1227
|
const patch = {
|