diff --git a/packages/shell/src/model/resource.ts b/packages/shell/src/model/resource.ts index 3fa625ed6..1f037c606 100644 --- a/packages/shell/src/model/resource.ts +++ b/packages/shell/src/model/resource.ts @@ -32,4 +32,12 @@ export class Resource implements IPublicModelResource { get category() { return this[resourceSymbol].category; } + + get children() { + return this[resourceSymbol].children.map((child) => new Resource(child)); + } + + get viewType() { + return this[resourceSymbol].viewType; + } } \ No newline at end of file diff --git a/packages/types/src/shell/api/workspace.ts b/packages/types/src/shell/api/workspace.ts index ee05bbe56..8f8b16e39 100644 --- a/packages/types/src/shell/api/workspace.ts +++ b/packages/types/src/shell/api/workspace.ts @@ -21,7 +21,7 @@ export interface IPublicApiWorkspace { setResourceList(resourceList: IPublicResourceList): void; /** 资源树列表更新事件 */ - onResourceListChange(fn: (resourceList: IPublicResourceList) => void): () => IPublicTypeDisposable; + onResourceListChange(fn: (resourceList: IPublicResourceList) => void): IPublicTypeDisposable; /** 注册资源 */ registerResourceType(resourceTypeModel: IPublicTypeResourceType): void; diff --git a/packages/types/src/shell/type/editor-view-config.ts b/packages/types/src/shell/type/editor-view-config.ts index 2b36a718a..9bb3b7555 100644 --- a/packages/types/src/shell/type/editor-view-config.ts +++ b/packages/types/src/shell/type/editor-view-config.ts @@ -5,4 +5,7 @@ export interface IPublicEditorViewConfig { /** 资源保存时,会调用视图的钩子 */ save?: () => Promise; + + /** viewType 类型为 'webview' 时渲染的地址 */ + url?: () => Promise; } \ No newline at end of file diff --git a/packages/types/src/shell/type/resource-list.ts b/packages/types/src/shell/type/resource-list.ts index 9b8cc3272..bd5e4a3b0 100644 --- a/packages/types/src/shell/type/resource-list.ts +++ b/packages/types/src/shell/type/resource-list.ts @@ -1,10 +1,15 @@ +import { ReactElement } from 'react'; + export interface IPublicResourceData { resourceName: string; title: string; category?: string; + viewType?: string; + icon?: ReactElement; options: { [key: string]: any; }; + children?: IPublicResourceData[]; } export type IPublicResourceList = IPublicResourceData[]; \ No newline at end of file diff --git a/packages/types/src/shell/type/resource-type-config.ts b/packages/types/src/shell/type/resource-type-config.ts index 93534fea2..e47afb53d 100644 --- a/packages/types/src/shell/type/resource-type-config.ts +++ b/packages/types/src/shell/type/resource-type-config.ts @@ -28,4 +28,7 @@ export interface IPublicResourceTypeConfig { /** 默认标题 */ defaultTitle?: string; + + /** resourceType 类型为 'webview' 时渲染的地址 */ + url?: () => Promise; } diff --git a/packages/types/src/shell/type/resource-type.ts b/packages/types/src/shell/type/resource-type.ts index 64ec85c79..7cfb125aa 100644 --- a/packages/types/src/shell/type/resource-type.ts +++ b/packages/types/src/shell/type/resource-type.ts @@ -4,7 +4,7 @@ import { IPublicResourceTypeConfig } from './resource-type-config'; export interface IPublicTypeResourceType { resourceName: string; - resourceType: string; + resourceType: 'editor' | 'webview'; - (ctx: IPublicModelPluginContext): IPublicResourceTypeConfig; + (ctx: IPublicModelPluginContext, options: Object): IPublicResourceTypeConfig; } \ No newline at end of file diff --git a/packages/workspace/src/context/base-context.ts b/packages/workspace/src/context/base-context.ts index b90a131a3..37ce7fafa 100644 --- a/packages/workspace/src/context/base-context.ts +++ b/packages/workspace/src/context/base-context.ts @@ -28,18 +28,22 @@ import { Canvas, } from '@alilc/lowcode-shell'; import { + IPluginPreferenceMananger, + IPublicApiEvent, + IPublicModelPluginContext, IPublicTypePluginMeta, } from '@alilc/lowcode-types'; import { getLogger } from '@alilc/lowcode-utils'; import { Workspace as InnerWorkspace } from '../workspace'; import { EditorWindow } from '../window'; -export class BasicContext { +export class BasicContext implements IPublicModelPluginContext { skeleton: Skeleton; plugins: Plugins; project: Project; setters: Setters; material: Material; + common: Common; config; event; logger; @@ -53,6 +57,8 @@ export class BasicContext { innerHotkey: InnerHotkey; innerPlugins: LowCodePluginManager; canvas: Canvas; + pluginEvent: IPublicApiEvent; + preference: IPluginPreferenceMananger; constructor(innerWorkspace: InnerWorkspace, viewName: string, public editorWindow?: EditorWindow) { const editor = new Editor(viewName, true); @@ -101,6 +107,7 @@ export class BasicContext { this.designer = designer; this.canvas = canvas; const common = new Common(editor, innerSkeleton); + this.common = common; let plugins: any; const pluginContextApiAssembler: ILowCodePluginContextApiAssembler = { diff --git a/packages/workspace/src/context/view-context.ts b/packages/workspace/src/context/view-context.ts index 55bbf2d57..d14549667 100644 --- a/packages/workspace/src/context/view-context.ts +++ b/packages/workspace/src/context/view-context.ts @@ -1,4 +1,4 @@ -import { makeObservable, obx } from '@alilc/lowcode-editor-core'; +import { computed, makeObservable, obx } from '@alilc/lowcode-editor-core'; import { IPublicEditorViewConfig, IPublicTypeEditorView } from '@alilc/lowcode-types'; import { flow } from 'mobx'; import { Workspace as InnerWorkspace } from '../workspace'; @@ -17,7 +17,7 @@ export class Context extends BasicContext { @obx isInit: boolean = false; - get active() { + @computed get active() { return this._activate; } @@ -33,7 +33,7 @@ export class Context extends BasicContext { this.isInit = true; }); - constructor(public workspace: InnerWorkspace, public editorWindow: EditorWindow, public editorView: IPublicTypeEditorView, options: Object) { + constructor(public workspace: InnerWorkspace, public editorWindow: EditorWindow, public editorView: IPublicTypeEditorView, options: Object | undefined) { super(workspace, editorView.viewName, editorWindow); this.viewType = editorView.viewType || 'editor'; this.viewName = editorView.viewName; diff --git a/packages/workspace/src/inner-plugins/webview.tsx b/packages/workspace/src/inner-plugins/webview.tsx index be9c15f8b..de40bb521 100644 --- a/packages/workspace/src/inner-plugins/webview.tsx +++ b/packages/workspace/src/inner-plugins/webview.tsx @@ -1,6 +1,6 @@ import { IPublicModelPluginContext } from '@alilc/lowcode-types'; -function DesignerView(props: { +export function DesignerView(props: { url: string; viewName: string; }) { diff --git a/packages/workspace/src/resource.ts b/packages/workspace/src/resource.ts index effa6abee..119bcc488 100644 --- a/packages/workspace/src/resource.ts +++ b/packages/workspace/src/resource.ts @@ -17,12 +17,16 @@ export class Resource implements IPublicModelResource { return this.resourceType.name; } + get viewType() { + return this.resourceData.viewType; + } + get description() { return this.resourceTypeInstance?.description; } get icon() { - return this.resourceTypeInstance?.icon; + return this.resourceData.icon || this.resourceTypeInstance?.icon; } get type() { @@ -45,9 +49,13 @@ export class Resource implements IPublicModelResource { return this.context.innerSkeleton; } - constructor(readonly resourceData: IPublicResourceData, readonly resourceType: ResourceType, workspace: InnerWorkSpace) { + get children(): Resource[] { + return this.resourceData?.children?.map(d => new Resource(d, this.resourceType, this.workspace)) || []; + } + + constructor(readonly resourceData: IPublicResourceData, readonly resourceType: ResourceType, readonly workspace: InnerWorkSpace) { this.context = new BasicContext(workspace, `resource-${resourceData.resourceName || resourceType.name}`); - this.resourceTypeInstance = resourceType.resourceTypeModel(this.context, {}); + this.resourceTypeInstance = resourceType.resourceTypeModel(this.context, this.options); this.init(); if (this.resourceTypeInstance.editorViews) { this.resourceTypeInstance.editorViews.forEach((d: any) => { @@ -68,6 +76,10 @@ export class Resource implements IPublicModelResource { return await this.resourceTypeInstance.import?.(schema); } + async url() { + return await this.resourceTypeInstance.url?.(); + } + async save(value: any) { return await this.resourceTypeInstance.save?.(value); } diff --git a/packages/workspace/src/view/window-view.tsx b/packages/workspace/src/view/window-view.tsx index 396582a02..c468b084d 100644 --- a/packages/workspace/src/view/window-view.tsx +++ b/packages/workspace/src/view/window-view.tsx @@ -3,6 +3,7 @@ import { ResourceView } from './resource-view'; import { engineConfig, observer } from '@alilc/lowcode-editor-core'; import { EditorWindow } from '../window'; import { BuiltinLoading } from '@alilc/lowcode-designer'; +import { DesignerView } from '../inner-plugins/webview'; @observer export class WindowView extends PureComponent<{ @@ -11,16 +12,21 @@ export class WindowView extends PureComponent<{ }, any> { render() { const { active } = this.props; - const { editorView, resource } = this.props.window; - if (!editorView) { + const { resource, initReady, url } = this.props.window; + + if (!initReady) { const Loading = engineConfig.get('loadingComponent', BuiltinLoading); return ( -
+
); } + if (resource.type === 'webview' && url) { + return ; + } + return (
= new Map(); - constructor(readonly resource: Resource, readonly workspace: Workspace, public title: string | undefined = '', private options: Object = {}) { + @obx initReady = false; + + constructor(readonly resource: Resource, readonly workspace: Workspace, private config: IWindowCOnfig) { makeObservable(this); this.init(); + this.title = config.title; this.icon = resource.icon; } @@ -48,11 +61,16 @@ export class EditorWindow { async init() { await this.initViewTypes(); await this.execViewTypesInit(); + this.url = await this.resource.url(); this.setDefaultViewType(); + this.initReady = true; } initViewTypes = async () => { const editorViews = this.resource.editorViews; + if (!editorViews) { + return; + } for (let i = 0; i < editorViews.length; i++) { const name = editorViews[i].viewName; await this.initViewType(name); @@ -72,6 +90,9 @@ export class EditorWindow { execViewTypesInit = async () => { const editorViews = this.resource.editorViews; + if (!editorViews) { + return; + } for (let i = 0; i < editorViews.length; i++) { const name = editorViews[i].viewName; this.changeViewType(name); @@ -80,15 +101,19 @@ export class EditorWindow { }; setDefaultViewType = () => { - this.changeViewType(this.resource.defaultViewType); + this.changeViewType(this.config.viewType ?? this.resource.defaultViewType); }; + get resourceType() { + return this.resource.resourceType.type; + } + initViewType = async (name: string) => { const viewInfo = this.resource.getEditorView(name); if (this.editorViews.get(name)) { return; } - const editorView = new Context(this.workspace, this, viewInfo as any, this.options); + const editorView = new Context(this.workspace, this, viewInfo as any, this.config.options); this.editorViews.set(name, editorView); }; @@ -96,6 +121,10 @@ export class EditorWindow { this.editorView?.setActivate(false); this.editorView = this.editorViews.get(name)!; + if (!this.editorView) { + return; + } + if (!ignoreEmit) { this.emitter.emit('window.change.view.type', name); } diff --git a/packages/workspace/src/workspace.ts b/packages/workspace/src/workspace.ts index 509ca9d9d..0b7181cd8 100644 --- a/packages/workspace/src/workspace.ts +++ b/packages/workspace/src/workspace.ts @@ -46,7 +46,7 @@ export class Workspace implements IPublicApiWorkspace { return null; } - windows: EditorWindow[] = []; + @obx.ref windows: EditorWindow[] = []; editorWindowMap: Map = new Map(); @@ -71,7 +71,9 @@ export class Workspace implements IPublicApiWorkspace { } const title = this.defaultResourceType.name; const resource = new Resource({}, this.defaultResourceType, this); - this.window = new EditorWindow(resource, this, title); + this.window = new EditorWindow(resource, this, { + title, + }); this.editorWindowMap.set(this.window.id, this.window); this.windows.push(this.window); this.emitChangeWindow(); @@ -83,13 +85,11 @@ export class Workspace implements IPublicApiWorkspace { } async registerResourceType(resourceTypeModel: IPublicTypeResourceType): Promise { - if (resourceTypeModel.resourceType === 'editor') { - const resourceType = new ResourceType(resourceTypeModel); - this.resourceTypeMap.set(resourceTypeModel.resourceName, resourceType); + const resourceType = new ResourceType(resourceTypeModel); + this.resourceTypeMap.set(resourceTypeModel.resourceName, resourceType); - if (!this.window && this.defaultResourceType) { - this.initWindow(); - } + if (!this.window && this.defaultResourceType) { + this.initWindow(); } } @@ -150,7 +150,7 @@ export class Workspace implements IPublicApiWorkspace { openEditorWindow(name: string, title: string, options: Object, viewType?: string) { const resourceType = this.resourceTypeMap.get(name); if (!resourceType) { - console.error(`${name} is not available`); + console.error(`${name} resourceType is not available`); return; } const filterWindows = this.windows.filter(d => (d.resource.name === name && d.resource.title == title)); @@ -164,8 +164,12 @@ export class Workspace implements IPublicApiWorkspace { title, options, }, resourceType, this); - this.window = new EditorWindow(resource, this, title, options); - this.windows.push(this.window); + this.window = new EditorWindow(resource, this, { + title, + options, + viewType, + }); + this.windows = [...this.windows, this.window]; this.editorWindowMap.set(this.window.id, this.window); this.emitChangeWindow(); this.emitChangeActiveWindow();