Merge branch 'release/1.0.0' of gitlab.alibaba-inc.com:ali-lowcode/ali-lowcode-engine into release/1.0.0

This commit is contained in:
荣彬 2020-08-14 16:35:45 +08:00
commit c343a59c26
14 changed files with 1079 additions and 722 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "@ali/lowcode-code-generator", "name": "@ali/lowcode-code-generator",
"version": "0.8.10", "version": "0.8.12",
"description": "出码引擎 for LowCode Engine", "description": "出码引擎 for LowCode Engine",
"main": "lib/index.js", "main": "lib/index.js",
"files": [ "files": [

View File

@ -0,0 +1,3 @@
import { FileType } from '../types/core';
export const FILE_TYPE_FAMILY = [[FileType.TSX, FileType.TS, FileType.JSX, FileType.JS]];

View File

@ -1,28 +1,72 @@
import { import { BuilderComponentPlugin, IChunkBuilder, ICodeChunk, ICodeStruct, FileType } from '../types';
BuilderComponentPlugin,
IChunkBuilder,
ICodeChunk,
ICodeStruct,
} from '../types';
import { COMMON_SUB_MODULE_NAME } from '../const/generator'; import { COMMON_SUB_MODULE_NAME } from '../const/generator';
import { FILE_TYPE_FAMILY } from '../const/file';
type ChunkGroupInfo = {
chunk: ICodeChunk;
familyIdx?: number;
};
function whichFamily(type: FileType): [number, FileType[]] | undefined {
const idx = FILE_TYPE_FAMILY.findIndex((family) => family.indexOf(type) >= 0);
if (idx < 0) {
return undefined;
}
return [idx, FILE_TYPE_FAMILY[idx]];
}
export const groupChunks = (chunks: ICodeChunk[]): ICodeChunk[][] => { export const groupChunks = (chunks: ICodeChunk[]): ICodeChunk[][] => {
const col = chunks.reduce( const tmp: Record<string, Record<number, number>> = {};
(chunksSet: Record<string, ICodeChunk[]>, chunk) => { const col = chunks.reduce((chunksSet: Record<string, ChunkGroupInfo[]>, chunk) => {
const fileKey = `${chunk.subModule || COMMON_SUB_MODULE_NAME}.${ const fileKey = chunk.subModule || COMMON_SUB_MODULE_NAME;
chunk.fileType
}`;
if (!chunksSet[fileKey]) { if (!chunksSet[fileKey]) {
chunksSet[fileKey] = []; chunksSet[fileKey] = [];
} }
chunksSet[fileKey].push(chunk); const res = whichFamily(chunk.fileType as FileType);
return chunksSet; const info: ChunkGroupInfo = {
}, chunk,
{}, };
); if (res) {
const [familyIdx, family] = res;
const rank = family.indexOf(chunk.fileType as FileType);
if (tmp[fileKey]) {
if (tmp[fileKey][familyIdx] !== undefined) {
if (tmp[fileKey][familyIdx] > rank) {
tmp[fileKey][familyIdx] = rank;
}
} else {
tmp[fileKey][familyIdx] = rank;
}
} else {
tmp[fileKey] = {};
tmp[fileKey][familyIdx] = rank;
}
info.familyIdx = familyIdx;
}
return Object.keys(col).map(key => col[key]); chunksSet[fileKey].push(info);
return chunksSet;
}, {});
const result: ICodeChunk[][] = [];
Object.keys(col).forEach((key) => {
const byType: Record<string, ICodeChunk[]> = {};
col[key].forEach((info) => {
let t: string = info.chunk.fileType;
if (info.familyIdx !== undefined) {
t = FILE_TYPE_FAMILY[info.familyIdx][tmp[key][info.familyIdx]];
info.chunk.fileType = t;
}
if (!byType[t]) {
byType[t] = [];
}
byType[t].push(info.chunk);
});
result.push(...Object.keys(byType).map((t) => byType[t]));
});
return result;
}; };
/** /**
@ -39,7 +83,7 @@ export default class ChunkBuilder implements IChunkBuilder {
this.plugins = plugins; this.plugins = plugins;
} }
public async run( async run(
ir: unknown, ir: unknown,
initialStructure: ICodeStruct = { initialStructure: ICodeStruct = {
ir, ir,
@ -64,11 +108,11 @@ export default class ChunkBuilder implements IChunkBuilder {
}; };
} }
public getPlugins() { getPlugins() {
return this.plugins; return this.plugins;
} }
public addPlugin(plugin: BuilderComponentPlugin) { addPlugin(plugin: BuilderComponentPlugin) {
this.plugins.push(plugin); this.plugins.push(plugin);
} }
} }

View File

@ -37,29 +37,23 @@ export function createModuleBuilder(
const generateModule = async (input: unknown): Promise<ICompiledModule> => { const generateModule = async (input: unknown): Promise<ICompiledModule> => {
const moduleMainName = options.mainFileName || COMMON_SUB_MODULE_NAME; const moduleMainName = options.mainFileName || COMMON_SUB_MODULE_NAME;
if (chunkGenerator.getPlugins().length <= 0) { if (chunkGenerator.getPlugins().length <= 0) {
throw new CodeGeneratorError( throw new CodeGeneratorError('No plugins found. Component generation cannot work without any plugins!');
'No plugins found. Component generation cannot work without any plugins!',
);
} }
let files: IResultFile[] = []; let files: IResultFile[] = [];
const { chunks } = await chunkGenerator.run(input); const { chunks } = await chunkGenerator.run(input);
chunks.forEach(fileChunkList => { chunks.forEach((fileChunkList) => {
const content = linker.link(fileChunkList); const content = linker.link(fileChunkList);
const file = new ResultFile( const file = new ResultFile(fileChunkList[0].subModule || moduleMainName, fileChunkList[0].fileType, content);
fileChunkList[0].subModule || moduleMainName,
fileChunkList[0].fileType,
content,
);
files.push(file); files.push(file);
}); });
if (options.postProcessors.length > 0) { if (options.postProcessors.length > 0) {
files = files.map(file => { files = files.map((file) => {
let content = file.content; let content = file.content;
const type = file.ext; const type = file.ext;
options.postProcessors.forEach(processer => { options.postProcessors.forEach((processer) => {
content = processer(content, type); content = processer(content, type);
}); });
@ -81,25 +75,18 @@ export function createModuleBuilder(
const { files } = await generateModule(containerInfo); const { files } = await generateModule(containerInfo);
const dir = new ResultDir(containerInfo.moduleName); const dir = new ResultDir(containerInfo.moduleName);
files.forEach(file => dir.addFile(file)); files.forEach((file) => dir.addFile(file));
return dir; return dir;
} };
const linkCodeChunks = ( const linkCodeChunks = (chunks: Record<string, ICodeChunk[]>, fileName: string) => {
chunks: Record<string, ICodeChunk[]>,
fileName: string,
) => {
const files: IResultFile[] = []; const files: IResultFile[] = [];
Object.keys(chunks).forEach(fileKey => { Object.keys(chunks).forEach((fileKey) => {
const fileChunkList = chunks[fileKey]; const fileChunkList = chunks[fileKey];
const content = linker.link(fileChunkList); const content = linker.link(fileChunkList);
const file = new ResultFile( const file = new ResultFile(fileChunkList[0].subModule || fileName, fileChunkList[0].fileType, content);
fileChunkList[0].subModule || fileName,
fileChunkList[0].fileType,
content,
);
files.push(file); files.push(file);
}); });

View File

@ -166,15 +166,13 @@ export function generateBasicNode(
export function generateReactCtrlLine(ctx: INodeGeneratorContext, nodeItem: IComponentNodeItem): CodePiece[] { export function generateReactCtrlLine(ctx: INodeGeneratorContext, nodeItem: IComponentNodeItem): CodePiece[] {
const pieces: CodePiece[] = []; const pieces: CodePiece[] = [];
if (nodeItem.loop && nodeItem.loopArgs) { if (nodeItem.loop) {
let loopDataExp; const args: [string, string] = nodeItem.loopArgs || ['item', 'index'];
if (isJsExpression(nodeItem.loop)) { const loopData = generateCompositeType(nodeItem.loop, {
loopDataExp = `(${generateExpression(nodeItem.loop)})`; nodeGenerator: ctx.generator,
} else { });
loopDataExp = JSON.stringify(nodeItem.loop);
}
pieces.unshift({ pieces.unshift({
value: `${loopDataExp}.map((${nodeItem.loopArgs[0]}, ${nodeItem.loopArgs[1]}) => (`, value: `(${loopData}).map((${args[0]}, ${args[1]}) => (`,
type: PIECE_TYPE.BEFORE, type: PIECE_TYPE.BEFORE,
}); });
pieces.push({ pieces.push({
@ -198,7 +196,7 @@ export function generateReactCtrlLine(ctx: INodeGeneratorContext, nodeItem: ICom
}); });
} }
if (nodeItem.condition || (nodeItem.loop && nodeItem.loopArgs)) { if (nodeItem.condition || nodeItem.loop) {
pieces.unshift({ pieces.unshift({
value: '{', value: '{',
type: PIECE_TYPE.BEFORE, type: PIECE_TYPE.BEFORE,

File diff suppressed because it is too large Load Diff

View File

@ -64,44 +64,61 @@ const icons = [
]; ];
interface IconSetterProps { interface IconSetterProps {
value: string; value: string;
type: string;
defaultValue: string; defaultValue: string;
placeholder: string; placeholder: string;
hasClear: boolean; hasClear: boolean;
onChange: (icon: string) => undefined; onChange: (icon: string | object) => undefined;
icons: string[]; icons: string[];
} }
export default class IconSetter extends PureComponent<IconSetterProps, {}> { export default class IconSetter extends PureComponent<IconSetterProps, {}> {
static defaultProps = { static defaultProps = {
value: undefined, value: undefined,
type: 'string',
defaultValue: '', defaultValue: '',
hasClear: true, hasClear: true,
icons: icons, icons: icons,
placeholder: '请点击选择 Icon', placeholder: '请点击选择 Icon',
onChange: (icon: string) => undefined, onChange: (icon: string | object) => undefined,
}; };
state = { state = {
firstLoad: true, firstLoad: true,
}; };
onInputChange = (icon: string) => { _onChange = (icon: string) => {
const { onChange } = this.props; const { onChange, type } = this.props;
if (type === 'string') {
onChange(icon); onChange(icon);
} else if (type === 'node') {
onChange({
componentName: 'Icon',
props: {
type: icon,
},
});
}
};
onInputChange = (icon: string) => {
this._onChange(icon);
}; };
onSelectIcon = (icon: string) => { onSelectIcon = (icon: string) => {
const { onChange } = this.props; this._onChange(icon);
onChange(icon);
}; };
render() { render() {
const { icons, value, defaultValue, onChange, placeholder, hasClear } = this.props; const { icons, value, defaultValue, onChange, placeholder, hasClear, type } = this.props;
const { firstLoad } = this.state; const { firstLoad } = this.state;
if (firstLoad && defaultValue && typeof value === 'undefined') onChange(defaultValue); const _value = typeof value === 'object' ? value?.props?.type : value;
if (firstLoad && defaultValue && typeof value === 'undefined') {
onChange(defaultValue);
this.setState({ this.setState({
firstLoad: false, firstLoad: false,
}); });
const currentIcon = <Icon size="xs" type={value} />; }
const currentIcon = <Icon size="xs" type={_value} />;
const clearIcon = hasClear && ( const clearIcon = hasClear && (
<Icon <Icon
size="xs" size="xs"
@ -121,7 +138,7 @@ export default class IconSetter extends PureComponent<IconSetterProps, {}> {
placeholder={placeholder} placeholder={placeholder}
addonTextBefore={currentIcon} addonTextBefore={currentIcon}
onChange={this.onInputChange} onChange={this.onInputChange}
value={value} value={_value}
defaultValue={defaultValue} defaultValue={defaultValue}
readOnly readOnly
addonTextAfter={clearIcon} addonTextAfter={clearIcon}
@ -141,7 +158,7 @@ export default class IconSetter extends PureComponent<IconSetterProps, {}> {
> >
<ul className="lowcode-icon-list"> <ul className="lowcode-icon-list">
{icons.map((icon) => ( {icons.map((icon) => (
<li onClick={() => this.onSelectIcon(icon)}> <li key={icon} onClick={() => this.onSelectIcon(icon)}>
<Icon type={icon} size="medium" /> <Icon type={icon} size="medium" />
</li> </li>
))} ))}

View File

@ -15,6 +15,10 @@ export const StringSetter = {
defaultProps: { placeholder: '请输入', style: { maxWidth: 180 } }, defaultProps: { placeholder: '请输入', style: { maxWidth: 180 } },
title: 'StringSetter', title: 'StringSetter',
recommend: true, recommend: true,
condition: (field: any) => {
const v = field.getValue();
return typeof v === 'string';
},
}; };
export const NumberSetter = NumberPicker; export const NumberSetter = NumberPicker;
export class BoolSetter extends Component { export class BoolSetter extends Component {
@ -38,6 +42,10 @@ export const TextAreaSetter = {
defaultProps: { placeholder: '请输入', style: { maxWidth: 180 } }, defaultProps: { placeholder: '请输入', style: { maxWidth: 180 } },
title: 'TextAreaSetter', title: 'TextAreaSetter',
recommend: true, recommend: true,
condition: (field: any) => {
const v = field.getValue();
return typeof v === 'string';
},
}; };
export const DateSetter = DatePicker; export const DateSetter = DatePicker;
export const DateYearSetter = DatePicker.YearPicker; export const DateYearSetter = DatePicker.YearPicker;

View File

@ -13,7 +13,7 @@
--popup-border-radius: @popup-border-radius; --popup-border-radius: @popup-border-radius;
--left-area-width: 48px; --left-area-width: 48px;
--right-area-width: 280px; --right-area-width: 300px;
--top-area-height: 48px; --top-area-height: 48px;
--toolbar-height: 36px; --toolbar-height: 36px;
--dock-pane-width: 280px; --dock-pane-width: 280px;

View File

@ -47,6 +47,12 @@ function propTypeToSetter(propType: PropType): SetterType {
isRequired, isRequired,
initialValue: '', initialValue: '',
}; };
case 'Json':
return {
componentName: 'JsonSetter',
isRequired,
initialValue: '',
};
case 'color': case 'color':
return { return {
componentName: 'ColorSetter', componentName: 'ColorSetter',
@ -88,7 +94,7 @@ function propTypeToSetter(propType: PropType): SetterType {
value, value,
}; };
}); });
const componentName = dataSource.length > 4 ? 'SelectSetter' : 'RadioGroupSetter'; const componentName = dataSource.length >= 4 ? 'SelectSetter' : 'RadioGroupSetter';
return { return {
componentName, componentName,
props: { dataSource }, props: { dataSource },

View File

@ -1,5 +1,5 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { Editor, Title } from '@ali/lowcode-editor-core'; import { Editor } from '@ali/lowcode-editor-core';
import Icon from '@ali/ve-icons'; import Icon from '@ali/ve-icons';
import { Button } from '@alifd/next'; import { Button } from '@alifd/next';
import { Designer } from '@ali/lowcode-designer'; import { Designer } from '@ali/lowcode-designer';
@ -81,7 +81,7 @@ export default class UndoRedo extends PureComponent<IProps, IState> {
return ( return (
<div className="lowcode-plugin-undo-redo"> <div className="lowcode-plugin-undo-redo">
<Button <Button
size="18px" size="medium"
data-tip="撤销" data-tip="撤销"
data-dir="bottom" data-dir="bottom"
className="ve-local-history-item" className="ve-local-history-item"
@ -92,7 +92,7 @@ export default class UndoRedo extends PureComponent<IProps, IState> {
<Icon name="amindUndo" size="18px" /> <Icon name="amindUndo" size="18px" />
</Button> </Button>
<Button <Button
size="18px" size="medium"
data-tip="恢复" data-tip="恢复"
data-dir="bottom" data-dir="bottom"
className="ve-local-history-item" className="ve-local-history-item"

View File

@ -109,20 +109,32 @@ class Renderer extends Component<{ renderer: SimulatorRenderer }> {
viewProps._leaf = leaf; viewProps._leaf = leaf;
viewProps._componentName = leaf?.componentName; viewProps._componentName = leaf?.componentName;
// 如果是容器 && 无children && 高宽为空 增加一个占位容器,方便拖动 // 如果是容器 && 无children && 高宽为空 增加一个占位容器,方便拖动
if (leaf?.isContainer() && (children == null || (Array.isArray(children) && !children.length)) && (!viewProps.style || Object.keys(viewProps.style).length == 0)){ if (
children = <div style={{ !viewProps.dataSource &&
leaf?.isContainer() &&
(children == null || (Array.isArray(children) && !children.length)) &&
(!viewProps.style || Object.keys(viewProps.style).length === 0)
) {
children = (
<div
style={{
height: '66px', height: '66px',
backgroundColor: '#f0f0f0', backgroundColor: '#f0f0f0',
borderColor: '#a7b1bd', borderColor: '#a7b1bd',
border: '1px dotted', border: '1px dotted',
color: '#a7b1bd', color: '#a7b1bd',
textAlign: 'center', textAlign: 'center',
lineHeight:'66px' lineHeight: '66px',
}}> }}
>
{viewProps.placeholder || '拖拽组件或模板到这里'}
</div> </div>
);
} }
if(viewProps._componentName === 'a') {
delete viewProps.href;
}
// FIXME: 渲染仍有问题
if (viewProps._componentName === 'Menu') { if (viewProps._componentName === 'Menu') {
Object.assign(viewProps, { Object.assign(viewProps, {
_componentName: 'Menu', _componentName: 'Menu',

View File

@ -12,5 +12,5 @@ export interface DataSourceConfig {
} }
export interface DataSource { export interface DataSource {
items: DataSourceConfig[]; list: DataSourceConfig[];
} }

View File

@ -12,7 +12,7 @@ function accessLibrary(library: string | object) {
return library; return library;
} }
return (window as any)[library]; return (window as any)[library] || library;
} }
export function getSubComponent(library: any, paths: string[]) { export function getSubComponent(library: any, paths: string[]) {