mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-04-06 03:57:56 +08:00
feat(editor,dep): 支持复制组件时将组件绑定的数据源一并复制
This commit is contained in:
parent
2ea876a2a0
commit
faabf1bb3a
@ -15,6 +15,10 @@ export enum DepTargetType {
|
|||||||
DATA_SOURCE_COND = 'data-source-cond',
|
DATA_SOURCE_COND = 'data-source-cond',
|
||||||
/** 复制组件时关联的组件 */
|
/** 复制组件时关联的组件 */
|
||||||
RELATED_COMP_WHEN_COPY = 'related-comp-when-copy',
|
RELATED_COMP_WHEN_COPY = 'related-comp-when-copy',
|
||||||
|
/** 复制组件时关联的代码块 */
|
||||||
|
RELATED_CODE_WHEN_COPY = 'related-code-when-copy',
|
||||||
|
/** 复制组件时关联的数据源 */
|
||||||
|
RELATED_DS_WHEN_COPY = 'related-ds-when-copy',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type IsTarget = (key: string | number, value: any) => boolean;
|
export type IsTarget = (key: string | number, value: any) => boolean;
|
||||||
|
@ -32,10 +32,10 @@ export const createCodeBlockTarget = (id: Id, codeBlock: CodeBlockContent, initi
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const createRelatedCompTarget = (options: CustomTargetOptions) =>
|
export const createRelatedTargetForCopy = (options: CustomTargetOptions, type: DepTargetType) =>
|
||||||
new Target({
|
new Target({
|
||||||
id: DepTargetType.RELATED_COMP_WHEN_COPY,
|
id: type,
|
||||||
type: DepTargetType.RELATED_COMP_WHEN_COPY,
|
type,
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -70,8 +70,12 @@ export interface EditorProps {
|
|||||||
codeOptions?: { [key: string]: any };
|
codeOptions?: { [key: string]: any };
|
||||||
/** 禁用鼠标左键按下时就开始拖拽,需要先选中再可以拖拽 */
|
/** 禁用鼠标左键按下时就开始拖拽,需要先选中再可以拖拽 */
|
||||||
disabledDragStart?: boolean;
|
disabledDragStart?: boolean;
|
||||||
/** 自定义依赖收集器,复制组件时会将关联依赖一并复制 */
|
/** 自定义依赖收集器,复制组件时会将关联组件一并复制 */
|
||||||
collectorOptions?: CustomTargetOptions;
|
collectorOptions?: CustomTargetOptions;
|
||||||
|
/** 自定义依赖收集器,复制组件时会将关联代码块一并复制 */
|
||||||
|
collectorOptionsForCode?: CustomTargetOptions;
|
||||||
|
/** 自定义依赖收集器,复制组件时会将关联数据源一并复制 */
|
||||||
|
collectorOptionsForDataSource?: CustomTargetOptions;
|
||||||
/** 标尺配置 */
|
/** 标尺配置 */
|
||||||
guidesOptions?: Partial<GuidesOptions>;
|
guidesOptions?: Partial<GuidesOptions>;
|
||||||
/** 禁止多选 */
|
/** 禁止多选 */
|
||||||
|
@ -67,8 +67,6 @@ export const useCodeBlockEdit = (codeBlockService?: CodeBlockService) => {
|
|||||||
|
|
||||||
await codeBlockService?.setCodeDslById(codeId.value, values);
|
await codeBlockService?.setCodeDslById(codeId.value, values);
|
||||||
|
|
||||||
tMagicMessage.success('代码块保存成功');
|
|
||||||
|
|
||||||
codeBlockEditor.value?.hide();
|
codeBlockEditor.value?.hide();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
createDataSourceCondTarget,
|
createDataSourceCondTarget,
|
||||||
createDataSourceMethodTarget,
|
createDataSourceMethodTarget,
|
||||||
createDataSourceTarget,
|
createDataSourceTarget,
|
||||||
createRelatedCompTarget,
|
createRelatedTargetForCopy,
|
||||||
DepTargetType,
|
DepTargetType,
|
||||||
Target,
|
Target,
|
||||||
} from '@tmagic/dep';
|
} from '@tmagic/dep';
|
||||||
@ -412,7 +412,17 @@ export const initServiceEvents = (
|
|||||||
|
|
||||||
// 初始化复制组件相关的依赖收集器
|
// 初始化复制组件相关的依赖收集器
|
||||||
if (props.collectorOptions && !depService.hasTarget(DepTargetType.RELATED_COMP_WHEN_COPY)) {
|
if (props.collectorOptions && !depService.hasTarget(DepTargetType.RELATED_COMP_WHEN_COPY)) {
|
||||||
depService.addTarget(createRelatedCompTarget(props.collectorOptions));
|
depService.addTarget(createRelatedTargetForCopy(props.collectorOptions, DepTargetType.RELATED_COMP_WHEN_COPY));
|
||||||
|
}
|
||||||
|
if (props.collectorOptionsForCode && !depService.hasTarget(DepTargetType.RELATED_CODE_WHEN_COPY)) {
|
||||||
|
depService.addTarget(
|
||||||
|
createRelatedTargetForCopy(props.collectorOptionsForCode, DepTargetType.RELATED_CODE_WHEN_COPY),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (props.collectorOptionsForDataSource && !depService.hasTarget(DepTargetType.RELATED_DS_WHEN_COPY)) {
|
||||||
|
depService.addTarget(
|
||||||
|
createRelatedTargetForCopy(props.collectorOptionsForDataSource, DepTargetType.RELATED_DS_WHEN_COPY),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
|
@ -17,21 +17,26 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { reactive } from 'vue';
|
import { reactive } from 'vue';
|
||||||
import { keys, pick } from 'lodash-es';
|
import { cloneDeep, get, keys, pick } from 'lodash-es';
|
||||||
import type { Writable } from 'type-fest';
|
import type { Writable } from 'type-fest';
|
||||||
|
|
||||||
|
import { DepTargetType } from '@tmagic/dep';
|
||||||
import type { ColumnConfig } from '@tmagic/form';
|
import type { ColumnConfig } from '@tmagic/form';
|
||||||
import type { CodeBlockContent, CodeBlockDSL, Id } from '@tmagic/schema';
|
import type { CodeBlockContent, CodeBlockDSL, Id, MNode } from '@tmagic/schema';
|
||||||
|
|
||||||
|
import depService from '@editor/services/dep';
|
||||||
|
import editorService from '@editor/services/editor';
|
||||||
|
import storageService, { Protocol } from '@editor/services/storage';
|
||||||
import type { AsyncHookPlugin, CodeState } from '@editor/type';
|
import type { AsyncHookPlugin, CodeState } from '@editor/type';
|
||||||
import { CODE_DRAFT_STORAGE_KEY } from '@editor/type';
|
import { CODE_DRAFT_STORAGE_KEY } from '@editor/type';
|
||||||
import { getConfig } from '@editor/utils/config';
|
import { getConfig } from '@editor/utils/config';
|
||||||
|
import { COPY_CODE_STORAGE_KEY } from '@editor/utils/editor';
|
||||||
|
|
||||||
import BaseService from './BaseService';
|
import BaseService from './BaseService';
|
||||||
|
|
||||||
const canUsePluginMethods = {
|
const canUsePluginMethods = {
|
||||||
async: ['setCodeDslById', 'setEditStatus', 'setCombineIds', 'setUndeleteableList', 'deleteCodeDslByIds'] as const,
|
async: ['setCodeDslById', 'setEditStatus', 'setCombineIds', 'setUndeleteableList', 'deleteCodeDslByIds'] as const,
|
||||||
sync: [],
|
sync: ['setCodeDslByIdSync'],
|
||||||
};
|
};
|
||||||
|
|
||||||
type AsyncMethodName = Writable<(typeof canUsePluginMethods)['async']>;
|
type AsyncMethodName = Writable<(typeof canUsePluginMethods)['async']>;
|
||||||
@ -46,7 +51,10 @@ class CodeBlock extends BaseService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super(canUsePluginMethods.async.map((methodName) => ({ name: methodName, isAsync: true })));
|
super([
|
||||||
|
...canUsePluginMethods.async.map((methodName) => ({ name: methodName, isAsync: true })),
|
||||||
|
...canUsePluginMethods.sync.map((methodName) => ({ name: methodName, isAsync: false })),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -88,20 +96,32 @@ class CodeBlock extends BaseService {
|
|||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
public async setCodeDslById(id: Id, codeConfig: Partial<CodeBlockContent>): Promise<void> {
|
public async setCodeDslById(id: Id, codeConfig: Partial<CodeBlockContent>): Promise<void> {
|
||||||
|
this.setCodeDslByIdSync(id, codeConfig, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为了兼容历史原因
|
||||||
|
* 设置代码块ID和代码内容到源dsl
|
||||||
|
* @param {Id} id 代码块id
|
||||||
|
* @param {CodeBlockContent} codeConfig 代码块内容配置信息
|
||||||
|
* @param {boolean} force 是否强制写入,默认true
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
public setCodeDslByIdSync(id: Id, codeConfig: Partial<CodeBlockContent>, force = true): void {
|
||||||
const codeDsl = this.getCodeDsl();
|
const codeDsl = this.getCodeDsl();
|
||||||
|
|
||||||
if (!codeDsl) {
|
if (!codeDsl) {
|
||||||
throw new Error('dsl中没有codeBlocks');
|
throw new Error('dsl中没有codeBlocks');
|
||||||
}
|
}
|
||||||
|
if (codeDsl[id] && !force) return;
|
||||||
|
|
||||||
const codeConfigProcessed = codeConfig;
|
const codeConfigProcessed = cloneDeep(codeConfig);
|
||||||
if (codeConfig.content) {
|
if (codeConfigProcessed.content) {
|
||||||
// 在保存的时候转换代码内容
|
// 在保存的时候转换代码内容
|
||||||
const parseDSL = getConfig('parseDSL');
|
const parseDSL = getConfig('parseDSL');
|
||||||
if (typeof codeConfig.content === 'string') {
|
if (typeof codeConfigProcessed.content === 'string') {
|
||||||
codeConfig.content = parseDSL<(...args: any[]) => any>(codeConfig.content);
|
codeConfigProcessed.content = parseDSL<(...args: any[]) => any>(codeConfigProcessed.content);
|
||||||
}
|
}
|
||||||
codeConfigProcessed.content = codeConfig.content;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const existContent = codeDsl[id] || {};
|
const existContent = codeDsl[id] || {};
|
||||||
@ -233,6 +253,54 @@ class CodeBlock extends BaseService {
|
|||||||
return await this.getUniqueId();
|
return await this.getUniqueId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制时会带上组件关联的代码块
|
||||||
|
* @param config 组件节点配置
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public copyWithRelated(config: MNode | MNode[]): void {
|
||||||
|
const copyNodes: MNode[] = Array.isArray(config) ? config : [config];
|
||||||
|
// 关联的代码块也一并复制
|
||||||
|
depService.clearByType(DepTargetType.RELATED_CODE_WHEN_COPY);
|
||||||
|
depService.collect(copyNodes, true, DepTargetType.RELATED_CODE_WHEN_COPY);
|
||||||
|
const customTarget = depService.getTarget(
|
||||||
|
DepTargetType.RELATED_CODE_WHEN_COPY,
|
||||||
|
DepTargetType.RELATED_CODE_WHEN_COPY,
|
||||||
|
);
|
||||||
|
const copyData: CodeBlockDSL = {};
|
||||||
|
if (customTarget) {
|
||||||
|
Object.keys(customTarget.deps).forEach((nodeId: Id) => {
|
||||||
|
const node = editorService.getNodeById(nodeId);
|
||||||
|
if (!node) return;
|
||||||
|
customTarget!.deps[nodeId].keys.forEach((key) => {
|
||||||
|
const relateCodeId = get(node, key);
|
||||||
|
const isExist = Object.keys(copyData).find((codeId: Id) => codeId === relateCodeId);
|
||||||
|
if (!isExist) {
|
||||||
|
const relateCode = this.getCodeContentById(relateCodeId);
|
||||||
|
if (relateCode) {
|
||||||
|
copyData[relateCodeId] = relateCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
storageService.setItem(COPY_CODE_STORAGE_KEY, copyData, {
|
||||||
|
protocol: Protocol.OBJECT,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 粘贴代码块
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public paste() {
|
||||||
|
const codeDsl: CodeBlockDSL = storageService.getItem(COPY_CODE_STORAGE_KEY);
|
||||||
|
Object.keys(codeDsl).forEach((codeId: Id) => {
|
||||||
|
// 不覆盖同样id的代码块
|
||||||
|
this.setCodeDslByIdSync(codeId, codeDsl[codeId], false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public resetState() {
|
public resetState() {
|
||||||
this.state.codeDsl = null;
|
this.state.codeDsl = null;
|
||||||
this.state.editable = true;
|
this.state.editable = true;
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
import { reactive } from 'vue';
|
import { reactive } from 'vue';
|
||||||
import { cloneDeep } from 'lodash-es';
|
import { cloneDeep, get } from 'lodash-es';
|
||||||
import { Writable } from 'type-fest';
|
import { Writable } from 'type-fest';
|
||||||
|
|
||||||
import type { EventOption } from '@tmagic/core';
|
import type { EventOption } from '@tmagic/core';
|
||||||
|
import { DepTargetType } from '@tmagic/dep';
|
||||||
import type { FormConfig } from '@tmagic/form';
|
import type { FormConfig } from '@tmagic/form';
|
||||||
import type { DataSourceSchema } from '@tmagic/schema';
|
import type { DataSourceSchema, Id, MNode } from '@tmagic/schema';
|
||||||
import { guid, toLine } from '@tmagic/utils';
|
import { guid, toLine } from '@tmagic/utils';
|
||||||
|
|
||||||
|
import depService from '@editor/services/dep';
|
||||||
|
import editorService from '@editor/services/editor';
|
||||||
|
import storageService, { Protocol } from '@editor/services/storage';
|
||||||
import type { DatasourceTypeOption, SyncHookPlugin } from '@editor/type';
|
import type { DatasourceTypeOption, SyncHookPlugin } from '@editor/type';
|
||||||
import { getFormConfig, getFormValue } from '@editor/utils/data-source';
|
import { getFormConfig, getFormValue } from '@editor/utils/data-source';
|
||||||
|
import { COPY_DS_STORAGE_KEY } from '@editor/utils/editor';
|
||||||
|
|
||||||
import BaseService from './BaseService';
|
import BaseService from './BaseService';
|
||||||
|
|
||||||
@ -102,7 +107,7 @@ class DataSource extends BaseService {
|
|||||||
public add(config: DataSourceSchema) {
|
public add(config: DataSourceSchema) {
|
||||||
const newConfig = {
|
const newConfig = {
|
||||||
...config,
|
...config,
|
||||||
id: this.createId(),
|
id: config.id && !this.getDataSourceById(config.id) ? config.id : this.createId(),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.get('dataSources').push(newConfig);
|
this.get('dataSources').push(newConfig);
|
||||||
@ -116,7 +121,6 @@ class DataSource extends BaseService {
|
|||||||
const dataSources = this.get('dataSources');
|
const dataSources = this.get('dataSources');
|
||||||
|
|
||||||
const index = dataSources.findIndex((ds) => ds.id === config.id);
|
const index = dataSources.findIndex((ds) => ds.id === config.id);
|
||||||
|
|
||||||
dataSources[index] = cloneDeep(config);
|
dataSources[index] = cloneDeep(config);
|
||||||
|
|
||||||
this.emit('update', config);
|
this.emit('update', config);
|
||||||
@ -153,6 +157,51 @@ class DataSource extends BaseService {
|
|||||||
public usePlugin(options: SyncHookPlugin<SyncMethodName, DataSource>): void {
|
public usePlugin(options: SyncHookPlugin<SyncMethodName, DataSource>): void {
|
||||||
super.usePlugin(options);
|
super.usePlugin(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制时会带上组件关联的数据源
|
||||||
|
* @param config 组件节点配置
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public copyWithRelated(config: MNode | MNode[]): void {
|
||||||
|
const copyNodes: MNode[] = Array.isArray(config) ? config : [config];
|
||||||
|
depService.clearByType(DepTargetType.RELATED_DS_WHEN_COPY);
|
||||||
|
depService.collect(copyNodes, true, DepTargetType.RELATED_DS_WHEN_COPY);
|
||||||
|
const customTarget = depService.getTarget(DepTargetType.RELATED_DS_WHEN_COPY, DepTargetType.RELATED_DS_WHEN_COPY);
|
||||||
|
const copyData: DataSourceSchema[] = [];
|
||||||
|
if (customTarget) {
|
||||||
|
Object.keys(customTarget.deps).forEach((nodeId: Id) => {
|
||||||
|
const node = editorService.getNodeById(nodeId);
|
||||||
|
if (!node) return;
|
||||||
|
customTarget!.deps[nodeId].keys.forEach((key) => {
|
||||||
|
const [relateDsId] = get(node, key);
|
||||||
|
const isExist = copyData.find((dsItem) => dsItem.id === relateDsId);
|
||||||
|
if (!isExist) {
|
||||||
|
const relateDs = this.getDataSourceById(relateDsId);
|
||||||
|
if (relateDs) {
|
||||||
|
copyData.push(relateDs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
storageService.setItem(COPY_DS_STORAGE_KEY, copyData, {
|
||||||
|
protocol: Protocol.OBJECT,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 粘贴数据源
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public paste() {
|
||||||
|
const dataSource: DataSourceSchema[] = storageService.getItem(COPY_DS_STORAGE_KEY);
|
||||||
|
dataSource.forEach((item: DataSourceSchema) => {
|
||||||
|
// 不覆盖同样id的数据源
|
||||||
|
if (!this.getDataSourceById(item.id)) {
|
||||||
|
this.add(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DataSourceService = DataSource;
|
export type DataSourceService = DataSource;
|
||||||
|
@ -23,7 +23,7 @@ import { Writable } from 'type-fest';
|
|||||||
import { DepTargetType } from '@tmagic/dep';
|
import { DepTargetType } from '@tmagic/dep';
|
||||||
import type { FormConfig } from '@tmagic/form';
|
import type { FormConfig } from '@tmagic/form';
|
||||||
import type { Id, MComponent, MNode } from '@tmagic/schema';
|
import type { Id, MComponent, MNode } from '@tmagic/schema';
|
||||||
import { getValueByKeyPath, guid, setValueByKeyPath, toLine } from '@tmagic/utils';
|
import { getNodePath, getValueByKeyPath, guid, setValueByKeyPath, toLine } from '@tmagic/utils';
|
||||||
|
|
||||||
import depService from '@editor/services/dep';
|
import depService from '@editor/services/dep';
|
||||||
import editorService from '@editor/services/editor';
|
import editorService from '@editor/services/editor';
|
||||||
@ -199,13 +199,14 @@ class Props extends BaseService {
|
|||||||
public replaceRelateId(originConfigs: MNode[], targetConfigs: MNode[]) {
|
public replaceRelateId(originConfigs: MNode[], targetConfigs: MNode[]) {
|
||||||
const relateIdMap = this.getRelateIdMap();
|
const relateIdMap = this.getRelateIdMap();
|
||||||
if (Object.keys(relateIdMap).length === 0) return;
|
if (Object.keys(relateIdMap).length === 0) return;
|
||||||
|
depService.clearByType(DepTargetType.RELATED_COMP_WHEN_COPY);
|
||||||
|
depService.collect(originConfigs, true, DepTargetType.RELATED_COMP_WHEN_COPY);
|
||||||
const target = depService.getTarget(DepTargetType.RELATED_COMP_WHEN_COPY, DepTargetType.RELATED_COMP_WHEN_COPY);
|
const target = depService.getTarget(DepTargetType.RELATED_COMP_WHEN_COPY, DepTargetType.RELATED_COMP_WHEN_COPY);
|
||||||
if (!target) return;
|
if (!target) return;
|
||||||
|
|
||||||
originConfigs.forEach((config: MNode) => {
|
originConfigs.forEach((config: MNode) => {
|
||||||
const newId = relateIdMap[config.id];
|
const newId = relateIdMap[config.id];
|
||||||
const targetConfig = targetConfigs.find((targetConfig) => targetConfig.id === newId);
|
const path = getNodePath(newId, targetConfigs);
|
||||||
|
const targetConfig = path[path.length - 1];
|
||||||
|
|
||||||
if (!targetConfig) return;
|
if (!targetConfig) return;
|
||||||
|
|
||||||
@ -213,9 +214,13 @@ class Props extends BaseService {
|
|||||||
const relateOriginId = getValueByKeyPath(fullKey, config);
|
const relateOriginId = getValueByKeyPath(fullKey, config);
|
||||||
const relateTargetId = relateIdMap[relateOriginId];
|
const relateTargetId = relateIdMap[relateOriginId];
|
||||||
if (!relateTargetId) return;
|
if (!relateTargetId) return;
|
||||||
|
|
||||||
setValueByKeyPath(fullKey, relateTargetId, targetConfig);
|
setValueByKeyPath(fullKey, relateTargetId, targetConfig);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 递归items
|
||||||
|
if (config.items && Array.isArray(config.items)) {
|
||||||
|
this.replaceRelateId(config.items, targetConfigs);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,8 @@ import { calcValueByFontsize, getNodePath, isNumber, isPage, isPageFragment, isP
|
|||||||
|
|
||||||
import { Layout } from '@editor/type';
|
import { Layout } from '@editor/type';
|
||||||
export const COPY_STORAGE_KEY = '$MagicEditorCopyData';
|
export const COPY_STORAGE_KEY = '$MagicEditorCopyData';
|
||||||
|
export const COPY_CODE_STORAGE_KEY = '$MagicEditorCopyCode';
|
||||||
|
export const COPY_DS_STORAGE_KEY = '$MagicEditorCopyDataSource';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取所有页面配置
|
* 获取所有页面配置
|
||||||
|
@ -23,12 +23,14 @@
|
|||||||
"@tmagic/stage": "1.4.5",
|
"@tmagic/stage": "1.4.5",
|
||||||
"@tmagic/utils": "1.4.5",
|
"@tmagic/utils": "1.4.5",
|
||||||
"element-plus": "^2.6.1",
|
"element-plus": "^2.6.1",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
"monaco-editor": "^0.48.0",
|
"monaco-editor": "^0.48.0",
|
||||||
"serialize-javascript": "^6.0.0",
|
"serialize-javascript": "^6.0.0",
|
||||||
"vue": "^3.4.27",
|
"vue": "^3.4.27",
|
||||||
"vue-router": "^4.0.10"
|
"vue-router": "^4.0.10"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/lodash-es": "^4.17.4",
|
||||||
"@types/node": "^18.19.0",
|
"@types/node": "^18.19.0",
|
||||||
"@types/serialize-javascript": "^5.0.1",
|
"@types/serialize-javascript": "^5.0.1",
|
||||||
"@vitejs/plugin-legacy": "^5.4.0",
|
"@vitejs/plugin-legacy": "^5.4.0",
|
||||||
|
@ -17,6 +17,9 @@
|
|||||||
:moveable-options="moveableOptions"
|
:moveable-options="moveableOptions"
|
||||||
:auto-scroll-into-view="true"
|
:auto-scroll-into-view="true"
|
||||||
:stage-rect="stageRect"
|
:stage-rect="stageRect"
|
||||||
|
:collector-options="collectorOptions"
|
||||||
|
:layerContentMenu="contentMenuData"
|
||||||
|
:stageContentMenu="contentMenuData"
|
||||||
@props-submit-error="propsSubmitErrorHandler"
|
@props-submit-error="propsSubmitErrorHandler"
|
||||||
>
|
>
|
||||||
<template #workspace-content>
|
<template #workspace-content>
|
||||||
@ -38,17 +41,29 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, nextTick, onBeforeUnmount, ref, toRaw } from 'vue';
|
import { computed, markRaw, nextTick, onBeforeUnmount, Ref, ref, toRaw } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { Coin, Connection, Document } from '@element-plus/icons-vue';
|
import { Coin, Connection, CopyDocument, Document, DocumentCopy } from '@element-plus/icons-vue';
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
import serialize from 'serialize-javascript';
|
import serialize from 'serialize-javascript';
|
||||||
|
|
||||||
import { TMagicDialog, tMagicMessage, tMagicMessageBox } from '@tmagic/design';
|
import { TMagicDialog, tMagicMessage, tMagicMessageBox } from '@tmagic/design';
|
||||||
import { DatasourceTypeOption, editorService, MenuBarData, MoveableOptions, TMagicEditor } from '@tmagic/editor';
|
import {
|
||||||
|
ContentMenu,
|
||||||
|
COPY_STORAGE_KEY,
|
||||||
|
DatasourceTypeOption,
|
||||||
|
DepTargetType,
|
||||||
|
editorService,
|
||||||
|
MenuBarData,
|
||||||
|
MenuButton,
|
||||||
|
MoveableOptions,
|
||||||
|
Services,
|
||||||
|
TMagicEditor,
|
||||||
|
} from '@tmagic/editor';
|
||||||
import type { MContainer, MNode } from '@tmagic/schema';
|
import type { MContainer, MNode } from '@tmagic/schema';
|
||||||
import { NodeType } from '@tmagic/schema';
|
import { NodeType } from '@tmagic/schema';
|
||||||
import type { CustomizeMoveableOptionsCallbackConfig } from '@tmagic/stage';
|
import type { CustomizeMoveableOptionsCallbackConfig } from '@tmagic/stage';
|
||||||
import { asyncLoadJs } from '@tmagic/utils';
|
import { asyncLoadJs, calcValueByFontsize } from '@tmagic/utils';
|
||||||
|
|
||||||
import DeviceGroup from '../components/DeviceGroup.vue';
|
import DeviceGroup from '../components/DeviceGroup.vue';
|
||||||
import componentGroupList from '../configs/componentGroupList';
|
import componentGroupList from '../configs/componentGroupList';
|
||||||
@ -86,6 +101,58 @@ const previewUrl = computed(
|
|||||||
() => `${VITE_RUNTIME_PATH}/page/index.html?localPreview=1&page=${editor.value?.editorService.get('page')?.id}`,
|
() => `${VITE_RUNTIME_PATH}/page/index.html?localPreview=1&page=${editor.value?.editorService.get('page')?.id}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const collectorOptions = {
|
||||||
|
name: '蒙层',
|
||||||
|
isTarget: (key: string | number, value: any) =>
|
||||||
|
typeof key === 'string' && typeof value === 'string' && key.includes('events') && value.startsWith('overlay_'),
|
||||||
|
autoCollect: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const usePasteMenu = (menu?: Ref<InstanceType<typeof ContentMenu> | undefined>): MenuButton => ({
|
||||||
|
type: 'button',
|
||||||
|
text: '粘贴(带关联信息)',
|
||||||
|
icon: markRaw(DocumentCopy),
|
||||||
|
display: (services) => !!services?.storageService?.getItem(COPY_STORAGE_KEY),
|
||||||
|
handler: (services) => {
|
||||||
|
const nodes = services?.editorService?.get('nodes');
|
||||||
|
if (!nodes || nodes.length === 0) return;
|
||||||
|
|
||||||
|
if (menu?.value?.$el) {
|
||||||
|
const stage = services?.editorService?.get('stage');
|
||||||
|
const rect = menu.value.$el.getBoundingClientRect();
|
||||||
|
const parentRect = stage?.container?.getBoundingClientRect();
|
||||||
|
const initialLeft =
|
||||||
|
calcValueByFontsize(stage?.renderer.getDocument(), (rect.left || 0) - (parentRect?.left || 0)) /
|
||||||
|
services.uiService.get('zoom');
|
||||||
|
const initialTop =
|
||||||
|
calcValueByFontsize(stage?.renderer.getDocument(), (rect.top || 0) - (parentRect?.top || 0)) /
|
||||||
|
services.uiService.get('zoom');
|
||||||
|
services?.editorService?.paste({ left: initialLeft, top: initialTop });
|
||||||
|
} else {
|
||||||
|
services?.editorService?.paste();
|
||||||
|
services?.codeBlockService?.paste();
|
||||||
|
services?.dataSourceService?.paste();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const contentMenuData = computed(() => [
|
||||||
|
{
|
||||||
|
type: 'button',
|
||||||
|
text: '复制(带关联信息)',
|
||||||
|
icon: markRaw(CopyDocument),
|
||||||
|
display: (services: Services) =>
|
||||||
|
services?.depService?.hasSpecifiedTypeTarget(DepTargetType.RELATED_COMP_WHEN_COPY) || false,
|
||||||
|
handler: (services: Services) => {
|
||||||
|
const nodes = services?.editorService?.get('nodes');
|
||||||
|
nodes && services?.editorService?.copyWithRelated(cloneDeep(nodes));
|
||||||
|
nodes && services?.codeBlockService?.copyWithRelated(cloneDeep(nodes));
|
||||||
|
nodes && services?.dataSourceService?.copyWithRelated(cloneDeep(nodes));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
usePasteMenu(),
|
||||||
|
]);
|
||||||
|
|
||||||
const menu: MenuBarData = {
|
const menu: MenuBarData = {
|
||||||
left: [
|
left: [
|
||||||
{
|
{
|
||||||
|
6
pnpm-lock.yaml
generated
6
pnpm-lock.yaml
generated
@ -770,6 +770,9 @@ importers:
|
|||||||
element-plus:
|
element-plus:
|
||||||
specifier: ^2.6.1
|
specifier: ^2.6.1
|
||||||
version: 2.6.1(@vue/composition-api@1.7.2(vue@3.4.27(typescript@5.4.2)))(vue@3.4.27(typescript@5.4.2))
|
version: 2.6.1(@vue/composition-api@1.7.2(vue@3.4.27(typescript@5.4.2)))(vue@3.4.27(typescript@5.4.2))
|
||||||
|
lodash-es:
|
||||||
|
specifier: ^4.17.21
|
||||||
|
version: 4.17.21
|
||||||
monaco-editor:
|
monaco-editor:
|
||||||
specifier: ^0.48.0
|
specifier: ^0.48.0
|
||||||
version: 0.48.0
|
version: 0.48.0
|
||||||
@ -783,6 +786,9 @@ importers:
|
|||||||
specifier: ^4.0.10
|
specifier: ^4.0.10
|
||||||
version: 4.0.10(vue@3.4.27(typescript@5.4.2))
|
version: 4.0.10(vue@3.4.27(typescript@5.4.2))
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
'@types/lodash-es':
|
||||||
|
specifier: ^4.17.4
|
||||||
|
version: 4.17.7
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^18.19.0
|
specifier: ^18.19.0
|
||||||
version: 18.19.3
|
version: 18.19.3
|
||||||
|
Loading…
x
Reference in New Issue
Block a user