mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-09-01 04:09:49 +08:00
style(editor): 完善service use-plugin的ts类型定义
This commit is contained in:
parent
5cf137e5e8
commit
16e45cb45d
@ -84,6 +84,7 @@
|
||||
"rimraf": "^3.0.2",
|
||||
"sass": "^1.35.1",
|
||||
"tsc-alias": "^1.8.5",
|
||||
"type-fest": "^4.10.3",
|
||||
"typescript": "^5.0.4",
|
||||
"vite": "^5.0.7",
|
||||
"vue-tsc": "^1.8.25"
|
||||
|
@ -194,6 +194,9 @@ export default class extends EventEmitter {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 请使用usePlugin代替
|
||||
*/
|
||||
public use(options: Record<string, Function>) {
|
||||
Object.entries(options).forEach(([methodName, method]: [string, Function]) => {
|
||||
if (typeof method === 'function') this.middleware[methodName].push(method);
|
||||
|
@ -18,16 +18,24 @@
|
||||
|
||||
import { reactive } from 'vue';
|
||||
import { keys, pick } from 'lodash-es';
|
||||
import type { Writable } from 'type-fest';
|
||||
|
||||
import type { ColumnConfig } from '@tmagic/form';
|
||||
import type { CodeBlockContent, CodeBlockDSL, Id } from '@tmagic/schema';
|
||||
|
||||
import type { CodeState } from '@editor/type';
|
||||
import type { AsyncHookPlugin, CodeState } from '@editor/type';
|
||||
import { CODE_DRAFT_STORAGE_KEY } from '@editor/type';
|
||||
import { getConfig } from '@editor/utils/config';
|
||||
|
||||
import BaseService from './BaseService';
|
||||
|
||||
const canUsePluginMethods = {
|
||||
async: ['setCodeDslById', 'setEditStatus', 'setCombineIds', 'setUndeleteableList', 'deleteCodeDslByIds'] as const,
|
||||
sync: [],
|
||||
};
|
||||
|
||||
type AsyncMethodName = Writable<(typeof canUsePluginMethods)['async']>;
|
||||
|
||||
class CodeBlock extends BaseService {
|
||||
private state = reactive<CodeState>({
|
||||
codeDsl: null,
|
||||
@ -38,13 +46,7 @@ class CodeBlock extends BaseService {
|
||||
});
|
||||
|
||||
constructor() {
|
||||
super([
|
||||
{ name: 'setCodeDslById', isAsync: true },
|
||||
{ name: 'setEditStatus', isAsync: true },
|
||||
{ name: 'setCombineIds', isAsync: true },
|
||||
{ name: 'setUndeleteableList', isAsync: true },
|
||||
{ name: 'deleteCodeDslByIds', isAsync: true },
|
||||
]);
|
||||
super(canUsePluginMethods.async.map((methodName) => ({ name: methodName, isAsync: true })));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -243,6 +245,10 @@ class CodeBlock extends BaseService {
|
||||
this.removeAllListeners();
|
||||
this.removeAllPlugins();
|
||||
}
|
||||
|
||||
public usePlugin(options: AsyncHookPlugin<AsyncMethodName, CodeBlock>): void {
|
||||
super.usePlugin(options);
|
||||
}
|
||||
}
|
||||
|
||||
export type CodeBlockService = CodeBlock;
|
||||
|
@ -1,12 +1,13 @@
|
||||
import { reactive } from 'vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { Writable } from 'type-fest';
|
||||
|
||||
import type { EventOption } from '@tmagic/core';
|
||||
import type { FormConfig } from '@tmagic/form';
|
||||
import type { DataSourceSchema } from '@tmagic/schema';
|
||||
import { guid } from '@tmagic/utils';
|
||||
|
||||
import type { DatasourceTypeOption } from '@editor/type';
|
||||
import type { DatasourceTypeOption, SyncHookPlugin } from '@editor/type';
|
||||
import { getFormConfig, getFormValue } from '@editor/utils/data-source';
|
||||
|
||||
import BaseService from './BaseService';
|
||||
@ -22,6 +23,27 @@ interface State {
|
||||
}
|
||||
|
||||
type StateKey = keyof State;
|
||||
|
||||
const canUsePluginMethods = {
|
||||
async: [],
|
||||
sync: [
|
||||
'getFormConfig',
|
||||
'setFormConfig',
|
||||
'getFormValue',
|
||||
'setFormValue',
|
||||
'getFormEvent',
|
||||
'setFormEvent',
|
||||
'getFormMethod',
|
||||
'setFormMethod',
|
||||
'add',
|
||||
'update',
|
||||
'remove',
|
||||
'createId',
|
||||
] as const,
|
||||
};
|
||||
|
||||
type SyncMethodName = Writable<(typeof canUsePluginMethods)['sync']>;
|
||||
|
||||
class DataSource extends BaseService {
|
||||
private state = reactive<State>({
|
||||
datasourceTypeList: [],
|
||||
@ -34,20 +56,7 @@ class DataSource extends BaseService {
|
||||
});
|
||||
|
||||
constructor() {
|
||||
super([
|
||||
{ name: 'getFormConfig', isAsync: false },
|
||||
{ name: 'setFormConfig', isAsync: false },
|
||||
{ name: 'getFormValue', isAsync: false },
|
||||
{ name: 'setFormValue', isAsync: false },
|
||||
{ name: 'getFormEvent', isAsync: false },
|
||||
{ name: 'setFormEvent', isAsync: false },
|
||||
{ name: 'getFormMethod', isAsync: false },
|
||||
{ name: 'setFormMethod', isAsync: false },
|
||||
{ name: 'add', isAsync: false },
|
||||
{ name: 'update', isAsync: false },
|
||||
{ name: 'remove', isAsync: false },
|
||||
{ name: 'createId', isAsync: false },
|
||||
]);
|
||||
super(canUsePluginMethods.sync.map((methodName) => ({ name: methodName, isAsync: false })));
|
||||
}
|
||||
|
||||
public set<K extends StateKey, T extends State[K]>(name: K, value: T) {
|
||||
@ -123,6 +132,10 @@ class DataSource extends BaseService {
|
||||
this.emit('remove', id);
|
||||
}
|
||||
|
||||
public createId(): string {
|
||||
return `ds_${guid()}`;
|
||||
}
|
||||
|
||||
public getDataSourceById(id: string) {
|
||||
return this.get('dataSources').find((ds) => ds.id === id);
|
||||
}
|
||||
@ -137,8 +150,8 @@ class DataSource extends BaseService {
|
||||
this.removeAllPlugins();
|
||||
}
|
||||
|
||||
private createId(): string {
|
||||
return `ds_${guid()}`;
|
||||
public usePlugin(options: SyncHookPlugin<SyncMethodName, DataSource>): void {
|
||||
super.usePlugin(options);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
import { reactive, toRaw } from 'vue';
|
||||
import { cloneDeep, get, isObject, mergeWith, uniq } from 'lodash-es';
|
||||
import { Writable } from 'type-fest';
|
||||
|
||||
import { DepTargetType } from '@tmagic/dep';
|
||||
import type { Id, MApp, MComponent, MContainer, MNode, MPage, MPageFragment } from '@tmagic/schema';
|
||||
@ -29,7 +30,15 @@ import propsService from '@editor/services//props';
|
||||
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';
|
||||
import type {
|
||||
AddMNode,
|
||||
AsyncHookPlugin,
|
||||
EditorNodeInfo,
|
||||
PastePosition,
|
||||
StepValue,
|
||||
StoreState,
|
||||
StoreStateKey,
|
||||
} from '@editor/type';
|
||||
import { LayerOffset, Layout } from '@editor/type';
|
||||
import {
|
||||
change2Fixed,
|
||||
@ -46,6 +55,36 @@ import {
|
||||
} from '@editor/utils/editor';
|
||||
import { beforePaste, getAddParent } from '@editor/utils/operator';
|
||||
|
||||
const canUsePluginMethods = {
|
||||
async: [
|
||||
'getLayout',
|
||||
'highlight',
|
||||
'select',
|
||||
'multiSelect',
|
||||
'doAdd',
|
||||
'add',
|
||||
'doRemove',
|
||||
'remove',
|
||||
'doUpdate',
|
||||
'update',
|
||||
'sort',
|
||||
'copy',
|
||||
'paste',
|
||||
'doPaste',
|
||||
'doAlignCenter',
|
||||
'alignCenter',
|
||||
'moveLayer',
|
||||
'moveToContainer',
|
||||
'dragTo',
|
||||
'undo',
|
||||
'redo',
|
||||
'move',
|
||||
] as const,
|
||||
sync: [],
|
||||
};
|
||||
|
||||
type AsyncMethodName = Writable<(typeof canUsePluginMethods)['async']>;
|
||||
|
||||
class Editor extends BaseService {
|
||||
public state: StoreState = reactive({
|
||||
root: null,
|
||||
@ -65,29 +104,7 @@ class Editor extends BaseService {
|
||||
|
||||
constructor() {
|
||||
super(
|
||||
[
|
||||
{ name: 'getLayout', isAsync: true },
|
||||
{ name: 'select', isAsync: true },
|
||||
{ name: 'doAdd', isAsync: true },
|
||||
{ name: 'add', isAsync: true },
|
||||
{ name: 'doRemove', isAsync: true },
|
||||
{ name: 'remove', isAsync: true },
|
||||
{ name: 'doUpdate', isAsync: true },
|
||||
{ name: 'update', isAsync: true },
|
||||
{ name: 'sort', isAsync: true },
|
||||
{ name: 'copy', isAsync: true },
|
||||
{ name: 'paste', isAsync: true },
|
||||
{ name: 'doPaste', isAsync: true },
|
||||
{ name: 'doAlignCenter', isAsync: true },
|
||||
{ name: 'alignCenter', isAsync: true },
|
||||
{ name: 'moveLayer', isAsync: true },
|
||||
{ name: 'moveToContainer', isAsync: true },
|
||||
{ name: 'move', isAsync: true },
|
||||
{ name: 'undo', isAsync: true },
|
||||
{ name: 'redo', isAsync: true },
|
||||
{ name: 'highlight', isAsync: true },
|
||||
{ name: 'dragTo', isAsync: true },
|
||||
],
|
||||
canUsePluginMethods.async.map((methodName) => ({ name: methodName, isAsync: true })),
|
||||
// 需要注意循环依赖问题,如果函数间有相互调用的话,不能设置为串行调用
|
||||
['select', 'update', 'moveLayer'],
|
||||
);
|
||||
@ -696,7 +713,7 @@ class Editor extends BaseService {
|
||||
|
||||
public async doPaste(config: MNode[], position: PastePosition = {}): Promise<MNode[]> {
|
||||
propsService.clearRelateId();
|
||||
const pasteConfigs = await beforePaste(position, cloneDeep(config));
|
||||
const pasteConfigs = beforePaste(position, cloneDeep(config));
|
||||
return pasteConfigs;
|
||||
}
|
||||
|
||||
@ -985,6 +1002,10 @@ class Editor extends BaseService {
|
||||
this.get('modifiedNodeIds').clear();
|
||||
}
|
||||
|
||||
public usePlugin(options: AsyncHookPlugin<AsyncMethodName, Editor>): void {
|
||||
super.usePlugin(options);
|
||||
}
|
||||
|
||||
private addModifiedNodeId(id: Id) {
|
||||
if (!this.isHistoryStateChange) {
|
||||
this.get('modifiedNodeIds').set(id, id);
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
import { reactive } from 'vue';
|
||||
import { cloneDeep, mergeWith } from 'lodash-es';
|
||||
import { Writable } from 'type-fest';
|
||||
|
||||
import { DepTargetType } from '@tmagic/dep';
|
||||
import type { FormConfig } from '@tmagic/form';
|
||||
@ -26,11 +27,26 @@ import { getValueByKeyPath, guid, setValueByKeyPath, toLine } from '@tmagic/util
|
||||
|
||||
import depService from '@editor/services/dep';
|
||||
import editorService from '@editor/services/editor';
|
||||
import type { PropsState } from '@editor/type';
|
||||
import type { AsyncHookPlugin, PropsState, SyncHookPlugin } from '@editor/type';
|
||||
import { fillConfig } from '@editor/utils/props';
|
||||
|
||||
import BaseService from './BaseService';
|
||||
|
||||
const canUsePluginMethods = {
|
||||
async: [
|
||||
'setPropsConfig',
|
||||
'getPropsConfig',
|
||||
'setPropsValue',
|
||||
'getPropsValue',
|
||||
'fillConfig',
|
||||
'getDefaultPropsValue',
|
||||
] as const,
|
||||
sync: ['createId', 'setNewItemId'] as const,
|
||||
};
|
||||
|
||||
type AsyncMethodName = Writable<(typeof canUsePluginMethods)['async']>;
|
||||
type SyncMethodName = Writable<(typeof canUsePluginMethods)['sync']>;
|
||||
|
||||
class Props extends BaseService {
|
||||
private state = reactive<PropsState>({
|
||||
propsConfigMap: {},
|
||||
@ -40,14 +56,8 @@ class Props extends BaseService {
|
||||
|
||||
constructor() {
|
||||
super([
|
||||
{ name: 'setPropsConfig', isAsync: true },
|
||||
{ name: 'getPropsConfig', isAsync: true },
|
||||
{ name: 'setPropsValue', isAsync: true },
|
||||
{ name: 'getPropsValue', isAsync: true },
|
||||
{ name: 'createId', isAsync: false },
|
||||
{ name: 'setNewItemId', isAsync: true },
|
||||
{ name: 'fillConfig', isAsync: true },
|
||||
{ name: 'getDefaultPropsValue', isAsync: true },
|
||||
...canUsePluginMethods.async.map((methodName) => ({ name: methodName, isAsync: true })),
|
||||
...canUsePluginMethods.sync.map((methodName) => ({ name: methodName, isAsync: false })),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -62,11 +72,6 @@ class Props extends BaseService {
|
||||
return fillConfig(config, typeof labelWidth !== 'function' ? labelWidth : '80px');
|
||||
}
|
||||
|
||||
/**
|
||||
* 为指定类型组件设置组件属性表单配置
|
||||
* @param type 组件类型
|
||||
* @param config 组件属性表单配置
|
||||
*/
|
||||
public async setPropsConfig(type: string, config: FormConfig) {
|
||||
this.state.propsConfigMap[type] = await this.fillConfig(Array.isArray(config) ? config : [config]);
|
||||
}
|
||||
@ -115,16 +120,14 @@ class Props extends BaseService {
|
||||
return value;
|
||||
}
|
||||
|
||||
const [id, defaultPropsValue, data] = await Promise.all([
|
||||
this.createId(type),
|
||||
this.getDefaultPropsValue(type),
|
||||
this.setNewItemId(
|
||||
cloneDeep({
|
||||
type,
|
||||
...defaultValue,
|
||||
} as any),
|
||||
),
|
||||
]);
|
||||
const id = this.createId(type);
|
||||
const defaultPropsValue = this.getDefaultPropsValue(type);
|
||||
const data = this.setNewItemId(
|
||||
cloneDeep({
|
||||
type,
|
||||
...defaultValue,
|
||||
} as any),
|
||||
);
|
||||
|
||||
return {
|
||||
id,
|
||||
@ -133,7 +136,7 @@ class Props extends BaseService {
|
||||
};
|
||||
}
|
||||
|
||||
public async createId(type: string | number): Promise<string> {
|
||||
public createId(type: string | number): string {
|
||||
return `${type}_${guid()}`;
|
||||
}
|
||||
|
||||
@ -144,16 +147,16 @@ class Props extends BaseService {
|
||||
* @param {Boolean} force 是否强制设置新的ID
|
||||
*/
|
||||
/* eslint no-param-reassign: ["error", { "props": false }] */
|
||||
public async setNewItemId(config: MNode, force = true) {
|
||||
public setNewItemId(config: MNode, force = true) {
|
||||
if (force || editorService.getNodeById(config.id)) {
|
||||
const newId = await this.createId(config.type || 'component');
|
||||
const newId = this.createId(config.type || 'component');
|
||||
this.setRelateId(config.id, newId);
|
||||
config.id = newId;
|
||||
}
|
||||
|
||||
if (config.items && Array.isArray(config.items)) {
|
||||
for (const item of config.items) {
|
||||
await this.setNewItemId(item);
|
||||
this.setNewItemId(item);
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,7 +168,7 @@ class Props extends BaseService {
|
||||
* @param type 组件类型
|
||||
* @returns Object
|
||||
*/
|
||||
public async getDefaultPropsValue(type: string) {
|
||||
public getDefaultPropsValue(type: string) {
|
||||
return ['page', 'container'].includes(type)
|
||||
? {
|
||||
type,
|
||||
@ -227,6 +230,10 @@ class Props extends BaseService {
|
||||
this.removeAllPlugins();
|
||||
}
|
||||
|
||||
public usePlugin(options: AsyncHookPlugin<AsyncMethodName, Props> & SyncHookPlugin<SyncMethodName, Props>): void {
|
||||
super.usePlugin(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取setNewItemId前后映射关系
|
||||
* @param oldId 原组件ID
|
||||
|
@ -1,11 +1,19 @@
|
||||
import { reactive } from 'vue';
|
||||
import type { Writable } from 'type-fest';
|
||||
|
||||
import StageCore from '@tmagic/stage';
|
||||
|
||||
import { useStage } from '@editor/hooks/use-stage';
|
||||
import BaseService from '@editor/services//BaseService';
|
||||
import editorService from '@editor/services//editor';
|
||||
import type { StageOptions, StageOverlayState } from '@editor/type';
|
||||
import type { StageOptions, StageOverlayState, SyncHookPlugin } from '@editor/type';
|
||||
|
||||
const canUsePluginMethods = {
|
||||
async: [],
|
||||
sync: ['openOverlay', 'closeOverlay', 'updateOverlay', 'createStage'] as const,
|
||||
};
|
||||
|
||||
type SyncMethodName = Writable<(typeof canUsePluginMethods)['sync']>;
|
||||
|
||||
class StageOverlay extends BaseService {
|
||||
private state: StageOverlayState = reactive({
|
||||
@ -20,12 +28,7 @@ class StageOverlay extends BaseService {
|
||||
});
|
||||
|
||||
constructor() {
|
||||
super([
|
||||
{ name: 'openOverlay', isAsync: false },
|
||||
{ name: 'closeOverlay', isAsync: false },
|
||||
{ name: 'updateOverlay', isAsync: false },
|
||||
{ name: 'createStage', isAsync: false },
|
||||
]);
|
||||
super(canUsePluginMethods.sync.map((methodName) => ({ name: methodName, isAsync: false })));
|
||||
|
||||
this.get('wrapDiv').classList.add('tmagic-editor-sub-stage-wrap');
|
||||
}
|
||||
@ -111,6 +114,10 @@ class StageOverlay extends BaseService {
|
||||
});
|
||||
}
|
||||
|
||||
public usePlugin(options: SyncHookPlugin<SyncMethodName, StageOverlay>): void {
|
||||
super.usePlugin(options);
|
||||
}
|
||||
|
||||
private createContentEl() {
|
||||
const sourceEl = this.get('sourceEl');
|
||||
if (!sourceEl) return;
|
||||
|
@ -1,5 +1,7 @@
|
||||
import serialize from 'serialize-javascript';
|
||||
import type { Writable } from 'type-fest';
|
||||
|
||||
import type { SyncHookPlugin } from '@editor/type';
|
||||
import { getConfig } from '@editor/utils/config';
|
||||
|
||||
import BaseService from './BaseService';
|
||||
@ -17,6 +19,13 @@ export enum Protocol {
|
||||
BOOLEAN = 'boolean',
|
||||
}
|
||||
|
||||
const canUsePluginMethods = {
|
||||
async: [],
|
||||
sync: ['getStorage', 'getNamespace', 'clear', 'getItem', 'removeItem', 'setItem'] as const,
|
||||
};
|
||||
|
||||
type SyncMethodName = Writable<(typeof canUsePluginMethods)['sync']>;
|
||||
|
||||
/**
|
||||
* 数据存储服务
|
||||
*/
|
||||
@ -25,14 +34,7 @@ export class WebStorage extends BaseService {
|
||||
private namespace = 'tmagic';
|
||||
|
||||
constructor() {
|
||||
super([
|
||||
{ name: 'getStorage', isAsync: false },
|
||||
{ name: 'getNamespace', isAsync: false },
|
||||
{ name: 'clear', isAsync: false },
|
||||
{ name: 'getItem', isAsync: false },
|
||||
{ name: 'removeItem', isAsync: false },
|
||||
{ name: 'setItem', isAsync: false },
|
||||
]);
|
||||
super(canUsePluginMethods.sync.map((methodName) => ({ name: methodName, isAsync: false })));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -123,6 +125,10 @@ export class WebStorage extends BaseService {
|
||||
this.removeAllPlugins();
|
||||
}
|
||||
|
||||
public usePlugin(options: SyncHookPlugin<SyncMethodName, WebStorage>): void {
|
||||
super.usePlugin(options);
|
||||
}
|
||||
|
||||
private getValueAndProtocol(value: string | null) {
|
||||
let protocol = '';
|
||||
|
||||
|
@ -17,11 +17,12 @@
|
||||
*/
|
||||
|
||||
import { reactive } from 'vue';
|
||||
import type { Writable } from 'type-fest';
|
||||
|
||||
import { convertToNumber } from '@tmagic/utils';
|
||||
|
||||
import editorService from '@editor/services/editor';
|
||||
import type { StageRect, UiState } from '@editor/type';
|
||||
import type { AsyncHookPlugin, StageRect, UiState } from '@editor/type';
|
||||
|
||||
import BaseService from './BaseService';
|
||||
|
||||
@ -50,12 +51,16 @@ const state = reactive<UiState>({
|
||||
hideSlideBar: false,
|
||||
});
|
||||
|
||||
const canUsePluginMethods = {
|
||||
async: ['zoom', 'calcZoom'] as const,
|
||||
sync: [] as const,
|
||||
};
|
||||
|
||||
type AsyncMethodName = Writable<(typeof canUsePluginMethods)['async']>;
|
||||
|
||||
class Ui extends BaseService {
|
||||
constructor() {
|
||||
super([
|
||||
{ name: 'zoom', isAsync: true },
|
||||
{ name: 'calcZoom', isAsync: true },
|
||||
]);
|
||||
super(canUsePluginMethods.async.map((methodName) => ({ name: methodName, isAsync: true })));
|
||||
}
|
||||
|
||||
public set<K extends keyof UiState, T extends UiState[K]>(name: K, value: T) {
|
||||
@ -134,6 +139,10 @@ class Ui extends BaseService {
|
||||
this.removeAllPlugins();
|
||||
}
|
||||
|
||||
public usePlugin(options: AsyncHookPlugin<AsyncMethodName, Ui>): void {
|
||||
super.usePlugin(options);
|
||||
}
|
||||
|
||||
private async setStageRect(value: StageRect) {
|
||||
state.stageRect = {
|
||||
...state.stageRect,
|
||||
|
@ -17,6 +17,7 @@
|
||||
*/
|
||||
|
||||
import type { Component } from 'vue';
|
||||
import type { PascalCasedProperties } from 'type-fest';
|
||||
|
||||
import type { ColumnConfig, FilterFunction, FormConfig, FormItem } from '@tmagic/form';
|
||||
import type { CodeBlockContent, CodeBlockDSL, Id, MApp, MContainer, MNode, MPage, MPageFragment } from '@tmagic/schema';
|
||||
@ -670,3 +671,35 @@ export interface TreeNodeData {
|
||||
items?: TreeNodeData[];
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export type AsyncBeforeHook<Value extends Array<string>, C extends Record<Value[number], (...args: any) => any>> = {
|
||||
[K in Value[number]]?: (...args: Parameters<C[K]>) => Promise<Parameters<C[K]>>;
|
||||
};
|
||||
|
||||
export type AsyncAfterHook<Value extends Array<string>, C extends Record<Value[number], (...args: any) => any>> = {
|
||||
[K in Value[number]]?: (result: Awaited<ReturnType<C[K]>>, ...args: Parameters<C[K]>) => ReturnType<C[K]>;
|
||||
};
|
||||
|
||||
export type SyncBeforeHook<Value extends Array<string>, C extends Record<Value[number], (...args: any) => any>> = {
|
||||
[K in Value[number]]?: (...args: Parameters<C[K]>) => Parameters<C[K]>;
|
||||
};
|
||||
|
||||
export type SyncAfterHook<Value extends Array<string>, C extends Record<Value[number], (...args: any) => any>> = {
|
||||
[K in Value[number]]?: (result: ReturnType<C[K]>, ...args: Parameters<C[K]>) => ReturnType<C[K]>;
|
||||
};
|
||||
|
||||
export type AddPrefixToObject<T, P extends string> = {
|
||||
[K in keyof T as K extends string ? `${P}${K}` : never]: T[K];
|
||||
};
|
||||
|
||||
export type AsyncHookPlugin<
|
||||
T extends Array<string>,
|
||||
C extends Record<T[number], (...args: any) => any>,
|
||||
> = AddPrefixToObject<PascalCasedProperties<AsyncBeforeHook<T, C>>, 'before'> &
|
||||
AddPrefixToObject<PascalCasedProperties<AsyncAfterHook<T, C>>, 'after'>;
|
||||
|
||||
export type SyncHookPlugin<
|
||||
T extends Array<string>,
|
||||
C extends Record<T[number], (...args: any) => any>,
|
||||
> = AddPrefixToObject<PascalCasedProperties<SyncBeforeHook<T, C>>, 'before'> &
|
||||
AddPrefixToObject<PascalCasedProperties<SyncAfterHook<T, C>>, 'after'>;
|
||||
|
@ -15,55 +15,53 @@ import { generatePageNameByApp, getInitPositionStyle } from '@editor/utils/edito
|
||||
* @param config 待粘贴的元素配置(复制时保存的那份配置)
|
||||
* @returns
|
||||
*/
|
||||
export const beforePaste = async (position: PastePosition, config: MNode[]): Promise<MNode[]> => {
|
||||
export const beforePaste = (position: PastePosition, config: MNode[]): MNode[] => {
|
||||
if (!config[0]?.style) return config;
|
||||
const curNode = editorService.get('node');
|
||||
// 将数组中第一个元素的坐标作为参照点
|
||||
const { left: referenceLeft, top: referenceTop } = config[0].style;
|
||||
// 坐标校准后的粘贴数据
|
||||
const pasteConfigs: MNode[] = await Promise.all(
|
||||
config.map(async (configItem: MNode): Promise<MNode> => {
|
||||
// 解构 position 对象,从 position 删除 offsetX、offsetY字段
|
||||
const { offsetX = 0, offsetY = 0, ...positionClone } = position;
|
||||
let pastePosition = positionClone;
|
||||
const pasteConfigs: MNode[] = config.map((configItem: MNode): MNode => {
|
||||
// 解构 position 对象,从 position 删除 offsetX、offsetY字段
|
||||
const { offsetX = 0, offsetY = 0, ...positionClone } = position;
|
||||
let pastePosition = positionClone;
|
||||
|
||||
if (!isEmpty(pastePosition) && curNode?.items) {
|
||||
// 如果没有传入粘贴坐标则可能为键盘操作,不再转换
|
||||
// 如果粘贴时选中了容器,则将元素粘贴到容器内,坐标需要转换为相对于容器的坐标
|
||||
pastePosition = getPositionInContainer(pastePosition, curNode.id);
|
||||
if (!isEmpty(pastePosition) && curNode?.items) {
|
||||
// 如果没有传入粘贴坐标则可能为键盘操作,不再转换
|
||||
// 如果粘贴时选中了容器,则将元素粘贴到容器内,坐标需要转换为相对于容器的坐标
|
||||
pastePosition = getPositionInContainer(pastePosition, curNode.id);
|
||||
}
|
||||
|
||||
// 将所有待粘贴元素坐标相对于多选第一个元素坐标重新计算,以保证多选粘贴后元素间距不变
|
||||
if (pastePosition.left && configItem.style?.left) {
|
||||
pastePosition.left = configItem.style.left - referenceLeft + pastePosition.left;
|
||||
}
|
||||
if (pastePosition.top && configItem.style?.top) {
|
||||
pastePosition.top = configItem.style?.top - referenceTop + pastePosition.top;
|
||||
}
|
||||
const pasteConfig = propsService.setNewItemId(configItem, false);
|
||||
|
||||
if (pasteConfig.style) {
|
||||
const { left, top } = pasteConfig.style;
|
||||
// 判断能转换为数字时,做粘贴偏移量计算
|
||||
if (typeof left === 'number' || (!!left && !isNaN(Number(left)))) {
|
||||
pasteConfig.style.left = Number(left) + offsetX;
|
||||
}
|
||||
if (typeof top === 'number' || (!!top && !isNaN(Number(top)))) {
|
||||
pasteConfig.style.top = Number(top) + offsetY;
|
||||
}
|
||||
|
||||
// 将所有待粘贴元素坐标相对于多选第一个元素坐标重新计算,以保证多选粘贴后元素间距不变
|
||||
if (pastePosition.left && configItem.style?.left) {
|
||||
pastePosition.left = configItem.style.left - referenceLeft + pastePosition.left;
|
||||
}
|
||||
if (pastePosition.top && configItem.style?.top) {
|
||||
pastePosition.top = configItem.style?.top - referenceTop + pastePosition.top;
|
||||
}
|
||||
const pasteConfig = await propsService.setNewItemId(configItem, false);
|
||||
|
||||
if (pasteConfig.style) {
|
||||
const { left, top } = pasteConfig.style;
|
||||
// 判断能转换为数字时,做粘贴偏移量计算
|
||||
if (typeof left === 'number' || (!!left && !isNaN(Number(left)))) {
|
||||
pasteConfig.style.left = Number(left) + offsetX;
|
||||
}
|
||||
if (typeof top === 'number' || (!!top && !isNaN(Number(top)))) {
|
||||
pasteConfig.style.top = Number(top) + offsetY;
|
||||
}
|
||||
|
||||
pasteConfig.style = {
|
||||
...pasteConfig.style,
|
||||
...pastePosition,
|
||||
};
|
||||
}
|
||||
const root = editorService.get('root');
|
||||
if ((isPage(pasteConfig) || isPageFragment(pasteConfig)) && root) {
|
||||
pasteConfig.name = generatePageNameByApp(root, isPage(pasteConfig) ? NodeType.PAGE : NodeType.PAGE_FRAGMENT);
|
||||
}
|
||||
return pasteConfig as MNode;
|
||||
}),
|
||||
);
|
||||
pasteConfig.style = {
|
||||
...pasteConfig.style,
|
||||
...pastePosition,
|
||||
};
|
||||
}
|
||||
const root = editorService.get('root');
|
||||
if ((isPage(pasteConfig) || isPageFragment(pasteConfig)) && root) {
|
||||
pasteConfig.name = generatePageNameByApp(root, isPage(pasteConfig) ? NodeType.PAGE : NodeType.PAGE_FRAGMENT);
|
||||
}
|
||||
return pasteConfig as MNode;
|
||||
});
|
||||
return pasteConfigs;
|
||||
};
|
||||
|
||||
|
@ -328,7 +328,7 @@ export const displayTabConfig: TabPaneConfig = {
|
||||
* @param config 组件属性配置
|
||||
* @returns Object
|
||||
*/
|
||||
export const fillConfig = (config: FormConfig = [], labelWidth = '80px') => [
|
||||
export const fillConfig = (config: FormConfig = [], labelWidth = '80px'): FormConfig => [
|
||||
{
|
||||
type: 'tab',
|
||||
labelWidth,
|
||||
|
@ -11,17 +11,17 @@ test('createId', async () => {
|
||||
});
|
||||
|
||||
describe('setNewItemId', () => {
|
||||
test('普通', async () => {
|
||||
test('普通', () => {
|
||||
const config = {
|
||||
id: 1,
|
||||
type: 'text',
|
||||
};
|
||||
// 将组件与组件的子元素配置中的id都设置成一个新的ID
|
||||
await props.setNewItemId(config);
|
||||
props.setNewItemId(config);
|
||||
expect(config.id === 1).toBeFalsy();
|
||||
});
|
||||
|
||||
test('items', async () => {
|
||||
test('items', () => {
|
||||
const config = {
|
||||
id: 1,
|
||||
type: NodeType.PAGE,
|
||||
@ -32,7 +32,7 @@ describe('setNewItemId', () => {
|
||||
},
|
||||
],
|
||||
};
|
||||
await props.setNewItemId(config);
|
||||
props.setNewItemId(config);
|
||||
expect(config.id === 1).toBeFalsy();
|
||||
expect(config.items[0].id === 2).toBeFalsy();
|
||||
});
|
||||
|
@ -227,7 +227,7 @@ asyncLoadJs(`${VITE_ENTRY_PATH}/ds-value/index.umd.cjs`).then(() => {
|
||||
save();
|
||||
|
||||
editorService.usePlugin({
|
||||
beforeDoAdd: (config: MNode, parent?: MContainer | null) => {
|
||||
beforeDoAdd: async (config: MNode, parent: MContainer) => {
|
||||
if (config.type === 'overlay') {
|
||||
config.style = {
|
||||
...config.style,
|
||||
@ -235,7 +235,7 @@ editorService.usePlugin({
|
||||
top: 0,
|
||||
};
|
||||
|
||||
return [config, editorService.get('page')];
|
||||
return [config, editorService.get('page') as MContainer];
|
||||
}
|
||||
|
||||
return [config, parent];
|
||||
|
11
pnpm-lock.yaml
generated
11
pnpm-lock.yaml
generated
@ -377,6 +377,9 @@ importers:
|
||||
tsc-alias:
|
||||
specifier: ^1.8.5
|
||||
version: 1.8.5
|
||||
type-fest:
|
||||
specifier: ^4.10.3
|
||||
version: 4.11.1
|
||||
typescript:
|
||||
specifier: ^5.0.4
|
||||
version: 5.0.4
|
||||
@ -9552,7 +9555,7 @@ packages:
|
||||
dependencies:
|
||||
find-up: 6.3.0
|
||||
read-pkg: 8.1.0
|
||||
type-fest: 4.6.0
|
||||
type-fest: 4.11.1
|
||||
dev: true
|
||||
|
||||
/read-pkg-up@7.0.1:
|
||||
@ -9581,7 +9584,7 @@ packages:
|
||||
'@types/normalize-package-data': 2.4.1
|
||||
normalize-package-data: 6.0.0
|
||||
parse-json: 7.1.1
|
||||
type-fest: 4.6.0
|
||||
type-fest: 4.11.1
|
||||
dev: true
|
||||
|
||||
/readable-stream@3.6.2:
|
||||
@ -10491,8 +10494,8 @@ packages:
|
||||
engines: {node: '>=14.16'}
|
||||
dev: true
|
||||
|
||||
/type-fest@4.6.0:
|
||||
resolution: {integrity: sha512-rLjWJzQFOq4xw7MgJrCZ6T1jIOvvYElXT12r+y0CC6u67hegDHaxcPqb2fZHOGlqxugGQPNB1EnTezjBetkwkw==}
|
||||
/type-fest@4.11.1:
|
||||
resolution: {integrity: sha512-MFMf6VkEVZAETidGGSYW2B1MjXbGX+sWIywn2QPEaJ3j08V+MwVRHMXtf2noB8ENJaD0LIun9wh5Z6OPNf1QzQ==}
|
||||
engines: {node: '>=16'}
|
||||
dev: true
|
||||
|
||||
|
@ -21,7 +21,7 @@ export const useRuntime = ({
|
||||
fillConfig = (config) => config,
|
||||
}: {
|
||||
plugins?: Plugin[];
|
||||
fillConfig?: (config: FormConfig) => FormConfig;
|
||||
fillConfig?: (config: FormConfig, mForm: any) => FormConfig;
|
||||
} = {}) => {
|
||||
const render = (stage: StageCore) => {
|
||||
injectStyle(stage.renderer.getDocument()!, cssStyle);
|
||||
@ -60,24 +60,24 @@ export const useRuntime = ({
|
||||
};
|
||||
|
||||
propsService.usePlugin({
|
||||
afterFillConfig(config: FormConfig, itemConfig: FormConfig) {
|
||||
async afterFillConfig(config: FormConfig, itemConfig: FormConfig, labelWidth = '80px') {
|
||||
return [
|
||||
{
|
||||
type: 'tab',
|
||||
items: [
|
||||
{
|
||||
title: '属性',
|
||||
labelWidth: '80px',
|
||||
labelWidth,
|
||||
items: [...commonConfig, ...itemConfig],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
] as FormConfig;
|
||||
},
|
||||
});
|
||||
|
||||
editorService.usePlugin({
|
||||
afterGetLayout() {
|
||||
async afterGetLayout() {
|
||||
return Layout.RELATIVE;
|
||||
},
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user