diff --git a/packages/designer/src/builtin-simulator/create-simulator.ts b/packages/designer/src/builtin-simulator/create-simulator.ts index e46582180..15ab3507b 100644 --- a/packages/designer/src/builtin-simulator/create-simulator.ts +++ b/packages/designer/src/builtin-simulator/create-simulator.ts @@ -20,7 +20,9 @@ export function createSimulator( ): Promise { const win: any = iframe.contentWindow; const doc = iframe.contentDocument!; + const innerPlugins = host.designer.editor.get('innerPlugins'); + win.AliLowCodeEngine = innerPlugins._getLowCodePluginContext({}); win.LCSimulatorHost = host; win._ = window._; diff --git a/packages/designer/src/builtin-simulator/host-view.tsx b/packages/designer/src/builtin-simulator/host-view.tsx index a7943ea9f..21e007930 100644 --- a/packages/designer/src/builtin-simulator/host-view.tsx +++ b/packages/designer/src/builtin-simulator/host-view.tsx @@ -1,5 +1,5 @@ import React, { Component } from 'react'; -import { observer, globalContext } from '@alilc/lowcode-editor-core'; +import { observer } from '@alilc/lowcode-editor-core'; import { BuiltinSimulatorHost, BuiltinSimulatorProps } from './host'; import { BemTools } from './bem-tools'; import { Project } from '../project'; @@ -76,8 +76,7 @@ class Content extends Component<{ host: BuiltinSimulatorHost }> { private dispose?: () => void; componentDidMount() { - const workspace = globalContext.get('workspace'); - const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor'); + const editor = this.props.host.designer.editor; const onEnableEvents = (type: boolean) => { this.setState({ disabledEvents: type, diff --git a/packages/designer/src/plugin/plugin-types.ts b/packages/designer/src/plugin/plugin-types.ts index f76e20827..23aac2849 100644 --- a/packages/designer/src/plugin/plugin-types.ts +++ b/packages/designer/src/plugin/plugin-types.ts @@ -16,6 +16,7 @@ import { IPublicApiWorkspace, IPublicTypePluginMeta, IPublicTypePluginRegisterOptions, + IPublicModelWindow, } from '@alilc/lowcode-types'; import PluginContext from './plugin-context'; @@ -56,6 +57,7 @@ export interface ILowCodePluginContextPrivate { set pluginEvent(event: IPublicApiEvent); set canvas(canvas: IPublicApiCanvas); set workspace(workspace: IPublicApiWorkspace); + set editorWindow(window: IPublicModelWindow); } export interface ILowCodePluginContextApiAssembler { assembleApis( diff --git a/packages/designer/tests/builtin-simulator/host.test.ts b/packages/designer/tests/builtin-simulator/host.test.ts index 61ae61742..57e74be57 100644 --- a/packages/designer/tests/builtin-simulator/host.test.ts +++ b/packages/designer/tests/builtin-simulator/host.test.ts @@ -1,3 +1,4 @@ +import { IPublicTypePluginMeta } from './../../../../lib/packages/types/src/shell/type/plugin-meta.d'; import '../fixtures/window'; import { Editor, @@ -22,6 +23,7 @@ import { BuiltinSimulatorHost } from '../../src/builtin-simulator/host'; import { fireEvent } from '@testing-library/react'; import { shellModelFactory } from '../../../engine/src/modules/shell-model-factory'; import { Setters, Workspace } from '@alilc/lowcode-shell'; +import { ILowCodePluginContextApiAssembler, ILowCodePluginContextPrivate, LowCodePluginManager } from '@alilc/lowcode-designer'; describe('Host 测试', () => { let editor: Editor; @@ -32,10 +34,20 @@ describe('Host 测试', () => { beforeAll(() => { editor = new Editor(); - const innerWorkspace = new InnerWorkspace(); + const pluginContextApiAssembler: ILowCodePluginContextApiAssembler = { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + assembleApis: (context: ILowCodePluginContextPrivate, pluginName: string, meta: IPublicTypePluginMeta) => { + context.project = project; + const eventPrefix = meta?.eventPrefix || 'common'; + context.workspace = workspace; + }, + }; + const innerPlugins = new LowCodePluginManager(pluginContextApiAssembler); + const innerWorkspace = new InnerWorkspace(() => {}, {}); const workspace = new Workspace(innerWorkspace); editor.set('innerHotkey', new InnerHotkey()) editor.set('setters', new Setters(new InnerSetters())); + editor.set('innerPlugins' as any, innerPlugins); !globalContext.has(Editor) && globalContext.register(editor, Editor); !globalContext.has('workspace') && globalContext.register(innerWorkspace, 'workspace'); }); diff --git a/packages/editor-skeleton/src/components/settings/settings-pane.tsx b/packages/editor-skeleton/src/components/settings/settings-pane.tsx index 46b188acc..31ea78cce 100644 --- a/packages/editor-skeleton/src/components/settings/settings-pane.tsx +++ b/packages/editor-skeleton/src/components/settings/settings-pane.tsx @@ -1,5 +1,5 @@ import { Component, MouseEvent, Fragment } from 'react'; -import { shallowIntl, observer, obx, engineConfig, runInAction, globalContext } from '@alilc/lowcode-editor-core'; +import { shallowIntl, observer, obx, engineConfig, runInAction } from '@alilc/lowcode-editor-core'; import { createContent, isJSSlot, isSetterConfig } from '@alilc/lowcode-utils'; import { Skeleton, Stage } from '@alilc/lowcode-editor-skeleton'; import { IPublicTypeCustomView } from '@alilc/lowcode-types'; @@ -40,7 +40,7 @@ class SettingFieldView extends Component { @@ -291,9 +291,8 @@ class SettingGroupView extends Component { const { field } = this.props; const { extraProps } = field; const { display } = extraProps; - const workspace = globalContext.get('workspace'); - const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor'); - const { stages } = editor.get('skeleton') as Skeleton; + const editor = this.props.field.designer?.editor; + const { stages } = editor?.get('skeleton') as Skeleton; // const items = field.items; let stageName; @@ -343,15 +342,15 @@ class SettingGroupView extends Component { } } -export function createSettingFieldView(item: ISettingField | IPublicTypeCustomView, field: ISettingEntry, index?: number) { - if (isSettingField(item)) { - if (item.isGroup) { - return ; +export function createSettingFieldView(field: ISettingField | IPublicTypeCustomView, fieldEntry: ISettingEntry, index?: number) { + if (isSettingField(field)) { + if (field.isGroup) { + return ; } else { - return ; + return ; } } else { - return createContent(item, { key: index, field }); + return createContent(field, { key: index, field: fieldEntry }); } } diff --git a/packages/engine/src/engine-core.ts b/packages/engine/src/engine-core.ts index b038df818..7148bb38a 100644 --- a/packages/engine/src/engine-core.ts +++ b/packages/engine/src/engine-core.ts @@ -14,6 +14,7 @@ import { IPublicTypeEngineOptions, IPublicModelDocumentModel, IPublicTypePluginMeta, + IPublicTypeDisposable, } from '@alilc/lowcode-types'; import { Designer, @@ -60,14 +61,26 @@ export * from './modules/skeleton-types'; export * from './modules/designer-types'; export * from './modules/lowcode-types'; -async function registryInnerPlugin(designer: Designer, editor: Editor, plugins: Plugins) { +async function registryInnerPlugin(designer: Designer, editor: Editor, plugins: Plugins): Promise { // 注册一批内置插件 + const componentMetaParserPlugin = componentMetaParser(designer); + const defaultPanelRegistryPlugin = defaultPanelRegistry(editor); await plugins.register(OutlinePlugin, {}, { autoInit: true }); - await plugins.register(componentMetaParser(designer)); + await plugins.register(componentMetaParserPlugin); await plugins.register(setterRegistry, {}); - await plugins.register(defaultPanelRegistry(editor)); + await plugins.register(defaultPanelRegistryPlugin); await plugins.register(builtinHotkey); await plugins.register(registerDefaults, {}, { autoInit: true }); + + return () => { + plugins.delete(OutlinePlugin.pluginName); + plugins.delete(componentMetaParserPlugin.pluginName); + plugins.delete(setterRegistry.pluginName); + plugins.delete(defaultPanelRegistryPlugin.pluginName); + plugins.delete(defaultPanelRegistryPlugin.pluginName); + plugins.delete(builtinHotkey.pluginName); + plugins.delete(registerDefaults.pluginName); + }; } const innerWorkspace = new InnerWorkspace(registryInnerPlugin, shellModelFactory); @@ -158,7 +171,7 @@ let engineContainer: HTMLElement; export const version = VERSION_PLACEHOLDER; engineConfig.set('ENGINE_VERSION', version); -registryInnerPlugin(designer, editor, plugins); +const pluginPromise = registryInnerPlugin(designer, editor, plugins); export async function init( container?: HTMLElement, @@ -183,10 +196,10 @@ export async function init( } engineConfig.setEngineOptions(engineOptions as any); - await plugins.init(pluginPreference as any); - const { Workbench } = common.skeletonCabin; if (options && options.enableWorkspaceMode) { + const disposeFun = await pluginPromise; + disposeFun && disposeFun(); render( createElement(WorkSpaceWorkbench, { workspace: innerWorkspace, @@ -202,6 +215,8 @@ export async function init( return; } + await plugins.init(pluginPreference as any); + render( createElement(Workbench, { skeleton: innerSkeleton, diff --git a/packages/renderer-core/src/hoc/leaf.tsx b/packages/renderer-core/src/hoc/leaf.tsx index 5df988048..98b267475 100644 --- a/packages/renderer-core/src/hoc/leaf.tsx +++ b/packages/renderer-core/src/hoc/leaf.tsx @@ -102,6 +102,9 @@ function initRerenderEvent({ return; } cache.event.get(schema.id)?.dispose.forEach((disposeFn: any) => disposeFn && disposeFn()); + const debounceRerender = debounce(() => { + container.rerender(); + }, 20); cache.event.set(schema.id, { clear: false, leaf, @@ -111,21 +114,21 @@ function initRerenderEvent({ return; } __debug(`${schema.componentName}[${schema.id}] leaf not render in SimulatorRendererView, leaf onPropsChange make rerender`); - container.rerender(); + debounceRerender(); }), leaf?.onChildrenChange?.(() => { if (!container.autoRepaintNode) { return; } __debug(`${schema.componentName}[${schema.id}] leaf not render in SimulatorRendererView, leaf onChildrenChange make rerender`); - container.rerender(); + debounceRerender(); }) as Function, leaf?.onVisibleChange?.(() => { if (!container.autoRepaintNode) { return; } __debug(`${schema.componentName}[${schema.id}] leaf not render in SimulatorRendererView, leaf onVisibleChange make rerender`); - container.rerender(); + debounceRerender(); }), ], }); diff --git a/packages/shell/src/api/common.tsx b/packages/shell/src/api/common.tsx index 6a44dc82e..d9009ff8e 100644 --- a/packages/shell/src/api/common.tsx +++ b/packages/shell/src/api/common.tsx @@ -1,4 +1,4 @@ -import { editorSymbol, skeletonSymbol, designerCabinSymbol, designerSymbol } from '../symbols'; +import { editorSymbol, skeletonSymbol, designerCabinSymbol, designerSymbol, settingFieldSymbol } from '../symbols'; import { isFormEvent as innerIsFormEvent, compatibleLegaoSchema as innerCompatibleLegaoSchema, @@ -25,6 +25,7 @@ import { IPublicTypeLocationDetailType as InnerLocationDetailType, IPublicApiCommonEditorCabin, IPublicModelDragon, + IPublicModelSettingField, } from '@alilc/lowcode-types'; import { SettingField as InnerSettingField, @@ -168,8 +169,8 @@ class SkeletonCabin implements IPublicApiCommonSkeletonCabin { /** * @deprecated */ - createSettingFieldView(item: any, field: any) { - return innerCreateSettingFieldView(item, field); + createSettingFieldView(field: IPublicModelSettingField, fieldEntry: any) { + return innerCreateSettingFieldView((field as any)[settingFieldSymbol] || field, fieldEntry); } /** diff --git a/packages/shell/src/api/project.ts b/packages/shell/src/api/project.ts index ed45701b3..e33a038b9 100644 --- a/packages/shell/src/api/project.ts +++ b/packages/shell/src/api/project.ts @@ -18,6 +18,9 @@ import { import { DocumentModel as ShellDocumentModel } from '../model'; import { SimulatorHost } from './simulator-host'; import { editorSymbol, projectSymbol, simulatorHostSymbol, documentSymbol } from '../symbols'; +import { getLogger } from '@alilc/lowcode-utils'; + +const logger = getLogger({ level: 'warn', bizName: 'shell-project' }); const innerProjectSymbol = Symbol('innerProject'); export class Project implements IPublicApiProject { @@ -29,6 +32,10 @@ export class Project implements IPublicApiProject { } const workspace = globalContext.get('workspace'); if (workspace.isActive) { + if (!workspace.window.innerProject) { + logger.error('project api 调用时机出现问题,请检查'); + return this[innerProjectSymbol]; + } return workspace.window.innerProject; } @@ -43,8 +50,8 @@ export class Project implements IPublicApiProject { this[innerProjectSymbol] = project; } - static create(project: InnerProject) { - return new Project(project); + static create(project: InnerProject, workspaceMode: boolean = false) { + return new Project(project, workspaceMode); } /** @@ -225,15 +232,15 @@ export class Project implements IPublicApiProject { */ setConfig(key: T, value: IPublicTypeAppConfig[T]): void; setConfig(value: IPublicTypeAppConfig): void; - setConfig(...params: any[]): void{ - if(params.length === 2) { + setConfig(...params: any[]): void { + if (params.length === 2) { const oldConfig = this[projectSymbol].get('config'); this[projectSymbol].set('config', { ...oldConfig, [params[0]]: params[1], - }) + }); } else { - this[projectSymbol].set('config', params[0]) + this[projectSymbol].set('config', params[0]); } } } diff --git a/packages/shell/src/api/setters.ts b/packages/shell/src/api/setters.ts index 553f32c4e..72d29c8a8 100644 --- a/packages/shell/src/api/setters.ts +++ b/packages/shell/src/api/setters.ts @@ -1,10 +1,13 @@ import { IPublicTypeCustomView, IPublicApiSetters, IPublicTypeRegisteredSetter } from '@alilc/lowcode-types'; import { Setters as InnerSetters, globalContext } from '@alilc/lowcode-editor-core'; import { ReactNode } from 'react'; +import { getLogger } from '@alilc/lowcode-utils'; const innerSettersSymbol = Symbol('setters'); const settersSymbol = Symbol('setters'); +const logger = getLogger({ level: 'warn', bizName: 'shell-setters' }); + export class Setters implements IPublicApiSetters { readonly [innerSettersSymbol]: InnerSetters; @@ -15,6 +18,10 @@ export class Setters implements IPublicApiSetters { const workspace = globalContext.get('workspace'); if (workspace.isActive) { + if (!workspace.window.innerSetters) { + logger.error('setter api 调用时机出现问题,请检查'); + return this[innerSettersSymbol]; + } return workspace.window.innerSetters; } diff --git a/packages/shell/src/api/workspace.ts b/packages/shell/src/api/workspace.ts index 21015431f..aa56d0d37 100644 --- a/packages/shell/src/api/workspace.ts +++ b/packages/shell/src/api/workspace.ts @@ -35,8 +35,8 @@ export class Workspace implements IPublicApiWorkspace { this[workspaceSymbol].registerResourceType(resourceTypeModel); } - openEditorWindow(resourceName: string, title: string, extra: object, viewName?: string) { - this[workspaceSymbol].openEditorWindow(resourceName, title, extra, viewName); + openEditorWindow(resourceName: string, title: string, extra: object, viewName?: string, sleep?: boolean): void { + this[workspaceSymbol].openEditorWindow(resourceName, title, extra, viewName, sleep); } openEditorWindowById(id: string) { diff --git a/packages/shell/src/index.ts b/packages/shell/src/index.ts index 7017ca289..b3fca90b0 100644 --- a/packages/shell/src/index.ts +++ b/packages/shell/src/index.ts @@ -10,6 +10,7 @@ import { SettingTopEntry, Clipboard, SettingField, + Window, } from './model'; import { Project, @@ -50,6 +51,7 @@ export { Selection, Setters, Hotkey, + Window, Skeleton, SettingField as SettingPropEntry, SettingTopEntry, diff --git a/packages/shell/src/model/document-model.ts b/packages/shell/src/model/document-model.ts index 68252e3a5..06f3cebcd 100644 --- a/packages/shell/src/model/document-model.ts +++ b/packages/shell/src/model/document-model.ts @@ -90,7 +90,7 @@ export class DocumentModel implements IPublicModelDocumentModel { * @returns */ get project(): IPublicApiProject { - return ShellProject.create(this[documentSymbol].project); + return ShellProject.create(this[documentSymbol].project, true); } /** diff --git a/packages/types/src/shell/api/workspace.ts b/packages/types/src/shell/api/workspace.ts index 8c19846f7..4422dde4e 100644 --- a/packages/types/src/shell/api/workspace.ts +++ b/packages/types/src/shell/api/workspace.ts @@ -30,7 +30,7 @@ export interface IPublicApiWorkspace< registerResourceType(resourceTypeModel: IPublicTypeResourceType): void; /** 打开视图窗口 */ - openEditorWindow(resourceName: string, title: string, extra: Object, viewName?: string): void; + openEditorWindow(resourceName: string, title: string, extra: Object, viewName?: string, sleep?: boolean): void; /** 通过视图 id 打开窗口 */ openEditorWindowById(id: string): void; diff --git a/packages/workspace/src/context/base-context.ts b/packages/workspace/src/context/base-context.ts index e74ae2ffb..57a38712b 100644 --- a/packages/workspace/src/context/base-context.ts +++ b/packages/workspace/src/context/base-context.ts @@ -30,6 +30,7 @@ import { Common, Logger, Workspace, + Window, Canvas, } from '@alilc/lowcode-shell'; import { @@ -164,6 +165,9 @@ export class BasicContext implements IBasicContext { context.plugins = plugins; context.logger = new Logger({ level: 'warn', bizName: `plugin:${pluginName}` }); context.canvas = canvas; + if (editorWindow) { + context.editorWindow = new Window(editorWindow); + } }, }; diff --git a/packages/workspace/src/layouts/workbench.less b/packages/workspace/src/layouts/workbench.less index 95574871a..c8c89d6f0 100644 --- a/packages/workspace/src/layouts/workbench.less +++ b/packages/workspace/src/layouts/workbench.less @@ -368,6 +368,9 @@ body { right: 0; bottom: 0; left: 0; + flex-direction: column; + display: flex; + align-content: stretch; } .engine-actionitem { diff --git a/packages/workspace/src/resource.ts b/packages/workspace/src/resource.ts index 10cb7f0a1..222cb1162 100644 --- a/packages/workspace/src/resource.ts +++ b/packages/workspace/src/resource.ts @@ -39,7 +39,7 @@ export class Resource implements IResource { } get viewName() { - return this.resourceData.viewName || (this.resourceData as any).viewType; + return this.resourceData.viewName || (this.resourceData as any).viewType || this.defaultViewType; } get description() { diff --git a/packages/workspace/src/window.ts b/packages/workspace/src/window.ts index 96707dcb8..1b9bec410 100644 --- a/packages/workspace/src/window.ts +++ b/packages/workspace/src/window.ts @@ -10,6 +10,7 @@ interface IWindowCOnfig { title: string | undefined; options?: Object; viewType?: string | undefined; + sleep?: boolean; } export interface IEditorWindow extends Omit, 'changeViewType'> { @@ -18,6 +19,12 @@ export interface IEditorWindow extends Omit, 'chan editorViews: Map; changeViewType: (name: string, ignoreEmit?: boolean) => void; + + initReady: boolean; + + sleep?: boolean; + + init(): void; } export class EditorWindow implements IEditorWindow { @@ -36,11 +43,16 @@ export class EditorWindow implements IEditorWindow { @obx initReady = false; + sleep: boolean | undefined; + constructor(readonly resource: IResource, readonly workspace: IWorkspace, private config: IWindowCOnfig) { makeObservable(this); - this.init(); this.title = config.title; this.icon = resource.icon; + this.sleep = config.sleep; + if (!config.sleep) { + this.init(); + } } async importSchema(schema: any) { @@ -73,6 +85,8 @@ export class EditorWindow implements IEditorWindow { this.url = await this.resource.url(); this.setDefaultViewType(); this.initReady = true; + this.workspace.checkWindowQueue(); + this.sleep = false; } initViewTypes = async () => { diff --git a/packages/workspace/src/workspace.ts b/packages/workspace/src/workspace.ts index d959c55eb..30c8fcdad 100644 --- a/packages/workspace/src/workspace.ts +++ b/packages/workspace/src/workspace.ts @@ -30,6 +30,8 @@ export interface IWorkspace extends Omit Promise, readonly shellModelFactory: any, @@ -77,6 +86,17 @@ export class Workspace implements IWorkspace { makeObservable(this); } + checkWindowQueue() { + if (!this.windowQueue || !this.windowQueue.length) { + return; + } + + const windowInfo = this.windowQueue.shift(); + if (windowInfo) { + this.openEditorWindow(windowInfo.name, windowInfo.title, windowInfo.options, windowInfo.viewType); + } + } + init() { this.initWindow(); this.context = new BasicContext(this, ''); @@ -86,13 +106,13 @@ export class Workspace implements IWorkspace { if (!this.defaultResourceType) { return; } - const title = this.defaultResourceType.name; + const resourceName = this.defaultResourceType.name; const resource = new Resource({ - resourceName: title, + resourceName, options: {}, }, this.defaultResourceType, this); this.window = new EditorWindow(resource, this, { - title, + title: resource.title, }); this.editorWindowMap.set(this.window.id, this.window); this.windows.push(this.window); @@ -167,7 +187,13 @@ export class Workspace implements IWorkspace { } } - openEditorWindow(name: string, title: string, options: Object, viewType?: string) { + openEditorWindow(name: string, title: string, options: Object, viewType?: string, sleep?: boolean) { + if (!this.window?.initReady && !sleep) { + this.windowQueue.push({ + name, title, options, viewType, + }); + return; + } const resourceType = this.resourceTypeMap.get(name); if (!resourceType) { console.error(`${name} resourceType is not available`); @@ -176,6 +202,11 @@ export class Workspace implements IWorkspace { const filterWindows = this.windows.filter(d => (d.resource?.name === name && d.resource.title == title)); if (filterWindows && filterWindows.length) { this.window = filterWindows[0]; + if (!sleep && this.window.sleep) { + this.window.init(); + } else { + this.checkWindowQueue(); + } this.emitChangeActiveWindow(); return; } @@ -184,13 +215,17 @@ export class Workspace implements IWorkspace { title, options, }, resourceType, this); - this.window = new EditorWindow(resource, this, { + const window = new EditorWindow(resource, this, { title, options, viewType, + sleep, }); - this.windows = [...this.windows, this.window]; - this.editorWindowMap.set(this.window.id, this.window); + this.windows = [...this.windows, window]; + this.editorWindowMap.set(window.id, window); + if (!sleep) { + this.window = window; + } this.emitChangeWindow(); this.emitChangeActiveWindow(); }