Skip to content

Commit

Permalink
fix(editor): 有复制的内容时展示粘贴菜单
Browse files Browse the repository at this point in the history
  • Loading branch information
parisma authored and roymondchen committed Dec 11, 2023
1 parent fc89d4c commit 456692f
Show file tree
Hide file tree
Showing 13 changed files with 167 additions and 32 deletions.
5 changes: 5 additions & 0 deletions packages/dep/src/Target.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,16 @@ export default class Target {
* 实例:{ 'node_id': { name: 'node_name', keys: [ created, mounted ] } }
*/
public deps: DepData = {};
/**
* 是否默认收集,默认为true,当值为false时需要传入type参数给collect方法才会被收集
*/
public isCollectByDefault?: boolean;

constructor(options: TargetOptions) {
this.isTarget = options.isTarget;
this.id = options.id;
this.name = options.name;
this.isCollectByDefault = options.isCollectByDefault ?? true;
if (options.type) {
this.type = options.type;
}
Expand Down
15 changes: 13 additions & 2 deletions packages/dep/src/Watcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ export default class Watcher {
return Boolean(this.getTarget(id, type));
}

/**
* 判断是否存在指定类型的target
* @param type target type
* @returns boolean
*/
public hasSpecifiedTypeTarget(type: string = DepTargetType.DEFAULT): boolean {
return Object.keys(this.getTargets(type)).length > 0;
}

/**
* 删除指定id的target
* @param id target id
Expand Down Expand Up @@ -95,10 +104,12 @@ export default class Watcher {
* 收集依赖
* @param nodes 需要收集的节点
* @param deep 是否需要收集子节点
* @param type 强制收集指定类型的依赖
*/
public collect(nodes: Record<string | number, any>[], deep = false) {
public collect(nodes: Record<string | number, any>[], deep = false, type?: DepTargetType) {
Object.values(this.targetsList).forEach((targets) => {
Object.values(targets).forEach((target) => {
if ((!type && !target.isCollectByDefault) || (type && target.type !== type)) return;
nodes.forEach((node) => {
// 先删除原有依赖,重新收集
target.removeDep(node);
Expand Down Expand Up @@ -143,7 +154,7 @@ export default class Watcher {
} else if (!keyIsItems && Array.isArray(value)) {
value.forEach((item, index) => {
if (isObject(item)) {
collectTarget(item, `${fullKey}.${index}`);
collectTarget(item, `${fullKey}[${index}]`);
}
});
} else if (isObject(value)) {
Expand Down
12 changes: 12 additions & 0 deletions packages/dep/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export enum DepTargetType {
DATA_SOURCE_METHOD = 'data-source-method',
/** 数据源条件 */
DATA_SOURCE_COND = 'data-source-cond',
/** 复制组件时关联的组件 */
RELATED_COMP_WHEN_COPY = 'related-comp-when-copy',
}

export type IsTarget = (key: string | number, value: any) => boolean;
Expand All @@ -24,6 +26,16 @@ export interface TargetOptions {
type?: string;
name?: string;
initialDeps?: DepData;
/** 是否默认收集,默认为true,当值为false时需要传入type参数给collect方法才会被收集 */
isCollectByDefault?: boolean;
}

export interface CustomTargetOptions {
isTarget: IsTarget;
name?: string;
initialDeps?: DepData;
/** 是否默认收集,默认为true,当值为false时需要传入type参数给collect方法才会被收集 */
isCollectByDefault?: boolean;
}

export interface TargetList {
Expand Down
9 changes: 8 additions & 1 deletion packages/dep/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
import { DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX } from '@tmagic/utils';

import Target from './Target';
import { DepTargetType } from './types';
import { CustomTargetOptions, DepTargetType } from './types';

export const createCodeBlockTarget = (id: Id, codeBlock: CodeBlockContent, initialDeps: DepData = {}) =>
new Target({
Expand All @@ -31,6 +31,13 @@ export const createCodeBlockTarget = (id: Id, codeBlock: CodeBlockContent, initi
},
});

export const createRelatedCompTarget = (options: CustomTargetOptions) =>
new Target({
id: DepTargetType.RELATED_COMP_WHEN_COPY,
type: DepTargetType.RELATED_COMP_WHEN_COPY,
...options,
});

export const createDataSourceTarget = (ds: DataSourceSchema, initialDeps: DepData = {}) =>
new Target({
type: DepTargetType.DATA_SOURCE,
Expand Down
3 changes: 3 additions & 0 deletions packages/editor/src/editorProps.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { EventOption } from '@tmagic/core';
import { CustomTargetOptions } from '@tmagic/dep';
import type { FormConfig, FormState } from '@tmagic/form';
import type { DataSourceSchema, Id, MApp, MNode } from '@tmagic/schema';
import StageCore, {
Expand Down Expand Up @@ -65,6 +66,8 @@ export interface EditorProps {
updateDragEl?: UpdateDragEl;
disabledDragStart?: boolean;
extendFormState?: (state: FormState) => Record<string, any> | Promise<Record<string, any>>;
/** 自定义依赖收集器,复制组件时会将关联依赖一并复制 */
collectorOptions?: CustomTargetOptions;
}

export const defaultEditorProps = {
Expand Down
20 changes: 13 additions & 7 deletions packages/editor/src/initService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { onUnmounted, reactive, toRaw, watch } from 'vue';
import { cloneDeep } from 'lodash-es';

import type { EventOption } from '@tmagic/core';
import type { Target } from '@tmagic/dep';
import {
createCodeBlockTarget,
createDataSourceCondTarget,
createDataSourceMethodTarget,
createDataSourceTarget,
createRelatedCompTarget,
DepTargetType,
Target,
} from '@tmagic/dep';
import type { CodeBlockContent, DataSourceSchema, Id, MApp, MNode, MPage } from '@tmagic/schema';
import { getNodes } from '@tmagic/utils';
Expand Down Expand Up @@ -186,15 +187,15 @@ export const initServiceEvents = (
return stage?.renderer.runtime?.getApp?.();
};

const updateDataSoucreSchema = () => {
const updateDataSourceSchema = () => {
const root = editorService.get('root');

if (root?.dataSources) {
getApp()?.dataSourceManager?.updateSchema(root.dataSources);
}
};

const upateNodeWhenDataSourceChange = (nodes: MNode[]) => {
const updateNodeWhenDataSourceChange = (nodes: MNode[]) => {
const root = editorService.get('root');
const stage = editorService.get('stage');

Expand All @@ -210,7 +211,7 @@ export const initServiceEvents = (
app.dsl.dataSources = root.dataSources;
}

updateDataSoucreSchema();
updateDataSourceSchema();

nodes.forEach((node) => {
const deps = Object.values(root.dataSourceDeps || {});
Expand Down Expand Up @@ -258,11 +259,11 @@ export const initServiceEvents = (
};

const depUpdateHandler = (node: MNode) => {
upateNodeWhenDataSourceChange([node]);
updateNodeWhenDataSourceChange([node]);
};

const collectedHandler = (nodes: MNode[]) => {
upateNodeWhenDataSourceChange(nodes);
updateNodeWhenDataSourceChange(nodes);
};

depService.on('add-target', targetAddHandler);
Expand Down Expand Up @@ -381,7 +382,7 @@ export const initServiceEvents = (

const nodes = getNodes(Object.keys(targets[config.id].deps), root?.items);

upateNodeWhenDataSourceChange(nodes);
updateNodeWhenDataSourceChange(nodes);
};

const removeDataSourceTarget = (id: string) => {
Expand All @@ -399,6 +400,11 @@ export const initServiceEvents = (
dataSourceService.on('update', dataSourceUpdateHandler);
dataSourceService.on('remove', dataSourceRemoveHandler);

// 初始化复制组件相关的依赖收集器
if (props.collectorOptions && !depService.hasTarget(DepTargetType.RELATED_COMP_WHEN_COPY)) {
depService.addTarget(createRelatedCompTarget(props.collectorOptions));
}

onUnmounted(() => {
depService.off('add-target', targetAddHandler);
depService.off('remove-target', targetRemoveHandler);
Expand Down
5 changes: 0 additions & 5 deletions packages/editor/src/layouts/workspace/viewer/ViewerMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,8 @@ import { isPage } from '@tmagic/utils';
import ContentMenu from '@editor/components/ContentMenu.vue';
import CenterIcon from '@editor/icons/CenterIcon.vue';
import storageService from '@editor/services/storage';
import { LayerOffset, Layout, MenuButton, MenuComponent, Services } from '@editor/type';
import { useCopyMenu, useDeleteMenu, useMoveToMenu, usePasteMenu } from '@editor/utils/content-menu';
import { COPY_STORAGE_KEY } from '@editor/utils/editor';
defineOptions({
name: 'MEditorViewerMenu',
Expand All @@ -28,7 +26,6 @@ const props = withDefaults(
const services = inject<Services>('services');
const editorService = services?.editorService;
const menu = ref<InstanceType<typeof ContentMenu>>();
const canPaste = ref(false);
const canCenter = ref(false);
const node = computed(() => editorService?.get('node'));
Expand Down Expand Up @@ -129,8 +126,6 @@ watch(
const show = (e: MouseEvent) => {
menu.value?.show(e);
const data = storageService.getItem(COPY_STORAGE_KEY);
canPaste.value = data !== 'undefined' && !!data;
};
defineExpose({ show });
Expand Down
8 changes: 6 additions & 2 deletions packages/editor/src/services/dep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ class Dep extends BaseService {
this.watcher.clearTargets();
}

public collect(nodes: MNode[], deep = false) {
this.watcher.collect(nodes, deep);
public collect(nodes: MNode[], deep = false, type?: DepTargetType) {
this.watcher.collect(nodes, deep, type);
this.emit('collected', nodes, deep);
}

Expand All @@ -65,6 +65,10 @@ class Dep extends BaseService {
public hasTarget(id: Id, type: string = DepTargetType.DEFAULT) {
return this.watcher.hasTarget(id, type);
}

public hasSpecifiedTypeTarget(type: string = DepTargetType.DEFAULT): boolean {
return this.watcher.hasSpecifiedTypeTarget(type);
}
}

export type DepService = Dep;
Expand Down
48 changes: 42 additions & 6 deletions packages/editor/src/services/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@
*/

import { reactive, toRaw } from 'vue';
import { cloneDeep, isObject, mergeWith, uniq } from 'lodash-es';
import { cloneDeep, get, isObject, mergeWith, uniq } from 'lodash-es';

import { DepTargetType } from '@tmagic/dep';
import type { Id, MApp, MComponent, MContainer, MNode, MPage } from '@tmagic/schema';
import { NodeType } from '@tmagic/schema';
import StageCore from '@tmagic/stage';
import { getNodePath, isNumber, isPage, isPop } from '@tmagic/utils';

import depService from '@editor/services/dep';
import historyService from '@editor/services/history';
import storageService, { Protocol } from '@editor/services/storage';
import type { AddMNode, EditorNodeInfo, PastePosition, StepValue, StoreState, StoreStateKey } from '@editor/type';
Expand Down Expand Up @@ -593,24 +595,58 @@ class Editor extends BaseService {
}

/**
* 将组将节点配置转化成string,然后存储到localStorage中
* 将组件节点配置存储到localStorage中
* @param config 组件节点配置
* @returns 组件节点配置
* @returns
*/
public copy(config: MNode | MNode[]): void {
storageService.setItem(COPY_STORAGE_KEY, Array.isArray(config) ? config : [config], {
protocol: Protocol.OBJECT,
});
}

/**
* 复制时会带上组件关联的依赖
* @param config 组件节点配置
* @returns
*/
public copyWithRelated(config: MNode | MNode[]): void {
const copyNodes: MNode[] = Array.isArray(config) ? config : [config];
// 关联的组件也一并复制
depService.getTarget(DepTargetType.RELATED_COMP_WHEN_COPY, DepTargetType.RELATED_COMP_WHEN_COPY)?.removeDep();
depService.collect(copyNodes, true, DepTargetType.RELATED_COMP_WHEN_COPY);
const customTarget = depService.getTarget(
DepTargetType.RELATED_COMP_WHEN_COPY,
DepTargetType.RELATED_COMP_WHEN_COPY,
);
if (customTarget) {
Object.keys(customTarget.deps).forEach((nodeId: Id) => {
const node = this.getNodeById(nodeId);
if (!node) return;
customTarget!.deps[nodeId].keys.forEach((key) => {
const relateNodeId = get(node, key);
const isExist = copyNodes.find((node) => node.id === relateNodeId);
if (!isExist) {
const relateNode = this.getNodeById(relateNodeId);
if (relateNode) {
copyNodes.push(relateNode);
}
}
});
});
}
storageService.setItem(COPY_STORAGE_KEY, copyNodes, {
protocol: Protocol.OBJECT,
});
}

/**
* 从localStorage中获取节点,然后添加到当前容器中
* @param position 粘贴的坐标
* @returns 添加后的组件节点配置
*/
public async paste(position: PastePosition = {}): Promise<MNode | MNode[] | void> {
const config: MNode[] = storageService.getItem(COPY_STORAGE_KEY);

if (!Array.isArray(config)) return;

const node = this.get('node');
Expand All @@ -623,13 +659,13 @@ class Editor extends BaseService {
parent = this.get('page');
}
}

const pasteConfigs = await this.doPaste(config, position);

propsService.replaceRelateId(config, pasteConfigs);
return this.add(pasteConfigs, parent);
}

public async doPaste(config: MNode[], position: PastePosition = {}): Promise<MNode[]> {
propsService.clearRelateId();
const pasteConfigs = await beforePaste(position, cloneDeep(config));
return pasteConfigs;
}
Expand Down
Loading

0 comments on commit 456692f

Please sign in to comment.