From f324c86c240249230dd5bb1ef3796d851dfaab13 Mon Sep 17 00:00:00 2001 From: liujuping Date: Wed, 19 Apr 2023 15:27:34 +0800 Subject: [PATCH] feat(workspace): add enableAutoOpenFirstWindow config and onWindowRendererReady function --- packages/editor-core/src/config.ts | 5 +++ packages/engine/src/engine-core.ts | 13 ++++-- packages/shell/src/api/project.ts | 2 +- packages/shell/src/api/skeleton.ts | 7 +++ packages/shell/src/api/workspace.ts | 7 +++ packages/shell/src/model/window.ts | 4 ++ packages/types/src/shell/api/workspace.ts | 8 +++- .../types/src/shell/type/engine-options.ts | 9 ++++ .../workspace/src/context/view-context.ts | 16 +++++-- packages/workspace/src/layouts/workbench.tsx | 2 +- packages/workspace/src/window.ts | 7 +++ packages/workspace/src/workspace.ts | 44 +++++++++++++------ 12 files changed, 101 insertions(+), 23 deletions(-) diff --git a/packages/editor-core/src/config.ts b/packages/editor-core/src/config.ts index ef889e727..4f0f708e7 100644 --- a/packages/editor-core/src/config.ts +++ b/packages/editor-core/src/config.ts @@ -145,6 +145,11 @@ const VALID_ENGINE_OPTIONS = { type: 'function', description: '配置指定节点为根组件', }, + enableAutoOpenFirstWindow: { + type: 'boolean', + description: '应用级设计模式下,自动打开第一个窗口', + default: true, + }, }; const getStrictModeValue = (engineOptions: IPublicTypeEngineOptions, defaultValue: boolean): boolean => { diff --git a/packages/engine/src/engine-core.ts b/packages/engine/src/engine-core.ts index a94f192d4..f0c283ec1 100644 --- a/packages/engine/src/engine-core.ts +++ b/packages/engine/src/engine-core.ts @@ -9,12 +9,15 @@ import { engineConfig, Setters as InnerSetters, Hotkey as InnerHotkey, + IEditor, } from '@alilc/lowcode-editor-core'; import { IPublicTypeEngineOptions, IPublicModelDocumentModel, IPublicTypePluginMeta, IPublicTypeDisposable, + IPublicApiPlugins, + IPublicApiWorkspace, } from '@alilc/lowcode-types'; import { Designer, @@ -22,6 +25,7 @@ import { ILowCodePluginContextPrivate, ILowCodePluginContextApiAssembler, PluginPreference, + IDesigner, } from '@alilc/lowcode-designer'; import { Skeleton as InnerSkeleton, @@ -30,6 +34,7 @@ import { import { Workspace as InnerWorkspace, Workbench as WorkSpaceWorkbench, + IWorkspace, } from '@alilc/lowcode-workspace'; import { @@ -61,7 +66,7 @@ export * from './modules/skeleton-types'; export * from './modules/designer-types'; export * from './modules/lowcode-types'; -async function registryInnerPlugin(designer: Designer, editor: Editor, plugins: Plugins): Promise { +async function registryInnerPlugin(designer: IDesigner, editor: IEditor, plugins: IPublicApiPlugins): Promise { // 注册一批内置插件 const componentMetaParserPlugin = componentMetaParser(designer); const defaultPanelRegistryPlugin = defaultPanelRegistry(editor); @@ -83,8 +88,8 @@ async function registryInnerPlugin(designer: Designer, editor: Editor, plugins: }; } -const innerWorkspace = new InnerWorkspace(registryInnerPlugin, shellModelFactory); -const workspace = new Workspace(innerWorkspace); +const innerWorkspace: IWorkspace = new InnerWorkspace(registryInnerPlugin, shellModelFactory); +const workspace: IPublicApiWorkspace = new Workspace(innerWorkspace); const editor = new Editor(); globalContext.register(editor, Editor); globalContext.register(editor, 'editor'); @@ -207,7 +212,9 @@ export async function init( }), engineContainer, ); + innerWorkspace.enableAutoOpenFirstWindow = engineConfig.get('enableAutoOpenFirstWindow', true); innerWorkspace.setActive(true); + innerWorkspace.initWindow(); innerHotkey.activate(false); await innerWorkspace.plugins.init(pluginPreference); return; diff --git a/packages/shell/src/api/project.ts b/packages/shell/src/api/project.ts index e33a038b9..f005d0af0 100644 --- a/packages/shell/src/api/project.ts +++ b/packages/shell/src/api/project.ts @@ -32,7 +32,7 @@ export class Project implements IPublicApiProject { } const workspace = globalContext.get('workspace'); if (workspace.isActive) { - if (!workspace.window.innerProject) { + if (!workspace.window?.innerProject) { logger.error('project api 调用时机出现问题,请检查'); return this[innerProjectSymbol]; } diff --git a/packages/shell/src/api/skeleton.ts b/packages/shell/src/api/skeleton.ts index 928c55a0d..cb5c8f8aa 100644 --- a/packages/shell/src/api/skeleton.ts +++ b/packages/shell/src/api/skeleton.ts @@ -5,9 +5,12 @@ import { } from '@alilc/lowcode-editor-skeleton'; import { skeletonSymbol } from '../symbols'; import { IPublicApiSkeleton, IPublicTypeDisposable, IPublicTypeSkeletonConfig, IPublicTypeWidgetConfigArea } from '@alilc/lowcode-types'; +import { getLogger } from '@alilc/lowcode-utils'; const innerSkeletonSymbol = Symbol('skeleton'); +const logger = getLogger({ level: 'warn', bizName: 'shell-skeleton' }); + export class Skeleton implements IPublicApiSkeleton { private readonly [innerSkeletonSymbol]: ISkeleton; private readonly pluginName: string; @@ -18,6 +21,10 @@ export class Skeleton implements IPublicApiSkeleton { } const workspace = globalContext.get('workspace'); if (workspace.isActive) { + if (!workspace.window.innerSkeleton) { + logger.error('skeleton api 调用时机出现问题,请检查'); + return this[innerSkeletonSymbol]; + } return workspace.window.innerSkeleton; } diff --git a/packages/shell/src/api/workspace.ts b/packages/shell/src/api/workspace.ts index aa56d0d37..9676b8522 100644 --- a/packages/shell/src/api/workspace.ts +++ b/packages/shell/src/api/workspace.ts @@ -28,9 +28,16 @@ export class Workspace implements IPublicApiWorkspace { } get window() { + if (!this[workspaceSymbol].window) { + return null; + } return new ShellWindow(this[workspaceSymbol].window); } + onWindowRendererReady(fn: () => void): IPublicTypeDisposable { + return this[workspaceSymbol].onWindowRendererReady(fn); + } + registerResourceType(resourceTypeModel: IPublicTypeResourceType): void { this[workspaceSymbol].registerResourceType(resourceTypeModel); } diff --git a/packages/shell/src/model/window.ts b/packages/shell/src/model/window.ts index b1263d541..23bc5c06d 100644 --- a/packages/shell/src/model/window.ts +++ b/packages/shell/src/model/window.ts @@ -41,4 +41,8 @@ export class Window implements IPublicModelWindow { async save() { return await this[windowSymbol].save(); } + + get plugins() { + return this[windowSymbol].plugins; + } } diff --git a/packages/types/src/shell/api/workspace.ts b/packages/types/src/shell/api/workspace.ts index 4422dde4e..8904a8231 100644 --- a/packages/types/src/shell/api/workspace.ts +++ b/packages/types/src/shell/api/workspace.ts @@ -10,7 +10,7 @@ export interface IPublicApiWorkspace< isActive: boolean; /** 当前设计器窗口 */ - window: ModelWindow; + window: ModelWindow | null; plugins: Plugins; @@ -46,4 +46,10 @@ export interface IPublicApiWorkspace< /** active 窗口变更事件 */ onChangeActiveWindow(fn: () => void): IPublicTypeDisposable; + + /** + * window 下的所有视图 renderer ready 事件 + * @since v1.1.7 + */ + onWindowRendererReady(fn: () => void): IPublicTypeDisposable; } \ No newline at end of file diff --git a/packages/types/src/shell/type/engine-options.ts b/packages/types/src/shell/type/engine-options.ts index 195db8912..f17716653 100644 --- a/packages/types/src/shell/type/engine-options.ts +++ b/packages/types/src/shell/type/engine-options.ts @@ -2,6 +2,7 @@ import { RequestHandlersMap } from '@alilc/lowcode-datasource-types'; import { ComponentType } from 'react'; export interface IPublicTypeEngineOptions { + /** * 是否开启 condition 的能力,默认在设计器中不管 condition 是啥都正常展示 * when this is true, node that configured as conditional not renderring @@ -136,8 +137,10 @@ export interface IPublicTypeEngineOptions { * 与 react-renderer 的 appHelper 一致,https://lowcode-engine.cn/site/docs/guide/expand/runtime/renderer#apphelper */ appHelper?: { + /** 全局公共函数 */ utils?: Record; + /** 全局常量 */ constants?: Record; }; @@ -168,6 +171,12 @@ export interface IPublicTypeEngineOptions { * 开启应用级设计模式 */ enableWorkspaceMode?: boolean; + + /** + * @default true + * 应用级设计模式下,自动打开第一个窗口 + */ + enableAutoOpenFirstWindow?: boolean; } /** diff --git a/packages/workspace/src/context/view-context.ts b/packages/workspace/src/context/view-context.ts index 38a9e570f..393989850 100644 --- a/packages/workspace/src/context/view-context.ts +++ b/packages/workspace/src/context/view-context.ts @@ -17,10 +17,6 @@ export class Context extends BasicContext { @obx isInit: boolean = false; - @computed get active() { - return this._activate; - } - init = flow(function* (this: Context) { if (this.viewType === 'webview') { const url = yield this.instance?.url?.(); @@ -43,6 +39,18 @@ export class Context extends BasicContext { makeObservable(this); } + @computed get active() { + return this._activate; + } + + onSimulatorRendererReady = (): Promise => { + return new Promise((resolve) => { + this.project.onSimulatorRendererReady(() => { + resolve(); + }); + }); + }; + setActivate = (_activate: boolean) => { this._activate = _activate; this.innerHotkey.activate(this._activate); diff --git a/packages/workspace/src/layouts/workbench.tsx b/packages/workspace/src/layouts/workbench.tsx index fe5ef846f..0c69f9717 100644 --- a/packages/workspace/src/layouts/workbench.tsx +++ b/packages/workspace/src/layouts/workbench.tsx @@ -47,7 +47,7 @@ export class Workbench extends Component<{ { workspace.windows.map(d => ( diff --git a/packages/workspace/src/window.ts b/packages/workspace/src/window.ts index 1b9bec410..37a1622c0 100644 --- a/packages/workspace/src/window.ts +++ b/packages/workspace/src/window.ts @@ -82,6 +82,9 @@ export class EditorWindow implements IEditorWindow { async init() { await this.initViewTypes(); await this.execViewTypesInit(); + Promise.all(Array.from(this.editorViews.values()).map((d) => d.onSimulatorRendererReady)).then(() => { + this.workspace.emitWindowRendererReady(); + }); this.url = await this.resource.url(); this.setDefaultViewType(); this.initReady = true; @@ -182,6 +185,10 @@ export class EditorWindow implements IEditorWindow { return this.editorView?.designer; } + get plugins() { + return this.editorView?.plugins; + } + get innerPlugins() { return this.editorView?.innerPlugins; } diff --git a/packages/workspace/src/workspace.ts b/packages/workspace/src/workspace.ts index 30c8fcdad..ea19ea0c0 100644 --- a/packages/workspace/src/workspace.ts +++ b/packages/workspace/src/workspace.ts @@ -1,6 +1,6 @@ import { IDesigner, ILowCodePluginManager, LowCodePluginManager } from '@alilc/lowcode-designer'; -import { createModuleEventBus, Editor, IEventBus, makeObservable, obx } from '@alilc/lowcode-editor-core'; -import { IPublicApiPlugins, IPublicApiWorkspace, IPublicResourceList, IPublicTypeResourceType, IShellModelFactory } from '@alilc/lowcode-types'; +import { createModuleEventBus, Editor, IEditor, IEventBus, makeObservable, obx } from '@alilc/lowcode-editor-core'; +import { IPublicApiPlugins, IPublicApiWorkspace, IPublicResourceList, IPublicTypeDisposable, IPublicTypeResourceType, IShellModelFactory } from '@alilc/lowcode-types'; import { BasicContext } from './context/base-context'; import { EditorWindow } from './window'; import type { IEditorWindow } from './window'; @@ -11,6 +11,8 @@ enum EVENT { CHANGE_WINDOW = 'change_window', CHANGE_ACTIVE_WINDOW = 'change_active_window', + + WINDOW_RENDER_READY = 'window_render_ready', } const CHANGE_EVENT = 'resource.list.change'; @@ -19,10 +21,12 @@ export interface IWorkspace extends Omit, 'resourceList' | 'plugins'> { - readonly registryInnerPlugin: (designer: IDesigner, editor: Editor, plugins: IPublicApiPlugins) => Promise; + readonly registryInnerPlugin: (designer: IDesigner, editor: Editor, plugins: IPublicApiPlugins) => Promise; readonly shellModelFactory: IShellModelFactory; + enableAutoOpenFirstWindow: boolean; + window: IEditorWindow; plugins: ILowCodePluginManager; @@ -32,11 +36,19 @@ export interface IWorkspace extends Omit Promise, + readonly registryInnerPlugin: (designer: IDesigner, editor: IEditor, plugins: IPublicApiPlugins) => Promise, readonly shellModelFactory: any, ) { - this.init(); + this.context = new BasicContext(this, ''); makeObservable(this); } @@ -97,13 +109,8 @@ export class Workspace implements IWorkspace { } } - init() { - this.initWindow(); - this.context = new BasicContext(this, ''); - } - initWindow() { - if (!this.defaultResourceType) { + if (!this.defaultResourceType || this.enableAutoOpenFirstWindow === false) { return; } const resourceName = this.defaultResourceType.name; @@ -128,7 +135,7 @@ export class Workspace implements IWorkspace { const resourceType = new ResourceType(resourceTypeModel); this.resourceTypeMap.set(resourceTypeModel.resourceName, resourceType); - if (!this.window && this.defaultResourceType) { + if (!this.window && this.defaultResourceType && this._isActive) { this.initWindow(); } } @@ -149,6 +156,17 @@ export class Workspace implements IWorkspace { }; } + onWindowRendererReady(fn: () => void): IPublicTypeDisposable { + this.emitter.on(EVENT.WINDOW_RENDER_READY, fn); + return () => { + this.emitter.off(EVENT.WINDOW_RENDER_READY, fn); + }; + } + + emitWindowRendererReady() { + this.emitter.emit(EVENT.WINDOW_RENDER_READY); + } + getResourceType(resourceName: string): IResourceType { return this.resourceTypeMap.get(resourceName)!; } @@ -188,7 +206,7 @@ export class Workspace implements IWorkspace { } openEditorWindow(name: string, title: string, options: Object, viewType?: string, sleep?: boolean) { - if (!this.window?.initReady && !sleep) { + if (this.window && !this.window?.initReady && !sleep) { this.windowQueue.push({ name, title, options, viewType, });