feat: support webview type resource in workspace mode

This commit is contained in:
liujuping 2023-02-15 15:30:05 +08:00 committed by 林熠
parent 8c82fe8f00
commit 36d1d3bef1
13 changed files with 105 additions and 28 deletions

View File

@ -32,4 +32,12 @@ export class Resource implements IPublicModelResource {
get category() { get category() {
return this[resourceSymbol].category; return this[resourceSymbol].category;
} }
get children() {
return this[resourceSymbol].children.map((child) => new Resource(child));
}
get viewType() {
return this[resourceSymbol].viewType;
}
} }

View File

@ -21,7 +21,7 @@ export interface IPublicApiWorkspace {
setResourceList(resourceList: IPublicResourceList): void; setResourceList(resourceList: IPublicResourceList): void;
/** 资源树列表更新事件 */ /** 资源树列表更新事件 */
onResourceListChange(fn: (resourceList: IPublicResourceList) => void): () => IPublicTypeDisposable; onResourceListChange(fn: (resourceList: IPublicResourceList) => void): IPublicTypeDisposable;
/** 注册资源 */ /** 注册资源 */
registerResourceType(resourceTypeModel: IPublicTypeResourceType): void; registerResourceType(resourceTypeModel: IPublicTypeResourceType): void;

View File

@ -5,4 +5,7 @@ export interface IPublicEditorViewConfig {
/** 资源保存时,会调用视图的钩子 */ /** 资源保存时,会调用视图的钩子 */
save?: () => Promise<void>; save?: () => Promise<void>;
/** viewType 类型为 'webview' 时渲染的地址 */
url?: () => Promise<string>;
} }

View File

@ -1,10 +1,15 @@
import { ReactElement } from 'react';
export interface IPublicResourceData { export interface IPublicResourceData {
resourceName: string; resourceName: string;
title: string; title: string;
category?: string; category?: string;
viewType?: string;
icon?: ReactElement;
options: { options: {
[key: string]: any; [key: string]: any;
}; };
children?: IPublicResourceData[];
} }
export type IPublicResourceList = IPublicResourceData[]; export type IPublicResourceList = IPublicResourceData[];

View File

@ -28,4 +28,7 @@ export interface IPublicResourceTypeConfig {
/** 默认标题 */ /** 默认标题 */
defaultTitle?: string; defaultTitle?: string;
/** resourceType 类型为 'webview' 时渲染的地址 */
url?: () => Promise<string>;
} }

View File

@ -4,7 +4,7 @@ import { IPublicResourceTypeConfig } from './resource-type-config';
export interface IPublicTypeResourceType { export interface IPublicTypeResourceType {
resourceName: string; resourceName: string;
resourceType: string; resourceType: 'editor' | 'webview';
(ctx: IPublicModelPluginContext): IPublicResourceTypeConfig; (ctx: IPublicModelPluginContext, options: Object): IPublicResourceTypeConfig;
} }

View File

