mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-01-13 01:21:58 +00:00
Merge branch 'demo' of gitlab.alibaba-inc.com:ali-lowcode/ali-lowcode-engine into v/0.8.0
This commit is contained in:
commit
0c7a8b0a7f
@ -16,5 +16,6 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="lce-container"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
export default {
|
||||
sdkVersion: '1.0.3',
|
||||
historyMode: 'hash', // 浏览器路由:brower 哈希路由:hash
|
||||
constainerId: 'app',
|
||||
history: 'hash', // 浏览器路由:brower 哈希路由:hash
|
||||
containerId: 'lce-container',
|
||||
layout: {
|
||||
componentName: 'BasicLayout',
|
||||
props: {
|
||||
name: '低代码引擎预览 demo',
|
||||
logo: {
|
||||
src: 'https://img.alicdn.com/tfs/TB1kAfWyrY1gK0jSZTEXXXDQVXa-75-33.png',
|
||||
width: 40,
|
||||
height: 20,
|
||||
src: 'https://img.alicdn.com/tfs/TB1L.1QAeL2gK0jSZFmXXc7iXXa-90-90.png',
|
||||
width: 25,
|
||||
height: 25,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -1,12 +0,0 @@
|
||||
/**
|
||||
* 内置组件
|
||||
*/
|
||||
import Engine from '@ali/iceluna-sdk/lib/engine';
|
||||
import Page from '@ali/iceluna-sdk/lib/engine/pageEngine'
|
||||
import Div from '@ali/iceluna-comp-div';
|
||||
|
||||
export default {
|
||||
Engine,
|
||||
Page,
|
||||
Div,
|
||||
}
|
||||
5
packages/demo/src/app/config/components.ts
Normal file
5
packages/demo/src/app/config/components.ts
Normal file
@ -0,0 +1,5 @@
|
||||
/**
|
||||
* 内置组件
|
||||
*/
|
||||
|
||||
export default {};
|
||||
1
packages/demo/src/app/config/utils.ts
Normal file
1
packages/demo/src/app/config/utils.ts
Normal file
@ -0,0 +1 @@
|
||||
export default {};
|
||||
@ -1,22 +1,20 @@
|
||||
import { boot, run } from '@ali/lowcode-runtime';
|
||||
import { contribution, run } from '@ali/lowcode-runtime';
|
||||
import Renderer from '@ali/lowcode-react-renderer';
|
||||
import FusionLoading from './plugins/loading/fusion';
|
||||
import BasicLayout from './layouts/BasicLayout';
|
||||
import provider from './plugins/provider';
|
||||
import Preview from './plugins/provider';
|
||||
|
||||
// 注册渲染模块
|
||||
boot.registerRenderer(Renderer);
|
||||
contribution.registerRenderer(Renderer);
|
||||
|
||||
// 注册布局组件,可注册多个
|
||||
boot.registerLayout('BasicLayout', BasicLayout);
|
||||
contribution.registerLayout('BasicLayout', BasicLayout);
|
||||
|
||||
// 注册页面 Loading
|
||||
boot.registerLoading(FusionLoading);
|
||||
contribution.registerLoading(FusionLoading);
|
||||
|
||||
const appProvider = provider.create('lowcode_demo'); // 入参为应用唯一标识
|
||||
// appKey:应用唯一标识
|
||||
contribution.registerProvider(new Preview({ appKey: 'lowcode_demo' }));
|
||||
|
||||
// 异步加载应用配置
|
||||
appProvider.then(({ App, config }) => {
|
||||
// 启动应用
|
||||
run(App, config);
|
||||
});
|
||||
// 启动应用
|
||||
run();
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
@header-height: 52px;
|
||||
$header-height: 52px;
|
||||
|
||||
.avatar {
|
||||
width: 24px;
|
||||
@ -10,13 +10,13 @@
|
||||
.basic-shell {
|
||||
min-height: 100vh;
|
||||
.next-shell-header {
|
||||
height: @header-height;
|
||||
height: $header-height;
|
||||
}
|
||||
.next-shell-main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
min-height: calc(100% - @header-height);
|
||||
min-height: calc(100% - $header-height);
|
||||
.next-shell-sub-main {
|
||||
flex: 1;
|
||||
}
|
||||
@ -1,15 +1,22 @@
|
||||
import { Search, Icon, Shell } from '@alifd/next';
|
||||
import './index.less';
|
||||
import './index.scss';
|
||||
|
||||
// eslint-disable-next-line react/prop-types
|
||||
export default ({ name, children, logo }) => (
|
||||
<Shell className="basic-shell" type="dark" style={{ border: '1px solid #eee' }}>
|
||||
export default ({
|
||||
name,
|
||||
children,
|
||||
logo,
|
||||
}: {
|
||||
name: string;
|
||||
children: any;
|
||||
logo: { src: string; width: number; height: number };
|
||||
}) => (
|
||||
<Shell className="basic-shell" style={{ border: '1px solid #eee' }}>
|
||||
<Shell.Branding>
|
||||
<img src={logo.src} width={logo.width} height={logo.height} alt="logo" />
|
||||
<span style={{ marginLeft: 10 }}>{name}</span>
|
||||
</Shell.Branding>
|
||||
<Shell.Navigation direction="hoz">
|
||||
<Search key="2" shape="simple" type="dark" palceholder="Search" style={{ width: '200px' }} />
|
||||
<Search key="2" shape="simple" type="dark" style={{ width: '200px' }} />
|
||||
</Shell.Navigation>
|
||||
<Shell.Action>
|
||||
<Icon type="ic_tongzhi" />
|
||||
@ -1,11 +0,0 @@
|
||||
.recore-loading {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: url(https://g.alicdn.com/uxcore/pic/loading.svg) center no-repeat;
|
||||
background-size: contain;
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-top: -24px;
|
||||
margin-left: -24px;
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
import './index.less';
|
||||
|
||||
export default () => <div className="recore-loading" />;
|
||||
@ -1,4 +1,4 @@
|
||||
import { Loading } from '@alifd/next';
|
||||
import './index.less';
|
||||
import './index.scss';
|
||||
|
||||
export default () => <Loading tip="加载中..." className="fusion-loading" />;
|
||||
|
||||
@ -1,45 +1,45 @@
|
||||
import { createElement } from 'react';
|
||||
import { Provider, boot, Router } from '@ali/lowcode-runtime';
|
||||
import { ReactProvider, Utils } from '@ali/lowcode-runtime';
|
||||
import appConfig from '../config/app';
|
||||
import builtInComps from '../config/components';
|
||||
import componentsMap from '../config/componentsMap';
|
||||
import util from '../config/utils';
|
||||
import { buildComponents } from './utils';
|
||||
import constants from '../config/constants';
|
||||
import utils from '../config/utils';
|
||||
|
||||
// 定制加载应用配置的逻辑
|
||||
class PreviewProvider extends Provider {
|
||||
export default class Preview extends ReactProvider {
|
||||
// 定制获取、处理应用配置(组件、插件、路由模式、布局等)的逻辑
|
||||
async getAppData(appkey: string, restOptions?: any): Promise<any> {
|
||||
const { historyMode, layout, constainerId } = appConfig;
|
||||
async getAppData(appkey: string): Promise<any> {
|
||||
const { history, layout, containerId } = appConfig;
|
||||
const appSchemaStr: any = localStorage.getItem('lce-dev-store');
|
||||
const appSchema = JSON.parse(appSchemaStr || '');
|
||||
const history = {
|
||||
mode: historyMode || 'hash',
|
||||
basement: '/',
|
||||
};
|
||||
this.layout = layout;
|
||||
if (!appSchemaStr) {
|
||||
return;
|
||||
}
|
||||
const appSchema = JSON.parse(appSchemaStr);
|
||||
if (!appSchema) {
|
||||
return;
|
||||
}
|
||||
const routes: any = {};
|
||||
appSchema.componentsTree.forEach((page: any, idx: number) => {
|
||||
appSchema.componentsTree.forEach((page: any) => {
|
||||
if (!page.fileName) {
|
||||
return;
|
||||
}
|
||||
const pageId = page.fileName;
|
||||
routes[pageId] = `/${pageId}`;
|
||||
});
|
||||
this.routerConfig = routes;
|
||||
this.componentsMap = componentsMap;
|
||||
this.globalComponents = { ...builtInComps, ...buildComponents({ '@alifd/next': 'Next' }, componentsMap) };
|
||||
this.globalUtils = util;
|
||||
return {
|
||||
history,
|
||||
globalComponents: this.globalComponents,
|
||||
globalUtils: this.globalUtils,
|
||||
constainerId,
|
||||
layout,
|
||||
routes,
|
||||
containerId,
|
||||
components: { ...builtInComps, ...Utils.buildComponents({ '@alifd/next': 'Next' }, componentsMap) },
|
||||
componentsMap,
|
||||
utils: utils,
|
||||
constants,
|
||||
};
|
||||
}
|
||||
|
||||
// 定制获取、处理页面 schema 的逻辑
|
||||
async getPageData(pageId: string, restOptions?: any) {
|
||||
async getPageData(pageId: string) {
|
||||
const appSchemaStr = localStorage.getItem('lce-dev-store');
|
||||
const appSchema = JSON.parse(appSchemaStr || '');
|
||||
const idx = appSchema.componentsTree.findIndex(
|
||||
@ -48,67 +48,4 @@ class PreviewProvider extends Provider {
|
||||
const schema = appSchema.componentsTree[idx];
|
||||
return schema;
|
||||
}
|
||||
|
||||
// 定制构造根组件的逻辑,如切换路由机制
|
||||
createApp() {
|
||||
if (!this.routerConfig) {
|
||||
return;
|
||||
}
|
||||
const routes: Array<{ path: string; children: any; exact: boolean; keepAlive: boolean }> = [];
|
||||
let homePageId = '';
|
||||
Object.keys(this.routerConfig).forEach((pageId: string, idx: number) => {
|
||||
if (!pageId) {
|
||||
return;
|
||||
}
|
||||
const path = this.routerConfig[pageId];
|
||||
if (idx === 0 || path === '/') {
|
||||
homePageId = pageId;
|
||||
}
|
||||
routes.push({
|
||||
path,
|
||||
children: (props: any) => this.getLazyComponent(pageId, props),
|
||||
exact: true,
|
||||
keepAlive: true,
|
||||
});
|
||||
});
|
||||
if (homePageId) {
|
||||
routes.push({
|
||||
path: '**',
|
||||
children: (props: any) => this.getLazyComponent(homePageId, { ...props }),
|
||||
exact: true,
|
||||
keepAlive: true,
|
||||
});
|
||||
}
|
||||
const RouterView = (props: any) => {
|
||||
return createElement(Router as any, {
|
||||
routes,
|
||||
components: this.globalComponents,
|
||||
utils: this.globalUtils,
|
||||
componentsMap: this.componentsMap,
|
||||
...props,
|
||||
});
|
||||
};
|
||||
let App;
|
||||
if (!this.layout || !(this.layout as any).componentName) {
|
||||
App = (props: any) => createElement(RouterView, { ...props });
|
||||
return App;
|
||||
}
|
||||
const { componentName: layoutName, props: layoutProps } = this.layout as any;
|
||||
const Layout = boot.getLayout(layoutName);
|
||||
if (Layout) {
|
||||
App = (props: any) =>
|
||||
createElement(
|
||||
Layout,
|
||||
{
|
||||
...layoutProps,
|
||||
},
|
||||
RouterView({ props }),
|
||||
);
|
||||
} else {
|
||||
App = (props: any) => createElement(RouterView, props);
|
||||
}
|
||||
return App;
|
||||
}
|
||||
}
|
||||
|
||||
export default new PreviewProvider();
|
||||
|
||||
@ -1 +1 @@
|
||||
import "./editor"
|
||||
import './editor';
|
||||
|
||||
@ -1 +1 @@
|
||||
import "./app";
|
||||
import './app';
|
||||
|
||||
@ -25,7 +25,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove abstract identifer ([2e45266](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/2e45266))
|
||||
* remove abstract identifier ([2e45266](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/2e45266))
|
||||
|
||||
|
||||
### Features
|
||||
@ -41,17 +41,9 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
<<<<<<< HEAD
|
||||
* remove abstract identifer ([2e45266](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/2e45266))
|
||||
=======
|
||||
* remove abstract identifer ([2e45266](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/2e4526667d563dccb85b9e3c60d862500f308915))
|
||||
>>>>>>> df955e1db90ff104cd11160def80113cfd6faccc
|
||||
* remove abstract identifier ([2e45266](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/2e4526667d563dccb85b9e3c60d862500f308915))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
<<<<<<< HEAD
|
||||
* complet preview ([56c16ff](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/56c16ff))
|
||||
=======
|
||||
* complet preview ([56c16ff](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/56c16ffa5c39c2d01abd9cfa90fea49a4539da1d))
|
||||
>>>>>>> df955e1db90ff104cd11160def80113cfd6faccc
|
||||
|
||||
@ -1,44 +0,0 @@
|
||||
import { ComponentClass, FunctionComponent } from 'react';
|
||||
|
||||
type TComponent = ComponentClass | FunctionComponent;
|
||||
|
||||
class Trunk {
|
||||
private renderer: TComponent | null = null;
|
||||
private layouts: { [key: string]: TComponent } = {};
|
||||
private loading: TComponent | null = null;
|
||||
|
||||
registerRenderer(renderer: TComponent): any {
|
||||
this.renderer = renderer;
|
||||
}
|
||||
|
||||
registerLayout(componentName: string, Layout: TComponent): any {
|
||||
if (!componentName || !Layout) {
|
||||
return;
|
||||
}
|
||||
this.layouts[componentName] = Layout;
|
||||
}
|
||||
|
||||
registerLoading(component: TComponent) {
|
||||
if (!component) {
|
||||
return;
|
||||
}
|
||||
this.loading = component;
|
||||
}
|
||||
|
||||
getLayout(componentName: string) {
|
||||
if (!componentName) {
|
||||
return;
|
||||
}
|
||||
return this.layouts[componentName];
|
||||
}
|
||||
|
||||
getRenderer(): TComponent | null {
|
||||
return this.renderer;
|
||||
}
|
||||
|
||||
getLoading(): TComponent | null {
|
||||
return this.loading;
|
||||
}
|
||||
}
|
||||
|
||||
export default new Trunk();
|
||||
52
packages/runtime/src/core/contribution.ts
Normal file
52
packages/runtime/src/core/contribution.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { ReactType } from 'react';
|
||||
import { IProvider } from './provider/base';
|
||||
|
||||
class Contribution {
|
||||
private renderer: ReactType | null = null;
|
||||
private layouts: { [key: string]: ReactType } = {};
|
||||
private loading: ReactType | null = null;
|
||||
private provider: any;
|
||||
|
||||
registerRenderer(renderer: ReactType): any {
|
||||
this.renderer = renderer;
|
||||
}
|
||||
|
||||
registerLayout(componentName: string, Layout: ReactType): any {
|
||||
if (!componentName || !Layout) {
|
||||
return;
|
||||
}
|
||||
this.layouts[componentName] = Layout;
|
||||
}
|
||||
|
||||
registerLoading(component: ReactType) {
|
||||
if (!component) {
|
||||
return;
|
||||
}
|
||||
this.loading = component;
|
||||
}
|
||||
|
||||
registerProvider(provider: IProvider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
getLayout(componentName: string) {
|
||||
if (!componentName) {
|
||||
return;
|
||||
}
|
||||
return this.layouts[componentName];
|
||||
}
|
||||
|
||||
getRenderer(): ReactType | null {
|
||||
return this.renderer;
|
||||
}
|
||||
|
||||
getLoading(): ReactType | null {
|
||||
return this.loading;
|
||||
}
|
||||
|
||||
getProvider() {
|
||||
return this.provider;
|
||||
}
|
||||
}
|
||||
|
||||
export default new Contribution();
|
||||
297
packages/runtime/src/core/provider/base.ts
Normal file
297
packages/runtime/src/core/provider/base.ts
Normal file
@ -0,0 +1,297 @@
|
||||
import { IAppConfig, IUtils, IComponents, HistoryMode } from '../run';
|
||||
|
||||
interface IConstants {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
interface IComponentMap {
|
||||
componentName: string;
|
||||
package?: string;
|
||||
version?: string;
|
||||
destructuring?: boolean;
|
||||
exportName?: string;
|
||||
subName?: string;
|
||||
}
|
||||
|
||||
interface ILayoutConfig {
|
||||
componentName: string;
|
||||
props: any;
|
||||
}
|
||||
|
||||
interface IRouterConfig {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
interface IHistoryConfig {
|
||||
mode: HistoryMode;
|
||||
basement?: string;
|
||||
}
|
||||
|
||||
interface IAppData {
|
||||
history?: HistoryMode;
|
||||
layout?: ILayoutConfig;
|
||||
routes?: IRouterConfig;
|
||||
containerId?: string;
|
||||
components?: IComponents;
|
||||
componentsMap?: IComponentMap[];
|
||||
utils?: IUtils;
|
||||
constants?: IConstants;
|
||||
}
|
||||
|
||||
interface IOptions {
|
||||
appKey: string;
|
||||
}
|
||||
|
||||
export interface ComponentProps {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface JSExpression {
|
||||
type: string;
|
||||
value: string;
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
export interface DataSourceItem {
|
||||
id: string;
|
||||
isInit: boolean;
|
||||
type: string;
|
||||
options: {
|
||||
uri: string;
|
||||
params: object;
|
||||
method: string;
|
||||
shouldFetch?: string;
|
||||
willFetch?: string;
|
||||
fit?: string;
|
||||
didFetch?: string;
|
||||
};
|
||||
dataHandler: JSExpression;
|
||||
}
|
||||
|
||||
export interface DataSource {
|
||||
list: DataSourceItem[];
|
||||
dataHandler: JSExpression;
|
||||
}
|
||||
|
||||
export interface LifeCycles {
|
||||
[key: string]: JSExpression;
|
||||
}
|
||||
|
||||
export interface Methods {
|
||||
[key: string]: JSExpression;
|
||||
}
|
||||
|
||||
export interface ComponentModel {
|
||||
id?: string;
|
||||
componentName: string;
|
||||
fileName?: string;
|
||||
props?: ComponentProps;
|
||||
css?: string;
|
||||
dataSource?: DataSource;
|
||||
lifeCycles?: LifeCycles;
|
||||
methods?: Methods;
|
||||
children?: ComponentModel[];
|
||||
condition?: JSExpression | boolean;
|
||||
loop?: string[];
|
||||
loopArgs?: string[];
|
||||
}
|
||||
|
||||
export interface IProvider {
|
||||
init?(): void;
|
||||
getAppData?(appkey: string): Promise<IAppData | undefined>;
|
||||
getPageData?(pageId: string): Promise<ComponentModel | undefined>;
|
||||
getLazyComponent?(pageId: string, props: any): any;
|
||||
createApp?(): void;
|
||||
}
|
||||
|
||||
export default class Provider implements IProvider {
|
||||
private appKey = '';
|
||||
private components: IComponents = {};
|
||||
private utils: IUtils = {};
|
||||
private constants: IConstants = {};
|
||||
private routes: IRouterConfig | null = null;
|
||||
private layout: ILayoutConfig | null = null;
|
||||
private componentsMap: IComponentMap[] = [];
|
||||
private history: HistoryMode = 'hash';
|
||||
private containerId = '';
|
||||
private lazyElementsMap: { [key: string]: any } = {};
|
||||
|
||||
constructor(options: IOptions) {
|
||||
const { appKey } = options;
|
||||
this.appKey = appKey;
|
||||
this.init();
|
||||
}
|
||||
|
||||
async(): Promise<IAppConfig> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const appData = await this.getAppData(this.appKey || '');
|
||||
if (!appData) {
|
||||
return;
|
||||
}
|
||||
const { history, layout, routes, containerId, components, componentsMap, utils, constants } = appData;
|
||||
this.setHistory(history);
|
||||
this.setLayoutConfig(layout);
|
||||
this.setRouterConfig(routes);
|
||||
this.setContainerId(containerId);
|
||||
this.registerComponents(components);
|
||||
this.registerComponentsMap(componentsMap);
|
||||
this.registerUtils(utils);
|
||||
this.registerContants(constants);
|
||||
resolve({
|
||||
history,
|
||||
components,
|
||||
utils,
|
||||
containerId,
|
||||
});
|
||||
} catch (err) {
|
||||
reject(err.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async init() {
|
||||
console.log('init');
|
||||
}
|
||||
|
||||
async getAppData(appkey: string): Promise<IAppData | undefined> {
|
||||
throw new Error('Method called "getPageData" not implemented.');
|
||||
}
|
||||
|
||||
async getPageData(pageId: string): Promise<ComponentModel | undefined> {
|
||||
throw new Error('Method called "getPageData" not implemented.');
|
||||
}
|
||||
|
||||
getLazyComponent(pageId: string, props: any): any {
|
||||
throw new Error('Method called "getLazyComponent" not implemented.');
|
||||
}
|
||||
|
||||
// 定制构造根组件的逻辑,如切换路由机制
|
||||
createApp() {
|
||||
throw new Error('Method called "createApp" not implemented.');
|
||||
}
|
||||
|
||||
registerComponents(components: IComponents | undefined) {
|
||||
if (!components) {
|
||||
return;
|
||||
}
|
||||
this.components = components;
|
||||
}
|
||||
|
||||
registerComponentsMap(componentsMap: IComponentMap[] | undefined) {
|
||||
if (!componentsMap) {
|
||||
return;
|
||||
}
|
||||
this.componentsMap = componentsMap;
|
||||
}
|
||||
|
||||
registerUtils(utils: IUtils | undefined) {
|
||||
if (!utils) {
|
||||
return;
|
||||
}
|
||||
this.utils = utils;
|
||||
}
|
||||
|
||||
registerContants(constants: IConstants | undefined) {
|
||||
if (!constants) {
|
||||
return;
|
||||
}
|
||||
this.constants = constants;
|
||||
}
|
||||
|
||||
setLayoutConfig(config: ILayoutConfig | undefined) {
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
this.layout = config;
|
||||
}
|
||||
|
||||
setRouterConfig(config: IRouterConfig | undefined) {
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
this.routes = config;
|
||||
}
|
||||
|
||||
setHistory(config: HistoryMode | undefined) {
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
this.history = config;
|
||||
}
|
||||
|
||||
setContainerId(id: string | undefined) {
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
this.containerId = id;
|
||||
}
|
||||
|
||||
setlazyElement(pageId: string, cache: any) {
|
||||
if (!pageId || !cache) {
|
||||
return;
|
||||
}
|
||||
this.lazyElementsMap[pageId] = cache;
|
||||
}
|
||||
|
||||
getComponents() {
|
||||
return this.components;
|
||||
}
|
||||
|
||||
getComponent(name: string) {
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
return this.components[name];
|
||||
}
|
||||
|
||||
getUtils() {
|
||||
return this.utils;
|
||||
}
|
||||
|
||||
getConstants() {
|
||||
return this.constants;
|
||||
}
|
||||
|
||||
getComponentsMap() {
|
||||
return this.componentsMap;
|
||||
}
|
||||
|
||||
getComponentsMapObj() {
|
||||
const compMapArr = this.getComponentsMap();
|
||||
if (!compMapArr || !Array.isArray(compMapArr)) {
|
||||
return;
|
||||
}
|
||||
const compMapObj: any = {};
|
||||
compMapArr.forEach((item: IComponentMap) => {
|
||||
if (!item || !item.componentName) {
|
||||
return;
|
||||
}
|
||||
compMapObj[item.componentName] = item;
|
||||
});
|
||||
return compMapObj;
|
||||
}
|
||||
|
||||
getLayoutConfig() {
|
||||
return this.layout;
|
||||
}
|
||||
|
||||
getRouterConfig() {
|
||||
return this.routes;
|
||||
}
|
||||
|
||||
getHistory() {
|
||||
return this.history;
|
||||
}
|
||||
|
||||
getContainerId() {
|
||||
return this.containerId;
|
||||
}
|
||||
|
||||
getlazyElement(pageId: string) {
|
||||
if (!pageId) {
|
||||
return;
|
||||
}
|
||||
return this.lazyElementsMap[pageId];
|
||||
}
|
||||
}
|
||||
80
packages/runtime/src/core/provider/react/index.ts
Normal file
80
packages/runtime/src/core/provider/react/index.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import { createElement, ReactElement } from 'react';
|
||||
import { Router } from '@ali/recore';
|
||||
import contribution from '../../contribution';
|
||||
import Provider from '../base';
|
||||
import LazyComponent from './lazy-component';
|
||||
|
||||
export default class ReactProvider extends Provider {
|
||||
// 定制构造根组件的逻辑,如切换路由机制
|
||||
createApp() {
|
||||
const routerConfig = this.getRouterConfig();
|
||||
if (!routerConfig) {
|
||||
return;
|
||||
}
|
||||
const routes: Array<{ path: string; children: any; exact: boolean; keepAlive: boolean }> = [];
|
||||
let homePageId = '';
|
||||
Object.keys(routerConfig).forEach((pageId: string, idx: number) => {
|
||||
if (!pageId) {
|
||||
return;
|
||||
}
|
||||
const path = routerConfig[pageId];
|
||||
if (idx === 0 || path === '/') {
|
||||
homePageId = pageId;
|
||||
}
|
||||
routes.push({
|
||||
path,
|
||||
children: (props: any) => this.getLazyComponent(pageId, props),
|
||||
exact: true,
|
||||
keepAlive: true,
|
||||
});
|
||||
});
|
||||
if (homePageId) {
|
||||
routes.push({
|
||||
path: '**',
|
||||
children: (props: any) => this.getLazyComponent(homePageId, { ...props }),
|
||||
exact: true,
|
||||
keepAlive: true,
|
||||
});
|
||||
}
|
||||
const RouterView = (props: any) => {
|
||||
return createElement(Router as any, {
|
||||
routes,
|
||||
components: this.getComponents(),
|
||||
utils: this.getUtils(),
|
||||
componentsMap: this.getComponentsMapObj(),
|
||||
...props,
|
||||
});
|
||||
};
|
||||
let App;
|
||||
const layoutConfig = this.getLayoutConfig();
|
||||
if (!layoutConfig || !layoutConfig.componentName) {
|
||||
App = (props: any) => createElement(RouterView, { ...props });
|
||||
return App;
|
||||
}
|
||||
const { componentName: layoutName, props: layoutProps } = layoutConfig;
|
||||
const Layout = contribution.getLayout(layoutName);
|
||||
if (Layout) {
|
||||
App = (props: any) => createElement(Layout, layoutProps, RouterView({ props }));
|
||||
} else {
|
||||
App = (props: any) => createElement(RouterView, props);
|
||||
}
|
||||
return App;
|
||||
}
|
||||
|
||||
getLazyComponent(pageId: string, props: any): ReactElement | null {
|
||||
if (!pageId) {
|
||||
return null;
|
||||
}
|
||||
if (this.getlazyElement(pageId)) {
|
||||
return this.getlazyElement(pageId);
|
||||
} else {
|
||||
const lazyElement = createElement(LazyComponent as any, {
|
||||
getPageData: async () => await this.getPageData(pageId),
|
||||
key: pageId,
|
||||
...props,
|
||||
});
|
||||
this.setlazyElement(pageId, lazyElement);
|
||||
return lazyElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
import { Component, createElement } from 'react';
|
||||
import boot from './boot';
|
||||
import contribution from '../../contribution';
|
||||
|
||||
interface IProps {
|
||||
getPageData: () => any;
|
||||
@ -29,8 +29,8 @@ export default class LazyComponent extends Component<IProps, IState> {
|
||||
render() {
|
||||
const { getPageData, ...restProps } = this.props;
|
||||
const { schema } = this.state;
|
||||
const Renderer = boot.getRenderer();
|
||||
const Loading = boot.getLoading();
|
||||
const Renderer = contribution.getRenderer();
|
||||
const Loading = contribution.getLoading();
|
||||
if (!Renderer || !schema) {
|
||||
if (!Loading) {
|
||||
return null;
|
||||
66
packages/runtime/src/core/run.ts
Normal file
66
packages/runtime/src/core/run.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { ReactType } from 'react';
|
||||
import { runApp } from '@ali/recore';
|
||||
import { HashHistoryBuildOptions, BrowserHistoryBuildOptions, MemoryHistoryBuildOptions } from '@recore/history';
|
||||
import contribution from './contribution';
|
||||
|
||||
export type HistoryOptions = {
|
||||
mode?: HistoryMode;
|
||||
} & (HashHistoryBuildOptions | BrowserHistoryBuildOptions | MemoryHistoryBuildOptions);
|
||||
|
||||
export interface IComponents {
|
||||
[key: string]: ReactType;
|
||||
}
|
||||
|
||||
export interface IUtils {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export type HistoryMode = 'browser' | 'hash';
|
||||
|
||||
export interface IAppConfig {
|
||||
history?: HistoryMode;
|
||||
components?: IComponents;
|
||||
utils?: IUtils;
|
||||
containerId?: string;
|
||||
}
|
||||
|
||||
export interface IRecoreAppConfig {
|
||||
history?: HistoryMode;
|
||||
globalComponents?: IComponents;
|
||||
globalUtils?: IUtils;
|
||||
containerId?: string;
|
||||
}
|
||||
|
||||
function transformConfig(config: IAppConfig | (() => IAppConfig)): IRecoreAppConfig {
|
||||
if (!config) {
|
||||
return {};
|
||||
}
|
||||
if (typeof config === 'function') {
|
||||
config = config();
|
||||
}
|
||||
return {
|
||||
history: config.history,
|
||||
globalComponents: config.components,
|
||||
globalUtils: config.utils,
|
||||
containerId: config.containerId,
|
||||
};
|
||||
}
|
||||
|
||||
export default function run(config?: IAppConfig | (() => IAppConfig)) {
|
||||
const provider = contribution.getProvider();
|
||||
if (config) {
|
||||
config = transformConfig(config);
|
||||
const App = provider.createApp();
|
||||
runApp(App, config);
|
||||
return;
|
||||
}
|
||||
const promise = provider.async();
|
||||
promise.then((config: IAppConfig) => {
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
const App = provider.createApp();
|
||||
config = transformConfig(config);
|
||||
runApp(App, config);
|
||||
});
|
||||
}
|
||||
@ -1,5 +1,8 @@
|
||||
import { navigator, Router, runApp as run } from '@ali/recore';
|
||||
import boot from './boot';
|
||||
import Provider from './provider';
|
||||
import { navigator, Router } from '@ali/recore';
|
||||
import run from './core/run';
|
||||
import contribution from './core/contribution';
|
||||
import Provider from './core/provider/base';
|
||||
import ReactProvider from './core/provider/react';
|
||||
import * as Utils from './utils';
|
||||
|
||||
export { run, Router, boot, Provider, navigator };
|
||||
export { run, Router, contribution, Provider, ReactProvider, navigator, Utils };
|
||||
|
||||
@ -1,90 +0,0 @@
|
||||
import { createElement, ReactElement, ReactType } from 'react';
|
||||
import LazyComponent from './lazyComponent';
|
||||
|
||||
export interface IAppData {
|
||||
App: any;
|
||||
config: object;
|
||||
}
|
||||
|
||||
interface IComponentsMap {
|
||||
[key: string]: ReactType;
|
||||
}
|
||||
|
||||
interface IUtilsMap {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
type HistoryMode = 'browser' | 'hash';
|
||||
|
||||
export interface IAppConfig {
|
||||
history?: HistoryMode;
|
||||
globalComponents?: IComponentsMap;
|
||||
globalUtils?: IUtilsMap;
|
||||
containerId?: string;
|
||||
}
|
||||
|
||||
export default class Provider {
|
||||
globalComponents: any = {};
|
||||
globalUtils: any = {};
|
||||
routerConfig: { [key: string]: string } = {};
|
||||
layout: { componentName: string; props: any } | null = null;
|
||||
componentsMap: any = null;
|
||||
private lazyElementsMap: { [key: string]: any } = {};
|
||||
|
||||
constructor() {
|
||||
this.init();
|
||||
}
|
||||
|
||||
create(appkey: string): Promise<IAppData> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const config = await this.getAppData(appkey);
|
||||
const App = this.createApp();
|
||||
resolve({
|
||||
App,
|
||||
config,
|
||||
});
|
||||
} catch (err) {
|
||||
reject(err.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async init() {
|
||||
console.log('init');
|
||||
}
|
||||
|
||||
async getAppData(appkey: string, restOptions?: any): Promise<object> {
|
||||
console.log('getAppData');
|
||||
return {};
|
||||
}
|
||||
|
||||
async getPageData(pageId: string, restOptions?: any): Promise<any> {
|
||||
console.log('getPageData');
|
||||
return;
|
||||
}
|
||||
|
||||
getLazyComponent(pageId: string, props: any): ReactElement | null {
|
||||
if (!pageId) {
|
||||
return null;
|
||||
}
|
||||
if (this.lazyElementsMap[pageId]) {
|
||||
console.log('缓存');
|
||||
return this.lazyElementsMap[pageId];
|
||||
} else {
|
||||
const lazyElement = createElement(LazyComponent as any, {
|
||||
getPageData: async () => await this.getPageData(pageId),
|
||||
key: pageId,
|
||||
...props,
|
||||
});
|
||||
this.lazyElementsMap[pageId] = lazyElement;
|
||||
console.log('新组件');
|
||||
return lazyElement;
|
||||
}
|
||||
}
|
||||
|
||||
createApp() {
|
||||
console.log('createApp');
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1,17 +1,25 @@
|
||||
function isESModule(obj) {
|
||||
function isESModule(obj: any): obj is { [key: string]: any } {
|
||||
return obj && obj.__esModule;
|
||||
}
|
||||
|
||||
function getSubComponent(library, paths) {
|
||||
function accessLibrary(library: string | object) {
|
||||
if (typeof library !== 'string') {
|
||||
return library;
|
||||
}
|
||||
|
||||
return (window as any)[library];
|
||||
}
|
||||
|
||||
function getSubComponent(library: any, paths: string[]) {
|
||||
const l = paths.length;
|
||||
if (l < 1 || !library) {
|
||||
return library;
|
||||
}
|
||||
let i = 0;
|
||||
let component;
|
||||
let component: any;
|
||||
while (i < l) {
|
||||
const key = paths[i];
|
||||
let ex;
|
||||
const key = paths[i]!;
|
||||
let ex: any;
|
||||
try {
|
||||
component = library[key];
|
||||
} catch (e) {
|
||||
@ -32,15 +40,7 @@ function getSubComponent(library, paths) {
|
||||
return component;
|
||||
}
|
||||
|
||||
function accessLibrary(library) {
|
||||
if (typeof library !== 'string') {
|
||||
return library;
|
||||
}
|
||||
|
||||
return window[library];
|
||||
}
|
||||
|
||||
function findComponent(libraryMap, componentName, npm) {
|
||||
function findComponent(libraryMap: LibraryMap, componentName: string, npm?: NpmInfo) {
|
||||
if (!npm) {
|
||||
return accessLibrary(componentName);
|
||||
}
|
||||
@ -62,10 +62,37 @@ function findComponent(libraryMap, componentName, npm) {
|
||||
return getSubComponent(library, paths);
|
||||
}
|
||||
|
||||
export function buildComponents(libraryMap, componentsMap) {
|
||||
const components = {};
|
||||
export interface LibraryMap {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
export interface NpmInfo {
|
||||
componentName?: string;
|
||||
package: string;
|
||||
version: string;
|
||||
destructuring?: boolean;
|
||||
exportName?: string;
|
||||
subName?: string;
|
||||
main?: string;
|
||||
}
|
||||
|
||||
export function buildComponents(
|
||||
libraryMap: LibraryMap,
|
||||
componentsMap: { [componentName: string]: NpmInfo } | NpmInfo[],
|
||||
) {
|
||||
const components: any = {};
|
||||
if (componentsMap && Array.isArray(componentsMap)) {
|
||||
const compMapObj: any = {};
|
||||
componentsMap.forEach((item: NpmInfo) => {
|
||||
if (!item || !item.componentName) {
|
||||
return;
|
||||
}
|
||||
compMapObj[item.componentName] = item;
|
||||
});
|
||||
componentsMap = compMapObj;
|
||||
}
|
||||
Object.keys(componentsMap).forEach((componentName) => {
|
||||
const component = findComponent(libraryMap, componentName, componentsMap[componentName]);
|
||||
const component = findComponent(libraryMap, componentName, (componentsMap as any)[componentName]);
|
||||
if (component) {
|
||||
components[componentName] = component;
|
||||
}
|
||||
1
packages/runtime/src/utils/index.ts
Normal file
1
packages/runtime/src/utils/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './assets';
|
||||
Loading…
x
Reference in New Issue
Block a user