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() {
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;
/** 资源树列表更新事件 */
onResourceListChange(fn: (resourceList: IPublicResourceList) => void): () => IPublicTypeDisposable;
onResourceListChange(fn: (resourceList: IPublicResourceList) => void): IPublicTypeDisposable;
/** 注册资源 */
registerResourceType(resourceTypeModel: IPublicTypeResourceType): void;

View File

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

View File

@ -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[];

View File

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

View File

@ -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;
}

View File

@ -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 = {

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 { 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;

View File

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

View File

@ -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);
}

View File

@ -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 (
<div className={`workspace-engine-main ${active ? 'active' : ''}`}>
<div className={`workspace-engine-main 111 ${active ? 'active' : ''}`}>
<Loading />
</div>
);
}
if (resource.type === 'webview' && url) {
return <DesignerView url={url} viewName={resource.name} />;
}
return (
<div className={`workspace-engine-main ${active ? 'active' : ''}`}>
<ResourceView

View File

@ -5,19 +5,32 @@ import { Workspace } from './workspace';
import { Resource } from './resource';
import { IPublicTypeDisposable } from '../../types/es/shell/type/disposable';
interface IWindowCOnfig {
title: string | undefined;
options?: Object;
viewType?: string | undefined;
}
export class EditorWindow {
id: string = uniqueId('window');
icon: React.ReactElement | undefined;
private emitter: IEventBus = createModuleEventBus('Project');
title: string | undefined;
url: string | undefined;
@obx.ref editorView: 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);
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);
}

View File

@ -46,7 +46,7 @@ export class Workspace implements IPublicApiWorkspace {
return null;
}
windows: EditorWindow[] = [];
@obx.ref windows: EditorWindow[] = [];
editorWindowMap: Map<string, EditorWindow> = new Map<string, EditorWindow>();
@ -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<void> {
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();