feat: conditionBuilder 支持 showIf 来开启条件组启动条件配置 (#7649)

This commit is contained in:
liaoxuezhi 2023-08-08 13:00:32 +08:00 committed by GitHub
parent 890fec5a05
commit 21c1e5091f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 271 additions and 62 deletions

View File

@ -1248,6 +1248,175 @@ selectMode 为`chained`时,使用`source`字段
}
```
## 开启条件设置
通过配置 `showIf` 开启,开启后条件中额外还能配置启动条件。
```schema: scope="body"
{
"type": "form",
"debug": true,
"body": [
{
"type": "condition-builder",
"label": "条件组件",
"title": "条件组合设置",
"draggable": false,
"name": "conditions",
"description": "适合让用户自己拼查询条件,然后后端根据数据生成 query where",
"showIf": true,
"pickerIcon": {
"type": "icon",
"icon": "edit",
"className": "w-4 h-4"
},
"value": {
"id": "1a23a453ce5c",
"conjunction": "and",
"children": [
{
"id": "550de894b404"
}
]
},
"formula": {
"mode":"input-group",
"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": "文本"
}
]
}
]
},
"fields": [
{
"label": "文本",
"type": "text",
"name": "text"
},
{
"label": "数字",
"type": "number",
"name": "number"
},
{
"label": "布尔",
"type": "boolean",
"name": "boolean"
},
{
"label": "选项",
"type": "select",
"name": "select",
"options": [
{
"label": "A",
"value": "a"
},
{
"label": "B",
"value": "b"
},
{
"label": "C",
"value": "c"
},
{
"label": "D",
"value": "d"
},
{
"label": "E",
"value": "e"
}
]
},
{
"label": "动态选项",
"type": "select",
"name": "select2",
"source": "/api/mock2/form/getOptions?waitSeconds=1"
},
{
"label": "日期",
"children": [
{
"label": "日期",
"type": "date",
"name": "date"
},
{
"label": "时间",
"type": "time",
"name": "time"
},
{
"label": "日期时间",
"type": "datetime",
"name": "datetime"
}
]
}
]
}
]
}
```
## 属性表
| 属性名 | 类型 | 默认值 | 说明 | 版本 |
@ -1266,6 +1435,7 @@ selectMode 为`chained`时,使用`source`字段
| addBtnVisibleOn | `string` | | 表达式:控制按钮“添加条件”的显示。参数为`depth`、`breadth`,分别代表深度、长度。表达式需要返回`boolean`类型 | `3.2.0` |
| addGroupBtnVisibleOn | `string` | | 表达式:控制按钮“添加条件组”的显示。参数为`depth`、`breadth`,分别代表深度、长度。表达式需要返回`boolean`类型 | `3.2.0` |
| inputSettings | `InputSettings` | | 开启公式编辑模式时的输入控件类型 | `3.2.0` |
| showIf | `boolean` | | 开启后条件中额外还能配置启动条件。 | `3.2.0` |
### InputSettings

View File

@ -718,6 +718,7 @@ export interface ConditionRule {
left?: ExpressionComplex;
op?: OperatorType;
right?: ExpressionComplex | Array<ExpressionComplex>;
if?: string;
}
export interface ConditionGroupValue {
@ -725,6 +726,7 @@ export interface ConditionGroupValue {
conjunction: 'and' | 'or';
not?: boolean;
children?: Array<ConditionRule | ConditionGroupValue>;
if?: string;
}
export interface ConditionValue extends ConditionGroupValue {}

View File

@ -237,6 +237,22 @@
// }
// }
.#{$ns}CBIf {
margin-left: 10px;
font-size: 16px;
color: #84868c;
&:hover {
font-size: 16px;
}
&.is-active {
color: #2468f1;
}
cursor: pointer;
// margin-left: auto;
}
.#{$ns}CBGroupOrItem {
position: relative;
transition: box-shadow 0.3s ease;

View File

@ -41,7 +41,7 @@ export interface ExpressionProps extends ThemeProps, LocaleProps {
valueField?: FieldSimple;
fields?: ConditionBuilderField[];
funcs?: ConditionBuilderFuncs;
allowedTypes?: Array<'value' | 'field' | 'func' | 'formula'>;
allowedTypes?: Array<'value' | 'field' | 'func'>;
op?: OperatorType;
config: ConditionBuilderConfig;
disabled?: boolean;
@ -56,13 +56,12 @@ export interface ExpressionProps extends ThemeProps, LocaleProps {
const fieldMap = {
value: '值',
field: '字段',
func: '函数',
formula: '公式'
func: '函数'
};
export class Expression extends React.Component<ExpressionProps> {
@autobind
handleInputTypeChange(type: 'value' | 'field' | 'func' | 'formula') {
handleInputTypeChange(type: 'value' | 'field' | 'func') {
let value = this.props.value;
const onChange = this.props.onChange;
@ -84,11 +83,6 @@ export class Expression extends React.Component<ExpressionProps> {
type: 'field',
field: ''
};
} else if (type === 'formula') {
value = {
type: 'formula',
value: ''
};
}
onChange(value, this.props.index);
}
@ -120,17 +114,6 @@ export class Expression extends React.Component<ExpressionProps> {
onChange(value, this.props.index);
}
@autobind
handleFormulaChange(formula: string) {
let value = this.props.value;
const onChange = this.props.onChange;
value = {
type: 'formula',
value: formula
};
onChange(value, this.props.index);
}
render() {
const {
value,
@ -155,8 +138,6 @@ export class Expression extends React.Component<ExpressionProps> {
? 'field'
: (value as any)?.type === 'func'
? 'func'
: (value as any)?.type === 'formula'
? 'formula'
: value !== undefined
? 'value'
: undefined) ||
@ -219,14 +200,6 @@ export class Expression extends React.Component<ExpressionProps> {
/>
) : null}
{inputType === 'formula' ? (
<Formula
value={(value as any)?.value}
onChange={this.handleFormulaChange}
disabled={disabled}
/>
) : null}
{types.length > 1 ? (
<InputSwitch
disabled={disabled}

View File

@ -29,7 +29,7 @@ export interface ConditionFuncProps extends ThemeProps, LocaleProps {
config: ConditionBuilderConfig;
fields?: ConditionBuilderField[];
funcs?: ConditionBuilderFuncs;
allowedTypes?: Array<'value' | 'field' | 'func' | 'formula'>;
allowedTypes?: Array<'value' | 'field' | 'func'>;
fieldClassName?: string;
}

View File

@ -31,6 +31,7 @@ export interface ConditionGroupProps extends ThemeProps, LocaleProps {
funcs?: ConditionBuilderFuncs;
showNot?: boolean;
showANDOR?: boolean;
showIf?: boolean;
data?: any;
disabled?: boolean;
searchable?: boolean;
@ -192,7 +193,8 @@ export class ConditionGroup extends React.Component<
draggable,
depth,
isAddBtnVisibleOn,
isAddGroupBtnVisibleOn
isAddGroupBtnVisibleOn,
showIf
} = this.props;
const {isCollapsed} = this.state;
@ -283,6 +285,7 @@ export class ConditionGroup extends React.Component<
depth={depth}
isAddBtnVisibleOn={isAddBtnVisibleOn}
isAddGroupBtnVisibleOn={isAddGroupBtnVisibleOn}
showIf={showIf}
/>
))
) : (

View File

@ -5,7 +5,7 @@ import React from 'react';
import {Icon} from '../icons';
import ConditionGroup from './Group';
import ConditionItem from './Item';
import {FormulaPickerProps} from '../formula/Picker';
import FormulaPicker, {FormulaPickerProps} from '../formula/Picker';
import Button from '../Button';
import type {ConditionGroupValue, ConditionValue} from 'amis-core';
@ -33,6 +33,7 @@ export interface CBGroupOrItemProps extends ThemeProps {
depth: number;
isAddBtnVisibleOn?: (param: {depth: number; breadth: number}) => boolean;
isAddGroupBtnVisibleOn?: (param: {depth: number; breadth: number}) => boolean;
showIf?: boolean;
}
export class CBGroupOrItem extends React.Component<CBGroupOrItemProps> {
@ -67,6 +68,15 @@ export class CBGroupOrItem extends React.Component<CBGroupOrItemProps> {
});
}
@autobind
handleIfChange(condition: string) {
const value: ConditionGroupValue = {
...(this.props.value as any),
if: condition
};
this.props.onChange(value, this.props.index);
}
render() {
const {
builderMode,
@ -88,7 +98,8 @@ export class CBGroupOrItem extends React.Component<CBGroupOrItemProps> {
isCollapsed,
depth,
isAddBtnVisibleOn,
isAddGroupBtnVisibleOn
isAddGroupBtnVisibleOn,
showIf
} = this.props;
return (
@ -138,6 +149,7 @@ export class CBGroupOrItem extends React.Component<CBGroupOrItemProps> {
depth={depth + 1}
isAddBtnVisibleOn={isAddBtnVisibleOn}
isAddGroupBtnVisibleOn={isAddGroupBtnVisibleOn}
showIf={showIf}
/>
</div>
) : (
@ -166,6 +178,26 @@ export class CBGroupOrItem extends React.Component<CBGroupOrItemProps> {
renderEtrValue={renderEtrValue}
selectMode={selectMode}
/>
{showIf ? (
<FormulaPicker
{...formula}
evalMode={true}
mixedMode={false}
header="设置条件"
value={value?.if || ''}
onChange={this.handleIfChange}
>
{({onClick}) => (
<a
className={cx('CBIf', value?.if ? 'is-active' : '')}
onClick={onClick}
data-tooltip="配置启动条件,当前规则只有在此条件成立时才会生效"
>
<Icon icon="if" className="icon" />
</a>
)}
</FormulaPicker>
) : null}
<Button
className={cx('CBDelete')}
onClick={this.handleItemRemove}

View File

@ -350,7 +350,7 @@ export class ConditionItem extends React.Component<ConditionItemProps> {
fields={fields}
allowedTypes={
field?.valueTypes ||
config.valueTypes || ['value', 'field', 'func', 'formula']
config.valueTypes || ['value', 'field', 'func']
}
disabled={disabled}
formula={formula}
@ -370,7 +370,7 @@ export class ConditionItem extends React.Component<ConditionItemProps> {
fields={fields}
allowedTypes={
field?.valueTypes ||
config.valueTypes || ['value', 'field', 'func', 'formula']
config.valueTypes || ['value', 'field', 'func']
}
disabled={disabled}
formula={formula}
@ -394,7 +394,7 @@ export class ConditionItem extends React.Component<ConditionItemProps> {
fields={fields}
allowedTypes={
field?.valueTypes ||
config.valueTypes || ['value', 'field', 'func', 'formula']
config.valueTypes || ['value', 'field', 'func']
}
disabled={disabled}
formula={formula}
@ -416,8 +416,7 @@ export class ConditionItem extends React.Component<ConditionItemProps> {
onChange={this.handleRightChange}
fields={fields}
allowedTypes={
field?.valueTypes ||
config.valueTypes || ['value', 'field', 'func', 'formula']
field?.valueTypes || config.valueTypes || ['value', 'field', 'func']
}
disabled={disabled}
formula={formula}

View File

@ -39,14 +39,16 @@ export class Value extends React.Component<ValueProps> {
let input: JSX.Element | undefined = undefined;
if (formula) {
// 如果配置了 formula 字段,则所有的输入变为 formula 形式
formula = Object.assign(formula, {
formula = {
...formula,
translate: __,
classnames: cx,
data,
value: value ?? field.defaultValue,
onChange,
disabled
});
};
const inputSettings =
field.type !== 'custom' && formula?.inputSettings
? {

View File

@ -11,13 +11,11 @@ export interface BaseFieldConfig {
}
export interface ConditionBuilderConfig {
valueTypes?: Array<'value' | 'field' | 'func' | 'formula'>;
valueTypes?: Array<'value' | 'field' | 'func'>;
fields?: ConditionBuilderFields;
funcs?: ConditionBuilderFuncs;
maxLevel?: number; // 还没实现
// todo 起码需要支持 formula 组件可以自定义。
// todo 很多还不能配置。
types: {
[propName: string]: ConditionBuilderType;

View File

@ -30,8 +30,9 @@ export interface ConditionBuilderProps extends ThemeProps, LocaleProps {
title?: string;
fields: ConditionBuilderFields;
funcs?: ConditionBuilderFuncs;
showNot?: boolean;
showANDOR?: boolean;
showNot?: boolean; // 是否显示非按钮
showANDOR?: boolean; // 是否显示并或切换键按钮
showIf?: boolean; // 是否显示条件
value?: ConditionGroupValue;
data?: any;
onChange: (value?: ConditionGroupValue) => void;
@ -261,7 +262,8 @@ export class QueryBuilder extends React.Component<
renderEtrValue,
selectMode,
isAddBtnVisibleOn,
isAddGroupBtnVisibleOn
isAddGroupBtnVisibleOn,
showIf
} = this.props;
const normalizedValue = Array.isArray(value?.children)
@ -305,6 +307,7 @@ export class QueryBuilder extends React.Component<
depth={1}
isAddBtnVisibleOn={isAddBtnVisibleOn}
isAddGroupBtnVisibleOn={isAddGroupBtnVisibleOn}
showIf={showIf}
/>
);
}

View File

@ -24,7 +24,7 @@ interface customOperator {
interface BaseField {
type: FieldTypes;
label: string;
valueTypes?: Array<'value' | 'field' | 'func' | 'formula'>;
valueTypes?: Array<'value' | 'field' | 'func'>;
operators?: Array<string | customOperator>;
// valueTypes 里面配置 func 才有效。
@ -147,5 +147,5 @@ export type ConditionBuilderType = {
defaultOp?: OperatorType;
operators: Array<OperatorType>;
placeholder?: string;
valueTypes?: Array<'value' | 'field' | 'func' | 'formula'>;
valueTypes?: Array<'value' | 'field' | 'func'>;
};

View File

@ -101,6 +101,8 @@ import NewEdit from '../icons/new-edit.svg';
import RotateLeft from '../icons/rotate-left.svg';
import RotateRight from '../icons/rotate-right.svg';
import ScaleOrigin from '../icons/scale-origin.svg';
import If from '../icons/if.svg';
import isObject from 'lodash/isObject';
// 兼容原来的用法,后续不直接试用。
@ -235,6 +237,7 @@ registerIcon('new-edit', NewEdit);
registerIcon('rotate-left', RotateLeft);
registerIcon('rotate-right', RotateRight);
registerIcon('scale-origin', ScaleOrigin);
registerIcon('if', If);
export interface IconCheckedSchema {
id: string;

View File

@ -0,0 +1,7 @@
<?xml version="1.0" standalone="no"?>
<svg t="1690876977113" class="icon" viewBox="0 0 1194 1024" version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path
d="M545.877333 227.84c50.346667-183.466667 143.36-139.093333 228.693334-139.093333 14.506667 0 67.413333 4.266667 70.826666-32.426667 5.12-45.226667-40.96-49.493333-61.44-52.053333-49.493333-5.12-152.746667-12.8-215.04 27.306666-63.146667 40.96-98.986667 110.933333-116.053333 193.706667l-24.746667 116.906667h-98.133333c-20.48 0-36.693333 5.973333-38.4 42.666666 1.706667 20.48 17.92 36.693333 38.4 36.693334h81.066667l-104.96 513.706666s-27.306667 83.626667 31.573333 87.04c52.053333 3.413333 62.293333-80.213333 62.293333-80.213333l105.813334-520.533333h134.826666a39.68 39.68 0 0 0 0-79.36h-117.76l23.04-114.346667zM182.613333 338.176a39.253333 39.253333 0 0 0-46.933333 29.866667l-133.973333 601.6c-4.266667 19.626667-1.706667 37.546667 33.28 46.933333 20.48 2.56 39.253333-10.24 43.52-29.866667l133.973333-601.6c4.266667-21.333333-8.533333-41.813333-29.866667-46.933333z m108.288 43.690667z m0 0v9.386666c0-2.56 0.853333-4.266667 0.853334-5.973333 0-0.853333-0.853333-2.56-0.853334-3.413333z m-134.570666-189.44c0 32.426667 26.453333 58.88 58.88 58.88 32.426667 0 58.88-26.453333 58.88-58.88 0-32.426667-26.453333-58.88-58.88-58.88-32.426667 0-58.88 26.453333-58.88 58.88z m707.328 203.178666a39.253333 39.253333 0 0 0-62.293334-1.706666c-4.266667 5.12-106.666667 121.173333-117.76 299.52-10.24 176.64 57.173333 296.96 59.733334 302.08 7.68 13.653333 20.48 21.333333 33.28 22.186666 9.386667 0.853333 19.626667-2.56 28.16-9.386666 19.626667-17.066667 24.746667-50.346667 11.946666-74.24-0.853333-0.853333-53.76-97.28-45.226666-234.666667 8.533333-134.826667 87.893333-226.986667 89.6-228.693333a61.44 61.44 0 0 0 2.56-75.093334z m172.373333 617.984c12.8 2.56 26.453333-2.56 36.693333-13.653333 4.266667-5.12 93.866667-107.52 116.906667-281.6 24.746667-177.493333-53.76-314.88-56.32-320-13.653333-23.893333-40.96-29.013333-61.44-11.946667-20.48 16.213333-25.6 49.493333-11.093333 73.386667 0.853333 2.56 60.586667 110.08 42.666666 244.053333-18.773333 136.533333-89.6 218.453333-90.453333 219.306667-17.066667 20.48-18.773333 53.76-1.706667 75.093333 6.826667 8.533333 15.36 13.653333 24.746667 15.36z"
fill="currentColor"></path>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -6,7 +6,8 @@ import {
Schema,
isPureVariable,
resolveVariableAndFilter,
createObject
createObject,
evalExpression
} from 'amis-core';
import {
FormBaseControlSchema,
@ -26,6 +27,7 @@ import {
import {IconSchema} from '../Icon';
import {isMobile} from 'amis-core';
import type {InputFormulaControlSchema} from './InputFormula';
/**
*
@ -90,7 +92,12 @@ export interface ConditionBuilderControlSchema extends FormBaseControlSchema {
/**
*
*/
addConditionVisible?: string;
addGroupBtnVisibleOn?: string;
/**
*
*/
formula?: Omit<InputFormulaControlSchema, 'type'>;
}
export interface ConditionBuilderProps
@ -118,11 +125,8 @@ export default class ConditionBuilderControl extends React.PureComponent<Conditi
@autobind
getAddBtnVisible(param: {depth: number; breadth: number}) {
const {data, addBtnVisibleOn} = this.props;
if (addBtnVisibleOn && isPureVariable(addBtnVisibleOn)) {
return resolveVariableAndFilter(
addBtnVisibleOn,
createObject(data, param)
);
if (typeof addBtnVisibleOn === 'string' && addBtnVisibleOn) {
return evalExpression(addBtnVisibleOn, createObject(data, param));
}
return true;
}
@ -130,11 +134,8 @@ export default class ConditionBuilderControl extends React.PureComponent<Conditi
@autobind
getAddGroupBtnVisible(param: {depth: number; breadth: number}) {
const {data, addGroupBtnVisibleOn} = this.props;
if (addGroupBtnVisibleOn && isPureVariable(addGroupBtnVisibleOn)) {
return resolveVariableAndFilter(
addGroupBtnVisibleOn,
createObject(data, param)
);
if (typeof addGroupBtnVisibleOn === 'string' && addGroupBtnVisibleOn) {
return evalExpression(addGroupBtnVisibleOn, createObject(data, param));
}
return true;
}
@ -176,7 +177,7 @@ export default class ConditionBuilderControl extends React.PureComponent<Conditi
isAddGroupBtnVisibleOn={this.getAddGroupBtnVisible}
popOverContainer={popOverContainer || env.getModalContainer}
{...rest}
formula={formula}
formula={formula as any}
/>
</div>
);