feat: 弹窗参数映射支持公式 (#9960)

This commit is contained in:
liaoxuezhi 2024-04-03 11:33:42 +08:00 committed by GitHub
parent 067b82fa51
commit e64b239dd3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 282 additions and 13 deletions

View File

@ -115,6 +115,139 @@ order: 61
}
```
## 搭配公式使用
> 6.4.0 及以上版本
通过配置 `formula` 属性,可以配合公式使用。
```schema: scope="body"
{
"type": "form",
"api": "/api/mock2/form/saveForm",
debug: true,
"body": [
{
"type": "json-schema",
"name": "value",
"label": "字段值",
"schema": {
type: 'object',
additionalProperties: false,
required: ['id', 'name'],
properties: {
id: {
type: 'number',
title: 'ID'
},
name: {
type: 'string',
title: '名称'
},
description: {
type: 'string',
title: '描述'
},
date: {
type: 'object',
title: '日期',
additionalProperties: false,
required: ['year', 'month', 'day'],
properties: {
year: {
type: 'number',
title: '年'
},
month: {
type: 'number',
title: '月'
},
day: {
type: 'number',
title: '日'
}
}
},
tag: {
type: 'array',
title: '个人标签',
items: {
type: 'string'
},
minContains: 2,
maxContains: 10
}
}
},
"formula": {
"mode":"input-group",
"mixedMode": true,
"variables": [
{
"label": "表单字段",
"children": [
{
"label": "文章名",
"value": "name",
"tag": "文本"
},
{
"label": "作者",
"value": "author",
"tag": "文本"
},
{
"label": "售价",
"value": "price",
"tag": "数字"
},
{
"label": "出版时间",
"value": "time",
"tag": "时间"
},
{
"label": "版本号",
"value": "version",
"tag": "数字"
},
{
"label": "出版社",
"value": "publisher",
"tag": "文本"
}
]
},
{
"label": "流程字段",
"children": [
{
"label": "联系电话",
"value": "telphone"
},
{
"label": "地址",
"value": "addr"
}
]
},
{
"label": "长文本测试分类长文本测试分类长文本测试分类长文本测试分类",
"children": [
{
"label": "这是一段测试长文本这是一段测试长文本这是一段测试长文本",
"value": "longtext",
"tag": "文本"
}
]
}
]
}
}
]
}
```
## 远程获取 schema
```schema: scope="body"

View File

