@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
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
position: absolute;
|
|
14
14
|
top: 4px;
|
|
15
15
|
right: 4px;
|
|
16
|
-
z-index:
|
|
16
|
+
z-index: 2;
|
|
17
17
|
display: flex;
|
|
18
18
|
align-items: center;
|
|
19
19
|
height: 40px;
|
|
@@ -45,6 +45,28 @@
|
|
|
45
45
|
list-style: none;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
// 历史列表工具条:放置「清空」等列表级操作,右对齐。
|
|
49
|
+
.m-editor-history-list-toolbar {
|
|
50
|
+
display: flex;
|
|
51
|
+
align-items: center;
|
|
52
|
+
justify-content: flex-end;
|
|
53
|
+
padding: 0 4px 4px;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 「清空」按钮:红色文字按钮,强调破坏性操作(点击后会二次确认)。
|
|
57
|
+
.m-editor-history-list-clear {
|
|
58
|
+
padding: 2px 8px;
|
|
59
|
+
border-radius: 4px;
|
|
60
|
+
font-size: 12px;
|
|
61
|
+
color: #f56c6c;
|
|
62
|
+
cursor: pointer;
|
|
63
|
+
user-select: none;
|
|
64
|
+
|
|
65
|
+
&:hover {
|
|
66
|
+
background-color: rgba(245, 108, 108, 0.12);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
48
70
|
.m-editor-history-list-item {
|
|
49
71
|
display: flex;
|
|
50
72
|
align-items: center;
|
|
@@ -215,6 +237,10 @@
|
|
|
215
237
|
|
|
216
238
|
.m-editor-history-list-item-index {
|
|
217
239
|
flex: 0 0 auto;
|
|
240
|
+
// 固定最小宽度并右对齐:序号位数不一(#6 / #16)时右边界仍统一,
|
|
241
|
+
// 使紧随其后的「类型」徽标在各行间对齐成整齐的一列。
|
|
242
|
+
min-width: 30px;
|
|
243
|
+
text-align: right;
|
|
218
244
|
color: #909399;
|
|
219
245
|
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
|
|
220
246
|
font-size: 11px;
|
|
@@ -222,6 +248,15 @@
|
|
|
222
248
|
white-space: nowrap;
|
|
223
249
|
}
|
|
224
250
|
|
|
251
|
+
// 合并组头部展示的是步骤区间(如 #10-#20),宽度本就不定、也无需与单步行对齐,
|
|
252
|
+
// 恢复自然宽度与左对齐,避免被强制成固定列后显得突兀。
|
|
253
|
+
.m-editor-history-list-group.is-merged
|
|
254
|
+
> .m-editor-history-list-group-head
|
|
255
|
+
> .m-editor-history-list-item-index {
|
|
256
|
+
min-width: 0;
|
|
257
|
+
text-align: left;
|
|
258
|
+
}
|
|
259
|
+
|
|
225
260
|
// 操作时间:弱化展示,紧贴在描述之后、各操作按钮之前。
|
|
226
261
|
.m-editor-history-list-item-time {
|
|
227
262
|
flex: 0 0 auto;
|
|
@@ -281,6 +316,34 @@
|
|
|
281
316
|
white-space: nowrap;
|
|
282
317
|
}
|
|
283
318
|
|
|
319
|
+
// 「操作途径」徽标:浅灰描边胶囊,弱化展示来源(画布 / 图层 / 配置面板…),不抢占描述焦点。
|
|
320
|
+
.m-editor-history-list-item-source {
|
|
321
|
+
flex: 0 0 auto;
|
|
322
|
+
padding: 0 6px;
|
|
323
|
+
border: 1px solid #dcdfe6;
|
|
324
|
+
border-radius: 8px;
|
|
325
|
+
font-size: 10px;
|
|
326
|
+
line-height: 14px;
|
|
327
|
+
color: #909399;
|
|
328
|
+
background-color: #f4f4f5;
|
|
329
|
+
white-space: nowrap;
|
|
330
|
+
font-weight: 400; // 防止被合并组头部的粗体继承
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// 「已保存」徽标:绿色实心胶囊,标记最近一次保存对应的历史记录(与 historyService.markSaved 对应)。
|
|
334
|
+
.m-editor-history-list-item-saved {
|
|
335
|
+
flex: 0 0 auto;
|
|
336
|
+
padding: 0 6px;
|
|
337
|
+
border-radius: 8px;
|
|
338
|
+
font-size: 10px;
|
|
339
|
+
line-height: 16px;
|
|
340
|
+
color: #fff;
|
|
341
|
+
background-color: #67c23a;
|
|
342
|
+
white-space: nowrap;
|
|
343
|
+
font-weight: 500;
|
|
344
|
+
letter-spacing: 0.2px;
|
|
345
|
+
}
|
|
346
|
+
|
|
284
347
|
// 「合并 N 步」徽标:紫色实心胶囊,与合并组卡片色系一致,醒目区分单步条目。
|
|
285
348
|
.m-editor-history-list-item-merge {
|
|
286
349
|
flex: 0 0 auto;
|
|
@@ -294,6 +357,24 @@
|
|
|
294
357
|
letter-spacing: 0.2px;
|
|
295
358
|
}
|
|
296
359
|
|
|
360
|
+
// 操作区:把「回滚 / 回到 / 查看差异」收敛为一个统一容器,默认隐藏,
|
|
361
|
+
// 仅在指针悬停于所在行时显示。静止状态下每行最右侧固定为「时间」,
|
|
362
|
+
// 各行因此能对齐成整齐的右侧列,避免按钮数量不一导致的参差错乱。
|
|
363
|
+
.m-editor-history-list-item-actions {
|
|
364
|
+
display: none;
|
|
365
|
+
flex: 0 0 auto;
|
|
366
|
+
align-items: center;
|
|
367
|
+
gap: 6px;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.m-editor-history-list-group-head:hover > .m-editor-history-list-item-actions,
|
|
371
|
+
.m-editor-history-list-substeps
|
|
372
|
+
> li:hover
|
|
373
|
+
> .m-editor-history-list-item-actions,
|
|
374
|
+
.m-editor-history-list-initial:hover > .m-editor-history-list-item-actions {
|
|
375
|
+
display: flex;
|
|
376
|
+
}
|
|
377
|
+
|
|
297
378
|
.m-editor-history-list-item-diff {
|
|
298
379
|
flex: 0 0 auto;
|
|
299
380
|
padding: 0 6px;
|
|
@@ -310,21 +391,21 @@
|
|
|
310
391
|
}
|
|
311
392
|
}
|
|
312
393
|
|
|
313
|
-
//
|
|
314
|
-
//
|
|
394
|
+
// 「回到」按钮:将历史游标移动到该 step。使用绿色色系,
|
|
395
|
+
// 与红色「回滚」、蓝色「查看差异」区分,也避免与紧邻的灰色「来源」徽标混淆。
|
|
315
396
|
.m-editor-history-list-item-goto {
|
|
316
397
|
flex: 0 0 auto;
|
|
317
398
|
padding: 0 6px;
|
|
318
399
|
border-radius: 2px;
|
|
319
400
|
font-size: 10px;
|
|
320
401
|
line-height: 16px;
|
|
321
|
-
color: #
|
|
322
|
-
background-color: rgba(
|
|
402
|
+
color: #529b2e;
|
|
403
|
+
background-color: rgba(103, 194, 58, 0.12);
|
|
323
404
|
cursor: pointer;
|
|
324
405
|
user-select: none;
|
|
325
406
|
|
|
326
407
|
&:hover {
|
|
327
|
-
background-color: rgba(
|
|
408
|
+
background-color: rgba(103, 194, 58, 0.24);
|
|
328
409
|
}
|
|
329
410
|
}
|
|
330
411
|
|
package/src/theme/page-bar.scss
CHANGED
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
position: absolute;
|
|
59
59
|
right: 15px;
|
|
60
60
|
bottom: 15px;
|
|
61
|
-
z-index:
|
|
61
|
+
z-index: 32;
|
|
62
62
|
opacity: 0.5;
|
|
63
63
|
|
|
64
64
|
&:hover {
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
position: absolute;
|
|
71
71
|
right: 15px;
|
|
72
72
|
bottom: 60px;
|
|
73
|
-
z-index:
|
|
73
|
+
z-index: 31;
|
|
74
74
|
opacity: 0.5;
|
|
75
75
|
|
|
76
76
|
&:hover {
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
position: absolute;
|
|
83
83
|
left: 0;
|
|
84
84
|
top: 0;
|
|
85
|
-
z-index:
|
|
85
|
+
z-index: 31;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
.m-editor-resizer {
|
package/src/type.ts
CHANGED
|
@@ -56,7 +56,7 @@ import type { PropsService } from './services/props';
|
|
|
56
56
|
import type { StageOverlayService } from './services/stageOverlay';
|
|
57
57
|
import type { StorageService } from './services/storage';
|
|
58
58
|
import type { UiService } from './services/ui';
|
|
59
|
-
import type { UndoRedo } from './utils/undo-redo';
|
|
59
|
+
import type { SerializedUndoRedo, UndoRedo } from './utils/undo-redo';
|
|
60
60
|
|
|
61
61
|
export type EditorSlots = FrameworkSlots &
|
|
62
62
|
WorkspaceSlots &
|
|
@@ -312,6 +312,8 @@ export interface UiState {
|
|
|
312
312
|
hideSlideBar: boolean;
|
|
313
313
|
/** 侧边栏面板配置 */
|
|
314
314
|
sideBarItems: SideComponent[];
|
|
315
|
+
/** 当前激活的侧边栏面板 */
|
|
316
|
+
sideBarActiveTabName: string;
|
|
315
317
|
|
|
316
318
|
// navMenu 的宽高
|
|
317
319
|
navMenuRect: {
|
|
@@ -390,6 +392,9 @@ export interface MenuButton {
|
|
|
390
392
|
items?: MenuButton[];
|
|
391
393
|
/** 唯一标识,用于高亮 */
|
|
392
394
|
id?: string | number;
|
|
395
|
+
buttonProps?: {
|
|
396
|
+
type?: string;
|
|
397
|
+
};
|
|
393
398
|
}
|
|
394
399
|
// #endregion MenuButton
|
|
395
400
|
|
|
@@ -510,7 +515,7 @@ export interface HistoryListExtraTab {
|
|
|
510
515
|
* - data-source: 数据源,按 `type`(base/http/...) 从 dataSourceService 获取数据源表单配置
|
|
511
516
|
* - code-block: 数据源代码块,使用内置的代码块表单配置
|
|
512
517
|
*/
|
|
513
|
-
export type CompareCategory = 'node' | 'data-source' | 'code-block';
|
|
518
|
+
export type CompareCategory = 'node' | 'data-source' | 'code-block' | string;
|
|
514
519
|
|
|
515
520
|
/**
|
|
516
521
|
* 自定义 `loadConfig` 时回传的上下文,聚合了组件当前的对比入参,
|
|
@@ -680,95 +685,172 @@ export interface CodeParamStatement {
|
|
|
680
685
|
}
|
|
681
686
|
|
|
682
687
|
// #region HistoryOpType
|
|
683
|
-
|
|
688
|
+
/**
|
|
689
|
+
* 历史记录操作类型:
|
|
690
|
+
* - `add` / `remove` / `update`:普通可撤销/重做的节点变更;
|
|
691
|
+
* - `initial`:页面「未修改的初始状态」基线(设置 root 时生成),作为页面栈 index 0 的固定底线 step。
|
|
692
|
+
* 该 step 不可被撤销/回滚(cursor 不会低于它),仅用于历史面板底部的初始行展示。
|
|
693
|
+
*/
|
|
694
|
+
export type HistoryOpType = 'add' | 'remove' | 'update' | 'initial';
|
|
684
695
|
// #endregion HistoryOpType
|
|
685
696
|
|
|
686
|
-
// #region
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
697
|
+
// #region HistoryOpSource
|
|
698
|
+
/**
|
|
699
|
+
* 历史记录的「操作途径」——标记本次变更由哪条交互入口触发,仅用于历史面板展示 / 业务埋点,
|
|
700
|
+
* 不影响 undo/redo 行为。缺省(未传)时 UI 视为「未知」。
|
|
701
|
+
*
|
|
702
|
+
* - `stage`:画布(拖拽 / 缩放 / 排序等舞台直接操作)
|
|
703
|
+
* - `tree`:树形面板(图层 / 数据源 / 代码块等树形结构里的拖拽 / 菜单操作)
|
|
704
|
+
* - `component-panel`:组件面板(左侧组件列表点击 / 拖拽新增组件)
|
|
705
|
+
* - `props`:配置面板表单(属性表单字段编辑)
|
|
706
|
+
* - `code`:源码编辑器(配置面板「源码」面板里直接编辑 JSON/代码后保存)
|
|
707
|
+
* - `stage-contextmenu`:画布右键菜单(舞台上节点的右键上下文菜单)
|
|
708
|
+
* - `tree-contextmenu`:树面板右键菜单(图层 / 数据源 / 代码块等树形列表上的右键上下文菜单)
|
|
709
|
+
* - `toolbar`:工具栏菜单(顶部导航工具栏按钮)
|
|
710
|
+
* - `shortcut`:键盘快捷键
|
|
711
|
+
* - `rollback`:历史回滚(历史面板里对某条历史「回滚」,反向应用为一条新记录,类 git revert)
|
|
712
|
+
* - `api`:代码 / 接口调用(程序化触发)
|
|
713
|
+
* - `ai`:AI 生成 / 智能助手触发的变更
|
|
714
|
+
* - `unknown`:未知来源
|
|
715
|
+
*
|
|
716
|
+
* 通过 `(string & {})` 允许业务侧扩展自定义途径字符串,同时保留内置值的自动补全。
|
|
717
|
+
*/
|
|
718
|
+
export type HistoryOpSource =
|
|
719
|
+
| 'initial'
|
|
720
|
+
| 'stage'
|
|
721
|
+
| 'tree'
|
|
722
|
+
| 'component-panel'
|
|
723
|
+
| 'props'
|
|
724
|
+
| 'code'
|
|
725
|
+
| 'root-code'
|
|
726
|
+
| 'stage-contextmenu'
|
|
727
|
+
| 'tree-contextmenu'
|
|
728
|
+
| 'toolbar'
|
|
729
|
+
| 'shortcut'
|
|
730
|
+
| 'rollback'
|
|
731
|
+
| 'api'
|
|
732
|
+
| 'ai'
|
|
733
|
+
// 同步
|
|
734
|
+
| 'sync'
|
|
735
|
+
| 'unknown'
|
|
736
|
+
| (string & {});
|
|
737
|
+
// #endregion HistoryOpSource
|
|
738
|
+
|
|
739
|
+
// #region StepDiffItem
|
|
740
|
+
/**
|
|
741
|
+
* 单条变更的 diff 描述,统一表达「页面节点 / 代码块 / 数据源」的变化内容,
|
|
742
|
+
* 被 {@link StepValue} / {@link CodeBlockStepValue} / {@link DataSourceStepValue} 的 `diff` 复用。
|
|
743
|
+
*
|
|
744
|
+
* 按 `opType` 区分携带的字段:
|
|
745
|
+
* - `add`:仅 `newSchema`(页面节点还带 `parentId` / `index`);
|
|
746
|
+
* - `remove`:仅 `oldSchema`(页面节点还带 `parentId` / `index`);
|
|
747
|
+
* - `update`:`oldSchema` + `newSchema`,并可带 `changeRecords` 做局部更新。
|
|
748
|
+
*
|
|
749
|
+
* 泛型 `T` 为变化内容的快照类型:页面节点为 `MNode`,代码块为 `CodeBlockContent`,数据源为 `DataSourceSchema`。
|
|
750
|
+
*/
|
|
751
|
+
export interface StepDiffItem<T = unknown> {
|
|
752
|
+
/** 变更后的内容快照。`opType` 为 `add` / `update` 时有,`remove` 时无。 */
|
|
753
|
+
newSchema?: T;
|
|
754
|
+
/** 变更前的内容快照。`opType` 为 `remove` / `update` 时有,`add` 时无。 */
|
|
755
|
+
oldSchema?: T;
|
|
756
|
+
/** 父节点 id。仅页面节点有(数据源 / 代码块没有父节点)。 */
|
|
699
757
|
parentId?: Id;
|
|
700
|
-
/**
|
|
701
|
-
|
|
702
|
-
/**
|
|
703
|
-
|
|
758
|
+
/** 在父节点 items 数组中的索引。仅页面节点有(数据源 / 代码块无需排序)。 */
|
|
759
|
+
index?: number;
|
|
760
|
+
/**
|
|
761
|
+
* form 端 propPath/value 变更列表,仅 `opType` 为 `update` 时有;
|
|
762
|
+
* 撤销/重做时若有则按 propPath 局部更新,缺省才退化为整内容替换。
|
|
763
|
+
*/
|
|
764
|
+
changeRecords?: ChangeRecord[];
|
|
765
|
+
}
|
|
766
|
+
// #endregion StepDiffItem
|
|
767
|
+
|
|
768
|
+
// #region BaseStepValue
|
|
769
|
+
/**
|
|
770
|
+
* 历史记录条目公共字段,被 {@link StepValue} / {@link CodeBlockStepValue} / {@link DataSourceStepValue} 复用。
|
|
771
|
+
*
|
|
772
|
+
* 泛型 `T` 为 `diff` 中变化内容的快照类型(页面节点 `MNode` / 代码块 `CodeBlockContent` / 数据源 `DataSourceSchema`)。
|
|
773
|
+
*/
|
|
774
|
+
export interface BaseStepValue<T = unknown> {
|
|
704
775
|
/**
|
|
705
|
-
*
|
|
706
|
-
*
|
|
707
|
-
* `
|
|
708
|
-
* 缺省(未传 / 空数组)才退化为整节点替换。
|
|
776
|
+
* 历史记录唯一标识(uuid)。入栈时自动写入(若调用方未指定),
|
|
777
|
+
* 用于精确定位 / 引用某一条历史记录(如 revert、埋点、跨端同步等)。
|
|
778
|
+
* 注意与各自的 `id`(关联的页面 / 代码块 / 数据源 id)区分。
|
|
709
779
|
*/
|
|
710
|
-
|
|
780
|
+
uuid: string;
|
|
781
|
+
/** 操作类型:新增 / 删除 / 更新(三类历史记录统一携带)。 */
|
|
782
|
+
opType: HistoryOpType;
|
|
783
|
+
/**
|
|
784
|
+
* 本次变更的内容(统一 diff 表达),每项见 {@link StepDiffItem}。
|
|
785
|
+
* 页面节点(add/remove 多节点、update 多节点)会有多项,代码块 / 数据源通常只有一项。
|
|
786
|
+
*/
|
|
787
|
+
diff: StepDiffItem<T>[];
|
|
711
788
|
/**
|
|
712
789
|
* 调用方可选传入的人类可读描述(如「调整按钮颜色」),用于历史面板展示。
|
|
713
790
|
* 不影响 undo/redo 行为;缺省时面板会根据节点 / propPath 自动生成描述。
|
|
714
791
|
*/
|
|
715
792
|
historyDescription?: string;
|
|
716
793
|
/**
|
|
717
|
-
*
|
|
794
|
+
* 操作途径:标记本次变更由哪条交互入口触发,取值见 {@link HistoryOpSource}
|
|
795
|
+
* (画布 / 树面板 / 组件面板 / 配置面板 / 源码编辑器 / 右键菜单 / 工具栏 / 快捷键 / 回滚 / 接口 等)。
|
|
796
|
+
* 仅用于历史面板展示与业务埋点,不影响 undo/redo 行为;缺省时面板视为「未知」。
|
|
797
|
+
*/
|
|
798
|
+
source?: HistoryOpSource;
|
|
799
|
+
/**
|
|
800
|
+
* 入栈时间戳(毫秒)。入栈时自动写入(若调用方未指定),仅用于历史面板展示。
|
|
718
801
|
*/
|
|
719
802
|
timestamp?: number;
|
|
803
|
+
/**
|
|
804
|
+
* 是否为「已保存」记录:DSL 落库(如保存到后端 / 本地)时由 historyService.markSaved 标记。
|
|
805
|
+
* 同一栈内任意时刻最多只有一条记录为 true;从 IndexedDB 恢复时游标会被定位到最近一条已保存记录之后。
|
|
806
|
+
*/
|
|
807
|
+
saved?: boolean;
|
|
808
|
+
/**
|
|
809
|
+
* 是否为「整体设置 root」(set root)产生的记录(由 {@link Editor.pushRootDiffHistory} 写入)。
|
|
810
|
+
* 用于「连续 set root 合并」:当某页栈最新一条已是 root 记录时,下一条 set root 会替换它而非新增,
|
|
811
|
+
* 避免源码反复保存 / 外部重设 DSL 时堆积多条 root 记录。
|
|
812
|
+
*/
|
|
813
|
+
rootStep?: boolean;
|
|
814
|
+
}
|
|
815
|
+
// #endregion BaseStepValue
|
|
816
|
+
|
|
817
|
+
// #region StepValue
|
|
818
|
+
export interface StepValue extends BaseStepValue<MNode> {
|
|
819
|
+
/** 页面信息 */
|
|
820
|
+
data: { name: string; id: Id };
|
|
821
|
+
/** 操作前选中的节点 ID,用于撤销后恢复选择状态 */
|
|
822
|
+
selectedBefore: Id[];
|
|
823
|
+
/** 操作后选中的节点 ID,用于重做后恢复选择状态 */
|
|
824
|
+
selectedAfter: Id[];
|
|
825
|
+
modifiedNodeIds: Map<Id, Id>;
|
|
720
826
|
}
|
|
721
827
|
// #endregion StepValue
|
|
722
828
|
|
|
723
829
|
// #region CodeBlockStepValue
|
|
724
830
|
/**
|
|
725
831
|
* 代码块历史记录条目。按 codeBlock.id 分组保存到 historyState.codeBlockState。
|
|
726
|
-
*
|
|
727
|
-
* -
|
|
728
|
-
* -
|
|
832
|
+
* 变更内容统一由 `diff`(单项)表达,每项见 {@link StepDiffItem}:
|
|
833
|
+
* - 新增(opType 'add'):仅 `newSchema`(新内容);
|
|
834
|
+
* - 更新(opType 'update'):`oldSchema` + `newSchema`,并可带 `changeRecords` 做局部更新;
|
|
835
|
+
* - 删除(opType 'remove'):仅 `oldSchema`(删除前内容)。
|
|
729
836
|
*/
|
|
730
|
-
export interface CodeBlockStepValue {
|
|
837
|
+
export interface CodeBlockStepValue extends BaseStepValue<CodeBlockContent> {
|
|
731
838
|
/** 关联的代码块 id */
|
|
732
839
|
id: Id;
|
|
733
|
-
/** 变更前的代码块内容,新增时为 null */
|
|
734
|
-
oldContent: CodeBlockContent | null;
|
|
735
|
-
/** 变更后的代码块内容,删除时为 null */
|
|
736
|
-
newContent: CodeBlockContent | null;
|
|
737
|
-
/**
|
|
738
|
-
* form 端 propPath/value 列表。撤销/重做时若有则按 propPath 局部更新;
|
|
739
|
-
* 缺省才退化为整内容替换。新增/删除场景通常无 changeRecords。
|
|
740
|
-
*/
|
|
741
|
-
changeRecords?: ChangeRecord[];
|
|
742
|
-
/** 调用方可选传入的人类可读描述,用于历史面板展示;不影响 undo/redo 行为。 */
|
|
743
|
-
historyDescription?: string;
|
|
744
|
-
/** 入栈时间戳(毫秒),入栈时自动写入,仅用于历史面板展示。 */
|
|
745
|
-
timestamp?: number;
|
|
746
840
|
}
|
|
747
841
|
// #endregion CodeBlockStepValue
|
|
748
842
|
|
|
749
843
|
// #region DataSourceStepValue
|
|
750
844
|
/**
|
|
751
845
|
* 数据源历史记录条目。按 dataSource.id 分组保存到 historyState.dataSourceState。
|
|
752
|
-
*
|
|
753
|
-
* -
|
|
754
|
-
* -
|
|
846
|
+
* 变更内容统一由 `diff`(单项)表达,每项见 {@link StepDiffItem}:
|
|
847
|
+
* - 新增(opType 'add'):仅 `newSchema`(新 schema);
|
|
848
|
+
* - 更新(opType 'update'):`oldSchema` + `newSchema`,并可带 `changeRecords` 做局部更新;
|
|
849
|
+
* - 删除(opType 'remove'):仅 `oldSchema`(删除前 schema)。
|
|
755
850
|
*/
|
|
756
|
-
export interface DataSourceStepValue {
|
|
851
|
+
export interface DataSourceStepValue extends BaseStepValue<DataSourceSchema> {
|
|
757
852
|
/** 关联的数据源 id */
|
|
758
853
|
id: Id;
|
|
759
|
-
/** 变更前的数据源 schema,新增时为 null */
|
|
760
|
-
oldSchema: DataSourceSchema | null;
|
|
761
|
-
/** 变更后的数据源 schema,删除时为 null */
|
|
762
|
-
newSchema: DataSourceSchema | null;
|
|
763
|
-
/**
|
|
764
|
-
* form 端 propPath/value 列表。撤销/重做时若有则按 propPath 局部更新;
|
|
765
|
-
* 缺省才退化为整 schema 替换。新增/删除场景通常无 changeRecords。
|
|
766
|
-
*/
|
|
767
|
-
changeRecords?: ChangeRecord[];
|
|
768
|
-
/** 调用方可选传入的人类可读描述,用于历史面板展示;不影响 undo/redo 行为。 */
|
|
769
|
-
historyDescription?: string;
|
|
770
|
-
/** 入栈时间戳(毫秒),入栈时自动写入,仅用于历史面板展示。 */
|
|
771
|
-
timestamp?: number;
|
|
772
854
|
}
|
|
773
855
|
// #endregion DataSourceStepValue
|
|
774
856
|
|
|
@@ -789,6 +871,45 @@ export interface HistoryState {
|
|
|
789
871
|
dataSourceState: Record<Id, UndoRedo<DataSourceStepValue>>;
|
|
790
872
|
}
|
|
791
873
|
|
|
874
|
+
// #region PersistedHistoryState
|
|
875
|
+
/**
|
|
876
|
+
* 历史记录的可持久化快照。由 historyService.saveToIndexedDB 写入 IndexedDB,
|
|
877
|
+
* 再由 historyService.restoreFromIndexedDB 读出并重建各 UndoRedo 栈。
|
|
878
|
+
*/
|
|
879
|
+
export interface PersistedHistoryState {
|
|
880
|
+
/** 快照结构版本号,便于后续兼容升级。 */
|
|
881
|
+
version: number;
|
|
882
|
+
/** 保存时的活动页 id。 */
|
|
883
|
+
pageId?: Id;
|
|
884
|
+
/** 各页面历史栈的序列化快照,按 pageId 分组。 */
|
|
885
|
+
pageSteps: Record<Id, SerializedUndoRedo<StepValue>>;
|
|
886
|
+
/** 各代码块历史栈的序列化快照,按 codeBlockId 分组。 */
|
|
887
|
+
codeBlockState: Record<Id, SerializedUndoRedo<CodeBlockStepValue>>;
|
|
888
|
+
/** 各数据源历史栈的序列化快照,按 dataSourceId 分组。 */
|
|
889
|
+
dataSourceState: Record<Id, SerializedUndoRedo<DataSourceStepValue>>;
|
|
890
|
+
/** 保存时间戳(毫秒)。 */
|
|
891
|
+
savedAt: number;
|
|
892
|
+
}
|
|
893
|
+
// #endregion PersistedHistoryState
|
|
894
|
+
|
|
895
|
+
// #region HistoryPersistOptions
|
|
896
|
+
/** historyService 持久化相关 API 的可选配置。 */
|
|
897
|
+
export interface HistoryPersistOptions {
|
|
898
|
+
/** IndexedDB 数据库名,默认 `tmagic-editor`(最终库名会拼上当前 DSL app id)。 */
|
|
899
|
+
dbName?: string;
|
|
900
|
+
/** objectStore 名,默认 `history`。 */
|
|
901
|
+
storeName?: string;
|
|
902
|
+
/** 记录 key,用于区分不同活动页 / 项目,默认 `default`。 */
|
|
903
|
+
key?: IDBValidKey;
|
|
904
|
+
/**
|
|
905
|
+
* 显式指定用于库名隔离的 DSL app id。
|
|
906
|
+
* 缺省时回退到当前 editorService 的 `root.id`;在「先恢复历史再 set root」场景下 root 尚未设置,
|
|
907
|
+
* 需由调用方(如从待加载 DSL 取 id)显式传入,否则会读 / 写到未按 app 隔离的默认库。
|
|
908
|
+
*/
|
|
909
|
+
appId?: Id;
|
|
910
|
+
}
|
|
911
|
+
// #endregion HistoryPersistOptions
|
|
912
|
+
|
|
792
913
|
// #region HistoryListEntry
|
|
793
914
|
/**
|
|
794
915
|
* 历史面板用:当前页面的一条历史步骤(包含位置和是否已应用)。
|
|
@@ -1099,16 +1220,21 @@ export const canUsePluginMethods = {
|
|
|
1099
1220
|
|
|
1100
1221
|
export type AsyncMethodName = Writable<(typeof canUsePluginMethods)['async']>;
|
|
1101
1222
|
|
|
1223
|
+
// #region HistoryOpOptions
|
|
1102
1224
|
/**
|
|
1103
1225
|
* 历史记录写入相关的通用配置(codeBlock / dataSource / editor 共用)
|
|
1104
1226
|
* - doNotPushHistory: 操作完成后是否不要将本次操作压入历史栈(撤销/重做记录),默认 false
|
|
1105
1227
|
* - historyDescription: 入栈时附带的人类可读描述,用于历史面板展示;不影响 undo/redo 行为,缺省时面板会自动生成描述
|
|
1228
|
+
* - historySource: 操作途径,取值见 {@link HistoryOpSource}(画布 / 树面板 / 组件面板 / 配置面板 / 源码编辑器 / 右键菜单 / 工具栏 / 快捷键 / 回滚 / 接口 等),用于历史面板展示与埋点;不影响 undo/redo 行为
|
|
1106
1229
|
*/
|
|
1107
1230
|
export interface HistoryOpOptions {
|
|
1108
1231
|
doNotPushHistory?: boolean;
|
|
1109
1232
|
historyDescription?: string;
|
|
1233
|
+
historySource?: HistoryOpSource;
|
|
1110
1234
|
}
|
|
1235
|
+
// #endregion HistoryOpOptions
|
|
1111
1236
|
|
|
1237
|
+
// #region HistoryOpOptionsWithChangeRecords
|
|
1112
1238
|
/**
|
|
1113
1239
|
* 在 HistoryOpOptions 基础上携带 form 端 propPath/value 变更记录,
|
|
1114
1240
|
* 用于历史记录的精细化撤销/重做(按 propPath 局部 patch)。
|
|
@@ -1116,7 +1242,9 @@ export interface HistoryOpOptions {
|
|
|
1116
1242
|
export interface HistoryOpOptionsWithChangeRecords extends HistoryOpOptions {
|
|
1117
1243
|
changeRecords?: ChangeRecord[];
|
|
1118
1244
|
}
|
|
1245
|
+
// #endregion HistoryOpOptionsWithChangeRecords
|
|
1119
1246
|
|
|
1247
|
+
// #region DslOpOptions
|
|
1120
1248
|
/**
|
|
1121
1249
|
* DSL 修改类操作的通用配置
|
|
1122
1250
|
* - doNotSelect: 操作后是否不要自动触发选中(不调用 this.select / this.multiSelect / stage.select / stage.multiSelect)
|
|
@@ -1126,11 +1254,12 @@ export interface DslOpOptions extends HistoryOpOptions {
|
|
|
1126
1254
|
doNotSelect?: boolean;
|
|
1127
1255
|
doNotSwitchPage?: boolean;
|
|
1128
1256
|
}
|
|
1257
|
+
// #endregion DslOpOptions
|
|
1129
1258
|
|
|
1130
1259
|
/** 差异对话框的入参 */
|
|
1131
1260
|
export interface DiffDialogPayload {
|
|
1132
1261
|
/** 表单类别 */
|
|
1133
|
-
category
|
|
1262
|
+
category?: CompareCategory;
|
|
1134
1263
|
/** 节点类型 / 数据源类型 */
|
|
1135
1264
|
type?: string;
|
|
1136
1265
|
/** 代码块场景下的数据源类型 */
|
|
@@ -1146,3 +1275,34 @@ export interface DiffDialogPayload {
|
|
|
1146
1275
|
/** 用于标题展示的目标 id */
|
|
1147
1276
|
id?: string | number;
|
|
1148
1277
|
}
|
|
1278
|
+
|
|
1279
|
+
/**
|
|
1280
|
+
* 一组「描述 + 可操作性」的判定函数集合。页面 / 数据源 / 代码块及业务自定义历史
|
|
1281
|
+
* 各自实现一份,作为整体注入,避免把 describe* / isStep* 拆成多个独立 props 反复透传。
|
|
1282
|
+
*/
|
|
1283
|
+
export interface HistoryRowDescriptor<T extends BaseStepValue = BaseStepValue> {
|
|
1284
|
+
/** 组级描述文案生成器,接收一个 group,返回展示文本。 */
|
|
1285
|
+
describeGroup: (_group: any) => string;
|
|
1286
|
+
/** 单步描述文案生成器,接收一个 step,返回展示文本(合并组展开后的子步列表用)。 */
|
|
1287
|
+
describeStep: (_step: T) => string;
|
|
1288
|
+
/** 判断某个 step 是否可查看差异(前后值都存在)。不传则一律不展示差异入口。 */
|
|
1289
|
+
isStepDiffable?: (_step: T) => boolean;
|
|
1290
|
+
/** 判断某个 step 是否支持回滚(如更新需带 changeRecords)。不传则已应用即可回滚。 */
|
|
1291
|
+
isStepRevertable?: (_step: T) => boolean;
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
/**
|
|
1295
|
+
* 通用 bucket(数据源 / 代码块 / 业务自定义历史)的整体渲染配置。
|
|
1296
|
+
* 把原先散落在 Bucket / BucketTab 上的 title / prefix / describe* / isStep* / showInitial / gotoEnabled
|
|
1297
|
+
* 收敛成一个对象作为单一 prop 传递,调用方一次配齐、组件内部按需读取。
|
|
1298
|
+
*/
|
|
1299
|
+
export interface HistoryBucketConfig<T extends BaseStepValue = BaseStepValue> extends HistoryRowDescriptor<T> {
|
|
1300
|
+
/** bucket 头部标题,例如 "数据源" / "代码块"。 */
|
|
1301
|
+
title: string;
|
|
1302
|
+
/** 子项 key 的命名空间前缀(`ds` 数据源 / `cb` 代码块 / 业务自定义如 `mod`)。 */
|
|
1303
|
+
prefix: string;
|
|
1304
|
+
/** 是否展示底部「回到初始状态」入口,默认 true。无 undo cursor 语义的自定义历史可传 false。 */
|
|
1305
|
+
showInitial?: boolean;
|
|
1306
|
+
/** 是否支持「跳转到该记录」(goto),默认 true。 */
|
|
1307
|
+
gotoEnabled?: boolean;
|
|
1308
|
+
}
|
|
@@ -5,11 +5,16 @@ import { cloneDeep, Id, MContainer, NodeType } from '@tmagic/core';
|
|
|
5
5
|
import { calcValueByFontsize, isPage, isPageFragment } from '@tmagic/utils';
|
|
6
6
|
|
|
7
7
|
import ContentMenu from '@editor/components/ContentMenu.vue';
|
|
8
|
-
import type { MenuButton, Services } from '@editor/type';
|
|
8
|
+
import type { HistoryOpSource, MenuButton, Services } from '@editor/type';
|
|
9
9
|
|
|
10
10
|
import { COPY_STORAGE_KEY } from './editor';
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* 共享的右键菜单项构造器(画布 ViewerMenu 与图层树 LayerMenu 共用)。
|
|
14
|
+
* `historySource` 用于标记本次操作的途径,调用方按所在面板传入:
|
|
15
|
+
* 画布传 `'stage-contextmenu'`,树形面板传 `'tree-contextmenu'`。
|
|
16
|
+
*/
|
|
17
|
+
export const useDeleteMenu = (historySource?: HistoryOpSource): MenuButton => ({
|
|
13
18
|
type: 'button',
|
|
14
19
|
text: '删除',
|
|
15
20
|
icon: Delete,
|
|
@@ -19,7 +24,7 @@ export const useDeleteMenu = (): MenuButton => ({
|
|
|
19
24
|
},
|
|
20
25
|
handler: ({ editorService }) => {
|
|
21
26
|
const nodes = editorService.get('nodes');
|
|
22
|
-
nodes && editorService.remove(nodes);
|
|
27
|
+
nodes && editorService.remove(nodes, { historySource });
|
|
23
28
|
},
|
|
24
29
|
});
|
|
25
30
|
|
|
@@ -33,7 +38,10 @@ export const useCopyMenu = (): MenuButton => ({
|
|
|
33
38
|
},
|
|
34
39
|
});
|
|
35
40
|
|
|
36
|
-
export const usePasteMenu = (
|
|
41
|
+
export const usePasteMenu = (
|
|
42
|
+
historySource?: HistoryOpSource,
|
|
43
|
+
menu?: ShallowRef<InstanceType<typeof ContentMenu> | null>,
|
|
44
|
+
): MenuButton => ({
|
|
37
45
|
type: 'button',
|
|
38
46
|
text: '粘贴',
|
|
39
47
|
icon: markRaw(DocumentCopy),
|
|
@@ -52,14 +60,14 @@ export const usePasteMenu = (menu?: ShallowRef<InstanceType<typeof ContentMenu>
|
|
|
52
60
|
const initialTop =
|
|
53
61
|
calcValueByFontsize(stage?.renderer?.getDocument(), (rect.top || 0) - (parentRect?.top || 0)) /
|
|
54
62
|
uiService.get('zoom');
|
|
55
|
-
editorService.paste({ left: initialLeft, top: initialTop });
|
|
63
|
+
editorService.paste({ left: initialLeft, top: initialTop }, undefined, { historySource });
|
|
56
64
|
} else {
|
|
57
|
-
editorService.paste();
|
|
65
|
+
editorService.paste(undefined, undefined, { historySource });
|
|
58
66
|
}
|
|
59
67
|
},
|
|
60
68
|
});
|
|
61
69
|
|
|
62
|
-
const moveTo = async (id: Id, { editorService }: Services) => {
|
|
70
|
+
const moveTo = async (id: Id, { editorService }: Services, historySource?: HistoryOpSource) => {
|
|
63
71
|
const nodes = editorService.get('nodes') || [];
|
|
64
72
|
const parent = editorService.getNodeById(id) as MContainer;
|
|
65
73
|
|
|
@@ -69,10 +77,11 @@ const moveTo = async (id: Id, { editorService }: Services) => {
|
|
|
69
77
|
// 不要再走 remove + add 两步,否则会被切成两条历史(且语义也不正确)。
|
|
70
78
|
await editorService.moveToContainer(cloneDeep(nodes), parent.id, {
|
|
71
79
|
doNotSwitchPage: true,
|
|
80
|
+
historySource,
|
|
72
81
|
});
|
|
73
82
|
};
|
|
74
83
|
|
|
75
|
-
export const useMoveToMenu = ({ editorService }: Services): MenuButton => {
|
|
84
|
+
export const useMoveToMenu = ({ editorService }: Services, historySource?: HistoryOpSource): MenuButton => {
|
|
76
85
|
const root = computed(() => editorService.get('root'));
|
|
77
86
|
|
|
78
87
|
return {
|
|
@@ -89,7 +98,7 @@ export const useMoveToMenu = ({ editorService }: Services): MenuButton => {
|
|
|
89
98
|
text: `${page.name}(${page.id})`,
|
|
90
99
|
type: 'button',
|
|
91
100
|
handler: (services: Services) => {
|
|
92
|
-
moveTo(page.id, services);
|
|
101
|
+
moveTo(page.id, services, historySource);
|
|
93
102
|
},
|
|
94
103
|
})),
|
|
95
104
|
};
|