mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2026-06-27 10:38:28 +08:00
refactor(editor): 历史对比 services 改为显式传入并可选
CompareForm / HistoryDiffDialog / useHistoryRevert 不再通过 useServices() 默认 inject 获取 services,改为由调用方显式传入并设为可选,避免在 createApp 动态挂载游离弹窗场景下 inject 链不可靠。同步更新调用点、 测试与文档示例签名。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
5ea992f5f7
commit
5e661d0958
@ -82,7 +82,7 @@ const {
|
||||
onPageDiff,
|
||||
onDataSourceDiff,
|
||||
onCodeBlockDiff,
|
||||
} = useHistoryRevert(editorRef.value);
|
||||
} = useHistoryRevert({}, editorRef.value);
|
||||
|
||||
// 回滚:可差异步骤弹出差异确认弹窗、其余步骤弹普通二次确认框;用户点「确定」后回滚第 index 步,
|
||||
// 命中前置校验或用户取消时不执行,返回 null
|
||||
|
||||
@ -24,7 +24,6 @@ import { isEqual } from 'lodash-es';
|
||||
import { type CodeBlockContent, type DataSourceSchema, HookType, type MNode } from '@tmagic/core';
|
||||
import { type FormConfig, type FormState, type FormValue, MForm } from '@tmagic/form';
|
||||
|
||||
import { useServices } from '@editor/hooks/use-services';
|
||||
import type { CompareCategory, CompareFormLoadConfig, Services } from '@editor/type';
|
||||
import { getCodeBlockFormConfig } from '@editor/utils/code-block';
|
||||
|
||||
@ -76,18 +75,15 @@ const props = withDefaults(
|
||||
*/
|
||||
loadConfig?: CompareFormLoadConfig;
|
||||
/** 编辑器服务集合,由调用方传入(不再通过 inject('services') 获取)。 */
|
||||
services: Services;
|
||||
services?: Services;
|
||||
}>(),
|
||||
{
|
||||
category: 'node',
|
||||
labelWidth: '120px',
|
||||
services: () => useServices(),
|
||||
extendState: (state: FormState) => state,
|
||||
},
|
||||
);
|
||||
|
||||
const { propsService, dataSourceService, codeBlockService, editorService } = props.services;
|
||||
|
||||
provide('services', props.services);
|
||||
|
||||
const config = ref<FormConfig>([]);
|
||||
@ -192,17 +188,21 @@ const mergedExtendState = (state: FormState) => {
|
||||
* 作为 ctx.defaultLoadConfig 透传给自定义 `loadConfig`,方便复用与二次加工。
|
||||
*/
|
||||
const defaultLoadConfig = async (): Promise<FormConfig> => {
|
||||
if (!props.services) {
|
||||
return [];
|
||||
}
|
||||
|
||||
switch (props.category) {
|
||||
case 'node': {
|
||||
if (!props.type) {
|
||||
return [];
|
||||
}
|
||||
return removeStyleDisplayConfig(
|
||||
await propsService.getPropsConfig(props.type, { node: props.value as unknown as MNode }),
|
||||
await props.services.propsService.getPropsConfig(props.type, { node: props.value as unknown as MNode }),
|
||||
);
|
||||
}
|
||||
case 'data-source': {
|
||||
const config = dataSourceService.getFormConfig(props.type || 'base');
|
||||
const config = props.services.dataSourceService.getFormConfig(props.type || 'base');
|
||||
// 数据源表单外层 tab 的「数据定义」项 status 为 'fields',tab-pane name 随之为 'fields'。
|
||||
// 未显式设置 active 时,Tabs 默认取 '0',与 'fields' 不匹配会导致打开弹窗时无默认激活项,
|
||||
// 这里与 DataSourceConfigPanel 保持一致,默认激活「数据定义」tab。
|
||||
@ -210,7 +210,7 @@ const defaultLoadConfig = async (): Promise<FormConfig> => {
|
||||
}
|
||||
case 'code-block': {
|
||||
return getCodeBlockFormConfig({
|
||||
paramColConfig: codeBlockService.getParamsColConfig(),
|
||||
paramColConfig: props.services.codeBlockService.getParamsColConfig(),
|
||||
// 通过传入 dataSourceType 间接表达"是数据源代码块"——在对比场景下 props.dataSourceType
|
||||
// 由调用方按 step 上下文显式传入,未传则视为普通代码块,「执行时机」字段隐藏。
|
||||
isDataSource: () => Boolean(props.dataSourceType),
|
||||
@ -258,11 +258,9 @@ const formRef = useTemplateRef<InstanceType<typeof MForm>>('form');
|
||||
* - services:整个 useServices() 返回的服务集合;
|
||||
* - stage:当前 editorService.get('stage') 的最新值。
|
||||
*/
|
||||
const stage = computed(() => editorService.get('stage'));
|
||||
|
||||
watchEffect(() => {
|
||||
if (formRef.value) {
|
||||
formRef.value.formState.stage = stage.value;
|
||||
if (formRef.value && props.services) {
|
||||
formRef.value.formState.stage = props.services.editorService.get('stage');
|
||||
formRef.value.formState.services = props.services;
|
||||
}
|
||||
});
|
||||
|
||||
@ -81,7 +81,6 @@ import { TMagicButton, TMagicDialog, TMagicRadioButton, TMagicRadioGroup, TMagic
|
||||
import type { FormState } from '@tmagic/form';
|
||||
|
||||
import CompareForm from '@editor/components/CompareForm.vue';
|
||||
import { useServices } from '@editor/hooks/use-services';
|
||||
import CodeEditor from '@editor/layouts/CodeEditor.vue';
|
||||
import type { CompareCategory, CompareFormLoadConfig, DiffDialogPayload, Services } from '@editor/type';
|
||||
|
||||
@ -92,7 +91,7 @@ defineOptions({
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
/** 编辑器服务集合,由调用方传入(不再通过 inject('services') 获取)。 */
|
||||
services: Services;
|
||||
services?: Services;
|
||||
/**
|
||||
* 来自 Editor 顶层的 `extendFormState`,用于扩展 MForm.formState。
|
||||
* 透传给 CompareForm,从而让差异对比时表单 item 中依赖业务上下文的
|
||||
@ -111,7 +110,6 @@ const props = withDefaults(
|
||||
compareFormState?: FormState;
|
||||
}>(),
|
||||
{
|
||||
services: () => useServices(),
|
||||
width: '900px',
|
||||
},
|
||||
);
|
||||
|
||||
@ -313,10 +313,10 @@ const onCodeBlockGotoInitial = (id: string | number) => {
|
||||
/**
|
||||
* 「单步回滚」与「查看差异」的完整逻辑收敛到 useHistoryRevert,面板与业务方共用:
|
||||
* 二者均由 useHistoryRevert 内部按需动态挂载 HistoryDiffDialog,
|
||||
* 业务方亦可直接 import useHistoryRevert(services) 调用,无需自行挂载任何弹窗。
|
||||
* 业务方亦可直接 import useHistoryRevert(options, services) 调用,无需自行挂载任何弹窗。
|
||||
*/
|
||||
const { onPageRevert, onDataSourceRevert, onCodeBlockRevert, onPageDiff, onDataSourceDiff, onCodeBlockDiff } =
|
||||
useHistoryRevert(services, { extendState: extendFormState, getPropsPanelFormState });
|
||||
useHistoryRevert({ extendState: extendFormState, getPropsPanelFormState }, services);
|
||||
|
||||
/**
|
||||
* 把内存中(已清空对应类别后的)历史状态重新写回 IndexedDB,
|
||||
|
||||
@ -76,7 +76,7 @@ interface MountedDiffDialog {
|
||||
const mountHistoryDiffDialog = async (
|
||||
options: Pick<UseHistoryRevertOptions, 'appContext' | 'extendState'> &
|
||||
CustomDiffFormOptions & {
|
||||
services: Services;
|
||||
services?: Services;
|
||||
isConfirm?: boolean;
|
||||
onClose?: () => void;
|
||||
},
|
||||
@ -125,7 +125,7 @@ const confirmRevertWithDiffDialog = async (
|
||||
payload: DiffDialogPayload,
|
||||
options: Pick<UseHistoryRevertOptions, 'appContext' | 'extendState'> &
|
||||
CustomDiffFormOptions & {
|
||||
services: Services;
|
||||
services?: Services;
|
||||
},
|
||||
): Promise<boolean> => {
|
||||
const { instance, destroy } = await mountHistoryDiffDialog({
|
||||
@ -147,7 +147,7 @@ const viewHistoryDiffDialog = async (
|
||||
payload: DiffDialogPayload,
|
||||
options: Pick<UseHistoryRevertOptions, 'appContext' | 'extendState'> &
|
||||
CustomDiffFormOptions & {
|
||||
services: Services;
|
||||
services?: Services;
|
||||
},
|
||||
): Promise<void> => {
|
||||
// onClose 在用户关闭弹窗时才触发,此时 handle.destroy 早已赋值。
|
||||
@ -184,13 +184,12 @@ const viewHistoryDiffDialog = async (
|
||||
* ```ts
|
||||
* import { useHistoryRevert } from '@tmagic/editor';
|
||||
*
|
||||
* const { onPageRevert, onPageDiff } = useHistoryRevert(editorRef.value); // editorRef.value 即 Editor 暴露的 services
|
||||
* const { onPageRevert, onPageDiff } = useHistoryRevert({}, editorRef.value); // editorRef.value 即 Editor 暴露的 services
|
||||
* await onPageRevert(index); // 弹出差异 / 二次确认弹窗后回滚
|
||||
* onPageDiff(index); // 弹出只读差异弹窗查看前后值差异
|
||||
* ```
|
||||
*/
|
||||
export const useHistoryRevert = (services: Services, options: UseHistoryRevertOptions = {}) => {
|
||||
const { editorService, dataSourceService, codeBlockService, historyService } = services;
|
||||
export const useHistoryRevert = (options: UseHistoryRevertOptions = {}, services?: Services) => {
|
||||
// 自动捕获调用方所在组件的 appContext(在 setup 中调用时),业务方亦可显式覆盖。
|
||||
const appContext = options.appContext ?? getCurrentInstance()?.appContext ?? null;
|
||||
const { extendState, getPropsPanelFormState } = options;
|
||||
@ -226,8 +225,8 @@ export const useHistoryRevert = (services: Services, options: UseHistoryRevertOp
|
||||
buildDiffPayload(
|
||||
{
|
||||
category: 'node',
|
||||
groups: () => historyService.getHistoryGroups('page', editorService.get('page')?.id),
|
||||
getCurrent: (id) => editorService.getNodeById(id) as Record<string, any> | null,
|
||||
groups: () => services?.historyService.getHistoryGroups('page', services?.editorService.get('page')?.id) ?? [],
|
||||
getCurrent: (id) => services?.editorService.getNodeById(id) as Record<string, any> | null,
|
||||
resolveType: (n, o) => n.type || o.type || '',
|
||||
resolveLabel: (n, o) => n.name || o.name || n.type || o.type || '',
|
||||
},
|
||||
@ -238,8 +237,8 @@ export const useHistoryRevert = (services: Services, options: UseHistoryRevertOp
|
||||
buildDiffPayload(
|
||||
{
|
||||
category: 'data-source',
|
||||
groups: () => historyService.getHistoryGroups('dataSource'),
|
||||
getCurrent: (id) => dataSourceService.getDataSourceById(`${id}`) as Record<string, any> | null,
|
||||
groups: () => services?.historyService.getHistoryGroups('dataSource') ?? [],
|
||||
getCurrent: (id) => services?.dataSourceService.getDataSourceById(`${id}`) as Record<string, any> | null,
|
||||
resolveType: (n, o) => n.type || o.type || 'base',
|
||||
resolveLabel: (n, o, id) => n.title || o.title || `${id}`,
|
||||
},
|
||||
@ -251,8 +250,8 @@ export const useHistoryRevert = (services: Services, options: UseHistoryRevertOp
|
||||
buildDiffPayload(
|
||||
{
|
||||
category: 'code-block',
|
||||
groups: () => historyService.getHistoryGroups('codeBlock'),
|
||||
getCurrent: (id) => codeBlockService.getCodeContentById(id) as Record<string, any> | null,
|
||||
groups: () => services?.historyService.getHistoryGroups('codeBlock') ?? [],
|
||||
getCurrent: (id) => services?.codeBlockService.getCodeContentById(id) as Record<string, any> | null,
|
||||
resolveLabel: (n, o, id) => n.name || o.name || `${id}`,
|
||||
},
|
||||
index,
|
||||
@ -266,17 +265,17 @@ export const useHistoryRevert = (services: Services, options: UseHistoryRevertOp
|
||||
* add(回滚即删除)即使目标已不在,也已达成「删除」目的,不视为失败。
|
||||
*/
|
||||
const isPageRevertTargetMissing = (index: number): boolean => {
|
||||
const step = historyService.getStepList('page', editorService.get('page')?.id)[index]?.step;
|
||||
const step = services?.historyService.getStepList('page', services?.editorService.get('page')?.id)[index]?.step;
|
||||
if (!step) return false;
|
||||
if (step.opType === 'update') {
|
||||
return (step.diff ?? []).some((item) => {
|
||||
const id = item.newSchema?.id ?? item.oldSchema?.id;
|
||||
return id !== undefined && !editorService.getNodeById(id, false);
|
||||
return id !== undefined && !services?.editorService.getNodeById(id, false);
|
||||
});
|
||||
}
|
||||
if (step.opType === 'remove') {
|
||||
return (step.diff ?? []).some(
|
||||
(item) => item.parentId !== undefined && !editorService.getNodeById(item.parentId, false),
|
||||
(item) => item.parentId !== undefined && !services?.editorService.getNodeById(item.parentId, false),
|
||||
);
|
||||
}
|
||||
return false;
|
||||
@ -284,14 +283,14 @@ export const useHistoryRevert = (services: Services, options: UseHistoryRevertOp
|
||||
|
||||
/** 数据源 update 步骤回滚时,对应数据源必须仍存在(已删除则无处写回旧值)。 */
|
||||
const isDataSourceRevertTargetMissing = (id: string | number, index: number): boolean => {
|
||||
const step = historyService.getStepList('dataSource', id)[index]?.step;
|
||||
return Boolean(step?.opType === 'update' && !dataSourceService.getDataSourceById(`${id}`));
|
||||
const step = services?.historyService.getStepList('dataSource', id)[index]?.step;
|
||||
return Boolean(step?.opType === 'update' && !services?.dataSourceService.getDataSourceById(`${id}`));
|
||||
};
|
||||
|
||||
/** 代码块 update 步骤回滚时,对应代码块必须仍存在(已删除则无处写回旧值)。 */
|
||||
const isCodeBlockRevertTargetMissing = (id: string | number, index: number): boolean => {
|
||||
const step = historyService.getStepList('codeBlock', id)[index]?.step;
|
||||
return Boolean(step?.opType === 'update' && !codeBlockService.getCodeContentById(id));
|
||||
const step = services?.historyService.getStepList('codeBlock', id)[index]?.step;
|
||||
return Boolean(step?.opType === 'update' && !services?.codeBlockService.getCodeContentById(id));
|
||||
};
|
||||
|
||||
const onPageRevert = (index: number) => {
|
||||
@ -300,7 +299,7 @@ export const useHistoryRevert = (services: Services, options: UseHistoryRevertOp
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
return runRevert(buildPageDiffPayload(index), { compareFormState: getPropsPanelFormState?.() }).then((result) =>
|
||||
result ? editorService.revertPageStep(index) : null,
|
||||
result ? services?.editorService.revertPageStep(index) : null,
|
||||
);
|
||||
};
|
||||
|
||||
@ -310,7 +309,7 @@ export const useHistoryRevert = (services: Services, options: UseHistoryRevertOp
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
return runRevert(buildDataSourceDiffPayload(id, index)).then((result) =>
|
||||
result ? dataSourceService.revert(id, index) : null,
|
||||
result ? services?.dataSourceService.revert(id, index) : null,
|
||||
);
|
||||
};
|
||||
|
||||
@ -320,7 +319,7 @@ export const useHistoryRevert = (services: Services, options: UseHistoryRevertOp
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
return runRevert(buildCodeBlockDiffPayload(id, index)).then((result) =>
|
||||
result ? codeBlockService.revert(id, index) : null,
|
||||
result ? services?.codeBlockService.revert(id, index) : null,
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -87,7 +87,7 @@ const services = {} as any;
|
||||
const factory = () =>
|
||||
mount(HistoryDiffDialog, {
|
||||
// 让 Teleport 内容内联渲染,便于通过 wrapper 查询
|
||||
global: { stubs: { teleport: true }, provide: { services } },
|
||||
global: { stubs: { teleport: true } },
|
||||
});
|
||||
|
||||
const basePayload = (extra: any = {}) => ({
|
||||
|
||||
@ -62,7 +62,7 @@ describe('useHistoryRevert', () => {
|
||||
]);
|
||||
services.editorService.getNodeById.mockReturnValue(null);
|
||||
|
||||
const { onPageRevert } = useHistoryRevert(services);
|
||||
const { onPageRevert } = useHistoryRevert({}, services);
|
||||
await onPageRevert(0);
|
||||
|
||||
expect(tMagicMessage.error).toHaveBeenCalledWith('回滚失败:该记录对应的数据已被删除');
|
||||
@ -80,7 +80,7 @@ describe('useHistoryRevert', () => {
|
||||
},
|
||||
]);
|
||||
|
||||
const { onPageRevert } = useHistoryRevert(services);
|
||||
const { onPageRevert } = useHistoryRevert({}, services);
|
||||
await onPageRevert(0);
|
||||
|
||||
expect(confirmHistoryAction).toHaveBeenCalled();
|
||||
@ -99,7 +99,7 @@ describe('useHistoryRevert', () => {
|
||||
]);
|
||||
services.dataSourceService.getDataSourceById.mockReturnValue(null);
|
||||
|
||||
const { onDataSourceRevert } = useHistoryRevert(services);
|
||||
const { onDataSourceRevert } = useHistoryRevert({}, services);
|
||||
await onDataSourceRevert('ds_1', 0);
|
||||
|
||||
expect(tMagicMessage.error).toHaveBeenCalledWith('回滚失败:该记录对应的数据已被删除');
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user