@tmagic/editor 1.8.0-beta.1 → 1.8.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/dist/es/Editor.vue_vue_type_script_setup_true_lang.js +20 -2
  2. package/dist/es/components/CodeBlockEditor.vue_vue_type_script_setup_true_lang.js +18 -110
  3. package/dist/es/components/CodeParams.vue_vue_type_script_setup_true_lang.js +6 -0
  4. package/dist/es/components/CompareForm.js +5 -0
  5. package/dist/es/components/CompareForm.vue_vue_type_script_setup_true_lang.js +171 -0
  6. package/dist/es/editorProps.js +2 -0
  7. package/dist/es/fields/Code.vue_vue_type_script_setup_true_lang.js +19 -3
  8. package/dist/es/fields/CodeLink.vue_vue_type_script_setup_true_lang.js +2 -5
  9. package/dist/es/fields/CodeSelect.vue_vue_type_script_setup_true_lang.js +18 -2
  10. package/dist/es/fields/CodeSelectCol.vue_vue_type_script_setup_true_lang.js +37 -6
  11. package/dist/es/fields/DataSourceFieldSelect/FieldSelect.vue_vue_type_script_setup_true_lang.js +3 -1
  12. package/dist/es/fields/DataSourceFieldSelect/Index.vue_vue_type_script_setup_true_lang.js +2 -2
  13. package/dist/es/fields/DataSourceFields.vue_vue_type_script_setup_true_lang.js +14 -6
  14. package/dist/es/fields/DataSourceMethodSelect.vue_vue_type_script_setup_true_name_true_lang.js +3 -1
  15. package/dist/es/fields/DataSourceMethods.vue_vue_type_script_setup_true_lang.js +15 -7
  16. package/dist/es/fields/DataSourceMocks.vue_vue_type_script_setup_true_lang.js +14 -6
  17. package/dist/es/fields/DataSourceSelect.vue_vue_type_script_setup_true_lang.js +3 -1
  18. package/dist/es/fields/EventSelect.vue_vue_type_script_setup_true_lang.js +58 -11
  19. package/dist/es/fields/KeyValue.vue_vue_type_script_setup_true_lang.js +11 -6
  20. package/dist/es/fields/UISelect.vue_vue_type_script_setup_true_lang.js +8 -5
  21. package/dist/es/hooks/use-code-block-edit.js +2 -2
  22. package/dist/es/hooks/use-stage.js +17 -8
  23. package/dist/es/index.js +5 -1
  24. package/dist/es/initService.js +6 -6
  25. package/dist/es/layouts/CodeEditor.vue_vue_type_script_setup_true_lang.js +6 -5
  26. package/dist/es/layouts/Framework.vue_vue_type_script_setup_true_lang.js +12 -7
  27. package/dist/es/layouts/NavMenu.vue_vue_type_script_setup_true_lang.js +8 -0
  28. package/dist/es/layouts/history-list/Bucket.js +5 -0
  29. package/dist/es/layouts/history-list/Bucket.vue_vue_type_script_setup_true_lang.js +103 -0
  30. package/dist/es/layouts/history-list/BucketTab.js +5 -0
  31. package/dist/es/layouts/history-list/BucketTab.vue_vue_type_script_setup_true_lang.js +73 -0
  32. package/dist/es/layouts/history-list/GroupRow.js +5 -0
  33. package/dist/es/layouts/history-list/GroupRow.vue_vue_type_script_setup_true_lang.js +198 -0
  34. package/dist/es/layouts/history-list/HistoryDiffDialog.js +5 -0
  35. package/dist/es/layouts/history-list/HistoryDiffDialog.vue_vue_type_script_setup_true_lang.js +245 -0
  36. package/dist/es/layouts/history-list/HistoryListPanel.js +5 -0
  37. package/dist/es/layouts/history-list/HistoryListPanel.vue_vue_type_script_setup_true_lang.js +340 -0
  38. package/dist/es/layouts/history-list/InitialRow.js +5 -0
  39. package/dist/es/layouts/history-list/InitialRow.vue_vue_type_script_setup_true_lang.js +54 -0
  40. package/dist/es/layouts/history-list/PageTab.js +5 -0
  41. package/dist/es/layouts/history-list/PageTab.vue_vue_type_script_setup_true_lang.js +105 -0
  42. package/dist/es/layouts/history-list/composables.js +172 -0
  43. package/dist/es/layouts/sidebar/layer/use-node-status.js +1 -1
  44. package/dist/es/services/BaseService.js +31 -68
  45. package/dist/es/services/codeBlock.js +205 -8
  46. package/dist/es/services/componentList.js +2 -2
  47. package/dist/es/services/dataSource.js +217 -8
  48. package/dist/es/services/dep.js +21 -9
  49. package/dist/es/services/editor.js +350 -160
  50. package/dist/es/services/events.js +2 -2
  51. package/dist/es/services/history.js +410 -12
  52. package/dist/es/services/keybinding.js +2 -2
  53. package/dist/es/services/props.js +8 -8
  54. package/dist/es/services/stageOverlay.js +2 -2
  55. package/dist/es/services/storage.js +2 -2
  56. package/dist/es/services/ui.js +2 -2
  57. package/dist/es/style.css +379 -1
  58. package/dist/es/utils/code-block.js +122 -0
  59. package/dist/es/utils/content-menu.js +4 -5
  60. package/dist/es/utils/dep/worker.js +1 -1
  61. package/dist/es/utils/editor.js +2 -2
  62. package/dist/es/utils/undo-redo.js +20 -0
  63. package/dist/style.css +379 -1
  64. package/dist/tmagic-editor.umd.cjs +3331 -787
  65. package/package.json +7 -7
  66. package/src/Editor.vue +15 -0
  67. package/src/components/CodeBlockEditor.vue +20 -109
  68. package/src/components/CodeParams.vue +6 -0
  69. package/src/components/CompareForm.vue +238 -0
  70. package/src/editorProps.ts +9 -0
  71. package/src/fields/Code.vue +19 -3
  72. package/src/fields/CodeLink.vue +2 -5
  73. package/src/fields/CodeSelect.vue +17 -1
  74. package/src/fields/CodeSelectCol.vue +30 -2
  75. package/src/fields/DataSourceFieldSelect/FieldSelect.vue +7 -1
  76. package/src/fields/DataSourceFieldSelect/Index.vue +2 -2
  77. package/src/fields/DataSourceFields.vue +12 -3
  78. package/src/fields/DataSourceMethodSelect.vue +4 -1
  79. package/src/fields/DataSourceMethods.vue +14 -4
  80. package/src/fields/DataSourceMocks.vue +13 -3
  81. package/src/fields/DataSourceSelect.vue +4 -1
  82. package/src/fields/EventSelect.vue +57 -8
  83. package/src/fields/KeyValue.vue +17 -4
  84. package/src/fields/UISelect.vue +5 -2
  85. package/src/hooks/use-code-block-edit.ts +5 -2
  86. package/src/hooks/use-stage.ts +24 -7
  87. package/src/index.ts +3 -0
  88. package/src/layouts/CodeEditor.vue +17 -5
  89. package/src/layouts/Framework.vue +10 -5
  90. package/src/layouts/NavMenu.vue +9 -0
  91. package/src/layouts/history-list/Bucket.vue +124 -0
  92. package/src/layouts/history-list/BucketTab.vue +77 -0
  93. package/src/layouts/history-list/GroupRow.vue +242 -0
  94. package/src/layouts/history-list/HistoryDiffDialog.vue +231 -0
  95. package/src/layouts/history-list/HistoryListPanel.vue +380 -0
  96. package/src/layouts/history-list/InitialRow.vue +52 -0
  97. package/src/layouts/history-list/PageTab.vue +108 -0
  98. package/src/layouts/history-list/composables.ts +226 -0
  99. package/src/layouts/sidebar/layer/use-node-status.ts +9 -6
  100. package/src/services/BaseService.ts +35 -86
  101. package/src/services/codeBlock.ts +256 -5
  102. package/src/services/dataSource.ts +257 -6
  103. package/src/services/dep.ts +32 -9
  104. package/src/services/editor.ts +454 -171
  105. package/src/services/history.ts +455 -8
  106. package/src/services/props.ts +2 -2
  107. package/src/theme/history-list-panel.scss +462 -0
  108. package/src/theme/theme.scss +1 -0
  109. package/src/type.ts +269 -6
  110. package/src/utils/code-block.ts +150 -0
  111. package/src/utils/content-menu.ts +8 -5
  112. package/src/utils/dep/worker.ts +5 -3
  113. package/src/utils/index.ts +1 -0
  114. package/src/utils/undo-redo.ts +23 -0
  115. package/types/index.d.ts +2274 -819
  116. package/dist/es/utils/compose.js +0 -44
  117. package/dist/es/utils/editor-history.js +0 -100
  118. package/src/utils/compose.ts +0 -52
  119. package/src/utils/editor-history.ts +0 -138
