@tmagic/editor 1.8.0-manmanyu.11 → 1.8.0-manmanyu.13
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/package.json +7 -7
- package/src/components/CompareForm.vue +10 -12
- package/src/layouts/history-list/BucketTab.vue +2 -2
- package/src/layouts/history-list/HistoryDiffDialog.vue +22 -6
- package/src/layouts/history-list/HistoryListPanel.vue +2 -2
- package/src/layouts/history-list/useHistoryRevert.ts +42 -27
- package/src/services/codeBlock.ts +4 -0
- package/src/services/dataSource.ts +3 -0
- package/src/services/editor.ts +37 -0
- package/src/type.ts +54 -0
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.8.0-manmanyu.
|
|
2
|
+
"version": "1.8.0-manmanyu.13",
|
|
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/
|
|
62
|
-
"@tmagic/form": "1.8.0-manmanyu.
|
|
63
|
-
"@tmagic/
|
|
64
|
-
"@tmagic/
|
|
65
|
-
"@tmagic/
|
|
61
|
+
"@tmagic/utils": "1.8.0-manmanyu.13",
|
|
62
|
+
"@tmagic/form": "1.8.0-manmanyu.13",
|
|
63
|
+
"@tmagic/table": "1.8.0-manmanyu.13",
|
|
64
|
+
"@tmagic/stage": "1.8.0-manmanyu.13",
|
|
65
|
+
"@tmagic/design": "1.8.0-manmanyu.13"
|
|
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.38",
|
|
79
|
-
"@tmagic/core": "1.8.0-manmanyu.
|
|
79
|
+
"@tmagic/core": "1.8.0-manmanyu.13"
|
|
80
80
|
},
|
|
81
81
|
"peerDependenciesMeta": {
|
|
82
82
|
"typescript": {
|
|
@@ -24,7 +24,6 @@ import { isEqual } from 'lodash-es';
|
|
|
24
24
|
import { type CodeBlockContent, type DataSourceSchema, HookType, type MNode } from '@tmagic/core';
|
|
25
25
|
import { type FormConfig, type FormState, type FormValue, MForm } from '@tmagic/form';
|
|
26
26
|
|
|
27
|
-
import { useServices } from '@editor/hooks/use-services';
|
|
28
27
|
import type { CompareCategory, CompareFormLoadConfig, Services } from '@editor/type';
|
|
29
28
|
import { getCodeBlockFormConfig } from '@editor/utils/code-block';
|
|
30
29
|
|
|
@@ -76,18 +75,15 @@ const props = withDefaults(
|
|
|
76
75
|
*/
|
|
77
76
|
loadConfig?: CompareFormLoadConfig;
|
|
78
77
|
/** 编辑器服务集合,由调用方传入(不再通过 inject('services') 获取)。 */
|
|
79
|
-
services
|
|
78
|
+
services?: Services;
|
|
80
79
|
}>(),
|
|
81
80
|
{
|
|
82
81
|
category: 'node',
|
|
83
82
|
labelWidth: '120px',
|
|
84
|
-
services: () => useServices(),
|
|
85
83
|
extendState: (state: FormState) => state,
|
|
86
84
|
},
|
|
87
85
|
);
|
|
88
86
|
|
|
89
|
-
const { propsService, dataSourceService, codeBlockService, editorService } = props.services;
|
|
90
|
-
|
|
91
87
|
provide('services', props.services);
|
|
92
88
|
|
|
93
89
|
const config = ref<FormConfig>([]);
|
|
@@ -192,17 +188,21 @@ const mergedExtendState = (state: FormState) => {
|
|
|
192
188
|
* 作为 ctx.defaultLoadConfig 透传给自定义 `loadConfig`,方便复用与二次加工。
|
|
193
189
|
*/
|
|
194
190
|
const defaultLoadConfig = async (): Promise<FormConfig> => {
|
|
191
|
+
if (!props.services) {
|
|
192
|
+
return [];
|
|
193
|
+
}
|
|
194
|
+
|
|
195
195
|
switch (props.category) {
|
|
196
196
|
case 'node': {
|
|
197
197
|
if (!props.type) {
|
|
198
198
|
return [];
|
|
199
199
|
}
|
|
200
200
|
return removeStyleDisplayConfig(
|
|
201
|
-
await propsService.getPropsConfig(props.type, { node: props.value as unknown as MNode }),
|
|
201
|
+
await props.services.propsService.getPropsConfig(props.type, { node: props.value as unknown as MNode }),
|
|
202
202
|
);
|
|
203
203
|
}
|
|
204
204
|
case 'data-source': {
|
|
205
|
-
const config = dataSourceService.getFormConfig(props.type || 'base');
|
|
205
|
+
const config = props.services.dataSourceService.getFormConfig(props.type || 'base');
|
|
206
206
|
// 数据源表单外层 tab 的「数据定义」项 status 为 'fields',tab-pane name 随之为 'fields'。
|
|
207
207
|
// 未显式设置 active 时,Tabs 默认取 '0',与 'fields' 不匹配会导致打开弹窗时无默认激活项,
|
|
208
208
|
// 这里与 DataSourceConfigPanel 保持一致,默认激活「数据定义」tab。
|
|
@@ -210,7 +210,7 @@ const defaultLoadConfig = async (): Promise<FormConfig> => {
|
|
|
210
210
|
}
|
|
211
211
|
case 'code-block': {
|
|
212
212
|
return getCodeBlockFormConfig({
|
|
213
|
-
paramColConfig: codeBlockService.getParamsColConfig(),
|
|
213
|
+
paramColConfig: props.services.codeBlockService.getParamsColConfig(),
|
|
214
214
|
// 通过传入 dataSourceType 间接表达"是数据源代码块"——在对比场景下 props.dataSourceType
|
|
215
215
|
// 由调用方按 step 上下文显式传入,未传则视为普通代码块,「执行时机」字段隐藏。
|
|
216
216
|
isDataSource: () => Boolean(props.dataSourceType),
|
|
@@ -258,11 +258,9 @@ const formRef = useTemplateRef<InstanceType<typeof MForm>>('form');
|
|
|
258
258
|
* - services:整个 useServices() 返回的服务集合;
|
|
259
259
|
* - stage:当前 editorService.get('stage') 的最新值。
|
|
260
260
|
*/
|
|
261
|
-
const stage = computed(() => editorService.get('stage'));
|
|
262
|
-
|
|
263
261
|
watchEffect(() => {
|
|
264
|
-
if (formRef.value) {
|
|
265
|
-
formRef.value.formState.stage = stage
|
|
262
|
+
if (formRef.value && props.services) {
|
|
263
|
+
formRef.value.formState.stage = props.services.editorService.get('stage');
|
|
266
264
|
formRef.value.formState.services = props.services;
|
|
267
265
|
}
|
|
268
266
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div v-if="!buckets.length" class="m-editor-history-list-empty">暂无操作记录</div>
|
|
3
3
|
<template v-else>
|
|
4
|
-
<div class="m-editor-history-list-toolbar">
|
|
4
|
+
<div v-if="config.showClear !== false" class="m-editor-history-list-toolbar">
|
|
5
5
|
<span class="m-editor-history-list-clear" :title="`清空${config.title}的历史记录`" @click="$emit('clear')"
|
|
6
6
|
>清空</span
|
|
7
7
|
>
|
|
@@ -38,7 +38,7 @@ defineOptions({
|
|
|
38
38
|
|
|
39
39
|
defineProps<{
|
|
40
40
|
/**
|
|
41
|
-
* 该类历史的整体渲染配置(title / prefix / describe* / isStep* / showInitial / gotoEnabled),
|
|
41
|
+
* 该类历史的整体渲染配置(title / prefix / describe* / isStep* / showInitial / gotoEnabled / showClear),
|
|
42
42
|
* 由父组件按业务类型注入并整体透传给 Bucket,避免逐项透传多个 props。
|
|
43
43
|
*/
|
|
44
44
|
config: HistoryBucketConfig<T>;
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
</TMagicRadioGroup>
|
|
22
22
|
|
|
23
23
|
<TMagicRadioGroup v-model="mode" size="small" class="m-editor-history-diff-dialog-mode">
|
|
24
|
-
<TMagicRadioButton value="before">与修改前对比</TMagicRadioButton>
|
|
24
|
+
<TMagicRadioButton value="before" :disabled="!hasValue">与修改前对比</TMagicRadioButton>
|
|
25
25
|
<TMagicRadioButton value="current" :disabled="!hasCurrent">与当前对比</TMagicRadioButton>
|
|
26
26
|
</TMagicRadioGroup>
|
|
27
27
|
</div>
|
|
@@ -81,7 +81,6 @@ import { TMagicButton, TMagicDialog, TMagicRadioButton, TMagicRadioGroup, TMagic
|
|
|
81
81
|
import type { FormState } from '@tmagic/form';
|
|
82
82
|
|
|
83
83
|
import CompareForm from '@editor/components/CompareForm.vue';
|
|
84
|
-
import { useServices } from '@editor/hooks/use-services';
|
|
85
84
|
import CodeEditor from '@editor/layouts/CodeEditor.vue';
|
|
86
85
|
import type { CompareCategory, CompareFormLoadConfig, DiffDialogPayload, Services } from '@editor/type';
|
|
87
86
|
|
|
@@ -92,7 +91,7 @@ defineOptions({
|
|
|
92
91
|
const props = withDefaults(
|
|
93
92
|
defineProps<{
|
|
94
93
|
/** 编辑器服务集合,由调用方传入(不再通过 inject('services') 获取)。 */
|
|
95
|
-
services
|
|
94
|
+
services?: Services;
|
|
96
95
|
/**
|
|
97
96
|
* 来自 Editor 顶层的 `extendFormState`,用于扩展 MForm.formState。
|
|
98
97
|
* 透传给 CompareForm,从而让差异对比时表单 item 中依赖业务上下文的
|
|
@@ -111,7 +110,6 @@ const props = withDefaults(
|
|
|
111
110
|
compareFormState?: FormState;
|
|
112
111
|
}>(),
|
|
113
112
|
{
|
|
114
|
-
services: () => useServices(),
|
|
115
113
|
width: '900px',
|
|
116
114
|
},
|
|
117
115
|
);
|
|
@@ -161,6 +159,23 @@ const dialogTitle = computed(() => (props.onConfirm ? '确认回滚' : '查看
|
|
|
161
159
|
|
|
162
160
|
const hasCurrent = computed(() => payload.value?.currentValue !== undefined && payload.value?.currentValue !== null);
|
|
163
161
|
|
|
162
|
+
/** 是否存在该步「修改后的值」:不存在(如仅删除)时「与修改前对比」无意义,置灰禁用。 */
|
|
163
|
+
const hasValue = computed(() => payload.value?.value !== undefined && payload.value?.value !== null);
|
|
164
|
+
|
|
165
|
+
/** 指定模式当前是否可用:before 依赖「修改后的值」,current 依赖「当前值」。 */
|
|
166
|
+
const isModeAvailable = (m: DiffMode): boolean => (m === 'current' ? hasCurrent.value : hasValue.value);
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* 计算 open 时的初始对比模式:
|
|
170
|
+
* - 调用方通过 payload.mode 指定且该模式可用时,优先使用指定模式;
|
|
171
|
+
* - 否则没有「修改后的值」但有当前值时(「与修改前对比」不可用),默认进入「与当前对比」;
|
|
172
|
+
* - 其余情况默认「与修改前对比」。
|
|
173
|
+
*/
|
|
174
|
+
const resolveInitialMode = (specified?: DiffMode): DiffMode => {
|
|
175
|
+
if (specified && isModeAvailable(specified)) return specified;
|
|
176
|
+
return !hasValue.value && hasCurrent.value ? 'current' : 'before';
|
|
177
|
+
};
|
|
178
|
+
|
|
164
179
|
/** 左侧(旧/参照)值 */
|
|
165
180
|
const leftValue = computed<Record<string, any>>(() => {
|
|
166
181
|
if (!payload.value) return {};
|
|
@@ -214,8 +229,9 @@ const targetText = computed(() => {
|
|
|
214
229
|
|
|
215
230
|
const open = (p: DiffDialogPayload) => {
|
|
216
231
|
payload.value = p;
|
|
217
|
-
//
|
|
218
|
-
mode
|
|
232
|
+
// 每次打开按 payload 重置对比模式(hasValue / hasCurrent 依赖 payload,需先赋值):
|
|
233
|
+
// 优先使用 payload.mode 指定的模式;不可用或未指定时按数据自动推断。
|
|
234
|
+
mode.value = resolveInitialMode(p.mode);
|
|
219
235
|
// 默认回到表单对比形态,避免残留上一次选择的源码模式
|
|
220
236
|
viewMode.value = 'form';
|
|
221
237
|
visible.value = true;
|
|
@@ -313,10 +313,10 @@ const onCodeBlockGotoInitial = (id: string | number) => {
|
|
|
313
313
|
/**
|
|
314
314
|
* 「单步回滚」与「查看差异」的完整逻辑收敛到 useHistoryRevert,面板与业务方共用:
|
|
315
315
|
* 二者均由 useHistoryRevert 内部按需动态挂载 HistoryDiffDialog,
|
|
316
|
-
* 业务方亦可直接 import useHistoryRevert(services) 调用,无需自行挂载任何弹窗。
|
|
316
|
+
* 业务方亦可直接 import useHistoryRevert(options, services) 调用,无需自行挂载任何弹窗。
|
|
317
317
|
*/
|
|
318
318
|
const { onPageRevert, onDataSourceRevert, onCodeBlockRevert, onPageDiff, onDataSourceDiff, onCodeBlockDiff } =
|
|
319
|
-
useHistoryRevert(
|
|
319
|
+
useHistoryRevert({ extendState: extendFormState, getPropsPanelFormState }, services);
|
|
320
320
|
|
|
321
321
|
/**
|
|
322
322
|
* 把内存中(已清空对应类别后的)历史状态重新写回 IndexedDB,
|
|
@@ -76,7 +76,7 @@ interface MountedDiffDialog {
|
|
|
76
76
|
const mountHistoryDiffDialog = async (
|
|
77
77
|
options: Pick<UseHistoryRevertOptions, 'appContext' | 'extendState'> &
|
|
78
78
|
CustomDiffFormOptions & {
|
|
79
|
-
services
|
|
79
|
+
services?: Services;
|
|
80
80
|
isConfirm?: boolean;
|
|
81
81
|
onClose?: () => void;
|
|
82
82
|
},
|
|
@@ -94,6 +94,7 @@ const mountHistoryDiffDialog = async (
|
|
|
94
94
|
loadConfig: options.loadConfig,
|
|
95
95
|
selfDiffFieldTypes: options.selfDiffFieldTypes,
|
|
96
96
|
compareFormState: options.compareFormState,
|
|
97
|
+
width: options.width,
|
|
97
98
|
onClose: options.onClose,
|
|
98
99
|
});
|
|
99
100
|
if (options.appContext) {
|
|
@@ -125,7 +126,7 @@ const confirmRevertWithDiffDialog = async (
|
|
|
125
126
|
payload: DiffDialogPayload,
|
|
126
127
|
options: Pick<UseHistoryRevertOptions, 'appContext' | 'extendState'> &
|
|
127
128
|
CustomDiffFormOptions & {
|
|
128
|
-
services
|
|
129
|
+
services?: Services;
|
|
129
130
|
},
|
|
130
131
|
): Promise<boolean> => {
|
|
131
132
|
const { instance, destroy } = await mountHistoryDiffDialog({
|
|
@@ -147,7 +148,7 @@ const viewHistoryDiffDialog = async (
|
|
|
147
148
|
payload: DiffDialogPayload,
|
|
148
149
|
options: Pick<UseHistoryRevertOptions, 'appContext' | 'extendState'> &
|
|
149
150
|
CustomDiffFormOptions & {
|
|
150
|
-
services
|
|
151
|
+
services?: Services;
|
|
151
152
|
},
|
|
152
153
|
): Promise<void> => {
|
|
153
154
|
// onClose 在用户关闭弹窗时才触发,此时 handle.destroy 早已赋值。
|
|
@@ -184,16 +185,15 @@ const viewHistoryDiffDialog = async (
|
|
|
184
185
|
* ```ts
|
|
185
186
|
* import { useHistoryRevert } from '@tmagic/editor';
|
|
186
187
|
*
|
|
187
|
-
* const { onPageRevert, onPageDiff } = useHistoryRevert(editorRef.value); // editorRef.value 即 Editor 暴露的 services
|
|
188
|
+
* const { onPageRevert, onPageDiff } = useHistoryRevert({}, editorRef.value); // editorRef.value 即 Editor 暴露的 services
|
|
188
189
|
* await onPageRevert(index); // 弹出差异 / 二次确认弹窗后回滚
|
|
189
190
|
* onPageDiff(index); // 弹出只读差异弹窗查看前后值差异
|
|
190
191
|
* ```
|
|
191
192
|
*/
|
|
192
|
-
export const useHistoryRevert = (
|
|
193
|
-
const { editorService, dataSourceService, codeBlockService, historyService } = services;
|
|
193
|
+
export const useHistoryRevert = (options: UseHistoryRevertOptions = {}, services?: Services) => {
|
|
194
194
|
// 自动捕获调用方所在组件的 appContext(在 setup 中调用时),业务方亦可显式覆盖。
|
|
195
195
|
const appContext = options.appContext ?? getCurrentInstance()?.appContext ?? null;
|
|
196
|
-
const { extendState, getPropsPanelFormState } = options;
|
|
196
|
+
const { extendState, getPropsPanelFormState, dialogWidth } = options;
|
|
197
197
|
|
|
198
198
|
/** 目标数据已被删除、无法回滚时的统一提示。 */
|
|
199
199
|
const showRevertTargetMissing = () => {
|
|
@@ -217,7 +217,13 @@ export const useHistoryRevert = (services: Services, options: UseHistoryRevertOp
|
|
|
217
217
|
*/
|
|
218
218
|
const runRevert = (payload: DiffDialogPayload | null, extra?: CustomDiffFormOptions): Promise<boolean> => {
|
|
219
219
|
if (payload) {
|
|
220
|
-
return confirmRevertWithDiffDialog(payload, {
|
|
220
|
+
return confirmRevertWithDiffDialog(payload, {
|
|
221
|
+
appContext,
|
|
222
|
+
extendState,
|
|
223
|
+
services,
|
|
224
|
+
...extra,
|
|
225
|
+
width: extra?.width ?? dialogWidth,
|
|
226
|
+
});
|
|
221
227
|
}
|
|
222
228
|
return confirmRevert();
|
|
223
229
|
};
|
|
@@ -226,8 +232,8 @@ export const useHistoryRevert = (services: Services, options: UseHistoryRevertOp
|
|
|
226
232
|
buildDiffPayload(
|
|
227
233
|
{
|
|
228
234
|
category: 'node',
|
|
229
|
-
groups: () => historyService.getHistoryGroups('page', editorService.get('page')?.id),
|
|
230
|
-
getCurrent: (id) => editorService.getNodeById(id) as Record<string, any> | null,
|
|
235
|
+
groups: () => services?.historyService.getHistoryGroups('page', services?.editorService.get('page')?.id) ?? [],
|
|
236
|
+
getCurrent: (id) => services?.editorService.getNodeById(id) as Record<string, any> | null,
|
|
231
237
|
resolveType: (n, o) => n.type || o.type || '',
|
|
232
238
|
resolveLabel: (n, o) => n.name || o.name || n.type || o.type || '',
|
|
233
239
|
},
|
|
@@ -238,8 +244,8 @@ export const useHistoryRevert = (services: Services, options: UseHistoryRevertOp
|
|
|
238
244
|
buildDiffPayload(
|
|
239
245
|
{
|
|
240
246
|
category: 'data-source',
|
|
241
|
-
groups: () => historyService.getHistoryGroups('dataSource'),
|
|
242
|
-
getCurrent: (id) => dataSourceService.getDataSourceById(`${id}`) as Record<string, any> | null,
|
|
247
|
+
groups: () => services?.historyService.getHistoryGroups('dataSource') ?? [],
|
|
248
|
+
getCurrent: (id) => services?.dataSourceService.getDataSourceById(`${id}`) as Record<string, any> | null,
|
|
243
249
|
resolveType: (n, o) => n.type || o.type || 'base',
|
|
244
250
|
resolveLabel: (n, o, id) => n.title || o.title || `${id}`,
|
|
245
251
|
},
|
|
@@ -251,8 +257,8 @@ export const useHistoryRevert = (services: Services, options: UseHistoryRevertOp
|
|
|
251
257
|
buildDiffPayload(
|
|
252
258
|
{
|
|
253
259
|
category: 'code-block',
|
|
254
|
-
groups: () => historyService.getHistoryGroups('codeBlock'),
|
|
255
|
-
getCurrent: (id) => codeBlockService.getCodeContentById(id) as Record<string, any> | null,
|
|
260
|
+
groups: () => services?.historyService.getHistoryGroups('codeBlock') ?? [],
|
|
261
|
+
getCurrent: (id) => services?.codeBlockService.getCodeContentById(id) as Record<string, any> | null,
|
|
256
262
|
resolveLabel: (n, o, id) => n.name || o.name || `${id}`,
|
|
257
263
|
},
|
|
258
264
|
index,
|
|
@@ -266,17 +272,17 @@ export const useHistoryRevert = (services: Services, options: UseHistoryRevertOp
|
|
|
266
272
|
* add(回滚即删除)即使目标已不在,也已达成「删除」目的,不视为失败。
|
|
267
273
|
*/
|
|
268
274
|
const isPageRevertTargetMissing = (index: number): boolean => {
|
|
269
|
-
const step = historyService.getStepList('page', editorService.get('page')?.id)[index]?.step;
|
|
275
|
+
const step = services?.historyService.getStepList('page', services?.editorService.get('page')?.id)[index]?.step;
|
|
270
276
|
if (!step) return false;
|
|
271
277
|
if (step.opType === 'update') {
|
|
272
278
|
return (step.diff ?? []).some((item) => {
|
|
273
279
|
const id = item.newSchema?.id ?? item.oldSchema?.id;
|
|
274
|
-
return id !== undefined && !editorService.getNodeById(id, false);
|
|
280
|
+
return id !== undefined && !services?.editorService.getNodeById(id, false);
|
|
275
281
|
});
|
|
276
282
|
}
|
|
277
283
|
if (step.opType === 'remove') {
|
|
278
284
|
return (step.diff ?? []).some(
|
|
279
|
-
(item) => item.parentId !== undefined && !editorService.getNodeById(item.parentId, false),
|
|
285
|
+
(item) => item.parentId !== undefined && !services?.editorService.getNodeById(item.parentId, false),
|
|
280
286
|
);
|
|
281
287
|
}
|
|
282
288
|
return false;
|
|
@@ -284,14 +290,14 @@ export const useHistoryRevert = (services: Services, options: UseHistoryRevertOp
|
|
|
284
290
|
|
|
285
291
|
/** 数据源 update 步骤回滚时,对应数据源必须仍存在(已删除则无处写回旧值)。 */
|
|
286
292
|
const isDataSourceRevertTargetMissing = (id: string | number, index: number): boolean => {
|
|
287
|
-
const step = historyService.getStepList('dataSource', id)[index]?.step;
|
|
288
|
-
return Boolean(step?.opType === 'update' && !dataSourceService.getDataSourceById(`${id}`));
|
|
293
|
+
const step = services?.historyService.getStepList('dataSource', id)[index]?.step;
|
|
294
|
+
return Boolean(step?.opType === 'update' && !services?.dataSourceService.getDataSourceById(`${id}`));
|
|
289
295
|
};
|
|
290
296
|
|
|
291
297
|
/** 代码块 update 步骤回滚时,对应代码块必须仍存在(已删除则无处写回旧值)。 */
|
|
292
298
|
const isCodeBlockRevertTargetMissing = (id: string | number, index: number): boolean => {
|
|
293
|
-
const step = historyService.getStepList('codeBlock', id)[index]?.step;
|
|
294
|
-
return Boolean(step?.opType === 'update' && !codeBlockService.getCodeContentById(id));
|
|
299
|
+
const step = services?.historyService.getStepList('codeBlock', id)[index]?.step;
|
|
300
|
+
return Boolean(step?.opType === 'update' && !services?.codeBlockService.getCodeContentById(id));
|
|
295
301
|
};
|
|
296
302
|
|
|
297
303
|
const onPageRevert = (index: number) => {
|
|
@@ -300,7 +306,7 @@ export const useHistoryRevert = (services: Services, options: UseHistoryRevertOp
|
|
|
300
306
|
return Promise.resolve(null);
|
|
301
307
|
}
|
|
302
308
|
return runRevert(buildPageDiffPayload(index), { compareFormState: getPropsPanelFormState?.() }).then((result) =>
|
|
303
|
-
result ? editorService.revertPageStep(index) : null,
|
|
309
|
+
result ? services?.editorService.revertPageStep(index) : null,
|
|
304
310
|
);
|
|
305
311
|
};
|
|
306
312
|
|
|
@@ -310,7 +316,7 @@ export const useHistoryRevert = (services: Services, options: UseHistoryRevertOp
|
|
|
310
316
|
return Promise.resolve(null);
|
|
311
317
|
}
|
|
312
318
|
return runRevert(buildDataSourceDiffPayload(id, index)).then((result) =>
|
|
313
|
-
result ? dataSourceService.revert(id, index) : null,
|
|
319
|
+
result ? services?.dataSourceService.revert(id, index) : null,
|
|
314
320
|
);
|
|
315
321
|
};
|
|
316
322
|
|
|
@@ -320,7 +326,7 @@ export const useHistoryRevert = (services: Services, options: UseHistoryRevertOp
|
|
|
320
326
|
return Promise.resolve(null);
|
|
321
327
|
}
|
|
322
328
|
return runRevert(buildCodeBlockDiffPayload(id, index)).then((result) =>
|
|
323
|
-
result ? codeBlockService.revert(id, index) : null,
|
|
329
|
+
result ? services?.codeBlockService.revert(id, index) : null,
|
|
324
330
|
);
|
|
325
331
|
};
|
|
326
332
|
|
|
@@ -335,6 +341,7 @@ export const useHistoryRevert = (services: Services, options: UseHistoryRevertOp
|
|
|
335
341
|
appContext,
|
|
336
342
|
extendState,
|
|
337
343
|
services,
|
|
344
|
+
width: dialogWidth,
|
|
338
345
|
compareFormState: getPropsPanelFormState?.(),
|
|
339
346
|
});
|
|
340
347
|
}
|
|
@@ -342,12 +349,12 @@ export const useHistoryRevert = (services: Services, options: UseHistoryRevertOp
|
|
|
342
349
|
|
|
343
350
|
const onDataSourceDiff = (id: string | number, index: number): Promise<void> | void => {
|
|
344
351
|
const payload = buildDataSourceDiffPayload(id, index);
|
|
345
|
-
if (payload) return viewHistoryDiffDialog(payload, { appContext, extendState, services });
|
|
352
|
+
if (payload) return viewHistoryDiffDialog(payload, { appContext, extendState, services, width: dialogWidth });
|
|
346
353
|
};
|
|
347
354
|
|
|
348
355
|
const onCodeBlockDiff = (id: string | number, index: number): Promise<void> | void => {
|
|
349
356
|
const payload = buildCodeBlockDiffPayload(id, index);
|
|
350
|
-
if (payload) return viewHistoryDiffDialog(payload, { appContext, extendState, services });
|
|
357
|
+
if (payload) return viewHistoryDiffDialog(payload, { appContext, extendState, services, width: dialogWidth });
|
|
351
358
|
};
|
|
352
359
|
|
|
353
360
|
/**
|
|
@@ -374,6 +381,7 @@ export const useHistoryRevert = (services: Services, options: UseHistoryRevertOp
|
|
|
374
381
|
const confirmed = await runRevert(revertOptions.diffPayload ?? null, {
|
|
375
382
|
loadConfig: revertOptions.loadConfig,
|
|
376
383
|
selfDiffFieldTypes: revertOptions.selfDiffFieldTypes,
|
|
384
|
+
width: revertOptions.width,
|
|
377
385
|
});
|
|
378
386
|
if (!confirmed) return null;
|
|
379
387
|
return await revertOptions.revert();
|
|
@@ -384,7 +392,14 @@ export const useHistoryRevert = (services: Services, options: UseHistoryRevertOp
|
|
|
384
392
|
* 可透传 `loadConfig` / `selfDiffFieldTypes`。payload 为 null(不可对比)时静默返回。
|
|
385
393
|
*/
|
|
386
394
|
const viewDiff = (payload: DiffDialogPayload | null, extra?: CustomDiffFormOptions): Promise<void> | void => {
|
|
387
|
-
if (payload)
|
|
395
|
+
if (payload)
|
|
396
|
+
return viewHistoryDiffDialog(payload, {
|
|
397
|
+
appContext,
|
|
398
|
+
extendState,
|
|
399
|
+
services,
|
|
400
|
+
...extra,
|
|
401
|
+
width: extra?.width ?? dialogWidth,
|
|
402
|
+
});
|
|
388
403
|
};
|
|
389
404
|
|
|
390
405
|
return {
|
|
@@ -85,6 +85,7 @@ class CodeBlock extends BaseService {
|
|
|
85
85
|
public async setCodeDsl(codeDsl: CodeBlockDSL): Promise<void> {
|
|
86
86
|
this.state.codeDsl = codeDsl;
|
|
87
87
|
this.emit('code-dsl-change', this.state.codeDsl);
|
|
88
|
+
this.emit('change', this.state.codeDsl);
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
/**
|
|
@@ -199,6 +200,7 @@ class CodeBlock extends BaseService {
|
|
|
199
200
|
}
|
|
200
201
|
|
|
201
202
|
this.emit('addOrUpdate', id, codeDsl[id]);
|
|
203
|
+
this.emit('change', this.getCodeDsl());
|
|
202
204
|
}
|
|
203
205
|
|
|
204
206
|
/**
|
|
@@ -318,6 +320,8 @@ class CodeBlock extends BaseService {
|
|
|
318
320
|
|
|
319
321
|
this.emit('remove', id);
|
|
320
322
|
});
|
|
323
|
+
|
|
324
|
+
this.emit('change', this.getCodeDsl());
|
|
321
325
|
}
|
|
322
326
|
|
|
323
327
|
// #region AndGetHistoryId
|
|
@@ -147,6 +147,7 @@ class DataSource extends BaseService {
|
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
this.emit('add', newConfig);
|
|
150
|
+
this.emit('change', this.get('dataSources'));
|
|
150
151
|
|
|
151
152
|
return newConfig;
|
|
152
153
|
}
|
|
@@ -192,6 +193,7 @@ class DataSource extends BaseService {
|
|
|
192
193
|
oldConfig,
|
|
193
194
|
changeRecords,
|
|
194
195
|
});
|
|
196
|
+
this.emit('change', this.get('dataSources'));
|
|
195
197
|
|
|
196
198
|
return newConfig;
|
|
197
199
|
}
|
|
@@ -220,6 +222,7 @@ class DataSource extends BaseService {
|
|
|
220
222
|
}
|
|
221
223
|
|
|
222
224
|
this.emit('remove', id);
|
|
225
|
+
this.emit('change', this.get('dataSources'));
|
|
223
226
|
}
|
|
224
227
|
|
|
225
228
|
// #region AndGetHistoryId
|
package/src/services/editor.ts
CHANGED
|
@@ -535,6 +535,10 @@ class Editor extends BaseService {
|
|
|
535
535
|
}
|
|
536
536
|
|
|
537
537
|
this.emit('add', newNodes);
|
|
538
|
+
this.emit('change', {
|
|
539
|
+
type: 'add',
|
|
540
|
+
data: newNodes.map((node) => ({ node, page: this.getPageOfNode(node.id) })),
|
|
541
|
+
});
|
|
538
542
|
|
|
539
543
|
return Array.isArray(addNode) ? newNodes : newNodes[0];
|
|
540
544
|
}
|
|
@@ -635,6 +639,9 @@ class Editor extends BaseService {
|
|
|
635
639
|
|
|
636
640
|
const nodes = Array.isArray(nodeOrNodeList) ? nodeOrNodeList : [nodeOrNodeList];
|
|
637
641
|
|
|
642
|
+
// 删除后节点已从树中移除,无法再反查所属 page,这里在删除前先逐个捕获用于 `change` 事件
|
|
643
|
+
const changeItems = nodes.map((node) => ({ node, page: this.getPageOfNode(node.id) }));
|
|
644
|
+
|
|
638
645
|
const removedItems: StepDiffItem<MNode>[] = [];
|
|
639
646
|
let pageForOp: { name: string; id: Id } | null = null;
|
|
640
647
|
if (!(isPage(nodes[0]) || isPageFragment(nodes[0]))) {
|
|
@@ -670,6 +677,7 @@ class Editor extends BaseService {
|
|
|
670
677
|
}
|
|
671
678
|
|
|
672
679
|
this.emit('remove', nodes);
|
|
680
|
+
this.emit('change', { type: 'remove', data: changeItems });
|
|
673
681
|
}
|
|
674
682
|
|
|
675
683
|
public async doUpdate(
|
|
@@ -805,6 +813,10 @@ class Editor extends BaseService {
|
|
|
805
813
|
}
|
|
806
814
|
|
|
807
815
|
this.emit('update', updateData);
|
|
816
|
+
this.emit('change', {
|
|
817
|
+
type: 'update',
|
|
818
|
+
data: updateData.map((node) => ({ node, page: this.getPageOfNode(node.newNode.id) })),
|
|
819
|
+
});
|
|
808
820
|
return Array.isArray(config) ? updateData.map((item) => item.newNode) : updateData[0].newNode;
|
|
809
821
|
}
|
|
810
822
|
|
|
@@ -1046,6 +1058,11 @@ class Editor extends BaseService {
|
|
|
1046
1058
|
}
|
|
1047
1059
|
|
|
1048
1060
|
this.emit('move-layer', offset);
|
|
1061
|
+
this.emit('change', {
|
|
1062
|
+
type: 'move-layer',
|
|
1063
|
+
data: [{ node, page: this.getPageOfNode(node.id) }],
|
|
1064
|
+
offset,
|
|
1065
|
+
});
|
|
1049
1066
|
}
|
|
1050
1067
|
|
|
1051
1068
|
/**
|
|
@@ -1225,6 +1242,12 @@ class Editor extends BaseService {
|
|
|
1225
1242
|
}
|
|
1226
1243
|
|
|
1227
1244
|
this.emit('drag-to', { targetIndex, configs, targetParent });
|
|
1245
|
+
this.emit('change', {
|
|
1246
|
+
type: 'drag-to',
|
|
1247
|
+
data: configs.map((node) => ({ node, page: this.getPageOfNode(node.id) })),
|
|
1248
|
+
targetIndex,
|
|
1249
|
+
targetParent,
|
|
1250
|
+
});
|
|
1228
1251
|
}
|
|
1229
1252
|
|
|
1230
1253
|
// #region AndGetHistoryId
|
|
@@ -1558,6 +1581,20 @@ class Editor extends BaseService {
|
|
|
1558
1581
|
this.get('modifiedNodeIds').set(id, id);
|
|
1559
1582
|
}
|
|
1560
1583
|
|
|
1584
|
+
/**
|
|
1585
|
+
* 获取指定节点所属的页面 / 页面片:
|
|
1586
|
+
* - 普通节点返回其所在的 page;
|
|
1587
|
+
* - 节点本身就是 page / pageFragment 时返回它自己;
|
|
1588
|
+
* - 找不到时返回 null。
|
|
1589
|
+
* 供 `change` 事件携带「变更节点对应的 page」(而非编辑器当前选中页)。
|
|
1590
|
+
*/
|
|
1591
|
+
private getPageOfNode(id: Id): MPage | MPageFragment | null {
|
|
1592
|
+
const { node, page } = this.getNodeInfo(id, false);
|
|
1593
|
+
if (page) return page;
|
|
1594
|
+
if (node && (isPage(node) || isPageFragment(node))) return node as MPage | MPageFragment;
|
|
1595
|
+
return null;
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1561
1598
|
private captureSelectionBeforeOp() {
|
|
1562
1599
|
if (this.selectionBeforeOp) return;
|
|
1563
1600
|
this.selectionBeforeOp = this.get('nodes').map((n) => n.id);
|
package/src/type.ts
CHANGED
|
@@ -1218,8 +1218,41 @@ export interface EditorEvents {
|
|
|
1218
1218
|
'move-layer': [offset: number | LayerOffset];
|
|
1219
1219
|
'drag-to': [data: { targetIndex: number; configs: MNode | MNode[]; targetParent: MContainer }];
|
|
1220
1220
|
'history-change': [data: MPage | MPageFragment];
|
|
1221
|
+
/**
|
|
1222
|
+
* DSL 发生变更后统一触发,免去分别监听 add / remove / update / move-layer / drag-to。
|
|
1223
|
+
* 回调参数为 {@link EditorChangeEvent},按 `type` 区分操作类型并携带各自的操作内容(payload)
|
|
1224
|
+
* 以及变更所在的当前 page(可能为 null)。撤销 / 重做内部同样会经由
|
|
1225
|
+
* add / remove / update 触发本事件;如需区分「用户操作」与「撤销重做」请配合 `history-change`。
|
|
1226
|
+
*/
|
|
1227
|
+
change: [event: EditorChangeEvent];
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
// #region EditorChangeEvent
|
|
1231
|
+
/** `change` 事件中单个变更项:变更的 node 及其所属的 page(可能为 null)。 */
|
|
1232
|
+
export interface EditorChangeItem {
|
|
1233
|
+
node: MNode;
|
|
1234
|
+
page: StoreState['page'];
|
|
1221
1235
|
}
|
|
1222
1236
|
|
|
1237
|
+
/** `update` 类型变更项:node 为前后快照及 form 端变更记录,page 为其所属页面。 */
|
|
1238
|
+
export interface EditorUpdateChangeItem {
|
|
1239
|
+
node: { newNode: MNode; oldNode: MNode; changeRecords?: ChangeRecord[] };
|
|
1240
|
+
page: StoreState['page'];
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
/**
|
|
1244
|
+
* {@link EditorEvents.change} 的回调参数:以 `type` 区分操作类型,并携带对应的操作内容。
|
|
1245
|
+
* `data` 为本次变更的节点列表,每项包含 node 及其所属的 page(可能为 null);
|
|
1246
|
+
* `move-layer` 额外带层级偏移 `offset`,`drag-to` 额外带目标位置 `targetIndex` / `targetParent`。
|
|
1247
|
+
*/
|
|
1248
|
+
export type EditorChangeEvent =
|
|
1249
|
+
| { type: 'add'; data: EditorChangeItem[] }
|
|
1250
|
+
| { type: 'remove'; data: EditorChangeItem[] }
|
|
1251
|
+
| { type: 'update'; data: EditorUpdateChangeItem[] }
|
|
1252
|
+
| { type: 'move-layer'; data: EditorChangeItem[]; offset: number | LayerOffset }
|
|
1253
|
+
| { type: 'drag-to'; data: EditorChangeItem[]; targetIndex: number; targetParent: MContainer };
|
|
1254
|
+
// #endregion EditorChangeEvent
|
|
1255
|
+
|
|
1223
1256
|
export interface HistoryEvents {
|
|
1224
1257
|
change: [
|
|
1225
1258
|
state: BaseStepValue | StepValue | CodeBlockStepValue | DataSourceStepValue,
|
|
@@ -1319,6 +1352,14 @@ export interface DiffDialogPayload {
|
|
|
1319
1352
|
targetLabel?: string;
|
|
1320
1353
|
/** 用于标题展示的目标 id */
|
|
1321
1354
|
id?: string | number;
|
|
1355
|
+
/**
|
|
1356
|
+
* 指定打开时的初始对比模式:
|
|
1357
|
+
* - before:该步骤修改前 vs 修改后
|
|
1358
|
+
* - current:该步骤修改后 vs 当前最新值
|
|
1359
|
+
* 不传时按是否存在「修改后的值」/「当前值」自动推断。
|
|
1360
|
+
* 若指定的模式当前不可用(对应数据缺失),将回退到自动推断结果。
|
|
1361
|
+
*/
|
|
1362
|
+
mode?: 'before' | 'current';
|
|
1322
1363
|
}
|
|
1323
1364
|
|
|
1324
1365
|
/**
|
|
@@ -1353,6 +1394,8 @@ export interface HistoryBucketConfig<T extends BaseStepValue = BaseStepValue> ex
|
|
|
1353
1394
|
showInitial?: boolean;
|
|
1354
1395
|
/** 是否支持「跳转到该记录」(goto),默认 true。 */
|
|
1355
1396
|
gotoEnabled?: boolean;
|
|
1397
|
+
/** 是否展示顶部「清空」按钮,默认 true。无需提供清空能力的自定义历史可传 false。 */
|
|
1398
|
+
showClear?: boolean;
|
|
1356
1399
|
}
|
|
1357
1400
|
|
|
1358
1401
|
export interface UseHistoryRevertOptions {
|
|
@@ -1373,6 +1416,12 @@ export interface UseHistoryRevertOptions {
|
|
|
1373
1416
|
* 以保证两处 filterFunction 读取到一致的运行态上下文。
|
|
1374
1417
|
*/
|
|
1375
1418
|
getPropsPanelFormState?: () => FormState | undefined;
|
|
1419
|
+
/**
|
|
1420
|
+
* 内置页面 / 数据源 / 代码块的差异 / 回滚确认弹窗默认宽度(透传给 TMagicDialog 的 `width`),
|
|
1421
|
+
* 如 `'1200px'` / `'80%'`。缺省时使用弹窗内置默认宽度(900px)。
|
|
1422
|
+
* 业务自有历史(`viewDiff` / `confirmAndRevert`)可在调用时通过各自入参的 `width` 单独覆盖。
|
|
1423
|
+
*/
|
|
1424
|
+
dialogWidth?: string;
|
|
1376
1425
|
}
|
|
1377
1426
|
|
|
1378
1427
|
/**
|
|
@@ -1393,6 +1442,11 @@ export interface CustomDiffFormOptions {
|
|
|
1393
1442
|
* 对比弹窗会用它覆盖 CompareForm 中同名扩展字段,避免上下文不一致。
|
|
1394
1443
|
*/
|
|
1395
1444
|
compareFormState?: FormState;
|
|
1445
|
+
/**
|
|
1446
|
+
* 差异 / 确认回滚弹窗宽度(透传给 HistoryDiffDialog 的 TMagicDialog `width`),
|
|
1447
|
+
* 如 `'1200px'` / `'80%'`。缺省时使用弹窗内置默认宽度(900px)。
|
|
1448
|
+
*/
|
|
1449
|
+
width?: string;
|
|
1396
1450
|
}
|
|
1397
1451
|
|
|
1398
1452
|
/**
|