@lcap/asl
Version:
NetEase Application Specific Language
655 lines (619 loc) • 29.4 kB
text/typescript
import { dataTypesMap, vertexsMap, utils, Entity, View, EntityProperty, Enum, LEVEL_ENUM } from '../..';
import {
filterProperty, NameGroup, genUniqueQueryNameGroup, getFirstDisplayedProperty, getParamFromResolver, genGenericTypeSchema, genVariable, genInterParam, genCallComponentLogic, genCallInterface, genQueryInterface, genQueryStructure,
genEnumSelectBlock, genTableTemplate, genTableLoadLogic, genTableRemoveLogic, genLoadSelectLogic, genGridViewTemplate, genGridViewLoadLogic, genGridViewRemoveLogic,
} from '.';
import builtInFunctions = require('./builtInFunctions.json');
function genFilterTemplate(entity: Entity, nameGroup: NameGroup, selectNameGroupMap: Map<string, NameGroup>, tableRefName = 'tableView') {
const propertyList = entity.propertyList
.filter(filterProperty('inFilter'));
return `
<u-linear-layout justify="space-between">
<u-form layout="inline" label-size="auto">
${propertyList.map((property) => {
const label = property.label || property.name;
let formItem = `<u-form-item label="${label}">`;
if (property.$relationEntity) { // 有外键关联
const relationEntity = vertexsMap.get(property.$relationEntity) as Entity;
if (relationEntity) {
const relationProperty = vertexsMap.get(property.$relationProperty) as EntityProperty;
const displayedProperty = getFirstDisplayedProperty(relationEntity, 'inFilter');
if (displayedProperty) {
const lowerEntityName = utils.firstLowerCase(relationEntity.name);
formItem += `
<u-select clearable placeholder="请选择${label}" :data-source="${selectNameGroupMap.get(relationEntity.id).load}"
text-field="${lowerEntityName}.${displayedProperty.name}" value-field="${lowerEntityName}.${relationProperty.name}" pageable remote-paging v-model="filter.${property.name}">
</u-select>
`;
} else
return '';
} else
return '';
} else if (property.typeKey === '#/basicTypes/Boolean') {
formItem += `
<u-select clearable v-model="filter.${property.name}" placeholder="请输入${label}">
<u-select-item :value="true">是</u-select-item>
<u-select-item :value="false">否</u-select-item>
</u-select>
`;
} else if (property.typeKey === '#/basicTypes/Integer' || property.typeKey === '#/basicTypes/Long') {
formItem += `<u-number-input v-model="filter.${property.name}" placeholder="请输入${label}"></u-number-input>`;
} else if (property.typeKey === '#/basicTypes/Decimal') {
formItem += `<u-number-input v-model="filter.${property.name}" :precision="0" :step="0" placeholder="请输入${label}"></u-number-input>`;
} else if (property.typeKey === '#/basicTypes/Text') {
formItem += `<u-textarea v-model="filter.${property.name}" placeholder="请输入${label}"></u-textarea>`;
} else if (property.typeKey === '#/basicTypes/Date') {
formItem += `<u-date-picker clearable :date.sync="filter.${property.name}" placeholder="请输入${label}"></u-date-picker>`;
} else if (property.typeKey === '#/basicTypes/Time') {
formItem += `<u-time-picker :time.sync="filter.${property.name}" placeholder="请输入${label}"></u-time-picker>`;
} else if (property.typeKey === '#/basicTypes/DateTime') {
formItem += `<u-date-time-picker clearable :date.sync="filter.${property.name}" placeholder="请输入${label}"></u-date-time-picker>`;
} else if (dataTypesMap[property.typeKey].type === 'enum') {
formItem += genEnumSelectBlock(dataTypesMap[property.typeKey] as Enum, `filter.${property.name}`, '', `请选择${label}`);
} else {
formItem += `<u-input v-model="filter.${property.name}" placeholder="请输入${label}"></u-input>`;
}
formItem += `</u-form-item>`;
return formItem;
}).join('')}
<u-form-item>
<u-button color="primary" @click="$refs.${tableRefName}.reload()">查询</u-button>
</u-form-item>
</u-form>
<u-button color="primary" @click="${nameGroup.create}($event)">创建</u-button>
</u-linear-layout>
`;
}
function genSaveModalTemplate(entity: Entity, nameGroup: NameGroup, selectNameGroupMap: Map<string, NameGroup>) {
const propertyList = entity.propertyList
.filter(filterProperty('inForm'));
const idProperty = entity.propertyList.find((item) => item.primaryKey || item.name === 'id');
return `
<u-modal ref="saveModal">
<template #title>
<div v-if="input.${idProperty.name}">修改</div>
<div v-if="input.${idProperty.name} == undefined">创建</div>
</template>
<template #body>
<u-form ref="form1">
${propertyList.map((property) => {
const label = property.label || property.name;
const required = !!property.required;
const rules = [];
if (property.rules && property.rules.length) {
property.rules.forEach((rule) => rules.push(rule));
}
if (required)
rules.push('required');
const rulesStr = rules.join(' | ');
let formItem = `<u-form-item
label="${label}"
${required ? 'required' : ''}
${rulesStr.length ? `rules="${rulesStr}"` : ''}
>`;
if (property.$relationEntity) { // 有外键关联
const relationEntity = vertexsMap.get(property.$relationEntity) as Entity;
if (relationEntity) {
const relationProperty = vertexsMap.get(property.$relationProperty) as EntityProperty;
const displayedProperty = getFirstDisplayedProperty(relationEntity, 'inForm');
if (displayedProperty) {
const lowerEntityName = utils.firstLowerCase(relationEntity.name);
formItem += `
<u-select clearable placeholder="请选择${label}" :data-source="${selectNameGroupMap.get(relationEntity.id).load}"
text-field="${lowerEntityName}.${displayedProperty.name}" value-field="${lowerEntityName}.${relationProperty.name}" pageable remote-paging v-model="input.${property.name}">
</u-select>
`;
} else
return '';
} else
return '';
} else if (property.typeKey === '#/basicTypes/Boolean') {
formItem += `
<u-select clearable v-model="input.${property.name}" placeholder="请输入${label}">
<u-select-item :value="true">是</u-select-item>
<u-select-item :value="false">否</u-select-item>
</u-select>`;
} else if (property.typeKey === '#/basicTypes/Integer' || property.typeKey === '#/basicTypes/Long') {
formItem += `<u-number-input v-model="input.${property.name}" placeholder="请输入${label}"></u-number-input>`;
} else if (property.typeKey === '#/basicTypes/Decimal') {
formItem += `<u-number-input v-model="input.${property.name}" :precision="0" :step="0" placeholder="请输入${label}"></u-number-input>`;
} else if (property.typeKey === '#/basicTypes/Text') {
formItem += `<u-textarea v-model="input.${property.name}" placeholder="请输入${label}"></u-textarea>`;
} else if (property.typeKey === '#/basicTypes/Date') {
formItem += `<u-date-picker clearable :date.sync="input.${property.name}" placeholder="请输入${label}"></u-date-picker>`;
} else if (property.typeKey === '#/basicTypes/Time') {
formItem += `<u-time-picker :time.sync="input.${property.name}" placeholder="请输入${label}"></u-time-picker>`;
} else if (property.typeKey === '#/basicTypes/DateTime') {
formItem += `<u-date-time-picker clearable :date.sync="input.${property.name}" placeholder="请输入${label}"></u-date-time-picker>`;
} else if (dataTypesMap[property.typeKey].type === 'enum') {
formItem += genEnumSelectBlock(dataTypesMap[property.typeKey] as Enum, `input.${property.name}`, '', `请选择${label}`);
} else {
formItem += `<u-input v-model="input.${property.name}" placeholder="请输入${label}"></u-input>`;
}
formItem += `</u-form-item>\n`;
return formItem;
}).join('')}
</u-form>
</template>
<template #foot>
<u-linear-layout>
<u-button color="primary" @click="${nameGroup.submit}()" v-if="input.${idProperty.name}">提交修改</u-button>
<u-button color="primary" @click="${nameGroup.submit}()" v-if="input.${idProperty.name} == undefined">立即创建</u-button>
</u-linear-layout>
</template>
</u-modal>`;
}
function genInitLogic(entity: Entity, nameGroup: NameGroup) {
return {
level: 'logic',
expanded: false,
name: nameGroup.init,
description: '',
params: [] as Array<any>,
returns: [] as Array<any>,
variables: [] as Array<any>,
body: [
{
level: 'logicNode',
type: 'Start',
label: '开始',
},
{
level: 'expressionNode',
type: 'BuiltInFunction',
label: '内置函数',
calleeCode: 'Clear',
builtInFuncParams: [
{
parentAttr: 'builtInFuncParams',
level: 'expressionNode',
type: 'BuiltInFuncParam',
name: 'obj',
builtInFuncParamValue: {
parentAttr: 'builtInFuncParamValue',
level: 'expressionNode',
type: 'Identifier',
name: 'filter',
},
schema: builtInFunctions.Clear.params[0].schema,
},
],
},
{
level: 'logicNode',
type: 'End',
label: '结束',
},
],
playground: [] as Array<any>,
};
}
function genCreateLogic(entity: Entity, nameGroup: NameGroup) {
return {
level: 'logic',
expanded: false,
name: nameGroup.create,
description: '',
params: [] as Array<any>,
returns: [] as Array<any>,
variables: [] as Array<any>,
body: [
{
level: 'logicNode',
type: 'Start',
label: '开始',
},
{
level: 'logicNode',
type: 'AssignmentExpression',
label: '赋值',
operator: '=',
left: {
level: 'expressionNode',
type: 'Identifier',
name: 'input',
},
right: {
parentAttr: 'right',
level: 'expressionNode',
type: 'BuiltInFunction',
label: '内置函数',
calleeCode: 'Clone',
builtInFuncParams: [
{
parentAttr: 'builtInFuncParams',
level: 'expressionNode',
type: 'BuiltInFuncParam',
name: 'obj',
builtInFuncParamValue: {
parentAttr: 'builtInFuncParamValue',
level: 'expressionNode',
type: 'Identifier',
name: nameGroup.lowerEntity,
},
schema: builtInFunctions.Clone.params[0].schema,
},
],
},
},
genCallComponentLogic('saveModal', 'open'),
{
level: 'logicNode',
type: 'End',
label: '结束',
},
],
playground: [] as Array<any>,
};
}
function genModifyLogic(entity: Entity, nameGroup: NameGroup) {
const scopeStructureSchema = genGenericTypeSchema('ScopeOf', { T: { $ref: nameGroup.structure } });
return {
level: 'logic',
expanded: false,
name: nameGroup.modify,
description: '',
params: [
{
level: 'param',
type: 'Identifier',
name: 'event',
schema: {
type: 'string',
format: '',
},
},
{
level: 'param',
name: 'scope',
schema: scopeStructureSchema,
},
],
returns: [] as Array<any>,
variables: [] as Array<any>,
body: [
{
level: 'logicNode',
type: 'Start',
label: '开始',
},
{
level: 'logicNode',
type: 'AssignmentExpression',
label: '赋值',
operator: '=',
left: {
level: 'expressionNode',
type: 'Identifier',
name: 'input',
},
right: {
parentAttr: 'right',
level: 'expressionNode',
type: 'BuiltInFunction',
label: '内置函数',
calleeCode: 'Clone',
builtInFuncParams: [
{
parentAttr: 'builtInFuncParams',
level: 'expressionNode',
type: 'BuiltInFuncParam',
name: 'obj',
builtInFuncParamValue: {
parentAttr: 'builtInFuncParamValue',
level: 'expressionNode',
type: 'MemberExpression',
object: {
parentAttr: 'builtInFuncParamValue',
level: 'expressionNode',
type: 'MemberExpression',
object: {
parentAttr: 'object',
level: 'expressionNode',
type: 'Identifier',
name: 'scope',
},
property: {
parentAttr: 'property',
level: 'expressionNode',
type: 'Identifier',
name: 'item',
schemaRef: '',
code: 'item',
},
code: '',
},
property: {
parentAttr: 'property',
level: 'expressionNode',
type: 'Identifier',
name: nameGroup.lowerEntity,
schemaRef: `${nameGroup.structure}.${nameGroup.lowerEntity}`,
code: '',
},
code: '',
},
schema: builtInFunctions.Clone.params[0].schema,
},
],
},
},
genCallComponentLogic('saveModal', 'open'),
{
level: 'logicNode',
type: 'End',
label: '结束',
},
],
playground: [] as Array<any>,
};
}
function genSubmitLogic(entity: Entity, nameGroup: NameGroup, tableRefName = 'tableView') {
const updateResolver = entity.resolvers.find((item) => item.name === 'update');
const createResolver = entity.resolvers.find((item) => item.name === 'create');
const updateBodyParam = getParamFromResolver(updateResolver, 'body');
const createBodyparam = getParamFromResolver(createResolver, 'body');
const idProperty = entity.propertyList.find((item) => item.primaryKey || item.name === 'id');
return {
level: 'logic',
name: nameGroup.submit,
params: [] as Array<any>,
returns: [] as Array<any>,
variables: [
genVariable('validateResult', '#/systemTypes/ValidateEvent', LEVEL_ENUM.variable),
],
body: [
{
level: 'logicNode',
type: 'Start',
label: '开始',
},
{
level: 'logicNode',
type: 'AssignmentExpression',
label: '赋值',
operator: '=',
left: {
level: 'expressionNode',
type: 'Identifier',
name: 'validateResult',
},
right: genCallComponentLogic('form1', 'validate'),
},
{
level: 'logicNode',
type: 'IfStatement',
label: '条件分支',
test: {
level: 'expressionNode',
type: 'MemberExpression',
name: 'name',
hide: false,
object: {
level: 'expressionNode',
type: 'Identifier',
name: 'validateResult',
},
property: {
level: 'expressionNode',
type: 'Identifier',
name: 'valid',
},
},
consequent: [
{
level: 'logicNode',
type: 'IfStatement',
label: '条件分支',
test: {
parentAttr: 'test',
level: 'expressionNode',
type: 'BuiltInFunction',
label: '内置函数',
calleeCode: 'Convert',
builtInFuncParams: [
{
parentAttr: 'builtInFuncParams',
level: 'expressionNode',
type: 'BuiltInFuncParam',
name: 'value',
builtInFuncParamValue: {
level: 'expressionNode',
type: 'MemberExpression',
object: {
level: 'expressionNode',
type: 'Identifier',
name: 'input',
},
property: {
level: 'expressionNode',
type: 'Identifier',
name: 'id',
schemaRef: idProperty.id,
code: `ID_${idProperty.id}`,
},
},
schema: builtInFunctions.Convert.params[0].schema,
},
{
parentAttr: 'builtInFuncParams',
level: 'expressionNode',
type: 'BuiltInFuncParam',
name: 'convertTo',
builtInFuncParamValue: {
level: 'expressionNode',
type: 'TypeNote',
schema: {
format: '',
type: 'boolean',
},
},
schema: builtInFunctions.Convert.params[1].schema,
},
],
},
consequent: [
genCallInterface(updateResolver.interface, [
genInterParam(updateBodyParam.id, 'input'),
]),
],
alternate: [
genCallInterface(createResolver.interface, [
genInterParam(createBodyparam.id, 'input'),
]),
],
},
genCallComponentLogic('saveModal', 'close'),
genCallComponentLogic(tableRefName, 'reload'),
],
alternate: [] as Array<any>,
},
{
level: 'logicNode',
type: 'End',
label: '结束',
},
],
playground: [] as Array<any>,
expanded: false,
};
}
export function genCurdBlock(entity: Entity, view: View) {
const existingNameSets = {
viewLogic: new Set(view.$def.logics.map((logic) => logic.name)),
interface: new Set(entity.dataNode.service.interfaces.map((itface) => itface.name)),
structure: new Set(entity.dataNode.structures.map((structure) => structure.name)),
};
const nameGroup = genUniqueQueryNameGroup(existingNameSets, view.name, 'tableView');
nameGroup.remove = utils.unique('remove', existingNameSets.viewLogic);
nameGroup.init = utils.unique('init', existingNameSets.viewLogic);
nameGroup.create = utils.unique('create', existingNameSets.viewLogic);
nameGroup.modify = utils.unique('modify', existingNameSets.viewLogic);
nameGroup.submit = utils.unique('submit', existingNameSets.viewLogic);
nameGroup.lowerEntity = utils.firstLowerCase(entity.name);
// 收集所有和本实体关联的实体
const allEntities = [entity];
entity.propertyList.forEach((property) => {
if (property.$relationEntity) { // 有外键关联
const relationEntity = vertexsMap.get(property.$relationEntity) as Entity;
if (relationEntity) {
const displayedProperty = getFirstDisplayedProperty(relationEntity);
if (displayedProperty && !allEntities.includes(relationEntity))
allEntities.push(relationEntity);
}
}
});
const newStructures: Array<any> = [genQueryStructure(allEntities, nameGroup)];
const newInterfaces: Array<any> = [genQueryInterface(allEntities, nameGroup, true, true)];
const selectNameGroupMap: Map<string, NameGroup> = new Map();
const loadSelectLogics = allEntities.slice(1).map((entity) => {
const selectNameGroup = genUniqueQueryNameGroup(existingNameSets, view.name, 'select', false, entity.name);
selectNameGroupMap.set(entity.id, selectNameGroup);
return genLoadSelectLogic(entity, selectNameGroup, newStructures, newInterfaces);
});
return `<template>
<u-linear-layout direction="vertical">
${genFilterTemplate(entity, nameGroup, selectNameGroupMap)}
${genTableTemplate(entity, nameGroup)}
${genSaveModalTemplate(entity, nameGroup, selectNameGroupMap)}
</u-linear-layout>
</template>
<definition>
{
"variables": [
${JSON.stringify(genVariable(utils.firstLowerCase(entity.name), entity.schemaRef, LEVEL_ENUM.variable))},
${JSON.stringify(genVariable('input', entity.schemaRef, LEVEL_ENUM.variable))},
${JSON.stringify(genVariable('filter', entity.schemaRef, LEVEL_ENUM.variable))}
],
"lifecycles": [
{
"level": "lifecycle",
"name": "created",
"logicId": "${nameGroup.init}"
}
],
"logics": [
${JSON.stringify(genTableLoadLogic(entity, nameGroup, newStructures, newInterfaces, true))},
${JSON.stringify(genTableRemoveLogic(entity, nameGroup))},
${JSON.stringify(genInitLogic(entity, nameGroup))},
${JSON.stringify(genCreateLogic(entity, nameGroup))},
${JSON.stringify(genModifyLogic(entity, nameGroup))},
${JSON.stringify(genSubmitLogic(entity, nameGroup))}
${loadSelectLogics.map((logic) => ',' + JSON.stringify(logic)).join('\n')}
],
"interfaces": ${JSON.stringify(newInterfaces)},
"structures": ${JSON.stringify(newStructures)}
}
</definition>
`;
}
export function genCurdGridViewBlock(entity: Entity, view: View) {
const existingNameSets = {
viewLogic: new Set(view.$def.logics.map((logic) => logic.name)),
interface: new Set(entity.dataNode.service.interfaces.map((itface) => itface.name)),
structure: new Set(entity.dataNode.structures.map((structure) => structure.name)),
};
const nameGroup = genUniqueQueryNameGroup(existingNameSets, view.name, 'gridView');
nameGroup.remove = utils.unique('remove', existingNameSets.viewLogic);
nameGroup.init = utils.unique('init', existingNameSets.viewLogic);
nameGroup.create = utils.unique('create', existingNameSets.viewLogic);
nameGroup.modify = utils.unique('modify', existingNameSets.viewLogic);
nameGroup.submit = utils.unique('submit', existingNameSets.viewLogic);
nameGroup.lowerEntity = utils.firstLowerCase(entity.name);
// 收集所有和本实体关联的实体
const allEntities = [entity];
entity.propertyList.forEach((property) => {
if (property.$relationEntity) { // 有外键关联
const relationEntity = vertexsMap.get(property.$relationEntity) as Entity;
if (relationEntity) {
const displayedProperty = getFirstDisplayedProperty(relationEntity);
if (displayedProperty && !allEntities.includes(relationEntity))
allEntities.push(relationEntity);
}
}
});
const newStructures: Array<any> = [genQueryStructure(allEntities, nameGroup)];
const newInterfaces: Array<any> = [genQueryInterface(allEntities, nameGroup, true, true)];
const selectNameGroupMap: Map<string, NameGroup> = new Map();
const loadSelectLogics = allEntities.slice(1).map((entity) => {
const selectNameGroup = genUniqueQueryNameGroup(existingNameSets, view.name, 'select', false, entity.name);
selectNameGroupMap.set(entity.id, selectNameGroup);
return genLoadSelectLogic(entity, selectNameGroup, newStructures, newInterfaces);
});
return `<template>
<u-linear-layout direction="vertical">
${genFilterTemplate(entity, nameGroup, selectNameGroupMap, 'gridView')}
${genGridViewTemplate(entity, nameGroup)}
${genSaveModalTemplate(entity, nameGroup, selectNameGroupMap)}
</u-linear-layout>
</template>
<definition>
{
"variables": [
${JSON.stringify(genVariable(utils.firstLowerCase(entity.name), entity.schemaRef, LEVEL_ENUM.variable))},
${JSON.stringify(genVariable('input', entity.schemaRef, LEVEL_ENUM.variable))},
${JSON.stringify(genVariable('filter', entity.schemaRef, LEVEL_ENUM.variable))}
],
"lifecycles": [
{
"level": "lifecycle",
"name": "created",
"logicId": "${nameGroup.init}"
}
],
"logics": [
${JSON.stringify(genGridViewLoadLogic(entity, nameGroup, newStructures, newInterfaces, true))},
${JSON.stringify(genGridViewRemoveLogic(entity, nameGroup))},
${JSON.stringify(genInitLogic(entity, nameGroup))},
${JSON.stringify(genCreateLogic(entity, nameGroup))},
${JSON.stringify(genModifyLogic(entity, nameGroup))},
${JSON.stringify(genSubmitLogic(entity, nameGroup, 'gridView'))}
${loadSelectLogics.map((logic) => ',' + JSON.stringify(logic)).join('\n')}
],
"interfaces": ${JSON.stringify(newInterfaces)},
"structures": ${JSON.stringify(newStructures)}
}
</definition>
`;
}
export default genCurdBlock;