@ -28,18 +28,22 @@ import {
Canvas, Canvas,
} from '@alilc/lowcode-shell'; } from '@alilc/lowcode-shell';
import { import {
IPluginPreferenceMananger,
IPublicApiEvent,
IPublicModelPluginContext,
IPublicTypePluginMeta, IPublicTypePluginMeta,
} from '@alilc/lowcode-types'; } from '@alilc/lowcode-types';
import { getLogger } from '@alilc/lowcode-utils'; import { getLogger } from '@alilc/lowcode-utils';
import { Workspace as InnerWorkspace } from '../workspace'; import { Workspace as InnerWorkspace } from '../workspace';
import { EditorWindow } from '../window'; import { EditorWindow } from '../window';
export class BasicContext { export class BasicContext implements IPublicModelPluginContext {
skeleton: Skeleton; skeleton: Skeleton;
plugins: Plugins; plugins: Plugins;
project: Project; project: Project;
setters: Setters; setters: Setters;
material: Material; material: Material;
common: Common;
config; config;
event; event;
logger; logger;
@ -53,6 +57,8 @@ export class BasicContext {
innerHotkey: InnerHotkey; innerHotkey: InnerHotkey;
innerPlugins: LowCodePluginManager; innerPlugins: LowCodePluginManager;
canvas: Canvas; canvas: Canvas;
pluginEvent: IPublicApiEvent;
preference: IPluginPreferenceMananger;
constructor(innerWorkspace: InnerWorkspace, viewName: string, public editorWindow?: EditorWindow) { constructor(innerWorkspace: InnerWorkspace, viewName: string, public editorWindow?: EditorWindow) {
const editor = new Editor(viewName, true); const editor = new Editor(viewName, true);
@ -101,6 +107,7 @@ export class BasicContext {
this.designer = designer; this.designer = designer;
this.canvas = canvas; this.canvas = canvas;
const common = new Common(editor, innerSkeleton); const common = new Common(editor, innerSkeleton);
this.common = common;
let plugins: any; let plugins: any;
const pluginContextApiAssembler: ILowCodePluginContextApiAssembler = { const pluginContextApiAssembler: ILowCodePluginContextApiAssembler = {

View File

@ -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 { IPublicEditorViewConfig, IPublicTypeEditorView } from '@alilc/lowcode-types';
import { flow } from 'mobx'; import { flow } from 'mobx';
import { Workspace as InnerWorkspace } from '../workspace'; import { Workspace as InnerWorkspace } from '../workspace';
@ -17,7 +17,7 @@ export class Context extends BasicContext {
@obx isInit: boolean = false; @obx isInit: boolean = false;
get active() { @computed get active() {
return this._activate; return this._activate;
} }
@ -33,7 +33,7 @@ export class Context extends BasicContext {
this.isInit = true; 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); super(workspace, editorView.viewName, editorWindow);
this.viewType = editorView.viewType || 'editor'; this.viewType = editorView.viewType || 'editor';
this.viewName = editorView.viewName; this.viewName = editorView.viewName;

View File

@ -1,6 +1,6 @@
import { IPublicModelPluginContext } from '@alilc/lowcode-types'; import { IPublicModelPluginContext } from '@alilc/lowcode-types';
function DesignerView(props: { export function DesignerView(props: {
url: string; url: string;
viewName: string; viewName: string;
}) { }) {

View File

@ -17,12 +17,16 @@ export class Resource implements IPublicModelResource {
return this.resourceType.name; return this.resourceType.name;
} }
get viewType() {
return this.resourceData.viewType;
}
get description() { get description() {
return this.resourceTypeInstance?.description; return this.resourceTypeInstance?.description;
} }
get icon() { get icon() {
return this.resourceTypeInstance?.icon; return this.resourceData.icon || this.resourceTypeInstance?.icon;
} }
get type() { get type() {
@ -45,9 +49,13 @@ export class Resource implements IPublicModelResource {
return this.context.innerSkeleton; 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.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(); this.init();
if (this.resourceTypeInstance.editorViews) { if (this.resourceTypeInstance.editorViews) {
this.resourceTypeInstance.editorViews.forEach((d: any) => { this.resourceTypeInstance.editorViews.forEach((d: any) => {
@ -68,6 +76,10 @@ export class Resource implements IPublicModelResource {
return await this.resourceTypeInstance.import?.(schema); return await this.resourceTypeInstance.import?.(schema);
} }
async url() {
return await this.resourceTypeInstance.url?.();
}
async save(value: any) { async save(value: any) {
return await this.resourceTypeInstance.save?.(value); return await this.resourceTypeInstance.save?.(value);
} }

View File

@ -3,6 +3,7 @@ import { ResourceView } from './resource-view';
import { engineConfig, observer } from '@alilc/lowcode-editor-core'; import { engineConfig, observer } from '@alilc/lowcode-editor-core';
import { EditorWindow } from '../window'; import { EditorWindow } from '../window';
import { BuiltinLoading } from '@alilc/lowcode-designer'; import { BuiltinLoading } from '@alilc/lowcode-designer';
import { DesignerView } from '../inner-plugins/webview';
@observer @observer
export class WindowView extends PureComponent<{ export class WindowView extends PureComponent<{
@ -11,16 +12,21 @@ export class WindowView extends PureComponent<{
}, any> { }, any> {
render() { render() {
const { active } = this.props; const { active } = this.props;
const { editorView, resource } = this.props.window; const { resource, initReady, url } = this.props.window;
if (!editorView) {
if (!initReady) {
const Loading = engineConfig.get('loadingComponent', BuiltinLoading); const Loading = engineConfig.get('loadingComponent', BuiltinLoading);
return ( return (
<div className={`workspace-engine-main ${active ? 'active' : ''}`}> <div className={`workspace-engine-main 111 ${active ? 'active' : ''}`}>
<Loading /> <Loading />
</div> </div>
); );
} }
if (resource.type === 'webview' && url) {
return <DesignerView url={url} viewName={resource.name} />;
}
return ( return (
<div className={`workspace-engine-main ${active ? 'active' : ''}`}> <div className={`workspace-engine-main ${active ? 'active' : ''}`}>
<ResourceView <ResourceView

View File

@ -5,19 +5,32 @@ import { Workspace } from './workspace';
import { Resource } from './resource'; import { Resource } from './resource';
import { IPublicTypeDisposable } from '../../types/es/shell/type/disposable'; import { IPublicTypeDisposable } from '../../types/es/shell/type/disposable';
interface IWindowCOnfig {
title: string | undefined;
options?: Object;
viewType?: string | undefined;
}
export class EditorWindow { export class EditorWindow {
id: string = uniqueId('window'); id: string = uniqueId('window');
icon: React.ReactElement | undefined; icon: React.ReactElement | undefined;
private emitter: IEventBus = createModuleEventBus('Project'); private emitter: IEventBus = createModuleEventBus('Project');
title: string | undefined;
url: string | undefined;
@obx.ref editorView: Context; @obx.ref editorView: Context;
@obx editorViews: Map<string, Context> = new Map<string, Context>(); @obx editorViews: Map<string, Context> = new Map<string, Context>();
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); makeObservable(this);
this.init(); this.init();
this.title = config.title;
this.icon = resource.icon; this.icon = resource.icon;
} }
@ -48,11 +61,16 @@ export class EditorWindow {
async init() { async init() {
await this.initViewTypes(); await this.initViewTypes();
await this.execViewTypesInit(); await this.execViewTypesInit();
this.url = await this.resource.url();
this.setDefaultViewType(); this.setDefaultViewType();
this.initReady = true;
} }
initViewTypes = async () => { initViewTypes = async () => {
const editorViews = this.resource.editorViews; const editorViews = this.resource.editorViews;
if (!editorViews) {
return;
}
for (let i = 0; i < editorViews.length; i++) { for (let i = 0; i < editorViews.length; i++) {
const name = editorViews[i].viewName; const name = editorViews[i].viewName;
await this.initViewType(name); await this.initViewType(name);
@ -72,6 +90,9 @@ export class EditorWindow {
execViewTypesInit = async () => { execViewTypesInit = async () => {
const editorViews = this.resource.editorViews; const editorViews = this.resource.editorViews;
if (!editorViews) {
return;
}
for (let i = 0; i < editorViews.length; i++) { for (let i = 0; i < editorViews.length; i++) {
const name = editorViews[i].viewName; const name = editorViews[i].viewName;
this.changeViewType(name); this.changeViewType(name);
@ -80,15 +101,19 @@ export class EditorWindow {
}; };
setDefaultViewType = () => { 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) => { initViewType = async (name: string) => {
const viewInfo = this.resource.getEditorView(name); const viewInfo = this.resource.getEditorView(name);
if (this.editorViews.get(name)) { if (this.editorViews.get(name)) {
return; 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); this.editorViews.set(name, editorView);
}; };
@ -96,6 +121,10 @@ export class EditorWindow {
this.editorView?.setActivate(false); this.editorView?.setActivate(false);
this.editorView = this.editorViews.get(name)!; this.editorView = this.editorViews.get(name)!;
if (!this.editorView) {
return;
}
if (!ignoreEmit) { if (!ignoreEmit) {
this.emitter.emit('window.change.view.type', name); this.emitter.emit('window.change.view.type', name);
} }

View File

@ -46,7 +46,7 @@ export class Workspace implements IPublicApiWorkspace {
return null; return null;
} }
windows: EditorWindow[] = []; @obx.ref windows: EditorWindow[] = [];
editorWindowMap: Map<string, EditorWindow> = new Map<string, EditorWindow>(); editorWindowMap: Map<string, EditorWindow> = new Map<string, EditorWindow>();
@ -71,7 +71,9 @@ export class Workspace implements IPublicApiWorkspace {
} }
const title = this.defaultResourceType.name; const title = this.defaultResourceType.name;
const resource = new Resource({}, this.defaultResourceType, this); 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.editorWindowMap.set(this.window.id, this.window);
this.windows.push(this.window); this.windows.push(this.window);
this.emitChangeWindow(); this.emitChangeWindow();
@ -83,13 +85,11 @@ export class Workspace implements IPublicApiWorkspace {
} }
async registerResourceType(resourceTypeModel: IPublicTypeResourceType): Promise<void> { async registerResourceType(resourceTypeModel: IPublicTypeResourceType): Promise<void> {
if (resourceTypeModel.resourceType === 'editor') { const resourceType = new ResourceType(resourceTypeModel);
const resourceType = new ResourceType(resourceTypeModel); this.resourceTypeMap.set(resourceTypeModel.resourceName, resourceType);
this.resourceTypeMap.set(resourceTypeModel.resourceName, resourceType);
if (!this.window && this.defaultResourceType) { if (!this.window && this.defaultResourceType) {
this.initWindow(); this.initWindow();
}
} }
} }
@ -150,7 +150,7 @@ export class Workspace implements IPublicApiWorkspace {
openEditorWindow(name: string, title: string, options: Object, viewType?: string) { openEditorWindow(name: string, title: string, options: Object, viewType?: string) {
const resourceType = this.resourceTypeMap.get(name); const resourceType = this.resourceTypeMap.get(name);
if (!resourceType) { if (!resourceType) {
console.error(`${name} is not available`); console.error(`${name} resourceType is not available`);
return; return;
} }
const filterWindows = this.windows.filter(d => (d.resource.name === name && d.resource.title == title)); const filterWindows = this.windows.filter(d => (d.resource.name === name && d.resource.title == title));
@ -164,8 +164,12 @@ export class Workspace implements IPublicApiWorkspace {
title, title,
options, options,
}, resourceType, this); }, resourceType, this);
this.window = new EditorWindow(resource, this, title, options); this.window = new EditorWindow(resource, this, {
this.windows.push(this.window); title,
options,
viewType,
});
this.windows = [...this.windows, this.window];
this.editorWindowMap.set(this.window.id, this.window); this.editorWindowMap.set(this.window.id, this.window);
this.emitChangeWindow(); this.emitChangeWindow();
this.emitChangeActiveWindow(); this.emitChangeActiveWindow();