Merge branch 'pre-release' into feature-new-combo

This commit is contained in:
zhou999 2022-11-21 19:13:47 +08:00
commit e12f1196f5
54 changed files with 2077 additions and 709 deletions

View File

@ -1,6 +1,6 @@
{
"name": "amis-editor",
"version": "5.2.1-beta.7",
"version": "5.2.1-beta.15",
"description": "amis 可视化编辑器",
"main": "lib/index.js",
"module": "esm/index.js",
@ -46,17 +46,17 @@
"@types/lodash": "^4.14.76",
"@types/node": "^14.0.24",
"@types/qs": "^6.5.1",
"@types/react": "^16.9.11",
"@types/react-dom": "^16.9.4",
"@types/react": "^18.0.24",
"@types/react-dom": "^18.0.8",
"@types/react-router": "^4.0.16",
"@types/react-router-dom": "^5.1.7",
"@types/sortablejs": "^1.10.7",
"@types/tinycolor2": "^1.4.3",
"ajv": "^8.8.2",
"amis": "2.4.0",
"amis-core": "2.4.0",
"amis-formula": "2.4.0",
"amis-ui": "2.4.0",
"amis": "2.4.1-beta.0",
"amis-core": "2.4.1-beta.0",
"amis-formula": "2.4.1-beta.0",
"amis-ui": "2.4.1-beta.0",
"axios": "0.21.1",
"concurrently": "^6.2.0",
"css-loader": "^6.2.0",
@ -65,8 +65,8 @@
"lint-staged": "^12.1.2",
"mini-css-extract-plugin": "^2.3.0",
"prettier": "^2.2.1",
"react": "^16.14.0",
"react-dom": "^16.14.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-overlays": "5.1.1",
"react-router": "5.2.0",
"react-router-dom": "5.2.0",

View File

@ -79,8 +79,7 @@ function transpileDynamicImportForCJS(options) {
}
return {
left:
'Promise.resolve().then(function() {return new Promise(function(fullfill) {require([',
left: 'Promise.resolve().then(function() {return new Promise(function(fullfill) {require([',
right: '], function(mod) {fullfill(tslib.__importStar(mod))})})})'
};
@ -96,7 +95,6 @@ function transpileDynamicImportForCJS(options) {
}
function getPlugins(format = 'esm') {
const typeScriptOptions = {
typescript: require('typescript'),
sourceMap: false,
@ -142,11 +140,13 @@ function getPlugins(format = 'esm') {
Copyright 2018<%= moment().format('YYYY') > 2018 ? '-' + moment().format('YYYY') : null %> ${author}
`
}),
onRollupError((error) => {
onRollupError(error => {
console.warn(`[构建异常]${error}`);
// 构建异常时,删除 tsconfig.tsbuildinfo
fs.unlink(path.resolve(__dirname, 'tsconfig.tsbuildinfo'), () => {
console.info('[构建异常]已自动删除tsconfig.tsbuildinfo请重试构建命令。');
console.info(
'[构建异常]已自动删除tsconfig.tsbuildinfo请重试构建命令。'
);
});
})
];

View File

@ -155,6 +155,8 @@ import './renderer/ValidationItem';
import './renderer/SwitchMoreControl';
import './renderer/StatusControl';
import './renderer/FormulaControl';
import './renderer/ExpressionFormulaControl';
import './renderer/textarea-formula/TextareaFormulaControl';
import './renderer/DateShortCutControl';
import './renderer/BadgeControl';
import './renderer/style-control/BoxModel';

View File

@ -3399,7 +3399,7 @@ extendLocale('en-US', {
'6f420734edfaff00a8210a4c762a9207':
'The maximum height is the display height with the most current elements',
'411f9d120093314cd38e6dd5cce398c6':
'The minimum width is the smallest vertical display area of the current element',
'The minimum height is the smallest vertical display area of the current element',
'b31c6aaa78f8e24df665ce80ab5301e2':
'Scroll mode for setting the vertical direction',
'4fc0e68b093db41b45a4ea706fbe56f3': 'Center Display',
@ -3498,5 +3498,46 @@ extendLocale('en-US', {
'Turn on this option if you need to do additional data processing on the data in the returned results',
'7c583ecdf795ce4f1f40d7960ead9344': 'Default prompt text',
'70941a02776496ec446f21f98ebf754e': 'Request successful',
'f50bf418323ee425eca7208c09a4577e': 'Request failed'
'f50bf418323ee425eca7208c09a4577e': 'Request failed',
'f3dc08e3617d1e19cf8135be4412a80b':
'After clicking, ask the user first, and then execute the action after manual confirmation to avoid accidental touch. Values can be taken from data field variables.',
'faa29265819714253843e23437b9193e':
'The prompt content under normal status. If it is not filled, no prompt will pop up. Values can be taken from data field variables.',
'f855f46ce6146aa17a9ed423da16bfa2':
'Prompt content in disabled status. If it is not filled in, a normal prompt will pop up. Values can be taken from data field variables.',
'037becbe8bff2f8838d141cc7b6b2df7':
'Support such relative value usage as:<code>now,+3 days, - 2weeks,+1hour,+2years</code>, etc. (minute | min | hour | day | week | month | year | weekday | second | millisecond)',
'8f7ae284d0039fe05b9f57fd5ae3ede9': 'Please select a static value',
'98229308e2e9484583fde4ae363a979f': 'Expression or relative value',
'6b3c2a07db1bb3c229bbc5df48068792':
'Support such relative value usage as:<code>now,+3 days, - 2weeks,+1 hour,+2 years</code>(minute | hour | day | week | month | year | weekday | second | millisecond)',
'dcc94ea1715bd502c709c5d5092e9c82':
'Support such relative value usage as:<code>3days, 2weeks, 1hour, 2years</code>(minute | hour | day | week | month | year | weekday | second | millisecond)',
'13ce82d026daa5a30e50105bd2a204a6': 'Please enter a relative value',
'31c29c46536a5007522032d2a42db56a': 'Numerical template',
'a6a41d1bfb5896210eb527d183a07958':
'Value rendering template, which supports the use of JSX and data field variables. The default is ${value}%',
'eadd1d64cd6ceb2c50554281cd2d3be0':
'Dynamically re render the configured tabs based on this data',
'0d9d899edb456e8806a99850e9c38212': 'Configured Expression',
'fbb96f7ea104d34fc4b7bd625d892c45': 'Click Write Expression',
'e0c7ac5eb397512fdbe71600baa09dab': 'Please enter a static value',
'48942ef507ea38d8ead03f8bfdffae5a': 'Relative value configured',
'49041f245018a6d799fee3c6f177c782': 'Exit full screen',
'185926bf986c784d03a9a73102da6542': 'Full screen',
'891ec6336d4243714c25eecb2f8f774a': 'Option Text',
'fb7ea2b05ca7328ee16a562d90c2eb96': 'Option field',
'1ca87f0171481e27d94e81b477150b7d': 'Option Template',
'd6ecb32a380c91887a9346653c2427e9':
'Customized option rendering template, supporting the use of JSX and data field variables',
'cb048b2d8426afd464dc01b72c446342': 'Block level (default)',
'3b6e8d54b7b2ae890d5357b7eaaeaaf2': 'No line breaks (default)',
'0611733b53e0098e6fd880bd44b2806f':
'The minimum height is the smallest vertical display area of the current element',
'03bfb834c8a5fef58d885e448a4e13b4': 'Virtual List Threshold',
'50437e080edc71ab624c93d419472919':
'When the number of options exceeds the threshold, the virtual list is opened to optimize performance',
'02b9880e1d2df8a07e90e9878080c739': 'Option Height',
'a3f66655c3d2bcfecc6afba0e4424460':
'Height of each option when virtual list is turned on'
});

View File

@ -3002,7 +3002,7 @@ extendLocale('zh-CN', {
'55becc96b40692cc9cf898b331d16976': '自动适配',
'ede82efb4a69c35743185c6c73ab771e': '最小宽度即当前元素最小的水平展示区域',
'6f420734edfaff00a8210a4c762a9207': '最大高度即当前元素最多的展示高度',
'411f9d120093314cd38e6dd5cce398c6': '最小度即当前元素最小的垂直展示区域',
'411f9d120093314cd38e6dd5cce398c6': '最小度即当前元素最小的垂直展示区域',
'ff9e9329fe186be342ef59ee711b9371': ' y轴滚动模式',
'b31c6aaa78f8e24df665ce80ab5301e2': '用于设置垂直方向的滚动模式',
'4fc0e68b093db41b45a4ea706fbe56f3': '居中显示',
@ -3090,5 +3090,43 @@ extendLocale('zh-CN', {
'如果需要对返回结果中的data做额外的数据处理请开启此选项',
'7c583ecdf795ce4f1f40d7960ead9344': '默认提示文案',
'70941a02776496ec446f21f98ebf754e': '请求成功',
'f50bf418323ee425eca7208c09a4577e': '请求失败'
'f50bf418323ee425eca7208c09a4577e': '请求失败',
'f3dc08e3617d1e19cf8135be4412a80b':
'点击后先询问用户,由手动确认后再执行动作,避免误触。可从数据域变量中取值。',
'faa29265819714253843e23437b9193e':
'正常状态下的提示内容,不填则不弹出提示。可从数据域变量中取值。',
'f855f46ce6146aa17a9ed423da16bfa2':
'禁用状态下的提示内容,不填则弹出正常提示。可从数据域变量中取值。',
'037becbe8bff2f8838d141cc7b6b2df7':
'支持例如: <code>now、+3days、-2weeks、+1hour、+2years</code> 等minute|min|hour|day|week|month|year|weekday|second|millisecond这种相对值用法',
'8f7ae284d0039fe05b9f57fd5ae3ede9': '请选择静态值',
'98229308e2e9484583fde4ae363a979f': '表达式或相对值',
'6b3c2a07db1bb3c229bbc5df48068792':
'支持例如: <code>now、+3days、-2weeks、+1hour、+2years</code> 等minute|hour|day|week|month|year|weekday|second|millisecond这种相对值用法',
'dcc94ea1715bd502c709c5d5092e9c82':
'支持例如: <code>3days、2weeks、1hour、2years</code> 等minute|hour|day|week|month|year|weekday|second|millisecond这种相对值用法',
'13ce82d026daa5a30e50105bd2a204a6': '请输入相对值',
'31c29c46536a5007522032d2a42db56a': '数值模板',
'a6a41d1bfb5896210eb527d183a07958':
'值渲染模板支持JSX、数据域变量使用, 默认 ${value}%',
'eadd1d64cd6ceb2c50554281cd2d3be0': '根据该数据来动态重复渲染所配置的选项卡',
'0d9d899edb456e8806a99850e9c38212': '已配置表达式',
'fbb96f7ea104d34fc4b7bd625d892c45': '点击编写表达式',
'e0c7ac5eb397512fdbe71600baa09dab': '请输入静态值',
'48942ef507ea38d8ead03f8bfdffae5a': '已配置相对值',
'49041f245018a6d799fee3c6f177c782': '退出全屏',
'185926bf986c784d03a9a73102da6542': '全屏',
'891ec6336d4243714c25eecb2f8f774a': '选项文本',
'fb7ea2b05ca7328ee16a562d90c2eb96': '选项字段',
'1ca87f0171481e27d94e81b477150b7d': '选项模板',
'd6ecb32a380c91887a9346653c2427e9':
'自定义选项渲染模板支持JSX、数据域变量使用',
'cb048b2d8426afd464dc01b72c446342': '块级(默认)',
'3b6e8d54b7b2ae890d5357b7eaaeaaf2': '不换行(默认)',
'0611733b53e0098e6fd880bd44b2806f': '最小高度即当前元素最小的垂直展示区域',
'03bfb834c8a5fef58d885e448a4e13b4': '虚拟列表阈值',
'50437e080edc71ab624c93d419472919':
'当选项数量超过阈值后,会开启虚拟列表以优化性能',
'02b9880e1d2df8a07e90e9878080c739': '选项高度',
'a3f66655c3d2bcfecc6afba0e4424460': '开启虚拟列表时每个选项的高度'
});

View File

@ -132,14 +132,15 @@ export class ButtonPlugin extends BasePlugin {
formType: 'extend',
label: tipedLabel(
'二次确认',
'点击后先询问用户,由手动确认后再执行动作,避免误触。可用<code>\\${xxx}</code>取值。'
'点击后先询问用户,由手动确认后再执行动作,避免误触。可从数据域变量中取值。'
),
form: {
body: [
{
name: 'confirmText',
type: 'input-text',
label: '确认内容'
type: 'ae-textareaFormulaControl',
label: '确认内容',
mode: 'normal',
name: 'confirmText'
}
]
}
@ -154,19 +155,21 @@ export class ButtonPlugin extends BasePlugin {
form: {
body: [
{
type: 'input-text',
type: 'ae-textareaFormulaControl',
name: 'tooltip',
mode: 'normal',
label: tipedLabel(
'正常提示',
'正常状态下的提示内容,不填则不弹出提示。可用<code>\\${xxx}</code>取值。'
'正常状态下的提示内容,不填则不弹出提示。可从数据域变量中取值。'
)
},
{
type: 'input-text',
type: 'ae-textareaFormulaControl',
name: 'disabledTip',
mode: 'normal',
label: tipedLabel(
'禁用提示',
'禁用状态下的提示内容,不填则弹出正常提示。可用<code>\\${xxx}</code>取值。'
'禁用状态下的提示内容,不填则弹出正常提示。可从数据域变量中取值。'
),
clearValueOnHidden: true,
visibleOn: 'data.tooltipTrigger !== "focus"'

View File

@ -1326,16 +1326,28 @@ export class CRUDPlugin extends BasePlugin {
</div>
);
},
schema: getArgsWrapper({
type: 'input-formula',
variables: '${variables}',
evalMode: false,
variableMode: 'tabs',
label: '查询条件',
size: 'md',
name: 'query',
mode: 'horizontal'
})
schema: getArgsWrapper(
/*
{
type: 'input-formula',
variables: '${variables}',
evalMode: false,
variableMode: 'tabs',
label: '查询条件',
size: 'md',
name: 'query',
mode: 'horizontal'
}
*/
{
name: 'query',
label: '查询条件',
type: 'ae-formulaControl',
variables: '${variables}',
size: 'md',
mode: 'horizontal'
}
)
},
// {
// actionType: 'resetQuery',

View File

@ -17,7 +17,11 @@ export class ContainerPlugin extends BasePlugin {
pluginIcon = 'container-plugin';
scaffold = {
type: 'container',
body: []
body: [],
style: {
position: 'static',
display: 'block'
}
};
previewSchema = {
...this.scaffold

View File

@ -61,15 +61,17 @@ export class DropDownButtonPlugin extends BasePlugin {
body: [
{
children: (
<Button
level="info"
size="sm"
className="m-b-sm"
block
onClick={this.editDetail.bind(this, context.id)}
>
</Button>
<div className="mb-3">
<Button
level="info"
size="sm"
className="m-b-sm"
block
onClick={this.editDetail.bind(this, context.id)}
>
</Button>
</div>
)
},
{

View File

@ -136,6 +136,7 @@ export class CheckboxControlPlugin extends BasePlugin {
...context?.schema,
type: 'switch'
},
needDeleteProps: ['option'],
label: '默认勾选',
rendererWrapper: true, // 浅色线框包裹一下,增加边界感
valueType: 'boolean',

View File

@ -141,11 +141,13 @@ export class CodeEditorControlPlugin extends BasePlugin {
searchable: true,
options: availableLanguages.concat()
},
{
type: 'textarea',
name: 'value',
label: '默认值'
},
getSchemaTpl('valueFormula', {
rendererSchema: {
type: 'textarea'
},
mode: 'vertical' // 改成上下展示模式
}),
getSchemaTpl('switch', {
label: '可全屏',
name: 'allowFullscreen',

View File

@ -147,19 +147,24 @@ export class DiffEditorControlPlugin extends BasePlugin {
searchable: true,
options: availableLanguages.concat()
},
{
type: 'textarea',
name: 'diffValue',
getSchemaTpl('valueFormula', {
rendererSchema: {
type: 'textarea',
value: context?.schema.diffValue
},
label: '左侧默认值',
pipeOut: valuePipeOut,
placeholder: '支持使用 ${xxx} 来获取变量'
},
{
type: 'textarea',
name: 'value',
name: 'diffValue',
mode: 'vertical' // 改成上下展示模式
}),
getSchemaTpl('valueFormula', {
rendererSchema: {
type: 'textarea',
value: context?.schema.value
},
label: '右侧默认值',
placeholder: '支持使用 ${xxx} 来获取变量'
},
mode: 'vertical' // 改成上下展示模式
}),
getSchemaTpl('labelRemark'),
getSchemaTpl('remark'),
getSchemaTpl('description'),

View File

@ -4,6 +4,7 @@ import {BasePlugin, BaseEventContext, tipedLabel} from 'amis-editor-core';
import {ValidatorTag} from '../../validator';
import {getEventControlConfig} from '../../renderer/event-control/helper';
import {FormulaDateType} from '../../renderer/FormulaControl';
import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
const formatX = [
@ -135,8 +136,8 @@ const DateType: {
}
};
const tipedLabelText =
'支持 <code>now、+1day、-2weeks、+1hours、+2years</code>这种相对值用法,同时支持变量如<code>\\${start_date}</code>';
const dateTooltip =
'支持例如: <code>now、+3days、-2weeks、+1hour、+2years</code> 等minute|min|hour|day|week|month|year|weekday|second|millisecond这种相对值用法';
export class DateControlPlugin extends BasePlugin {
// 关联渲染器名字
@ -333,35 +334,42 @@ export class DateControlPlugin extends BasePlugin {
pipeIn: defaultValue(true)
}),
getSchemaTpl('valueFormula', {
rendererSchema: context?.schema,
label: tipedLabel(
'默认值',
'支持 <code>now、+1day、-2weeks、+1hours、+2years</code>等这种相对值用法'
)
rendererSchema: {
...context?.schema
},
placeholder: '请选择静态值',
header: '表达式或相对值',
DateTimeType: FormulaDateType.IsDate,
label: tipedLabel('默认值', dateTooltip)
}),
getSchemaTpl('valueFormula', {
name: 'minDate',
header: '表达式或相对值',
DateTimeType: FormulaDateType.IsDate,
rendererSchema: {
...context?.schema,
value: context?.schema.minDate
},
placeholder: '请选择静态值',
needDeleteProps: ['minDate'], // 避免自我限制
label: tipedLabel('最小值', tipedLabelText)
label: tipedLabel('最小值', dateTooltip)
}),
getSchemaTpl('valueFormula', {
name: 'maxDate',
header: '表达式或相对值',
DateTimeType: FormulaDateType.IsDate,
rendererSchema: {
...context?.schema,
value: context?.schema.maxDate
},
needDeleteProps: ['maxDate'], // 避免自我限制
label: tipedLabel('最大值', tipedLabelText)
label: tipedLabel('最大值', dateTooltip)
}),
getSchemaTpl('placeholder', {
pipeIn: defaultValue('请选择日期')
}),
// getSchemaTpl('remark'),
// getSchemaTpl('labelRemark'),
getSchemaTpl('remark'),
getSchemaTpl('labelRemark'),
getSchemaTpl('description'),
getSchemaTpl('autoFillApi')
]

View File

@ -4,6 +4,7 @@ import {BasePlugin, BaseEventContext} from 'amis-editor-core';
import {ValidatorTag} from '../../validator';
import {getEventControlConfig} from '../../renderer/event-control/helper';
import {FormulaDateType} from '../../renderer/FormulaControl';
import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
import {getRendererByName} from 'amis-core';
@ -68,13 +69,15 @@ const DateType: {
}
};
const dateTooltip =
'支持例如: <code>now、+3days、-2weeks、+1hour、+2years</code> 等minute|hour|day|week|month|year|weekday|second|millisecond这种相对值用法';
const rangTooltip =
'支持例如: <code>3days、2weeks、1hour、2years</code> 等minute|hour|day|week|month|year|weekday|second|millisecond这种相对值用法';
const sizeImmutableComponents = Object.values(DateType)
.map(item => (item?.sizeMutable === false ? item.type : null))
.filter(a => a);
const tipedLabelText =
'支持 <code>now、+1day、-2weeks、+1hours、+2years</code>这种相对值用法,同时支持变量如<code>\\${start_date}</code>';
export class DateRangeControlPlugin extends BasePlugin {
// 关联渲染器名字
rendererName = 'input-date-range';
@ -264,63 +267,69 @@ export class DateRangeControlPlugin extends BasePlugin {
}),
getSchemaTpl('valueFormula', {
/* : amis
rendererSchema: {
...context?.schema,
size: 'full', // 备注目前样式还有问题需要在amis端进行优化
mode: "inline"
},
mode: 'vertical',
*/
rendererSchema: {
type: 'input-date'
...context?.schema,
size: 'full',
mode: 'inline'
},
label: tipedLabel(
'默认值',
'支持 <code>now、+1day、-2weeks、+1hours、+2years</code>等这种相对值用法'
)
mode: 'vertical',
header: '表达式或相对值',
DateTimeType: FormulaDateType.IsRange,
label: tipedLabel('默认值', dateTooltip)
}),
getSchemaTpl('valueFormula', {
name: 'minDate',
header: '表达式或相对值',
DateTimeType: FormulaDateType.IsDate,
rendererSchema: {
...context?.schema,
value: context?.schema.minDate,
type: 'input-date'
},
placeholder: '请选择静态值',
needDeleteProps: ['minDate'], // 避免自我限制
label: tipedLabel('最小值', tipedLabelText)
label: tipedLabel('最小值', dateTooltip)
}),
getSchemaTpl('valueFormula', {
name: 'maxDate',
header: '表达式或相对值',
DateTimeType: FormulaDateType.IsDate,
rendererSchema: {
...context?.schema,
value: context?.schema.maxDate,
type: 'input-date'
},
placeholder: '请选择静态值',
needDeleteProps: ['maxDate'], // 避免自我限制
label: tipedLabel('最大值', tipedLabelText)
label: tipedLabel('最大值', dateTooltip)
}),
getSchemaTpl('valueFormula', {
name: 'minDuration',
header: '表达式',
DateTimeType: FormulaDateType.NotDate,
rendererSchema: {
...context?.schema,
value: context?.schema.minDuration,
type: 'input-text'
},
placeholder: '请输入相对值',
needDeleteProps: ['minDuration'], // 避免自我限制
label: tipedLabel('最小跨度', '例如 2days')
label: tipedLabel('最小跨度', rangTooltip)
}),
getSchemaTpl('valueFormula', {
name: 'maxDuration',
header: '表达式',
DateTimeType: FormulaDateType.NotDate,
rendererSchema: {
...context?.schema,
value: context?.schema.maxDuration,
type: 'input-text'
},
placeholder: '请输入相对值',
needDeleteProps: ['maxDuration'], // 避免自我限制
label: tipedLabel('最大跨度', '例如 1year')
label: tipedLabel('最大跨度', rangTooltip)
}),
getSchemaTpl('dateShortCutControl', {
mode: 'normal',
@ -347,8 +356,8 @@ export class DateRangeControlPlugin extends BasePlugin {
yearslater: 'n年以内'
}
}),
// getSchemaTpl('remark'),
// getSchemaTpl('labelRemark'),
getSchemaTpl('remark'),
getSchemaTpl('labelRemark'),
{
type: 'input-text',
name: 'startPlaceholder',

View File

@ -164,10 +164,7 @@ export class RangeControlPlugin extends BasePlugin {
},
pipeIn: defaultValue(0),
needDeleteProps: ['min'], // 避免自我限制
label: tipedLabel(
'最小值',
'请输入数字或使用 <code>\\${xxx}</code> 来获取变量,否则该配置不生效'
),
label: '最小值',
valueType: 'number'
}),
@ -179,10 +176,7 @@ export class RangeControlPlugin extends BasePlugin {
},
pipeIn: defaultValue(100),
needDeleteProps: ['max'], // 避免自我限制
label: tipedLabel(
'最大值',
'请输入数字或使用 <code>\\${xxx}</code> 来获取变量,否则该配置不生效'
),
label: '最大值',
valueType: 'number'
}),

View File

@ -124,10 +124,7 @@ export class RateControlPlugin extends BasePlugin {
precision: 0
},
needDeleteProps: ['count'], // 避免自我限制
label: tipedLabel(
'最大值',
'请输入数字或使用 <code>\\${xxx}</code> 来获取变量,否则该配置不生效'
),
label: '最大值',
valueType: 'number'
}),

View File

@ -179,16 +179,28 @@ export class TreeControlPlugin extends BasePlugin {
</div>
);
},
schema: getArgsWrapper({
type: 'input-formula',
variables: '${variables}',
evalMode: false,
variableMode: 'tabs',
label: '展开层级',
size: 'lg',
name: 'openLevel',
mode: 'horizontal'
})
schema: getArgsWrapper(
/*
{
type: 'input-formula',
variables: '${variables}',
evalMode: false,
variableMode: 'tabs',
label: '展开层级',
size: 'lg',
name: 'openLevel',
mode: 'horizontal'
},
*/
{
name: 'openLevel',
label: '展开层级',
type: 'ae-formulaControl',
variables: '${variables}',
size: 'lg',
mode: 'horizontal'
}
)
},
{
actionType: 'collapse',

View File

@ -251,7 +251,11 @@ export class SelectControlPlugin extends BasePlugin {
title: '选项',
body: [
getSchemaTpl('optionControlV2'),
getSchemaTpl('menuTpl'),
// 模板
getSchemaTpl('optionsMenuTpl', {
manager: this.manager,
onChange: (value: any) => {}
}),
getSchemaTpl('creatable', {
formType: 'extend',
hiddenOnDefault: true,
@ -308,7 +312,9 @@ export class SelectControlPlugin extends BasePlugin {
'开启后当选项值未匹配到当前options中的选项时选项文本飘红'
),
name: 'showInvalidMatch'
})
}),
getSchemaTpl('virtualThreshold'),
getSchemaTpl('virtualItemHeight')
]
},
getSchemaTpl('status', {isFormItem: true}),

View File

@ -182,6 +182,7 @@ export class SwitchControlPlugin extends BasePlugin {
*/
getSchemaTpl('valueFormula', {
rendererSchema: context?.schema,
needDeleteProps: ['option'],
rendererWrapper: true, // 浅色线框包裹一下,增加边界感
valueType: 'boolean',
pipeIn: (value: any, data: any) => {

View File

@ -300,19 +300,19 @@ export class TabsTransferPlugin extends BasePlugin {
getSchemaTpl('sortable'),
getSchemaTpl('formulaControl', {
{
label: '左侧选项标题',
name: 'selectTitle',
type: 'input-text',
inputClassName: 'is-inline '
}),
},
getSchemaTpl('formulaControl', {
{
label: '右侧结果标题',
name: 'resultTitle',
type: 'input-text',
inputClassName: 'is-inline '
})
}
]
},
{
@ -336,6 +336,13 @@ export class TabsTransferPlugin extends BasePlugin {
})
]
},
{
title: '高级',
body: [
getSchemaTpl('virtualThreshold'),
getSchemaTpl('virtualItemHeight')
]
},
getSchemaTpl('status', {isFormItem: true})
])
},

View File

@ -122,10 +122,16 @@ export class TextareaControlPlugin extends BasePlugin {
required: true
}),
getSchemaTpl('label'),
getSchemaTpl('valueFormula', {
rendererSchema: context?.schema,
mode: 'vertical' // 改成上下展示模式
}),
// getSchemaTpl('valueFormula', {
// rendererSchema: context?.schema,
// mode: 'vertical' // 改成上下展示模式
// }),
{
type: 'ae-textareaFormulaControl',
label: '默认值',
name: 'value',
mode: 'normal'
},
getSchemaTpl('switch', {
name: 'trimContents',
pipeIn: defaultValue(true),

View File

@ -266,20 +266,18 @@ export class TransferPlugin extends BasePlugin {
name: 'searchable'
}),
getSchemaTpl('menuTpl', {
label: tipedLabel(
'模板',
'左侧选项渲染模板支持JSX变量使用\\${xx}'
),
getSchemaTpl('optionsMenuTpl', {
manager: this.manager,
onChange: (value: any) => {},
visibleOn: 'data.selectMode !== "table"'
}),
getSchemaTpl('formulaControl', {
{
label: '标题',
name: 'selectTitle',
type: 'input-text',
inputClassName: 'is-inline '
})
}
]
},
{
@ -317,22 +315,26 @@ export class TransferPlugin extends BasePlugin {
'data.selectMode === "list" && !data.resultListModeFollowSelect'
}),
getSchemaTpl('menuTpl', {
getSchemaTpl('optionsMenuTpl', {
name: 'valueTpl',
label: tipedLabel(
'模板',
'结果选项渲染模板支持JSX变量使用\\${xx}'
),
manager: this.manager,
onChange: (value: any) => {},
visibleOn:
'!(data.selectMode === "table" && data.resultListModeFollowSelect)'
}),
getSchemaTpl('formulaControl', {
{
label: '标题',
name: 'resultTitle',
type: 'input-text',
inputClassName: 'is-inline '
})
}
]
},
{
title: '高级',
body: [
getSchemaTpl('virtualThreshold'),
getSchemaTpl('virtualItemHeight')
]
},
getSchemaTpl('status', {isFormItem: true}),

View File

@ -13,7 +13,7 @@ import {
} from 'amis-editor-core';
import {defaultValue, getSchemaTpl} from 'amis-editor-core';
import {EditorNodeType} from 'amis-editor-core';
import {Schema} from 'amis/lib/types';
import {Schema} from 'amis';
import {VRenderer} from 'amis-editor-core';
import {RegionWrapper as Region} from 'amis-editor-core';
import {Icon} from 'amis-editor-core';

View File

@ -13,7 +13,7 @@ import {
} from 'amis-editor-core';
import {defaultValue, getSchemaTpl} from 'amis-editor-core';
import {EditorNodeType} from 'amis-editor-core';
import {Schema} from 'amis/lib/types';
import {Schema} from 'amis';
import {VRenderer} from 'amis-editor-core';
import {RegionWrapper as Region} from 'amis-editor-core';
import {JSONChangeInArray, JSONPipeIn, repeatArray} from 'amis-editor-core';

View File

@ -41,9 +41,10 @@ export class IFramePlugin extends BasePlugin {
title: '基本',
body: [
{
type: 'ae-textareaFormulaControl',
name: 'src',
label: '页面地址',
type: 'input-text'
mode: 'normal',
label: '页面地址'
}
]
},

View File

@ -8,24 +8,22 @@ import type {
EditorNodeType,
RegionConfig,
RendererJSONSchemaResolveEventContext,
BasicToolbarItem
BasicToolbarItem,
PluginInterface
} from 'amis-editor-core';
// 默认的列容器Schema
const defaultFlexColumnSchema = (title: string) => {
export const defaultFlexColumnSchema = (title?: string) => {
return {
type: 'wrapper',
body: [],
size: 'xs',
style: {
position: 'static',
display: 'flex',
display: 'block',
flex: '1 1 auto',
flexGrow: 1,
flexBasis: 'auto',
flexDirection: 'column',
justifyContent: 'flex-start',
alignItems: 'stretch'
flexBasis: 'auto'
},
isFixedHeight: false,
isFixedWidth: false
@ -40,6 +38,10 @@ const defaultFlexContainerSchema = {
defaultFlexColumnSchema('第二列'),
defaultFlexColumnSchema('第三列')
],
style: {
position: 'static',
},
direction: "row",
justify: 'flex-start',
alignItems: 'stretch'
};
@ -222,6 +224,13 @@ export class FlexPluginBase extends BasePlugin {
const isFlexColumnItem = this.manager?.isFlexColumnItem(id);
const newColumnSchema = defaultFlexColumnSchema('新的一列');
const toolbarsTooltips:any = {};
toolbars.forEach(toolbar => {
if (toolbar.tooltip) {
toolbarsTooltips[toolbar.tooltip] = 1;
}
});
if (
parent &&
(info.renderer?.name === 'flex' || info.renderer?.name === 'container') &&
@ -229,74 +238,80 @@ export class FlexPluginBase extends BasePlugin {
!draggableContainer
) {
// 非特殊布局元素fixed、absolute支持前后插入追加布局元素功能icon
toolbars.push(
{
iconSvg: 'add-btn',
tooltip: '上方插入布局容器',
level: 'special',
placement: 'right',
className: 'ae-InsertBefore is-vertical',
onClick: () =>
this.manager.appendSiblingSchema(
defaultFlexContainerSchema,
true,
true
)
},
{
iconSvg: 'add-btn',
tooltip: '下方插入布局容器',
level: 'special',
placement: 'right',
className: 'ae-InsertAfter is-vertical',
onClick: () =>
this.manager.appendSiblingSchema(
defaultFlexContainerSchema,
false,
true
)
}
);
if (!toolbarsTooltips['上方插入布局容器']) {
toolbars.push(
{
iconSvg: 'add-btn',
tooltip: '上方插入布局容器',
level: 'special',
placement: 'right',
className: 'ae-InsertBefore is-vertical',
onClick: () =>
this.manager.appendSiblingSchema(
defaultFlexContainerSchema,
true,
true
)
},
{
iconSvg: 'add-btn',
tooltip: '下方插入布局容器',
level: 'special',
placement: 'right',
className: 'ae-InsertAfter is-vertical',
onClick: () =>
this.manager.appendSiblingSchema(
defaultFlexContainerSchema,
false,
true
)
}
);
}
// 布局容器 右上角插入子元素
if (info.renderer?.name === 'flex') {
toolbars.push({
iconSvg: 'add-btn',
tooltip: '新增列级元素',
level: 'special',
placement: 'bottom',
className: 'ae-AppendChild',
onClick: () => this.manager.addElem(newColumnSchema)
});
if (!toolbarsTooltips['新增列级元素']) {
toolbars.push({
iconSvg: 'add-btn',
tooltip: '新增列级元素',
level: 'special',
placement: 'bottom',
className: 'ae-AppendChild',
onClick: () => this.manager.addElem(newColumnSchema)
});
}
}
}
if (isFlexItem && !draggableContainer) {
// 布局容器的列级元素 增加左右插入icon
toolbars.push(
{
iconSvg: 'add-btn',
tooltip: `${isFlexColumnItem ? '上方' : '左侧'}插入列级容器`,
level: 'special',
placement: 'right',
className: isFlexColumnItem
? 'ae-InsertBefore is-vertical'
: 'ae-InsertBefore',
onClick: () =>
this.manager.appendSiblingSchema(newColumnSchema, true, true)
},
{
iconSvg: 'add-btn',
tooltip: `${isFlexColumnItem ? '下方' : '右侧'}插入列级容器`,
level: 'special',
placement: isFlexColumnItem ? 'right' : 'left',
className: isFlexColumnItem
? 'ae-InsertAfter is-vertical'
: 'ae-InsertAfter',
onClick: () =>
this.manager.appendSiblingSchema(newColumnSchema, false, true)
}
);
if (!toolbarsTooltips[`${isFlexColumnItem ? '上方' : '左侧'}插入列级容器`]) {
// 布局容器的列级元素 增加左右插入icon
toolbars.push(
{
iconSvg: 'add-btn',
tooltip: `${isFlexColumnItem ? '上方' : '左侧'}插入列级容器`,
level: 'special',
placement: 'right',
className: isFlexColumnItem
? 'ae-InsertBefore is-vertical'
: 'ae-InsertBefore',
onClick: () =>
this.manager.appendSiblingSchema(newColumnSchema, true, true)
},
{
iconSvg: 'add-btn',
tooltip: `${isFlexColumnItem ? '下方' : '右侧'}插入列级容器`,
level: 'special',
placement: isFlexColumnItem ? 'right' : 'left',
className: isFlexColumnItem
? 'ae-InsertAfter is-vertical'
: 'ae-InsertAfter',
onClick: () =>
this.manager.appendSiblingSchema(newColumnSchema, false, true)
}
);
}
}
}

View File

@ -18,10 +18,7 @@ export default class Layout1_2_v4 extends FlexPluginBase {
body: [],
style: {
flex: '0 0 auto',
display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-start',
alignItems: 'stretch'
display: 'block'
}
},
{
@ -34,10 +31,7 @@ export default class Layout1_2_v4 extends FlexPluginBase {
style: {
flex: '0 0 auto',
flexBasis: '250px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-start',
alignItems: 'stretch'
display: 'block'
}
},
{

View File

@ -9,19 +9,16 @@ export default class Layout_fixed extends FlexPluginBase {
tags = ['常见布局'];
order = 503;
scaffold: any = {
type: 'wrapper',
type: 'container',
size: 'xs',
body: [],
style: {
position: 'fixed',
inset: 'auto 50px 50px auto',
zIndex: 10,
display: 'flex',
minWidth: '80px',
minHeight: '80px',
flexDirection: 'column',
justifyContent: 'flex-start',
alignItems: 'stretch'
display: 'block'
},
originPosition: 'right-bottom'
};

View File

@ -1,5 +1,5 @@
import {registerEditorPlugin} from 'amis-editor-core';
import {FlexPluginBase} from './FlexPluginBase';
import {FlexPluginBase, defaultFlexColumnSchema} from './FlexPluginBase';
export default class Layout_fixed_bottom extends FlexPluginBase {
name = '吸底容器';
@ -12,48 +12,10 @@ export default class Layout_fixed_bottom extends FlexPluginBase {
type: 'flex',
className: 'p-1',
items: [
{
type: 'wrapper',
size: 'xs',
body: [],
style: {
flex: '1 1 auto',
flexBasis: 'auto',
flexGrow: 1,
display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-start',
alignItems: 'stretch'
}
},
{
type: 'wrapper',
size: 'xs',
body: [],
style: {
flex: '1 1 auto',
flexBasis: 'auto',
flexGrow: 1,
display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-start',
alignItems: 'stretch'
}
},
{
type: 'wrapper',
size: 'xs',
body: [],
style: {
flex: '1 1 auto',
flexBasis: 'auto',
flexGrow: 1,
display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-start',
alignItems: 'stretch'
}
}
defaultFlexColumnSchema(),
defaultFlexColumnSchema(),
defaultFlexColumnSchema(),
],
style: {
position: 'fixed',

View File

@ -1,5 +1,5 @@
import {registerEditorPlugin} from 'amis-editor-core';
import {FlexPluginBase} from './FlexPluginBase';
import {FlexPluginBase, defaultFlexColumnSchema} from './FlexPluginBase';
export default class Layout_fixed_top extends FlexPluginBase {
name = '吸顶容器';
@ -12,39 +12,9 @@ export default class Layout_fixed_top extends FlexPluginBase {
type: 'flex',
className: 'p-1',
items: [
{
type: 'wrapper',
size: 'xs',
body: [],
style: {
flex: '1 1 auto',
flexBasis: 'auto',
flexGrow: 1,
display: 'block'
}
},
{
type: 'wrapper',
size: 'xs',
body: [],
style: {
flex: '1 1 auto',
flexBasis: 'auto',
flexGrow: 1,
display: 'block'
}
},
{
type: 'wrapper',
size: 'xs',
body: [],
style: {
flex: '1 1 auto',
display: 'block',
flexBasis: 'auto',
flexGrow: 1
}
}
defaultFlexColumnSchema(),
defaultFlexColumnSchema(),
defaultFlexColumnSchema(),
],
style: {
position: 'fixed',

View File

@ -19,10 +19,7 @@ export default class Layout_scroll_x extends FlexPluginBase {
style: {
flex: '0 0 auto',
flexBasis: '200px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-start',
alignItems: 'stretch',
display: 'block',
position: 'static',
minWidth: 'auto',
minHeight: 'auto'
@ -35,10 +32,7 @@ export default class Layout_scroll_x extends FlexPluginBase {
style: {
flex: '0 0 auto',
flexBasis: '200px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-start',
alignItems: 'stretch',
display: 'block',
position: 'static',
minWidth: 'auto',
minHeight: 'auto'
@ -50,10 +44,7 @@ export default class Layout_scroll_x extends FlexPluginBase {
body: [],
style: {
flex: '0 0 auto',
display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-start',
alignItems: 'stretch',
display: 'block',
position: 'static',
minWidth: 'auto',
minHeight: 'auto',
@ -67,10 +58,7 @@ export default class Layout_scroll_x extends FlexPluginBase {
style: {
flex: '0 0 auto',
flexBasis: '200px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-start',
alignItems: 'stretch',
display: 'block',
position: 'static',
minWidth: 'auto',
minHeight: 'auto'
@ -83,10 +71,7 @@ export default class Layout_scroll_x extends FlexPluginBase {
style: {
flex: '0 0 auto',
flexBasis: '200px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-start',
alignItems: 'stretch',
display: 'block',
position: 'static',
minWidth: 'auto',
minHeight: 'auto'
@ -99,10 +84,7 @@ export default class Layout_scroll_x extends FlexPluginBase {
style: {
flex: '0 0 auto',
flexBasis: '200px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-start',
alignItems: 'stretch',
display: 'block',
position: 'static',
minWidth: 'auto',
minHeight: 'auto'
@ -115,10 +97,7 @@ export default class Layout_scroll_x extends FlexPluginBase {
style: {
flex: '0 0 auto',
flexBasis: '200px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-start',
alignItems: 'stretch',
display: 'block',
position: 'static',
minWidth: 'auto',
minHeight: 'auto'

View File

@ -19,10 +19,7 @@ export default class Layout_scroll_y extends FlexPluginBase {
style: {
flex: '0 0 auto',
flexBasis: '60px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-start',
alignItems: 'stretch',
display: 'block',
minWidth: 'auto',
minHeight: 'auto'
}
@ -34,10 +31,7 @@ export default class Layout_scroll_y extends FlexPluginBase {
style: {
flex: '0 0 auto',
flexBasis: '60px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-start',
alignItems: 'stretch',
display: 'block',
position: 'static',
minWidth: 'auto',
minHeight: 'auto'
@ -50,10 +44,7 @@ export default class Layout_scroll_y extends FlexPluginBase {
style: {
flex: '0 0 auto',
flexBasis: '60px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-start',
alignItems: 'stretch',
display: 'block',
position: 'static',
minWidth: 'auto',
minHeight: 'auto'
@ -66,10 +57,7 @@ export default class Layout_scroll_y extends FlexPluginBase {
style: {
flex: '0 0 auto',
flexBasis: '60px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-start',
alignItems: 'stretch',
display: 'block',
position: 'static',
minWidth: 'auto',
minHeight: 'auto'
@ -82,10 +70,7 @@ export default class Layout_scroll_y extends FlexPluginBase {
style: {
flex: '0 0 auto',
flexBasis: '60px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-start',
alignItems: 'stretch',
display: 'block',
position: 'static',
minWidth: 'auto',
minHeight: 'auto'
@ -98,10 +83,7 @@ export default class Layout_scroll_y extends FlexPluginBase {
style: {
flex: '0 0 auto',
flexBasis: '60px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-start',
alignItems: 'stretch',
display: 'block',
position: 'static',
minWidth: 'auto',
minHeight: 'auto'

View File

@ -22,7 +22,8 @@ export class ProgressPlugin extends BasePlugin {
scaffold = {
type: 'progress',
value: 66,
strokeWidth: 6
strokeWidth: 6,
valueTpl: '${value}%'
};
previewSchema = {
...this.scaffold
@ -46,7 +47,7 @@ export class ProgressPlugin extends BasePlugin {
name: 'mode',
type: 'select',
option: '继承',
value: 'line',
pipeIn: defaultValue('line'),
tiled: true,
options: [
{
@ -79,15 +80,33 @@ export class ProgressPlugin extends BasePlugin {
}
}
},
{
type: 'input-number',
name: 'value',
label: '默认值',
min: 0,
max: 100
},
getSchemaTpl('valueFormula', {
rendererSchema: {
...context?.schema,
type: 'input-number'
},
needDeleteProps: ['placeholder'],
valueType: 'number' // 期望数值类型,不过 amis中会尝试字符串 trans 数值类型
}),
getSchemaTpl('menuTpl', {
name: 'valueTpl'
label: tipedLabel(
'数值模板',
'值渲染模板支持JSX、数据域变量使用, 默认 ${value}%'
),
name: 'valueTpl',
variables: [
{
label: '值字段',
children: [
{
label: '进度值',
value: 'value',
tag: 'number'
}
]
}
],
requiredDataPropsVariables: true
}),
getSchemaTpl('switch', {

View File

@ -295,6 +295,7 @@ export class TablePlugin extends BasePlugin {
description: '设置表格的选中项',
innerArgs: ['selected'],
schema: getArgsWrapper([
/*
{
type: 'input-formula',
variables: '${variables}',
@ -305,6 +306,15 @@ export class TablePlugin extends BasePlugin {
name: 'selected',
mode: 'horizontal'
}
*/
{
name: 'selected',
label: '选中项',
type: 'ae-formulaControl',
variables: '${variables}',
size: 'lg',
mode: 'horizontal'
}
])
},
{

View File

@ -293,6 +293,7 @@ export class Table2Plugin extends BasePlugin {
actionLabel: '设置选中项',
description: '设置表格的选中项',
schema: getArgsWrapper([
/*
{
type: 'input-formula',
variables: '${variables}',
@ -303,6 +304,15 @@ export class Table2Plugin extends BasePlugin {
name: 'selected',
mode: 'horizontal'
}
*/
{
name: 'selected',
label: '选中项',
type: 'ae-formulaControl',
variables: '${variables}',
size: 'lg',
mode: 'horizontal'
}
])
},
{

View File

@ -101,16 +101,28 @@ export class TabsPlugin extends BasePlugin {
</div>
);
},
schema: getArgsWrapper({
type: 'input-formula',
variables: '${variables}',
evalMode: false,
variableMode: 'tabs',
label: '激活项',
size: 'lg',
name: 'activeKey',
mode: 'horizontal'
})
schema: getArgsWrapper(
/*
{
type: 'input-formula',
variables: '${variables}',
evalMode: false,
variableMode: 'tabs',
label: '激活项',
size: 'lg',
name: 'activeKey',
mode: 'horizontal'
}
*/
{
name: 'activeKey',
label: '激活项',
type: 'ae-formulaControl',
variables: '${variables}',
size: 'lg',
mode: 'horizontal'
}
)
}
];
@ -173,10 +185,11 @@ export class TabsPlugin extends BasePlugin {
title: '高级',
body: [
{
type: 'ae-formulaControl',
type: 'ae-expressionFormulaControl',
evalMode: true,
label: tipedLabel(
'关联数据',
'可用<code>\\${xxx}</code>取值,根据该数据来动态重复渲染所配置的选项卡'
'根据该数据来动态重复渲染所配置的选项卡'
),
name: 'source'
},

View File

@ -5,13 +5,9 @@ import {tipedLabel} from 'amis-editor-core';
import {ValidatorTag} from '../validator';
setSchemaTpl('tpl:content', {
label: tipedLabel(
'文字内容',
'支持使用 <code>\\${xxx}</code> 来获取变量,或者用 lodash.template 语法来写模板逻辑。<a target="_blank" href="/amis/zh-CN/docs/concepts/template">详情</a>'
),
type: 'textarea',
minRows: 5,
language: 'html',
label: '文字内容',
type: 'ae-textareaFormulaControl',
mode: 'normal',
visibleOn: 'data.wrapperComponent !== undefined',
pipeIn: (value: any, data: any) => value || (data && data.html),
name: 'tpl'

View File

@ -254,6 +254,7 @@ export class WizardPlugin extends BasePlugin {
);
},
schema: getArgsWrapper([
/*
{
type: 'input-formula',
variables: '${variables}',
@ -265,6 +266,16 @@ export class WizardPlugin extends BasePlugin {
name: 'step',
mode: 'horizontal'
}
*/
{
name: 'step',
label: '目标步骤',
type: 'ae-formulaControl',
variables: '${variables}',
size: 'lg',
mode: 'horizontal',
required: true
}
])
},
{

View File

@ -17,7 +17,7 @@ import {
} from 'amis-editor-core';
import type {SchemaObject, SchemaCollection, SchemaApi} from 'amis/lib/Schema';
import type {Api} from 'amis/lib/types';
import type {Api} from 'amis';
import type {FormControlProps} from 'amis-core';
import type {ActionSchema} from 'amis/lib/renderers/Action';

View File

@ -12,7 +12,7 @@ import {autobind, isObject, anyChanged, createObject} from 'amis-editor-core';
import {tipedLabel} from 'amis-editor-core';
import type {SchemaObject, SchemaCollection, SchemaApi} from 'amis/lib/Schema';
import type {Api} from 'amis/lib/types';
import type {Api} from 'amis';
import type {FormControlProps} from 'amis-core';
import type {ActionSchema} from 'amis/lib/renderers/Action';

View File

@ -0,0 +1,197 @@
/**
* @file
*/
import React from 'react';
import {autobind, FormControlProps} from 'amis-core';
import cx from 'classnames';
import {FormItem, Button, Icon, PickerContainer} from 'amis';
import {FormulaEditor} from 'amis-ui/lib/components/formula/Editor';
import type {VariableItem} from 'amis-ui/lib/components/formula/Editor';
import {getVariables} from './textarea-formula/utils';
interface ExpressionFormulaControlProps extends FormControlProps {
/**
*
*/
variables?: Array<VariableItem> | Function;
/**
* variables 使
* props.variables amis数据域中取变量集合 false
*/
requiredDataPropsVariables?: boolean;
/**
* 'tabs' 'tree'
*/
variableMode?: 'tabs' | 'tree';
/**
* 使 ${} false
*/
evalMode: boolean;
}
interface ExpressionFormulaControlState {
variables: Array<VariableItem>;
formulaPickerValue: string;
}
export default class ExpressionFormulaControl extends React.Component<
ExpressionFormulaControlProps,
ExpressionFormulaControlState
> {
static defaultProps: Partial<ExpressionFormulaControlProps> = {
variableMode: 'tabs',
requiredDataPropsVariables: false,
evalMode: false
};
isUnmount: boolean;
constructor(props: ExpressionFormulaControlProps) {
super(props);
this.state = {
variables: [],
formulaPickerValue: ''
};
}
async componentDidMount() {
this.initFormulaPickerValue(this.props.value);
const variablesArr = await getVariables(this);
this.setState({
variables: variablesArr
});
}
async componentDidUpdate(prevProps: ExpressionFormulaControlProps) {
if (this.props.data !== prevProps.data) {
const variablesArr = await getVariables(this);
this.setState({
variables: variablesArr
});
}
if (prevProps.value !== this.props.value) {
this.initFormulaPickerValue(this.props.value);
}
}
componentWillUnmount() {
this.isUnmount = true;
}
@autobind
initFormulaPickerValue(value: string) {
const formulaPickerValue =
value?.replace(/^\$\{(.*)\}$/, (match: string, p1: string) => p1) || '';
this.setState({
formulaPickerValue
});
}
@autobind
renderFormulaValue(item: any) {
const html = {__html: item.html};
// bca-disable-next-line
return <span dangerouslySetInnerHTML={html}></span>;
}
@autobind
handleConfirm(value = '') {
if (this.props.evalMode) {
value = value.replace(/^\$\{(.*)\}$/, (match: string, p1: string) => p1);
value = value ? `\${${value}}` : '';
}
this.props?.onChange?.(value);
}
@autobind
handleClearExpression(e: React.MouseEvent<HTMLElement>) {
e.stopPropagation();
e.preventDefault();
this.props?.onChange?.('');
}
render() {
const {value, className, variableMode, header, ...rest} = this.props;
const {formulaPickerValue, variables} = this.state;
const highlightValue = FormulaEditor.highlightValue(
formulaPickerValue,
variables
) || {
html: formulaPickerValue
};
// 自身字段
const selfName = this.props?.data?.name;
return (
<div className={cx('ae-ExpressionFormulaControl', className)}>
<PickerContainer
showTitle={false}
bodyRender={({
value,
onChange
}: {
onChange: (value: any) => void;
value: any;
}) => {
return (
<FormulaEditor
{...rest}
evalMode={true}
variableMode={variableMode}
variables={variables}
header={header || '表达式'}
value={formulaPickerValue}
onChange={onChange}
selfVariableName={selfName}
/>
);
}}
value={formulaPickerValue}
onConfirm={this.handleConfirm}
size="md"
>
{({onClick}: {onClick: (e: React.MouseEvent) => void}) =>
formulaPickerValue ? (
<Button
className="btn-configured"
tooltip={{
placement: 'top',
tooltipTheme: 'dark',
mouseLeaveDelay: 20,
content: value,
tooltipClassName: 'btn-configured-tooltip',
children: () => this.renderFormulaValue(highlightValue)
}}
onClick={onClick}
>
<Icon
icon="input-clear"
className="icon"
onClick={this.handleClearExpression}
/>
</Button>
) : (
<>
<Button className="btn-set-expression" onClick={onClick}>
</Button>
</>
)
}
</PickerContainer>
</div>
);
}
}
@FormItem({
type: 'ae-expressionFormulaControl'
})
export class ExpressionFormulaControlRenderer extends ExpressionFormulaControl {}

View File

@ -13,9 +13,16 @@ import isString from 'lodash/isString';
import isEqual from 'lodash/isEqual';
import omit from 'lodash/omit';
import cx from 'classnames';
import {FormItem, Button, InputBox, Icon, ResultBox} from 'amis';
import {
FormItem,
Button,
InputBox,
Icon,
ResultBox,
TooltipWrapper
} from 'amis';
import {FormulaExec, isExpression} from 'amis';
import {PickerContainer} from 'amis';
import {PickerContainer, relativeValueRe} from 'amis';
import {FormulaEditor} from 'amis-ui/lib/components/formula/Editor';
import {autobind} from 'amis-editor-core';
@ -27,24 +34,30 @@ import type {
import {dataMapping, FormControlProps} from 'amis-core';
import type {BaseEventContext} from 'amis-editor-core';
import {EditorManager} from 'amis-editor-core';
import {getVariables} from './textarea-formula/utils';
export enum FormulaDateType {
NotDate, // 不是时间类
IsDate, // 日期时间类
IsRange // 日期时间范围类
}
export interface FormulaControlProps extends FormControlProps {
/**
* evalMode
* ${}
* true ${}
*/
evalMode?: boolean;
manager?: EditorManager;
/**
*
*/
variables?: Array<VariableItem>;
variables?: Array<VariableItem> | Function;
/**
* 'tabs' 'tree'
* variables 使
* props.variables amis数据域中取变量集合 false;
*/
requiredDataPropsVariables?: boolean;
/**
* 'tabs' 'tree', tabs
*/
variableMode?: 'tabs' | 'tree';
@ -55,10 +68,15 @@ export interface FormulaControlProps extends FormControlProps {
functions: Array<FuncGroup>;
/**
*
* "表达式"
*/
header: string;
/**
* &
*/
placeholder: string;
/**
* Form的其他字段
*/
@ -103,6 +121,14 @@ export interface FormulaControlProps extends FormControlProps {
* 使Form数据
*/
useExternalFormData?: boolean;
/**
* 使formulaControl
* 使 now+1day-2weeks+1hours+2years等这种相对值写法 ${}
* 使 1day2weeks1hours2years等这种相对值写法 ${}
* FormulaDateType.NotDate
*/
DateTimeType?: FormulaDateType;
}
interface FormulaControlState {
@ -110,8 +136,6 @@ interface FormulaControlState {
variables: any;
variableMode?: 'tree' | 'tabs';
evalMode?: boolean;
}
export default class FormulaControl extends React.Component<
@ -119,37 +143,33 @@ export default class FormulaControl extends React.Component<
FormulaControlState
> {
static defaultProps: Partial<FormulaControlProps> = {
simple: false
simple: false,
rendererWrapper: false,
DateTimeType: FormulaDateType.NotDate,
requiredDataPropsVariables: false
};
isUnmount: boolean;
constructor(props: FormulaControlProps) {
super(props);
this.state = {
variables: this.normalizeVariables(props.variables), // 备注: 待沟通
variableMode: 'tabs',
evalMode: true
variables: [],
variableMode: 'tabs'
};
}
componentDidUpdate(prevProps: FormulaControlProps) {
// 优先使用props中的变量数据
if (!this.props.variables) {
// 从amis数据域中取变量数据
this.resolveVariablesFromScope().then(variables => {
if (Array.isArray(variables)) {
const vars = variables.filter(item => item.children?.length);
if (!this.isUnmount && !isEqual(vars, this.state.variables)) {
this.setState({
variables: vars
});
}
}
});
}
async componentDidMount() {
const variablesArr = await getVariables(this);
this.setState({
variables: variablesArr
});
}
async componentDidUpdate(prevProps: FormulaControlProps) {
if (this.props.data !== prevProps.data) {
const variablesArr = await getVariables(this);
this.setState({
variables: dataMapping(this.props.variables, this.props.data)
variables: variablesArr
});
}
}
@ -158,58 +178,36 @@ export default class FormulaControl extends React.Component<
this.isUnmount = true;
}
// 组件默认值设置交互中未使用
normalizeVariables(variables: any) {
if (!variables) {
return [];
}
if (
variables &&
variables.some((item: any) => isExpression(item.children))
) {
variables = dataMapping(variables, this.props.data);
}
const {context, evalMode} = this.props;
// 自身字段
const field = this.props?.data?.name;
const ancestorField = context?.node?.ancestorField;
return uniqBy(
[
...(Array.isArray(variables) ? variables : []),
...(ancestorField
? ancestorField.map((item: any) => ({
label: item,
value: evalMode ? `this.${item}` : item
}))
: []),
...(field ? [{label: field, value: `this.${field}`}] : [])
],
'value'
);
}
/**
* ${xx} \${xx} \${xx}
* 备注: 手动编辑时 ${xx}
*/
@autobind
replaceExpression(expression: any): any {
outReplaceExpression(expression: any): any {
if (expression && isString(expression) && isExpression(expression)) {
return expression.replace(/(^|[^\\])\$\{/g, '\\${');
}
return expression;
}
@autobind
inReplaceExpression(expression: any): any {
if (expression && isString(expression)) {
return expression.replace(/\\\$\{/g, '${');
}
return expression;
}
// 根据 name 值 判断当前表达式是否 存在循环引用问题
@autobind
isLoopExpression(expression: any, selfName: string): boolean {
if (!expression || !selfName || !isString(expression)) {
return false;
}
const variables = FormulaExec.collect(expression);
let variables = [];
try {
variables = FormulaExec.collect(expression);
} catch (e) {}
return variables.some((variable: string) => variable === selfName);
}
@ -267,36 +265,79 @@ export default class FormulaControl extends React.Component<
return false;
}
async resolveVariablesFromScope() {
const {node, manager} = this.props.formProps || this.props;
await manager?.getContextSchemas(node);
const dataPropsAsOptions = manager?.dataSchema?.getDataPropsAsOptions();
matchDate(str: string): boolean {
const matchDate =
/^(.+)?(\+|-)(\d+)(minute|min|hour|day|week|month|year|weekday|second|millisecond)s?$/i;
const m = matchDate.exec(str);
return m ? (m[1] ? this.matchDate(m[1]) : true) : false;
}
if (dataPropsAsOptions) {
return dataPropsAsOptions.map((item: any) => ({
selectMode: 'tree',
...item
}));
matchDateRange(str: string): boolean {
if (/^(now|today)$/.test(str)) {
return true;
}
return [];
return this.matchDate(str);
}
// 日期类组件 & 是否存在快捷键判断
@autobind
hasDateShortcutkey(str: string): boolean {
const {DateTimeType} = this.props;
if (DateTimeType === FormulaDateType.IsDate) {
if (/^(now|today)$/.test(str)) {
return true;
}
return this.matchDate(str);
} else if (DateTimeType === FormulaDateType.IsRange) {
const start_end = str?.split?.(',');
if (start_end && start_end.length === 2) {
return (
this.matchDateRange(start_end[0].trim()) &&
this.matchDateRange(start_end[1].trim())
);
}
}
// 非日期类组件使用也直接false
// if (DateTimeType === FormulaDateType.NotDate) {
// return false;
// }
return false;
}
@autobind
transExpr(str: string) {
if (
typeof str === 'string' &&
str?.slice(0, 2) === '${' &&
str?.slice(-1) === '}'
) {
// 非最外层内容还存在表达式情况
if (isExpression(str.slice(2, -1))) {
return str;
}
if (str.lastIndexOf('${') > str.indexOf('}') && str.indexOf('}') > -1) {
return str;
}
return str.slice(2, -1);
}
return str;
}
@autobind
handleConfirm(value: any) {
this.props?.onChange?.(value);
const val = !value
? undefined
: isExpression(value) || this.hasDateShortcutkey(value)
? value
: `\${${value}}`;
this.props?.onChange?.(val);
}
handleSimpleInputChange = debounce(
(value: any) => {
const curValue = this.replaceExpression(value);
this.props?.onChange?.(curValue);
},
250,
{
trailing: true,
leading: false
}
);
handleSimpleInputChange = (value: any) => {
const curValue = this.outReplaceExpression(value);
this.props?.onChange?.(curValue);
};
handleInputChange = (value: any) => {
this.props?.onChange?.(value);
@ -305,12 +346,13 @@ export default class FormulaControl extends React.Component<
// 剔除掉一些用不上的属性
@autobind
filterCustomRendererProps(rendererSchema: any) {
const {data, name} = this.props;
const {data, name, placeholder} = this.props;
let curRendererSchema: any = null;
if (rendererSchema) {
curRendererSchema = Object.assign({}, rendererSchema, data, {
type: rendererSchema.type ?? data.type
type: rendererSchema.type ?? data.type,
name: rendererSchema.name ?? data.name ?? 'value'
});
// 默认要剔除的字段
@ -361,27 +403,23 @@ export default class FormulaControl extends React.Component<
}
curRendererSchema = omit(curRendererSchema, deleteProps);
// 避免没有清空icon
if (
curRendererSchema.clearable !== undefined &&
!curRendererSchema.clearable
) {
curRendererSchema.clearable = true;
}
// 设置可清空
curRendererSchema.clearable = true;
// 设置统一的占位提示
if (curRendererSchema.type === 'select') {
curRendererSchema.placeholder = '请选择默认值';
!curRendererSchema.placeholder &&
(curRendererSchema.placeholder = '请选择静态值');
curRendererSchema.inputClassName =
'ae-editor-FormulaControl-select-style';
} else if (placeholder) {
curRendererSchema.placeholder = placeholder;
} else {
curRendererSchema.placeholder = '请输入静态默认值';
curRendererSchema.placeholder = '请输入静态值';
}
// 设置popOverContainer
curRendererSchema.popOverContainer = window.document.body;
}
return curRendererSchema;
}
@ -411,7 +449,6 @@ export default class FormulaControl extends React.Component<
variables,
placeholder,
simple,
evalMode,
rendererSchema,
rendererWrapper,
manager,
@ -420,7 +457,6 @@ export default class FormulaControl extends React.Component<
...rest
} = this.props;
const labelText = typeof label === 'string' ? label : '';
// 自身字段
const selfName = this.props?.data?.name;
@ -437,17 +473,26 @@ export default class FormulaControl extends React.Component<
// 判断是否含有公式表达式
const isTypeError = !this.isExpectType(value);
const exprValue = this.transExpr(value);
const isError = isLoop || isTypeError;
const highlightValue = isExpression(value)
? FormulaEditor.highlightValue(
value,
this.state.variables,
evalMode ?? this.state.evalMode
)
? FormulaEditor.highlightValue(exprValue, this.state.variables) || {
html: exprValue
}
: value;
// 公式表达式弹窗内容过滤
const filterValue = isExpression(value)
? exprValue
: this.hasDateShortcutkey(value)
? value
: undefined;
// 值 是表达式或日期快捷
const isFx = !simple && (isExpr || this.hasDateShortcutkey(value));
return (
<div
className={cx(
@ -456,51 +501,78 @@ export default class FormulaControl extends React.Component<
className
)}
>
{!simple && !rendererSchema && !isExpr && (
<InputBox
className="ae-editor-FormulaControl-input"
value={value}
clearable={true}
placeholder={placeholder}
onChange={this.handleSimpleInputChange}
/>
)}
{!simple && rendererSchema && !isExpr && (
<div
className={cx(
'ae-editor-FormulaControl-custom-renderer',
rendererWrapper ? 'border-wrapper' : ''
)}
>
{render('inner', this.filterCustomRendererProps(rendererSchema), {
inputOnly: true,
value: value,
data: useExternalFormData
? {
...this.props.data
}
: {},
onChange: this.handleSimpleInputChange,
manager: manager
})}
</div>
)}
{!simple && isExpr && (
<ResultBox
className={cx(
'ae-editor-FormulaControl-ResultBox',
isError ? 'is-error' : ''
)}
allowInput={false}
clearable={true}
value={value}
result={highlightValue}
itemRender={this.renderFormulaValue}
onChange={this.handleInputChange}
onResultChange={() => {
this.handleInputChange(undefined);
{/* 非简单模式 & 非表达式 & 非日期快捷 & 无自定义渲染 */}
{!simple &&
!isExpr &&
!this.hasDateShortcutkey(value) &&
!rendererSchema && (
<InputBox
className="ae-editor-FormulaControl-input"
value={this.inReplaceExpression(value)}
clearable={true}
placeholder={placeholder ?? '请输入静态值'}
onChange={this.handleSimpleInputChange}
/>
)}
{/* 非简单模式 & 非表达式 & 非日期快捷 & 自定义渲染 */}
{!simple &&
!isExpr &&
!this.hasDateShortcutkey(value) &&
rendererSchema && (
<div
className={cx(
'ae-editor-FormulaControl-custom-renderer',
rendererWrapper ? 'border-wrapper' : ''
)}
>
{render('inner', this.filterCustomRendererProps(rendererSchema), {
inputOnly: true,
value: this.inReplaceExpression(value),
data: useExternalFormData
? {
...this.props.data
}
: {},
onChange: this.handleSimpleInputChange,
manager: manager
})}
</div>
)}
{/* 非简单模式 &(表达式 或 日期快捷)*/}
{isFx && (
<TooltipWrapper
trigger="hover"
placement="top"
style={{fontSize: '12px'}}
tooltip={{
tooltipTheme: 'dark',
mouseLeaveDelay: 20,
content: exprValue,
children: () => this.renderFormulaValue(highlightValue)
}}
/>
>
<div className="ae-editor-FormulaControl-tooltipBox">
<ResultBox
className={cx(
'ae-editor-FormulaControl-ResultBox',
isError ? 'is-error' : ''
)}
allowInput={false}
clearable={true}
value={value}
result={{
html: this.hasDateShortcutkey(value)
? '已配置相对值'
: '已配置表达式'
}}
itemRender={this.renderFormulaValue}
onChange={this.handleInputChange}
onResultChange={() => {
this.handleInputChange(undefined);
}}
/>
</div>
</TooltipWrapper>
)}
<PickerContainer
showTitle={false}
@ -514,11 +586,11 @@ export default class FormulaControl extends React.Component<
return (
<FormulaEditor
{...rest}
evalMode={evalMode ?? this.state.evalMode}
evalMode={true}
variableMode={rest.variableMode ?? this.state.variableMode}
variables={this.state.variables}
header={header || labelText}
value={isString(value) ? value : undefined}
header={header || '表达式'}
value={filterValue}
onChange={onChange}
selfVariableName={selfName}
/>
@ -530,26 +602,26 @@ export default class FormulaControl extends React.Component<
>
{({onClick}: {onClick: (e: React.MouseEvent) => void}) => (
<Button
className="ae-editor-FormulaControl-button"
size="sm"
tooltip={'点击配置表达式'}
tooltipPlacement="left"
tooltip={{
enterable: false,
content: '点击配置表达式',
placement: 'left',
mouseLeaveDelay: 0
}}
onClick={onClick}
// active={simple && value} // 不需要,避免 hover 时无任何反馈效果
>
<Icon
icon="function"
className={cx('ae-editor-FormulaControl-icon', 'icon', {
['is-filled']: !!value
['is-filled']: !!isFx
})}
/>
</Button>
)}
</PickerContainer>
{isExpr && !isError && (
<div className="desc-msg info-msg" title={value}>
{value}
</div>
)}
{isError && (
<div className="desc-msg error-msg">
{isLoop ? '当前表达式异常(存在循环引用)' : '数值类型不匹配'}

View File

@ -180,11 +180,12 @@ export class StatusControl extends React.Component<
]
},
{
type: 'ae-formulaControl',
name: 'expression',
type: 'ae-expressionFormulaControl',
label: '表达式',
name: 'expression',
placeholder: `请输入${label}条件`,
visibleOn: 'this.statusType === 2'
visibleOn: 'this.statusType === 2',
onChange: (value: any) => {}
}
]
},

View File

@ -11,7 +11,7 @@ import {FormItem, Button, Overlay, PopOver, Icon, Switch} from 'amis';
import {isObject, autobind} from 'amis-editor-core';
import type {Action} from 'amis/lib/types';
import type {Action} from 'amis';
import type {SchemaCollection} from 'amis/lib/Schema';
import type {IScopedContext} from 'amis-core';
import type {FormSchema} from 'amis/lib/schema';

View File

@ -280,6 +280,7 @@ export default class ActionDialog extends React.Component<ActionDialogProp> {
className: 'action-panel-title',
visibleOn: 'data.actionType'
},
/*
{
name: 'expression',
title: '',
@ -293,6 +294,17 @@ export default class ActionDialog extends React.Component<ActionDialogProp> {
size: 'lg',
placeholder: '默认执行该动作',
visibleOn: 'data.actionType'
},
*/
{
name: 'expression',
label: '执行条件',
type: 'ae-expressionFormulaControl',
variables: '${variables}',
mode: 'horizontal',
size: 'lg',
placeholder: '默认执行该动作',
visibleOn: 'data.actionType'
}
]
}

View File

@ -50,6 +50,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
type: 'wrapper',
className: 'p-none',
body: [
/**
{
label: '页面地址',
type: 'input-formula',
@ -64,6 +65,18 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
required: true,
visibleOn: 'data.actionType === "url"'
},
*/
{
name: 'url',
label: '页面地址',
type: 'ae-textareaFormulaControl',
variables: '${variables}',
mode: 'horizontal',
// placeholder: 'http://', 长文本暂不支持
size: 'lg',
required: true,
visibleOn: 'data.actionType === "url"'
},
{
type: 'combo',
name: 'params',
@ -75,11 +88,10 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
{
name: 'key',
placeholder: '参数名',
type: 'input-text',
mode: 'inline',
size: 'xs'
type: 'input-text'
},
{
/**
{
name: 'val',
placeholder: '参数值',
type: 'input-formula',
@ -88,6 +100,14 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
variableMode: 'tabs',
inputMode: 'input-group',
size: 'xs'
},
*/
{
type: 'ae-formulaControl',
variables: '${variables}',
name: 'val',
variableMode: 'tabs',
placeholder: '参数值'
}
]
},
@ -306,23 +326,21 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
</div>
);
},
schema: getArgsWrapper({
type: 'wrapper',
className: 'p-none',
body: [
{
type: 'button-group-select',
name: 'msgType',
label: '消息类型',
value: 'info',
required: true,
mode: 'horizontal',
options: Object.keys(MSG_TYPES).map(key => ({
label: MSG_TYPES[key],
value: key,
level: 'default'
}))
},
schema: [
{
type: 'button-group-select',
name: 'msgType',
label: '消息类型',
value: 'info',
required: true,
mode: 'horizontal',
options: Object.keys(MSG_TYPES).map(key => ({
label: MSG_TYPES[key],
value: key,
level: 'default'
}))
},
/*
{
name: 'msg',
label: '消息内容',
@ -335,81 +353,114 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
size: 'lg',
required: true
},
{
name: 'title',
type: 'input-formula',
variables: '${variables}',
evalMode: false,
variableMode: 'tabs',
inputMode: 'input-group',
label: '标题内容',
size: 'lg',
mode: 'horizontal'
*/
{
name: 'msg',
label: '消息内容',
type: 'ae-textareaFormulaControl',
mode: 'horizontal',
variables: '${variables}',
size: 'lg',
required: true
},
/*
{
name: 'title',
type: 'input-formula',
variables: '${variables}',
evalMode: false,
variableMode: 'tabs',
inputMode: 'input-group',
label: '标题内容',
size: 'lg',
mode: 'horizontal'
},
*/
{
name: 'title',
label: '标题内容',
type: 'ae-textareaFormulaControl',
variables: '${variables}',
mode: 'horizontal',
size: 'lg'
},
/*
{
name: 'timeout',
type: 'input-formula',
variables: '${variables}',
evalMode: false,
variableMode: 'tabs',
inputMode: 'input-group',
label: '持续时间(ms)',
size: 'lg',
mode: 'horizontal'
},
*/
{
name: 'timeout',
label: '持续时间(ms)',
type: 'ae-formulaControl',
rendererSchema: {
type: 'input-number'
},
{
name: 'timeout',
type: 'input-formula',
variables: '${variables}',
evalMode: false,
variableMode: 'tabs',
inputMode: 'input-group',
label: '持续时间(ms)',
size: 'lg',
mode: 'horizontal'
},
{
type: 'button-group-select',
name: 'position',
value: 'top-right',
mode: 'horizontal',
label: '显示位置',
options: [
{
label: '左上',
value: 'top-left'
},
valueType: 'number',
variables: '${variables}',
size: 'lg',
mode: 'horizontal'
},
{
type: 'button-group-select',
name: 'position',
value: 'top-right',
mode: 'horizontal',
label: '显示位置',
options: [
{
label: '左上',
value: 'top-left'
},
{
label: '中上',
value: 'top-center'
},
{
label: '中上',
value: 'top-center'
},
{
label: '右上',
value: 'top-right'
},
{
label: '右上',
value: 'top-right'
},
{
label: '左下',
value: 'bottom-left'
},
{
label: '中下',
value: 'bottom-center'
},
{
label: '左下',
value: 'bottom-left'
},
{
label: '中下',
value: 'bottom-center'
},
{
label: '右下',
value: 'bottom-right'
}
]
},
{
type: 'switch',
name: 'closeButton',
value: true,
label: '展示关闭按钮',
mode: 'horizontal'
},
{
type: 'switch',
name: 'showIcon',
value: true,
label: '展示图标',
mode: 'horizontal'
}
]
})
{
label: '右下',
value: 'bottom-right'
}
]
},
{
type: 'switch',
name: 'closeButton',
value: true,
label: '展示关闭按钮',
mode: 'horizontal'
},
{
type: 'switch',
name: 'showIcon',
value: true,
label: '展示图标',
mode: 'horizontal'
}
]
}
]
},
@ -833,6 +884,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
form.setValueByName('__valueInput', undefined);
}
},
/*
{
name: '__valueInput',
type: 'input-formula',
@ -846,6 +898,17 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
mode: 'horizontal',
visibleOn: `data.__addParam && data.__customData && data.__containerType === "all" && data.actionType === "reload" && ${IS_DATA_CONTAINER}`
},
*/
{
name: '__valueInput',
label: '',
type: 'ae-formulaControl',
variables: '${variables}',
size: 'lg',
mode: 'horizontal',
required: true,
visibleOn: `data.__addParam && data.__customData && data.__containerType === "all" && data.actionType === "reload" && ${IS_DATA_CONTAINER}`
},
{
type: 'combo',
name: '__reloadParams',
@ -866,6 +929,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
valueField: 'value',
required: true
},
/*
{
name: 'val',
type: 'input-formula',
@ -875,6 +939,13 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
variableMode: 'tabs',
inputMode: 'input-group'
}
*/
{
name: 'val',
type: 'ae-formulaControl',
variables: '${variables}',
placeholder: '参数值'
}
],
visibleOn: `data.__addParam && data.__customData && data.__containerType === "appoint" && data.actionType === "reload" && ${IS_DATA_CONTAINER}`
},
@ -1035,6 +1106,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
valueField: 'value',
required: true
},
/*
{
name: 'val',
type: 'input-formula',
@ -1044,6 +1116,13 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
variableMode: 'tabs',
inputMode: 'input-group'
}
*/
{
name: 'val',
type: 'ae-formulaControl',
variables: '${variables}',
placeholder: '字段值'
}
],
visibleOn: `${IS_DATA_CONTAINER} && data.__containerType === 'appoint' || data.__comboType === 'appoint'`
},
@ -1084,6 +1163,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
required: true,
visibleOn: `data.__rendererName`
},
/*
{
name: 'val',
type: 'input-formula',
@ -1092,11 +1172,18 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
variableMode: 'tabs',
inputMode: 'input-group'
}
*/
{
name: 'val',
type: 'ae-formulaControl',
variables: '${variables}'
}
]
}
],
visibleOn: `data.__rendererName === 'combo' && data.__comboType === 'all'`
},
/*
{
name: '__valueInput',
type: 'input-formula',
@ -1110,6 +1197,19 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
visibleOn: `(${IS_DATA_CONTAINER} || ${SHOW_SELECT_PROP}) && data.__containerType === 'all'`,
required: true
},
*/
{
name: '__valueInput',
label: '',
type: 'ae-formulaControl',
variables: '${variables}',
size: 'lg',
mode: 'horizontal',
visibleOn: `(${IS_DATA_CONTAINER} || ${SHOW_SELECT_PROP}) && data.__containerType === 'all'`,
required: true
},
,
/*
{
name: '__valueInput',
type: 'input-formula',
@ -1123,6 +1223,17 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
visibleOn: `data.__rendererName && !${IS_DATA_CONTAINER} && data.__rendererName !== 'combo'`,
required: true
}
*/
{
name: '__valueInput',
label: '数据设置',
type: 'ae-formulaControl',
variables: '${variables}',
size: 'lg',
mode: 'horizontal',
visibleOn: `data.__rendererName && !${IS_DATA_CONTAINER} && data.__rendererName !== 'combo'`,
required: true
}
]
})
]
@ -1231,7 +1342,8 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
type: 'wrapper',
className: 'p-none',
body: [
{
/*
{
name: 'content',
type: 'input-formula',
variables: '${variables}',
@ -1244,6 +1356,17 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
visibleOn: 'data.actionType === "copy"',
required: true
},
*/
{
name: 'content',
label: '内容模板',
type: 'ae-textareaFormulaControl',
variables: '${variables}',
mode: 'horizontal',
size: 'lg',
visibleOn: 'data.actionType === "copy"',
required: true
},
{
type: 'select',
name: 'copyFormat',
@ -1281,6 +1404,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
lineNumbers: 'off',
glyphMargin: false,
tabSize: 2,
fontSize: '12px',
wordWrap: 'on',
lineDecorationsWidth: 0,
lineNumbersMinChars: 0,

View File

@ -226,6 +226,7 @@ export const COMMON_ACTION_SCHEMA_MAP: {
valueField: 'value',
required: true
},
/*
{
name: 'val',
type: 'input-formula',
@ -235,6 +236,13 @@ export const COMMON_ACTION_SCHEMA_MAP: {
variableMode: 'tabs',
inputMode: 'input-group'
}
*/
{
name: 'val',
type: 'ae-formulaControl',
variables: '${variables}',
placeholder: '变量值'
}
],
visibleOn: `${IS_DATA_CONTAINER}`
},
@ -269,6 +277,7 @@ export const COMMON_ACTION_SCHEMA_MAP: {
type: 'input-text',
required: true
},
/*
{
name: 'val',
type: 'input-formula',
@ -277,11 +286,18 @@ export const COMMON_ACTION_SCHEMA_MAP: {
variableMode: 'tabs',
inputMode: 'input-group'
}
*/
{
name: 'val',
type: 'ae-formulaControl',
variables: '${variables}'
}
]
}
],
visibleOn: `data.__rendererName === 'combo'`
},
/*
{
name: '__valueInput',
type: 'input-formula',
@ -295,6 +311,17 @@ export const COMMON_ACTION_SCHEMA_MAP: {
visibleOn: `!${IS_DATA_CONTAINER} && data.__rendererName !== 'combo'`,
required: true
}
*/
{
name: '__valueInput',
label: '变量赋值',
type: 'ae-formulaControl',
variables: '${variables}',
size: 'lg',
mode: 'horizontal',
visibleOn: `!${IS_DATA_CONTAINER} && data.__rendererName !== 'combo'`,
required: true
}
]
})
},

View File

@ -0,0 +1,66 @@
import React, {useEffect} from 'react';
import {Modal, Button} from 'amis';
import cx from 'classnames';
import FormulaEditor from 'amis-ui/lib/components/formula/Editor';
export interface FormulaPickerProps {
onConfirm: (data: string) => void;
onClose: () => void;
variables: any[];
value?: string;
initable?: boolean;
variableMode?: 'tabs' | 'tree';
evalMode?: boolean;
}
const FormulaPicker: React.FC<FormulaPickerProps> = props => {
const {variables, variableMode, evalMode = true} = props;
const [formula, setFormula] = React.useState('');
useEffect(() => {
const {initable, value} = props;
if (initable && value) {
setFormula(value);
}
}, [props.value]);
const handleChange = (data: any) => {
setFormula(data);
};
const handleClose = () => {
props.onClose && props.onClose();
};
const handleConfirm = () => {
props.onConfirm && props.onConfirm(formula);
};
return (
<Modal
className={cx('FormulaPicker-Modal')}
size="md"
show
onHide={handleClose}
closeOnEsc
>
<Modal.Body>
<FormulaEditor
header="表达式"
variables={variables}
variableMode={variableMode}
value={formula}
evalMode={evalMode}
onChange={handleChange}
/>
</Modal.Body>
<Modal.Footer>
<Button onClick={handleClose}></Button>
<Button onClick={handleConfirm} level="primary">
</Button>
</Modal.Footer>
</Modal>
);
};
export default FormulaPicker;

View File

@ -0,0 +1,286 @@
/**
* @file
*/
import React from 'react';
import cx from 'classnames';
import {Icon, FormItem} from 'amis';
import {autobind, FormControlProps, Schema} from 'amis-core';
import CodeMirrorEditor from 'amis-ui/lib/components/CodeMirror';
import {FormulaPlugin, editorFactory} from './plugin';
import FormulaPicker from './FormulaPicker';
import debounce from 'lodash/debounce';
import CodeMirror from 'codemirror';
import {getVariables} from './utils';
import {VariableItem} from 'amis-ui/lib/components/formula/Editor';
export interface TextareaFormulaControlProps extends FormControlProps {
/**
* 100 px
*/
height?: number;
/**
*
*/
variables?: Array<VariableItem> | Function;
/**
* variables 使
* props.variables amis数据域中取变量集合 false;
*/
requiredDataPropsVariables?: boolean;
/**
* 'tabs' 'tree'
*/
variableMode?: 'tree' | 'tabs';
/**
*
*/
additionalMenus?: Array<{
label: string; // 文案当存在图标时为tooltip内容
onClick: () => void; // 触发事件
icon?: string; // 图标
className?: string; //外层类名
}>;
}
interface TextareaFormulaControlState {
value: string; // 当前文本值
variables: Array<VariableItem>; // 变量数据
formulaPickerOpen: boolean; // 是否打开公式编辑器
formulaPickerValue: string; // 公式编辑器内容
expressionBrace?: Array<CodeMirror.Position>; // 表达式所在位置
isFullscreen: boolean; //是否全屏
}
export class TextareaFormulaControl extends React.Component<
TextareaFormulaControlProps,
TextareaFormulaControlState
> {
static defaultProps: Partial<TextareaFormulaControlProps> = {
variableMode: 'tabs',
requiredDataPropsVariables: false,
height: 100
};
isUnmount: boolean;
editorPlugin?: FormulaPlugin;
constructor(props: TextareaFormulaControlProps) {
super(props);
this.state = {
value: '',
variables: [],
formulaPickerOpen: false,
formulaPickerValue: '',
isFullscreen: false
};
}
async componentDidMount() {
const variablesArr = await getVariables(this);
this.setState({
variables: variablesArr
});
}
async componentDidUpdate(prevProps: TextareaFormulaControlProps) {
if (this.props.data !== prevProps.data) {
const variablesArr = await getVariables(this);
this.setState({
variables: variablesArr
});
}
}
componentWillUnmount() {
this.isUnmount = true;
}
@autobind
onExpressionClick(expression: string, brace?: Array<CodeMirror.Position>) {
this.setState({
formulaPickerValue: expression,
formulaPickerOpen: true,
expressionBrace: brace
});
}
@autobind
closeFormulaPicker() {
this.setState({formulaPickerOpen: false});
}
@autobind
handleConfirm(value: any) {
const {expressionBrace} = this.state;
// 去除可能包裹的最外层的${}
value = value.replace(/^\$\{(.*)\}$/, (match: string, p1: string) => p1);
value = value ? `\${${value}}` : value;
this.editorPlugin?.insertContent(value, 'expression', expressionBrace);
this.setState({
formulaPickerOpen: false,
expressionBrace: undefined
});
this.closeFormulaPicker();
}
handleOnChange = debounce((value: any) => {
this.props.onChange?.(value);
}, 200);
@autobind
editorFactory(dom: HTMLElement, cm: any) {
const variables = this.props.variables || this.state.variables;
return editorFactory(dom, cm, {...this.props, variables});
}
@autobind
handleEditorMounted(cm: any, editor: any) {
const variables = this.props.variables || this.state.variables;
this.editorPlugin = new FormulaPlugin(
editor,
cm,
() => ({...this.props, variables}),
this.onExpressionClick
);
}
@autobind
handleFullscreenModeChange() {
this.setState({
isFullscreen: !this.state.isFullscreen
});
}
@autobind
handleFormulaClick() {
this.setState({
formulaPickerOpen: true,
formulaPickerValue: '',
expressionBrace: undefined
});
}
@autobind
editorAutoMark() {
this.editorPlugin?.autoMark();
}
render() {
const {
className,
header,
label,
placeholder,
height,
additionalMenus,
...rest
} = this.props;
const {
value,
formulaPickerOpen,
formulaPickerValue,
isFullscreen,
variables
} = this.state;
// 输入框样式
let resultBoxStyle: {[key in string]: string} = {};
if (height) {
resultBoxStyle.height = `${height}px`;
}
return (
<div
className={cx(
'ae-TextareaFormulaControl',
{
'is-fullscreen': this.state.isFullscreen
},
className
)}
>
<div className={cx('ae-TextareaResultBox')} style={resultBoxStyle}>
<CodeMirrorEditor
className="ae-TextareaResultBox-editor"
value={value}
onChange={this.handleOnChange}
editorFactory={this.editorFactory}
editorDidMount={this.handleEditorMounted}
onBlur={this.editorAutoMark}
/>
<ul className="ae-TextareaResultBox-footer">
<li className="ae-TextareaResultBox-footer-fullscreen">
<a
className={cx('Modal-fullscreen')}
data-tooltip={isFullscreen ? '退出全屏' : '全屏'}
data-position="top"
onClick={this.handleFullscreenModeChange}
>
<Icon
icon={isFullscreen ? 'compress-alt' : 'expand-alt'}
className="icon"
/>
</a>
</li>
<li className="ae-TextareaResultBox-footer-fxIcon">
<a
data-tooltip="表达式"
data-position="top"
onClick={this.handleFormulaClick}
>
<Icon icon="function" className="icon" />
</a>
</li>
{/* 附加底部按钮菜单项 */}
{additionalMenus?.length &&
additionalMenus?.map((item, i) => {
return (
<li key={i} className={item?.className || ''}>
{item.icon ? (
<a
data-tooltip={item.label}
data-position="top"
onClick={() => item.onClick()}
>
<Icon icon={item.icon} className="icon" />
</a>
) : (
<a onClick={() => item?.onClick()}>{item.label}</a>
)}
</li>
);
})}
</ul>
</div>
{formulaPickerOpen ? (
<FormulaPicker
value={formulaPickerValue}
initable={true}
variables={variables}
variableMode={rest.variableMode}
evalMode={true}
onClose={() => this.setState({formulaPickerOpen: false})}
onConfirm={this.handleConfirm}
/>
) : null}
</div>
);
}
}
@FormItem({
type: 'ae-textareaFormulaControl'
})
export default class TextareaFormulaControlRenderer extends TextareaFormulaControl {}

View File

@ -0,0 +1,234 @@
/**
* @file codemirror
*/
import type CodeMirror from 'codemirror';
import {TextareaFormulaControlProps} from './TextareaFormulaControl';
import {FormulaEditor} from 'amis-ui/lib/components/formula/Editor';
export function editorFactory(
dom: HTMLElement,
cm: typeof CodeMirror,
props: any
) {
return cm(dom, {
value: props.value || '',
autofocus: false,
lineWrapping: true
});
}
export class FormulaPlugin {
constructor(
readonly editor: CodeMirror.Editor,
readonly cm: typeof CodeMirror,
readonly getProps: () => TextareaFormulaControlProps,
readonly onExpressionClick: (
expression: string,
brace?: Array<CodeMirror.Position>
) => any
) {
const {value} = this.getProps();
if (value) {
this.autoMark();
this.focus(value);
}
}
autoMark() {
const editor = this.editor;
const lines = editor.lineCount();
for (let line = 0; line < lines; line++) {
const content = editor.getLine(line);
const braces = this.computedBracesPosition(content);
for (let i = 0; i < braces.length; i++) {
// 替换每个公式表达式中的内容
const start = braces[i].begin;
const end = braces[i].end;
const expression = content.slice(start, end);
this.markExpression(
{
line: line,
ch: start - 2
},
{
line: line,
ch: end + 1
},
expression
);
}
}
}
// 找到表达式所在的位置
getExpressionBrace(expression: string) {
const editor = this.editor;
const lines = editor.lineCount();
for (let line = 0; line < lines; line++) {
const content = editor.getLine(line);
const braces = this.computedBracesPosition(content);
for (let i = 0; i < braces.length; i++) {
// 替换每个公式表达式中的内容
const start = braces[i].begin;
const end = braces[i].end;
if (expression === content.slice(start, end)) {
return [
{
line: line,
ch: start - 2
},
{
line: line,
ch: end + 1
}
];
}
}
}
return undefined;
}
// 计算 `${`、`}` 括号的位置,如 ${a}+${b}, 结果是 [ { from: 0, to: 3 }, { from: 5, to: 8 } ]
computedBracesPosition(exp: string) {
const braces: {begin: number; end: number}[] = [];
exp?.replace(/\$\{/g, (val, offset) => {
if (val) {
const charArr = exp.slice(offset + val.length).split('');
const cache = ['${'];
for (let index = 0; index < charArr.length; index++) {
const char = charArr[index];
if (char === '$' && charArr[index + 1] === '{') {
cache.push('${');
} else if (char === '}') {
cache.pop();
}
if (cache.length === 0) {
braces.push({begin: offset + 2, end: index + offset + 2});
break;
}
}
}
return '';
});
return braces;
}
// 判断字符串是否在 ${} 中
checkStrIsInBraces(
[from, to]: number[],
braces: {begin: number; end: number}[]
) {
let isIn = false;
if (braces.length) {
for (let index = 0; index < braces.length; index++) {
const brace = braces[index];
if (from >= brace.begin && to <= brace.end) {
isIn = true;
break;
}
}
}
return isIn;
}
insertBraces(originFrom: CodeMirror.Position, originTo: CodeMirror.Position) {
const str = this.editor.getValue();
const braces = this.computedBracesPosition(str);
if (!this.checkStrIsInBraces([originFrom.ch, originTo.ch], braces)) {
this.editor.setCursor({
line: originFrom.line,
ch: originFrom.ch
});
this.editor.replaceSelection('${');
this.editor.setCursor({
line: originTo.line,
ch: originTo.ch + 2
});
this.editor.replaceSelection('}');
}
}
insertContent(
value: any,
type?: 'expression',
brace?: Array<CodeMirror.Position>
) {
if (brace) {
// 替换
const [from, to] = brace;
if (type === 'expression') {
this.editor.replaceRange(value, from, to);
this.autoMark();
} else if (typeof value === 'string') {
this.editor.replaceRange(value, from, to);
}
} else {
// 新增
if (type === 'expression') {
this.editor.replaceSelection(value);
this.autoMark();
} else if (typeof value === 'string') {
this.editor.replaceSelection(value);
}
this.editor.focus();
}
}
markExpression(
from: CodeMirror.Position,
to: CodeMirror.Position,
expression = '',
className = 'cm-expression'
) {
const wrap = document.createElement('span');
wrap.className = className;
const text = document.createElement('span');
text.className = `${className}-text`;
text.innerText = expression;
text.setAttribute('data-expression', expression);
text.onclick = () => {
const brace = this.getExpressionBrace(expression);
this.onExpressionClick(expression, brace);
};
const {variables} = this.getProps();
const highlightValue = FormulaEditor.highlightValue(
expression,
variables
) || {
html: expression
};
// 添加popover
const popoverEl = document.createElement('div');
// bca-disable-next-line
popoverEl.innerHTML = highlightValue.html;
popoverEl.classList.add('expression-popover');
const arrow = document.createElement('div');
arrow.classList.add('expression-popover-arrow');
popoverEl.appendChild(arrow);
wrap.appendChild(text);
wrap.appendChild(popoverEl);
this.editor.markText(from, to, {
atomic: true,
replacedWith: wrap
});
}
// 焦点放在最后
focus(value: string) {
this.editor.setCursor({
line: 0,
ch: value?.length || 0
});
}
dispose() {}
validate() {}
}

View File

@ -0,0 +1,59 @@
import {isExpression, resolveVariableAndFilter} from 'amis-core';
/**
* amis数据域中取变量数据
* @param node
* @param manager
* @returns
*/
export async function resolveVariablesFromScope(node: any, manager: any) {
await manager?.getContextSchemas(node);
const dataPropsAsOptions = manager?.dataSchema?.getDataPropsAsOptions();
if (dataPropsAsOptions) {
return dataPropsAsOptions
.map((item: any) => ({
selectMode: 'tree',
...item
}))
.filter((item: any) => item.children?.length);
}
return [];
}
/**
* props & amis数据域 variables
* @param that this
**/
export async function getVariables(that: any) {
let variablesArr: any[] = [];
const {variables, requiredDataPropsVariables} = that.props;
if (!variables || requiredDataPropsVariables) {
// 从amis数据域中取变量数据
const {node, manager} = that.props.formProps || that.props;
let vars = await resolveVariablesFromScope(node, manager);
if (Array.isArray(vars)) {
if (!that.isUnmount) {
variablesArr = vars;
}
}
}
if (variables) {
if (Array.isArray(variables)) {
variablesArr = [...variables, ...variablesArr];
} else if (typeof variables === 'function') {
variablesArr = [...variables(that), ...variablesArr];
} else if (isExpression(variables)) {
variablesArr = [
...resolveVariableAndFilter(
that.props.variables as any,
that.props.data,
'| raw'
),
...variablesArr
];
}
}
return variablesArr;
}

View File

@ -4,12 +4,16 @@ import {
defaultValue,
isObject,
tipedLabel,
DSField
DSField,
EditorManager
} from 'amis-editor-core';
import {remarkTpl} from '../component/BaseControl';
import {SchemaObject} from 'amis/lib/Schema';
import flatten from 'lodash/flatten';
import _ from 'lodash';
import {InputComponentName} from '../component/InputComponentName';
import {FormulaDateType} from '../renderer/FormulaControl';
import {VariableItem} from 'amis-ui/lib/components/formula/Editor';
/**
* @deprecated switch
@ -345,60 +349,72 @@ setSchemaTpl(
mode?: string; // 自定义展示默认值,上下展示: vertical, 左右展示: horizontal
label?: string; // 表单项 label
name?: string; // 表单项 name
header?: string; // 表达式弹窗标题
placeholder?: string; // 表达式自定义渲染 占位符
rendererSchema?: any;
rendererWrapper?: boolean; // 自定义渲染器 是否需要浅色边框包裹
needDeleteValue?: boolean; // 是否需要剔除默认值
needDeleteProps?: string[]; // 需要剔除的其他属性,默认 deleteProps 中包含一些通用属性
useSelectMode?: boolean; // 是否使用Select选择设置模式需要确保 rendererSchema.options 不为 undefined
valueType?: string; // 用于设置期望数值类型
visibleOn?: string; // 用于控制显示的表达式
evalMode?: boolean; // 为false时则会用 ${这里面才是表达式} 包裹变量
DateTimeType?: FormulaDateType; // 日期类组件要支持 表达式 & 相对值
variables?: Array<VariableItem> | Function; // 自定义变量集合
requiredDataPropsVariables?: boolean; // 是否再从amis数据域中取变量结合 默认 false
variableMode?: 'tabs' | 'tree'; // 变量展现模式
[key: string]: any; // 其他属性例如包括表单项pipeIn\Out 等等
}) => {
let curRendererSchema = config?.rendererSchema;
if (
config?.useSelectMode &&
curRendererSchema &&
curRendererSchema.options
) {
const {
rendererSchema,
useSelectMode,
mode,
visibleOn,
label,
name,
rendererWrapper,
needDeleteProps,
valueType,
header,
placeholder,
DateTimeType,
variables,
requiredDataPropsVariables,
variableMode,
...rest
} = config || {};
let curRendererSchema = rendererSchema;
if (useSelectMode && curRendererSchema && curRendererSchema.options) {
curRendererSchema = {
...curRendererSchema,
type: 'select'
};
}
if (config?.mode === 'vertical') {
// 上下展示,可避免 自定义渲染器 出现挤压
return {
type: 'group',
mode: 'vertical',
visibleOn: config?.visibleOn,
body: [
{
type: 'ae-formulaControl',
label: config?.label ?? '默认值',
name: config?.name || 'value',
rendererSchema: curRendererSchema,
rendererWrapper: config?.rendererWrapper,
needDeleteValue: config?.needDeleteValue,
valueType: config?.valueType,
visibleOn: config?.visibleOn,
evalMode: config?.evalMode ?? false // 默认需要${}包裹变量
}
]
};
} else {
return {
type: 'group',
// 默认左右展示
return {
type: 'ae-formulaControl',
label: config?.label || '默认值',
name: config?.name || 'value',
rendererSchema: curRendererSchema,
rendererWrapper: config?.rendererWrapper,
needDeleteValue: config?.needDeleteValue,
valueType: config?.valueType,
visibleOn: config?.visibleOn,
evalMode: config?.evalMode ?? false // 默认需要${}包裹变量
};
}
// 上下展示,可避免 自定义渲染器 出现挤压
mode: mode === 'vertical' ? 'vertical' : 'horizontal',
visibleOn,
body: [
{
type: 'ae-formulaControl',
label: label ?? '默认值',
name: name || 'value',
rendererWrapper,
needDeleteProps,
valueType,
header,
placeholder,
rendererSchema: curRendererSchema,
variables,
requiredDataPropsVariables,
variableMode,
DateTimeType: DateTimeType ?? FormulaDateType.NotDate,
...rest
}
]
};
}
);
@ -493,9 +509,80 @@ setSchemaTpl('selectDateRangeType', {
]
});
setSchemaTpl(
'optionsMenuTpl',
(config: {manager: EditorManager; [key: string]: any}) => {
const {manager, ...rest} = config;
// 设置 options 中变量集合
function getOptionVars(that: any) {
let schema = manager.store.valueWithoutHiddenProps;
let children = [];
if (schema.labelField) {
children.push({
label: '显示字段',
value: schema.labelField,
tag: typeof schema.labelField
});
}
if (schema.valueField) {
children.push({
label: '值字段',
value: schema.valueField,
tag: typeof schema.valueField
});
}
if (schema.options) {
let optionItem = _.reduce(
schema.options,
function (result, item) {
return {...result, ...item};
},
{}
);
delete optionItem?.$$id;
optionItem = _.omit(
optionItem,
_.map(children, item => item?.label)
);
let otherItem = _.map(_.keys(optionItem), item => ({
label:
item === 'label' ? '选项文本' : item === 'value' ? '选项值' : item,
value: item,
tag: typeof optionItem[item]
}));
children.push(...otherItem);
}
let variablesArr = [
{
label: '选项字段',
children
}
];
return variablesArr;
}
return {
type: 'ae-textareaFormulaControl',
mode: 'normal',
label: tipedLabel(
'选项模板',
'自定义选项渲染模板支持JSX、数据域变量使用'
),
name: 'menuTpl',
variables: getOptionVars,
requiredDataPropsVariables: true,
...rest
};
}
);
setSchemaTpl('menuTpl', {
type: 'ae-formulaControl',
label: tipedLabel('模板', '选项渲染模板支持JSX变量使用\\${xx}'),
type: 'ae-textareaFormulaControl',
mode: 'normal',
label: tipedLabel('模板', '自定义选项渲染模板支持JSX、数据域变量使用'),
name: 'menuTpl'
});
@ -1000,6 +1087,7 @@ setSchemaTpl('badge', {
type: 'ae-badge'
});
// 暂未使用
setSchemaTpl('formulaControl', (schema: object = {}) => {
return {
type: 'ae-formulaControl',
@ -1061,7 +1149,8 @@ setSchemaTpl('app-page-args', {
valueField: 'value',
required: true
},
{
/*
{
name: 'val',
type: 'input-formula',
placeholder: '参数值',
@ -1070,6 +1159,13 @@ setSchemaTpl('app-page-args', {
variableMode: 'tabs',
inputMode: 'input-group'
}
*/
{
name: 'val',
type: 'ae-formulaControl',
variables: '${variables}',
placeholder: '参数值'
}
]
});
@ -1091,3 +1187,26 @@ setSchemaTpl(
});
}
);
setSchemaTpl('virtualThreshold', {
name: 'virtualThreshold',
type: 'input-number',
min: 1,
step: 1,
precision: 0,
label: tipedLabel(
'虚拟列表阈值',
'当选项数量超过阈值后,会开启虚拟列表以优化性能'
),
pipeOut: (value: any) => value || undefined
});
setSchemaTpl('virtualItemHeight', {
name: 'itemHeight',
type: 'input-number',
min: 1,
step: 1,
precision: 0,
label: tipedLabel('选项高度', '开启虚拟列表时每个选项的高度'),
pipeOut: (value: any) => value || undefined
});

View File

@ -54,9 +54,14 @@ setSchemaTpl(
if (value === 'static') {
form.setValueByName('style.inset', undefined);
form.setValueByName('style.zIndex', undefined);
form.setValueByName('originPosition', undefined);
} else if (value === 'fixed' || value === 'absolute') {
// 默认使用右下角进行相对定位
form.setValueByName('style.inset', 'auto 50px 50px auto');
form.setValueByName('originPosition', 'right-bottom');
} else if (value === 'relative') {
form.setValueByName('style.inset', 'auto');
form.setValueByName('originPosition', undefined);
}
},
options: [
@ -138,7 +143,6 @@ setSchemaTpl(
};
},
pipeOut: (value: any) => {
console.log('pipeOut:', value);
return `${value.insetTop ?? 'auto'} ${value.insetRight ?? 'auto'} ${
value.insetBottom ?? 'auto'
} ${value.insetLeft ?? 'auto'}`;
@ -246,7 +250,7 @@ setSchemaTpl(
},
options: [
{
label: '默认',
label: '块级(默认)',
value: 'block'
},
{
@ -508,7 +512,7 @@ setSchemaTpl(
pipeOut: config?.pipeOut,
options: [
{
label: '默认(不换行)',
label: '不换行(默认)',
value: 'nowrap'
},
{
@ -588,7 +592,15 @@ setSchemaTpl(
clearable: true,
unitOptions: config?.unitOptions ?? LayoutUnitOptions,
pipeIn: config?.pipeIn,
pipeOut: config?.pipeOut
// pipeOut: config?.pipeOut,
pipeOut: (value: string) => {
const curValue = parseInt(value);
if (curValue || curValue === 0) {
return value;
} else {
return undefined;
}
}
};
}
);
@ -681,7 +693,15 @@ setSchemaTpl(
clearable: true,
unitOptions: config?.unitOptions ?? LayoutUnitOptions,
pipeIn: config?.pipeIn,
pipeOut: config?.pipeOut
// pipeOut: config?.pipeOut,
pipeOut: (value: string) => {
const curValue = parseInt(value);
if (curValue || curValue === 0) {
return value;
} else {
return undefined;
}
}
};
}
);
@ -712,7 +732,15 @@ setSchemaTpl(
clearable: true,
unitOptions: config?.unitOptions ?? LayoutUnitOptions,
pipeIn: config?.pipeIn,
pipeOut: config?.pipeOut
// pipeOut: config?.pipeOut
pipeOut: (value: string) => {
const curValue = parseInt(value);
if (curValue || curValue === 0) {
return value;
} else {
return undefined;
}
}
};
}
);
@ -743,7 +771,15 @@ setSchemaTpl(
clearable: true,
unitOptions: config?.unitOptions ?? LayoutUnitOptions,
pipeIn: config?.pipeIn,
pipeOut: config?.pipeOut
// pipeOut: config?.pipeOut
pipeOut: (value: string) => {
const curValue = parseInt(value);
if (curValue || curValue === 0) {
return value;
} else {
return undefined;
}
}
};
}
);
@ -848,7 +884,15 @@ setSchemaTpl(
clearable: true,
unitOptions: config?.unitOptions ?? LayoutUnitOptions,
pipeIn: config?.pipeIn,
pipeOut: config?.pipeOut
// pipeOut: config?.pipeOut
pipeOut: (value: string) => {
const curValue = parseInt(value);
if (curValue || curValue === 0) {
return value;
} else {
return undefined;
}
}
};
}
);
@ -879,7 +923,15 @@ setSchemaTpl(
clearable: true,
unitOptions: config?.unitOptions ?? LayoutUnitOptions,
pipeIn: config?.pipeIn,
pipeOut: config?.pipeOut
// pipeOut: config?.pipeOut
pipeOut: (value: string) => {
const curValue = parseInt(value);
if (curValue || curValue === 0) {
return value;
} else {
return undefined;
}
}
};
}
);
@ -900,7 +952,7 @@ setSchemaTpl(
type: 'input-number',
label:
config?.label ||
tipedLabel('最小高度', '最小度即当前元素最小的垂直展示区域'),
tipedLabel('最小高度', '最小度即当前元素最小的垂直展示区域'),
name: config?.name || 'style.minHeight',
value: config?.value,
max: '${style.maxHeight | toInt}',
@ -910,7 +962,15 @@ setSchemaTpl(
clearable: true,
unitOptions: config?.unitOptions ?? LayoutUnitOptions,
pipeIn: config?.pipeIn,
pipeOut: config?.pipeOut
// pipeOut: config?.pipeOut
pipeOut: (value: string) => {
const curValue = parseInt(value);
if (curValue || curValue === 0) {
return value;
} else {
return undefined;
}
}
};
}
);

View File

@ -9,7 +9,7 @@ const path = require('path');
module.exports = {
mode: 'production', // development production
entry: {
index: ['./src/index.tsx'],
index: ['./src/index.tsx']
},
output: {
path: path.join(__dirname, 'lib'),
@ -48,9 +48,9 @@ module.exports = {
// webpackImporter: true, // 开启 / 关闭默认的 Webpack importer。默认为true所以不需要额外配置这个
sassOptions: {
// 正常情况下不需要这个配置项但有些情况下需要比如导入的sass文件包含 media queries等
includePaths: ['node_modules', '../../node_modules'],
},
},
includePaths: ['node_modules', '../../node_modules']
}
}
}
]
},