feat: 重构renderer

This commit is contained in:
wuyue.xht 2021-01-29 12:36:31 +08:00
parent 71b55298ef
commit 630d3201ff
71 changed files with 4176 additions and 4957 deletions

View File

@ -32,28 +32,9 @@
"build": "build-scripts build" "build": "build-scripts build"
}, },
"dependencies": { "dependencies": {
"@ali/b3-one": "^0.0.17", "@ali/lowcode-renderer-core": "^1.0.33",
"@ali/bzb-request": "2.6.1",
"@ali/lib-mtop": "^2.5.1",
"@ali/lowcode-datasource-engine": "^1.0.22",
"@ali/lowcode-utils": "^1.0.33", "@ali/lowcode-utils": "^1.0.33",
"@ali/ui-table": "^1.0.1-beta.6", "rax-find-dom-node": "^1.0.1"
"classnames": "^2.2.6",
"debug": "^4.1.1",
"events": "^3.0.0",
"fetch-jsonp": "^1.1.3",
"fs-extra": "^9.0.1",
"intl-messageformat": "^9.3.1",
"jsonuri": "^2.1.2",
"keymaster": "^1.6.2",
"lodash": "^4.17.11",
"moment": "^2.24.0",
"rax-find-dom-node": "^1.0.1",
"rax-text": "^1.1.6",
"rax-view": "^1.0.0",
"react-is": "^16.10.1",
"serialize-javascript": "^1.7.0",
"whatwg-fetch": "^3.0.0"
}, },
"devDependencies": { "devDependencies": {
"@alib/build-scripts": "^0.1.0", "@alib/build-scripts": "^0.1.0",

View File

@ -1,11 +0,0 @@
import { createElement, PureComponent } from 'rax';
export default class DivView extends PureComponent {
static displayName = 'Div';
static version = '0.0.0';
render(): any {
return createElement('div', this.props);
}
}

View File

@ -1,24 +0,0 @@
// @ts-nocheck
import { Component } from 'rax';
import './index.css';
export default class VisualDom extends Component {
static displayName = 'VisualDom';
static defaultProps = {
children: null,
};
render() {
const { children, title, label, text, __componentName } = this.props;
return (
<div className="visual-dom">
<div className="panel-container">
<span className="title">{title || label || text || __componentName}</span>
<div className="content">{children}</div>
</div>
</div>
);
}
}

View File

@ -1,4 +0,0 @@
import { createContext } from 'rax';
const context = createContext({});
export default context;

View File

@ -1,634 +0,0 @@
// @ts-nocheck
import { Component, createElement } from 'rax';
import PropTypes from 'prop-types';
import Debug from 'debug';
import classnames from 'classnames';
import { create as createDataSourceEngine } from '@ali/lowcode-datasource-engine/interpret';
import DataHelper from '../utils/dataHelper';
import {
forEach,
getValue,
parseData,
parseExpression,
isEmpty,
isSchema,
isFileSchema,
isJSExpression,
isJSSlot,
isJSFunction,
transformArrayToMap,
checkPropTypes,
generateI18n,
acceptsRef,
getFileCssName,
} from '../utils';
import VisualDom from '../comp/visualDom';
import Div from '../comp/Div';
import AppContext from '../context/appContext';
import compWrapper from '../hoc/compWrapper';
const debug = Debug('engine:base');
const DESIGN_MODE = {
EXTEND: 'extend',
BORDER: 'border',
PREVIEW: 'preview',
};
const OVERLAY_LIST = ['Dialog', 'Overlay'];
let scopeIdx = 0;
export default class BaseEngine extends Component {
static dislayName = 'base-engine';
static propTypes = {
locale: PropTypes.string,
messages: PropTypes.object,
__appHelper: PropTypes.object,
__components: PropTypes.object,
// eslint-disable-next-line react/no-unused-prop-types
__componentsMap: PropTypes.object,
__ctx: PropTypes.object,
__schema: PropTypes.object,
};
static defaultProps = {
__schema: {},
};
static contextType = AppContext;
constructor(props, context) {
super(props, context);
this.appHelper = props.__appHelper;
this.__compScopes = {};
this.__instanceMap = {};
const { locale, messages } = props;
this.i18n = generateI18n(locale, messages);
this.__bindCustomMethods(props);
}
async getSnapshotBeforeUpdate() {
this.__setLifeCycleMethods('getSnapshotBeforeUpdate', arguments);
}
async componentDidMount() {
this.reloadDataSource();
this.__setLifeCycleMethods('componentDidMount', arguments);
}
async componentDidUpdate() {
this.__setLifeCycleMethods('componentDidUpdate', arguments);
}
async componentWillUnmount() {
this.__setLifeCycleMethods('componentWillUnmount', arguments);
}
async componentDidCatch(e) {
this.__setLifeCycleMethods('componentDidCatch', arguments);
console.warn(e);
}
reloadDataSource = () => new Promise((resolve, reject) => {
debug('reload data source');
if (!this.__dataHelper) {
this.__showPlaceholder = false;
return resolve();
}
this.__dataHelper
.getInitData()
.then((res) => {
this.__showPlaceholder = false;
if (isEmpty(res)) {
this.forceUpdate();
return resolve();
}
this.setState(res, resolve);
})
.catch((err) => {
if (this.__showPlaceholder) {
this.__showPlaceholder = false;
this.forceUpdate();
}
reject(err);
});
});
__setLifeCycleMethods = (method, args) => {
const lifeCycleMethods = getValue(this.props.__schema, 'lifeCycles', {});
if (lifeCycleMethods[method]) {
try {
return lifeCycleMethods[method].apply(this, args);
} catch (e) {
console.error(`[${this.props.__schema.componentName}]生命周期${method}出错`, e);
}
}
};
__bindCustomMethods = (props = this.props) => {
const { __schema } = props;
const customMethodsList = Object.keys(__schema.methods || {}) || [];
this.__customMethodsList
&& this.__customMethodsList.forEach((item) => {
if (!customMethodsList.includes(item)) {
delete this[item];
}
});
this.__customMethodsList = customMethodsList;
forEach(__schema.methods, (val, key) => {
this[key] = val.bind(this);
});
};
__generateCtx = (ctx) => {
const { pageContext, compContext } = this.context;
const obj = {
page: pageContext,
component: compContext,
...ctx,
};
forEach(obj, (val, key) => {
this[key] = val;
});
};
__parseData = (data, ctx) => {
const { __ctx } = this.props;
return parseData(data, ctx || __ctx || this);
};
__initDataSource = (props = this.props) => {
const schema = props.__schema || {};
const dataSource = (schema && schema.dataSource) || {};
// requestHandlersMap 存在才走数据源引擎方案
if (props?.__appHelper?.requestHandlersMap) {
const { dataSourceMap, reloadDataSource } = createDataSourceEngine(dataSource, this, {
requestHandlersMap: props.__appHelper.requestHandlersMap,
});
this.dataSourceMap = dataSourceMap;
this.reloadDataSource = () => new Promise((resolve, reject) => {
debug('reload data source');
// this.__showPlaceholder = true;
reloadDataSource().then(() => {
// this.__showPlaceholder = false;
// @TODO 是否需要 forceUpate
resolve();
});
});
} else {
const appHelper = props.__appHelper;
this.__dataHelper = new DataHelper(this, dataSource, appHelper, (config) => this.__parseData(config));
this.dataSourceMap = this.__dataHelper.dataSourceMap;
this.reloadDataSource = () => new Promise((resolve, reject) => {
debug('reload data source');
if (!this.__dataHelper) {
// this.__showPlaceholder = false;
return resolve();
}
this.__dataHelper
.getInitData()
.then((res) => {
// this.__showPlaceholder = false;
if (isEmpty(res)) {
this.forceUpdate();
return resolve();
}
this.setState(res, resolve);
})
.catch((err) => {
if (this.__showPlaceholder) {
this.__showPlaceholder = false;
this.forceUpdate();
}
reject(err);
});
});
}
// 设置容器组件占位若设置占位则在初始异步请求完成之前用loading占位且不渲染容器组件内部内容
// @TODO __showPlaceholder 的逻辑一旦开启就关不掉,先注释掉了
/* if (this.__parseData(schema.props && schema.props.autoLoading)) {
this.__showPlaceholder = (dataSource.list || []).some((item) => !!this.__parseData(item.isInit));
} */
};
__render = () => {
const schema = this.props.__schema;
this.__setLifeCycleMethods('render');
const { engine } = this.context;
if (engine) {
engine.props.onCompGetCtx(schema, this);
// 画布场景才需要每次渲染bind自定义方法
if (engine.props.designMode) {
this.__bindCustomMethods();
this.dataSourceMap = this.__dataHelper && this.__dataHelper.updateConfig(schema.dataSource);
}
}
};
__getRef = (ref) => {
const { engine } = this.context;
const { __schema } = this.props;
engine && engine.props.onCompGetRef(__schema, ref);
this.__ref = ref;
};
__createDom = () => {
const { __schema, __ctx, __components = {} } = this.props;
const self = {};
self.__proto__ = __ctx || this;
return this.__createVirtualDom(__schema.children, self, {
schema: __schema,
Comp: __components[__schema.componentName],
});
};
// 将模型结构转换成react Element
// schema 模型结构
// self 为每个渲染组件构造的上下文self是自上而下继承的
// parentInfo 父组件的信息包含schema和Comp
// idx 若为循环渲染的循环Index
__createVirtualDom = (schema, self, parentInfo, idx) => {
const { engine } = this.context || {};
try {
if (!schema) return null;
if (schema.componentName === 'Text') { // 这个是不是不应该在这里处理
if (typeof schema.props.text === 'string') {
schema = { ...schema };
schema.children = [schema.props.text];
}
}
const { __appHelper: appHelper, __components: components = {} } = this.props || {};
if (isJSExpression(schema)) {
return parseExpression(schema, self);
}
if (isJSSlot(schema)) {
return this.__createVirtualDom(schema.value, self, parentInfo);
}
if (typeof schema === 'string') return schema;
if (typeof schema === 'number' || typeof schema === 'boolean') {
return schema.toString();
}
if (Array.isArray(schema)) {
if (schema.length === 1) return this.__createVirtualDom(schema[0], self, parentInfo);
return schema.map((item, idy) => this.__createVirtualDom(item, self, parentInfo, item && item.__ctx && item.__ctx.lunaKey ? '' : idy));
}
// FIXME
const _children = this.getSchemaChildren(schema);
// 解析占位组件
if (schema.componentName === 'Flagment' && _children) {
const tarChildren = isJSExpression(_children) ? parseExpression(_children, self) : _children;
return this.__createVirtualDom(tarChildren, self, parentInfo);
}
if (schema.$$typeof) {
return schema;
}
if (!isSchema(schema)) return null;
let Comp = components[schema.componentName] || engine.getNotFoundComponent();
if (schema.hidden) {
return null;
}
if (schema.loop != null) {
const loop = parseData(schema.loop, self);
if ((Array.isArray(loop) && loop.length > 0) || isJSExpression(loop)) {
return this.__createLoopVirtualDom(
{
...schema,
loop,
},
self,
parentInfo,
idx,
);
}
}
const condition = schema.condition == null ? true : parseData(schema.condition, self);
if (!condition) return null;
let scopeKey = '';
// 判断组件是否需要生成scope且只生成一次挂在this.__compScopes上
if (Comp.generateScope) {
const key = parseExpression(schema.props.key, self);
if (key) {
// 如果组件自己设置key则使用组件自己的key
scopeKey = key;
} else if (!schema.__ctx) {
// 在生产环境schema没有__ctx上下文需要手动生成一个lunaKey
schema.__ctx = {
lunaKey: `luna${++scopeIdx}`,
};
scopeKey = schema.__ctx.lunaKey;
} else {
// 需要判断循环的情况
scopeKey = schema.__ctx.lunaKey + (idx !== undefined ? `_${idx}` : '');
}
if (!this.__compScopes[scopeKey]) {
this.__compScopes[scopeKey] = Comp.generateScope(this, schema);
}
}
// 如果组件有设置scope需要为组件生成一个新的scope上下文
if (scopeKey && this.__compScopes[scopeKey]) {
const compSelf = { ...this.__compScopes[scopeKey] };
compSelf.__proto__ = self;
self = compSelf;
}
// 容器类组件的上下文通过props传递避免context传递带来的嵌套问题
const otherProps = isFileSchema(schema)
? {
__schema: schema,
__appHelper: appHelper,
__components: components,
}
: {};
if (engine && engine.props.designMode) {
otherProps.__designMode = engine.props.designMode;
}
const componentInfo = {};
const props =
this.__parseProps(schema.props, self, '', {
schema,
Comp,
componentInfo: {
...componentInfo,
props: transformArrayToMap(componentInfo.props, 'name'),
},
}) || {};
// 对于可以获取到ref的组件做特殊处理
if (!acceptsRef(Comp)) {
Comp = compWrapper(Comp);
}
// if (acceptsRef(Comp)) {
otherProps.ref = (ref) => {
this.$(props.fieldId, ref); // 收集ref
const refProps = props.ref;
if (refProps && typeof refProps === 'string') {
this[refProps] = ref;
}
ref && engine && engine.props.onCompGetRef(schema, ref);
};
// }
// scope需要传入到组件上
if (scopeKey && this.__compScopes[scopeKey]) {
props.__scope = this.__compScopes[scopeKey];
}
// FIXME 这里清除 key 是为了避免循环渲染中更改 key 导致的渲染重复
props.key = '';
if (schema.__ctx && schema.__ctx.lunaKey) {
if (!isFileSchema(schema)) {
engine && engine.props.onCompGetCtx(schema, self);
}
props.key = props.key || `${schema.__ctx.lunaKey}_${schema.__ctx.idx || 0}_${idx !== undefined ? idx : ''}`;
} else if (typeof idx === 'number' && !props.key) {
props.key = idx;
}
props.__id = schema.id;
if (!props.key) {
props.key = props.__id;
}
let child = null;
if (/*!isFileSchema(schema) && */!!_children) {
child = this.__createVirtualDom(
isJSExpression(_children) ? parseExpression(_children, self) : _children,
self,
{
schema,
Comp,
},
);
}
const renderComp = (props) => engine.createElement(Comp, props, child);
// 设计模式下的特殊处理
if (engine && [DESIGN_MODE.EXTEND, DESIGN_MODE.BORDER].includes(engine.props.designMode)) {
// 对于overlay,dialog等组件为了使其在设计模式下显示外层需要增加一个div容器
if (OVERLAY_LIST.includes(schema.componentName)) {
const { ref, ...overlayProps } = otherProps;
return (
<Div ref={ref} __designMode={engine.props.designMode}>
{renderComp({ ...props, ...overlayProps })}
</Div>
);
}
// 虚拟dom显示
if (componentInfo && componentInfo.parentRule) {
const parentList = componentInfo.parentRule.split(',');
const { schema: parentSchema, Comp: parentComp } = parentInfo;
if (
!parentList.includes(parentSchema.componentName) ||
parentComp !== components[parentSchema.componentName]
) {
props.__componentName = schema.componentName;
Comp = VisualDom;
} else {
// 若虚拟dom在正常的渲染上下文中就不显示设计模式了
props.__disableDesignMode = true;
}
}
}
return renderComp({ ...props, ...otherProps });
} catch (e) {
return engine.createElement(engine.getFaultComponent(), {
error: e,
schema,
self,
parentInfo,
idx,
});
}
};
getSchemaChildren = (schema) => {
if (!schema || !schema.props) {
return schema?.children;
}
if (!schema.children) return schema.props.children;
if (!schema.props.children) return schema.children;
let _children = [].concat(schema.children);
if (Array.isArray(schema.props.children)) {
_children = _children.concat(schema.props.children);
} else {
_children.push(schema.props.children);
}
return _children;
};
__createLoopVirtualDom = (schema, self, parentInfo, idx) => {
if (isFileSchema(schema)) {
console.warn('file type not support Loop');
return null;
}
if (!Array.isArray(schema.loop)) return null;
const itemArg = (schema.loopArgs && schema.loopArgs[0]) || 'item';
const indexArg = (schema.loopArgs && schema.loopArgs[1]) || 'index';
return schema.loop.map((item, i) => {
const loopSelf = {
[itemArg]: item,
[indexArg]: i,
};
loopSelf.__proto__ = self;
return this.__createVirtualDom(
{
...schema,
loop: undefined,
},
loopSelf,
parentInfo,
idx ? `${idx}_${i}` : i,
);
});
};
__createContextDom = (childCtx, currCtx, props) => (
<AppContext.Consumer>
{(context) => {
this.context = context;
this.__generateCtx(currCtx);
this.__render();
return (
<AppContext.Provider
value={{
...this.context,
...childCtx,
}}
>
{context.engine.createElement(
props.__components.Page,
{
...props,
ref: this.__getRef,
className: classnames(getFileCssName(props.__schema.fileName), props.className),
__id: props.__schema.id,
},
this.__createDom(),
)}
</AppContext.Provider>
);
}}
</AppContext.Consumer>
);
__parseProps = (props, self, path, info) => {
const { schema, Comp, componentInfo = {} } = info;
const propInfo = getValue(componentInfo.props, path);
const propType = propInfo && propInfo.extra && propInfo.extra.propType;
const ignoreParse = schema.__ignoreParse || [];
const checkProps = (value) => {
if (!propType) return value;
return checkPropTypes(value, path, propType, componentInfo.name) ? value : undefined;
};
const parseReactNode = (data, params) => {
if (isEmpty(params)) {
return checkProps(this.__createVirtualDom(data, self, { schema, Comp }));
}
return checkProps(function () {
const args = {};
if (Array.isArray(params) && params.length) {
params.forEach((item, idx) => {
if (typeof item === 'string') {
args[item] = arguments[idx];
} else if (item && typeof item === 'object') {
args[item.name] = arguments[idx];
}
});
}
args.__proto__ = self;
return self.__createVirtualDom(data, args, { schema, Comp });
});
};
// 判断是否需要解析变量
if (
ignoreParse.some((item) => {
if (item instanceof RegExp) {
return item.test(path);
}
return item === path;
})
) {
return checkProps(props);
}
if (isJSExpression(props)) {
props = parseExpression(props, self);
// 只有当变量解析出来为模型结构的时候才会继续解析
if (!isSchema(props) && !isJSSlot(props)) return checkProps(props);
}
if (isJSFunction(props)) {
props = props.value;
}
if (isJSSlot(props)) {
const { params, value } = props;
if (!isSchema(value) || isEmpty(value)) return undefined;
return parseReactNode(value, params);
}
// 兼容通过componentInfo判断的情况
if (isSchema(props)) {
return parseReactNode(props);
}
if (Array.isArray(props)) {
return checkProps(props.map((item, idx) => this.__parseProps(item, self, path ? `${path}.${idx}` : idx, info)));
}
if (typeof props === 'function') {
return checkProps(props.bind(self));
}
if (props && typeof props === 'object') {
if (props.$$typeof) return checkProps(props);
const res = {};
forEach(props, (val, key) => {
if (key.startsWith('__')) {
res[key] = val;
return;
}
res[key] = this.__parseProps(val, self, path ? `${path}.${key}` : key, info);
});
return checkProps(res);
}
if (typeof props === 'string') {
return checkProps(props.trim());
}
return checkProps(props);
};
$(filedId, instance) {
this.__instanceMap = this.__instanceMap || {};
if (!filedId) {
return this.__instanceMap;
}
if (instance) {
this.__instanceMap[filedId] = instance;
}
return this.__instanceMap[filedId];
}
get utils() {
return this.appHelper && this.appHelper.utils;
}
get constants() {
return this.appHelper && this.appHelper.constants;
}
get history() {
return this.appHelper && this.appHelper.history;
}
get location() {
return this.appHelper && this.appHelper.location;
}
get match() {
return this.appHelper && this.appHelper.match;
}
render() {
return null;
}
}

View File

@ -1,90 +0,0 @@
// @ts-nocheck
import { createElement } from 'rax';
import PropTypes from 'prop-types';
import Debug from 'debug';
import classnames from 'classnames';
import { isSchema, getFileCssName } from '../utils';
import BaseEngine from './base';
const debug = Debug('engine:block');
export default class BlockEngine extends BaseEngine {
static dislayName = 'block-engine';
static propTypes = {
__schema: PropTypes.object,
};
static defaultProps = {
__schema: {},
};
static getDerivedStateFromProps(props, state) {
debug('block.getDerivedStateFromProps');
const func = props.__schema.lifeCycles && props.__schema.lifeCycles.getDerivedStateFromProps;
if (func) {
return func(props, state);
}
return null;
}
constructor(props, context) {
super(props, context);
this.__generateCtx();
const schema = props.__schema || {};
this.state = this.__parseData(schema.state || {});
this.__initDataSource(props);
this.__setLifeCycleMethods('constructor', arguments);
debug(`block.constructor - ${schema.fileName}`);
}
async getSnapshotBeforeUpdate() {
super.getSnapshotBeforeUpdate(...arguments);
debug(`block.getSnapshotBeforeUpdate - ${this.props.__schema.fileName}`);
}
async componentDidMount() {
super.componentDidMount(...arguments);
debug(`block.componentDidMount - ${this.props.__schema.fileName}`);
}
async componentDidUpdate() {
super.componentDidUpdate(...arguments);
debug(`block.componentDidUpdate - ${this.props.__schema.fileName}`);
}
async componentWillUnmount() {
super.componentWillUnmount(...arguments);
debug(`block.componentWillUnmount - ${this.props.__schema.fileName}`);
}
async componentDidCatch() {
await super.componentDidCatch(...arguments);
debug(`block.componentDidCatch - ${this.props.__schema.fileName}`);
}
render() {
const { __schema } = this.props;
if (!isSchema(__schema, true) || __schema.componentName !== 'Block') {
return '区块schema结构异常';
}
debug(`block.render - ${__schema.fileName}`);
const { id, className, style } = this.__parseData(__schema.props);
return (
<div
// ref={this.__getRef}
className={classnames('luna-block', getFileCssName(__schema.fileName), className, this.props.className)}
id={id}
style={style}
>
{this.__createContextDom({
blockContext: this,
})}
</div>
);
}
}

View File

@ -1,144 +0,0 @@
// @ts-nocheck
import { createElement } from 'rax';
import PropTypes from 'prop-types';
import Debug from 'debug';
import classnames from 'classnames';
import { isSchema, getFileCssName } from '../utils';
import BaseEngine from './base';
import AppContext from '../context/appContext';
const debug = Debug('engine:comp');
export default class CompEngine extends BaseEngine {
static dislayName = 'comp-engine';
static propTypes = {
__schema: PropTypes.object,
};
static defaultProps = {
__schema: {},
};
static getDerivedStateFromProps(props, state) {
debug('comp.getDerivedStateFromProps');
const func = props.__schema.lifeCycles && props.__schema.lifeCycles.getDerivedStateFromProps;
if (func) {
return func(props, state);
}
return null;
}
constructor(props, context) {
super(props, context);
this.__generateCtx({
component: this,
});
const schema = props.__schema || {};
this.state = this.__parseData(schema.state || {});
this.__initDataSource(props);
this.__setLifeCycleMethods('constructor', arguments);
debug(`comp.constructor - ${schema.fileName}`);
}
async getSnapshotBeforeUpdate() {
super.getSnapshotBeforeUpdate(...arguments);
debug(`comp.getSnapshotBeforeUpdate - ${this.props.__schema.fileName}`);
}
async componentDidMount() {
super.componentDidMount(...arguments);
debug(`comp.componentDidMount - ${this.props.__schema.fileName}`);
}
async componentDidUpdate() {
super.componentDidUpdate(...arguments);
debug(`comp.componentDidUpdate - ${this.props.__schema.fileName}`);
}
async componentWillUnmount() {
super.componentWillUnmount(...arguments);
debug(`comp.componentWillUnmount - ${this.props.__schema.fileName}`);
}
async componentDidCatch() {
super.componentDidCatch(...arguments);
debug(`comp.componentDidCatch - ${this.props.__schema.fileName}`);
}
__createContextDom = (childCtx, currCtx, props) => (
<AppContext.Consumer>
{(context) => {
this.context = context;
this.__generateCtx(currCtx);
this.__render();
return (
<AppContext.Provider
value={{
...this.context,
...childCtx,
}}
>
{context.engine.createElement(
props.__components.Component,
{
...props,
ref: this.__getRef,
className: classnames(getFileCssName(props.__schema.fileName), props.className),
__id: props.__schema.id,
},
this.__createDom(),
)}
</AppContext.Provider>
);
}}
</AppContext.Consumer>
);
render() {
const { __schema } = this.props;
if (!isSchema(__schema, true) || __schema.componentName !== 'Component') {
return '自定义组件schema结构异常';
}
debug(`comp.render - ${__schema.fileName}`);
const {
id, className, style, noContainer,
} = this.__parseData(__schema.props);
if (noContainer) {
return this.__createContextDom(
{
compContext: this,
blockContext: this,
},
{
component: this,
},
);
}
return (
<div
// ref={this.__getRef}
className={classnames('luna-comp', getFileCssName(__schema.fileName), className, this.props.className)}
id={this.props.id || id}
style={{ ...style, ...this.props.style }}
>
{this.__createContextDom(
{
compContext: this,
blockContext: this,
},
{
component: this,
},
this.props,
)}
</div>
);
}
}

View File

@ -1,205 +0,0 @@
// @ts-nocheck
/* eslint-disable */
import { Component, createElement } from 'rax';
import PropTypes from 'prop-types';
import Debug from 'debug';
import isEmpty from 'lodash/isEmpty';
import findDOMNode from 'rax-find-dom-node';
import { isFileSchema, goldlog } from '../utils';
import AppContext from '../context/appContext';
import PageEngine from './pageEngine';
import ComponentEngine from './compEngine';
import BlockEngine from './blockEngine';
import TempEngine from './tempEngine';
import BaseEngine from './base';
import compWrapper from '../hoc/compWrapper';
const debug = Debug('engine:entry');
const ENGINE_COMPS = {
PageEngine,
ComponentEngine,
BlockEngine,
TempEngine,
};
const raxCreateElement = createElement;
class FaultComponent extends Component {
render() {
// FIXME: errorlog
console.error('render error', this.props);
return <div>RenderError</div>;
}
}
class NotFoundComponent extends Component {
render() {
console.error('component not found', this.props);
return <div {...this.props} />;
}
}
export default class Engine extends Component {
static dislayName = 'engine';
static propTypes = {
appHelper: PropTypes.object,
components: PropTypes.object,
componentsMap: PropTypes.object,
// 数据源请求处理
requestHandlersMap: PropTypes.object,
designMode: PropTypes.string,
suspended: PropTypes.bool,
schema: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
onCompGetRef: PropTypes.func,
onCompGetCtx: PropTypes.func,
customCreateElement: PropTypes.func,
notFoundComponent: PropTypes.element,
faultComponent: PropTypes.element,
};
static defaultProps = {
appHelper: null,
components: {},
componentsMap: {},
// 数据源请求处理
requestHandlersMap: null,
designMode: '',
suspended: false,
schema: {},
onCompGetRef: () => {},
onCompGetCtx: () => {},
};
constructor(props, context) {
super(props, context);
this.state = {};
debug(`entry.constructor - ${props.schema && props.schema.componentName}`);
}
async componentDidMount() {
goldlog(
'EXP',
{
action: 'appear',
value: !!this.props.designMode,
},
'engine',
);
debug(`entry.componentDidMount - ${this.props.schema && this.props.schema.componentName}`);
}
async componentDidUpdate() {
debug(`entry.componentDidUpdate - ${this.props.schema && this.props.schema.componentName}`);
}
async componentWillUnmount() {
debug(`entry.componentWillUnmount - ${this.props.schema && this.props.schema.componentName}`);
}
async componentDidCatch(e) {
console.warn(e);
}
shouldComponentUpdate(nextProps) {
return !nextProps.suspended;
}
getNotFoundComponent() {
const { notFoundComponent } = this.props;
return notFoundComponent || NotFoundComponent;
}
getFaultComponent() {
const { faultComponent } = this.props;
return faultComponent || FaultComponent;
}
__getRef = (ref) => {
const { schema, onCompGetRef } = this.props;
this.__ref = ref;
if (ref) {
onCompGetRef(schema, ref, true);
}
};
patchDidCatch(Comp) {
if (Comp.patchedCatch) {
return;
}
Comp.patchedCatch = true;// eslint-disable-line
Comp.getDerivedStateFromError = (error) => ({ engineRenderError: true, error });// eslint-disable-line
// const engine = this;
const originRender = Comp.prototype.render;
Comp.prototype.render = function() {
if (this.state && this.state.engineRenderError) {
this.state.engineRenderError = false;
return engine.createElement(engine.getFaultComponent(), {
...this.props,
error: this.state.error,
});
}
return originRender.call(this);
};
const originShouldComponentUpdate = Comp.prototype.shouldComponentUpdate;
Comp.prototype.shouldComponentUpdate = (nextProps, nextState) => {// eslint-disable-line
if (nextState && nextState.engineRenderError) {
return true;
}
return originShouldComponentUpdate ? originShouldComponentUpdate.call(this, nextProps, nextState) : true;// eslint-disable-line
};
}
createElement(Comp, props, children) {
const { customCreateElement } = this.props;
// TODO: enable in runtime mode?
this.patchDidCatch(Component);
return (customCreateElement || raxCreateElement)(Comp, props, children);
}
render() {
const { schema, designMode, appHelper, components } = this.props;
if (isEmpty(schema)) {
return null;
}
if (!isFileSchema(schema)) {
return '模型结构异常';
}
debug('entry.render');
const allComponents = { ...ENGINE_COMPS, ...components };
const { componentName } = schema;
// const Comp = allComponents[schema.componentName];
let Comp = allComponents[componentName] || ENGINE_COMPS[`${componentName}Engine`];
if (Comp && Comp.prototype) {
// const proto = Comp.prototype;
if (!(Comp.prototype instanceof BaseEngine)) {
Comp = ENGINE_COMPS[`${componentName}Engine`];
}
}
return (
Comp
? <AppContext.Provider
value={{
appHelper,
components: allComponents,
engine: this,
}}
>
<Comp
key={schema.__ctx && `${schema.__ctx.lunaKey}_${schema.__ctx.idx || '0'}`}
ref={this.__getRef}
__appHelper={appHelper}
__components={allComponents}
__schema={schema}
__designMode={designMode}
// __id={schema.id}
{...this.props}
/>
</AppContext.Provider>
: null
);
}
}
Engine.findDOMNode = findDOMNode;

View File

@ -1,158 +0,0 @@
// @ts-nocheck
import { createElement } from 'rax';
import PropTypes from 'prop-types';
import Debug from 'debug';
import classnames from 'classnames';
import AppContext from '../context/appContext';
import { isSchema, getFileCssName } from '../utils';
import BaseEngine from './base';
const debug = Debug('engine:page');
export default class PageEngine extends BaseEngine {
static dislayName = 'page-engine';
static propTypes = {
__schema: PropTypes.object,
};
static defaultProps = {
__schema: {},
};
static contextType = AppContext;
static getDerivedStateFromProps(props, state) {
debug('page.getDerivedStateFromProps');
const func = props.__schema.lifeCycles && props.__schema.lifeCycles.getDerivedStateFromProps;
if (func) {
return func(props, state);
}
return null;
}
constructor(props, context) {
super(props, context);
this.__generateCtx({
page: this,
});
const schema = props.__schema || {};
this.state = this.__parseData(schema.state || {});
this.__initDataSource(props);
this.__setLifeCycleMethods('constructor', arguments);
debug(`page.constructor - ${schema.fileName}`);
}
async getSnapshotBeforeUpdate() {
super.getSnapshotBeforeUpdate(...arguments);
debug(`page.getSnapshotBeforeUpdate - ${this.props.__schema.fileName}`);
}
async componentDidMount() {
super.componentDidMount(...arguments);
debug(`page.componentDidMount - ${this.props.__schema.fileName}`);
}
async componentDidUpdate() {
super.componentDidUpdate(...arguments);
debug(`page.componentDidUpdate - ${this.props.__schema.fileName}`);
}
async componentWillUnmount() {
super.componentWillUnmount(...arguments);
debug(`page.componentWillUnmount - ${this.props.__schema.fileName}`);
}
async componentDidCatch() {
await super.componentDidCatch(...arguments);
debug(`page.componentDidCatch - ${this.props.__schema.fileName}`);
}
render() {
const { __schema, __components } = this.props;
if (!isSchema(__schema, true) || __schema.componentName !== 'Page') {
return '页面schema结构异常';
}
debug(`page.render - ${__schema.fileName}`);
const {
id, className, style,
} = this.__parseData(__schema.props);
const { Page } = __components;
if (Page) {
// const { engine } = this.context || {};
return (
<AppContext.Consumer>
{(context) => {
this.context = context;
this.__render();
return (
<AppContext.Provider
value={{
...this.context,
pageContext: this,
blockContext: this,
}}
>
{this.context.engine.createElement(
Page,
{
...this.props,
ref: this.__getRef,
className: classnames(getFileCssName(__schema.fileName), className, this.props.className),
__id: __schema.id,
},
this.__createDom(),
)}
</AppContext.Provider>
);
}}
</AppContext.Consumer>
);
}
const renderContent = () => (
<AppContext.Provider
value={{
...this.context,
pageContext: this,
blockContext: this,
}}
>
{this.__createDom()}
</AppContext.Provider>
);
// if (autoLoading || loading !== undefined) {
// return (
// <Loading
// size="medium"
// visible={!!(this.__showPlaceholder || loading)}
// style={{
// height: this.__showPlaceholder ? defaultHeight : 'auto',
// display: 'block',
// ...style,
// }}
// className={classnames('luna-page', getFileCssName(__schema.fileName), className, this.props.className)}
// id={id}
// >
// {!this.__showPlaceholder && renderContent()}
// </Loading>
// );
// }
return (
<div
ref={this.__getRef}
className={classnames('luna-page', getFileCssName(__schema.fileName), className, this.props.className)}
id={id}
style={style}
>
{renderContent()}
</div>
);
}
}

View File

@ -1,77 +0,0 @@
// @ts-nocheck
import { createElement } from 'rax';
import PropTypes from 'prop-types';
import Debug from 'debug';
import { isSchema } from '../utils';
import AppContext from '../context/appContext';
import BaseEngine from './base';
const debug = Debug('engine:temp');
export default class TempEngine extends BaseEngine {
static dislayName = 'temp-engine';
static propTypes = {
__ctx: PropTypes.object,
__schema: PropTypes.object,
};
static defaultProps = {
__ctx: {},
__schema: {},
};
constructor(props, context) {
super(props, context);
this.state = {};
this.cacheSetState = {};
debug(`temp.constructor - ${props.__schema.fileName}`);
}
componentDidMount() {
const ctx = this.props.__ctx;
if (!ctx) return;
const { setState } = ctx;
this.cacheSetState = setState;
ctx.setState = (...args) => {
setState.call(ctx, ...args);
setTimeout(() => this.forceUpdate(), 0);
};
debug(`temp.componentDidMount - ${this.props.__schema.fileName}`);
}
componentDidUpdate() {
debug(`temp.componentDidUpdate - ${this.props.__schema.fileName}`);
}
componentWillUnmount() {
const ctx = this.props.__ctx;
if (!ctx || !this.cacheSetState) return;
ctx.setState = this.cacheSetState;
delete this.cacheSetState;
debug(`temp.componentWillUnmount - ${this.props.__schema.fileName}`);
}
componentDidCatch(e) {
console.warn(e);
debug(`temp.componentDidCatch - ${this.props.__schema.fileName}`);
}
render() {
const { __schema, __ctx } = this.props;
if (!isSchema(__schema, true) || __schema.componentName !== 'Temp') {
return '下钻编辑schema结构异常';
}
debug(`temp.render - ${__schema.fileName}`);
return (
<div
// ref={this.__getRef}
className="luna-temp"
>
<AppContext.Provider value={{ ...this.context, ...__ctx }}>{this.__createDom()}</AppContext.Provider>
</div>
);
}
}

View File

@ -3,16 +3,18 @@
import { Component, createElement, forwardRef } from 'rax'; import { Component, createElement, forwardRef } from 'rax';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { AppHelper } from '@ali/lowcode-utils'; import { AppHelper } from '@ali/lowcode-utils';
import { forEach, isFileSchema } from '../utils'; import { utils } from '@ali/lowcode-renderer-core';
import CompEngine from '../engine/compEngine'; import CompEngine from '../engine/compEngine';
import BlockEngine from '../engine/blockEngine'; import BlockEngine from '../engine/blockEngine';
import AppContext from '../context/appContext'; import AppContext from '../context/appContext';
const { forEach, isFileSchema } = utils;
export default function compFactory(schema, components = {}, componentsMap = {}, config = {}) { export default function compFactory(schema, components = {}, componentsMap = {}, config = {}) {
// 自定义组件需要有自己独立的appHelper // 自定义组件需要有自己独立的appHelper
const appHelper = new AppHelper(config); const appHelper = new AppHelper(config);
class LNCompView extends Component { class LNCompView extends Component {
static dislayName = 'luna-comp-factory'; static dislayName = 'lce-comp-factory';
static version = config.version || '0.0.0'; static version = config.version || '0.0.0';

View File

@ -1,23 +0,0 @@
// @ts-nocheck
import { createElement, Component, forwardRef } from 'rax';
export default function (Comp) {
class compWrapper extends Component {
// eslint-disable-next-line no-useless-constructor
constructor(props, context) {
super(props, context);
}
render() {
const { forwardRef } = this.props;
return createElement(Comp, {
...this.props,
ref: forwardRef,
});
}
}
return forwardRef((props, ref) => {
return createElement(compWrapper, { ...props, forwardRef: ref });
});
}

View File

@ -0,0 +1,52 @@
import { Component, PureComponent, createElement, createContext, forwardRef } from 'rax';
import findDOMNode from 'rax-find-dom-node';
import {
adapter,
addonRendererFactory,
tempRendererFactory,
rendererFactory
} from '@ali/lowcode-renderer-core';
import pageRendererFactory from './renderer/page';
import componentRendererFactory from './renderer/component';
import blockRendererFactory from './renderer/block';
import CompFactory from './hoc/compFactory';
adapter.setRuntime({
Component,
PureComponent,
createContext,
createElement,
forwardRef,
findDOMNode,
});
adapter.setRenderers({
PageRenderer: pageRendererFactory(),
ComponentRenderer: componentRendererFactory(),
BlockRenderer: blockRendererFactory(),
AddonRenderer: addonRendererFactory(),
TempRenderer: tempRendererFactory(),
});
function factory() {
const Renderer = rendererFactory();
return class extends Renderer {
constructor(props: any, context: any) {
super(props, context);
}
isValidComponent(obj: any) {
return obj?.prototype?.setState || obj?.prototype instanceof Component;
}
}
}
const RaxRenderer = factory();
const Engine = RaxRenderer;
export {
Engine,
CompFactory,
}
export default RaxRenderer;

View File

@ -1,5 +0,0 @@
import Engine from './engine';
export { default as Engine } from './engine';
export { default as CompFactory } from './hoc/compFactory';
export default Engine;

View File

@ -0,0 +1,24 @@
import { blockRendererFactory, types } from '@ali/lowcode-renderer-core';
export default function raxBlockRendererFactory() {
const OriginBlock = blockRendererFactory();
return class BlockRenderer extends OriginBlock {
render() {
// @ts-ignore
const that: types.IRenderer = this;
const { __schema, __components } = that.props;
if (that.__checkSchema(__schema)) {
return '区块 schema 结构异常!';
}
that.__debug(`render - ${__schema.fileName}`);
const children = ((context) => {
that.context = context;
that.__generateCtx({});
that.__render();
return that.__renderComp((__components as any)?.Block, { blockContext: that });
});
return that.__renderContextConsumer(children);
}
}
}

View File

@ -0,0 +1,36 @@
import { componentRendererFactory, types } from '@ali/lowcode-renderer-core';
export default function raxComponentRendererFactory() {
const OriginComponent = componentRendererFactory();
return class ComponentRenderer extends OriginComponent {
render() {
// @ts-ignore
const that: types.IRenderer = this;
const { __schema, __components } = that.props;
if (that.__checkSchema(__schema)) {
return '自定义组件 schema 结构异常!';
}
that.__debug(`render - ${__schema.fileName}`);
const { noContainer } = that.__parseData(__schema.props);
const children = ((context) => {
that.context = context;
that.__generateCtx({ component: that });
that.__render();
// 传 null使用内置的 div 来渲染,解决在页面中渲染 vc-component 报错的问题
return that.__renderComp(null, {
compContext: that,
blockContext: that,
});
});
const content = that.__renderContextConsumer(children);
if (noContainer) {
return content;
}
return that.__renderContent(content);
}
}
}

View File

@ -0,0 +1,33 @@
import { pageRendererFactory, types } from '@ali/lowcode-renderer-core';
export default function raxPageRendererFactory() {
const OriginPage = pageRendererFactory();
return class PageRenderer extends OriginPage {
async componentDidUpdate() {
// @ts-ignore
super.componentDidUpdate(...arguments);
}
render() {
// @ts-ignore
const that: types.IRenderer = this;
const { __schema, __components } = that.props;
if (that.__checkSchema(__schema)) {
return '页面 schema 结构异常!';
}
that.__debug(`render - ${__schema?.fileName}`);
const { Page } = __components as any;
if (Page) {
const children = ((context) => {
that.context = context;
that.__render();
return that.__renderComp(Page, { pageContext: that });
});
return that.__renderContextConsumer(children);
}
return that.__renderContent(that.__renderContextProvider({ pageContext: that }));
}
}
}

View File

@ -1,302 +0,0 @@
/* eslint-disable object-curly-newline */
// @ts-nocheck
import { transformArrayToMap, isJSFunction, transformStringToFunction, clone, comboSkeletonConfig } from './index';
import { jsonp, mtop, request, get, post, bzb, webTableProxy } from './request';
const DS_STATUS = {
INIT: 'init',
LOADING: 'loading',
LOADED: 'loaded',
ERROR: 'error',
};
export default class DataHelper {
constructor(comp, config = {}, appHelper, parser) {
this.host = comp;
this.config = config;
this.parser = parser;
this.ajaxList = (config && config.list) || [];
this.ajaxMap = transformArrayToMap(this.ajaxList, 'id');
this.dataSourceMap = this.generateDataSourceMap();
this.appHelper = appHelper;
}
// 重置configdataSourceMap状态会被重置
resetConfig(config = {}) {
this.config = config;
this.ajaxList = (config && config.list) || [];
this.ajaxMap = transformArrayToMap(this.ajaxList, 'id');
this.dataSourceMap = this.generateDataSourceMap();
return this.dataSourceMap;
}
// 更新config只会更新配置状态保存
updateConfig(config = {}) {
this.config = config;
this.ajaxList = (config && config.list) || [];
const ajaxMap = transformArrayToMap(this.ajaxList, 'id');
// 删除已经移除的接口
Object.keys(this.ajaxMap).forEach((key) => {
if (!ajaxMap[key]) {
delete this.dataSourceMap[key];
}
});
this.ajaxMap = ajaxMap;
// 添加未加入到dataSourceMap中的接口
this.ajaxList.forEach((item) => {
if (!this.dataSourceMap[item.id]) {
this.dataSourceMap[item.id] = {
status: DS_STATUS.INIT,
load: (...args) => this.getDataSource(item.id, ...args),
};
}
});
return this.dataSourceMap;
}
generateDataSourceMap() {
const res = {};
this.ajaxList.forEach((item) => {
item.id = item.id || item.name;
res[item.id] = {
status: DS_STATUS.INIT,
load: (...args) => this.getDataSource(item.id, ...args),
};
});
return res;
}
updateDataSourceMap(id, data, error) {
this.dataSourceMap[id].error = error || undefined;
this.dataSourceMap[id].data = data;
this.dataSourceMap[id].status = error ? DS_STATUS.ERROR : DS_STATUS.LOADED;
}
getInitData() {
const initSyncData = this.parser(this.ajaxList).filter((item) => {
if (item.isInit) {
this.dataSourceMap[item.id].status = DS_STATUS.LOADING;
return true;
}
return false;
});
return this.asyncDataHandler(initSyncData).then((res) => {
let { dataHandler } = this.config;
if (isJSFunction(dataHandler)) {
dataHandler = transformStringToFunction(dataHandler.value);
}
if (!dataHandler || typeof dataHandler !== 'function') return res;
try {
return dataHandler.call(this.host, res);
} catch (e) {
console.error('请求数据处理函数运行出错', e);
}
});
}
getDataSource(id, params, otherOptions, callback) {
console.log('load',id);
const req = this.parser(this.ajaxMap[id]);
const options = req.options || {};
if (typeof otherOptions === 'function') {
callback = otherOptions;
otherOptions = {};
}
const { headers, ...otherProps } = otherOptions || {};
if (!req) {
console.warn(`getDataSource API named ${id} not exist`);
return;
}
return this.asyncDataHandler([
{
...req,
options: {
...options,
// 支持参数为array的情况当参数为array时不做参数合并
params:
Array.isArray(options.params) || Array.isArray(params)
? params || options.params
: {
...options.params,
...params,
},
headers: {
...options.headers,
...headers,
},
...otherProps,
},
},
])
.then(res => {
try {
callback && callback(res && res[id]);
} catch (e) {
console.error('load请求回调函数报错', e);
}
return res && res[id];
})
.catch(err => {
try {
callback && callback(null, err);
} catch (e) {
console.error('load请求回调函数报错', e);
}
return err;
});
}
asyncDataHandler(asyncDataList) {
return new Promise((resolve, reject) => {
const allReq = [];
const doserReq = [];
const doserList = [];
const beforeRequest = this.appHelper && this.appHelper.utils && this.appHelper.utils.beforeRequest;
const afterRequest = this.appHelper && this.appHelper.utils && this.appHelper.utils.afterRequest;
const csrfInput = document.getElementById('_csrf_token');
const _tb_token_ = csrfInput && csrfInput.value;
asyncDataList.forEach(req => {
const { id, type, options } = req;
if (!id || !type) return;
if (type === 'doServer') {
const { uri, params } = options || {};
if (!uri) return;
doserList.push(id);
doserReq.push({ name: uri, package: 'cms', params });
} else {
allReq.push(req);
}
});
if (doserReq.length > 0) {
allReq.push({
type: 'doServer',
options: {
uri: '/nrsService.do',
cors: true,
method: 'POST',
params: {
data: JSON.stringify(doserReq),
_tb_token_,
},
},
});
}
if (allReq.length === 0) resolve({});
const res = {};
Promise.all(
allReq.map(
(item) =>
new Promise((resolve) => {
const { type, id, dataHandler, options } = item;
const fetchHandler = async (data, error) => {
if (type === 'doServer') {
if (!Array.isArray(data)) {
data = [data];
}
doserList.forEach(async (id, idx) => {
const req = this.ajaxMap[id];
if (req) {
res[id] = await this.dataHandler(id, req.dataHandler, data && data[idx], error);
this.updateDataSourceMap(id, res[id], error);
}
});
} else {
res[id] = await this.dataHandler(id, dataHandler, data, error);
this.updateDataSourceMap(id, res[id], error);
}
resolve();
};
const doFetch = (type, req) => {
this.fetchOne(type, req)
.then(async (data) => {
console.log(data)
if (afterRequest) {
this.appHelper.utils.afterRequest(item, data, undefined, async (data, error) => {
await fetchHandler(data, error);
});
} else {
await fetchHandler(data, undefined);
}
})
.catch(async (err) => {
if (afterRequest) {
// 必须要这么调用否则beforeRequest中的this会丢失
this.appHelper.utils.afterRequest(item, undefined, err, async (data, error) => {
await fetchHandler(data, error);
});
} else {
await fetchHandler(undefined, err);
}
});
};
if (type === 'doServer') {
doserList.forEach(item => {
this.dataSourceMap[item].status = DS_STATUS.LOADING;
});
} else {
this.dataSourceMap[id].status = DS_STATUS.LOADING;
}
// 请求切片
if (beforeRequest) {
// 必须要这么调用否则beforeRequest中的this会丢失
this.appHelper.utils.beforeRequest(item, clone(options), () => doFetch(type, item));
} else {
doFetch(type, item);
}
})
),
).then(() => {
resolve(res);
})
.catch((e) => {
reject(e);
});
});
}
async dataHandler(id, dataHandler, data, error) {
if (isJSFunction(dataHandler)) {
dataHandler = transformStringToFunction(dataHandler.value);
}
if (!dataHandler || typeof dataHandler !== 'function') return data;
try {
return await dataHandler.call(this.host, data, error);
} catch (e) {
console.error(`[${id}]单个请求数据处理函数运行出错`, e);
}
}
fetchOne(type, req) {
const { options } = req;
// eslint-disable-next-line prefer-const
let { uri, method = 'GET', headers, params, ...otherProps } = options;
otherProps = otherProps || {};
switch (type) {
case 'mtop':
method && (otherProps.method = method);
return mtop(uri, params, otherProps);
case 'jsonp':
return jsonp(uri, params, otherProps);
case 'bzb':
return bzb(uri, params, {
method,
headers,
...otherProps,
});
case 'legao':
return webTableProxy(req);
default:
method = method.toUpperCase();
if (method === 'GET') {
return get(uri || otherProps.url, params, headers, otherProps);
}
if (method === 'POST') {
return post(uri, params, headers, otherProps);
}
return request(uri, method, params, headers, otherProps);
}
}
}

View File

@ -1,725 +0,0 @@
// @ts-nocheck
import Debug from 'debug';
import _keymaster from 'keymaster';
import { forEach as _forEach, shallowEqual as _shallowEqual } from '@ali/b3-one/lib/obj';
import { serialize as serializeParams } from '@ali/b3-one/lib/url';
import _moment from 'moment';
import 'moment/locale/zh-cn';
import _pick from 'lodash/pick';
import _deepEqual from 'lodash/isEqualWith';
import _clone from 'lodash/cloneDeep';
import _isEmpty from 'lodash/isEmpty';
import _throttle from 'lodash/throttle';
import _debounce from 'lodash/debounce';
import _serialize from 'serialize-javascript';
import * as _jsonuri from 'jsonuri';
import IntlMessageFormat from 'intl-messageformat';
const sdkVersion = (require('../../package.json')).version;
window.sdkVersion = sdkVersion;
export const moment = _moment;
moment.locale('zh-cn');
export const forEach = _forEach;
export const shallowEqual = _shallowEqual;
export const keymaster = _keymaster;
export const pick = _pick;
export const deepEqual = _deepEqual;
export const clone = _clone;
export const isEmpty = _isEmpty;
export const throttle = _throttle;
export const debounce = _debounce;
export const serialize = _serialize;
export const jsonuri = _jsonuri;
export {
get, post, jsonp, mtop, request,
} from './request';
const ReactIs = require('react-is');
const ReactPropTypesSecret = require('prop-types/lib/ReactPropTypesSecret');
const factoryWithTypeCheckers = require('prop-types/factoryWithTypeCheckers');
const PropTypes2 = factoryWithTypeCheckers(ReactIs.isElement, true);
const EXPRESSION_TYPE = {
JSEXPRESSION: 'JSExpression',
JSFUNCTION: 'JSFunction',
JSSLOT: 'JSSlot',
};
const EXPRESSION_REG = /^\{\{(\{.*\}|.*?)\}\}$/;
const debug = Debug('utils:index');
const ENV = {
TBE: 'TBE',
WEBIDE: 'WEB-IDE',
VSCODE: 'VSCODE',
WEB: 'WEB',
};
/**
* @name isSchema
* @description
*/
export function isSchema(schema, ignoreArr) {
if (isEmpty(schema)) return false;
if (!ignoreArr && Array.isArray(schema)) return schema.every((item) => isSchema(item));
return !!(schema.componentName && schema.props && (typeof schema.props === 'object' || isJSExpression(schema.props)));
}
export function isFileSchema(schema) {
if (isEmpty(schema)) return false;
return ['Page', 'Block', 'Component', 'Addon', 'Temp'].includes(schema.componentName);
}
// 判断当前页面是否被嵌入到同域的页面中
export function inSameDomain() {
try {
return window.parent !== window && window.parent.location.host === window.location.host;
} catch (e) {
return false;
}
}
export function getFileCssName(fileName) {
if (!fileName) return;
const name = fileName.replace(/([A-Z])/g, '-$1').toLowerCase();
return `luna-${name}`
.split('-')
.filter((p) => !!p)
.join('-');
}
export function isJSSlot(obj) {
return obj && typeof obj === 'object' && EXPRESSION_TYPE.JSSLOT === obj.type;
}
export function isJSFunction(obj) {
return obj && typeof obj === 'object' && EXPRESSION_TYPE.JSFUNCTION === obj.type;
}
export function isJSExpression(obj) {
// 兼容两种写法有js构造表达式的情况
const isJSExpressionObj = obj && typeof obj === 'object' && EXPRESSION_TYPE.JSEXPRESSION === obj.type && typeof obj.value === 'string';
const isJSExpressionStr = typeof obj === 'string' && EXPRESSION_REG.test(obj.trim());
return isJSExpressionObj || isJSExpressionStr;
}
/**
* @name wait
* @description
*/
export function wait(ms) {
return new Promise((resolve) => setTimeout(() => resolve(true), ms));
}
export function curry(Comp, hocs = []) {
return hocs.reverse().reduce((pre, cur) => cur(pre), Comp);
}
/**
* parent window上的值
* @param {string} key
*/
export function getParentWinValue(key) {
if (inSameDomain()) {
return window.parent && window.parent[key];
}
}
export function getValue(obj, path, defaultValue) {
if (isEmpty(obj) || typeof obj !== 'object') return defaultValue;
const res = path.split('.').reduce((pre, cur) => pre && pre[cur], obj);
if (res === undefined) return defaultValue;
return res;
}
export function parseObj(schemaStr) {
if (typeof schemaStr !== 'string') return schemaStr;
// 默认调用顶层窗口的parseObj,保障new Function的window对象是顶层的window对象
try {
if (inSameDomain() && window.parent.__newFunc) {
return window.parent.__newFunc(`"use strict"; return ${schemaStr}`)();
}
return new Function(`"use strict"; return ${schemaStr}`)();
} catch (err) {
return undefined;
}
}
export function parseSearch(search) {
if (!search || typeof search !== 'string') {
return {};
}
const str = search.replace(/^\?/, '');
const paramStr = str.split('&');
const res = {};
paramStr.forEach((item) => {
const regRes = item.split('=');
if (regRes[0] && regRes[1]) {
res[regRes[0]] = decodeURIComponent(regRes[1]);
}
});
return res;
}
export function fastClone(obj) {
return parseObj(serialize(obj, { unsafe: true }));
}
// 更新obj的内容但不改变obj的指针
export function fillObj(receiver = {}, ...suppliers) {
Object.keys(receiver).forEach((item) => {
delete receiver[item];
});
Object.assign(receiver, ...suppliers);
return receiver;
}
// 中划线转驼峰
export function toHump(name) {
// eslint-disable-next-line no-useless-escape
return name.replace(/\-(\w)/g, (all, letter) => letter.toUpperCase());
}
// 驼峰转中划线
export function toLine(name) {
return name.replace(/([A-Z])/g, '-$1').toLowerCase();
}
// 获取当前环境
export function getEnv() {
const { userAgent } = navigator;
const isVscode = /Electron\//.test(userAgent);
if (isVscode) return ENV.VSCODE;
const isTheia = window.is_theia === true;
if (isTheia) return ENV.WEBIDE;
return ENV.WEB;
}
/**
* skeleton配置
* @param {*}
* @param {*}
*/
export function comboSkeletonConfig(defaultConfig = {}, customConfig) {
const {
skeleton, theme, addons, hooks, shortCuts, extensions, constants, utils, i18n,
} = customConfig || {};
if (skeleton && skeleton.handler && typeof skeleton.handler === 'function') {
return skeleton.handler({
skeleton,
...defaultConfig,
});
}
const defaultShortCuts = transformArrayToMap(defaultConfig.shortCuts || [], 'keyboard');
const customShortCuts = transformArrayToMap(shortCuts || [], 'keyboard');
const localeList = ['zh-CN', 'zh-TW', 'en-US', 'ja-JP'];
const i18nConfig = {};
localeList.forEach((key) => {
i18nConfig[key] = {
...(defaultConfig.i18n && defaultConfig.i18n[key]),
...(i18n && i18n[key]),
};
});
return {
skeleton,
theme: {
...defaultConfig.theme,
...theme,
},
addons: {
...defaultConfig.addons,
...addons,
},
hooks: [...(defaultConfig.hooks || []), ...(hooks || [])],
shortCuts: Object.values({
...defaultShortCuts,
...customShortCuts,
}),
extensions: {
...defaultConfig.extensions,
...extensions,
},
constants: {
...defaultConfig.constants,
...constants,
},
utils: [...(defaultConfig.utils || []), ...(utils || [])],
i18n: i18nConfig,
};
}
/**
*
* @param {*} locale zh-CNen-US
* @param {*} messages
*/
export function generateI18n(locale = 'zh-CN', messages = {}) {
return (key, values = {}) => {
if (!messages || !messages[key]) return '';
const formater = new IntlMessageFormat(messages[key], locale);
return formater.format(values);
};
}
/**
* ref
* @param {*} Comp
*/
export function acceptsRef(Comp) {
return Comp && Comp.prototype && Comp.prototype.setState;
}
/**
*
* @param {String} gmKey
* @param {Object} params
* @param {String} logKey
*/
export function goldlog(gmKey, params = {}, logKey = 'other') {
// vscode 黄金令箭API
const sendIDEMessage = window.sendIDEMessage || getParentWinValue('sendIDEMessage');
const goKey = serializeParams({
sdkVersion,
env: getEnv(),
...params,
});
if (sendIDEMessage) {
sendIDEMessage({
action: 'goldlog',
data: {
logKey: `/iceluna.core.${logKey}`,
gmKey,
goKey,
},
});
}
window.goldlog && window.goldlog.record(`/iceluna.core.${logKey}`, gmKey, goKey, 'POST');
}
// utils为编辑器打包生成的utils文件内容utilsConfig为数据库存放的utils配置
export function generateUtils(utils, utilsConfig) {
if (!Array.isArray(utilsConfig)) return { ...utils };
const res = {};
utilsConfig.forEach((item) => {
if (!item.name || !item.type || !item.content) return;
if (item.type === 'function' && typeof item.content === 'function') {
res[item.name] = item.content;
} else if (item.type === 'npm' && utils[item.name]) {
res[item.name] = utils[item.name];
}
});
return res;
}
// 复制到粘贴板
export function setClipboardData(str) {
return new Promise((resolve, reject) => {
if (typeof str !== 'string') reject('不支持拷贝');
if (navigator.clipboard) {
navigator.clipboard
.writeText(str)
.then(() => {
resolve();
})
.catch((err) => {
reject('复制失败,请重试!', err);
});
} else {
const textArea = document.createElement('textarea');
textArea.value = str;
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
const successful = document.execCommand('copy');
if (successful) {
document.body.removeChild(textArea);
resolve();
}
} catch (err) {
document.body.removeChild(textArea);
reject('复制失败,请重试!', err);
}
}
});
}
// 获取粘贴板数据
export function getClipboardData() {
return new Promise((resolve, reject) => {
if (window.clipboardData) {
resolve(window.clipboardData.getData('text'));
} else if (navigator.clipboard) {
return navigator.clipboard
.readText()
.then((res) => {
resolve(res);
})
.catch((err) => {
reject('粘贴板获取失败', err);
});
} else {
reject('粘贴板获取失败');
}
});
}
// 将函数返回结果转成promise形式如果函数有返回值则根据返回值的bool类型判断是reject还是resolve若函数无返回值默认执行resolve
export function transformToPromise(input) {
if (input instanceof Promise) return input;
return new Promise((resolve, reject) => {
if (input || input === undefined) {
resolve();
} else {
reject();
}
});
}
export function moveArrayItem(arr, sourceIdx, distIdx, direction) {
if (
!Array.isArray(arr)
|| sourceIdx === distIdx
|| sourceIdx < 0
|| sourceIdx >= arr.length
|| distIdx < 0
|| distIdx >= arr.length
) {
return arr;
}
const item = arr[sourceIdx];
if (direction === 'after') {
arr.splice(distIdx + 1, 0, item);
} else {
arr.splice(distIdx, 0, item);
}
if (sourceIdx < distIdx) {
arr.splice(sourceIdx, 1);
} else {
arr.splice(sourceIdx + 1, 1);
}
return arr;
}
export function transformArrayToMap(arr, key, overwrite = true) {
if (isEmpty(arr) || !Array.isArray(arr)) return {};
const res = {};
arr.forEach((item) => {
const curKey = item[key];
if (item[key] === undefined) return;
if (res[curKey] && !overwrite) return;
res[curKey] = item;
});
return res;
}
export function checkPropTypes(value, name, rule, componentName) {
if (typeof rule === 'string') {
rule = new Function(`"use strict"; const PropTypes = arguments[0]; return ${rule}`)(PropTypes2);
}
if (!rule || typeof rule !== 'function') {
console.warn('checkPropTypes should have a function type rule argument');
return true;
}
const err = rule(
{
[name]: value,
},
name,
componentName,
'prop',
null,
ReactPropTypesSecret,
);
if (err) {
console.warn(err);
}
return !err;
}
export function transformSchemaToPure(obj) {
const pureObj = (obj) => {
if (Array.isArray(obj)) {
return obj.map((item) => pureObj(item));
}
if (typeof obj === 'object') {
// 对于undefined及null直接返回
if (!obj) return obj;
const res = {};
forEach(obj, (val, key) => {
if (key.startsWith('__') && key !== '__ignoreParse') return;
res[key] = pureObj(val);
});
return res;
}
return obj;
};
return pureObj(obj);
}
export function transformSchemaToStandard(obj) {
const standardObj = (obj) => {
if (Array.isArray(obj)) {
return obj.map((item) => standardObj(item));
}
if (typeof obj === 'object') {
// 对于undefined及null直接返回
if (!obj) return obj;
const res = {};
forEach(obj, (val, key) => {
if (key.startsWith('__') && key !== '__ignoreParse') return;
if (isSchema(val) && key !== 'children' && obj.type !== 'JSSlot') {
res[key] = {
type: 'JSSlot',
value: standardObj(val),
};
// table特殊处理
if (key === 'cell') {
res[key].params = ['value', 'index', 'record'];
}
} else {
res[key] = standardObj(val);
}
});
return res;
}
if (typeof obj === 'function') {
return {
type: 'JSFunction',
value: obj.toString(),
};
}
if (typeof obj === 'string' && EXPRESSION_REG.test(obj.trim())) {
const regRes = obj.trim().match(EXPRESSION_REG);
return {
type: 'JSExpression',
value: (regRes && regRes[1]) || '',
};
}
return obj;
};
return standardObj(obj, false);
}
export function transformStringToFunction(str) {
if (typeof str !== 'string') return str;
if (inSameDomain() && window.parent.__newFunc) {
return window.parent.__newFunc(`"use strict"; return ${str}`)();
}
return new Function(`"use strict"; return ${str}`)();
}
export function addCssTag(id, content) {
let styleTag = document.getElementById(id);
if (styleTag) {
styleTag.innerHTML = content;
return;
}
styleTag = document.createElement('style');
styleTag.id = id;
styleTag.class = 'luna-style';
styleTag.innerHTML = content;
document.head.appendChild(styleTag);
}
// 注册快捷
export function registShortCuts(config, appHelper) {
keymaster.filter = (event) => {
const eTarget = event.target || event.srcElement;
const { tagName } = eTarget;
const isInput = !!(tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA');
const isContenteditable = (target) => {
while (target) {
if (target.contentEditable === 'true') return true;
target = target.parentNode;
}
return false;
};
if (isInput || isContenteditable(eTarget)) {
if (event.metaKey === true && [70, 83].includes(event.keyCode)) event.preventDefault(); // 禁止触发chrome原生的页面保存或查找
return false;
}
return true;
};
const keyboardFilter = keymaster.filter;
const ideMessage = appHelper.utils && appHelper.utils.ideMessage;
// 复制
if (!document.copyListener) {
document.copyListener = (e) => {
if (!keyboardFilter(e) || appHelper.isCopying) return;
const schema = appHelper.schemaHelper && appHelper.schemaHelper.schemaMap[appHelper.activeKey];
if (!schema || !isSchema(schema)) return;
appHelper.isCopying = true;
const schemaStr = serialize(transformSchemaToPure(schema), {
unsafe: true,
});
setClipboardData(schemaStr)
.then(() => {
ideMessage && ideMessage('success', '当前内容已复制到剪贴板请使用快捷键Command+v进行粘贴');
appHelper.emit('schema.copy', schemaStr, schema);
appHelper.isCopying = false;
})
.catch((errMsg) => {
ideMessage && ideMessage('error', errMsg);
appHelper.isCopying = false;
});
};
document.addEventListener('copy', document.copyListener);
if (getParentWinValue('vscode')) {
keymaster('command+c', document.copyListener);
}
}
// 粘贴
if (!document.pasteListener) {
const doPaste = (e, text) => {
if (!keyboardFilter(e) || appHelper.isPasting) return;
const { schemaHelper } = appHelper;
let targetKey = appHelper.activeKey;
let direction = 'after';
const topKey = schemaHelper.schema && schemaHelper.schema.__ctx && schemaHelper.schema.__ctx.lunaKey;
if (!targetKey || topKey === targetKey) {
const { schemaHelper } = appHelper;
const topKey = schemaHelper.schema && schemaHelper.schema.__ctx && schemaHelper.schema.__ctx.lunaKey;
if (!topKey) return;
targetKey = topKey;
direction = 'in';
}
appHelper.isPasting = true;
const schema = parseObj(text);
if (!isSchema(schema)) {
appHelper.emit('illegalSchema.paste', text);
// ideMessage && ideMessage('error', '当前内容不是模型结构,不能粘贴进来!');
console.warn('paste schema illegal');
appHelper.isPasting = false;
return;
}
appHelper.emit('material.add', {
schema,
targetKey,
direction,
});
appHelper.isPasting = false;
appHelper.emit('schema.paste', schema);
};
document.pasteListener = (e) => {
const clipboardData = e.clipboardData || window.clipboardData;
const text = clipboardData && clipboardData.getData('text');
doPaste(e, text);
};
document.addEventListener('paste', document.pasteListener);
if (getParentWinValue('vscode')) {
keymaster('command+v', (e) => {
const sendIDEMessage = getParentWinValue('sendIDEMessage');
sendIDEMessage
&& sendIDEMessage({
action: 'readClipboard',
})
.then((text) => {
doPaste(e, text);
})
.catch((err) => {
console.warn(err);
});
});
}
}
(config || []).forEach((item) => {
keymaster(item.keyboard, (ev) => {
ev.preventDefault();
item.handler(ev, appHelper, keymaster);
});
});
}
// 取消注册快捷
export function unRegistShortCuts(config) {
(config || []).forEach((item) => {
keymaster.unbind(item.keyboard);
});
if (getParentWinValue('vscode')) {
keymaster.unbind('command+c');
keymaster.unbind('command+v');
}
if (document.copyListener) {
document.removeEventListener('copy', document.copyListener);
delete document.copyListener;
}
if (document.pasteListener) {
document.removeEventListener('paste', document.pasteListener);
delete document.pasteListener;
}
}
export function parseData(schema, self) {
if (isJSExpression(schema)) {
return parseExpression(schema, self);
}
if (typeof schema === 'string') {
return schema.trim();
}
if (Array.isArray(schema)) {
return schema.map((item) => parseData(item, self));
}
if (typeof schema === 'function') {
return schema.bind(self);
}
if (typeof schema === 'object') {
// 对于undefined及null直接返回
if (!schema) return schema;
const res = {};
forEach(schema, (val, key) => {
if (key.startsWith('__')) return;
res[key] = parseData(val, self);
});
return res;
}
return schema;
}
/* 全匹配{{开头,}}结尾的变量表达式或者对象类型JSExpression且均不支持省略this */
// todo:
export function parseExpression(str, self) {
try {
const contextArr = ['"use strict";', 'var __self = arguments[0];'];
contextArr.push('return ');
let tarStr;
// 向前兼容,支持标准协议新格式
if (typeof str === 'string') {
const regRes = str.trim().match(EXPRESSION_REG);
tarStr = regRes[1];
} else {
tarStr = (str.value || '').trim();
}
tarStr = tarStr.replace(/this(\W|$)/g, (a, b) => `__self${b}`);
tarStr = contextArr.join('\n') + tarStr;
// 默认调用顶层窗口的parseObj,保障new Function的window对象是顶层的window对象
if (inSameDomain() && window.parent.__newFunc) {
return window.parent.__newFunc(tarStr)(self);
}
return new Function(tarStr)(self);
} catch (err) {
debug('parseExpression.error', err, str, self);
return undefined;
}
}
/**
* reactNode且type为function的props
* @param {*} componentInfo
*/
export function hasReactNodeFuncProps(componentInfo) {
const isReactNodeFuncProps = (config) => {
if (config.type === 'ReactNode') {
return config.props && config.props.type === 'function';
}
if (config.type === 'Mixin') {
return config.props && config.props.reactNodeProps && config.props.reactNodeProps.type === 'function';
}
};
return componentInfo && (componentInfo.props || []).some((item) => isReactNodeFuncProps(item));
}

View File

@ -1,199 +0,0 @@
// @ts-nocheck
import 'whatwg-fetch';
import fetchMtop from '@ali/lib-mtop';
import fetchJsonp from 'fetch-jsonp';
import bzbRequest from '@ali/bzb-request';
import { serialize, buildUrl, parseUrl } from '@ali/b3-one/lib/url';
export function get(dataAPI, params = {}, headers = {}, otherProps = {}) {
headers = {
Accept: 'application/json',
...headers,
};
dataAPI = buildUrl(dataAPI, params);
return request(dataAPI, 'GET', null, headers, otherProps);
}
export function post(dataAPI, params = {}, headers = {}, otherProps = {}) {
headers = {
Accept: 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
...headers,
};
return request(
dataAPI,
'POST',
headers['Content-Type'].indexOf('application/json') > -1 || Array.isArray(params)
? JSON.stringify(params)
: serialize(params),
headers,
otherProps,
);
}
export function request(dataAPI, method = 'GET', data, headers = {}, otherProps = {}) {
switch (method) {
case 'PUT':
case 'DELETE':
headers = {
Accept: 'application/json',
'Content-Type': 'application/json',
...headers,
};
data = JSON.stringify(data || {});
break;
}
return new Promise((resolve, reject) => {
if (otherProps.timeout) {
setTimeout(() => {
reject(new Error('timeout'));
}, otherProps.timeout);
}
fetch(dataAPI, {
method,
credentials: 'include',
headers,
body: data,
...otherProps,
})
.then(response => {
switch (response.status) {
case 200:
case 201:
case 202:
return response.json();
case 204:
if (method === 'DELETE') {
return {
success: true,
};
} else {
return {
__success: false,
code: response.status,
};
}
case 400:
case 401:
case 403:
case 404:
case 406:
case 410:
case 422:
case 500:
return response
.json()
.then(res => {
return {
__success: false,
code: response.status,
data: res,
};
})
.catch(() => {
return {
__success: false,
code: response.status,
};
});
}
return null;
})
.then(json => {
if (json && json.__success !== false) {
resolve(json);
} else {
delete json.__success;
reject(json);
}
})
.catch(err => {
reject(err);
});
});
}
export function jsonp(dataAPI, params = {}, otherProps = {}) {
return new Promise((resolve, reject) => {
otherProps = {
timeout: 5000,
...otherProps,
};
fetchJsonp(buildUrl(dataAPI, params), otherProps)
.then(response => response.json())
.then(json => {
if (json) {
resolve(json);
} else {
reject();
}
})
.catch(err => {
reject(err);
});
});
}
export function mtop(dataAPI, params, otherProps = {}) {
fetchMtop.config.subDomain = otherProps.subDomain || 'm';
return fetchMtop.request({
api: dataAPI,
v: '1.0',
data: params,
ecode: otherProps.ecode || 0,
type: otherProps.method || 'GET',
dataType: otherProps.dataType || 'jsonp',
AntiFlood: true, // 防刷
timeout: otherProps.timeout || 20000,
});
}
export function bzb(apiCode, params, otherProps = {}) {
// 通过url参数设置小二工作台请求环境
const getUrlEnv = () => {
try {
if (window.parent && window.parent.location.host === window.location.host) {
const urlInfo = parseUrl(window.parent && window.parent.location.href);
return urlInfo && urlInfo.params && urlInfo.params._env;
}
const urlInfo = parseUrl(window.location.href);
return urlInfo && urlInfo.params && urlInfo.params._env;
} catch (e) {
return null;
}
};
otherProps.method = otherProps.method || 'GET';
otherProps.env = getUrlEnv() || otherProps.env || 'prod';
return bzbRequest(apiCode, {
data: params,
...otherProps,
});
}
export async function webTableProxy(req) {
const { _table } = window.parent;
const { VisualEngine } = window;
const { Bus } = VisualEngine;
if (_table) {
const { options } = req;
const { params, oneAPIConfig } = options;
const { code } = oneAPIConfig;
const sheetId = oneAPIConfig['x-model'];
const sheet = await _table.find({ id: sheetId });
const result = await sheet.instance.fetch({ code }, params);
return result;
}
return new Promise((resolve, reject) => {
Bus.emitter.on('table.ready', async (table) => {
const { options } = req;
const { params, oneAPIConfig } = options;
const { code } = oneAPIConfig;
const sheetId = oneAPIConfig['x-model'];
const sheet = await table.find({ id: sheetId });
const result = await sheet.instance.fetch({ code }, params);
resolve(result);
});
});
}

View File

@ -1,4 +1,4 @@
import RaxEngine from '@ali/lowcode-rax-renderer/lib/index'; import RaxRenderer from '@ali/lowcode-rax-renderer';
import { History } from 'history'; import { History } from 'history';
import { Component, createElement, Fragment } from 'rax'; import { Component, createElement, Fragment } from 'rax';
import { useRouter } from './rax-use-router'; import { useRouter } from './rax-use-router';
@ -175,7 +175,7 @@ class Renderer extends Component<{
const { designMode, device } = container; const { designMode, device } = container;
const { rendererContainer: renderer } = this.props; const { rendererContainer: renderer } = this.props;
return ( return (
<RaxEngine <RaxRenderer
schema={documentInstance.schema} schema={documentInstance.schema}
components={renderer.components} components={renderer.components}
appHelper={renderer.context} appHelper={renderer.context}

View File

@ -1,6 +1,7 @@
import { BuiltinSimulatorRenderer, Component, DocumentModel, Node, NodeInstance } from '@ali/lowcode-designer'; import { BuiltinSimulatorRenderer, Component, DocumentModel, Node, NodeInstance } from '@ali/lowcode-designer';
import { ComponentSchema, NodeSchema, NpmInfo, RootSchema, TransformStage } from '@ali/lowcode-types'; import { ComponentSchema, NodeSchema, NpmInfo, RootSchema, TransformStage } from '@ali/lowcode-types';
import { Asset, cursor, isElement, isESModule, isReactComponent, setNativeSelection } from '@ali/lowcode-utils'; import { Asset, cursor, isElement, isESModule, isReactComponent, setNativeSelection } from '@ali/lowcode-utils';
import LowCodeRenderer from '@ali/lowcode-rax-renderer';
import { computed, obx } from '@recore/obx'; import { computed, obx } from '@recore/obx';
import DriverUniversal from 'driver-universal'; import DriverUniversal from 'driver-universal';
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
@ -412,8 +413,7 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer {
} }
instance = Instance.get(dom); instance = Instance.get(dom);
while (instance && instance[INTERNAL]) { while (instance && instance[INTERNAL]) {
// 剔除低代码组件的内部组件 if (isValidDesignModeRaxComponentInstance(instance)) {
if (isValidDesignModeRaxComponentInstance(instance) && !findComponentCreator(instance)) {
// if (instance && SYMBOL_VNID in instance) { // if (instance && SYMBOL_VNID in instance) {
// const docId = (instance.props as any).schema.docId; // const docId = (instance.props as any).schema.docId;
return { return {
@ -509,15 +509,38 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer {
doc.getElementsByTagName('head')[0].appendChild(s); doc.getElementsByTagName('head')[0].appendChild(s);
} }
// const node = host.currentDocument?.createNode(_schema);
// _schema = node?.export(TransformStage.Render) || {};
const renderer = this; const renderer = this;
const { componentsMap } = renderer; const { componentsMap: components } = renderer;
return getComponentController(schema, componentsMap); class LowCodeComp extends Rax.Component {
render() {
// @ts-ignore
return createElement(LowCodeRenderer, {
schema: _schema,
components,
designMode: renderer.designMode,
device: renderer.device,
appHelper: renderer.context,
customCreateElement: (Comp: any, props: any, children: any) => {
const componentMeta = host.currentDocument?.getComponentMeta(Comp.displayName);
if (componentMeta?.isModal) {
return null;
}
const { __id, __designMode, ...viewProps } = props;
// mock _leaf减少性能开销
const _leaf = {
isEmpty: () => false,
};
viewProps._leaf = _leaf;
return createElement(Comp, viewProps, children);
}
});
}
}
return LowCodeComp;
} }
private _running = false; private _running = false;
@ -599,101 +622,4 @@ function findComponent(libraryMap: LibraryMap, componentName: string, npm?: NpmI
return getSubComponent(library, paths); return getSubComponent(library, paths);
} }
const processPropsSchema = (propsSchema: any, propsMap: any, componentsMap: any): any => {
if (!propsSchema) {
return {};
}
const result = { ...propsSchema };
const reg = /^(?:this\.props|props)\.(\S+)$/;
Object.keys(result).map((key: string) => {
if (result[key]?.type === 'JSExpression') {
const { value } = result[key];
const matched = reg.exec(value);
if (matched) {
const propName = matched[1];
result[key] = propsMap[propName];
}
} else if (result[key]?.type === 'JSSlot') {
const schema = result[key].value;
result[key] = createElement(ComponentCreator, { schema, propsMap: {}, componentsMap });
}
});
return result;
};
function findComponentCreator(instance: any) {
let isComponentCreator = false;
while (instance && !isComponentCreator) {
instance = instance[INTERNAL]?.__parentInstance;
isComponentCreator = instance instanceof ComponentCreator;
}
return isComponentCreator;
}
class ComponentCreator extends Rax.Component<{ schema: any; propsMap: any, componentsMap: any }> {
private isModal: boolean;
constructor(props: any) {
super(props);
const componentMeta = host.currentDocument?.getComponentMeta(props.schema.componentName);
if (componentMeta?.prototype?.isModal()) {
this.isModal = true;
}
}
render() {
if (this.isModal) {
return null;
}
const { schema, propsMap, componentsMap } = this.props;
const ComponentClass = componentsMap[schema.componentName];
if (!ComponentClass) {
return null;
}
let children = null;
if (schema.children && schema.children.length > 0) {
children = schema.children.map((item: any, index: number) => createElement(ComponentCreator, { schema: item, propsMap, componentsMap, key: item?.id || index }));
}
const props = processPropsSchema(schema.props, propsMap, componentsMap);
const _leaf = host.currentDocument?.createNode(schema);
return createElement(ComponentClass, { ...props, _leaf }, children);
}
}
function getComponentController(schema: NodeSchema, componentsMap: any) {
class ComponentController extends Rax.Component<{ schema: any }> {
renderSchema: any;
constructor(props: any) {
super(props);
const node = host.currentDocument?.createNode(schema);
this.renderSchema = node?.export(TransformStage.Render) || {};
}
// TODO: 暂时解决性能问题
shouldComponentUpdate() {
return false;
}
render() {
const { renderSchema } = this;
const { componentName } = renderSchema;
if (componentName === 'Component') {
let children = [] as any;
const propsMap = this.props || {};
if (renderSchema.children && Array.isArray(renderSchema.children)) {
children = renderSchema.children.map((item: any, index: number) => createElement(ComponentCreator, { schema: item, propsMap, componentsMap, key: item?.id || index }));
}
return createElement('div', {}, children);
} else {
return createElement(ComponentCreator, { schema, propsMap: {}, componentsMap });
}
}
}
return ComponentController;
}
export default new SimulatorRendererContainer(); export default new SimulatorRendererContainer();

View File

@ -0,0 +1,6 @@
{
"plugins": [
"build-plugin-component",
"@ali/lowcode-test-mate/plugin/index.ts"
]
}

View File

@ -0,0 +1,20 @@
const esModules = ['@recore/obx-react'].join('|');
module.exports = {
// transform: {
// '^.+\\.[jt]sx?$': 'babel-jest',
// // '^.+\\.(ts|tsx)$': 'ts-jest',
// // '^.+\\.(js|jsx)$': 'babel-jest',
// },
// testMatch: ['(/tests?/.*(test))\\.[jt]s$'],
transformIgnorePatterns: [
`/node_modules/(?!${esModules})/`,
],
moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
collectCoverage: true,
collectCoverageFrom: [
'src/**/*.{ts,tsx}',
'!**/node_modules/**',
'!**/vendor/**',
],
};

View File

@ -9,7 +9,7 @@
"es" "es"
], ],
"scripts": { "scripts": {
"test": "echo \"Error: run tests from root\" && exit 1", "test": "build-scripts test --config build.test.json",
"start": "build-scripts start", "start": "build-scripts start",
"build": "build-scripts build --skip-demo", "build": "build-scripts build --skip-demo",
"prepublishOnly": "npm run build" "prepublishOnly": "npm run build"
@ -24,32 +24,19 @@
"react" "react"
], ],
"dependencies": { "dependencies": {
"@ali/b3-one": "^0.0.17", "@ali/lowcode-renderer-core": "^1.0.33",
"@ali/bzb-request": "^2.6.0-beta.13", "@alifd/next": "^1.21.16"
"@ali/lib-mtop": "^2.5.1",
"@ali/lowcode-datasource-engine": "^1.0.22",
"@alifd/next": "^1.19.17",
"debug": "^4.1.1",
"events": "^3.0.0",
"fetch-jsonp": "^1.1.3",
"intl-messageformat": "^9.3.1",
"jsonuri": "^2.1.2",
"keymaster": "^1.6.2",
"lodash": "^4.17.11",
"moment": "^2.24.0",
"react-is": "^16.10.1",
"serialize-javascript": "^1.7.0",
"socket.io-client": "^2.2.0",
"whatwg-fetch": "^3.0.0"
}, },
"devDependencies": { "devDependencies": {
"@alifd/next": "^1.19.17",
"@ali/lowcode-test-mate": "^1.0.1",
"@alib/build-scripts": "^0.1.18", "@alib/build-scripts": "^0.1.18",
"build-plugin-component": "^0.2.10", "build-plugin-component": "^0.2.10",
"build-plugin-fusion": "^0.1.0", "build-plugin-fusion": "^0.1.0",
"build-plugin-moment-locales": "^0.1.0", "build-plugin-moment-locales": "^0.1.0",
"moment": "^2.24.0",
"react": "^16.4.1", "react": "^16.4.1",
"react-dom": "^16.4.1" "react-dom": "^16.4.1",
"react-test-renderer": "^16"
}, },
"publishConfig": { "publishConfig": {
"registry": "http://registry.npm.alibaba-inc.com" "registry": "http://registry.npm.alibaba-inc.com"

View File

@ -1,11 +0,0 @@
import React, { PureComponent } from 'react';
export default class DivView extends PureComponent {
static displayName = 'Div';
static version = '0.0.0';
render() {
return <div {...this.props} />;
}
}

View File

@ -1,19 +0,0 @@
.visual-dom {
.panel-container {
box-sizing: border-box;
border: 1px solid #e9e9e9;
.title {
display: block;
font-size: 12px;
color: #333;
background-color: #ebecf0;
line-height: 28px;
padding: 0 12px;
border-bottom: 1px solid #e9e9e9;
}
.content {
min-height: 20px;
padding: 5px;
}
}
}

View File

@ -1,31 +0,0 @@
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import './VisualDom.scss';
export default class VisualDom extends PureComponent {
static displayName = 'VisualDom';
static propTypes = {
children: PropTypes.oneOfType([PropTypes.element, PropTypes.arrayOf(PropTypes.element)]),
};
static defaultProps = {
children: null,
};
render() {
const { children, cell, title, label, text, __componentName } = this.props;
let mainContent = children;
if (cell && typeof cell === 'function') {
mainContent = cell();
}
return (
<div className="visual-dom">
<div className="panel-container">
<span className="title">{title || label || text || __componentName}</span>
<div className="content">{mainContent}</div>
</div>
</div>
);
}
}

View File

@ -1,5 +0,0 @@
import { createContext } from 'react';
const context = createContext({});
window.__appContext = context;
export default context;

View File

@ -1,230 +0,0 @@
import React, { Component, PureComponent, createElement as reactCreateElement } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import Debug from 'debug';
import { isEmpty } from '@ali/b3-one/lib/obj';
import Div from '@ali/iceluna-comp-div';
import AppContext from '../context/appContext';
import { isFileSchema, goldlog } from '../utils';
import PageEngine from './pageEngine';
import ComponentEngine from './compEngine';
import BlockEngine from './blockEngine';
import AddonEngine from './addonEngine';
import TempEngine from './tempEngine';
import BaseEngine from './base';
window.React = React;
window.ReactDom = ReactDOM;
const debug = Debug('engine:entry');
const ENGINE_COMPS = {
PageEngine,
ComponentEngine,
BlockEngine,
AddonEngine,
TempEngine,
DivEngine: BlockEngine,
};
class FaultComponent extends PureComponent {
render() {
// FIXME: errorlog
console.error('render error', this.props);
const { _componentName: componentName } = this.props;
return (
<Div
style={{
backgroundColor: '#DE2710',
padding: '15px',
fontSize: '18px',
textAlign: 'center',
color: 'white',
}}
>
{componentName}
</Div>
);
}
}
class NotFoundComponent extends PureComponent {
render() {
console.error('component not found:', this.props);
const { _componentName: componentName } = this.props;
return (
<Div
{...this.props}
style={{
backgroundColor: '#3E91C9',
padding: '15px',
fontSize: '18px',
textAlign: 'center',
color: 'white',
}}
>
{componentName}
</Div>
);
}
}
function isReactClass(obj) {
return obj && obj.prototype && (obj.prototype.isReactComponent || obj.prototype instanceof Component);
}
// eslint-disable-next-line react/no-redundant-should-component-update
export default class Engine extends PureComponent {
static dislayName = 'engine';
static propTypes = {
appHelper: PropTypes.object,
components: PropTypes.object,
designMode: PropTypes.string,
suspended: PropTypes.bool,
schema: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
onCompGetRef: PropTypes.func,
onCompGetCtx: PropTypes.func,
customCreateElement: PropTypes.func,
};
static defaultProps = {
appHelper: null,
components: {},
designMode: '',
suspended: false,
schema: {},
onCompGetRef: () => {},
onCompGetCtx: () => {},
};
constructor(props, context) {
super(props, context);
this.state = {};
debug(`entry.constructor - ${props.schema && props.schema.componentName}`);
}
async componentDidMount() {
goldlog(
'EXP',
{
action: 'appear',
value: !!this.props.designMode,
},
'engine',
);
debug(`entry.componentDidMount - ${this.props.schema && this.props.schema.componentName}`);
}
async componentDidUpdate() {
debug(`entry.componentDidUpdate - ${this.props.schema && this.props.schema.componentName}`);
}
async componentWillUnmount() {
debug(`entry.componentWillUnmount - ${this.props.schema && this.props.schema.componentName}`);
}
async componentDidCatch(e) {
console.warn(e);
}
shouldComponentUpdate(nextProps) {
return !nextProps.suspended;
}
__getRef = (ref) => {
this.__ref = ref;
if (ref) {
this.props.onCompGetRef(this.props.schema, ref, true);
}
};
patchDidCatch(SetComponent) {
if (!isReactClass(SetComponent)) {
return;
}
if (SetComponent.patchedCatch) {
return;
}
SetComponent.patchedCatch = true;
SetComponent.getDerivedStateFromError = (error) => ({ engineRenderError: true, error });
const engine = this;
const originRender = SetComponent.prototype.render;
SetComponent.prototype.render = function () {
if (this.state && this.state.engineRenderError) {
this.state.engineRenderError = false;
return engine.createElement(engine.getFaultComponent(), {
...this.props,
error: this.state.error,
});
}
return originRender.call(this);
};
const originShouldComponentUpdate = SetComponent.prototype.shouldComponentUpdate;
SetComponent.prototype.shouldComponentUpdate = function (nextProps, nextState) {
if (nextState && nextState.engineRenderError) {
return true;
}
return originShouldComponentUpdate ? originShouldComponentUpdate.call(this, nextProps, nextState) : true;
};
}
createElement(SetComponent, props, children) {
// TODO: enable in runtime mode?
this.patchDidCatch(SetComponent);
return (this.props.customCreateElement || reactCreateElement)(SetComponent, props, children);
}
getNotFoundComponent() {
return this.props.notFoundComponent || NotFoundComponent;
}
getFaultComponent() {
return this.props.faultComponent || FaultComponent;
}
render() {
const {
schema, designMode, appHelper, components,
} = this.props;
if (isEmpty(schema)) {
return null;
}
// 兼容乐高区块模板
if (schema.componentName !== 'Div' && !isFileSchema(schema)) {
return '模型结构异常';
}
debug('entry.render');
const { componentName } = schema;
const allComponents = { ...ENGINE_COMPS, ...components };
let Comp = allComponents[componentName] || ENGINE_COMPS[`${componentName}Engine`];
if (Comp && Comp.prototype) {
if (!(Comp.prototype instanceof BaseEngine)) {
Comp = ENGINE_COMPS[`${componentName}Engine`];
}
}
if (Comp) {
return (
<AppContext.Provider
value={{
appHelper,
components: allComponents,
engine: this,
}}
>
<Comp
key={schema.__ctx && `${schema.__ctx.lunaKey}_${schema.__ctx.idx || '0'}`}
ref={this.__getRef}
__appHelper={appHelper}
__components={allComponents}
__schema={schema}
__designMode={designMode}
{...this.props}
/>
</AppContext.Provider>
);
}
return null;
}
}
Engine.findDOMNode = ReactDOM.findDOMNode;

View File

@ -0,0 +1,50 @@
import React, { Component, PureComponent, createElement, createContext, forwardRef } from 'react';
import ReactDOM from 'react-dom';
import {
adapter,
pageRendererFactory,
componentRendererFactory,
blockRendererFactory,
addonRendererFactory,
tempRendererFactory,
rendererFactory
} from '@ali/lowcode-renderer-core';
import ConfigProvider from '@alifd/next/lib/config-provider';
window.React = React;
(window as any).ReactDom = ReactDOM;
adapter.setRuntime({
Component,
PureComponent,
createContext,
createElement,
forwardRef,
findDOMNode: ReactDOM.findDOMNode,
});
adapter.setRenderers({
PageRenderer: pageRendererFactory(),
ComponentRenderer: componentRendererFactory(),
BlockRenderer: blockRendererFactory(),
AddonRenderer: addonRendererFactory(),
TempRenderer: tempRendererFactory(),
DivRenderer: blockRendererFactory(),
});
adapter.setConfigProvider(ConfigProvider);
function factory() {
const Renderer = rendererFactory();
return class ReactRenderer extends Renderer {
constructor(props: any, context: any) {
super(props, context);
}
isValidComponent(obj: any) {
return obj?.prototype?.isReactComponent || obj?.prototype instanceof Component;
}
}
}
export default factory();

View File

@ -1,218 +0,0 @@
import React, { Component, PureComponent, createElement as reactCreateElement } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import Debug from 'debug';
import ConfigProvider from '@alifd/next/lib/config-provider';
import { isEmpty } from '@ali/b3-one/lib/obj';
import AppContext from './context/appContext';
import { isFileSchema, goldlog } from './utils';
import PageEngine from './renderer/page';
import ComponentEngine from './renderer/component';
import BlockEngine from './renderer/block';
import AddonEngine from './renderer/addon';
import TempEngine from './renderer/temp';
import BaseEngine from './renderer/base';
import Div from './components/Div';
window.React = React;
window.ReactDom = ReactDOM;
const debug = Debug('renderer:entry');
const ENGINE_COMPS = {
PageEngine,
ComponentEngine,
BlockEngine,
AddonEngine,
TempEngine,
DivEngine: BlockEngine,
};
class FaultComponent extends PureComponent {
render() {
// FIXME: errorlog
console.error('render error', this.props);
return (
<Div style={{
width: '100%',
height: '50px',
lineHeight: '50px',
textAlign: 'center',
fontSize: '15px',
color: '#ff0000',
border: '2px solid #ff0000',
}}
>
</Div>
);
}
}
class NotFoundComponent extends PureComponent {
render() {
console.error('component not found', this.props);
return <Div {...this.props} >{this.props.children || 'Component Not Found'}</Div>;
}
}
function isReactClass(obj) {
return obj && obj.prototype && (obj.prototype.isReactComponent || obj.prototype instanceof Component);
}
export default class Renderer extends Component {
static dislayName = 'renderer';
static propTypes = {
appHelper: PropTypes.object,
components: PropTypes.object,
designMode: PropTypes.string,
suspended: PropTypes.bool,
schema: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
onCompGetRef: PropTypes.func,
onCompGetCtx: PropTypes.func,
customCreateElement: PropTypes.func,
};
static defaultProps = {
appHelper: null,
components: {},
designMode: '',
suspended: false,
schema: {},
onCompGetRef: () => {},
onCompGetCtx: () => {},
};
constructor(props, context) {
super(props, context);
this.state = {};
debug(`entry.constructor - ${props.schema && props.schema.componentName}`);
}
async componentDidMount() {
goldlog(
'EXP',
{
action: 'appear',
value: !!this.props.designMode,
},
'engine',
);
debug(`entry.componentDidMount - ${this.props.schema && this.props.schema.componentName}`);
}
async componentDidUpdate() {
debug(`entry.componentDidUpdate - ${this.props.schema && this.props.schema.componentName}`);
}
async componentWillUnmount() {
debug(`entry.componentWillUnmount - ${this.props.schema && this.props.schema.componentName}`);
}
async componentDidCatch(e) {
console.warn(e);
}
shouldComponentUpdate(nextProps) {
return !nextProps.suspended;
}
__getRef = (ref) => {
this.__ref = ref;
if (ref) {
this.props.onCompGetRef(this.props.schema, ref, true);
}
};
patchDidCatch(SetComponent) {
if (!isReactClass(SetComponent)) {
return;
}
if (SetComponent.patchedCatch) {
return;
}
SetComponent.patchedCatch = true;
SetComponent.getDerivedStateFromError = (error) => {
return { engineRenderError: true, error };
};
const engine = this;
const originRender = SetComponent.prototype.render;
SetComponent.prototype.render = function () {
if (this.state && this.state.engineRenderError) {
this.state.engineRenderError = false;
return engine.createElement(engine.getFaultComponent(), {
...this.props,
error: this.state.error,
});
}
return originRender.call(this);
};
const originShouldComponentUpdate = SetComponent.prototype.shouldComponentUpdate;
SetComponent.prototype.shouldComponentUpdate = function (nextProps, nextState) {
if (nextState && nextState.engineRenderError) {
return true;
}
return originShouldComponentUpdate ? originShouldComponentUpdate.call(this, nextProps, nextState) : true;
};
}
createElement(SetComponent, props, children) {
// TODO: enable in runtime mode?
this.patchDidCatch(SetComponent);
return (this.props.customCreateElement || reactCreateElement)(SetComponent, props, children);
}
getNotFoundComponent() {
return this.props.notFoundComponent || NotFoundComponent;
}
getFaultComponent() {
return this.props.faultComponent || FaultComponent;
}
render() {
const { schema, designMode, appHelper, components } = this.props;
if (isEmpty(schema)) {
return null;
}
// 兼容乐高区块模板
if (schema.componentName !== 'Div' && !isFileSchema(schema)) {
return '模型结构异常';
}
debug('entry.render');
const { componentName } = schema;
const allComponents = { ...ENGINE_COMPS, ...components };
let Comp = allComponents[componentName] || ENGINE_COMPS[`${componentName}Engine`];
if (Comp && Comp.prototype) {
if (!(Comp.prototype instanceof BaseEngine)) {
Comp = ENGINE_COMPS[`${componentName}Engine`];
}
}
if (Comp) {
return (
<AppContext.Provider
value={{
appHelper,
components: allComponents,
engine: this,
}}
>
<ConfigProvider device={this.props.device}>
<Comp
key={schema.__ctx && `${schema.__ctx.lunaKey}_${schema.__ctx.idx || '0'}`}
ref={this.__getRef}
__appHelper={appHelper}
__components={allComponents}
__schema={schema}
__designMode={designMode}
{...this.props}
/>
</ConfigProvider>
</AppContext.Provider>
);
}
return null;
}
}
Renderer.findDOMNode = ReactDOM.findDOMNode;

View File

@ -1,139 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import Debug from 'debug';
import classnames from 'classnames';
import AppContext from '../context/appContext';
import BaseRenderer from './base';
import { isSchema, getFileCssName, isEmpty, goldlog } from '../utils';
const debug = Debug('renderer:addon');
export default class AddonRenderer extends BaseRenderer {
static dislayName = 'addon-renderer';
static propTypes = {
config: PropTypes.object,
__schema: PropTypes.object,
};
static defaultProps = {
config: {},
__schema: {},
};
static getDerivedStateFromProps(props, state) {
debug('comp.getDerivedStateFromProps');
const func = props.__schema.lifeCycles && props.__schema.lifeCycles.getDerivedStateFromProps;
if (func) {
return func(props, state);
}
return null;
}
constructor(props, context) {
super(props, context);
this.__generateCtx({
component: this,
});
const schema = props.__schema || {};
this.state = this.__parseData(schema.state || {});
if (isEmpty(props.config) || !props.config.addonKey) {
console.warn('luna addon has wrong config');
this.state.__hasError = true;
return;
}
// 注册插件
this.addonKey = props.config.addonKey;
this.appHelper.addons = this.appHelper.addons || {};
this.appHelper.addons[this.addonKey] = this;
this.__initDataSource(props);
this.open = this.open || (() => {});
this.close = this.close || (() => {});
this.__setLifeCycleMethods('constructor', arguments);
debug(`addon.constructor - ${schema.fileName}`);
}
async getSnapshotBeforeUpdate() {
super.getSnapshotBeforeUpdate(...arguments);
debug(`addon.getSnapshotBeforeUpdate - ${this.props.__schema.fileName}`);
}
async componentDidMount() {
super.componentDidMount(...arguments);
debug(`addon.componentDidMount - ${this.props.__schema.fileName}`);
}
async componentDidUpdate() {
super.componentDidUpdate(...arguments);
debug(`addon.componentDidUpdate - ${this.props.__schema.fileName}`);
}
async componentWillUnmount() {
super.componentWillUnmount(...arguments);
// 注销插件
const config = this.props.config || {};
if (config && this.appHelper.addons) {
delete this.appHelper.addons[config.addonKey];
}
debug(`addon.componentWillUnmount - ${this.props.__schema.fileName}`);
}
async componentDidCatch() {
super.componentDidCatch(...arguments);
debug(`addon.componentDidCatch - ${this.props.__schema.fileName}`);
}
goldlog = (goKey, params) => {
const { addonKey, addonConfig = {} } = this.props.config || {};
goldlog(
goKey,
{
addonKey,
package: addonConfig.package,
version: addonConfig.version,
...this.appHelper.logParams,
...params,
},
'addon',
);
};
get utils() {
const { utils = {} } = this.context.config || {};
return { ...this.appHelper.utils, ...utils };
}
render() {
const { __schema } = this.props;
if (!isSchema(__schema, true) || __schema.componentName !== 'Addon') {
return '插件schema结构异常';
}
debug(`addon.render - ${__schema.fileName}`);
this.__generateCtx({
component: this,
});
this.__render();
const { id, className, style } = this.__parseData(__schema.props);
return (
<div
ref={this.__getRef}
className={classnames('luna-addon', getFileCssName(__schema.fileName), className, this.props.className)}
id={this.props.id || id}
style={{ ...style, ...this.props.style }}
>
<AppContext.Provider
value={{
...this.context,
compContext: this,
blockContext: this,
}}
>
{this.__createDom()}
</AppContext.Provider>
</div>
);
}
}

View File

@ -1,620 +0,0 @@
/* eslint-disable no-proto */
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import Debug from 'debug';
import { create as createDataSourceEngine } from '@ali/lowcode-datasource-engine/interpret';
import Div from '../components/Div';
import VisualDom from '../components/VisualDom';
import AppContext from '../context/appContext';
import DataHelper from '../utils/dataHelper';
import {
forEach,
getValue,
parseData,
parseExpression,
isEmpty,
isSchema,
isFileSchema,
isJSExpression,
isJSSlot,
isJSFunction,
transformArrayToMap,
transformStringToFunction,
checkPropTypes,
generateI18n,
acceptsRef,
} from '../utils';
const debug = Debug('renderer:base');
const DESIGN_MODE = {
EXTEND: 'extend',
BORDER: 'border',
PREVIEW: 'preview',
};
const OVERLAY_LIST = ['Dialog', 'Overlay', 'Animate', 'ConfigProvider'];
let scopeIdx = 0;
export default class BaseRender extends PureComponent {
static dislayName = 'base-renderer';
static propTypes = {
locale: PropTypes.string,
messages: PropTypes.object,
__appHelper: PropTypes.object,
__components: PropTypes.object,
__ctx: PropTypes.object,
__schema: PropTypes.object,
};
static defaultProps = {
__schema: {},
};
static contextType = AppContext;
constructor(props, context) {
super(props, context);
this.appHelper = props.__appHelper;
this.__compScopes = {};
const { locale, messages } = props;
this.i18n = generateI18n(locale, messages);
this.__bindCustomMethods(props);
}
async getSnapshotBeforeUpdate() {
this.__setLifeCycleMethods('getSnapshotBeforeUpdate', arguments);
}
async componentDidMount() {
this.reloadDataSource();
this.__setLifeCycleMethods('componentDidMount', arguments);
}
async componentDidUpdate() {
this.__setLifeCycleMethods('componentDidUpdate', arguments);
}
async componentWillUnmount() {
this.__setLifeCycleMethods('componentWillUnmount', arguments);
}
async componentDidCatch(e) {
this.__setLifeCycleMethods('componentDidCatch', arguments);
console.warn(e);
}
reloadDataSource = () => new Promise((resolve, reject) => {
debug('reload data source');
if (!this.__dataHelper) {
this.__showPlaceholder = false;
return resolve();
}
this.__dataHelper
.getInitData()
.then((res) => {
this.__showPlaceholder = false;
if (isEmpty(res)) {
this.forceUpdate();
return resolve();
}
this.setState(res, resolve);
})
.catch((err) => {
if (this.__showPlaceholder) {
this.__showPlaceholder = false;
this.forceUpdate();
}
reject(err);
});
});
__setLifeCycleMethods = (method, args) => {
const lifeCycleMethods = getValue(this.props.__schema, 'lifeCycles', {});
let fn = lifeCycleMethods[method];
if (fn) {
// TODO, cache
if (isJSExpression(fn) || isJSFunction(fn)) {
fn = parseExpression(fn, this);
}
if (typeof fn !== 'function') {
console.error(`生命周期${method}类型不符`, fn);
return;
}
try {
return fn.apply(this, args);
} catch (e) {
console.error(`[${this.props.__schema.componentName}]生命周期${method}出错`, e);
}
}
};
__bindCustomMethods = (props = this.props) => {
const { __schema } = props;
const customMethodsList = Object.keys(__schema.methods || {}) || [];
this.__customMethodsList
&& this.__customMethodsList.forEach((item) => {
if (!customMethodsList.includes(item)) {
delete this[item];
}
});
this.__customMethodsList = customMethodsList;
forEach(__schema.methods, (val, key) => {
if (isJSExpression(val) || isJSFunction(val)) {
val = parseExpression(val, this);
}
if (typeof val !== 'function') {
console.error(`自定义函数${key}类型不符`, val);
return;
}
this[key] = val.bind(this);
});
};
__generateCtx = (ctx) => {
const { pageContext, compContext } = this.context;
const obj = {
page: pageContext,
component: compContext,
...ctx,
};
forEach(obj, (val, key) => {
this[key] = val;
});
};
__parseData = (data, ctx) => {
const { __ctx } = this.props;
return parseData(data, ctx || __ctx || this);
};
__initDataSource = (props = this.props) => {
const schema = props.__schema || {};
const dataSource = (schema && schema.dataSource) || {};
// requestHandlersMap 存在才走数据源引擎方案
if (props?.__appHelper?.requestHandlersMap) {
const { dataSourceMap, reloadDataSource } = createDataSourceEngine(dataSource, this, {
requestHandlersMap: props.__appHelper.requestHandlersMap,
});
this.dataSourceMap = dataSourceMap;
this.reloadDataSource = () => new Promise((resolve) => {
debug('reload data source');
// this.__showPlaceholder = true;
reloadDataSource().then(() => {
// this.__showPlaceholder = false;
// @TODO 是否需要 forceUpate
// this.forceUpdate();
resolve();
});
});
} else {
const appHelper = props.__appHelper;
this.__dataHelper = new DataHelper(this, dataSource, appHelper, (config) => this.__parseData(config));
this.dataSourceMap = this.__dataHelper.dataSourceMap;
this.reloadDataSource = () => new Promise((resolve, reject) => {
debug('reload data source');
if (!this.__dataHelper) {
// this.__showPlaceholder = false;
return resolve();
}
this.__dataHelper
.getInitData()
.then((res) => {
// this.__showPlaceholder = false;
if (isEmpty(res)) {
this.forceUpdate();
return resolve();
}
this.setState(res, resolve);
})
.catch((err) => {
if (this.__showPlaceholder) {
this.__showPlaceholder = false;
this.forceUpdate();
}
reject(err);
});
});
}
// 设置容器组件占位若设置占位则在初始异步请求完成之前用loading占位且不渲染容器组件内部内容
// @TODO __showPlaceholder 的逻辑一旦开启就关不掉,先注释掉了
/* this.__showPlaceholder = this.__parseData(schema.props && schema.props.autoLoading) && (dataSource.list || []).some(
(item) => !!this.__parseData(item.isInit),
); */
};
__render = () => {
const schema = this.props.__schema;
this.__setLifeCycleMethods('render');
const { engine } = this.context;
if (engine) {
engine.props.onCompGetCtx(schema, this);
// 画布场景才需要每次渲染bind自定义方法
if (engine.props.designMode) {
this.__bindCustomMethods();
this.dataSourceMap = this.__dataHelper && this.__dataHelper.updateConfig(schema.dataSource);
}
}
};
__getRef = (ref) => {
this.__ref = ref;
};
getSchemaChildren = (schema) => {
if (!schema || !schema.props) {
return schema?.children;
}
if (!schema.children) return schema.props.children;
if (!schema.props.children) return schema.children;
let _children = [].concat(schema.children);
if (Array.isArray(schema.props.children)) {
_children = _children.concat(schema.props.children);
} else {
_children.push(schema.props.children);
}
return _children;
};
__createDom = () => {
const { __schema, __ctx, __components = {} } = this.props;
const self = {};
self.__proto__ = __ctx || this;
const _children = this.getSchemaChildren(__schema);
return this.__createVirtualDom(_children, self, {
schema: __schema,
Comp: __components[__schema.componentName],
});
};
// 将模型结构转换成react Element
// schema 模型结构
// self 为每个渲染组件构造的上下文self是自上而下继承的
// parentInfo 父组件的信息包含schema和Comp
// idx 若为循环渲染的循环Index
__createVirtualDom = (schema, self, parentInfo, idx) => {
const { engine } = this.context || {};
try {
if (!schema) return null;
const { __appHelper: appHelper, __components: components = {} } = this.props || {};
if (isJSExpression(schema)) {
return parseExpression(schema, self);
}
if (isJSSlot(schema)) {
return this.__createVirtualDom(schema.value, self, parentInfo);
}
if (typeof schema === 'string') return schema;
if (typeof schema === 'number' || typeof schema === 'boolean') {
return schema.toString();
}
if (Array.isArray(schema)) {
if (schema.length === 1) return this.__createVirtualDom(schema[0], self, parentInfo);
return schema.map((item, idy) => this.__createVirtualDom(item, self, parentInfo, item && item.__ctx && item.__ctx.lunaKey ? '' : idy));
}
// FIXME
const _children = this.getSchemaChildren(schema);
// 解析占位组件
if (schema.componentName === 'Flagment' && _children) {
const tarChildren = isJSExpression(_children) ? parseExpression(_children, self) : _children;
return this.__createVirtualDom(tarChildren, self, parentInfo);
}
if (schema.$$typeof) {
return schema;
}
if (!isSchema(schema)) return null;
let Comp = components[schema.componentName] || engine.getNotFoundComponent();
if (schema.hidden) {
return null;
}
if (schema.loop != null) {
const loop = parseData(schema.loop, self);
if ((Array.isArray(loop) && loop.length > 0) || isJSExpression(loop)) {
return this.__createLoopVirtualDom(
{
...schema,
loop,
},
self,
parentInfo,
idx,
);
}
}
const condition = schema.condition == null ? true : parseData(schema.condition, self);
if (!condition) return null;
let scopeKey = '';
// 判断组件是否需要生成scope且只生成一次挂在this.__compScopes上
if (Comp.generateScope) {
const key = parseExpression(schema.props.key, self);
if (key) {
// 如果组件自己设置key则使用组件自己的key
scopeKey = key;
} else if (!schema.__ctx) {
// 在生产环境schema没有__ctx上下文需要手动生成一个lunaKey
schema.__ctx = {
lunaKey: `luna${++scopeIdx}`,
};
scopeKey = schema.__ctx.lunaKey;
} else {
// 需要判断循环的情况
scopeKey = schema.__ctx.lunaKey + (idx !== undefined ? `_${idx}` : '');
}
if (!this.__compScopes[scopeKey]) {
this.__compScopes[scopeKey] = Comp.generateScope(this, schema);
}
}
// 如果组件有设置scope需要为组件生成一个新的scope上下文
if (scopeKey && this.__compScopes[scopeKey]) {
const compSelf = { ...this.__compScopes[scopeKey] };
compSelf.__proto__ = self;
self = compSelf;
}
// 容器类组件的上下文通过props传递避免context传递带来的嵌套问题
const otherProps = isFileSchema(schema)
? {
__schema: schema,
__appHelper: appHelper,
__components: components,
}
: {};
if (engine && engine.props.designMode) {
otherProps.__designMode = engine.props.designMode;
}
const componentInfo = {};
const props =
this.__parseProps(schema.props, self, '', {
schema,
Comp,
componentInfo: {
...componentInfo,
props: transformArrayToMap(componentInfo.props, 'name'),
},
}) || {};
// 对于可以获取到ref的组件做特殊处理
if (acceptsRef(Comp)) {
otherProps.ref = (ref) => {
const refProps = props.ref;
if (refProps && typeof refProps === 'string') {
this[refProps] = ref;
}
ref && engine && engine.props.onCompGetRef(schema, ref);
};
}
// scope需要传入到组件上
if (scopeKey && this.__compScopes[scopeKey]) {
props.__scope = this.__compScopes[scopeKey];
}
// FIXME 这里清除 key 是为了避免循环渲染中更改 key 导致的渲染重复
props.key = '';
if (schema.__ctx && schema.__ctx.lunaKey) {
if (!isFileSchema(schema)) {
engine && engine.props.onCompGetCtx(schema, self);
}
props.key = props.key || `${schema.__ctx.lunaKey}_${schema.__ctx.idx || 0}_${idx !== undefined ? idx : ''}`;
} else if (typeof idx === 'number' && !props.key) {
props.key = idx;
}
props.__id = schema.id;
if (!props.key) {
props.key = props.__id;
}
let child = null;
if (/*!isFileSchema(schema) && */!!_children) {
child = this.__createVirtualDom(
isJSExpression(_children) ? parseExpression(_children, self) : _children,
self,
{
schema,
Comp,
},
);
}
const renderComp = (props) => engine.createElement(Comp, props, child);
// 设计模式下的特殊处理
if (engine && [DESIGN_MODE.EXTEND, DESIGN_MODE.BORDER].includes(engine.props.designMode)) {
// 对于overlay,dialog等组件为了使其在设计模式下显示外层需要增加一个div容器
if (OVERLAY_LIST.includes(schema.componentName)) {
const { ref, ...overlayProps } = otherProps;
return (
<Div ref={ref} __designMode={engine.props.designMode}>
{renderComp({ ...props, ...overlayProps })}
</Div>
);
}
// 虚拟dom显示
if (componentInfo && componentInfo.parentRule) {
const parentList = componentInfo.parentRule.split(',');
const { schema: parentSchema, Comp: parentComp } = parentInfo;
if (
!parentList.includes(parentSchema.componentName) ||
parentComp !== components[parentSchema.componentName]
) {
props.__componentName = schema.componentName;
Comp = VisualDom;
} else {
// 若虚拟dom在正常的渲染上下文中就不显示设计模式了
props.__disableDesignMode = true;
}
}
}
return renderComp({ ...props, ...otherProps });
} catch (e) {
return engine.createElement(engine.getFaultComponent(), {
error: e,
schema,
self,
parentInfo,
idx,
});
}
};
__createLoopVirtualDom = (schema, self, parentInfo, idx) => {
if (isFileSchema(schema)) {
console.warn('file type not support Loop');
return null;
}
if (!Array.isArray(schema.loop)) return null;
const itemArg = (schema.loopArgs && schema.loopArgs[0]) || 'item';
const indexArg = (schema.loopArgs && schema.loopArgs[1]) || 'index';
return schema.loop.map((item, i) => {
const loopSelf = {
[itemArg]: item,
[indexArg]: i,
};
loopSelf.__proto__ = self;
return this.__createVirtualDom(
{
...schema,
loop: undefined,
},
loopSelf,
parentInfo,
idx ? `${idx}_${i}` : i,
);
});
};
__parseProps = (props, self, path, info) => {
const { schema, Comp, componentInfo = {} } = info;
const propInfo = getValue(componentInfo.props, path);
// FIXME! 将这行逻辑外置,解耦,线上环境不要验证参数,调试环境可以有,通过传参自定义
const propType = propInfo && propInfo.extra && propInfo.extra.propType;
const ignoreParse = schema.__ignoreParse || [];
const checkProps = (value) => {
if (!propType) return value;
return checkPropTypes(value, path, propType, componentInfo.name) ? value : undefined;
};
const parseReactNode = (data, params) => {
if (isEmpty(params)) {
return checkProps(this.__createVirtualDom(data, self, { schema, Comp }));
}
return checkProps(function () {
const args = {};
if (Array.isArray(params) && params.length) {
params.forEach((item, idx) => {
if (typeof item === 'string') {
args[item] = arguments[idx];
} else if (item && typeof item === 'object') {
args[item.name] = arguments[idx];
}
});
}
args.__proto__ = self;
return self.__createVirtualDom(data, args, { schema, Comp });
});
};
// 判断是否需要解析变量
if (
ignoreParse.some((item) => {
if (item instanceof RegExp) {
return item.test(path);
}
return item === path;
})
) {
return checkProps(props);
}
if (isJSExpression(props)) {
props = parseExpression(props, self);
// 只有当变量解析出来为模型结构的时候才会继续解析
if (!isSchema(props) && !isJSSlot(props)) return checkProps(props);
}
if (isJSFunction(props)) {
props = transformStringToFunction(props.value);
}
if (isJSSlot(props)) {
const { params, value } = props;
if (!isSchema(value) || isEmpty(value)) return undefined;
return parseReactNode(value, params);
}
// 兼容通过componentInfo判断的情况
if (isSchema(props)) {
const isReactNodeFunction = !!(
propInfo
&& propInfo.type === 'ReactNode'
&& propInfo.props
&& propInfo.props.type === 'function'
);
const isMixinReactNodeFunction = !!(
propInfo
&& propInfo.type === 'Mixin'
&& propInfo.props
&& propInfo.props.types
&& propInfo.props.types.indexOf('ReactNode') > -1
&& propInfo.props.reactNodeProps
&& propInfo.props.reactNodeProps.type === 'function'
);
return parseReactNode(
props,
isReactNodeFunction
? propInfo.props.params
: isMixinReactNodeFunction
? propInfo.props.reactNodeProps.params
: null,
);
}
if (Array.isArray(props)) {
return checkProps(props.map((item, idx) => this.__parseProps(item, self, path ? `${path}.${idx}` : idx, info)));
}
if (typeof props === 'function') {
return checkProps(props.bind(self));
}
if (props && typeof props === 'object') {
if (props.$$typeof) return checkProps(props);
const res = {};
forEach(props, (val, key) => {
if (key.startsWith('__')) {
res[key] = val;
return;
}
res[key] = this.__parseProps(val, self, path ? `${path}.${key}` : key, info);
});
return checkProps(res);
}
if (typeof props === 'string') {
return checkProps(props.trim());
}
return checkProps(props);
};
get requestHandlersMap() {
return this.appHelper && this.appHelper.requestHandlersMap;
}
get utils() {
return this.appHelper && this.appHelper.utils;
}
get constants() {
return this.appHelper && this.appHelper.constants;
}
get history() {
return this.appHelper && this.appHelper.history;
}
get location() {
return this.appHelper && this.appHelper.location;
}
get match() {
return this.appHelper && this.appHelper.match;
}
render() {
return null;
}
}

View File

@ -1,146 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import Debug from 'debug';
import classnames from 'classnames';
// import Loading from '@alifd/next/lib/loading';
// import '@alifd/next/lib/loading/style';
import BaseRenderer from './base';
import AppContext from '../context/appContext';
import { isSchema, getFileCssName } from '../utils';
const debug = Debug('renderer:block');
export default class BlockRenderer extends BaseRenderer {
static dislayName = 'block-renderer';
static propTypes = {
__schema: PropTypes.object,
};
static defaultProps = {
__schema: {},
};
static getDerivedStateFromProps(props, state) {
debug('block.getDerivedStateFromProps');
const func = props.__schema.lifeCycles && props.__schema.lifeCycles.getDerivedStateFromProps;
if (func) {
return func(props, state);
}
return null;
}
constructor(props, context) {
super(props, context);
this.__generateCtx();
const schema = props.__schema || {};
this.state = this.__parseData(schema.state || {});
this.__initDataSource(props);
this.__setLifeCycleMethods('constructor', arguments);
debug(`block.constructor - ${schema.fileName}`);
}
async getSnapshotBeforeUpdate() {
super.getSnapshotBeforeUpdate(...arguments);
debug(`block.getSnapshotBeforeUpdate - ${this.props.__schema.fileName}`);
}
async componentDidMount() {
super.componentDidMount(...arguments);
debug(`block.componentDidMount - ${this.props.__schema.fileName}`);
}
async componentDidUpdate() {
super.componentDidUpdate(...arguments);
debug(`block.componentDidUpdate - ${this.props.__schema.fileName}`);
}
async componentWillUnmount() {
super.componentWillUnmount(...arguments);
debug(`block.componentWillUnmount - ${this.props.__schema.fileName}`);
}
async componentDidCatch() {
await super.componentDidCatch(...arguments);
debug(`block.componentDidCatch - ${this.props.__schema.fileName}`);
}
render() {
const { __schema, __components } = this.props;
if (!isSchema(__schema, true) || (__schema.componentName !== 'Block' && __schema.componentName !== 'Div')) {
return '区块schema结构异常';
}
debug(`block.render - ${__schema.fileName}`);
this.__generateCtx();
this.__render();
const props = this.__parseData(__schema.props);
const { id, className, style, autoLoading, defaultHeight = 300, loading } = props;
const { Block } = __components;
if (Block) {
const { engine } = this.context || {};
return (
<AppContext.Provider
value={{
...this.context,
blockContext: this,
}}
>
{engine.createElement(
Block,
{
...props,
ref: this.__getRef,
className: classnames(getFileCssName(__schema.fileName), className, this.props.className),
__id: __schema.id,
},
this.__createDom(),
)}
</AppContext.Provider>
);
}
const renderContent = () => (
<AppContext.Provider
value={{
...this.context,
blockContext: this,
}}
>
{this.__createDom()}
</AppContext.Provider>
);
// if (autoLoading || loading !== undefined) {
// return (
// <Loading
// size="medium"
// visible={!!(this.__showPlaceholder || loading)}
// style={{
// height: this.__showPlaceholder ? defaultHeight : 'auto',
// display: 'block',
// ...style,
// }}
// className={classnames('luna-block', getFileCssName(__schema.fileName), className, this.props.className)}
// id={id}
// >
// {!this.__showPlaceholder && renderContent()}
// </Loading>
// );
// }
return (
<div
ref={this.__getRef}
className={classnames('luna-block', getFileCssName(__schema.fileName), className, this.props.className)}
id={id}
style={style}
>
{renderContent()}
</div>
);
}
}

View File

@ -1,129 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import Debug from 'debug';
import classnames from 'classnames';
// import Loading from '@alifd/next/lib/loading';
// import '@alifd/next/lib/loading/style';
import AppContext from '../context/appContext';
import BaseRenderer from './base';
import { isSchema, getFileCssName } from '../utils';
const debug = Debug('renderer:comp');
export default class CompRenderer extends BaseRenderer {
static dislayName = 'comp-renderer';
static propTypes = {
__schema: PropTypes.object,
};
static defaultProps = {
__schema: {},
};
static getDerivedStateFromProps(props, state) {
debug('comp.getDerivedStateFromProps');
const func = props.__schema.lifeCycles && props.__schema.lifeCycles.getDerivedStateFromProps;
if (func) {
return func(props, state);
}
return null;
}
constructor(props, context) {
super(props, context);
this.__generateCtx({
component: this,
});
const schema = props.__schema || {};
this.state = this.__parseData(schema.state || {});
this.__initDataSource(props);
this.__setLifeCycleMethods('constructor', arguments);
debug(`comp.constructor - ${schema.fileName}`);
}
async getSnapshotBeforeUpdate() {
super.getSnapshotBeforeUpdate(...arguments);
debug(`comp.getSnapshotBeforeUpdate - ${this.props.__schema.fileName}`);
}
async componentDidMount() {
super.componentDidMount(...arguments);
debug(`comp.componentDidMount - ${this.props.__schema.fileName}`);
}
async componentDidUpdate() {
super.componentDidUpdate(...arguments);
debug(`comp.componentDidUpdate - ${this.props.__schema.fileName}`);
}
async componentWillUnmount() {
super.componentWillUnmount(...arguments);
debug(`comp.componentWillUnmount - ${this.props.__schema.fileName}`);
}
async componentDidCatch() {
super.componentDidCatch(...arguments);
debug(`comp.componentDidCatch - ${this.props.__schema.fileName}`);
}
render() {
const { __schema } = this.props;
if (!isSchema(__schema, true) || __schema.componentName !== 'Component') {
return '自定义组件schema结构异常';
}
debug(`comp.render - ${__schema.fileName}`);
this.__generateCtx({
component: this,
});
this.__render();
const { id, className, style, noContainer, autoLoading, defaultHeight = 300, loading } = this.__parseData(
__schema.props,
);
const renderContent = () => (
<AppContext.Provider
value={{
...this.context,
compContext: this,
blockContext: this,
}}
>
{this.__createDom()}
</AppContext.Provider>
);
if (noContainer) {
return renderContent();
}
// if (autoLoading || loading !== undefined) {
// return (
// <Loading
// size="medium"
// visible={!!(this.__showPlaceholder || loading)}
// style={{
// height: this.__showPlaceholder ? defaultHeight : 'auto',
// display: 'block',
// ...style,
// }}
// className={classnames('luna-comp', getFileCssName(__schema.fileName), className, this.props.className)}
// id={this.props.id || id}
// >
// {!this.__showPlaceholder && renderContent()}
// </Loading>
// );
// }
return (
<div
ref={this.__getRef}
className={classnames('luna-comp', getFileCssName(__schema.fileName), className, this.props.className)}
id={this.props.id || id}
style={{ ...style, ...this.props.style }}
>
{renderContent()}
</div>
);
}
}

View File

@ -1,166 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import Debug from 'debug';
import classnames from 'classnames';
// import Loading from '@alifd/next/lib/loading';
// import '@alifd/next/lib/loading/style';
import AppContext from '../context/appContext';
import BaseRenderer from './base';
import { isSchema, getFileCssName, parseData } from '../utils';
const debug = Debug('renderer:page');
export default class PageRenderer extends BaseRenderer {
static dislayName = 'page-renderer';
static propTypes = {
__schema: PropTypes.object,
};
static defaultProps = {
__schema: {},
};
static getDerivedStateFromProps(props, state) {
debug('page.getDerivedStateFromProps');
const func = props.__schema.lifeCycles && props.__schema.lifeCycles.getDerivedStateFromProps;
if (func) {
return func(props, state);
}
return null;
}
constructor(props, context) {
super(props, context);
this.__generateCtx({
page: this,
});
const schema = props.__schema || {};
this.state = this.__parseData(schema.state || {});
this.__initDataSource(props);
this.__setLifeCycleMethods('constructor', arguments);
debug(`page.constructor - ${schema.fileName}`);
}
async getSnapshotBeforeUpdate() {
super.getSnapshotBeforeUpdate(...arguments);
debug(`page.getSnapshotBeforeUpdate - ${this.props.__schema.fileName}`);
}
async componentDidMount() {
super.componentDidMount(...arguments);
debug(`page.componentDidMount - ${this.props.__schema.fileName}`);
}
async componentDidUpdate(prevProps) {
const { __ctx } = this.props;
const prevState = parseData(prevProps.__schema.state, __ctx);
const newState = parseData(this.props.__schema.state, __ctx);
// 当编排的时候修改schema.state值需要将最新schema.state值setState
if (JSON.stringify(newState) != JSON.stringify(prevState)) {
this.setState(newState);
}
super.componentDidUpdate(...arguments);
debug(`page.componentDidUpdate - ${this.props.__schema.fileName}`);
}
async componentWillUnmount() {
super.componentWillUnmount(...arguments);
debug(`page.componentWillUnmount - ${this.props.__schema.fileName}`);
}
async componentDidCatch() {
await super.componentDidCatch(...arguments);
debug(`page.componentDidCatch - ${this.props.__schema.fileName}`);
}
render() {
const { __schema, __components } = this.props;
if (!isSchema(__schema, true) || __schema.componentName !== 'Page') {
return '页面schema结构异常';
}
debug(`page.render - ${__schema.fileName}`);
this.__bindCustomMethods(this.props);
this.__initDataSource(this.props);
// this.__setLifeCycleMethods('constructor', arguments);
this.__generateCtx({
page: this,
});
this.__render();
const props = this.__parseData(__schema.props);
const { id, className, style, autoLoading, defaultHeight = 300, loading,
} = props;
const { Page } = __components;
if (Page) {
const { engine } = this.context || {};
return (
<AppContext.Provider
value={{
...this.context,
pageContext: this,
blockContext: this,
}}
>
{engine.createElement(
Page,
{
...props,
ref: this.__getRef,
className: classnames(getFileCssName(__schema.fileName), className, this.props.className),
__id: __schema.id,
},
this.__createDom(),
)}
</AppContext.Provider>
);
}
const renderContent = () => (
<AppContext.Provider
value={{
...this.context,
pageContext: this,
blockContext: this,
}}
>
{this.__createDom()}
</AppContext.Provider>
);
// if (autoLoading || loading !== undefined) {
// return (
// <Loading
// size="medium"
// visible={!!(this.__showPlaceholder || loading)}
// style={{
// height: this.__showPlaceholder ? defaultHeight : 'auto',
// display: 'block',
// ...style,
// }}
// className={classnames('luna-page', getFileCssName(__schema.fileName), className, this.props.className)}
// id={id}
// >
// {!this.__showPlaceholder && renderContent()}
// </Loading>
// );
// }
return (
<div
ref={this.__getRef}
className={classnames('luna-page', getFileCssName(__schema.fileName), className, this.props.className)}
id={id}
style={style}
>
{renderContent()}
</div>
);
}
}

View File

@ -1,73 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import Debug from 'debug';
import AppContext from '../context/appContext';
import BaseRenderer from './base';
import { isSchema } from '../utils';
const debug = Debug('renderer:temp');
export default class TempRenderer extends BaseRenderer {
static dislayName = 'temp-renderer';
static propTypes = {
__ctx: PropTypes.object,
__schema: PropTypes.object,
};
static defaultProps = {
__ctx: {},
__schema: {},
};
constructor(props, context) {
super(props, context);
this.state = {};
this.cacheSetState = {};
debug(`temp.constructor - ${props.__schema.fileName}`);
}
componentDidMount() {
const ctx = this.props.__ctx;
if (!ctx) return;
const { setState } = ctx;
this.cacheSetState = setState;
ctx.setState = (...args) => {
setState.call(ctx, ...args);
setTimeout(() => this.forceUpdate(), 0);
};
debug(`temp.componentDidMount - ${this.props.__schema.fileName}`);
}
componentDidUpdate() {
debug(`temp.componentDidUpdate - ${this.props.__schema.fileName}`);
}
componentWillUnmount() {
const ctx = this.props.__ctx;
if (!ctx || !this.cacheSetState) return;
ctx.setState = this.cacheSetState;
delete this.cacheSetState;
debug(`temp.componentWillUnmount - ${this.props.__schema.fileName}`);
}
componentDidCatch(e) {
console.warn(e);
debug(`temp.componentDidCatch - ${this.props.__schema.fileName}`);
}
render() {
const { __schema, __ctx } = this.props;
if (!isSchema(__schema, true) || __schema.componentName !== 'Temp') {
return '下钻编辑 schema 结构异常!';
}
debug(`temp.render - ${__schema.fileName}`);
return (
<div ref={this.__getRef} className="luna-temp">
<AppContext.Provider value={{ ...this.context, ...__ctx }}>{this.__createDom()}</AppContext.Provider>
</div>
);
}
}

View File

@ -0,0 +1,865 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`React Renderer render basic case 1`] = `
<div
className="lce-page luna-test"
style={
Object {
"padding": "0 5px 0 5px",
}
}
>
<div
__id="node_dockcy8n9xed"
className="next-box"
style={
Object {
"backgroundColor": "rgba(31,56,88,0.1)",
"flexDirection": "column",
"flexWrap": "nowrap",
"msFlexDirection": "column",
"msFlexWrap": "none",
"padding": "12px 12px 12px 12px",
}
}
>
<div
__id="node_dockcy8n9xee"
className="next-box"
style={
Object {
"backgroundColor": "#ffffff",
"flexDirection": "column",
"flexWrap": "nowrap",
"msFlexDirection": "column",
"msFlexWrap": "none",
"padding": "12px 12px 12px 12px",
}
}
>
<nav
__id="node_dockcy8n9xef"
aria-label="Breadcrumb"
style={
Object {
"position": "relative",
}
}
>
<ul
className="next-breadcrumb"
>
<li
className="next-breadcrumb-item"
dir={null}
>
<span
__id="node_dockcy8n9xeg"
className="next-breadcrumb-text"
>
首页
</span>
<span
className="next-breadcrumb-separator"
>
<i
className="next-icon next-icon-arrow-right next-medium next-breadcrumb-icon-sep"
style={Object {}}
/>
</span>
</li>
<li
className="next-breadcrumb-item"
dir={null}
>
<span
__id="node_dockcy8n9xei"
className="next-breadcrumb-text"
>
品质中台
</span>
<span
className="next-breadcrumb-separator"
>
<i
className="next-icon next-icon-arrow-right next-medium next-breadcrumb-icon-sep"
style={Object {}}
/>
</span>
</li>
<li
className="next-breadcrumb-item"
dir={null}
>
<span
__id="node_dockcy8n9xek"
className="next-breadcrumb-text"
>
商家品质页面管理
</span>
<span
className="next-breadcrumb-separator"
>
<i
className="next-icon next-icon-arrow-right next-medium next-breadcrumb-icon-sep"
style={Object {}}
/>
</span>
</li>
<li
className="next-breadcrumb-item"
dir={null}
>
<span
__id="node_dockcy8n9xem"
aria-current="page"
className="next-breadcrumb-text activated"
>
质检知识条配置
</span>
</li>
</ul>
</nav>
</div>
<div
__id="node_dockcy8n9xeo"
className="next-box"
style={
Object {
"backgroundColor": "#ffffff",
"flexDirection": "column",
"flexWrap": "nowrap",
"marginTop": "12px",
"msFlexDirection": "column",
"msFlexWrap": "none",
}
}
>
<form
__events={Array []}
__id="node_dockcy8n9xep"
className="next-form next-inline next-medium"
onSubmit={[Function]}
role="grid"
style={
Object {
"marginLeft": "12px",
"marginRight": "12px",
"marginTop": "12px",
}
}
>
<div
__id="node_dockcy8n9xeq"
className="next-form-item next-left next-medium"
style={
Object {
"marginBottom": "0",
}
}
>
<div
className="next-form-item-label"
>
<label>
类目名:
</label>
</div>
<div
className="next-form-item-control"
>
<span
aria-haspopup={true}
className="next-select next-select-trigger next-select-single next-medium next-inactive next-no-search"
onClick={[Function]}
onKeyDown={[Function]}
onMouseDown={[Function]}
onMouseEnter={[Function]}
onMouseLeave={[Function]}
style={
Object {
"width": "150px",
}
}
>
<span
className="next-input next-medium next-select-inner"
>
<span
className="next-select-values next-input-text-field"
>
<span
className="next-select-trigger-search"
>
<input
__id="node_dockcy8n9xer"
autoComplete="off"
disabled={false}
height="100%"
maxLength={null}
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
placeholder="请选择"
readOnly={true}
role="combobox"
size="1"
tabIndex={0}
value=""
/>
<span
aria-hidden={true}
>
<span>
请选择
</span>
<span>
 
</span>
</span>
</span>
</span>
<span
className="next-input-control"
>
<span
aria-hidden={true}
className="next-select-arrow"
onClick={[Function]}
>
<i
className="next-icon next-icon-arrow-down next-medium next-select-symbol-fold"
style={Object {}}
/>
</span>
</span>
</span>
<span
aria-live="polite"
className="next-sr-only"
>
</span>
</span>
</div>
</div>
<div
__id="node_dockcy8n9xes"
className="next-form-item next-left next-medium"
style={
Object {
"marginBottom": "0",
}
}
>
<div
className="next-form-item-label"
>
<label>
项目类型:
</label>
</div>
<div
className="next-form-item-control"
>
<span
aria-haspopup={true}
className="next-select next-select-trigger next-select-single next-medium next-inactive next-no-search"
onClick={[Function]}
onKeyDown={[Function]}
onMouseDown={[Function]}
onMouseEnter={[Function]}
onMouseLeave={[Function]}
style={
Object {
"width": "200px",
}
}
>
<span
className="next-input next-medium next-select-inner"
>
<span
className="next-select-values next-input-text-field"
>
<span
className="next-select-trigger-search"
>
<input
__id="node_dockcy8n9xet"
autoComplete="off"
disabled={false}
height="100%"
maxLength={null}
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
placeholder="请选择"
readOnly={true}
role="combobox"
size="1"
tabIndex={0}
value=""
/>
<span
aria-hidden={true}
>
<span>
请选择
</span>
<span>
 
</span>
</span>
</span>
</span>
<span
className="next-input-control"
>
<span
aria-hidden={true}
className="next-select-arrow"
onClick={[Function]}
>
<i
className="next-icon next-icon-arrow-down next-medium next-select-symbol-fold"
style={Object {}}
/>
</span>
</span>
</span>
<span
aria-live="polite"
className="next-sr-only"
>
</span>
</span>
</div>
</div>
<div
__id="node_dockcy8n9xeu"
className="next-form-item next-left next-medium"
style={
Object {
"marginBottom": "0",
}
}
>
<div
className="next-form-item-label"
>
<label>
项目 ID
</label>
</div>
<div
className="next-form-item-control"
>
<span
className="next-input next-medium"
style={
Object {
"width": "200px",
}
}
>
<input
__id="node_dockcy8n9xev"
autoComplete="off"
disabled={false}
height="100%"
maxLength={null}
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
readOnly={false}
value=""
/>
</span>
</div>
</div>
<div
__id="node_dockcy8n9xew"
className="next-btn-group"
>
<button
__id="node_dockcy8n9xex"
className="next-btn next-medium next-btn-primary"
disabled={false}
onClick={[Function]}
onMouseUp={[Function]}
style={
Object {
"margin": "0 5px 0 5px",
}
}
type="submit"
>
<span
className="next-btn-helper"
>
搜索
</span>
</button>
<button
__id="node_dockcy8n9xe10"
className="next-btn next-medium next-btn-normal"
disabled={false}
onClick={[Function]}
onMouseUp={[Function]}
style={
Object {
"margin": "0 5px 0 5px",
}
}
type="reset"
>
<span
className="next-btn-helper"
>
清空
</span>
</button>
</div>
</form>
</div>
<div
__id="node_dockcy8n9xe1f"
className="next-box"
style={
Object {
"backgroundColor": "#ffffff",
"display": "flex",
"flexDirection": "row",
"flexWrap": "nowrap",
"justifyContent": "flex-end",
"msFlexDirection": "column",
"msFlexWrap": "none",
"paddingBottom": "24px",
}
}
>
<button
__events={
Array [
Object {
"name": "onClick",
"relatedEventName": "onClick",
"type": "componentEvent",
},
]
}
__id="node_dockd5nrh9p4"
className="next-btn next-medium next-btn-primary"
disabled={false}
onClick={[Function]}
onMouseUp={[Function]}
style={Object {}}
type="button"
>
<span
className="next-btn-helper"
>
新建配置
</span>
</button>
</div>
<div
__id="node_dockd5nrh9p5"
className="next-box"
style={
Object {
"flexDirection": "column",
"flexWrap": "nowrap",
"msFlexDirection": "column",
"msFlexWrap": "none",
}
}
>
<div
__id="node_dockjielosj1"
actionBar={
Array [
Object {
"title": "新增",
"type": "primary",
},
Object {
"title": "编辑",
},
]
}
actionColumn={
Array [
Object {
"callback": [Function],
"device": Array [
"desktop",
],
"title": "编辑",
},
Object {
"callback": [Function],
"mode": "EDIT",
"title": "保存",
},
]
}
actionFixed="right"
actionHidden={false}
actionTitle="操作"
actionType="link"
actionWidth={180}
className="next-table next-table-medium"
data={
Array [
Object {
"age": 15000,
"email": "aaa@abc.com",
"id": "1",
"name": "王小",
},
Object {
"age": 25000,
"email": "bbb@abc.com",
"id": "2",
"name": "王中",
},
Object {
"age": 35000,
"email": "ccc@abc.com",
"id": "3",
"name": "王大",
},
]
}
maxWebShownActionCount={2}
showActionBar={true}
showMiniPager={true}
style={Object {}}
>
<table
role="table"
style={
Object {
"width": undefined,
}
}
>
<colgroup>
<col
style={
Object {
"width": 200,
}
}
/>
<col
style={
Object {
"width": 200,
}
}
/>
<col
style={
Object {
"width": 200,
}
}
/>
</colgroup>
<thead
className="next-table-header"
>
<tr>
<th
className="next-table-cell next-table-header-node"
dataKey="name"
editType="text"
role="gridcell"
rowSpan={1}
style={
Object {
"textAlign": "center",
}
}
>
<div
className="next-table-cell-wrapper"
data-next-table-col={0}
>
姓名
</div>
</th>
<th
className="next-table-cell next-table-header-node"
dataKey="age"
role="gridcell"
rowSpan={1}
style={
Object {
"textAlign": "center",
}
}
>
<div
className="next-table-cell-wrapper"
data-next-table-col={1}
>
年龄
</div>
</th>
<th
className="next-table-cell next-table-header-node"
dataKey="email"
role="gridcell"
rowSpan={1}
style={
Object {
"textAlign": "center",
}
}
>
<div
className="next-table-cell-wrapper"
data-next-table-col={2}
>
邮箱
</div>
</th>
</tr>
</thead>
<tbody
className="next-table-body"
>
<tr>
<td
colSpan={3}
>
<div
className="next-table-empty"
style={
Object {
"left": 0,
"overflow": "hidden",
"position": "sticky",
"width": -2,
}
}
>
没有数据
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div
__id="node_dockd5nrh9pg"
className="next-box"
style={
Object {
"display": "flex",
"flexDirection": "row",
"flexWrap": "nowrap",
"justifyContent": "flex-end",
"msFlexDirection": "column",
"msFlexWrap": "none",
}
}
>
<div
__id="node_dockd5nrh9pf"
className="next-pagination next-medium next-normal"
style={Object {}}
>
<div
className="next-pagination-pages"
>
<button
aria-label="上一页当前第1页"
className="next-btn next-medium next-btn-normal next-pagination-item next-prev"
disabled={true}
onClick={[Function]}
onMouseUp={[Function]}
type="button"
>
<i
className="next-icon next-icon-arrow-left next-xs next-btn-icon next-icon-first next-pagination-icon-prev"
style={Object {}}
/>
<span
className="next-btn-helper"
>
上一页
</span>
</button>
<div
className="next-pagination-list"
>
<button
aria-label="第1页共10页"
className="next-btn next-medium next-btn-normal next-pagination-item next-current"
disabled={false}
onClick={[Function]}
onMouseUp={[Function]}
type="button"
>
<span
className="next-btn-helper"
>
1
</span>
</button>
<button
aria-label="第2页共10页"
className="next-btn next-medium next-btn-normal next-pagination-item"
disabled={false}
onClick={[Function]}
onMouseUp={[Function]}
type="button"
>
<span
className="next-btn-helper"
>
2
</span>
</button>
<button
aria-label="第3页共10页"
className="next-btn next-medium next-btn-normal next-pagination-item"
disabled={false}
onClick={[Function]}
onMouseUp={[Function]}
type="button"
>
<span
className="next-btn-helper"
>
3
</span>
</button>
<button
aria-label="第4页共10页"
className="next-btn next-medium next-btn-normal next-pagination-item"
disabled={false}
onClick={[Function]}
onMouseUp={[Function]}
type="button"
>
<span
className="next-btn-helper"
>
4
</span>
</button>
<i
className="next-icon next-icon-ellipsis next-medium next-pagination-ellipsis next-pagination-icon-ellipsis"
style={Object {}}
/>
<button
aria-label="第10页共10页"
className="next-btn next-medium next-btn-normal next-pagination-item"
disabled={false}
onClick={[Function]}
onMouseUp={[Function]}
type="button"
>
<span
className="next-btn-helper"
>
10
</span>
</button>
</div>
<button
aria-label="下一页当前第1页"
className="next-btn next-medium next-btn-normal next-pagination-item next-next"
disabled={false}
onClick={[Function]}
onMouseUp={[Function]}
type="button"
>
<span
className="next-btn-helper"
>
下一页
</span>
<i
className="next-icon next-icon-arrow-right next-xs next-btn-icon next-icon-last next-pagination-icon-next"
style={Object {}}
/>
</button>
<span
className="next-pagination-display"
>
<em>
1
</em>
/
10
</span>
<span
className="next-pagination-jump-text"
>
到第
</span>
<span
className="next-input next-medium next-pagination-jump-input"
>
<input
aria-label="请输入跳转到第几页"
autoComplete="off"
disabled={false}
height="100%"
maxLength={null}
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
readOnly={false}
value=""
/>
</span>
<span
className="next-pagination-jump-text"
>
</span>
<button
className="next-btn next-medium next-btn-normal next-pagination-jump-go"
disabled={false}
onClick={[Function]}
onMouseUp={[Function]}
type="button"
>
<span
className="next-btn-helper"
>
确定
</span>
</button>
</div>
</div>
</div>
</div>
</div>
<div
__id="node_dockd5nrh9pr"
name="error"
>
Component Not Found
</div>
</div>
`;

View File

@ -0,0 +1,567 @@
export default{
"componentName": "Page",
"id": "node_dockcviv8fo1",
"props": {
"ref": "outterView",
"autoLoading": true,
"style": {
"padding": "0 5px 0 5px"
}
},
"fileName": "test",
"dataSource": {
"list": []
},
"state": {
"text": "outter",
"isShowDialog": false
},
"css": "body {font-size: 12px;} .botton{width:100px;color:#ff00ff}",
"lifeCycles": {
"componentDidMount": {
"type": "JSFunction",
"value": "function() {\n console.log('did mount');\n }"
},
"componentWillUnmount": {
"type": "JSFunction",
"value": "function() {\n console.log('will umount');\n }"
}
},
"methods": {
"testFunc": {
"type": "JSFunction",
"value": "function() {\n console.log('test func');\n }"
},
"onClick": {
"type": "JSFunction",
"value": "function() {\n this.setState({\n isShowDialog: true\n })\n }"
},
"closeDialog": {
"type": "JSFunction",
"value": "function() {\n this.setState({\n isShowDialog: false\n })\n }"
}
},
"children": [
{
"componentName": "Box",
"id": "node_dockcy8n9xed",
"props": {
"style": {
"backgroundColor": "rgba(31,56,88,0.1)",
"padding": "12px 12px 12px 12px"
}
},
"children": [
{
"componentName": "Box",
"id": "node_dockcy8n9xee",
"props": {
"style": {
"padding": "12px 12px 12px 12px",
"backgroundColor": "#ffffff"
}
},
"children": [
{
"componentName": "Breadcrumb",
"id": "node_dockcy8n9xef",
"props": {
"prefix": "next-",
"maxNode": 100,
"component": "nav"
},
"children": [
{
"componentName": "Breadcrumb.Item",
"id": "node_dockcy8n9xeg",
"props": {
"prefix": "next-",
"children": "首页"
}
},
{
"componentName": "Breadcrumb.Item",
"id": "node_dockcy8n9xei",
"props": {
"prefix": "next-",
"children": "品质中台"
}
},
{
"componentName": "Breadcrumb.Item",
"id": "node_dockcy8n9xek",
"props": {
"prefix": "next-",
"children": "商家品质页面管理"
}
},
{
"componentName": "Breadcrumb.Item",
"id": "node_dockcy8n9xem",
"props": {
"prefix": "next-",
"children": "质检知识条配置"
}
}
]
}
]
},
{
"componentName": "Box",
"id": "node_dockcy8n9xeo",
"props": {
"style": {
"marginTop": "12px",
"backgroundColor": "#ffffff"
}
},
"children": [
{
"componentName": "Form",
"id": "node_dockcy8n9xep",
"props": {
"inline": true,
"style": {
"marginTop": "12px",
"marginRight": "12px",
"marginLeft": "12px"
},
"__events": []
},
"children": [
{
"componentName": "Form.Item",
"id": "node_dockcy8n9xeq",
"props": {
"style": {
"marginBottom": "0"
},
"label": "类目名:"
},
"children": [
{
"componentName": "Select",
"id": "node_dockcy8n9xer",
"props": {
"mode": "single",
"hasArrow": true,
"cacheValue": true,
"style": {
"width": "150px"
}
}
}
]
},
{
"componentName": "Form.Item",
"id": "node_dockcy8n9xes",
"props": {
"style": {
"marginBottom": "0"
},
"label": "项目类型:"
},
"children": [
{
"componentName": "Select",
"id": "node_dockcy8n9xet",
"props": {
"mode": "single",
"hasArrow": true,
"cacheValue": true,
"style": {
"width": "200px"
}
}
}
]
},
{
"componentName": "Form.Item",
"id": "node_dockcy8n9xeu",
"props": {
"style": {
"marginBottom": "0"
},
"label": "项目 ID"
},
"children": [
{
"componentName": "Input",
"id": "node_dockcy8n9xev",
"props": {
"hasBorder": true,
"size": "medium",
"autoComplete": "off",
"style": {
"width": "200px"
}
}
}
]
},
{
"componentName": "Button.Group",
"id": "node_dockcy8n9xew",
"props": {},
"children": [
{
"componentName": "Button",
"id": "node_dockcy8n9xex",
"props": {
"type": "primary",
"style": {
"margin": "0 5px 0 5px"
},
"htmlType": "submit",
"children": "搜索"
}
},
{
"componentName": "Button",
"id": "node_dockcy8n9xe10",
"props": {
"type": "normal",
"style": {
"margin": "0 5px 0 5px"
},
"htmlType": "reset",
"children": "清空"
}
}
]
}
]
}
]
},
{
"componentName": "Box",
"id": "node_dockcy8n9xe1f",
"props": {
"style": {
"backgroundColor": "#ffffff",
"paddingBottom": "24px",
"display": "flex",
"flexDirection": "row",
"justifyContent": "flex-end"
}
},
"children": [
{
"componentName": "Button",
"id": "node_dockd5nrh9p4",
"props": {
"type": "primary",
"size": "medium",
"htmlType": "button",
"component": "button",
"children": "新建配置",
"style": {},
"__events": [
{
"type": "componentEvent",
"name": "onClick",
"relatedEventName": "onClick"
}
],
"onClick": {
"type": "JSFunction",
"value": "function(){ this.onClick() }"
}
}
}
]
},
{
"componentName": "Box",
"id": "node_dockd5nrh9p5",
"props": {},
"children": [
{
"componentName": "Table",
"id": "node_dockjielosj1",
"props": {
"showMiniPager": true,
"showActionBar": true,
"actionBar": [
{
"title": "新增",
"type": "primary"
},
{
"title": "编辑"
}
],
"columns": [
{
"dataKey": "name",
"width": 200,
"align": "center",
"title": "姓名",
"editType": "text"
},
{
"dataKey": "age",
"width": 200,
"align": "center",
"title": "年龄"
},
{
"dataKey": "email",
"width": 200,
"align": "center",
"title": "邮箱"
}
],
"data": [
{
"name": "王小",
"id": "1",
"age": 15000,
"email": "aaa@abc.com"
},
{
"name": "王中",
"id": "2",
"age": 25000,
"email": "bbb@abc.com"
},
{
"name": "王大",
"id": "3",
"age": 35000,
"email": "ccc@abc.com"
}
],
"actionTitle": "操作",
"actionWidth": 180,
"actionType": "link",
"actionFixed": "right",
"actionHidden": false,
"maxWebShownActionCount": 2,
"actionColumn": [
{
"title": "编辑",
"callback": {
"type": "JSFunction",
"value": "(rowData, action, table) => {\n return table.editRow(rowData).then((row) => {\n console.log(row);\n });\n }"
},
"device": [
"desktop"
]
},
{
"title": "保存",
"callback": {
"type": "JSFunction",
"value": "(rowData, action, table) => { \nreturn table.saveRow(rowData).then((row) => { \nconsole.log(row); \n}); \n}"
},
"mode": "EDIT"
}
]
}
},
{
"componentName": "Box",
"id": "node_dockd5nrh9pg",
"props": {
"style": {
"display": "flex",
"flexDirection": "row",
"justifyContent": "flex-end"
}
},
"children": [
{
"componentName": "Pagination",
"id": "node_dockd5nrh9pf",
"props": {
"prefix": "next-",
"type": "normal",
"shape": "normal",
"size": "medium",
"defaultCurrent": 1,
"total": 100,
"pageShowCount": 5,
"pageSize": 10,
"pageSizePosition": "start",
"showJump": true,
"style": {}
}
}
]
}
]
}
]
},
{
"componentName": "Dialog",
"id": "node_dockcy8n9xe1h",
"props": {
"prefix": "next-",
"footerAlign": "right",
"footerActions": [
"ok",
"cancel"
],
"closeable": "esc,close",
"hasMask": true,
"align": "cc cc",
"minMargin": 40,
"visible": {
"type": "JSExpression",
"value": "this.state.isShowDialog"
},
"title": "标题",
"events": [],
"__events": [
{
"type": "componentEvent",
"name": "onCancel",
"relatedEventName": "closeDialog"
},
{
"type": "componentEvent",
"name": "onClose",
"relatedEventName": "closeDialog"
},
{
"type": "componentEvent",
"name": "onOk",
"relatedEventName": "testFunc"
}
],
"onCancel": {
"type": "JSFunction",
"value": "function(){ this.closeDialog() }"
},
"onClose": {
"type": "JSFunction",
"value": "function(){ this.closeDialog() }"
},
"onOk": {
"type": "JSFunction",
"value": "function(){ this.testFunc() }"
}
},
"children": [
{
"componentName": "Form",
"id": "node_dockd5nrh9pi",
"props": {
"inline": false,
"labelAlign": "top",
"labelTextAlign": "right",
"size": "medium"
},
"children": [
{
"componentName": "Form.Item",
"id": "node_dockd5nrh9pj",
"props": {
"style": {
"marginBottom": "0",
"minWidth": "200px",
"minHeight": "28px"
},
"label": "商品类目"
},
"children": [
{
"componentName": "Select",
"id": "node_dockd5nrh9pk",
"props": {
"mode": "single",
"hasArrow": true,
"cacheValue": true
}
}
]
},
{
"componentName": "Form.Item",
"id": "node_dockd5nrh9pl",
"props": {
"style": {
"marginBottom": "0",
"minWidth": "200px",
"minHeight": "28px"
},
"label": "商品类目"
},
"children": [
{
"componentName": "Select",
"id": "node_dockd5nrh9pm",
"props": {
"mode": "single",
"hasArrow": true,
"cacheValue": true
}
}
]
},
{
"componentName": "Form.Item",
"id": "node_dockd5nrh9pn",
"props": {
"style": {
"marginBottom": "0",
"minWidth": "200px",
"minHeight": "28px"
},
"label": "商品类目",
"asterisk": true
},
"children": [
{
"componentName": "Select",
"id": "node_dockd5nrh9po",
"props": {
"mode": "single",
"hasArrow": true,
"cacheValue": true
}
}
]
},
{
"componentName": "Form.Item",
"id": "node_dockd5nrh9pp",
"props": {
"style": {
"marginBottom": "0",
"minWidth": "200px",
"minHeight": "28px"
},
"label": "商品类目"
},
"children": [
{
"componentName": "Input",
"id": "node_dockd5nrh9pr",
"props": {
"hasBorder": true,
"size": "medium",
"autoComplete": "off"
}
},
]
},
]
},
]
},
{
"componentName": "ErrorComponent",
"id": "node_dockd5nrh9pr",
"props": {
"name": "error"
}
}
]
}

View File

@ -0,0 +1,31 @@
import React from 'react';
import renderer from 'react-test-renderer';
import { Box, Breadcrumb, Form, Select, Input, Button, Table, Pagination, Dialog } from '@alifd/next';
import ReactRenderer from '../src';
import schema from './fixtures/schema/basic';
describe('React Renderer', () => {
it('render basic case', () => {
const components = {
Box,
Breadcrumb,
'Breadcrumb.Item': Breadcrumb.Item,
Form,
'Form.Item': Form.Item,
Select,
Input,
Button,
'Button.Group': Button.Group,
Table,
Pagination,
Dialog,
};
const content = (
<ReactRenderer
schema={schema}
components={components}
/>);
const tree = renderer.create(content).toJSON();
expect(tree).toMatchSnapshot();
});
});

View File

@ -3,22 +3,6 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
<a name="1.0.33"></a>
## [1.0.33](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.33-beta.1...v1.0.33) (2021-01-29)
**Note:** Version bump only for package @ali/lowcode-react-simulator-renderer
<a name="1.0.33-beta.1"></a>
## [1.0.33-beta.1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.33-beta.0...v1.0.33-beta.1) (2021-01-28)
**Note:** Version bump only for package @ali/lowcode-react-simulator-renderer
<a name="1.0.33-beta.0"></a> <a name="1.0.33-beta.0"></a>
## [1.0.33-beta.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.32...v1.0.33-beta.0) (2021-01-28) ## [1.0.33-beta.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.32...v1.0.33-beta.0) (2021-01-28)

View File

@ -16,10 +16,10 @@
"build": "build-scripts build --skip-demo" "build": "build-scripts build --skip-demo"
}, },
"dependencies": { "dependencies": {
"@ali/lowcode-designer": "^1.0.33", "@ali/lowcode-designer": "^1.0.33-beta.0",
"@ali/lowcode-react-renderer": "^1.0.33", "@ali/lowcode-react-renderer": "^1.0.33-beta.0",
"@ali/lowcode-types": "^1.0.33", "@ali/lowcode-types": "^1.0.33-beta.0",
"@ali/lowcode-utils": "^1.0.33", "@ali/lowcode-utils": "^1.0.33-beta.0",
"@ali/vu-css-style": "^1.0.2", "@ali/vu-css-style": "^1.0.2",
"@recore/obx": "^1.0.8", "@recore/obx": "^1.0.8",
"@recore/obx-react": "^1.0.7", "@recore/obx-react": "^1.0.7",

View File

@ -18,6 +18,7 @@ import { RootSchema, ComponentSchema, TransformStage, NodeSchema } from '@ali/lo
// import { RootSchema, NpmInfo, ComponentSchema, TransformStage, NodeSchema } from '@ali/lowcode-types'; // import { RootSchema, NpmInfo, ComponentSchema, TransformStage, NodeSchema } from '@ali/lowcode-types';
// just use types // just use types
import { BuiltinSimulatorRenderer, NodeInstance, Component, DocumentModel } from '@ali/lowcode-designer'; import { BuiltinSimulatorRenderer, NodeInstance, Component, DocumentModel } from '@ali/lowcode-designer';
import LowCodeRenderer from '@ali/lowcode-react-renderer';
import { createMemoryHistory, MemoryHistory } from 'history'; import { createMemoryHistory, MemoryHistory } from 'history';
import Slot from './builtin-components/slot'; import Slot from './builtin-components/slot';
import Leaf from './builtin-components/leaf'; import Leaf from './builtin-components/leaf';
@ -392,12 +393,37 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer {
doc.getElementsByTagName('head')[0].appendChild(s); doc.getElementsByTagName('head')[0].appendChild(s);
} }
// const node = host.currentDocument?.createNode(_schema);
// _schema = node?.export(TransformStage.Render) || {};
const renderer = this; const renderer = this;
const { componentsMap } = renderer; const { componentsMap: components } = renderer;
return getComponentController(schema, componentsMap);
class LowCodeComp extends React.Component {
render() {
// @ts-ignore
return createElement(LowCodeRenderer, {
schema: _schema,
components,
designMode: renderer.designMode,
device: renderer.device,
appHelper: renderer.context,
customCreateElement: (Comp: any, props: any, children: any) => {
const componentMeta = host.currentDocument?.getComponentMeta(Comp.displayName);
if (componentMeta?.isModal) {
return null;
}
const { __id, __designMode, ...viewProps } = props;
// mock _leaf减少性能开销
const _leaf = {
isEmpty: () => false,
};
viewProps._leaf = _leaf;
return createElement(Comp, viewProps, children);
}
});
}
}
return LowCodeComp;
} }
private _running = false; private _running = false;
@ -500,91 +526,4 @@ function checkInstanceMounted(instance: any): boolean {
return true; return true;
} }
const processPropsSchema = (propsSchema: any, propsMap: any, componentsMap: any): any => {
if (!propsSchema) {
return {};
}
const result = { ...propsSchema };
const reg = /^(?:this\.props|props)\.(\S+)$/;
Object.keys(result).map((key: string) => {
if (result[key]?.type === 'JSExpression') {
const { value } = result[key];
const matched = reg.exec(value);
if (matched) {
const propName = matched[1];
result[key] = propsMap[propName];
}
} else if (result[key]?.type === 'JSSlot') {
const schema = result[key].value;
result[key] = createElement(ComponentCreator, { schema, propsMap: {}, componentsMap });
}
});
return result;
};
class ComponentCreator extends React.Component<{ schema: any; propsMap: any, componentsMap: any }> {
private isModal: boolean;
constructor(props: any) {
super(props);
const componentMeta = host.currentDocument?.getComponentMeta(props.schema.componentName);
if (componentMeta?.isModal) {
this.isModal = true;
}
}
render() {
if (this.isModal) {
return null;
}
const { schema, propsMap, componentsMap } = this.props;
const ComponentClass = componentsMap[schema.componentName];
if (!ComponentClass) {
return null;
}
let children = null;
if (schema.children && schema.children.length > 0) {
children = schema.children.map((item: any) => createElement(ComponentCreator, { schema: item, propsMap, componentsMap }));
}
const props = processPropsSchema(schema.props, propsMap, componentsMap);
const _leaf = host.currentDocument?.createNode(schema);
return createElement(ComponentClass, { ...props, _leaf }, children);
}
}
function getComponentController(schema: NodeSchema, componentsMap: any) {
class ComponentController extends React.Component<{ schema: any }> {
renderSchema: any;
constructor(props: any) {
super(props);
const node = host.currentDocument?.createNode(schema);
this.renderSchema = node?.export(TransformStage.Render) || {};
}
// TODO: 暂时解决性能问题
shouldComponentUpdate() {
return false;
}
render() {
const { renderSchema } = this;
const { componentName } = renderSchema;
if (componentName === 'Component') {
let children = [] as any;
const propsMap = this.props || {};
if (renderSchema.children && Array.isArray(renderSchema.children)) {
children = renderSchema.children.map((item: any) => createElement(ComponentCreator, { schema: item, propsMap, componentsMap }));
}
return createElement('div', {}, children);
} else {
return createElement(ComponentCreator, { schema, propsMap: {}, componentsMap });
}
}
}
return ComponentController;
}
export default new SimulatorRendererContainer(); export default new SimulatorRendererContainer();

View File

@ -0,0 +1,14 @@
module.exports = {
extends: 'eslint-config-ali/typescript/react',
rules: {
'react/no-multi-comp': 1,
'no-unused-expressions': 1,
'implicit-arrow-linebreak': 1,
'no-nested-ternary': 1,
'no-mixed-operators': 1,
'@typescript-eslint/no-parameter-properties': 1,
'@typescript-eslint/ban-types': 1,
'no-shadow': 1,
'no-prototype-builtins': 1,
}
}

View File

@ -0,0 +1,648 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
<a name="1.0.27"></a>
## [1.0.27](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.27-beta.2...v1.0.27) (2020-12-24)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.27-beta.2"></a>
## [1.0.27-beta.2](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.27-beta.1...v1.0.27-beta.2) (2020-12-23)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.27-beta.1"></a>
## [1.0.27-beta.1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.27-beta.0...v1.0.27-beta.1) (2020-12-23)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.27-beta.0"></a>
## [1.0.27-beta.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.26...v1.0.27-beta.0) (2020-12-23)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.26"></a>
## [1.0.26](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.26-beta.1...v1.0.26) (2020-12-22)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.26-beta.1"></a>
## [1.0.26-beta.1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.26-beta.0...v1.0.26-beta.1) (2020-12-22)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.26-beta.0"></a>
## [1.0.26-beta.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.25-beta.1...v1.0.26-beta.0) (2020-12-22)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.25-beta.1"></a>
## [1.0.25-beta.1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.4...v1.0.25-beta.1) (2020-12-15)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.24-beta.4"></a>
## [1.0.24-beta.4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.3...v1.0.24-beta.4) (2020-12-14)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.24-beta.3"></a>
## [1.0.24-beta.3](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.2...v1.0.24-beta.3) (2020-12-11)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.24-beta.2"></a>
## [1.0.24-beta.2](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.1...v1.0.24-beta.2) (2020-12-10)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.24-beta.1"></a>
## [1.0.24-beta.1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.0...v1.0.24-beta.1) (2020-12-09)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.24-beta.0"></a>
## [1.0.24-beta.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.23...v1.0.24-beta.0) (2020-12-09)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.23"></a>
## [1.0.23](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.23-beta.2...v1.0.23) (2020-12-08)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.23-beta.5"></a>
## [1.0.23-beta.5](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.23-beta.4...v1.0.23-beta.5) (2020-12-08)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.23-beta.4"></a>
## [1.0.23-beta.4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.23-beta.3...v1.0.23-beta.4) (2020-12-08)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.23-beta.3"></a>
## [1.0.23-beta.3](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.23-beta.2...v1.0.23-beta.3) (2020-12-08)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.23-beta.2"></a>
## [1.0.23-beta.2](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.23-beta.1...v1.0.23-beta.2) (2020-12-08)
### Bug Fixes
* editor-core 统一版本 ([edd4129](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/edd4129))
### Features
* 增加 plugin-designer ([8bff207](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/8bff207))
<a name="1.0.23-beta.1"></a>
## [1.0.23-beta.1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-29...v1.0.23-beta.1) (2020-12-07)
### Bug Fixes
* 修复setter设置defaultValue不生效的问题 ([0cf47da](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/0cf47da))
### Features
* 合入 trunk-vision 代码 ([ea6bc7a](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/ea6bc7a))
<a name="1.0.23-beta.0"></a>
## [1.0.23-beta.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-29...v1.0.23-beta.0) (2020-12-07)
### Bug Fixes
* 修复setter设置defaultValue不生效的问题 ([0cf47da](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/0cf47da))
### Features
* 合入 trunk-vision 代码 ([ea6bc7a](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/ea6bc7a))
<a name="1.0.22"></a>
## [1.0.22](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@1.0.21...@ali/lowcode-editor-core@1.0.22) (2020-11-16)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.21"></a>
## [1.0.21](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@1.0.20...@ali/lowcode-editor-core@1.0.21) (2020-11-10)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.20"></a>
## [1.0.20](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@1.0.19...@ali/lowcode-editor-core@1.0.20) (2020-11-10)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.19"></a>
## [1.0.19](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@1.0.18...@ali/lowcode-editor-core@1.0.19) (2020-11-05)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.18"></a>
## [1.0.18](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@1.0.17...@ali/lowcode-editor-core@1.0.18) (2020-11-05)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.17"></a>
## [1.0.17](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@1.0.16...@ali/lowcode-editor-core@1.0.17) (2020-11-05)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.16"></a>
## [1.0.16](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@1.0.15...@ali/lowcode-editor-core@1.0.16) (2020-11-04)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.15"></a>
## [1.0.15](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@1.0.13...@ali/lowcode-editor-core@1.0.15) (2020-11-04)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.14"></a>
## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@1.0.13...@ali/lowcode-editor-core@1.0.14) (2020-11-04)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.13"></a>
## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@1.0.12...@ali/lowcode-editor-core@1.0.13) (2020-11-02)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.12"></a>
## [1.0.12](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@1.0.11...@ali/lowcode-editor-core@1.0.12) (2020-10-20)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.11"></a>
## [1.0.11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@1.0.10...@ali/lowcode-editor-core@1.0.11) (2020-10-19)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.10"></a>
## [1.0.10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@1.0.9...@ali/lowcode-editor-core@1.0.10) (2020-09-29)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.9"></a>
## [1.0.9](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@1.0.8...@ali/lowcode-editor-core@1.0.9) (2020-09-28)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.8"></a>
## [1.0.8](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@1.0.8-0...@ali/lowcode-editor-core@1.0.8) (2020-09-28)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.8-0"></a>
## [1.0.8-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.36...@ali/lowcode-editor-core@1.0.8-0) (2020-09-09)
### Bug Fixes
* 合并master分支 ([bd2c6ad](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/bd2c6ad))
* 清理代码依赖及版本 ([0b15d30](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/0b15d30))
<a name="1.0.7-0"></a>
## [1.0.7-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@1.0.6-0...@ali/lowcode-editor-core@1.0.7-0) (2020-09-02)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.6-0"></a>
## [1.0.6-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.34...@ali/lowcode-editor-core@1.0.6-0) (2020-09-02)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="0.8.36"></a>
## [0.8.36](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.35...@ali/lowcode-editor-core@0.8.36) (2020-09-03)
<a name="0.8.35"></a>
## [0.8.35](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.34...@ali/lowcode-editor-core@0.8.35) (2020-09-03)
### Bug Fixes
* 合并master分支 ([bd2c6ad](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/bd2c6ad))
* 清理代码依赖及版本 ([0b15d30](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/0b15d30))
<a name="1.0.5-0"></a>
## [1.0.5-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@1.0.4-0...@ali/lowcode-editor-core@1.0.5-0) (2020-08-20)
<a name="0.8.34"></a>
## [0.8.34](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.33...@ali/lowcode-editor-core@0.8.34) (2020-08-27)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="0.8.33"></a>
## [0.8.33](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.32...@ali/lowcode-editor-core@0.8.33) (2020-08-24)
### Features
* 编辑器 hooks 能力实现 ([f3ac23b](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/f3ac23b))
<a name="0.8.32"></a>
## [0.8.32](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.30...@ali/lowcode-editor-core@0.8.32) (2020-08-20)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.4-0"></a>
## [1.0.4-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@1.0.3-0...@ali/lowcode-editor-core@1.0.4-0) (2020-08-20)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.3-0"></a>
## [1.0.3-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@1.0.2-0...@ali/lowcode-editor-core@1.0.3-0) (2020-08-20)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.2-0"></a>
## [1.0.2-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@1.0.1-0...@ali/lowcode-editor-core@1.0.2-0) (2020-08-20)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.1-0"></a>
## [1.0.1-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.30...@ali/lowcode-editor-core@1.0.1-0) (2020-08-20)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="1.0.0"></a>
# [1.0.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.13.0...@ali/lowcode-editor-core@1.0.0) (2020-08-17)
<a name="0.8.30"></a>
## [0.8.30](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.29...@ali/lowcode-editor-core@0.8.30) (2020-08-19)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="0.13.0"></a>
# [0.13.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.12.0...@ali/lowcode-editor-core@0.13.0) (2020-08-17)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="0.12.0"></a>
# [0.12.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.10.0...@ali/lowcode-editor-core@0.12.0) (2020-08-17)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="0.11.0"></a>
# [0.11.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.10.0...@ali/lowcode-editor-core@0.11.0) (2020-08-17)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="0.10.0"></a>
# [0.10.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.9.0...@ali/lowcode-editor-core@0.10.0) (2020-08-16)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="0.9.0"></a>
# [0.9.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.28...@ali/lowcode-editor-core@0.9.0) (2020-08-14)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="0.8.28"></a>
## [0.8.28](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.27...@ali/lowcode-editor-core@0.8.28) (2020-08-04)
### Bug Fixes
* 增加try catch ([6f5d11c](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/6f5d11c))
<a name="0.8.27"></a>
## [0.8.27](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.25...@ali/lowcode-editor-core@0.8.27) (2020-08-04)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="0.8.26"></a>
## [0.8.26](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.25...@ali/lowcode-editor-core@0.8.26) (2020-08-04)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="0.8.25"></a>
## [0.8.25](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.24...@ali/lowcode-editor-core@0.8.25) (2020-07-28)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="0.8.24"></a>
## [0.8.24](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.23...@ali/lowcode-editor-core@0.8.24) (2020-07-22)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="0.8.23"></a>
## [0.8.23](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.22...@ali/lowcode-editor-core@0.8.23) (2020-07-21)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="0.8.22"></a>
## [0.8.22](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.21...@ali/lowcode-editor-core@0.8.22) (2020-07-21)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="0.8.21"></a>
## [0.8.21](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.20...@ali/lowcode-editor-core@0.8.21) (2020-07-13)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="0.8.20"></a>
## [0.8.20](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.19...@ali/lowcode-editor-core@0.8.20) (2020-07-12)
### Bug Fixes
* 修复删除时,当前组件信息丢失问题 ([3bd1248](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/3bd1248))
<a name="0.8.19"></a>
## [0.8.19](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.17...@ali/lowcode-editor-core@0.8.19) (2020-06-23)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="0.8.17"></a>
## [0.8.17](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.16...@ali/lowcode-editor-core@0.8.17) (2020-06-23)
### Features
* export Monitor ([51025f0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/51025f0))
* 引擎层埋点 ([69de533](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/69de533))
<a name="0.8.16"></a>
## [0.8.16](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.15...@ali/lowcode-editor-core@0.8.16) (2020-06-15)
### Features
* add Monitor ([f915d19](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/f915d19))
* add URL link for setter titles ([4678408](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/4678408))
* ve事件埋点 ([700e5b0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/700e5b0))
<a name="0.8.15"></a>
## [0.8.15](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.14...@ali/lowcode-editor-core@0.8.15) (2020-05-20)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="0.8.14"></a>
## [0.8.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.13...@ali/lowcode-editor-core@0.8.14) (2020-05-18)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="0.8.13"></a>
## [0.8.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.12...@ali/lowcode-editor-core@0.8.13) (2020-05-15)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="0.8.12"></a>
## [0.8.12](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.11...@ali/lowcode-editor-core@0.8.12) (2020-05-13)
### Bug Fixes
* supports ([371b84c](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/371b84c))
* tip direction ([f51d496](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/f51d496))
<a name="0.8.11"></a>
## [0.8.11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.10...@ali/lowcode-editor-core@0.8.11) (2020-05-08)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="0.8.10"></a>
## [0.8.10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.9...@ali/lowcode-editor-core@0.8.10) (2020-05-07)
### Bug Fixes
* intl ([8a061ab](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/8a061ab))
<a name="0.8.9"></a>
## [0.8.9](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.8...@ali/lowcode-editor-core@0.8.9) (2020-04-27)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="0.8.8"></a>
## [0.8.8](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.7...@ali/lowcode-editor-core@0.8.8) (2020-04-27)
**Note:** Version bump only for package @ali/lowcode-editor-core
<a name="0.8.7"></a>
## [0.8.7](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@0.8.6...@ali/lowcode-editor-core@0.8.7) (2020-04-27)
**Note:** Version bump only for package @ali/lowcode-editor-core

View File

@ -0,0 +1,3 @@
shared globals
发 CDN

View File

@ -0,0 +1,7 @@
{
"plugins": [
[
"build-plugin-component"
]
]
}

View File

@ -0,0 +1,48 @@
{
"name": "@ali/lowcode-renderer-core",
"version": "1.0.33",
"description": "renderer core",
"license": "MIT",
"main": "lib/index.js",
"module": "es/index.js",
"files": [
"lib",
"es"
],
"scripts": {
"build": "build-scripts build --skip-demo",
"cloud-build": "build-scripts build --skip-demo"
},
"dependencies": {
"@ali/b3-one": "^0.0.17",
"@ali/bzb-request": "^2.6.0-beta.13",
"@ali/lib-mtop": "^2.5.1",
"@ali/lowcode-datasource-engine": "^1.0.22",
"classnames": "^2.2.6",
"prop-types": "^15.7.2",
"debug": "^4.1.1",
"events": "^3.0.0",
"fetch-jsonp": "^1.1.3",
"intl-messageformat": "^9.3.1",
"jsonuri": "^2.1.2",
"lodash": "^4.17.11",
"moment": "^2.24.0",
"react-is": "^16.10.1",
"serialize-javascript": "^1.7.0",
"socket.io-client": "^2.2.0",
"whatwg-fetch": "^3.0.0"
},
"devDependencies": {
"@alib/build-scripts": "^0.1.18",
"@types/classnames": "^2.2.11",
"@types/debug": "^4.1.5",
"@types/lodash": "^4.14.167",
"@types/node": "^13.7.1",
"@types/prop-types": "^15.7.3",
"@types/serialize-javascript": "^5.0.0",
"build-plugin-component": "^0.2.11"
},
"publishConfig": {
"registry": "https://registry.npm.alibaba-inc.com"
}
}

View File

@ -0,0 +1,89 @@
import { IRuntime, IRendererModules } from '../types';
export enum Env {
React = 'react',
Rax = 'rax',
}
class Adapter {
runtime: IRuntime;
builtinModules = ['Component', 'PureComponent', 'createElement', 'createContext', 'forwardRef', 'findDOMNode'];
env: Env;
renderers: IRendererModules;
configProvider: any;
constructor() {
this.initRuntime();
}
initRuntime() {
const Component = class {};
const PureComponent = class {};
const createElement = () => {};
const createContext = () => {};
const forwardRef = () => {};
const findDOMNode = () => {};
this.runtime = {
Component,
PureComponent,
createElement,
createContext,
forwardRef,
findDOMNode,
};
}
setRuntime(runtime: IRuntime) {
if (this.isValidRuntime(runtime)) {
this.runtime = runtime;
}
}
isValidRuntime(runtime: IRuntime) {
if (typeof runtime !== 'object' || Array.isArray(runtime)) {
return false;
}
return this.builtinModules.every(m => {
const flag = !!this.runtime[m];
if (!flag) {
throw new Error(`runtime is inValid, module '${m}' is not existed`);
}
return flag;
});
}
getRuntime() {
return this.runtime;
}
setEnv(env: Env) {
this.env = env;
}
isReact() {
return this.env === Env.React;
}
isRax() {
return this.env === Env.Rax;
}
setRenderers(renderers: IRendererModules) {
this.renderers = renderers;
}
getRenderers() {
return this.renderers || {};
}
setConfigProvider(Comp: any) {
this.configProvider = Comp;
}
getConfigProvider() {
return this.configProvider;
}
}
export default new Adapter();

View File

@ -0,0 +1,14 @@
import adapter from '../adapter';
export default function divFactory() {
const { PureComponent, createElement } = adapter.getRuntime();
return class Div extends PureComponent {
static displayName = 'Div';
static version = '0.0.0';
render() {
return createElement('div', this.props);
}
};
}

View File

@ -0,0 +1,34 @@
import PropTypes from 'prop-types';
import adapter from '../../adapter';
import './index.css';
export default function visualDomFactory() {
const { PureComponent, createElement } = adapter.getRuntime();
return class VisualDom extends PureComponent {
static displayName = 'VisualDom';
static propTypes = {
children: PropTypes.oneOfType([PropTypes.element, PropTypes.arrayOf(PropTypes.element)]),
};
static defaultProps = {
children: null,
};
render() {
const { children, cell, title, label, text, __componentName } = this.props;
let mainContent = children;
if (cell && typeof cell === 'function') {
mainContent = cell();
}
return createElement('div', { className: 'visual-dom' },
createElement('div', { className: 'panel-container' },
[
createElement('span', { className: 'title' }, title || label || text || __componentName),
createElement('div', { className: 'content' }, mainContent),
]
)
);
}
};
}

View File

@ -0,0 +1,13 @@
import adapter from '../adapter';
export default function contextFactory() {
const { createContext } = adapter.getRuntime();
let context = (window as any).__appContext;
if (!context) {
context = createContext({});
(window as any).__appContext = context;
}
return context;
}

View File

@ -0,0 +1,22 @@
import adapter from '../adapter';
export function compWrapper(Comp: any) {
const { createElement, Component, forwardRef } = adapter.getRuntime();
class Wrapper extends Component {
constructor(props: any, context: any) {
super(props, context)
}
render() {
const { forwardRef } = this.props;
return createElement(Comp, {
...this.props,
ref: forwardRef,
});
}
}
return forwardRef((props: any, ref: any) => {
return createElement(Wrapper, { ...props, forwardRef: ref });
});
}

View File

@ -0,0 +1,8 @@
import adapter from './adapter';
export { adapter };
export * from './renderer';
export * as types from './types';
export * as utils from './utils';
export * from './hoc';

View File

@ -0,0 +1,3 @@
declare module '@ali/b3-one/lib/obj';
declare module '@ali/b3-one/lib/url';
declare module '@ali/lib-mtop';

View File

@ -0,0 +1,88 @@
import PropTypes from 'prop-types';
import baseRendererFactory from './base';
import { isEmpty, goldlog } from '../utils';
import { IRendererProps } from '../types';
export default function addonRendererFactory() {
const BaseRenderer = baseRendererFactory();
return class AddonRenderer extends BaseRenderer {
static dislayName = 'addon-renderer';
__namespace = 'addon';
static propTypes = {
config: PropTypes.object,
__schema: PropTypes.object,
};
static defaultProps = {
config: {},
__schema: {},
};
__afterInit(props: IRendererProps) {
this.__generateCtx({
component: this,
});
const schema = props.__schema || {};
this.state = this.__parseData(schema.state || {});
if (isEmpty(props.config) || !props.config.addonKey) {
console.warn('luna addon has wrong config');
this.state.__hasError = true;
return;
}
// 注册插件
this.addonKey = props.config.addonKey;
this.appHelper.addons = this.appHelper.addons || {};
this.appHelper.addons[this.addonKey] = this;
this.__initDataSource(props);
this.open = this.open || (() => { });
this.close = this.close || (() => { });
this.__setLifeCycleMethods('constructor', arguments);
}
async componentWillUnmount() {
super.componentWillUnmount(...arguments);
// 注销插件
const config = this.props.config || {};
if (config && this.appHelper.addons) {
delete this.appHelper.addons[config.addonKey];
}
}
goldlog = (goKey: string, params: any) => {
const { addonKey, addonConfig = {} } = this.props.config || {};
goldlog(
goKey,
{
addonKey,
package: addonConfig.package,
version: addonConfig.version,
...this.appHelper.logParams,
...params,
},
'addon',
);
};
get utils() {
const { utils = {} } = this.context.config || {};
return { ...this.appHelper.utils, ...utils };
}
render() {
const { __schema } = this.props;
if (this.__checkSchema(__schema)) {
return '插件 schema 结构异常!';
}
this.__debug(`render - ${__schema.fileName}`);
this.__generateCtx({
component: this,
});
this.__render();
return this.__renderContent(this.__renderContextProvider({ compContext: this }));
}
};
}

View File

@ -0,0 +1,761 @@
import classnames from 'classnames';
import Debug from 'debug';
import { create as createDataSourceEngine } from '@ali/lowcode-datasource-engine/interpret';
import adapter from '../adapter';
import divFactory from '../components/Div';
import visualDomFactory from '../components/VisualDom';
import contextFactory from '../context';
import {
forEach,
getValue,
parseData,
parseExpression,
isEmpty,
isSchema,
isFileSchema,
isJSExpression,
isJSSlot,
isJSFunction,
transformArrayToMap,
transformStringToFunction,
checkPropTypes,
generateI18n,
acceptsRef,
getFileCssName,
capitalizeFirstLetter,
DataHelper,
isI18n,
isVariable
} from '../utils';
import { IRendererProps, ISchema, IInfo, ComponentModel, IRenderer } from '../types';
import { compWrapper } from '../hoc';
export default function baseRenererFactory() {
const { BaseRenderer: customBaseRenderer } = adapter.getRenderers();
if (customBaseRenderer) {
return customBaseRenderer;
}
const { Component, createElement } = adapter.getRuntime();
const Div = divFactory();
const VisualDom = visualDomFactory();
const AppContext = contextFactory();
const debug = Debug('renderer:base');
const DESIGN_MODE = {
EXTEND: 'extend',
BORDER: 'border',
PREVIEW: 'preview',
};
const OVERLAY_LIST = ['Dialog', 'Overlay', 'Animate', 'ConfigProvider'];
let scopeIdx = 0;
return class BaseRenderer extends Component implements IRenderer {
static dislayName = 'base-renderer';
static defaultProps = {
__schema: {},
};
static contextType = AppContext;
__namespace = 'base';
constructor(props: IRendererProps, context: any) {
super(props, context);
this.__beforeInit(props);
this.__init(props);
this.__afterInit(props);
this.__initDebug();
this.__debug(`constructor - ${props?.__schema?.fileName}`);
}
__beforeInit(props: IRendererProps) { }
__init(props: IRendererProps) {
this.appHelper = props.__appHelper;
this.__compScopes = {};
this.__instanceMap = {};
const { locale, messages } = props;
this.i18n = generateI18n(locale, messages);
this.__bindCustomMethods(props);
}
__afterInit(props: IRendererProps) { }
static getDerivedStateFromProps(props: IRendererProps, state: any) {
debug('getDerivedStateFromProps');
const func = props?.__schema?.lifeCycles?.getDerivedStateFromProps;
if (func) {
return func(props, state);
}
return null;
}
async getSnapshotBeforeUpdate() {
this.__setLifeCycleMethods('getSnapshotBeforeUpdate', arguments);
this.__debug(`getSnapshotBeforeUpdate - ${this.props?.__schema?.fileName}`);
}
async componentDidMount() {
this.reloadDataSource();
this.__setLifeCycleMethods('componentDidMount', arguments);
this.__debug(`componentDidMount - ${this.props?.__schema?.fileName}`);
}
async componentDidUpdate(...args: any) {
this.__setLifeCycleMethods('componentDidUpdate', args);
this.__debug(`componentDidUpdate - ${this.props.__schema.fileName}`);
}
async componentWillUnmount(...args: any) {
this.__setLifeCycleMethods('componentWillUnmount', args);
this.__debug(`componentWillUnmount - ${this.props?.__schema?.fileName}`);
}
async componentDidCatch(e: any) {
this.__setLifeCycleMethods('componentDidCatch', arguments);
console.warn(e);
}
reloadDataSource = () => new Promise((resolve, reject) => {
this.__debug('reload data source');
if (!this.__dataHelper) {
this.__showPlaceholder = false;
return resolve({});
}
this.__dataHelper
.getInitData()
.then((res: any) => {
this.__showPlaceholder = false;
if (isEmpty(res)) {
this.forceUpdate();
return resolve({});
}
this.setState(res, resolve);
})
.catch((err: Error) => {
if (this.__showPlaceholder) {
this.__showPlaceholder = false;
this.forceUpdate();
}
reject(err);
});
});
__setLifeCycleMethods = (method: string, args?: any) => {
const lifeCycleMethods = getValue(this.props.__schema, 'lifeCycles', {});
let fn = lifeCycleMethods[method];
if (fn) {
// TODO, cache
if (isJSExpression(fn) || isJSFunction(fn)) {
fn = parseExpression(fn, this);
}
if (typeof fn !== 'function') {
console.error(`生命周期${method}类型不符`, fn);
return;
}
try {
return fn.apply(this, args);
} catch (e) {
console.error(`[${this.props.__schema.componentName}]生命周期${method}出错`, e);
}
}
};
__bindCustomMethods = (props = this.props) => {
const { __schema } = props;
const customMethodsList = Object.keys(__schema.methods || {}) || [];
this.__customMethodsList
&& this.__customMethodsList.forEach((item: any) => {
if (!customMethodsList.includes(item)) {
delete this[item];
}
});
this.__customMethodsList = customMethodsList;
forEach(__schema.methods, (val: any, key: string) => {
if (isJSExpression(val) || isJSFunction(val)) {
val = parseExpression(val, this);
}
if (typeof val !== 'function') {
console.error(`自定义函数${key}类型不符`, val);
return;
}
this[key] = val.bind(this);
});
};
__generateCtx = (ctx: object) => {
const { pageContext, compContext } = this.context;
const obj = {
page: pageContext,
component: compContext,
...ctx,
};
forEach(obj, (val: any, key: string) => {
this[key] = val;
});
};
__parseData = (data: any, ctx?: object) => {
const { __ctx } = this.props;
return parseData(data, ctx || __ctx || this);
};
__initDataSource = (props = this.props) => {
const schema = props.__schema || {};
const dataSource = (schema && schema.dataSource) || {};
// requestHandlersMap 存在才走数据源引擎方案
if (props?.__appHelper?.requestHandlersMap) {
const { dataSourceMap, reloadDataSource } = createDataSourceEngine(dataSource, (this as any), {
requestHandlersMap: props.__appHelper.requestHandlersMap,
});
this.dataSourceMap = dataSourceMap;
this.reloadDataSource = () => new Promise((resolve) => {
this.__debug('reload data source');
// this.__showPlaceholder = true;
reloadDataSource().then(() => {
// this.__showPlaceholder = false;
// @TODO 是否需要 forceUpate
// this.forceUpdate();
resolve({});
});
});
} else {
const appHelper = props.__appHelper;
this.__dataHelper = new DataHelper(this, dataSource, appHelper, (config: any) => this.__parseData(config));
this.dataSourceMap = this.__dataHelper.dataSourceMap;
this.reloadDataSource = () => new Promise((resolve, reject) => {
this.__debug('reload data source');
if (!this.__dataHelper) {
// this.__showPlaceholder = false;
return resolve({});
}
this.__dataHelper
.getInitData()
.then((res: any) => {
// this.__showPlaceholder = false;
if (isEmpty(res)) {
this.forceUpdate();
return resolve({});
}
this.setState(res, resolve);
})
.catch((err: Error) => {
if (this.__showPlaceholder) {
this.__showPlaceholder = false;
this.forceUpdate();
}
reject(err);
});
});
}
// 设置容器组件占位若设置占位则在初始异步请求完成之前用loading占位且不渲染容器组件内部内容
// @TODO __showPlaceholder 的逻辑一旦开启就关不掉,先注释掉了
/* this.__showPlaceholder = this.__parseData(schema.props && schema.props.autoLoading) && (dataSource.list || []).some(
(item) => !!this.__parseData(item.isInit),
); */
};
__render = () => {
const schema = this.props.__schema;
this.__setLifeCycleMethods('render');
const { engine } = this.context;
if (engine) {
engine.props.onCompGetCtx(schema, this);
// 画布场景才需要每次渲染bind自定义方法
if (engine.props.designMode) {
this.__bindCustomMethods();
this.dataSourceMap = this.__dataHelper && this.__dataHelper.updateConfig(schema.dataSource);
}
}
};
__getRef = (ref: any) => {
const { engine } = this.context;
const { __schema } = this.props;
ref && engine?.props?.onCompGetRef(__schema, ref);
this.__ref = ref;
};
getSchemaChildren = (schema: ISchema) => {
if (!schema || !schema.props) {
return schema?.children;
}
if (!schema.children) return schema.props.children;
if (!schema.props.children) return schema.children;
let _children = ([] as ComponentModel[]).concat(schema.children);
if (Array.isArray(schema.props.children)) {
_children = _children.concat(schema.props.children);
} else {
_children.push(schema.props.children);
}
return _children;
};
__createDom = () => {
const { __schema, __ctx, __components = {} } = this.props;
const self: any = {};
self.__proto__ = __ctx || this;
const _children = this.getSchemaChildren(__schema);
return this.__createVirtualDom(_children, self, ({
schema: __schema,
Comp: __components[__schema.componentName],
} as IInfo));
};
// 将模型结构转换成react Element
// schema 模型结构
// self 为每个渲染组件构造的上下文self是自上而下继承的
// parentInfo 父组件的信息包含schema和Comp
// idx 若为循环渲染的循环Index
__createVirtualDom = (schema: any, self: any, parentInfo: IInfo, idx: string | number = ''): any => {
const { engine } = this.context || {};
try {
if (!schema) return null;
// FIXME
if (schema.componentName === 'Text' && typeof schema.props.text === 'string') {
schema = { ...schema };
schema.children = [schema.props.text];
}
const { __appHelper: appHelper, __components: components = {} } = this.props || {};
if (isJSExpression(schema)) {
return parseExpression(schema, self);
}
if (isJSSlot(schema)) {
return this.__createVirtualDom(schema.value, self, parentInfo);
}
if (typeof schema === 'string') return schema;
if (typeof schema === 'number' || typeof schema === 'boolean') {
return schema.toString();
}
if (Array.isArray(schema)) {
if (schema.length === 1) return this.__createVirtualDom(schema[0], self, parentInfo);
return schema.map((item, idy) => this.__createVirtualDom(item, self, parentInfo, item?.__ctx?.lceKey ? '' : idy));
}
// FIXME
const _children = this.getSchemaChildren(schema);
// 解析占位组件
if (schema.componentName === 'Flagment' && _children) {
const tarChildren = isJSExpression(_children) ? parseExpression(_children, self) : _children;
return this.__createVirtualDom(tarChildren, self, parentInfo);
}
if (schema.$$typeof) {
return schema;
}
if (!isSchema(schema)) return null;
let Comp = components[schema.componentName] || engine.getNotFoundComponent();
if (schema.hidden) {
return null;
}
if (schema.loop != null) {
const loop = parseData(schema.loop, self);
if ((Array.isArray(loop) && loop.length > 0) || isJSExpression(loop)) {
return this.__createLoopVirtualDom(
{
...schema,
loop,
},
self,
parentInfo,
idx,
);
}
}
const condition = schema.condition == null ? true : parseData(schema.condition, self);
if (!condition) return null;
let scopeKey = '';
// 判断组件是否需要生成scope且只生成一次挂在this.__compScopes上
if (Comp.generateScope) {
const key = parseExpression(schema.props.key, self);
if (key) {
// 如果组件自己设置key则使用组件自己的key
scopeKey = key;
} else if (!schema.__ctx) {
// 在生产环境schema没有__ctx上下文需要手动生成一个lceKey
schema.__ctx = {
lceKey: `lce${++scopeIdx}`,
};
scopeKey = schema.__ctx.lceKey;
} else {
// 需要判断循环的情况
scopeKey = schema.__ctx.lceKey + (idx !== undefined ? `_${idx}` : '');
}
if (!this.__compScopes[scopeKey]) {
this.__compScopes[scopeKey] = Comp.generateScope(this, schema);
}
}
// 如果组件有设置scope需要为组件生成一个新的scope上下文
if (scopeKey && this.__compScopes[scopeKey]) {
const compSelf = { ...this.__compScopes[scopeKey] };
compSelf.__proto__ = self;
self = compSelf;
}
// 容器类组件的上下文通过props传递避免context传递带来的嵌套问题
const otherProps: any = isFileSchema(schema)
? {
__schema: schema,
__appHelper: appHelper,
__components: components,
}
: {};
if (engine?.props?.designMode) {
otherProps.__designMode = engine.props.designMode;
}
const componentInfo: any = {};
const props: any =
this.__parseProps(schema.props, self, '', {
schema,
Comp,
componentInfo: {
...componentInfo,
props: transformArrayToMap(componentInfo.props, 'name'),
},
}) || {};
// 对于可以获取到ref的组件做特殊处理
if (!acceptsRef(Comp)) {
Comp = compWrapper(Comp);
}
otherProps.ref = (ref: any) => {
this.$(props.fieldId, ref); // 收集ref
const refProps = props.ref;
if (refProps && typeof refProps === 'string') {
this[refProps] = ref;
}
ref && engine?.props?.onCompGetRef(schema, ref);
};
// scope需要传入到组件上
if (scopeKey && this.__compScopes[scopeKey]) {
props.__scope = this.__compScopes[scopeKey];
}
// FIXME 这里清除 key 是为了避免循环渲染中更改 key 导致的渲染重复
props.key = '';
if (schema?.__ctx?.lceKey) {
if (!isFileSchema(schema)) {
engine?.props?.onCompGetCtx(schema, self);
}
props.key = props.key || `${schema.__ctx.lceKey}_${schema.__ctx.idx || 0}_${idx !== undefined ? idx : ''}`;
} else if (typeof idx === 'number' && !props.key) {
props.key = idx;
}
props.__id = schema.id;
if (!props.key) {
props.key = props.__id;
}
let child: any = null;
if (/*!isFileSchema(schema) && */!!_children) {
child = this.__createVirtualDom(
isJSExpression(_children) ? parseExpression(_children, self) : _children,
self,
{
schema,
Comp,
},
);
}
const renderComp = (props: any) => engine.createElement(Comp, props, child);
// 设计模式下的特殊处理
if (engine && [DESIGN_MODE.EXTEND, DESIGN_MODE.BORDER].includes(engine.props.designMode)) {
// 对于overlay,dialog等组件为了使其在设计模式下显示外层需要增加一个div容器
if (OVERLAY_LIST.includes(schema.componentName)) {
const { ref, ...overlayProps } = otherProps;
return createElement(Div, {
ref,
__designMode: engine.props.designMode,
}, renderComp({ ...props, ...overlayProps }));
}
// 虚拟dom显示
if (componentInfo?.parentRule) {
const parentList = componentInfo.parentRule.split(',');
const { schema: parentSchema, Comp: parentComp } = parentInfo;
if (
!parentList.includes(parentSchema.componentName) ||
parentComp !== components[parentSchema.componentName]
) {
props.__componentName = schema.componentName;
Comp = VisualDom;
} else {
// 若虚拟dom在正常的渲染上下文中就不显示设计模式了
props.__disableDesignMode = true;
}
}
}
return renderComp({ ...props, ...otherProps });
} catch (e) {
return engine.createElement(engine.getFaultComponent(), {
error: e,
schema,
self,
parentInfo,
idx,
});
}
};
__createLoopVirtualDom = (schema: any, self: any, parentInfo: IInfo, idx: number | string) => {
if (isFileSchema(schema)) {
console.warn('file type not support Loop');
return null;
}
if (!Array.isArray(schema.loop)) return null;
const itemArg = (schema.loopArgs && schema.loopArgs[0]) || 'item';
const indexArg = (schema.loopArgs && schema.loopArgs[1]) || 'index';
return schema.loop.map((item: Array<string>, i: number) => {
const loopSelf: any = {
[itemArg]: item,
[indexArg]: i,
};
loopSelf.__proto__ = self;
return this.__createVirtualDom(
{
...schema,
loop: undefined,
},
loopSelf,
parentInfo,
idx ? `${idx}_${i}` : i,
);
});
};
__parseProps = (props: any, self: any, path: string, info: IInfo): any => {
const { schema, Comp, componentInfo = {} } = info;
const propInfo = getValue(componentInfo.props, path);
// FIXME! 将这行逻辑外置,解耦,线上环境不要验证参数,调试环境可以有,通过传参自定义
const propType = propInfo?.extra?.propType;
const ignoreParse = schema.__ignoreParse || [];
const checkProps = (value: any) => {
if (!propType) return value;
return checkPropTypes(value, path, propType, componentInfo.name) ? value : undefined;
};
const parseReactNode = (data: any, params: any) => {
if (isEmpty(params)) {
return checkProps(this.__createVirtualDom(data, self, ({ schema, Comp } as IInfo)));
}
return checkProps(function () {
const args: any = {};
if (Array.isArray(params) && params.length) {
params.forEach((item, idx) => {
if (typeof item === 'string') {
args[item] = arguments[idx];
} else if (item && typeof item === 'object') {
args[item.name] = arguments[idx];
}
});
}
args.__proto__ = self;
return self.__createVirtualDom(data, args, { schema, Comp });
});
};
// 判断是否需要解析变量
if (
ignoreParse.some((item: any) => {
if (item instanceof RegExp) {
return item.test(path);
}
return item === path;
})
) {
return checkProps(props);
}
if (isJSExpression(props)) {
props = parseExpression(props, self);
// 只有当变量解析出来为模型结构的时候才会继续解析
if (!isSchema(props) && !isJSSlot(props)) return checkProps(props);
}
const handleI18n = (props: any) => props[props.use || 'zh_CN'];
// 兼容乐高设计态 i18n 数据
if (isI18n(props)) {
props = handleI18n(props);
}
// 兼容乐高设计态的变量绑定
if (isVariable(props)) {
props = props.value;
if (isI18n(props)) {
props = handleI18n(props);
}
}
if (isJSFunction(props)) {
props = transformStringToFunction(props.value);
}
if (isJSSlot(props)) {
const { params, value } = props;
if (!isSchema(value) || isEmpty(value)) return undefined;
return parseReactNode(value, params);
}
// 兼容通过componentInfo判断的情况
if (isSchema(props)) {
const isReactNodeFunction = !!(
propInfo?.type === 'ReactNode'
&& propInfo?.props?.type === 'function'
);
const isMixinReactNodeFunction = !!(
propInfo?.type === 'Mixin'
&& propInfo?.props?.types?.indexOf('ReactNode') > -1
&& propInfo?.props?.reactNodeProps?.type === 'function'
);
return parseReactNode(
props,
isReactNodeFunction
? propInfo.props.params
: isMixinReactNodeFunction
? propInfo.props.reactNodeProps.params
: null,
);
}
if (Array.isArray(props)) {
return checkProps(props.map((item, idx) => this.__parseProps(item, self, path ? `${path}.${idx}` : `${idx}`, info)));
}
if (typeof props === 'function') {
return checkProps(props.bind(self));
}
if (props && typeof props === 'object') {
if (props.$$typeof) return checkProps(props);
const res: any = {};
forEach(props, (val: any, key: string) => {
if (key.startsWith('__')) {
res[key] = val;
return;
}
res[key] = this.__parseProps(val, self, path ? `${path}.${key}` : key, info);
});
return checkProps(res);
}
if (typeof props === 'string') {
return checkProps(props.trim());
}
return checkProps(props);
};
$(filedId: string, instance?: any) {
this.__instanceMap = this.__instanceMap || {};
if (!filedId) {
return this.__instanceMap;
}
if (instance) {
this.__instanceMap[filedId] = instance;
}
return this.__instanceMap[filedId];
}
__initDebug = () => {
this.__logger = Debug(`renderer:${this.__namespace || 'base'}`);
}
__debug = (msg: string = '') => {
if (this.__logger) {
this.__logger(`${this.__namespace}.${msg}`);
}
}
__renderContextProvider = (customProps?: object, children?: any) => {
customProps = customProps || {};
children = children || this.__createDom();
return createElement(AppContext.Provider, {
value: {
...this.context,
blockContext: this,
...customProps,
}, children
});
}
__renderContextConsumer = (children: any) => {
return createElement(AppContext.Consumer, {}, children);
}
__renderComp(Comp: any, ctxProps: object) {
const { __schema } = this.props;
const data = this.__parseData(__schema?.props);
const { className } = data;
const { engine } = this.context || {};
if (!engine) {
return null;
}
const child = engine.createElement(
Comp || Div,
{
...data,
...this.props,
ref: this.__getRef,
className: classnames(getFileCssName(__schema?.fileName), className, this.props.className),
__id: __schema?.id,
},
this.__createDom(),
);
return this.__renderContextProvider(ctxProps, child);
}
__renderContent(children: any) {
const { __schema } = this.props;
const props = this.__parseData(__schema.props);
const { id, className, style = {} } = props;
return createElement('div', {
ref: this.__getRef,
className: classnames(`lce-${this.__namespace}`, getFileCssName(__schema.fileName), className, this.props.className),
id: this.props.id || id,
style: { ...style, ...(typeof this.props.style === 'object' ? this.props.style : {}) },
}, children);
}
__checkSchema = (schema: ISchema, extraComponents: string | string[] = []) => {
if (typeof extraComponents === 'string') {
extraComponents = [extraComponents];
}
const buitin = capitalizeFirstLetter(this.__namespace);
const componentNames = [buitin, ...extraComponents];
return !isSchema(schema, true) || !componentNames.includes(schema.componentName)
}
get requestHandlersMap() {
return this.appHelper?.requestHandlersMap;
}
get utils() {
return this.appHelper?.utils;
}
get constants() {
return this.appHelper?.constants;
}
get history() {
return this.appHelper?.history;
}
get location() {
return this.appHelper?.location;
}
get match() {
return this.appHelper?.match;
}
render() {
return null;
}
};
}

View File

@ -0,0 +1,39 @@
import classnames from 'classnames';
import baseRendererFactory from './base';
import { IRendererProps } from '../types';
import { getFileCssName } from '../utils';
export default function blockRendererFactory() {
const BaseRenderer = baseRendererFactory();
return class BlockRenderer extends BaseRenderer {
static dislayName = 'block-renderer';
__namespace = 'block';
__afterInit(props: IRendererProps) {
this.__generateCtx({});
const schema = props.__schema || {};
this.state = this.__parseData(schema.state || {});
this.__initDataSource(props);
this.__setLifeCycleMethods('constructor', arguments);
}
render() {
const { __schema, __components } = this.props;
if (this.__checkSchema(__schema, 'Div')) {
return '区块 schema 结构异常!';
}
this.__debug(`render - ${__schema.fileName}`);
this.__generateCtx({});
this.__render();
const { Block } = __components;
if (Block) {
return this.__renderComp(Block, {});
}
return this.__renderContent(this.__renderContextProvider());
}
};
}

View File

@ -0,0 +1,41 @@
import baseRendererFactory from './base';
import { IRendererProps } from '../types';
export default function componentRendererFactory() {
const BaseRenderer = baseRendererFactory();
return class CompRenderer extends BaseRenderer {
static dislayName = 'comp-renderer';
__namespace = 'component';
__afterInit(props: IRendererProps) {
this.__generateCtx({
component: this,
});
const schema = props.__schema || {};
this.state = this.__parseData(schema.state || {});
this.__initDataSource(props);
this.__setLifeCycleMethods('constructor', arguments);
}
render() {
const { __schema } = this.props;
if (this.__checkSchema(__schema)) {
return '自定义组件 schema 结构异常!';
}
this.__debug(`render - ${__schema.fileName}`);
this.__generateCtx({
component: this,
});
this.__render();
const { noContainer } = this.__parseData(__schema.props);
if (noContainer) {
return this.__renderContextProvider({ compContext: this });
}
return this.__renderContent(this.__renderContextProvider({ compContext: this }));
}
};
}

View File

@ -0,0 +1,17 @@
import baseRendererFactory from './base';
import pageRendererFactory from './page';
import componentRendererFactory from './component';
import blockRendererFactory from './block';
import addonRendererFactory from './addon';
import tempRendererFactory from './temp';
import rendererFactory from './renderer';
export {
baseRendererFactory,
pageRendererFactory,
componentRendererFactory,
blockRendererFactory,
addonRendererFactory,
tempRendererFactory,
rendererFactory,
};

View File

@ -0,0 +1,59 @@
import baseRendererFactory from './base';
import { parseData } from '../utils';
import { IRendererProps } from '../types';
export default function pageRendererFactory() {
const BaseRenderer = baseRendererFactory();
return class PageRenderer extends BaseRenderer {
static dislayName = 'page-renderer';
__namespace = 'page';
__afterInit(props: IRendererProps) {
this.__generateCtx({
page: this,
});
const schema = props.__schema || {};
this.state = this.__parseData(schema.state || {});
this.__initDataSource(props);
this.__setLifeCycleMethods('constructor', arguments);
}
async componentDidUpdate(prevProps: any) {
const { __ctx } = this.props;
const prevState = parseData(prevProps.__schema.state, __ctx);
const newState = parseData(this.props.__schema.state, __ctx);
// 当编排的时候修改schema.state值需要将最新schema.state值setState
if (JSON.stringify(newState) != JSON.stringify(prevState)) {
this.setState(newState);
}
super.componentDidUpdate(...arguments);
}
render() {
const { __schema, __components } = this.props;
if (this.__checkSchema(__schema)) {
return '页面schema结构异常';
}
this.__debug(`render - ${__schema.fileName}`);
this.__bindCustomMethods(this.props);
this.__initDataSource(this.props);
// this.__setLifeCycleMethods('constructor', arguments);
this.__generateCtx({
page: this,
});
this.__render();
const { Page } = __components;
if (Page) {
return this.__renderComp(Page, { pageContext: this });
}
return this.__renderContent(this.__renderContextProvider({ pageContext: this }));
}
};
}

View File

@ -0,0 +1,189 @@
import Debug from 'debug';
import { isEmpty } from '@ali/b3-one/lib/obj';
import adapter from '../adapter';
import contextFactory from '../context';
import { isFileSchema, goldlog } from '../utils';
import baseRendererFactory from './base';
import divFactory from '../components/Div';
import { IProps } from '../types';
export default function rendererFactory() {
const { createElement, Component, PureComponent, findDOMNode } = adapter.getRuntime();
const RENDERER_COMPS: any = adapter.getRenderers();
const BaseRenderer = baseRendererFactory();
const AppContext = contextFactory();
const Div = divFactory();
const ConfigProvider = adapter.getConfigProvider() || Div;
const debug = Debug('renderer:entry');
class FaultComponent extends PureComponent {
render() {
// FIXME: errorlog
console.error('render error', this.props);
return createElement(Div, {
style: {
width: '100%',
height: '50px',
lineHeight: '50px',
textAlign: 'center',
fontSize: '15px',
color: '#ff0000',
border: '2px solid #ff0000',
},
}, '组件渲染异常,请查看控制台日志');
}
}
class NotFoundComponent extends PureComponent {
render() {
console.error('component not found', this.props);
return createElement(Div, this.props, this.props.children || 'Component Not Found');
}
}
return class Renderer extends Component {
static dislayName = 'renderer';
static defaultProps = {
appHelper: null,
components: {},
designMode: '',
suspended: false,
schema: {},
onCompGetRef: () => { },
onCompGetCtx: () => { },
};
static findDOMNode = findDOMNode;
constructor(props: IProps, context: any) {
super(props, context);
this.state = {};
debug(`entry.constructor - ${props?.schema?.componentName}`);
}
async componentDidMount() {
goldlog(
'EXP',
{
action: 'appear',
value: !!this.props.designMode,
},
'renderer',
);
debug(`entry.componentDidMount - ${this.props.schema && this.props.schema.componentName}`);
}
async componentDidUpdate() {
debug(`entry.componentDidUpdate - ${this.props?.schema?.componentName}`);
}
async componentWillUnmount() {
debug(`entry.componentWillUnmount - ${this.props?.schema?.componentName}`);
}
async componentDidCatch(e: any) {
console.warn(e);
}
shouldComponentUpdate(nextProps: IProps) {
return !nextProps.suspended;
}
__getRef = (ref: any) => {
this.__ref = ref;
if (ref) {
this.props.onCompGetRef(this.props.schema, ref, true);
}
};
isValidComponent(SetComponent: any) {
return SetComponent;
}
patchDidCatch(SetComponent: any) {
if (!this.isValidComponent(SetComponent)) {
return;
}
if (SetComponent.patchedCatch) {
return;
}
SetComponent.patchedCatch = true;
SetComponent.getDerivedStateFromError = (error: Error) => {
return { engineRenderError: true, error };
};
const engine = this;
const originRender = SetComponent.prototype.render;
SetComponent.prototype.render = function () {
if (this.state && this.state.engineRenderError) {
this.state.engineRenderError = false;
return engine.createElement(engine.getFaultComponent(), {
...this.props,
error: this.state.error,
});
}
return originRender.call(this);
};
const originShouldComponentUpdate = SetComponent.prototype.shouldComponentUpdate;
SetComponent.prototype.shouldComponentUpdate = function (nextProps: IProps, nextState: any) {
if (nextState && nextState.engineRenderError) {
return true;
}
return originShouldComponentUpdate ? originShouldComponentUpdate.call(this, nextProps, nextState) : true;
};
}
createElement(SetComponent: any, props: any, children?: any) {
// TODO: enable in runtime mode?
this.patchDidCatch(SetComponent);
return (this.props.customCreateElement || createElement)(SetComponent, props, children);
}
getNotFoundComponent() {
return this.props.notFoundComponent || NotFoundComponent;
}
getFaultComponent() {
return this.props.faultComponent || FaultComponent;
}
render() {
const { schema, designMode, appHelper, components } = this.props;
if (isEmpty(schema)) {
return null;
}
// 兼容乐高区块模板
if (schema.componentName !== 'Div' && !isFileSchema(schema)) {
return '模型结构异常';
}
debug('entry.render');
const { componentName } = schema;
const allComponents = { ...RENDERER_COMPS, ...components };
let Comp = allComponents[componentName] || RENDERER_COMPS[`${componentName}Renderer`];
if (Comp && Comp.prototype) {
if (!(Comp.prototype instanceof BaseRenderer)) {
Comp = RENDERER_COMPS[`${componentName}Renderer`];
}
}
if (Comp) {
return createElement(AppContext.Provider, { value: {
appHelper,
components: allComponents,
engine: this,
}}, createElement(ConfigProvider, {device: this.props.device}, createElement(Comp, {
key: schema.__ctx && `${schema.__ctx.lceKey}_${schema.__ctx.idx || '0'}`,
ref: this.__getRef,
__appHelper: appHelper,
__components: allComponents,
__schema: schema,
__designMode: designMode,
...this.props,
})));
}
return null;
}
};
}

View File

@ -0,0 +1,55 @@
import baseRendererFactory from './base';
export default function tempRendererFactory() {
const BaseRenderer = baseRendererFactory();
return class TempRenderer extends BaseRenderer {
static dislayName = 'temp-renderer';
__namespace = 'temp';
__init() {
this.state = {};
this.cacheSetState = {};
}
async componentDidMount() {
const ctx = this.props.__ctx;
if (!ctx) return;
const { setState } = ctx;
this.cacheSetState = setState;
ctx.setState = (...args: any) => {
setState.call(ctx, ...args);
setTimeout(() => this.forceUpdate(), 0);
};
this.__debug(`componentDidMount - ${this.props.__schema.fileName}`);
}
async componentDidUpdate() {
this.__debug(`componentDidUpdate - ${this.props.__schema.fileName}`);
}
async componentWillUnmount() {
const ctx = this.props.__ctx;
if (!ctx || !this.cacheSetState) return;
ctx.setState = this.cacheSetState;
delete this.cacheSetState;
this.__debug(`componentWillUnmount - ${this.props.__schema.fileName}`);
}
async componentDidCatch(e: any) {
console.warn(e);
this.__debug(`componentDidCatch - ${this.props.__schema.fileName}`);
}
render() {
const { __schema, __ctx } = this.props;
if (this.__checkSchema(__schema)) {
return '下钻编辑 schema 结构异常!';
}
this.__debug(`render - ${__schema.fileName}`);
return this.__renderContent(this.__renderContextProvider({ __ctx }));
}
};
}

View File

@ -0,0 +1,122 @@
export interface IProps {
appHelper: any;
components: { [key: string]: any };
componentsMap: { [key: string]: any };
designMode: string;
suspended: boolean;
schema: ISchema;
onCompGetRef: (schema: ISchema, ref: any) => void;
onCompGetCtx: (schema: ISchema, ref: any) => void;
customCreateElement: (...args: any) => any;
notFoundComponent: any;
faultComponent: any;
}
export interface IRendererProps {
locale: string;
messages: object;
__appHelper: object;
__components: object;
__ctx: object;
__schema: ISchema;
[key: string]: any;
}
export interface ComponentModel {
componentName: string;
props: any;
children: ComponentModel[];
}
export interface ISchema {
componentName: string;
props: any;
children: ComponentModel[]
dataSource?: any;
methods?: any;
lifeCycles?: any;
[key: string]: any;
}
export interface IInfo {
schema: any;
Comp: any;
componentInfo?: any;
}
export interface JSExpression {
type: string;
value: 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;
}
type Constructor = new(...args: any) => any;
export interface IRuntime {
Component: Constructor;
PureComponent: Constructor;
createElement: (...args: any) => any;
createContext: (...args: any) => any;
forwardRef: (...args: any) => any;
findDOMNode: (...args: any) => any;
[key: string]: any;
}
export interface IRendererModules {
BaseRenderer?: any;
PageRenderer: any;
ComponentRenderer: any;
BlockRenderer?: any,
AddonRenderer?: any,
TempRenderer?: any,
DivRenderer?: any;
}
export interface IRenderer {
props?: IRendererProps;
context?: any;
__beforeInit: (props: IRendererProps) => any;
__init: (props: IRendererProps) => any;
__afterInit: (props: IRendererProps) => any;
reloadDataSource: () => Promise<any>;
__setLifeCycleMethods: (method: string, args?: any) => any;
__bindCustomMethods: (props: IRendererProps) => any;
__generateCtx: (ctx: object) => any;
__parseData: (data: any, ctx?: object) => any;
__initDataSource: (props: IRendererProps) => any;
__render: () => any;
__getRef: (ref: any) => any;
getSchemaChildren: (schema: ISchema) => any;
__createDom: () => any;
__createVirtualDom: (schema: any, self: any, parentInfo: IInfo, idx: string | number) => any;
__createLoopVirtualDom: (schema: any, self: any, parentInfo: IInfo, idx: number | string) => any;
__parseProps: (props: any, self: any, path: string, info: IInfo) => any;
__initDebug: () => void;
__debug: (msg: string) => void;
__renderContextProvider: (customProps?: object, children?: any) => any;
__renderContextConsumer: (children: any) => any;
__renderContent: (children: any) => any;
__checkSchema: (schema: ISchema, extraComponents?: string | string[]) => any;
__renderComp: (Comp: any, ctxProps: object) => any;
$: (filedId: string, instance?: any) => any;
}

View File

@ -1,6 +1,5 @@
/* eslint-disable no-new-func */ /* eslint-disable no-new-func */
import Debug from 'debug'; import Debug from 'debug';
import _keymaster from 'keymaster';
import { forEach as _forEach, shallowEqual as _shallowEqual } from '@ali/b3-one/lib/obj'; import { forEach as _forEach, shallowEqual as _shallowEqual } from '@ali/b3-one/lib/obj';
import { serialize as serializeParams } from '@ali/b3-one/lib/url'; import { serialize as serializeParams } from '@ali/b3-one/lib/url';
// moment对象配置 // moment对象配置
@ -20,12 +19,13 @@ import * as _jsonuri from 'jsonuri';
import IntlMessageFormat from 'intl-messageformat'; import IntlMessageFormat from 'intl-messageformat';
export const keymaster = _keymaster; import { ISchema } from '../types';
export const forEach = _forEach; export const forEach = _forEach;
export const shallowEqual = _shallowEqual; export const shallowEqual = _shallowEqual;
export const moment = _moment; export const moment = _moment;
moment.locale('zh-cn'); moment.locale('zh-cn');
window.sdkVersion = pkg.version; (window as any).sdkVersion = pkg.version;
export const pick = _pick; export const pick = _pick;
export const deepEqual = _deepEqual; export const deepEqual = _deepEqual;
@ -35,7 +35,6 @@ export const throttle = _throttle;
export const debounce = _debounce; export const debounce = _debounce;
export const serialize = _serialize; export const serialize = _serialize;
export const jsonuri = _jsonuri; export const jsonuri = _jsonuri;
export { get, post, jsonp, mtop, request } from './request';
const ReactIs = require('react-is'); const ReactIs = require('react-is');
const ReactPropTypesSecret = require('prop-types/lib/ReactPropTypesSecret'); const ReactPropTypesSecret = require('prop-types/lib/ReactPropTypesSecret');
@ -47,6 +46,7 @@ const EXPRESSION_TYPE = {
JSEXPRESSION: 'JSExpression', JSEXPRESSION: 'JSExpression',
JSFUNCTION: 'JSFunction', JSFUNCTION: 'JSFunction',
JSSLOT: 'JSSlot', JSSLOT: 'JSSlot',
JSBLOCK: 'JSBlock'
}; };
const EXPRESSION_REG = /^\{\{(\{.*\}|.*?)\}\}$/; const EXPRESSION_REG = /^\{\{(\{.*\}|.*?)\}\}$/;
const hasSymbol = typeof Symbol === 'function' && Symbol.for; const hasSymbol = typeof Symbol === 'function' && Symbol.for;
@ -64,7 +64,7 @@ const ENV = {
* @name isSchema * @name isSchema
* @description * @description
*/ */
export function isSchema(schema, ignoreArr) { export function isSchema(schema: ISchema, ignoreArr = false): boolean {
if (isEmpty(schema)) return false; if (isEmpty(schema)) return false;
// Leaf 组件也返回 true // Leaf 组件也返回 true
if (schema.componentName === 'Leaf' || schema.componentName === 'Slot') return true; if (schema.componentName === 'Leaf' || schema.componentName === 'Slot') return true;
@ -72,7 +72,7 @@ export function isSchema(schema, ignoreArr) {
return !!(schema.componentName && schema.props && (typeof schema.props === 'object' || isJSExpression(schema.props))); return !!(schema.componentName && schema.props && (typeof schema.props === 'object' || isJSExpression(schema.props)));
} }
export function isFileSchema(schema) { export function isFileSchema(schema: ISchema) {
if (isEmpty(schema)) return false; if (isEmpty(schema)) return false;
return ['Page', 'Block', 'Component', 'Addon', 'Temp'].includes(schema.componentName); return ['Page', 'Block', 'Component', 'Addon', 'Temp'].includes(schema.componentName);
} }
@ -86,22 +86,25 @@ export function inSameDomain() {
} }
} }
export function getFileCssName(fileName) { export function getFileCssName(fileName: string) {
if (!fileName) return; if (!fileName) return;
const name = fileName.replace(/([A-Z])/g, '-$1').toLowerCase(); const name = fileName.replace(/([A-Z])/g, '-$1').toLowerCase();
return (`luna-${ name}`) return (`luna-${name}`)
.split('-') .split('-')
.filter((p) => !!p) .filter((p) => !!p)
.join('-'); .join('-');
} }
export function isJSSlot(obj) { // 兼容乐高设计态 JSBlock 的老协议
return obj && typeof obj === 'object' && EXPRESSION_TYPE.JSSLOT === obj.type; export function isJSSlot(obj: any) {
return obj && typeof obj === 'object' && ([EXPRESSION_TYPE.JSSLOT, EXPRESSION_TYPE.JSBLOCK].includes(obj.type));
} }
export function isJSFunction(obj) {
export function isJSFunction(obj: any) {
return obj && typeof obj === 'object' && EXPRESSION_TYPE.JSFUNCTION === obj.type; return obj && typeof obj === 'object' && EXPRESSION_TYPE.JSFUNCTION === obj.type;
} }
export function isJSExpression(obj) {
export function isJSExpression(obj: any) {
// 兼容两种写法有js构造表达式的情况 // 兼容两种写法有js构造表达式的情况
const isJSExpressionObj = const isJSExpressionObj =
obj && typeof obj === 'object' && EXPRESSION_TYPE.JSEXPRESSION === obj.type && typeof obj.value === 'string'; obj && typeof obj === 'object' && EXPRESSION_TYPE.JSEXPRESSION === obj.type && typeof obj.value === 'string';
@ -113,17 +116,17 @@ export function isJSExpression(obj) {
* @name wait * @name wait
* @description * @description
*/ */
export function wait(ms) { export function wait(ms: number) {
return new Promise((resolve) => setTimeout(() => resolve(true), ms)); return new Promise((resolve) => setTimeout(() => resolve(true), ms));
} }
export function curry(Comp, hocs = []) { export function curry(Comp: any, hocs = []) {
return hocs.reverse().reduce((pre, cur) => { return hocs.reverse().reduce((pre, cur: (pre: any) => any) => {
return cur(pre); return cur(pre);
}, Comp); }, Comp);
} }
export function getValue(obj, path, defaultValue) { export function getValue(obj: any, path: string, defaultValue = {}) {
if (isEmpty(obj) || typeof obj !== 'object') return defaultValue; if (isEmpty(obj) || typeof obj !== 'object') return defaultValue;
const res = path.split('.').reduce((pre, cur) => { const res = path.split('.').reduce((pre, cur) => {
return pre && pre[cur]; return pre && pre[cur];
@ -132,25 +135,8 @@ export function getValue(obj, path, defaultValue) {
return res; return res;
} }
export function parseObj(schemaStr) {
if (typeof schemaStr !== 'string') return schemaStr;
// 默认调用顶层窗口的parseObj,保障new Function的window对象是顶层的window对象
try {
if (inSameDomain() && window.parent.__newFunc) {
return window.parent.__newFunc(`"use strict"; return ${schemaStr}`)();
}
return new Function(`"use strict"; return ${schemaStr}`)();
} catch (err) {
return undefined;
}
}
export function fastClone(obj) {
return parseObj(serialize(obj, { unsafe: true }));
}
// 更新obj的内容但不改变obj的指针 // 更新obj的内容但不改变obj的指针
export function fillObj(receiver = {}, ...suppliers) { export function fillObj(receiver: any = {}, ...suppliers: any) {
Object.keys(receiver).forEach((item) => { Object.keys(receiver).forEach((item) => {
delete receiver[item]; delete receiver[item];
}); });
@ -159,14 +145,15 @@ export function fillObj(receiver = {}, ...suppliers) {
} }
// 中划线转驼峰 // 中划线转驼峰
export function toHump(name) { export function toHump(name: string) {
// eslint-disable-next-line no-useless-escape // eslint-disable-next-line no-useless-escape
return name.replace(/\-(\w)/g, (all, letter) => { return name.replace(/\-(\w)/g, (_: any, letter: string) => {
return letter.toUpperCase(); return letter.toUpperCase();
}); });
} }
// 驼峰转中划线 // 驼峰转中划线
export function toLine(name) { export function toLine(name: string) {
return name.replace(/([A-Z])/g, '-$1').toLowerCase(); return name.replace(/([A-Z])/g, '-$1').toLowerCase();
} }
@ -175,7 +162,7 @@ export function getEnv() {
const { userAgent } = navigator; const { userAgent } = navigator;
const isVscode = /Electron\//.test(userAgent); const isVscode = /Electron\//.test(userAgent);
if (isVscode) return ENV.VSCODE; if (isVscode) return ENV.VSCODE;
const isTheia = window.is_theia === true; const isTheia = (window as any).is_theia === true;
if (isTheia) return ENV.WEBIDE; if (isTheia) return ENV.WEBIDE;
return ENV.WEB; return ENV.WEB;
} }
@ -185,8 +172,8 @@ export function getEnv() {
* @param {*} locale zh-CNen-US * @param {*} locale zh-CNen-US
* @param {*} messages * @param {*} messages
*/ */
export function generateI18n(locale = 'zh-CN', messages = {}) { export function generateI18n(locale = 'zh-CN', messages: any = {}) {
return (key, values = {}) => { return (key: string, values = {}) => {
if (!messages || !messages[key]) return ''; if (!messages || !messages[key]) return '';
const formater = new IntlMessageFormat(messages[key], locale); const formater = new IntlMessageFormat(messages[key], locale);
return formater.format(values); return formater.format(values);
@ -197,10 +184,8 @@ export function generateI18n(locale = 'zh-CN', messages = {}) {
* ref * ref
* @param {*} Comp * @param {*} Comp
*/ */
export function acceptsRef(Comp) { export function acceptsRef(Comp: any) {
return ( return Comp?.$$typeof === REACT_FORWARD_REF_TYPE || Comp?.prototype?.isReactComponent || Comp?.prototype?.setState;
(Comp.$$typeof && Comp.$$typeof === REACT_FORWARD_REF_TYPE) || (Comp.prototype && Comp.prototype.isReactComponent)
);
} }
/** /**
@ -209,9 +194,9 @@ export function acceptsRef(Comp) {
* @param {Object} params * @param {Object} params
* @param {String} logKey * @param {String} logKey
*/ */
export function goldlog(gmKey, params = {}, logKey = 'other') { export function goldlog(gmKey: string, params = {}, logKey = 'other') {
// vscode 黄金令箭API // vscode 黄金令箭API
const sendIDEMessage = window.sendIDEMessage || (inSameDomain() && window.parent.sendIDEMessage); const sendIDEMessage = (window as any).sendIDEMessage || (inSameDomain() && (window.parent as any).sendIDEMessage);
const goKey = serializeParams({ const goKey = serializeParams({
sdkVersion: pkg.version, sdkVersion: pkg.version,
env: getEnv(), env: getEnv(),
@ -221,19 +206,19 @@ export function goldlog(gmKey, params = {}, logKey = 'other') {
sendIDEMessage({ sendIDEMessage({
action: 'goldlog', action: 'goldlog',
data: { data: {
logKey: `/iceluna.core.${logKey}`, logKey: `/lce.core.${logKey}`,
gmKey, gmKey,
goKey, goKey,
}, },
}); });
} }
window.goldlog && window.goldlog.record(`/iceluna.core.${logKey}`, gmKey, goKey, 'POST'); (window as any)?.goldlog?.record(`/lce.core.${logKey}`, gmKey, goKey, 'POST');
} }
// utils为编辑器打包生成的utils文件内容utilsConfig为数据库存放的utils配置 // utils为编辑器打包生成的utils文件内容utilsConfig为数据库存放的utils配置
export function generateUtils(utils, utilsConfig) { export function generateUtils(utils: any, utilsConfig: { name: string; type: string; content: any }[]) {
if (!Array.isArray(utilsConfig)) return { ...utils }; if (!Array.isArray(utilsConfig)) return { ...utils };
const res = {}; const res: any = {};
utilsConfig.forEach((item) => { utilsConfig.forEach((item) => {
if (!item.name || !item.type || !item.content) return; if (!item.name || !item.type || !item.content) return;
if (item.type === 'function' && typeof item.content === 'function') { if (item.type === 'function' && typeof item.content === 'function') {
@ -244,70 +229,20 @@ export function generateUtils(utils, utilsConfig) {
}); });
return res; return res;
} }
// 复制到粘贴板
export function setClipboardData(str) {
return new Promise((resolve, reject) => {
if (typeof str !== 'string') reject('不支持拷贝');
if (navigator.clipboard) {
navigator.clipboard
.writeText(str)
.then(() => {
resolve();
})
.catch((err) => {
reject('复制失败,请重试!', err);
});
} else {
const textArea = document.createElement('textarea');
textArea.value = str;
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
const successful = document.execCommand('copy');
if (successful) {
document.body.removeChild(textArea);
resolve();
}
} catch (err) {
document.body.removeChild(textArea);
reject('复制失败,请重试!', err);
}
}
});
}
// 获取粘贴板数据
export function getClipboardData() {
return new Promise((resolve, reject) => {
if (window.clipboardData) {
resolve(window.clipboardData.getData('text'));
} else if (navigator.clipboard) {
return navigator.clipboard
.readText()
.then((res) => {
resolve(res);
})
.catch((err) => {
reject('粘贴板获取失败', err);
});
} else {
reject('粘贴板获取失败');
}
});
}
// 将函数返回结果转成promise形式如果函数有返回值则根据返回值的bool类型判断是reject还是resolve若函数无返回值默认执行resolve // 将函数返回结果转成promise形式如果函数有返回值则根据返回值的bool类型判断是reject还是resolve若函数无返回值默认执行resolve
export function transformToPromise(input) { export function transformToPromise(input: any) {
if (input instanceof Promise) return input; if (input instanceof Promise) return input;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (input || input === undefined) { if (input || input === undefined) {
resolve(); resolve({});
} else { } else {
reject(); reject();
} }
}); });
} }
export function moveArrayItem(arr, sourceIdx, distIdx, direction) { export function moveArrayItem(arr: any[], sourceIdx: number, distIdx: number, direction: 'after' | 'before') {
if ( if (
!Array.isArray(arr) || !Array.isArray(arr) ||
sourceIdx === distIdx || sourceIdx === distIdx ||
@ -330,9 +265,9 @@ export function moveArrayItem(arr, sourceIdx, distIdx, direction) {
return arr; return arr;
} }
export function transformArrayToMap(arr, key, overwrite = true) { export function transformArrayToMap(arr: any[], key: string, overwrite = true) {
if (isEmpty(arr) || !Array.isArray(arr)) return {}; if (isEmpty(arr) || !Array.isArray(arr)) return {};
const res = {}; const res: any = {};
arr.forEach((item) => { arr.forEach((item) => {
const curKey = item[key]; const curKey = item[key];
if (item[key] === undefined) return; if (item[key] === undefined) return;
@ -342,7 +277,7 @@ export function transformArrayToMap(arr, key, overwrite = true) {
return res; return res;
} }
export function checkPropTypes(value, name, rule, componentName) { export function checkPropTypes(value: any, name: string, rule: any, componentName: string) {
if (typeof rule === 'string') { if (typeof rule === 'string') {
rule = new Function(`"use strict"; const PropTypes = arguments[0]; return ${rule}`)(PropTypes2); rule = new Function(`"use strict"; const PropTypes = arguments[0]; return ${rule}`)(PropTypes2);
} }
@ -366,15 +301,15 @@ export function checkPropTypes(value, name, rule, componentName) {
return !err; return !err;
} }
export function transformSchemaToPure(obj) { export function transformSchemaToPure(obj: any) {
const pureObj = (obj) => { const pureObj = (obj: any): any => {
if (Array.isArray(obj)) { if (Array.isArray(obj)) {
return obj.map((item) => pureObj(item)); return obj.map((item) => pureObj(item));
} else if (typeof obj === 'object') { } else if (typeof obj === 'object') {
// 对于undefined及null直接返回 // 对于undefined及null直接返回
if (!obj) return obj; if (!obj) return obj;
const res = {}; const res: any = {};
forEach(obj, (val, key) => { forEach(obj, (val: any, key: string) => {
if (key.startsWith('__') && key !== '__ignoreParse') return; if (key.startsWith('__') && key !== '__ignoreParse') return;
res[key] = pureObj(val); res[key] = pureObj(val);
}); });
@ -385,15 +320,15 @@ export function transformSchemaToPure(obj) {
return pureObj(obj); return pureObj(obj);
} }
export function transformSchemaToStandard(obj) { export function transformSchemaToStandard(obj: any) {
const standardObj = (obj) => { const standardObj = (obj: any): any => {
if (Array.isArray(obj)) { if (Array.isArray(obj)) {
return obj.map((item) => standardObj(item)); return obj.map((item) => standardObj(item));
} else if (typeof obj === 'object') { } else if (typeof obj === 'object') {
// 对于undefined及null直接返回 // 对于undefined及null直接返回
if (!obj) return obj; if (!obj) return obj;
const res = {}; const res: any = {};
forEach(obj, (val, key) => { forEach(obj, (val: any, key: string) => {
if (key.startsWith('__') && key !== '__ignoreParse') return; if (key.startsWith('__') && key !== '__ignoreParse') return;
if (isSchema(val) && key !== 'children' && obj.type !== 'JSSlot') { if (isSchema(val) && key !== 'children' && obj.type !== 'JSSlot') {
res[key] = { res[key] = {
@ -423,159 +358,19 @@ export function transformSchemaToStandard(obj) {
} }
return obj; return obj;
}; };
return standardObj(obj, false); return standardObj(obj);
} }
export function transformStringToFunction(str) { export function transformStringToFunction(str: string) {
if (typeof str !== 'string') return str; if (typeof str !== 'string') return str;
if (inSameDomain() && window.parent.__newFunc) { if (inSameDomain() && (window.parent as any).__newFunc) {
return window.parent.__newFunc(`"use strict"; return ${str}`)(); return (window.parent as any).__newFunc(`"use strict"; return ${str}`)();
} else { } else {
return new Function(`"use strict"; return ${str}`)(); return new Function(`"use strict"; return ${str}`)();
} }
} }
export function addCssTag(id, content) { export function parseData(schema: any, self: any): any {
let styleTag = document.getElementById(id);
if (styleTag) {
styleTag.innerHTML = content;
return;
}
styleTag = document.createElement('style');
styleTag.id = id;
styleTag.class = 'luna-style';
styleTag.innerHTML = content;
document.head.appendChild(styleTag);
}
// 注册快捷
export function registShortCuts(config, appHelper) {
const keyboardFilter = (event) => {
const eTarget = event.target || event.srcElement;
const { tagName } = eTarget;
const isInput = !!(tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA');
const isContenteditable = !!eTarget.getAttribute('contenteditable');
if (isInput || isContenteditable) {
if (event.metaKey === true && [70, 83].includes(event.keyCode)) event.preventDefault(); // 禁止触发chrome原生的页面保存或查找
return false;
} else {
return true;
}
};
keymaster.filter = keyboardFilter;
const ideMessage = appHelper.utils && appHelper.utils.ideMessage;
// 复制
if (!document.copyListener) {
document.copyListener = (e) => {
if (!keyboardFilter(e) || appHelper.isCopying) return;
const schema = appHelper.schemaHelper && appHelper.schemaHelper.schemaMap[appHelper.activeKey];
if (!schema || !isSchema(schema)) return;
appHelper.isCopying = true;
const schemaStr = serialize(transformSchemaToPure(schema), {
unsafe: true,
});
setClipboardData(schemaStr)
.then(() => {
ideMessage && ideMessage('success', '当前内容已复制到剪贴板请使用快捷键Command+v进行粘贴');
appHelper.emit('schema.copy', schemaStr, schema);
appHelper.isCopying = false;
})
.catch((errMsg) => {
ideMessage && ideMessage('error', errMsg);
appHelper.isCopying = false;
});
};
document.addEventListener('copy', document.copyListener);
if (window.parent.vscode) {
keymaster('command+c', document.copyListener);
}
}
// 粘贴
if (!document.pasteListener) {
const doPaste = (e, text) => {
if (!keyboardFilter(e) || appHelper.isPasting) return;
const { schemaHelper } = appHelper;
let targetKey = appHelper.activeKey;
let direction = 'after';
const topKey = schemaHelper.schema && schemaHelper.schema.__ctx && schemaHelper.schema.__ctx.lunaKey;
if (!targetKey || topKey === targetKey) {
const { schemaHelper } = appHelper;
const topKey = schemaHelper.schema && schemaHelper.schema.__ctx && schemaHelper.schema.__ctx.lunaKey;
if (!topKey) return;
targetKey = topKey;
direction = 'in';
}
appHelper.isPasting = true;
const schema = parseObj(text);
if (!isSchema(schema)) {
appHelper.emit('illegalSchema.paste', text);
// ideMessage && ideMessage('error', '当前内容不是模型结构,不能粘贴进来!');
console.warn('paste schema illegal');
appHelper.isPasting = false;
return;
}
appHelper.emit('material.add', {
schema,
targetKey,
direction,
});
appHelper.isPasting = false;
appHelper.emit('schema.paste', schema);
};
document.pasteListener = (e) => {
const clipboardData = e.clipboardData || window.clipboardData;
const text = clipboardData && clipboardData.getData('text');
doPaste(e, text);
};
document.addEventListener('paste', document.pasteListener);
if (window.parent.vscode) {
keymaster('command+v', (e) => {
const { sendIDEMessage } = window.parent;
sendIDEMessage &&
sendIDEMessage({
action: 'readClipboard',
})
.then((text) => {
doPaste(e, text);
})
.catch((err) => {
console.warn(err);
});
});
}
}
(config || []).forEach((item) => {
keymaster(item.keyboard, (ev) => {
ev.preventDefault();
item.handler(ev, appHelper, keymaster);
});
});
}
// 取消注册快捷
export function unRegistShortCuts(config) {
(config || []).forEach((item) => {
keymaster.unbind(item.keyboard);
});
if (window.parent.vscode) {
keymaster.unbind('command+c');
keymaster.unbind('command+v');
}
if (document.copyListener) {
document.removeEventListener('copy', document.copyListener);
delete document.copyListener;
}
if (document.pasteListener) {
document.removeEventListener('paste', document.pasteListener);
delete document.pasteListener;
}
}
export function parseData(schema, self) {
if (isJSExpression(schema)) { if (isJSExpression(schema)) {
return parseExpression(schema, self); return parseExpression(schema, self);
} else if (typeof schema === 'string') { } else if (typeof schema === 'string') {
@ -587,8 +382,8 @@ export function parseData(schema, self) {
} else if (typeof schema === 'object') { } else if (typeof schema === 'object') {
// 对于undefined及null直接返回 // 对于undefined及null直接返回
if (!schema) return schema; if (!schema) return schema;
const res = {}; const res: any = {};
forEach(schema, (val, key) => { forEach(schema, (val: any, key: string) => {
if (key.startsWith('__')) return; if (key.startsWith('__')) return;
res[key] = parseData(val, self); res[key] = parseData(val, self);
}); });
@ -598,14 +393,14 @@ export function parseData(schema, self) {
} }
/* 全匹配{{开头,}}结尾的变量表达式或者对象类型JSExpression且均不支持省略this */ /* 全匹配{{开头,}}结尾的变量表达式或者对象类型JSExpression且均不支持省略this */
export function parseExpression(str, self) { export function parseExpression(str: any, self: any) {
try { try {
const contextArr = ['"use strict";', 'var __self = arguments[0];']; const contextArr = ['"use strict";', 'var __self = arguments[0];'];
contextArr.push('return '); contextArr.push('return ');
let tarStr; let tarStr;
// 向前兼容,支持标准协议新格式 // 向前兼容,支持标准协议新格式
if (typeof str === 'string') { if (typeof str === 'string') {
const regRes = str.trim().match(EXPRESSION_REG); const regRes: any = str.trim().match(EXPRESSION_REG);
tarStr = regRes[1]; tarStr = regRes[1];
} else { } else {
tarStr = (str.value || '').trim(); tarStr = (str.value || '').trim();
@ -613,8 +408,8 @@ export function parseExpression(str, self) {
tarStr = tarStr.replace(/this(\W|$)/g, (a, b) => `__self${b}`); tarStr = tarStr.replace(/this(\W|$)/g, (a, b) => `__self${b}`);
tarStr = contextArr.join('\n') + tarStr; tarStr = contextArr.join('\n') + tarStr;
// 默认调用顶层窗口的parseObj,保障new Function的window对象是顶层的window对象 // 默认调用顶层窗口的parseObj,保障new Function的window对象是顶层的window对象
if (inSameDomain() && window.parent.__newFunc) { if (inSameDomain() && (window.parent as any).__newFunc) {
return window.parent.__newFunc(tarStr)(self); return (window.parent as any).__newFunc(tarStr)(self);
} }
return new Function(tarStr)(self); return new Function(tarStr)(self);
} catch (err) { } catch (err) {
@ -622,3 +417,16 @@ export function parseExpression(str, self) {
return undefined; return undefined;
} }
} }
// 首字母大写
export function capitalizeFirstLetter(word: string) {
return word[0].toUpperCase() + word.slice(1);
}
export function isI18n(obj: any) {
return obj && typeof obj === 'object' && obj?.type === 'i18n';
}
export function isVariable(obj: any) {
return obj && typeof obj === 'object' && obj?.type === 'variable';
}

View File

@ -1,6 +1,7 @@
/* eslint-disable object-curly-newline */ /* eslint-disable object-curly-newline */
import { transformArrayToMap, isJSFunction, transformStringToFunction, clone } from './index'; import { transformArrayToMap, isJSFunction, transformStringToFunction, clone } from './common';
import { jsonp, mtop, request, get, post, bzb } from './request'; import { jsonp, mtop, request, get, post, bzb } from './request';
import { DataSource, DataSourceItem } from '../types';
const DS_STATUS = { const DS_STATUS = {
INIT: 'init', INIT: 'init',
@ -8,12 +9,21 @@ const DS_STATUS = {
LOADED: 'loaded', LOADED: 'loaded',
ERROR: 'error', ERROR: 'error',
}; };
export default class DataHelper {
constructor(comp, config = {}, appHelper, parser) { export class DataHelper {
host: any;
config: DataSource;
parser: any;
ajaxList: any[];
ajaxMap: any;
dataSourceMap: any;
appHelper: any;
constructor(comp: any, config: DataSource, appHelper: any, parser: any) {
this.host = comp; this.host = comp;
this.config = config; this.config = config || {};
this.parser = parser; this.parser = parser;
this.ajaxList = (config && config.list) || []; this.ajaxList = config?.list || [];
this.ajaxMap = transformArrayToMap(this.ajaxList, 'id'); this.ajaxMap = transformArrayToMap(this.ajaxList, 'id');
this.dataSourceMap = this.generateDataSourceMap(); this.dataSourceMap = this.generateDataSourceMap();
this.appHelper = appHelper; this.appHelper = appHelper;
@ -21,8 +31,8 @@ export default class DataHelper {
// 重置configdataSourceMap状态会被重置 // 重置configdataSourceMap状态会被重置
resetConfig(config = {}) { resetConfig(config = {}) {
this.config = config; this.config = config as DataSource;
this.ajaxList = (config && config.list) || []; this.ajaxList = (config as DataSource)?.list || [];
this.ajaxMap = transformArrayToMap(this.ajaxList, 'id'); this.ajaxMap = transformArrayToMap(this.ajaxList, 'id');
this.dataSourceMap = this.generateDataSourceMap(); this.dataSourceMap = this.generateDataSourceMap();
return this.dataSourceMap; return this.dataSourceMap;
@ -30,9 +40,9 @@ export default class DataHelper {
// 更新config只会更新配置状态保存 // 更新config只会更新配置状态保存
updateConfig(config = {}) { updateConfig(config = {}) {
this.config = config; this.config = config as DataSource;
this.ajaxList = (config && config.list) || []; this.ajaxList = (config as DataSource)?.list || [];
const ajaxMap = transformArrayToMap(this.ajaxList, 'id'); const ajaxMap: any = transformArrayToMap(this.ajaxList, 'id');
// 删除已经移除的接口 // 删除已经移除的接口
Object.keys(this.ajaxMap).forEach((key) => { Object.keys(this.ajaxMap).forEach((key) => {
if (!ajaxMap[key]) { if (!ajaxMap[key]) {
@ -45,7 +55,8 @@ export default class DataHelper {
if (!this.dataSourceMap[item.id]) { if (!this.dataSourceMap[item.id]) {
this.dataSourceMap[item.id] = { this.dataSourceMap[item.id] = {
status: DS_STATUS.INIT, status: DS_STATUS.INIT,
load: (...args) => { load: (...args: any) => {
// @ts-ignore
return this.getDataSource(item.id, ...args); return this.getDataSource(item.id, ...args);
}, },
}; };
@ -55,11 +66,12 @@ export default class DataHelper {
} }
generateDataSourceMap() { generateDataSourceMap() {
const res = {}; const res: any = {};
this.ajaxList.forEach((item) => { this.ajaxList.forEach((item) => {
res[item.id] = { res[item.id] = {
status: DS_STATUS.INIT, status: DS_STATUS.INIT,
load: (...args) => { load: (...args: any) => {
// @ts-ignore
return this.getDataSource(item.id, ...args); return this.getDataSource(item.id, ...args);
}, },
}; };
@ -67,14 +79,14 @@ export default class DataHelper {
return res; return res;
} }
updateDataSourceMap(id, data, error) { updateDataSourceMap(id: string, data: any, error: any) {
this.dataSourceMap[id].error = error || undefined; this.dataSourceMap[id].error = error || undefined;
this.dataSourceMap[id].data = data; this.dataSourceMap[id].data = data;
this.dataSourceMap[id].status = error ? DS_STATUS.ERROR : DS_STATUS.LOADED; this.dataSourceMap[id].status = error ? DS_STATUS.ERROR : DS_STATUS.LOADED;
} }
getInitData() { getInitData() {
const initSyncData = this.parser(this.ajaxList).filter((item) => { const initSyncData = this.parser(this.ajaxList).filter((item: DataSourceItem) => {
if (item.isInit) { if (item.isInit) {
this.dataSourceMap[item.id].status = DS_STATUS.LOADING; this.dataSourceMap[item.id].status = DS_STATUS.LOADING;
return true; return true;
@ -89,14 +101,14 @@ export default class DataHelper {
} }
if (!dataHandler || typeof dataHandler !== 'function') return res; if (!dataHandler || typeof dataHandler !== 'function') return res;
try { try {
return dataHandler.call(this.host, res); return (dataHandler as any).call(this.host, res);
} catch (e) { } catch (e) {
console.error('请求数据处理函数运行出错', e); console.error('请求数据处理函数运行出错', e);
} }
}); });
} }
getDataSource(id, params, otherOptions, callback) { getDataSource(id: string, params: any, otherOptions: any, callback: any) {
const req = this.parser(this.ajaxMap[id]); const req = this.parser(this.ajaxMap[id]);
const options = req.options || {}; const options = req.options || {};
if (typeof otherOptions === 'function') { if (typeof otherOptions === 'function') {
@ -129,7 +141,7 @@ export default class DataHelper {
}, },
}, },
]) ])
.then((res) => { .then((res: any) => {
try { try {
callback && callback(res && res[id]); callback && callback(res && res[id]);
} catch (e) { } catch (e) {
@ -149,15 +161,15 @@ export default class DataHelper {
}); });
} }
asyncDataHandler(asyncDataList) { asyncDataHandler(asyncDataList: any[]) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const allReq = []; const allReq = [];
const doserReq = []; const doserReq: {name: string; package: string; params: any }[] = [];
const doserList = []; const doserList: string[] = [];
const beforeRequest = this.appHelper && this.appHelper.utils && this.appHelper.utils.beforeRequest; const beforeRequest = this.appHelper && this.appHelper.utils && this.appHelper.utils.beforeRequest;
const afterRequest = this.appHelper && this.appHelper.utils && this.appHelper.utils.afterRequest; const afterRequest = this.appHelper && this.appHelper.utils && this.appHelper.utils.afterRequest;
const csrfInput = document.getElementById('_csrf_token'); const csrfInput = document.getElementById('_csrf_token');
const _tb_token_ = csrfInput && csrfInput.value; const _tb_token_ = (csrfInput as any)?.value;
asyncDataList.forEach((req) => { asyncDataList.forEach((req) => {
const { id, type, options } = req; const { id, type, options } = req;
if (!id || !type || type === 'legao') return; if (!id || !type || type === 'legao') return;
@ -186,27 +198,27 @@ export default class DataHelper {
}); });
} }
if (allReq.length === 0) resolve({}); if (allReq.length === 0) resolve({});
const res = {}; const res: any = {};
// todo: // todo:
Promise.all( Promise.all(
allReq.map((item) => { allReq.map((item: any) => {
return new Promise((resolve) => { return new Promise((resolve) => {
const { type, id, dataHandler, options } = item; const { type, id, dataHandler, options } = item;
const doFetch = (type, options) => { const doFetch = (type: string, options: any) => {
this.fetchOne(type, options) this.fetchOne(type, options)
.then((data) => { .then((data: any) => {
if (afterRequest) { if (afterRequest) {
this.appHelper.utils.afterRequest(item, data, undefined, (data, error) => { this.appHelper.utils.afterRequest(item, data, undefined, (data: any, error: any) => {
fetchHandler(data, error); fetchHandler(data, error);
}); });
} else { } else {
fetchHandler(data, undefined); fetchHandler(data, undefined);
} }
}) })
.catch((err) => { .catch((err: Error) => {
if (afterRequest) { if (afterRequest) {
// 必须要这么调用否则beforeRequest中的this会丢失 // 必须要这么调用否则beforeRequest中的this会丢失
this.appHelper.utils.afterRequest(item, undefined, err, (data, error) => { this.appHelper.utils.afterRequest(item, undefined, err, (data: any, error: any) => {
fetchHandler(data, error); fetchHandler(data, error);
}); });
} else { } else {
@ -214,13 +226,13 @@ export default class DataHelper {
} }
}); });
}; };
const fetchHandler = (data, error) => { const fetchHandler = (data: any, error: any) => {
if (type === 'doServer') { if (type === 'doServer') {
if (!Array.isArray(data)) { if (!Array.isArray(data)) {
data = [data]; data = [data];
} }
doserList.forEach((id, idx) => { doserList.forEach((id, idx) => {
const req = this.ajaxMap[id]; const req: any = this.ajaxMap[id];
if (req) { if (req) {
res[id] = this.dataHandler(id, req.dataHandler, data && data[idx], error); res[id] = this.dataHandler(id, req.dataHandler, data && data[idx], error);
this.updateDataSourceMap(id, res[id], error); this.updateDataSourceMap(id, res[id], error);
@ -230,7 +242,7 @@ export default class DataHelper {
res[id] = this.dataHandler(id, dataHandler, data, error); res[id] = this.dataHandler(id, dataHandler, data, error);
this.updateDataSourceMap(id, res[id], error); this.updateDataSourceMap(id, res[id], error);
} }
resolve(); resolve({});
}; };
if (type === 'doServer') { if (type === 'doServer') {
@ -243,7 +255,7 @@ export default class DataHelper {
// 请求切片 // 请求切片
if (beforeRequest) { if (beforeRequest) {
// 必须要这么调用否则beforeRequest中的this会丢失 // 必须要这么调用否则beforeRequest中的this会丢失
this.appHelper.utils.beforeRequest(item, clone(options), (options) => doFetch(type, options)); this.appHelper.utils.beforeRequest(item, clone(options), (options: any) => doFetch(type, options));
} else { } else {
doFetch(type, options); doFetch(type, options);
} }
@ -260,7 +272,7 @@ export default class DataHelper {
} }
// dataHandler todo: // dataHandler todo:
dataHandler(id, dataHandler, data, error) { dataHandler(id: string, dataHandler: any, data: any, error: any) {
if (isJSFunction(dataHandler)) { if (isJSFunction(dataHandler)) {
dataHandler = transformStringToFunction(dataHandler.value); dataHandler = transformStringToFunction(dataHandler.value);
} }
@ -272,7 +284,7 @@ export default class DataHelper {
} }
} }
fetchOne(type, options) { fetchOne(type: string, options: any) {
// eslint-disable-next-line prefer-const // eslint-disable-next-line prefer-const
let { uri, url, method = 'GET', headers, params, ...otherProps } = options; let { uri, url, method = 'GET', headers, params, ...otherProps } = options;
otherProps = otherProps || {}; otherProps = otherProps || {};

View File

@ -0,0 +1,3 @@
export * from './common';
export * from './dataHelper';
export * from './request';

View File

@ -4,7 +4,7 @@ import fetchJsonp from 'fetch-jsonp';
import bzbRequest from '@ali/bzb-request'; import bzbRequest from '@ali/bzb-request';
import { serialize, buildUrl, parseUrl } from '@ali/b3-one/lib/url'; import { serialize, buildUrl, parseUrl } from '@ali/b3-one/lib/url';
export function get(dataAPI, params = {}, headers = {}, otherProps = {}) { export function get(dataAPI: any, params = {}, headers = {}, otherProps = {}) {
headers = { headers = {
Accept: 'application/json', Accept: 'application/json',
...headers, ...headers,
@ -13,7 +13,7 @@ export function get(dataAPI, params = {}, headers = {}, otherProps = {}) {
return request(dataAPI, 'GET', null, headers, otherProps); return request(dataAPI, 'GET', null, headers, otherProps);
} }
export function post(dataAPI, params = {}, headers = {}, otherProps = {}) { export function post(dataAPI: any, params = {}, headers: any = {}, otherProps = {}) {
headers = { headers = {
Accept: 'application/json', Accept: 'application/json',
'Content-Type': 'application/x-www-form-urlencoded', 'Content-Type': 'application/x-www-form-urlencoded',
@ -30,7 +30,7 @@ export function post(dataAPI, params = {}, headers = {}, otherProps = {}) {
); );
} }
export function request(dataAPI, method = 'GET', data, headers = {}, otherProps = {}) { export function request(dataAPI: any, method = 'GET', data: any, headers = {}, otherProps: any = {}) {
switch (method) { switch (method) {
case 'PUT': case 'PUT':
case 'DELETE': case 'DELETE':
@ -112,7 +112,7 @@ export function request(dataAPI, method = 'GET', data, headers = {}, otherProps
}); });
} }
export function jsonp(dataAPI, params = {}, otherProps = {}) { export function jsonp(dataAPI: any, params = {}, otherProps = {}) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
otherProps = { otherProps = {
timeout: 5000, timeout: 5000,
@ -133,7 +133,7 @@ export function jsonp(dataAPI, params = {}, otherProps = {}) {
}); });
} }
export function mtop(dataAPI, params, otherProps = {}) { export function mtop(dataAPI: any, params: any, otherProps: any = {}) {
fetchMtop.config.subDomain = otherProps.subDomain || 'm'; fetchMtop.config.subDomain = otherProps.subDomain || 'm';
return fetchMtop.request({ return fetchMtop.request({
api: dataAPI, api: dataAPI,
@ -147,7 +147,7 @@ export function mtop(dataAPI, params, otherProps = {}) {
}); });
} }
export function bzb(apiCode, params, otherProps = {}) { export function bzb(apiCode: string, params: any, otherProps: any = {}) {
// 通过url参数设置小二工作台请求环境 // 通过url参数设置小二工作台请求环境
const getUrlEnv = () => { const getUrlEnv = () => {
try { try {

View File

@ -0,0 +1,7 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "lib"
},
"include": ["./src/"],
}