@tmagic/editor 1.8.0-beta.2 → 1.8.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/Editor.vue_vue_type_script_setup_true_lang.js +9 -0
- package/dist/es/components/CompareForm.vue_vue_type_script_setup_true_lang.js +46 -28
- package/dist/es/editorProps.js +2 -0
- package/dist/es/fields/CodeLink.vue_vue_type_script_setup_true_lang.js +2 -5
- 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 +8 -4
- package/dist/es/index.js +3 -1
- package/dist/es/layouts/CodeEditor.vue_vue_type_script_setup_true_lang.js +2 -5
- 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 +37 -14
- package/dist/es/layouts/history-list/BucketTab.js +5 -0
- package/dist/es/layouts/history-list/{CodeBlockTab.vue_vue_type_script_setup_true_lang.js → BucketTab.vue_vue_type_script_setup_true_lang.js} +30 -16
- package/dist/es/layouts/history-list/GroupRow.vue_vue_type_script_setup_true_lang.js +91 -60
- package/dist/es/layouts/history-list/HistoryDiffDialog.vue_vue_type_script_setup_true_lang.js +82 -30
- package/dist/es/layouts/history-list/HistoryListPanel.vue_vue_type_script_setup_true_lang.js +140 -66
- package/dist/es/layouts/history-list/InitialRow.vue_vue_type_script_setup_true_lang.js +15 -9
- package/dist/es/layouts/history-list/PageTab.vue_vue_type_script_setup_true_lang.js +15 -6
- package/dist/es/layouts/history-list/composables.js +72 -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/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 +25 -10
- package/dist/es/services/dataSource.js +24 -10
- package/dist/es/services/editor.js +98 -46
- package/dist/es/services/history.js +7 -2
- package/dist/es/services/keybinding.js +3 -3
- package/dist/es/style.css +60 -16
- package/dist/es/utils/content-menu.js +17 -9
- package/dist/style.css +60 -16
- package/dist/tmagic-editor.umd.cjs +795 -443
- package/package.json +7 -7
- package/src/Editor.vue +8 -0
- package/src/components/CompareForm.vue +50 -19
- package/src/editorProps.ts +7 -0
- package/src/fields/CodeLink.vue +2 -5
- 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 +4 -3
- package/src/index.ts +2 -0
- package/src/layouts/CodeEditor.vue +2 -5
- package/src/layouts/NavMenu.vue +1 -1
- package/src/layouts/history-list/Bucket.vue +58 -29
- package/src/layouts/history-list/BucketTab.vue +80 -0
- package/src/layouts/history-list/GroupRow.vue +110 -58
- package/src/layouts/history-list/HistoryDiffDialog.vue +53 -33
- package/src/layouts/history-list/HistoryListPanel.vue +165 -52
- package/src/layouts/history-list/InitialRow.vue +17 -6
- package/src/layouts/history-list/PageTab.vue +25 -7
- package/src/layouts/history-list/composables.ts +92 -1
- package/src/layouts/props-panel/PropsPanel.vue +5 -1
- package/src/layouts/sidebar/ComponentListPanel.vue +9 -5
- 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 +33 -9
- package/src/services/dataSource.ts +30 -8
- package/src/services/editor.ts +111 -34
- package/src/services/history.ts +10 -0
- package/src/services/keybinding.ts +3 -3
- package/src/theme/history-list-panel.scss +67 -14
- package/src/theme/props-panel.scss +3 -3
- package/src/type.ts +146 -0
- package/src/utils/content-menu.ts +18 -9
- package/types/index.d.ts +387 -156
- package/dist/es/layouts/history-list/CodeBlockTab.js +0 -5
- package/dist/es/layouts/history-list/DataSourceTab.js +0 -5
- package/dist/es/layouts/history-list/DataSourceTab.vue_vue_type_script_setup_true_lang.js +0 -62
- package/src/layouts/history-list/CodeBlockTab.vue +0 -61
- package/src/layouts/history-list/DataSourceTab.vue +0 -61
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.8.0-beta.
|
|
2
|
+
"version": "1.8.0-beta.4",
|
|
3
3
|
"name": "@tmagic/editor",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": [
|
|
@@ -58,11 +58,11 @@
|
|
|
58
58
|
"moveable": "^0.53.0",
|
|
59
59
|
"serialize-javascript": "^7.0.0",
|
|
60
60
|
"sortablejs": "^1.15.6",
|
|
61
|
-
"@tmagic/design": "1.8.0-beta.
|
|
62
|
-
"@tmagic/
|
|
63
|
-
"@tmagic/
|
|
64
|
-
"@tmagic/
|
|
65
|
-
"@tmagic/
|
|
61
|
+
"@tmagic/design": "1.8.0-beta.4",
|
|
62
|
+
"@tmagic/form": "1.8.0-beta.4",
|
|
63
|
+
"@tmagic/stage": "1.8.0-beta.4",
|
|
64
|
+
"@tmagic/table": "1.8.0-beta.4",
|
|
65
|
+
"@tmagic/utils": "1.8.0-beta.4"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
68
|
"@types/events": "^3.0.3",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"type-fest": "^5.2.0",
|
|
77
77
|
"typescript": "^6.0.3",
|
|
78
78
|
"vue": "^3.5.34",
|
|
79
|
-
"@tmagic/core": "1.8.0-beta.
|
|
79
|
+
"@tmagic/core": "1.8.0-beta.4"
|
|
80
80
|
},
|
|
81
81
|
"peerDependenciesMeta": {
|
|
82
82
|
"typescript": {
|
package/src/Editor.vue
CHANGED
|
@@ -221,6 +221,7 @@ const stageOptions: StageOptions = {
|
|
|
221
221
|
guidesOptions: props.guidesOptions,
|
|
222
222
|
disabledMultiSelect: props.disabledMultiSelect,
|
|
223
223
|
alwaysMultiSelect: props.alwaysMultiSelect,
|
|
224
|
+
disabledFlashTip: props.disabledFlashTip,
|
|
224
225
|
beforeDblclick: props.beforeDblclick,
|
|
225
226
|
};
|
|
226
227
|
|
|
@@ -237,6 +238,13 @@ provide('stageOptions', stageOptions);
|
|
|
237
238
|
*/
|
|
238
239
|
provide('extendFormState', props.extendFormState);
|
|
239
240
|
|
|
241
|
+
/**
|
|
242
|
+
* 把历史记录面板的自定义扩展 tab 提供给深层的 HistoryListPanel(它挂在 NavMenu 中,
|
|
243
|
+
* 以 markRaw component 形式渲染,无法直接通过 props 透传)。业务方可借此在历史记录
|
|
244
|
+
* 面板内追加自定义模块的历史 tab。
|
|
245
|
+
*/
|
|
246
|
+
provide('historyListExtraTabs', props.historyListExtraTabs);
|
|
247
|
+
|
|
240
248
|
provide<EventBus>('eventBus', new EventEmitter());
|
|
241
249
|
|
|
242
250
|
const propsPanelMountedHandler = (e: InstanceType<typeof FormPanel>) => {
|
|
@@ -24,20 +24,13 @@ import { type CodeBlockContent, type DataSourceSchema, HookType, type MNode } fr
|
|
|
24
24
|
import { type FormConfig, type FormState, type FormValue, MForm } from '@tmagic/form';
|
|
25
25
|
|
|
26
26
|
import { useServices } from '@editor/hooks/use-services';
|
|
27
|
+
import type { CompareCategory, CompareFormLoadConfig } from '@editor/type';
|
|
27
28
|
import { getCodeBlockFormConfig } from '@editor/utils/code-block';
|
|
28
29
|
|
|
29
30
|
defineOptions({
|
|
30
31
|
name: 'MEditorCompareForm',
|
|
31
32
|
});
|
|
32
33
|
|
|
33
|
-
/**
|
|
34
|
-
* 对比类型:
|
|
35
|
-
* - node: 节点组件,按 `type` 从 propsService 获取属性表单配置
|
|
36
|
-
* - data-source: 数据源,按 `type`(base/http/...) 从 dataSourceService 获取数据源表单配置
|
|
37
|
-
* - code-block: 数据源代码块,使用内置的代码块表单配置
|
|
38
|
-
*/
|
|
39
|
-
export type CompareCategory = 'node' | 'data-source' | 'code-block';
|
|
40
|
-
|
|
41
34
|
const props = withDefaults(
|
|
42
35
|
defineProps<{
|
|
43
36
|
/** 当前值(修改后的值) */
|
|
@@ -68,6 +61,12 @@ const props = withDefaults(
|
|
|
68
61
|
* 因此在差异对比场景下也需要透传,避免出现 `formState.xxx is undefined` 的运行时错误。
|
|
69
62
|
*/
|
|
70
63
|
extendState?: (_state: FormState) => Record<string, any> | Promise<Record<string, any>>;
|
|
64
|
+
/**
|
|
65
|
+
* 自定义 FormConfig 加载逻辑。传入后将接管内置的按 `category`(node/data-source/code-block)
|
|
66
|
+
* 取配置逻辑,调用方可根据业务自行返回(或异步返回)表单配置。可通过
|
|
67
|
+
* `ctx.defaultLoadConfig()` 复用默认结果再做二次加工。返回的 config 直接用于对比展示。
|
|
68
|
+
*/
|
|
69
|
+
loadConfig?: CompareFormLoadConfig;
|
|
71
70
|
}>(),
|
|
72
71
|
{
|
|
73
72
|
category: 'node',
|
|
@@ -153,22 +152,41 @@ const showDiff = ({ curValue, lastValue, config }: { curValue: any; lastValue: a
|
|
|
153
152
|
return !isEqual(curValue, lastValue);
|
|
154
153
|
};
|
|
155
154
|
|
|
156
|
-
const
|
|
155
|
+
const removeStyleDisplayConfig = (formConfig: FormConfig): FormConfig =>
|
|
156
|
+
formConfig.map((item) => {
|
|
157
|
+
if (!('type' in item)) return item;
|
|
158
|
+
if (item?.type !== 'tab' || !Array.isArray(item.items)) return item;
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
...item,
|
|
162
|
+
items: item.items.map((tabPane) => {
|
|
163
|
+
if (tabPane?.title !== '样式' || !Array.isArray(tabPane.items)) return tabPane;
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
...tabPane,
|
|
167
|
+
display: true,
|
|
168
|
+
};
|
|
169
|
+
}),
|
|
170
|
+
};
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* 内置的默认 FormConfig 加载逻辑:按 `category` 从对应 service / 工具取配置。
|
|
175
|
+
* 作为 ctx.defaultLoadConfig 透传给自定义 `loadConfig`,方便复用与二次加工。
|
|
176
|
+
*/
|
|
177
|
+
const defaultLoadConfig = async (): Promise<FormConfig> => {
|
|
157
178
|
switch (props.category) {
|
|
158
179
|
case 'node': {
|
|
159
180
|
if (!props.type) {
|
|
160
|
-
|
|
161
|
-
return;
|
|
181
|
+
return [];
|
|
162
182
|
}
|
|
163
|
-
|
|
164
|
-
break;
|
|
183
|
+
return removeStyleDisplayConfig(await propsService.getPropsConfig(props.type));
|
|
165
184
|
}
|
|
166
185
|
case 'data-source': {
|
|
167
|
-
|
|
168
|
-
break;
|
|
186
|
+
return dataSourceService.getFormConfig(props.type || 'base');
|
|
169
187
|
}
|
|
170
188
|
case 'code-block': {
|
|
171
|
-
|
|
189
|
+
return getCodeBlockFormConfig({
|
|
172
190
|
paramColConfig: codeBlockService.getParamsColConfig(),
|
|
173
191
|
// 通过传入 dataSourceType 间接表达"是数据源代码块"——在对比场景下 props.dataSourceType
|
|
174
192
|
// 由调用方按 step 上下文显式传入,未传则视为普通代码块,「执行时机」字段隐藏。
|
|
@@ -178,15 +196,28 @@ const loadConfig = async () => {
|
|
|
178
196
|
// 对比模式只读,不需要校验/语法检查
|
|
179
197
|
editable: false,
|
|
180
198
|
});
|
|
181
|
-
break;
|
|
182
199
|
}
|
|
183
200
|
default:
|
|
184
|
-
|
|
201
|
+
return [];
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const loadConfig = async () => {
|
|
206
|
+
if (props.loadConfig) {
|
|
207
|
+
config.value = await props.loadConfig({
|
|
208
|
+
category: props.category,
|
|
209
|
+
type: props.type,
|
|
210
|
+
dataSourceType: props.dataSourceType,
|
|
211
|
+
defaultLoadConfig,
|
|
212
|
+
});
|
|
213
|
+
return;
|
|
185
214
|
}
|
|
215
|
+
|
|
216
|
+
config.value = await defaultLoadConfig();
|
|
186
217
|
};
|
|
187
218
|
|
|
188
219
|
watch(
|
|
189
|
-
[() => props.category, () => props.type, () => props.dataSourceType],
|
|
220
|
+
[() => props.category, () => props.type, () => props.dataSourceType, () => props.loadConfig],
|
|
190
221
|
() => {
|
|
191
222
|
loadConfig();
|
|
192
223
|
},
|
package/src/editorProps.ts
CHANGED
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
ComponentGroup,
|
|
16
16
|
CustomContentMenuFunction,
|
|
17
17
|
DatasourceTypeOption,
|
|
18
|
+
HistoryListExtraTab,
|
|
18
19
|
IsExpandableFunction,
|
|
19
20
|
MenuBarData,
|
|
20
21
|
MenuButton,
|
|
@@ -86,6 +87,8 @@ export interface EditorProps {
|
|
|
86
87
|
alwaysMultiSelect?: boolean;
|
|
87
88
|
/** 禁用页面片 */
|
|
88
89
|
disabledPageFragment?: boolean;
|
|
90
|
+
/** 禁用「非点击画布选中组件时(如从图层树、面包屑等外部选中),对选中区域做高亮闪烁提示」,默认 false(即默认开启闪烁) */
|
|
91
|
+
disabledFlashTip?: boolean;
|
|
89
92
|
/** 禁用双击在浮层中单独编辑选中组件 */
|
|
90
93
|
disabledStageOverlay?: boolean;
|
|
91
94
|
/** 禁用属性配置面板右下角显示源码的按钮 */
|
|
@@ -125,6 +128,8 @@ export interface EditorProps {
|
|
|
125
128
|
/** 组件树节点双击前的钩子函数,返回 false 则阻止默认的双击行为 */
|
|
126
129
|
beforeLayerNodeDblclick?: (event: MouseEvent, data: TreeNodeData) => Promise<boolean | void> | boolean | void;
|
|
127
130
|
extendFormState?: (state: FormState) => Record<string, any> | Promise<Record<string, any>>;
|
|
131
|
+
/** 历史记录面板的自定义扩展 tab,追加在内置的页面/数据源/代码块 tab 之后 */
|
|
132
|
+
historyListExtraTabs?: HistoryListExtraTab[];
|
|
128
133
|
/** 页面顺序拖拽配置参数 */
|
|
129
134
|
pageBarSortOptions?: PageBarSortOptions;
|
|
130
135
|
/** 页面搜索函数 */
|
|
@@ -136,6 +141,7 @@ export const defaultEditorProps = {
|
|
|
136
141
|
disabledMultiSelect: false,
|
|
137
142
|
alwaysMultiSelect: false,
|
|
138
143
|
disabledPageFragment: false,
|
|
144
|
+
disabledFlashTip: false,
|
|
139
145
|
disabledStageOverlay: false,
|
|
140
146
|
containerHighlightClassName: CONTAINER_HIGHLIGHT_CLASS_NAME,
|
|
141
147
|
containerHighlightDuration: 800,
|
|
@@ -145,6 +151,7 @@ export const defaultEditorProps = {
|
|
|
145
151
|
disabledCodeBlock: false,
|
|
146
152
|
componentGroupList: () => [],
|
|
147
153
|
datasourceList: () => [],
|
|
154
|
+
historyListExtraTabs: () => [],
|
|
148
155
|
menu: () => ({ left: [], right: [] }),
|
|
149
156
|
layerContentMenu: () => [],
|
|
150
157
|
stageContentMenu: () => [],
|
package/src/fields/CodeLink.vue
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
<script lang="ts" setup>
|
|
6
6
|
import { computed, reactive, watch } from 'vue';
|
|
7
|
-
import serialize from 'serialize-javascript';
|
|
8
7
|
|
|
9
8
|
import type { CodeLinkConfig, FieldProps, MLink } from '@tmagic/form';
|
|
10
9
|
|
|
11
10
|
import { getEditorConfig } from '@editor/utils/config';
|
|
11
|
+
import { serializeConfig } from '@editor/utils/editor';
|
|
12
12
|
|
|
13
13
|
defineOptions({
|
|
14
14
|
name: 'MFieldsCodeLink',
|
|
@@ -47,10 +47,7 @@ watch(
|
|
|
47
47
|
() => props.model[props.name],
|
|
48
48
|
(value) => {
|
|
49
49
|
modelValue.form = {
|
|
50
|
-
[props.name]:
|
|
51
|
-
space: 2,
|
|
52
|
-
unsafe: true,
|
|
53
|
-
}).replace(/"(\w+)":\s/g, '$1: '),
|
|
50
|
+
[props.name]: serializeConfig(value),
|
|
54
51
|
};
|
|
55
52
|
},
|
|
56
53
|
{
|
|
@@ -6,7 +6,7 @@ import { tMagicMessage } from '@tmagic/design';
|
|
|
6
6
|
import type { ContainerChangeEventData } from '@tmagic/form';
|
|
7
7
|
|
|
8
8
|
import CodeBlockEditor from '@editor/components/CodeBlockEditor.vue';
|
|
9
|
-
import type { Services } from '@editor/type';
|
|
9
|
+
import type { HistoryOpSource, Services } from '@editor/type';
|
|
10
10
|
|
|
11
11
|
export const useCodeBlockEdit = (codeBlockService: Services['codeBlockService']) => {
|
|
12
12
|
const codeConfig = ref<Omit<CodeBlockContent, 'content'> & { content: string }>();
|
|
@@ -58,8 +58,8 @@ export const useCodeBlockEdit = (codeBlockService: Services['codeBlockService'])
|
|
|
58
58
|
};
|
|
59
59
|
|
|
60
60
|
// 删除代码块
|
|
61
|
-
const deleteCode = async (key: string) => {
|
|
62
|
-
codeBlockService.deleteCodeDslByIds([key]);
|
|
61
|
+
const deleteCode = async (key: string, { historySource }: { historySource?: HistoryOpSource } = {}) => {
|
|
62
|
+
codeBlockService.deleteCodeDslByIds([key], { historySource });
|
|
63
63
|
};
|
|
64
64
|
|
|
65
65
|
const submitCodeBlockHandler = async (values: CodeBlockContent, eventData?: ContainerChangeEventData) => {
|
|
@@ -67,6 +67,7 @@ export const useCodeBlockEdit = (codeBlockService: Services['codeBlockService'])
|
|
|
67
67
|
|
|
68
68
|
await codeBlockService.setCodeDslById(codeId.value, values, {
|
|
69
69
|
changeRecords: eventData?.changeRecords,
|
|
70
|
+
historySource: 'props',
|
|
70
71
|
});
|
|
71
72
|
|
|
72
73
|
codeBlockEditorRef.value?.hide();
|
|
@@ -27,9 +27,9 @@ export const useDataSourceEdit = (dataSourceService: Services['dataSourceService
|
|
|
27
27
|
|
|
28
28
|
const submitDataSourceHandler = (value: DataSourceSchema, eventData: ContainerChangeEventData) => {
|
|
29
29
|
if (value.id) {
|
|
30
|
-
dataSourceService.update(value, { changeRecords: eventData.changeRecords });
|
|
30
|
+
dataSourceService.update(value, { changeRecords: eventData.changeRecords, historySource: 'props' });
|
|
31
31
|
} else {
|
|
32
|
-
dataSourceService.add(value);
|
|
32
|
+
dataSourceService.add(value, { historySource: 'props' });
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
editDialog.value?.hide();
|
package/src/hooks/use-stage.ts
CHANGED
|
@@ -48,6 +48,7 @@ export const useStage = (stageOptions: StageOptions) => {
|
|
|
48
48
|
disabledMultiSelect: stageOptions.disabledMultiSelect,
|
|
49
49
|
alwaysMultiSelect: stageOptions.alwaysMultiSelect,
|
|
50
50
|
disabledRule: stageOptions.disabledRule,
|
|
51
|
+
disabledFlashTip: stageOptions.disabledFlashTip,
|
|
51
52
|
});
|
|
52
53
|
|
|
53
54
|
watch(
|
|
@@ -129,16 +130,16 @@ export const useStage = (stageOptions: StageOptions) => {
|
|
|
129
130
|
});
|
|
130
131
|
if (configs.length === 0) return;
|
|
131
132
|
|
|
132
|
-
editorService.update(configs, { changeRecordList });
|
|
133
|
+
editorService.update(configs, { changeRecordList, historySource: 'stage' });
|
|
133
134
|
});
|
|
134
135
|
|
|
135
136
|
stage.on('sort', (ev: SortEventData) => {
|
|
136
|
-
editorService.sort(ev.src, ev.dist);
|
|
137
|
+
editorService.sort(ev.src, ev.dist, { historySource: 'stage' });
|
|
137
138
|
});
|
|
138
139
|
|
|
139
140
|
stage.on('remove', (ev: RemoveEventData) => {
|
|
140
141
|
const nodes = ev.data.map(({ el }) => editorService.getNodeById(getIdFromEl()(el) || ''));
|
|
141
|
-
editorService.remove(nodes.filter((node) => Boolean(node)) as MNode[]);
|
|
142
|
+
editorService.remove(nodes.filter((node) => Boolean(node)) as MNode[], { historySource: 'stage' });
|
|
142
143
|
});
|
|
143
144
|
|
|
144
145
|
stage.on('select-parent', () => {
|
package/src/index.ts
CHANGED
|
@@ -71,6 +71,8 @@ export { default as SplitView } from './components/SplitView.vue';
|
|
|
71
71
|
export { default as Resizer } from './components/Resizer.vue';
|
|
72
72
|
export { default as CodeBlockEditor } from './components/CodeBlockEditor.vue';
|
|
73
73
|
export { default as CompareForm } from './components/CompareForm.vue';
|
|
74
|
+
export { default as HistoryListBucket } from './layouts/history-list/Bucket.vue';
|
|
75
|
+
export { default as HistoryDiffDialog } from './layouts/history-list/HistoryDiffDialog.vue';
|
|
74
76
|
export { default as FloatingBox } from './components/FloatingBox.vue';
|
|
75
77
|
export { default as Tree } from './components/Tree.vue';
|
|
76
78
|
export { default as TreeNode } from './components/TreeNode.vue';
|
|
@@ -21,12 +21,12 @@ import { computed, nextTick, onBeforeUnmount, onMounted, onUnmounted, ref, useTe
|
|
|
21
21
|
import { FullScreen } from '@element-plus/icons-vue';
|
|
22
22
|
import { throttle } from 'lodash-es';
|
|
23
23
|
import type * as Monaco from 'monaco-editor';
|
|
24
|
-
import serialize from 'serialize-javascript';
|
|
25
24
|
|
|
26
25
|
import { TMagicButton } from '@tmagic/design';
|
|
27
26
|
|
|
28
27
|
import MIcon from '@editor/components/Icon.vue';
|
|
29
28
|
import { getEditorConfig } from '@editor/utils/config';
|
|
29
|
+
import { serializeConfig } from '@editor/utils/editor';
|
|
30
30
|
import loadMonaco from '@editor/utils/monaco-editor';
|
|
31
31
|
|
|
32
32
|
defineOptions({
|
|
@@ -163,10 +163,7 @@ const toString = (v: string | any, language: string): string => {
|
|
|
163
163
|
if (language === 'json') {
|
|
164
164
|
value = JSON.stringify(v, null, 2);
|
|
165
165
|
} else {
|
|
166
|
-
value =
|
|
167
|
-
space: 2,
|
|
168
|
-
unsafe: true,
|
|
169
|
-
}).replace(/"(\w+)":\s/g, '$1: ');
|
|
166
|
+
value = serializeConfig(v);
|
|
170
167
|
}
|
|
171
168
|
} else {
|
|
172
169
|
value = v;
|
package/src/layouts/NavMenu.vue
CHANGED
|
@@ -80,7 +80,7 @@ const getConfig = (item: MenuItem): (MenuButton | MenuComponent)[] => {
|
|
|
80
80
|
disabled: () => editorService.get('node')?.type === NodeType.PAGE,
|
|
81
81
|
handler: () => {
|
|
82
82
|
const node = editorService.get('node');
|
|
83
|
-
node && editorService.remove(node);
|
|
83
|
+
node && editorService.remove(node, { historySource: 'toolbar' });
|
|
84
84
|
},
|
|
85
85
|
});
|
|
86
86
|
break;
|
|
@@ -8,13 +8,16 @@
|
|
|
8
8
|
|
|
9
9
|
<ul class="m-editor-history-list-ul">
|
|
10
10
|
<GroupRow
|
|
11
|
-
v-for="
|
|
12
|
-
:key="`${prefix}-${bucketId}-${
|
|
13
|
-
:group-key="`${prefix}-${bucketId}-${
|
|
11
|
+
v-for="group in groups"
|
|
12
|
+
:key="`${prefix}-${bucketId}-${group.steps[0]?.index}`"
|
|
13
|
+
:group-key="`${prefix}-${bucketId}-${group.steps[0]?.index}`"
|
|
14
14
|
:applied="group.applied"
|
|
15
15
|
:merged="group.steps.length > 1"
|
|
16
16
|
:op-type="group.opType"
|
|
17
17
|
:desc="describeGroup(group)"
|
|
18
|
+
:source="groupSource(group)"
|
|
19
|
+
:time="formatHistoryTime(groupTimestamp(group))"
|
|
20
|
+
:time-title="formatHistoryFullTime(groupTimestamp(group))"
|
|
18
21
|
:step-count="group.steps.length"
|
|
19
22
|
:sub-steps="
|
|
20
23
|
group.steps.map((s: any) => ({
|
|
@@ -23,11 +26,15 @@
|
|
|
23
26
|
isCurrent: s.isCurrent,
|
|
24
27
|
desc: describeStep(s.step),
|
|
25
28
|
diffable: isStepDiffable ? isStepDiffable(s.step) : false,
|
|
26
|
-
revertable: s.applied,
|
|
29
|
+
revertable: s.applied && (isStepRevertable ? isStepRevertable(s.step) : true),
|
|
30
|
+
source: s.step.source,
|
|
31
|
+
time: formatHistoryTime(s.step.timestamp),
|
|
32
|
+
timeTitle: formatHistoryFullTime(s.step.timestamp),
|
|
27
33
|
}))
|
|
28
34
|
"
|
|
29
35
|
:is-current="group.isCurrent"
|
|
30
|
-
:expanded="!!expanded[`${prefix}-${bucketId}-${
|
|
36
|
+
:expanded="!!expanded[`${prefix}-${bucketId}-${group.steps[0]?.index}`]"
|
|
37
|
+
:goto-enabled="gotoEnabled"
|
|
31
38
|
@toggle="(key: string) => $emit('toggle', key)"
|
|
32
39
|
@goto="(index: number) => $emit('goto', bucketId, index)"
|
|
33
40
|
@diff-step="(index: number) => $emit('diff-step', bucketId, index)"
|
|
@@ -36,8 +43,14 @@
|
|
|
36
43
|
<!--
|
|
37
44
|
初始状态项:永远位于该 bucket 列表底部(同样按倒序展示,最底部 = 最早状态)。
|
|
38
45
|
当 bucket 内所有 group 都未 applied 时即为当前位置。
|
|
46
|
+
showInitial=false 时不展示(用于没有"撤销到初始状态"语义的自定义历史,如业务模块历史)。
|
|
39
47
|
-->
|
|
40
|
-
<InitialRow
|
|
48
|
+
<InitialRow
|
|
49
|
+
v-if="showInitial !== false"
|
|
50
|
+
:is-current="isInitial"
|
|
51
|
+
:goto-enabled="gotoEnabled"
|
|
52
|
+
@goto-initial="$emit('goto-initial', bucketId)"
|
|
53
|
+
/>
|
|
41
54
|
</ul>
|
|
42
55
|
</div>
|
|
43
56
|
</template>
|
|
@@ -47,6 +60,7 @@ import { computed } from 'vue';
|
|
|
47
60
|
|
|
48
61
|
import type { HistoryOpType } from '@editor/type';
|
|
49
62
|
|
|
63
|
+
import { formatHistoryFullTime, formatHistoryTime, groupSource, groupTimestamp } from './composables';
|
|
50
64
|
import GroupRow from './GroupRow.vue';
|
|
51
65
|
import InitialRow from './InitialRow.vue';
|
|
52
66
|
|
|
@@ -54,29 +68,44 @@ defineOptions({
|
|
|
54
68
|
name: 'MEditorHistoryListBucket',
|
|
55
69
|
});
|
|
56
70
|
|
|
57
|
-
const props =
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
71
|
+
const props = withDefaults(
|
|
72
|
+
defineProps<{
|
|
73
|
+
/** Bucket 标题,例如 "数据源" / "代码块",渲染在 bucket 头部。 */
|
|
74
|
+
title: string;
|
|
75
|
+
/** 当前 bucket 对应的目标 id(dataSource.id 或 codeBlock.id),同时用于组装子项的 key。 */
|
|
76
|
+
bucketId: string | number;
|
|
77
|
+
/**
|
|
78
|
+
* 子项 key 的命名空间前缀:内置 `ds` 表示数据源,`cb` 表示代码块;
|
|
79
|
+
* 业务方复用 Bucket 时可传入自定义前缀(如 `mod`)。与上层折叠状态 key 保持一致。
|
|
80
|
+
*/
|
|
81
|
+
prefix: string;
|
|
82
|
+
/** 是否展示底部「回到初始状态」入口,默认 true。无 undo cursor 语义的自定义历史可传 false 关闭。 */
|
|
83
|
+
showInitial?: boolean;
|
|
84
|
+
/** 当前 bucket 下的所有历史分组,按时间倒序展示(最近的操作在前)。 */
|
|
85
|
+
groups: {
|
|
86
|
+
applied: boolean;
|
|
87
|
+
isCurrent?: boolean;
|
|
88
|
+
opType: HistoryOpType;
|
|
89
|
+
steps: { index: number; applied: boolean; isCurrent?: boolean; step: any }[];
|
|
90
|
+
}[];
|
|
91
|
+
/** 组级描述文案生成器,接收一个 group,返回展示文本。由父组件按业务类型注入。 */
|
|
92
|
+
describeGroup: (_group: any) => string;
|
|
93
|
+
/** 单步描述文案生成器,接收一个 step,返回展示文本。用于合并组展开后的子步列表。 */
|
|
94
|
+
describeStep: (_step: any) => string;
|
|
95
|
+
/** 判断某个 step 是否可查看差异(前后值都存在)。由父组件按业务类型注入;不传则一律不展示差异入口。 */
|
|
96
|
+
isStepDiffable?: (_step: any) => boolean;
|
|
97
|
+
/** 判断某个 step 是否支持回滚(如更新需带 changeRecords)。由父组件按业务类型注入;不传则已应用即可回滚。 */
|
|
98
|
+
isStepRevertable?: (_step: any) => boolean;
|
|
99
|
+
/** 共享的折叠状态表(key -> 是否展开),由顶层 panel 统一维护以便跨 tab 复用。 */
|
|
100
|
+
expanded: Record<string, boolean>;
|
|
101
|
+
/** 是否支持「跳转到该记录」(goto)。默认 true。 */
|
|
102
|
+
gotoEnabled?: boolean;
|
|
103
|
+
}>(),
|
|
104
|
+
{
|
|
105
|
+
showInitial: true,
|
|
106
|
+
gotoEnabled: true,
|
|
107
|
+
},
|
|
108
|
+
);
|
|
80
109
|
|
|
81
110
|
defineEmits<{
|
|
82
111
|
/** 透传子组件 GroupRow 的 toggle,由上层 panel 更新 expanded。 */
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="!buckets.length" class="m-editor-history-list-empty">暂无操作记录</div>
|
|
3
|
+
<TMagicScrollbar v-else max-height="360px">
|
|
4
|
+
<Bucket
|
|
5
|
+
v-for="bucket in buckets"
|
|
6
|
+
:key="`${prefix}-${bucket.id}`"
|
|
7
|
+
:title="title"
|
|
8
|
+
:bucket-id="bucket.id"
|
|
9
|
+
:prefix="prefix"
|
|
10
|
+
:groups="bucket.groups"
|
|
11
|
+
:describe-group="describeGroup"
|
|
12
|
+
:describe-step="describeStep"
|
|
13
|
+
:is-step-diffable="isStepDiffable"
|
|
14
|
+
:is-step-revertable="isStepRevertable"
|
|
15
|
+
:expanded="expanded"
|
|
16
|
+
:goto-enabled="gotoEnabled"
|
|
17
|
+
@toggle="(key: string) => $emit('toggle', key)"
|
|
18
|
+
@goto="(id: string | number, index: number) => $emit('goto', id, index)"
|
|
19
|
+
@goto-initial="(id: string | number) => $emit('goto-initial', id)"
|
|
20
|
+
@diff-step="(id: string | number, index: number) => $emit('diff-step', id, index)"
|
|
21
|
+
@revert-step="(id: string | number, index: number) => $emit('revert-step', id, index)"
|
|
22
|
+
/>
|
|
23
|
+
</TMagicScrollbar>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<script lang="ts" setup>
|
|
27
|
+
import { TMagicScrollbar } from '@tmagic/design';
|
|
28
|
+
|
|
29
|
+
import Bucket from './Bucket.vue';
|
|
30
|
+
|
|
31
|
+
defineOptions({
|
|
32
|
+
name: 'MEditorHistoryListBucketTab',
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
withDefaults(
|
|
36
|
+
defineProps<{
|
|
37
|
+
/** bucket 头部展示的标题,例如 "数据源" / "代码块"。 */
|
|
38
|
+
title: string;
|
|
39
|
+
/** 子项 key 的命名空间前缀(`ds` 数据源 / `cb` 代码块),与上层折叠状态 key 保持一致。 */
|
|
40
|
+
prefix: string;
|
|
41
|
+
/**
|
|
42
|
+
* 已按目标 id 聚拢成的 bucket 列表,每个 bucket 内部的 groups 已按时间倒序排好。
|
|
43
|
+
* 空数组时显示空态。
|
|
44
|
+
*/
|
|
45
|
+
buckets: { id: string | number; groups: any[] }[];
|
|
46
|
+
/** 组级描述文案生成器,由父组件按业务类型注入。 */
|
|
47
|
+
describeGroup: (_group: any) => string;
|
|
48
|
+
/** 单步描述文案生成器,由父组件按业务类型注入。 */
|
|
49
|
+
describeStep: (_step: any) => string;
|
|
50
|
+
/** 判断某个 step 是否可查看差异(前后值都存在)。由父组件按业务类型注入。 */
|
|
51
|
+
isStepDiffable: (_step: any) => boolean;
|
|
52
|
+
/** 判断某个 step 是否支持回滚(如更新需带 changeRecords)。由父组件按业务类型注入;不传则已应用即可回滚。 */
|
|
53
|
+
isStepRevertable?: (_step: any) => boolean;
|
|
54
|
+
/**
|
|
55
|
+
* 共享的折叠状态表(key -> 是否展开),由顶层 panel 统一维护。
|
|
56
|
+
* 本 tab 使用 `${prefix}-${id}-${组内首步 index}` 作为 key——以稳定的 step 索引而非展示位置标识分组,
|
|
57
|
+
* 这样历史数据更新后已展开的分组状态仍能正确保持。
|
|
58
|
+
*/
|
|
59
|
+
expanded: Record<string, boolean>;
|
|
60
|
+
/** 是否支持「跳转到该记录」(goto),透传给 Bucket。默认 true。 */
|
|
61
|
+
gotoEnabled?: boolean;
|
|
62
|
+
}>(),
|
|
63
|
+
{
|
|
64
|
+
gotoEnabled: true,
|
|
65
|
+
},
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
defineEmits<{
|
|
69
|
+
/** 透传子组件 Bucket 的 toggle 事件给上层 panel,由其更新 expanded。 */
|
|
70
|
+
(_e: 'toggle', _key: string): void;
|
|
71
|
+
/** 透传 Bucket 的 goto 事件,携带目标 id 与目标 step 索引。 */
|
|
72
|
+
(_e: 'goto', _targetId: string | number, _index: number): void;
|
|
73
|
+
/** 透传 Bucket 的 goto-initial 事件,携带目标 id(回到该目标未修改时的状态)。 */
|
|
74
|
+
(_e: 'goto-initial', _targetId: string | number): void;
|
|
75
|
+
/** 透传 Bucket 的 diff-step 事件,携带目标 id 与 step 索引。 */
|
|
76
|
+
(_e: 'diff-step', _targetId: string | number, _index: number): void;
|
|
77
|
+
/** 透传 Bucket 的 revert-step 事件,携带目标 id 与 step 索引(类 git revert)。 */
|
|
78
|
+
(_e: 'revert-step', _targetId: string | number, _index: number): void;
|
|
79
|
+
}>();
|
|
80
|
+
</script>
|