@@ -0,0 +1,245 @@
1
+ import CodeEditor_default from "../CodeEditor.js";
2
+ import CompareForm_default from "../../components/CompareForm.js";
3
+ import { TMagicButton, TMagicDialog, TMagicRadioButton, TMagicRadioGroup, TMagicTag } from "@tmagic/design";
4
+ import { Fragment, Teleport, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, openBlock, ref, toDisplayString, unref, watch, withCtx } from "vue";
5
+ import { isEqual } from "lodash-es";
6
+ //#region packages/editor/src/layouts/history-list/HistoryDiffDialog.vue?vue&type=script&setup=true&lang.ts
7
+ var _hoisted_1 = {
8
+ key: 0,
9
+ class: "m-editor-history-diff-dialog-body"
10
+ };
11
+ var _hoisted_2 = {
12
+ key: 0,
13
+ class: "m-editor-history-diff-dialog-notice"
14
+ };
15
+ var _hoisted_3 = { class: "m-editor-history-diff-dialog-header" };
16
+ var _hoisted_4 = { class: "m-editor-history-diff-dialog-target" };
17
+ var _hoisted_5 = { class: "m-editor-history-diff-dialog-controls" };
18
+ var _hoisted_6 = { class: "m-editor-history-diff-dialog-legend" };
19
+ var _hoisted_7 = {
20
+ key: 0,
21
+ class: "m-editor-history-diff-dialog-tip"
22
+ };
23
+ var HistoryDiffDialog_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
24
+ name: "MEditorHistoryDiffDialog",
25
+ __name: "HistoryDiffDialog",
26
+ props: {
27
+ extendState: {},
28
+ loadConfig: {},
29
+ width: { default: "900px" },
30
+ onConfirm: {},
31
+ selfDiffFieldTypes: {}
32
+ },
33
+ emits: ["close"],
34
+ setup(__props, { expose: __expose, emit: __emit }) {
35
+ const props = __props;
36
+ const emit = __emit;
37
+ /**
38
+ * 差异对比模式:
39
+ * - before:该步骤修改前 vs 该步骤修改后(默认行为,体现这一步带来的变化)
40
+ * - current:该步骤修改后 vs 当前最新值(用于查看「该步骤之后是否还被改过」)
41
+ */
42
+ const visible = ref(false);
43
+ const payload = ref(null);
44
+ const mode = ref("before");
45
+ const viewMode = ref("form");
46
+ /**
47
+ * 源码对比始终只读,关闭小地图,强制左右并排(side-by-side)展示。
48
+ *
49
+ * monaco diff 编辑器在宽度低于 `renderSideBySideInlineBreakpoint`(默认 900px)时
50
+ * 会自动退化为 inline(上下/单栏)视图。本弹窗宽度约 900px,去掉内边距后编辑器实际
51
+ * 宽度小于该阈值,会被切到 inline。这里通过 `useInlineViewWhenSpaceIsLimited: false`
52
+ * 关闭该自动降级,确保始终保持左右两栏对比。
53
+ */
54
+ const codeDiffOptions = {
55
+ readOnly: true,
56
+ tabSize: 2,
57
+ minimap: { enabled: false },
58
+ renderSideBySide: true,
59
+ useInlineViewWhenSpaceIsLimited: false,
60
+ scrollBeyondLastLine: false,
61
+ hideUnchangedRegions: { enabled: true }
62
+ };
63
+ const dialogTitle = computed(() => props.onConfirm ? "确认回滚" : "查看修改差异");
64
+ const hasCurrent = computed(() => payload.value?.currentValue !== void 0 && payload.value?.currentValue !== null);
65
+ /** 左侧(旧/参照)值 */
66
+ const leftValue = computed(() => {
67
+ if (!payload.value) return {};
68
+ if (mode.value === "current") return payload.value.value;
69
+ return payload.value.lastValue;
70
+ });
71
+ /** 右侧(新/对比)值 */
72
+ const rightValue = computed(() => {
73
+ if (!payload.value) return {};
74
+ if (mode.value === "current") return payload.value.currentValue || {};
75
+ return payload.value.value;
76
+ });
77
+ const leftLabel = computed(() => mode.value === "current" ? "该步修改后" : "修改前");
78
+ const rightLabel = computed(() => mode.value === "current" ? "当前" : "修改后");
79
+ /** 「与当前对比」模式下,若当前值与该步修改后值相等,则展示提示 */
80
+ const isSameAsCurrent = computed(() => {
81
+ if (mode.value !== "current" || !payload.value) return false;
82
+ return isEqual(payload.value.value, payload.value.currentValue);
83
+ });
84
+ const onConfirmClick = () => {
85
+ const cb = props.onConfirm;
86
+ cb?.();
87
+ visible.value = false;
88
+ };
89
+ const targetText = computed(() => {
90
+ if (!payload.value) return "";
91
+ const prefix = {
92
+ node: "节点",
93
+ "data-source": "数据源",
94
+ "code-block": "代码块"
95
+ }[payload.value.category] || "";
96
+ const label = payload.value.targetLabel || payload.value.type || "";
97
+ const { id } = payload.value;
98
+ return [prefix, id !== void 0 && id !== "" ? `${label}(${id})` : label].filter(Boolean).join(":");
99
+ });
100
+ const open = (p) => {
101
+ payload.value = p;
102
+ mode.value = "before";
103
+ viewMode.value = "form";
104
+ visible.value = true;
105
+ };
106
+ const close = () => {
107
+ visible.value = false;
108
+ };
109
+ watch(visible, (v) => {
110
+ if (!v) payload.value = null;
111
+ });
112
+ const onClose = () => {
113
+ emit("close");
114
+ };
115
+ __expose({
116
+ open,
117
+ close
118
+ });
119
+ return (_ctx, _cache) => {
120
+ return openBlock(), createBlock(Teleport, { to: "body" }, [createVNode(unref(TMagicDialog), {
121
+ modelValue: visible.value,
122
+ "onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => visible.value = $event),
123
+ class: "m-editor-history-diff-dialog",
124
+ title: dialogTitle.value,
125
+ top: "5vh",
126
+ "destroy-on-close": "",
127
+ "append-to-body": "",
128
+ width: __props.width,
129
+ onClose
130
+ }, {
131
+ footer: withCtx(() => [__props.onConfirm ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createVNode(unref(TMagicButton), {
132
+ size: "small",
133
+ onClick: _cache[2] || (_cache[2] = ($event) => visible.value = false)
134
+ }, {
135
+ default: withCtx(() => [..._cache[10] || (_cache[10] = [createTextVNode("取消", -1)])]),
136
+ _: 1
137
+ }), createVNode(unref(TMagicButton), {
138
+ size: "small",
139
+ type: "primary",
140
+ onClick: onConfirmClick
141
+ }, {
142
+ default: withCtx(() => [..._cache[11] || (_cache[11] = [createTextVNode("确定回滚", -1)])]),
143
+ _: 1
144
+ })], 64)) : (openBlock(), createBlock(unref(TMagicButton), {
145
+ key: 1,
146
+ size: "small",
147
+ onClick: _cache[3] || (_cache[3] = ($event) => visible.value = false)
148
+ }, {
149
+ default: withCtx(() => [..._cache[12] || (_cache[12] = [createTextVNode("关闭", -1)])]),
150
+ _: 1
151
+ }))]),
152
+ default: withCtx(() => [payload.value ? (openBlock(), createElementBlock("div", _hoisted_1, [
153
+ __props.onConfirm ? (openBlock(), createElementBlock("div", _hoisted_2, "仅回滚有差异的字段")) : createCommentVNode("v-if", true),
154
+ createElementVNode("div", _hoisted_3, [createElementVNode("span", _hoisted_4, toDisplayString(targetText.value), 1), createElementVNode("div", _hoisted_5, [createVNode(unref(TMagicRadioGroup), {
155
+ modelValue: viewMode.value,
156
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => viewMode.value = $event),
157
+ size: "small",
158
+ class: "m-editor-history-diff-dialog-view"
159
+ }, {
160
+ default: withCtx(() => [createVNode(unref(TMagicRadioButton), { value: "form" }, {
161
+ default: withCtx(() => [..._cache[5] || (_cache[5] = [createTextVNode("表单对比", -1)])]),
162
+ _: 1
163
+ }), createVNode(unref(TMagicRadioButton), { value: "code" }, {
164
+ default: withCtx(() => [..._cache[6] || (_cache[6] = [createTextVNode("源码对比", -1)])]),
165
+ _: 1
166
+ })]),
167
+ _: 1
168
+ }, 8, ["modelValue"]), createVNode(unref(TMagicRadioGroup), {
169
+ modelValue: mode.value,
170
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => mode.value = $event),
171
+ size: "small",
172
+ class: "m-editor-history-diff-dialog-mode"
173
+ }, {
174
+ default: withCtx(() => [createVNode(unref(TMagicRadioButton), { value: "before" }, {
175
+ default: withCtx(() => [..._cache[7] || (_cache[7] = [createTextVNode("与修改前对比", -1)])]),
176
+ _: 1
177
+ }), createVNode(unref(TMagicRadioButton), {
178
+ value: "current",
179
+ disabled: !hasCurrent.value
180
+ }, {
181
+ default: withCtx(() => [..._cache[8] || (_cache[8] = [createTextVNode("与当前对比", -1)])]),
182
+ _: 1
183
+ }, 8, ["disabled"])]),
184
+ _: 1
185
+ }, 8, ["modelValue"])])]),
186
+ createElementVNode("div", _hoisted_6, [
187
+ createVNode(unref(TMagicTag), {
188
+ size: "small",
189
+ type: "danger"
190
+ }, {
191
+ default: withCtx(() => [createTextVNode(toDisplayString(leftLabel.value), 1)]),
192
+ _: 1
193
+ }),
194
+ _cache[9] || (_cache[9] = createElementVNode("span", { class: "m-editor-history-diff-dialog-arrow" }, "→", -1)),
195
+ createVNode(unref(TMagicTag), {
196
+ size: "small",
197
+ type: "success"
198
+ }, {
199
+ default: withCtx(() => [createTextVNode(toDisplayString(rightLabel.value), 1)]),
200
+ _: 1
201
+ }),
202
+ mode.value === "current" && isSameAsCurrent.value ? (openBlock(), createElementBlock("span", _hoisted_7, " 当前值与该步修改后一致,无差异 ")) : createCommentVNode("v-if", true)
203
+ ]),
204
+ viewMode.value === "form" ? (openBlock(), createBlock(CompareForm_default, {
205
+ key: 1,
206
+ category: payload.value.category,
207
+ type: payload.value.type,
208
+ "data-source-type": payload.value.dataSourceType,
209
+ value: rightValue.value,
210
+ "last-value": leftValue.value,
211
+ "extend-state": __props.extendState,
212
+ "load-config": __props.loadConfig,
213
+ "self-diff-field-types": __props.selfDiffFieldTypes,
214
+ height: "70vh"
215
+ }, null, 8, [
216
+ "category",
217
+ "type",
218
+ "data-source-type",
219
+ "value",
220
+ "last-value",
221
+ "extend-state",
222
+ "load-config",
223
+ "self-diff-field-types"
224
+ ])) : (openBlock(), createBlock(CodeEditor_default, {
225
+ key: 2,
226
+ type: "diff",
227
+ language: "json",
228
+ "init-values": leftValue.value,
229
+ "modified-values": rightValue.value,
230
+ options: codeDiffOptions,
231
+ "disabled-full-screen": "",
232
+ height: "70vh"
233
+ }, null, 8, ["init-values", "modified-values"]))
234
+ ])) : createCommentVNode("v-if", true)]),
235
+ _: 1
236
+ }, 8, [
237
+ "modelValue",
238
+ "title",
239
+ "width"
240
+ ])]);
241
+ };
242
+ }
243
+ });
244
+ //#endregion
245
+ export { HistoryDiffDialog_vue_vue_type_script_setup_true_lang_default as default };
@@ -0,0 +1,5 @@
1
+ import HistoryListPanel_vue_vue_type_script_setup_true_lang_default from "./HistoryListPanel.vue_vue_type_script_setup_true_lang.js";
2
+ //#region packages/editor/src/layouts/history-list/HistoryListPanel.vue
3
+ var HistoryListPanel_default = HistoryListPanel_vue_vue_type_script_setup_true_lang_default;
4
+ //#endregion
5
+ export { HistoryListPanel_default as default };
@@ -0,0 +1,340 @@
1
+ import { useServices } from "../../hooks/use-services.js";
2
+ import Icon_default from "../../components/Icon.js";
3
+ import { describeCodeBlockGroup, describeCodeBlockStep, describeDataSourceGroup, describeDataSourceStep, useHistoryList } from "./composables.js";
4
+ import BucketTab_default from "./BucketTab.js";
5
+ import HistoryDiffDialog_default from "./HistoryDiffDialog.js";
6
+ import PageTab_default from "./PageTab.js";
7
+ import { TMagicButton, TMagicPopover, TMagicTabs, TMagicTooltip, getDesignConfig } from "@tmagic/design";
8
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, guardReactiveProps, inject, markRaw, mergeProps, normalizeProps, openBlock, ref, renderList, resolveDynamicComponent, shallowRef, toHandlers, unref, useTemplateRef, watch, withCtx } from "vue";
9
+ import { Clock, Close } from "@element-plus/icons-vue";
10
+ //#region packages/editor/src/layouts/history-list/HistoryListPanel.vue?vue&type=script&setup=true&lang.ts
11
+ var _hoisted_1 = { class: "m-editor-history-list" };
12
+ var HistoryListPanel_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
13
+ name: "MEditorHistoryListPanel",
14
+ __name: "HistoryListPanel",
15
+ setup(__props) {
16
+ /**
17
+ * 历史记录面板:在顶部 NavMenu 上点击图标打开 popover,分三个 tab:
18
+ * - 页面:当前活动页面的历史栈,连续修改同一节点的多步会被合并成一组
19
+ * - 数据源:以 dataSource.id 分组,每组内部相邻的连续 update 自动合并
20
+ * - 代码块:同上,按 codeBlock.id 分组并合并相邻 update
21
+ *
22
+ * 数据通过 historyService 暴露的聚合 API 读取,UI 仅用于只读展示,
23
+ * 同时支持点击任意一条记录跳转至该状态:
24
+ * - 页面 tab:调用 editorService.gotoPageStep(targetCursor)
25
+ * - 数据源 tab:调用 dataSourceService.goto(id, targetCursor)
26
+ * - 代码块 tab:调用 codeBlockService.goto(id, targetCursor)
27
+ *
28
+ * 这里的 targetCursor = 用户点击的 step.index + 1,即"应用至此步完成的状态"。
29
+ *
30
+ * 此外每条 step 上提供"查看差异"入口(仅在前后值都存在的 update 步骤显示),
31
+ * 点击后弹出 HistoryDiffDialog,使用 CompareForm 组件以表单形式展示新旧值差异。
32
+ *
33
+ * 各 tab 的内容拆分为独立的 SFC:页面用 PageTab,数据源 / 代码块复用通用的 BucketTab
34
+ * (通过 title / prefix / describe* / isStepDiffable 注入差异)。
35
+ * 共享的描述生成与折叠状态在 composables.ts 中维护。
36
+ */
37
+ const ClockIcon = markRaw(Clock);
38
+ const CloseIcon = markRaw(Close);
39
+ const activeTab = ref("page");
40
+ /** 面板显隐受控:reference 图标点击切换,右上角关闭按钮置为 false。 */
41
+ const visible = ref(false);
42
+ const tabPaneComponent = getDesignConfig("components")?.tabPane;
43
+ /**
44
+ * 业务方自定义的扩展 tab,由 Editor 顶层通过 `historyListExtraTabs` 注入。
45
+ * 追加在内置「页面 / 数据源 / 代码块」三个 tab 之后,未提供时为空数组。
46
+ */
47
+ const extraTabs = inject("historyListExtraTabs", []);
48
+ /** label 支持字符串或函数,函数形式便于展示动态数量等内容。 */
49
+ const resolveTabLabel = (tab) => typeof tab.label === "function" ? tab.label() : tab.label;
50
+ const { editorService, dataSourceService, codeBlockService, historyService, propsService } = useServices();
51
+ /**
52
+ * 数据源 / 代码块功能可被业务方通过 `disabledDataSource` / `disabledCodeBlock` 禁用,
53
+ * 禁用后对应的历史记录 tab 不再展示。若当前激活的 tab 恰好被禁用,则回退到「页面」tab。
54
+ */
55
+ const disabledDataSource = computed(() => propsService.getDisabledDataSource());
56
+ const disabledCodeBlock = computed(() => propsService.getDisabledCodeBlock());
57
+ watch([disabledDataSource, disabledCodeBlock], ([dsDisabled, cbDisabled]) => {
58
+ if (activeTab.value === "data-source" && dsDisabled || activeTab.value === "code-block" && cbDisabled) activeTab.value = "page";
59
+ });
60
+ /**
61
+ * 通过 inject 拿到 Editor 顶层注入的 `extendFormState`,转交给 HistoryDiffDialog
62
+ * 内部的 CompareForm,使差异对比表单的 filterFunction 能拿到完整的业务上下文。
63
+ * 未提供时为 undefined,CompareForm/MForm 会跳过 extendState 处理。
64
+ */
65
+ const extendFormState = inject("extendFormState", void 0);
66
+ const { expanded, toggleGroup, pageGroups, dataSourceGroups, codeBlockGroups, pageGroupsDisplay, dataSourceGroupsByTarget, codeBlockGroupsByTarget } = useHistoryList();
67
+ /** 数据源 step 仅 update(前后 schema 都存在)时可查看差异。 */
68
+ const isDataSourceStepDiffable = (step) => Boolean(step.oldSchema && step.newSchema);
69
+ /** 代码块 step 仅 update(前后 content 都存在)时可查看差异。 */
70
+ const isCodeBlockStepDiffable = (step) => Boolean(step.oldContent && step.newContent);
71
+ /** 把"目标 step 索引"翻译成"目标 cursor"(已应用步骤数量)。 */
72
+ const indexToCursor = (index) => index + 1;
73
+ const onPageGoto = (index) => {
74
+ editorService.gotoPageStep(indexToCursor(index));
75
+ };
76
+ const onDataSourceGoto = (id, index) => {
77
+ dataSourceService.goto(id, indexToCursor(index));
78
+ };
79
+ const onCodeBlockGoto = (id, index) => {
80
+ codeBlockService.goto(id, indexToCursor(index));
81
+ };
82
+ /**
83
+ * "回到初始状态" = 把对应栈 cursor 移到 0(全部已撤销)。
84
+ * 复用 service.goto*(0) 即可,所有真实 step 的反向应用由 service 层的 undo 链路完成。
85
+ */
86
+ const onPageGotoInitial = () => {
87
+ editorService.gotoPageStep(0);
88
+ };
89
+ const onDataSourceGotoInitial = (id) => {
90
+ dataSourceService.goto(id, 0);
91
+ };
92
+ const onCodeBlockGotoInitial = (id) => {
93
+ codeBlockService.goto(id, 0);
94
+ };
95
+ const diffDialogRef = useTemplateRef("diffDialog");
96
+ /**
97
+ * 构造页面 step 的差异弹窗入参:仅 update 单节点修改可对比,传入旧/新节点。
98
+ * 节点类型 `type` 优先取 newNode.type,再回退 oldNode.type。
99
+ * `currentValue` 取自 editorService 中该节点当前实际值,用于支持「与当前对比」。
100
+ * 无可对比内容(如多节点 / add / remove)时返回 null。
101
+ */
102
+ const buildPageDiffPayload = (index) => {
103
+ const groups = historyService.getPageHistoryGroups();
104
+ for (const group of groups) {
105
+ const entry = group.steps.find((s) => s.index === index);
106
+ if (!entry) continue;
107
+ const item = entry.step.updatedItems?.[0];
108
+ if (!item?.oldNode || !item?.newNode) return null;
109
+ const type = item.newNode.type || item.oldNode.type || "";
110
+ const nodeId = item.newNode.id ?? item.oldNode.id;
111
+ const currentNode = nodeId !== void 0 ? editorService.getNodeById(nodeId) : null;
112
+ return {
113
+ category: "node",
114
+ type,
115
+ lastValue: item.oldNode,
116
+ value: item.newNode,
117
+ currentValue: currentNode || null,
118
+ targetLabel: item.newNode.name || item.oldNode.name || type,
119
+ id: nodeId
120
+ };
121
+ }
122
+ return null;
123
+ };
124
+ /**
125
+ * 在指定分组列表中按 id / index 查找命中的 step,命中后交由 build 构造差异弹窗入参。
126
+ * 用于统一数据源、代码块两类历史的查找逻辑。
127
+ */
128
+ const findGroupStep = (groups, id, index, build) => {
129
+ for (const group of groups) {
130
+ if (group.id !== id) continue;
131
+ const entry = group.steps.find((s) => s.index === index);
132
+ if (!entry) continue;
133
+ return build(entry.step);
134
+ }
135
+ return null;
136
+ };
137
+ const buildDataSourceDiffPayload = (id, index) => findGroupStep(historyService.getDataSourceHistoryGroups(), id, index, ({ oldSchema, newSchema }) => {
138
+ if (!oldSchema || !newSchema) return null;
139
+ const currentSchema = dataSourceService.getDataSourceById(`${id}`);
140
+ return {
141
+ category: "data-source",
142
+ type: newSchema.type || oldSchema.type || "base",
143
+ lastValue: oldSchema,
144
+ value: newSchema,
145
+ currentValue: currentSchema || null,
146
+ targetLabel: newSchema.title || oldSchema.title || `${id}`,
147
+ id
148
+ };
149
+ });
150
+ const buildCodeBlockDiffPayload = (id, index) => findGroupStep(historyService.getCodeBlockHistoryGroups(), id, index, ({ oldContent, newContent }) => {
151
+ if (!oldContent || !newContent) return null;
152
+ return {
153
+ category: "code-block",
154
+ lastValue: oldContent,
155
+ value: newContent,
156
+ currentValue: codeBlockService.getCodeContentById(id) || null,
157
+ targetLabel: newContent.name || oldContent.name || `${id}`,
158
+ id
159
+ };
160
+ });
161
+ const onPageDiff = (index) => {
162
+ const payload = buildPageDiffPayload(index);
163
+ if (payload) diffDialogRef.value?.open(payload);
164
+ };
165
+ const onDataSourceDiff = (id, index) => {
166
+ const payload = buildDataSourceDiffPayload(id, index);
167
+ if (payload) diffDialogRef.value?.open(payload);
168
+ };
169
+ const onCodeBlockDiff = (id, index) => {
170
+ const payload = buildCodeBlockDiffPayload(id, index);
171
+ if (payload) diffDialogRef.value?.open(payload);
172
+ };
173
+ const onConfirmRevert = shallowRef();
174
+ /**
175
+ * 「回滚」入口:把目标历史步骤的修改作为一次新操作反向应用(类 git revert),
176
+ * 不破坏原有栈结构。各 service 内部完成反向 + 入栈,并自带描述用于面板展示。
177
+ *
178
+ * 交互:先弹出该步骤的差异弹窗供用户确认,点击「确定回滚」后再真正执行回滚;
179
+ * 对没有可对比内容的步骤(如 add / remove / 多节点更新)则直接回滚。
180
+ */
181
+ const onPageRevert = (index) => {
182
+ const payload = buildPageDiffPayload(index);
183
+ onConfirmRevert.value = () => editorService.revertPageStep(index);
184
+ if (payload) diffDialogRef.value?.open({ ...payload });
185
+ else onConfirmRevert.value();
186
+ };
187
+ const onDataSourceRevert = (id, index) => {
188
+ const payload = buildDataSourceDiffPayload(id, index);
189
+ onConfirmRevert.value = () => dataSourceService.revert(id, index);
190
+ if (payload) diffDialogRef.value?.open({ ...payload });
191
+ else onConfirmRevert.value();
192
+ };
193
+ const onCodeBlockRevert = (id, index) => {
194
+ const payload = buildCodeBlockDiffPayload(id, index);
195
+ onConfirmRevert.value = () => codeBlockService.revert(id, index);
196
+ if (payload) diffDialogRef.value?.open({ ...payload });
197
+ else onConfirmRevert.value();
198
+ };
199
+ const onDiffDialogClose = () => {
200
+ onConfirmRevert.value = void 0;
201
+ };
202
+ return (_ctx, _cache) => {
203
+ return openBlock(), createElementBlock(Fragment, null, [createVNode(unref(TMagicPopover), {
204
+ "popper-class": "m-editor-history-list-popover",
205
+ placement: "bottom",
206
+ trigger: "click",
207
+ visible: visible.value,
208
+ width: 660
209
+ }, {
210
+ reference: withCtx(() => [createVNode(unref(TMagicTooltip), {
211
+ effect: "dark",
212
+ placement: "bottom",
213
+ content: "历史记录"
214
+ }, {
215
+ default: withCtx(() => [createVNode(unref(TMagicButton), {
216
+ size: "small",
217
+ link: "",
218
+ onClick: _cache[2] || (_cache[2] = ($event) => visible.value = !visible.value)
219
+ }, {
220
+ icon: withCtx(() => [createVNode(Icon_default, { icon: unref(ClockIcon) }, null, 8, ["icon"])]),
221
+ _: 1
222
+ })]),
223
+ _: 1
224
+ })]),
225
+ default: withCtx(() => [createElementVNode("div", _hoisted_1, [createVNode(unref(TMagicTooltip), {
226
+ effect: "dark",
227
+ placement: "top",
228
+ content: "关闭"
229
+ }, {
230
+ default: withCtx(() => [createVNode(unref(TMagicButton), {
231
+ class: "m-editor-history-list-close",
232
+ size: "small",
233
+ link: "",
234
+ onClick: _cache[0] || (_cache[0] = ($event) => visible.value = false)
235
+ }, {
236
+ icon: withCtx(() => [createVNode(Icon_default, { icon: unref(CloseIcon) }, null, 8, ["icon"])]),
237
+ _: 1
238
+ })]),
239
+ _: 1
240
+ }), createVNode(unref(TMagicTabs), {
241
+ modelValue: activeTab.value,
242
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => activeTab.value = $event),
243
+ class: "m-editor-history-list-tabs"
244
+ }, {
245
+ default: withCtx(() => [
246
+ (openBlock(), createBlock(resolveDynamicComponent(unref(tabPaneComponent)?.component || "el-tab-pane"), normalizeProps(guardReactiveProps(unref(tabPaneComponent)?.props({
247
+ name: "page",
248
+ label: `页面 (${unref(pageGroups).length})`
249
+ }) || {})), {
250
+ default: withCtx(() => [createVNode(PageTab_default, {
251
+ list: unref(pageGroupsDisplay),
252
+ expanded: unref(expanded),
253
+ onToggle: unref(toggleGroup),
254
+ onGoto: onPageGoto,
255
+ onGotoInitial: onPageGotoInitial,
256
+ onDiffStep: onPageDiff,
257
+ onRevertStep: onPageRevert
258
+ }, null, 8, [
259
+ "list",
260
+ "expanded",
261
+ "onToggle"
262
+ ])]),
263
+ _: 1
264
+ }, 16)),
265
+ !disabledDataSource.value ? (openBlock(), createBlock(resolveDynamicComponent(unref(tabPaneComponent)?.component || "el-tab-pane"), normalizeProps(mergeProps({ key: 0 }, unref(tabPaneComponent)?.props({
266
+ name: "data-source",
267
+ label: `数据源 (${unref(dataSourceGroups).length})`
268
+ }) || {})), {
269
+ default: withCtx(() => [createVNode(BucketTab_default, {
270
+ title: "数据源",
271
+ prefix: "ds",
272
+ buckets: unref(dataSourceGroupsByTarget),
273
+ expanded: unref(expanded),
274
+ "describe-group": unref(describeDataSourceGroup),
275
+ "describe-step": unref(describeDataSourceStep),
276
+ "is-step-diffable": isDataSourceStepDiffable,
277
+ onToggle: unref(toggleGroup),
278
+ onGoto: onDataSourceGoto,
279
+ onGotoInitial: onDataSourceGotoInitial,
280
+ onDiffStep: onDataSourceDiff,
281
+ onRevertStep: onDataSourceRevert
282
+ }, null, 8, [
283
+ "buckets",
284
+ "expanded",
285
+ "describe-group",
286
+ "describe-step",
287
+ "onToggle"
288
+ ])]),
289
+ _: 1
290
+ }, 16)) : createCommentVNode("v-if", true),
291
+ !disabledCodeBlock.value ? (openBlock(), createBlock(resolveDynamicComponent(unref(tabPaneComponent)?.component || "el-tab-pane"), normalizeProps(mergeProps({ key: 1 }, unref(tabPaneComponent)?.props({
292
+ name: "code-block",
293
+ label: `代码块 (${unref(codeBlockGroups).length})`
294
+ }) || {})), {
295
+ default: withCtx(() => [createVNode(BucketTab_default, {
296
+ title: "代码块",
297
+ prefix: "cb",
298
+ buckets: unref(codeBlockGroupsByTarget),
299
+ expanded: unref(expanded),
300
+ "describe-group": unref(describeCodeBlockGroup),
301
+ "describe-step": unref(describeCodeBlockStep),
302
+ "is-step-diffable": isCodeBlockStepDiffable,
303
+ onToggle: unref(toggleGroup),
304
+ onGoto: onCodeBlockGoto,
305
+ onGotoInitial: onCodeBlockGotoInitial,
306
+ onDiffStep: onCodeBlockDiff,
307
+ onRevertStep: onCodeBlockRevert
308
+ }, null, 8, [
309
+ "buckets",
310
+ "expanded",
311
+ "describe-group",
312
+ "describe-step",
313
+ "onToggle"
314
+ ])]),
315
+ _: 1
316
+ }, 16)) : createCommentVNode("v-if", true),
317
+ (openBlock(true), createElementBlock(Fragment, null, renderList(unref(extraTabs), (tab) => {
318
+ return openBlock(), createBlock(resolveDynamicComponent(unref(tabPaneComponent)?.component || "el-tab-pane"), mergeProps({ key: tab.name }, { ref_for: true }, unref(tabPaneComponent)?.props({
319
+ name: tab.name,
320
+ label: resolveTabLabel(tab)
321
+ }) || {}), {
322
+ default: withCtx(() => [(openBlock(), createBlock(resolveDynamicComponent(tab.component), mergeProps({ ref_for: true }, tab.props || {}, toHandlers(tab.listeners || {})), null, 16))]),
323
+ _: 2
324
+ }, 1040);
325
+ }), 128))
326
+ ]),
327
+ _: 1
328
+ }, 8, ["modelValue"])])]),
329
+ _: 1
330
+ }, 8, ["visible"]), createVNode(HistoryDiffDialog_default, {
331
+ ref: "diffDialog",
332
+ "extend-state": unref(extendFormState),
333
+ "on-confirm": onConfirmRevert.value,
334
+ onClose: onDiffDialogClose
335
+ }, null, 8, ["extend-state", "on-confirm"])], 64);
336
+ };
337
+ }
338
+ });
339
+ //#endregion
340
+ export { HistoryListPanel_vue_vue_type_script_setup_true_lang_default as default };
@@ -0,0 +1,5 @@
1
+ import InitialRow_vue_vue_type_script_setup_true_lang_default from "./InitialRow.vue_vue_type_script_setup_true_lang.js";
2
+ //#region packages/editor/src/layouts/history-list/InitialRow.vue
3
+ var InitialRow_default = InitialRow_vue_vue_type_script_setup_true_lang_default;
4
+ //#endregion
5
+ export { InitialRow_default as default };
@@ -0,0 +1,54 @@
1
+ import { createCommentVNode, createElementBlock, createElementVNode, defineComponent, normalizeClass, openBlock, withModifiers } from "vue";
2
+ //#region packages/editor/src/layouts/history-list/InitialRow.vue?vue&type=script&setup=true&lang.ts
3
+ var _hoisted_1 = ["title"];
4
+ var InitialRow_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
5
+ name: "MEditorHistoryListInitialRow",
6
+ __name: "InitialRow",
7
+ props: {
8
+ isCurrent: { type: Boolean },
9
+ gotoEnabled: {
10
+ type: Boolean,
11
+ default: true
12
+ }
13
+ },
14
+ emits: ["goto-initial"],
15
+ setup(__props, { emit: __emit }) {
16
+ /**
17
+ * 「初始状态」记录行:渲染于历史列表底部,作为整个栈的"零点"。
18
+ * - 点击该行会把对应栈撤销到 cursor === 0(即没有任何已应用步骤),等同于回到所有修改之前。
19
+ * - 当对应栈本身已处于 cursor === 0 时(isCurrent=true),用户已在初始状态,点击不再触发动作。
20
+ *
21
+ * 该行不是真实 step,仅作为 UI 入口;上层负责把"点击"翻译为 `service.goto*(0)`。
22
+ */
23
+ const props = __props;
24
+ const emit = __emit;
25
+ const onClick = () => {
26
+ if (props.isCurrent) return;
27
+ emit("goto-initial");
28
+ };
29
+ return (_ctx, _cache) => {
30
+ return openBlock(), createElementBlock("li", {
31
+ class: normalizeClass(["m-editor-history-list-item m-editor-history-list-initial", {
32
+ "is-current": __props.isCurrent,
33
+ "is-clickable": !__props.isCurrent
34
+ }]),
35
+ title: __props.isCurrent ? "当前已回到未修改的初始状态" : "点击回到未修改的初始状态"
36
+ }, [
37
+ _cache[0] || (_cache[0] = createElementVNode("span", {
38
+ class: "m-editor-history-list-item-index",
39
+ title: "历史步骤编号 #0(未修改的初始状态)"
40
+ }, "#0", -1)),
41
+ _cache[1] || (_cache[1] = createElementVNode("span", { class: "m-editor-history-list-item-op op-initial" }, "初始", -1)),
42
+ _cache[2] || (_cache[2] = createElementVNode("span", { class: "m-editor-history-list-item-desc" }, "未修改的初始状态", -1)),
43
+ __props.gotoEnabled && !__props.isCurrent ? (openBlock(), createElementBlock("span", {
44
+ key: 0,
45
+ class: "m-editor-history-list-item-goto",
46
+ title: "回到该记录",
47
+ onClick: withModifiers(onClick, ["stop"])
48
+ }, "回到")) : createCommentVNode("v-if", true)
49
+ ], 10, _hoisted_1);
50
+ };
51
+ }
52
+ });
53
+ //#endregion
54
+ export { InitialRow_vue_vue_type_script_setup_true_lang_default as default };
@@ -0,0 +1,5 @@
1
+ import PageTab_vue_vue_type_script_setup_true_lang_default from "./PageTab.vue_vue_type_script_setup_true_lang.js";
2
+ //#region packages/editor/src/layouts/history-list/PageTab.vue
3
+ var PageTab_default = PageTab_vue_vue_type_script_setup_true_lang_default;
4
+ //#endregion
5
+ export { PageTab_default as default };