@tmagic/editor 1.8.0-beta.4 → 1.8.0-beta.5
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/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 +333 -211
- package/dist/es/layouts/history-list/InitialRow.vue_vue_type_script_setup_true_lang.js +28 -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/services/codeBlock.js +85 -37
- package/dist/es/services/dataSource.js +62 -26
- package/dist/es/services/editor.js +243 -100
- package/dist/es/services/history.js +270 -206
- package/dist/es/services/ui.js +3 -0
- package/dist/es/style.css +49 -6
- package/dist/es/utils/editor.js +35 -1
- package/dist/es/utils/history.js +223 -0
- package/dist/es/utils/indexed-db.js +86 -0
- package/dist/es/utils/undo-redo.js +60 -1
- package/dist/style.css +49 -6
- package/dist/tmagic-editor.umd.cjs +1799 -905
- package/package.json +7 -7
- package/src/components/CompareForm.vue +3 -1
- package/src/components/ToolButton.vue +2 -2
- 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 +296 -113
- package/src/layouts/history-list/InitialRow.vue +25 -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/services/codeBlock.ts +107 -37
- package/src/services/dataSource.ts +89 -40
- package/src/services/editor.ts +370 -134
- package/src/services/history.ts +305 -203
- 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/type.ts +167 -63
- package/src/utils/editor.ts +41 -1
- package/src/utils/history.ts +298 -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 +783 -291
|
@@ -3,44 +3,24 @@ import BaseService from "./BaseService.js";
|
|
|
3
3
|
import props_default from "./props.js";
|
|
4
4
|
import history_default from "./history.js";
|
|
5
5
|
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";
|
|
6
|
+
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
7
|
import { beforePaste, getAddParent } from "../utils/operator.js";
|
|
8
8
|
import { isFixed } from "@tmagic/stage";
|
|
9
|
-
import { getNodeInfo, getNodePath, getValueByKeyPath, isPage, isPageFragment, setValueByKeyPath } from "@tmagic/utils";
|
|
9
|
+
import { getNodeInfo, getNodePath, getValueByKeyPath, guid, isPage, isPageFragment, setValueByKeyPath } from "@tmagic/utils";
|
|
10
10
|
import { NodeType } from "@tmagic/core";
|
|
11
11
|
import { reactive, toRaw } from "vue";
|
|
12
|
-
import { cloneDeep as cloneDeep$1, isObject, mergeWith, uniq } from "lodash-es";
|
|
12
|
+
import { cloneDeep as cloneDeep$1, isEmpty, isEqual, isObject, mergeWith, uniq } from "lodash-es";
|
|
13
13
|
//#region packages/editor/src/services/editor.ts
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
15
|
+
* 把「变更前后节点快照」列表归一成 update 类型的 {@link StepDiffItem} 列表,供 {@link StepValue.diff} 使用。
|
|
16
|
+
* `changeRecords` 来自 form 端的 propPath/value 列表,撤销/重做时只对这些 propPath 做局部更新;
|
|
17
|
+
* 缺省(未传 / 空数组)才退化为整节点替换。
|
|
17
18
|
*/
|
|
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
|
-
};
|
|
19
|
+
var buildUpdateDiff = (items) => items.map(({ oldNode, newNode, changeRecords }) => ({
|
|
20
|
+
oldSchema: oldNode,
|
|
21
|
+
newSchema: newNode,
|
|
22
|
+
...changeRecords?.length ? { changeRecords } : {}
|
|
23
|
+
}));
|
|
44
24
|
var Editor = class extends BaseService {
|
|
45
25
|
state = reactive({
|
|
46
26
|
root: null,
|
|
@@ -58,6 +38,12 @@ var Editor = class extends BaseService {
|
|
|
58
38
|
alwaysMultiSelect: false
|
|
59
39
|
});
|
|
60
40
|
selectionBeforeOp = null;
|
|
41
|
+
/**
|
|
42
|
+
* 最近一次 pushOpHistory 写入的历史记录 uuid。
|
|
43
|
+
* 供 *AndGetHistoryId 系列方法在调用普通操作后取回本次产生的历史记录 id;
|
|
44
|
+
* 普通操作不会读取它,调用前由 *AndGetHistoryId 重置为 null。
|
|
45
|
+
*/
|
|
46
|
+
lastPushedHistoryId = null;
|
|
61
47
|
constructor() {
|
|
62
48
|
super(canUsePluginMethods.async.map((methodName) => ({
|
|
63
49
|
name: methodName,
|
|
@@ -72,8 +58,9 @@ var Editor = class extends BaseService {
|
|
|
72
58
|
* 设置当前指点节点配置
|
|
73
59
|
* @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength' | 'pageFragmentLength
|
|
74
60
|
* @param value MNode
|
|
61
|
+
* @param options.historySource 设置 root 时,本次变更写入历史记录的「操作来源」(仅 name === 'root' 时生效)
|
|
75
62
|
*/
|
|
76
|
-
set(name, value) {
|
|
63
|
+
set(name, value, options = {}) {
|
|
77
64
|
const preValue = this.state[name];
|
|
78
65
|
this.state[name] = value;
|
|
79
66
|
if (name === "nodes" && Array.isArray(value)) this.set("node", value[0]);
|
|
@@ -84,6 +71,13 @@ var Editor = class extends BaseService {
|
|
|
84
71
|
this.state.pageLength = getPageList(app).length || 0;
|
|
85
72
|
this.state.pageFragmentLength = getPageFragmentList(app).length || 0;
|
|
86
73
|
this.state.stageLoading = this.state.pageLength !== 0;
|
|
74
|
+
if (preValue && !isEmpty(preValue)) this.pushRootDiffHistory(preValue, app, options.historySource);
|
|
75
|
+
else app.items?.forEach((pageNode) => {
|
|
76
|
+
if (pageNode?.id !== void 0 && !history_default.getPageMarker(pageNode.id)) history_default.setPageMarker(pageNode.id, {
|
|
77
|
+
name: pageNode.name,
|
|
78
|
+
source: options.historySource
|
|
79
|
+
});
|
|
80
|
+
});
|
|
87
81
|
} else {
|
|
88
82
|
this.state.pageLength = 0;
|
|
89
83
|
this.state.pageFragmentLength = 0;
|
|
@@ -109,7 +103,23 @@ var Editor = class extends BaseService {
|
|
|
109
103
|
getNodeInfo(id, raw = true) {
|
|
110
104
|
let root = this.get("root");
|
|
111
105
|
if (raw) root = toRaw(root);
|
|
112
|
-
|
|
106
|
+
if (!root) return {
|
|
107
|
+
node: null,
|
|
108
|
+
parent: null,
|
|
109
|
+
page: null
|
|
110
|
+
};
|
|
111
|
+
if (id === root.id) return {
|
|
112
|
+
node: root,
|
|
113
|
+
parent: null,
|
|
114
|
+
page: null
|
|
115
|
+
};
|
|
116
|
+
const pageIdStr = `${this.get("page")?.id || ""}`;
|
|
117
|
+
const currentPageNode = root.items?.find((item) => `${item.id}` === pageIdStr);
|
|
118
|
+
if (currentPageNode && `${id}` !== pageIdStr) {
|
|
119
|
+
const info = getNodeInfo(id, currentPageNode);
|
|
120
|
+
if (info.node) return info;
|
|
121
|
+
}
|
|
122
|
+
return getNodeInfo(id, root, currentPageNode);
|
|
113
123
|
}
|
|
114
124
|
/**
|
|
115
125
|
* 根据ID获取指点节点配置
|
|
@@ -302,23 +312,26 @@ var Editor = class extends BaseService {
|
|
|
302
312
|
}
|
|
303
313
|
if (!(isPage(newNodes[0]) || isPageFragment(newNodes[0]))) {
|
|
304
314
|
const pageForOp = this.getNodeInfo(newNodes[0].id, false).page;
|
|
305
|
-
if (!doNotPushHistory)
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
indexMap: Object.fromEntries(newNodes.map((n) => {
|
|
315
|
+
if (!doNotPushHistory) {
|
|
316
|
+
const parentId = (this.getParentById(newNodes[0].id, false) ?? this.get("root")).id;
|
|
317
|
+
this.pushOpHistory("add", {
|
|
318
|
+
diff: newNodes.map((n) => {
|
|
310
319
|
const p = this.getParentById(n.id, false);
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
320
|
+
const idx = p ? getNodeIndex(n.id, p) : -1;
|
|
321
|
+
return {
|
|
322
|
+
newSchema: cloneDeep$1(toRaw(n)),
|
|
323
|
+
parentId,
|
|
324
|
+
index: typeof idx === "number" ? idx : -1
|
|
325
|
+
};
|
|
326
|
+
}),
|
|
327
|
+
pageData: {
|
|
328
|
+
name: pageForOp?.name || "",
|
|
329
|
+
id: pageForOp.id
|
|
330
|
+
},
|
|
331
|
+
historyDescription,
|
|
332
|
+
source: historySource
|
|
333
|
+
});
|
|
334
|
+
} else this.selectionBeforeOp = null;
|
|
322
335
|
}
|
|
323
336
|
this.emit("add", newNodes);
|
|
324
337
|
return Array.isArray(addNode) ? newNodes : newNodes[0];
|
|
@@ -398,7 +411,7 @@ var Editor = class extends BaseService {
|
|
|
398
411
|
};
|
|
399
412
|
const idx = getNodeIndex(curNode.id, parent);
|
|
400
413
|
removedItems.push({
|
|
401
|
-
|
|
414
|
+
oldSchema: cloneDeep$1(toRaw(curNode)),
|
|
402
415
|
parentId: parent.id,
|
|
403
416
|
index: typeof idx === "number" ? idx : -1
|
|
404
417
|
});
|
|
@@ -409,7 +422,7 @@ var Editor = class extends BaseService {
|
|
|
409
422
|
doNotSwitchPage
|
|
410
423
|
})));
|
|
411
424
|
if (removedItems.length > 0 && pageForOp) if (!doNotPushHistory) this.pushOpHistory("remove", {
|
|
412
|
-
|
|
425
|
+
diff: removedItems,
|
|
413
426
|
pageData: pageForOp,
|
|
414
427
|
historyDescription,
|
|
415
428
|
source: historySource
|
|
@@ -417,7 +430,7 @@ var Editor = class extends BaseService {
|
|
|
417
430
|
else this.selectionBeforeOp = null;
|
|
418
431
|
this.emit("remove", nodes);
|
|
419
432
|
}
|
|
420
|
-
async doUpdate(config, { changeRecords = [] } = {}) {
|
|
433
|
+
async doUpdate(config, { changeRecords = [], historySource } = {}) {
|
|
421
434
|
const root = this.get("root");
|
|
422
435
|
if (!root) throw new Error("root为空");
|
|
423
436
|
if (!config?.id) throw new Error("没有配置或者配置缺少id值");
|
|
@@ -428,7 +441,7 @@ var Editor = class extends BaseService {
|
|
|
428
441
|
newConfig = mergeWith(cloneDeep$1(node), newConfig, editorNodeMergeCustomizer);
|
|
429
442
|
if (!newConfig.type) throw new Error("配置缺少type值");
|
|
430
443
|
if (newConfig.type === NodeType.ROOT) {
|
|
431
|
-
this.set("root", newConfig);
|
|
444
|
+
this.set("root", newConfig, { historySource });
|
|
432
445
|
return {
|
|
433
446
|
oldNode: node,
|
|
434
447
|
newNode: newConfig,
|
|
@@ -478,17 +491,20 @@ var Editor = class extends BaseService {
|
|
|
478
491
|
const nodes = Array.isArray(config) ? config : [config];
|
|
479
492
|
const updateData = await Promise.all(nodes.map((node, index) => {
|
|
480
493
|
const recordsForNode = changeRecordList ? changeRecordList[index] ?? [] : changeRecords ?? [];
|
|
481
|
-
return this.doUpdate(node, {
|
|
494
|
+
return this.doUpdate(node, {
|
|
495
|
+
changeRecords: recordsForNode,
|
|
496
|
+
historySource
|
|
497
|
+
});
|
|
482
498
|
}));
|
|
483
499
|
if (updateData[0].oldNode?.type !== NodeType.ROOT) {
|
|
484
500
|
if (this.get("nodes").length) if (!doNotPushHistory) {
|
|
485
501
|
const pageForOp = this.getNodeInfo(nodes[0].id, false).page;
|
|
486
502
|
this.pushOpHistory("update", {
|
|
487
|
-
|
|
503
|
+
diff: buildUpdateDiff(updateData.map((d) => ({
|
|
488
504
|
oldNode: cloneDeep$1(d.oldNode),
|
|
489
|
-
newNode: cloneDeep$1(
|
|
505
|
+
newNode: cloneDeep$1(d.newNode),
|
|
490
506
|
changeRecords: d.changeRecords?.length ? cloneDeep$1(d.changeRecords) : void 0
|
|
491
|
-
}))
|
|
507
|
+
}))),
|
|
492
508
|
pageData: {
|
|
493
509
|
name: pageForOp?.name || "",
|
|
494
510
|
id: pageForOp.id
|
|
@@ -651,10 +667,10 @@ var Editor = class extends BaseService {
|
|
|
651
667
|
if (!doNotPushHistory) {
|
|
652
668
|
const pageForOp = this.getNodeInfo(node.id, false).page;
|
|
653
669
|
this.pushOpHistory("update", {
|
|
654
|
-
|
|
670
|
+
diff: buildUpdateDiff([{
|
|
655
671
|
oldNode: oldParent,
|
|
656
672
|
newNode: cloneDeep$1(toRaw(parent))
|
|
657
|
-
}]
|
|
673
|
+
}]),
|
|
658
674
|
pageData: {
|
|
659
675
|
name: pageForOp?.name || "",
|
|
660
676
|
id: pageForOp.id
|
|
@@ -728,7 +744,7 @@ var Editor = class extends BaseService {
|
|
|
728
744
|
id: target.id
|
|
729
745
|
};
|
|
730
746
|
this.pushOpHistory("update", {
|
|
731
|
-
|
|
747
|
+
diff: buildUpdateDiff(updatedItems),
|
|
732
748
|
pageData: historyPage,
|
|
733
749
|
historyDescription,
|
|
734
750
|
source: historySource
|
|
@@ -779,7 +795,7 @@ var Editor = class extends BaseService {
|
|
|
779
795
|
if (!doNotPushHistory) {
|
|
780
796
|
const pageForOp = this.getNodeInfo(configs[0].id, false).page;
|
|
781
797
|
this.pushOpHistory("update", {
|
|
782
|
-
|
|
798
|
+
diff: buildUpdateDiff(updatedItems),
|
|
783
799
|
pageData: {
|
|
784
800
|
name: pageForOp?.name || "",
|
|
785
801
|
id: pageForOp.id
|
|
@@ -795,6 +811,49 @@ var Editor = class extends BaseService {
|
|
|
795
811
|
});
|
|
796
812
|
}
|
|
797
813
|
/**
|
|
814
|
+
* 下列 *AndGetHistoryId 方法与对应的普通操作(add / remove / update ...)行为完全一致,
|
|
815
|
+
* 唯一区别是返回值为本次写入历史栈的历史记录 uuid({@link StepValue.uuid}),
|
|
816
|
+
* 而非节点 / 节点数组。可用于精确引用 / 定位该条历史记录(埋点、revert、跨端同步等)。
|
|
817
|
+
*
|
|
818
|
+
* 当本次操作未写入历史(doNotPushHistory 为 true、或操作无实际变更 / 提前返回)时返回 null。
|
|
819
|
+
*/
|
|
820
|
+
/** 等价于 {@link add},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
|
|
821
|
+
async addAndGetHistoryId(addNode, parent, options = {}) {
|
|
822
|
+
this.lastPushedHistoryId = null;
|
|
823
|
+
await this.add(addNode, parent, options);
|
|
824
|
+
return this.lastPushedHistoryId;
|
|
825
|
+
}
|
|
826
|
+
/** 等价于 {@link remove},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
|
|
827
|
+
async removeAndGetHistoryId(nodeOrNodeList, options = {}) {
|
|
828
|
+
this.lastPushedHistoryId = null;
|
|
829
|
+
await this.remove(nodeOrNodeList, options);
|
|
830
|
+
return this.lastPushedHistoryId;
|
|
831
|
+
}
|
|
832
|
+
/** 等价于 {@link update},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
|
|
833
|
+
async updateAndGetHistoryId(config, data = {}) {
|
|
834
|
+
this.lastPushedHistoryId = null;
|
|
835
|
+
await this.update(config, data);
|
|
836
|
+
return this.lastPushedHistoryId;
|
|
837
|
+
}
|
|
838
|
+
/** 等价于 {@link moveLayer},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
|
|
839
|
+
async moveLayerAndGetHistoryId(offset, options = {}) {
|
|
840
|
+
this.lastPushedHistoryId = null;
|
|
841
|
+
await this.moveLayer(offset, options);
|
|
842
|
+
return this.lastPushedHistoryId;
|
|
843
|
+
}
|
|
844
|
+
/** 等价于 {@link moveToContainer},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
|
|
845
|
+
async moveToContainerAndGetHistoryId(config, targetId, options = {}) {
|
|
846
|
+
this.lastPushedHistoryId = null;
|
|
847
|
+
await this.moveToContainer(config, targetId, options);
|
|
848
|
+
return this.lastPushedHistoryId;
|
|
849
|
+
}
|
|
850
|
+
/** 等价于 {@link dragTo},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
|
|
851
|
+
async dragToAndGetHistoryId(config, targetParent, targetIndex, options = {}) {
|
|
852
|
+
this.lastPushedHistoryId = null;
|
|
853
|
+
await this.dragTo(config, targetParent, targetIndex, options);
|
|
854
|
+
return this.lastPushedHistoryId;
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
798
857
|
* 撤销当前操作
|
|
799
858
|
* @returns 被撤销的操作
|
|
800
859
|
*/
|
|
@@ -832,9 +891,10 @@ var Editor = class extends BaseService {
|
|
|
832
891
|
const entry = history_default.getPageStepList()[index];
|
|
833
892
|
if (!entry?.applied) return null;
|
|
834
893
|
const { step } = entry;
|
|
894
|
+
if (step.opType === "initial") return null;
|
|
835
895
|
if (!this.get("root")) return null;
|
|
836
896
|
if (step.opType === "update") {
|
|
837
|
-
const items = step.
|
|
897
|
+
const items = step.diff ?? [];
|
|
838
898
|
if (!items.length || !items.every((item) => item.changeRecords?.length)) return null;
|
|
839
899
|
}
|
|
840
900
|
let revertedStep = null;
|
|
@@ -851,24 +911,26 @@ var Editor = class extends BaseService {
|
|
|
851
911
|
};
|
|
852
912
|
try {
|
|
853
913
|
switch (step.opType) {
|
|
854
|
-
case "add":
|
|
855
|
-
const
|
|
856
|
-
|
|
857
|
-
const existing = this.getNodeById(
|
|
914
|
+
case "add":
|
|
915
|
+
for (const { newSchema } of step.diff ?? []) {
|
|
916
|
+
if (!newSchema) continue;
|
|
917
|
+
const existing = this.getNodeById(newSchema.id, false);
|
|
858
918
|
if (existing) await this.remove(existing, opts);
|
|
859
919
|
}
|
|
860
920
|
break;
|
|
861
|
-
}
|
|
862
921
|
case "remove": {
|
|
863
|
-
const sorted = [...step.
|
|
864
|
-
for (const {
|
|
922
|
+
const sorted = [...step.diff ?? []].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
|
|
923
|
+
for (const { oldSchema, parentId } of sorted) {
|
|
924
|
+
if (!oldSchema || parentId === void 0) continue;
|
|
865
925
|
const parent = this.getNodeById(parentId, false);
|
|
866
|
-
if (parent) await this.add([cloneDeep$1(
|
|
926
|
+
if (parent) await this.add([cloneDeep$1(oldSchema)], parent, opts);
|
|
867
927
|
}
|
|
868
928
|
break;
|
|
869
929
|
}
|
|
870
930
|
case "update": {
|
|
871
|
-
const configs = (step.
|
|
931
|
+
const configs = (step.diff ?? []).filter((item) => item.oldSchema && item.newSchema).map(({ oldSchema, newSchema, changeRecords }) => {
|
|
932
|
+
const oldNode = oldSchema;
|
|
933
|
+
const newNode = newSchema;
|
|
872
934
|
if (changeRecords?.length) {
|
|
873
935
|
const patch = {
|
|
874
936
|
id: newNode.id,
|
|
@@ -898,6 +960,19 @@ var Editor = class extends BaseService {
|
|
|
898
960
|
return revertedStep;
|
|
899
961
|
}
|
|
900
962
|
/**
|
|
963
|
+
* 通过历史记录 uuid 回滚当前页面的某条历史步骤,语义与 {@link revertPageStep} 完全一致,
|
|
964
|
+
* 仅入参从 index 改为 uuid({@link StepValue.uuid})。uuid 不随栈内步骤增删而变化,
|
|
965
|
+
* 更适合业务侧持有引用后再回滚(埋点、跨端同步等场景)。
|
|
966
|
+
*
|
|
967
|
+
* @param uuid 目标历史记录的 uuid,通常由 *AndGetHistoryId 方法返回
|
|
968
|
+
* @returns 反向后产生的新 step;找不到对应 uuid / 未应用 / 反向失败时返回 null
|
|
969
|
+
*/
|
|
970
|
+
async revertPageStepById(uuid) {
|
|
971
|
+
const index = history_default.getPageStepIndexByUuid(uuid);
|
|
972
|
+
if (index < 0) return null;
|
|
973
|
+
return this.revertPageStep(index);
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
901
976
|
* 跳转当前页面历史栈到指定游标位置。
|
|
902
977
|
*
|
|
903
978
|
* `targetCursor` 与 `UndoRedo.getCursor()` 同义:表示"已应用步骤数量",
|
|
@@ -973,19 +1048,83 @@ var Editor = class extends BaseService {
|
|
|
973
1048
|
if (this.selectionBeforeOp) return;
|
|
974
1049
|
this.selectionBeforeOp = this.get("nodes").map((n) => n.id);
|
|
975
1050
|
}
|
|
976
|
-
|
|
1051
|
+
/**
|
|
1052
|
+
* 比较「上一次 root」与「新 root」的页面 / 页面片,按页面粒度把整体替换拆成历史记录:
|
|
1053
|
+
* - 新旧都存在且内容变化的页面 → 一条 `update`(整页快照替换,无 changeRecords);
|
|
1054
|
+
* - 仅新 root 存在的页面 → 一条 `add`;
|
|
1055
|
+
* - 仅旧 root 存在的页面 → 一条 `remove`。
|
|
1056
|
+
*
|
|
1057
|
+
* 每条记录落到对应页面自己的历史栈(与普通节点操作一致),并标记来源 `source`。
|
|
1058
|
+
* 内容未变化的页面不产生记录,避免重复设置相同 DSL 时产生噪声。
|
|
1059
|
+
*/
|
|
1060
|
+
pushRootDiffHistory(preRoot, nextRoot, source) {
|
|
1061
|
+
const prevPages = preRoot?.items || [];
|
|
1062
|
+
const nextPages = nextRoot?.items || [];
|
|
1063
|
+
const prevMap = new Map(prevPages.map((p) => [`${p.id}`, p]));
|
|
1064
|
+
const nextMap = new Map(nextPages.map((p) => [`${p.id}`, p]));
|
|
1065
|
+
const indexInItems = (root, id) => (root.items ?? []).findIndex((item) => `${item.id}` === `${id}`);
|
|
1066
|
+
nextPages.forEach((nextPage) => {
|
|
1067
|
+
const prevPage = prevMap.get(`${nextPage.id}`);
|
|
1068
|
+
if (!prevPage) this.pushPageDiffStep("add", nextPage, {
|
|
1069
|
+
newSchema: cloneDeep$1(toRaw(nextPage)),
|
|
1070
|
+
parentId: nextRoot.id,
|
|
1071
|
+
index: indexInItems(nextRoot, nextPage.id)
|
|
1072
|
+
}, source);
|
|
1073
|
+
else if (!isEqual(toRaw(prevPage), toRaw(nextPage))) this.pushPageDiffStep("update", nextPage, {
|
|
1074
|
+
oldSchema: cloneDeep$1(toRaw(prevPage)),
|
|
1075
|
+
newSchema: cloneDeep$1(toRaw(nextPage))
|
|
1076
|
+
}, source);
|
|
1077
|
+
});
|
|
1078
|
+
prevPages.forEach((prevPage) => {
|
|
1079
|
+
if (!nextMap.has(`${prevPage.id}`)) this.pushPageDiffStep("remove", prevPage, {
|
|
1080
|
+
oldSchema: cloneDeep$1(toRaw(prevPage)),
|
|
1081
|
+
parentId: preRoot.id,
|
|
1082
|
+
index: indexInItems(preRoot, prevPage.id)
|
|
1083
|
+
}, source);
|
|
1084
|
+
});
|
|
1085
|
+
}
|
|
1086
|
+
/**
|
|
1087
|
+
* 构造一条页面级「set root」历史记录(不携带选区 / modifiedNodeIds 上下文)并落到该页面自己的栈。
|
|
1088
|
+
*
|
|
1089
|
+
* 连续 set root 替换:若该页栈最新一条已是**同来源**的 set root 记录({@link StepValue.rootStep} 且 `source` 相同),
|
|
1090
|
+
* 则用本次记录**替换**它而非新增,避免源码反复保存 / 外部重设 DSL 时堆积多条 root 记录;
|
|
1091
|
+
* 来源不同则照常新增(initial 基线不是 rootStep,不在此列)。
|
|
1092
|
+
*/
|
|
1093
|
+
pushPageDiffStep(opType, page, diffItem, source) {
|
|
1094
|
+
const step = {
|
|
1095
|
+
uuid: guid(),
|
|
1096
|
+
data: {
|
|
1097
|
+
name: page.name || "",
|
|
1098
|
+
id: page.id
|
|
1099
|
+
},
|
|
1100
|
+
opType,
|
|
1101
|
+
selectedBefore: [],
|
|
1102
|
+
selectedAfter: [],
|
|
1103
|
+
modifiedNodeIds: /* @__PURE__ */ new Map(),
|
|
1104
|
+
diff: [diffItem],
|
|
1105
|
+
rootStep: true
|
|
1106
|
+
};
|
|
1107
|
+
if (source) step.source = source;
|
|
1108
|
+
const top = history_default.getCurrentPageStep(page.id);
|
|
1109
|
+
if (top?.rootStep && top.source === source) history_default.replaceCurrentPageStep(step, page.id);
|
|
1110
|
+
else history_default.push(step, page.id);
|
|
1111
|
+
}
|
|
1112
|
+
pushOpHistory(opType, { diff, pageData, historyDescription, source }) {
|
|
977
1113
|
const step = {
|
|
1114
|
+
uuid: guid(),
|
|
978
1115
|
data: pageData,
|
|
979
1116
|
opType,
|
|
980
1117
|
selectedBefore: this.selectionBeforeOp ?? [],
|
|
981
1118
|
selectedAfter: this.get("nodes").map((n) => n.id),
|
|
982
1119
|
modifiedNodeIds: new Map(this.get("modifiedNodeIds")),
|
|
983
|
-
|
|
1120
|
+
diff
|
|
984
1121
|
};
|
|
985
1122
|
if (historyDescription) step.historyDescription = historyDescription;
|
|
986
1123
|
if (source) step.source = source;
|
|
987
|
-
history_default.push(step, pageData.id);
|
|
1124
|
+
const historyId = history_default.push(step, pageData.id) ? step.uuid : null;
|
|
1125
|
+
this.lastPushedHistoryId = historyId;
|
|
988
1126
|
this.selectionBeforeOp = null;
|
|
1127
|
+
return historyId;
|
|
989
1128
|
}
|
|
990
1129
|
/**
|
|
991
1130
|
* 应用历史操作(撤销 / 重做)
|
|
@@ -1000,6 +1139,7 @@ var Editor = class extends BaseService {
|
|
|
1000
1139
|
* @param reverse true = 撤销,false = 重做
|
|
1001
1140
|
*/
|
|
1002
1141
|
async applyHistoryOp(step, reverse) {
|
|
1142
|
+
if (step.opType === "initial") return;
|
|
1003
1143
|
const root = this.get("root");
|
|
1004
1144
|
const stage = this.get("stage");
|
|
1005
1145
|
if (!root) return;
|
|
@@ -1010,56 +1150,59 @@ var Editor = class extends BaseService {
|
|
|
1010
1150
|
};
|
|
1011
1151
|
switch (step.opType) {
|
|
1012
1152
|
case "add": {
|
|
1013
|
-
const
|
|
1014
|
-
if (reverse) for (const
|
|
1015
|
-
|
|
1153
|
+
const items = step.diff ?? [];
|
|
1154
|
+
if (reverse) for (const { newSchema } of items) {
|
|
1155
|
+
if (!newSchema) continue;
|
|
1156
|
+
const existing = this.getNodeById(newSchema.id, false);
|
|
1016
1157
|
if (existing) await this.remove(existing, commonOpts);
|
|
1017
1158
|
}
|
|
1018
1159
|
else {
|
|
1019
|
-
const
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
if (parent.items)
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
});
|
|
1033
|
-
}
|
|
1160
|
+
const sorted = [...items].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
|
|
1161
|
+
for (const { newSchema, parentId, index } of sorted) {
|
|
1162
|
+
if (!newSchema || parentId === void 0) continue;
|
|
1163
|
+
const parent = this.getNodeById(parentId, false);
|
|
1164
|
+
if (parent?.items) {
|
|
1165
|
+
if (typeof index === "number" && index >= 0 && index < parent.items.length) parent.items.splice(index, 0, cloneDeep$1(newSchema));
|
|
1166
|
+
else parent.items.push(cloneDeep$1(newSchema));
|
|
1167
|
+
await stage?.add({
|
|
1168
|
+
config: cloneDeep$1(newSchema),
|
|
1169
|
+
parent: cloneDeep$1(parent),
|
|
1170
|
+
parentId: parent.id,
|
|
1171
|
+
root: cloneDeep$1(root)
|
|
1172
|
+
});
|
|
1034
1173
|
}
|
|
1035
1174
|
}
|
|
1036
1175
|
}
|
|
1037
1176
|
break;
|
|
1038
1177
|
}
|
|
1039
1178
|
case "remove": {
|
|
1040
|
-
const items = step.
|
|
1179
|
+
const items = step.diff ?? [];
|
|
1041
1180
|
if (reverse) {
|
|
1042
|
-
const sorted = [...items].sort((a, b) => a.index - b.index);
|
|
1043
|
-
for (const {
|
|
1181
|
+
const sorted = [...items].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
|
|
1182
|
+
for (const { oldSchema, parentId, index } of sorted) {
|
|
1183
|
+
if (!oldSchema || parentId === void 0) continue;
|
|
1044
1184
|
const parent = this.getNodeById(parentId, false);
|
|
1045
1185
|
if (parent?.items) {
|
|
1046
|
-
parent.items.splice(index, 0, cloneDeep$1(
|
|
1186
|
+
parent.items.splice(index ?? parent.items.length, 0, cloneDeep$1(oldSchema));
|
|
1047
1187
|
await stage?.add({
|
|
1048
|
-
config: cloneDeep$1(
|
|
1188
|
+
config: cloneDeep$1(oldSchema),
|
|
1049
1189
|
parent: cloneDeep$1(parent),
|
|
1050
1190
|
parentId,
|
|
1051
1191
|
root: cloneDeep$1(root)
|
|
1052
1192
|
});
|
|
1053
1193
|
}
|
|
1054
1194
|
}
|
|
1055
|
-
} else for (const {
|
|
1056
|
-
|
|
1195
|
+
} else for (const { oldSchema } of items) {
|
|
1196
|
+
if (!oldSchema) continue;
|
|
1197
|
+
const existing = this.getNodeById(oldSchema.id, false);
|
|
1057
1198
|
if (existing) await this.remove(existing, commonOpts);
|
|
1058
1199
|
}
|
|
1059
1200
|
break;
|
|
1060
1201
|
}
|
|
1061
1202
|
case "update": {
|
|
1062
|
-
const configs = (step.
|
|
1203
|
+
const configs = (step.diff ?? []).filter((item) => item.oldSchema && item.newSchema).map(({ oldSchema, newSchema, changeRecords }) => {
|
|
1204
|
+
const oldNode = oldSchema;
|
|
1205
|
+
const newNode = newSchema;
|
|
1063
1206
|
if (changeRecords?.length) {
|
|
1064
1207
|
const sourceForValues = reverse ? oldNode : newNode;
|
|
1065
1208
|
const patch = {
|