mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-14 04:43:14 +00:00
feat: complet preview
This commit is contained in:
parent
904bd3d4a2
commit
56c16ffa5c
@ -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": {
|
||||
|
||||
@ -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"
|
||||
},
|
||||
|
||||
20
packages/demo/public/preview.html
Normal file
20
packages/demo/public/preview.html
Normal file
@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge,chrome=1" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>LowCodeEngine DEMO</title>
|
||||
<script src="https://g.alicdn.com/code/lib/react/16.9.0/umd/react.development.js"></script>
|
||||
<script src="https://g.alicdn.com/code/lib/react-dom/16.9.0/umd/react-dom.development.js"></script>
|
||||
<script src="https://g.alicdn.com/code/lib/prop-types/15.7.2/prop-types.js"></script>
|
||||
<script> React.PropTypes = PropTypes; </script>
|
||||
<script src="https://g.alicdn.com/mylib/moment/2.24.0/min/moment.min.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="https://alifd.alicdn.com/npm/@alifd/next/1.11.6/next.min.css">
|
||||
<script src="https://unpkg.alibaba-inc.com/@alifd/next@1.18.17/dist/next.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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);
|
||||
});
|
||||
21
packages/demo/src/preview/config/app.js
Normal file
21
packages/demo/src/preview/config/app.js
Normal file
@ -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: [],
|
||||
};
|
||||
12
packages/demo/src/preview/config/components.js
Normal file
12
packages/demo/src/preview/config/components.js
Normal file
@ -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,
|
||||
}
|
||||
53
packages/demo/src/preview/config/componentsMap.js
Normal file
53
packages/demo/src/preview/config/componentsMap.js
Normal file
@ -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',
|
||||
},
|
||||
};
|
||||
1
packages/demo/src/preview/config/utils.js
Normal file
1
packages/demo/src/preview/config/utils.js
Normal file
@ -0,0 +1 @@
|
||||
export default {};
|
||||
27
packages/demo/src/preview/layouts/BasicLayout/index.js
Normal file
27
packages/demo/src/preview/layouts/BasicLayout/index.js
Normal file
@ -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 }) => (
|
||||
<Shell className="basic-shell" type="dark" 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' }} />
|
||||
</Shell.Navigation>
|
||||
<Shell.Action>
|
||||
<Icon type="ic_tongzhi" />
|
||||
<img src="https://img.alicdn.com/tfs/TB1.ZBecq67gK0jSZFHXXa9jVXa-904-826.png" className="avatar" alt="用户头像" />
|
||||
<span style={{ marginLeft: 10 }}>MyName</span>
|
||||
</Shell.Action>
|
||||
|
||||
<Shell.Content className="content">{children}</Shell.Content>
|
||||
|
||||
<Shell.Footer>
|
||||
<span>Alibaba Fusion</span>
|
||||
<span>@ 2019 Alibaba Piecework 版权所有</span>
|
||||
</Shell.Footer>
|
||||
</Shell>
|
||||
);
|
||||
24
packages/demo/src/preview/layouts/BasicLayout/index.less
Normal file
24
packages/demo/src/preview/layouts/BasicLayout/index.less
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
packages/demo/src/preview/plugins/loading/deep/index.less
Normal file
11
packages/demo/src/preview/plugins/loading/deep/index.less
Normal file
@ -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;
|
||||
}
|
||||
3
packages/demo/src/preview/plugins/loading/deep/index.tsx
Normal file
3
packages/demo/src/preview/plugins/loading/deep/index.tsx
Normal file
@ -0,0 +1,3 @@
|
||||
import './index.less';
|
||||
|
||||
export default () => <div className="recore-loading" />;
|
||||
@ -0,0 +1,9 @@
|
||||
.fusion-loading {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-top: -24px;
|
||||
margin-left: -24px;
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
import { Loading } from '@alifd/next';
|
||||
import './index.less';
|
||||
|
||||
export default () => <Loading tip="加载中..." className="fusion-loading" />;
|
||||
117
packages/demo/src/preview/plugins/provider.ts
Normal file
117
packages/demo/src/preview/plugins/provider.ts
Normal file
@ -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<any> {
|
||||
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();
|
||||
74
packages/demo/src/preview/plugins/utils.js
Normal file
74
packages/demo/src/preview/plugins/utils.js
Normal file
@ -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;
|
||||
}
|
||||
34
packages/runtime/index.d.ts
vendored
34
packages/runtime/index.d.ts
vendored
@ -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;
|
||||
}
|
||||
export function run(Component: any, config?: AppConfig | (() => AppConfig)): any;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user