Merge branch 'fix/pageHistory' into 'release/0.9.0'

Fix/page history



See merge request !864869
This commit is contained in:
康为 2020-06-23 10:35:32 +08:00
commit f463fe8c6b
8 changed files with 177 additions and 5 deletions

View File

@ -1,5 +1,6 @@
import { computed, obx } from '@ali/lowcode-editor-core'; import { computed, obx } from '@ali/lowcode-editor-core';
import { NodeData, isJSExpression, isDOMText, NodeSchema, isNodeSchema, RootSchema } from '@ali/lowcode-types'; import { NodeData, isJSExpression, isDOMText, NodeSchema, isNodeSchema, RootSchema } from '@ali/lowcode-types';
import { EventEmitter } from 'events';
import { Project } from '../project'; import { Project } from '../project';
import { ISimulatorHost } from '../simulator'; import { ISimulatorHost } from '../simulator';
import { ComponentMeta } from '../component-meta'; import { ComponentMeta } from '../component-meta';
@ -26,7 +27,7 @@ export class DocumentModel {
/** /**
* *
*/ */
readonly id: string = uniqueId('doc'); id: string = uniqueId('doc');
/** /**
* *
*/ */
@ -40,6 +41,8 @@ export class DocumentModel {
@obx.val private nodes = new Set<Node>(); @obx.val private nodes = new Set<Node>();
private seqId = 0; private seqId = 0;
private _simulator?: ISimulatorHost; private _simulator?: ISimulatorHost;
private emitter: EventEmitter;
private rootNodeVisitorMap: { [visitorName: string]: any } = {};
/** /**
* *
@ -75,6 +78,7 @@ export class DocumentModel {
console.info(this.willPurgeSpace); console.info(this.willPurgeSpace);
}, true); }, true);
*/ */
this.emitter = new EventEmitter();
if (!schema) { if (!schema) {
this._blank = true; this._blank = true;
@ -194,9 +198,14 @@ export class DocumentModel {
this.nodesMap.set(node.id, node); this.nodesMap.set(node.id, node);
this.nodes.add(node); this.nodes.add(node);
this.emitter.emit('nodecreate', node);
return node as any; return node as any;
} }
public destroyNode(node: Node) {
this.emitter.emit('nodedestroy', node);
}
/** /**
* *
*/ */
@ -406,7 +415,7 @@ export class DocumentModel {
/** /**
* *
*/ */
open(): void { open(): DocumentModel {
const originState = this._opened; const originState = this._opened;
this._opened = true; this._opened = true;
if (originState === false) { if (originState === false) {
@ -417,6 +426,7 @@ export class DocumentModel {
} else { } else {
this.project.checkExclusive(this); this.project.checkExclusive(this);
} }
return this;
} }
/** /**
@ -499,6 +509,52 @@ export class DocumentModel {
get root() { get root() {
return this.rootNode; return this.rootNode;
} }
onRendererReady(fn: (args: any) => void): () => void {
this.emitter.on('lowcode_engine_renderer_ready', fn);
return () => {
this.emitter.removeListener('lowcode_engine_renderer_ready', fn);
};
}
setRendererReady(renderer) {
this.emitter.emit('lowcode_engine_renderer_ready', renderer);
}
acceptRootNodeVisitor(
visitorName: string = 'default',
visitorFn: (node: RootNode) => any ) {
let visitorResult = {};
if (!visitorName) {
/* tslint:disable no-console */
console.warn('Invalid or empty RootNodeVisitor name.');
}
try {
visitorResult = visitorFn.call(this, this.rootNode);
this.rootNodeVisitorMap[visitorName] = visitorResult;
} catch (e) {
console.error('RootNodeVisitor is not valid.');
}
return visitorResult;
}
getRootNodeVisitor(name: string) {
return this.rootNodeVisitorMap[name];
}
onNodeCreate(func: (node: Node) => void) {
this.emitter.on('nodecreate', func);
return () => {
this.emitter.removeListener('nodecreate', func);
};
}
onNodeDestroy(func: (node: Node) => void) {
this.emitter.on('nodedestroy', func);
return () => {
this.emitter.removeListener('nodedestroy', func);
};
}
} }
export function isDocumentModel(obj: any): obj is DocumentModel { export function isDocumentModel(obj: any): obj is DocumentModel {

View File

@ -637,6 +637,8 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
this.autoruns?.forEach((dispose) => dispose()); this.autoruns?.forEach((dispose) => dispose());
this.props.purge(); this.props.purge();
this.document.internalRemoveAndPurgeNode(this); this.document.internalRemoveAndPurgeNode(this);
this.document.destroyNode(this);
} }
// ======= compatible apis ==== // ======= compatible apis ====

View File

@ -122,7 +122,7 @@ export class Project {
if (data) { if (data) {
doc = new DocumentModel(this, data); doc = new DocumentModel(this, data);
this.documents.push(doc); this.documents.push(doc);
doc.open(); return doc.open();
} }
return; return;
@ -134,7 +134,7 @@ export class Project {
doc = new DocumentModel(this, doc); doc = new DocumentModel(this, doc);
this.documents.push(doc); this.documents.push(doc);
doc.open(); return doc.open();
} }
checkExclusive(actived: DocumentModel) { checkExclusive(actived: DocumentModel) {

View File

@ -5,6 +5,8 @@ import { Designer, LiveEditing, TransformStage, Node } from '@ali/lowcode-design
import Outline, { OutlineBackupPane, getTreeMaster } from '@ali/lowcode-plugin-outline-pane'; import Outline, { OutlineBackupPane, getTreeMaster } from '@ali/lowcode-plugin-outline-pane';
import { toCss } from '@ali/vu-css-style'; import { toCss } from '@ali/vu-css-style';
import logger from '@ali/vu-logger'; import logger from '@ali/vu-logger';
import bus from './bus';
import { VE_EVENTS } from './base/const';
import DesignerPlugin from '@ali/lowcode-plugin-designer'; import DesignerPlugin from '@ali/lowcode-plugin-designer';
import { Skeleton, SettingsPrimaryPane } from '@ali/lowcode-editor-skeleton'; import { Skeleton, SettingsPrimaryPane } from '@ali/lowcode-editor-skeleton';
@ -23,6 +25,12 @@ export const designer = new Designer({ editor: editor });
editor.set(Designer, designer); editor.set(Designer, designer);
editor.set('designer', designer); editor.set('designer', designer);
designer.project.onCurrentDocumentChange((doc) => {
doc.onRendererReady(() => {
bus.emit(VE_EVENTS.VE_PAGE_PAGE_READY);
});
});
// 节点 props 初始化 // 节点 props 初始化
designer.addPropsReducer((props, node) => { designer.addPropsReducer((props, node) => {
// run initials // run initials

View File

@ -1,6 +1,7 @@
import { designer } from './editor'; import { designer } from './editor';
import { RootSchema } from '@ali/lowcode-types'; import { RootSchema } from '@ali/lowcode-types';
import { DocumentModel } from '@ali/lowcode-designer'; import { DocumentModel } from '@ali/lowcode-designer';
import NodeCacheVisitor from './rootNodeVisitor';
const { project } = designer; const { project } = designer;
@ -96,4 +97,15 @@ Object.defineProperty(pages, 'currentPage', {
} }
}) })
pages.onCurrentPageChange((page: DocumentModel) => {
if (!page) { return; }
page.acceptRootNodeVisitor('NodeCache', (rootNode) => {
const visitor: NodeCacheVisitor = page.getRootNodeVisitor('NodeCache');
if (visitor) {
visitor.destroy();
}
return new NodeCacheVisitor(page, rootNode);
});
});
export default pages; export default pages;

View File

@ -0,0 +1,91 @@
import { findIndex } from 'lodash';
import { DocumentModel, Node, Root } from '@ali/lowcode-designer';
/**
* RootNodeVisitor for VisualEngine Page
*
* - store / cache node
* - quickly find / search or do operations on Node
*/
export default class RootNodeVisitor {
public nodeIdMap: {[id: string]: Node} = {};
public nodeFieldIdMap: {[fieldId: string]: Node} = {};
public nodeList: Node[] = [];
private page: DocumentModel;
private root: RootNode;
private cancelers: Function[] = [];
constructor(page: DocumentModel, rootNode: RootNode) {
this.page = page;
this.root = rootNode;
this._findNode(this.root);
this._init();
}
public getNodeList() {
return this.nodeList;
}
public getNodeIdMap() {
return this.nodeIdMap;
}
public getNodeFieldIdMap() {
return this.nodeFieldIdMap;
}
public getNodeById(id?: string) {
if (!id) { return this.nodeIdMap; }
return this.nodeIdMap[id];
}
public getNodeByFieldId(fieldId?: string) {
if (!fieldId) { return this.nodeFieldIdMap; }
return this.nodeFieldIdMap[fieldId];
}
public destroy() {
this.cancelers.forEach((canceler) => canceler());
}
private _init() {
this.cancelers.push(
this.page.onNodeCreate((node) => {
this.nodeList.push(node);
this.nodeIdMap[node.id] = node;
if (node.getPropValue('fieldId')) {
this.nodeFieldIdMap[node.getPropValue('fieldId')] = node;
}
}),
);
this.cancelers.push(
this.page.onNodeDestroy((node) => {
const idx = findIndex(this.nodeList, (n) => node.id === n.id);
this.nodeList.splice(idx, 1);
delete this.nodeIdMap[node.id];
if (node.getPropValue('fieldId')) {
delete this.nodeFieldIdMap[node.getPropValue('fieldId')];
}
}),
);
}
private _findNode(node: Node) {
const props = node.getProps();
const fieldId = props && props.getPropValue('fieldId');
this.nodeIdMap[node.getId()] = node;
this.nodeList.push(node);
if (fieldId) {
this.nodeFieldIdMap[fieldId] = node;
}
const children = node.getChildren();
if (children) {
children.forEach((child) => this._findNode(child));
}
}
}

View File

@ -17,10 +17,12 @@ import Leaf from './builtin-components/leaf';
export class SimulatorRenderer implements BuiltinSimulatorRenderer { export class SimulatorRenderer implements BuiltinSimulatorRenderer {
readonly isSimulatorRenderer = true; readonly isSimulatorRenderer = true;
private dispose?: () => void; private dispose?: () => void;
constructor() { constructor() {
if (!host) { if (!host) {
return; return;
} }
this.dispose = host.connect(this, () => { this.dispose = host.connect(this, () => {
// sync layout config // sync layout config
@ -286,6 +288,7 @@ export class SimulatorRenderer implements BuiltinSimulatorRenderer {
document.body.classList.add('engine-document'); // important! Stylesheet.invoke depends document.body.classList.add('engine-document'); // important! Stylesheet.invoke depends
reactRender(createElement(SimulatorRendererView, { renderer: this }), container); reactRender(createElement(SimulatorRendererView, { renderer: this }), container);
host.document.setRendererReady(this);
} }
} }