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

View File

@ -79,8 +79,7 @@ function transpileDynamicImportForCJS(options) {
} }
return { return {
left: left: 'Promise.resolve().then(function() {return new Promise(function(fullfill) {require([',
'Promise.resolve().then(function() {return new Promise(function(fullfill) {require([',
right: '], function(mod) {fullfill(tslib.__importStar(mod))})})})' right: '], function(mod) {fullfill(tslib.__importStar(mod))})})})'
}; };
@ -96,7 +95,6 @@ function transpileDynamicImportForCJS(options) {
} }
function getPlugins(format = 'esm') { function getPlugins(format = 'esm') {
const typeScriptOptions = { const typeScriptOptions = {
typescript: require('typescript'), typescript: require('typescript'),
sourceMap: false, sourceMap: false,
@ -142,11 +140,13 @@ function getPlugins(format = 'esm') {
Copyright 2018<%= moment().format('YYYY') > 2018 ? '-' + moment().format('YYYY') : null %> ${author} Copyright 2018<%= moment().format('YYYY') > 2018 ? '-' + moment().format('YYYY') : null %> ${author}
` `
}), }),
onRollupError((error) => { onRollupError(error => {
console.warn(`[构建异常]${error}`); console.warn(`[构建异常]${error}`);
// 构建异常时,删除 tsconfig.tsbuildinfo // 构建异常时,删除 tsconfig.tsbuildinfo
fs.unlink(path.resolve(__dirname, '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/SwitchMoreControl';
import './renderer/StatusControl'; import './renderer/StatusControl';
import './renderer/FormulaControl'; import './renderer/FormulaControl';
import './renderer/ExpressionFormulaControl';
import './renderer/textarea-formula/TextareaFormulaControl';
import './renderer/DateShortCutControl'; import './renderer/DateShortCutControl';
import './renderer/BadgeControl'; import './renderer/BadgeControl';
import './renderer/style-control/BoxModel'; import './renderer/style-control/BoxModel';

View File

@ -3399,7 +3399,7 @@ extendLocale('en-US', {
'6f420734edfaff00a8210a4c762a9207': '6f420734edfaff00a8210a4c762a9207':
'The maximum height is the display height with the most current elements', 'The maximum height is the display height with the most current elements',
'411f9d120093314cd38e6dd5cce398c6': '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': 'b31c6aaa78f8e24df665ce80ab5301e2':
'Scroll mode for setting the vertical direction', 'Scroll mode for setting the vertical direction',
'4fc0e68b093db41b45a4ea706fbe56f3': 'Center Display', '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', 'Turn on this option if you need to do additional data processing on the data in the returned results',
'7c583ecdf795ce4f1f40d7960ead9344': 'Default prompt text', '7c583ecdf795ce4f1f40d7960ead9344': 'Default prompt text',
'70941a02776496ec446f21f98ebf754e': 'Request successful', '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': '自动适配', '55becc96b40692cc9cf898b331d16976': '自动适配',
'ede82efb4a69c35743185c6c73ab771e': '最小宽度即当前元素最小的水平展示区域', 'ede82efb4a69c35743185c6c73ab771e': '最小宽度即当前元素最小的水平展示区域',
'6f420734edfaff00a8210a4c762a9207': '最大高度即当前元素最多的展示高度', '6f420734edfaff00a8210a4c762a9207': '最大高度即当前元素最多的展示高度',
'411f9d120093314cd38e6dd5cce398c6': '最小度即当前元素最小的垂直展示区域', '411f9d120093314cd38e6dd5cce398c6': '最小度即当前元素最小的垂直展示区域',
'ff9e9329fe186be342ef59ee711b9371': ' y轴滚动模式', 'ff9e9329fe186be342ef59ee711b9371': ' y轴滚动模式',
'b31c6aaa78f8e24df665ce80ab5301e2': '用于设置垂直方向的滚动模式', 'b31c6aaa78f8e24df665ce80ab5301e2': '用于设置垂直方向的滚动模式',
'4fc0e68b093db41b45a4ea706fbe56f3': '居中显示', '4fc0e68b093db41b45a4ea706fbe56f3': '居中显示',
@ -3090,5 +3090,43 @@ extendLocale('zh-CN', {
'如果需要对返回结果中的data做额外的数据处理请开启此选项', '如果需要对返回结果中的data做额外的数据处理请开启此选项',
'7c583ecdf795ce4f1f40d7960ead9344': '默认提示文案', '7c583ecdf795ce4f1f40d7960ead9344': '默认提示文案',
'70941a02776496ec446f21f98ebf754e': '请求成功', '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', formType: 'extend',
label: tipedLabel( label: tipedLabel(
'二次确认', '二次确认',
'点击后先询问用户,由手动确认后再执行动作,避免误触。可用<code>\\${xxx}</code>取值。' '点击后先询问用户,由手动确认后再执行动作,避免误触。可从数据域变量中取值。'
), ),
form: { form: {
body: [ body: [
{ {
name: 'confirmText', type: 'ae-textareaFormulaControl',
type: 'input-text', label: '确认内容',
label: '确认内容' mode: 'normal',
name: 'confirmText'
} }
] ]
} }
@ -154,19 +155,21 @@ export class ButtonPlugin extends BasePlugin {
form: { form: {
body: [ body: [
{ {
type: 'input-text', type: 'ae-textareaFormulaControl',
name: 'tooltip', name: 'tooltip',
mode: 'normal',
label: tipedLabel( label: tipedLabel(
'正常提示', '正常提示',
'正常状态下的提示内容,不填则不弹出提示。可用<code>\\${xxx}</code>取值。' '正常状态下的提示内容,不填则不弹出提示。可从数据域变量中取值。'
) )
}, },
{ {
type: 'input-text', type: 'ae-textareaFormulaControl',
name: 'disabledTip', name: 'disabledTip',
mode: 'normal',
label: tipedLabel( label: tipedLabel(
'禁用提示', '禁用提示',
'禁用状态下的提示内容,不填则弹出正常提示。可用<code>\\${xxx}</code>取值。' '禁用状态下的提示内容,不填则弹出正常提示。可从数据域变量中取值。'
), ),
clearValueOnHidden: true, clearValueOnHidden: true,
visibleOn: 'data.tooltipTrigger !== "focus"' visibleOn: 'data.tooltipTrigger !== "focus"'

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -13,7 +13,7 @@ import {
} from 'amis-editor-core'; } from 'amis-editor-core';
import {defaultValue, getSchemaTpl} from 'amis-editor-core'; import {defaultValue, getSchemaTpl} from 'amis-editor-core';
import {EditorNodeType} 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 {VRenderer} from 'amis-editor-core';
import {RegionWrapper as Region} from 'amis-editor-core'; import {RegionWrapper as Region} from 'amis-editor-core';
import {Icon} from 'amis-editor-core'; import {Icon} from 'amis-editor-core';

View File

@ -13,7 +13,7 @@ import {
} from 'amis-editor-core'; } from 'amis-editor-core';
import {defaultValue, getSchemaTpl} from 'amis-editor-core'; import {defaultValue, getSchemaTpl} from 'amis-editor-core';
import {EditorNodeType} 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 {VRenderer} from 'amis-editor-core';
import {RegionWrapper as Region} from 'amis-editor-core'; import {RegionWrapper as Region} from 'amis-editor-core';
import {JSONChangeInArray, JSONPipeIn, repeatArray} from 'amis-editor-core'; import {JSONChangeInArray, JSONPipeIn, repeatArray} from 'amis-editor-core';

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
import {registerEditorPlugin} from 'amis-editor-core'; import {registerEditorPlugin} from 'amis-editor-core';
import {FlexPluginBase} from './FlexPluginBase'; import {FlexPluginBase, defaultFlexColumnSchema} from './FlexPluginBase';
export default class Layout_fixed_bottom extends FlexPluginBase { export default class Layout_fixed_bottom extends FlexPluginBase {
name = '吸底容器'; name = '吸底容器';
@ -12,48 +12,10 @@ export default class Layout_fixed_bottom extends FlexPluginBase {
type: 'flex', type: 'flex',
className: 'p-1', className: 'p-1',
items: [ items: [
{ defaultFlexColumnSchema(),
type: 'wrapper', defaultFlexColumnSchema(),
size: 'xs', defaultFlexColumnSchema(),
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'
}
}
], ],
style: { style: {
position: 'fixed', position: 'fixed',

View File

@ -1,5 +1,5 @@
import {registerEditorPlugin} from 'amis-editor-core'; import {registerEditorPlugin} from 'amis-editor-core';
import {FlexPluginBase} from './FlexPluginBase'; import {FlexPluginBase, defaultFlexColumnSchema} from './FlexPluginBase';
export default class Layout_fixed_top extends FlexPluginBase { export default class Layout_fixed_top extends FlexPluginBase {
name = '吸顶容器'; name = '吸顶容器';
@ -12,39 +12,9 @@ export default class Layout_fixed_top extends FlexPluginBase {
type: 'flex', type: 'flex',
className: 'p-1', className: 'p-1',
items: [ items: [
{ defaultFlexColumnSchema(),
type: 'wrapper', defaultFlexColumnSchema(),
size: 'xs', defaultFlexColumnSchema(),
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
}
}
], ],
style: { style: {
position: 'fixed', position: 'fixed',

View File

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

View File

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

View File

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

View File

@ -295,6 +295,7 @@ export class TablePlugin extends BasePlugin {
description: '设置表格的选中项', description: '设置表格的选中项',
innerArgs: ['selected'], innerArgs: ['selected'],
schema: getArgsWrapper([ schema: getArgsWrapper([
/*
{ {
type: 'input-formula', type: 'input-formula',
variables: '${variables}', variables: '${variables}',
@ -305,6 +306,15 @@ export class TablePlugin extends BasePlugin {
name: 'selected', name: 'selected',
mode: 'horizontal' 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: '设置选中项', actionLabel: '设置选中项',
description: '设置表格的选中项', description: '设置表格的选中项',
schema: getArgsWrapper([ schema: getArgsWrapper([
/*
{ {
type: 'input-formula', type: 'input-formula',
variables: '${variables}', variables: '${variables}',
@ -303,6 +304,15 @@ export class Table2Plugin extends BasePlugin {
name: 'selected', name: 'selected',
mode: 'horizontal' 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> </div>
); );
}, },
schema: getArgsWrapper({ schema: getArgsWrapper(
type: 'input-formula', /*
variables: '${variables}', {
evalMode: false, type: 'input-formula',
variableMode: 'tabs', variables: '${variables}',
label: '激活项', evalMode: false,
size: 'lg', variableMode: 'tabs',
name: 'activeKey', label: '激活项',
mode: 'horizontal' 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: '高级', title: '高级',
body: [ body: [
{ {
type: 'ae-formulaControl', type: 'ae-expressionFormulaControl',
evalMode: true,
label: tipedLabel( label: tipedLabel(
'关联数据', '关联数据',
'可用<code>\\${xxx}</code>取值,根据该数据来动态重复渲染所配置的选项卡' '根据该数据来动态重复渲染所配置的选项卡'
), ),
name: 'source' name: 'source'
}, },

View File

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

View File

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

View File

@ -180,11 +180,12 @@ export class StatusControl extends React.Component<
] ]
}, },
{ {
type: 'ae-formulaControl', type: 'ae-expressionFormulaControl',
name: 'expression',
label: '表达式', label: '表达式',
name: 'expression',
placeholder: `请输入${label}条件`, 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 {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 {SchemaCollection} from 'amis/lib/Schema';
import type {IScopedContext} from 'amis-core'; import type {IScopedContext} from 'amis-core';
import type {FormSchema} from 'amis/lib/schema'; 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', className: 'action-panel-title',
visibleOn: 'data.actionType' visibleOn: 'data.actionType'
}, },
/*
{ {
name: 'expression', name: 'expression',
title: '', title: '',
@ -293,6 +294,17 @@ export default class ActionDialog extends React.Component<ActionDialogProp> {
size: 'lg', size: 'lg',
placeholder: '默认执行该动作', placeholder: '默认执行该动作',
visibleOn: 'data.actionType' 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', type: 'wrapper',
className: 'p-none', className: 'p-none',
body: [ body: [
/**
{ {
label: '页面地址', label: '页面地址',
type: 'input-formula', type: 'input-formula',
@ -64,6 +65,18 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
required: true, required: true,
visibleOn: 'data.actionType === "url"' 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', type: 'combo',
name: 'params', name: 'params',
@ -75,11 +88,10 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
{ {
name: 'key', name: 'key',
placeholder: '参数名', placeholder: '参数名',
type: 'input-text', type: 'input-text'
mode: 'inline',
size: 'xs'
}, },
{ /**
{
name: 'val', name: 'val',
placeholder: '参数值', placeholder: '参数值',
type: 'input-formula', type: 'input-formula',
@ -88,6 +100,14 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
variableMode: 'tabs', variableMode: 'tabs',
inputMode: 'input-group', inputMode: 'input-group',
size: 'xs' size: 'xs'
},
*/
{
type: 'ae-formulaControl',
variables: '${variables}',
name: 'val',
variableMode: 'tabs',
placeholder: '参数值'
} }
] ]
}, },
@ -306,23 +326,21 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
</div> </div>
); );
}, },
schema: getArgsWrapper({ schema: [
type: 'wrapper', {
className: 'p-none', type: 'button-group-select',
body: [ name: 'msgType',
{ label: '消息类型',
type: 'button-group-select', value: 'info',
name: 'msgType', required: true,
label: '消息类型', mode: 'horizontal',
value: 'info', options: Object.keys(MSG_TYPES).map(key => ({
required: true, label: MSG_TYPES[key],
mode: 'horizontal', value: key,
options: Object.keys(MSG_TYPES).map(key => ({ level: 'default'
label: MSG_TYPES[key], }))
value: key, },
level: 'default' /*
}))
},
{ {
name: 'msg', name: 'msg',
label: '消息内容', label: '消息内容',
@ -335,81 +353,114 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
size: 'lg', size: 'lg',
required: true required: true
}, },
{ */
name: 'title', {
type: 'input-formula', name: 'msg',
variables: '${variables}', label: '消息内容',
evalMode: false, type: 'ae-textareaFormulaControl',
variableMode: 'tabs', mode: 'horizontal',
inputMode: 'input-group', variables: '${variables}',
label: '标题内容', size: 'lg',
size: 'lg', required: true
mode: 'horizontal' },
/*
{
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'
}, },
{ valueType: 'number',
name: 'timeout', variables: '${variables}',
type: 'input-formula', size: 'lg',
variables: '${variables}', mode: 'horizontal'
evalMode: false, },
variableMode: 'tabs', {
inputMode: 'input-group', type: 'button-group-select',
label: '持续时间(ms)', name: 'position',
size: 'lg', value: 'top-right',
mode: 'horizontal' mode: 'horizontal',
}, label: '显示位置',
{ options: [
type: 'button-group-select', {
name: 'position', label: '左上',
value: 'top-right', value: 'top-left'
mode: 'horizontal', },
label: '显示位置',
options: [
{
label: '左上',
value: 'top-left'
},
{ {
label: '中上', label: '中上',
value: 'top-center' value: 'top-center'
}, },
{ {
label: '右上', label: '右上',
value: 'top-right' value: 'top-right'
}, },
{ {
label: '左下', label: '左下',
value: 'bottom-left' value: 'bottom-left'
}, },
{ {
label: '中下', label: '中下',
value: 'bottom-center' value: 'bottom-center'
}, },
{ {
label: '右下', label: '右下',
value: 'bottom-right' value: 'bottom-right'
} }
] ]
}, },
{ {
type: 'switch', type: 'switch',
name: 'closeButton', name: 'closeButton',
value: true, value: true,
label: '展示关闭按钮', label: '展示关闭按钮',
mode: 'horizontal' mode: 'horizontal'
}, },
{ {
type: 'switch', type: 'switch',
name: 'showIcon', name: 'showIcon',
value: true, value: true,
label: '展示图标', label: '展示图标',
mode: 'horizontal' mode: 'horizontal'
} }
] ]
})
} }
] ]
}, },
@ -833,6 +884,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
form.setValueByName('__valueInput', undefined); form.setValueByName('__valueInput', undefined);
} }
}, },
/*
{ {
name: '__valueInput', name: '__valueInput',
type: 'input-formula', type: 'input-formula',
@ -846,6 +898,17 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
mode: 'horizontal', mode: 'horizontal',
visibleOn: `data.__addParam && data.__customData && data.__containerType === "all" && data.actionType === "reload" && ${IS_DATA_CONTAINER}` 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', type: 'combo',
name: '__reloadParams', name: '__reloadParams',
@ -866,6 +929,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
valueField: 'value', valueField: 'value',
required: true required: true
}, },
/*
{ {
name: 'val', name: 'val',
type: 'input-formula', type: 'input-formula',
@ -875,6 +939,13 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
variableMode: 'tabs', variableMode: 'tabs',
inputMode: 'input-group' 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}` 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', valueField: 'value',
required: true required: true
}, },
/*
{ {
name: 'val', name: 'val',
type: 'input-formula', type: 'input-formula',
@ -1044,6 +1116,13 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
variableMode: 'tabs', variableMode: 'tabs',
inputMode: 'input-group' inputMode: 'input-group'
} }
*/
{
name: 'val',
type: 'ae-formulaControl',
variables: '${variables}',
placeholder: '字段值'
}
], ],
visibleOn: `${IS_DATA_CONTAINER} && data.__containerType === 'appoint' || data.__comboType === 'appoint'` visibleOn: `${IS_DATA_CONTAINER} && data.__containerType === 'appoint' || data.__comboType === 'appoint'`
}, },
@ -1084,6 +1163,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
required: true, required: true,
visibleOn: `data.__rendererName` visibleOn: `data.__rendererName`
}, },
/*
{ {
name: 'val', name: 'val',
type: 'input-formula', type: 'input-formula',
@ -1092,11 +1172,18 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
variableMode: 'tabs', variableMode: 'tabs',
inputMode: 'input-group' inputMode: 'input-group'
} }
*/
{
name: 'val',
type: 'ae-formulaControl',
variables: '${variables}'
}
] ]
} }
], ],
visibleOn: `data.__rendererName === 'combo' && data.__comboType === 'all'` visibleOn: `data.__rendererName === 'combo' && data.__comboType === 'all'`
}, },
/*
{ {
name: '__valueInput', name: '__valueInput',
type: 'input-formula', type: 'input-formula',
@ -1110,6 +1197,19 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
visibleOn: `(${IS_DATA_CONTAINER} || ${SHOW_SELECT_PROP}) && data.__containerType === 'all'`, visibleOn: `(${IS_DATA_CONTAINER} || ${SHOW_SELECT_PROP}) && data.__containerType === 'all'`,
required: true 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', name: '__valueInput',
type: 'input-formula', type: 'input-formula',
@ -1123,6 +1223,17 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
visibleOn: `data.__rendererName && !${IS_DATA_CONTAINER} && data.__rendererName !== 'combo'`, visibleOn: `data.__rendererName && !${IS_DATA_CONTAINER} && data.__rendererName !== 'combo'`,
required: true 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', type: 'wrapper',
className: 'p-none', className: 'p-none',
body: [ body: [
{ /*
{
name: 'content', name: 'content',
type: 'input-formula', type: 'input-formula',
variables: '${variables}', variables: '${variables}',
@ -1244,6 +1356,17 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
visibleOn: 'data.actionType === "copy"', visibleOn: 'data.actionType === "copy"',
required: true required: true
}, },
*/
{
name: 'content',
label: '内容模板',
type: 'ae-textareaFormulaControl',
variables: '${variables}',
mode: 'horizontal',
size: 'lg',
visibleOn: 'data.actionType === "copy"',
required: true
},
{ {
type: 'select', type: 'select',
name: 'copyFormat', name: 'copyFormat',
@ -1281,6 +1404,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
lineNumbers: 'off', lineNumbers: 'off',
glyphMargin: false, glyphMargin: false,
tabSize: 2, tabSize: 2,
fontSize: '12px',
wordWrap: 'on', wordWrap: 'on',
lineDecorationsWidth: 0, lineDecorationsWidth: 0,
lineNumbersMinChars: 0, lineNumbersMinChars: 0,

View File

@ -226,6 +226,7 @@ export const COMMON_ACTION_SCHEMA_MAP: {
valueField: 'value', valueField: 'value',
required: true required: true
}, },
/*
{ {
name: 'val', name: 'val',
type: 'input-formula', type: 'input-formula',
@ -235,6 +236,13 @@ export const COMMON_ACTION_SCHEMA_MAP: {
variableMode: 'tabs', variableMode: 'tabs',
inputMode: 'input-group' inputMode: 'input-group'
} }
*/
{
name: 'val',
type: 'ae-formulaControl',
variables: '${variables}',
placeholder: '变量值'
}
], ],
visibleOn: `${IS_DATA_CONTAINER}` visibleOn: `${IS_DATA_CONTAINER}`
}, },
@ -269,6 +277,7 @@ export const COMMON_ACTION_SCHEMA_MAP: {
type: 'input-text', type: 'input-text',
required: true required: true
}, },
/*
{ {
name: 'val', name: 'val',
type: 'input-formula', type: 'input-formula',
@ -277,11 +286,18 @@ export const COMMON_ACTION_SCHEMA_MAP: {
variableMode: 'tabs', variableMode: 'tabs',
inputMode: 'input-group' inputMode: 'input-group'
} }
*/
{
name: 'val',
type: 'ae-formulaControl',
variables: '${variables}'
}
] ]
} }
], ],
visibleOn: `data.__rendererName === 'combo'` visibleOn: `data.__rendererName === 'combo'`
}, },
/*
{ {
name: '__valueInput', name: '__valueInput',
type: 'input-formula', type: 'input-formula',
@ -295,6 +311,17 @@ export const COMMON_ACTION_SCHEMA_MAP: {
visibleOn: `!${IS_DATA_CONTAINER} && data.__rendererName !== 'combo'`, visibleOn: `!${IS_DATA_CONTAINER} && data.__rendererName !== 'combo'`,
required: true 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, defaultValue,
isObject, isObject,
tipedLabel, tipedLabel,
DSField DSField,
EditorManager
} from 'amis-editor-core'; } from 'amis-editor-core';
import {remarkTpl} from '../component/BaseControl'; import {remarkTpl} from '../component/BaseControl';
import {SchemaObject} from 'amis/lib/Schema'; import {SchemaObject} from 'amis/lib/Schema';
import flatten from 'lodash/flatten'; import flatten from 'lodash/flatten';
import _ from 'lodash';
import {InputComponentName} from '../component/InputComponentName'; import {InputComponentName} from '../component/InputComponentName';
import {FormulaDateType} from '../renderer/FormulaControl';
import {VariableItem} from 'amis-ui/lib/components/formula/Editor';
/** /**
* @deprecated switch * @deprecated switch
@ -345,60 +349,72 @@ setSchemaTpl(
mode?: string; // 自定义展示默认值,上下展示: vertical, 左右展示: horizontal mode?: string; // 自定义展示默认值,上下展示: vertical, 左右展示: horizontal
label?: string; // 表单项 label label?: string; // 表单项 label
name?: string; // 表单项 name name?: string; // 表单项 name
header?: string; // 表达式弹窗标题
placeholder?: string; // 表达式自定义渲染 占位符
rendererSchema?: any; rendererSchema?: any;
rendererWrapper?: boolean; // 自定义渲染器 是否需要浅色边框包裹 rendererWrapper?: boolean; // 自定义渲染器 是否需要浅色边框包裹
needDeleteValue?: boolean; // 是否需要剔除默认值 needDeleteProps?: string[]; // 需要剔除的其他属性,默认 deleteProps 中包含一些通用属性
useSelectMode?: boolean; // 是否使用Select选择设置模式需要确保 rendererSchema.options 不为 undefined useSelectMode?: boolean; // 是否使用Select选择设置模式需要确保 rendererSchema.options 不为 undefined
valueType?: string; // 用于设置期望数值类型 valueType?: string; // 用于设置期望数值类型
visibleOn?: 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; const {
if ( rendererSchema,
config?.useSelectMode && useSelectMode,
curRendererSchema && mode,
curRendererSchema.options visibleOn,
) { label,
name,
rendererWrapper,
needDeleteProps,
valueType,
header,
placeholder,
DateTimeType,
variables,
requiredDataPropsVariables,
variableMode,
...rest
} = config || {};
let curRendererSchema = rendererSchema;
if (useSelectMode && curRendererSchema && curRendererSchema.options) {
curRendererSchema = { curRendererSchema = {
...curRendererSchema, ...curRendererSchema,
type: 'select' type: 'select'
}; };
} }
if (config?.mode === 'vertical') { return {
// 上下展示,可避免 自定义渲染器 出现挤压 type: 'group',
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: 'ae-formulaControl', mode: mode === 'vertical' ? 'vertical' : 'horizontal',
label: config?.label || '默认值', visibleOn,
name: config?.name || 'value', body: [
rendererSchema: curRendererSchema, {
rendererWrapper: config?.rendererWrapper, type: 'ae-formulaControl',
needDeleteValue: config?.needDeleteValue, label: label ?? '默认值',
valueType: config?.valueType, name: name || 'value',
visibleOn: config?.visibleOn, rendererWrapper,
evalMode: config?.evalMode ?? false // 默认需要${}包裹变量 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', { setSchemaTpl('menuTpl', {
type: 'ae-formulaControl', type: 'ae-textareaFormulaControl',
label: tipedLabel('模板', '选项渲染模板支持JSX变量使用\\${xx}'), mode: 'normal',
label: tipedLabel('模板', '自定义选项渲染模板支持JSX、数据域变量使用'),
name: 'menuTpl' name: 'menuTpl'
}); });
@ -1000,6 +1087,7 @@ setSchemaTpl('badge', {
type: 'ae-badge' type: 'ae-badge'
}); });
// 暂未使用
setSchemaTpl('formulaControl', (schema: object = {}) => { setSchemaTpl('formulaControl', (schema: object = {}) => {
return { return {
type: 'ae-formulaControl', type: 'ae-formulaControl',
@ -1061,7 +1149,8 @@ setSchemaTpl('app-page-args', {
valueField: 'value', valueField: 'value',
required: true required: true
}, },
{ /*
{
name: 'val', name: 'val',
type: 'input-formula', type: 'input-formula',
placeholder: '参数值', placeholder: '参数值',
@ -1070,6 +1159,13 @@ setSchemaTpl('app-page-args', {
variableMode: 'tabs', variableMode: 'tabs',
inputMode: 'input-group' 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') { if (value === 'static') {
form.setValueByName('style.inset', undefined); form.setValueByName('style.inset', undefined);
form.setValueByName('style.zIndex', undefined); form.setValueByName('style.zIndex', undefined);
form.setValueByName('originPosition', undefined);
} else if (value === 'fixed' || value === 'absolute') { } else if (value === 'fixed' || value === 'absolute') {
// 默认使用右下角进行相对定位 // 默认使用右下角进行相对定位
form.setValueByName('style.inset', 'auto 50px 50px auto'); 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: [ options: [
@ -138,7 +143,6 @@ setSchemaTpl(
}; };
}, },
pipeOut: (value: any) => { pipeOut: (value: any) => {
console.log('pipeOut:', value);
return `${value.insetTop ?? 'auto'} ${value.insetRight ?? 'auto'} ${ return `${value.insetTop ?? 'auto'} ${value.insetRight ?? 'auto'} ${
value.insetBottom ?? 'auto' value.insetBottom ?? 'auto'
} ${value.insetLeft ?? 'auto'}`; } ${value.insetLeft ?? 'auto'}`;
@ -246,7 +250,7 @@ setSchemaTpl(
}, },
options: [ options: [
{ {
label: '默认', label: '块级(默认)',
value: 'block' value: 'block'
}, },
{ {
@ -508,7 +512,7 @@ setSchemaTpl(
pipeOut: config?.pipeOut, pipeOut: config?.pipeOut,
options: [ options: [
{ {
label: '默认(不换行)', label: '不换行(默认)',
value: 'nowrap' value: 'nowrap'
}, },
{ {
@ -588,7 +592,15 @@ setSchemaTpl(
clearable: true, clearable: true,
unitOptions: config?.unitOptions ?? LayoutUnitOptions, unitOptions: config?.unitOptions ?? LayoutUnitOptions,
pipeIn: config?.pipeIn, 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, clearable: true,
unitOptions: config?.unitOptions ?? LayoutUnitOptions, unitOptions: config?.unitOptions ?? LayoutUnitOptions,
pipeIn: config?.pipeIn, 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, clearable: true,
unitOptions: config?.unitOptions ?? LayoutUnitOptions, unitOptions: config?.unitOptions ?? LayoutUnitOptions,
pipeIn: config?.pipeIn, 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, clearable: true,
unitOptions: config?.unitOptions ?? LayoutUnitOptions, unitOptions: config?.unitOptions ?? LayoutUnitOptions,
pipeIn: config?.pipeIn, 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, clearable: true,
unitOptions: config?.unitOptions ?? LayoutUnitOptions, unitOptions: config?.unitOptions ?? LayoutUnitOptions,
pipeIn: config?.pipeIn, 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, clearable: true,
unitOptions: config?.unitOptions ?? LayoutUnitOptions, unitOptions: config?.unitOptions ?? LayoutUnitOptions,
pipeIn: config?.pipeIn, 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', type: 'input-number',
label: label:
config?.label || config?.label ||
tipedLabel('最小高度', '最小度即当前元素最小的垂直展示区域'), tipedLabel('最小高度', '最小度即当前元素最小的垂直展示区域'),
name: config?.name || 'style.minHeight', name: config?.name || 'style.minHeight',
value: config?.value, value: config?.value,
max: '${style.maxHeight | toInt}', max: '${style.maxHeight | toInt}',
@ -910,7 +962,15 @@ setSchemaTpl(
clearable: true, clearable: true,
unitOptions: config?.unitOptions ?? LayoutUnitOptions, unitOptions: config?.unitOptions ?? LayoutUnitOptions,
pipeIn: config?.pipeIn, 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 = { module.exports = {
mode: 'production', // development production mode: 'production', // development production
entry: { entry: {
index: ['./src/index.tsx'], index: ['./src/index.tsx']
}, },
output: { output: {
path: path.join(__dirname, 'lib'), path: path.join(__dirname, 'lib'),
@ -48,9 +48,9 @@ module.exports = {
// webpackImporter: true, // 开启 / 关闭默认的 Webpack importer。默认为true所以不需要额外配置这个 // webpackImporter: true, // 开启 / 关闭默认的 Webpack importer。默认为true所以不需要额外配置这个
sassOptions: { sassOptions: {
// 正常情况下不需要这个配置项但有些情况下需要比如导入的sass文件包含 media queries等 // 正常情况下不需要这个配置项但有些情况下需要比如导入的sass文件包含 media queries等
includePaths: ['node_modules', '../../node_modules'], includePaths: ['node_modules', '../../node_modules']
}, }
}, }
} }
] ]
}, },