mirror of
https://gitee.com/baidu/amis.git
synced 2024-12-15 17:31:18 +08:00
Merge branch 'pre-release' into feature-new-combo
This commit is contained in:
commit
e12f1196f5
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "amis-editor",
|
||||
"version": "5.2.1-beta.7",
|
||||
"version": "5.2.1-beta.15",
|
||||
"description": "amis 可视化编辑器",
|
||||
"main": "lib/index.js",
|
||||
"module": "esm/index.js",
|
||||
@ -46,17 +46,17 @@
|
||||
"@types/lodash": "^4.14.76",
|
||||
"@types/node": "^14.0.24",
|
||||
"@types/qs": "^6.5.1",
|
||||
"@types/react": "^16.9.11",
|
||||
"@types/react-dom": "^16.9.4",
|
||||
"@types/react": "^18.0.24",
|
||||
"@types/react-dom": "^18.0.8",
|
||||
"@types/react-router": "^4.0.16",
|
||||
"@types/react-router-dom": "^5.1.7",
|
||||
"@types/sortablejs": "^1.10.7",
|
||||
"@types/tinycolor2": "^1.4.3",
|
||||
"ajv": "^8.8.2",
|
||||
"amis": "2.4.0",
|
||||
"amis-core": "2.4.0",
|
||||
"amis-formula": "2.4.0",
|
||||
"amis-ui": "2.4.0",
|
||||
"amis": "2.4.1-beta.0",
|
||||
"amis-core": "2.4.1-beta.0",
|
||||
"amis-formula": "2.4.1-beta.0",
|
||||
"amis-ui": "2.4.1-beta.0",
|
||||
"axios": "0.21.1",
|
||||
"concurrently": "^6.2.0",
|
||||
"css-loader": "^6.2.0",
|
||||
@ -65,8 +65,8 @@
|
||||
"lint-staged": "^12.1.2",
|
||||
"mini-css-extract-plugin": "^2.3.0",
|
||||
"prettier": "^2.2.1",
|
||||
"react": "^16.14.0",
|
||||
"react-dom": "^16.14.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-overlays": "5.1.1",
|
||||
"react-router": "5.2.0",
|
||||
"react-router-dom": "5.2.0",
|
||||
|
@ -79,8 +79,7 @@ function transpileDynamicImportForCJS(options) {
|
||||
}
|
||||
|
||||
return {
|
||||
left:
|
||||
'Promise.resolve().then(function() {return new Promise(function(fullfill) {require([',
|
||||
left: 'Promise.resolve().then(function() {return new Promise(function(fullfill) {require([',
|
||||
right: '], function(mod) {fullfill(tslib.__importStar(mod))})})})'
|
||||
};
|
||||
|
||||
@ -96,7 +95,6 @@ function transpileDynamicImportForCJS(options) {
|
||||
}
|
||||
|
||||
function getPlugins(format = 'esm') {
|
||||
|
||||
const typeScriptOptions = {
|
||||
typescript: require('typescript'),
|
||||
sourceMap: false,
|
||||
@ -142,11 +140,13 @@ function getPlugins(format = 'esm') {
|
||||
Copyright 2018<%= moment().format('YYYY') > 2018 ? '-' + moment().format('YYYY') : null %> ${author}
|
||||
`
|
||||
}),
|
||||
onRollupError((error) => {
|
||||
onRollupError(error => {
|
||||
console.warn(`[构建异常]${error}`);
|
||||
// 构建异常时,删除 tsconfig.tsbuildinfo
|
||||
fs.unlink(path.resolve(__dirname, 'tsconfig.tsbuildinfo'), () => {
|
||||
console.info('[构建异常]已自动删除tsconfig.tsbuildinfo,请重试构建命令。');
|
||||
console.info(
|
||||
'[构建异常]已自动删除tsconfig.tsbuildinfo,请重试构建命令。'
|
||||
);
|
||||
});
|
||||
})
|
||||
];
|
||||
|
@ -155,6 +155,8 @@ import './renderer/ValidationItem';
|
||||
import './renderer/SwitchMoreControl';
|
||||
import './renderer/StatusControl';
|
||||
import './renderer/FormulaControl';
|
||||
import './renderer/ExpressionFormulaControl';
|
||||
import './renderer/textarea-formula/TextareaFormulaControl';
|
||||
import './renderer/DateShortCutControl';
|
||||
import './renderer/BadgeControl';
|
||||
import './renderer/style-control/BoxModel';
|
||||
|
@ -3399,7 +3399,7 @@ extendLocale('en-US', {
|
||||
'6f420734edfaff00a8210a4c762a9207':
|
||||
'The maximum height is the display height with the most current elements',
|
||||
'411f9d120093314cd38e6dd5cce398c6':
|
||||
'The minimum width is the smallest vertical display area of the current element',
|
||||
'The minimum height is the smallest vertical display area of the current element',
|
||||
'b31c6aaa78f8e24df665ce80ab5301e2':
|
||||
'Scroll mode for setting the vertical direction',
|
||||
'4fc0e68b093db41b45a4ea706fbe56f3': 'Center Display',
|
||||
@ -3498,5 +3498,46 @@ extendLocale('en-US', {
|
||||
'Turn on this option if you need to do additional data processing on the data in the returned results',
|
||||
'7c583ecdf795ce4f1f40d7960ead9344': 'Default prompt text',
|
||||
'70941a02776496ec446f21f98ebf754e': 'Request successful',
|
||||
'f50bf418323ee425eca7208c09a4577e': 'Request failed'
|
||||
'f50bf418323ee425eca7208c09a4577e': 'Request failed',
|
||||
'f3dc08e3617d1e19cf8135be4412a80b':
|
||||
'After clicking, ask the user first, and then execute the action after manual confirmation to avoid accidental touch. Values can be taken from data field variables.',
|
||||
'faa29265819714253843e23437b9193e':
|
||||
'The prompt content under normal status. If it is not filled, no prompt will pop up. Values can be taken from data field variables.',
|
||||
'f855f46ce6146aa17a9ed423da16bfa2':
|
||||
'Prompt content in disabled status. If it is not filled in, a normal prompt will pop up. Values can be taken from data field variables.',
|
||||
'037becbe8bff2f8838d141cc7b6b2df7':
|
||||
'Support such relative value usage as:<code>now,+3 days, - 2weeks,+1hour,+2years</code>, etc. (minute | min | hour | day | week | month | year | weekday | second | millisecond)',
|
||||
'8f7ae284d0039fe05b9f57fd5ae3ede9': 'Please select a static value',
|
||||
'98229308e2e9484583fde4ae363a979f': 'Expression or relative value',
|
||||
'6b3c2a07db1bb3c229bbc5df48068792':
|
||||
'Support such relative value usage as:<code>now,+3 days, - 2weeks,+1 hour,+2 years</code>(minute | hour | day | week | month | year | weekday | second | millisecond)',
|
||||
'dcc94ea1715bd502c709c5d5092e9c82':
|
||||
'Support such relative value usage as:<code>3days, 2weeks, 1hour, 2years</code>(minute | hour | day | week | month | year | weekday | second | millisecond)',
|
||||
'13ce82d026daa5a30e50105bd2a204a6': 'Please enter a relative value',
|
||||
'31c29c46536a5007522032d2a42db56a': 'Numerical template',
|
||||
'a6a41d1bfb5896210eb527d183a07958':
|
||||
'Value rendering template, which supports the use of JSX and data field variables. The default is ${value}%',
|
||||
'eadd1d64cd6ceb2c50554281cd2d3be0':
|
||||
'Dynamically re render the configured tabs based on this data',
|
||||
'0d9d899edb456e8806a99850e9c38212': 'Configured Expression',
|
||||
'fbb96f7ea104d34fc4b7bd625d892c45': 'Click Write Expression',
|
||||
'e0c7ac5eb397512fdbe71600baa09dab': 'Please enter a static value',
|
||||
'48942ef507ea38d8ead03f8bfdffae5a': 'Relative value configured',
|
||||
'49041f245018a6d799fee3c6f177c782': 'Exit full screen',
|
||||
'185926bf986c784d03a9a73102da6542': 'Full screen',
|
||||
'891ec6336d4243714c25eecb2f8f774a': 'Option Text',
|
||||
'fb7ea2b05ca7328ee16a562d90c2eb96': 'Option field',
|
||||
'1ca87f0171481e27d94e81b477150b7d': 'Option Template',
|
||||
'd6ecb32a380c91887a9346653c2427e9':
|
||||
'Customized option rendering template, supporting the use of JSX and data field variables',
|
||||
'cb048b2d8426afd464dc01b72c446342': 'Block level (default)',
|
||||
'3b6e8d54b7b2ae890d5357b7eaaeaaf2': 'No line breaks (default)',
|
||||
'0611733b53e0098e6fd880bd44b2806f':
|
||||
'The minimum height is the smallest vertical display area of the current element',
|
||||
'03bfb834c8a5fef58d885e448a4e13b4': 'Virtual List Threshold',
|
||||
'50437e080edc71ab624c93d419472919':
|
||||
'When the number of options exceeds the threshold, the virtual list is opened to optimize performance',
|
||||
'02b9880e1d2df8a07e90e9878080c739': 'Option Height',
|
||||
'a3f66655c3d2bcfecc6afba0e4424460':
|
||||
'Height of each option when virtual list is turned on'
|
||||
});
|
||||
|
@ -3002,7 +3002,7 @@ extendLocale('zh-CN', {
|
||||
'55becc96b40692cc9cf898b331d16976': '自动适配',
|
||||
'ede82efb4a69c35743185c6c73ab771e': '最小宽度即当前元素最小的水平展示区域',
|
||||
'6f420734edfaff00a8210a4c762a9207': '最大高度即当前元素最多的展示高度',
|
||||
'411f9d120093314cd38e6dd5cce398c6': '最小宽度即当前元素最小的垂直展示区域',
|
||||
'411f9d120093314cd38e6dd5cce398c6': '最小高度即当前元素最小的垂直展示区域',
|
||||
'ff9e9329fe186be342ef59ee711b9371': ' y轴滚动模式',
|
||||
'b31c6aaa78f8e24df665ce80ab5301e2': '用于设置垂直方向的滚动模式',
|
||||
'4fc0e68b093db41b45a4ea706fbe56f3': '居中显示',
|
||||
@ -3090,5 +3090,43 @@ extendLocale('zh-CN', {
|
||||
'如果需要对返回结果中的data做额外的数据处理,请开启此选项',
|
||||
'7c583ecdf795ce4f1f40d7960ead9344': '默认提示文案',
|
||||
'70941a02776496ec446f21f98ebf754e': '请求成功',
|
||||
'f50bf418323ee425eca7208c09a4577e': '请求失败'
|
||||
'f50bf418323ee425eca7208c09a4577e': '请求失败',
|
||||
'f3dc08e3617d1e19cf8135be4412a80b':
|
||||
'点击后先询问用户,由手动确认后再执行动作,避免误触。可从数据域变量中取值。',
|
||||
'faa29265819714253843e23437b9193e':
|
||||
'正常状态下的提示内容,不填则不弹出提示。可从数据域变量中取值。',
|
||||
'f855f46ce6146aa17a9ed423da16bfa2':
|
||||
'禁用状态下的提示内容,不填则弹出正常提示。可从数据域变量中取值。',
|
||||
'037becbe8bff2f8838d141cc7b6b2df7':
|
||||
'支持例如: <code>now、+3days、-2weeks、+1hour、+2years</code> 等(minute|min|hour|day|week|month|year|weekday|second|millisecond)这种相对值用法',
|
||||
'8f7ae284d0039fe05b9f57fd5ae3ede9': '请选择静态值',
|
||||
'98229308e2e9484583fde4ae363a979f': '表达式或相对值',
|
||||
'6b3c2a07db1bb3c229bbc5df48068792':
|
||||
'支持例如: <code>now、+3days、-2weeks、+1hour、+2years</code> 等(minute|hour|day|week|month|year|weekday|second|millisecond)这种相对值用法',
|
||||
'dcc94ea1715bd502c709c5d5092e9c82':
|
||||
'支持例如: <code>3days、2weeks、1hour、2years</code> 等(minute|hour|day|week|month|year|weekday|second|millisecond)这种相对值用法',
|
||||
'13ce82d026daa5a30e50105bd2a204a6': '请输入相对值',
|
||||
'31c29c46536a5007522032d2a42db56a': '数值模板',
|
||||
'a6a41d1bfb5896210eb527d183a07958':
|
||||
'值渲染模板,支持JSX、数据域变量使用, 默认 ${value}%',
|
||||
'eadd1d64cd6ceb2c50554281cd2d3be0': '根据该数据来动态重复渲染所配置的选项卡',
|
||||
'0d9d899edb456e8806a99850e9c38212': '已配置表达式',
|
||||
'fbb96f7ea104d34fc4b7bd625d892c45': '点击编写表达式',
|
||||
'e0c7ac5eb397512fdbe71600baa09dab': '请输入静态值',
|
||||
'48942ef507ea38d8ead03f8bfdffae5a': '已配置相对值',
|
||||
'49041f245018a6d799fee3c6f177c782': '退出全屏',
|
||||
'185926bf986c784d03a9a73102da6542': '全屏',
|
||||
'891ec6336d4243714c25eecb2f8f774a': '选项文本',
|
||||
'fb7ea2b05ca7328ee16a562d90c2eb96': '选项字段',
|
||||
'1ca87f0171481e27d94e81b477150b7d': '选项模板',
|
||||
'd6ecb32a380c91887a9346653c2427e9':
|
||||
'自定义选项渲染模板,支持JSX、数据域变量使用',
|
||||
'cb048b2d8426afd464dc01b72c446342': '块级(默认)',
|
||||
'3b6e8d54b7b2ae890d5357b7eaaeaaf2': '不换行(默认)',
|
||||
'0611733b53e0098e6fd880bd44b2806f': '最小高度即当前元素最小的垂直展示区域',
|
||||
'03bfb834c8a5fef58d885e448a4e13b4': '虚拟列表阈值',
|
||||
'50437e080edc71ab624c93d419472919':
|
||||
'当选项数量超过阈值后,会开启虚拟列表以优化性能',
|
||||
'02b9880e1d2df8a07e90e9878080c739': '选项高度',
|
||||
'a3f66655c3d2bcfecc6afba0e4424460': '开启虚拟列表时每个选项的高度'
|
||||
});
|
||||
|
@ -132,14 +132,15 @@ export class ButtonPlugin extends BasePlugin {
|
||||
formType: 'extend',
|
||||
label: tipedLabel(
|
||||
'二次确认',
|
||||
'点击后先询问用户,由手动确认后再执行动作,避免误触。可用<code>\\${xxx}</code>取值。'
|
||||
'点击后先询问用户,由手动确认后再执行动作,避免误触。可从数据域变量中取值。'
|
||||
),
|
||||
form: {
|
||||
body: [
|
||||
{
|
||||
name: 'confirmText',
|
||||
type: 'input-text',
|
||||
label: '确认内容'
|
||||
type: 'ae-textareaFormulaControl',
|
||||
label: '确认内容',
|
||||
mode: 'normal',
|
||||
name: 'confirmText'
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -154,19 +155,21 @@ export class ButtonPlugin extends BasePlugin {
|
||||
form: {
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
type: 'ae-textareaFormulaControl',
|
||||
name: 'tooltip',
|
||||
mode: 'normal',
|
||||
label: tipedLabel(
|
||||
'正常提示',
|
||||
'正常状态下的提示内容,不填则不弹出提示。可用<code>\\${xxx}</code>取值。'
|
||||
'正常状态下的提示内容,不填则不弹出提示。可从数据域变量中取值。'
|
||||
)
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
type: 'ae-textareaFormulaControl',
|
||||
name: 'disabledTip',
|
||||
mode: 'normal',
|
||||
label: tipedLabel(
|
||||
'禁用提示',
|
||||
'禁用状态下的提示内容,不填则弹出正常提示。可用<code>\\${xxx}</code>取值。'
|
||||
'禁用状态下的提示内容,不填则弹出正常提示。可从数据域变量中取值。'
|
||||
),
|
||||
clearValueOnHidden: true,
|
||||
visibleOn: 'data.tooltipTrigger !== "focus"'
|
||||
|
@ -1326,16 +1326,28 @@ export class CRUDPlugin extends BasePlugin {
|
||||
</div>
|
||||
);
|
||||
},
|
||||
schema: getArgsWrapper({
|
||||
type: 'input-formula',
|
||||
variables: '${variables}',
|
||||
evalMode: false,
|
||||
variableMode: 'tabs',
|
||||
label: '查询条件',
|
||||
size: 'md',
|
||||
name: 'query',
|
||||
mode: 'horizontal'
|
||||
})
|
||||
schema: getArgsWrapper(
|
||||
/*
|
||||
{
|
||||
type: 'input-formula',
|
||||
variables: '${variables}',
|
||||
evalMode: false,
|
||||
variableMode: 'tabs',
|
||||
label: '查询条件',
|
||||
size: 'md',
|
||||
name: 'query',
|
||||
mode: 'horizontal'
|
||||
}
|
||||
*/
|
||||
{
|
||||
name: 'query',
|
||||
label: '查询条件',
|
||||
type: 'ae-formulaControl',
|
||||
variables: '${variables}',
|
||||
size: 'md',
|
||||
mode: 'horizontal'
|
||||
}
|
||||
)
|
||||
},
|
||||
// {
|
||||
// actionType: 'resetQuery',
|
||||
|
@ -17,7 +17,11 @@ export class ContainerPlugin extends BasePlugin {
|
||||
pluginIcon = 'container-plugin';
|
||||
scaffold = {
|
||||
type: 'container',
|
||||
body: []
|
||||
body: [],
|
||||
style: {
|
||||
position: 'static',
|
||||
display: 'block'
|
||||
}
|
||||
};
|
||||
previewSchema = {
|
||||
...this.scaffold
|
||||
|
@ -61,15 +61,17 @@ export class DropDownButtonPlugin extends BasePlugin {
|
||||
body: [
|
||||
{
|
||||
children: (
|
||||
<Button
|
||||
level="info"
|
||||
size="sm"
|
||||
className="m-b-sm"
|
||||
block
|
||||
onClick={this.editDetail.bind(this, context.id)}
|
||||
>
|
||||
配置下拉按钮集合
|
||||
</Button>
|
||||
<div className="mb-3">
|
||||
<Button
|
||||
level="info"
|
||||
size="sm"
|
||||
className="m-b-sm"
|
||||
block
|
||||
onClick={this.editDetail.bind(this, context.id)}
|
||||
>
|
||||
配置下拉按钮集合
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
|
@ -136,6 +136,7 @@ export class CheckboxControlPlugin extends BasePlugin {
|
||||
...context?.schema,
|
||||
type: 'switch'
|
||||
},
|
||||
needDeleteProps: ['option'],
|
||||
label: '默认勾选',
|
||||
rendererWrapper: true, // 浅色线框包裹一下,增加边界感
|
||||
valueType: 'boolean',
|
||||
|
@ -141,11 +141,13 @@ export class CodeEditorControlPlugin extends BasePlugin {
|
||||
searchable: true,
|
||||
options: availableLanguages.concat()
|
||||
},
|
||||
{
|
||||
type: 'textarea',
|
||||
name: 'value',
|
||||
label: '默认值'
|
||||
},
|
||||
|
||||
getSchemaTpl('valueFormula', {
|
||||
rendererSchema: {
|
||||
type: 'textarea'
|
||||
},
|
||||
mode: 'vertical' // 改成上下展示模式
|
||||
}),
|
||||
getSchemaTpl('switch', {
|
||||
label: '可全屏',
|
||||
name: 'allowFullscreen',
|
||||
|
@ -147,19 +147,24 @@ export class DiffEditorControlPlugin extends BasePlugin {
|
||||
searchable: true,
|
||||
options: availableLanguages.concat()
|
||||
},
|
||||
{
|
||||
type: 'textarea',
|
||||
name: 'diffValue',
|
||||
|
||||
getSchemaTpl('valueFormula', {
|
||||
rendererSchema: {
|
||||
type: 'textarea',
|
||||
value: context?.schema.diffValue
|
||||
},
|
||||
label: '左侧默认值',
|
||||
pipeOut: valuePipeOut,
|
||||
placeholder: '支持使用 ${xxx} 来获取变量'
|
||||
},
|
||||
{
|
||||
type: 'textarea',
|
||||
name: 'value',
|
||||
name: 'diffValue',
|
||||
mode: 'vertical' // 改成上下展示模式
|
||||
}),
|
||||
getSchemaTpl('valueFormula', {
|
||||
rendererSchema: {
|
||||
type: 'textarea',
|
||||
value: context?.schema.value
|
||||
},
|
||||
label: '右侧默认值',
|
||||
placeholder: '支持使用 ${xxx} 来获取变量'
|
||||
},
|
||||
mode: 'vertical' // 改成上下展示模式
|
||||
}),
|
||||
getSchemaTpl('labelRemark'),
|
||||
getSchemaTpl('remark'),
|
||||
getSchemaTpl('description'),
|
||||
|
@ -4,6 +4,7 @@ import {BasePlugin, BaseEventContext, tipedLabel} from 'amis-editor-core';
|
||||
|
||||
import {ValidatorTag} from '../../validator';
|
||||
import {getEventControlConfig} from '../../renderer/event-control/helper';
|
||||
import {FormulaDateType} from '../../renderer/FormulaControl';
|
||||
import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
|
||||
|
||||
const formatX = [
|
||||
@ -135,8 +136,8 @@ const DateType: {
|
||||
}
|
||||
};
|
||||
|
||||
const tipedLabelText =
|
||||
'支持 <code>now、+1day、-2weeks、+1hours、+2years</code>这种相对值用法,同时支持变量如<code>\\${start_date}</code>';
|
||||
const dateTooltip =
|
||||
'支持例如: <code>now、+3days、-2weeks、+1hour、+2years</code> 等(minute|min|hour|day|week|month|year|weekday|second|millisecond)这种相对值用法';
|
||||
|
||||
export class DateControlPlugin extends BasePlugin {
|
||||
// 关联渲染器名字
|
||||
@ -333,35 +334,42 @@ export class DateControlPlugin extends BasePlugin {
|
||||
pipeIn: defaultValue(true)
|
||||
}),
|
||||
getSchemaTpl('valueFormula', {
|
||||
rendererSchema: context?.schema,
|
||||
label: tipedLabel(
|
||||
'默认值',
|
||||
'支持 <code>now、+1day、-2weeks、+1hours、+2years</code>等这种相对值用法'
|
||||
)
|
||||
rendererSchema: {
|
||||
...context?.schema
|
||||
},
|
||||
placeholder: '请选择静态值',
|
||||
header: '表达式或相对值',
|
||||
DateTimeType: FormulaDateType.IsDate,
|
||||
label: tipedLabel('默认值', dateTooltip)
|
||||
}),
|
||||
getSchemaTpl('valueFormula', {
|
||||
name: 'minDate',
|
||||
header: '表达式或相对值',
|
||||
DateTimeType: FormulaDateType.IsDate,
|
||||
rendererSchema: {
|
||||
...context?.schema,
|
||||
value: context?.schema.minDate
|
||||
},
|
||||
placeholder: '请选择静态值',
|
||||
needDeleteProps: ['minDate'], // 避免自我限制
|
||||
label: tipedLabel('最小值', tipedLabelText)
|
||||
label: tipedLabel('最小值', dateTooltip)
|
||||
}),
|
||||
getSchemaTpl('valueFormula', {
|
||||
name: 'maxDate',
|
||||
header: '表达式或相对值',
|
||||
DateTimeType: FormulaDateType.IsDate,
|
||||
rendererSchema: {
|
||||
...context?.schema,
|
||||
value: context?.schema.maxDate
|
||||
},
|
||||
needDeleteProps: ['maxDate'], // 避免自我限制
|
||||
label: tipedLabel('最大值', tipedLabelText)
|
||||
label: tipedLabel('最大值', dateTooltip)
|
||||
}),
|
||||
getSchemaTpl('placeholder', {
|
||||
pipeIn: defaultValue('请选择日期')
|
||||
}),
|
||||
// getSchemaTpl('remark'),
|
||||
// getSchemaTpl('labelRemark'),
|
||||
getSchemaTpl('remark'),
|
||||
getSchemaTpl('labelRemark'),
|
||||
getSchemaTpl('description'),
|
||||
getSchemaTpl('autoFillApi')
|
||||
]
|
||||
|
@ -4,6 +4,7 @@ import {BasePlugin, BaseEventContext} from 'amis-editor-core';
|
||||
|
||||
import {ValidatorTag} from '../../validator';
|
||||
import {getEventControlConfig} from '../../renderer/event-control/helper';
|
||||
import {FormulaDateType} from '../../renderer/FormulaControl';
|
||||
import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
|
||||
import {getRendererByName} from 'amis-core';
|
||||
|
||||
@ -68,13 +69,15 @@ const DateType: {
|
||||
}
|
||||
};
|
||||
|
||||
const dateTooltip =
|
||||
'支持例如: <code>now、+3days、-2weeks、+1hour、+2years</code> 等(minute|hour|day|week|month|year|weekday|second|millisecond)这种相对值用法';
|
||||
const rangTooltip =
|
||||
'支持例如: <code>3days、2weeks、1hour、2years</code> 等(minute|hour|day|week|month|year|weekday|second|millisecond)这种相对值用法';
|
||||
|
||||
const sizeImmutableComponents = Object.values(DateType)
|
||||
.map(item => (item?.sizeMutable === false ? item.type : null))
|
||||
.filter(a => a);
|
||||
|
||||
const tipedLabelText =
|
||||
'支持 <code>now、+1day、-2weeks、+1hours、+2years</code>这种相对值用法,同时支持变量如<code>\\${start_date}</code>';
|
||||
|
||||
export class DateRangeControlPlugin extends BasePlugin {
|
||||
// 关联渲染器名字
|
||||
rendererName = 'input-date-range';
|
||||
@ -264,63 +267,69 @@ export class DateRangeControlPlugin extends BasePlugin {
|
||||
}),
|
||||
|
||||
getSchemaTpl('valueFormula', {
|
||||
/* 备注: 待 amis 日期组件优化
|
||||
rendererSchema: {
|
||||
...context?.schema,
|
||||
size: 'full', // 备注:目前样式还有问题,需要在amis端进行优化
|
||||
mode: "inline"
|
||||
},
|
||||
mode: 'vertical',
|
||||
*/
|
||||
rendererSchema: {
|
||||
type: 'input-date'
|
||||
...context?.schema,
|
||||
size: 'full',
|
||||
mode: 'inline'
|
||||
},
|
||||
label: tipedLabel(
|
||||
'默认值',
|
||||
'支持 <code>now、+1day、-2weeks、+1hours、+2years</code>等这种相对值用法'
|
||||
)
|
||||
mode: 'vertical',
|
||||
header: '表达式或相对值',
|
||||
DateTimeType: FormulaDateType.IsRange,
|
||||
label: tipedLabel('默认值', dateTooltip)
|
||||
}),
|
||||
getSchemaTpl('valueFormula', {
|
||||
name: 'minDate',
|
||||
header: '表达式或相对值',
|
||||
DateTimeType: FormulaDateType.IsDate,
|
||||
rendererSchema: {
|
||||
...context?.schema,
|
||||
value: context?.schema.minDate,
|
||||
type: 'input-date'
|
||||
},
|
||||
placeholder: '请选择静态值',
|
||||
needDeleteProps: ['minDate'], // 避免自我限制
|
||||
label: tipedLabel('最小值', tipedLabelText)
|
||||
label: tipedLabel('最小值', dateTooltip)
|
||||
}),
|
||||
getSchemaTpl('valueFormula', {
|
||||
name: 'maxDate',
|
||||
header: '表达式或相对值',
|
||||
DateTimeType: FormulaDateType.IsDate,
|
||||
rendererSchema: {
|
||||
...context?.schema,
|
||||
value: context?.schema.maxDate,
|
||||
type: 'input-date'
|
||||
},
|
||||
placeholder: '请选择静态值',
|
||||
needDeleteProps: ['maxDate'], // 避免自我限制
|
||||
label: tipedLabel('最大值', tipedLabelText)
|
||||
label: tipedLabel('最大值', dateTooltip)
|
||||
}),
|
||||
|
||||
getSchemaTpl('valueFormula', {
|
||||
name: 'minDuration',
|
||||
header: '表达式',
|
||||
DateTimeType: FormulaDateType.NotDate,
|
||||
rendererSchema: {
|
||||
...context?.schema,
|
||||
value: context?.schema.minDuration,
|
||||
type: 'input-text'
|
||||
},
|
||||
placeholder: '请输入相对值',
|
||||
needDeleteProps: ['minDuration'], // 避免自我限制
|
||||
label: tipedLabel('最小跨度', '例如 2days')
|
||||
label: tipedLabel('最小跨度', rangTooltip)
|
||||
}),
|
||||
|
||||
getSchemaTpl('valueFormula', {
|
||||
name: 'maxDuration',
|
||||
header: '表达式',
|
||||
DateTimeType: FormulaDateType.NotDate,
|
||||
rendererSchema: {
|
||||
...context?.schema,
|
||||
value: context?.schema.maxDuration,
|
||||
type: 'input-text'
|
||||
},
|
||||
placeholder: '请输入相对值',
|
||||
needDeleteProps: ['maxDuration'], // 避免自我限制
|
||||
label: tipedLabel('最大跨度', '例如 1year')
|
||||
label: tipedLabel('最大跨度', rangTooltip)
|
||||
}),
|
||||
getSchemaTpl('dateShortCutControl', {
|
||||
mode: 'normal',
|
||||
@ -347,8 +356,8 @@ export class DateRangeControlPlugin extends BasePlugin {
|
||||
yearslater: 'n年以内'
|
||||
}
|
||||
}),
|
||||
// getSchemaTpl('remark'),
|
||||
// getSchemaTpl('labelRemark'),
|
||||
getSchemaTpl('remark'),
|
||||
getSchemaTpl('labelRemark'),
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'startPlaceholder',
|
||||
|
@ -164,10 +164,7 @@ export class RangeControlPlugin extends BasePlugin {
|
||||
},
|
||||
pipeIn: defaultValue(0),
|
||||
needDeleteProps: ['min'], // 避免自我限制
|
||||
label: tipedLabel(
|
||||
'最小值',
|
||||
'请输入数字或使用 <code>\\${xxx}</code> 来获取变量,否则该配置不生效'
|
||||
),
|
||||
label: '最小值',
|
||||
valueType: 'number'
|
||||
}),
|
||||
|
||||
@ -179,10 +176,7 @@ export class RangeControlPlugin extends BasePlugin {
|
||||
},
|
||||
pipeIn: defaultValue(100),
|
||||
needDeleteProps: ['max'], // 避免自我限制
|
||||
label: tipedLabel(
|
||||
'最大值',
|
||||
'请输入数字或使用 <code>\\${xxx}</code> 来获取变量,否则该配置不生效'
|
||||
),
|
||||
label: '最大值',
|
||||
valueType: 'number'
|
||||
}),
|
||||
|
||||
|
@ -124,10 +124,7 @@ export class RateControlPlugin extends BasePlugin {
|
||||
precision: 0
|
||||
},
|
||||
needDeleteProps: ['count'], // 避免自我限制
|
||||
label: tipedLabel(
|
||||
'最大值',
|
||||
'请输入数字或使用 <code>\\${xxx}</code> 来获取变量,否则该配置不生效'
|
||||
),
|
||||
label: '最大值',
|
||||
valueType: 'number'
|
||||
}),
|
||||
|
||||
|
@ -179,16 +179,28 @@ export class TreeControlPlugin extends BasePlugin {
|
||||
</div>
|
||||
);
|
||||
},
|
||||
schema: getArgsWrapper({
|
||||
type: 'input-formula',
|
||||
variables: '${variables}',
|
||||
evalMode: false,
|
||||
variableMode: 'tabs',
|
||||
label: '展开层级',
|
||||
size: 'lg',
|
||||
name: 'openLevel',
|
||||
mode: 'horizontal'
|
||||
})
|
||||
schema: getArgsWrapper(
|
||||
/*
|
||||
{
|
||||
type: 'input-formula',
|
||||
variables: '${variables}',
|
||||
evalMode: false,
|
||||
variableMode: 'tabs',
|
||||
label: '展开层级',
|
||||
size: 'lg',
|
||||
name: 'openLevel',
|
||||
mode: 'horizontal'
|
||||
},
|
||||
*/
|
||||
{
|
||||
name: 'openLevel',
|
||||
label: '展开层级',
|
||||
type: 'ae-formulaControl',
|
||||
variables: '${variables}',
|
||||
size: 'lg',
|
||||
mode: 'horizontal'
|
||||
}
|
||||
)
|
||||
},
|
||||
{
|
||||
actionType: 'collapse',
|
||||
|
@ -251,7 +251,11 @@ export class SelectControlPlugin extends BasePlugin {
|
||||
title: '选项',
|
||||
body: [
|
||||
getSchemaTpl('optionControlV2'),
|
||||
getSchemaTpl('menuTpl'),
|
||||
// 模板
|
||||
getSchemaTpl('optionsMenuTpl', {
|
||||
manager: this.manager,
|
||||
onChange: (value: any) => {}
|
||||
}),
|
||||
getSchemaTpl('creatable', {
|
||||
formType: 'extend',
|
||||
hiddenOnDefault: true,
|
||||
@ -308,7 +312,9 @@ export class SelectControlPlugin extends BasePlugin {
|
||||
'开启后,当选项值未匹配到当前options中的选项时,选项文本飘红'
|
||||
),
|
||||
name: 'showInvalidMatch'
|
||||
})
|
||||
}),
|
||||
getSchemaTpl('virtualThreshold'),
|
||||
getSchemaTpl('virtualItemHeight')
|
||||
]
|
||||
},
|
||||
getSchemaTpl('status', {isFormItem: true}),
|
||||
|
@ -182,6 +182,7 @@ export class SwitchControlPlugin extends BasePlugin {
|
||||
*/
|
||||
getSchemaTpl('valueFormula', {
|
||||
rendererSchema: context?.schema,
|
||||
needDeleteProps: ['option'],
|
||||
rendererWrapper: true, // 浅色线框包裹一下,增加边界感
|
||||
valueType: 'boolean',
|
||||
pipeIn: (value: any, data: any) => {
|
||||
|
@ -300,19 +300,19 @@ export class TabsTransferPlugin extends BasePlugin {
|
||||
|
||||
getSchemaTpl('sortable'),
|
||||
|
||||
getSchemaTpl('formulaControl', {
|
||||
{
|
||||
label: '左侧选项标题',
|
||||
name: 'selectTitle',
|
||||
type: 'input-text',
|
||||
inputClassName: 'is-inline '
|
||||
}),
|
||||
},
|
||||
|
||||
getSchemaTpl('formulaControl', {
|
||||
{
|
||||
label: '右侧结果标题',
|
||||
name: 'resultTitle',
|
||||
type: 'input-text',
|
||||
inputClassName: 'is-inline '
|
||||
})
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -336,6 +336,13 @@ export class TabsTransferPlugin extends BasePlugin {
|
||||
})
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '高级',
|
||||
body: [
|
||||
getSchemaTpl('virtualThreshold'),
|
||||
getSchemaTpl('virtualItemHeight')
|
||||
]
|
||||
},
|
||||
getSchemaTpl('status', {isFormItem: true})
|
||||
])
|
||||
},
|
||||
|
@ -122,10 +122,16 @@ export class TextareaControlPlugin extends BasePlugin {
|
||||
required: true
|
||||
}),
|
||||
getSchemaTpl('label'),
|
||||
getSchemaTpl('valueFormula', {
|
||||
rendererSchema: context?.schema,
|
||||
mode: 'vertical' // 改成上下展示模式
|
||||
}),
|
||||
// getSchemaTpl('valueFormula', {
|
||||
// rendererSchema: context?.schema,
|
||||
// mode: 'vertical' // 改成上下展示模式
|
||||
// }),
|
||||
{
|
||||
type: 'ae-textareaFormulaControl',
|
||||
label: '默认值',
|
||||
name: 'value',
|
||||
mode: 'normal'
|
||||
},
|
||||
getSchemaTpl('switch', {
|
||||
name: 'trimContents',
|
||||
pipeIn: defaultValue(true),
|
||||
|
@ -266,20 +266,18 @@ export class TransferPlugin extends BasePlugin {
|
||||
name: 'searchable'
|
||||
}),
|
||||
|
||||
getSchemaTpl('menuTpl', {
|
||||
label: tipedLabel(
|
||||
'模板',
|
||||
'左侧选项渲染模板,支持JSX,变量使用\\${xx}'
|
||||
),
|
||||
getSchemaTpl('optionsMenuTpl', {
|
||||
manager: this.manager,
|
||||
onChange: (value: any) => {},
|
||||
visibleOn: 'data.selectMode !== "table"'
|
||||
}),
|
||||
|
||||
getSchemaTpl('formulaControl', {
|
||||
{
|
||||
label: '标题',
|
||||
name: 'selectTitle',
|
||||
type: 'input-text',
|
||||
inputClassName: 'is-inline '
|
||||
})
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -317,22 +315,26 @@ export class TransferPlugin extends BasePlugin {
|
||||
'data.selectMode === "list" && !data.resultListModeFollowSelect'
|
||||
}),
|
||||
|
||||
getSchemaTpl('menuTpl', {
|
||||
getSchemaTpl('optionsMenuTpl', {
|
||||
name: 'valueTpl',
|
||||
label: tipedLabel(
|
||||
'模板',
|
||||
'结果选项渲染模板,支持JSX,变量使用\\${xx}'
|
||||
),
|
||||
manager: this.manager,
|
||||
onChange: (value: any) => {},
|
||||
visibleOn:
|
||||
'!(data.selectMode === "table" && data.resultListModeFollowSelect)'
|
||||
}),
|
||||
|
||||
getSchemaTpl('formulaControl', {
|
||||
{
|
||||
label: '标题',
|
||||
name: 'resultTitle',
|
||||
type: 'input-text',
|
||||
inputClassName: 'is-inline '
|
||||
})
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '高级',
|
||||
body: [
|
||||
getSchemaTpl('virtualThreshold'),
|
||||
getSchemaTpl('virtualItemHeight')
|
||||
]
|
||||
},
|
||||
getSchemaTpl('status', {isFormItem: true}),
|
||||
|
@ -13,7 +13,7 @@ import {
|
||||
} from 'amis-editor-core';
|
||||
import {defaultValue, getSchemaTpl} from 'amis-editor-core';
|
||||
import {EditorNodeType} from 'amis-editor-core';
|
||||
import {Schema} from 'amis/lib/types';
|
||||
import {Schema} from 'amis';
|
||||
import {VRenderer} from 'amis-editor-core';
|
||||
import {RegionWrapper as Region} from 'amis-editor-core';
|
||||
import {Icon} from 'amis-editor-core';
|
||||
|
@ -13,7 +13,7 @@ import {
|
||||
} from 'amis-editor-core';
|
||||
import {defaultValue, getSchemaTpl} from 'amis-editor-core';
|
||||
import {EditorNodeType} from 'amis-editor-core';
|
||||
import {Schema} from 'amis/lib/types';
|
||||
import {Schema} from 'amis';
|
||||
import {VRenderer} from 'amis-editor-core';
|
||||
import {RegionWrapper as Region} from 'amis-editor-core';
|
||||
import {JSONChangeInArray, JSONPipeIn, repeatArray} from 'amis-editor-core';
|
||||
|
@ -41,9 +41,10 @@ export class IFramePlugin extends BasePlugin {
|
||||
title: '基本',
|
||||
body: [
|
||||
{
|
||||
type: 'ae-textareaFormulaControl',
|
||||
name: 'src',
|
||||
label: '页面地址',
|
||||
type: 'input-text'
|
||||
mode: 'normal',
|
||||
label: '页面地址'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -8,24 +8,22 @@ import type {
|
||||
EditorNodeType,
|
||||
RegionConfig,
|
||||
RendererJSONSchemaResolveEventContext,
|
||||
BasicToolbarItem
|
||||
BasicToolbarItem,
|
||||
PluginInterface
|
||||
} from 'amis-editor-core';
|
||||
|
||||
// 默认的列容器Schema
|
||||
const defaultFlexColumnSchema = (title: string) => {
|
||||
export const defaultFlexColumnSchema = (title?: string) => {
|
||||
return {
|
||||
type: 'wrapper',
|
||||
body: [],
|
||||
size: 'xs',
|
||||
style: {
|
||||
position: 'static',
|
||||
display: 'flex',
|
||||
display: 'block',
|
||||
flex: '1 1 auto',
|
||||
flexGrow: 1,
|
||||
flexBasis: 'auto',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'stretch'
|
||||
flexBasis: 'auto'
|
||||
},
|
||||
isFixedHeight: false,
|
||||
isFixedWidth: false
|
||||
@ -40,6 +38,10 @@ const defaultFlexContainerSchema = {
|
||||
defaultFlexColumnSchema('第二列'),
|
||||
defaultFlexColumnSchema('第三列')
|
||||
],
|
||||
style: {
|
||||
position: 'static',
|
||||
},
|
||||
direction: "row",
|
||||
justify: 'flex-start',
|
||||
alignItems: 'stretch'
|
||||
};
|
||||
@ -222,6 +224,13 @@ export class FlexPluginBase extends BasePlugin {
|
||||
const isFlexColumnItem = this.manager?.isFlexColumnItem(id);
|
||||
const newColumnSchema = defaultFlexColumnSchema('新的一列');
|
||||
|
||||
const toolbarsTooltips:any = {};
|
||||
toolbars.forEach(toolbar => {
|
||||
if (toolbar.tooltip) {
|
||||
toolbarsTooltips[toolbar.tooltip] = 1;
|
||||
}
|
||||
});
|
||||
|
||||
if (
|
||||
parent &&
|
||||
(info.renderer?.name === 'flex' || info.renderer?.name === 'container') &&
|
||||
@ -229,74 +238,80 @@ export class FlexPluginBase extends BasePlugin {
|
||||
!draggableContainer
|
||||
) {
|
||||
// 非特殊布局元素(fixed、absolute)支持前后插入追加布局元素功能icon
|
||||
toolbars.push(
|
||||
{
|
||||
iconSvg: 'add-btn',
|
||||
tooltip: '上方插入布局容器',
|
||||
level: 'special',
|
||||
placement: 'right',
|
||||
className: 'ae-InsertBefore is-vertical',
|
||||
onClick: () =>
|
||||
this.manager.appendSiblingSchema(
|
||||
defaultFlexContainerSchema,
|
||||
true,
|
||||
true
|
||||
)
|
||||
},
|
||||
{
|
||||
iconSvg: 'add-btn',
|
||||
tooltip: '下方插入布局容器',
|
||||
level: 'special',
|
||||
placement: 'right',
|
||||
className: 'ae-InsertAfter is-vertical',
|
||||
onClick: () =>
|
||||
this.manager.appendSiblingSchema(
|
||||
defaultFlexContainerSchema,
|
||||
false,
|
||||
true
|
||||
)
|
||||
}
|
||||
);
|
||||
if (!toolbarsTooltips['上方插入布局容器']) {
|
||||
toolbars.push(
|
||||
{
|
||||
iconSvg: 'add-btn',
|
||||
tooltip: '上方插入布局容器',
|
||||
level: 'special',
|
||||
placement: 'right',
|
||||
className: 'ae-InsertBefore is-vertical',
|
||||
onClick: () =>
|
||||
this.manager.appendSiblingSchema(
|
||||
defaultFlexContainerSchema,
|
||||
true,
|
||||
true
|
||||
)
|
||||
},
|
||||
{
|
||||
iconSvg: 'add-btn',
|
||||
tooltip: '下方插入布局容器',
|
||||
level: 'special',
|
||||
placement: 'right',
|
||||
className: 'ae-InsertAfter is-vertical',
|
||||
onClick: () =>
|
||||
this.manager.appendSiblingSchema(
|
||||
defaultFlexContainerSchema,
|
||||
false,
|
||||
true
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 布局容器 右上角插入子元素
|
||||
if (info.renderer?.name === 'flex') {
|
||||
toolbars.push({
|
||||
iconSvg: 'add-btn',
|
||||
tooltip: '新增列级元素',
|
||||
level: 'special',
|
||||
placement: 'bottom',
|
||||
className: 'ae-AppendChild',
|
||||
onClick: () => this.manager.addElem(newColumnSchema)
|
||||
});
|
||||
if (!toolbarsTooltips['新增列级元素']) {
|
||||
toolbars.push({
|
||||
iconSvg: 'add-btn',
|
||||
tooltip: '新增列级元素',
|
||||
level: 'special',
|
||||
placement: 'bottom',
|
||||
className: 'ae-AppendChild',
|
||||
onClick: () => this.manager.addElem(newColumnSchema)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isFlexItem && !draggableContainer) {
|
||||
// 布局容器的列级元素 增加左右插入icon
|
||||
toolbars.push(
|
||||
{
|
||||
iconSvg: 'add-btn',
|
||||
tooltip: `${isFlexColumnItem ? '上方' : '左侧'}插入列级容器`,
|
||||
level: 'special',
|
||||
placement: 'right',
|
||||
className: isFlexColumnItem
|
||||
? 'ae-InsertBefore is-vertical'
|
||||
: 'ae-InsertBefore',
|
||||
onClick: () =>
|
||||
this.manager.appendSiblingSchema(newColumnSchema, true, true)
|
||||
},
|
||||
{
|
||||
iconSvg: 'add-btn',
|
||||
tooltip: `${isFlexColumnItem ? '下方' : '右侧'}插入列级容器`,
|
||||
level: 'special',
|
||||
placement: isFlexColumnItem ? 'right' : 'left',
|
||||
className: isFlexColumnItem
|
||||
? 'ae-InsertAfter is-vertical'
|
||||
: 'ae-InsertAfter',
|
||||
onClick: () =>
|
||||
this.manager.appendSiblingSchema(newColumnSchema, false, true)
|
||||
}
|
||||
);
|
||||
if (!toolbarsTooltips[`${isFlexColumnItem ? '上方' : '左侧'}插入列级容器`]) {
|
||||
// 布局容器的列级元素 增加左右插入icon
|
||||
toolbars.push(
|
||||
{
|
||||
iconSvg: 'add-btn',
|
||||
tooltip: `${isFlexColumnItem ? '上方' : '左侧'}插入列级容器`,
|
||||
level: 'special',
|
||||
placement: 'right',
|
||||
className: isFlexColumnItem
|
||||
? 'ae-InsertBefore is-vertical'
|
||||
: 'ae-InsertBefore',
|
||||
onClick: () =>
|
||||
this.manager.appendSiblingSchema(newColumnSchema, true, true)
|
||||
},
|
||||
{
|
||||
iconSvg: 'add-btn',
|
||||
tooltip: `${isFlexColumnItem ? '下方' : '右侧'}插入列级容器`,
|
||||
level: 'special',
|
||||
placement: isFlexColumnItem ? 'right' : 'left',
|
||||
className: isFlexColumnItem
|
||||
? 'ae-InsertAfter is-vertical'
|
||||
: 'ae-InsertAfter',
|
||||
onClick: () =>
|
||||
this.manager.appendSiblingSchema(newColumnSchema, false, true)
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,10 +18,7 @@ export default class Layout1_2_v4 extends FlexPluginBase {
|
||||
body: [],
|
||||
style: {
|
||||
flex: '0 0 auto',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'stretch'
|
||||
display: 'block'
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -34,10 +31,7 @@ export default class Layout1_2_v4 extends FlexPluginBase {
|
||||
style: {
|
||||
flex: '0 0 auto',
|
||||
flexBasis: '250px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'stretch'
|
||||
display: 'block'
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -9,19 +9,16 @@ export default class Layout_fixed extends FlexPluginBase {
|
||||
tags = ['常见布局'];
|
||||
order = 503;
|
||||
scaffold: any = {
|
||||
type: 'wrapper',
|
||||
type: 'container',
|
||||
size: 'xs',
|
||||
body: [],
|
||||
style: {
|
||||
position: 'fixed',
|
||||
inset: 'auto 50px 50px auto',
|
||||
zIndex: 10,
|
||||
display: 'flex',
|
||||
minWidth: '80px',
|
||||
minHeight: '80px',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'stretch'
|
||||
display: 'block'
|
||||
},
|
||||
originPosition: 'right-bottom'
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {registerEditorPlugin} from 'amis-editor-core';
|
||||
import {FlexPluginBase} from './FlexPluginBase';
|
||||
import {FlexPluginBase, defaultFlexColumnSchema} from './FlexPluginBase';
|
||||
|
||||
export default class Layout_fixed_bottom extends FlexPluginBase {
|
||||
name = '吸底容器';
|
||||
@ -12,48 +12,10 @@ export default class Layout_fixed_bottom extends FlexPluginBase {
|
||||
type: 'flex',
|
||||
className: 'p-1',
|
||||
items: [
|
||||
{
|
||||
type: 'wrapper',
|
||||
size: 'xs',
|
||||
body: [],
|
||||
style: {
|
||||
flex: '1 1 auto',
|
||||
flexBasis: 'auto',
|
||||
flexGrow: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'stretch'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'wrapper',
|
||||
size: 'xs',
|
||||
body: [],
|
||||
style: {
|
||||
flex: '1 1 auto',
|
||||
flexBasis: 'auto',
|
||||
flexGrow: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'stretch'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'wrapper',
|
||||
size: 'xs',
|
||||
body: [],
|
||||
style: {
|
||||
flex: '1 1 auto',
|
||||
flexBasis: 'auto',
|
||||
flexGrow: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'stretch'
|
||||
}
|
||||
}
|
||||
defaultFlexColumnSchema(),
|
||||
defaultFlexColumnSchema(),
|
||||
defaultFlexColumnSchema(),
|
||||
|
||||
],
|
||||
style: {
|
||||
position: 'fixed',
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {registerEditorPlugin} from 'amis-editor-core';
|
||||
import {FlexPluginBase} from './FlexPluginBase';
|
||||
import {FlexPluginBase, defaultFlexColumnSchema} from './FlexPluginBase';
|
||||
|
||||
export default class Layout_fixed_top extends FlexPluginBase {
|
||||
name = '吸顶容器';
|
||||
@ -12,39 +12,9 @@ export default class Layout_fixed_top extends FlexPluginBase {
|
||||
type: 'flex',
|
||||
className: 'p-1',
|
||||
items: [
|
||||
{
|
||||
type: 'wrapper',
|
||||
size: 'xs',
|
||||
body: [],
|
||||
style: {
|
||||
flex: '1 1 auto',
|
||||
flexBasis: 'auto',
|
||||
flexGrow: 1,
|
||||
display: 'block'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'wrapper',
|
||||
size: 'xs',
|
||||
body: [],
|
||||
style: {
|
||||
flex: '1 1 auto',
|
||||
flexBasis: 'auto',
|
||||
flexGrow: 1,
|
||||
display: 'block'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'wrapper',
|
||||
size: 'xs',
|
||||
body: [],
|
||||
style: {
|
||||
flex: '1 1 auto',
|
||||
display: 'block',
|
||||
flexBasis: 'auto',
|
||||
flexGrow: 1
|
||||
}
|
||||
}
|
||||
defaultFlexColumnSchema(),
|
||||
defaultFlexColumnSchema(),
|
||||
defaultFlexColumnSchema(),
|
||||
],
|
||||
style: {
|
||||
position: 'fixed',
|
||||
|
@ -19,10 +19,7 @@ export default class Layout_scroll_x extends FlexPluginBase {
|
||||
style: {
|
||||
flex: '0 0 auto',
|
||||
flexBasis: '200px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'stretch',
|
||||
display: 'block',
|
||||
position: 'static',
|
||||
minWidth: 'auto',
|
||||
minHeight: 'auto'
|
||||
@ -35,10 +32,7 @@ export default class Layout_scroll_x extends FlexPluginBase {
|
||||
style: {
|
||||
flex: '0 0 auto',
|
||||
flexBasis: '200px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'stretch',
|
||||
display: 'block',
|
||||
position: 'static',
|
||||
minWidth: 'auto',
|
||||
minHeight: 'auto'
|
||||
@ -50,10 +44,7 @@ export default class Layout_scroll_x extends FlexPluginBase {
|
||||
body: [],
|
||||
style: {
|
||||
flex: '0 0 auto',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'stretch',
|
||||
display: 'block',
|
||||
position: 'static',
|
||||
minWidth: 'auto',
|
||||
minHeight: 'auto',
|
||||
@ -67,10 +58,7 @@ export default class Layout_scroll_x extends FlexPluginBase {
|
||||
style: {
|
||||
flex: '0 0 auto',
|
||||
flexBasis: '200px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'stretch',
|
||||
display: 'block',
|
||||
position: 'static',
|
||||
minWidth: 'auto',
|
||||
minHeight: 'auto'
|
||||
@ -83,10 +71,7 @@ export default class Layout_scroll_x extends FlexPluginBase {
|
||||
style: {
|
||||
flex: '0 0 auto',
|
||||
flexBasis: '200px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'stretch',
|
||||
display: 'block',
|
||||
position: 'static',
|
||||
minWidth: 'auto',
|
||||
minHeight: 'auto'
|
||||
@ -99,10 +84,7 @@ export default class Layout_scroll_x extends FlexPluginBase {
|
||||
style: {
|
||||
flex: '0 0 auto',
|
||||
flexBasis: '200px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'stretch',
|
||||
display: 'block',
|
||||
position: 'static',
|
||||
minWidth: 'auto',
|
||||
minHeight: 'auto'
|
||||
@ -115,10 +97,7 @@ export default class Layout_scroll_x extends FlexPluginBase {
|
||||
style: {
|
||||
flex: '0 0 auto',
|
||||
flexBasis: '200px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'stretch',
|
||||
display: 'block',
|
||||
position: 'static',
|
||||
minWidth: 'auto',
|
||||
minHeight: 'auto'
|
||||
|
@ -19,10 +19,7 @@ export default class Layout_scroll_y extends FlexPluginBase {
|
||||
style: {
|
||||
flex: '0 0 auto',
|
||||
flexBasis: '60px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'stretch',
|
||||
display: 'block',
|
||||
minWidth: 'auto',
|
||||
minHeight: 'auto'
|
||||
}
|
||||
@ -34,10 +31,7 @@ export default class Layout_scroll_y extends FlexPluginBase {
|
||||
style: {
|
||||
flex: '0 0 auto',
|
||||
flexBasis: '60px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'stretch',
|
||||
display: 'block',
|
||||
position: 'static',
|
||||
minWidth: 'auto',
|
||||
minHeight: 'auto'
|
||||
@ -50,10 +44,7 @@ export default class Layout_scroll_y extends FlexPluginBase {
|
||||
style: {
|
||||
flex: '0 0 auto',
|
||||
flexBasis: '60px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'stretch',
|
||||
display: 'block',
|
||||
position: 'static',
|
||||
minWidth: 'auto',
|
||||
minHeight: 'auto'
|
||||
@ -66,10 +57,7 @@ export default class Layout_scroll_y extends FlexPluginBase {
|
||||
style: {
|
||||
flex: '0 0 auto',
|
||||
flexBasis: '60px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'stretch',
|
||||
display: 'block',
|
||||
position: 'static',
|
||||
minWidth: 'auto',
|
||||
minHeight: 'auto'
|
||||
@ -82,10 +70,7 @@ export default class Layout_scroll_y extends FlexPluginBase {
|
||||
style: {
|
||||
flex: '0 0 auto',
|
||||
flexBasis: '60px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'stretch',
|
||||
display: 'block',
|
||||
position: 'static',
|
||||
minWidth: 'auto',
|
||||
minHeight: 'auto'
|
||||
@ -98,10 +83,7 @@ export default class Layout_scroll_y extends FlexPluginBase {
|
||||
style: {
|
||||
flex: '0 0 auto',
|
||||
flexBasis: '60px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'stretch',
|
||||
display: 'block',
|
||||
position: 'static',
|
||||
minWidth: 'auto',
|
||||
minHeight: 'auto'
|
||||
|
@ -22,7 +22,8 @@ export class ProgressPlugin extends BasePlugin {
|
||||
scaffold = {
|
||||
type: 'progress',
|
||||
value: 66,
|
||||
strokeWidth: 6
|
||||
strokeWidth: 6,
|
||||
valueTpl: '${value}%'
|
||||
};
|
||||
previewSchema = {
|
||||
...this.scaffold
|
||||
@ -46,7 +47,7 @@ export class ProgressPlugin extends BasePlugin {
|
||||
name: 'mode',
|
||||
type: 'select',
|
||||
option: '继承',
|
||||
value: 'line',
|
||||
pipeIn: defaultValue('line'),
|
||||
tiled: true,
|
||||
options: [
|
||||
{
|
||||
@ -79,15 +80,33 @@ export class ProgressPlugin extends BasePlugin {
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input-number',
|
||||
name: 'value',
|
||||
label: '默认值',
|
||||
min: 0,
|
||||
max: 100
|
||||
},
|
||||
getSchemaTpl('valueFormula', {
|
||||
rendererSchema: {
|
||||
...context?.schema,
|
||||
type: 'input-number'
|
||||
},
|
||||
needDeleteProps: ['placeholder'],
|
||||
valueType: 'number' // 期望数值类型,不过 amis中会尝试字符串 trans 数值类型
|
||||
}),
|
||||
getSchemaTpl('menuTpl', {
|
||||
name: 'valueTpl'
|
||||
label: tipedLabel(
|
||||
'数值模板',
|
||||
'值渲染模板,支持JSX、数据域变量使用, 默认 ${value}%'
|
||||
),
|
||||
name: 'valueTpl',
|
||||
variables: [
|
||||
{
|
||||
label: '值字段',
|
||||
children: [
|
||||
{
|
||||
label: '进度值',
|
||||
value: 'value',
|
||||
tag: 'number'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
requiredDataPropsVariables: true
|
||||
}),
|
||||
|
||||
getSchemaTpl('switch', {
|
||||
|
@ -295,6 +295,7 @@ export class TablePlugin extends BasePlugin {
|
||||
description: '设置表格的选中项',
|
||||
innerArgs: ['selected'],
|
||||
schema: getArgsWrapper([
|
||||
/*
|
||||
{
|
||||
type: 'input-formula',
|
||||
variables: '${variables}',
|
||||
@ -305,6 +306,15 @@ export class TablePlugin extends BasePlugin {
|
||||
name: 'selected',
|
||||
mode: 'horizontal'
|
||||
}
|
||||
*/
|
||||
{
|
||||
name: 'selected',
|
||||
label: '选中项',
|
||||
type: 'ae-formulaControl',
|
||||
variables: '${variables}',
|
||||
size: 'lg',
|
||||
mode: 'horizontal'
|
||||
}
|
||||
])
|
||||
},
|
||||
{
|
||||
|
@ -293,6 +293,7 @@ export class Table2Plugin extends BasePlugin {
|
||||
actionLabel: '设置选中项',
|
||||
description: '设置表格的选中项',
|
||||
schema: getArgsWrapper([
|
||||
/*
|
||||
{
|
||||
type: 'input-formula',
|
||||
variables: '${variables}',
|
||||
@ -303,6 +304,15 @@ export class Table2Plugin extends BasePlugin {
|
||||
name: 'selected',
|
||||
mode: 'horizontal'
|
||||
}
|
||||
*/
|
||||
{
|
||||
name: 'selected',
|
||||
label: '选中项',
|
||||
type: 'ae-formulaControl',
|
||||
variables: '${variables}',
|
||||
size: 'lg',
|
||||
mode: 'horizontal'
|
||||
}
|
||||
])
|
||||
},
|
||||
{
|
||||
|
@ -101,16 +101,28 @@ export class TabsPlugin extends BasePlugin {
|
||||
</div>
|
||||
);
|
||||
},
|
||||
schema: getArgsWrapper({
|
||||
type: 'input-formula',
|
||||
variables: '${variables}',
|
||||
evalMode: false,
|
||||
variableMode: 'tabs',
|
||||
label: '激活项',
|
||||
size: 'lg',
|
||||
name: 'activeKey',
|
||||
mode: 'horizontal'
|
||||
})
|
||||
schema: getArgsWrapper(
|
||||
/*
|
||||
{
|
||||
type: 'input-formula',
|
||||
variables: '${variables}',
|
||||
evalMode: false,
|
||||
variableMode: 'tabs',
|
||||
label: '激活项',
|
||||
size: 'lg',
|
||||
name: 'activeKey',
|
||||
mode: 'horizontal'
|
||||
}
|
||||
*/
|
||||
{
|
||||
name: 'activeKey',
|
||||
label: '激活项',
|
||||
type: 'ae-formulaControl',
|
||||
variables: '${variables}',
|
||||
size: 'lg',
|
||||
mode: 'horizontal'
|
||||
}
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
@ -173,10 +185,11 @@ export class TabsPlugin extends BasePlugin {
|
||||
title: '高级',
|
||||
body: [
|
||||
{
|
||||
type: 'ae-formulaControl',
|
||||
type: 'ae-expressionFormulaControl',
|
||||
evalMode: true,
|
||||
label: tipedLabel(
|
||||
'关联数据',
|
||||
'可用<code>\\${xxx}</code>取值,根据该数据来动态重复渲染所配置的选项卡'
|
||||
'根据该数据来动态重复渲染所配置的选项卡'
|
||||
),
|
||||
name: 'source'
|
||||
},
|
||||
|
@ -5,13 +5,9 @@ import {tipedLabel} from 'amis-editor-core';
|
||||
import {ValidatorTag} from '../validator';
|
||||
|
||||
setSchemaTpl('tpl:content', {
|
||||
label: tipedLabel(
|
||||
'文字内容',
|
||||
'支持使用 <code>\\${xxx}</code> 来获取变量,或者用 lodash.template 语法来写模板逻辑。<a target="_blank" href="/amis/zh-CN/docs/concepts/template">详情</a>'
|
||||
),
|
||||
type: 'textarea',
|
||||
minRows: 5,
|
||||
language: 'html',
|
||||
label: '文字内容',
|
||||
type: 'ae-textareaFormulaControl',
|
||||
mode: 'normal',
|
||||
visibleOn: 'data.wrapperComponent !== undefined',
|
||||
pipeIn: (value: any, data: any) => value || (data && data.html),
|
||||
name: 'tpl'
|
||||
|
@ -254,6 +254,7 @@ export class WizardPlugin extends BasePlugin {
|
||||
);
|
||||
},
|
||||
schema: getArgsWrapper([
|
||||
/*
|
||||
{
|
||||
type: 'input-formula',
|
||||
variables: '${variables}',
|
||||
@ -265,6 +266,16 @@ export class WizardPlugin extends BasePlugin {
|
||||
name: 'step',
|
||||
mode: 'horizontal'
|
||||
}
|
||||
*/
|
||||
{
|
||||
name: 'step',
|
||||
label: '目标步骤',
|
||||
type: 'ae-formulaControl',
|
||||
variables: '${variables}',
|
||||
size: 'lg',
|
||||
mode: 'horizontal',
|
||||
required: true
|
||||
}
|
||||
])
|
||||
},
|
||||
{
|
||||
|
@ -17,7 +17,7 @@ import {
|
||||
} from 'amis-editor-core';
|
||||
|
||||
import type {SchemaObject, SchemaCollection, SchemaApi} from 'amis/lib/Schema';
|
||||
import type {Api} from 'amis/lib/types';
|
||||
import type {Api} from 'amis';
|
||||
import type {FormControlProps} from 'amis-core';
|
||||
import type {ActionSchema} from 'amis/lib/renderers/Action';
|
||||
|
||||
|
@ -12,7 +12,7 @@ import {autobind, isObject, anyChanged, createObject} from 'amis-editor-core';
|
||||
import {tipedLabel} from 'amis-editor-core';
|
||||
|
||||
import type {SchemaObject, SchemaCollection, SchemaApi} from 'amis/lib/Schema';
|
||||
import type {Api} from 'amis/lib/types';
|
||||
import type {Api} from 'amis';
|
||||
import type {FormControlProps} from 'amis-core';
|
||||
import type {ActionSchema} from 'amis/lib/renderers/Action';
|
||||
|
||||
|
197
packages/amis-editor/src/renderer/ExpressionFormulaControl.tsx
Normal file
197
packages/amis-editor/src/renderer/ExpressionFormulaControl.tsx
Normal 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 {}
|
@ -13,9 +13,16 @@ import isString from 'lodash/isString';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import omit from 'lodash/omit';
|
||||
import cx from 'classnames';
|
||||
import {FormItem, Button, InputBox, Icon, ResultBox} from 'amis';
|
||||
import {
|
||||
FormItem,
|
||||
Button,
|
||||
InputBox,
|
||||
Icon,
|
||||
ResultBox,
|
||||
TooltipWrapper
|
||||
} from 'amis';
|
||||
import {FormulaExec, isExpression} from 'amis';
|
||||
import {PickerContainer} from 'amis';
|
||||
import {PickerContainer, relativeValueRe} from 'amis';
|
||||
import {FormulaEditor} from 'amis-ui/lib/components/formula/Editor';
|
||||
|
||||
import {autobind} from 'amis-editor-core';
|
||||
@ -27,24 +34,30 @@ import type {
|
||||
import {dataMapping, FormControlProps} from 'amis-core';
|
||||
import type {BaseEventContext} from 'amis-editor-core';
|
||||
import {EditorManager} from 'amis-editor-core';
|
||||
import {getVariables} from './textarea-formula/utils';
|
||||
|
||||
export enum FormulaDateType {
|
||||
NotDate, // 不是时间类
|
||||
IsDate, // 日期时间类
|
||||
IsRange // 日期时间范围类
|
||||
}
|
||||
|
||||
export interface FormulaControlProps extends FormControlProps {
|
||||
/**
|
||||
* evalMode 即直接就是表达式,否则
|
||||
* 需要 ${这里面才是表达式}
|
||||
* 默认为 true,即默认不会将表达式用 ${} 包裹
|
||||
*/
|
||||
evalMode?: boolean;
|
||||
|
||||
manager?: EditorManager;
|
||||
|
||||
/**
|
||||
* 用于提示的变量集合,默认为空
|
||||
*/
|
||||
variables?: Array<VariableItem>;
|
||||
variables?: Array<VariableItem> | Function;
|
||||
|
||||
/**
|
||||
* 变量展现模式,可选值:'tabs' | 'tree'
|
||||
* 配合 variables 使用
|
||||
* 当 props.variables 存在时, 是否再从 amis数据域中取变量集合,默认 false;
|
||||
*/
|
||||
requiredDataPropsVariables?: boolean;
|
||||
|
||||
/**
|
||||
* 变量展现模式,可选值:'tabs' | 'tree', 默认 tabs
|
||||
*/
|
||||
variableMode?: 'tabs' | 'tree';
|
||||
|
||||
@ -55,10 +68,15 @@ export interface FormulaControlProps extends FormControlProps {
|
||||
functions: Array<FuncGroup>;
|
||||
|
||||
/**
|
||||
* 顶部标题,默认为表达式
|
||||
* 弹窗顶部标题,默认为 "表达式"
|
||||
*/
|
||||
header: string;
|
||||
|
||||
/**
|
||||
* 静态输入框的占位提示内容,可用于默认静态输入框 & 自定义自定义渲染器 中
|
||||
*/
|
||||
placeholder: string;
|
||||
|
||||
/**
|
||||
* 编辑器上下文数据,用于获取字段所在Form的其他字段
|
||||
*/
|
||||
@ -103,6 +121,14 @@ export interface FormulaControlProps extends FormControlProps {
|
||||
* 是否使用外部的Form数据
|
||||
*/
|
||||
useExternalFormData?: boolean;
|
||||
|
||||
/**
|
||||
* 是否是日期类组件使用formulaControl
|
||||
* 日期类 值 使用表达式时,支持 now、+1day、-2weeks、+1hours、+2years等这种相对值写法,不会最外层包裹 ${}
|
||||
* 日期类 跨度值 使用表达式时,支持 1day、2weeks、1hours、2years等这种相对值写法,不会最外层包裹 ${}
|
||||
* 默认为 FormulaDateType.NotDate
|
||||
*/
|
||||
DateTimeType?: FormulaDateType;
|
||||
}
|
||||
|
||||
interface FormulaControlState {
|
||||
@ -110,8 +136,6 @@ interface FormulaControlState {
|
||||
variables: any;
|
||||
|
||||
variableMode?: 'tree' | 'tabs';
|
||||
|
||||
evalMode?: boolean;
|
||||
}
|
||||
|
||||
export default class FormulaControl extends React.Component<
|
||||
@ -119,37 +143,33 @@ export default class FormulaControl extends React.Component<
|
||||
FormulaControlState
|
||||
> {
|
||||
static defaultProps: Partial<FormulaControlProps> = {
|
||||
simple: false
|
||||
simple: false,
|
||||
rendererWrapper: false,
|
||||
DateTimeType: FormulaDateType.NotDate,
|
||||
requiredDataPropsVariables: false
|
||||
};
|
||||
isUnmount: boolean;
|
||||
|
||||
constructor(props: FormulaControlProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
variables: this.normalizeVariables(props.variables), // 备注: 待沟通
|
||||
variableMode: 'tabs',
|
||||
evalMode: true
|
||||
variables: [],
|
||||
variableMode: 'tabs'
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: FormulaControlProps) {
|
||||
// 优先使用props中的变量数据
|
||||
if (!this.props.variables) {
|
||||
// 从amis数据域中取变量数据
|
||||
this.resolveVariablesFromScope().then(variables => {
|
||||
if (Array.isArray(variables)) {
|
||||
const vars = variables.filter(item => item.children?.length);
|
||||
if (!this.isUnmount && !isEqual(vars, this.state.variables)) {
|
||||
this.setState({
|
||||
variables: vars
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
async componentDidMount() {
|
||||
const variablesArr = await getVariables(this);
|
||||
this.setState({
|
||||
variables: variablesArr
|
||||
});
|
||||
}
|
||||
|
||||
async componentDidUpdate(prevProps: FormulaControlProps) {
|
||||
if (this.props.data !== prevProps.data) {
|
||||
const variablesArr = await getVariables(this);
|
||||
this.setState({
|
||||
variables: dataMapping(this.props.variables, this.props.data)
|
||||
variables: variablesArr
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -158,58 +178,36 @@ export default class FormulaControl extends React.Component<
|
||||
this.isUnmount = true;
|
||||
}
|
||||
|
||||
// 组件默认值设置交互中未使用
|
||||
normalizeVariables(variables: any) {
|
||||
if (!variables) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (
|
||||
variables &&
|
||||
variables.some((item: any) => isExpression(item.children))
|
||||
) {
|
||||
variables = dataMapping(variables, this.props.data);
|
||||
}
|
||||
|
||||
const {context, evalMode} = this.props;
|
||||
// 自身字段
|
||||
const field = this.props?.data?.name;
|
||||
const ancestorField = context?.node?.ancestorField;
|
||||
|
||||
return uniqBy(
|
||||
[
|
||||
...(Array.isArray(variables) ? variables : []),
|
||||
...(ancestorField
|
||||
? ancestorField.map((item: any) => ({
|
||||
label: item,
|
||||
value: evalMode ? `this.${item}` : item
|
||||
}))
|
||||
: []),
|
||||
...(field ? [{label: field, value: `this.${field}`}] : [])
|
||||
],
|
||||
'value'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 ${xx}(非 \${xx})替换成 \${xx}
|
||||
* 备注: 手动编辑时,自动处理掉 ${xx},避免识别成 公式表达式
|
||||
*/
|
||||
@autobind
|
||||
replaceExpression(expression: any): any {
|
||||
outReplaceExpression(expression: any): any {
|
||||
if (expression && isString(expression) && isExpression(expression)) {
|
||||
return expression.replace(/(^|[^\\])\$\{/g, '\\${');
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
|
||||
@autobind
|
||||
inReplaceExpression(expression: any): any {
|
||||
if (expression && isString(expression)) {
|
||||
return expression.replace(/\\\$\{/g, '${');
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
|
||||
// 根据 name 值 判断当前表达式是否 存在循环引用问题
|
||||
@autobind
|
||||
isLoopExpression(expression: any, selfName: string): boolean {
|
||||
if (!expression || !selfName || !isString(expression)) {
|
||||
return false;
|
||||
}
|
||||
const variables = FormulaExec.collect(expression);
|
||||
let variables = [];
|
||||
try {
|
||||
variables = FormulaExec.collect(expression);
|
||||
} catch (e) {}
|
||||
return variables.some((variable: string) => variable === selfName);
|
||||
}
|
||||
|
||||
@ -267,36 +265,79 @@ export default class FormulaControl extends React.Component<
|
||||
return false;
|
||||
}
|
||||
|
||||
async resolveVariablesFromScope() {
|
||||
const {node, manager} = this.props.formProps || this.props;
|
||||
await manager?.getContextSchemas(node);
|
||||
const dataPropsAsOptions = manager?.dataSchema?.getDataPropsAsOptions();
|
||||
matchDate(str: string): boolean {
|
||||
const matchDate =
|
||||
/^(.+)?(\+|-)(\d+)(minute|min|hour|day|week|month|year|weekday|second|millisecond)s?$/i;
|
||||
const m = matchDate.exec(str);
|
||||
return m ? (m[1] ? this.matchDate(m[1]) : true) : false;
|
||||
}
|
||||
|
||||
if (dataPropsAsOptions) {
|
||||
return dataPropsAsOptions.map((item: any) => ({
|
||||
selectMode: 'tree',
|
||||
...item
|
||||
}));
|
||||
matchDateRange(str: string): boolean {
|
||||
if (/^(now|today)$/.test(str)) {
|
||||
return true;
|
||||
}
|
||||
return [];
|
||||
return this.matchDate(str);
|
||||
}
|
||||
|
||||
// 日期类组件 & 是否存在快捷键判断
|
||||
@autobind
|
||||
hasDateShortcutkey(str: string): boolean {
|
||||
const {DateTimeType} = this.props;
|
||||
|
||||
if (DateTimeType === FormulaDateType.IsDate) {
|
||||
if (/^(now|today)$/.test(str)) {
|
||||
return true;
|
||||
}
|
||||
return this.matchDate(str);
|
||||
} else if (DateTimeType === FormulaDateType.IsRange) {
|
||||
const start_end = str?.split?.(',');
|
||||
if (start_end && start_end.length === 2) {
|
||||
return (
|
||||
this.matchDateRange(start_end[0].trim()) &&
|
||||
this.matchDateRange(start_end[1].trim())
|
||||
);
|
||||
}
|
||||
}
|
||||
// 非日期类组件使用,也直接false
|
||||
// if (DateTimeType === FormulaDateType.NotDate) {
|
||||
// return false;
|
||||
// }
|
||||
return false;
|
||||
}
|
||||
|
||||
@autobind
|
||||
transExpr(str: string) {
|
||||
if (
|
||||
typeof str === 'string' &&
|
||||
str?.slice(0, 2) === '${' &&
|
||||
str?.slice(-1) === '}'
|
||||
) {
|
||||
// 非最外层内容还存在表达式情况
|
||||
if (isExpression(str.slice(2, -1))) {
|
||||
return str;
|
||||
}
|
||||
if (str.lastIndexOf('${') > str.indexOf('}') && str.indexOf('}') > -1) {
|
||||
return str;
|
||||
}
|
||||
return str.slice(2, -1);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleConfirm(value: any) {
|
||||
this.props?.onChange?.(value);
|
||||
const val = !value
|
||||
? undefined
|
||||
: isExpression(value) || this.hasDateShortcutkey(value)
|
||||
? value
|
||||
: `\${${value}}`;
|
||||
this.props?.onChange?.(val);
|
||||
}
|
||||
|
||||
handleSimpleInputChange = debounce(
|
||||
(value: any) => {
|
||||
const curValue = this.replaceExpression(value);
|
||||
this.props?.onChange?.(curValue);
|
||||
},
|
||||
250,
|
||||
{
|
||||
trailing: true,
|
||||
leading: false
|
||||
}
|
||||
);
|
||||
handleSimpleInputChange = (value: any) => {
|
||||
const curValue = this.outReplaceExpression(value);
|
||||
this.props?.onChange?.(curValue);
|
||||
};
|
||||
|
||||
handleInputChange = (value: any) => {
|
||||
this.props?.onChange?.(value);
|
||||
@ -305,12 +346,13 @@ export default class FormulaControl extends React.Component<
|
||||
// 剔除掉一些用不上的属性
|
||||
@autobind
|
||||
filterCustomRendererProps(rendererSchema: any) {
|
||||
const {data, name} = this.props;
|
||||
const {data, name, placeholder} = this.props;
|
||||
|
||||
let curRendererSchema: any = null;
|
||||
if (rendererSchema) {
|
||||
curRendererSchema = Object.assign({}, rendererSchema, data, {
|
||||
type: rendererSchema.type ?? data.type
|
||||
type: rendererSchema.type ?? data.type,
|
||||
name: rendererSchema.name ?? data.name ?? 'value'
|
||||
});
|
||||
|
||||
// 默认要剔除的字段
|
||||
@ -361,27 +403,23 @@ export default class FormulaControl extends React.Component<
|
||||
}
|
||||
curRendererSchema = omit(curRendererSchema, deleteProps);
|
||||
|
||||
// 避免没有清空icon
|
||||
if (
|
||||
curRendererSchema.clearable !== undefined &&
|
||||
!curRendererSchema.clearable
|
||||
) {
|
||||
curRendererSchema.clearable = true;
|
||||
}
|
||||
// 设置可清空
|
||||
curRendererSchema.clearable = true;
|
||||
|
||||
// 设置统一的占位提示
|
||||
if (curRendererSchema.type === 'select') {
|
||||
curRendererSchema.placeholder = '请选择默认值';
|
||||
!curRendererSchema.placeholder &&
|
||||
(curRendererSchema.placeholder = '请选择静态值');
|
||||
curRendererSchema.inputClassName =
|
||||
'ae-editor-FormulaControl-select-style';
|
||||
} else if (placeholder) {
|
||||
curRendererSchema.placeholder = placeholder;
|
||||
} else {
|
||||
curRendererSchema.placeholder = '请输入静态默认值';
|
||||
curRendererSchema.placeholder = '请输入静态值';
|
||||
}
|
||||
|
||||
// 设置popOverContainer
|
||||
curRendererSchema.popOverContainer = window.document.body;
|
||||
}
|
||||
|
||||
return curRendererSchema;
|
||||
}
|
||||
|
||||
@ -411,7 +449,6 @@ export default class FormulaControl extends React.Component<
|
||||
variables,
|
||||
placeholder,
|
||||
simple,
|
||||
evalMode,
|
||||
rendererSchema,
|
||||
rendererWrapper,
|
||||
manager,
|
||||
@ -420,7 +457,6 @@ export default class FormulaControl extends React.Component<
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
const labelText = typeof label === 'string' ? label : '';
|
||||
// 自身字段
|
||||
const selfName = this.props?.data?.name;
|
||||
|
||||
@ -437,17 +473,26 @@ export default class FormulaControl extends React.Component<
|
||||
|
||||
// 判断是否含有公式表达式
|
||||
const isTypeError = !this.isExpectType(value);
|
||||
const exprValue = this.transExpr(value);
|
||||
|
||||
const isError = isLoop || isTypeError;
|
||||
|
||||
const highlightValue = isExpression(value)
|
||||
? FormulaEditor.highlightValue(
|
||||
value,
|
||||
this.state.variables,
|
||||
evalMode ?? this.state.evalMode
|
||||
)
|
||||
? FormulaEditor.highlightValue(exprValue, this.state.variables) || {
|
||||
html: exprValue
|
||||
}
|
||||
: value;
|
||||
|
||||
// 公式表达式弹窗内容过滤
|
||||
const filterValue = isExpression(value)
|
||||
? exprValue
|
||||
: this.hasDateShortcutkey(value)
|
||||
? value
|
||||
: undefined;
|
||||
|
||||
// 值 是表达式或日期快捷
|
||||
const isFx = !simple && (isExpr || this.hasDateShortcutkey(value));
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cx(
|
||||
@ -456,51 +501,78 @@ export default class FormulaControl extends React.Component<
|
||||
className
|
||||
)}
|
||||
>
|
||||
{!simple && !rendererSchema && !isExpr && (
|
||||
<InputBox
|
||||
className="ae-editor-FormulaControl-input"
|
||||
value={value}
|
||||
clearable={true}
|
||||
placeholder={placeholder}
|
||||
onChange={this.handleSimpleInputChange}
|
||||
/>
|
||||
)}
|
||||
{!simple && rendererSchema && !isExpr && (
|
||||
<div
|
||||
className={cx(
|
||||
'ae-editor-FormulaControl-custom-renderer',
|
||||
rendererWrapper ? 'border-wrapper' : ''
|
||||
)}
|
||||
>
|
||||
{render('inner', this.filterCustomRendererProps(rendererSchema), {
|
||||
inputOnly: true,
|
||||
value: value,
|
||||
data: useExternalFormData
|
||||
? {
|
||||
...this.props.data
|
||||
}
|
||||
: {},
|
||||
onChange: this.handleSimpleInputChange,
|
||||
manager: manager
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
{!simple && isExpr && (
|
||||
<ResultBox
|
||||
className={cx(
|
||||
'ae-editor-FormulaControl-ResultBox',
|
||||
isError ? 'is-error' : ''
|
||||
)}
|
||||
allowInput={false}
|
||||
clearable={true}
|
||||
value={value}
|
||||
result={highlightValue}
|
||||
itemRender={this.renderFormulaValue}
|
||||
onChange={this.handleInputChange}
|
||||
onResultChange={() => {
|
||||
this.handleInputChange(undefined);
|
||||
{/* 非简单模式 & 非表达式 & 非日期快捷 & 无自定义渲染 */}
|
||||
{!simple &&
|
||||
!isExpr &&
|
||||
!this.hasDateShortcutkey(value) &&
|
||||
!rendererSchema && (
|
||||
<InputBox
|
||||
className="ae-editor-FormulaControl-input"
|
||||
value={this.inReplaceExpression(value)}
|
||||
clearable={true}
|
||||
placeholder={placeholder ?? '请输入静态值'}
|
||||
onChange={this.handleSimpleInputChange}
|
||||
/>
|
||||
)}
|
||||
{/* 非简单模式 & 非表达式 & 非日期快捷 & 自定义渲染 */}
|
||||
{!simple &&
|
||||
!isExpr &&
|
||||
!this.hasDateShortcutkey(value) &&
|
||||
rendererSchema && (
|
||||
<div
|
||||
className={cx(
|
||||
'ae-editor-FormulaControl-custom-renderer',
|
||||
rendererWrapper ? 'border-wrapper' : ''
|
||||
)}
|
||||
>
|
||||
{render('inner', this.filterCustomRendererProps(rendererSchema), {
|
||||
inputOnly: true,
|
||||
value: this.inReplaceExpression(value),
|
||||
data: useExternalFormData
|
||||
? {
|
||||
...this.props.data
|
||||
}
|
||||
: {},
|
||||
onChange: this.handleSimpleInputChange,
|
||||
manager: manager
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
{/* 非简单模式 &(表达式 或 日期快捷)*/}
|
||||
{isFx && (
|
||||
<TooltipWrapper
|
||||
trigger="hover"
|
||||
placement="top"
|
||||
style={{fontSize: '12px'}}
|
||||
tooltip={{
|
||||
tooltipTheme: 'dark',
|
||||
mouseLeaveDelay: 20,
|
||||
content: exprValue,
|
||||
children: () => this.renderFormulaValue(highlightValue)
|
||||
}}
|
||||
/>
|
||||
>
|
||||
<div className="ae-editor-FormulaControl-tooltipBox">
|
||||
<ResultBox
|
||||
className={cx(
|
||||
'ae-editor-FormulaControl-ResultBox',
|
||||
isError ? 'is-error' : ''
|
||||
)}
|
||||
allowInput={false}
|
||||
clearable={true}
|
||||
value={value}
|
||||
result={{
|
||||
html: this.hasDateShortcutkey(value)
|
||||
? '已配置相对值'
|
||||
: '已配置表达式'
|
||||
}}
|
||||
itemRender={this.renderFormulaValue}
|
||||
onChange={this.handleInputChange}
|
||||
onResultChange={() => {
|
||||
this.handleInputChange(undefined);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</TooltipWrapper>
|
||||
)}
|
||||
<PickerContainer
|
||||
showTitle={false}
|
||||
@ -514,11 +586,11 @@ export default class FormulaControl extends React.Component<
|
||||
return (
|
||||
<FormulaEditor
|
||||
{...rest}
|
||||
evalMode={evalMode ?? this.state.evalMode}
|
||||
evalMode={true}
|
||||
variableMode={rest.variableMode ?? this.state.variableMode}
|
||||
variables={this.state.variables}
|
||||
header={header || labelText}
|
||||
value={isString(value) ? value : undefined}
|
||||
header={header || '表达式'}
|
||||
value={filterValue}
|
||||
onChange={onChange}
|
||||
selfVariableName={selfName}
|
||||
/>
|
||||
@ -530,26 +602,26 @@ export default class FormulaControl extends React.Component<
|
||||
>
|
||||
{({onClick}: {onClick: (e: React.MouseEvent) => void}) => (
|
||||
<Button
|
||||
className="ae-editor-FormulaControl-button"
|
||||
size="sm"
|
||||
tooltip={'点击配置表达式'}
|
||||
tooltipPlacement="left"
|
||||
tooltip={{
|
||||
enterable: false,
|
||||
content: '点击配置表达式',
|
||||
placement: 'left',
|
||||
mouseLeaveDelay: 0
|
||||
}}
|
||||
onClick={onClick}
|
||||
// active={simple && value} // 不需要,避免 hover 时无任何反馈效果
|
||||
>
|
||||
<Icon
|
||||
icon="function"
|
||||
className={cx('ae-editor-FormulaControl-icon', 'icon', {
|
||||
['is-filled']: !!value
|
||||
['is-filled']: !!isFx
|
||||
})}
|
||||
/>
|
||||
</Button>
|
||||
)}
|
||||
</PickerContainer>
|
||||
{isExpr && !isError && (
|
||||
<div className="desc-msg info-msg" title={value}>
|
||||
{value}
|
||||
</div>
|
||||
)}
|
||||
{isError && (
|
||||
<div className="desc-msg error-msg">
|
||||
{isLoop ? '当前表达式异常(存在循环引用)' : '数值类型不匹配'}
|
||||
|
@ -180,11 +180,12 @@ export class StatusControl extends React.Component<
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'ae-formulaControl',
|
||||
name: 'expression',
|
||||
type: 'ae-expressionFormulaControl',
|
||||
label: '表达式',
|
||||
name: 'expression',
|
||||
placeholder: `请输入${label}条件`,
|
||||
visibleOn: 'this.statusType === 2'
|
||||
visibleOn: 'this.statusType === 2',
|
||||
onChange: (value: any) => {}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -11,7 +11,7 @@ import {FormItem, Button, Overlay, PopOver, Icon, Switch} from 'amis';
|
||||
|
||||
import {isObject, autobind} from 'amis-editor-core';
|
||||
|
||||
import type {Action} from 'amis/lib/types';
|
||||
import type {Action} from 'amis';
|
||||
import type {SchemaCollection} from 'amis/lib/Schema';
|
||||
import type {IScopedContext} from 'amis-core';
|
||||
import type {FormSchema} from 'amis/lib/schema';
|
||||
|
@ -280,6 +280,7 @@ export default class ActionDialog extends React.Component<ActionDialogProp> {
|
||||
className: 'action-panel-title',
|
||||
visibleOn: 'data.actionType'
|
||||
},
|
||||
/*
|
||||
{
|
||||
name: 'expression',
|
||||
title: '',
|
||||
@ -293,6 +294,17 @@ export default class ActionDialog extends React.Component<ActionDialogProp> {
|
||||
size: 'lg',
|
||||
placeholder: '默认执行该动作',
|
||||
visibleOn: 'data.actionType'
|
||||
},
|
||||
*/
|
||||
{
|
||||
name: 'expression',
|
||||
label: '执行条件',
|
||||
type: 'ae-expressionFormulaControl',
|
||||
variables: '${variables}',
|
||||
mode: 'horizontal',
|
||||
size: 'lg',
|
||||
placeholder: '默认执行该动作',
|
||||
visibleOn: 'data.actionType'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
|
||||
type: 'wrapper',
|
||||
className: 'p-none',
|
||||
body: [
|
||||
/**
|
||||
{
|
||||
label: '页面地址',
|
||||
type: 'input-formula',
|
||||
@ -64,6 +65,18 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
|
||||
required: true,
|
||||
visibleOn: 'data.actionType === "url"'
|
||||
},
|
||||
*/
|
||||
{
|
||||
name: 'url',
|
||||
label: '页面地址',
|
||||
type: 'ae-textareaFormulaControl',
|
||||
variables: '${variables}',
|
||||
mode: 'horizontal',
|
||||
// placeholder: 'http://', 长文本暂不支持
|
||||
size: 'lg',
|
||||
required: true,
|
||||
visibleOn: 'data.actionType === "url"'
|
||||
},
|
||||
{
|
||||
type: 'combo',
|
||||
name: 'params',
|
||||
@ -75,11 +88,10 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
|
||||
{
|
||||
name: 'key',
|
||||
placeholder: '参数名',
|
||||
type: 'input-text',
|
||||
mode: 'inline',
|
||||
size: 'xs'
|
||||
type: 'input-text'
|
||||
},
|
||||
{
|
||||
/**
|
||||
{
|
||||
name: 'val',
|
||||
placeholder: '参数值',
|
||||
type: 'input-formula',
|
||||
@ -88,6 +100,14 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
|
||||
variableMode: 'tabs',
|
||||
inputMode: 'input-group',
|
||||
size: 'xs'
|
||||
},
|
||||
*/
|
||||
{
|
||||
type: 'ae-formulaControl',
|
||||
variables: '${variables}',
|
||||
name: 'val',
|
||||
variableMode: 'tabs',
|
||||
placeholder: '参数值'
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -306,23 +326,21 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
|
||||
</div>
|
||||
);
|
||||
},
|
||||
schema: getArgsWrapper({
|
||||
type: 'wrapper',
|
||||
className: 'p-none',
|
||||
body: [
|
||||
{
|
||||
type: 'button-group-select',
|
||||
name: 'msgType',
|
||||
label: '消息类型',
|
||||
value: 'info',
|
||||
required: true,
|
||||
mode: 'horizontal',
|
||||
options: Object.keys(MSG_TYPES).map(key => ({
|
||||
label: MSG_TYPES[key],
|
||||
value: key,
|
||||
level: 'default'
|
||||
}))
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
type: 'button-group-select',
|
||||
name: 'msgType',
|
||||
label: '消息类型',
|
||||
value: 'info',
|
||||
required: true,
|
||||
mode: 'horizontal',
|
||||
options: Object.keys(MSG_TYPES).map(key => ({
|
||||
label: MSG_TYPES[key],
|
||||
value: key,
|
||||
level: 'default'
|
||||
}))
|
||||
},
|
||||
/*
|
||||
{
|
||||
name: 'msg',
|
||||
label: '消息内容',
|
||||
@ -335,81 +353,114 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
|
||||
size: 'lg',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'title',
|
||||
type: 'input-formula',
|
||||
variables: '${variables}',
|
||||
evalMode: false,
|
||||
variableMode: 'tabs',
|
||||
inputMode: 'input-group',
|
||||
label: '标题内容',
|
||||
size: 'lg',
|
||||
mode: 'horizontal'
|
||||
*/
|
||||
{
|
||||
name: 'msg',
|
||||
label: '消息内容',
|
||||
type: 'ae-textareaFormulaControl',
|
||||
mode: 'horizontal',
|
||||
variables: '${variables}',
|
||||
size: 'lg',
|
||||
required: true
|
||||
},
|
||||
/*
|
||||
{
|
||||
name: 'title',
|
||||
type: 'input-formula',
|
||||
variables: '${variables}',
|
||||
evalMode: false,
|
||||
variableMode: 'tabs',
|
||||
inputMode: 'input-group',
|
||||
label: '标题内容',
|
||||
size: 'lg',
|
||||
mode: 'horizontal'
|
||||
},
|
||||
*/
|
||||
{
|
||||
name: 'title',
|
||||
label: '标题内容',
|
||||
type: 'ae-textareaFormulaControl',
|
||||
variables: '${variables}',
|
||||
mode: 'horizontal',
|
||||
size: 'lg'
|
||||
},
|
||||
/*
|
||||
{
|
||||
name: 'timeout',
|
||||
type: 'input-formula',
|
||||
variables: '${variables}',
|
||||
evalMode: false,
|
||||
variableMode: 'tabs',
|
||||
inputMode: 'input-group',
|
||||
label: '持续时间(ms)',
|
||||
size: 'lg',
|
||||
mode: 'horizontal'
|
||||
},
|
||||
*/
|
||||
{
|
||||
name: 'timeout',
|
||||
label: '持续时间(ms)',
|
||||
type: 'ae-formulaControl',
|
||||
rendererSchema: {
|
||||
type: 'input-number'
|
||||
},
|
||||
{
|
||||
name: 'timeout',
|
||||
type: 'input-formula',
|
||||
variables: '${variables}',
|
||||
evalMode: false,
|
||||
variableMode: 'tabs',
|
||||
inputMode: 'input-group',
|
||||
label: '持续时间(ms)',
|
||||
size: 'lg',
|
||||
mode: 'horizontal'
|
||||
},
|
||||
{
|
||||
type: 'button-group-select',
|
||||
name: 'position',
|
||||
value: 'top-right',
|
||||
mode: 'horizontal',
|
||||
label: '显示位置',
|
||||
options: [
|
||||
{
|
||||
label: '左上',
|
||||
value: 'top-left'
|
||||
},
|
||||
valueType: 'number',
|
||||
variables: '${variables}',
|
||||
size: 'lg',
|
||||
mode: 'horizontal'
|
||||
},
|
||||
{
|
||||
type: 'button-group-select',
|
||||
name: 'position',
|
||||
value: 'top-right',
|
||||
mode: 'horizontal',
|
||||
label: '显示位置',
|
||||
options: [
|
||||
{
|
||||
label: '左上',
|
||||
value: 'top-left'
|
||||
},
|
||||
|
||||
{
|
||||
label: '中上',
|
||||
value: 'top-center'
|
||||
},
|
||||
{
|
||||
label: '中上',
|
||||
value: 'top-center'
|
||||
},
|
||||
|
||||
{
|
||||
label: '右上',
|
||||
value: 'top-right'
|
||||
},
|
||||
{
|
||||
label: '右上',
|
||||
value: 'top-right'
|
||||
},
|
||||
|
||||
{
|
||||
label: '左下',
|
||||
value: 'bottom-left'
|
||||
},
|
||||
{
|
||||
label: '中下',
|
||||
value: 'bottom-center'
|
||||
},
|
||||
{
|
||||
label: '左下',
|
||||
value: 'bottom-left'
|
||||
},
|
||||
{
|
||||
label: '中下',
|
||||
value: 'bottom-center'
|
||||
},
|
||||
|
||||
{
|
||||
label: '右下',
|
||||
value: 'bottom-right'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'switch',
|
||||
name: 'closeButton',
|
||||
value: true,
|
||||
label: '展示关闭按钮',
|
||||
mode: 'horizontal'
|
||||
},
|
||||
{
|
||||
type: 'switch',
|
||||
name: 'showIcon',
|
||||
value: true,
|
||||
label: '展示图标',
|
||||
mode: 'horizontal'
|
||||
}
|
||||
]
|
||||
})
|
||||
{
|
||||
label: '右下',
|
||||
value: 'bottom-right'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'switch',
|
||||
name: 'closeButton',
|
||||
value: true,
|
||||
label: '展示关闭按钮',
|
||||
mode: 'horizontal'
|
||||
},
|
||||
{
|
||||
type: 'switch',
|
||||
name: 'showIcon',
|
||||
value: true,
|
||||
label: '展示图标',
|
||||
mode: 'horizontal'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -833,6 +884,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
|
||||
form.setValueByName('__valueInput', undefined);
|
||||
}
|
||||
},
|
||||
/*
|
||||
{
|
||||
name: '__valueInput',
|
||||
type: 'input-formula',
|
||||
@ -846,6 +898,17 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
|
||||
mode: 'horizontal',
|
||||
visibleOn: `data.__addParam && data.__customData && data.__containerType === "all" && data.actionType === "reload" && ${IS_DATA_CONTAINER}`
|
||||
},
|
||||
*/
|
||||
{
|
||||
name: '__valueInput',
|
||||
label: '',
|
||||
type: 'ae-formulaControl',
|
||||
variables: '${variables}',
|
||||
size: 'lg',
|
||||
mode: 'horizontal',
|
||||
required: true,
|
||||
visibleOn: `data.__addParam && data.__customData && data.__containerType === "all" && data.actionType === "reload" && ${IS_DATA_CONTAINER}`
|
||||
},
|
||||
{
|
||||
type: 'combo',
|
||||
name: '__reloadParams',
|
||||
@ -866,6 +929,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
|
||||
valueField: 'value',
|
||||
required: true
|
||||
},
|
||||
/*
|
||||
{
|
||||
name: 'val',
|
||||
type: 'input-formula',
|
||||
@ -875,6 +939,13 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
|
||||
variableMode: 'tabs',
|
||||
inputMode: 'input-group'
|
||||
}
|
||||
*/
|
||||
{
|
||||
name: 'val',
|
||||
type: 'ae-formulaControl',
|
||||
variables: '${variables}',
|
||||
placeholder: '参数值'
|
||||
}
|
||||
],
|
||||
visibleOn: `data.__addParam && data.__customData && data.__containerType === "appoint" && data.actionType === "reload" && ${IS_DATA_CONTAINER}`
|
||||
},
|
||||
@ -1035,6 +1106,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
|
||||
valueField: 'value',
|
||||
required: true
|
||||
},
|
||||
/*
|
||||
{
|
||||
name: 'val',
|
||||
type: 'input-formula',
|
||||
@ -1044,6 +1116,13 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
|
||||
variableMode: 'tabs',
|
||||
inputMode: 'input-group'
|
||||
}
|
||||
*/
|
||||
{
|
||||
name: 'val',
|
||||
type: 'ae-formulaControl',
|
||||
variables: '${variables}',
|
||||
placeholder: '字段值'
|
||||
}
|
||||
],
|
||||
visibleOn: `${IS_DATA_CONTAINER} && data.__containerType === 'appoint' || data.__comboType === 'appoint'`
|
||||
},
|
||||
@ -1084,6 +1163,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
|
||||
required: true,
|
||||
visibleOn: `data.__rendererName`
|
||||
},
|
||||
/*
|
||||
{
|
||||
name: 'val',
|
||||
type: 'input-formula',
|
||||
@ -1092,11 +1172,18 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
|
||||
variableMode: 'tabs',
|
||||
inputMode: 'input-group'
|
||||
}
|
||||
*/
|
||||
{
|
||||
name: 'val',
|
||||
type: 'ae-formulaControl',
|
||||
variables: '${variables}'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
visibleOn: `data.__rendererName === 'combo' && data.__comboType === 'all'`
|
||||
},
|
||||
/*
|
||||
{
|
||||
name: '__valueInput',
|
||||
type: 'input-formula',
|
||||
@ -1110,6 +1197,19 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
|
||||
visibleOn: `(${IS_DATA_CONTAINER} || ${SHOW_SELECT_PROP}) && data.__containerType === 'all'`,
|
||||
required: true
|
||||
},
|
||||
*/
|
||||
{
|
||||
name: '__valueInput',
|
||||
label: '',
|
||||
type: 'ae-formulaControl',
|
||||
variables: '${variables}',
|
||||
size: 'lg',
|
||||
mode: 'horizontal',
|
||||
visibleOn: `(${IS_DATA_CONTAINER} || ${SHOW_SELECT_PROP}) && data.__containerType === 'all'`,
|
||||
required: true
|
||||
},
|
||||
,
|
||||
/*
|
||||
{
|
||||
name: '__valueInput',
|
||||
type: 'input-formula',
|
||||
@ -1123,6 +1223,17 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
|
||||
visibleOn: `data.__rendererName && !${IS_DATA_CONTAINER} && data.__rendererName !== 'combo'`,
|
||||
required: true
|
||||
}
|
||||
*/
|
||||
{
|
||||
name: '__valueInput',
|
||||
label: '数据设置',
|
||||
type: 'ae-formulaControl',
|
||||
variables: '${variables}',
|
||||
size: 'lg',
|
||||
mode: 'horizontal',
|
||||
visibleOn: `data.__rendererName && !${IS_DATA_CONTAINER} && data.__rendererName !== 'combo'`,
|
||||
required: true
|
||||
}
|
||||
]
|
||||
})
|
||||
]
|
||||
@ -1231,7 +1342,8 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
|
||||
type: 'wrapper',
|
||||
className: 'p-none',
|
||||
body: [
|
||||
{
|
||||
/*
|
||||
{
|
||||
name: 'content',
|
||||
type: 'input-formula',
|
||||
variables: '${variables}',
|
||||
@ -1244,6 +1356,17 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
|
||||
visibleOn: 'data.actionType === "copy"',
|
||||
required: true
|
||||
},
|
||||
*/
|
||||
{
|
||||
name: 'content',
|
||||
label: '内容模板',
|
||||
type: 'ae-textareaFormulaControl',
|
||||
variables: '${variables}',
|
||||
mode: 'horizontal',
|
||||
size: 'lg',
|
||||
visibleOn: 'data.actionType === "copy"',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
name: 'copyFormat',
|
||||
@ -1281,6 +1404,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
|
||||
lineNumbers: 'off',
|
||||
glyphMargin: false,
|
||||
tabSize: 2,
|
||||
fontSize: '12px',
|
||||
wordWrap: 'on',
|
||||
lineDecorationsWidth: 0,
|
||||
lineNumbersMinChars: 0,
|
||||
|
@ -226,6 +226,7 @@ export const COMMON_ACTION_SCHEMA_MAP: {
|
||||
valueField: 'value',
|
||||
required: true
|
||||
},
|
||||
/*
|
||||
{
|
||||
name: 'val',
|
||||
type: 'input-formula',
|
||||
@ -235,6 +236,13 @@ export const COMMON_ACTION_SCHEMA_MAP: {
|
||||
variableMode: 'tabs',
|
||||
inputMode: 'input-group'
|
||||
}
|
||||
*/
|
||||
{
|
||||
name: 'val',
|
||||
type: 'ae-formulaControl',
|
||||
variables: '${variables}',
|
||||
placeholder: '变量值'
|
||||
}
|
||||
],
|
||||
visibleOn: `${IS_DATA_CONTAINER}`
|
||||
},
|
||||
@ -269,6 +277,7 @@ export const COMMON_ACTION_SCHEMA_MAP: {
|
||||
type: 'input-text',
|
||||
required: true
|
||||
},
|
||||
/*
|
||||
{
|
||||
name: 'val',
|
||||
type: 'input-formula',
|
||||
@ -277,11 +286,18 @@ export const COMMON_ACTION_SCHEMA_MAP: {
|
||||
variableMode: 'tabs',
|
||||
inputMode: 'input-group'
|
||||
}
|
||||
*/
|
||||
{
|
||||
name: 'val',
|
||||
type: 'ae-formulaControl',
|
||||
variables: '${variables}'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
visibleOn: `data.__rendererName === 'combo'`
|
||||
},
|
||||
/*
|
||||
{
|
||||
name: '__valueInput',
|
||||
type: 'input-formula',
|
||||
@ -295,6 +311,17 @@ export const COMMON_ACTION_SCHEMA_MAP: {
|
||||
visibleOn: `!${IS_DATA_CONTAINER} && data.__rendererName !== 'combo'`,
|
||||
required: true
|
||||
}
|
||||
*/
|
||||
{
|
||||
name: '__valueInput',
|
||||
label: '变量赋值',
|
||||
type: 'ae-formulaControl',
|
||||
variables: '${variables}',
|
||||
size: 'lg',
|
||||
mode: 'horizontal',
|
||||
visibleOn: `!${IS_DATA_CONTAINER} && data.__rendererName !== 'combo'`,
|
||||
required: true
|
||||
}
|
||||
]
|
||||
})
|
||||
},
|
||||
|
@ -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;
|
@ -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 {}
|
234
packages/amis-editor/src/renderer/textarea-formula/plugin.tsx
Normal file
234
packages/amis-editor/src/renderer/textarea-formula/plugin.tsx
Normal 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() {}
|
||||
}
|
59
packages/amis-editor/src/renderer/textarea-formula/utils.ts
Normal file
59
packages/amis-editor/src/renderer/textarea-formula/utils.ts
Normal 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;
|
||||
}
|
@ -4,12 +4,16 @@ import {
|
||||
defaultValue,
|
||||
isObject,
|
||||
tipedLabel,
|
||||
DSField
|
||||
DSField,
|
||||
EditorManager
|
||||
} from 'amis-editor-core';
|
||||
import {remarkTpl} from '../component/BaseControl';
|
||||
import {SchemaObject} from 'amis/lib/Schema';
|
||||
import flatten from 'lodash/flatten';
|
||||
import _ from 'lodash';
|
||||
import {InputComponentName} from '../component/InputComponentName';
|
||||
import {FormulaDateType} from '../renderer/FormulaControl';
|
||||
import {VariableItem} from 'amis-ui/lib/components/formula/Editor';
|
||||
|
||||
/**
|
||||
* @deprecated 兼容当前组件的switch
|
||||
@ -345,60 +349,72 @@ setSchemaTpl(
|
||||
mode?: string; // 自定义展示默认值,上下展示: vertical, 左右展示: horizontal
|
||||
label?: string; // 表单项 label
|
||||
name?: string; // 表单项 name
|
||||
header?: string; // 表达式弹窗标题
|
||||
placeholder?: string; // 表达式自定义渲染 占位符
|
||||
rendererSchema?: any;
|
||||
rendererWrapper?: boolean; // 自定义渲染器 是否需要浅色边框包裹
|
||||
needDeleteValue?: boolean; // 是否需要剔除默认值
|
||||
needDeleteProps?: string[]; // 需要剔除的其他属性,默认 deleteProps 中包含一些通用属性
|
||||
useSelectMode?: boolean; // 是否使用Select选择设置模式,需要确保 rendererSchema.options 不为 undefined
|
||||
valueType?: string; // 用于设置期望数值类型
|
||||
visibleOn?: string; // 用于控制显示的表达式
|
||||
evalMode?: boolean; // 为false时,则会用 ${这里面才是表达式} 包裹变量
|
||||
DateTimeType?: FormulaDateType; // 日期类组件要支持 表达式 & 相对值
|
||||
variables?: Array<VariableItem> | Function; // 自定义变量集合
|
||||
requiredDataPropsVariables?: boolean; // 是否再从amis数据域中取变量结合, 默认 false
|
||||
variableMode?: 'tabs' | 'tree'; // 变量展现模式
|
||||
[key: string]: any; // 其他属性,例如包括表单项pipeIn\Out 等等
|
||||
}) => {
|
||||
let curRendererSchema = config?.rendererSchema;
|
||||
if (
|
||||
config?.useSelectMode &&
|
||||
curRendererSchema &&
|
||||
curRendererSchema.options
|
||||
) {
|
||||
const {
|
||||
rendererSchema,
|
||||
useSelectMode,
|
||||
mode,
|
||||
visibleOn,
|
||||
label,
|
||||
name,
|
||||
rendererWrapper,
|
||||
needDeleteProps,
|
||||
valueType,
|
||||
header,
|
||||
placeholder,
|
||||
DateTimeType,
|
||||
variables,
|
||||
requiredDataPropsVariables,
|
||||
variableMode,
|
||||
...rest
|
||||
} = config || {};
|
||||
let curRendererSchema = rendererSchema;
|
||||
|
||||
if (useSelectMode && curRendererSchema && curRendererSchema.options) {
|
||||
curRendererSchema = {
|
||||
...curRendererSchema,
|
||||
type: 'select'
|
||||
};
|
||||
}
|
||||
|
||||
if (config?.mode === 'vertical') {
|
||||
// 上下展示,可避免 自定义渲染器 出现挤压
|
||||
return {
|
||||
type: 'group',
|
||||
mode: 'vertical',
|
||||
visibleOn: config?.visibleOn,
|
||||
body: [
|
||||
{
|
||||
type: 'ae-formulaControl',
|
||||
label: config?.label ?? '默认值',
|
||||
name: config?.name || 'value',
|
||||
rendererSchema: curRendererSchema,
|
||||
rendererWrapper: config?.rendererWrapper,
|
||||
needDeleteValue: config?.needDeleteValue,
|
||||
valueType: config?.valueType,
|
||||
visibleOn: config?.visibleOn,
|
||||
evalMode: config?.evalMode ?? false // 默认需要${}包裹变量
|
||||
}
|
||||
]
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
type: 'group',
|
||||
// 默认左右展示
|
||||
return {
|
||||
type: 'ae-formulaControl',
|
||||
label: config?.label || '默认值',
|
||||
name: config?.name || 'value',
|
||||
rendererSchema: curRendererSchema,
|
||||
rendererWrapper: config?.rendererWrapper,
|
||||
needDeleteValue: config?.needDeleteValue,
|
||||
valueType: config?.valueType,
|
||||
visibleOn: config?.visibleOn,
|
||||
evalMode: config?.evalMode ?? false // 默认需要${}包裹变量
|
||||
};
|
||||
}
|
||||
// 上下展示,可避免 自定义渲染器 出现挤压
|
||||
mode: mode === 'vertical' ? 'vertical' : 'horizontal',
|
||||
visibleOn,
|
||||
body: [
|
||||
{
|
||||
type: 'ae-formulaControl',
|
||||
label: label ?? '默认值',
|
||||
name: name || 'value',
|
||||
rendererWrapper,
|
||||
needDeleteProps,
|
||||
valueType,
|
||||
header,
|
||||
placeholder,
|
||||
rendererSchema: curRendererSchema,
|
||||
variables,
|
||||
requiredDataPropsVariables,
|
||||
variableMode,
|
||||
DateTimeType: DateTimeType ?? FormulaDateType.NotDate,
|
||||
...rest
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
@ -493,9 +509,80 @@ setSchemaTpl('selectDateRangeType', {
|
||||
]
|
||||
});
|
||||
|
||||
setSchemaTpl(
|
||||
'optionsMenuTpl',
|
||||
(config: {manager: EditorManager; [key: string]: any}) => {
|
||||
const {manager, ...rest} = config;
|
||||
// 设置 options 中变量集合
|
||||
function getOptionVars(that: any) {
|
||||
let schema = manager.store.valueWithoutHiddenProps;
|
||||
let children = [];
|
||||
if (schema.labelField) {
|
||||
children.push({
|
||||
label: '显示字段',
|
||||
value: schema.labelField,
|
||||
tag: typeof schema.labelField
|
||||
});
|
||||
}
|
||||
if (schema.valueField) {
|
||||
children.push({
|
||||
label: '值字段',
|
||||
value: schema.valueField,
|
||||
tag: typeof schema.valueField
|
||||
});
|
||||
}
|
||||
if (schema.options) {
|
||||
let optionItem = _.reduce(
|
||||
schema.options,
|
||||
function (result, item) {
|
||||
return {...result, ...item};
|
||||
},
|
||||
{}
|
||||
);
|
||||
delete optionItem?.$$id;
|
||||
|
||||
optionItem = _.omit(
|
||||
optionItem,
|
||||
_.map(children, item => item?.label)
|
||||
);
|
||||
|
||||
let otherItem = _.map(_.keys(optionItem), item => ({
|
||||
label:
|
||||
item === 'label' ? '选项文本' : item === 'value' ? '选项值' : item,
|
||||
value: item,
|
||||
tag: typeof optionItem[item]
|
||||
}));
|
||||
|
||||
children.push(...otherItem);
|
||||
}
|
||||
let variablesArr = [
|
||||
{
|
||||
label: '选项字段',
|
||||
children
|
||||
}
|
||||
];
|
||||
return variablesArr;
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'ae-textareaFormulaControl',
|
||||
mode: 'normal',
|
||||
label: tipedLabel(
|
||||
'选项模板',
|
||||
'自定义选项渲染模板,支持JSX、数据域变量使用'
|
||||
),
|
||||
name: 'menuTpl',
|
||||
variables: getOptionVars,
|
||||
requiredDataPropsVariables: true,
|
||||
...rest
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
setSchemaTpl('menuTpl', {
|
||||
type: 'ae-formulaControl',
|
||||
label: tipedLabel('模板', '选项渲染模板,支持JSX,变量使用\\${xx}'),
|
||||
type: 'ae-textareaFormulaControl',
|
||||
mode: 'normal',
|
||||
label: tipedLabel('模板', '自定义选项渲染模板,支持JSX、数据域变量使用'),
|
||||
name: 'menuTpl'
|
||||
});
|
||||
|
||||
@ -1000,6 +1087,7 @@ setSchemaTpl('badge', {
|
||||
type: 'ae-badge'
|
||||
});
|
||||
|
||||
// 暂未使用
|
||||
setSchemaTpl('formulaControl', (schema: object = {}) => {
|
||||
return {
|
||||
type: 'ae-formulaControl',
|
||||
@ -1061,7 +1149,8 @@ setSchemaTpl('app-page-args', {
|
||||
valueField: 'value',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
/*
|
||||
{
|
||||
name: 'val',
|
||||
type: 'input-formula',
|
||||
placeholder: '参数值',
|
||||
@ -1070,6 +1159,13 @@ setSchemaTpl('app-page-args', {
|
||||
variableMode: 'tabs',
|
||||
inputMode: 'input-group'
|
||||
}
|
||||
*/
|
||||
{
|
||||
name: 'val',
|
||||
type: 'ae-formulaControl',
|
||||
variables: '${variables}',
|
||||
placeholder: '参数值'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
@ -1091,3 +1187,26 @@ setSchemaTpl(
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
setSchemaTpl('virtualThreshold', {
|
||||
name: 'virtualThreshold',
|
||||
type: 'input-number',
|
||||
min: 1,
|
||||
step: 1,
|
||||
precision: 0,
|
||||
label: tipedLabel(
|
||||
'虚拟列表阈值',
|
||||
'当选项数量超过阈值后,会开启虚拟列表以优化性能'
|
||||
),
|
||||
pipeOut: (value: any) => value || undefined
|
||||
});
|
||||
|
||||
setSchemaTpl('virtualItemHeight', {
|
||||
name: 'itemHeight',
|
||||
type: 'input-number',
|
||||
min: 1,
|
||||
step: 1,
|
||||
precision: 0,
|
||||
label: tipedLabel('选项高度', '开启虚拟列表时每个选项的高度'),
|
||||
pipeOut: (value: any) => value || undefined
|
||||
});
|
||||
|
@ -54,9 +54,14 @@ setSchemaTpl(
|
||||
if (value === 'static') {
|
||||
form.setValueByName('style.inset', undefined);
|
||||
form.setValueByName('style.zIndex', undefined);
|
||||
form.setValueByName('originPosition', undefined);
|
||||
} else if (value === 'fixed' || value === 'absolute') {
|
||||
// 默认使用右下角进行相对定位
|
||||
form.setValueByName('style.inset', 'auto 50px 50px auto');
|
||||
form.setValueByName('originPosition', 'right-bottom');
|
||||
} else if (value === 'relative') {
|
||||
form.setValueByName('style.inset', 'auto');
|
||||
form.setValueByName('originPosition', undefined);
|
||||
}
|
||||
},
|
||||
options: [
|
||||
@ -138,7 +143,6 @@ setSchemaTpl(
|
||||
};
|
||||
},
|
||||
pipeOut: (value: any) => {
|
||||
console.log('pipeOut:', value);
|
||||
return `${value.insetTop ?? 'auto'} ${value.insetRight ?? 'auto'} ${
|
||||
value.insetBottom ?? 'auto'
|
||||
} ${value.insetLeft ?? 'auto'}`;
|
||||
@ -246,7 +250,7 @@ setSchemaTpl(
|
||||
},
|
||||
options: [
|
||||
{
|
||||
label: '默认',
|
||||
label: '块级(默认)',
|
||||
value: 'block'
|
||||
},
|
||||
{
|
||||
@ -508,7 +512,7 @@ setSchemaTpl(
|
||||
pipeOut: config?.pipeOut,
|
||||
options: [
|
||||
{
|
||||
label: '默认(不换行)',
|
||||
label: '不换行(默认)',
|
||||
value: 'nowrap'
|
||||
},
|
||||
{
|
||||
@ -588,7 +592,15 @@ setSchemaTpl(
|
||||
clearable: true,
|
||||
unitOptions: config?.unitOptions ?? LayoutUnitOptions,
|
||||
pipeIn: config?.pipeIn,
|
||||
pipeOut: config?.pipeOut
|
||||
// pipeOut: config?.pipeOut,
|
||||
pipeOut: (value: string) => {
|
||||
const curValue = parseInt(value);
|
||||
if (curValue || curValue === 0) {
|
||||
return value;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
@ -681,7 +693,15 @@ setSchemaTpl(
|
||||
clearable: true,
|
||||
unitOptions: config?.unitOptions ?? LayoutUnitOptions,
|
||||
pipeIn: config?.pipeIn,
|
||||
pipeOut: config?.pipeOut
|
||||
// pipeOut: config?.pipeOut,
|
||||
pipeOut: (value: string) => {
|
||||
const curValue = parseInt(value);
|
||||
if (curValue || curValue === 0) {
|
||||
return value;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
@ -712,7 +732,15 @@ setSchemaTpl(
|
||||
clearable: true,
|
||||
unitOptions: config?.unitOptions ?? LayoutUnitOptions,
|
||||
pipeIn: config?.pipeIn,
|
||||
pipeOut: config?.pipeOut
|
||||
// pipeOut: config?.pipeOut
|
||||
pipeOut: (value: string) => {
|
||||
const curValue = parseInt(value);
|
||||
if (curValue || curValue === 0) {
|
||||
return value;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
@ -743,7 +771,15 @@ setSchemaTpl(
|
||||
clearable: true,
|
||||
unitOptions: config?.unitOptions ?? LayoutUnitOptions,
|
||||
pipeIn: config?.pipeIn,
|
||||
pipeOut: config?.pipeOut
|
||||
// pipeOut: config?.pipeOut
|
||||
pipeOut: (value: string) => {
|
||||
const curValue = parseInt(value);
|
||||
if (curValue || curValue === 0) {
|
||||
return value;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
@ -848,7 +884,15 @@ setSchemaTpl(
|
||||
clearable: true,
|
||||
unitOptions: config?.unitOptions ?? LayoutUnitOptions,
|
||||
pipeIn: config?.pipeIn,
|
||||
pipeOut: config?.pipeOut
|
||||
// pipeOut: config?.pipeOut
|
||||
pipeOut: (value: string) => {
|
||||
const curValue = parseInt(value);
|
||||
if (curValue || curValue === 0) {
|
||||
return value;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
@ -879,7 +923,15 @@ setSchemaTpl(
|
||||
clearable: true,
|
||||
unitOptions: config?.unitOptions ?? LayoutUnitOptions,
|
||||
pipeIn: config?.pipeIn,
|
||||
pipeOut: config?.pipeOut
|
||||
// pipeOut: config?.pipeOut
|
||||
pipeOut: (value: string) => {
|
||||
const curValue = parseInt(value);
|
||||
if (curValue || curValue === 0) {
|
||||
return value;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
@ -900,7 +952,7 @@ setSchemaTpl(
|
||||
type: 'input-number',
|
||||
label:
|
||||
config?.label ||
|
||||
tipedLabel('最小高度', '最小宽度即当前元素最小的垂直展示区域'),
|
||||
tipedLabel('最小高度', '最小高度即当前元素最小的垂直展示区域'),
|
||||
name: config?.name || 'style.minHeight',
|
||||
value: config?.value,
|
||||
max: '${style.maxHeight | toInt}',
|
||||
@ -910,7 +962,15 @@ setSchemaTpl(
|
||||
clearable: true,
|
||||
unitOptions: config?.unitOptions ?? LayoutUnitOptions,
|
||||
pipeIn: config?.pipeIn,
|
||||
pipeOut: config?.pipeOut
|
||||
// pipeOut: config?.pipeOut
|
||||
pipeOut: (value: string) => {
|
||||
const curValue = parseInt(value);
|
||||
if (curValue || curValue === 0) {
|
||||
return value;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
|
@ -9,7 +9,7 @@ const path = require('path');
|
||||
module.exports = {
|
||||
mode: 'production', // development production
|
||||
entry: {
|
||||
index: ['./src/index.tsx'],
|
||||
index: ['./src/index.tsx']
|
||||
},
|
||||
output: {
|
||||
path: path.join(__dirname, 'lib'),
|
||||
@ -48,9 +48,9 @@ module.exports = {
|
||||
// webpackImporter: true, // 开启 / 关闭默认的 Webpack importer。默认为true,所以不需要额外配置这个
|
||||
sassOptions: {
|
||||
// 正常情况下不需要这个配置项,但有些情况下需要(比如导入的sass文件包含 media queries等)。
|
||||
includePaths: ['node_modules', '../../node_modules'],
|
||||
},
|
||||
},
|
||||
includePaths: ['node_modules', '../../node_modules']
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user