@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
|
@@ -41,11 +41,15 @@ const createMenuItems = (group: ComponentGroup): MenuButton[] =>
|
|
|
41
41
|
type: 'button',
|
|
42
42
|
icon: component.icon,
|
|
43
43
|
handler: () => {
|
|
44
|
-
editorService.add(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
editorService.add(
|
|
45
|
+
{
|
|
46
|
+
name: component.text,
|
|
47
|
+
type: component.type,
|
|
48
|
+
...(component.data || {}),
|
|
49
|
+
},
|
|
50
|
+
undefined,
|
|
51
|
+
{ historySource: 'tree-contextmenu' },
|
|
52
|
+
);
|
|
49
53
|
},
|
|
50
54
|
}));
|
|
51
55
|
|
|
@@ -57,9 +61,13 @@ const getSubMenuData = computed<MenuButton[]>(() => {
|
|
|
57
61
|
type: 'button',
|
|
58
62
|
icon: Files,
|
|
59
63
|
handler: () => {
|
|
60
|
-
editorService.add(
|
|
61
|
-
|
|
62
|
-
|
|
64
|
+
editorService.add(
|
|
65
|
+
{
|
|
66
|
+
type: 'tab-pane',
|
|
67
|
+
},
|
|
68
|
+
undefined,
|
|
69
|
+
{ historySource: 'tree-contextmenu' },
|
|
70
|
+
);
|
|
63
71
|
},
|
|
64
72
|
},
|
|
65
73
|
];
|
|
@@ -106,9 +114,9 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() =>
|
|
|
106
114
|
items: getSubMenuData.value,
|
|
107
115
|
},
|
|
108
116
|
useCopyMenu(),
|
|
109
|
-
usePasteMenu(),
|
|
110
|
-
useDeleteMenu(),
|
|
111
|
-
useMoveToMenu(services),
|
|
117
|
+
usePasteMenu('tree-contextmenu'),
|
|
118
|
+
useDeleteMenu('tree-contextmenu'),
|
|
119
|
+
useMoveToMenu(services, 'tree-contextmenu'),
|
|
112
120
|
...props.layerContentMenu,
|
|
113
121
|
],
|
|
114
122
|
'layer',
|
|
@@ -25,9 +25,12 @@ const props = defineProps<{
|
|
|
25
25
|
const { editorService } = useServices();
|
|
26
26
|
|
|
27
27
|
const setNodeVisible = (visible: boolean) => {
|
|
28
|
-
editorService.update(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
editorService.update(
|
|
29
|
+
{
|
|
30
|
+
id: props.data.id,
|
|
31
|
+
visible,
|
|
32
|
+
},
|
|
33
|
+
{ historySource: 'tree' },
|
|
34
|
+
);
|
|
32
35
|
};
|
|
33
36
|
</script>
|
|
@@ -49,11 +49,11 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() =>
|
|
|
49
49
|
display: () => canCenter.value,
|
|
50
50
|
handler: () => {
|
|
51
51
|
if (!nodes.value) return;
|
|
52
|
-
editorService.alignCenter(nodes.value);
|
|
52
|
+
editorService.alignCenter(nodes.value, { historySource: 'stage-contextmenu' });
|
|
53
53
|
},
|
|
54
54
|
},
|
|
55
55
|
useCopyMenu(),
|
|
56
|
-
usePasteMenu(menuRef),
|
|
56
|
+
usePasteMenu('stage-contextmenu', menuRef),
|
|
57
57
|
{
|
|
58
58
|
type: 'divider',
|
|
59
59
|
direction: 'horizontal',
|
|
@@ -68,7 +68,7 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() =>
|
|
|
68
68
|
icon: markRaw(Top),
|
|
69
69
|
display: () => !isPage(node.value) && !isPageFragment(node.value) && !props.isMultiSelect,
|
|
70
70
|
handler: () => {
|
|
71
|
-
editorService.moveLayer(1);
|
|
71
|
+
editorService.moveLayer(1, { historySource: 'stage-contextmenu' });
|
|
72
72
|
},
|
|
73
73
|
},
|
|
74
74
|
{
|
|
@@ -77,7 +77,7 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() =>
|
|
|
77
77
|
icon: markRaw(Bottom),
|
|
78
78
|
display: () => !isPage(node.value) && !isPageFragment(node.value) && !props.isMultiSelect,
|
|
79
79
|
handler: () => {
|
|
80
|
-
editorService.moveLayer(-1);
|
|
80
|
+
editorService.moveLayer(-1, { historySource: 'stage-contextmenu' });
|
|
81
81
|
},
|
|
82
82
|
},
|
|
83
83
|
{
|
|
@@ -86,7 +86,7 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() =>
|
|
|
86
86
|
icon: markRaw(Top),
|
|
87
87
|
display: () => !isPage(node.value) && !isPageFragment(node.value) && !props.isMultiSelect,
|
|
88
88
|
handler: () => {
|
|
89
|
-
editorService.moveLayer(LayerOffset.TOP);
|
|
89
|
+
editorService.moveLayer(LayerOffset.TOP, { historySource: 'stage-contextmenu' });
|
|
90
90
|
},
|
|
91
91
|
},
|
|
92
92
|
{
|
|
@@ -95,16 +95,16 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() =>
|
|
|
95
95
|
icon: markRaw(Bottom),
|
|
96
96
|
display: () => !isPage(node.value) && !isPageFragment(node.value) && !props.isMultiSelect,
|
|
97
97
|
handler: () => {
|
|
98
|
-
editorService.moveLayer(LayerOffset.BOTTOM);
|
|
98
|
+
editorService.moveLayer(LayerOffset.BOTTOM, { historySource: 'stage-contextmenu' });
|
|
99
99
|
},
|
|
100
100
|
},
|
|
101
|
-
useMoveToMenu(services),
|
|
101
|
+
useMoveToMenu(services, 'stage-contextmenu'),
|
|
102
102
|
{
|
|
103
103
|
type: 'divider',
|
|
104
104
|
direction: 'horizontal',
|
|
105
105
|
display: () => !isPage(node.value) && !isPageFragment(node.value) && !props.isMultiSelect,
|
|
106
106
|
},
|
|
107
|
-
useDeleteMenu(),
|
|
107
|
+
useDeleteMenu('stage-contextmenu'),
|
|
108
108
|
{
|
|
109
109
|
type: 'divider',
|
|
110
110
|
direction: 'horizontal',
|
|
@@ -38,6 +38,7 @@ import type {
|
|
|
38
38
|
import { CODE_DRAFT_STORAGE_KEY } from '@editor/type';
|
|
39
39
|
import { getEditorConfig } from '@editor/utils/config';
|
|
40
40
|
import { COPY_CODE_STORAGE_KEY } from '@editor/utils/editor';
|
|
41
|
+
import { describeRevertStep } from '@editor/utils/history';
|
|
41
42
|
|
|
42
43
|
import BaseService from './BaseService';
|
|
43
44
|
|
|
@@ -48,18 +49,6 @@ const canUsePluginMethods = {
|
|
|
48
49
|
|
|
49
50
|
type AsyncMethodName = Writable<(typeof canUsePluginMethods)['async']>;
|
|
50
51
|
|
|
51
|
-
/**
|
|
52
|
-
* 「回滚」生成的新 step 简短描述。仅 service 层使用。
|
|
53
|
-
*/
|
|
54
|
-
const describeRevertCodeBlockStep = (step: CodeBlockStepValue): string => {
|
|
55
|
-
const { oldContent, newContent, changeRecords, id } = step;
|
|
56
|
-
if (oldContent === null && newContent) return `撤回新增 ${newContent.name || newContent.id || id}`;
|
|
57
|
-
if (oldContent && newContent === null) return `还原已删除的 ${oldContent.name || oldContent.id || id}`;
|
|
58
|
-
const name = newContent?.name || oldContent?.name || `${id}`;
|
|
59
|
-
const propPath = changeRecords?.[0]?.propPath;
|
|
60
|
-
return propPath ? `还原 ${name} · ${propPath}` : `还原 ${name}`;
|
|
61
|
-
};
|
|
62
|
-
|
|
63
52
|
class CodeBlock extends BaseService {
|
|
64
53
|
private state = reactive<CodeState>({
|
|
65
54
|
codeDsl: null,
|
|
@@ -69,6 +58,17 @@ class CodeBlock extends BaseService {
|
|
|
69
58
|
paramsColConfig: undefined,
|
|
70
59
|
});
|
|
71
60
|
|
|
61
|
+
/**
|
|
62
|
+
* 最近一次写入历史栈的代码块历史记录 uuid(单条写入场景:新增 / 更新)。
|
|
63
|
+
* 供 setCodeDslById(Sync)AndGetHistoryId 取回,普通方法不读取它。
|
|
64
|
+
*/
|
|
65
|
+
private lastPushedHistoryId: string | null = null;
|
|
66
|
+
/**
|
|
67
|
+
* deleteCodeDslByIds 一次删除多个代码块时,按写入顺序收集的历史记录 uuid 列表。
|
|
68
|
+
* 在 deleteCodeDslByIds 入口处重置,供 deleteCodeDslByIdsAndGetHistoryId 取回。
|
|
69
|
+
*/
|
|
70
|
+
private lastDeletedHistoryIds: string[] = [];
|
|
71
|
+
|
|
72
72
|
constructor() {
|
|
73
73
|
super([
|
|
74
74
|
...canUsePluginMethods.async.map((methodName) => ({ name: methodName, isAsync: true })),
|
|
@@ -120,9 +120,19 @@ class CodeBlock extends BaseService {
|
|
|
120
120
|
public async setCodeDslById(
|
|
121
121
|
id: Id,
|
|
122
122
|
codeConfig: Partial<CodeBlockContent>,
|
|
123
|
-
{
|
|
123
|
+
{
|
|
124
|
+
changeRecords,
|
|
125
|
+
doNotPushHistory = false,
|
|
126
|
+
historyDescription,
|
|
127
|
+
historySource,
|
|
128
|
+
}: HistoryOpOptionsWithChangeRecords = {},
|
|
124
129
|
): Promise<void> {
|
|
125
|
-
this.setCodeDslByIdSync(id, codeConfig, true, {
|
|
130
|
+
this.setCodeDslByIdSync(id, codeConfig, true, {
|
|
131
|
+
changeRecords,
|
|
132
|
+
doNotPushHistory,
|
|
133
|
+
historyDescription,
|
|
134
|
+
historySource,
|
|
135
|
+
});
|
|
126
136
|
}
|
|
127
137
|
|
|
128
138
|
/**
|
|
@@ -141,7 +151,12 @@ class CodeBlock extends BaseService {
|
|
|
141
151
|
id: Id,
|
|
142
152
|
codeConfig: Partial<CodeBlockContent>,
|
|
143
153
|
force = true,
|
|
144
|
-
{
|
|
154
|
+
{
|
|
155
|
+
changeRecords,
|
|
156
|
+
doNotPushHistory = false,
|
|
157
|
+
historyDescription,
|
|
158
|
+
historySource,
|
|
159
|
+
}: HistoryOpOptionsWithChangeRecords = {},
|
|
145
160
|
): void {
|
|
146
161
|
const codeDsl = this.getCodeDsl();
|
|
147
162
|
|
|
@@ -172,7 +187,14 @@ class CodeBlock extends BaseService {
|
|
|
172
187
|
const newContent = cloneDeep(codeDsl[id]);
|
|
173
188
|
|
|
174
189
|
if (!doNotPushHistory) {
|
|
175
|
-
|
|
190
|
+
this.lastPushedHistoryId =
|
|
191
|
+
historyService.pushCodeBlock(id, {
|
|
192
|
+
oldContent,
|
|
193
|
+
newContent,
|
|
194
|
+
changeRecords,
|
|
195
|
+
historyDescription,
|
|
196
|
+
source: historySource,
|
|
197
|
+
})?.uuid ?? null;
|
|
176
198
|
}
|
|
177
199
|
|
|
178
200
|
this.emit('addOrUpdate', id, codeDsl[id]);
|
|
@@ -268,12 +290,14 @@ class CodeBlock extends BaseService {
|
|
|
268
290
|
*/
|
|
269
291
|
public async deleteCodeDslByIds(
|
|
270
292
|
codeIds: Id[],
|
|
271
|
-
{ doNotPushHistory = false, historyDescription }: HistoryOpOptions = {},
|
|
293
|
+
{ doNotPushHistory = false, historyDescription, historySource }: HistoryOpOptions = {},
|
|
272
294
|
): Promise<void> {
|
|
273
295
|
const currentDsl = await this.getCodeDsl();
|
|
274
296
|
|
|
275
297
|
if (!currentDsl) return;
|
|
276
298
|
|
|
299
|
+
this.lastDeletedHistoryIds = [];
|
|
300
|
+
|
|
277
301
|
codeIds.forEach((id) => {
|
|
278
302
|
// 历史记录:删除前快照内容;不存在的 id 直接跳过历史推入
|
|
279
303
|
const oldContent: CodeBlockContent | null = currentDsl[id] ? cloneDeep(currentDsl[id]) : null;
|
|
@@ -281,13 +305,62 @@ class CodeBlock extends BaseService {
|
|
|
281
305
|
delete currentDsl[id];
|
|
282
306
|
|
|
283
307
|
if (oldContent && !doNotPushHistory) {
|
|
284
|
-
historyService.pushCodeBlock(id, {
|
|
308
|
+
const uuid = historyService.pushCodeBlock(id, {
|
|
309
|
+
oldContent,
|
|
310
|
+
newContent: null,
|
|
311
|
+
historyDescription,
|
|
312
|
+
source: historySource,
|
|
313
|
+
})?.uuid;
|
|
314
|
+
if (uuid) this.lastDeletedHistoryIds.push(uuid);
|
|
285
315
|
}
|
|
286
316
|
|
|
287
317
|
this.emit('remove', id);
|
|
288
318
|
});
|
|
289
319
|
}
|
|
290
320
|
|
|
321
|
+
// #region AndGetHistoryId
|
|
322
|
+
/**
|
|
323
|
+
* 下列 *AndGetHistoryId 方法与对应的写入方法行为完全一致,
|
|
324
|
+
* 唯一区别是返回值为本次写入历史栈的历史记录 uuid({@link CodeBlockStepValue.uuid}),
|
|
325
|
+
* 可用于精确引用 / 定位该条历史记录(埋点、revert、跨端同步等)。
|
|
326
|
+
*
|
|
327
|
+
* 当本次操作未写入历史(doNotPushHistory 为 true、或无对应记录)时:单条写入返回 null,批量删除返回空数组。
|
|
328
|
+
*/
|
|
329
|
+
|
|
330
|
+
/** 等价于 {@link setCodeDslById},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
|
|
331
|
+
public async setCodeDslByIdAndGetHistoryId(
|
|
332
|
+
id: Id,
|
|
333
|
+
codeConfig: Partial<CodeBlockContent>,
|
|
334
|
+
options: HistoryOpOptionsWithChangeRecords = {},
|
|
335
|
+
): Promise<string | null> {
|
|
336
|
+
this.lastPushedHistoryId = null;
|
|
337
|
+
await this.setCodeDslById(id, codeConfig, options);
|
|
338
|
+
return this.lastPushedHistoryId;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/** 等价于 {@link setCodeDslByIdSync},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
|
|
342
|
+
public setCodeDslByIdSyncAndGetHistoryId(
|
|
343
|
+
id: Id,
|
|
344
|
+
codeConfig: Partial<CodeBlockContent>,
|
|
345
|
+
force = true,
|
|
346
|
+
options: HistoryOpOptionsWithChangeRecords = {},
|
|
347
|
+
): string | null {
|
|
348
|
+
this.lastPushedHistoryId = null;
|
|
349
|
+
this.setCodeDslByIdSync(id, codeConfig, force, options);
|
|
350
|
+
return this.lastPushedHistoryId;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* 等价于 {@link deleteCodeDslByIds},但返回本次写入的全部历史记录 uuid(按删除顺序)。
|
|
355
|
+
* 一次删除多个代码块会产生多条历史记录,因此返回数组;未写入任何历史时返回空数组。
|
|
356
|
+
*/
|
|
357
|
+
public async deleteCodeDslByIdsAndGetHistoryId(codeIds: Id[], options: HistoryOpOptions = {}): Promise<string[]> {
|
|
358
|
+
this.lastDeletedHistoryIds = [];
|
|
359
|
+
await this.deleteCodeDslByIds(codeIds, options);
|
|
360
|
+
return [...this.lastDeletedHistoryIds];
|
|
361
|
+
}
|
|
362
|
+
// #endregion AndGetHistoryId
|
|
363
|
+
|
|
291
364
|
public setParamsColConfig(config: TableColumnConfig): void {
|
|
292
365
|
this.state.paramsColConfig = config;
|
|
293
366
|
}
|
|
@@ -373,10 +446,28 @@ class CodeBlock extends BaseService {
|
|
|
373
446
|
const list = historyService.getCodeBlockStepList(id);
|
|
374
447
|
const entry = list[index];
|
|
375
448
|
if (!entry?.applied) return null;
|
|
376
|
-
|
|
449
|
+
// 更新类步骤(前后 content 都存在)必须带 changeRecords 才支持回滚,否则只能整内容替换,会冲掉后续无关变更。
|
|
450
|
+
const { oldSchema, newSchema, changeRecords } = entry.step.diff?.[0] ?? {};
|
|
451
|
+
|
|
452
|
+
if (oldSchema && newSchema && !changeRecords?.length) return null;
|
|
453
|
+
const description = `回滚 #${index + 1}: ${describeRevertStep<CodeBlockContent>(entry.step.id, entry.step.diff?.[0], (s) => s.name)}`;
|
|
377
454
|
return await this.applyRevertStep(entry.step, description);
|
|
378
455
|
}
|
|
379
456
|
|
|
457
|
+
/**
|
|
458
|
+
* 通过历史记录 uuid 回滚某条代码块历史步骤,语义同 {@link revert},
|
|
459
|
+
* 仅无需调用方再传 codeBlockId 与 index:内部会按 uuid({@link CodeBlockStepValue.uuid})
|
|
460
|
+
* 在全部代码块栈中定位对应步骤后再回滚。
|
|
461
|
+
*
|
|
462
|
+
* @param uuid 目标历史记录的 uuid,通常由 {@link setCodeDslByIdAndGetHistoryId} 等方法返回
|
|
463
|
+
* @returns 反向后产生的新 step;找不到对应 uuid / 未应用时返回 null
|
|
464
|
+
*/
|
|
465
|
+
public async revertById(uuid: string): Promise<CodeBlockStepValue | null> {
|
|
466
|
+
const location = historyService.findCodeBlockStepLocationByUuid(uuid);
|
|
467
|
+
if (!location) return null;
|
|
468
|
+
return await this.revert(location.id, location.index);
|
|
469
|
+
}
|
|
470
|
+
|
|
380
471
|
/**
|
|
381
472
|
* 生成代码块唯一id
|
|
382
473
|
* @returns {Id} 代码块唯一id
|
|
@@ -467,21 +558,22 @@ class CodeBlock extends BaseService {
|
|
|
467
558
|
step: CodeBlockStepValue,
|
|
468
559
|
historyDescription: string,
|
|
469
560
|
): Promise<CodeBlockStepValue | null> {
|
|
470
|
-
const { id
|
|
561
|
+
const { id } = step;
|
|
562
|
+
const { oldSchema, newSchema, changeRecords } = step.diff?.[0] ?? {};
|
|
471
563
|
|
|
472
564
|
// 原本是新增 → revert 即删除
|
|
473
|
-
if (
|
|
474
|
-
await this.deleteCodeDslByIds([id], { historyDescription });
|
|
565
|
+
if (!oldSchema && newSchema) {
|
|
566
|
+
await this.deleteCodeDslByIds([id], { historyDescription, historySource: 'rollback' });
|
|
475
567
|
return historyService.getCodeBlockStepList(id).slice(-1)[0]?.step ?? null;
|
|
476
568
|
}
|
|
477
569
|
|
|
478
570
|
// 原本是删除 → revert 即写回
|
|
479
|
-
if (
|
|
480
|
-
this.setCodeDslByIdSync(id, cloneDeep(
|
|
571
|
+
if (oldSchema && !newSchema) {
|
|
572
|
+
this.setCodeDslByIdSync(id, cloneDeep(oldSchema), true, { historyDescription, historySource: 'rollback' });
|
|
481
573
|
return historyService.getCodeBlockStepList(id).slice(-1)[0]?.step ?? null;
|
|
482
574
|
}
|
|
483
575
|
|
|
484
|
-
if (!
|
|
576
|
+
if (!oldSchema || !newSchema) return null;
|
|
485
577
|
|
|
486
578
|
// 原本是更新 → 把 oldContent 写回;优先按 changeRecords 局部 patch
|
|
487
579
|
if (changeRecords?.length) {
|
|
@@ -494,17 +586,18 @@ class CodeBlock extends BaseService {
|
|
|
494
586
|
fallbackToFullReplace = true;
|
|
495
587
|
break;
|
|
496
588
|
}
|
|
497
|
-
const value = cloneDeep(getValueByKeyPath(record.propPath,
|
|
589
|
+
const value = cloneDeep(getValueByKeyPath(record.propPath, oldSchema));
|
|
498
590
|
setValueByKeyPath(record.propPath, value, patched);
|
|
499
591
|
}
|
|
500
|
-
this.setCodeDslByIdSync(id, fallbackToFullReplace ? cloneDeep(
|
|
592
|
+
this.setCodeDslByIdSync(id, fallbackToFullReplace ? cloneDeep(oldSchema) : patched, true, {
|
|
501
593
|
changeRecords,
|
|
502
594
|
historyDescription,
|
|
595
|
+
historySource: 'rollback',
|
|
503
596
|
});
|
|
504
597
|
return historyService.getCodeBlockStepList(id).slice(-1)[0]?.step ?? null;
|
|
505
598
|
}
|
|
506
599
|
|
|
507
|
-
this.setCodeDslByIdSync(id, cloneDeep(
|
|
600
|
+
this.setCodeDslByIdSync(id, cloneDeep(oldSchema), true, { historyDescription, historySource: 'rollback' });
|
|
508
601
|
return historyService.getCodeBlockStepList(id).slice(-1)[0]?.step ?? null;
|
|
509
602
|
}
|
|
510
603
|
|
|
@@ -523,31 +616,32 @@ class CodeBlock extends BaseService {
|
|
|
523
616
|
* @param reverse true=撤销,false=重做
|
|
524
617
|
*/
|
|
525
618
|
private async applyHistoryStep(step: CodeBlockStepValue, reverse: boolean): Promise<void> {
|
|
526
|
-
const { id
|
|
619
|
+
const { id } = step;
|
|
620
|
+
const { oldSchema, newSchema, changeRecords } = step.diff?.[0] ?? {};
|
|
527
621
|
|
|
528
622
|
// 新增 / 删除:直接 set 或 delete,不走 patch 逻辑
|
|
529
|
-
if (
|
|
623
|
+
if (!oldSchema && newSchema) {
|
|
530
624
|
if (reverse) {
|
|
531
625
|
await this.deleteCodeDslByIds([id], { doNotPushHistory: true });
|
|
532
626
|
} else {
|
|
533
|
-
this.setCodeDslByIdSync(id, cloneDeep(
|
|
627
|
+
this.setCodeDslByIdSync(id, cloneDeep(newSchema), true, { doNotPushHistory: true });
|
|
534
628
|
}
|
|
535
629
|
return;
|
|
536
630
|
}
|
|
537
631
|
|
|
538
|
-
if (
|
|
632
|
+
if (oldSchema && !newSchema) {
|
|
539
633
|
if (reverse) {
|
|
540
|
-
this.setCodeDslByIdSync(id, cloneDeep(
|
|
634
|
+
this.setCodeDslByIdSync(id, cloneDeep(oldSchema), true, { doNotPushHistory: true });
|
|
541
635
|
} else {
|
|
542
636
|
await this.deleteCodeDslByIds([id], { doNotPushHistory: true });
|
|
543
637
|
}
|
|
544
638
|
return;
|
|
545
639
|
}
|
|
546
640
|
|
|
547
|
-
if (!
|
|
641
|
+
if (!oldSchema || !newSchema) return;
|
|
548
642
|
|
|
549
643
|
// 更新场景:优先按 changeRecords 局部 patch;缺省退化为整内容替换
|
|
550
|
-
const sourceForValues = reverse ?
|
|
644
|
+
const sourceForValues = reverse ? oldSchema : newSchema;
|
|
551
645
|
|
|
552
646
|
if (changeRecords?.length) {
|
|
553
647
|
const current = this.getCodeContentById(id);
|