refactor(utils): 新增 isPageOrFragment 统一页面与页面片判断

This commit is contained in:
roymondchen 2026-07-30 14:55:36 +08:00
parent 21baf58cda
commit b85eaa9452
16 changed files with 38 additions and 49 deletions

View File

@ -7,8 +7,7 @@ import {
DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX,
dataSourceTemplateRegExp,
getValueByKeyPath,
isPage,
isPageFragment,
isPageOrFragment,
NODE_CONDS_KEY,
replaceChildNode,
} from '@tmagic/core';
@ -68,7 +67,7 @@ export const compliedConditions = (node: { [NODE_CONDS_KEY]?: DisplayCond[] }, d
};
export const updateNode = (node: MNode, dsl: MApp) => {
if (isPage(node) || isPageFragment(node)) {
if (isPageOrFragment(node)) {
const index = dsl.items?.findIndex((child: MNode) => child.id === node.id);
dsl.items.splice(index, 1, node as MPage | MPageFragment);
} else {

View File

@ -6,7 +6,7 @@
import { computed, markRaw, useTemplateRef } from 'vue';
import { Files, Plus } from '@element-plus/icons-vue';
import { isPage, isPageFragment } from '@tmagic/utils';
import { isPageOrFragment } from '@tmagic/utils';
import ContentMenu from '@editor/components/ContentMenu.vue';
import { useServices } from '@editor/hooks/use-services';
@ -101,7 +101,7 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() =>
type: 'button',
text: '全部折叠',
icon: FolderMinusIcon,
display: () => isPage(node.value) || isPageFragment(node.value),
display: () => isPageOrFragment(node.value),
handler: () => {
emit('collapse-all');
},

View File

@ -2,7 +2,7 @@ import { computed, type ComputedRef, nextTick, type Ref, type ShallowRef } from
import { throttle } from 'lodash-es';
import { Id, MNode } from '@tmagic/core';
import { getElById, isPage, isPageFragment } from '@tmagic/utils';
import { getElById, isPageOrFragment } from '@tmagic/utils';
import type { LayerNodeStatus, Services, TreeNodeData } from '@editor/type';
import { UI_SELECT_MODE_EVENT_NAME } from '@editor/utils/const';
@ -36,7 +36,7 @@ export const useClick = (
};
const multiSelect = async (data: MNode) => {
if (isPage(data) || isPageFragment(data)) {
if (isPageOrFragment(data)) {
return;
}
@ -50,7 +50,7 @@ export const useClick = (
return;
}
if (isPage(node) || isPageFragment(node)) {
if (isPageOrFragment(node)) {
return;
}

View File

@ -1,7 +1,7 @@
import { computed, onBeforeUnmount, ref, watch } from 'vue';
import type { Id, MApp, MNode, MPage, MPageFragment } from '@tmagic/core';
import { getNodePath, isPage, isPageFragment, traverseNode } from '@tmagic/utils';
import { getNodePath, isPageOrFragment, traverseNode } from '@tmagic/utils';
import type { LayerNodeStatus, Services } from '@editor/type';
import { updateStatus } from '@editor/utils/tree';
@ -136,7 +136,7 @@ export const useNodeStatus = ({ editorService }: Services) => {
const addHandler = (newNodes: MNode[]) => {
newNodes.forEach((node) => {
if (isPage(node) || isPageFragment(node)) return;
if (isPageOrFragment(node)) return;
traverseNode(node, (node: MNode) => {
nodeStatusMap.value?.set(node.id, {

View File

@ -7,7 +7,7 @@ import { computed, markRaw, ref, useTemplateRef, watch } from 'vue';
import { Bottom, Top } from '@element-plus/icons-vue';
import { NodeType } from '@tmagic/core';
import { isPage, isPageFragment } from '@tmagic/utils';
import { isPageOrFragment } from '@tmagic/utils';
import ContentMenu from '@editor/components/ContentMenu.vue';
import { useServices } from '@editor/hooks/use-services';
@ -59,14 +59,14 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() =>
direction: 'horizontal',
display: () => {
if (!node.value) return false;
return !isPage(node.value) && !isPageFragment(node.value);
return !isPageOrFragment(node.value);
},
},
{
type: 'button',
text: '上移一层',
icon: markRaw(Top),
display: () => !isPage(node.value) && !isPageFragment(node.value) && !props.isMultiSelect,
display: () => !isPageOrFragment(node.value) && !props.isMultiSelect,
handler: () => {
editorService.moveLayer(1, { historySource: 'stage-contextmenu' });
},
@ -75,7 +75,7 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() =>
type: 'button',
text: '下移一层',
icon: markRaw(Bottom),
display: () => !isPage(node.value) && !isPageFragment(node.value) && !props.isMultiSelect,
display: () => !isPageOrFragment(node.value) && !props.isMultiSelect,
handler: () => {
editorService.moveLayer(-1, { historySource: 'stage-contextmenu' });
},
@ -84,7 +84,7 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() =>
type: 'button',
text: '置顶',
icon: markRaw(Top),
display: () => !isPage(node.value) && !isPageFragment(node.value) && !props.isMultiSelect,
display: () => !isPageOrFragment(node.value) && !props.isMultiSelect,
handler: () => {
editorService.moveLayer(LayerOffset.TOP, { historySource: 'stage-contextmenu' });
},
@ -93,7 +93,7 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() =>
type: 'button',
text: '置底',
icon: markRaw(Bottom),
display: () => !isPage(node.value) && !isPageFragment(node.value) && !props.isMultiSelect,
display: () => !isPageOrFragment(node.value) && !props.isMultiSelect,
handler: () => {
editorService.moveLayer(LayerOffset.BOTTOM, { historySource: 'stage-contextmenu' });
},
@ -102,7 +102,7 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() =>
{
type: 'divider',
direction: 'horizontal',
display: () => !isPage(node.value) && !isPageFragment(node.value) && !props.isMultiSelect,
display: () => !isPageOrFragment(node.value) && !props.isMultiSelect,
},
useDeleteMenu('stage-contextmenu'),
{

View File

@ -357,7 +357,7 @@ class History extends BaseService {
* / `page-structure-change`
*
* `editorService.add` / `remove` `page` editor.add / remove
* isPage / isPageFragment historyService
* isPageOrFragment historyService
* setRoot DSL
*
* ** change **

View File

@ -1,6 +1,6 @@
import KeyController, { KeyControllerEvent } from 'keycon';
import { isPage, isPageFragment } from '@tmagic/utils';
import { isPageOrFragment } from '@tmagic/utils';
import { KeyBindingCacheItem, KeyBindingCommand, KeyBindingItem } from '@editor/type';
@ -19,7 +19,7 @@ class Keybinding extends BaseService {
[KeyBindingCommand.DELETE_NODE]: () => {
const nodes = editorService.get('nodes');
if (!nodes || isPage(nodes[0]) || isPageFragment(nodes[0])) return;
if (!nodes || isPageOrFragment(nodes[0])) return;
editorService.remove(nodes, { historySource: 'shortcut' });
},
[KeyBindingCommand.COPY_NODE]: () => {
@ -29,7 +29,7 @@ class Keybinding extends BaseService {
[KeyBindingCommand.CUT_NODE]: () => {
const nodes = editorService.get('nodes');
if (!nodes || isPage(nodes[0]) || isPageFragment(nodes[0])) return;
if (!nodes || isPageOrFragment(nodes[0])) return;
editorService.copy(nodes);
editorService.remove(nodes, { historySource: 'shortcut' });
},

View File

@ -2,7 +2,7 @@ import { computed, markRaw, type ShallowRef } from 'vue';
import { CopyDocument, Delete, DocumentCopy } from '@element-plus/icons-vue';
import { cloneDeep, Id, MContainer, NodeType } from '@tmagic/core';
import { calcValueByFontsize, isPage, isPageFragment } from '@tmagic/utils';
import { calcValueByFontsize, isPage, isPageOrFragment } from '@tmagic/utils';
import ContentMenu from '@editor/components/ContentMenu.vue';
import type { HistoryOpSource, MenuButton, Services } from '@editor/type';
@ -20,7 +20,7 @@ export const useDeleteMenu = (historySource?: HistoryOpSource): MenuButton => ({
icon: Delete,
display: ({ editorService }) => {
const node = editorService.get('node');
return node?.type !== NodeType.ROOT && !isPage(node) && !isPageFragment(node);
return node?.type !== NodeType.ROOT && !isPageOrFragment(node);
},
handler: ({ editorService }) => {
const nodes = editorService.get('nodes');

View File

@ -3,7 +3,7 @@ import { isEmpty } from 'lodash-es';
import type { Id, MContainer, MNode } from '@tmagic/core';
import { NodeType } from '@tmagic/core';
import { calcValueByFontsize, getElById, isPage, isPageFragment } from '@tmagic/utils';
import { calcValueByFontsize, getElById, isPage, isPageOrFragment } from '@tmagic/utils';
import editorService from '@editor/services/editor';
import propsService from '@editor/services/props';
@ -58,7 +58,7 @@ export const beforePaste = (position: PastePosition, config: MNode[], doc?: Docu
};
}
const root = editorService.get('root');
if ((isPage(pasteConfig) || isPageFragment(pasteConfig)) && root) {
if (isPageOrFragment(pasteConfig) && root) {
pasteConfig.name = generatePageNameByApp(root, isPage(pasteConfig) ? NodeType.PAGE : NodeType.PAGE_FRAGMENT);
}
return pasteConfig as MNode;

View File

@ -144,7 +144,6 @@ vi.mock('@tmagic/utils', async () => {
...actual,
getDepNodeIds: vi.fn(() => []),
getNodes: vi.fn(() => []),
isPage: vi.fn((n: any) => n?.type === 'page'),
isValueIncludeDataSource: vi.fn((v: any) => /\$\{/.test(String(v))),
};
});

View File

@ -28,15 +28,6 @@ vi.mock('@editor/utils/content-menu', () => ({
useMoveToMenu: () => ({ type: 'button', text: 'moveto' }),
}));
vi.mock('@tmagic/utils', async () => {
const actual = await vi.importActual<any>('@tmagic/utils');
return {
...actual,
isPage: (n: any) => n?.type === 'page',
isPageFragment: (n: any) => n?.type === 'page-fragment',
};
});
const showMock = vi.fn();
vi.mock('@editor/components/ContentMenu.vue', () => ({
default: defineComponent({

View File

@ -17,8 +17,6 @@ vi.mock('@tmagic/utils', async () => {
const actual = await vi.importActual<any>('@tmagic/utils');
return {
...actual,
isPage: (n: any) => n.type === 'page',
isPageFragment: (n: any) => n.type === 'page-fragment',
getElById: () => (_doc: any, id: any) => (id === 'no-el' ? null : { id }),
};
});

View File

@ -12,13 +12,7 @@ vi.mock('@tmagic/utils', async () => {
const actual = await vi.importActual<any>('@tmagic/utils');
return {
...actual,
isPage: (n: any) => n.type === 'page',
isPageFragment: (n: any) => n.type === 'page-fragment',
getNodePath: vi.fn(() => []),
traverseNode: (node: any, fn: any) => {
fn(node);
node.items?.forEach((c: any) => fn(c));
},
};
});

View File

@ -24,11 +24,6 @@ vi.mock('@editor/hooks/use-services', () => ({
useServices: () => ({ editorService }),
}));
vi.mock('@tmagic/utils', () => ({
isPage: (n: any) => n?.type === 'page',
isPageFragment: (n: any) => n?.type === 'page-fragment',
}));
vi.mock('@editor/utils/content-menu', () => ({
useCopyMenu: () => ({ type: 'button', text: '复制', handler: vi.fn() }),
usePasteMenu: () => ({ type: 'button', text: '粘贴', handler: vi.fn() }),

View File

@ -142,7 +142,7 @@ export const getNodeInfo = (id: Id, root: { id: Id; items?: MNode[] } | null, sk
info.parent = path[path.length - 2] as MContainer;
for (const item of path) {
if (isPage(item) || isPageFragment(item)) {
if (isPageOrFragment(item)) {
info.page = item as MPage | MPageFragment;
break;
}
@ -279,6 +279,10 @@ export const isPageFragment = (node?: Pick<MComponent, 'type'> | null): boolean
return Boolean(node.type?.toLowerCase() === NodeType.PAGE_FRAGMENT);
};
/** 是否为页面或页面片 */
export const isPageOrFragment = (node?: Pick<MComponent, 'type'> | null): boolean =>
isPage(node) || isPageFragment(node);
export const isNumber = (value: any) =>
(typeof value === 'number' && !isNaN(value)) || /^(-?\d+)(\.\d+)?$/.test(`${value}`);

View File

@ -36,6 +36,7 @@ import {
isNumber,
isObject,
isPageFragment,
isPageOrFragment,
isPercentage,
isPop,
isValueIncludeDataSource,
@ -112,6 +113,14 @@ describe('node 类型判断', () => {
expect(isPageFragment(null)).toBe(false);
});
test('isPageOrFragment 识别 page 与 page-fragment', () => {
expect(isPageOrFragment({ id: 1, type: NodeType.PAGE })).toBe(true);
expect(isPageOrFragment({ id: 1, type: NodeType.PAGE_FRAGMENT })).toBe(true);
expect(isPageOrFragment({ id: 1, type: 'text' })).toBe(false);
expect(isPageOrFragment(undefined)).toBe(false);
expect(isPageOrFragment(null)).toBe(false);
});
test('isDslNode 默认为 true手动 false 关闭', () => {
expect(isDslNode({ type: 'text' } as any)).toBe(true);
expect(isDslNode({ type: 'text', [IS_DSL_NODE_KEY]: false } as any)).toBe(false);