diff --git a/packages/demo/build.json b/packages/demo/build.json
index 623ad7a36..02e5fccff 100644
--- a/packages/demo/build.json
+++ b/packages/demo/build.json
@@ -1,7 +1,8 @@
{
"entry": {
"index": "src/index.jsx",
- "react-simulator-renderer": "../react-simulator-renderer/src/index.js"
+ "react-simulator-renderer": "../react-simulator-renderer/src/index.js",
+ "preview": "src/preview.js"
},
"vendor": false,
"devServer": {
diff --git a/packages/demo/package.json b/packages/demo/package.json
index 2efe9ba32..b35294f08 100644
--- a/packages/demo/package.json
+++ b/packages/demo/package.json
@@ -10,11 +10,14 @@
"@ali/lowcode-editor-skeleton": "^0.8.0",
"@ali/lowcode-plugin-components-pane": "^0.8.0",
"@ali/lowcode-plugin-designer": "^0.8.0",
- "@ali/lowcode-plugin-logo": "^0.8.0",
- "@ali/lowcode-plugin-save": "^0.8.0",
+ "@ali/lowcode-plugin-sample-logo": "^0.8.0",
+ "@ali/lowcode-plugin-sample-save": "^0.8.0",
"@ali/lowcode-plugin-undo-redo": "^0.8.0",
+ "@ali/lowcode-runtime": "^0.8.0",
+ "@ali/lowcode-react-renderer": "^0.8.0",
"@alife/theme-lowcode-dark": "^0.1.0",
"@alife/theme-lowcode-light": "^0.1.0",
+ "@alifd/next": "^1.19.21",
"react": "^16.8.1",
"react-dom": "^16.8.1"
},
diff --git a/packages/demo/public/preview.html b/packages/demo/public/preview.html
new file mode 100644
index 000000000..34c8cf86c
--- /dev/null
+++ b/packages/demo/public/preview.html
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+ LowCodeEngine DEMO
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/demo/src/config/components.js b/packages/demo/src/designer/config/components.js
similarity index 100%
rename from packages/demo/src/config/components.js
rename to packages/demo/src/designer/config/components.js
diff --git a/packages/demo/src/config/constants.js b/packages/demo/src/designer/config/constants.js
similarity index 100%
rename from packages/demo/src/config/constants.js
rename to packages/demo/src/designer/config/constants.js
diff --git a/packages/demo/src/config/skeleton.js b/packages/demo/src/designer/config/skeleton.js
similarity index 100%
rename from packages/demo/src/config/skeleton.js
rename to packages/demo/src/designer/config/skeleton.js
diff --git a/packages/demo/src/config/theme.scss b/packages/demo/src/designer/config/theme.scss
similarity index 100%
rename from packages/demo/src/config/theme.scss
rename to packages/demo/src/designer/config/theme.scss
diff --git a/packages/demo/src/config/utils.js b/packages/demo/src/designer/config/utils.js
similarity index 100%
rename from packages/demo/src/config/utils.js
rename to packages/demo/src/designer/config/utils.js
diff --git a/packages/demo/src/index.jsx b/packages/demo/src/index.jsx
index f666fe2c5..342bb86fb 100644
--- a/packages/demo/src/index.jsx
+++ b/packages/demo/src/index.jsx
@@ -2,12 +2,12 @@ import React from 'react';
import ReactDOM from 'react-dom';
import Skeleton from '@ali/lowcode-editor-skeleton';
import { registerSetters } from '@ali/lowcode-setters';
-import config from './config/skeleton';
-import components from './config/components';
-import utils from './config/utils';
+import config from './designer/config/skeleton';
+import components from './designer/config/components';
+import utils from './designer/config/utils';
import './global.scss';
-import './config/theme.scss';
+import './designer/config/theme.scss';
registerSetters();
diff --git a/packages/demo/src/preview.js b/packages/demo/src/preview.js
index e69de29bb..3ed2c2eca 100644
--- a/packages/demo/src/preview.js
+++ b/packages/demo/src/preview.js
@@ -0,0 +1,22 @@
+import { run, Boot } from '@ali/lowcode-runtime';
+import Renderer from '@ali/lowcode-react-renderer';
+import FusionLoading from './preview/plugins/loading/fusion';
+import BasicLayout from './preview/layouts/BasicLayout';
+import provider from './preview/plugins/provider';
+
+// 注册渲染模块
+Boot.registerRenderer(Renderer);
+
+// 注册布局组件,可注册多个
+Boot.registerLayout('BasicLayout', BasicLayout);
+
+// 注册页面 loading
+Boot.registerLoading(FusionLoading);
+
+const appProvider = provider.create('lowcode_demo'); // 入参为应用唯一标识
+
+// 异步加载应用配置
+appProvider.then(({ App, config }) => {
+ // 启动应用
+ run(App, config);
+});
diff --git a/packages/demo/src/preview/config/app.js b/packages/demo/src/preview/config/app.js
new file mode 100644
index 000000000..c1ad5b57c
--- /dev/null
+++ b/packages/demo/src/preview/config/app.js
@@ -0,0 +1,21 @@
+export default {
+ sdkVersion: '1.0.3',
+ historyMode: 'hash', // 浏览器路由:brower 哈希路由:hash
+ constainerId: 'app',
+ layout: {
+ componentName: 'BasicLayout',
+ props: {
+ name: '低代码引擎预览 demo',
+ logo: {
+ src: 'https://img.alicdn.com/tfs/TB1kAfWyrY1gK0jSZTEXXXDQVXa-75-33.png',
+ width: 40,
+ height: 20,
+ },
+ },
+ },
+ theme: {
+ package: '@alife/theme-fusion',
+ version: '^0.1.0',
+ },
+ compDependencies: [],
+};
diff --git a/packages/demo/src/preview/config/components.js b/packages/demo/src/preview/config/components.js
new file mode 100644
index 000000000..91aef7993
--- /dev/null
+++ b/packages/demo/src/preview/config/components.js
@@ -0,0 +1,12 @@
+/**
+ * 内置组件
+ */
+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,
+}
diff --git a/packages/demo/src/preview/config/componentsMap.js b/packages/demo/src/preview/config/componentsMap.js
new file mode 100644
index 000000000..a9bb73edb
--- /dev/null
+++ b/packages/demo/src/preview/config/componentsMap.js
@@ -0,0 +1,53 @@
+export default {
+ Button: {
+ package: '@alifd/next',
+ version: '1.19.18',
+ destructuring: true,
+ exportName: 'Button',
+ },
+ 'Button.Group': {
+ package: '@alifd/next',
+ version: '1.19.18',
+ destructuring: true,
+ exportName: 'Button',
+ subName: 'Group',
+ },
+ Input: {
+ package: '@alifd/next',
+ version: '1.19.18',
+ destructuring: true,
+ exportName: 'Input',
+ },
+ Form: {
+ package: '@alifd/next',
+ version: '1.19.18',
+ destructuring: true,
+ exportName: 'Form',
+ },
+ 'Form.Item': {
+ package: '@alifd/next',
+ version: '1.19.18',
+ destructuring: true,
+ exportName: 'Form',
+ subName: 'Item',
+ },
+ NumberPicker: {
+ package: '@alifd/next',
+ version: '1.19.18',
+ destructuring: true,
+ exportName: 'NumberPicker',
+ },
+ Select: {
+ package: '@alifd/next',
+ version: '1.19.18',
+ destructuring: true,
+ exportName: 'Select',
+ },
+ 'Select.Option': {
+ package: '@alifd/next',
+ version: '1.19.18',
+ destructuring: true,
+ exportName: 'Select',
+ subName: 'Option',
+ },
+};
diff --git a/packages/demo/src/preview/config/utils.js b/packages/demo/src/preview/config/utils.js
new file mode 100644
index 000000000..ff8b4c563
--- /dev/null
+++ b/packages/demo/src/preview/config/utils.js
@@ -0,0 +1 @@
+export default {};
diff --git a/packages/demo/src/preview/layouts/BasicLayout/index.js b/packages/demo/src/preview/layouts/BasicLayout/index.js
new file mode 100644
index 000000000..6a51fdc75
--- /dev/null
+++ b/packages/demo/src/preview/layouts/BasicLayout/index.js
@@ -0,0 +1,27 @@
+import { Search, Icon, Shell } from '@alifd/next';
+import './index.less';
+
+// eslint-disable-next-line react/prop-types
+export default ({ name, children, logo }) => (
+
+
+
+ {name}
+
+
+
+
+
+
+
+ MyName
+
+
+ {children}
+
+
+ Alibaba Fusion
+ @ 2019 Alibaba Piecework 版权所有
+
+
+);
diff --git a/packages/demo/src/preview/layouts/BasicLayout/index.less b/packages/demo/src/preview/layouts/BasicLayout/index.less
new file mode 100644
index 000000000..000abd1c8
--- /dev/null
+++ b/packages/demo/src/preview/layouts/BasicLayout/index.less
@@ -0,0 +1,24 @@
+@header-height: 52px;
+
+.avatar {
+ width: 24px;
+ height: 24px;
+ border-radius: 50%;
+ vertical-align: middle;
+}
+
+.basic-shell {
+ min-height: 100vh;
+ .next-shell-header {
+ height: @header-height;
+ }
+ .next-shell-main {
+ flex: 1;
+ display: flex;
+ flex-flow: column;
+ min-height: calc(100% - @header-height);
+ .next-shell-sub-main {
+ flex: 1;
+ }
+ }
+}
diff --git a/packages/demo/src/preview/plugins/loading/deep/index.less b/packages/demo/src/preview/plugins/loading/deep/index.less
new file mode 100644
index 000000000..2b59ea5ef
--- /dev/null
+++ b/packages/demo/src/preview/plugins/loading/deep/index.less
@@ -0,0 +1,11 @@
+.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;
+}
diff --git a/packages/demo/src/preview/plugins/loading/deep/index.tsx b/packages/demo/src/preview/plugins/loading/deep/index.tsx
new file mode 100644
index 000000000..02b2fc692
--- /dev/null
+++ b/packages/demo/src/preview/plugins/loading/deep/index.tsx
@@ -0,0 +1,3 @@
+import './index.less';
+
+export default () => ;
diff --git a/packages/demo/src/preview/plugins/loading/fusion/index.less b/packages/demo/src/preview/plugins/loading/fusion/index.less
new file mode 100644
index 000000000..0e53165cb
--- /dev/null
+++ b/packages/demo/src/preview/plugins/loading/fusion/index.less
@@ -0,0 +1,9 @@
+.fusion-loading {
+ width: 48px;
+ height: 48px;
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ margin-top: -24px;
+ margin-left: -24px;
+}
diff --git a/packages/demo/src/preview/plugins/loading/fusion/index.tsx b/packages/demo/src/preview/plugins/loading/fusion/index.tsx
new file mode 100644
index 000000000..e61bfd9f0
--- /dev/null
+++ b/packages/demo/src/preview/plugins/loading/fusion/index.tsx
@@ -0,0 +1,4 @@
+import { Loading } from '@alifd/next';
+import './index.less';
+
+export default () => ;
diff --git a/packages/demo/src/preview/plugins/provider.ts b/packages/demo/src/preview/plugins/provider.ts
new file mode 100644
index 000000000..d654af45e
--- /dev/null
+++ b/packages/demo/src/preview/plugins/provider.ts
@@ -0,0 +1,117 @@
+import { createElement } from 'react';
+import { Provider, Boot, Router, navigator } 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';
+
+// 定制加载应用配置的逻辑
+class PreviewProvider extends Provider {
+ // 定制获取、处理应用配置(组件、插件、路由模式、布局等)的逻辑
+ async getAppData(appkey: string, restOptions?: any): Promise {
+ const { historyMode, layout, constainerId } = appConfig;
+ const appSchemaStr: any = localStorage.getItem('lce-dev-store');
+ const appSchema = JSON.parse(appSchemaStr || '');
+ const history = {
+ mode: historyMode || 'hash',
+ basement: '/',
+ };
+ this.layout = layout;
+ const routes: any = {};
+ appSchema.componentsTree.forEach((page: any, idx: number) => {
+ 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,
+ };
+ }
+
+ // 定制获取、处理页面 schema 的逻辑
+ async getPageData(pageId: string, restOptions?: any) {
+ const appSchemaStr = localStorage.getItem('lce-dev-store');
+ const appSchema = JSON.parse(appSchemaStr || '');
+ const idx = appSchema.componentsTree.findIndex(
+ (page: any, idx: number) => (page.fileName || `page${idx}`) === pageId,
+ );
+ 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,
+ onNavChange: ({ selectedKey }: any) => {
+ navigator.goto(`/${selectedKey}`);
+ },
+ },
+ RouterView({ props }),
+ );
+ } else {
+ App = (props: any) => createElement(RouterView, props);
+ }
+ return App;
+ }
+}
+
+export default new PreviewProvider();
diff --git a/packages/demo/src/preview/plugins/utils.js b/packages/demo/src/preview/plugins/utils.js
new file mode 100644
index 000000000..b65d4864e
--- /dev/null
+++ b/packages/demo/src/preview/plugins/utils.js
@@ -0,0 +1,74 @@
+function isESModule(obj) {
+ return obj && obj.__esModule;
+}
+
+function getSubComponent(library, paths) {
+ const l = paths.length;
+ if (l < 1 || !library) {
+ return library;
+ }
+ let i = 0;
+ let component;
+ while (i < l) {
+ const key = paths[i];
+ let ex;
+ try {
+ component = library[key];
+ } catch (e) {
+ ex = e;
+ component = null;
+ }
+ if (i === 0 && component == null && key === 'default') {
+ if (ex) {
+ return l === 1 ? library : null;
+ }
+ component = library;
+ } else if (component == null) {
+ return null;
+ }
+ library = component;
+ i++;
+ }
+ return component;
+}
+
+function accessLibrary(library) {
+ if (typeof library !== 'string') {
+ return library;
+ }
+
+ return window[library];
+}
+
+function findComponent(libraryMap, componentName, npm) {
+ if (!npm) {
+ return accessLibrary(componentName);
+ }
+ // libraryName the key access to global
+ // export { exportName } from xxx exportName === global.libraryName.exportName
+ // export exportName from xxx exportName === global.libraryName.default || global.libraryName
+ // export { exportName as componentName } from package
+ // if exportName == null exportName === componentName;
+ // const componentName = exportName.subName, if exportName empty subName donot use
+ const exportName = npm.exportName || npm.componentName || componentName;
+ const libraryName = libraryMap[npm.package] || exportName;
+ const library = accessLibrary(libraryName);
+ const paths = npm.exportName && npm.subName ? npm.subName.split('.') : [];
+ if (npm.destructuring) {
+ paths.unshift(exportName);
+ } else if (isESModule(library)) {
+ paths.unshift('default');
+ }
+ return getSubComponent(library, paths);
+}
+
+export function buildComponents(libraryMap, componentsMap) {
+ const components = {};
+ Object.keys(componentsMap).forEach((componentName) => {
+ const component = findComponent(libraryMap, componentName, componentsMap[componentName]);
+ if (component) {
+ components[componentName] = component;
+ }
+ });
+ return components;
+}
diff --git a/packages/runtime/index.d.ts b/packages/runtime/index.d.ts
index 5988aa400..6e3013290 100644
--- a/packages/runtime/index.d.ts
+++ b/packages/runtime/index.d.ts
@@ -1,25 +1,19 @@
import { ReactType } from 'react';
+type HistoryMode = 'browser' | 'hash';
-export as namespace LowCodeEngineRuntime;
-export = LowCodeEngineRuntime;
+interface ComponentsMap {
+ [key: string]: ReactType;
+}
-declare module LowCodeEngineRuntime {
- type HistoryMode = 'browser' | 'hash';
+interface UtilsMap {
+ [key: string]: any;
+}
- interface ComponentsMap {
- [key: string]: ReactType;
- }
+export interface AppConfig {
+ history?: HistoryMode;
+ globalComponents?: ComponentsMap;
+ globalUtils?: UtilsMap;
+ containerId?: string;
+}
- interface UtilsMap {
- [key: string]: any;
- }
-
- interface AppConfig {
- history?: HistoryMode;
- globalComponents?: ComponentsMap;
- globalUtils?: UtilsMap;
- containerId?: string;
- }
-
- function runApp(Component: any, config?: AppConfig | (() => AppConfig), exposeModule?: boolean): any;
-}
\ No newline at end of file
+export function run(Component: any, config?: AppConfig | (() => AppConfig)): any;