@tmagic/editor 1.8.0-beta.3 → 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 +15 -1
- package/dist/es/components/ToolButton.vue_vue_type_script_setup_true_lang.js +6 -6
- package/dist/es/hooks/use-code-block-edit.js +6 -3
- package/dist/es/hooks/use-data-source-edit.js +5 -2
- package/dist/es/hooks/use-stage.js +6 -3
- 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/NavMenu.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 -51
- package/dist/es/layouts/history-list/BucketTab.vue_vue_type_script_setup_true_lang.js +22 -36
- package/dist/es/layouts/history-list/GroupRow.vue_vue_type_script_setup_true_lang.js +152 -93
- package/dist/es/layouts/history-list/HistoryDiffDialog.vue_vue_type_script_setup_true_lang.js +36 -10
- package/dist/es/layouts/history-list/HistoryListPanel.vue_vue_type_script_setup_true_lang.js +334 -208
- 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 -57
- package/dist/es/layouts/history-list/composables.js +118 -25
- package/dist/es/layouts/page-bar/PageBar.vue_vue_type_script_setup_true_lang.js +4 -2
- package/dist/es/layouts/props-panel/PropsPanel.vue_vue_type_script_setup_true_lang.js +5 -1
- package/dist/es/layouts/sidebar/ComponentListPanel.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/layouts/sidebar/Sidebar.vue_vue_type_script_setup_true_lang.js +5 -1
- package/dist/es/layouts/sidebar/code-block/CodeBlockList.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/es/layouts/sidebar/code-block/CodeBlockListPanel.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/layouts/sidebar/code-block/useContentMenu.js +1 -1
- package/dist/es/layouts/sidebar/data-source/DataSourceListPanel.vue_vue_type_script_setup_true_lang.js +1 -1
- 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 +5 -5
- package/dist/es/layouts/sidebar/layer/LayerNodeTool.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 +1 -1
- package/dist/es/layouts/workspace/viewer/ViewerMenu.vue_vue_type_script_setup_true_lang.js +8 -8
- package/dist/es/services/codeBlock.js +105 -42
- package/dist/es/services/dataSource.js +85 -35
- package/dist/es/services/editor.js +317 -122
- package/dist/es/services/history.js +270 -204
- package/dist/es/services/keybinding.js +3 -3
- package/dist/es/services/ui.js +3 -0
- package/dist/es/style.css +65 -10
- package/dist/es/utils/content-menu.js +17 -9
- 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 +65 -10
- package/dist/tmagic-editor.umd.cjs +2047 -954
- package/package.json +7 -7
- package/src/components/CompareForm.vue +21 -1
- package/src/components/ToolButton.vue +2 -2
- package/src/hooks/use-code-block-edit.ts +4 -3
- package/src/hooks/use-data-source-edit.ts +2 -2
- package/src/hooks/use-stage.ts +3 -3
- package/src/index.ts +1 -0
- package/src/initService.ts +1 -1
- package/src/layouts/Framework.vue +1 -1
- package/src/layouts/NavMenu.vue +1 -1
- package/src/layouts/history-list/Bucket.vue +34 -67
- package/src/layouts/history-list/BucketTab.vue +46 -51
- package/src/layouts/history-list/GroupRow.vue +150 -98
- package/src/layouts/history-list/HistoryDiffDialog.vue +96 -69
- package/src/layouts/history-list/HistoryListPanel.vue +298 -111
- package/src/layouts/history-list/InitialRow.vue +25 -9
- package/src/layouts/history-list/PageTab.vue +58 -48
- package/src/layouts/history-list/composables.ts +210 -29
- 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 +6 -1
- 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/DataSourceListPanel.vue +1 -1
- 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/workspace/viewer/Stage.vue +1 -1
- package/src/layouts/workspace/viewer/ViewerMenu.vue +8 -8
- package/src/services/codeBlock.ts +129 -35
- package/src/services/dataSource.ts +105 -34
- package/src/services/editor.ts +470 -157
- package/src/services/history.ts +312 -203
- package/src/services/keybinding.ts +3 -3
- package/src/services/ui.ts +7 -0
- package/src/theme/history-list-panel.scss +87 -6
- package/src/theme/page-bar.scss +0 -4
- package/src/theme/props-panel.scss +3 -3
- package/src/type.ts +221 -61
- package/src/utils/content-menu.ts +18 -9
- 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 +871 -308
|
@@ -4331,12 +4331,15 @@
|
|
|
4331
4331
|
await (0, vue.nextTick)();
|
|
4332
4332
|
codeBlockEditorRef.value?.show();
|
|
4333
4333
|
};
|
|
4334
|
-
const deleteCode = async (key) => {
|
|
4335
|
-
codeBlockService.deleteCodeDslByIds([key]);
|
|
4334
|
+
const deleteCode = async (key, { historySource } = {}) => {
|
|
4335
|
+
codeBlockService.deleteCodeDslByIds([key], { historySource });
|
|
4336
4336
|
};
|
|
4337
4337
|
const submitCodeBlockHandler = async (values, eventData) => {
|
|
4338
4338
|
if (!codeId.value) return;
|
|
4339
|
-
await codeBlockService.setCodeDslById(codeId.value, values, {
|
|
4339
|
+
await codeBlockService.setCodeDslById(codeId.value, values, {
|
|
4340
|
+
changeRecords: eventData?.changeRecords,
|
|
4341
|
+
historySource: "props"
|
|
4342
|
+
});
|
|
4340
4343
|
codeBlockEditorRef.value?.hide();
|
|
4341
4344
|
};
|
|
4342
4345
|
return {
|
|
@@ -4914,8 +4917,41 @@
|
|
|
4914
4917
|
};
|
|
4915
4918
|
var props_default = new Props();
|
|
4916
4919
|
//#endregion
|
|
4920
|
+
//#region packages/editor/src/utils/config.ts
|
|
4921
|
+
var $TMAGIC_EDITOR = {};
|
|
4922
|
+
var setEditorConfig = (option) => {
|
|
4923
|
+
$TMAGIC_EDITOR = option;
|
|
4924
|
+
};
|
|
4925
|
+
var getEditorConfig = (key) => $TMAGIC_EDITOR[key];
|
|
4926
|
+
//#endregion
|
|
4917
4927
|
//#region packages/editor/src/utils/undo-redo.ts
|
|
4918
|
-
var UndoRedo = class {
|
|
4928
|
+
var UndoRedo = class UndoRedo {
|
|
4929
|
+
/**
|
|
4930
|
+
* 由 {@link UndoRedo.serialize} 产出的快照重建一个 UndoRedo 实例。
|
|
4931
|
+
* 游标会被夹紧到 [0, length] 区间,避免脏数据导致越界。
|
|
4932
|
+
*
|
|
4933
|
+
* @param options.isSavedStep 可选谓词:若提供,则把游标定位到「最近一条满足该谓词的记录」之后
|
|
4934
|
+
* (即恢复到最近一个已保存点);找不到匹配记录时退回快照中的原游标。
|
|
4935
|
+
*/
|
|
4936
|
+
static fromSerialized(data, options = {}) {
|
|
4937
|
+
const undoRedo = new UndoRedo(data.listMaxSize);
|
|
4938
|
+
const list = Array.isArray(data.elementList) ? data.elementList.map((item) => cloneDeep$1(item)) : [];
|
|
4939
|
+
let cursor = Number.isFinite(data.listCursor) ? data.listCursor : list.length;
|
|
4940
|
+
const overflow = list.length - undoRedo.listMaxSize;
|
|
4941
|
+
if (overflow > 0) {
|
|
4942
|
+
list.splice(0, overflow);
|
|
4943
|
+
cursor -= overflow;
|
|
4944
|
+
}
|
|
4945
|
+
if (options.isSavedStep) {
|
|
4946
|
+
for (let i = list.length - 1; i >= 0; i--) if (options.isSavedStep(list[i])) {
|
|
4947
|
+
cursor = i + 1;
|
|
4948
|
+
break;
|
|
4949
|
+
}
|
|
4950
|
+
}
|
|
4951
|
+
undoRedo.elementList = list;
|
|
4952
|
+
undoRedo.listCursor = Math.max(0, Math.min(cursor, list.length));
|
|
4953
|
+
return undoRedo;
|
|
4954
|
+
}
|
|
4919
4955
|
elementList;
|
|
4920
4956
|
listCursor;
|
|
4921
4957
|
listMaxSize;
|
|
@@ -4925,6 +4961,17 @@
|
|
|
4925
4961
|
this.listCursor = 0;
|
|
4926
4962
|
this.listMaxSize = listMaxSize > minListMaxSize ? listMaxSize : minListMaxSize;
|
|
4927
4963
|
}
|
|
4964
|
+
/**
|
|
4965
|
+
* 导出当前栈的可序列化快照(深克隆,避免外部改动污染内部状态)。
|
|
4966
|
+
* 配合 {@link UndoRedo.fromSerialized} 可在持久化后完整还原撤销/重做栈。
|
|
4967
|
+
*/
|
|
4968
|
+
serialize() {
|
|
4969
|
+
return {
|
|
4970
|
+
elementList: this.elementList.map((item) => cloneDeep$1(item)),
|
|
4971
|
+
listCursor: this.listCursor,
|
|
4972
|
+
listMaxSize: this.listMaxSize
|
|
4973
|
+
};
|
|
4974
|
+
}
|
|
4928
4975
|
pushElement(element) {
|
|
4929
4976
|
this.elementList.splice(this.listCursor, this.elementList.length - this.listCursor, cloneDeep$1(element));
|
|
4930
4977
|
this.listCursor += 1;
|
|
@@ -4957,6 +5004,28 @@
|
|
|
4957
5004
|
return cloneDeep$1(this.elementList[this.listCursor - 1]);
|
|
4958
5005
|
}
|
|
4959
5006
|
/**
|
|
5007
|
+
* 用 `element` 替换当前游标所在元素(cursor - 1),并丢弃其后的重做尾部(与 {@link pushElement} 的丢尾一致),
|
|
5008
|
+
* 游标位置保持不变(元素数量不增)。cursor 为 0(无已应用元素)时不做任何操作并返回 false。
|
|
5009
|
+
* 用于「连续同类记录合并」等就地替换最新一条的场景。
|
|
5010
|
+
*/
|
|
5011
|
+
replaceCurrentElement(element) {
|
|
5012
|
+
if (this.listCursor < 1) return false;
|
|
5013
|
+
this.elementList.splice(this.listCursor - 1, this.elementList.length - (this.listCursor - 1), cloneDeep$1(element));
|
|
5014
|
+
return true;
|
|
5015
|
+
}
|
|
5016
|
+
/**
|
|
5017
|
+
* 对当前游标所在元素(cursor - 1)做就地更新;cursor 为 0(全部已撤销)时不做任何操作。
|
|
5018
|
+
* 用于给「当前步骤」打标记(如标记为已保存)等元数据写入场景。
|
|
5019
|
+
*/
|
|
5020
|
+
updateCurrentElement(updater) {
|
|
5021
|
+
if (this.listCursor < 1) return;
|
|
5022
|
+
updater(this.elementList[this.listCursor - 1]);
|
|
5023
|
+
}
|
|
5024
|
+
/** 对栈内全部元素做就地更新。用于批量清理元数据(如清空所有元素的已保存标记)。 */
|
|
5025
|
+
updateElements(updater) {
|
|
5026
|
+
this.elementList.forEach(updater);
|
|
5027
|
+
}
|
|
5028
|
+
/**
|
|
4960
5029
|
* 返回栈内全部元素的浅克隆数组(按时间顺序,索引 0 为最早一步)。
|
|
4961
5030
|
* 仅用于历史面板等只读展示场景,不应直接修改返回值。
|
|
4962
5031
|
*/
|
|
@@ -4978,172 +5047,317 @@
|
|
|
4978
5047
|
}
|
|
4979
5048
|
};
|
|
4980
5049
|
//#endregion
|
|
4981
|
-
//#region packages/editor/src/
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5050
|
+
//#region packages/editor/src/utils/history.ts
|
|
5051
|
+
/**
|
|
5052
|
+
* 「回滚」生成的新 step 简短描述。代码块 / 数据源共用。
|
|
5053
|
+
* 二者逻辑一致,仅展示名取值字段不同(代码块取 `name`,数据源取 `title`),
|
|
5054
|
+
* 因此通过 `getLabel` 注入取值方式。
|
|
5055
|
+
*
|
|
5056
|
+
* @param id 关联的代码块 / 数据源 id
|
|
5057
|
+
* @param diff 单条变更 diff(缺省视为空)
|
|
5058
|
+
* @param getLabel 从快照取展示名
|
|
5059
|
+
*/
|
|
5060
|
+
var describeRevertStep = (id, { oldSchema, newSchema, changeRecords } = {}, getLabel) => {
|
|
5061
|
+
const labelOf = (schema) => getLabel(schema) || schema.id;
|
|
5062
|
+
if (!oldSchema && newSchema) return `撤回新增 ${labelOf(newSchema) || id}`;
|
|
5063
|
+
if (oldSchema && !newSchema) return `还原已删除的 ${labelOf(oldSchema) || id}`;
|
|
5064
|
+
const label = newSchema && getLabel(newSchema) || oldSchema && getLabel(oldSchema) || `${id}`;
|
|
5065
|
+
const propPath = changeRecords?.[0]?.propPath;
|
|
5066
|
+
return propPath ? `还原 ${label} · ${propPath}` : `还原 ${label}`;
|
|
5067
|
+
};
|
|
5068
|
+
/**
|
|
5069
|
+
* 根据 old/new 是否为 null 推断 opType(与 push 时的约定一致)。
|
|
5070
|
+
*/
|
|
5071
|
+
var detectStackOpType = (oldVal, newVal) => {
|
|
5072
|
+
if (oldVal === null && newVal !== null) return "add";
|
|
5073
|
+
if (oldVal !== null && newVal === null) return "remove";
|
|
5074
|
+
return "update";
|
|
5075
|
+
};
|
|
5076
|
+
/**
|
|
5077
|
+
* 构造一条代码块 / 数据源「按 id 分栈」的历史记录:两者除 payload 字段命名外完全一致。
|
|
5078
|
+
*
|
|
5079
|
+
* - `add`:oldValue = null;`remove`:newValue = null;`update`:两者都有,可带 changeRecords 做局部更新。
|
|
5080
|
+
* - 内容会做 cloneDeep 防止后续被外部引用篡改;opType 依据 old/new 是否为 null 推断。
|
|
5081
|
+
* - 仅负责构造 step 并返回,入栈与事件 emit 由各公共方法(pushCodeBlock / pushDataSource)自行处理。
|
|
5082
|
+
* - 不直接驱动业务 service,调用方负责实际写回。
|
|
5083
|
+
*/
|
|
5084
|
+
var createStackStep = (id, payload) => {
|
|
5085
|
+
if (!id) return null;
|
|
5086
|
+
const oldSchema = payload.oldValue ? cloneDeep$1(payload.oldValue) : null;
|
|
5087
|
+
const newSchema = payload.newValue ? cloneDeep$1(payload.newValue) : null;
|
|
5088
|
+
const changeRecords = payload.changeRecords?.length ? cloneDeep$1(payload.changeRecords) : void 0;
|
|
5089
|
+
const opType = detectStackOpType(payload.oldValue, payload.newValue);
|
|
5090
|
+
return {
|
|
5091
|
+
uuid: (0, _tmagic_utils.guid)(),
|
|
5092
|
+
id,
|
|
5093
|
+
opType,
|
|
5094
|
+
diff: [{
|
|
5095
|
+
...newSchema !== null ? { newSchema } : {},
|
|
5096
|
+
...oldSchema !== null ? { oldSchema } : {},
|
|
5097
|
+
...opType === "update" && changeRecords ? { changeRecords } : {}
|
|
5098
|
+
}],
|
|
5099
|
+
historyDescription: payload.historyDescription,
|
|
5100
|
+
source: payload.source,
|
|
5101
|
+
timestamp: Date.now()
|
|
5102
|
+
};
|
|
5103
|
+
};
|
|
5104
|
+
var markStackSaved = (undoRedo) => {
|
|
5105
|
+
if (!undoRedo) return;
|
|
5106
|
+
undoRedo.updateElements((element) => {
|
|
5107
|
+
element.saved = false;
|
|
5108
|
+
});
|
|
5109
|
+
undoRedo.updateCurrentElement((element) => {
|
|
5110
|
+
element.saved = true;
|
|
5111
|
+
});
|
|
5112
|
+
};
|
|
5113
|
+
/**
|
|
5114
|
+
* 把单个「按 id 分栈」的历史栈(代码块 / 数据源)拆成若干 group:
|
|
5115
|
+
* - 把"新增/删除"独立成组(语义上属于一次性事件,不应与 update 合并);
|
|
5116
|
+
* - 连续 'update' 合并到同一组,组内 steps 顺序就是发生顺序。
|
|
5117
|
+
*
|
|
5118
|
+
* 代码块与数据源除 `kind` 外结构完全一致,统一由本方法处理;`kind` 决定返回的具体分组类型。
|
|
5119
|
+
*/
|
|
5120
|
+
var mergeStackSteps = (kind, id, list, cursor) => {
|
|
5121
|
+
const groups = [];
|
|
5122
|
+
let current = null;
|
|
5123
|
+
const currentIndex = cursor - 1;
|
|
5124
|
+
list.forEach((step, index) => {
|
|
5125
|
+
const { opType } = step;
|
|
5126
|
+
const applied = index < cursor;
|
|
5127
|
+
const isCurrent = index === currentIndex;
|
|
5128
|
+
if (opType === "update" && current?.opType === "update") {
|
|
5129
|
+
current.steps.push({
|
|
5130
|
+
step,
|
|
5131
|
+
index,
|
|
5132
|
+
applied,
|
|
5133
|
+
isCurrent
|
|
5134
|
+
});
|
|
5135
|
+
current.applied = applied;
|
|
5136
|
+
if (isCurrent) current.isCurrent = true;
|
|
5137
|
+
} else {
|
|
5138
|
+
current = {
|
|
5139
|
+
kind,
|
|
5140
|
+
id,
|
|
5141
|
+
opType,
|
|
5142
|
+
steps: [{
|
|
5034
5143
|
step,
|
|
5035
5144
|
index,
|
|
5036
5145
|
applied,
|
|
5037
5146
|
isCurrent
|
|
5038
|
-
}
|
|
5039
|
-
current.applied = applied;
|
|
5040
|
-
if (isCurrent) current.isCurrent = true;
|
|
5041
|
-
} else {
|
|
5042
|
-
current = {
|
|
5043
|
-
kind: "data-source",
|
|
5044
|
-
id: dataSourceId,
|
|
5045
|
-
opType,
|
|
5046
|
-
steps: [{
|
|
5047
|
-
step,
|
|
5048
|
-
index,
|
|
5049
|
-
applied,
|
|
5050
|
-
isCurrent
|
|
5051
|
-
}],
|
|
5052
|
-
applied,
|
|
5053
|
-
isCurrent
|
|
5054
|
-
};
|
|
5055
|
-
groups.push(current);
|
|
5056
|
-
}
|
|
5057
|
-
});
|
|
5058
|
-
return groups;
|
|
5059
|
-
}
|
|
5060
|
-
/**
|
|
5061
|
-
* 根据 old/new 是否为 null 推断 opType(与 push 时的约定一致)。
|
|
5062
|
-
*/
|
|
5063
|
-
static detectOpType(oldVal, newVal) {
|
|
5064
|
-
if (oldVal === null && newVal !== null) return "add";
|
|
5065
|
-
if (oldVal !== null && newVal === null) return "remove";
|
|
5066
|
-
return "update";
|
|
5067
|
-
}
|
|
5068
|
-
/**
|
|
5069
|
-
* 把页面栈拆成若干 group:
|
|
5070
|
-
* - 单节点的 'update' 按 targetId 与相邻同 targetId 的 update 合并到一个 group;
|
|
5071
|
-
* - 'add' / 'remove' 始终独立成组(语义上是结构变更,不应被收纳进单节点修改组);
|
|
5072
|
-
* - 多节点 'update'(如批量改属性)也独立成组(无明确单一目标,避免误合并)。
|
|
5073
|
-
*/
|
|
5074
|
-
static mergePageSteps(pageId, list, cursor) {
|
|
5075
|
-
const groups = [];
|
|
5076
|
-
let current = null;
|
|
5077
|
-
const currentIndex = cursor - 1;
|
|
5078
|
-
list.forEach((step, index) => {
|
|
5079
|
-
const applied = index < cursor;
|
|
5080
|
-
const isCurrent = index === currentIndex;
|
|
5081
|
-
const targetId = History.detectPageTargetId(step);
|
|
5082
|
-
const targetName = History.detectPageTargetName(step);
|
|
5083
|
-
const entry = {
|
|
5084
|
-
step,
|
|
5085
|
-
index,
|
|
5147
|
+
}],
|
|
5086
5148
|
applied,
|
|
5087
5149
|
isCurrent
|
|
5088
5150
|
};
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5151
|
+
groups.push(current);
|
|
5152
|
+
}
|
|
5153
|
+
});
|
|
5154
|
+
return groups;
|
|
5155
|
+
};
|
|
5156
|
+
/**
|
|
5157
|
+
* 把页面栈拆成若干 group:
|
|
5158
|
+
* - 单节点的 'update' 按 targetId 与相邻同 targetId 的 update 合并到一个 group;
|
|
5159
|
+
* - 'add' / 'remove' 始终独立成组(语义上是结构变更,不应被收纳进单节点修改组);
|
|
5160
|
+
* - 多节点 'update'(如批量改属性)也独立成组(无明确单一目标,避免误合并)。
|
|
5161
|
+
*/
|
|
5162
|
+
var mergePageSteps = (pageId, list, cursor) => {
|
|
5163
|
+
const groups = [];
|
|
5164
|
+
let current = null;
|
|
5165
|
+
const currentIndex = cursor - 1;
|
|
5166
|
+
list.forEach((step, index) => {
|
|
5167
|
+
const applied = index < cursor;
|
|
5168
|
+
const isCurrent = index === currentIndex;
|
|
5169
|
+
const targetId = detectPageTargetId(step);
|
|
5170
|
+
const targetName = detectPageTargetName(step);
|
|
5171
|
+
const entry = {
|
|
5172
|
+
step,
|
|
5173
|
+
index,
|
|
5174
|
+
applied,
|
|
5175
|
+
isCurrent
|
|
5176
|
+
};
|
|
5177
|
+
const mergeable = step.opType === "update" && targetId !== void 0;
|
|
5178
|
+
if (mergeable && current?.opType === "update" && current.targetId === targetId) {
|
|
5179
|
+
current.steps.push(entry);
|
|
5180
|
+
current.applied = applied;
|
|
5181
|
+
if (isCurrent) current.isCurrent = true;
|
|
5182
|
+
if (targetName) current.targetName = targetName;
|
|
5183
|
+
} else {
|
|
5184
|
+
current = {
|
|
5185
|
+
kind: "page",
|
|
5186
|
+
pageId,
|
|
5187
|
+
opType: step.opType,
|
|
5188
|
+
targetId: mergeable ? targetId : void 0,
|
|
5189
|
+
targetName,
|
|
5190
|
+
steps: [entry],
|
|
5191
|
+
applied,
|
|
5192
|
+
isCurrent
|
|
5193
|
+
};
|
|
5194
|
+
groups.push(current);
|
|
5195
|
+
}
|
|
5196
|
+
});
|
|
5197
|
+
return groups;
|
|
5198
|
+
};
|
|
5199
|
+
/**
|
|
5200
|
+
* 解析 StepValue 中的"目标节点 id"用于合并:
|
|
5201
|
+
* - 单节点 update:取唯一一项 updatedItems 的节点 id;
|
|
5202
|
+
* - 其它情形(多节点 update / add / remove):返回 undefined,表示不参与合并。
|
|
5203
|
+
*/
|
|
5204
|
+
var detectPageTargetId = (step) => {
|
|
5205
|
+
if (step.opType !== "update") return void 0;
|
|
5206
|
+
const items = step.diff;
|
|
5207
|
+
if (items?.length !== 1) return void 0;
|
|
5208
|
+
return items[0].newSchema?.id ?? items[0].oldSchema?.id;
|
|
5209
|
+
};
|
|
5210
|
+
/** 解析 StepValue 中的目标节点可读名(用于 UI 展示)。 */
|
|
5211
|
+
var detectPageTargetName = (step) => {
|
|
5212
|
+
const items = step.diff;
|
|
5213
|
+
if (step.opType === "update") {
|
|
5214
|
+
if (items?.length === 1) {
|
|
5215
|
+
const node = items[0].newSchema || items[0].oldSchema;
|
|
5216
|
+
return node?.name || node?.type || (node?.id !== void 0 ? `${node.id}` : void 0);
|
|
5217
|
+
}
|
|
5218
|
+
return items?.length ? `${items.length} 个节点` : void 0;
|
|
5110
5219
|
}
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
*/
|
|
5116
|
-
static detectPageTargetId(step) {
|
|
5117
|
-
if (step.opType !== "update") return void 0;
|
|
5118
|
-
const items = step.updatedItems;
|
|
5119
|
-
if (items?.length !== 1) return void 0;
|
|
5120
|
-
return items[0].newNode?.id ?? items[0].oldNode?.id;
|
|
5121
|
-
}
|
|
5122
|
-
/** 解析 StepValue 中的目标节点可读名(用于 UI 展示)。 */
|
|
5123
|
-
static detectPageTargetName(step) {
|
|
5124
|
-
if (step.opType === "update") {
|
|
5125
|
-
const items = step.updatedItems;
|
|
5126
|
-
if (items?.length === 1) {
|
|
5127
|
-
const node = items[0].newNode || items[0].oldNode;
|
|
5128
|
-
return node?.name || node?.type || (node?.id !== void 0 ? `${node.id}` : void 0);
|
|
5129
|
-
}
|
|
5130
|
-
return items?.length ? `${items.length} 个节点` : void 0;
|
|
5220
|
+
if (step.opType === "add") {
|
|
5221
|
+
if (items?.length === 1) {
|
|
5222
|
+
const n = items[0].newSchema;
|
|
5223
|
+
return n?.name || n?.type || `${n?.id}`;
|
|
5131
5224
|
}
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
return
|
|
5225
|
+
return items?.length ? `${items.length} 个节点` : void 0;
|
|
5226
|
+
}
|
|
5227
|
+
if (step.opType === "remove") {
|
|
5228
|
+
if (items?.length === 1) {
|
|
5229
|
+
const n = items[0].oldSchema;
|
|
5230
|
+
return n?.name || n?.type || `${n?.id}`;
|
|
5138
5231
|
}
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5232
|
+
return items?.length ? `${items.length} 个节点` : void 0;
|
|
5233
|
+
}
|
|
5234
|
+
};
|
|
5235
|
+
/** 把 `Record<Id, UndoRedo>` 整体序列化为 `Record<Id, SerializedUndoRedo>`。 */
|
|
5236
|
+
var serializeStacks = (stacks) => {
|
|
5237
|
+
const result = {};
|
|
5238
|
+
Object.entries(stacks).forEach(([id, undoRedo]) => {
|
|
5239
|
+
if (undoRedo) result[id] = undoRedo.serialize();
|
|
5240
|
+
});
|
|
5241
|
+
return result;
|
|
5242
|
+
};
|
|
5243
|
+
/**
|
|
5244
|
+
* 把 `Record<Id, SerializedUndoRedo>` 整体还原为 `Record<Id, UndoRedo>`。
|
|
5245
|
+
* 还原时把每个栈的游标定位到最近一条已保存(`saved === true`)记录之后。
|
|
5246
|
+
*/
|
|
5247
|
+
var deserializeStacks = (stacks = {}) => {
|
|
5248
|
+
const result = {};
|
|
5249
|
+
Object.entries(stacks).forEach(([id, serialized]) => {
|
|
5250
|
+
if (serialized) result[id] = UndoRedo.fromSerialized(serialized, { isSavedStep: (element) => element.saved === true });
|
|
5251
|
+
});
|
|
5252
|
+
return result;
|
|
5253
|
+
};
|
|
5254
|
+
/**
|
|
5255
|
+
* 按 id 从「按 id 分栈」的记录表(代码块 / 数据源)中获取(或创建)对应的 UndoRedo 栈。
|
|
5256
|
+
*/
|
|
5257
|
+
var getOrCreateStack = (stacks, id) => {
|
|
5258
|
+
if (!stacks[id]) stacks[id] = new UndoRedo();
|
|
5259
|
+
return stacks[id];
|
|
5260
|
+
};
|
|
5261
|
+
/**
|
|
5262
|
+
* 撤销下限:当页面栈 index 0 是 `opType: 'initial'` 的基线 step 时为 1(基线不可被撤销),否则为 0。
|
|
5263
|
+
* 用于把 cursor 钉在基线之上,保证 undo / canUndo / goto 都不会越过初始基线。
|
|
5264
|
+
*/
|
|
5265
|
+
var undoFloor = (undoRedo) => {
|
|
5266
|
+
return undoRedo.getElementList()[0]?.opType === "initial" ? 1 : 0;
|
|
5267
|
+
};
|
|
5268
|
+
//#endregion
|
|
5269
|
+
//#region packages/editor/src/utils/indexed-db.ts
|
|
5270
|
+
/**
|
|
5271
|
+
* 一组极简的、基于原生 IndexedDB 的 Promise KV 工具,避免引入额外依赖。
|
|
5272
|
+
* 仅用于浏览器环境;在不支持 IndexedDB 的环境(如 SSR / 部分测试环境)下会 reject。
|
|
5273
|
+
*/
|
|
5274
|
+
/** 是否处于支持 IndexedDB 的环境。 */
|
|
5275
|
+
var isIndexedDBSupported = () => typeof indexedDB !== "undefined" && indexedDB !== null;
|
|
5276
|
+
/**
|
|
5277
|
+
* 打开(必要时升级)数据库,确保目标 objectStore 存在后返回连接。
|
|
5278
|
+
*
|
|
5279
|
+
* 由于 objectStore 只能在 `onupgradeneeded` 内创建,这里先以当前版本打开,
|
|
5280
|
+
* 若发现 store 不存在则关闭连接、以更高版本重开来按需创建,兼容动态 storeName。
|
|
5281
|
+
*/
|
|
5282
|
+
var openIndexedDB = (dbName, storeName) => new Promise((resolve, reject) => {
|
|
5283
|
+
if (!isIndexedDBSupported()) {
|
|
5284
|
+
reject(/* @__PURE__ */ new Error("当前环境不支持 IndexedDB"));
|
|
5285
|
+
return;
|
|
5286
|
+
}
|
|
5287
|
+
const request = indexedDB.open(dbName);
|
|
5288
|
+
request.onupgradeneeded = () => {
|
|
5289
|
+
const db = request.result;
|
|
5290
|
+
if (!db.objectStoreNames.contains(storeName)) db.createObjectStore(storeName);
|
|
5291
|
+
};
|
|
5292
|
+
request.onerror = () => reject(request.error);
|
|
5293
|
+
request.onsuccess = () => {
|
|
5294
|
+
const db = request.result;
|
|
5295
|
+
if (db.objectStoreNames.contains(storeName)) {
|
|
5296
|
+
resolve(db);
|
|
5297
|
+
return;
|
|
5145
5298
|
}
|
|
5299
|
+
const nextVersion = db.version + 1;
|
|
5300
|
+
db.close();
|
|
5301
|
+
const upgradeRequest = indexedDB.open(dbName, nextVersion);
|
|
5302
|
+
upgradeRequest.onupgradeneeded = () => {
|
|
5303
|
+
const upgradeDb = upgradeRequest.result;
|
|
5304
|
+
if (!upgradeDb.objectStoreNames.contains(storeName)) upgradeDb.createObjectStore(storeName);
|
|
5305
|
+
};
|
|
5306
|
+
upgradeRequest.onerror = () => reject(upgradeRequest.error);
|
|
5307
|
+
upgradeRequest.onsuccess = () => resolve(upgradeRequest.result);
|
|
5308
|
+
};
|
|
5309
|
+
});
|
|
5310
|
+
/** 写入(覆盖)一条记录。value 通过结构化克隆存储,支持 Map / Set 等结构。 */
|
|
5311
|
+
var idbSet = async (dbName, storeName, key, value) => {
|
|
5312
|
+
const db = await openIndexedDB(dbName, storeName);
|
|
5313
|
+
try {
|
|
5314
|
+
await new Promise((resolve, reject) => {
|
|
5315
|
+
const tx = db.transaction(storeName, "readwrite");
|
|
5316
|
+
tx.objectStore(storeName).put(value, key);
|
|
5317
|
+
tx.oncomplete = () => resolve();
|
|
5318
|
+
tx.onabort = () => reject(tx.error);
|
|
5319
|
+
tx.onerror = () => reject(tx.error);
|
|
5320
|
+
});
|
|
5321
|
+
} finally {
|
|
5322
|
+
db.close();
|
|
5323
|
+
}
|
|
5324
|
+
};
|
|
5325
|
+
/** 读取一条记录,不存在时返回 undefined。 */
|
|
5326
|
+
var idbGet = async (dbName, storeName, key) => {
|
|
5327
|
+
const db = await openIndexedDB(dbName, storeName);
|
|
5328
|
+
try {
|
|
5329
|
+
return await new Promise((resolve, reject) => {
|
|
5330
|
+
const request = db.transaction(storeName, "readonly").objectStore(storeName).get(key);
|
|
5331
|
+
request.onsuccess = () => resolve(request.result);
|
|
5332
|
+
request.onerror = () => reject(request.error);
|
|
5333
|
+
});
|
|
5334
|
+
} finally {
|
|
5335
|
+
db.close();
|
|
5336
|
+
}
|
|
5337
|
+
};
|
|
5338
|
+
/** 删除一条记录。 */
|
|
5339
|
+
var idbDelete = async (dbName, storeName, key) => {
|
|
5340
|
+
const db = await openIndexedDB(dbName, storeName);
|
|
5341
|
+
try {
|
|
5342
|
+
await new Promise((resolve, reject) => {
|
|
5343
|
+
const tx = db.transaction(storeName, "readwrite");
|
|
5344
|
+
tx.objectStore(storeName).delete(key);
|
|
5345
|
+
tx.oncomplete = () => resolve();
|
|
5346
|
+
tx.onabort = () => reject(tx.error);
|
|
5347
|
+
tx.onerror = () => reject(tx.error);
|
|
5348
|
+
});
|
|
5349
|
+
} finally {
|
|
5350
|
+
db.close();
|
|
5146
5351
|
}
|
|
5352
|
+
};
|
|
5353
|
+
//#endregion
|
|
5354
|
+
//#region packages/editor/src/services/history.ts
|
|
5355
|
+
/** 历史记录持久化快照的默认存储位置与结构版本。 */
|
|
5356
|
+
var DEFAULT_DB_NAME = "tmagic-editor";
|
|
5357
|
+
var DEFAULT_STORE_NAME = "history";
|
|
5358
|
+
var DEFAULT_KEY = "default";
|
|
5359
|
+
var PERSIST_VERSION = 1;
|
|
5360
|
+
var History = class extends BaseService {
|
|
5147
5361
|
state = (0, vue.reactive)({
|
|
5148
5362
|
pageSteps: {},
|
|
5149
5363
|
pageId: void 0,
|
|
@@ -5183,6 +5397,53 @@
|
|
|
5183
5397
|
this.state.dataSourceState = {};
|
|
5184
5398
|
}
|
|
5185
5399
|
/**
|
|
5400
|
+
* 为指定页面 / 页面片种入一条「初始基线」记录(如加载 DSL 时的「初始 / 加载」基线)。
|
|
5401
|
+
*
|
|
5402
|
+
* 该记录是一条 `opType: 'initial'` 的 {@link StepValue},作为页面历史栈 **index 0 的固定底线**:
|
|
5403
|
+
* - 它是一条真实入栈的 step(随栈一起持久化),但被钉为撤销/回滚的下限——cursor 永不低于它,
|
|
5404
|
+
* 因此不会被 undo / goto / revert 触达(详见 {@link undo} / {@link setCanUndoRedo});
|
|
5405
|
+
* - 历史面板把它过滤出分组列表(见 {@link getPageHistoryGroups}),改由底部「初始」行展示。
|
|
5406
|
+
*
|
|
5407
|
+
* 仅当目标页面栈为空时种入(保证 initial 一定位于 index 0);已存在 initial 底线时默认不重复种入,
|
|
5408
|
+
* 传 `force=true` 且栈为空时按新基线种入。
|
|
5409
|
+
*/
|
|
5410
|
+
setPageMarker(pageId, options = {}) {
|
|
5411
|
+
if (pageId === void 0 || pageId === null || `${pageId}` === "") return null;
|
|
5412
|
+
const existing = this.getPageMarker(pageId);
|
|
5413
|
+
if (existing) return existing;
|
|
5414
|
+
const stack = getOrCreateStack(this.state.pageSteps, pageId);
|
|
5415
|
+
if (stack.getLength() > 0) return null;
|
|
5416
|
+
const marker = {
|
|
5417
|
+
uuid: (0, _tmagic_utils.guid)(),
|
|
5418
|
+
opType: "initial",
|
|
5419
|
+
diff: [],
|
|
5420
|
+
data: {
|
|
5421
|
+
name: options.name || "",
|
|
5422
|
+
id: pageId
|
|
5423
|
+
},
|
|
5424
|
+
selectedBefore: [],
|
|
5425
|
+
selectedAfter: [],
|
|
5426
|
+
modifiedNodeIds: /* @__PURE__ */ new Map(),
|
|
5427
|
+
historyDescription: options.description || "未修改的初始状态",
|
|
5428
|
+
timestamp: Date.now(),
|
|
5429
|
+
...options.source ? { source: options.source } : {}
|
|
5430
|
+
};
|
|
5431
|
+
stack.pushElement(marker);
|
|
5432
|
+
if (`${pageId}` === `${this.state.pageId}`) this.setCanUndoRedo();
|
|
5433
|
+
this.emit("page-marker-change", marker);
|
|
5434
|
+
return marker;
|
|
5435
|
+
}
|
|
5436
|
+
/**
|
|
5437
|
+
* 读取指定页面(缺省当前活动页)的初始基线 step(页面栈 index 0 且 `opType: 'initial'`);
|
|
5438
|
+
* 不存在时返回 undefined。
|
|
5439
|
+
*/
|
|
5440
|
+
getPageMarker(pageId) {
|
|
5441
|
+
const targetPageId = pageId ?? this.state.pageId;
|
|
5442
|
+
if (!targetPageId) return void 0;
|
|
5443
|
+
const first = this.state.pageSteps[targetPageId]?.getElementList()[0];
|
|
5444
|
+
return first?.opType === "initial" ? first : void 0;
|
|
5445
|
+
}
|
|
5446
|
+
/**
|
|
5186
5447
|
* 把一条步骤推入指定页面的栈;不指定 pageId 时落到当前活动页。
|
|
5187
5448
|
*
|
|
5188
5449
|
* 跨页操作(例如 `moveToContainer` 把节点搬到其它页)必须显式传入 `pageId`,
|
|
@@ -5191,11 +5452,31 @@
|
|
|
5191
5452
|
push(state, pageId) {
|
|
5192
5453
|
const undoRedo = this.getUndoRedo(pageId);
|
|
5193
5454
|
if (!undoRedo) return null;
|
|
5455
|
+
if (state.uuid === void 0) state.uuid = (0, _tmagic_utils.guid)();
|
|
5194
5456
|
if (state.timestamp === void 0) state.timestamp = Date.now();
|
|
5195
5457
|
undoRedo.pushElement(state);
|
|
5196
5458
|
if (pageId === void 0 || `${pageId}` === `${this.state.pageId}`) this.emit("change", state);
|
|
5197
5459
|
return state;
|
|
5198
5460
|
}
|
|
5461
|
+
/** 读取指定页面(缺省当前活动页)历史栈当前游标所在的 step(cursor - 1);无则返回 null。 */
|
|
5462
|
+
getCurrentPageStep(pageId) {
|
|
5463
|
+
const targetPageId = pageId ?? this.state.pageId;
|
|
5464
|
+
if (!targetPageId) return null;
|
|
5465
|
+
return this.state.pageSteps[targetPageId]?.getCurrentElement() ?? null;
|
|
5466
|
+
}
|
|
5467
|
+
/**
|
|
5468
|
+
* 用 `state` 替换指定页面栈当前游标所在的 step(并丢弃其后的重做尾部),游标不变。
|
|
5469
|
+
* 用于「连续 set root 记录合并」等就地替换最新一条的场景;替换成功后按需刷新 / 通知。
|
|
5470
|
+
*/
|
|
5471
|
+
replaceCurrentPageStep(state, pageId) {
|
|
5472
|
+
const undoRedo = this.getUndoRedo(pageId);
|
|
5473
|
+
if (!undoRedo) return null;
|
|
5474
|
+
if (state.uuid === void 0) state.uuid = (0, _tmagic_utils.guid)();
|
|
5475
|
+
if (state.timestamp === void 0) state.timestamp = Date.now();
|
|
5476
|
+
if (!undoRedo.replaceCurrentElement(state)) return null;
|
|
5477
|
+
this.emit("change", state);
|
|
5478
|
+
return state;
|
|
5479
|
+
}
|
|
5199
5480
|
/**
|
|
5200
5481
|
* 推入一条代码块变更记录(与页面/节点完全无关),按 `codeBlockId` 维度独立一份 UndoRedo 栈。
|
|
5201
5482
|
*
|
|
@@ -5206,16 +5487,15 @@
|
|
|
5206
5487
|
* - 不直接驱动 codeBlockService,调用方负责实际写回。
|
|
5207
5488
|
*/
|
|
5208
5489
|
pushCodeBlock(codeBlockId, payload) {
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
newContent: payload.newContent ? cloneDeep$1(payload.newContent) : null,
|
|
5214
|
-
changeRecords: payload.changeRecords?.length ? cloneDeep$1(payload.changeRecords) : void 0,
|
|
5490
|
+
const step = createStackStep(codeBlockId, {
|
|
5491
|
+
oldValue: payload.oldContent,
|
|
5492
|
+
newValue: payload.newContent,
|
|
5493
|
+
changeRecords: payload.changeRecords,
|
|
5215
5494
|
historyDescription: payload.historyDescription,
|
|
5216
|
-
|
|
5217
|
-
};
|
|
5218
|
-
|
|
5495
|
+
source: payload.source
|
|
5496
|
+
});
|
|
5497
|
+
if (!step) return null;
|
|
5498
|
+
getOrCreateStack(this.state.codeBlockState, codeBlockId).pushElement(step);
|
|
5219
5499
|
this.emit("code-block-history-change", codeBlockId, step);
|
|
5220
5500
|
return step;
|
|
5221
5501
|
}
|
|
@@ -5224,16 +5504,15 @@
|
|
|
5224
5504
|
* 行为同 pushCodeBlock(新增 oldSchema=null;删除 newSchema=null)。
|
|
5225
5505
|
*/
|
|
5226
5506
|
pushDataSource(dataSourceId, payload) {
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
newSchema: payload.newSchema ? cloneDeep$1(payload.newSchema) : null,
|
|
5232
|
-
changeRecords: payload.changeRecords?.length ? cloneDeep$1(payload.changeRecords) : void 0,
|
|
5507
|
+
const step = createStackStep(dataSourceId, {
|
|
5508
|
+
oldValue: payload.oldSchema,
|
|
5509
|
+
newValue: payload.newSchema,
|
|
5510
|
+
changeRecords: payload.changeRecords,
|
|
5233
5511
|
historyDescription: payload.historyDescription,
|
|
5234
|
-
|
|
5235
|
-
};
|
|
5236
|
-
|
|
5512
|
+
source: payload.source
|
|
5513
|
+
});
|
|
5514
|
+
if (!step) return null;
|
|
5515
|
+
getOrCreateStack(this.state.dataSourceState, dataSourceId).pushElement(step);
|
|
5237
5516
|
this.emit("data-source-history-change", dataSourceId, step);
|
|
5238
5517
|
return step;
|
|
5239
5518
|
}
|
|
@@ -5288,6 +5567,7 @@
|
|
|
5288
5567
|
undo() {
|
|
5289
5568
|
const undoRedo = this.getUndoRedo();
|
|
5290
5569
|
if (!undoRedo) return null;
|
|
5570
|
+
if (undoRedo.getCursor() <= undoFloor(undoRedo)) return null;
|
|
5291
5571
|
const state = undoRedo.undo();
|
|
5292
5572
|
this.emit("change", state);
|
|
5293
5573
|
return state;
|
|
@@ -5305,6 +5585,127 @@
|
|
|
5305
5585
|
this.removeAllPlugins();
|
|
5306
5586
|
}
|
|
5307
5587
|
/**
|
|
5588
|
+
* 清空指定页面(缺省当前活动页)的历史记录栈。
|
|
5589
|
+
* 仅删除撤销/重做记录,不会改动当前 DSL;清空后该页将无法再撤销/重做之前的操作。
|
|
5590
|
+
*/
|
|
5591
|
+
clearPage(pageId) {
|
|
5592
|
+
const targetPageId = pageId ?? this.state.pageId;
|
|
5593
|
+
if (!targetPageId) return;
|
|
5594
|
+
const marker = this.getPageMarker(targetPageId);
|
|
5595
|
+
this.state.pageSteps[targetPageId] = new UndoRedo();
|
|
5596
|
+
if (marker) this.setPageMarker(targetPageId, {
|
|
5597
|
+
name: marker.data?.name,
|
|
5598
|
+
description: marker.historyDescription,
|
|
5599
|
+
source: marker.source
|
|
5600
|
+
});
|
|
5601
|
+
if (`${targetPageId}` === `${this.state.pageId}`) {
|
|
5602
|
+
this.setCanUndoRedo();
|
|
5603
|
+
this.emit("clear-page", null);
|
|
5604
|
+
}
|
|
5605
|
+
}
|
|
5606
|
+
/**
|
|
5607
|
+
* 清空数据源历史记录栈:传入 `dataSourceId` 仅清空该数据源,缺省清空全部数据源。
|
|
5608
|
+
* 仅删除撤销/重做记录,不会改动数据源本身。
|
|
5609
|
+
*/
|
|
5610
|
+
clearDataSource(dataSourceId) {
|
|
5611
|
+
if (dataSourceId !== void 0) delete this.state.dataSourceState[dataSourceId];
|
|
5612
|
+
else this.state.dataSourceState = {};
|
|
5613
|
+
}
|
|
5614
|
+
/**
|
|
5615
|
+
* 清空代码块历史记录栈:传入 `codeBlockId` 仅清空该代码块,缺省清空全部代码块。
|
|
5616
|
+
* 仅删除撤销/重做记录,不会改动代码块本身。
|
|
5617
|
+
*/
|
|
5618
|
+
clearCodeBlock(codeBlockId) {
|
|
5619
|
+
if (codeBlockId !== void 0) delete this.state.codeBlockState[codeBlockId];
|
|
5620
|
+
else this.state.codeBlockState = {};
|
|
5621
|
+
}
|
|
5622
|
+
/**
|
|
5623
|
+
* 标记「整份 DSL 已保存」:把页面 / 代码块 / 数据源所有栈当前游标所在的记录都标为 `saved`。
|
|
5624
|
+
* 适用于「整体落库」场景;若只保存了其中一类,请改用更细粒度的
|
|
5625
|
+
* {@link markPageSaved} / {@link markCodeBlockSaved} / {@link markDataSourceSaved}。
|
|
5626
|
+
*/
|
|
5627
|
+
markSaved() {
|
|
5628
|
+
Object.values(this.state.pageSteps).forEach(markStackSaved);
|
|
5629
|
+
Object.values(this.state.codeBlockState).forEach(markStackSaved);
|
|
5630
|
+
Object.values(this.state.dataSourceState).forEach(markStackSaved);
|
|
5631
|
+
this.emit("mark-saved", { kind: "all" });
|
|
5632
|
+
}
|
|
5633
|
+
/**
|
|
5634
|
+
* 标记指定页面(缺省为当前活动页)的历史栈当前记录为已保存。
|
|
5635
|
+
* 仅影响该页面自己的栈,不波及代码块 / 数据源 / 其它页面。
|
|
5636
|
+
*/
|
|
5637
|
+
markPageSaved(pageId) {
|
|
5638
|
+
const targetPageId = pageId ?? this.state.pageId;
|
|
5639
|
+
if (!targetPageId) return;
|
|
5640
|
+
markStackSaved(this.state.pageSteps[targetPageId]);
|
|
5641
|
+
this.emit("mark-saved", {
|
|
5642
|
+
kind: "page",
|
|
5643
|
+
id: targetPageId
|
|
5644
|
+
});
|
|
5645
|
+
}
|
|
5646
|
+
/** 标记指定代码块的历史栈当前记录为已保存,仅影响该代码块自己的栈。 */
|
|
5647
|
+
markCodeBlockSaved(codeBlockId) {
|
|
5648
|
+
if (!codeBlockId) return;
|
|
5649
|
+
markStackSaved(this.state.codeBlockState[codeBlockId]);
|
|
5650
|
+
this.emit("mark-saved", {
|
|
5651
|
+
kind: "code-block",
|
|
5652
|
+
id: codeBlockId
|
|
5653
|
+
});
|
|
5654
|
+
}
|
|
5655
|
+
/** 标记指定数据源的历史栈当前记录为已保存,仅影响该数据源自己的栈。 */
|
|
5656
|
+
markDataSourceSaved(dataSourceId) {
|
|
5657
|
+
if (!dataSourceId) return;
|
|
5658
|
+
markStackSaved(this.state.dataSourceState[dataSourceId]);
|
|
5659
|
+
this.emit("mark-saved", {
|
|
5660
|
+
kind: "data-source",
|
|
5661
|
+
id: dataSourceId
|
|
5662
|
+
});
|
|
5663
|
+
}
|
|
5664
|
+
/**
|
|
5665
|
+
* 把当前内存中的全部历史栈(页面 / 代码块 / 数据源)序列化后写入本地 IndexedDB。
|
|
5666
|
+
*
|
|
5667
|
+
* - 每个 UndoRedo 栈连同其游标、容量一并保存,恢复后可继续 undo/redo;
|
|
5668
|
+
* - `key` 用于区分不同活动页 / 项目(同一 store 下可保存多份快照),缺省为 `default`;
|
|
5669
|
+
* - 返回写入成功的快照对象,便于调用方记录 savedAt 等信息;
|
|
5670
|
+
* - 不支持 IndexedDB 的环境(如 SSR)会 reject。
|
|
5671
|
+
*/
|
|
5672
|
+
async saveToIndexedDB(options = {}) {
|
|
5673
|
+
const { dbName = DEFAULT_DB_NAME, storeName = DEFAULT_STORE_NAME, key = DEFAULT_KEY, appId } = options;
|
|
5674
|
+
const snapshot = {
|
|
5675
|
+
version: PERSIST_VERSION,
|
|
5676
|
+
pageId: this.state.pageId,
|
|
5677
|
+
pageSteps: serializeStacks(this.state.pageSteps),
|
|
5678
|
+
codeBlockState: serializeStacks(this.state.codeBlockState),
|
|
5679
|
+
dataSourceState: serializeStacks(this.state.dataSourceState),
|
|
5680
|
+
savedAt: Date.now()
|
|
5681
|
+
};
|
|
5682
|
+
await idbSet(this.resolveDbName(dbName, appId), storeName, key, (0, serialize_javascript.default)(snapshot));
|
|
5683
|
+
this.emit("save-to-indexed-db", snapshot);
|
|
5684
|
+
return snapshot;
|
|
5685
|
+
}
|
|
5686
|
+
/**
|
|
5687
|
+
* 从本地 IndexedDB 读取此前保存的历史快照并重建全部撤销/重做栈。
|
|
5688
|
+
*
|
|
5689
|
+
* - 读取到的每个栈都会经 {@link UndoRedo.fromSerialized} 还原(含游标),随后可直接 undo/redo;
|
|
5690
|
+
* - 会整体覆盖当前内存中的历史状态,并把活动页恢复为快照中的 pageId;
|
|
5691
|
+
* - 找不到对应记录时返回 null,且不改动当前状态;
|
|
5692
|
+
* - 不支持 IndexedDB 的环境(如 SSR)会 reject。
|
|
5693
|
+
*/
|
|
5694
|
+
async restoreFromIndexedDB(options = {}) {
|
|
5695
|
+
const { dbName = DEFAULT_DB_NAME, storeName = DEFAULT_STORE_NAME, key = DEFAULT_KEY, appId } = options;
|
|
5696
|
+
const raw = await idbGet(this.resolveDbName(dbName, appId), storeName, key);
|
|
5697
|
+
if (!raw) return null;
|
|
5698
|
+
const snapshot = typeof raw === "string" ? getEditorConfig("parseDSL")(`(${raw})`) : raw;
|
|
5699
|
+
if (!snapshot) return null;
|
|
5700
|
+
this.state.pageSteps = deserializeStacks(snapshot.pageSteps);
|
|
5701
|
+
this.state.codeBlockState = deserializeStacks(snapshot.codeBlockState);
|
|
5702
|
+
this.state.dataSourceState = deserializeStacks(snapshot.dataSourceState);
|
|
5703
|
+
this.state.pageId = snapshot.pageId;
|
|
5704
|
+
this.setCanUndoRedo();
|
|
5705
|
+
this.emit("restore-from-indexed-db", snapshot);
|
|
5706
|
+
return snapshot;
|
|
5707
|
+
}
|
|
5708
|
+
/**
|
|
5308
5709
|
* 取出当前活动页的历史步骤平铺列表(包含已应用 + 已撤销)。
|
|
5309
5710
|
* 列表按时间正序,最早一步在最前面。
|
|
5310
5711
|
* 通常 UI 应使用 `getPageHistoryGroups` 取已合并分组的版本;本方法仅为兼容/调试保留。
|
|
@@ -5335,8 +5736,7 @@
|
|
|
5335
5736
|
if (!undoRedo) return [];
|
|
5336
5737
|
const list = undoRedo.getElementList();
|
|
5337
5738
|
if (!list.length) return [];
|
|
5338
|
-
|
|
5339
|
-
return History.mergePageSteps(targetPageId, list, cursor);
|
|
5739
|
+
return mergePageSteps(targetPageId, list, undoRedo.getCursor()).filter((group) => group.opType !== "initial");
|
|
5340
5740
|
}
|
|
5341
5741
|
/**
|
|
5342
5742
|
* 取出全部代码块的历史栈,按 codeBlockId 分组。
|
|
@@ -5352,7 +5752,7 @@
|
|
|
5352
5752
|
const list = undoRedo.getElementList();
|
|
5353
5753
|
if (!list.length) return;
|
|
5354
5754
|
const cursor = undoRedo.getCursor();
|
|
5355
|
-
groups.push(...
|
|
5755
|
+
groups.push(...mergeStackSteps("code-block", id, list, cursor));
|
|
5356
5756
|
});
|
|
5357
5757
|
return groups;
|
|
5358
5758
|
}
|
|
@@ -5402,6 +5802,44 @@
|
|
|
5402
5802
|
}));
|
|
5403
5803
|
}
|
|
5404
5804
|
/**
|
|
5805
|
+
* 按历史记录 uuid 在指定页面(默认当前活动页)的栈中查找其索引。
|
|
5806
|
+
* 找不到时返回 -1。供「按 uuid 回滚」等需要把 uuid 映射回 index 的场景使用。
|
|
5807
|
+
*/
|
|
5808
|
+
getPageStepIndexByUuid(uuid, pageId) {
|
|
5809
|
+
if (!uuid) return -1;
|
|
5810
|
+
return this.getPageStepList(pageId).findIndex((entry) => entry.step.uuid === uuid);
|
|
5811
|
+
}
|
|
5812
|
+
/**
|
|
5813
|
+
* 按历史记录 uuid 在全部代码块栈中查找其所属 codeBlockId 与索引。
|
|
5814
|
+
* 找不到时返回 null。
|
|
5815
|
+
*/
|
|
5816
|
+
findCodeBlockStepLocationByUuid(uuid) {
|
|
5817
|
+
if (!uuid) return null;
|
|
5818
|
+
for (const id of Object.keys(this.state.codeBlockState)) {
|
|
5819
|
+
const index = this.getCodeBlockStepList(id).findIndex((entry) => entry.step.uuid === uuid);
|
|
5820
|
+
if (index >= 0) return {
|
|
5821
|
+
id,
|
|
5822
|
+
index
|
|
5823
|
+
};
|
|
5824
|
+
}
|
|
5825
|
+
return null;
|
|
5826
|
+
}
|
|
5827
|
+
/**
|
|
5828
|
+
* 按历史记录 uuid 在全部数据源栈中查找其所属 dataSourceId 与索引。
|
|
5829
|
+
* 找不到时返回 null。
|
|
5830
|
+
*/
|
|
5831
|
+
findDataSourceStepLocationByUuid(uuid) {
|
|
5832
|
+
if (!uuid) return null;
|
|
5833
|
+
for (const id of Object.keys(this.state.dataSourceState)) {
|
|
5834
|
+
const index = this.getDataSourceStepList(id).findIndex((entry) => entry.step.uuid === uuid);
|
|
5835
|
+
if (index >= 0) return {
|
|
5836
|
+
id,
|
|
5837
|
+
index
|
|
5838
|
+
};
|
|
5839
|
+
}
|
|
5840
|
+
return null;
|
|
5841
|
+
}
|
|
5842
|
+
/**
|
|
5405
5843
|
* 取出全部数据源的历史栈,按 dataSourceId 分组。同上。
|
|
5406
5844
|
*/
|
|
5407
5845
|
getDataSourceHistoryGroups() {
|
|
@@ -5411,7 +5849,7 @@
|
|
|
5411
5849
|
const list = undoRedo.getElementList();
|
|
5412
5850
|
if (!list.length) return;
|
|
5413
5851
|
const cursor = undoRedo.getCursor();
|
|
5414
|
-
groups.push(...
|
|
5852
|
+
groups.push(...mergeStackSteps("data-source", id, list, cursor));
|
|
5415
5853
|
});
|
|
5416
5854
|
return groups;
|
|
5417
5855
|
}
|
|
@@ -5427,35 +5865,22 @@
|
|
|
5427
5865
|
if (!this.state.pageSteps[targetPageId]) this.state.pageSteps[targetPageId] = new UndoRedo();
|
|
5428
5866
|
return this.state.pageSteps[targetPageId];
|
|
5429
5867
|
}
|
|
5430
|
-
setCanUndoRedo() {
|
|
5431
|
-
const undoRedo = this.getUndoRedo();
|
|
5432
|
-
this.state.canRedo = undoRedo?.canRedo() || false;
|
|
5433
|
-
this.state.canUndo = undoRedo?.canUndo() || false;
|
|
5434
|
-
}
|
|
5435
5868
|
/**
|
|
5436
|
-
*
|
|
5869
|
+
* 把基础 dbName 与当前 DSL(root app)的 id 拼成最终库名,实现不同应用历史隔离。
|
|
5870
|
+
* 取不到 app id(如尚未加载 DSL)时退回基础 dbName。
|
|
5437
5871
|
*/
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
return
|
|
5872
|
+
resolveDbName(dbName, appId) {
|
|
5873
|
+
const resolvedAppId = appId ?? editor_default.get("root")?.id;
|
|
5874
|
+
return resolvedAppId ? `${dbName}-${resolvedAppId}` : dbName;
|
|
5441
5875
|
}
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
if (!this.state.dataSourceState[dataSourceId]) this.state.dataSourceState[dataSourceId] = new UndoRedo();
|
|
5447
|
-
return this.state.dataSourceState[dataSourceId];
|
|
5876
|
+
setCanUndoRedo() {
|
|
5877
|
+
const undoRedo = this.getUndoRedo();
|
|
5878
|
+
this.state.canRedo = undoRedo?.canRedo() || false;
|
|
5879
|
+
this.state.canUndo = undoRedo ? undoRedo.getCursor() > undoFloor(undoRedo) : false;
|
|
5448
5880
|
}
|
|
5449
5881
|
};
|
|
5450
5882
|
var history_default = new History();
|
|
5451
5883
|
//#endregion
|
|
5452
|
-
//#region packages/editor/src/utils/config.ts
|
|
5453
|
-
var $TMAGIC_EDITOR = {};
|
|
5454
|
-
var setEditorConfig = (option) => {
|
|
5455
|
-
$TMAGIC_EDITOR = option;
|
|
5456
|
-
};
|
|
5457
|
-
var getEditorConfig = (key) => $TMAGIC_EDITOR[key];
|
|
5458
|
-
//#endregion
|
|
5459
5884
|
//#region packages/editor/src/services/storage.ts
|
|
5460
5885
|
var Protocol = /* @__PURE__ */ function(Protocol) {
|
|
5461
5886
|
Protocol["OBJECT"] = "object";
|
|
@@ -6005,6 +6430,40 @@
|
|
|
6005
6430
|
aborted: false
|
|
6006
6431
|
};
|
|
6007
6432
|
};
|
|
6433
|
+
/**
|
|
6434
|
+
* 给「回滚」生成的新 step 用的简短描述生成器。
|
|
6435
|
+
* 与 UI 层 `describePageStep` 同义,但避免 service 反向依赖 layouts/,故放在此工具函数中。
|
|
6436
|
+
*/
|
|
6437
|
+
var describeStepForRevert = (step) => {
|
|
6438
|
+
const items = step.diff ?? [];
|
|
6439
|
+
const withId = (node, label) => {
|
|
6440
|
+
const id = node?.id;
|
|
6441
|
+
if (id === void 0 || id === null || `${id}` === "") return label;
|
|
6442
|
+
return label ? `${label}(id: ${id})` : `id: ${id}`;
|
|
6443
|
+
};
|
|
6444
|
+
switch (step.opType) {
|
|
6445
|
+
case "add": {
|
|
6446
|
+
const count = items.length;
|
|
6447
|
+
const node = items[0]?.newSchema;
|
|
6448
|
+
const label = node?.name || node?.type || "";
|
|
6449
|
+
return `撤回新增 ${count} 个节点${count === 1 ? `(${withId(node, label)})` : ""}`;
|
|
6450
|
+
}
|
|
6451
|
+
case "remove": {
|
|
6452
|
+
const count = items.length;
|
|
6453
|
+
const node = items[0]?.oldSchema;
|
|
6454
|
+
const label = node?.name || node?.type || "";
|
|
6455
|
+
return `还原已删除的 ${count} 个节点${count === 1 ? `(${withId(node, label)})` : ""}`;
|
|
6456
|
+
}
|
|
6457
|
+
default:
|
|
6458
|
+
if (items.length === 1) {
|
|
6459
|
+
const { newSchema, oldSchema, changeRecords } = items[0];
|
|
6460
|
+
const target = withId(newSchema || oldSchema, newSchema?.name || newSchema?.type || oldSchema?.name || oldSchema?.type || "");
|
|
6461
|
+
const propPath = changeRecords?.[0]?.propPath;
|
|
6462
|
+
return propPath ? `还原 ${target} · ${propPath}` : `还原 ${target}`;
|
|
6463
|
+
}
|
|
6464
|
+
return `还原 ${items.length} 个节点的修改`;
|
|
6465
|
+
}
|
|
6466
|
+
};
|
|
6008
6467
|
//#endregion
|
|
6009
6468
|
//#region packages/editor/src/utils/operator.ts
|
|
6010
6469
|
/**
|
|
@@ -6073,35 +6532,15 @@
|
|
|
6073
6532
|
//#endregion
|
|
6074
6533
|
//#region packages/editor/src/services/editor.ts
|
|
6075
6534
|
/**
|
|
6076
|
-
*
|
|
6077
|
-
*
|
|
6535
|
+
* 把「变更前后节点快照」列表归一成 update 类型的 {@link StepDiffItem} 列表,供 {@link StepValue.diff} 使用。
|
|
6536
|
+
* `changeRecords` 来自 form 端的 propPath/value 列表,撤销/重做时只对这些 propPath 做局部更新;
|
|
6537
|
+
* 缺省(未传 / 空数组)才退化为整节点替换。
|
|
6078
6538
|
*/
|
|
6079
|
-
var
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
const label = node?.name || node?.type || (node?.id !== void 0 ? `${node.id}` : "");
|
|
6085
|
-
return `撤回新增 ${count} 个节点${count === 1 && label ? `(${label})` : ""}`;
|
|
6086
|
-
}
|
|
6087
|
-
case "remove": {
|
|
6088
|
-
const count = step.removedItems?.length ?? 0;
|
|
6089
|
-
const node = step.removedItems?.[0]?.node;
|
|
6090
|
-
const label = node?.name || node?.type || (node?.id !== void 0 ? `${node.id}` : "");
|
|
6091
|
-
return `还原已删除的 ${count} 个节点${count === 1 && label ? `(${label})` : ""}`;
|
|
6092
|
-
}
|
|
6093
|
-
default: {
|
|
6094
|
-
const items = step.updatedItems ?? [];
|
|
6095
|
-
if (items.length === 1) {
|
|
6096
|
-
const { newNode, oldNode, changeRecords } = items[0];
|
|
6097
|
-
const target = newNode?.name || newNode?.type || oldNode?.name || oldNode?.type || `${newNode?.id ?? ""}`;
|
|
6098
|
-
const propPath = changeRecords?.[0]?.propPath;
|
|
6099
|
-
return propPath ? `还原 ${target} · ${propPath}` : `还原 ${target}`;
|
|
6100
|
-
}
|
|
6101
|
-
return `还原 ${items.length} 个节点的修改`;
|
|
6102
|
-
}
|
|
6103
|
-
}
|
|
6104
|
-
};
|
|
6539
|
+
var buildUpdateDiff = (items) => items.map(({ oldNode, newNode, changeRecords }) => ({
|
|
6540
|
+
oldSchema: oldNode,
|
|
6541
|
+
newSchema: newNode,
|
|
6542
|
+
...changeRecords?.length ? { changeRecords } : {}
|
|
6543
|
+
}));
|
|
6105
6544
|
var Editor = class extends BaseService {
|
|
6106
6545
|
state = (0, vue.reactive)({
|
|
6107
6546
|
root: null,
|
|
@@ -6119,6 +6558,12 @@
|
|
|
6119
6558
|
alwaysMultiSelect: false
|
|
6120
6559
|
});
|
|
6121
6560
|
selectionBeforeOp = null;
|
|
6561
|
+
/**
|
|
6562
|
+
* 最近一次 pushOpHistory 写入的历史记录 uuid。
|
|
6563
|
+
* 供 *AndGetHistoryId 系列方法在调用普通操作后取回本次产生的历史记录 id;
|
|
6564
|
+
* 普通操作不会读取它,调用前由 *AndGetHistoryId 重置为 null。
|
|
6565
|
+
*/
|
|
6566
|
+
lastPushedHistoryId = null;
|
|
6122
6567
|
constructor() {
|
|
6123
6568
|
super(canUsePluginMethods.async.map((methodName) => ({
|
|
6124
6569
|
name: methodName,
|
|
@@ -6133,8 +6578,9 @@
|
|
|
6133
6578
|
* 设置当前指点节点配置
|
|
6134
6579
|
* @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength' | 'pageFragmentLength
|
|
6135
6580
|
* @param value MNode
|
|
6581
|
+
* @param options.historySource 设置 root 时,本次变更写入历史记录的「操作来源」(仅 name === 'root' 时生效)
|
|
6136
6582
|
*/
|
|
6137
|
-
set(name, value) {
|
|
6583
|
+
set(name, value, options = {}) {
|
|
6138
6584
|
const preValue = this.state[name];
|
|
6139
6585
|
this.state[name] = value;
|
|
6140
6586
|
if (name === "nodes" && Array.isArray(value)) this.set("node", value[0]);
|
|
@@ -6145,6 +6591,13 @@
|
|
|
6145
6591
|
this.state.pageLength = getPageList(app).length || 0;
|
|
6146
6592
|
this.state.pageFragmentLength = getPageFragmentList(app).length || 0;
|
|
6147
6593
|
this.state.stageLoading = this.state.pageLength !== 0;
|
|
6594
|
+
if (preValue && !isEmpty(preValue)) this.pushRootDiffHistory(preValue, app, options.historySource);
|
|
6595
|
+
else app.items?.forEach((pageNode) => {
|
|
6596
|
+
if (pageNode?.id !== void 0 && !history_default.getPageMarker(pageNode.id)) history_default.setPageMarker(pageNode.id, {
|
|
6597
|
+
name: pageNode.name,
|
|
6598
|
+
source: options.historySource
|
|
6599
|
+
});
|
|
6600
|
+
});
|
|
6148
6601
|
} else {
|
|
6149
6602
|
this.state.pageLength = 0;
|
|
6150
6603
|
this.state.pageFragmentLength = 0;
|
|
@@ -6170,7 +6623,23 @@
|
|
|
6170
6623
|
getNodeInfo(id, raw = true) {
|
|
6171
6624
|
let root = this.get("root");
|
|
6172
6625
|
if (raw) root = (0, vue.toRaw)(root);
|
|
6173
|
-
|
|
6626
|
+
if (!root) return {
|
|
6627
|
+
node: null,
|
|
6628
|
+
parent: null,
|
|
6629
|
+
page: null
|
|
6630
|
+
};
|
|
6631
|
+
if (id === root.id) return {
|
|
6632
|
+
node: root,
|
|
6633
|
+
parent: null,
|
|
6634
|
+
page: null
|
|
6635
|
+
};
|
|
6636
|
+
const pageIdStr = `${this.get("page")?.id || ""}`;
|
|
6637
|
+
const currentPageNode = root.items?.find((item) => `${item.id}` === pageIdStr);
|
|
6638
|
+
if (currentPageNode && `${id}` !== pageIdStr) {
|
|
6639
|
+
const info = (0, _tmagic_utils.getNodeInfo)(id, currentPageNode);
|
|
6640
|
+
if (info.node) return info;
|
|
6641
|
+
}
|
|
6642
|
+
return (0, _tmagic_utils.getNodeInfo)(id, root, currentPageNode);
|
|
6174
6643
|
}
|
|
6175
6644
|
/**
|
|
6176
6645
|
* 根据ID获取指点节点配置
|
|
@@ -6330,7 +6799,7 @@
|
|
|
6330
6799
|
* @param options.doNotPushHistory 是否不写入历史记录(默认 false)
|
|
6331
6800
|
* @returns 添加后的节点
|
|
6332
6801
|
*/
|
|
6333
|
-
async add(addNode, parent, { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false, historyDescription } = {}) {
|
|
6802
|
+
async add(addNode, parent, { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false, historyDescription, historySource } = {}) {
|
|
6334
6803
|
this.captureSelectionBeforeOp();
|
|
6335
6804
|
const stage = this.get("stage");
|
|
6336
6805
|
const addNodes = [];
|
|
@@ -6363,18 +6832,26 @@
|
|
|
6363
6832
|
}
|
|
6364
6833
|
if (!((0, _tmagic_utils.isPage)(newNodes[0]) || (0, _tmagic_utils.isPageFragment)(newNodes[0]))) {
|
|
6365
6834
|
const pageForOp = this.getNodeInfo(newNodes[0].id, false).page;
|
|
6366
|
-
if (!doNotPushHistory)
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
|
|
6370
|
-
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
|
|
6835
|
+
if (!doNotPushHistory) {
|
|
6836
|
+
const parentId = (this.getParentById(newNodes[0].id, false) ?? this.get("root")).id;
|
|
6837
|
+
this.pushOpHistory("add", {
|
|
6838
|
+
diff: newNodes.map((n) => {
|
|
6839
|
+
const p = this.getParentById(n.id, false);
|
|
6840
|
+
const idx = p ? getNodeIndex(n.id, p) : -1;
|
|
6841
|
+
return {
|
|
6842
|
+
newSchema: cloneDeep$1((0, vue.toRaw)(n)),
|
|
6843
|
+
parentId,
|
|
6844
|
+
index: typeof idx === "number" ? idx : -1
|
|
6845
|
+
};
|
|
6846
|
+
}),
|
|
6847
|
+
pageData: {
|
|
6848
|
+
name: pageForOp?.name || "",
|
|
6849
|
+
id: pageForOp.id
|
|
6850
|
+
},
|
|
6851
|
+
historyDescription,
|
|
6852
|
+
source: historySource
|
|
6853
|
+
});
|
|
6854
|
+
} else this.selectionBeforeOp = null;
|
|
6378
6855
|
}
|
|
6379
6856
|
this.emit("add", newNodes);
|
|
6380
6857
|
return Array.isArray(addNode) ? newNodes : newNodes[0];
|
|
@@ -6440,7 +6917,7 @@
|
|
|
6440
6917
|
* @param options.doNotSwitchPage 删除后是否不切换当前页面(默认 false;删除页面 / 页面片段时为 true 会跳过自动切换到首个剩余页面)
|
|
6441
6918
|
* @param options.doNotPushHistory 是否不写入历史记录(默认 false)
|
|
6442
6919
|
*/
|
|
6443
|
-
async remove(nodeOrNodeList, { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false, historyDescription } = {}) {
|
|
6920
|
+
async remove(nodeOrNodeList, { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false, historyDescription, historySource } = {}) {
|
|
6444
6921
|
this.captureSelectionBeforeOp();
|
|
6445
6922
|
const nodes = Array.isArray(nodeOrNodeList) ? nodeOrNodeList : [nodeOrNodeList];
|
|
6446
6923
|
const removedItems = [];
|
|
@@ -6454,7 +6931,7 @@
|
|
|
6454
6931
|
};
|
|
6455
6932
|
const idx = getNodeIndex(curNode.id, parent);
|
|
6456
6933
|
removedItems.push({
|
|
6457
|
-
|
|
6934
|
+
oldSchema: cloneDeep$1((0, vue.toRaw)(curNode)),
|
|
6458
6935
|
parentId: parent.id,
|
|
6459
6936
|
index: typeof idx === "number" ? idx : -1
|
|
6460
6937
|
});
|
|
@@ -6464,11 +6941,16 @@
|
|
|
6464
6941
|
doNotSelect,
|
|
6465
6942
|
doNotSwitchPage
|
|
6466
6943
|
})));
|
|
6467
|
-
if (removedItems.length > 0 && pageForOp) if (!doNotPushHistory) this.pushOpHistory("remove", {
|
|
6944
|
+
if (removedItems.length > 0 && pageForOp) if (!doNotPushHistory) this.pushOpHistory("remove", {
|
|
6945
|
+
diff: removedItems,
|
|
6946
|
+
pageData: pageForOp,
|
|
6947
|
+
historyDescription,
|
|
6948
|
+
source: historySource
|
|
6949
|
+
});
|
|
6468
6950
|
else this.selectionBeforeOp = null;
|
|
6469
6951
|
this.emit("remove", nodes);
|
|
6470
6952
|
}
|
|
6471
|
-
async doUpdate(config, { changeRecords = [] } = {}) {
|
|
6953
|
+
async doUpdate(config, { changeRecords = [], historySource } = {}) {
|
|
6472
6954
|
const root = this.get("root");
|
|
6473
6955
|
if (!root) throw new Error("root为空");
|
|
6474
6956
|
if (!config?.id) throw new Error("没有配置或者配置缺少id值");
|
|
@@ -6479,7 +6961,7 @@
|
|
|
6479
6961
|
newConfig = mergeWith(cloneDeep$1(node), newConfig, editorNodeMergeCustomizer);
|
|
6480
6962
|
if (!newConfig.type) throw new Error("配置缺少type值");
|
|
6481
6963
|
if (newConfig.type === _tmagic_core.NodeType.ROOT) {
|
|
6482
|
-
this.set("root", newConfig);
|
|
6964
|
+
this.set("root", newConfig, { historySource });
|
|
6483
6965
|
return {
|
|
6484
6966
|
oldNode: node,
|
|
6485
6967
|
newNode: newConfig,
|
|
@@ -6525,23 +7007,31 @@
|
|
|
6525
7007
|
*/
|
|
6526
7008
|
async update(config, data = {}) {
|
|
6527
7009
|
this.captureSelectionBeforeOp();
|
|
6528
|
-
const { doNotPushHistory = false, changeRecordList, changeRecords, historyDescription } = data;
|
|
7010
|
+
const { doNotPushHistory = false, changeRecordList, changeRecords, historyDescription, historySource } = data;
|
|
6529
7011
|
const nodes = Array.isArray(config) ? config : [config];
|
|
6530
7012
|
const updateData = await Promise.all(nodes.map((node, index) => {
|
|
6531
7013
|
const recordsForNode = changeRecordList ? changeRecordList[index] ?? [] : changeRecords ?? [];
|
|
6532
|
-
return this.doUpdate(node, {
|
|
7014
|
+
return this.doUpdate(node, {
|
|
7015
|
+
changeRecords: recordsForNode,
|
|
7016
|
+
historySource
|
|
7017
|
+
});
|
|
6533
7018
|
}));
|
|
6534
7019
|
if (updateData[0].oldNode?.type !== _tmagic_core.NodeType.ROOT) {
|
|
6535
7020
|
if (this.get("nodes").length) if (!doNotPushHistory) {
|
|
6536
7021
|
const pageForOp = this.getNodeInfo(nodes[0].id, false).page;
|
|
6537
|
-
this.pushOpHistory("update", {
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
7022
|
+
this.pushOpHistory("update", {
|
|
7023
|
+
diff: buildUpdateDiff(updateData.map((d) => ({
|
|
7024
|
+
oldNode: cloneDeep$1(d.oldNode),
|
|
7025
|
+
newNode: cloneDeep$1(d.newNode),
|
|
7026
|
+
changeRecords: d.changeRecords?.length ? cloneDeep$1(d.changeRecords) : void 0
|
|
7027
|
+
}))),
|
|
7028
|
+
pageData: {
|
|
7029
|
+
name: pageForOp?.name || "",
|
|
7030
|
+
id: pageForOp.id
|
|
7031
|
+
},
|
|
7032
|
+
historyDescription,
|
|
7033
|
+
source: historySource
|
|
7034
|
+
});
|
|
6545
7035
|
} else this.selectionBeforeOp = null;
|
|
6546
7036
|
}
|
|
6547
7037
|
this.emit("update", updateData);
|
|
@@ -6557,7 +7047,7 @@
|
|
|
6557
7047
|
* @param options.doNotPushHistory 是否不写入历史记录(默认 false)
|
|
6558
7048
|
* @returns void
|
|
6559
7049
|
*/
|
|
6560
|
-
async sort(id1, id2, { doNotSelect = false, doNotPushHistory = false } = {}) {
|
|
7050
|
+
async sort(id1, id2, { doNotSelect = false, doNotPushHistory = false, historySource } = {}) {
|
|
6561
7051
|
this.captureSelectionBeforeOp();
|
|
6562
7052
|
const root = this.get("root");
|
|
6563
7053
|
if (!root) throw new Error("root为空");
|
|
@@ -6569,7 +7059,10 @@
|
|
|
6569
7059
|
if (index2 < 0) return;
|
|
6570
7060
|
const index1 = parent.items.findIndex((node) => `${node.id}` === `${id1}`);
|
|
6571
7061
|
parent.items.splice(index2, 0, ...parent.items.splice(index1, 1));
|
|
6572
|
-
await this.update(parent, {
|
|
7062
|
+
await this.update(parent, {
|
|
7063
|
+
doNotPushHistory,
|
|
7064
|
+
historySource
|
|
7065
|
+
});
|
|
6573
7066
|
if (!doNotSelect) await this.select(node);
|
|
6574
7067
|
this.get("stage")?.update({
|
|
6575
7068
|
config: cloneDeep$1(node),
|
|
@@ -6605,7 +7098,7 @@
|
|
|
6605
7098
|
* @param options.doNotPushHistory 是否不写入历史记录(默认 false)
|
|
6606
7099
|
* @returns 添加后的组件节点配置
|
|
6607
7100
|
*/
|
|
6608
|
-
async paste(position = {}, collectorOptions, { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false } = {}) {
|
|
7101
|
+
async paste(position = {}, collectorOptions, { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false, historyDescription, historySource } = {}) {
|
|
6609
7102
|
const config = storage_default.getItem(COPY_STORAGE_KEY);
|
|
6610
7103
|
if (!Array.isArray(config)) return;
|
|
6611
7104
|
const node = this.get("node");
|
|
@@ -6619,7 +7112,9 @@
|
|
|
6619
7112
|
return this.add(pasteConfigs, parent, {
|
|
6620
7113
|
doNotSelect,
|
|
6621
7114
|
doNotSwitchPage,
|
|
6622
|
-
doNotPushHistory
|
|
7115
|
+
doNotPushHistory,
|
|
7116
|
+
historyDescription,
|
|
7117
|
+
historySource
|
|
6623
7118
|
});
|
|
6624
7119
|
}
|
|
6625
7120
|
async doPaste(config, position = {}) {
|
|
@@ -6647,11 +7142,15 @@
|
|
|
6647
7142
|
* @param options.doNotPushHistory 是否不写入历史记录(默认 false)
|
|
6648
7143
|
* @returns 当前组件节点配置
|
|
6649
7144
|
*/
|
|
6650
|
-
async alignCenter(config, { doNotSelect = false, doNotPushHistory = false } = {}) {
|
|
7145
|
+
async alignCenter(config, { doNotSelect = false, doNotPushHistory = false, historyDescription, historySource } = {}) {
|
|
6651
7146
|
const nodes = Array.isArray(config) ? config : [config];
|
|
6652
7147
|
const stage = this.get("stage");
|
|
6653
7148
|
const newNodes = await Promise.all(nodes.map((node) => this.doAlignCenter(node)));
|
|
6654
|
-
const newNode = await this.update(newNodes, {
|
|
7149
|
+
const newNode = await this.update(newNodes, {
|
|
7150
|
+
doNotPushHistory,
|
|
7151
|
+
historyDescription,
|
|
7152
|
+
historySource
|
|
7153
|
+
});
|
|
6655
7154
|
if (!doNotSelect) if (newNodes.length > 1) await stage?.multiSelect(newNodes.map((node) => node.id));
|
|
6656
7155
|
else await stage?.select(newNodes[0].id);
|
|
6657
7156
|
return newNode;
|
|
@@ -6662,7 +7161,7 @@
|
|
|
6662
7161
|
* @param options 可选配置
|
|
6663
7162
|
* @param options.doNotPushHistory 是否不写入历史记录(默认 false)
|
|
6664
7163
|
*/
|
|
6665
|
-
async moveLayer(offset, { doNotPushHistory = false } = {}) {
|
|
7164
|
+
async moveLayer(offset, { doNotPushHistory = false, historyDescription, historySource } = {}) {
|
|
6666
7165
|
this.captureSelectionBeforeOp();
|
|
6667
7166
|
const root = this.get("root");
|
|
6668
7167
|
if (!root) throw new Error("root为空");
|
|
@@ -6687,12 +7186,17 @@
|
|
|
6687
7186
|
this.addModifiedNodeId(parent.id);
|
|
6688
7187
|
if (!doNotPushHistory) {
|
|
6689
7188
|
const pageForOp = this.getNodeInfo(node.id, false).page;
|
|
6690
|
-
this.pushOpHistory("update", {
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
7189
|
+
this.pushOpHistory("update", {
|
|
7190
|
+
diff: buildUpdateDiff([{
|
|
7191
|
+
oldNode: oldParent,
|
|
7192
|
+
newNode: cloneDeep$1((0, vue.toRaw)(parent))
|
|
7193
|
+
}]),
|
|
7194
|
+
pageData: {
|
|
7195
|
+
name: pageForOp?.name || "",
|
|
7196
|
+
id: pageForOp.id
|
|
7197
|
+
},
|
|
7198
|
+
historyDescription,
|
|
7199
|
+
source: historySource
|
|
6696
7200
|
});
|
|
6697
7201
|
} else this.selectionBeforeOp = null;
|
|
6698
7202
|
this.emit("move-layer", offset);
|
|
@@ -6711,7 +7215,7 @@
|
|
|
6711
7215
|
* @param options.doNotSwitchPage 移动后是否不切换当前页面(默认 false;目标容器位于其它页面时为 true 会跳过自动选中以避免页面切换)
|
|
6712
7216
|
* @param options.doNotPushHistory 是否不写入历史记录(默认 false)
|
|
6713
7217
|
*/
|
|
6714
|
-
async moveToContainer(config, targetId, { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false } = {}) {
|
|
7218
|
+
async moveToContainer(config, targetId, { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false, historyDescription, historySource } = {}) {
|
|
6715
7219
|
const isBatch = Array.isArray(config);
|
|
6716
7220
|
const configs = (isBatch ? config : [config]).filter((item) => !((0, _tmagic_utils.isPage)(item) || (0, _tmagic_utils.isPageFragment)(item)));
|
|
6717
7221
|
if (configs.length === 0) throw new Error("没有可移动的节点");
|
|
@@ -6759,11 +7263,16 @@
|
|
|
6759
7263
|
name: "",
|
|
6760
7264
|
id: target.id
|
|
6761
7265
|
};
|
|
6762
|
-
this.pushOpHistory("update", {
|
|
7266
|
+
this.pushOpHistory("update", {
|
|
7267
|
+
diff: buildUpdateDiff(updatedItems),
|
|
7268
|
+
pageData: historyPage,
|
|
7269
|
+
historyDescription,
|
|
7270
|
+
source: historySource
|
|
7271
|
+
});
|
|
6763
7272
|
} else this.selectionBeforeOp = null;
|
|
6764
7273
|
return isBatch ? newConfigs : newConfigs[0];
|
|
6765
7274
|
}
|
|
6766
|
-
async dragTo(config, targetParent, targetIndex, { doNotPushHistory = false } = {}) {
|
|
7275
|
+
async dragTo(config, targetParent, targetIndex, { doNotPushHistory = false, historyDescription, historySource } = {}) {
|
|
6767
7276
|
this.captureSelectionBeforeOp();
|
|
6768
7277
|
if (!targetParent || !Array.isArray(targetParent.items)) return;
|
|
6769
7278
|
const configs = Array.isArray(config) ? config : [config];
|
|
@@ -6805,9 +7314,14 @@
|
|
|
6805
7314
|
}
|
|
6806
7315
|
if (!doNotPushHistory) {
|
|
6807
7316
|
const pageForOp = this.getNodeInfo(configs[0].id, false).page;
|
|
6808
|
-
this.pushOpHistory("update", {
|
|
6809
|
-
|
|
6810
|
-
|
|
7317
|
+
this.pushOpHistory("update", {
|
|
7318
|
+
diff: buildUpdateDiff(updatedItems),
|
|
7319
|
+
pageData: {
|
|
7320
|
+
name: pageForOp?.name || "",
|
|
7321
|
+
id: pageForOp.id
|
|
7322
|
+
},
|
|
7323
|
+
historyDescription,
|
|
7324
|
+
source: historySource
|
|
6811
7325
|
});
|
|
6812
7326
|
} else this.selectionBeforeOp = null;
|
|
6813
7327
|
this.emit("drag-to", {
|
|
@@ -6817,6 +7331,49 @@
|
|
|
6817
7331
|
});
|
|
6818
7332
|
}
|
|
6819
7333
|
/**
|
|
7334
|
+
* 下列 *AndGetHistoryId 方法与对应的普通操作(add / remove / update ...)行为完全一致,
|
|
7335
|
+
* 唯一区别是返回值为本次写入历史栈的历史记录 uuid({@link StepValue.uuid}),
|
|
7336
|
+
* 而非节点 / 节点数组。可用于精确引用 / 定位该条历史记录(埋点、revert、跨端同步等)。
|
|
7337
|
+
*
|
|
7338
|
+
* 当本次操作未写入历史(doNotPushHistory 为 true、或操作无实际变更 / 提前返回)时返回 null。
|
|
7339
|
+
*/
|
|
7340
|
+
/** 等价于 {@link add},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
|
|
7341
|
+
async addAndGetHistoryId(addNode, parent, options = {}) {
|
|
7342
|
+
this.lastPushedHistoryId = null;
|
|
7343
|
+
await this.add(addNode, parent, options);
|
|
7344
|
+
return this.lastPushedHistoryId;
|
|
7345
|
+
}
|
|
7346
|
+
/** 等价于 {@link remove},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
|
|
7347
|
+
async removeAndGetHistoryId(nodeOrNodeList, options = {}) {
|
|
7348
|
+
this.lastPushedHistoryId = null;
|
|
7349
|
+
await this.remove(nodeOrNodeList, options);
|
|
7350
|
+
return this.lastPushedHistoryId;
|
|
7351
|
+
}
|
|
7352
|
+
/** 等价于 {@link update},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
|
|
7353
|
+
async updateAndGetHistoryId(config, data = {}) {
|
|
7354
|
+
this.lastPushedHistoryId = null;
|
|
7355
|
+
await this.update(config, data);
|
|
7356
|
+
return this.lastPushedHistoryId;
|
|
7357
|
+
}
|
|
7358
|
+
/** 等价于 {@link moveLayer},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
|
|
7359
|
+
async moveLayerAndGetHistoryId(offset, options = {}) {
|
|
7360
|
+
this.lastPushedHistoryId = null;
|
|
7361
|
+
await this.moveLayer(offset, options);
|
|
7362
|
+
return this.lastPushedHistoryId;
|
|
7363
|
+
}
|
|
7364
|
+
/** 等价于 {@link moveToContainer},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
|
|
7365
|
+
async moveToContainerAndGetHistoryId(config, targetId, options = {}) {
|
|
7366
|
+
this.lastPushedHistoryId = null;
|
|
7367
|
+
await this.moveToContainer(config, targetId, options);
|
|
7368
|
+
return this.lastPushedHistoryId;
|
|
7369
|
+
}
|
|
7370
|
+
/** 等价于 {@link dragTo},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
|
|
7371
|
+
async dragToAndGetHistoryId(config, targetParent, targetIndex, options = {}) {
|
|
7372
|
+
this.lastPushedHistoryId = null;
|
|
7373
|
+
await this.dragTo(config, targetParent, targetIndex, options);
|
|
7374
|
+
return this.lastPushedHistoryId;
|
|
7375
|
+
}
|
|
7376
|
+
/**
|
|
6820
7377
|
* 撤销当前操作
|
|
6821
7378
|
* @returns 被撤销的操作
|
|
6822
7379
|
*/
|
|
@@ -6854,7 +7411,12 @@
|
|
|
6854
7411
|
const entry = history_default.getPageStepList()[index];
|
|
6855
7412
|
if (!entry?.applied) return null;
|
|
6856
7413
|
const { step } = entry;
|
|
7414
|
+
if (step.opType === "initial") return null;
|
|
6857
7415
|
if (!this.get("root")) return null;
|
|
7416
|
+
if (step.opType === "update") {
|
|
7417
|
+
const items = step.diff ?? [];
|
|
7418
|
+
if (!items.length || !items.every((item) => item.changeRecords?.length)) return null;
|
|
7419
|
+
}
|
|
6858
7420
|
let revertedStep = null;
|
|
6859
7421
|
const captureRevert = (s) => {
|
|
6860
7422
|
revertedStep = s;
|
|
@@ -6864,28 +7426,31 @@
|
|
|
6864
7426
|
const opts = {
|
|
6865
7427
|
doNotSelect: true,
|
|
6866
7428
|
doNotSwitchPage: true,
|
|
6867
|
-
historyDescription
|
|
7429
|
+
historyDescription,
|
|
7430
|
+
historySource: "rollback"
|
|
6868
7431
|
};
|
|
6869
7432
|
try {
|
|
6870
7433
|
switch (step.opType) {
|
|
6871
|
-
case "add":
|
|
6872
|
-
const
|
|
6873
|
-
|
|
6874
|
-
const existing = this.getNodeById(
|
|
7434
|
+
case "add":
|
|
7435
|
+
for (const { newSchema } of step.diff ?? []) {
|
|
7436
|
+
if (!newSchema) continue;
|
|
7437
|
+
const existing = this.getNodeById(newSchema.id, false);
|
|
6875
7438
|
if (existing) await this.remove(existing, opts);
|
|
6876
7439
|
}
|
|
6877
7440
|
break;
|
|
6878
|
-
}
|
|
6879
7441
|
case "remove": {
|
|
6880
|
-
const sorted = [...step.
|
|
6881
|
-
for (const {
|
|
7442
|
+
const sorted = [...step.diff ?? []].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
|
|
7443
|
+
for (const { oldSchema, parentId } of sorted) {
|
|
7444
|
+
if (!oldSchema || parentId === void 0) continue;
|
|
6882
7445
|
const parent = this.getNodeById(parentId, false);
|
|
6883
|
-
if (parent) await this.add([cloneDeep$1(
|
|
7446
|
+
if (parent) await this.add([cloneDeep$1(oldSchema)], parent, opts);
|
|
6884
7447
|
}
|
|
6885
7448
|
break;
|
|
6886
7449
|
}
|
|
6887
7450
|
case "update": {
|
|
6888
|
-
const configs = (step.
|
|
7451
|
+
const configs = (step.diff ?? []).filter((item) => item.oldSchema && item.newSchema).map(({ oldSchema, newSchema, changeRecords }) => {
|
|
7452
|
+
const oldNode = oldSchema;
|
|
7453
|
+
const newNode = newSchema;
|
|
6889
7454
|
if (changeRecords?.length) {
|
|
6890
7455
|
const patch = {
|
|
6891
7456
|
id: newNode.id,
|
|
@@ -6900,7 +7465,10 @@
|
|
|
6900
7465
|
}
|
|
6901
7466
|
return cloneDeep$1(oldNode);
|
|
6902
7467
|
});
|
|
6903
|
-
if (configs.length) await this.update(configs, {
|
|
7468
|
+
if (configs.length) await this.update(configs, {
|
|
7469
|
+
historyDescription,
|
|
7470
|
+
historySource: "rollback"
|
|
7471
|
+
});
|
|
6904
7472
|
break;
|
|
6905
7473
|
}
|
|
6906
7474
|
}
|
|
@@ -6912,6 +7480,19 @@
|
|
|
6912
7480
|
return revertedStep;
|
|
6913
7481
|
}
|
|
6914
7482
|
/**
|
|
7483
|
+
* 通过历史记录 uuid 回滚当前页面的某条历史步骤,语义与 {@link revertPageStep} 完全一致,
|
|
7484
|
+
* 仅入参从 index 改为 uuid({@link StepValue.uuid})。uuid 不随栈内步骤增删而变化,
|
|
7485
|
+
* 更适合业务侧持有引用后再回滚(埋点、跨端同步等场景)。
|
|
7486
|
+
*
|
|
7487
|
+
* @param uuid 目标历史记录的 uuid,通常由 *AndGetHistoryId 方法返回
|
|
7488
|
+
* @returns 反向后产生的新 step;找不到对应 uuid / 未应用 / 反向失败时返回 null
|
|
7489
|
+
*/
|
|
7490
|
+
async revertPageStepById(uuid) {
|
|
7491
|
+
const index = history_default.getPageStepIndexByUuid(uuid);
|
|
7492
|
+
if (index < 0) return null;
|
|
7493
|
+
return this.revertPageStep(index);
|
|
7494
|
+
}
|
|
7495
|
+
/**
|
|
6915
7496
|
* 跳转当前页面历史栈到指定游标位置。
|
|
6916
7497
|
*
|
|
6917
7498
|
* `targetCursor` 与 `UndoRedo.getCursor()` 同义:表示"已应用步骤数量",
|
|
@@ -6934,7 +7515,7 @@
|
|
|
6934
7515
|
}
|
|
6935
7516
|
return cursor;
|
|
6936
7517
|
}
|
|
6937
|
-
async move(left, top, { doNotPushHistory = false } = {}) {
|
|
7518
|
+
async move(left, top, { doNotPushHistory = false, historyDescription, historySource } = {}) {
|
|
6938
7519
|
const node = (0, vue.toRaw)(this.get("node"));
|
|
6939
7520
|
if (!node || (0, _tmagic_utils.isPage)(node)) return;
|
|
6940
7521
|
const newStyle = calcMoveStyle(node.style || {}, left, top);
|
|
@@ -6943,7 +7524,11 @@
|
|
|
6943
7524
|
id: node.id,
|
|
6944
7525
|
type: node.type,
|
|
6945
7526
|
style: newStyle
|
|
6946
|
-
}, {
|
|
7527
|
+
}, {
|
|
7528
|
+
doNotPushHistory,
|
|
7529
|
+
historyDescription,
|
|
7530
|
+
historySource
|
|
7531
|
+
});
|
|
6947
7532
|
}
|
|
6948
7533
|
resetState() {
|
|
6949
7534
|
this.set("root", null);
|
|
@@ -6983,18 +7568,83 @@
|
|
|
6983
7568
|
if (this.selectionBeforeOp) return;
|
|
6984
7569
|
this.selectionBeforeOp = this.get("nodes").map((n) => n.id);
|
|
6985
7570
|
}
|
|
6986
|
-
|
|
7571
|
+
/**
|
|
7572
|
+
* 比较「上一次 root」与「新 root」的页面 / 页面片,按页面粒度把整体替换拆成历史记录:
|
|
7573
|
+
* - 新旧都存在且内容变化的页面 → 一条 `update`(整页快照替换,无 changeRecords);
|
|
7574
|
+
* - 仅新 root 存在的页面 → 一条 `add`;
|
|
7575
|
+
* - 仅旧 root 存在的页面 → 一条 `remove`。
|
|
7576
|
+
*
|
|
7577
|
+
* 每条记录落到对应页面自己的历史栈(与普通节点操作一致),并标记来源 `source`。
|
|
7578
|
+
* 内容未变化的页面不产生记录,避免重复设置相同 DSL 时产生噪声。
|
|
7579
|
+
*/
|
|
7580
|
+
pushRootDiffHistory(preRoot, nextRoot, source) {
|
|
7581
|
+
const prevPages = preRoot?.items || [];
|
|
7582
|
+
const nextPages = nextRoot?.items || [];
|
|
7583
|
+
const prevMap = new Map(prevPages.map((p) => [`${p.id}`, p]));
|
|
7584
|
+
const nextMap = new Map(nextPages.map((p) => [`${p.id}`, p]));
|
|
7585
|
+
const indexInItems = (root, id) => (root.items ?? []).findIndex((item) => `${item.id}` === `${id}`);
|
|
7586
|
+
nextPages.forEach((nextPage) => {
|
|
7587
|
+
const prevPage = prevMap.get(`${nextPage.id}`);
|
|
7588
|
+
if (!prevPage) this.pushPageDiffStep("add", nextPage, {
|
|
7589
|
+
newSchema: cloneDeep$1((0, vue.toRaw)(nextPage)),
|
|
7590
|
+
parentId: nextRoot.id,
|
|
7591
|
+
index: indexInItems(nextRoot, nextPage.id)
|
|
7592
|
+
}, source);
|
|
7593
|
+
else if (!isEqual((0, vue.toRaw)(prevPage), (0, vue.toRaw)(nextPage))) this.pushPageDiffStep("update", nextPage, {
|
|
7594
|
+
oldSchema: cloneDeep$1((0, vue.toRaw)(prevPage)),
|
|
7595
|
+
newSchema: cloneDeep$1((0, vue.toRaw)(nextPage))
|
|
7596
|
+
}, source);
|
|
7597
|
+
});
|
|
7598
|
+
prevPages.forEach((prevPage) => {
|
|
7599
|
+
if (!nextMap.has(`${prevPage.id}`)) this.pushPageDiffStep("remove", prevPage, {
|
|
7600
|
+
oldSchema: cloneDeep$1((0, vue.toRaw)(prevPage)),
|
|
7601
|
+
parentId: preRoot.id,
|
|
7602
|
+
index: indexInItems(preRoot, prevPage.id)
|
|
7603
|
+
}, source);
|
|
7604
|
+
});
|
|
7605
|
+
}
|
|
7606
|
+
/**
|
|
7607
|
+
* 构造一条页面级「set root」历史记录(不携带选区 / modifiedNodeIds 上下文)并落到该页面自己的栈。
|
|
7608
|
+
*
|
|
7609
|
+
* 连续 set root 替换:若该页栈最新一条已是**同来源**的 set root 记录({@link StepValue.rootStep} 且 `source` 相同),
|
|
7610
|
+
* 则用本次记录**替换**它而非新增,避免源码反复保存 / 外部重设 DSL 时堆积多条 root 记录;
|
|
7611
|
+
* 来源不同则照常新增(initial 基线不是 rootStep,不在此列)。
|
|
7612
|
+
*/
|
|
7613
|
+
pushPageDiffStep(opType, page, diffItem, source) {
|
|
6987
7614
|
const step = {
|
|
7615
|
+
uuid: (0, _tmagic_utils.guid)(),
|
|
7616
|
+
data: {
|
|
7617
|
+
name: page.name || "",
|
|
7618
|
+
id: page.id
|
|
7619
|
+
},
|
|
7620
|
+
opType,
|
|
7621
|
+
selectedBefore: [],
|
|
7622
|
+
selectedAfter: [],
|
|
7623
|
+
modifiedNodeIds: /* @__PURE__ */ new Map(),
|
|
7624
|
+
diff: [diffItem],
|
|
7625
|
+
rootStep: true
|
|
7626
|
+
};
|
|
7627
|
+
if (source) step.source = source;
|
|
7628
|
+
const top = history_default.getCurrentPageStep(page.id);
|
|
7629
|
+
if (top?.rootStep && top.source === source) history_default.replaceCurrentPageStep(step, page.id);
|
|
7630
|
+
else history_default.push(step, page.id);
|
|
7631
|
+
}
|
|
7632
|
+
pushOpHistory(opType, { diff, pageData, historyDescription, source }) {
|
|
7633
|
+
const step = {
|
|
7634
|
+
uuid: (0, _tmagic_utils.guid)(),
|
|
6988
7635
|
data: pageData,
|
|
6989
7636
|
opType,
|
|
6990
7637
|
selectedBefore: this.selectionBeforeOp ?? [],
|
|
6991
7638
|
selectedAfter: this.get("nodes").map((n) => n.id),
|
|
6992
7639
|
modifiedNodeIds: new Map(this.get("modifiedNodeIds")),
|
|
6993
|
-
|
|
7640
|
+
diff
|
|
6994
7641
|
};
|
|
6995
7642
|
if (historyDescription) step.historyDescription = historyDescription;
|
|
6996
|
-
|
|
7643
|
+
if (source) step.source = source;
|
|
7644
|
+
const historyId = history_default.push(step, pageData.id) ? step.uuid : null;
|
|
7645
|
+
this.lastPushedHistoryId = historyId;
|
|
6997
7646
|
this.selectionBeforeOp = null;
|
|
7647
|
+
return historyId;
|
|
6998
7648
|
}
|
|
6999
7649
|
/**
|
|
7000
7650
|
* 应用历史操作(撤销 / 重做)
|
|
@@ -7009,6 +7659,7 @@
|
|
|
7009
7659
|
* @param reverse true = 撤销,false = 重做
|
|
7010
7660
|
*/
|
|
7011
7661
|
async applyHistoryOp(step, reverse) {
|
|
7662
|
+
if (step.opType === "initial") return;
|
|
7012
7663
|
const root = this.get("root");
|
|
7013
7664
|
const stage = this.get("stage");
|
|
7014
7665
|
if (!root) return;
|
|
@@ -7019,56 +7670,59 @@
|
|
|
7019
7670
|
};
|
|
7020
7671
|
switch (step.opType) {
|
|
7021
7672
|
case "add": {
|
|
7022
|
-
const
|
|
7023
|
-
if (reverse) for (const
|
|
7024
|
-
|
|
7673
|
+
const items = step.diff ?? [];
|
|
7674
|
+
if (reverse) for (const { newSchema } of items) {
|
|
7675
|
+
if (!newSchema) continue;
|
|
7676
|
+
const existing = this.getNodeById(newSchema.id, false);
|
|
7025
7677
|
if (existing) await this.remove(existing, commonOpts);
|
|
7026
7678
|
}
|
|
7027
7679
|
else {
|
|
7028
|
-
const
|
|
7029
|
-
|
|
7030
|
-
|
|
7031
|
-
|
|
7032
|
-
|
|
7033
|
-
if (parent.items)
|
|
7034
|
-
|
|
7035
|
-
|
|
7036
|
-
|
|
7037
|
-
|
|
7038
|
-
|
|
7039
|
-
|
|
7040
|
-
|
|
7041
|
-
});
|
|
7042
|
-
}
|
|
7680
|
+
const sorted = [...items].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
|
|
7681
|
+
for (const { newSchema, parentId, index } of sorted) {
|
|
7682
|
+
if (!newSchema || parentId === void 0) continue;
|
|
7683
|
+
const parent = this.getNodeById(parentId, false);
|
|
7684
|
+
if (parent?.items) {
|
|
7685
|
+
if (typeof index === "number" && index >= 0 && index < parent.items.length) parent.items.splice(index, 0, cloneDeep$1(newSchema));
|
|
7686
|
+
else parent.items.push(cloneDeep$1(newSchema));
|
|
7687
|
+
await stage?.add({
|
|
7688
|
+
config: cloneDeep$1(newSchema),
|
|
7689
|
+
parent: cloneDeep$1(parent),
|
|
7690
|
+
parentId: parent.id,
|
|
7691
|
+
root: cloneDeep$1(root)
|
|
7692
|
+
});
|
|
7043
7693
|
}
|
|
7044
7694
|
}
|
|
7045
7695
|
}
|
|
7046
7696
|
break;
|
|
7047
7697
|
}
|
|
7048
7698
|
case "remove": {
|
|
7049
|
-
const items = step.
|
|
7699
|
+
const items = step.diff ?? [];
|
|
7050
7700
|
if (reverse) {
|
|
7051
|
-
const sorted = [...items].sort((a, b) => a.index - b.index);
|
|
7052
|
-
for (const {
|
|
7701
|
+
const sorted = [...items].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
|
|
7702
|
+
for (const { oldSchema, parentId, index } of sorted) {
|
|
7703
|
+
if (!oldSchema || parentId === void 0) continue;
|
|
7053
7704
|
const parent = this.getNodeById(parentId, false);
|
|
7054
7705
|
if (parent?.items) {
|
|
7055
|
-
parent.items.splice(index, 0, cloneDeep$1(
|
|
7706
|
+
parent.items.splice(index ?? parent.items.length, 0, cloneDeep$1(oldSchema));
|
|
7056
7707
|
await stage?.add({
|
|
7057
|
-
config: cloneDeep$1(
|
|
7708
|
+
config: cloneDeep$1(oldSchema),
|
|
7058
7709
|
parent: cloneDeep$1(parent),
|
|
7059
7710
|
parentId,
|
|
7060
7711
|
root: cloneDeep$1(root)
|
|
7061
7712
|
});
|
|
7062
7713
|
}
|
|
7063
7714
|
}
|
|
7064
|
-
} else for (const {
|
|
7065
|
-
|
|
7715
|
+
} else for (const { oldSchema } of items) {
|
|
7716
|
+
if (!oldSchema) continue;
|
|
7717
|
+
const existing = this.getNodeById(oldSchema.id, false);
|
|
7066
7718
|
if (existing) await this.remove(existing, commonOpts);
|
|
7067
7719
|
}
|
|
7068
7720
|
break;
|
|
7069
7721
|
}
|
|
7070
7722
|
case "update": {
|
|
7071
|
-
const configs = (step.
|
|
7723
|
+
const configs = (step.diff ?? []).filter((item) => item.oldSchema && item.newSchema).map(({ oldSchema, newSchema, changeRecords }) => {
|
|
7724
|
+
const oldNode = oldSchema;
|
|
7725
|
+
const newNode = newSchema;
|
|
7072
7726
|
if (changeRecords?.length) {
|
|
7073
7727
|
const sourceForValues = reverse ? oldNode : newNode;
|
|
7074
7728
|
const patch = {
|
|
@@ -7149,6 +7803,7 @@
|
|
|
7149
7803
|
showPageListButton: true,
|
|
7150
7804
|
hideSlideBar: false,
|
|
7151
7805
|
sideBarItems: [],
|
|
7806
|
+
sideBarActiveTabName: "",
|
|
7152
7807
|
navMenuRect: {
|
|
7153
7808
|
left: 0,
|
|
7154
7809
|
top: 0,
|
|
@@ -7181,7 +7836,9 @@
|
|
|
7181
7836
|
}
|
|
7182
7837
|
if (name === "showGuides") mask?.showGuides(value);
|
|
7183
7838
|
if (name === "showRule") mask?.showRule(value);
|
|
7839
|
+
const preValue = state[name];
|
|
7184
7840
|
state[name] = value;
|
|
7841
|
+
if (preValue !== value) this.emit("state-change", name, value, preValue);
|
|
7185
7842
|
}
|
|
7186
7843
|
get(name) {
|
|
7187
7844
|
return state[name];
|
|
@@ -7319,14 +7976,17 @@
|
|
|
7319
7976
|
changeRecordList.push(buildChangeRecords(style, "style"));
|
|
7320
7977
|
});
|
|
7321
7978
|
if (configs.length === 0) return;
|
|
7322
|
-
editor_default.update(configs, {
|
|
7979
|
+
editor_default.update(configs, {
|
|
7980
|
+
changeRecordList,
|
|
7981
|
+
historySource: "stage"
|
|
7982
|
+
});
|
|
7323
7983
|
});
|
|
7324
7984
|
stage.on("sort", (ev) => {
|
|
7325
|
-
editor_default.sort(ev.src, ev.dist);
|
|
7985
|
+
editor_default.sort(ev.src, ev.dist, { historySource: "stage" });
|
|
7326
7986
|
});
|
|
7327
7987
|
stage.on("remove", (ev) => {
|
|
7328
7988
|
const nodes = ev.data.map(({ el }) => editor_default.getNodeById((0, _tmagic_utils.getIdFromEl)()(el) || ""));
|
|
7329
|
-
editor_default.remove(nodes.filter((node) => Boolean(node)));
|
|
7989
|
+
editor_default.remove(nodes.filter((node) => Boolean(node)), { historySource: "stage" });
|
|
7330
7990
|
});
|
|
7331
7991
|
stage.on("select-parent", () => {
|
|
7332
7992
|
const parent = editor_default.get("parent");
|
|
@@ -8401,7 +9061,7 @@
|
|
|
8401
9061
|
key: 1,
|
|
8402
9062
|
class: "menu-item-text"
|
|
8403
9063
|
};
|
|
8404
|
-
var _hoisted_2$
|
|
9064
|
+
var _hoisted_2$30 = { class: "el-dropdown-link menubar-menu-button" };
|
|
8405
9065
|
var ToolButton_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
8406
9066
|
name: "MEditorToolButton",
|
|
8407
9067
|
__name: "ToolButton",
|
|
@@ -8466,11 +9126,11 @@
|
|
|
8466
9126
|
placement: "bottom-start",
|
|
8467
9127
|
content: __props.data.tooltip
|
|
8468
9128
|
}, {
|
|
8469
|
-
default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), {
|
|
9129
|
+
default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), (0, vue.mergeProps)({
|
|
8470
9130
|
size: "small",
|
|
8471
9131
|
link: "",
|
|
8472
9132
|
disabled: disabled.value
|
|
8473
|
-
}, (0, vue.createSlots)({ _: 2 }, [__props.data.icon ? {
|
|
9133
|
+
}, __props.data.buttonProps || {}), (0, vue.createSlots)({ _: 2 }, [__props.data.icon ? {
|
|
8474
9134
|
name: "icon",
|
|
8475
9135
|
fn: (0, vue.withCtx)(() => [(0, vue.createVNode)(Icon_default, { icon: __props.data.icon }, null, 8, ["icon"])]),
|
|
8476
9136
|
key: "0"
|
|
@@ -8478,15 +9138,15 @@
|
|
|
8478
9138
|
name: "default",
|
|
8479
9139
|
fn: (0, vue.withCtx)(() => [(0, vue.createTextVNode)((0, vue.toDisplayString)(__props.data.text), 1)]),
|
|
8480
9140
|
key: "1"
|
|
8481
|
-
} : void 0]),
|
|
9141
|
+
} : void 0]), 1040, ["disabled"])]),
|
|
8482
9142
|
_: 1
|
|
8483
|
-
}, 8, ["content"])) : ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicButton), {
|
|
9143
|
+
}, 8, ["content"])) : ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicButton), (0, vue.mergeProps)({
|
|
8484
9144
|
key: 1,
|
|
8485
9145
|
size: "small",
|
|
8486
9146
|
link: "",
|
|
8487
9147
|
disabled: disabled.value,
|
|
8488
9148
|
title: __props.data.text
|
|
8489
|
-
}, (0, vue.createSlots)({ _: 2 }, [__props.data.icon ? {
|
|
9149
|
+
}, __props.data.buttonProps || {}), (0, vue.createSlots)({ _: 2 }, [__props.data.icon ? {
|
|
8490
9150
|
name: "icon",
|
|
8491
9151
|
fn: (0, vue.withCtx)(() => [(0, vue.createVNode)(Icon_default, { icon: __props.data.icon }, null, 8, ["icon"])]),
|
|
8492
9152
|
key: "0"
|
|
@@ -8494,7 +9154,7 @@
|
|
|
8494
9154
|
name: "default",
|
|
8495
9155
|
fn: (0, vue.withCtx)(() => [(0, vue.createTextVNode)((0, vue.toDisplayString)(__props.data.text), 1)]),
|
|
8496
9156
|
key: "1"
|
|
8497
|
-
} : void 0]),
|
|
9157
|
+
} : void 0]), 1040, ["disabled", "title"]))], 64)) : __props.data.type === "dropdown" ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicDropdown), {
|
|
8498
9158
|
key: 3,
|
|
8499
9159
|
trigger: "click",
|
|
8500
9160
|
disabled: disabled.value,
|
|
@@ -8515,7 +9175,7 @@
|
|
|
8515
9175
|
}), 128))]),
|
|
8516
9176
|
_: 1
|
|
8517
9177
|
})) : (0, vue.createCommentVNode)("v-if", true)]),
|
|
8518
|
-
default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("span", _hoisted_2$
|
|
9178
|
+
default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("span", _hoisted_2$30, [(0, vue.createTextVNode)((0, vue.toDisplayString)(__props.data.text), 1), (0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicIcon), { class: "el-icon--right" }, {
|
|
8519
9179
|
default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_element_plus_icons_vue.ArrowDown))]),
|
|
8520
9180
|
_: 1
|
|
8521
9181
|
})])]),
|
|
@@ -8534,7 +9194,7 @@
|
|
|
8534
9194
|
id: "m-editor-page-bar-add-icon",
|
|
8535
9195
|
class: "m-editor-page-bar-item m-editor-page-bar-item-icon"
|
|
8536
9196
|
};
|
|
8537
|
-
var _hoisted_2$
|
|
9197
|
+
var _hoisted_2$29 = {
|
|
8538
9198
|
key: 1,
|
|
8539
9199
|
style: { "width": "21px" }
|
|
8540
9200
|
};
|
|
@@ -8571,7 +9231,7 @@
|
|
|
8571
9231
|
}
|
|
8572
9232
|
} }, null, 8, ["data"])]),
|
|
8573
9233
|
_: 1
|
|
8574
|
-
})])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_2$
|
|
9234
|
+
})])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_2$29));
|
|
8575
9235
|
};
|
|
8576
9236
|
}
|
|
8577
9237
|
});
|
|
@@ -8584,7 +9244,7 @@
|
|
|
8584
9244
|
class: "m-editor-page-bar",
|
|
8585
9245
|
ref: "pageBar"
|
|
8586
9246
|
};
|
|
8587
|
-
var _hoisted_2$
|
|
9247
|
+
var _hoisted_2$28 = {
|
|
8588
9248
|
key: 0,
|
|
8589
9249
|
class: "m-editor-page-bar-items",
|
|
8590
9250
|
ref: "itemsContainer"
|
|
@@ -8676,7 +9336,7 @@
|
|
|
8676
9336
|
return (_ctx, _cache) => {
|
|
8677
9337
|
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$68, [
|
|
8678
9338
|
(0, vue.renderSlot)(_ctx.$slots, "prepend"),
|
|
8679
|
-
__props.length ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_2$
|
|
9339
|
+
__props.length ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_2$28, [(0, vue.renderSlot)(_ctx.$slots, "default")], 512)) : (0, vue.createCommentVNode)("v-if", true),
|
|
8680
9340
|
canScroll.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
8681
9341
|
key: 1,
|
|
8682
9342
|
class: "m-editor-page-bar-item m-editor-page-bar-item-icon m-editor-page-bar-item-left-icon",
|
|
@@ -8701,8 +9361,8 @@
|
|
|
8701
9361
|
id: "m-editor-page-bar-list-icon",
|
|
8702
9362
|
class: "m-editor-page-bar-item m-editor-page-bar-item-icon"
|
|
8703
9363
|
};
|
|
8704
|
-
var _hoisted_2$
|
|
8705
|
-
var _hoisted_3$
|
|
9364
|
+
var _hoisted_2$27 = { class: "page-bar-popover-wrapper" };
|
|
9365
|
+
var _hoisted_3$13 = { class: "page-bar-popover-inner" };
|
|
8706
9366
|
var PageList_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
8707
9367
|
name: "MEditorPageList",
|
|
8708
9368
|
__name: "PageList",
|
|
@@ -8726,7 +9386,7 @@
|
|
|
8726
9386
|
default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_element_plus_icons_vue.Files))]),
|
|
8727
9387
|
_: 1
|
|
8728
9388
|
})]),
|
|
8729
|
-
default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("div", _hoisted_2$
|
|
9389
|
+
default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("div", _hoisted_2$27, [(0, vue.createElementVNode)("div", _hoisted_3$13, [(0, vue.renderSlot)(_ctx.$slots, "page-list-popover", { list: __props.list }, () => [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.list, (item, index) => {
|
|
8730
9390
|
return (0, vue.openBlock)(), (0, vue.createBlock)(ToolButton_default, {
|
|
8731
9391
|
data: {
|
|
8732
9392
|
type: "button",
|
|
@@ -8805,9 +9465,9 @@
|
|
|
8805
9465
|
//#endregion
|
|
8806
9466
|
//#region packages/editor/src/layouts/page-bar/PageBar.vue?vue&type=script&setup=true&lang.ts
|
|
8807
9467
|
var _hoisted_1$65 = { class: "m-editor-page-bar-tabs" };
|
|
8808
|
-
var _hoisted_2$
|
|
8809
|
-
var _hoisted_3$
|
|
8810
|
-
var _hoisted_4$
|
|
9468
|
+
var _hoisted_2$26 = ["data-page-id", "onClick"];
|
|
9469
|
+
var _hoisted_3$12 = { class: "m-editor-page-bar-title" };
|
|
9470
|
+
var _hoisted_4$9 = ["title"];
|
|
8811
9471
|
var PageBar_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
8812
9472
|
name: "MEditorPageBar",
|
|
8813
9473
|
__name: "PageBar",
|
|
@@ -8849,7 +9509,8 @@
|
|
|
8849
9509
|
top: 0
|
|
8850
9510
|
});
|
|
8851
9511
|
};
|
|
8852
|
-
const remove = (node) => {
|
|
9512
|
+
const remove = async (node) => {
|
|
9513
|
+
await _tmagic_design.tMagicMessageBox.confirm("确定删除该页面吗?");
|
|
8853
9514
|
editorService.remove(node);
|
|
8854
9515
|
};
|
|
8855
9516
|
const pageBarScrollContainerRef = (0, vue.useTemplateRef)("pageBarScrollContainer");
|
|
@@ -8896,7 +9557,7 @@
|
|
|
8896
9557
|
key: item.id,
|
|
8897
9558
|
"data-page-id": item.id,
|
|
8898
9559
|
onClick: ($event) => switchPage(item.id)
|
|
8899
|
-
}, [(0, vue.createElementVNode)("div", _hoisted_3$
|
|
9560
|
+
}, [(0, vue.createElementVNode)("div", _hoisted_3$12, [(0, vue.renderSlot)(_ctx.$slots, "page-bar-title", { page: item }, () => [(0, vue.createElementVNode)("span", { title: item.name }, (0, vue.toDisplayString)(item.name || item.id), 9, _hoisted_4$9)])]), (0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicPopover), {
|
|
8900
9561
|
"popper-class": "page-bar-popover",
|
|
8901
9562
|
placement: "top",
|
|
8902
9563
|
trigger: "hover",
|
|
@@ -8916,10 +9577,11 @@
|
|
|
8916
9577
|
type: "button",
|
|
8917
9578
|
text: "删除",
|
|
8918
9579
|
icon: (0, vue.unref)(_element_plus_icons_vue.Delete),
|
|
9580
|
+
buttonProps: { type: "danger" },
|
|
8919
9581
|
handler: () => remove(item)
|
|
8920
9582
|
} }, null, 8, ["data"])])])]),
|
|
8921
9583
|
_: 2
|
|
8922
|
-
}, 1024)], 10, _hoisted_2$
|
|
9584
|
+
}, 1024)], 10, _hoisted_2$26);
|
|
8923
9585
|
}), 128))]),
|
|
8924
9586
|
_: 3
|
|
8925
9587
|
}, 8, ["page-bar-sort-options", "length"])]);
|
|
@@ -8932,7 +9594,7 @@
|
|
|
8932
9594
|
//#endregion
|
|
8933
9595
|
//#region packages/editor/src/layouts/AddPageBox.vue?vue&type=script&setup=true&lang.ts
|
|
8934
9596
|
var _hoisted_1$64 = { class: "m-editor-empty-panel" };
|
|
8935
|
-
var _hoisted_2$
|
|
9597
|
+
var _hoisted_2$25 = { class: "m-editor-empty-content" };
|
|
8936
9598
|
var AddPageBox_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
8937
9599
|
name: "MEditorAddPageBox",
|
|
8938
9600
|
__name: "AddPageBox",
|
|
@@ -8949,7 +9611,7 @@
|
|
|
8949
9611
|
});
|
|
8950
9612
|
};
|
|
8951
9613
|
return (_ctx, _cache) => {
|
|
8952
|
-
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$64, [(0, vue.createElementVNode)("div", _hoisted_2$
|
|
9614
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$64, [(0, vue.createElementVNode)("div", _hoisted_2$25, [(0, vue.createElementVNode)("div", {
|
|
8953
9615
|
class: "m-editor-empty-button",
|
|
8954
9616
|
onClick: _cache[0] || (_cache[0] = ($event) => clickHandler((0, vue.unref)(_tmagic_core.NodeType).PAGE))
|
|
8955
9617
|
}, [(0, vue.createElementVNode)("div", null, [(0, vue.createVNode)(Icon_default, { icon: (0, vue.unref)(_element_plus_icons_vue.Plus) }, null, 8, ["icon"])]), _cache[2] || (_cache[2] = (0, vue.createElementVNode)("p", null, "新增页面", -1))]), !__props.disabledPageFragment ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
@@ -8966,7 +9628,7 @@
|
|
|
8966
9628
|
//#endregion
|
|
8967
9629
|
//#region packages/editor/src/layouts/CodeEditor.vue?vue&type=script&setup=true&lang.ts
|
|
8968
9630
|
var _hoisted_1$63 = { class: "magic-code-editor" };
|
|
8969
|
-
var _hoisted_2$
|
|
9631
|
+
var _hoisted_2$24 = {
|
|
8970
9632
|
ref: "codeEditor",
|
|
8971
9633
|
class: "magic-code-editor-content"
|
|
8972
9634
|
};
|
|
@@ -9221,7 +9883,7 @@
|
|
|
9221
9883
|
}, {
|
|
9222
9884
|
default: (0, vue.withCtx)(() => [(0, vue.createVNode)(Icon_default, { icon: (0, vue.unref)(_element_plus_icons_vue.FullScreen) }, null, 8, ["icon"])]),
|
|
9223
9885
|
_: 1
|
|
9224
|
-
})) : (0, vue.createCommentVNode)("v-if", true), (0, vue.createElementVNode)("div", _hoisted_2$
|
|
9886
|
+
})) : (0, vue.createCommentVNode)("v-if", true), (0, vue.createElementVNode)("div", _hoisted_2$24, null, 512)], 6)], 8, ["disabled"]))]);
|
|
9225
9887
|
};
|
|
9226
9888
|
}
|
|
9227
9889
|
});
|
|
@@ -9288,7 +9950,7 @@
|
|
|
9288
9950
|
const saveCode = (value) => {
|
|
9289
9951
|
try {
|
|
9290
9952
|
const parseDSL = getEditorConfig("parseDSL");
|
|
9291
|
-
editorService.set("root", parseDSL(value));
|
|
9953
|
+
editorService.set("root", parseDSL(value), { historySource: "root-code" });
|
|
9292
9954
|
} catch (e) {
|
|
9293
9955
|
console.error(e);
|
|
9294
9956
|
}
|
|
@@ -9362,6 +10024,8 @@
|
|
|
9362
10024
|
var Framework_default = Framework_vue_vue_type_script_setup_true_lang_default;
|
|
9363
10025
|
//#endregion
|
|
9364
10026
|
//#region packages/editor/src/layouts/history-list/composables.ts
|
|
10027
|
+
/** 合并组默认展开;仅当 expanded[key] === false 时为收起。 */
|
|
10028
|
+
var isHistoryGroupExpanded = (expanded, key) => expanded[key] !== false;
|
|
9365
10029
|
/**
|
|
9366
10030
|
* 历史记录面板共享逻辑:
|
|
9367
10031
|
* - 暴露三类历史的聚合数据(页面 / 数据源 / 代码块);
|
|
@@ -9375,10 +10039,11 @@
|
|
|
9375
10039
|
/**
|
|
9376
10040
|
* 折叠状态:key 形如 `pg-${组内首步 index}` / `ds-${id}-${组内首步 index}` / `cb-${id}-${组内首步 index}`。
|
|
9377
10041
|
* 用组内首步的稳定 index(而非展示位置)作为 key,确保历史数据更新后已展开的分组状态保持不变。
|
|
10042
|
+
* 合并组默认展开;仅当值为 `false` 时表示收起。
|
|
9378
10043
|
*/
|
|
9379
10044
|
const expanded = (0, vue.reactive)({});
|
|
9380
10045
|
const toggleGroup = (key) => {
|
|
9381
|
-
expanded[key] =
|
|
10046
|
+
expanded[key] = expanded[key] === false;
|
|
9382
10047
|
};
|
|
9383
10048
|
const pageGroups = (0, vue.computed)(() => historyService.getPageHistoryGroups());
|
|
9384
10049
|
const dataSourceGroups = (0, vue.computed)(() => historyService.getDataSourceHistoryGroups());
|
|
@@ -9433,6 +10098,62 @@
|
|
|
9433
10098
|
default: return "修改";
|
|
9434
10099
|
}
|
|
9435
10100
|
};
|
|
10101
|
+
/** 内置操作途径的中文文案;自定义来源直接回显原值,未知 / 缺省返回空串(UI 据此不渲染)。 */
|
|
10102
|
+
var HISTORY_SOURCE_LABELS = {
|
|
10103
|
+
stage: "画布",
|
|
10104
|
+
tree: "树面板",
|
|
10105
|
+
"component-panel": "组件面板",
|
|
10106
|
+
props: "配置面板",
|
|
10107
|
+
code: "源码",
|
|
10108
|
+
"root-code": "DSL源码",
|
|
10109
|
+
"stage-contextmenu": "画布菜单",
|
|
10110
|
+
"tree-contextmenu": "树菜单",
|
|
10111
|
+
toolbar: "工具栏",
|
|
10112
|
+
shortcut: "快捷键",
|
|
10113
|
+
rollback: "回滚",
|
|
10114
|
+
api: "接口",
|
|
10115
|
+
ai: "AI",
|
|
10116
|
+
initial: "初始值",
|
|
10117
|
+
sync: "同步",
|
|
10118
|
+
unknown: "未知"
|
|
10119
|
+
};
|
|
10120
|
+
/** 操作途径文案:用于历史面板展示「画布 / 树面板 / 配置面板…」标签。 */
|
|
10121
|
+
var sourceLabel = (source = "unknown") => {
|
|
10122
|
+
return HISTORY_SOURCE_LABELS[source] ?? `${source}`;
|
|
10123
|
+
};
|
|
10124
|
+
/** 取一组历史步骤里最后一步(最近一次)的操作途径,用于组头部展示。 */
|
|
10125
|
+
var groupSource = (group) => group.steps[group.steps.length - 1]?.step.source;
|
|
10126
|
+
/**
|
|
10127
|
+
* 把一个历史分组(页面 / bucket)派生为 GroupRow 直接消费的视图模型 {@link HistoryRowGroup}。
|
|
10128
|
+
* 统一了原先 PageTab / Bucket 各自内联的 sub-steps 映射逻辑:描述、可差异、可回滚、时间、途径
|
|
10129
|
+
* 全部在此一次性算好,组件层只负责渲染。
|
|
10130
|
+
*/
|
|
10131
|
+
var toRowGroup = (group, key, descriptor) => {
|
|
10132
|
+
const { describeGroup, describeStep, isStepDiffable, isStepRevertable } = descriptor;
|
|
10133
|
+
const timestamp = groupTimestamp(group);
|
|
10134
|
+
return {
|
|
10135
|
+
key,
|
|
10136
|
+
applied: group.applied,
|
|
10137
|
+
isCurrent: Boolean(group.isCurrent),
|
|
10138
|
+
opType: group.opType,
|
|
10139
|
+
desc: describeGroup(group),
|
|
10140
|
+
source: groupSource(group),
|
|
10141
|
+
time: formatHistoryTime(timestamp),
|
|
10142
|
+
timeTitle: formatHistoryFullTime(timestamp),
|
|
10143
|
+
subSteps: group.steps.map((s) => ({
|
|
10144
|
+
index: s.index,
|
|
10145
|
+
applied: s.applied,
|
|
10146
|
+
isCurrent: s.isCurrent,
|
|
10147
|
+
saved: s.step.saved,
|
|
10148
|
+
desc: describeStep(s.step),
|
|
10149
|
+
diffable: isStepDiffable ? isStepDiffable(s.step) : false,
|
|
10150
|
+
revertable: s.applied && (isStepRevertable ? isStepRevertable(s.step) : true),
|
|
10151
|
+
source: s.step.source,
|
|
10152
|
+
time: formatHistoryTime(s.step.timestamp),
|
|
10153
|
+
timeTitle: formatHistoryFullTime(s.step.timestamp)
|
|
10154
|
+
}))
|
|
10155
|
+
};
|
|
10156
|
+
};
|
|
9436
10157
|
var nameOf = (node) => node?.name || node?.type || `${node?.id ?? ""}`;
|
|
9437
10158
|
/**
|
|
9438
10159
|
* 默认描述里展示「名称 (id: xxx)」,便于区分同名实体。
|
|
@@ -9451,24 +10172,24 @@
|
|
|
9451
10172
|
var describePageStep = (step) => {
|
|
9452
10173
|
if (step.historyDescription) return step.historyDescription;
|
|
9453
10174
|
const { opType } = step;
|
|
10175
|
+
const items = step.diff ?? [];
|
|
9454
10176
|
if (opType === "add") {
|
|
9455
|
-
const count =
|
|
9456
|
-
const node =
|
|
10177
|
+
const count = items.length;
|
|
10178
|
+
const node = items[0]?.newSchema;
|
|
9457
10179
|
return `新增 ${count} 个节点${count === 1 && node ? `(${labelWithId(nameOf(node), node.id)})` : ""}`;
|
|
9458
10180
|
}
|
|
9459
10181
|
if (opType === "remove") {
|
|
9460
|
-
const count =
|
|
9461
|
-
const node =
|
|
10182
|
+
const count = items.length;
|
|
10183
|
+
const node = items[0]?.oldSchema;
|
|
9462
10184
|
return `删除 ${count} 个节点${count === 1 && node ? `(${labelWithId(nameOf(node), node.id)})` : ""}`;
|
|
9463
10185
|
}
|
|
9464
|
-
|
|
9465
|
-
if (
|
|
9466
|
-
|
|
9467
|
-
const { newNode, changeRecords } = updated[0];
|
|
10186
|
+
if (!items.length) return "修改节点";
|
|
10187
|
+
if (items.length === 1) {
|
|
10188
|
+
const { newSchema, changeRecords } = items[0];
|
|
9468
10189
|
const propPath = changeRecords?.[0]?.propPath;
|
|
9469
|
-
return `修改 ${labelWithId(nameOf(
|
|
10190
|
+
return `修改 ${labelWithId(nameOf(newSchema), newSchema?.id)}${propPath ? ` · ${propPath}` : ""}`;
|
|
9470
10191
|
}
|
|
9471
|
-
return `修改 ${
|
|
10192
|
+
return `修改 ${items.length} 个节点`;
|
|
9472
10193
|
};
|
|
9473
10194
|
/**
|
|
9474
10195
|
* 合并组的展示文案:
|
|
@@ -9482,7 +10203,7 @@
|
|
|
9482
10203
|
if (group.steps.length === 1) return describePageStep(group.steps[0].step);
|
|
9483
10204
|
const paths = /* @__PURE__ */ new Set();
|
|
9484
10205
|
group.steps.forEach((s) => {
|
|
9485
|
-
s.step.
|
|
10206
|
+
s.step.diff?.[0]?.changeRecords?.forEach((r) => r.propPath && paths.add(r.propPath));
|
|
9486
10207
|
});
|
|
9487
10208
|
const pathList = Array.from(paths).slice(0, 3).join(", ");
|
|
9488
10209
|
const target = labelWithId(group.targetName ?? (group.targetId !== void 0 ? `${group.targetId}` : "节点"), group.targetId);
|
|
@@ -9490,10 +10211,11 @@
|
|
|
9490
10211
|
};
|
|
9491
10212
|
var describeDataSourceStep = (step) => {
|
|
9492
10213
|
if (step.historyDescription) return step.historyDescription;
|
|
9493
|
-
|
|
9494
|
-
if (
|
|
9495
|
-
|
|
9496
|
-
const
|
|
10214
|
+
const { oldSchema, newSchema, changeRecords } = step.diff?.[0] ?? {};
|
|
10215
|
+
if (!oldSchema && newSchema) return `创建 ${labelWithId(newSchema.title, newSchema.id ?? step.id)}`;
|
|
10216
|
+
if (!newSchema && oldSchema) return `删除 ${labelWithId(oldSchema.title, oldSchema.id ?? step.id)}`;
|
|
10217
|
+
const propPath = changeRecords?.[0]?.propPath;
|
|
10218
|
+
const title = labelWithId(newSchema?.title || oldSchema?.title, step.id);
|
|
9497
10219
|
return propPath ? `修改 ${title} · ${propPath}` : `修改 ${title}`;
|
|
9498
10220
|
};
|
|
9499
10221
|
var describeDataSourceGroup = (group) => {
|
|
@@ -9502,18 +10224,19 @@
|
|
|
9502
10224
|
if (group.steps.length === 1) return describeDataSourceStep(group.steps[0].step);
|
|
9503
10225
|
const paths = /* @__PURE__ */ new Set();
|
|
9504
10226
|
group.steps.forEach((s) => {
|
|
9505
|
-
s.step.changeRecords?.forEach((r) => r.propPath && paths.add(r.propPath));
|
|
10227
|
+
s.step.diff?.[0]?.changeRecords?.forEach((r) => r.propPath && paths.add(r.propPath));
|
|
9506
10228
|
});
|
|
9507
10229
|
const pathList = Array.from(paths).slice(0, 3).join(", ");
|
|
9508
|
-
const target = labelWithId(group.steps[group.steps.length - 1].step.newSchema?.title || group.steps[0].step.oldSchema?.title, group.id);
|
|
10230
|
+
const target = labelWithId(group.steps[group.steps.length - 1].step.diff?.[0]?.newSchema?.title || group.steps[0].step.diff?.[0]?.oldSchema?.title, group.id);
|
|
9509
10231
|
return pathList ? `修改 ${target} · ${pathList}${paths.size > 3 ? "…" : ""}` : `修改 ${target}`;
|
|
9510
10232
|
};
|
|
9511
10233
|
var describeCodeBlockStep = (step) => {
|
|
9512
10234
|
if (step.historyDescription) return step.historyDescription;
|
|
9513
|
-
|
|
9514
|
-
if (
|
|
9515
|
-
|
|
9516
|
-
const
|
|
10235
|
+
const { oldSchema: oldContent, newSchema: newContent, changeRecords } = step.diff?.[0] ?? {};
|
|
10236
|
+
if (!oldContent && newContent) return `创建 ${labelWithId(newContent.name, newContent.id ?? step.id)}`;
|
|
10237
|
+
if (!newContent && oldContent) return `删除 ${labelWithId(oldContent.name, oldContent.id ?? step.id)}`;
|
|
10238
|
+
const propPath = changeRecords?.[0]?.propPath;
|
|
10239
|
+
const title = labelWithId(newContent?.name || oldContent?.name, step.id);
|
|
9517
10240
|
return propPath ? `修改 ${title} · ${propPath}` : `修改 ${title}`;
|
|
9518
10241
|
};
|
|
9519
10242
|
var describeCodeBlockGroup = (group) => {
|
|
@@ -9522,117 +10245,190 @@
|
|
|
9522
10245
|
if (group.steps.length === 1) return describeCodeBlockStep(group.steps[0].step);
|
|
9523
10246
|
const paths = /* @__PURE__ */ new Set();
|
|
9524
10247
|
group.steps.forEach((s) => {
|
|
9525
|
-
s.step.changeRecords?.forEach((r) => r.propPath && paths.add(r.propPath));
|
|
10248
|
+
s.step.diff?.[0]?.changeRecords?.forEach((r) => r.propPath && paths.add(r.propPath));
|
|
9526
10249
|
});
|
|
9527
10250
|
const pathList = Array.from(paths).slice(0, 3).join(", ");
|
|
9528
|
-
const target = labelWithId(group.steps[group.steps.length - 1].step.
|
|
10251
|
+
const target = labelWithId(group.steps[group.steps.length - 1].step.diff?.[0]?.newSchema?.name || group.steps[0].step.diff?.[0]?.oldSchema?.name, group.id);
|
|
9529
10252
|
return pathList ? `修改 ${target} · ${pathList}${paths.size > 3 ? "…" : ""}` : `修改 ${target}`;
|
|
9530
10253
|
};
|
|
10254
|
+
/**
|
|
10255
|
+
* 页面 step 是否支持「回滚」(类 git revert):
|
|
10256
|
+
* - 新增 / 删除:不依赖 changeRecords,反向应用即删除 / 加回,始终可回滚;
|
|
10257
|
+
* - 更新:必须每个被更新节点都带有 changeRecords,才支持按 propPath 局部反向 patch。
|
|
10258
|
+
* 缺失 changeRecords 的更新只能整节点替换,会冲掉该节点后续的无关变更,因此不支持回滚。
|
|
10259
|
+
*/
|
|
10260
|
+
var isPageStepRevertable = (step) => {
|
|
10261
|
+
if (step.opType !== "update") return true;
|
|
10262
|
+
const items = step.diff ?? [];
|
|
10263
|
+
if (!items.length) return false;
|
|
10264
|
+
return items.every((item) => Boolean(item.changeRecords?.length));
|
|
10265
|
+
};
|
|
10266
|
+
/**
|
|
10267
|
+
* 数据源 step 是否支持「回滚」:
|
|
10268
|
+
* - 新增(无 oldSchema)/ 删除(无 newSchema):不依赖 changeRecords,始终可回滚;
|
|
10269
|
+
* - 更新(前后 schema 都存在):必须有 changeRecords 才支持局部反向 patch,否则不支持回滚。
|
|
10270
|
+
*/
|
|
10271
|
+
var isDataSourceStepRevertable = (step) => {
|
|
10272
|
+
const item = step.diff?.[0];
|
|
10273
|
+
if (!item?.oldSchema || !item?.newSchema) return true;
|
|
10274
|
+
return Boolean(item.changeRecords?.length);
|
|
10275
|
+
};
|
|
10276
|
+
/**
|
|
10277
|
+
* 代码块 step 是否支持「回滚」:
|
|
10278
|
+
* - 新增(无 oldSchema)/ 删除(无 newSchema):不依赖 changeRecords,始终可回滚;
|
|
10279
|
+
* - 更新(前后 content 都存在):必须有 changeRecords 才支持局部反向 patch,否则不支持回滚。
|
|
10280
|
+
*/
|
|
10281
|
+
var isCodeBlockStepRevertable = (step) => {
|
|
10282
|
+
const item = step.diff?.[0];
|
|
10283
|
+
if (!item?.oldSchema || !item?.newSchema) return true;
|
|
10284
|
+
return Boolean(item.changeRecords?.length);
|
|
10285
|
+
};
|
|
9531
10286
|
//#endregion
|
|
9532
10287
|
//#region packages/editor/src/layouts/history-list/GroupRow.vue?vue&type=script&setup=true&lang.ts
|
|
9533
10288
|
var _hoisted_1$61 = ["title"];
|
|
9534
|
-
var _hoisted_2$
|
|
9535
|
-
var _hoisted_3$
|
|
9536
|
-
var _hoisted_4$
|
|
10289
|
+
var _hoisted_2$23 = ["title"];
|
|
10290
|
+
var _hoisted_3$11 = { class: "m-editor-history-list-item-desc" };
|
|
10291
|
+
var _hoisted_4$8 = {
|
|
10292
|
+
key: 0,
|
|
10293
|
+
class: "m-editor-history-list-item-saved",
|
|
10294
|
+
title: "该记录为最近一次保存的状态"
|
|
10295
|
+
};
|
|
9537
10296
|
var _hoisted_5$3 = {
|
|
9538
10297
|
key: 1,
|
|
10298
|
+
class: "m-editor-history-list-item-actions"
|
|
10299
|
+
};
|
|
10300
|
+
var _hoisted_6$3 = ["title"];
|
|
10301
|
+
var _hoisted_7$1 = ["title"];
|
|
10302
|
+
var _hoisted_8 = {
|
|
10303
|
+
key: 4,
|
|
9539
10304
|
class: "m-editor-history-list-item-merge"
|
|
9540
10305
|
};
|
|
9541
|
-
var
|
|
10306
|
+
var _hoisted_9 = {
|
|
9542
10307
|
key: 0,
|
|
9543
10308
|
class: "m-editor-history-list-substeps"
|
|
9544
10309
|
};
|
|
9545
|
-
var
|
|
9546
|
-
var
|
|
9547
|
-
var
|
|
9548
|
-
var
|
|
9549
|
-
|
|
9550
|
-
|
|
9551
|
-
|
|
10310
|
+
var _hoisted_10 = ["title", "onClick"];
|
|
10311
|
+
var _hoisted_11 = { class: "m-editor-history-list-item-index" };
|
|
10312
|
+
var _hoisted_12 = { class: "m-editor-history-list-substep-desc" };
|
|
10313
|
+
var _hoisted_13 = {
|
|
10314
|
+
key: 0,
|
|
10315
|
+
class: "m-editor-history-list-item-saved",
|
|
10316
|
+
title: "该记录为最近一次保存的状态"
|
|
10317
|
+
};
|
|
10318
|
+
var _hoisted_14 = {
|
|
10319
|
+
key: 1,
|
|
10320
|
+
class: "m-editor-history-list-item-actions"
|
|
10321
|
+
};
|
|
10322
|
+
var _hoisted_15 = ["onClick"];
|
|
10323
|
+
var _hoisted_16 = ["onClick"];
|
|
10324
|
+
var _hoisted_17 = ["onClick"];
|
|
10325
|
+
var _hoisted_18 = ["title"];
|
|
10326
|
+
var _hoisted_19 = ["title"];
|
|
9552
10327
|
var GroupRow_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
9553
10328
|
name: "MEditorHistoryListGroupRow",
|
|
9554
10329
|
__name: "GroupRow",
|
|
9555
10330
|
props: {
|
|
9556
|
-
|
|
9557
|
-
applied: { type: Boolean },
|
|
9558
|
-
merged: { type: Boolean },
|
|
9559
|
-
opType: {},
|
|
9560
|
-
desc: {},
|
|
9561
|
-
time: {},
|
|
9562
|
-
timeTitle: {},
|
|
9563
|
-
stepCount: {},
|
|
9564
|
-
subSteps: {},
|
|
10331
|
+
group: {},
|
|
9565
10332
|
expanded: { type: Boolean },
|
|
9566
|
-
isCurrent: {
|
|
9567
|
-
type: Boolean,
|
|
9568
|
-
default: false
|
|
9569
|
-
},
|
|
9570
10333
|
gotoEnabled: {
|
|
9571
10334
|
type: Boolean,
|
|
9572
10335
|
default: true
|
|
10336
|
+
},
|
|
10337
|
+
selectEnabled: {
|
|
10338
|
+
type: Boolean,
|
|
10339
|
+
default: false
|
|
9573
10340
|
}
|
|
9574
10341
|
},
|
|
9575
10342
|
emits: [
|
|
9576
10343
|
"toggle",
|
|
9577
10344
|
"goto",
|
|
9578
10345
|
"diff-step",
|
|
9579
|
-
"revert-step"
|
|
10346
|
+
"revert-step",
|
|
10347
|
+
"select"
|
|
9580
10348
|
],
|
|
9581
10349
|
setup(__props, { emit: __emit }) {
|
|
9582
10350
|
const props = __props;
|
|
9583
10351
|
const emit = __emit;
|
|
10352
|
+
/** 子步数大于 1 即为合并组:决定是否展示合并标记与可展开的子步列表。 */
|
|
10353
|
+
const merged = (0, vue.computed)(() => props.group.subSteps.length > 1);
|
|
10354
|
+
/** 组内 step 总数,仅在合并组时显示为 "合并 N 步"。 */
|
|
10355
|
+
const stepCount = (0, vue.computed)(() => props.group.subSteps.length);
|
|
9584
10356
|
/**
|
|
9585
|
-
*
|
|
9586
|
-
*
|
|
10357
|
+
* 头部可点击的场景:
|
|
10358
|
+
* - 合并组:点击切换展开 / 收起;
|
|
10359
|
+
* - 开启 `selectEnabled`(页面 tab):点击选中对应节点。
|
|
10360
|
+
* 单步组的跳转仍由头部的「回到」按钮触发。
|
|
9587
10361
|
*/
|
|
9588
|
-
const isHeadClickable = (0, vue.computed)(() => props.
|
|
10362
|
+
const isHeadClickable = (0, vue.computed)(() => merged.value || props.selectEnabled);
|
|
9589
10363
|
const headTitle = (0, vue.computed)(() => {
|
|
9590
|
-
if (
|
|
9591
|
-
|
|
10364
|
+
if (merged.value) {
|
|
10365
|
+
const expandHint = props.expanded ? "点击收起子步" : "点击展开子步";
|
|
10366
|
+
return props.selectEnabled ? `${expandHint}(并选中该节点)` : expandHint;
|
|
10367
|
+
}
|
|
10368
|
+
if (props.selectEnabled) return "点击选中该节点";
|
|
10369
|
+
if (props.group.isCurrent) return "当前所在记录";
|
|
9592
10370
|
return "";
|
|
9593
10371
|
});
|
|
9594
10372
|
/**
|
|
9595
|
-
*
|
|
10373
|
+
* 头部点击行为:
|
|
10374
|
+
* - 开启 selectEnabled 时,发出 select(携带组内首步 index,上层据此选中节点);
|
|
10375
|
+
* - 合并组同时切换展开 / 收起。
|
|
9596
10376
|
*/
|
|
9597
10377
|
const onHeadClick = () => {
|
|
9598
|
-
if (props.
|
|
10378
|
+
if (props.selectEnabled && props.group.subSteps.length) emit("select", props.group.subSteps[0].index);
|
|
10379
|
+
if (merged.value) emit("toggle", props.group.key);
|
|
9599
10380
|
};
|
|
9600
10381
|
const onGotoClick = (index) => {
|
|
9601
10382
|
if (!props.gotoEnabled) return;
|
|
9602
10383
|
emit("goto", index);
|
|
9603
10384
|
};
|
|
10385
|
+
/** 点击子步行:开启 selectEnabled 时选中该子步对应的节点。 */
|
|
10386
|
+
const onSubStepClick = (index) => {
|
|
10387
|
+
if (!props.selectEnabled) return;
|
|
10388
|
+
emit("select", index);
|
|
10389
|
+
};
|
|
9604
10390
|
const subStepTitle = (s) => {
|
|
10391
|
+
if (props.selectEnabled) return "点击选中该节点";
|
|
9605
10392
|
if (s.isCurrent) return "当前所在记录";
|
|
9606
10393
|
return "";
|
|
9607
10394
|
};
|
|
10395
|
+
/**
|
|
10396
|
+
* 头部是否展示「已保存」标记:
|
|
10397
|
+
* - 单步组:取该唯一子步的 saved;
|
|
10398
|
+
* - 合并组:组内任一子步为已保存即在头部提示(具体落在哪一步可展开查看)。
|
|
10399
|
+
*/
|
|
10400
|
+
const headSaved = (0, vue.computed)(() => merged.value ? props.group.subSteps.some((s) => s.saved) : Boolean(props.group.subSteps[0]?.saved));
|
|
9608
10401
|
/** 单步组头部是否展示"查看差异"入口:要求该唯一子步本身可对比。 */
|
|
9609
|
-
const headDiffable = (0, vue.computed)(() => !
|
|
10402
|
+
const headDiffable = (0, vue.computed)(() => !merged.value && Boolean(props.group.subSteps[0]?.diffable));
|
|
9610
10403
|
/** 单步组头部是否展示"回滚"入口:要求该唯一子步本身可回滚(已应用)。 */
|
|
9611
|
-
const headRevertable = (0, vue.computed)(() => !
|
|
10404
|
+
const headRevertable = (0, vue.computed)(() => !merged.value && Boolean(props.group.subSteps[0]?.revertable));
|
|
10405
|
+
/** 单步组头部是否展示"回到"入口:可跳转、非当前、且存在唯一子步。 */
|
|
10406
|
+
const canHeadGoto = (0, vue.computed)(() => !merged.value && props.gotoEnabled && !props.group.isCurrent && props.group.subSteps.length > 0);
|
|
9612
10407
|
/**
|
|
9613
10408
|
* 合并组展开后的子步渲染顺序:与外层分组列表保持一致——倒序展示(最新的子步在最上方)。
|
|
9614
10409
|
* 外层 page tab / bucket 都已对 groups 做了 reverse,子步沿用同样的视觉规则更直观。
|
|
9615
10410
|
* 注意:仅用于渲染,原 `subSteps` 保持时间正序,`headIndexLabel` 等基于首尾索引的展示语义不变。
|
|
9616
10411
|
*/
|
|
9617
|
-
const subStepsDisplay = (0, vue.computed)(() => props.subSteps.slice().reverse());
|
|
10412
|
+
const subStepsDisplay = (0, vue.computed)(() => props.group.subSteps.slice().reverse());
|
|
9618
10413
|
/**
|
|
9619
10414
|
* 头部索引展示:
|
|
9620
|
-
* -
|
|
10415
|
+
* - 单步组(非合并):显示该唯一 step 的编号,如 `#5`;
|
|
9621
10416
|
* - 合并组:显示组内 step 的编号范围,如 `#3-#7`(首尾相同则退化为 `#5`)。
|
|
9622
10417
|
*
|
|
9623
10418
|
* 这里展示的是 step.index + 1(与子步列表 `#{{ s.index + 1 }}` 保持一致),从 1 起编号更符合直觉。
|
|
9624
10419
|
*/
|
|
9625
10420
|
const headIndexLabel = (0, vue.computed)(() => {
|
|
9626
|
-
const list = props.subSteps;
|
|
10421
|
+
const list = props.group.subSteps;
|
|
9627
10422
|
if (!list.length) return "";
|
|
9628
10423
|
const first = list[0].index + 1;
|
|
9629
10424
|
const last = list[list.length - 1].index + 1;
|
|
9630
|
-
if (!
|
|
10425
|
+
if (!merged.value || first === last) return `#${first}`;
|
|
9631
10426
|
return `#${first}-#${last}`;
|
|
9632
10427
|
});
|
|
9633
10428
|
const headIndexTitle = (0, vue.computed)(() => {
|
|
9634
|
-
|
|
9635
|
-
|
|
10429
|
+
const list = props.group.subSteps;
|
|
10430
|
+
if (!merged.value) return `历史步骤编号 #${list[0]?.index + 1}`;
|
|
10431
|
+
return `合并了第 ${list[0]?.index + 1} 至第 ${list[list.length - 1]?.index + 1} 共 ${list.length} 条历史步骤`;
|
|
9636
10432
|
});
|
|
9637
10433
|
const onDiffClick = (index) => {
|
|
9638
10434
|
emit("diff-step", index);
|
|
@@ -9642,9 +10438,9 @@
|
|
|
9642
10438
|
};
|
|
9643
10439
|
return (_ctx, _cache) => {
|
|
9644
10440
|
return (0, vue.openBlock)(), (0, vue.createElementBlock)("li", { class: (0, vue.normalizeClass)(["m-editor-history-list-item m-editor-history-list-group", {
|
|
9645
|
-
"is-undone": !__props.applied,
|
|
9646
|
-
"is-merged":
|
|
9647
|
-
"is-current": __props.isCurrent
|
|
10441
|
+
"is-undone": !__props.group.applied,
|
|
10442
|
+
"is-merged": merged.value,
|
|
10443
|
+
"is-current": __props.group.isCurrent
|
|
9648
10444
|
}]) }, [(0, vue.createElementVNode)("div", {
|
|
9649
10445
|
class: (0, vue.normalizeClass)(["m-editor-history-list-group-head", { "is-clickable": isHeadClickable.value }]),
|
|
9650
10446
|
title: headTitle.value,
|
|
@@ -9653,72 +10449,90 @@
|
|
|
9653
10449
|
(0, vue.createElementVNode)("span", {
|
|
9654
10450
|
class: "m-editor-history-list-item-index",
|
|
9655
10451
|
title: headIndexTitle.value
|
|
9656
|
-
}, (0, vue.toDisplayString)(headIndexLabel.value), 9, _hoisted_2$
|
|
9657
|
-
(0, vue.createElementVNode)("span", { class: (0, vue.normalizeClass)(["m-editor-history-list-item-op", `op-${__props.opType}`]) }, (0, vue.toDisplayString)((0, vue.unref)(opLabel)(__props.opType)), 3),
|
|
9658
|
-
(0, vue.createElementVNode)("span", _hoisted_3$
|
|
9659
|
-
|
|
9660
|
-
|
|
9661
|
-
|
|
9662
|
-
|
|
9663
|
-
|
|
9664
|
-
|
|
9665
|
-
|
|
10452
|
+
}, (0, vue.toDisplayString)(headIndexLabel.value), 9, _hoisted_2$23),
|
|
10453
|
+
(0, vue.createElementVNode)("span", { class: (0, vue.normalizeClass)(["m-editor-history-list-item-op", `op-${__props.group.opType}`]) }, (0, vue.toDisplayString)((0, vue.unref)(opLabel)(__props.group.opType)), 3),
|
|
10454
|
+
(0, vue.createElementVNode)("span", _hoisted_3$11, (0, vue.toDisplayString)(__props.group.desc), 1),
|
|
10455
|
+
headSaved.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_4$8, "已保存")) : (0, vue.createCommentVNode)("v-if", true),
|
|
10456
|
+
!merged.value && (headRevertable.value || headDiffable.value || canHeadGoto.value) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_5$3, [
|
|
10457
|
+
headRevertable.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
|
|
10458
|
+
key: 0,
|
|
10459
|
+
class: "m-editor-history-list-item-revert",
|
|
10460
|
+
title: "将该步骤的修改作为一次新操作反向应用(不影响后续历史)",
|
|
10461
|
+
onClick: _cache[0] || (_cache[0] = (0, vue.withModifiers)(($event) => onRevertClick(__props.group.subSteps[0].index), ["stop"]))
|
|
10462
|
+
}, "回滚")) : (0, vue.createCommentVNode)("v-if", true),
|
|
10463
|
+
canHeadGoto.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
|
|
10464
|
+
key: 1,
|
|
10465
|
+
class: "m-editor-history-list-item-goto",
|
|
10466
|
+
title: "回到该记录",
|
|
10467
|
+
onClick: _cache[1] || (_cache[1] = (0, vue.withModifiers)(($event) => onGotoClick(__props.group.subSteps[0].index), ["stop"]))
|
|
10468
|
+
}, "回到")) : (0, vue.createCommentVNode)("v-if", true),
|
|
10469
|
+
headDiffable.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
|
|
10470
|
+
key: 2,
|
|
10471
|
+
class: "m-editor-history-list-item-diff",
|
|
10472
|
+
title: "查看修改差异",
|
|
10473
|
+
onClick: _cache[2] || (_cache[2] = (0, vue.withModifiers)(($event) => onDiffClick(__props.group.subSteps[0].index), ["stop"]))
|
|
10474
|
+
}, "查看差异")) : (0, vue.createCommentVNode)("v-if", true)
|
|
10475
|
+
])) : (0, vue.createCommentVNode)("v-if", true),
|
|
10476
|
+
!merged.value && (0, vue.unref)(sourceLabel)(__props.group.source) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
|
|
9666
10477
|
key: 2,
|
|
9667
|
-
class: "m-editor-history-list-item-
|
|
9668
|
-
title:
|
|
9669
|
-
|
|
9670
|
-
|
|
9671
|
-
!__props.merged && __props.gotoEnabled && !__props.isCurrent && __props.subSteps.length ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
|
|
10478
|
+
class: "m-editor-history-list-item-source",
|
|
10479
|
+
title: `操作途径:${(0, vue.unref)(sourceLabel)(__props.group.source)}`
|
|
10480
|
+
}, (0, vue.toDisplayString)((0, vue.unref)(sourceLabel)(__props.group.source)), 9, _hoisted_6$3)) : (0, vue.createCommentVNode)("v-if", true),
|
|
10481
|
+
!merged.value && __props.group.time ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
|
|
9672
10482
|
key: 3,
|
|
9673
|
-
class: "m-editor-history-list-item-
|
|
9674
|
-
title:
|
|
9675
|
-
|
|
9676
|
-
|
|
9677
|
-
|
|
9678
|
-
key: 4,
|
|
9679
|
-
class: "m-editor-history-list-item-diff",
|
|
9680
|
-
title: "查看修改差异",
|
|
9681
|
-
onClick: _cache[2] || (_cache[2] = (0, vue.withModifiers)(($event) => onDiffClick(__props.subSteps[0].index), ["stop"]))
|
|
9682
|
-
}, "查看差异")) : (0, vue.createCommentVNode)("v-if", true),
|
|
9683
|
-
__props.merged ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
|
|
10483
|
+
class: "m-editor-history-list-item-time",
|
|
10484
|
+
title: __props.group.timeTitle || __props.group.time
|
|
10485
|
+
}, (0, vue.toDisplayString)(__props.group.time), 9, _hoisted_7$1)) : (0, vue.createCommentVNode)("v-if", true),
|
|
10486
|
+
merged.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_8, "合并 " + (0, vue.toDisplayString)(stepCount.value) + " 步", 1)) : (0, vue.createCommentVNode)("v-if", true),
|
|
10487
|
+
merged.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
|
|
9684
10488
|
key: 5,
|
|
9685
10489
|
class: (0, vue.normalizeClass)(["m-editor-history-list-group-toggle", { "is-expanded": __props.expanded }])
|
|
9686
10490
|
}, "▾", 2)) : (0, vue.createCommentVNode)("v-if", true)
|
|
9687
|
-
], 10, _hoisted_1$61),
|
|
10491
|
+
], 10, _hoisted_1$61), merged.value && __props.expanded ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("ul", _hoisted_9, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(subStepsDisplay.value, (s) => {
|
|
9688
10492
|
return (0, vue.openBlock)(), (0, vue.createElementBlock)("li", {
|
|
9689
10493
|
key: s.index,
|
|
9690
10494
|
class: (0, vue.normalizeClass)({
|
|
9691
10495
|
"is-undone": !s.applied,
|
|
9692
|
-
"is-current": s.isCurrent
|
|
10496
|
+
"is-current": s.isCurrent,
|
|
10497
|
+
"is-clickable": __props.selectEnabled
|
|
9693
10498
|
}),
|
|
9694
|
-
title: subStepTitle(s)
|
|
10499
|
+
title: subStepTitle(s),
|
|
10500
|
+
onClick: ($event) => onSubStepClick(s.index)
|
|
9695
10501
|
}, [
|
|
9696
|
-
(0, vue.createElementVNode)("span",
|
|
9697
|
-
(0, vue.createElementVNode)("span",
|
|
10502
|
+
(0, vue.createElementVNode)("span", _hoisted_11, "#" + (0, vue.toDisplayString)(s.index + 1), 1),
|
|
10503
|
+
(0, vue.createElementVNode)("span", _hoisted_12, (0, vue.toDisplayString)(s.desc), 1),
|
|
10504
|
+
s.saved ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_13, "已保存")) : (0, vue.createCommentVNode)("v-if", true),
|
|
10505
|
+
s.revertable || s.diffable || __props.gotoEnabled && !s.isCurrent ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_14, [
|
|
10506
|
+
s.revertable ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
|
|
10507
|
+
key: 0,
|
|
10508
|
+
class: "m-editor-history-list-item-revert",
|
|
10509
|
+
title: "将该步骤的修改作为一次新操作反向应用(不影响后续历史)",
|
|
10510
|
+
onClick: (0, vue.withModifiers)(($event) => onRevertClick(s.index), ["stop"])
|
|
10511
|
+
}, "回滚", 8, _hoisted_15)) : (0, vue.createCommentVNode)("v-if", true),
|
|
10512
|
+
__props.gotoEnabled && !s.isCurrent ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
|
|
10513
|
+
key: 1,
|
|
10514
|
+
class: "m-editor-history-list-item-goto",
|
|
10515
|
+
title: "回到该记录",
|
|
10516
|
+
onClick: (0, vue.withModifiers)(($event) => onGotoClick(s.index), ["stop"])
|
|
10517
|
+
}, "回到", 8, _hoisted_16)) : (0, vue.createCommentVNode)("v-if", true),
|
|
10518
|
+
s.diffable ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
|
|
10519
|
+
key: 2,
|
|
10520
|
+
class: "m-editor-history-list-item-diff",
|
|
10521
|
+
title: "查看修改差异",
|
|
10522
|
+
onClick: (0, vue.withModifiers)(($event) => onDiffClick(s.index), ["stop"])
|
|
10523
|
+
}, "查看差异", 8, _hoisted_17)) : (0, vue.createCommentVNode)("v-if", true)
|
|
10524
|
+
])) : (0, vue.createCommentVNode)("v-if", true),
|
|
10525
|
+
(0, vue.unref)(sourceLabel)(s.source) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
|
|
10526
|
+
key: 2,
|
|
10527
|
+
class: "m-editor-history-list-item-source",
|
|
10528
|
+
title: `操作途径:${(0, vue.unref)(sourceLabel)(s.source)}`
|
|
10529
|
+
}, (0, vue.toDisplayString)((0, vue.unref)(sourceLabel)(s.source)), 9, _hoisted_18)) : (0, vue.createCommentVNode)("v-if", true),
|
|
9698
10530
|
s.time ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
|
|
9699
|
-
key:
|
|
10531
|
+
key: 3,
|
|
9700
10532
|
class: "m-editor-history-list-item-time",
|
|
9701
10533
|
title: s.timeTitle || s.time
|
|
9702
|
-
}, (0, vue.toDisplayString)(s.time), 9,
|
|
9703
|
-
|
|
9704
|
-
key: 1,
|
|
9705
|
-
class: "m-editor-history-list-item-revert",
|
|
9706
|
-
title: "将该步骤的修改作为一次新操作反向应用(不影响后续历史)",
|
|
9707
|
-
onClick: (0, vue.withModifiers)(($event) => onRevertClick(s.index), ["stop"])
|
|
9708
|
-
}, "回滚", 8, _hoisted_11)) : (0, vue.createCommentVNode)("v-if", true),
|
|
9709
|
-
__props.gotoEnabled && !s.isCurrent ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
|
|
9710
|
-
key: 2,
|
|
9711
|
-
class: "m-editor-history-list-item-goto",
|
|
9712
|
-
title: "回到该记录",
|
|
9713
|
-
onClick: (0, vue.withModifiers)(($event) => onGotoClick(s.index), ["stop"])
|
|
9714
|
-
}, "回到", 8, _hoisted_12)) : (0, vue.createCommentVNode)("v-if", true),
|
|
9715
|
-
s.diffable ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
|
|
9716
|
-
key: 3,
|
|
9717
|
-
class: "m-editor-history-list-item-diff",
|
|
9718
|
-
title: "查看修改差异",
|
|
9719
|
-
onClick: (0, vue.withModifiers)(($event) => onDiffClick(s.index), ["stop"])
|
|
9720
|
-
}, "查看差异", 8, _hoisted_13)) : (0, vue.createCommentVNode)("v-if", true)
|
|
9721
|
-
], 10, _hoisted_7$1);
|
|
10534
|
+
}, (0, vue.toDisplayString)(s.time), 9, _hoisted_19)) : (0, vue.createCommentVNode)("v-if", true)
|
|
10535
|
+
], 10, _hoisted_10);
|
|
9722
10536
|
}), 128))])) : (0, vue.createCommentVNode)("v-if", true)], 2);
|
|
9723
10537
|
};
|
|
9724
10538
|
}
|
|
@@ -9729,6 +10543,12 @@
|
|
|
9729
10543
|
//#endregion
|
|
9730
10544
|
//#region packages/editor/src/layouts/history-list/InitialRow.vue?vue&type=script&setup=true&lang.ts
|
|
9731
10545
|
var _hoisted_1$60 = ["title"];
|
|
10546
|
+
var _hoisted_2$22 = { class: "m-editor-history-list-item-desc" };
|
|
10547
|
+
var _hoisted_3$10 = {
|
|
10548
|
+
key: 0,
|
|
10549
|
+
class: "m-editor-history-list-item-actions"
|
|
10550
|
+
};
|
|
10551
|
+
var _hoisted_4$7 = ["title"];
|
|
9732
10552
|
var InitialRow_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
9733
10553
|
name: "MEditorHistoryListInitialRow",
|
|
9734
10554
|
__name: "InitialRow",
|
|
@@ -9737,7 +10557,8 @@
|
|
|
9737
10557
|
gotoEnabled: {
|
|
9738
10558
|
type: Boolean,
|
|
9739
10559
|
default: true
|
|
9740
|
-
}
|
|
10560
|
+
},
|
|
10561
|
+
marker: { default: void 0 }
|
|
9741
10562
|
},
|
|
9742
10563
|
emits: ["goto-initial"],
|
|
9743
10564
|
setup(__props, { emit: __emit }) {
|
|
@@ -9745,10 +10566,19 @@
|
|
|
9745
10566
|
* 「初始状态」记录行:渲染于历史列表底部,作为整个栈的"零点"。
|
|
9746
10567
|
* - 点击该行会把对应栈撤销到 cursor === 0(即没有任何已应用步骤),等同于回到所有修改之前。
|
|
9747
10568
|
* - 当对应栈本身已处于 cursor === 0 时(isCurrent=true),用户已在初始状态,点击不再触发动作。
|
|
10569
|
+
* - 当上层传入 `marker`(设置 root 时为该页生成的「未修改的初始状态」标记)时,
|
|
10570
|
+
* 用标记的文案与时间渲染本行;标记不进入撤销/重做栈,仅作为该页基线展示。
|
|
9748
10571
|
*
|
|
9749
10572
|
* 该行不是真实 step,仅作为 UI 入口;上层负责把"点击"翻译为 `service.goto*(0)`。
|
|
9750
10573
|
*/
|
|
9751
10574
|
const props = __props;
|
|
10575
|
+
const desc = (0, vue.computed)(() => props.marker?.historyDescription || "未修改的初始状态");
|
|
10576
|
+
const time = (0, vue.computed)(() => formatHistoryTime(props.marker?.timestamp));
|
|
10577
|
+
const timeTitle = (0, vue.computed)(() => formatHistoryFullTime(props.marker?.timestamp));
|
|
10578
|
+
const rowTitle = (0, vue.computed)(() => {
|
|
10579
|
+
const base = props.marker?.historyDescription || "未修改的初始状态";
|
|
10580
|
+
return props.isCurrent ? `当前已回到${base}` : `点击回到${base}`;
|
|
10581
|
+
});
|
|
9752
10582
|
const emit = __emit;
|
|
9753
10583
|
const onClick = () => {
|
|
9754
10584
|
if (props.isCurrent) return;
|
|
@@ -9760,20 +10590,24 @@
|
|
|
9760
10590
|
"is-current": __props.isCurrent,
|
|
9761
10591
|
"is-clickable": !__props.isCurrent
|
|
9762
10592
|
}]),
|
|
9763
|
-
title:
|
|
10593
|
+
title: rowTitle.value
|
|
9764
10594
|
}, [
|
|
9765
10595
|
_cache[0] || (_cache[0] = (0, vue.createElementVNode)("span", {
|
|
9766
10596
|
class: "m-editor-history-list-item-index",
|
|
9767
10597
|
title: "历史步骤编号 #0(未修改的初始状态)"
|
|
9768
10598
|
}, "#0", -1)),
|
|
9769
10599
|
_cache[1] || (_cache[1] = (0, vue.createElementVNode)("span", { class: "m-editor-history-list-item-op op-initial" }, "初始", -1)),
|
|
9770
|
-
|
|
9771
|
-
__props.gotoEnabled && !__props.isCurrent ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
|
|
9772
|
-
key: 0,
|
|
10600
|
+
(0, vue.createElementVNode)("span", _hoisted_2$22, (0, vue.toDisplayString)(desc.value), 1),
|
|
10601
|
+
__props.gotoEnabled && !__props.isCurrent ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_3$10, [(0, vue.createElementVNode)("span", {
|
|
9773
10602
|
class: "m-editor-history-list-item-goto",
|
|
9774
10603
|
title: "回到该记录",
|
|
9775
10604
|
onClick: (0, vue.withModifiers)(onClick, ["stop"])
|
|
9776
|
-
}, "回到")) : (0, vue.createCommentVNode)("v-if", true)
|
|
10605
|
+
}, "回到")])) : (0, vue.createCommentVNode)("v-if", true),
|
|
10606
|
+
time.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
|
|
10607
|
+
key: 1,
|
|
10608
|
+
class: "m-editor-history-list-item-time",
|
|
10609
|
+
title: timeTitle.value
|
|
10610
|
+
}, (0, vue.toDisplayString)(time.value), 9, _hoisted_4$7)) : (0, vue.createCommentVNode)("v-if", true)
|
|
9777
10611
|
], 10, _hoisted_1$60);
|
|
9778
10612
|
};
|
|
9779
10613
|
}
|
|
@@ -9784,29 +10618,17 @@
|
|
|
9784
10618
|
//#endregion
|
|
9785
10619
|
//#region packages/editor/src/layouts/history-list/Bucket.vue?vue&type=script&setup=true&lang.ts
|
|
9786
10620
|
var _hoisted_1$59 = { class: "m-editor-history-list-bucket" };
|
|
9787
|
-
var _hoisted_2$
|
|
9788
|
-
var _hoisted_3$
|
|
10621
|
+
var _hoisted_2$21 = { class: "m-editor-history-list-bucket-title" };
|
|
10622
|
+
var _hoisted_3$9 = { class: "m-editor-history-list-bucket-count" };
|
|
9789
10623
|
var _hoisted_4$6 = { class: "m-editor-history-list-ul" };
|
|
9790
10624
|
var Bucket_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
9791
10625
|
name: "MEditorHistoryListBucket",
|
|
9792
10626
|
__name: "Bucket",
|
|
9793
10627
|
props: {
|
|
9794
|
-
|
|
10628
|
+
config: {},
|
|
9795
10629
|
bucketId: {},
|
|
9796
|
-
prefix: {},
|
|
9797
|
-
showInitial: {
|
|
9798
|
-
type: Boolean,
|
|
9799
|
-
default: true
|
|
9800
|
-
},
|
|
9801
10630
|
groups: {},
|
|
9802
|
-
|
|
9803
|
-
describeStep: {},
|
|
9804
|
-
isStepDiffable: {},
|
|
9805
|
-
expanded: {},
|
|
9806
|
-
gotoEnabled: {
|
|
9807
|
-
type: Boolean,
|
|
9808
|
-
default: true
|
|
9809
|
-
}
|
|
10631
|
+
expanded: {}
|
|
9810
10632
|
},
|
|
9811
10633
|
emits: [
|
|
9812
10634
|
"toggle",
|
|
@@ -9817,62 +10639,42 @@
|
|
|
9817
10639
|
],
|
|
9818
10640
|
setup(__props) {
|
|
9819
10641
|
const props = __props;
|
|
10642
|
+
/**
|
|
10643
|
+
* 子项 / 折叠状态 key:`${prefix}-${bucketId}-${组内首步 index}`。
|
|
10644
|
+
* 以稳定的 step 索引(而非展示位置)标识分组,历史数据更新后已展开的分组状态仍能正确保持。
|
|
10645
|
+
*/
|
|
10646
|
+
const rowKey = (group) => `${props.config.prefix}-${props.bucketId}-${group.steps[0]?.index}`;
|
|
10647
|
+
/** 把原始分组派生为 GroupRow 直接消费的视图模型。 */
|
|
10648
|
+
const toRow = (group) => toRowGroup(group, rowKey(group), props.config);
|
|
9820
10649
|
/** 该 bucket 是否处于初始状态(栈 cursor=0),等价于全部 group 都未 applied。 */
|
|
9821
10650
|
const isInitial = (0, vue.computed)(() => props.groups.length > 0 && props.groups.every((g) => !g.applied));
|
|
9822
10651
|
return (_ctx, _cache) => {
|
|
9823
|
-
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$59, [(0, vue.createElementVNode)("div", _hoisted_2$
|
|
9824
|
-
(0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)(__props.title), 1),
|
|
10652
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$59, [(0, vue.createElementVNode)("div", _hoisted_2$21, [
|
|
10653
|
+
(0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)(__props.config.title), 1),
|
|
9825
10654
|
(0, vue.createElementVNode)("code", null, (0, vue.toDisplayString)(String(__props.bucketId)), 1),
|
|
9826
|
-
(0, vue.createElementVNode)("span", _hoisted_3$
|
|
10655
|
+
(0, vue.createElementVNode)("span", _hoisted_3$9, (0, vue.toDisplayString)(__props.groups.length) + " 组", 1)
|
|
9827
10656
|
]), (0, vue.createElementVNode)("ul", _hoisted_4$6, [
|
|
9828
10657
|
((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.groups, (group) => {
|
|
9829
10658
|
return (0, vue.openBlock)(), (0, vue.createBlock)(GroupRow_default, {
|
|
9830
|
-
key:
|
|
9831
|
-
|
|
9832
|
-
|
|
9833
|
-
|
|
9834
|
-
"op-type": group.opType,
|
|
9835
|
-
desc: __props.describeGroup(group),
|
|
9836
|
-
time: (0, vue.unref)(formatHistoryTime)((0, vue.unref)(groupTimestamp)(group)),
|
|
9837
|
-
"time-title": (0, vue.unref)(formatHistoryFullTime)((0, vue.unref)(groupTimestamp)(group)),
|
|
9838
|
-
"step-count": group.steps.length,
|
|
9839
|
-
"sub-steps": group.steps.map((s) => ({
|
|
9840
|
-
index: s.index,
|
|
9841
|
-
applied: s.applied,
|
|
9842
|
-
isCurrent: s.isCurrent,
|
|
9843
|
-
desc: __props.describeStep(s.step),
|
|
9844
|
-
diffable: __props.isStepDiffable ? __props.isStepDiffable(s.step) : false,
|
|
9845
|
-
revertable: s.applied,
|
|
9846
|
-
time: (0, vue.unref)(formatHistoryTime)(s.step.timestamp),
|
|
9847
|
-
timeTitle: (0, vue.unref)(formatHistoryFullTime)(s.step.timestamp)
|
|
9848
|
-
})),
|
|
9849
|
-
"is-current": group.isCurrent,
|
|
9850
|
-
expanded: !!__props.expanded[`${__props.prefix}-${__props.bucketId}-${group.steps[0]?.index}`],
|
|
9851
|
-
"goto-enabled": __props.gotoEnabled,
|
|
10659
|
+
key: rowKey(group),
|
|
10660
|
+
group: toRow(group),
|
|
10661
|
+
expanded: (0, vue.unref)(isHistoryGroupExpanded)(__props.expanded, rowKey(group)),
|
|
10662
|
+
"goto-enabled": __props.config.gotoEnabled,
|
|
9852
10663
|
onToggle: _cache[0] || (_cache[0] = (key) => _ctx.$emit("toggle", key)),
|
|
9853
10664
|
onGoto: _cache[1] || (_cache[1] = (index) => _ctx.$emit("goto", __props.bucketId, index)),
|
|
9854
10665
|
onDiffStep: _cache[2] || (_cache[2] = (index) => _ctx.$emit("diff-step", __props.bucketId, index)),
|
|
9855
10666
|
onRevertStep: _cache[3] || (_cache[3] = (index) => _ctx.$emit("revert-step", __props.bucketId, index))
|
|
9856
10667
|
}, null, 8, [
|
|
9857
|
-
"group
|
|
9858
|
-
"applied",
|
|
9859
|
-
"merged",
|
|
9860
|
-
"op-type",
|
|
9861
|
-
"desc",
|
|
9862
|
-
"time",
|
|
9863
|
-
"time-title",
|
|
9864
|
-
"step-count",
|
|
9865
|
-
"sub-steps",
|
|
9866
|
-
"is-current",
|
|
10668
|
+
"group",
|
|
9867
10669
|
"expanded",
|
|
9868
10670
|
"goto-enabled"
|
|
9869
10671
|
]);
|
|
9870
10672
|
}), 128)),
|
|
9871
|
-
(0, vue.createCommentVNode)("\n 初始状态项:永远位于该 bucket 列表底部(同样按倒序展示,最底部 = 最早状态)。\n 当 bucket 内所有 group 都未 applied 时即为当前位置。\n showInitial=false 时不展示(用于没有\"撤销到初始状态\"语义的自定义历史,如业务模块历史)。\n "),
|
|
9872
|
-
__props.showInitial !== false ? ((0, vue.openBlock)(), (0, vue.createBlock)(InitialRow_default, {
|
|
10673
|
+
(0, vue.createCommentVNode)("\n 初始状态项:永远位于该 bucket 列表底部(同样按倒序展示,最底部 = 最早状态)。\n 当 bucket 内所有 group 都未 applied 时即为当前位置。\n config.showInitial=false 时不展示(用于没有\"撤销到初始状态\"语义的自定义历史,如业务模块历史)。\n "),
|
|
10674
|
+
__props.config.showInitial !== false ? ((0, vue.openBlock)(), (0, vue.createBlock)(InitialRow_default, {
|
|
9873
10675
|
key: 0,
|
|
9874
10676
|
"is-current": isInitial.value,
|
|
9875
|
-
"goto-enabled": __props.gotoEnabled,
|
|
10677
|
+
"goto-enabled": __props.config.gotoEnabled,
|
|
9876
10678
|
onGotoInitial: _cache[4] || (_cache[4] = ($event) => _ctx.$emit("goto-initial", __props.bucketId))
|
|
9877
10679
|
}, null, 8, ["is-current", "goto-enabled"])) : (0, vue.createCommentVNode)("v-if", true)
|
|
9878
10680
|
])]);
|
|
@@ -9888,66 +10690,52 @@
|
|
|
9888
10690
|
key: 0,
|
|
9889
10691
|
class: "m-editor-history-list-empty"
|
|
9890
10692
|
};
|
|
10693
|
+
var _hoisted_2$20 = { class: "m-editor-history-list-toolbar" };
|
|
10694
|
+
var _hoisted_3$8 = ["title"];
|
|
9891
10695
|
var BucketTab_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
9892
10696
|
name: "MEditorHistoryListBucketTab",
|
|
9893
10697
|
__name: "BucketTab",
|
|
9894
10698
|
props: {
|
|
9895
|
-
|
|
9896
|
-
prefix: {},
|
|
10699
|
+
config: {},
|
|
9897
10700
|
buckets: {},
|
|
9898
|
-
|
|
9899
|
-
describeStep: {},
|
|
9900
|
-
isStepDiffable: {},
|
|
9901
|
-
expanded: {},
|
|
9902
|
-
gotoEnabled: {
|
|
9903
|
-
type: Boolean,
|
|
9904
|
-
default: true
|
|
9905
|
-
}
|
|
10701
|
+
expanded: {}
|
|
9906
10702
|
},
|
|
9907
10703
|
emits: [
|
|
9908
10704
|
"toggle",
|
|
9909
10705
|
"goto",
|
|
9910
10706
|
"goto-initial",
|
|
9911
10707
|
"diff-step",
|
|
9912
|
-
"revert-step"
|
|
10708
|
+
"revert-step",
|
|
10709
|
+
"clear"
|
|
9913
10710
|
],
|
|
9914
10711
|
setup(__props) {
|
|
9915
10712
|
return (_ctx, _cache) => {
|
|
9916
|
-
return !__props.buckets.length ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$58, "暂无操作记录")) : ((0, vue.openBlock)(), (0, vue.
|
|
9917
|
-
|
|
9918
|
-
|
|
9919
|
-
|
|
10713
|
+
return !__props.buckets.length ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$58, "暂无操作记录")) : ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 1 }, [(0, vue.createElementVNode)("div", _hoisted_2$20, [(0, vue.createElementVNode)("span", {
|
|
10714
|
+
class: "m-editor-history-list-clear",
|
|
10715
|
+
title: `清空${__props.config.title}的历史记录`,
|
|
10716
|
+
onClick: _cache[0] || (_cache[0] = ($event) => _ctx.$emit("clear"))
|
|
10717
|
+
}, "清空", 8, _hoisted_3$8)]), (0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicScrollbar), { "max-height": "360px" }, {
|
|
9920
10718
|
default: (0, vue.withCtx)(() => [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.buckets, (bucket) => {
|
|
9921
10719
|
return (0, vue.openBlock)(), (0, vue.createBlock)(Bucket_default, {
|
|
9922
|
-
key: `${__props.prefix}-${bucket.id}`,
|
|
9923
|
-
|
|
10720
|
+
key: `${__props.config.prefix}-${bucket.id}`,
|
|
10721
|
+
config: __props.config,
|
|
9924
10722
|
"bucket-id": bucket.id,
|
|
9925
|
-
prefix: __props.prefix,
|
|
9926
10723
|
groups: bucket.groups,
|
|
9927
|
-
"describe-group": __props.describeGroup,
|
|
9928
|
-
"describe-step": __props.describeStep,
|
|
9929
|
-
"is-step-diffable": __props.isStepDiffable,
|
|
9930
10724
|
expanded: __props.expanded,
|
|
9931
|
-
"
|
|
9932
|
-
|
|
9933
|
-
|
|
9934
|
-
|
|
9935
|
-
|
|
9936
|
-
onRevertStep: _cache[4] || (_cache[4] = (id, index) => _ctx.$emit("revert-step", id, index))
|
|
10725
|
+
onToggle: _cache[1] || (_cache[1] = (key) => _ctx.$emit("toggle", key)),
|
|
10726
|
+
onGoto: _cache[2] || (_cache[2] = (id, index) => _ctx.$emit("goto", id, index)),
|
|
10727
|
+
onGotoInitial: _cache[3] || (_cache[3] = (id) => _ctx.$emit("goto-initial", id)),
|
|
10728
|
+
onDiffStep: _cache[4] || (_cache[4] = (id, index) => _ctx.$emit("diff-step", id, index)),
|
|
10729
|
+
onRevertStep: _cache[5] || (_cache[5] = (id, index) => _ctx.$emit("revert-step", id, index))
|
|
9937
10730
|
}, null, 8, [
|
|
9938
|
-
"
|
|
10731
|
+
"config",
|
|
9939
10732
|
"bucket-id",
|
|
9940
|
-
"prefix",
|
|
9941
10733
|
"groups",
|
|
9942
|
-
"
|
|
9943
|
-
"describe-step",
|
|
9944
|
-
"is-step-diffable",
|
|
9945
|
-
"expanded",
|
|
9946
|
-
"goto-enabled"
|
|
10734
|
+
"expanded"
|
|
9947
10735
|
]);
|
|
9948
10736
|
}), 128))]),
|
|
9949
10737
|
_: 1
|
|
9950
|
-
}));
|
|
10738
|
+
})], 64));
|
|
9951
10739
|
};
|
|
9952
10740
|
}
|
|
9953
10741
|
});
|
|
@@ -10030,6 +10818,20 @@
|
|
|
10030
10818
|
}
|
|
10031
10819
|
return !isEqual(curValue, lastValue);
|
|
10032
10820
|
};
|
|
10821
|
+
const removeStyleDisplayConfig = (formConfig) => formConfig.map((item) => {
|
|
10822
|
+
if (!("type" in item)) return item;
|
|
10823
|
+
if (item?.type !== "tab" || !Array.isArray(item.items)) return item;
|
|
10824
|
+
return {
|
|
10825
|
+
...item,
|
|
10826
|
+
items: item.items.map((tabPane) => {
|
|
10827
|
+
if (tabPane?.title !== "样式" || !Array.isArray(tabPane.items)) return tabPane;
|
|
10828
|
+
return {
|
|
10829
|
+
...tabPane,
|
|
10830
|
+
display: true
|
|
10831
|
+
};
|
|
10832
|
+
})
|
|
10833
|
+
};
|
|
10834
|
+
});
|
|
10033
10835
|
/**
|
|
10034
10836
|
* 内置的默认 FormConfig 加载逻辑:按 `category` 从对应 service / 工具取配置。
|
|
10035
10837
|
* 作为 ctx.defaultLoadConfig 透传给自定义 `loadConfig`,方便复用与二次加工。
|
|
@@ -10038,7 +10840,7 @@
|
|
|
10038
10840
|
switch (props.category) {
|
|
10039
10841
|
case "node":
|
|
10040
10842
|
if (!props.type) return [];
|
|
10041
|
-
return await propsService.getPropsConfig(props.type);
|
|
10843
|
+
return removeStyleDisplayConfig(await propsService.getPropsConfig(props.type, { node: props.value }));
|
|
10042
10844
|
case "data-source": return dataSourceService.getFormConfig(props.type || "base");
|
|
10043
10845
|
case "code-block": return getCodeBlockFormConfig({
|
|
10044
10846
|
paramColConfig: codeBlockService.getParamsColConfig(),
|
|
@@ -10131,7 +10933,7 @@
|
|
|
10131
10933
|
key: 0,
|
|
10132
10934
|
class: "m-editor-history-diff-dialog-notice"
|
|
10133
10935
|
};
|
|
10134
|
-
var _hoisted_3$
|
|
10936
|
+
var _hoisted_3$7 = { class: "m-editor-history-diff-dialog-header" };
|
|
10135
10937
|
var _hoisted_4$5 = { class: "m-editor-history-diff-dialog-target" };
|
|
10136
10938
|
var _hoisted_5$2 = { class: "m-editor-history-diff-dialog-controls" };
|
|
10137
10939
|
var _hoisted_6$2 = { class: "m-editor-history-diff-dialog-legend" };
|
|
@@ -10146,6 +10948,7 @@
|
|
|
10146
10948
|
extendState: {},
|
|
10147
10949
|
loadConfig: {},
|
|
10148
10950
|
width: { default: "900px" },
|
|
10951
|
+
isConfirm: { type: Boolean },
|
|
10149
10952
|
onConfirm: {},
|
|
10150
10953
|
selfDiffFieldTypes: {}
|
|
10151
10954
|
},
|
|
@@ -10200,18 +11003,23 @@
|
|
|
10200
11003
|
if (mode.value !== "current" || !payload.value) return false;
|
|
10201
11004
|
return isEqual(payload.value.value, payload.value.currentValue);
|
|
10202
11005
|
});
|
|
11006
|
+
/** confirm() 的 resolve,仅在「等待用户确认回滚」期间存在 */
|
|
11007
|
+
let confirmResolve = null;
|
|
10203
11008
|
const onConfirmClick = () => {
|
|
10204
|
-
|
|
10205
|
-
|
|
11009
|
+
props.onConfirm?.();
|
|
11010
|
+
confirmResolve?.(true);
|
|
11011
|
+
confirmResolve = null;
|
|
10206
11012
|
visible.value = false;
|
|
10207
11013
|
};
|
|
10208
11014
|
const targetText = (0, vue.computed)(() => {
|
|
10209
11015
|
if (!payload.value) return "";
|
|
10210
|
-
const
|
|
11016
|
+
const categoryText = {
|
|
10211
11017
|
node: "节点",
|
|
10212
11018
|
"data-source": "数据源",
|
|
10213
11019
|
"code-block": "代码块"
|
|
10214
|
-
}
|
|
11020
|
+
};
|
|
11021
|
+
const { category } = payload.value;
|
|
11022
|
+
const prefix = category ? categoryText[category] : "";
|
|
10215
11023
|
const label = payload.value.targetLabel || payload.value.type || "";
|
|
10216
11024
|
const { id } = payload.value;
|
|
10217
11025
|
return [prefix, id !== void 0 && id !== "" ? `${label}(${id})` : label].filter(Boolean).join(":");
|
|
@@ -10222,21 +11030,41 @@
|
|
|
10222
11030
|
viewMode.value = "form";
|
|
10223
11031
|
visible.value = true;
|
|
10224
11032
|
};
|
|
11033
|
+
/**
|
|
11034
|
+
* 以 Promise 形式打开确认回滚弹窗:
|
|
11035
|
+
* - 用户点击「确定回滚」时 resolve(true);
|
|
11036
|
+
* - 取消 / 关闭 / 按 Esc 等其他方式关闭弹窗时 resolve(false)。
|
|
11037
|
+
*
|
|
11038
|
+
* 同一时刻只允许一个待确认流程,重复调用会先 resolve(false) 掉上一个。
|
|
11039
|
+
*/
|
|
11040
|
+
const confirm = (p) => {
|
|
11041
|
+
confirmResolve?.(false);
|
|
11042
|
+
confirmResolve = null;
|
|
11043
|
+
return new Promise((resolve) => {
|
|
11044
|
+
confirmResolve = resolve;
|
|
11045
|
+
open(p);
|
|
11046
|
+
});
|
|
11047
|
+
};
|
|
10225
11048
|
const close = () => {
|
|
10226
11049
|
visible.value = false;
|
|
10227
11050
|
};
|
|
10228
11051
|
(0, vue.watch)(visible, (v) => {
|
|
10229
|
-
if (!v)
|
|
11052
|
+
if (!v) {
|
|
11053
|
+
payload.value = null;
|
|
11054
|
+
confirmResolve?.(false);
|
|
11055
|
+
confirmResolve = null;
|
|
11056
|
+
}
|
|
10230
11057
|
});
|
|
10231
11058
|
const onClose = () => {
|
|
10232
11059
|
emit("close");
|
|
10233
11060
|
};
|
|
10234
11061
|
__expose({
|
|
10235
11062
|
open,
|
|
11063
|
+
confirm,
|
|
10236
11064
|
close
|
|
10237
11065
|
});
|
|
10238
11066
|
return (_ctx, _cache) => {
|
|
10239
|
-
return (0, vue.openBlock)(), (0, vue.createBlock)(
|
|
11067
|
+
return (0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicDialog), {
|
|
10240
11068
|
modelValue: visible.value,
|
|
10241
11069
|
"onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => visible.value = $event),
|
|
10242
11070
|
class: "m-editor-history-diff-dialog",
|
|
@@ -10247,7 +11075,7 @@
|
|
|
10247
11075
|
width: __props.width,
|
|
10248
11076
|
onClose
|
|
10249
11077
|
}, {
|
|
10250
|
-
footer: (0, vue.withCtx)(() => [__props.
|
|
11078
|
+
footer: (0, vue.withCtx)(() => [__props.isConfirm ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), {
|
|
10251
11079
|
size: "small",
|
|
10252
11080
|
onClick: _cache[2] || (_cache[2] = ($event) => visible.value = false)
|
|
10253
11081
|
}, {
|
|
@@ -10268,9 +11096,9 @@
|
|
|
10268
11096
|
default: (0, vue.withCtx)(() => [..._cache[12] || (_cache[12] = [(0, vue.createTextVNode)("关闭", -1)])]),
|
|
10269
11097
|
_: 1
|
|
10270
11098
|
}))]),
|
|
10271
|
-
default: (0, vue.withCtx)(() => [payload.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$57, [
|
|
11099
|
+
default: (0, vue.withCtx)(() => [payload.value && visible.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$57, [
|
|
10272
11100
|
__props.onConfirm ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_2$19, "仅回滚有差异的字段")) : (0, vue.createCommentVNode)("v-if", true),
|
|
10273
|
-
(0, vue.createElementVNode)("div", _hoisted_3$
|
|
11101
|
+
(0, vue.createElementVNode)("div", _hoisted_3$7, [(0, vue.createElementVNode)("span", _hoisted_4$5, (0, vue.toDisplayString)(targetText.value), 1), (0, vue.createElementVNode)("div", _hoisted_5$2, [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicRadioGroup), {
|
|
10274
11102
|
modelValue: viewMode.value,
|
|
10275
11103
|
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => viewMode.value = $event),
|
|
10276
11104
|
size: "small",
|
|
@@ -10356,7 +11184,7 @@
|
|
|
10356
11184
|
"modelValue",
|
|
10357
11185
|
"title",
|
|
10358
11186
|
"width"
|
|
10359
|
-
])
|
|
11187
|
+
]);
|
|
10360
11188
|
};
|
|
10361
11189
|
}
|
|
10362
11190
|
});
|
|
@@ -10369,96 +11197,86 @@
|
|
|
10369
11197
|
key: 0,
|
|
10370
11198
|
class: "m-editor-history-list-empty"
|
|
10371
11199
|
};
|
|
10372
|
-
var _hoisted_2$18 = {
|
|
11200
|
+
var _hoisted_2$18 = {
|
|
11201
|
+
key: 0,
|
|
11202
|
+
class: "m-editor-history-list-toolbar"
|
|
11203
|
+
};
|
|
11204
|
+
var _hoisted_3$6 = { class: "m-editor-history-list-ul" };
|
|
10373
11205
|
var PageTab_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
10374
11206
|
name: "MEditorHistoryListPageTab",
|
|
10375
11207
|
__name: "PageTab",
|
|
10376
11208
|
props: {
|
|
10377
11209
|
list: {},
|
|
10378
|
-
expanded: {}
|
|
11210
|
+
expanded: {},
|
|
11211
|
+
marker: {}
|
|
10379
11212
|
},
|
|
10380
11213
|
emits: [
|
|
10381
11214
|
"toggle",
|
|
10382
11215
|
"goto",
|
|
10383
11216
|
"goto-initial",
|
|
10384
11217
|
"diff-step",
|
|
10385
|
-
"revert-step"
|
|
11218
|
+
"revert-step",
|
|
11219
|
+
"select",
|
|
11220
|
+
"clear"
|
|
10386
11221
|
],
|
|
10387
11222
|
setup(__props) {
|
|
10388
11223
|
const props = __props;
|
|
10389
11224
|
/**
|
|
10390
11225
|
* 当前 step 是否可查看差异:
|
|
10391
11226
|
* - 仅 update 操作;
|
|
10392
|
-
* - 单节点更新(
|
|
11227
|
+
* - 单节点更新(diff.length === 1),且 oldSchema / newSchema 都存在。
|
|
10393
11228
|
* 多节点更新难以选定单一对比目标,统一不展示差异入口。
|
|
10394
11229
|
*/
|
|
10395
11230
|
const isPageStepDiffable = (step) => {
|
|
10396
11231
|
if (step.opType !== "update") return false;
|
|
10397
|
-
const items = step.
|
|
11232
|
+
const items = step.diff ?? [];
|
|
10398
11233
|
if (items.length !== 1) return false;
|
|
10399
|
-
return Boolean(items[0]?.
|
|
11234
|
+
return Boolean(items[0]?.oldSchema && items[0]?.newSchema);
|
|
10400
11235
|
};
|
|
11236
|
+
/** 页面历史的描述 / 可操作性判定集合,注入给统一的 `toRowGroup`。 */
|
|
11237
|
+
const descriptor = {
|
|
11238
|
+
describeGroup: describePageGroup,
|
|
11239
|
+
describeStep: describePageStep,
|
|
11240
|
+
isStepDiffable: isPageStepDiffable,
|
|
11241
|
+
isStepRevertable: isPageStepRevertable
|
|
11242
|
+
};
|
|
11243
|
+
const rowKey = (group) => `pg-${group.steps[0]?.index}`;
|
|
11244
|
+
const toRow = (group) => toRowGroup(group, rowKey(group), descriptor);
|
|
10401
11245
|
/**
|
|
10402
11246
|
* 是否处于"初始状态"——即对应页面历史栈 cursor===0:
|
|
10403
|
-
* 当 list 中所有 group 的 applied 都为 false
|
|
10404
|
-
*
|
|
11247
|
+
* 当 list 中所有 group 的 applied 都为 false 时即为该状态(空列表 `every` 返回 true,
|
|
11248
|
+
* 即仅有 marker、无任何操作记录时也视为处于初始状态)。
|
|
10405
11249
|
*/
|
|
10406
|
-
const isInitial = (0, vue.computed)(() => props.list.
|
|
11250
|
+
const isInitial = (0, vue.computed)(() => props.list.every((g) => !g.applied));
|
|
10407
11251
|
return (_ctx, _cache) => {
|
|
10408
|
-
return !__props.list.length ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$56, "暂无操作记录")) : ((0, vue.openBlock)(), (0, vue.
|
|
10409
|
-
|
|
10410
|
-
|
|
10411
|
-
|
|
10412
|
-
|
|
11252
|
+
return !__props.list.length && !__props.marker ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$56, "暂无操作记录")) : ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 1 }, [__props.list.length ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_2$18, [(0, vue.createElementVNode)("span", {
|
|
11253
|
+
class: "m-editor-history-list-clear",
|
|
11254
|
+
title: "清空当前页面的历史记录",
|
|
11255
|
+
onClick: _cache[0] || (_cache[0] = ($event) => _ctx.$emit("clear"))
|
|
11256
|
+
}, "清空")])) : (0, vue.createCommentVNode)("v-if", true), (0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicScrollbar), { "max-height": "360px" }, {
|
|
11257
|
+
default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("ul", _hoisted_3$6, [
|
|
10413
11258
|
((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.list, (group) => {
|
|
10414
11259
|
return (0, vue.openBlock)(), (0, vue.createBlock)(GroupRow_default, {
|
|
10415
|
-
key:
|
|
10416
|
-
|
|
10417
|
-
|
|
10418
|
-
|
|
10419
|
-
"
|
|
10420
|
-
|
|
10421
|
-
|
|
10422
|
-
|
|
10423
|
-
"
|
|
10424
|
-
|
|
10425
|
-
index: s.index,
|
|
10426
|
-
applied: s.applied,
|
|
10427
|
-
isCurrent: s.isCurrent,
|
|
10428
|
-
desc: (0, vue.unref)(describePageStep)(s.step),
|
|
10429
|
-
diffable: isPageStepDiffable(s.step),
|
|
10430
|
-
revertable: s.applied,
|
|
10431
|
-
time: (0, vue.unref)(formatHistoryTime)(s.step.timestamp),
|
|
10432
|
-
timeTitle: (0, vue.unref)(formatHistoryFullTime)(s.step.timestamp)
|
|
10433
|
-
})),
|
|
10434
|
-
"is-current": group.isCurrent,
|
|
10435
|
-
expanded: !!__props.expanded[`pg-${group.steps[0]?.index}`],
|
|
10436
|
-
onToggle: _cache[0] || (_cache[0] = (key) => _ctx.$emit("toggle", key)),
|
|
10437
|
-
onGoto: _cache[1] || (_cache[1] = (index) => _ctx.$emit("goto", index)),
|
|
10438
|
-
onDiffStep: _cache[2] || (_cache[2] = (index) => _ctx.$emit("diff-step", index)),
|
|
10439
|
-
onRevertStep: _cache[3] || (_cache[3] = (index) => _ctx.$emit("revert-step", index))
|
|
10440
|
-
}, null, 8, [
|
|
10441
|
-
"group-key",
|
|
10442
|
-
"applied",
|
|
10443
|
-
"merged",
|
|
10444
|
-
"op-type",
|
|
10445
|
-
"desc",
|
|
10446
|
-
"time",
|
|
10447
|
-
"time-title",
|
|
10448
|
-
"step-count",
|
|
10449
|
-
"sub-steps",
|
|
10450
|
-
"is-current",
|
|
10451
|
-
"expanded"
|
|
10452
|
-
]);
|
|
11260
|
+
key: rowKey(group),
|
|
11261
|
+
group: toRow(group),
|
|
11262
|
+
expanded: (0, vue.unref)(isHistoryGroupExpanded)(__props.expanded, rowKey(group)),
|
|
11263
|
+
"select-enabled": true,
|
|
11264
|
+
onToggle: _cache[1] || (_cache[1] = (key) => _ctx.$emit("toggle", key)),
|
|
11265
|
+
onGoto: _cache[2] || (_cache[2] = (index) => _ctx.$emit("goto", index)),
|
|
11266
|
+
onDiffStep: _cache[3] || (_cache[3] = (index) => _ctx.$emit("diff-step", index)),
|
|
11267
|
+
onRevertStep: _cache[4] || (_cache[4] = (index) => _ctx.$emit("revert-step", index)),
|
|
11268
|
+
onSelect: _cache[5] || (_cache[5] = (index) => _ctx.$emit("select", index))
|
|
11269
|
+
}, null, 8, ["group", "expanded"]);
|
|
10453
11270
|
}), 128)),
|
|
10454
|
-
(0, vue.createCommentVNode)("\n 初始状态项:永远位于列表底部(页面 tab 倒序展示,最底部=最早),\n 作为\"未修改\"零点。当所有 group 都未 applied 时它即为当前位置。\n "),
|
|
11271
|
+
(0, vue.createCommentVNode)("\n 初始状态项:永远位于列表底部(页面 tab 倒序展示,最底部=最早),\n 作为\"未修改\"零点。当所有 group 都未 applied 时它即为当前位置。\n 设置 root 时生成的「未修改的初始状态」标记(marker)会作为该行的文案与时间来源。\n "),
|
|
10455
11272
|
(0, vue.createVNode)(InitialRow_default, {
|
|
10456
11273
|
"is-current": isInitial.value,
|
|
10457
|
-
|
|
10458
|
-
|
|
11274
|
+
marker: __props.marker,
|
|
11275
|
+
onGotoInitial: _cache[6] || (_cache[6] = ($event) => _ctx.$emit("goto-initial"))
|
|
11276
|
+
}, null, 8, ["is-current", "marker"])
|
|
10459
11277
|
])]),
|
|
10460
11278
|
_: 1
|
|
10461
|
-
}));
|
|
11279
|
+
})], 64));
|
|
10462
11280
|
};
|
|
10463
11281
|
}
|
|
10464
11282
|
});
|
|
@@ -10468,6 +11286,12 @@
|
|
|
10468
11286
|
//#endregion
|
|
10469
11287
|
//#region packages/editor/src/layouts/history-list/HistoryListPanel.vue?vue&type=script&setup=true&lang.ts
|
|
10470
11288
|
var _hoisted_1$55 = { class: "m-editor-history-list" };
|
|
11289
|
+
/**
|
|
11290
|
+
* 构造差异弹窗入参:仅 update(前后值都存在)可对比。
|
|
11291
|
+
* - 页面(无 id):在全部分组中按 index 定位 step,目标 id 取自快照;
|
|
11292
|
+
* - 数据源 / 代码块(带 id):先匹配分组 id 再按 index 定位。
|
|
11293
|
+
* 无可对比内容(多节点 / add / remove)或定位不到时返回 null。
|
|
11294
|
+
*/
|
|
10471
11295
|
var HistoryListPanel_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
10472
11296
|
name: "MEditorHistoryListPanel",
|
|
10473
11297
|
__name: "HistoryListPanel",
|
|
@@ -10496,7 +11320,10 @@
|
|
|
10496
11320
|
const ClockIcon = (0, vue.markRaw)(_element_plus_icons_vue.Clock);
|
|
10497
11321
|
const CloseIcon = (0, vue.markRaw)(_element_plus_icons_vue.Close);
|
|
10498
11322
|
const activeTab = (0, vue.ref)("page");
|
|
10499
|
-
/**
|
|
11323
|
+
/**
|
|
11324
|
+
* 面板显隐受控:reference 图标点击切换,右上角关闭按钮置为 false。
|
|
11325
|
+
* 点击面板以外区域的自动收起由 TMagicPopover 通过 v-model:visible 回写完成。
|
|
11326
|
+
*/
|
|
10500
11327
|
const visible = (0, vue.ref)(false);
|
|
10501
11328
|
const tabPaneComponent = (0, _tmagic_design.getDesignConfig)("components")?.tabPane;
|
|
10502
11329
|
/**
|
|
@@ -10506,7 +11333,7 @@
|
|
|
10506
11333
|
const extraTabs = (0, vue.inject)("historyListExtraTabs", []);
|
|
10507
11334
|
/** label 支持字符串或函数,函数形式便于展示动态数量等内容。 */
|
|
10508
11335
|
const resolveTabLabel = (tab) => typeof tab.label === "function" ? tab.label() : tab.label;
|
|
10509
|
-
const { editorService, dataSourceService, codeBlockService, historyService, propsService } = useServices();
|
|
11336
|
+
const { editorService, dataSourceService, codeBlockService, historyService, propsService, stageOverlayService } = useServices();
|
|
10510
11337
|
/**
|
|
10511
11338
|
* 数据源 / 代码块功能可被业务方通过 `disabledDataSource` / `disabledCodeBlock` 禁用,
|
|
10512
11339
|
* 禁用后对应的历史记录 tab 不再展示。若当前激活的 tab 恰好被禁用,则回退到「页面」tab。
|
|
@@ -10523,15 +11350,60 @@
|
|
|
10523
11350
|
*/
|
|
10524
11351
|
const extendFormState = (0, vue.inject)("extendFormState", void 0);
|
|
10525
11352
|
const { expanded, toggleGroup, pageGroups, dataSourceGroups, codeBlockGroups, pageGroupsDisplay, dataSourceGroupsByTarget, codeBlockGroupsByTarget } = useHistoryList();
|
|
11353
|
+
/**
|
|
11354
|
+
* 当前活动页的「加载/初始」标记记录(设置 root 时生成),透传给 PageTab 的底部初始行展示。
|
|
11355
|
+
* 基于 historyService 的 reactive state 派生,活动页切换或标记写入后自动刷新。
|
|
11356
|
+
*/
|
|
11357
|
+
const pageMarker = (0, vue.computed)(() => historyService.getPageMarker());
|
|
10526
11358
|
/** 数据源 step 仅 update(前后 schema 都存在)时可查看差异。 */
|
|
10527
|
-
const isDataSourceStepDiffable = (step) => Boolean(step.oldSchema && step.newSchema);
|
|
11359
|
+
const isDataSourceStepDiffable = (step) => Boolean(step.diff?.[0]?.oldSchema && step.diff?.[0]?.newSchema);
|
|
10528
11360
|
/** 代码块 step 仅 update(前后 content 都存在)时可查看差异。 */
|
|
10529
|
-
const isCodeBlockStepDiffable = (step) => Boolean(step.
|
|
11361
|
+
const isCodeBlockStepDiffable = (step) => Boolean(step.diff?.[0]?.oldSchema && step.diff?.[0]?.newSchema);
|
|
11362
|
+
/**
|
|
11363
|
+
* 数据源 / 代码块两类 bucket 历史的整体渲染配置:把 title / prefix 与各自的描述、
|
|
11364
|
+
* 可差异、可回滚判定收敛为单一对象整体注入 BucketTab,组件内部按需读取。
|
|
11365
|
+
*/
|
|
11366
|
+
const dataSourceConfig = {
|
|
11367
|
+
title: "数据源",
|
|
11368
|
+
prefix: "ds",
|
|
11369
|
+
describeGroup: describeDataSourceGroup,
|
|
11370
|
+
describeStep: describeDataSourceStep,
|
|
11371
|
+
isStepDiffable: isDataSourceStepDiffable,
|
|
11372
|
+
isStepRevertable: isDataSourceStepRevertable
|
|
11373
|
+
};
|
|
11374
|
+
const codeBlockConfig = {
|
|
11375
|
+
title: "代码块",
|
|
11376
|
+
prefix: "cb",
|
|
11377
|
+
describeGroup: describeCodeBlockGroup,
|
|
11378
|
+
describeStep: describeCodeBlockStep,
|
|
11379
|
+
isStepDiffable: isCodeBlockStepDiffable,
|
|
11380
|
+
isStepRevertable: isCodeBlockStepRevertable
|
|
11381
|
+
};
|
|
10530
11382
|
/** 把"目标 step 索引"翻译成"目标 cursor"(已应用步骤数量)。 */
|
|
10531
11383
|
const indexToCursor = (index) => index + 1;
|
|
10532
11384
|
const onPageGoto = (index) => {
|
|
10533
11385
|
editorService.gotoPageStep(indexToCursor(index));
|
|
10534
11386
|
};
|
|
11387
|
+
/**
|
|
11388
|
+
* 点击页面历史记录行:选中该记录对应的画布节点。
|
|
11389
|
+
* - 从目标 step 的 diff 中取节点 id(优先 newSchema,回退 oldSchema),按出现顺序找到第一个当前仍存在的节点;
|
|
11390
|
+
* - 与图层树点击选中一致:editorService.select + 画布 / overlay 画布 select 三者联动;
|
|
11391
|
+
* - 该 step 涉及的节点都已不存在(如删除记录、被撤销的新增)时给出提示,不做选中。
|
|
11392
|
+
*/
|
|
11393
|
+
const onPageSelect = async (index) => {
|
|
11394
|
+
const step = historyService.getPageStepList()[index]?.step;
|
|
11395
|
+
if (!step) return;
|
|
11396
|
+
const targetId = (step.diff ?? []).map((item) => item.newSchema?.id ?? item.oldSchema?.id).find((id) => id !== void 0 && id !== null && editorService.getNodeById(id, false));
|
|
11397
|
+
if (targetId === void 0 || targetId === null) {
|
|
11398
|
+
_tmagic_design.tMagicMessage.warning("该记录对应的节点已不存在,无法选中");
|
|
11399
|
+
return;
|
|
11400
|
+
}
|
|
11401
|
+
const node = editorService.getNodeById(targetId, false);
|
|
11402
|
+
if (!node) return;
|
|
11403
|
+
await editorService.select(node);
|
|
11404
|
+
editorService.get("stage")?.select(targetId);
|
|
11405
|
+
stageOverlayService.get("stage")?.select(targetId);
|
|
11406
|
+
};
|
|
10535
11407
|
const onDataSourceGoto = (id, index) => {
|
|
10536
11408
|
dataSourceService.goto(id, indexToCursor(index));
|
|
10537
11409
|
};
|
|
@@ -10552,71 +11424,53 @@
|
|
|
10552
11424
|
codeBlockService.goto(id, 0);
|
|
10553
11425
|
};
|
|
10554
11426
|
const diffDialogRef = (0, vue.useTemplateRef)("diffDialog");
|
|
11427
|
+
const confirmDialogRef = (0, vue.useTemplateRef)("confirmDialog");
|
|
10555
11428
|
/**
|
|
10556
|
-
*
|
|
10557
|
-
*
|
|
10558
|
-
* `currentValue` 取自 editorService 中该节点当前实际值,用于支持「与当前对比」。
|
|
10559
|
-
* 无可对比内容(如多节点 / add / remove)时返回 null。
|
|
11429
|
+
* 三类历史(页面 / 数据源 / 代码块)差异弹窗入参的构造差异,收敛为一份配置:
|
|
11430
|
+
* 仅「分组来源、当前值读取、类型 / 展示名提取」不同,定位 step、校验前后值、组装 payload 的流程共用。
|
|
10560
11431
|
*/
|
|
10561
|
-
const
|
|
10562
|
-
const
|
|
10563
|
-
|
|
10564
|
-
const
|
|
10565
|
-
if (!
|
|
10566
|
-
const
|
|
10567
|
-
|
|
10568
|
-
|
|
10569
|
-
const
|
|
10570
|
-
const
|
|
11432
|
+
const buildDiffPayload = (source, index, id) => {
|
|
11433
|
+
for (const group of source.groups()) {
|
|
11434
|
+
if (id !== void 0 && group.id !== id) continue;
|
|
11435
|
+
const step = group.steps.find((s) => s.index === index)?.step;
|
|
11436
|
+
if (!step) continue;
|
|
11437
|
+
const oldSchema = step.diff?.[0]?.oldSchema;
|
|
11438
|
+
const newSchema = step.diff?.[0]?.newSchema;
|
|
11439
|
+
if (!oldSchema || !newSchema) return null;
|
|
11440
|
+
const targetId = id ?? newSchema.id ?? oldSchema.id;
|
|
11441
|
+
const type = source.resolveType?.(newSchema, oldSchema);
|
|
10571
11442
|
return {
|
|
10572
|
-
category:
|
|
10573
|
-
type,
|
|
10574
|
-
lastValue:
|
|
10575
|
-
value:
|
|
10576
|
-
currentValue:
|
|
10577
|
-
targetLabel:
|
|
10578
|
-
id:
|
|
11443
|
+
category: source.category,
|
|
11444
|
+
...type !== void 0 ? { type } : {},
|
|
11445
|
+
lastValue: oldSchema,
|
|
11446
|
+
value: newSchema,
|
|
11447
|
+
currentValue: (targetId !== void 0 ? source.getCurrent(targetId) : null) || null,
|
|
11448
|
+
targetLabel: source.resolveLabel(newSchema, oldSchema, targetId),
|
|
11449
|
+
id: targetId
|
|
10579
11450
|
};
|
|
10580
11451
|
}
|
|
10581
11452
|
return null;
|
|
10582
11453
|
};
|
|
10583
|
-
|
|
10584
|
-
|
|
10585
|
-
|
|
10586
|
-
|
|
10587
|
-
|
|
10588
|
-
|
|
10589
|
-
|
|
10590
|
-
|
|
10591
|
-
|
|
10592
|
-
|
|
10593
|
-
}
|
|
10594
|
-
|
|
10595
|
-
|
|
10596
|
-
|
|
10597
|
-
|
|
10598
|
-
|
|
10599
|
-
|
|
10600
|
-
|
|
10601
|
-
|
|
10602
|
-
|
|
10603
|
-
value: newSchema,
|
|
10604
|
-
currentValue: currentSchema || null,
|
|
10605
|
-
targetLabel: newSchema.title || oldSchema.title || `${id}`,
|
|
10606
|
-
id
|
|
10607
|
-
};
|
|
10608
|
-
});
|
|
10609
|
-
const buildCodeBlockDiffPayload = (id, index) => findGroupStep(historyService.getCodeBlockHistoryGroups(), id, index, ({ oldContent, newContent }) => {
|
|
10610
|
-
if (!oldContent || !newContent) return null;
|
|
10611
|
-
return {
|
|
10612
|
-
category: "code-block",
|
|
10613
|
-
lastValue: oldContent,
|
|
10614
|
-
value: newContent,
|
|
10615
|
-
currentValue: codeBlockService.getCodeContentById(id) || null,
|
|
10616
|
-
targetLabel: newContent.name || oldContent.name || `${id}`,
|
|
10617
|
-
id
|
|
10618
|
-
};
|
|
10619
|
-
});
|
|
11454
|
+
const buildPageDiffPayload = (index) => buildDiffPayload({
|
|
11455
|
+
category: "node",
|
|
11456
|
+
groups: () => historyService.getPageHistoryGroups(),
|
|
11457
|
+
getCurrent: (id) => editorService.getNodeById(id),
|
|
11458
|
+
resolveType: (n, o) => n.type || o.type || "",
|
|
11459
|
+
resolveLabel: (n, o) => n.name || o.name || n.type || o.type || ""
|
|
11460
|
+
}, index);
|
|
11461
|
+
const buildDataSourceDiffPayload = (id, index) => buildDiffPayload({
|
|
11462
|
+
category: "data-source",
|
|
11463
|
+
groups: () => historyService.getDataSourceHistoryGroups(),
|
|
11464
|
+
getCurrent: (id) => dataSourceService.getDataSourceById(`${id}`),
|
|
11465
|
+
resolveType: (n, o) => n.type || o.type || "base",
|
|
11466
|
+
resolveLabel: (n, o, id) => n.title || o.title || `${id}`
|
|
11467
|
+
}, index, id);
|
|
11468
|
+
const buildCodeBlockDiffPayload = (id, index) => buildDiffPayload({
|
|
11469
|
+
category: "code-block",
|
|
11470
|
+
groups: () => historyService.getCodeBlockHistoryGroups(),
|
|
11471
|
+
getCurrent: (id) => codeBlockService.getCodeContentById(id),
|
|
11472
|
+
resolveLabel: (n, o, id) => n.name || o.name || `${id}`
|
|
11473
|
+
}, index, id);
|
|
10620
11474
|
const onPageDiff = (index) => {
|
|
10621
11475
|
const payload = buildPageDiffPayload(index);
|
|
10622
11476
|
if (payload) diffDialogRef.value?.open(payload);
|
|
@@ -10629,169 +11483,259 @@
|
|
|
10629
11483
|
const payload = buildCodeBlockDiffPayload(id, index);
|
|
10630
11484
|
if (payload) diffDialogRef.value?.open(payload);
|
|
10631
11485
|
};
|
|
10632
|
-
const onConfirmRevert = (0, vue.shallowRef)();
|
|
10633
11486
|
/**
|
|
10634
|
-
*
|
|
11487
|
+
* 「回滚」统一入口:把目标历史步骤的修改作为一次新操作反向应用(类 git revert),
|
|
10635
11488
|
* 不破坏原有栈结构。各 service 内部完成反向 + 入栈,并自带描述用于面板展示。
|
|
10636
11489
|
*
|
|
10637
|
-
*
|
|
10638
|
-
*
|
|
11490
|
+
* 交互:
|
|
11491
|
+
* - 可差异对比的步骤(单节点 / 单实体 update):弹出差异弹窗供用户确认,点「确定回滚」再执行;
|
|
11492
|
+
* - 无法对比的步骤(add / remove / 多节点更新,payload 为 null):弹出普通二次确认框,确认后执行。
|
|
11493
|
+
*
|
|
11494
|
+
* 页面 / 数据源 / 代码块三类回滚仅「差异入参构造」与「实际 revert 调用」不同,
|
|
11495
|
+
* 由调用方分别传入 payload 与 revert,公共的弹窗 / 确认流程在此收敛。
|
|
11496
|
+
*/
|
|
11497
|
+
const runRevert = (payload) => {
|
|
11498
|
+
if (payload && confirmDialogRef.value) return confirmDialogRef.value.confirm(payload);
|
|
11499
|
+
return confirmRevert();
|
|
11500
|
+
};
|
|
11501
|
+
/**
|
|
11502
|
+
* 回滚前置校验:若该历史步骤回滚所依赖的目标数据已被删除,则无法回滚。
|
|
11503
|
+
* - update(把旧值写回):被修改的目标必须仍存在;
|
|
11504
|
+
* - 页面 remove(还原被删节点):被删节点的原父容器必须仍存在,否则无处插回;
|
|
11505
|
+
* add(回滚即删除)即使目标已不在,也已达成「删除」目的,不视为失败。
|
|
11506
|
+
*
|
|
11507
|
+
* 命中时弹出「回滚失败」提示并返回 true,调用方据此中止本次回滚。
|
|
10639
11508
|
*/
|
|
11509
|
+
const isPageRevertTargetMissing = (index) => {
|
|
11510
|
+
const step = historyService.getPageStepList()[index]?.step;
|
|
11511
|
+
if (!step) return false;
|
|
11512
|
+
if (step.opType === "update") return (step.diff ?? []).some((item) => {
|
|
11513
|
+
const id = item.newSchema?.id ?? item.oldSchema?.id;
|
|
11514
|
+
return id !== void 0 && !editorService.getNodeById(id, false);
|
|
11515
|
+
});
|
|
11516
|
+
if (step.opType === "remove") return (step.diff ?? []).some((item) => item.parentId !== void 0 && !editorService.getNodeById(item.parentId, false));
|
|
11517
|
+
return false;
|
|
11518
|
+
};
|
|
11519
|
+
/** 数据源 update 步骤回滚时,对应数据源必须仍存在(已删除则无处写回旧值)。 */
|
|
11520
|
+
const isDataSourceRevertTargetMissing = (id, index) => {
|
|
11521
|
+
const step = historyService.getDataSourceStepList(id)[index]?.step;
|
|
11522
|
+
return Boolean(step && step.opType === "update" && !dataSourceService.getDataSourceById(`${id}`));
|
|
11523
|
+
};
|
|
11524
|
+
/** 代码块 update 步骤回滚时,对应代码块必须仍存在(已删除则无处写回旧值)。 */
|
|
11525
|
+
const isCodeBlockRevertTargetMissing = (id, index) => {
|
|
11526
|
+
const step = historyService.getCodeBlockStepList(id)[index]?.step;
|
|
11527
|
+
return Boolean(step && step.opType === "update" && !codeBlockService.getCodeContentById(id));
|
|
11528
|
+
};
|
|
11529
|
+
/** 目标数据已被删除、无法回滚时的统一提示。 */
|
|
11530
|
+
const showRevertTargetMissing = () => {
|
|
11531
|
+
_tmagic_design.tMagicMessage.error("回滚失败:该记录对应的数据已被删除");
|
|
11532
|
+
};
|
|
10640
11533
|
const onPageRevert = (index) => {
|
|
10641
|
-
|
|
10642
|
-
|
|
10643
|
-
|
|
10644
|
-
|
|
11534
|
+
if (isPageRevertTargetMissing(index)) {
|
|
11535
|
+
showRevertTargetMissing();
|
|
11536
|
+
return Promise.resolve(null);
|
|
11537
|
+
}
|
|
11538
|
+
return runRevert(buildPageDiffPayload(index)).then((result) => result ? editorService.revertPageStep(index) : null);
|
|
10645
11539
|
};
|
|
10646
11540
|
const onDataSourceRevert = (id, index) => {
|
|
10647
|
-
|
|
10648
|
-
|
|
10649
|
-
|
|
10650
|
-
|
|
11541
|
+
if (isDataSourceRevertTargetMissing(id, index)) {
|
|
11542
|
+
showRevertTargetMissing();
|
|
11543
|
+
return Promise.resolve(null);
|
|
11544
|
+
}
|
|
11545
|
+
return runRevert(buildDataSourceDiffPayload(id, index)).then((result) => result ? dataSourceService.revert(id, index) : null);
|
|
10651
11546
|
};
|
|
10652
11547
|
const onCodeBlockRevert = (id, index) => {
|
|
10653
|
-
|
|
10654
|
-
|
|
10655
|
-
|
|
10656
|
-
|
|
11548
|
+
if (isCodeBlockRevertTargetMissing(id, index)) {
|
|
11549
|
+
showRevertTargetMissing();
|
|
11550
|
+
return Promise.resolve(null);
|
|
11551
|
+
}
|
|
11552
|
+
return runRevert(buildCodeBlockDiffPayload(id, index)).then((result) => result ? codeBlockService.revert(id, index) : null);
|
|
11553
|
+
};
|
|
11554
|
+
/**
|
|
11555
|
+
* 「回滚」二次确认:新增 / 删除 / 多节点更新等无法做差异对比的步骤,
|
|
11556
|
+
* 不弹差异弹窗,改用一个普通确认框替代「确定回滚」按钮,避免点击后无任何提示直接执行。
|
|
11557
|
+
* 用户取消时返回 false,调用方据此中止回滚。
|
|
11558
|
+
*/
|
|
11559
|
+
const confirmRevert = () => confirmDialog("确定回滚该步骤吗?回滚会将该操作作为一条新记录反向应用(新增将被删除、删除将被还原),不影响后续历史记录。");
|
|
11560
|
+
/**
|
|
11561
|
+
* 通用二次确认弹窗:清空历史 / 无法差异对比的回滚等会改变状态的操作,先弹出确认框,
|
|
11562
|
+
* 用户点击「确定」返回 true,取消(confirm reject)时返回 false 并静默忽略。
|
|
11563
|
+
*/
|
|
11564
|
+
const confirmDialog = async (message) => {
|
|
11565
|
+
try {
|
|
11566
|
+
await _tmagic_design.tMagicMessageBox.confirm(message, "提示", {
|
|
11567
|
+
confirmButtonText: "确定",
|
|
11568
|
+
cancelButtonText: "取消",
|
|
11569
|
+
type: "warning"
|
|
11570
|
+
});
|
|
11571
|
+
return true;
|
|
11572
|
+
} catch (e) {
|
|
11573
|
+
return false;
|
|
11574
|
+
}
|
|
11575
|
+
};
|
|
11576
|
+
/**
|
|
11577
|
+
* 把内存中(已清空对应类别后的)历史状态重新写回 IndexedDB,
|
|
11578
|
+
* 使本地持久化的那份与内存保持一致——即「连同本地保存的一并删除」。
|
|
11579
|
+
* 不支持 IndexedDB 或写入失败时静默忽略(内存清空已生效)。
|
|
11580
|
+
*/
|
|
11581
|
+
const syncIndexedDB = async () => {
|
|
11582
|
+
try {
|
|
11583
|
+
await historyService.saveToIndexedDB();
|
|
11584
|
+
} catch (e) {}
|
|
11585
|
+
};
|
|
11586
|
+
const onPageClear = async () => {
|
|
11587
|
+
if (await confirmDialog("确定清空当前页面的历史记录吗?清空后将无法撤销/重做之前的操作,本地保存的记录也会一并删除。")) {
|
|
11588
|
+
historyService.clearPage();
|
|
11589
|
+
await syncIndexedDB();
|
|
11590
|
+
}
|
|
10657
11591
|
};
|
|
10658
|
-
const
|
|
10659
|
-
|
|
11592
|
+
const onDataSourceClear = async () => {
|
|
11593
|
+
if (await confirmDialog("确定清空数据源的历史记录吗?清空后将无法撤销/重做之前的操作,本地保存的记录也会一并删除。")) {
|
|
11594
|
+
historyService.clearDataSource();
|
|
11595
|
+
await syncIndexedDB();
|
|
11596
|
+
}
|
|
11597
|
+
};
|
|
11598
|
+
const onCodeBlockClear = async () => {
|
|
11599
|
+
if (await confirmDialog("确定清空代码块的历史记录吗?清空后将无法撤销/重做之前的操作,本地保存的记录也会一并删除。")) {
|
|
11600
|
+
historyService.clearCodeBlock();
|
|
11601
|
+
await syncIndexedDB();
|
|
11602
|
+
}
|
|
10660
11603
|
};
|
|
10661
11604
|
return (_ctx, _cache) => {
|
|
10662
|
-
return (0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, null, [
|
|
10663
|
-
|
|
10664
|
-
|
|
10665
|
-
trigger: "click",
|
|
10666
|
-
visible: visible.value,
|
|
10667
|
-
width: 660
|
|
10668
|
-
}, {
|
|
10669
|
-
reference: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicTooltip), {
|
|
10670
|
-
effect: "dark",
|
|
11605
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, null, [
|
|
11606
|
+
(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicPopover), {
|
|
11607
|
+
"popper-class": "m-editor-history-list-popover",
|
|
10671
11608
|
placement: "bottom",
|
|
10672
|
-
|
|
11609
|
+
trigger: "click",
|
|
11610
|
+
visible: visible.value,
|
|
11611
|
+
"onUpdate:visible": _cache[3] || (_cache[3] = ($event) => visible.value = $event),
|
|
11612
|
+
width: 660
|
|
10673
11613
|
}, {
|
|
10674
|
-
|
|
10675
|
-
|
|
10676
|
-
|
|
10677
|
-
|
|
11614
|
+
reference: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicTooltip), {
|
|
11615
|
+
effect: "dark",
|
|
11616
|
+
placement: "bottom",
|
|
11617
|
+
content: "历史记录"
|
|
10678
11618
|
}, {
|
|
10679
|
-
|
|
11619
|
+
default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), {
|
|
11620
|
+
size: "small",
|
|
11621
|
+
link: "",
|
|
11622
|
+
onClick: _cache[2] || (_cache[2] = ($event) => visible.value = !visible.value)
|
|
11623
|
+
}, {
|
|
11624
|
+
icon: (0, vue.withCtx)(() => [(0, vue.createVNode)(Icon_default, { icon: (0, vue.unref)(ClockIcon) }, null, 8, ["icon"])]),
|
|
11625
|
+
_: 1
|
|
11626
|
+
})]),
|
|
10680
11627
|
_: 1
|
|
10681
11628
|
})]),
|
|
10682
|
-
|
|
10683
|
-
|
|
10684
|
-
|
|
10685
|
-
|
|
10686
|
-
placement: "top",
|
|
10687
|
-
content: "关闭"
|
|
10688
|
-
}, {
|
|
10689
|
-
default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), {
|
|
10690
|
-
class: "m-editor-history-list-close",
|
|
10691
|
-
size: "small",
|
|
10692
|
-
link: "",
|
|
10693
|
-
onClick: _cache[0] || (_cache[0] = ($event) => visible.value = false)
|
|
11629
|
+
default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("div", _hoisted_1$55, [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicTooltip), {
|
|
11630
|
+
effect: "dark",
|
|
11631
|
+
placement: "top",
|
|
11632
|
+
content: "关闭"
|
|
10694
11633
|
}, {
|
|
10695
|
-
|
|
10696
|
-
|
|
10697
|
-
|
|
10698
|
-
|
|
10699
|
-
|
|
10700
|
-
|
|
10701
|
-
|
|
10702
|
-
class: "m-editor-history-list-tabs"
|
|
10703
|
-
}, {
|
|
10704
|
-
default: (0, vue.withCtx)(() => [
|
|
10705
|
-
((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)((0, vue.unref)(tabPaneComponent)?.component || "el-tab-pane"), (0, vue.normalizeProps)((0, vue.guardReactiveProps)((0, vue.unref)(tabPaneComponent)?.props({
|
|
10706
|
-
name: "page",
|
|
10707
|
-
label: `页面 (${(0, vue.unref)(pageGroups).length})`
|
|
10708
|
-
}) || {})), {
|
|
10709
|
-
default: (0, vue.withCtx)(() => [(0, vue.createVNode)(PageTab_default, {
|
|
10710
|
-
list: (0, vue.unref)(pageGroupsDisplay),
|
|
10711
|
-
expanded: (0, vue.unref)(expanded),
|
|
10712
|
-
onToggle: (0, vue.unref)(toggleGroup),
|
|
10713
|
-
onGoto: onPageGoto,
|
|
10714
|
-
onGotoInitial: onPageGotoInitial,
|
|
10715
|
-
onDiffStep: onPageDiff,
|
|
10716
|
-
onRevertStep: onPageRevert
|
|
10717
|
-
}, null, 8, [
|
|
10718
|
-
"list",
|
|
10719
|
-
"expanded",
|
|
10720
|
-
"onToggle"
|
|
10721
|
-
])]),
|
|
10722
|
-
_: 1
|
|
10723
|
-
}, 16)),
|
|
10724
|
-
!disabledDataSource.value ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)((0, vue.unref)(tabPaneComponent)?.component || "el-tab-pane"), (0, vue.normalizeProps)((0, vue.mergeProps)({ key: 0 }, (0, vue.unref)(tabPaneComponent)?.props({
|
|
10725
|
-
name: "data-source",
|
|
10726
|
-
label: `数据源 (${(0, vue.unref)(dataSourceGroups).length})`
|
|
10727
|
-
}) || {})), {
|
|
10728
|
-
default: (0, vue.withCtx)(() => [(0, vue.createVNode)(BucketTab_default, {
|
|
10729
|
-
title: "数据源",
|
|
10730
|
-
prefix: "ds",
|
|
10731
|
-
buckets: (0, vue.unref)(dataSourceGroupsByTarget),
|
|
10732
|
-
expanded: (0, vue.unref)(expanded),
|
|
10733
|
-
"describe-group": (0, vue.unref)(describeDataSourceGroup),
|
|
10734
|
-
"describe-step": (0, vue.unref)(describeDataSourceStep),
|
|
10735
|
-
"is-step-diffable": isDataSourceStepDiffable,
|
|
10736
|
-
onToggle: (0, vue.unref)(toggleGroup),
|
|
10737
|
-
onGoto: onDataSourceGoto,
|
|
10738
|
-
onGotoInitial: onDataSourceGotoInitial,
|
|
10739
|
-
onDiffStep: onDataSourceDiff,
|
|
10740
|
-
onRevertStep: onDataSourceRevert
|
|
10741
|
-
}, null, 8, [
|
|
10742
|
-
"buckets",
|
|
10743
|
-
"expanded",
|
|
10744
|
-
"describe-group",
|
|
10745
|
-
"describe-step",
|
|
10746
|
-
"onToggle"
|
|
10747
|
-
])]),
|
|
10748
|
-
_: 1
|
|
10749
|
-
}, 16)) : (0, vue.createCommentVNode)("v-if", true),
|
|
10750
|
-
!disabledCodeBlock.value ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)((0, vue.unref)(tabPaneComponent)?.component || "el-tab-pane"), (0, vue.normalizeProps)((0, vue.mergeProps)({ key: 1 }, (0, vue.unref)(tabPaneComponent)?.props({
|
|
10751
|
-
name: "code-block",
|
|
10752
|
-
label: `代码块 (${(0, vue.unref)(codeBlockGroups).length})`
|
|
10753
|
-
}) || {})), {
|
|
10754
|
-
default: (0, vue.withCtx)(() => [(0, vue.createVNode)(BucketTab_default, {
|
|
10755
|
-
title: "代码块",
|
|
10756
|
-
prefix: "cb",
|
|
10757
|
-
buckets: (0, vue.unref)(codeBlockGroupsByTarget),
|
|
10758
|
-
expanded: (0, vue.unref)(expanded),
|
|
10759
|
-
"describe-group": (0, vue.unref)(describeCodeBlockGroup),
|
|
10760
|
-
"describe-step": (0, vue.unref)(describeCodeBlockStep),
|
|
10761
|
-
"is-step-diffable": isCodeBlockStepDiffable,
|
|
10762
|
-
onToggle: (0, vue.unref)(toggleGroup),
|
|
10763
|
-
onGoto: onCodeBlockGoto,
|
|
10764
|
-
onGotoInitial: onCodeBlockGotoInitial,
|
|
10765
|
-
onDiffStep: onCodeBlockDiff,
|
|
10766
|
-
onRevertStep: onCodeBlockRevert
|
|
10767
|
-
}, null, 8, [
|
|
10768
|
-
"buckets",
|
|
10769
|
-
"expanded",
|
|
10770
|
-
"describe-group",
|
|
10771
|
-
"describe-step",
|
|
10772
|
-
"onToggle"
|
|
10773
|
-
])]),
|
|
11634
|
+
default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), {
|
|
11635
|
+
class: "m-editor-history-list-close",
|
|
11636
|
+
size: "small",
|
|
11637
|
+
link: "",
|
|
11638
|
+
onClick: _cache[0] || (_cache[0] = ($event) => visible.value = false)
|
|
11639
|
+
}, {
|
|
11640
|
+
icon: (0, vue.withCtx)(() => [(0, vue.createVNode)(Icon_default, { icon: (0, vue.unref)(CloseIcon) }, null, 8, ["icon"])]),
|
|
10774
11641
|
_: 1
|
|
10775
|
-
}
|
|
10776
|
-
|
|
10777
|
-
|
|
10778
|
-
|
|
10779
|
-
|
|
10780
|
-
|
|
10781
|
-
|
|
10782
|
-
|
|
10783
|
-
|
|
10784
|
-
|
|
10785
|
-
|
|
11642
|
+
})]),
|
|
11643
|
+
_: 1
|
|
11644
|
+
}), (0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicTabs), {
|
|
11645
|
+
modelValue: activeTab.value,
|
|
11646
|
+
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => activeTab.value = $event),
|
|
11647
|
+
class: "m-editor-history-list-tabs"
|
|
11648
|
+
}, {
|
|
11649
|
+
default: (0, vue.withCtx)(() => [
|
|
11650
|
+
((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)((0, vue.unref)(tabPaneComponent)?.component || "el-tab-pane"), (0, vue.normalizeProps)((0, vue.guardReactiveProps)((0, vue.unref)(tabPaneComponent)?.props({
|
|
11651
|
+
name: "page",
|
|
11652
|
+
label: `页面 (${(0, vue.unref)(pageGroups).length})`
|
|
11653
|
+
}) || {})), {
|
|
11654
|
+
default: (0, vue.withCtx)(() => [(0, vue.createVNode)(PageTab_default, {
|
|
11655
|
+
list: (0, vue.unref)(pageGroupsDisplay),
|
|
11656
|
+
expanded: (0, vue.unref)(expanded),
|
|
11657
|
+
marker: pageMarker.value,
|
|
11658
|
+
onToggle: (0, vue.unref)(toggleGroup),
|
|
11659
|
+
onGoto: onPageGoto,
|
|
11660
|
+
onGotoInitial: onPageGotoInitial,
|
|
11661
|
+
onDiffStep: onPageDiff,
|
|
11662
|
+
onRevertStep: onPageRevert,
|
|
11663
|
+
onSelect: onPageSelect,
|
|
11664
|
+
onClear: onPageClear
|
|
11665
|
+
}, null, 8, [
|
|
11666
|
+
"list",
|
|
11667
|
+
"expanded",
|
|
11668
|
+
"marker",
|
|
11669
|
+
"onToggle"
|
|
11670
|
+
])]),
|
|
11671
|
+
_: 1
|
|
11672
|
+
}, 16)),
|
|
11673
|
+
!disabledDataSource.value ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)((0, vue.unref)(tabPaneComponent)?.component || "el-tab-pane"), (0, vue.normalizeProps)((0, vue.mergeProps)({ key: 0 }, (0, vue.unref)(tabPaneComponent)?.props({
|
|
11674
|
+
name: "data-source",
|
|
11675
|
+
label: `数据源 (${(0, vue.unref)(dataSourceGroups).length})`
|
|
11676
|
+
}) || {})), {
|
|
11677
|
+
default: (0, vue.withCtx)(() => [(0, vue.createVNode)(BucketTab_default, {
|
|
11678
|
+
config: dataSourceConfig,
|
|
11679
|
+
buckets: (0, vue.unref)(dataSourceGroupsByTarget),
|
|
11680
|
+
expanded: (0, vue.unref)(expanded),
|
|
11681
|
+
onToggle: (0, vue.unref)(toggleGroup),
|
|
11682
|
+
onGoto: onDataSourceGoto,
|
|
11683
|
+
onGotoInitial: onDataSourceGotoInitial,
|
|
11684
|
+
onDiffStep: onDataSourceDiff,
|
|
11685
|
+
onRevertStep: onDataSourceRevert,
|
|
11686
|
+
onClear: onDataSourceClear
|
|
11687
|
+
}, null, 8, [
|
|
11688
|
+
"buckets",
|
|
11689
|
+
"expanded",
|
|
11690
|
+
"onToggle"
|
|
11691
|
+
])]),
|
|
11692
|
+
_: 1
|
|
11693
|
+
}, 16)) : (0, vue.createCommentVNode)("v-if", true),
|
|
11694
|
+
!disabledCodeBlock.value ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)((0, vue.unref)(tabPaneComponent)?.component || "el-tab-pane"), (0, vue.normalizeProps)((0, vue.mergeProps)({ key: 1 }, (0, vue.unref)(tabPaneComponent)?.props({
|
|
11695
|
+
name: "code-block",
|
|
11696
|
+
label: `代码块 (${(0, vue.unref)(codeBlockGroups).length})`
|
|
11697
|
+
}) || {})), {
|
|
11698
|
+
default: (0, vue.withCtx)(() => [(0, vue.createVNode)(BucketTab_default, {
|
|
11699
|
+
config: codeBlockConfig,
|
|
11700
|
+
buckets: (0, vue.unref)(codeBlockGroupsByTarget),
|
|
11701
|
+
expanded: (0, vue.unref)(expanded),
|
|
11702
|
+
onToggle: (0, vue.unref)(toggleGroup),
|
|
11703
|
+
onGoto: onCodeBlockGoto,
|
|
11704
|
+
onGotoInitial: onCodeBlockGotoInitial,
|
|
11705
|
+
onDiffStep: onCodeBlockDiff,
|
|
11706
|
+
onRevertStep: onCodeBlockRevert,
|
|
11707
|
+
onClear: onCodeBlockClear
|
|
11708
|
+
}, null, 8, [
|
|
11709
|
+
"buckets",
|
|
11710
|
+
"expanded",
|
|
11711
|
+
"onToggle"
|
|
11712
|
+
])]),
|
|
11713
|
+
_: 1
|
|
11714
|
+
}, 16)) : (0, vue.createCommentVNode)("v-if", true),
|
|
11715
|
+
((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)((0, vue.unref)(extraTabs), (tab) => {
|
|
11716
|
+
return (0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)((0, vue.unref)(tabPaneComponent)?.component || "el-tab-pane"), (0, vue.mergeProps)({ key: tab.name }, { ref_for: true }, (0, vue.unref)(tabPaneComponent)?.props({
|
|
11717
|
+
name: tab.name,
|
|
11718
|
+
label: resolveTabLabel(tab)
|
|
11719
|
+
}) || {}), {
|
|
11720
|
+
default: (0, vue.withCtx)(() => [((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)(tab.component), (0, vue.mergeProps)({ ref_for: true }, tab.props || {}, (0, vue.toHandlers)(tab.listeners || {})), null, 16))]),
|
|
11721
|
+
_: 2
|
|
11722
|
+
}, 1040);
|
|
11723
|
+
}), 128))
|
|
11724
|
+
]),
|
|
11725
|
+
_: 1
|
|
11726
|
+
}, 8, ["modelValue"])])]),
|
|
10786
11727
|
_: 1
|
|
10787
|
-
}, 8, ["
|
|
10788
|
-
|
|
10789
|
-
|
|
10790
|
-
|
|
10791
|
-
"extend-state"
|
|
10792
|
-
|
|
10793
|
-
|
|
10794
|
-
|
|
11728
|
+
}, 8, ["visible"]),
|
|
11729
|
+
(0, vue.createVNode)(HistoryDiffDialog_default, {
|
|
11730
|
+
ref: "diffDialog",
|
|
11731
|
+
"extend-state": (0, vue.unref)(extendFormState)
|
|
11732
|
+
}, null, 8, ["extend-state"]),
|
|
11733
|
+
(0, vue.createVNode)(HistoryDiffDialog_default, {
|
|
11734
|
+
ref: "confirmDialog",
|
|
11735
|
+
"is-confirm": true,
|
|
11736
|
+
"extend-state": (0, vue.unref)(extendFormState)
|
|
11737
|
+
}, null, 8, ["extend-state"])
|
|
11738
|
+
], 64);
|
|
10795
11739
|
};
|
|
10796
11740
|
}
|
|
10797
11741
|
});
|
|
@@ -11038,7 +11982,7 @@
|
|
|
11038
11982
|
disabled: () => editorService.get("node")?.type === _tmagic_core.NodeType.PAGE,
|
|
11039
11983
|
handler: () => {
|
|
11040
11984
|
const node = editorService.get("node");
|
|
11041
|
-
node && editorService.remove(node);
|
|
11985
|
+
node && editorService.remove(node, { historySource: "toolbar" });
|
|
11042
11986
|
}
|
|
11043
11987
|
});
|
|
11044
11988
|
break;
|
|
@@ -11402,7 +12346,11 @@
|
|
|
11402
12346
|
if (record.propPath?.startsWith("style") && record.value === "") (0, _tmagic_utils.setValueByKeyPath)(record.propPath, record.value, newValue);
|
|
11403
12347
|
});
|
|
11404
12348
|
}
|
|
11405
|
-
|
|
12349
|
+
const historySource = eventData ? "props" : "code";
|
|
12350
|
+
editorService.update(newValue, {
|
|
12351
|
+
changeRecords: eventData?.changeRecords,
|
|
12352
|
+
historySource
|
|
12353
|
+
});
|
|
11406
12354
|
} catch (e) {
|
|
11407
12355
|
emit("submit-error", e);
|
|
11408
12356
|
}
|
|
@@ -12371,7 +13319,7 @@
|
|
|
12371
13319
|
const editCode = (id) => {
|
|
12372
13320
|
emit("edit", id);
|
|
12373
13321
|
};
|
|
12374
|
-
const deleteCode = async (id) => {
|
|
13322
|
+
const deleteCode = async (id, { historySource } = {}) => {
|
|
12375
13323
|
const currentCode = codeList.value.find((codeItem) => codeItem.id === id);
|
|
12376
13324
|
const existBinds = Boolean(currentCode?.items?.length);
|
|
12377
13325
|
const undeleteableList = codeBlockService.getUndeletableList() || [];
|
|
@@ -12381,7 +13329,7 @@
|
|
|
12381
13329
|
cancelButtonText: "取消",
|
|
12382
13330
|
type: "warning"
|
|
12383
13331
|
});
|
|
12384
|
-
emit("remove", id);
|
|
13332
|
+
emit("remove", id, { historySource });
|
|
12385
13333
|
} else if (typeof props.customError === "function") props.customError(id, existBinds ? CodeDeleteErrorType.BIND : CodeDeleteErrorType.UNDELETEABLE);
|
|
12386
13334
|
else if (existBinds) _tmagic_design.tMagicMessage.error("代码块存在绑定关系,不可删除");
|
|
12387
13335
|
else _tmagic_design.tMagicMessage.error("代码块不可删除");
|
|
@@ -12431,7 +13379,7 @@
|
|
|
12431
13379
|
default: (0, vue.withCtx)(() => [(0, vue.createVNode)(Icon_default, {
|
|
12432
13380
|
icon: (0, vue.unref)(_element_plus_icons_vue.Close),
|
|
12433
13381
|
class: "edit-icon",
|
|
12434
|
-
onClick: (0, vue.withModifiers)(($event) => deleteCode(`${data.key}
|
|
13382
|
+
onClick: (0, vue.withModifiers)(($event) => deleteCode(`${data.key}`, { historySource: "tree" }), ["stop"])
|
|
12435
13383
|
}, null, 8, ["icon", "onClick"])]),
|
|
12436
13384
|
_: 2
|
|
12437
13385
|
}, 1024)) : (0, vue.createCommentVNode)("v-if", true),
|
|
@@ -12479,7 +13427,7 @@
|
|
|
12479
13427
|
const codeBlock = codeBlockService.getCodeContentById(selectId);
|
|
12480
13428
|
if (!codeBlock) return;
|
|
12481
13429
|
const newCodeId = await codeBlockService.getUniqueId();
|
|
12482
|
-
codeBlockService.setCodeDslById(newCodeId, cloneDeep$1(codeBlock));
|
|
13430
|
+
codeBlockService.setCodeDslById(newCodeId, cloneDeep$1(codeBlock), { historySource: "tree-contextmenu" });
|
|
12483
13431
|
}
|
|
12484
13432
|
},
|
|
12485
13433
|
{
|
|
@@ -12541,7 +13489,7 @@
|
|
|
12541
13489
|
if (codeBlockListRef.value) for (const [, status] of codeBlockListRef.value.nodeStatusMap.entries()) status.selected = false;
|
|
12542
13490
|
};
|
|
12543
13491
|
const { nodeContentMenuHandler, menuData: contentMenuData, contentMenuHideHandler } = useContentMenu$1((id) => {
|
|
12544
|
-
codeBlockListRef.value?.deleteCode(id);
|
|
13492
|
+
codeBlockListRef.value?.deleteCode(id, { historySource: "tree-contextmenu" });
|
|
12545
13493
|
});
|
|
12546
13494
|
const menuData = (0, vue.computed)(() => props.customContentMenu(contentMenuData, "code-block"));
|
|
12547
13495
|
return (_ctx, _cache) => {
|
|
@@ -12629,8 +13577,11 @@
|
|
|
12629
13577
|
editDialog.value.show();
|
|
12630
13578
|
};
|
|
12631
13579
|
const submitDataSourceHandler = (value, eventData) => {
|
|
12632
|
-
if (value.id) dataSourceService.update(value, {
|
|
12633
|
-
|
|
13580
|
+
if (value.id) dataSourceService.update(value, {
|
|
13581
|
+
changeRecords: eventData.changeRecords,
|
|
13582
|
+
historySource: "props"
|
|
13583
|
+
});
|
|
13584
|
+
else dataSourceService.add(value, { historySource: "props" });
|
|
12634
13585
|
editDialog.value?.hide();
|
|
12635
13586
|
};
|
|
12636
13587
|
return {
|
|
@@ -12963,7 +13914,7 @@
|
|
|
12963
13914
|
if (!selectId) return;
|
|
12964
13915
|
const ds = dataSourceService.getDataSourceById(selectId);
|
|
12965
13916
|
if (!ds) return;
|
|
12966
|
-
dataSourceService.add(cloneDeep$1(ds));
|
|
13917
|
+
dataSourceService.add(cloneDeep$1(ds), { historySource: "tree-contextmenu" });
|
|
12967
13918
|
}
|
|
12968
13919
|
},
|
|
12969
13920
|
{
|
|
@@ -13040,7 +13991,7 @@
|
|
|
13040
13991
|
cancelButtonText: "取消",
|
|
13041
13992
|
type: "warning"
|
|
13042
13993
|
});
|
|
13043
|
-
dataSourceService.remove(id);
|
|
13994
|
+
dataSourceService.remove(id, { historySource: "tree-contextmenu" });
|
|
13044
13995
|
};
|
|
13045
13996
|
const dataSourceListRef = (0, vue.useTemplateRef)("dataSourceList");
|
|
13046
13997
|
const filterTextChangeHandler = (val) => {
|
|
@@ -13147,7 +14098,12 @@
|
|
|
13147
14098
|
var FolderMinusIcon_default = FolderMinusIcon_vue_vue_type_script_setup_true_lang_default;
|
|
13148
14099
|
//#endregion
|
|
13149
14100
|
//#region packages/editor/src/utils/content-menu.ts
|
|
13150
|
-
|
|
14101
|
+
/**
|
|
14102
|
+
* 共享的右键菜单项构造器(画布 ViewerMenu 与图层树 LayerMenu 共用)。
|
|
14103
|
+
* `historySource` 用于标记本次操作的途径,调用方按所在面板传入:
|
|
14104
|
+
* 画布传 `'stage-contextmenu'`,树形面板传 `'tree-contextmenu'`。
|
|
14105
|
+
*/
|
|
14106
|
+
var useDeleteMenu = (historySource) => ({
|
|
13151
14107
|
type: "button",
|
|
13152
14108
|
text: "删除",
|
|
13153
14109
|
icon: _element_plus_icons_vue.Delete,
|
|
@@ -13157,7 +14113,7 @@
|
|
|
13157
14113
|
},
|
|
13158
14114
|
handler: ({ editorService }) => {
|
|
13159
14115
|
const nodes = editorService.get("nodes");
|
|
13160
|
-
nodes && editorService.remove(nodes);
|
|
14116
|
+
nodes && editorService.remove(nodes, { historySource });
|
|
13161
14117
|
}
|
|
13162
14118
|
});
|
|
13163
14119
|
var useCopyMenu = () => ({
|
|
@@ -13169,7 +14125,7 @@
|
|
|
13169
14125
|
nodes && editorService?.copy(nodes);
|
|
13170
14126
|
}
|
|
13171
14127
|
});
|
|
13172
|
-
var usePasteMenu = (menu) => ({
|
|
14128
|
+
var usePasteMenu = (historySource, menu) => ({
|
|
13173
14129
|
type: "button",
|
|
13174
14130
|
text: "粘贴",
|
|
13175
14131
|
icon: (0, vue.markRaw)(_element_plus_icons_vue.DocumentCopy),
|
|
@@ -13186,17 +14142,20 @@
|
|
|
13186
14142
|
editorService.paste({
|
|
13187
14143
|
left: initialLeft,
|
|
13188
14144
|
top: initialTop
|
|
13189
|
-
});
|
|
13190
|
-
} else editorService.paste();
|
|
14145
|
+
}, void 0, { historySource });
|
|
14146
|
+
} else editorService.paste(void 0, void 0, { historySource });
|
|
13191
14147
|
}
|
|
13192
14148
|
});
|
|
13193
|
-
var moveTo = async (id, { editorService }) => {
|
|
14149
|
+
var moveTo = async (id, { editorService }, historySource) => {
|
|
13194
14150
|
const nodes = editorService.get("nodes") || [];
|
|
13195
14151
|
const parent = editorService.getNodeById(id);
|
|
13196
14152
|
if (!parent || nodes.length === 0) return;
|
|
13197
|
-
await editorService.moveToContainer((0, _tmagic_core.cloneDeep)(nodes), parent.id, {
|
|
14153
|
+
await editorService.moveToContainer((0, _tmagic_core.cloneDeep)(nodes), parent.id, {
|
|
14154
|
+
doNotSwitchPage: true,
|
|
14155
|
+
historySource
|
|
14156
|
+
});
|
|
13198
14157
|
};
|
|
13199
|
-
var useMoveToMenu = ({ editorService }) => {
|
|
14158
|
+
var useMoveToMenu = ({ editorService }, historySource) => {
|
|
13200
14159
|
return {
|
|
13201
14160
|
type: "button",
|
|
13202
14161
|
text: "移动至",
|
|
@@ -13209,7 +14168,7 @@
|
|
|
13209
14168
|
text: `${page.name}(${page.id})`,
|
|
13210
14169
|
type: "button",
|
|
13211
14170
|
handler: (services) => {
|
|
13212
|
-
moveTo(page.id, services);
|
|
14171
|
+
moveTo(page.id, services, historySource);
|
|
13213
14172
|
}
|
|
13214
14173
|
}))
|
|
13215
14174
|
};
|
|
@@ -13242,7 +14201,7 @@
|
|
|
13242
14201
|
name: component.text,
|
|
13243
14202
|
type: component.type,
|
|
13244
14203
|
...component.data || {}
|
|
13245
|
-
});
|
|
14204
|
+
}, void 0, { historySource: "tree-contextmenu" });
|
|
13246
14205
|
}
|
|
13247
14206
|
}));
|
|
13248
14207
|
const getSubMenuData = (0, vue.computed)(() => {
|
|
@@ -13251,7 +14210,7 @@
|
|
|
13251
14210
|
type: "button",
|
|
13252
14211
|
icon: _element_plus_icons_vue.Files,
|
|
13253
14212
|
handler: () => {
|
|
13254
|
-
editorService.add({ type: "tab-pane" });
|
|
14213
|
+
editorService.add({ type: "tab-pane" }, void 0, { historySource: "tree-contextmenu" });
|
|
13255
14214
|
}
|
|
13256
14215
|
}];
|
|
13257
14216
|
if (node.value?.items) return componentList.value.reduce((subMenuData, group, index) => subMenuData.concat(createMenuItems(group), index < componentList.value.length - 1 ? [{
|
|
@@ -13278,9 +14237,9 @@
|
|
|
13278
14237
|
items: getSubMenuData.value
|
|
13279
14238
|
},
|
|
13280
14239
|
useCopyMenu(),
|
|
13281
|
-
usePasteMenu(),
|
|
13282
|
-
useDeleteMenu(),
|
|
13283
|
-
useMoveToMenu(services),
|
|
14240
|
+
usePasteMenu("tree-contextmenu"),
|
|
14241
|
+
useDeleteMenu("tree-contextmenu"),
|
|
14242
|
+
useMoveToMenu(services, "tree-contextmenu"),
|
|
13284
14243
|
...props.layerContentMenu
|
|
13285
14244
|
], "layer"));
|
|
13286
14245
|
const show = (e) => {
|
|
@@ -13311,7 +14270,7 @@
|
|
|
13311
14270
|
editorService.update({
|
|
13312
14271
|
id: props.data.id,
|
|
13313
14272
|
visible
|
|
13314
|
-
});
|
|
14273
|
+
}, { historySource: "tree" });
|
|
13315
14274
|
};
|
|
13316
14275
|
return (_ctx, _cache) => {
|
|
13317
14276
|
return __props.data.type !== "page" ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicButton), {
|
|
@@ -13927,7 +14886,7 @@
|
|
|
13927
14886
|
name: text,
|
|
13928
14887
|
type,
|
|
13929
14888
|
...data
|
|
13930
|
-
});
|
|
14889
|
+
}, void 0, { historySource: "component-panel" });
|
|
13931
14890
|
};
|
|
13932
14891
|
const dragstartHandler = ({ text, type, data = {} }, e) => {
|
|
13933
14892
|
e.dataTransfer?.setData("text/json", (0, serialize_javascript.default)({
|
|
@@ -14072,7 +15031,11 @@
|
|
|
14072
15031
|
});
|
|
14073
15032
|
}
|
|
14074
15033
|
}, { immediate: true });
|
|
14075
|
-
const activeTabName = (0, vue.
|
|
15034
|
+
const activeTabName = (0, vue.computed)({
|
|
15035
|
+
get: () => uiService.get("sideBarActiveTabName"),
|
|
15036
|
+
set: (value) => uiService.set("sideBarActiveTabName", value)
|
|
15037
|
+
});
|
|
15038
|
+
uiService.set("sideBarActiveTabName", props.data?.status || "");
|
|
14076
15039
|
const getItemConfig = (data) => {
|
|
14077
15040
|
const map = {
|
|
14078
15041
|
[SideItemKey.COMPONENT_LIST]: {
|
|
@@ -14732,11 +15695,11 @@
|
|
|
14732
15695
|
display: () => canCenter.value,
|
|
14733
15696
|
handler: () => {
|
|
14734
15697
|
if (!nodes.value) return;
|
|
14735
|
-
editorService.alignCenter(nodes.value);
|
|
15698
|
+
editorService.alignCenter(nodes.value, { historySource: "stage-contextmenu" });
|
|
14736
15699
|
}
|
|
14737
15700
|
},
|
|
14738
15701
|
useCopyMenu(),
|
|
14739
|
-
usePasteMenu(menuRef),
|
|
15702
|
+
usePasteMenu("stage-contextmenu", menuRef),
|
|
14740
15703
|
{
|
|
14741
15704
|
type: "divider",
|
|
14742
15705
|
direction: "horizontal",
|
|
@@ -14751,7 +15714,7 @@
|
|
|
14751
15714
|
icon: (0, vue.markRaw)(_element_plus_icons_vue.Top),
|
|
14752
15715
|
display: () => !(0, _tmagic_utils.isPage)(node.value) && !(0, _tmagic_utils.isPageFragment)(node.value) && !props.isMultiSelect,
|
|
14753
15716
|
handler: () => {
|
|
14754
|
-
editorService.moveLayer(1);
|
|
15717
|
+
editorService.moveLayer(1, { historySource: "stage-contextmenu" });
|
|
14755
15718
|
}
|
|
14756
15719
|
},
|
|
14757
15720
|
{
|
|
@@ -14760,7 +15723,7 @@
|
|
|
14760
15723
|
icon: (0, vue.markRaw)(_element_plus_icons_vue.Bottom),
|
|
14761
15724
|
display: () => !(0, _tmagic_utils.isPage)(node.value) && !(0, _tmagic_utils.isPageFragment)(node.value) && !props.isMultiSelect,
|
|
14762
15725
|
handler: () => {
|
|
14763
|
-
editorService.moveLayer(-1);
|
|
15726
|
+
editorService.moveLayer(-1, { historySource: "stage-contextmenu" });
|
|
14764
15727
|
}
|
|
14765
15728
|
},
|
|
14766
15729
|
{
|
|
@@ -14769,7 +15732,7 @@
|
|
|
14769
15732
|
icon: (0, vue.markRaw)(_element_plus_icons_vue.Top),
|
|
14770
15733
|
display: () => !(0, _tmagic_utils.isPage)(node.value) && !(0, _tmagic_utils.isPageFragment)(node.value) && !props.isMultiSelect,
|
|
14771
15734
|
handler: () => {
|
|
14772
|
-
editorService.moveLayer(LayerOffset.TOP);
|
|
15735
|
+
editorService.moveLayer(LayerOffset.TOP, { historySource: "stage-contextmenu" });
|
|
14773
15736
|
}
|
|
14774
15737
|
},
|
|
14775
15738
|
{
|
|
@@ -14778,16 +15741,16 @@
|
|
|
14778
15741
|
icon: (0, vue.markRaw)(_element_plus_icons_vue.Bottom),
|
|
14779
15742
|
display: () => !(0, _tmagic_utils.isPage)(node.value) && !(0, _tmagic_utils.isPageFragment)(node.value) && !props.isMultiSelect,
|
|
14780
15743
|
handler: () => {
|
|
14781
|
-
editorService.moveLayer(LayerOffset.BOTTOM);
|
|
15744
|
+
editorService.moveLayer(LayerOffset.BOTTOM, { historySource: "stage-contextmenu" });
|
|
14782
15745
|
}
|
|
14783
15746
|
},
|
|
14784
|
-
useMoveToMenu(services),
|
|
15747
|
+
useMoveToMenu(services, "stage-contextmenu"),
|
|
14785
15748
|
{
|
|
14786
15749
|
type: "divider",
|
|
14787
15750
|
direction: "horizontal",
|
|
14788
15751
|
display: () => !(0, _tmagic_utils.isPage)(node.value) && !(0, _tmagic_utils.isPageFragment)(node.value) && !props.isMultiSelect
|
|
14789
15752
|
},
|
|
14790
|
-
useDeleteMenu(),
|
|
15753
|
+
useDeleteMenu("stage-contextmenu"),
|
|
14791
15754
|
{
|
|
14792
15755
|
type: "divider",
|
|
14793
15756
|
direction: "horizontal"
|
|
@@ -15044,7 +16007,7 @@
|
|
|
15044
16007
|
left: (0, _tmagic_utils.calcValueByFontsize)(doc, left / zoom.value)
|
|
15045
16008
|
};
|
|
15046
16009
|
config.data.inputEvent = e;
|
|
15047
|
-
editorService.add(config.data, parent);
|
|
16010
|
+
editorService.add(config.data, parent, { historySource: "component-panel" });
|
|
15048
16011
|
}
|
|
15049
16012
|
};
|
|
15050
16013
|
return (_ctx, _cache) => {
|
|
@@ -15266,17 +16229,6 @@
|
|
|
15266
16229
|
],
|
|
15267
16230
|
sync: ["setCodeDslByIdSync"]
|
|
15268
16231
|
};
|
|
15269
|
-
/**
|
|
15270
|
-
* 「回滚」生成的新 step 简短描述。仅 service 层使用。
|
|
15271
|
-
*/
|
|
15272
|
-
var describeRevertCodeBlockStep = (step) => {
|
|
15273
|
-
const { oldContent, newContent, changeRecords, id } = step;
|
|
15274
|
-
if (oldContent === null && newContent) return `撤回新增 ${newContent.name || newContent.id || id}`;
|
|
15275
|
-
if (oldContent && newContent === null) return `还原已删除的 ${oldContent.name || oldContent.id || id}`;
|
|
15276
|
-
const name = newContent?.name || oldContent?.name || `${id}`;
|
|
15277
|
-
const propPath = changeRecords?.[0]?.propPath;
|
|
15278
|
-
return propPath ? `还原 ${name} · ${propPath}` : `还原 ${name}`;
|
|
15279
|
-
};
|
|
15280
16232
|
var CodeBlock = class extends BaseService {
|
|
15281
16233
|
state = (0, vue.reactive)({
|
|
15282
16234
|
codeDsl: null,
|
|
@@ -15285,6 +16237,16 @@
|
|
|
15285
16237
|
undeletableList: [],
|
|
15286
16238
|
paramsColConfig: void 0
|
|
15287
16239
|
});
|
|
16240
|
+
/**
|
|
16241
|
+
* 最近一次写入历史栈的代码块历史记录 uuid(单条写入场景:新增 / 更新)。
|
|
16242
|
+
* 供 setCodeDslById(Sync)AndGetHistoryId 取回,普通方法不读取它。
|
|
16243
|
+
*/
|
|
16244
|
+
lastPushedHistoryId = null;
|
|
16245
|
+
/**
|
|
16246
|
+
* deleteCodeDslByIds 一次删除多个代码块时,按写入顺序收集的历史记录 uuid 列表。
|
|
16247
|
+
* 在 deleteCodeDslByIds 入口处重置,供 deleteCodeDslByIdsAndGetHistoryId 取回。
|
|
16248
|
+
*/
|
|
16249
|
+
lastDeletedHistoryIds = [];
|
|
15288
16250
|
constructor() {
|
|
15289
16251
|
super([...canUsePluginMethods$4.async.map((methodName) => ({
|
|
15290
16252
|
name: methodName,
|
|
@@ -15332,10 +16294,12 @@
|
|
|
15332
16294
|
* @param options.doNotPushHistory 是否不写入历史记录(默认 false)
|
|
15333
16295
|
* @returns {void}
|
|
15334
16296
|
*/
|
|
15335
|
-
async setCodeDslById(id, codeConfig, { changeRecords, doNotPushHistory = false } = {}) {
|
|
16297
|
+
async setCodeDslById(id, codeConfig, { changeRecords, doNotPushHistory = false, historyDescription, historySource } = {}) {
|
|
15336
16298
|
this.setCodeDslByIdSync(id, codeConfig, true, {
|
|
15337
16299
|
changeRecords,
|
|
15338
|
-
doNotPushHistory
|
|
16300
|
+
doNotPushHistory,
|
|
16301
|
+
historyDescription,
|
|
16302
|
+
historySource
|
|
15339
16303
|
});
|
|
15340
16304
|
}
|
|
15341
16305
|
/**
|
|
@@ -15350,7 +16314,7 @@
|
|
|
15350
16314
|
* @param options.historyDescription 入栈时附带的人类可读描述,用于历史面板展示
|
|
15351
16315
|
* @returns {void}
|
|
15352
16316
|
*/
|
|
15353
|
-
setCodeDslByIdSync(id, codeConfig, force = true, { changeRecords, doNotPushHistory = false, historyDescription } = {}) {
|
|
16317
|
+
setCodeDslByIdSync(id, codeConfig, force = true, { changeRecords, doNotPushHistory = false, historyDescription, historySource } = {}) {
|
|
15354
16318
|
const codeDsl = this.getCodeDsl();
|
|
15355
16319
|
if (!codeDsl) throw new Error("dsl中没有codeBlocks");
|
|
15356
16320
|
if (codeDsl[id] && !force) return;
|
|
@@ -15365,12 +16329,13 @@
|
|
|
15365
16329
|
...codeConfigProcessed
|
|
15366
16330
|
};
|
|
15367
16331
|
const newContent = cloneDeep$1(codeDsl[id]);
|
|
15368
|
-
if (!doNotPushHistory) history_default.pushCodeBlock(id, {
|
|
16332
|
+
if (!doNotPushHistory) this.lastPushedHistoryId = history_default.pushCodeBlock(id, {
|
|
15369
16333
|
oldContent,
|
|
15370
16334
|
newContent,
|
|
15371
16335
|
changeRecords,
|
|
15372
|
-
historyDescription
|
|
15373
|
-
|
|
16336
|
+
historyDescription,
|
|
16337
|
+
source: historySource
|
|
16338
|
+
})?.uuid ?? null;
|
|
15374
16339
|
this.emit("addOrUpdate", id, codeDsl[id]);
|
|
15375
16340
|
}
|
|
15376
16341
|
/**
|
|
@@ -15450,20 +16415,53 @@
|
|
|
15450
16415
|
* @param options 可选配置
|
|
15451
16416
|
* @param options.doNotPushHistory 是否不写入历史记录(默认 false)
|
|
15452
16417
|
*/
|
|
15453
|
-
async deleteCodeDslByIds(codeIds, { doNotPushHistory = false, historyDescription } = {}) {
|
|
16418
|
+
async deleteCodeDslByIds(codeIds, { doNotPushHistory = false, historyDescription, historySource } = {}) {
|
|
15454
16419
|
const currentDsl = await this.getCodeDsl();
|
|
15455
16420
|
if (!currentDsl) return;
|
|
16421
|
+
this.lastDeletedHistoryIds = [];
|
|
15456
16422
|
codeIds.forEach((id) => {
|
|
15457
16423
|
const oldContent = currentDsl[id] ? cloneDeep$1(currentDsl[id]) : null;
|
|
15458
16424
|
delete currentDsl[id];
|
|
15459
|
-
if (oldContent && !doNotPushHistory)
|
|
15460
|
-
|
|
15461
|
-
|
|
15462
|
-
|
|
15463
|
-
|
|
16425
|
+
if (oldContent && !doNotPushHistory) {
|
|
16426
|
+
const uuid = history_default.pushCodeBlock(id, {
|
|
16427
|
+
oldContent,
|
|
16428
|
+
newContent: null,
|
|
16429
|
+
historyDescription,
|
|
16430
|
+
source: historySource
|
|
16431
|
+
})?.uuid;
|
|
16432
|
+
if (uuid) this.lastDeletedHistoryIds.push(uuid);
|
|
16433
|
+
}
|
|
15464
16434
|
this.emit("remove", id);
|
|
15465
16435
|
});
|
|
15466
16436
|
}
|
|
16437
|
+
/**
|
|
16438
|
+
* 下列 *AndGetHistoryId 方法与对应的写入方法行为完全一致,
|
|
16439
|
+
* 唯一区别是返回值为本次写入历史栈的历史记录 uuid({@link CodeBlockStepValue.uuid}),
|
|
16440
|
+
* 可用于精确引用 / 定位该条历史记录(埋点、revert、跨端同步等)。
|
|
16441
|
+
*
|
|
16442
|
+
* 当本次操作未写入历史(doNotPushHistory 为 true、或无对应记录)时:单条写入返回 null,批量删除返回空数组。
|
|
16443
|
+
*/
|
|
16444
|
+
/** 等价于 {@link setCodeDslById},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
|
|
16445
|
+
async setCodeDslByIdAndGetHistoryId(id, codeConfig, options = {}) {
|
|
16446
|
+
this.lastPushedHistoryId = null;
|
|
16447
|
+
await this.setCodeDslById(id, codeConfig, options);
|
|
16448
|
+
return this.lastPushedHistoryId;
|
|
16449
|
+
}
|
|
16450
|
+
/** 等价于 {@link setCodeDslByIdSync},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
|
|
16451
|
+
setCodeDslByIdSyncAndGetHistoryId(id, codeConfig, force = true, options = {}) {
|
|
16452
|
+
this.lastPushedHistoryId = null;
|
|
16453
|
+
this.setCodeDslByIdSync(id, codeConfig, force, options);
|
|
16454
|
+
return this.lastPushedHistoryId;
|
|
16455
|
+
}
|
|
16456
|
+
/**
|
|
16457
|
+
* 等价于 {@link deleteCodeDslByIds},但返回本次写入的全部历史记录 uuid(按删除顺序)。
|
|
16458
|
+
* 一次删除多个代码块会产生多条历史记录,因此返回数组;未写入任何历史时返回空数组。
|
|
16459
|
+
*/
|
|
16460
|
+
async deleteCodeDslByIdsAndGetHistoryId(codeIds, options = {}) {
|
|
16461
|
+
this.lastDeletedHistoryIds = [];
|
|
16462
|
+
await this.deleteCodeDslByIds(codeIds, options);
|
|
16463
|
+
return [...this.lastDeletedHistoryIds];
|
|
16464
|
+
}
|
|
15467
16465
|
setParamsColConfig(config) {
|
|
15468
16466
|
this.state.paramsColConfig = config;
|
|
15469
16467
|
}
|
|
@@ -15539,10 +16537,25 @@
|
|
|
15539
16537
|
async revert(id, index) {
|
|
15540
16538
|
const entry = history_default.getCodeBlockStepList(id)[index];
|
|
15541
16539
|
if (!entry?.applied) return null;
|
|
15542
|
-
const
|
|
16540
|
+
const { oldSchema, newSchema, changeRecords } = entry.step.diff?.[0] ?? {};
|
|
16541
|
+
if (oldSchema && newSchema && !changeRecords?.length) return null;
|
|
16542
|
+
const description = `回滚 #${index + 1}: ${describeRevertStep(entry.step.id, entry.step.diff?.[0], (s) => s.name)}`;
|
|
15543
16543
|
return await this.applyRevertStep(entry.step, description);
|
|
15544
16544
|
}
|
|
15545
16545
|
/**
|
|
16546
|
+
* 通过历史记录 uuid 回滚某条代码块历史步骤,语义同 {@link revert},
|
|
16547
|
+
* 仅无需调用方再传 codeBlockId 与 index:内部会按 uuid({@link CodeBlockStepValue.uuid})
|
|
16548
|
+
* 在全部代码块栈中定位对应步骤后再回滚。
|
|
16549
|
+
*
|
|
16550
|
+
* @param uuid 目标历史记录的 uuid,通常由 {@link setCodeDslByIdAndGetHistoryId} 等方法返回
|
|
16551
|
+
* @returns 反向后产生的新 step;找不到对应 uuid / 未应用时返回 null
|
|
16552
|
+
*/
|
|
16553
|
+
async revertById(uuid) {
|
|
16554
|
+
const location = history_default.findCodeBlockStepLocationByUuid(uuid);
|
|
16555
|
+
if (!location) return null;
|
|
16556
|
+
return await this.revert(location.id, location.index);
|
|
16557
|
+
}
|
|
16558
|
+
/**
|
|
15546
16559
|
* 生成代码块唯一id
|
|
15547
16560
|
* @returns {Id} 代码块唯一id
|
|
15548
16561
|
*/
|
|
@@ -15607,16 +16620,23 @@
|
|
|
15607
16620
|
* 差异仅在于通过公开的 setCodeDslByIdSync / deleteCodeDslByIds 触发 push。
|
|
15608
16621
|
*/
|
|
15609
16622
|
async applyRevertStep(step, historyDescription) {
|
|
15610
|
-
const { id
|
|
15611
|
-
|
|
15612
|
-
|
|
16623
|
+
const { id } = step;
|
|
16624
|
+
const { oldSchema, newSchema, changeRecords } = step.diff?.[0] ?? {};
|
|
16625
|
+
if (!oldSchema && newSchema) {
|
|
16626
|
+
await this.deleteCodeDslByIds([id], {
|
|
16627
|
+
historyDescription,
|
|
16628
|
+
historySource: "rollback"
|
|
16629
|
+
});
|
|
15613
16630
|
return history_default.getCodeBlockStepList(id).slice(-1)[0]?.step ?? null;
|
|
15614
16631
|
}
|
|
15615
|
-
if (
|
|
15616
|
-
this.setCodeDslByIdSync(id, cloneDeep$1(
|
|
16632
|
+
if (oldSchema && !newSchema) {
|
|
16633
|
+
this.setCodeDslByIdSync(id, cloneDeep$1(oldSchema), true, {
|
|
16634
|
+
historyDescription,
|
|
16635
|
+
historySource: "rollback"
|
|
16636
|
+
});
|
|
15617
16637
|
return history_default.getCodeBlockStepList(id).slice(-1)[0]?.step ?? null;
|
|
15618
16638
|
}
|
|
15619
|
-
if (!
|
|
16639
|
+
if (!oldSchema || !newSchema) return null;
|
|
15620
16640
|
if (changeRecords?.length) {
|
|
15621
16641
|
const current = this.getCodeContentById(id);
|
|
15622
16642
|
if (!current) return null;
|
|
@@ -15627,16 +16647,20 @@
|
|
|
15627
16647
|
fallbackToFullReplace = true;
|
|
15628
16648
|
break;
|
|
15629
16649
|
}
|
|
15630
|
-
const value = cloneDeep$1((0, _tmagic_utils.getValueByKeyPath)(record.propPath,
|
|
16650
|
+
const value = cloneDeep$1((0, _tmagic_utils.getValueByKeyPath)(record.propPath, oldSchema));
|
|
15631
16651
|
(0, _tmagic_utils.setValueByKeyPath)(record.propPath, value, patched);
|
|
15632
16652
|
}
|
|
15633
|
-
this.setCodeDslByIdSync(id, fallbackToFullReplace ? cloneDeep$1(
|
|
16653
|
+
this.setCodeDslByIdSync(id, fallbackToFullReplace ? cloneDeep$1(oldSchema) : patched, true, {
|
|
15634
16654
|
changeRecords,
|
|
15635
|
-
historyDescription
|
|
16655
|
+
historyDescription,
|
|
16656
|
+
historySource: "rollback"
|
|
15636
16657
|
});
|
|
15637
16658
|
return history_default.getCodeBlockStepList(id).slice(-1)[0]?.step ?? null;
|
|
15638
16659
|
}
|
|
15639
|
-
this.setCodeDslByIdSync(id, cloneDeep$1(
|
|
16660
|
+
this.setCodeDslByIdSync(id, cloneDeep$1(oldSchema), true, {
|
|
16661
|
+
historyDescription,
|
|
16662
|
+
historySource: "rollback"
|
|
16663
|
+
});
|
|
15640
16664
|
return history_default.getCodeBlockStepList(id).slice(-1)[0]?.step ?? null;
|
|
15641
16665
|
}
|
|
15642
16666
|
/**
|
|
@@ -15654,19 +16678,20 @@
|
|
|
15654
16678
|
* @param reverse true=撤销,false=重做
|
|
15655
16679
|
*/
|
|
15656
16680
|
async applyHistoryStep(step, reverse) {
|
|
15657
|
-
const { id
|
|
15658
|
-
|
|
16681
|
+
const { id } = step;
|
|
16682
|
+
const { oldSchema, newSchema, changeRecords } = step.diff?.[0] ?? {};
|
|
16683
|
+
if (!oldSchema && newSchema) {
|
|
15659
16684
|
if (reverse) await this.deleteCodeDslByIds([id], { doNotPushHistory: true });
|
|
15660
|
-
else this.setCodeDslByIdSync(id, cloneDeep$1(
|
|
16685
|
+
else this.setCodeDslByIdSync(id, cloneDeep$1(newSchema), true, { doNotPushHistory: true });
|
|
15661
16686
|
return;
|
|
15662
16687
|
}
|
|
15663
|
-
if (
|
|
15664
|
-
if (reverse) this.setCodeDslByIdSync(id, cloneDeep$1(
|
|
16688
|
+
if (oldSchema && !newSchema) {
|
|
16689
|
+
if (reverse) this.setCodeDslByIdSync(id, cloneDeep$1(oldSchema), true, { doNotPushHistory: true });
|
|
15665
16690
|
else await this.deleteCodeDslByIds([id], { doNotPushHistory: true });
|
|
15666
16691
|
return;
|
|
15667
16692
|
}
|
|
15668
|
-
if (!
|
|
15669
|
-
const sourceForValues = reverse ?
|
|
16693
|
+
if (!oldSchema || !newSchema) return;
|
|
16694
|
+
const sourceForValues = reverse ? oldSchema : newSchema;
|
|
15670
16695
|
if (changeRecords?.length) {
|
|
15671
16696
|
const current = this.getCodeContentById(id);
|
|
15672
16697
|
if (!current) return;
|
|
@@ -15735,18 +16760,6 @@
|
|
|
15735
16760
|
"createId"
|
|
15736
16761
|
]
|
|
15737
16762
|
};
|
|
15738
|
-
/**
|
|
15739
|
-
* 「回滚」生成的新 step 简短描述。
|
|
15740
|
-
* 仅在 service 层使用,避免依赖 UI 层 composables。
|
|
15741
|
-
*/
|
|
15742
|
-
var describeRevertDataSourceStep = (step) => {
|
|
15743
|
-
const { oldSchema, newSchema, changeRecords, id } = step;
|
|
15744
|
-
if (oldSchema === null && newSchema) return `撤回新增 ${newSchema.title || newSchema.id || id}`;
|
|
15745
|
-
if (oldSchema && newSchema === null) return `还原已删除的 ${oldSchema.title || oldSchema.id || id}`;
|
|
15746
|
-
const title = newSchema?.title || oldSchema?.title || `${id}`;
|
|
15747
|
-
const propPath = changeRecords?.[0]?.propPath;
|
|
15748
|
-
return propPath ? `还原 ${title} · ${propPath}` : `还原 ${title}`;
|
|
15749
|
-
};
|
|
15750
16763
|
var DataSource = class extends BaseService {
|
|
15751
16764
|
state = (0, vue.reactive)({
|
|
15752
16765
|
datasourceTypeList: [],
|
|
@@ -15757,6 +16770,12 @@
|
|
|
15757
16770
|
events: {},
|
|
15758
16771
|
methods: {}
|
|
15759
16772
|
});
|
|
16773
|
+
/**
|
|
16774
|
+
* 最近一次写入历史栈的数据源历史记录 uuid。
|
|
16775
|
+
* 供 *AndGetHistoryId 系列方法在调用 add / update / remove 后取回本次产生的历史记录 id;
|
|
16776
|
+
* 普通方法不读取它,调用前由 *AndGetHistoryId 重置为 null。
|
|
16777
|
+
*/
|
|
16778
|
+
lastPushedHistoryId = null;
|
|
15760
16779
|
constructor() {
|
|
15761
16780
|
super(canUsePluginMethods$3.sync.map((methodName) => ({
|
|
15762
16781
|
name: methodName,
|
|
@@ -15800,17 +16819,18 @@
|
|
|
15800
16819
|
* @param options.doNotPushHistory 是否不写入历史记录(默认 false)
|
|
15801
16820
|
* @param options.historyDescription 入栈时附带的人类可读描述,用于历史面板展示
|
|
15802
16821
|
*/
|
|
15803
|
-
add(config, { doNotPushHistory = false, historyDescription } = {}) {
|
|
16822
|
+
add(config, { doNotPushHistory = false, historyDescription, historySource } = {}) {
|
|
15804
16823
|
const newConfig = {
|
|
15805
16824
|
...config,
|
|
15806
16825
|
id: config.id && !this.getDataSourceById(config.id) ? config.id : this.createId()
|
|
15807
16826
|
};
|
|
15808
16827
|
this.get("dataSources").push(newConfig);
|
|
15809
|
-
if (!doNotPushHistory) history_default.pushDataSource(newConfig.id, {
|
|
16828
|
+
if (!doNotPushHistory) this.lastPushedHistoryId = history_default.pushDataSource(newConfig.id, {
|
|
15810
16829
|
oldSchema: null,
|
|
15811
16830
|
newSchema: newConfig,
|
|
15812
|
-
historyDescription
|
|
15813
|
-
|
|
16831
|
+
historyDescription,
|
|
16832
|
+
source: historySource
|
|
16833
|
+
})?.uuid ?? null;
|
|
15814
16834
|
this.emit("add", newConfig);
|
|
15815
16835
|
return newConfig;
|
|
15816
16836
|
}
|
|
@@ -15822,18 +16842,19 @@
|
|
|
15822
16842
|
* @param data.doNotPushHistory 是否不写入历史记录(默认 false)
|
|
15823
16843
|
* @param data.historyDescription 入栈时附带的人类可读描述,用于历史面板展示
|
|
15824
16844
|
*/
|
|
15825
|
-
update(config, { changeRecords = [], doNotPushHistory = false, historyDescription } = {}) {
|
|
16845
|
+
update(config, { changeRecords = [], doNotPushHistory = false, historyDescription, historySource } = {}) {
|
|
15826
16846
|
const dataSources = this.get("dataSources");
|
|
15827
16847
|
const index = dataSources.findIndex((ds) => ds.id === config.id);
|
|
15828
16848
|
const oldConfig = dataSources[index];
|
|
15829
16849
|
const newConfig = cloneDeep$1(config);
|
|
15830
16850
|
dataSources[index] = newConfig;
|
|
15831
|
-
if (!doNotPushHistory) history_default.pushDataSource(newConfig.id, {
|
|
16851
|
+
if (!doNotPushHistory) this.lastPushedHistoryId = history_default.pushDataSource(newConfig.id, {
|
|
15832
16852
|
oldSchema: oldConfig ? cloneDeep$1(oldConfig) : null,
|
|
15833
16853
|
newSchema: newConfig,
|
|
15834
16854
|
changeRecords,
|
|
15835
|
-
historyDescription
|
|
15836
|
-
|
|
16855
|
+
historyDescription,
|
|
16856
|
+
source: historySource
|
|
16857
|
+
})?.uuid ?? null;
|
|
15837
16858
|
this.emit("update", newConfig, {
|
|
15838
16859
|
oldConfig,
|
|
15839
16860
|
changeRecords
|
|
@@ -15847,19 +16868,45 @@
|
|
|
15847
16868
|
* @param options.doNotPushHistory 是否不写入历史记录(默认 false)
|
|
15848
16869
|
* @param options.historyDescription 入栈时附带的人类可读描述,用于历史面板展示
|
|
15849
16870
|
*/
|
|
15850
|
-
remove(id, { doNotPushHistory = false, historyDescription } = {}) {
|
|
16871
|
+
remove(id, { doNotPushHistory = false, historyDescription, historySource } = {}) {
|
|
15851
16872
|
const dataSources = this.get("dataSources");
|
|
15852
16873
|
const index = dataSources.findIndex((ds) => ds.id === id);
|
|
15853
16874
|
const oldConfig = index !== -1 ? dataSources[index] : null;
|
|
15854
16875
|
dataSources.splice(index, 1);
|
|
15855
|
-
if (oldConfig && !doNotPushHistory) history_default.pushDataSource(id, {
|
|
16876
|
+
if (oldConfig && !doNotPushHistory) this.lastPushedHistoryId = history_default.pushDataSource(id, {
|
|
15856
16877
|
oldSchema: cloneDeep$1(oldConfig),
|
|
15857
16878
|
newSchema: null,
|
|
15858
|
-
historyDescription
|
|
15859
|
-
|
|
16879
|
+
historyDescription,
|
|
16880
|
+
source: historySource
|
|
16881
|
+
})?.uuid ?? null;
|
|
15860
16882
|
this.emit("remove", id);
|
|
15861
16883
|
}
|
|
15862
16884
|
/**
|
|
16885
|
+
* 下列 *AndGetHistoryId 方法与对应的 add / update / remove 行为完全一致,
|
|
16886
|
+
* 唯一区别是返回值为本次写入历史栈的历史记录 uuid({@link DataSourceStepValue.uuid}),
|
|
16887
|
+
* 而非数据源配置。可用于精确引用 / 定位该条历史记录(埋点、revert、跨端同步等)。
|
|
16888
|
+
*
|
|
16889
|
+
* 当本次操作未写入历史(doNotPushHistory 为 true、或无对应记录)时返回 null。
|
|
16890
|
+
*/
|
|
16891
|
+
/** 等价于 {@link add},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
|
|
16892
|
+
addAndGetHistoryId(config, options = {}) {
|
|
16893
|
+
this.lastPushedHistoryId = null;
|
|
16894
|
+
this.add(config, options);
|
|
16895
|
+
return this.lastPushedHistoryId;
|
|
16896
|
+
}
|
|
16897
|
+
/** 等价于 {@link update},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
|
|
16898
|
+
updateAndGetHistoryId(config, options = {}) {
|
|
16899
|
+
this.lastPushedHistoryId = null;
|
|
16900
|
+
this.update(config, options);
|
|
16901
|
+
return this.lastPushedHistoryId;
|
|
16902
|
+
}
|
|
16903
|
+
/** 等价于 {@link remove},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
|
|
16904
|
+
removeAndGetHistoryId(id, options = {}) {
|
|
16905
|
+
this.lastPushedHistoryId = null;
|
|
16906
|
+
this.remove(id, options);
|
|
16907
|
+
return this.lastPushedHistoryId;
|
|
16908
|
+
}
|
|
16909
|
+
/**
|
|
15863
16910
|
* 撤销指定数据源的最近一次变更。
|
|
15864
16911
|
*
|
|
15865
16912
|
* 内部走 add / update / remove,因此会自动触发 dataSourceService 的事件,
|
|
@@ -15927,9 +16974,24 @@
|
|
|
15927
16974
|
revert(id, index) {
|
|
15928
16975
|
const entry = history_default.getDataSourceStepList(id)[index];
|
|
15929
16976
|
if (!entry?.applied) return null;
|
|
15930
|
-
const
|
|
16977
|
+
const { oldSchema, newSchema, changeRecords } = entry.step.diff?.[0] ?? {};
|
|
16978
|
+
if (oldSchema && newSchema && !changeRecords?.length) return null;
|
|
16979
|
+
const description = `回滚 #${index + 1}: ${describeRevertStep(entry.step.id, entry.step.diff?.[0], (s) => s.title)}`;
|
|
15931
16980
|
return this.applyRevertStep(entry.step, description);
|
|
15932
16981
|
}
|
|
16982
|
+
/**
|
|
16983
|
+
* 通过历史记录 uuid 回滚某条数据源历史步骤,语义同 {@link revert},
|
|
16984
|
+
* 仅无需调用方再传 dataSourceId 与 index:内部会按 uuid({@link DataSourceStepValue.uuid})
|
|
16985
|
+
* 在全部数据源栈中定位对应步骤后再回滚。
|
|
16986
|
+
*
|
|
16987
|
+
* @param uuid 目标历史记录的 uuid,通常由 {@link addAndGetHistoryId} 等方法返回
|
|
16988
|
+
* @returns 反向后产生的新 step;找不到对应 uuid / 未应用时返回 null
|
|
16989
|
+
*/
|
|
16990
|
+
revertById(uuid) {
|
|
16991
|
+
const location = history_default.findDataSourceStepLocationByUuid(uuid);
|
|
16992
|
+
if (!location) return null;
|
|
16993
|
+
return this.revert(location.id, location.index);
|
|
16994
|
+
}
|
|
15933
16995
|
createId() {
|
|
15934
16996
|
return `ds_${(0, _tmagic_utils.guid)()}`;
|
|
15935
16997
|
}
|
|
@@ -15988,13 +17050,20 @@
|
|
|
15988
17050
|
* 同构,差异仅在于走对应的公共 add / update / remove 而不是带 doNotPushHistory 的版本。
|
|
15989
17051
|
*/
|
|
15990
17052
|
applyRevertStep(step, historyDescription) {
|
|
15991
|
-
const { id
|
|
15992
|
-
|
|
15993
|
-
|
|
17053
|
+
const { id } = step;
|
|
17054
|
+
const { oldSchema, newSchema, changeRecords } = step.diff?.[0] ?? {};
|
|
17055
|
+
if (!oldSchema && newSchema) {
|
|
17056
|
+
this.remove(`${id}`, {
|
|
17057
|
+
historyDescription,
|
|
17058
|
+
historySource: "rollback"
|
|
17059
|
+
});
|
|
15994
17060
|
return history_default.getDataSourceStepList(id).slice(-1)[0]?.step ?? null;
|
|
15995
17061
|
}
|
|
15996
|
-
if (oldSchema && newSchema
|
|
15997
|
-
this.add(cloneDeep$1(oldSchema), {
|
|
17062
|
+
if (oldSchema && !newSchema) {
|
|
17063
|
+
this.add(cloneDeep$1(oldSchema), {
|
|
17064
|
+
historyDescription,
|
|
17065
|
+
historySource: "rollback"
|
|
17066
|
+
});
|
|
15998
17067
|
return history_default.getDataSourceStepList(id).slice(-1)[0]?.step ?? null;
|
|
15999
17068
|
}
|
|
16000
17069
|
if (!oldSchema || !newSchema) return null;
|
|
@@ -16013,11 +17082,15 @@
|
|
|
16013
17082
|
}
|
|
16014
17083
|
this.update(fallbackToFullReplace ? cloneDeep$1(oldSchema) : patched, {
|
|
16015
17084
|
changeRecords,
|
|
16016
|
-
historyDescription
|
|
17085
|
+
historyDescription,
|
|
17086
|
+
historySource: "rollback"
|
|
16017
17087
|
});
|
|
16018
17088
|
return history_default.getDataSourceStepList(id).slice(-1)[0]?.step ?? null;
|
|
16019
17089
|
}
|
|
16020
|
-
this.update(cloneDeep$1(oldSchema), {
|
|
17090
|
+
this.update(cloneDeep$1(oldSchema), {
|
|
17091
|
+
historyDescription,
|
|
17092
|
+
historySource: "rollback"
|
|
17093
|
+
});
|
|
16021
17094
|
return history_default.getDataSourceStepList(id).slice(-1)[0]?.step ?? null;
|
|
16022
17095
|
}
|
|
16023
17096
|
/**
|
|
@@ -16035,13 +17108,14 @@
|
|
|
16035
17108
|
* @param reverse true=撤销,false=重做
|
|
16036
17109
|
*/
|
|
16037
17110
|
applyHistoryStep(step, reverse) {
|
|
16038
|
-
const { id
|
|
16039
|
-
|
|
17111
|
+
const { id } = step;
|
|
17112
|
+
const { oldSchema, newSchema, changeRecords } = step.diff?.[0] ?? {};
|
|
17113
|
+
if (!oldSchema && newSchema) {
|
|
16040
17114
|
if (reverse) this.remove(`${id}`, { doNotPushHistory: true });
|
|
16041
17115
|
else this.add(cloneDeep$1(newSchema), { doNotPushHistory: true });
|
|
16042
17116
|
return;
|
|
16043
17117
|
}
|
|
16044
|
-
if (oldSchema && newSchema
|
|
17118
|
+
if (oldSchema && !newSchema) {
|
|
16045
17119
|
if (reverse) this.add(cloneDeep$1(oldSchema), { doNotPushHistory: true });
|
|
16046
17120
|
else this.remove(`${id}`, { doNotPushHistory: true });
|
|
16047
17121
|
return;
|
|
@@ -16334,7 +17408,7 @@
|
|
|
16334
17408
|
[KeyBindingCommand.DELETE_NODE]: () => {
|
|
16335
17409
|
const nodes = editor_default.get("nodes");
|
|
16336
17410
|
if (!nodes || (0, _tmagic_utils.isPage)(nodes[0]) || (0, _tmagic_utils.isPageFragment)(nodes[0])) return;
|
|
16337
|
-
editor_default.remove(nodes);
|
|
17411
|
+
editor_default.remove(nodes, { historySource: "shortcut" });
|
|
16338
17412
|
},
|
|
16339
17413
|
[KeyBindingCommand.COPY_NODE]: () => {
|
|
16340
17414
|
const nodes = editor_default.get("nodes");
|
|
@@ -16344,13 +17418,13 @@
|
|
|
16344
17418
|
const nodes = editor_default.get("nodes");
|
|
16345
17419
|
if (!nodes || (0, _tmagic_utils.isPage)(nodes[0]) || (0, _tmagic_utils.isPageFragment)(nodes[0])) return;
|
|
16346
17420
|
editor_default.copy(nodes);
|
|
16347
|
-
editor_default.remove(nodes);
|
|
17421
|
+
editor_default.remove(nodes, { historySource: "shortcut" });
|
|
16348
17422
|
},
|
|
16349
17423
|
[KeyBindingCommand.PASTE_NODE]: () => {
|
|
16350
17424
|
editor_default.get("nodes") && editor_default.paste({
|
|
16351
17425
|
offsetX: 10,
|
|
16352
17426
|
offsetY: 10
|
|
16353
|
-
});
|
|
17427
|
+
}, void 0, { historySource: "shortcut" });
|
|
16354
17428
|
},
|
|
16355
17429
|
[KeyBindingCommand.UNDO]: () => {
|
|
16356
17430
|
editor_default.undo();
|
|
@@ -16692,7 +17766,7 @@
|
|
|
16692
17766
|
//#region packages/editor/src/initService.ts
|
|
16693
17767
|
var initServiceState = (props, { editorService, historyService, componentListService, propsService, eventsService, uiService, codeBlockService, keybindingService, dataSourceService, depService }) => {
|
|
16694
17768
|
(0, vue.watch)(() => props.modelValue, (modelValue) => {
|
|
16695
|
-
editorService.set("root", modelValue || null);
|
|
17769
|
+
editorService.set("root", modelValue || null, { historySource: "initial" });
|
|
16696
17770
|
}, { immediate: true });
|
|
16697
17771
|
(0, vue.watch)(() => props.disabledMultiSelect, (disabledMultiSelect) => {
|
|
16698
17772
|
editorService.set("disabledMultiSelect", disabledMultiSelect || false);
|
|
@@ -21967,6 +23041,7 @@
|
|
|
21967
23041
|
exports.H_GUIDE_LINE_STORAGE_KEY = H_GUIDE_LINE_STORAGE_KEY;
|
|
21968
23042
|
exports.HistoryDiffDialog = HistoryDiffDialog_default;
|
|
21969
23043
|
exports.HistoryListBucket = Bucket_default;
|
|
23044
|
+
exports.HistoryListBucketTab = BucketTab_default;
|
|
21970
23045
|
exports.Icon = Icon_default;
|
|
21971
23046
|
exports.IdleTask = IdleTask;
|
|
21972
23047
|
exports.KeyBindingCommand = KeyBindingCommand;
|
|
@@ -22016,17 +23091,24 @@
|
|
|
22016
23091
|
exports.classifyDragSources = classifyDragSources;
|
|
22017
23092
|
exports.codeBlockService = codeBlock_default;
|
|
22018
23093
|
exports.collectRelatedNodes = collectRelatedNodes;
|
|
23094
|
+
exports.createStackStep = createStackStep;
|
|
22019
23095
|
exports.dataSourceService = dataSource_default;
|
|
22020
23096
|
exports.debug = debug;
|
|
22021
23097
|
exports.default = plugin_default;
|
|
22022
23098
|
exports.defaultIsExpandable = defaultIsExpandable;
|
|
22023
23099
|
exports.depService = dep_default;
|
|
23100
|
+
exports.describeRevertStep = describeRevertStep;
|
|
23101
|
+
exports.describeStepForRevert = describeStepForRevert;
|
|
23102
|
+
exports.deserializeStacks = deserializeStacks;
|
|
22024
23103
|
Object.defineProperty(exports, "designPlugin", {
|
|
22025
23104
|
enumerable: true,
|
|
22026
23105
|
get: function() {
|
|
22027
23106
|
return _tmagic_design.default;
|
|
22028
23107
|
}
|
|
22029
23108
|
});
|
|
23109
|
+
exports.detectPageTargetId = detectPageTargetId;
|
|
23110
|
+
exports.detectPageTargetName = detectPageTargetName;
|
|
23111
|
+
exports.detectStackOpType = detectStackOpType;
|
|
22030
23112
|
exports.displayTabConfig = displayTabConfig;
|
|
22031
23113
|
exports.editorNodeMergeCustomizer = editorNodeMergeCustomizer;
|
|
22032
23114
|
exports.editorService = editor_default;
|
|
@@ -22057,21 +23139,31 @@
|
|
|
22057
23139
|
exports.getGuideLineFromCache = getGuideLineFromCache;
|
|
22058
23140
|
exports.getInitPositionStyle = getInitPositionStyle;
|
|
22059
23141
|
exports.getNodeIndex = getNodeIndex;
|
|
23142
|
+
exports.getOrCreateStack = getOrCreateStack;
|
|
22060
23143
|
exports.getPageFragmentList = getPageFragmentList;
|
|
22061
23144
|
exports.getPageList = getPageList;
|
|
22062
23145
|
exports.getPageNameList = getPageNameList;
|
|
22063
23146
|
exports.getPositionInContainer = getPositionInContainer;
|
|
22064
23147
|
exports.getRelativeStyle = getRelativeStyle;
|
|
22065
23148
|
exports.historyService = history_default;
|
|
23149
|
+
exports.idbDelete = idbDelete;
|
|
23150
|
+
exports.idbGet = idbGet;
|
|
23151
|
+
exports.idbSet = idbSet;
|
|
22066
23152
|
exports.info = info;
|
|
22067
23153
|
exports.isIncludeDataSource = isIncludeDataSource;
|
|
23154
|
+
exports.isIndexedDBSupported = isIndexedDBSupported;
|
|
22068
23155
|
exports.loadMonaco = monaco_editor_default;
|
|
22069
23156
|
exports.log = log;
|
|
23157
|
+
exports.markStackSaved = markStackSaved;
|
|
23158
|
+
exports.mergePageSteps = mergePageSteps;
|
|
23159
|
+
exports.mergeStackSteps = mergeStackSteps;
|
|
22070
23160
|
exports.moveItemsInContainer = moveItemsInContainer;
|
|
22071
23161
|
exports.numberOptions = numberOptions;
|
|
23162
|
+
exports.openIndexedDB = openIndexedDB;
|
|
22072
23163
|
exports.propsService = props_default;
|
|
22073
23164
|
exports.resolveSelectedNode = resolveSelectedNode;
|
|
22074
23165
|
exports.serializeConfig = serializeConfig;
|
|
23166
|
+
exports.serializeStacks = serializeStacks;
|
|
22075
23167
|
exports.setChildrenLayout = setChildrenLayout;
|
|
22076
23168
|
exports.setEditorConfig = setEditorConfig;
|
|
22077
23169
|
exports.setLayout = setLayout;
|
|
@@ -22086,6 +23178,7 @@
|
|
|
22086
23178
|
});
|
|
22087
23179
|
exports.toggleFixedPosition = toggleFixedPosition;
|
|
22088
23180
|
exports.uiService = ui_default;
|
|
23181
|
+
exports.undoFloor = undoFloor;
|
|
22089
23182
|
exports.updateStatus = updateStatus;
|
|
22090
23183
|
exports.useCodeBlockEdit = useCodeBlockEdit;
|
|
22091
23184
|
exports.useEditorContentHeight = useEditorContentHeight;
|