@ -6,6 +6,7 @@ import {
JSONPipeOut,
JSONUpdate,
addModal,
getVariables,
modalsToDefinitions
} from 'amis-editor-core';
import React from 'react';
@ -57,7 +58,9 @@ function DialogActionPanel({
onBulkChange,
node,
addHook,
subscribeSchemaSubmit
subscribeSchemaSubmit,
appLocale,
appCorpusData
}: DialogActionPanelProps) {
const eventKey = data.eventKey;
@ -609,6 +612,17 @@ function DialogActionPanel({
);
}, []);
const formula: any = React.useMemo(() => {
return {
variables: () =>
getVariables({
props: {node, manager},
appLocale,
appCorpusData
})
};
}, [node, manager]);
return (
<div className={cx('ae-DialogActionPanel')}>
<FormField
@ -686,6 +700,7 @@ function DialogActionPanel({
onChange={handleDataChange}
schema={JSONPipeOut(currentModal.modal.inputParams)}
addButtonText="添加参数"
formula={formula}
/>
) : null}
</div>

View File

@ -230,6 +230,7 @@ export function withRemoteConfig<P = any>(
super(props);
this.setConfig = this.setConfig.bind(this);
this.childRef = this.childRef.bind(this);
props.store.setComponent(this);
this.deferLoadConfig = this.deferLoadConfig.bind(this);
props.remoteConfigRef?.(this);
@ -401,6 +402,20 @@ export function withRemoteConfig<P = any>(
ret2 && store.setConfig(ret2, config, 'after-defer-load');
}
ref: any;
childRef(ref: any) {
while (ref && ref.getWrappedInstance) {
ref = ref.getWrappedInstance();
}
this.ref = ref;
}
getWrappedInstance() {
return this.ref;
}
render() {
const store = this.props.store;
const env: RendererEnv =
@ -412,6 +427,12 @@ export function withRemoteConfig<P = any>(
updateConfig: this.setConfig
};
const {remoteConfigRef, autoComplete, ...rest} = this.props;
const refConfig =
ComposedComponent.prototype?.isReactComponent ||
(ComposedComponent as any).$$typeof ===
Symbol.for('react.forward_ref')
? {ref: this.childRef}
: {forwardedRef: this.childRef};
return (
<ComposedComponent
@ -425,6 +446,7 @@ export function withRemoteConfig<P = any>(
{...(config.injectedPropsFilter
? config.injectedPropsFilter(injectedProps, this.props)
: injectedProps)}
{...refConfig}
/>
);
}

View File

@ -3,7 +3,7 @@ import {guid} from 'amis-core';
import Button from '../Button';
import {Icon} from '../icons';
import type {InputJSONSchemaItemProps} from './index';
import {InputJSONSchemaItem} from './Item';
import InputJSONSchemaItem from './Item';
type JSONSchemaArrayMember = {
key: string;

View File

@ -4,16 +4,58 @@ import NumberInput from '../NumberInput';
import Switch from '../Switch';
import {InputJSONSchemaArray} from './Array';
import type {InputJSONSchemaItemProps} from './index';
import {InputJSONSchemaObject} from './Object';
import InputJSONSchemaObject from './Object';
import {FormulaPicker} from '../formula/Picker';
export function InputJSONSchemaItem(props: InputJSONSchemaItemProps, ref: any) {
const childRef = React.useRef<any>();
React.useImperativeHandle(ref, () => {
return {
validate() {
return childRef.current?.validate();
}
};
});
export function InputJSONSchemaItem(props: InputJSONSchemaItemProps) {
const schema = props.schema;
const formua = props.formula;
if (schema.type === 'object') {
return <InputJSONSchemaObject {...props} />;
return <InputJSONSchemaObject {...props} ref={childRef} />;
} else if (schema.type === 'array') {
return <InputJSONSchemaArray {...props} />;
} else if (props.renderValue) {
return props.renderValue(props.value, props.onChange, schema, props);
} else if (formua) {
const inputSettings = React.useMemo(() => {
const inputSettings: any = {
...formua.inputSettings
};
if (schema.type === 'number' || schema.type === 'integer') {
inputSettings.type = 'number';
} else if (schema.type === 'boolean') {
inputSettings.type = 'boolean';
}
return inputSettings;
}, [formua.inputSettings, schema.type]);
return (
<FormulaPicker
mode="input-group"
mixedMode={true}
{...formua}
inputSettings={inputSettings}
value={props.value ?? schema.default}
onChange={props.onChange}
className={props.className}
disabled={props.disabled}
placeholder={props.placeholder}
theme={props.theme}
classPrefix={props.classPrefix}
classnames={props.classnames}
translate={props.translate}
locale={props.locale}
/>
);
} else if (schema.type == 'number') {
return (
<NumberInput
@ -55,3 +97,5 @@ export function InputJSONSchemaItem(props: InputJSONSchemaItemProps) {
/>
);
}
export default React.forwardRef(InputJSONSchemaItem);

View File

@ -6,7 +6,7 @@ import InputBox from '../InputBox';
import InputBoxWithSuggestion from '../InputBoxWithSuggestion';
import Select from '../Select';
import type {InputJSONSchemaItemProps} from './index';
import {InputJSONSchemaItem} from './Item';
import InputJSONSchemaItem from './Item';
import isEqual from 'lodash/isEqual';
type JSONSchemaObjectMember = {
@ -18,7 +18,10 @@ type JSONSchemaObjectMember = {
required?: boolean;
value?: any;
};
export function InputJSONSchemaObject(props: InputJSONSchemaItemProps) {
export function InputJSONSchemaObject(
props: InputJSONSchemaItemProps,
ref: any
) {
const {
classnames: cx,
value,
@ -224,6 +227,16 @@ export function InputJSONSchemaObject(props: InputJSONSchemaItemProps) {
);
const allowInput = props.schema.additionalProperties !== false;
React.useImperativeHandle(ref, () => {
return {
validate(): any {
if (membersRef.current?.some(m => m.invalid)) {
return __('JSONSchema.key_invalid');
}
}
};
});
return (
<>
{collapsable ? (
@ -382,3 +395,5 @@ export function InputJSONSchemaObject(props: InputJSONSchemaItemProps) {
</>
);
}
export default React.forwardRef(InputJSONSchemaObject);

View File

@ -6,7 +6,8 @@ import {
themeable,
ThemeProps
} from 'amis-core';
import {InputJSONSchemaItem} from './Item';
import InputJSONSchemaItem from './Item';
import {FormulaPickerProps} from '../formula/Picker';
export interface InputJSONSchemaItemProps extends ThemeProps, LocaleProps {
schema: JSONSchema & {
@ -31,6 +32,7 @@ export interface InputJSONSchemaItemProps extends ThemeProps, LocaleProps {
placeholder?: string;
required?: boolean;
addButtonText?: string;
formula?: FormulaPickerProps;
}
export interface InputJSONSchemaProps
@ -38,13 +40,23 @@ export interface InputJSONSchemaProps
schema?: any;
}
function InputJSONSchema(props: InputJSONSchemaProps) {
function InputJSONSchema(props: InputJSONSchemaProps, ref: any) {
const schema = props.schema || {
type: 'object',
properties: {}
};
return <InputJSONSchemaItem {...props} schema={schema} />;
const childRef = React.useRef<any>();
React.useImperativeHandle(ref, () => {
return {
validate() {
return childRef.current?.validate();
}
};
});
return <InputJSONSchemaItem {...props} schema={schema} ref={childRef} />;
}
export default themeable(localeable(InputJSONSchema));
export default themeable(localeable(React.forwardRef(InputJSONSchema)));

View File

@ -433,6 +433,7 @@ register('de-DE', {
'JSONSchema.array_items': 'Artikel',
'JSONSchema.members': 'Mitglieder',
'JSONSchema.key_duplicated': 'Schlüssel existiert bereits',
'JSONSchema.key_invalid': 'Schlüssel ungültig',
'TimeNow': 'Jetzt',
'Steps.step': 'Schritt {{index}}',
'FormulaInput.True': 'Treu',

View File

@ -415,6 +415,7 @@ register('en-US', {
'JSONSchema.array_items': 'Items',
'JSONSchema.members': 'Members',
'JSONSchema.key_duplicated': 'Key already exists',
'JSONSchema.key_invalid': 'Key invalid',
'TimeNow': 'Now',
'IconSelect.all': 'All',
'IconSelect.choice': 'Icon selection',

View File

@ -409,6 +409,7 @@ register('zh-CN', {
'JSONSchema.array_items': '成员类型',
'JSONSchema.members': '成员',
'JSONSchema.key_duplicated': '字段名已经存在',
'JSONSchema.key_invalid': '字段名格式错误',
'Required': '必填',
'TimeNow': '此刻',
'IconSelect.all': '全部',

View File

@ -99,6 +99,11 @@ export interface ConditionBuilderControlSchema extends FormBaseControlSchema {
*
*/
formula?: Omit<InputFormulaControlSchema, 'type'>;
/**
* if
*/
formulaForIf?: any;
}
export interface ConditionBuilderProps

View File

@ -1,8 +1,9 @@
import React from 'react';
import {FormItem, FormControlProps, FormBaseControl} from 'amis-core';
import {FormItem, FormControlProps, FormBaseControl, autobind} from 'amis-core';
import {InputJSONSchema} from 'amis-ui';
import {withRemoteConfig} from 'amis-ui';
import {FormBaseControlSchema} from '../../Schema';
import {InputFormulaControlSchema} from './InputFormula';
/**
* JSON Schema
@ -18,6 +19,11 @@ export interface JSONSchemaControlSchema extends FormBaseControlSchema {
* json-schema
*/
schema?: any;
/**
*
*/
formula?: Omit<InputFormulaControlSchema, 'type'>;
}
export interface JSONSchemaProps
@ -37,10 +43,24 @@ const EnhancedInputJSONSchema = withRemoteConfig({
}
})(InputJSONSchema as any);
export default class JSONSchemaControl extends React.PureComponent<JSONSchemaProps> {
control: any;
@autobind
controlRef(ref: any) {
while (ref?.getWrappedInstance) {
ref = ref.getWrappedInstance();
}
this.control = ref;
}
validate() {
return this.control?.validate();
}
render() {
const {...rest} = this.props;
return <EnhancedInputJSONSchema {...rest} />;
return <EnhancedInputJSONSchema {...rest} ref={this.controlRef} />;
}
}