@tmagic/editor 1.8.0-beta.1 → 1.8.0-beta.10
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/Editor.vue_vue_type_script_setup_true_lang.js +31 -5
- package/dist/es/components/CodeBlockEditor.vue_vue_type_script_setup_true_lang.js +21 -113
- package/dist/es/components/CodeParams.vue_vue_type_script_setup_true_lang.js +7 -1
- package/dist/es/components/CompareForm.js +5 -0
- package/dist/es/components/CompareForm.vue_vue_type_script_setup_true_lang.js +196 -0
- package/dist/es/components/ContentMenu.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/components/FloatingBox.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/es/components/Icon.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/components/Resizer.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/components/ScrollBar.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/components/ScrollViewer.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/components/SearchInput.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/components/SplitView.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/components/ToolButton.vue_vue_type_script_setup_true_lang.js +7 -7
- package/dist/es/components/Tree.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/components/TreeNode.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/editorProps.js +2 -0
- package/dist/es/fields/Code.vue_vue_type_script_setup_true_lang.js +20 -4
- package/dist/es/fields/CodeLink.vue_vue_type_script_setup_true_lang.js +3 -6
- package/dist/es/fields/CodeSelect.vue_vue_type_script_setup_true_lang.js +19 -3
- package/dist/es/fields/CodeSelectCol.vue_vue_type_script_setup_true_lang.js +42 -8
- package/dist/es/fields/CondOpSelect.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/fields/DataSourceFieldSelect/FieldSelect.vue_vue_type_script_setup_true_lang.js +14 -6
- package/dist/es/fields/DataSourceFieldSelect/Index.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/es/fields/DataSourceFields.vue_vue_type_script_setup_true_lang.js +41 -16
- package/dist/es/fields/DataSourceInput.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/es/fields/DataSourceMethodSelect.vue_vue_type_script_setup_true_name_true_lang.js +9 -5
- package/dist/es/fields/DataSourceMethods.vue_vue_type_script_setup_true_lang.js +40 -19
- package/dist/es/fields/DataSourceMocks.vue_vue_type_script_setup_true_lang.js +17 -9
- package/dist/es/fields/DataSourceSelect.vue_vue_type_script_setup_true_lang.js +4 -2
- package/dist/es/fields/DisplayConds.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/fields/EventSelect.vue_vue_type_script_setup_true_lang.js +59 -12
- package/dist/es/fields/KeyValue.vue_vue_type_script_setup_true_lang.js +12 -7
- package/dist/es/fields/PageFragmentSelect.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/fields/StyleSetter/Index.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/fields/StyleSetter/components/BackgroundPosition.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/fields/StyleSetter/components/Border.vue_vue_type_script_setup_true_lang.js +28 -6
- package/dist/es/fields/StyleSetter/components/Box.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/fields/StyleSetter/icons/background-repeat/NoRepeat.js +1 -1
- package/dist/es/fields/StyleSetter/icons/background-repeat/Repeat.js +1 -1
- package/dist/es/fields/StyleSetter/icons/background-repeat/RepeatX.js +1 -1
- package/dist/es/fields/StyleSetter/icons/background-repeat/RepeatY.js +1 -1
- package/dist/es/fields/StyleSetter/icons/display/Block.js +1 -1
- package/dist/es/fields/StyleSetter/icons/display/Flex.js +1 -1
- package/dist/es/fields/StyleSetter/icons/display/Inline.js +1 -1
- package/dist/es/fields/StyleSetter/icons/display/InlineBlock.js +1 -1
- package/dist/es/fields/StyleSetter/icons/display/None.js +1 -1
- package/dist/es/fields/StyleSetter/icons/flex-direction/Column.js +1 -1
- package/dist/es/fields/StyleSetter/icons/flex-direction/ColumnReverse.js +1 -1
- package/dist/es/fields/StyleSetter/icons/flex-direction/Row.js +1 -1
- package/dist/es/fields/StyleSetter/icons/flex-direction/RowReverse.js +1 -1
- package/dist/es/fields/StyleSetter/icons/justify-content/Center.js +1 -1
- package/dist/es/fields/StyleSetter/icons/justify-content/FlexEnd.js +1 -1
- package/dist/es/fields/StyleSetter/icons/justify-content/FlexStart.js +1 -1
- package/dist/es/fields/StyleSetter/icons/justify-content/SpaceAround.js +1 -1
- package/dist/es/fields/StyleSetter/icons/justify-content/SpaceBetween.js +1 -1
- package/dist/es/fields/StyleSetter/icons/text-align/Center.js +1 -1
- package/dist/es/fields/StyleSetter/icons/text-align/Left.js +1 -1
- package/dist/es/fields/StyleSetter/icons/text-align/Right.js +1 -1
- package/dist/es/fields/StyleSetter/pro/Background.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/fields/StyleSetter/pro/Border.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/fields/StyleSetter/pro/Font.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/fields/StyleSetter/pro/Layout.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/fields/StyleSetter/pro/Position.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/fields/StyleSetter/pro/Transform.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/fields/UISelect.vue_vue_type_script_setup_true_lang.js +9 -6
- package/dist/es/hooks/use-code-block-edit.js +7 -4
- package/dist/es/hooks/use-data-source-edit.js +5 -2
- package/dist/es/hooks/use-stage.js +22 -10
- package/dist/es/icons/CenterIcon.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/icons/CodeIcon.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/icons/FolderMinusIcon.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/index.js +14 -3
- package/dist/es/initService.js +7 -7
- package/dist/es/layouts/AddPageBox.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/layouts/CodeEditor.vue_vue_type_script_setup_true_lang.js +7 -6
- package/dist/es/layouts/Framework.vue_vue_type_script_setup_true_lang.js +14 -9
- package/dist/es/layouts/NavMenu.vue_vue_type_script_setup_true_lang.js +12 -4
- package/dist/es/layouts/NavMenuColumn.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/layouts/history-list/Bucket.js +5 -0
- package/dist/es/layouts/history-list/Bucket.vue_vue_type_script_setup_true_lang.js +71 -0
- package/dist/es/layouts/history-list/BucketTab.js +5 -0
- package/dist/es/layouts/history-list/BucketTab.vue_vue_type_script_setup_true_lang.js +62 -0
- package/dist/es/layouts/history-list/GroupRow.js +5 -0
- package/dist/es/layouts/history-list/GroupRow.vue_vue_type_script_setup_true_lang.js +269 -0
- package/dist/es/layouts/history-list/HistoryDiffDialog.js +5 -0
- package/dist/es/layouts/history-list/HistoryDiffDialog.vue_vue_type_script_setup_true_lang.js +287 -0
- package/dist/es/layouts/history-list/HistoryListPanel.js +5 -0
- package/dist/es/layouts/history-list/HistoryListPanel.vue_vue_type_script_setup_true_lang.js +327 -0
- package/dist/es/layouts/history-list/InitialRow.js +5 -0
- package/dist/es/layouts/history-list/InitialRow.vue_vue_type_script_setup_true_lang.js +83 -0
- package/dist/es/layouts/history-list/PageTab.js +5 -0
- package/dist/es/layouts/history-list/PageTab.vue_vue_type_script_setup_true_lang.js +95 -0
- package/dist/es/layouts/history-list/composables.js +184 -0
- package/dist/es/layouts/history-list/useHistoryList.js +56 -0
- package/dist/es/layouts/history-list/useHistoryRevert.js +306 -0
- package/dist/es/layouts/page-bar/AddButton.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/layouts/page-bar/PageBar.vue_vue_type_script_setup_true_lang.js +5 -3
- package/dist/es/layouts/page-bar/PageBarScrollContainer.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/layouts/page-bar/PageList.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/layouts/page-bar/Search.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/es/layouts/props-panel/FormPanel.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/layouts/props-panel/PropsPanel.vue_vue_type_script_setup_true_lang.js +6 -2
- package/dist/es/layouts/sidebar/ComponentListPanel.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/es/layouts/sidebar/Sidebar.vue_vue_type_script_setup_true_lang.js +8 -4
- package/dist/es/layouts/sidebar/code-block/CodeBlockList.vue_vue_type_script_setup_true_lang.js +4 -4
- package/dist/es/layouts/sidebar/code-block/CodeBlockListPanel.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/es/layouts/sidebar/code-block/useContentMenu.js +1 -1
- package/dist/es/layouts/sidebar/data-source/DataSourceAddButton.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/layouts/sidebar/data-source/DataSourceConfigPanel.vue_vue_type_script_setup_true_lang.js +19 -6
- package/dist/es/layouts/sidebar/data-source/DataSourceList.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/layouts/sidebar/data-source/DataSourceListPanel.vue_vue_type_script_setup_true_lang.js +25 -3
- package/dist/es/layouts/sidebar/data-source/useContentMenu.js +1 -1
- package/dist/es/layouts/sidebar/layer/LayerMenu.vue_vue_type_script_setup_true_lang.js +6 -6
- package/dist/es/layouts/sidebar/layer/LayerNodeTool.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/es/layouts/sidebar/layer/LayerPanel.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/layouts/sidebar/layer/use-node-status.js +1 -1
- package/dist/es/layouts/workspace/Breadcrumb.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/layouts/workspace/Workspace.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/layouts/workspace/viewer/NodeListMenu.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/layouts/workspace/viewer/Stage.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/es/layouts/workspace/viewer/StageOverlay.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/layouts/workspace/viewer/ViewerMenu.vue_vue_type_script_setup_true_lang.js +9 -9
- package/dist/es/services/BaseService.js +31 -68
- package/dist/es/services/codeBlock.js +286 -8
- package/dist/es/services/componentList.js +2 -2
- package/dist/es/services/dataSource.js +287 -8
- package/dist/es/services/dep.js +21 -9
- package/dist/es/services/editor.js +655 -194
- package/dist/es/services/events.js +2 -2
- package/dist/es/services/history.js +390 -47
- package/dist/es/services/keybinding.js +5 -5
- package/dist/es/services/props.js +8 -8
- package/dist/es/services/stageOverlay.js +2 -2
- package/dist/es/services/storage.js +2 -2
- package/dist/es/services/ui.js +5 -2
- package/dist/es/style.css +456 -8
- package/dist/es/utils/code-block.js +122 -0
- package/dist/es/utils/content-menu.js +19 -12
- package/dist/es/utils/data-source/index.js +2 -0
- package/dist/es/utils/dep/worker.js +1 -1
- package/dist/es/utils/editor.js +105 -51
- package/dist/es/utils/history.js +220 -0
- package/dist/es/utils/indexed-db.js +86 -0
- package/dist/es/utils/undo-redo.js +80 -1
- package/dist/style.css +456 -8
- package/dist/tmagic-editor.umd.cjs +10135 -5646
- package/package.json +8 -8
- package/src/Editor.vue +25 -1
- package/src/components/CodeBlockEditor.vue +20 -109
- package/src/components/CodeParams.vue +6 -0
- package/src/components/CompareForm.vue +277 -0
- package/src/components/ToolButton.vue +2 -2
- package/src/editorProps.ts +9 -0
- package/src/fields/Code.vue +19 -3
- package/src/fields/CodeLink.vue +2 -5
- package/src/fields/CodeSelect.vue +17 -1
- package/src/fields/CodeSelectCol.vue +37 -3
- package/src/fields/DataSourceFieldSelect/FieldSelect.vue +23 -3
- package/src/fields/DataSourceFieldSelect/Index.vue +2 -2
- package/src/fields/DataSourceFields.vue +48 -10
- package/src/fields/DataSourceInput.vue +3 -3
- package/src/fields/DataSourceMethodSelect.vue +12 -4
- package/src/fields/DataSourceMethods.vue +55 -24
- package/src/fields/DataSourceMocks.vue +13 -3
- package/src/fields/DataSourceSelect.vue +4 -1
- package/src/fields/EventSelect.vue +57 -8
- package/src/fields/KeyValue.vue +17 -4
- package/src/fields/StyleSetter/components/Border.vue +15 -6
- package/src/fields/UISelect.vue +5 -2
- package/src/hooks/use-code-block-edit.ts +9 -5
- package/src/hooks/use-data-source-edit.ts +2 -2
- package/src/hooks/use-stage.ts +26 -9
- package/src/index.ts +8 -0
- package/src/initService.ts +1 -1
- package/src/layouts/CodeEditor.vue +17 -5
- package/src/layouts/Framework.vue +11 -6
- package/src/layouts/NavMenu.vue +12 -3
- package/src/layouts/history-list/Bucket.vue +91 -0
- package/src/layouts/history-list/BucketTab.vue +72 -0
- package/src/layouts/history-list/GroupRow.vue +304 -0
- package/src/layouts/history-list/HistoryDiffDialog.vue +273 -0
- package/src/layouts/history-list/HistoryListPanel.vue +367 -0
- package/src/layouts/history-list/InitialRow.vue +71 -0
- package/src/layouts/history-list/PageTab.vue +118 -0
- package/src/layouts/history-list/composables.ts +309 -0
- package/src/layouts/history-list/useHistoryList.ts +60 -0
- package/src/layouts/history-list/useHistoryRevert.ts +405 -0
- package/src/layouts/page-bar/PageBar.vue +4 -2
- package/src/layouts/props-panel/PropsPanel.vue +5 -1
- package/src/layouts/sidebar/ComponentListPanel.vue +9 -5
- package/src/layouts/sidebar/Sidebar.vue +8 -3
- package/src/layouts/sidebar/code-block/CodeBlockList.vue +5 -5
- package/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +1 -1
- package/src/layouts/sidebar/code-block/useContentMenu.ts +1 -1
- package/src/layouts/sidebar/data-source/DataSourceConfigPanel.vue +31 -2
- package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +27 -2
- package/src/layouts/sidebar/data-source/useContentMenu.ts +1 -1
- package/src/layouts/sidebar/layer/LayerMenu.vue +19 -11
- package/src/layouts/sidebar/layer/LayerNodeTool.vue +7 -4
- package/src/layouts/sidebar/layer/use-node-status.ts +9 -6
- package/src/layouts/workspace/viewer/Stage.vue +1 -1
- package/src/layouts/workspace/viewer/ViewerMenu.vue +8 -8
- package/src/services/BaseService.ts +35 -86
- package/src/services/codeBlock.ts +361 -5
- package/src/services/dataSource.ts +336 -6
- package/src/services/dep.ts +32 -9
- package/src/services/editor.ts +834 -194
- package/src/services/history.ts +509 -49
- package/src/services/keybinding.ts +3 -3
- package/src/services/props.ts +2 -2
- package/src/services/ui.ts +7 -0
- package/src/theme/history-list-panel.scss +557 -0
- package/src/theme/page-bar.scss +0 -4
- package/src/theme/props-panel.scss +3 -3
- package/src/theme/style-setter/border.scss +4 -1
- package/src/theme/theme.scss +1 -0
- package/src/type.ts +594 -31
- package/src/utils/code-block.ts +150 -0
- package/src/utils/content-menu.ts +25 -13
- package/src/utils/data-source/index.ts +2 -0
- package/src/utils/dep/worker.ts +5 -3
- package/src/utils/editor.ts +166 -60
- package/src/utils/history.ts +286 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/indexed-db.ts +122 -0
- package/src/utils/undo-redo.ts +111 -0
- package/types/index.d.ts +3171 -889
- package/dist/es/utils/compose.js +0 -44
- package/dist/es/utils/editor-history.js +0 -100
- package/src/utils/compose.ts +0 -52
- package/src/utils/editor-history.ts +0 -138
|
@@ -1,35 +1,28 @@
|
|
|
1
1
|
import { Layout, canUsePluginMethods } from "../type.js";
|
|
2
|
-
import
|
|
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 { applyHistoryAddOp, applyHistoryRemoveOp, applyHistoryUpdateOp } from "../utils/editor-history.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";
|
|
8
8
|
import { beforePaste, getAddParent } from "../utils/operator.js";
|
|
9
9
|
import { isFixed } from "@tmagic/stage";
|
|
10
|
-
import { getNodeInfo, getNodePath, isPage, isPageFragment } from "@tmagic/utils";
|
|
10
|
+
import { getNodeInfo, getNodePath, getValueByKeyPath, guid, isPage, isPageFragment, setValueByKeyPath } from "@tmagic/utils";
|
|
11
11
|
import { NodeType } from "@tmagic/core";
|
|
12
12
|
import { reactive, toRaw } from "vue";
|
|
13
|
-
import { cloneDeep, isObject, mergeWith, uniq } from "lodash-es";
|
|
13
|
+
import { cloneDeep as cloneDeep$1, isEmpty, isEqual, isObject, mergeWith, uniq } from "lodash-es";
|
|
14
14
|
//#region packages/editor/src/services/editor.ts
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* 把「变更前后节点快照」列表归一成 update 类型的 {@link StepDiffItem} 列表,供 {@link StepValue.diff} 使用。
|
|
17
|
+
* `changeRecords` 来自 form 端的 propPath/value 列表,撤销/重做时只对这些 propPath 做局部更新;
|
|
18
|
+
* 缺省(未传 / 空数组)才退化为整节点替换。
|
|
18
19
|
*/
|
|
19
|
-
var
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
* 经过 BaseService 的插件 / 中间件包装后,源方法的形参可能被注入为 dispatch 函数
|
|
26
|
-
* 当 parent 形参位置被注入为函数(或为空值)时,归一为 null,由调用方继续走默认 parent 逻辑
|
|
27
|
-
*/
|
|
28
|
-
var safeParent = (parent) => {
|
|
29
|
-
if (!parent || typeof parent === "function") return null;
|
|
30
|
-
return parent;
|
|
31
|
-
};
|
|
32
|
-
var Editor = class extends BaseService_default {
|
|
20
|
+
var buildUpdateDiff = (items) => items.map(({ oldNode, newNode, changeRecords }) => ({
|
|
21
|
+
oldSchema: oldNode,
|
|
22
|
+
newSchema: newNode,
|
|
23
|
+
...changeRecords?.length ? { changeRecords } : {}
|
|
24
|
+
}));
|
|
25
|
+
var Editor = class extends BaseService {
|
|
33
26
|
state = reactive({
|
|
34
27
|
root: null,
|
|
35
28
|
page: null,
|
|
@@ -45,8 +38,13 @@ var Editor = class extends BaseService_default {
|
|
|
45
38
|
disabledMultiSelect: false,
|
|
46
39
|
alwaysMultiSelect: false
|
|
47
40
|
});
|
|
48
|
-
isHistoryStateChange = false;
|
|
49
41
|
selectionBeforeOp = null;
|
|
42
|
+
/**
|
|
43
|
+
* 最近一次 pushOpHistory 写入的历史记录 uuid。
|
|
44
|
+
* 供 *AndGetHistoryId 系列方法在调用普通操作后取回本次产生的历史记录 id;
|
|
45
|
+
* 普通操作不会读取它,调用前由 *AndGetHistoryId 重置为 null。
|
|
46
|
+
*/
|
|
47
|
+
lastPushedHistoryId = null;
|
|
50
48
|
constructor() {
|
|
51
49
|
super(canUsePluginMethods.async.map((methodName) => ({
|
|
52
50
|
name: methodName,
|
|
@@ -61,8 +59,9 @@ var Editor = class extends BaseService_default {
|
|
|
61
59
|
* 设置当前指点节点配置
|
|
62
60
|
* @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength' | 'pageFragmentLength
|
|
63
61
|
* @param value MNode
|
|
62
|
+
* @param options.historySource 设置 root 时,本次变更写入历史记录的「操作来源」(仅 name === 'root' 时生效)
|
|
64
63
|
*/
|
|
65
|
-
set(name, value) {
|
|
64
|
+
set(name, value, options = {}) {
|
|
66
65
|
const preValue = this.state[name];
|
|
67
66
|
this.state[name] = value;
|
|
68
67
|
if (name === "nodes" && Array.isArray(value)) this.set("node", value[0]);
|
|
@@ -73,12 +72,19 @@ var Editor = class extends BaseService_default {
|
|
|
73
72
|
this.state.pageLength = getPageList(app).length || 0;
|
|
74
73
|
this.state.pageFragmentLength = getPageFragmentList(app).length || 0;
|
|
75
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.getMarker("page", pageNode.id)) history_default.setMarker("page", pageNode.id, {
|
|
78
|
+
name: pageNode.name,
|
|
79
|
+
source: options.historySource
|
|
80
|
+
});
|
|
81
|
+
});
|
|
76
82
|
} else {
|
|
77
83
|
this.state.pageLength = 0;
|
|
78
84
|
this.state.pageFragmentLength = 0;
|
|
79
85
|
this.state.stageLoading = false;
|
|
80
86
|
}
|
|
81
|
-
this.emit("root-change", value, preValue);
|
|
87
|
+
this.emit("root-change", value, preValue, options);
|
|
82
88
|
}
|
|
83
89
|
}
|
|
84
90
|
/**
|
|
@@ -98,7 +104,25 @@ var Editor = class extends BaseService_default {
|
|
|
98
104
|
getNodeInfo(id, raw = true) {
|
|
99
105
|
let root = this.get("root");
|
|
100
106
|
if (raw) root = toRaw(root);
|
|
101
|
-
|
|
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);
|
|
102
126
|
}
|
|
103
127
|
/**
|
|
104
128
|
* 根据ID获取指点节点配置
|
|
@@ -137,7 +161,7 @@ var Editor = class extends BaseService_default {
|
|
|
137
161
|
* 只有容器拥有布局
|
|
138
162
|
*/
|
|
139
163
|
async getLayout(parent, node) {
|
|
140
|
-
if (node &&
|
|
164
|
+
if (node && isFixed(node.style || {})) return Layout.FIXED;
|
|
141
165
|
if (parent.layout) return parent.layout;
|
|
142
166
|
if (!parent.style?.position) return Layout.RELATIVE;
|
|
143
167
|
return Layout.ABSOLUTE;
|
|
@@ -152,8 +176,6 @@ var Editor = class extends BaseService_default {
|
|
|
152
176
|
this.set("nodes", node ? [node] : []);
|
|
153
177
|
this.set("page", page);
|
|
154
178
|
this.set("parent", parent);
|
|
155
|
-
if (page) history_default.changePage(toRaw(page));
|
|
156
|
-
else history_default.resetState();
|
|
157
179
|
if (node?.id) this.get("stage")?.renderer?.runtime?.getApp?.()?.page?.emit("editor:select", {
|
|
158
180
|
node,
|
|
159
181
|
page,
|
|
@@ -231,18 +253,18 @@ var Editor = class extends BaseService_default {
|
|
|
231
253
|
const layout = await this.getLayout(toRaw(parent), node);
|
|
232
254
|
node.style = getInitPositionStyle(node.style, layout);
|
|
233
255
|
await stage?.add({
|
|
234
|
-
config: cloneDeep(node),
|
|
235
|
-
parent: cloneDeep(parent),
|
|
256
|
+
config: cloneDeep$1(node),
|
|
257
|
+
parent: cloneDeep$1(parent),
|
|
236
258
|
parentId: parent.id,
|
|
237
|
-
root: cloneDeep(root)
|
|
259
|
+
root: cloneDeep$1(root)
|
|
238
260
|
});
|
|
239
261
|
const newStyle = fixNodePosition(node, parent, stage);
|
|
240
262
|
if (newStyle && (newStyle.top !== node.style.top || newStyle.left !== node.style.left)) {
|
|
241
263
|
node.style = newStyle;
|
|
242
264
|
await stage?.update({
|
|
243
|
-
config: cloneDeep(node),
|
|
265
|
+
config: cloneDeep$1(node),
|
|
244
266
|
parentId: parent.id,
|
|
245
|
-
root: cloneDeep(root)
|
|
267
|
+
root: cloneDeep$1(root)
|
|
246
268
|
});
|
|
247
269
|
}
|
|
248
270
|
this.addModifiedNodeId(node.id);
|
|
@@ -255,11 +277,10 @@ var Editor = class extends BaseService_default {
|
|
|
255
277
|
* @param options 可选配置
|
|
256
278
|
* @param options.doNotSelect 添加后是否不更新当前选中节点(默认 false,添加后会选中新增的节点)
|
|
257
279
|
* @param options.doNotSwitchPage 添加后是否不切换当前页面(默认 false;新增页面 / 跨页新增时为 true 会跳过会引发页面切换的选中操作)
|
|
280
|
+
* @param options.doNotPushHistory 是否不写入历史记录(默认 false)
|
|
258
281
|
* @returns 添加后的节点
|
|
259
282
|
*/
|
|
260
|
-
async add(addNode, parent,
|
|
261
|
-
const safeParentNode = safeParent(parent);
|
|
262
|
-
const { doNotSelect = false, doNotSwitchPage = false } = safeOptions(options);
|
|
283
|
+
async add(addNode, parent, { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false, historyDescription, historySource } = {}) {
|
|
263
284
|
this.captureSelectionBeforeOp();
|
|
264
285
|
const stage = this.get("stage");
|
|
265
286
|
const addNodes = [];
|
|
@@ -271,7 +292,7 @@ var Editor = class extends BaseService_default {
|
|
|
271
292
|
const newNodes = await Promise.all(addNodes.map((node) => {
|
|
272
293
|
const root = this.get("root");
|
|
273
294
|
if ((isPage(node) || isPageFragment(node)) && root) return this.doAdd(node, root);
|
|
274
|
-
const parentNode =
|
|
295
|
+
const parentNode = parent ?? getAddParent(node);
|
|
275
296
|
if (!parentNode) throw new Error("未找到父元素");
|
|
276
297
|
return this.doAdd(node, parentNode);
|
|
277
298
|
}));
|
|
@@ -292,23 +313,38 @@ var Editor = class extends BaseService_default {
|
|
|
292
313
|
}
|
|
293
314
|
if (!(isPage(newNodes[0]) || isPageFragment(newNodes[0]))) {
|
|
294
315
|
const pageForOp = this.getNodeInfo(newNodes[0].id, false).page;
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
316
|
+
if (!doNotPushHistory) {
|
|
317
|
+
const parentId = (this.getParentById(newNodes[0].id, false) ?? this.get("root")).id;
|
|
318
|
+
this.pushOpHistory("add", {
|
|
319
|
+
diff: newNodes.map((n) => {
|
|
320
|
+
const p = this.getParentById(n.id, false);
|
|
321
|
+
const idx = p ? getNodeIndex(n.id, p) : -1;
|
|
322
|
+
return {
|
|
323
|
+
newSchema: cloneDeep$1(toRaw(n)),
|
|
324
|
+
parentId,
|
|
325
|
+
index: typeof idx === "number" ? idx : -1
|
|
326
|
+
};
|
|
327
|
+
}),
|
|
328
|
+
pageData: {
|
|
329
|
+
name: pageForOp?.name || "",
|
|
330
|
+
id: pageForOp.id
|
|
331
|
+
},
|
|
332
|
+
historyDescription,
|
|
333
|
+
source: historySource
|
|
334
|
+
});
|
|
335
|
+
} else this.selectionBeforeOp = null;
|
|
306
336
|
}
|
|
307
337
|
this.emit("add", newNodes);
|
|
338
|
+
this.emit("change", {
|
|
339
|
+
type: "add",
|
|
340
|
+
data: newNodes.map((node) => ({
|
|
341
|
+
node,
|
|
342
|
+
page: this.getPageOfNode(node.id)
|
|
343
|
+
}))
|
|
344
|
+
});
|
|
308
345
|
return Array.isArray(addNode) ? newNodes : newNodes[0];
|
|
309
346
|
}
|
|
310
|
-
async doRemove(node,
|
|
311
|
-
const { doNotSelect = false, doNotSwitchPage = false } = safeOptions(options);
|
|
347
|
+
async doRemove(node, { doNotSelect = false, doNotSwitchPage = false } = {}) {
|
|
312
348
|
const root = this.get("root");
|
|
313
349
|
if (!root) throw new Error("root不能为空");
|
|
314
350
|
const { parent, node: curNode } = this.getNodeInfo(node.id, false);
|
|
@@ -320,7 +356,7 @@ var Editor = class extends BaseService_default {
|
|
|
320
356
|
stage?.remove({
|
|
321
357
|
id: node.id,
|
|
322
358
|
parentId: parent.id,
|
|
323
|
-
root: cloneDeep(root)
|
|
359
|
+
root: cloneDeep$1(root)
|
|
324
360
|
});
|
|
325
361
|
const selectedNodes = this.get("nodes");
|
|
326
362
|
const removedSelectedIndex = selectedNodes.findIndex((n) => `${n.id}` === `${node.id}`);
|
|
@@ -337,10 +373,7 @@ var Editor = class extends BaseService_default {
|
|
|
337
373
|
if (pages[0]) {
|
|
338
374
|
await this.select(pages[0]);
|
|
339
375
|
stage?.select(pages[0].id);
|
|
340
|
-
} else
|
|
341
|
-
this.selectRoot();
|
|
342
|
-
history_default.resetPage();
|
|
343
|
-
}
|
|
376
|
+
} else this.selectRoot();
|
|
344
377
|
};
|
|
345
378
|
const rootItems = root.items || [];
|
|
346
379
|
if (isPage(node)) {
|
|
@@ -367,11 +400,15 @@ var Editor = class extends BaseService_default {
|
|
|
367
400
|
* @param options 可选配置
|
|
368
401
|
* @param options.doNotSelect 删除后是否不更新当前选中节点(默认 false,删除后会选中父节点或首个页面)
|
|
369
402
|
* @param options.doNotSwitchPage 删除后是否不切换当前页面(默认 false;删除页面 / 页面片段时为 true 会跳过自动切换到首个剩余页面)
|
|
403
|
+
* @param options.doNotPushHistory 是否不写入历史记录(默认 false)
|
|
370
404
|
*/
|
|
371
|
-
async remove(nodeOrNodeList,
|
|
372
|
-
const { doNotSelect = false, doNotSwitchPage = false } = safeOptions(options);
|
|
405
|
+
async remove(nodeOrNodeList, { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false, historyDescription, historySource } = {}) {
|
|
373
406
|
this.captureSelectionBeforeOp();
|
|
374
407
|
const nodes = Array.isArray(nodeOrNodeList) ? nodeOrNodeList : [nodeOrNodeList];
|
|
408
|
+
const changeItems = nodes.map((node) => ({
|
|
409
|
+
node,
|
|
410
|
+
page: this.getPageOfNode(node.id)
|
|
411
|
+
}));
|
|
375
412
|
const removedItems = [];
|
|
376
413
|
let pageForOp = null;
|
|
377
414
|
if (!(isPage(nodes[0]) || isPageFragment(nodes[0]))) for (const n of nodes) {
|
|
@@ -383,7 +420,7 @@ var Editor = class extends BaseService_default {
|
|
|
383
420
|
};
|
|
384
421
|
const idx = getNodeIndex(curNode.id, parent);
|
|
385
422
|
removedItems.push({
|
|
386
|
-
|
|
423
|
+
oldSchema: cloneDeep$1(toRaw(curNode)),
|
|
387
424
|
parentId: parent.id,
|
|
388
425
|
index: typeof idx === "number" ? idx : -1
|
|
389
426
|
});
|
|
@@ -393,21 +430,30 @@ var Editor = class extends BaseService_default {
|
|
|
393
430
|
doNotSelect,
|
|
394
431
|
doNotSwitchPage
|
|
395
432
|
})));
|
|
396
|
-
if (removedItems.length > 0 && pageForOp) this.pushOpHistory("remove", {
|
|
433
|
+
if (removedItems.length > 0 && pageForOp) if (!doNotPushHistory) this.pushOpHistory("remove", {
|
|
434
|
+
diff: removedItems,
|
|
435
|
+
pageData: pageForOp,
|
|
436
|
+
historyDescription,
|
|
437
|
+
source: historySource
|
|
438
|
+
});
|
|
439
|
+
else this.selectionBeforeOp = null;
|
|
397
440
|
this.emit("remove", nodes);
|
|
441
|
+
this.emit("change", {
|
|
442
|
+
type: "remove",
|
|
443
|
+
data: changeItems
|
|
444
|
+
});
|
|
398
445
|
}
|
|
399
|
-
async doUpdate(config, { changeRecords = [] } = {}) {
|
|
400
|
-
|
|
401
|
-
if (!root) throw new Error("root为空");
|
|
446
|
+
async doUpdate(config, { changeRecords = [], historySource } = {}) {
|
|
447
|
+
if (!this.get("root")) throw new Error("root为空");
|
|
402
448
|
if (!config?.id) throw new Error("没有配置或者配置缺少id值");
|
|
403
449
|
const info = this.getNodeInfo(config.id, false);
|
|
404
450
|
if (!info.node) throw new Error(`获取不到id为${config.id}的节点`);
|
|
405
451
|
const node = toRaw(info.node);
|
|
406
|
-
let newConfig = await toggleFixedPosition(toRaw(config), node,
|
|
407
|
-
newConfig = mergeWith(cloneDeep(node), newConfig, editorNodeMergeCustomizer);
|
|
452
|
+
let newConfig = await toggleFixedPosition(toRaw(config), node, info.path, this.getLayout);
|
|
453
|
+
newConfig = mergeWith(cloneDeep$1(node), newConfig, editorNodeMergeCustomizer);
|
|
408
454
|
if (!newConfig.type) throw new Error("配置缺少type值");
|
|
409
455
|
if (newConfig.type === NodeType.ROOT) {
|
|
410
|
-
this.set("root", newConfig);
|
|
456
|
+
this.set("root", newConfig, { historySource });
|
|
411
457
|
return {
|
|
412
458
|
oldNode: node,
|
|
413
459
|
newNode: newConfig,
|
|
@@ -444,27 +490,50 @@ var Editor = class extends BaseService_default {
|
|
|
444
490
|
* 更新节点
|
|
445
491
|
* update后会触发依赖收集,收集完后会掉stage.update方法
|
|
446
492
|
* @param config 新的节点配置,配置中需要有id信息
|
|
493
|
+
* @param data 额外数据
|
|
494
|
+
* @param data.changeRecords 单节点 form 端变更记录(多节点场景下被忽略,使用 changeRecordList)
|
|
495
|
+
* @param data.changeRecordList 多节点 form 端变更记录列表,按 config 数组同序对应每个节点;优先级高于 changeRecords
|
|
496
|
+
* @param data.doNotPushHistory 是否不写入历史记录(默认 false)
|
|
497
|
+
* @param data.historyDescription 入栈时附带的人类可读描述,用于历史面板展示(不影响 undo/redo 行为)
|
|
447
498
|
* @returns 更新后的节点配置
|
|
448
499
|
*/
|
|
449
500
|
async update(config, data = {}) {
|
|
450
501
|
this.captureSelectionBeforeOp();
|
|
502
|
+
const { doNotPushHistory = false, changeRecordList, changeRecords, historyDescription, historySource } = data;
|
|
451
503
|
const nodes = Array.isArray(config) ? config : [config];
|
|
452
|
-
const updateData = await Promise.all(nodes.map((node) =>
|
|
504
|
+
const updateData = await Promise.all(nodes.map((node, index) => {
|
|
505
|
+
const recordsForNode = changeRecordList ? changeRecordList[index] ?? [] : changeRecords ?? [];
|
|
506
|
+
return this.doUpdate(node, {
|
|
507
|
+
changeRecords: recordsForNode,
|
|
508
|
+
historySource
|
|
509
|
+
});
|
|
510
|
+
}));
|
|
453
511
|
if (updateData[0].oldNode?.type !== NodeType.ROOT) {
|
|
454
|
-
|
|
455
|
-
if (!this.isHistoryStateChange && curNodes.length) {
|
|
512
|
+
if (this.get("nodes").length) if (!doNotPushHistory) {
|
|
456
513
|
const pageForOp = this.getNodeInfo(nodes[0].id, false).page;
|
|
457
|
-
this.pushOpHistory("update", {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
514
|
+
this.pushOpHistory("update", {
|
|
515
|
+
diff: buildUpdateDiff(updateData.map((d) => ({
|
|
516
|
+
oldNode: cloneDeep$1(d.oldNode),
|
|
517
|
+
newNode: cloneDeep$1(d.newNode),
|
|
518
|
+
changeRecords: d.changeRecords?.length ? cloneDeep$1(d.changeRecords) : void 0
|
|
519
|
+
}))),
|
|
520
|
+
pageData: {
|
|
521
|
+
name: pageForOp?.name || "",
|
|
522
|
+
id: pageForOp.id
|
|
523
|
+
},
|
|
524
|
+
historyDescription,
|
|
525
|
+
source: historySource
|
|
463
526
|
});
|
|
464
|
-
}
|
|
465
|
-
this.isHistoryStateChange = false;
|
|
527
|
+
} else this.selectionBeforeOp = null;
|
|
466
528
|
}
|
|
467
529
|
this.emit("update", updateData);
|
|
530
|
+
this.emit("change", {
|
|
531
|
+
type: "update",
|
|
532
|
+
data: updateData.map((node) => ({
|
|
533
|
+
node,
|
|
534
|
+
page: this.getPageOfNode(node.newNode.id)
|
|
535
|
+
}))
|
|
536
|
+
});
|
|
468
537
|
return Array.isArray(config) ? updateData.map((item) => item.newNode) : updateData[0].newNode;
|
|
469
538
|
}
|
|
470
539
|
/**
|
|
@@ -474,27 +543,30 @@ var Editor = class extends BaseService_default {
|
|
|
474
543
|
* @param options 可选配置
|
|
475
544
|
* @param options.doNotSelect 排序后是否不更新当前选中节点(默认 false)
|
|
476
545
|
* @param options.doNotSwitchPage 排序后是否不切换当前页面(排序只发生在同一父节点内,方法内为空操作;保留以与其它 DSL 操作 API 一致)
|
|
546
|
+
* @param options.doNotPushHistory 是否不写入历史记录(默认 false)
|
|
477
547
|
* @returns void
|
|
478
548
|
*/
|
|
479
|
-
async sort(id1, id2,
|
|
480
|
-
const { doNotSelect = false } = safeOptions(options);
|
|
549
|
+
async sort(id1, id2, { doNotSelect = false, doNotPushHistory = false, historySource } = {}) {
|
|
481
550
|
this.captureSelectionBeforeOp();
|
|
482
551
|
const root = this.get("root");
|
|
483
552
|
if (!root) throw new Error("root为空");
|
|
484
553
|
const node = this.get("node");
|
|
485
554
|
if (!node) throw new Error("当前节点为空");
|
|
486
|
-
const parent = cloneDeep(toRaw(this.get("parent")));
|
|
555
|
+
const parent = cloneDeep$1(toRaw(this.get("parent")));
|
|
487
556
|
if (!parent) throw new Error("父节点为空");
|
|
488
557
|
const index2 = parent.items.findIndex((node) => `${node.id}` === `${id2}`);
|
|
489
558
|
if (index2 < 0) return;
|
|
490
559
|
const index1 = parent.items.findIndex((node) => `${node.id}` === `${id1}`);
|
|
491
560
|
parent.items.splice(index2, 0, ...parent.items.splice(index1, 1));
|
|
492
|
-
await this.update(parent
|
|
561
|
+
await this.update(parent, {
|
|
562
|
+
doNotPushHistory,
|
|
563
|
+
historySource
|
|
564
|
+
});
|
|
493
565
|
if (!doNotSelect) await this.select(node);
|
|
494
566
|
this.get("stage")?.update({
|
|
495
|
-
config: cloneDeep(node),
|
|
567
|
+
config: cloneDeep$1(node),
|
|
496
568
|
parentId: parent.id,
|
|
497
|
-
root: cloneDeep(root)
|
|
569
|
+
root: cloneDeep$1(root)
|
|
498
570
|
});
|
|
499
571
|
}
|
|
500
572
|
/**
|
|
@@ -522,10 +594,10 @@ var Editor = class extends BaseService_default {
|
|
|
522
594
|
* @param options 可选配置
|
|
523
595
|
* @param options.doNotSelect 粘贴后是否不更新当前选中节点(默认 false)
|
|
524
596
|
* @param options.doNotSwitchPage 粘贴后是否不切换当前页面(默认 false;跨页粘贴时为 true 会跳过页面切换)
|
|
597
|
+
* @param options.doNotPushHistory 是否不写入历史记录(默认 false)
|
|
525
598
|
* @returns 添加后的组件节点配置
|
|
526
599
|
*/
|
|
527
|
-
async paste(position = {}, collectorOptions,
|
|
528
|
-
const { doNotSelect = false, doNotSwitchPage = false } = safeOptions(options);
|
|
600
|
+
async paste(position = {}, collectorOptions, { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false, historyDescription, historySource } = {}) {
|
|
529
601
|
const config = storage_default.getItem(COPY_STORAGE_KEY);
|
|
530
602
|
if (!Array.isArray(config)) return;
|
|
531
603
|
const node = this.get("node");
|
|
@@ -538,18 +610,21 @@ var Editor = class extends BaseService_default {
|
|
|
538
610
|
if (collectorOptions && typeof collectorOptions.isTarget === "function") props_default.replaceRelateId(config, pasteConfigs, collectorOptions);
|
|
539
611
|
return this.add(pasteConfigs, parent, {
|
|
540
612
|
doNotSelect,
|
|
541
|
-
doNotSwitchPage
|
|
613
|
+
doNotSwitchPage,
|
|
614
|
+
doNotPushHistory,
|
|
615
|
+
historyDescription,
|
|
616
|
+
historySource
|
|
542
617
|
});
|
|
543
618
|
}
|
|
544
619
|
async doPaste(config, position = {}) {
|
|
545
620
|
props_default.clearRelateId();
|
|
546
621
|
const doc = this.get("stage")?.renderer?.contentWindow?.document;
|
|
547
|
-
return beforePaste(position, cloneDeep(config), doc);
|
|
622
|
+
return beforePaste(position, cloneDeep$1(config), doc);
|
|
548
623
|
}
|
|
549
624
|
async doAlignCenter(config) {
|
|
550
625
|
const parent = this.getParentById(config.id);
|
|
551
626
|
if (!parent) throw new Error("找不到父节点");
|
|
552
|
-
const node = cloneDeep(toRaw(config));
|
|
627
|
+
const node = cloneDeep$1(toRaw(config));
|
|
553
628
|
const layout = await this.getLayout(parent, node);
|
|
554
629
|
const doc = this.get("stage")?.renderer?.contentWindow?.document;
|
|
555
630
|
const newStyle = calcAlignCenterStyle(node, parent, layout, doc);
|
|
@@ -563,14 +638,18 @@ var Editor = class extends BaseService_default {
|
|
|
563
638
|
* @param options 可选配置
|
|
564
639
|
* @param options.doNotSelect 居中后是否不更新当前选中节点(默认 false)
|
|
565
640
|
* @param options.doNotSwitchPage 居中后是否不切换当前页面(居中只更新节点 style,方法内为空操作;保留以与其它 DSL 操作 API 一致)
|
|
641
|
+
* @param options.doNotPushHistory 是否不写入历史记录(默认 false)
|
|
566
642
|
* @returns 当前组件节点配置
|
|
567
643
|
*/
|
|
568
|
-
async alignCenter(config,
|
|
569
|
-
const { doNotSelect = false } = safeOptions(options);
|
|
644
|
+
async alignCenter(config, { doNotSelect = false, doNotPushHistory = false, historyDescription, historySource } = {}) {
|
|
570
645
|
const nodes = Array.isArray(config) ? config : [config];
|
|
571
646
|
const stage = this.get("stage");
|
|
572
647
|
const newNodes = await Promise.all(nodes.map((node) => this.doAlignCenter(node)));
|
|
573
|
-
const newNode = await this.update(newNodes
|
|
648
|
+
const newNode = await this.update(newNodes, {
|
|
649
|
+
doNotPushHistory,
|
|
650
|
+
historyDescription,
|
|
651
|
+
historySource
|
|
652
|
+
});
|
|
574
653
|
if (!doNotSelect) if (newNodes.length > 1) await stage?.multiSelect(newNodes.map((node) => node.id));
|
|
575
654
|
else await stage?.select(newNodes[0].id);
|
|
576
655
|
return newNode;
|
|
@@ -578,8 +657,10 @@ var Editor = class extends BaseService_default {
|
|
|
578
657
|
/**
|
|
579
658
|
* 移动当前选中节点位置
|
|
580
659
|
* @param offset 偏移量
|
|
660
|
+
* @param options 可选配置
|
|
661
|
+
* @param options.doNotPushHistory 是否不写入历史记录(默认 false)
|
|
581
662
|
*/
|
|
582
|
-
async moveLayer(offset) {
|
|
663
|
+
async moveLayer(offset, { doNotPushHistory = false, historyDescription, historySource } = {}) {
|
|
583
664
|
this.captureSelectionBeforeOp();
|
|
584
665
|
const root = this.get("root");
|
|
585
666
|
if (!root) throw new Error("root为空");
|
|
@@ -592,95 +673,122 @@ var Editor = class extends BaseService_default {
|
|
|
592
673
|
const isRelative = await this.getLayout(parent, node) === Layout.RELATIVE;
|
|
593
674
|
const offsetIndex = calcLayerTargetIndex(index, offset, brothers.length, isRelative);
|
|
594
675
|
if (offsetIndex > 0 && offsetIndex > brothers.length || offsetIndex < 0) return;
|
|
595
|
-
const oldParent = cloneDeep(toRaw(parent));
|
|
676
|
+
const oldParent = cloneDeep$1(toRaw(parent));
|
|
596
677
|
brothers.splice(index, 1);
|
|
597
678
|
brothers.splice(offsetIndex, 0, node);
|
|
598
679
|
const grandparent = this.getParentById(parent.id);
|
|
599
680
|
this.get("stage")?.update({
|
|
600
|
-
config: cloneDeep(toRaw(parent)),
|
|
681
|
+
config: cloneDeep$1(toRaw(parent)),
|
|
601
682
|
parentId: grandparent?.id,
|
|
602
|
-
root: cloneDeep(root)
|
|
683
|
+
root: cloneDeep$1(root)
|
|
603
684
|
});
|
|
604
685
|
this.addModifiedNodeId(parent.id);
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
686
|
+
if (!doNotPushHistory) {
|
|
687
|
+
const pageForOp = this.getNodeInfo(node.id, false).page;
|
|
688
|
+
this.pushOpHistory("update", {
|
|
689
|
+
diff: buildUpdateDiff([{
|
|
690
|
+
oldNode: oldParent,
|
|
691
|
+
newNode: cloneDeep$1(toRaw(parent))
|
|
692
|
+
}]),
|
|
693
|
+
pageData: {
|
|
694
|
+
name: pageForOp?.name || "",
|
|
695
|
+
id: pageForOp.id
|
|
696
|
+
},
|
|
697
|
+
historyDescription,
|
|
698
|
+
source: historySource
|
|
699
|
+
});
|
|
700
|
+
} else this.selectionBeforeOp = null;
|
|
613
701
|
this.emit("move-layer", offset);
|
|
702
|
+
this.emit("change", {
|
|
703
|
+
type: "move-layer",
|
|
704
|
+
data: [{
|
|
705
|
+
node,
|
|
706
|
+
page: this.getPageOfNode(node.id)
|
|
707
|
+
}],
|
|
708
|
+
offset
|
|
709
|
+
});
|
|
614
710
|
}
|
|
615
711
|
/**
|
|
616
|
-
*
|
|
617
|
-
*
|
|
712
|
+
* 移动一个或多个节点到指定容器中。
|
|
713
|
+
*
|
|
714
|
+
* 多选场景(config 是数组)只会产生一条历史记录,
|
|
715
|
+
* `updatedItems` 涵盖所有源父容器 + 目标容器的前后快照。
|
|
716
|
+
* 这避免了"多选移动到某容器"在历史栈里被切成 N 条记录。
|
|
717
|
+
*
|
|
718
|
+
* @param config 需要移动的节点(或节点数组,各项需带 id;style 等字段会与原节点合并)
|
|
618
719
|
* @param targetId 容器ID
|
|
619
720
|
* @param options 可选配置
|
|
620
721
|
* @param options.doNotSelect 移动后是否不更新当前选中节点(默认 false)
|
|
621
722
|
* @param options.doNotSwitchPage 移动后是否不切换当前页面(默认 false;目标容器位于其它页面时为 true 会跳过自动选中以避免页面切换)
|
|
723
|
+
* @param options.doNotPushHistory 是否不写入历史记录(默认 false)
|
|
622
724
|
*/
|
|
623
|
-
async moveToContainer(config, targetId,
|
|
624
|
-
const
|
|
725
|
+
async moveToContainer(config, targetId, { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false, historyDescription, historySource } = {}) {
|
|
726
|
+
const isBatch = Array.isArray(config);
|
|
727
|
+
const configs = (isBatch ? config : [config]).filter((item) => !(isPage(item) || isPageFragment(item)));
|
|
728
|
+
if (configs.length === 0) throw new Error("没有可移动的节点");
|
|
625
729
|
this.captureSelectionBeforeOp();
|
|
626
|
-
const root = this.get("root");
|
|
627
|
-
const { node, parent, page: pageForOp } = this.getNodeInfo(config.id, false);
|
|
628
730
|
const target = this.getNodeById(targetId, false);
|
|
731
|
+
if (!target) throw new Error("目标容器不存在");
|
|
732
|
+
const root = this.get("root");
|
|
629
733
|
const stage = this.get("stage");
|
|
630
|
-
if (root
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
const
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
if (Array.isArray(srcValue)) return srcValue;
|
|
643
|
-
});
|
|
644
|
-
newConfig.style = getInitPositionStyle(newConfig.style, layout);
|
|
645
|
-
target.items.push(newConfig);
|
|
646
|
-
const targetWouldSwitchPage = this.isOnDifferentPage(target);
|
|
647
|
-
const skipSelect = doNotSelect || doNotSwitchPage && targetWouldSwitchPage;
|
|
648
|
-
if (!skipSelect) await stage.select(targetId);
|
|
649
|
-
const targetParent = this.getParentById(target.id);
|
|
650
|
-
await stage.update({
|
|
651
|
-
config: cloneDeep(target),
|
|
652
|
-
parentId: targetParent?.id,
|
|
653
|
-
root: cloneDeep(root)
|
|
654
|
-
});
|
|
655
|
-
if (!skipSelect) {
|
|
656
|
-
await this.select(newConfig);
|
|
657
|
-
stage.select(newConfig.id);
|
|
658
|
-
}
|
|
659
|
-
this.addModifiedNodeId(target.id);
|
|
660
|
-
this.addModifiedNodeId(parent.id);
|
|
661
|
-
this.pushOpHistory("update", { updatedItems: [{
|
|
662
|
-
oldNode: oldSourceParent,
|
|
663
|
-
newNode: cloneDeep(toRaw(parent))
|
|
664
|
-
}, {
|
|
665
|
-
oldNode: oldTarget,
|
|
666
|
-
newNode: cloneDeep(toRaw(target))
|
|
667
|
-
}] }, {
|
|
668
|
-
name: pageForOp?.name || "",
|
|
669
|
-
id: pageForOp.id
|
|
734
|
+
if (!root || !stage) throw new Error("root 或 stage为空");
|
|
735
|
+
const moves = [];
|
|
736
|
+
for (const { id } of configs) {
|
|
737
|
+
const { node, parent, page } = this.getNodeInfo(id, false);
|
|
738
|
+
if (!node || !parent) continue;
|
|
739
|
+
moves.push({
|
|
740
|
+
node,
|
|
741
|
+
parent,
|
|
742
|
+
pageForOp: page ? {
|
|
743
|
+
name: page.name || "",
|
|
744
|
+
id: page.id
|
|
745
|
+
} : null
|
|
670
746
|
});
|
|
671
|
-
return newConfig;
|
|
672
747
|
}
|
|
748
|
+
if (moves.length === 0) throw new Error("没有可移动的节点");
|
|
749
|
+
const beforeSnapshots = /* @__PURE__ */ new Map();
|
|
750
|
+
beforeSnapshots.set(target.id, cloneDeep$1(toRaw(target)));
|
|
751
|
+
for (const { parent } of moves) if (!beforeSnapshots.has(parent.id)) beforeSnapshots.set(parent.id, cloneDeep$1(toRaw(parent)));
|
|
752
|
+
let newConfigs = [];
|
|
753
|
+
const moveNodes = moves.map(({ node }) => node);
|
|
754
|
+
await this.remove(moveNodes, {
|
|
755
|
+
doNotPushHistory: true,
|
|
756
|
+
doNotSelect,
|
|
757
|
+
doNotSwitchPage: true
|
|
758
|
+
});
|
|
759
|
+
newConfigs = await this.add(moveNodes, target, {
|
|
760
|
+
doNotPushHistory: true,
|
|
761
|
+
doNotSelect,
|
|
762
|
+
doNotSwitchPage
|
|
763
|
+
});
|
|
764
|
+
if (!doNotPushHistory) {
|
|
765
|
+
const updatedItems = Array.from(beforeSnapshots.entries()).map(([id, oldNode]) => ({
|
|
766
|
+
oldNode,
|
|
767
|
+
newNode: cloneDeep$1(toRaw(this.getNodeById(id, false)))
|
|
768
|
+
}));
|
|
769
|
+
const historyPage = moves[0].pageForOp ?? {
|
|
770
|
+
name: "",
|
|
771
|
+
id: target.id
|
|
772
|
+
};
|
|
773
|
+
this.pushOpHistory("update", {
|
|
774
|
+
diff: buildUpdateDiff(updatedItems),
|
|
775
|
+
pageData: historyPage,
|
|
776
|
+
historyDescription,
|
|
777
|
+
source: historySource
|
|
778
|
+
});
|
|
779
|
+
} else this.selectionBeforeOp = null;
|
|
780
|
+
return isBatch ? newConfigs : newConfigs[0];
|
|
673
781
|
}
|
|
674
|
-
async dragTo(config, targetParent, targetIndex) {
|
|
782
|
+
async dragTo(config, targetParent, targetIndex, { doNotPushHistory = false, historyDescription, historySource } = {}) {
|
|
675
783
|
this.captureSelectionBeforeOp();
|
|
676
784
|
if (!targetParent || !Array.isArray(targetParent.items)) return;
|
|
677
785
|
const configs = Array.isArray(config) ? config : [config];
|
|
678
786
|
const beforeSnapshots = /* @__PURE__ */ new Map();
|
|
679
787
|
for (const cfg of configs) {
|
|
680
788
|
const { parent } = this.getNodeInfo(cfg.id, false);
|
|
681
|
-
if (parent && !beforeSnapshots.has(`${parent.id}`)) beforeSnapshots.set(`${parent.id}`, cloneDeep(toRaw(parent)));
|
|
789
|
+
if (parent && !beforeSnapshots.has(`${parent.id}`)) beforeSnapshots.set(`${parent.id}`, cloneDeep$1(toRaw(parent)));
|
|
682
790
|
}
|
|
683
|
-
if (!beforeSnapshots.has(`${targetParent.id}`)) beforeSnapshots.set(`${targetParent.id}`, cloneDeep(toRaw(targetParent)));
|
|
791
|
+
if (!beforeSnapshots.has(`${targetParent.id}`)) beforeSnapshots.set(`${targetParent.id}`, cloneDeep$1(toRaw(targetParent)));
|
|
684
792
|
const newLayout = await this.getLayout(targetParent);
|
|
685
793
|
const { sameParentIndices, crossParentConfigs, aborted } = classifyDragSources(configs, targetParent, (id, raw) => this.getNodeInfo(id, raw));
|
|
686
794
|
if (aborted) return;
|
|
@@ -699,35 +807,111 @@ var Editor = class extends BaseService_default {
|
|
|
699
807
|
const root = this.get("root");
|
|
700
808
|
const stage = this.get("stage");
|
|
701
809
|
if (stage && page && root) stage.update({
|
|
702
|
-
config: cloneDeep(page),
|
|
810
|
+
config: cloneDeep$1(page),
|
|
703
811
|
parentId: root.id,
|
|
704
|
-
root: cloneDeep(root)
|
|
812
|
+
root: cloneDeep$1(root)
|
|
705
813
|
});
|
|
706
814
|
const updatedItems = [];
|
|
707
815
|
for (const oldNode of beforeSnapshots.values()) {
|
|
708
816
|
const newNode = this.getNodeById(oldNode.id, false);
|
|
709
817
|
if (newNode) updatedItems.push({
|
|
710
818
|
oldNode,
|
|
711
|
-
newNode: cloneDeep(toRaw(newNode))
|
|
819
|
+
newNode: cloneDeep$1(toRaw(newNode))
|
|
712
820
|
});
|
|
713
821
|
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
822
|
+
if (!doNotPushHistory) {
|
|
823
|
+
const pageForOp = this.getNodeInfo(configs[0].id, false).page;
|
|
824
|
+
this.pushOpHistory("update", {
|
|
825
|
+
diff: buildUpdateDiff(updatedItems),
|
|
826
|
+
pageData: {
|
|
827
|
+
name: pageForOp?.name || "",
|
|
828
|
+
id: pageForOp.id
|
|
829
|
+
},
|
|
830
|
+
historyDescription,
|
|
831
|
+
source: historySource
|
|
832
|
+
});
|
|
833
|
+
} else this.selectionBeforeOp = null;
|
|
719
834
|
this.emit("drag-to", {
|
|
720
835
|
targetIndex,
|
|
721
836
|
configs,
|
|
722
837
|
targetParent
|
|
723
838
|
});
|
|
839
|
+
this.emit("change", {
|
|
840
|
+
type: "drag-to",
|
|
841
|
+
data: configs.map((node) => ({
|
|
842
|
+
node,
|
|
843
|
+
page: this.getPageOfNode(node.id)
|
|
844
|
+
})),
|
|
845
|
+
targetIndex,
|
|
846
|
+
targetParent
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* 下列 *AndGetHistoryId 方法与对应的普通操作(add / remove / update ...)行为完全一致,
|
|
851
|
+
* 返回值在 {@link DslOpWithHistoryIdsResult} 中同时包含原操作结果与本次写入历史栈的 uuid 列表({@link StepValue.uuid}),
|
|
852
|
+
* 可用于精确引用 / 定位该条历史记录(埋点、revert、跨端同步等)。
|
|
853
|
+
*
|
|
854
|
+
* 当本次操作未写入历史(doNotPushHistory 为 true、或操作无实际变更 / 提前返回)时 historyIds 为 `[]`。
|
|
855
|
+
*/
|
|
856
|
+
/** 等价于 {@link add},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
|
|
857
|
+
async addAndGetHistoryId(addNode, parent, options = {}) {
|
|
858
|
+
this.lastPushedHistoryId = null;
|
|
859
|
+
return {
|
|
860
|
+
result: await this.add(addNode, parent, options),
|
|
861
|
+
historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
/** 等价于 {@link remove},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
|
|
865
|
+
async removeAndGetHistoryId(nodeOrNodeList, options = {}) {
|
|
866
|
+
this.lastPushedHistoryId = null;
|
|
867
|
+
await this.remove(nodeOrNodeList, options);
|
|
868
|
+
return {
|
|
869
|
+
result: void 0,
|
|
870
|
+
historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
/** 等价于 {@link update},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
|
|
874
|
+
async updateAndGetHistoryId(config, data = {}) {
|
|
875
|
+
this.lastPushedHistoryId = null;
|
|
876
|
+
return {
|
|
877
|
+
result: await this.update(config, data),
|
|
878
|
+
historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
/** 等价于 {@link moveLayer},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
|
|
882
|
+
async moveLayerAndGetHistoryId(offset, options = {}) {
|
|
883
|
+
this.lastPushedHistoryId = null;
|
|
884
|
+
await this.moveLayer(offset, options);
|
|
885
|
+
return {
|
|
886
|
+
result: void 0,
|
|
887
|
+
historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
/** 等价于 {@link moveToContainer},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
|
|
891
|
+
async moveToContainerAndGetHistoryId(config, targetId, options = {}) {
|
|
892
|
+
this.lastPushedHistoryId = null;
|
|
893
|
+
return {
|
|
894
|
+
result: await this.moveToContainer(config, targetId, options),
|
|
895
|
+
historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
/** 等价于 {@link dragTo},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
|
|
899
|
+
async dragToAndGetHistoryId(config, targetParent, targetIndex, options = {}) {
|
|
900
|
+
this.lastPushedHistoryId = null;
|
|
901
|
+
await this.dragTo(config, targetParent, targetIndex, options);
|
|
902
|
+
return {
|
|
903
|
+
result: void 0,
|
|
904
|
+
historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
|
|
905
|
+
};
|
|
724
906
|
}
|
|
725
907
|
/**
|
|
726
908
|
* 撤销当前操作
|
|
727
909
|
* @returns 被撤销的操作
|
|
728
910
|
*/
|
|
729
911
|
async undo() {
|
|
730
|
-
const
|
|
912
|
+
const pageId = this.get("page")?.id;
|
|
913
|
+
if (pageId === void 0) return null;
|
|
914
|
+
const value = history_default.undo("page", pageId);
|
|
731
915
|
if (value) await this.applyHistoryOp(value, true);
|
|
732
916
|
return value;
|
|
733
917
|
}
|
|
@@ -736,11 +920,141 @@ var Editor = class extends BaseService_default {
|
|
|
736
920
|
* @returns 被恢复的操作
|
|
737
921
|
*/
|
|
738
922
|
async redo() {
|
|
739
|
-
const
|
|
923
|
+
const pageId = this.get("page")?.id;
|
|
924
|
+
if (pageId === void 0) return null;
|
|
925
|
+
const value = history_default.redo("page", pageId);
|
|
740
926
|
if (value) await this.applyHistoryOp(value, false);
|
|
741
927
|
return value;
|
|
742
928
|
}
|
|
743
|
-
|
|
929
|
+
/**
|
|
930
|
+
* 「回滚」指定页面历史步骤(类 git revert 语义):
|
|
931
|
+
* - 不动原始历史栈结构(不移动 cursor、不丢弃任何步骤);
|
|
932
|
+
* - 取出 `index` 对应的 step,**反向应用**一次(add→remove / remove→add / update→旧值);
|
|
933
|
+
* - 把这次反向应用作为一条**新步骤**追加到栈顶,可被普通 undo / redo。
|
|
934
|
+
*
|
|
935
|
+
* 与 `gotoPageStep`(类 git reset)的区别在于此操作**不丢弃**目标之后的历史。
|
|
936
|
+
* 与 `applyHistoryOp(reverse=true)` 的区别在于:本方法**不带** `doNotPushHistory`,
|
|
937
|
+
* 反向应用会以一条新 step 入栈;并且不实施 step 中保存的选区与 modifiedNodeIds 状态,
|
|
938
|
+
* 选区由用户当前位置决定,符合"新提交"语义。
|
|
939
|
+
*
|
|
940
|
+
* 仅对处于「已应用」状态的步骤生效——未应用的步骤本身就不存在于当前 DSL 中,反向无意义。
|
|
941
|
+
*
|
|
942
|
+
* @param index 目标 step 在所属页面栈中的索引(0 为最早),通常由历史面板传入
|
|
943
|
+
* @returns 反向后产生的新 step;目标不存在 / 未应用 / 反向失败时返回 null
|
|
944
|
+
*/
|
|
945
|
+
async revertPageStep(index) {
|
|
946
|
+
const entry = history_default.getStepList("page", this.get("page")?.id)[index];
|
|
947
|
+
if (!entry?.applied) return null;
|
|
948
|
+
const { step } = entry;
|
|
949
|
+
if (step.opType === "initial") return null;
|
|
950
|
+
if (!this.get("root")) return null;
|
|
951
|
+
if (step.opType === "update") {
|
|
952
|
+
const items = step.diff ?? [];
|
|
953
|
+
if (!items.length || !items.every((item) => item.changeRecords?.length)) return null;
|
|
954
|
+
}
|
|
955
|
+
let revertedStep = null;
|
|
956
|
+
const captureRevert = (_pageId, s) => {
|
|
957
|
+
revertedStep = s;
|
|
958
|
+
};
|
|
959
|
+
history_default.once("change", captureRevert);
|
|
960
|
+
const historyDescription = `回滚 #${index + 1}: ${describeStepForRevert(step)}`;
|
|
961
|
+
const opts = {
|
|
962
|
+
doNotSelect: true,
|
|
963
|
+
doNotSwitchPage: true,
|
|
964
|
+
historyDescription,
|
|
965
|
+
historySource: "rollback"
|
|
966
|
+
};
|
|
967
|
+
try {
|
|
968
|
+
switch (step.opType) {
|
|
969
|
+
case "add":
|
|
970
|
+
for (const { newSchema } of step.diff ?? []) {
|
|
971
|
+
if (!newSchema) continue;
|
|
972
|
+
const existing = this.getNodeById(newSchema.id, false);
|
|
973
|
+
if (existing) await this.remove(existing, opts);
|
|
974
|
+
}
|
|
975
|
+
break;
|
|
976
|
+
case "remove": {
|
|
977
|
+
const sorted = [...step.diff ?? []].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
|
|
978
|
+
for (const { oldSchema, parentId } of sorted) {
|
|
979
|
+
if (!oldSchema || parentId === void 0) continue;
|
|
980
|
+
const parent = this.getNodeById(parentId, false);
|
|
981
|
+
if (parent) await this.add([cloneDeep$1(oldSchema)], parent, opts);
|
|
982
|
+
}
|
|
983
|
+
break;
|
|
984
|
+
}
|
|
985
|
+
case "update": {
|
|
986
|
+
const configs = (step.diff ?? []).filter((item) => item.oldSchema && item.newSchema).map(({ oldSchema, newSchema, changeRecords }) => {
|
|
987
|
+
const oldNode = oldSchema;
|
|
988
|
+
const newNode = newSchema;
|
|
989
|
+
if (changeRecords?.length) {
|
|
990
|
+
const patch = {
|
|
991
|
+
id: newNode.id,
|
|
992
|
+
type: newNode.type
|
|
993
|
+
};
|
|
994
|
+
for (const record of changeRecords) {
|
|
995
|
+
if (!record.propPath) return cloneDeep$1(oldNode);
|
|
996
|
+
const value = cloneDeep$1(getValueByKeyPath(record.propPath, oldNode));
|
|
997
|
+
setValueByKeyPath(record.propPath, value, patch);
|
|
998
|
+
}
|
|
999
|
+
return patch;
|
|
1000
|
+
}
|
|
1001
|
+
return cloneDeep$1(oldNode);
|
|
1002
|
+
});
|
|
1003
|
+
if (configs.length) await this.update(configs, {
|
|
1004
|
+
historyDescription,
|
|
1005
|
+
historySource: "rollback"
|
|
1006
|
+
});
|
|
1007
|
+
break;
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
} finally {
|
|
1011
|
+
history_default.off("change", captureRevert);
|
|
1012
|
+
}
|
|
1013
|
+
const page = toRaw(this.get("page"));
|
|
1014
|
+
if (page) this.emit("history-change", page);
|
|
1015
|
+
return revertedStep;
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* 通过历史记录 uuid 回滚当前页面的历史步骤,语义与 {@link revertPageStep} 完全一致,
|
|
1019
|
+
* 仅入参从 index 改为 uuid 列表({@link StepValue.uuid})。按数组顺序依次回滚,
|
|
1020
|
+
* 返回与入参同序的结果列表(某项失败时为 `null`)。
|
|
1021
|
+
*
|
|
1022
|
+
* @param uuids 目标历史记录的 uuid 列表,通常由 *AndGetHistoryId 方法返回的 `historyIds`
|
|
1023
|
+
*/
|
|
1024
|
+
async revertPageStepById(uuids) {
|
|
1025
|
+
const results = [];
|
|
1026
|
+
const pageId = this.get("page")?.id;
|
|
1027
|
+
for (const uuid of uuids) {
|
|
1028
|
+
const location = history_default.findStepLocationByUuid("page", uuid, pageId);
|
|
1029
|
+
results.push(!location ? null : await this.revertPageStep(location.index));
|
|
1030
|
+
}
|
|
1031
|
+
return results;
|
|
1032
|
+
}
|
|
1033
|
+
/**
|
|
1034
|
+
* 跳转当前页面历史栈到指定游标位置。
|
|
1035
|
+
*
|
|
1036
|
+
* `targetCursor` 与 `UndoRedo.getCursor()` 同义:表示"已应用步骤数量",
|
|
1037
|
+
* 取值范围 `[0, length]`。当目标 < 当前游标时循环 undo,否则循环 redo。
|
|
1038
|
+
* 通常由历史面板传入「点击的 step.index + 1」作为目标。
|
|
1039
|
+
*
|
|
1040
|
+
* @returns 实际移动到的最终游标位置
|
|
1041
|
+
*/
|
|
1042
|
+
async gotoPageStep(targetCursor) {
|
|
1043
|
+
const pageId = this.get("page")?.id;
|
|
1044
|
+
let cursor = history_default.getCursor("page", pageId);
|
|
1045
|
+
const { length } = history_default.getStepList("page", pageId);
|
|
1046
|
+
const target = Math.max(0, Math.min(targetCursor, length));
|
|
1047
|
+
while (cursor > target) {
|
|
1048
|
+
if (!await this.undo()) break;
|
|
1049
|
+
cursor -= 1;
|
|
1050
|
+
}
|
|
1051
|
+
while (cursor < target) {
|
|
1052
|
+
if (!await this.redo()) break;
|
|
1053
|
+
cursor += 1;
|
|
1054
|
+
}
|
|
1055
|
+
return cursor;
|
|
1056
|
+
}
|
|
1057
|
+
async move(left, top, { doNotPushHistory = false, historyDescription, historySource } = {}) {
|
|
744
1058
|
const node = toRaw(this.get("node"));
|
|
745
1059
|
if (!node || isPage(node)) return;
|
|
746
1060
|
const newStyle = calcMoveStyle(node.style || {}, left, top);
|
|
@@ -749,6 +1063,10 @@ var Editor = class extends BaseService_default {
|
|
|
749
1063
|
id: node.id,
|
|
750
1064
|
type: node.type,
|
|
751
1065
|
style: newStyle
|
|
1066
|
+
}, {
|
|
1067
|
+
doNotPushHistory,
|
|
1068
|
+
historyDescription,
|
|
1069
|
+
historySource
|
|
752
1070
|
});
|
|
753
1071
|
}
|
|
754
1072
|
resetState() {
|
|
@@ -783,63 +1101,207 @@ var Editor = class extends BaseService_default {
|
|
|
783
1101
|
return super.emit(eventName, ...args);
|
|
784
1102
|
}
|
|
785
1103
|
addModifiedNodeId(id) {
|
|
786
|
-
|
|
1104
|
+
this.get("modifiedNodeIds").set(id, id);
|
|
1105
|
+
}
|
|
1106
|
+
/**
|
|
1107
|
+
* 获取指定节点所属的页面 / 页面片:
|
|
1108
|
+
* - 普通节点返回其所在的 page;
|
|
1109
|
+
* - 节点本身就是 page / pageFragment 时返回它自己;
|
|
1110
|
+
* - 找不到时返回 null。
|
|
1111
|
+
* 供 `change` 事件携带「变更节点对应的 page」(而非编辑器当前选中页)。
|
|
1112
|
+
*/
|
|
1113
|
+
getPageOfNode(id) {
|
|
1114
|
+
const { node, page } = this.getNodeInfo(id, false);
|
|
1115
|
+
if (page) return page;
|
|
1116
|
+
if (node && (isPage(node) || isPageFragment(node))) return node;
|
|
1117
|
+
return null;
|
|
787
1118
|
}
|
|
788
1119
|
captureSelectionBeforeOp() {
|
|
789
|
-
if (this.
|
|
1120
|
+
if (this.selectionBeforeOp) return;
|
|
790
1121
|
this.selectionBeforeOp = this.get("nodes").map((n) => n.id);
|
|
791
1122
|
}
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
1123
|
+
/**
|
|
1124
|
+
* 比较「上一次 root」与「新 root」的页面 / 页面片,按页面粒度把整体替换拆成历史记录:
|
|
1125
|
+
* - 新旧都存在且内容变化的页面 → 一条 `update`(整页快照替换,无 changeRecords);
|
|
1126
|
+
* - 仅新 root 存在的页面 → 一条 `add`;
|
|
1127
|
+
* - 仅旧 root 存在的页面 → 一条 `remove`。
|
|
1128
|
+
*
|
|
1129
|
+
* 每条记录落到对应页面自己的历史栈(与普通节点操作一致),并标记来源 `source`。
|
|
1130
|
+
* 内容未变化的页面不产生记录,避免重复设置相同 DSL 时产生噪声。
|
|
1131
|
+
*/
|
|
1132
|
+
pushRootDiffHistory(preRoot, nextRoot, source) {
|
|
1133
|
+
const prevPages = preRoot?.items || [];
|
|
1134
|
+
const nextPages = nextRoot?.items || [];
|
|
1135
|
+
const prevMap = new Map(prevPages.map((p) => [`${p.id}`, p]));
|
|
1136
|
+
const nextMap = new Map(nextPages.map((p) => [`${p.id}`, p]));
|
|
1137
|
+
const indexInItems = (root, id) => (root.items ?? []).findIndex((item) => `${item.id}` === `${id}`);
|
|
1138
|
+
nextPages.forEach((nextPage) => {
|
|
1139
|
+
const prevPage = prevMap.get(`${nextPage.id}`);
|
|
1140
|
+
if (!prevPage) this.pushPageDiffStep("add", nextPage, {
|
|
1141
|
+
newSchema: cloneDeep$1(toRaw(nextPage)),
|
|
1142
|
+
parentId: nextRoot.id,
|
|
1143
|
+
index: indexInItems(nextRoot, nextPage.id)
|
|
1144
|
+
}, source);
|
|
1145
|
+
else if (!isEqual(toRaw(prevPage), toRaw(nextPage))) this.pushPageDiffStep("update", nextPage, {
|
|
1146
|
+
oldSchema: cloneDeep$1(toRaw(prevPage)),
|
|
1147
|
+
newSchema: cloneDeep$1(toRaw(nextPage))
|
|
1148
|
+
}, source);
|
|
1149
|
+
});
|
|
1150
|
+
prevPages.forEach((prevPage) => {
|
|
1151
|
+
if (!nextMap.has(`${prevPage.id}`)) this.pushPageDiffStep("remove", prevPage, {
|
|
1152
|
+
oldSchema: cloneDeep$1(toRaw(prevPage)),
|
|
1153
|
+
parentId: preRoot.id,
|
|
1154
|
+
index: indexInItems(preRoot, prevPage.id)
|
|
1155
|
+
}, source);
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
/**
|
|
1159
|
+
* 构造一条页面级「set root」历史记录(不携带选区 / modifiedNodeIds 上下文)并落到该页面自己的栈。
|
|
1160
|
+
*
|
|
1161
|
+
* 连续 set root 替换:若该页栈最新一条已是**同来源**的 set root 记录({@link StepValue.rootStep} 且 `source` 相同),
|
|
1162
|
+
* 则用本次记录**替换**它而非新增,避免源码反复保存 / 外部重设 DSL 时堆积多条 root 记录;
|
|
1163
|
+
* 来源不同则照常新增(initial 基线不是 rootStep,不在此列)。
|
|
1164
|
+
*/
|
|
1165
|
+
pushPageDiffStep(opType, page, diffItem, source) {
|
|
797
1166
|
const step = {
|
|
1167
|
+
uuid: guid(),
|
|
1168
|
+
data: {
|
|
1169
|
+
name: page.name || "",
|
|
1170
|
+
id: page.id
|
|
1171
|
+
},
|
|
1172
|
+
opType,
|
|
1173
|
+
extra: {
|
|
1174
|
+
selectedBefore: [],
|
|
1175
|
+
selectedAfter: [],
|
|
1176
|
+
modifiedNodeIds: /* @__PURE__ */ new Map()
|
|
1177
|
+
},
|
|
1178
|
+
diff: [diffItem],
|
|
1179
|
+
rootStep: true
|
|
1180
|
+
};
|
|
1181
|
+
if (source) step.source = source;
|
|
1182
|
+
const top = history_default.getCurrentPageStep(page.id);
|
|
1183
|
+
if (top?.rootStep && top.source === source) history_default.replaceCurrentStep("page", step, page.id);
|
|
1184
|
+
else history_default.push("page", step, page.id);
|
|
1185
|
+
}
|
|
1186
|
+
pushOpHistory(opType, { diff, pageData, historyDescription, source }) {
|
|
1187
|
+
const step = {
|
|
1188
|
+
uuid: guid(),
|
|
798
1189
|
data: pageData,
|
|
799
1190
|
opType,
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
1191
|
+
extra: {
|
|
1192
|
+
selectedBefore: this.selectionBeforeOp ?? [],
|
|
1193
|
+
selectedAfter: this.get("nodes").map((n) => n.id),
|
|
1194
|
+
modifiedNodeIds: new Map(this.get("modifiedNodeIds"))
|
|
1195
|
+
},
|
|
1196
|
+
diff
|
|
804
1197
|
};
|
|
805
|
-
|
|
1198
|
+
if (historyDescription) step.historyDescription = historyDescription;
|
|
1199
|
+
if (source) step.source = source;
|
|
1200
|
+
const historyId = history_default.push("page", step, pageData.id) ? step.uuid : null;
|
|
1201
|
+
this.lastPushedHistoryId = historyId;
|
|
806
1202
|
this.selectionBeforeOp = null;
|
|
807
|
-
|
|
1203
|
+
return historyId;
|
|
808
1204
|
}
|
|
809
1205
|
/**
|
|
810
1206
|
* 应用历史操作(撤销 / 重做)
|
|
1207
|
+
*
|
|
1208
|
+
* 所有 DSL 修改都走 `editor.add / remove / update`,并通过 `doNotPushHistory` 阻止再次入栈、
|
|
1209
|
+
* `doNotSelect / doNotSwitchPage` 让选区由方法末尾的统一逻辑兜底。
|
|
1210
|
+
*
|
|
1211
|
+
* 注意:这些公开方法会发出 add / remove / update 事件,业务侧若需要区分"用户操作"与"撤销重做触发",
|
|
1212
|
+
* 请监听 `history-change` 事件配合判断。
|
|
1213
|
+
*
|
|
811
1214
|
* @param step 操作记录
|
|
812
1215
|
* @param reverse true = 撤销,false = 重做
|
|
813
1216
|
*/
|
|
814
1217
|
async applyHistoryOp(step, reverse) {
|
|
815
|
-
|
|
1218
|
+
if (step.opType === "initial") return;
|
|
816
1219
|
const root = this.get("root");
|
|
817
1220
|
const stage = this.get("stage");
|
|
818
1221
|
if (!root) return;
|
|
819
|
-
const
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
getNodeInfo: (id, raw) => this.getNodeInfo(id, raw),
|
|
824
|
-
setRoot: (r) => this.set("root", r),
|
|
825
|
-
setPage: (p) => this.set("page", p),
|
|
826
|
-
getPage: () => this.get("page")
|
|
1222
|
+
const commonOpts = {
|
|
1223
|
+
doNotSelect: true,
|
|
1224
|
+
doNotSwitchPage: true,
|
|
1225
|
+
doNotPushHistory: true
|
|
827
1226
|
};
|
|
828
1227
|
switch (step.opType) {
|
|
829
|
-
case "add":
|
|
830
|
-
|
|
1228
|
+
case "add": {
|
|
1229
|
+
const items = step.diff ?? [];
|
|
1230
|
+
if (reverse) for (const { newSchema } of items) {
|
|
1231
|
+
if (!newSchema) continue;
|
|
1232
|
+
const existing = this.getNodeById(newSchema.id, false);
|
|
1233
|
+
if (existing) await this.remove(existing, commonOpts);
|
|
1234
|
+
}
|
|
1235
|
+
else {
|
|
1236
|
+
const sorted = [...items].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
|
|
1237
|
+
for (const { newSchema, parentId, index } of sorted) {
|
|
1238
|
+
if (!newSchema || parentId === void 0) continue;
|
|
1239
|
+
const parent = this.getNodeById(parentId, false);
|
|
1240
|
+
if (parent?.items) {
|
|
1241
|
+
if (typeof index === "number" && index >= 0 && index < parent.items.length) parent.items.splice(index, 0, cloneDeep$1(newSchema));
|
|
1242
|
+
else parent.items.push(cloneDeep$1(newSchema));
|
|
1243
|
+
await stage?.add({
|
|
1244
|
+
config: cloneDeep$1(newSchema),
|
|
1245
|
+
parent: cloneDeep$1(parent),
|
|
1246
|
+
parentId: parent.id,
|
|
1247
|
+
root: cloneDeep$1(root)
|
|
1248
|
+
});
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
831
1252
|
break;
|
|
832
|
-
|
|
833
|
-
|
|
1253
|
+
}
|
|
1254
|
+
case "remove": {
|
|
1255
|
+
const items = step.diff ?? [];
|
|
1256
|
+
if (reverse) {
|
|
1257
|
+
const sorted = [...items].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
|
|
1258
|
+
for (const { oldSchema, parentId, index } of sorted) {
|
|
1259
|
+
if (!oldSchema || parentId === void 0) continue;
|
|
1260
|
+
const parent = this.getNodeById(parentId, false);
|
|
1261
|
+
if (parent?.items) {
|
|
1262
|
+
parent.items.splice(index ?? parent.items.length, 0, cloneDeep$1(oldSchema));
|
|
1263
|
+
await stage?.add({
|
|
1264
|
+
config: cloneDeep$1(oldSchema),
|
|
1265
|
+
parent: cloneDeep$1(parent),
|
|
1266
|
+
parentId,
|
|
1267
|
+
root: cloneDeep$1(root)
|
|
1268
|
+
});
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
} else for (const { oldSchema } of items) {
|
|
1272
|
+
if (!oldSchema) continue;
|
|
1273
|
+
const existing = this.getNodeById(oldSchema.id, false);
|
|
1274
|
+
if (existing) await this.remove(existing, commonOpts);
|
|
1275
|
+
}
|
|
834
1276
|
break;
|
|
835
|
-
|
|
836
|
-
|
|
1277
|
+
}
|
|
1278
|
+
case "update": {
|
|
1279
|
+
const configs = (step.diff ?? []).filter((item) => item.oldSchema && item.newSchema).map(({ oldSchema, newSchema, changeRecords }) => {
|
|
1280
|
+
const oldNode = oldSchema;
|
|
1281
|
+
const newNode = newSchema;
|
|
1282
|
+
if (changeRecords?.length) {
|
|
1283
|
+
const sourceForValues = reverse ? oldNode : newNode;
|
|
1284
|
+
const patch = {
|
|
1285
|
+
id: newNode.id,
|
|
1286
|
+
type: newNode.type
|
|
1287
|
+
};
|
|
1288
|
+
for (const record of changeRecords) {
|
|
1289
|
+
if (!record.propPath) return cloneDeep$1(sourceForValues);
|
|
1290
|
+
const value = cloneDeep$1(getValueByKeyPath(record.propPath, sourceForValues));
|
|
1291
|
+
setValueByKeyPath(record.propPath, value, patch);
|
|
1292
|
+
}
|
|
1293
|
+
return patch;
|
|
1294
|
+
}
|
|
1295
|
+
return cloneDeep$1(reverse ? oldNode : newNode);
|
|
1296
|
+
});
|
|
1297
|
+
if (configs.length) await this.update(configs, { doNotPushHistory: true });
|
|
837
1298
|
break;
|
|
1299
|
+
}
|
|
838
1300
|
}
|
|
839
|
-
this.set("modifiedNodeIds", step.modifiedNodeIds);
|
|
1301
|
+
this.set("modifiedNodeIds", step.extra?.modifiedNodeIds ?? /* @__PURE__ */ new Map());
|
|
840
1302
|
const page = toRaw(this.get("page"));
|
|
841
1303
|
if (page) {
|
|
842
|
-
const selectIds = reverse ? step.selectedBefore : step.selectedAfter;
|
|
1304
|
+
const selectIds = (reverse ? step.extra?.selectedBefore : step.extra?.selectedAfter) ?? [];
|
|
843
1305
|
setTimeout(() => {
|
|
844
1306
|
if (!selectIds.length) return;
|
|
845
1307
|
if (selectIds.length > 1) {
|
|
@@ -849,7 +1311,6 @@ var Editor = class extends BaseService_default {
|
|
|
849
1311
|
}, 0);
|
|
850
1312
|
this.emit("history-change", page);
|
|
851
1313
|
}
|
|
852
|
-
this.isHistoryStateChange = false;
|
|
853
1314
|
}
|
|
854
1315
|
selectedConfigExceptionHandler(config) {
|
|
855
1316
|
return resolveSelectedNode(config, (id) => this.getNodeInfo(id), this.state.root?.id);
|