mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-04-06 03:57:56 +08:00
feat: 添加组件支持添加组合
This commit is contained in:
parent
c8c692b69c
commit
5e3125706c
@ -38,7 +38,6 @@ import {
|
|||||||
initPosition,
|
initPosition,
|
||||||
isFixed,
|
isFixed,
|
||||||
setLayout,
|
setLayout,
|
||||||
setNewItemId,
|
|
||||||
} from '@editor/utils/editor';
|
} from '@editor/utils/editor';
|
||||||
import { log } from '@editor/utils/logger';
|
import { log } from '@editor/utils/logger';
|
||||||
|
|
||||||
@ -495,7 +494,7 @@ class Editor extends BaseService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setNewItemId(config, this.get('root'));
|
await propsService.setNewItemId(config, this.get('root'));
|
||||||
if (config.style) {
|
if (config.style) {
|
||||||
config.style = {
|
config.style = {
|
||||||
...config.style,
|
...config.style,
|
||||||
|
@ -17,11 +17,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { reactive } from 'vue';
|
import { reactive } from 'vue';
|
||||||
import { cloneDeep, mergeWith } from 'lodash-es';
|
import { cloneDeep, mergeWith, random } from 'lodash-es';
|
||||||
|
|
||||||
import type { FormConfig } from '@tmagic/form';
|
import type { FormConfig } from '@tmagic/form';
|
||||||
import type { MComponent, MNode } from '@tmagic/schema';
|
import type { Id, MComponent, MNode, MPage } from '@tmagic/schema';
|
||||||
import { toLine } from '@tmagic/utils';
|
import { NodeType } from '@tmagic/schema';
|
||||||
|
import { isPop, toLine } from '@tmagic/utils';
|
||||||
|
|
||||||
import type { PropsState } from '@editor/type';
|
import type { PropsState } from '@editor/type';
|
||||||
import { DEFAULT_CONFIG, fillConfig, getDefaultPropsValue } from '@editor/utils/props';
|
import { DEFAULT_CONFIG, fillConfig, getDefaultPropsValue } from '@editor/utils/props';
|
||||||
@ -35,7 +36,7 @@ class Props extends BaseService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super(['setPropsConfig', 'getPropsConfig', 'setPropsValue', 'getPropsValue']);
|
super(['setPropsConfig', 'getPropsConfig', 'setPropsValue', 'getPropsValue', 'createId', 'setNewItemId']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setPropsConfigs(configs: Record<string, FormConfig>) {
|
public setPropsConfigs(configs: Record<string, FormConfig>) {
|
||||||
@ -87,7 +88,7 @@ class Props extends BaseService {
|
|||||||
* @param type 组件类型
|
* @param type 组件类型
|
||||||
* @returns 组件初始值
|
* @returns 组件初始值
|
||||||
*/
|
*/
|
||||||
public async getPropsValue(type: string, defaultValue = {}) {
|
public async getPropsValue(type: string, defaultValue: Record<string, any> = {}) {
|
||||||
if (type === 'area') {
|
if (type === 'area') {
|
||||||
const value = (await this.getPropsValue('button')) as MComponent;
|
const value = (await this.getPropsValue('button')) as MComponent;
|
||||||
value.className = 'action-area';
|
value.className = 'action-area';
|
||||||
@ -98,13 +99,67 @@ class Props extends BaseService {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const data = cloneDeep(defaultValue as any);
|
||||||
|
|
||||||
|
await this.setNewItemId(data);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...getDefaultPropsValue(type),
|
...getDefaultPropsValue(type, await this.createId(type)),
|
||||||
...mergeWith(cloneDeep(this.state.propsValueMap[type] || {}), cloneDeep(defaultValue)),
|
...mergeWith(cloneDeep(this.state.propsValueMap[type] || {}), data),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async createId(type: string | number): Promise<string> {
|
||||||
|
return `${type}_${random(10000, false)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将组件与组件的子元素配置中的id都设置成一个新的ID
|
||||||
|
* @param {Object} config 组件配置
|
||||||
|
*/
|
||||||
|
/* eslint no-param-reassign: ["error", { "props": false }] */
|
||||||
|
public async setNewItemId(config: MNode, parent?: MPage) {
|
||||||
|
const oldId = config.id;
|
||||||
|
|
||||||
|
config.id = await this.createId(config.type || 'component');
|
||||||
|
|
||||||
|
// 只有弹窗在页面下的一级子元素才有效
|
||||||
|
if (isPop(config) && parent?.type === NodeType.PAGE) {
|
||||||
|
updatePopId(oldId, config.id, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.items && Array.isArray(config.items)) {
|
||||||
|
for (const item of config.items) {
|
||||||
|
await this.setNewItemId(item, config as MPage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制页面时,需要把组件下关联的弹窗id换测复制出来的弹窗的id
|
||||||
|
* @param {number} oldId 复制的源弹窗id
|
||||||
|
* @param {number} popId 新的弹窗id
|
||||||
|
* @param {Object} pageConfig 页面配置
|
||||||
|
*/
|
||||||
|
const updatePopId = (oldId: Id, popId: Id, pageConfig: MPage) => {
|
||||||
|
pageConfig.items?.forEach((config) => {
|
||||||
|
if (config.pop === oldId) {
|
||||||
|
config.pop = popId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.popId === oldId) {
|
||||||
|
config.popId = popId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(config.items)) {
|
||||||
|
updatePopId(oldId, popId, config as MPage);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export type PropsService = Props;
|
export type PropsService = Props;
|
||||||
|
|
||||||
export default new Props();
|
export default new Props();
|
||||||
|
@ -16,9 +16,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { random } from 'lodash-es';
|
import type { MApp, MContainer, MNode, MPage } from '@tmagic/schema';
|
||||||
|
|
||||||
import type { Id, MApp, MContainer, MNode, MPage } from '@tmagic/schema';
|
|
||||||
import { NodeType } from '@tmagic/schema';
|
import { NodeType } from '@tmagic/schema';
|
||||||
import type StageCore from '@tmagic/stage';
|
import type StageCore from '@tmagic/stage';
|
||||||
import { getNodePath, isNumber, isPage, isPop } from '@tmagic/utils';
|
import { getNodePath, isNumber, isPage, isPop } from '@tmagic/utils';
|
||||||
@ -27,8 +25,6 @@ import { Layout } from '@editor/type';
|
|||||||
|
|
||||||
export const COPY_STORAGE_KEY = '$MagicEditorCopyData';
|
export const COPY_STORAGE_KEY = '$MagicEditorCopyData';
|
||||||
|
|
||||||
export const generateId = (type: string | number): string => `${type}_${random(10000, false)}`;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取所有页面配置
|
* 获取所有页面配置
|
||||||
* @param app DSL跟节点
|
* @param app DSL跟节点
|
||||||
@ -74,51 +70,6 @@ export const generatePageName = (pageNameList: string[]): string => {
|
|||||||
*/
|
*/
|
||||||
export const generatePageNameByApp = (app: MApp): string => generatePageName(getPageNameList(getPageList(app)));
|
export const generatePageNameByApp = (app: MApp): string => generatePageName(getPageNameList(getPageList(app)));
|
||||||
|
|
||||||
/**
|
|
||||||
* 复制页面时,需要把组件下关联的弹窗id换测复制出来的弹窗的id
|
|
||||||
* @param {number} oldId 复制的源弹窗id
|
|
||||||
* @param {number} popId 新的弹窗id
|
|
||||||
* @param {Object} pageConfig 页面配置
|
|
||||||
*/
|
|
||||||
const updatePopId = (oldId: Id, popId: Id, pageConfig: MPage) => {
|
|
||||||
pageConfig.items?.forEach((config) => {
|
|
||||||
if (config.pop === oldId) {
|
|
||||||
config.pop = popId;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.popId === oldId) {
|
|
||||||
config.popId = popId;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(config.items)) {
|
|
||||||
updatePopId(oldId, popId, config as MPage);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将组件与组件的子元素配置中的id都设置成一个新的ID
|
|
||||||
* @param {Object} config 组件配置
|
|
||||||
*/
|
|
||||||
/* eslint no-param-reassign: ["error", { "props": false }] */
|
|
||||||
export const setNewItemId = (config: MNode, parent?: MPage) => {
|
|
||||||
const oldId = config.id;
|
|
||||||
|
|
||||||
config.id = generateId(config.type || 'component');
|
|
||||||
config.name = `${config.name?.replace(/_(\d+)$/, '')}_${config.id}`;
|
|
||||||
|
|
||||||
// 只有弹窗在页面下的一级子元素才有效
|
|
||||||
if (isPop(config) && parent?.type === NodeType.PAGE) {
|
|
||||||
updatePopId(oldId, config.id, parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.items && Array.isArray(config.items)) {
|
|
||||||
config.items.forEach((item) => setNewItemId(item, config as MPage));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} node
|
* @param {Object} node
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
|
@ -20,7 +20,6 @@ import { FormConfig, FormState } from '@tmagic/form';
|
|||||||
|
|
||||||
import editorService from '@editor/services/editor';
|
import editorService from '@editor/services/editor';
|
||||||
import eventsService from '@editor/services/events';
|
import eventsService from '@editor/services/events';
|
||||||
import { generateId } from '@editor/utils/editor';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 统一为组件属性表单加上事件、高级、样式配置
|
* 统一为组件属性表单加上事件、高级、样式配置
|
||||||
@ -244,9 +243,9 @@ export const DEFAULT_CONFIG: FormConfig = fillConfig([]);
|
|||||||
* @param type 组件类型
|
* @param type 组件类型
|
||||||
* @returns Object
|
* @returns Object
|
||||||
*/
|
*/
|
||||||
export const getDefaultPropsValue = (type: string) => ({
|
export const getDefaultPropsValue = (type: string, id: string) => ({
|
||||||
type,
|
type,
|
||||||
id: generateId(type),
|
id,
|
||||||
style: {},
|
style: {},
|
||||||
name: type,
|
name: type,
|
||||||
});
|
});
|
||||||
|
63
packages/editor/tests/unit/services/props.spec.ts
Normal file
63
packages/editor/tests/unit/services/props.spec.ts
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import { describe, expect, test } from 'vitest';
|
||||||
|
|
||||||
|
import { NodeType } from '@tmagic/schema';
|
||||||
|
|
||||||
|
import props from '@editor/services/props';
|
||||||
|
|
||||||
|
test('createId', async () => {
|
||||||
|
const id = await props.createId('text');
|
||||||
|
|
||||||
|
console.log(id);
|
||||||
|
|
||||||
|
expect(id.startsWith('text')).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setNewItemId', () => {
|
||||||
|
test('普通', async () => {
|
||||||
|
const config = {
|
||||||
|
id: 1,
|
||||||
|
type: 'text',
|
||||||
|
};
|
||||||
|
// 将组件与组件的子元素配置中的id都设置成一个新的ID
|
||||||
|
await props.setNewItemId(config);
|
||||||
|
expect(config.id === 1).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('items', async () => {
|
||||||
|
const config = {
|
||||||
|
id: 1,
|
||||||
|
type: NodeType.PAGE,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
id: 2,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
await props.setNewItemId(config);
|
||||||
|
expect(config.id === 1).toBeFalsy();
|
||||||
|
expect(config.items[0].id === 2).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('pop', async () => {
|
||||||
|
const config = {
|
||||||
|
id: 1,
|
||||||
|
type: NodeType.PAGE,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
type: 'button',
|
||||||
|
id: 2,
|
||||||
|
pop: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'pop',
|
||||||
|
id: 3,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
await props.setNewItemId(config);
|
||||||
|
expect(config.items[0].pop === 3).toBeFalsy();
|
||||||
|
expect(config.items[1].id === 3).toBeFalsy();
|
||||||
|
expect(config.items[1].id === config.items[0].pop).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -23,12 +23,6 @@ import { NodeType } from '@tmagic/schema';
|
|||||||
import * as editor from '@editor/utils/editor';
|
import * as editor from '@editor/utils/editor';
|
||||||
|
|
||||||
describe('util form', () => {
|
describe('util form', () => {
|
||||||
test('generateId', () => {
|
|
||||||
const id = editor.generateId('text');
|
|
||||||
|
|
||||||
expect(id.startsWith('text')).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('getPageList', () => {
|
test('getPageList', () => {
|
||||||
const pageList = editor.getPageList({
|
const pageList = editor.getPageList({
|
||||||
id: 'app_1',
|
id: 'app_1',
|
||||||
@ -67,56 +61,6 @@ describe('util form', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('setNewItemId', () => {
|
|
||||||
test('普通', () => {
|
|
||||||
const config = {
|
|
||||||
id: 1,
|
|
||||||
type: 'text',
|
|
||||||
};
|
|
||||||
// 将组件与组件的子元素配置中的id都设置成一个新的ID
|
|
||||||
editor.setNewItemId(config);
|
|
||||||
expect(config.id === 1).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('items', () => {
|
|
||||||
const config = {
|
|
||||||
id: 1,
|
|
||||||
type: NodeType.PAGE,
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
id: 2,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
editor.setNewItemId(config);
|
|
||||||
expect(config.id === 1).toBeFalsy();
|
|
||||||
expect(config.items[0].id === 2).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('pop', () => {
|
|
||||||
const config = {
|
|
||||||
id: 1,
|
|
||||||
type: NodeType.PAGE,
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
type: 'button',
|
|
||||||
id: 2,
|
|
||||||
pop: 3,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'pop',
|
|
||||||
id: 3,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
editor.setNewItemId(config);
|
|
||||||
expect(config.items[0].pop === 3).toBeFalsy();
|
|
||||||
expect(config.items[1].id === 3).toBeFalsy();
|
|
||||||
expect(config.items[1].id === config.items[0].pop).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('isFixed', () => {
|
describe('isFixed', () => {
|
||||||
test('true', () => {
|
test('true', () => {
|
||||||
expect(
|
expect(
|
||||||
|
@ -44,7 +44,8 @@ describe('util form', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('getDefaultValue', () => {
|
test('getDefaultValue', () => {
|
||||||
const value = props.getDefaultPropsValue('text');
|
const value = props.getDefaultPropsValue('text', '1');
|
||||||
expect(value.id.startsWith('text')).toBeTruthy();
|
expect(value.id).toBe('1');
|
||||||
|
expect(value.type).toBe('text');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user