mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-30 02:58:05 +08:00
feat:ConditionBuilder支持自定义判断条件和右边渲染组件;优化ConditionBuilder样式 (#3887)
Co-authored-by: Qin,Haoyan <qinhaoyan@baidu.com>
This commit is contained in:
parent
42cbec8f44
commit
b2d2b27031
@ -71,57 +71,35 @@ exports[`Renderer:condition-builder 1`] = `
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="cxd-CBGroup"
|
class="cxd-CBGroup"
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="cxd-CBGroup-toolbar"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="cxd-CBGroup-toolbarCondition"
|
class="cxd-CBGroup-toolbarCondition"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="cxd-ButtonGroup"
|
aria-expanded="false"
|
||||||
|
aria-haspopup="listbox"
|
||||||
|
aria-labelledby="downshift-0-label"
|
||||||
|
class="cxd-Select"
|
||||||
|
role="combobox"
|
||||||
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<button
|
<div
|
||||||
class="cxd-Button cxd-Button--default cxd-Button--xs is-active"
|
class="cxd-Select-valueWrap"
|
||||||
type="button"
|
>
|
||||||
|
<div
|
||||||
|
class="cxd-Select-value"
|
||||||
>
|
>
|
||||||
并且
|
并且
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="cxd-Button cxd-Button--default cxd-Button--xs"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
或者
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<span
|
||||||
class="cxd-CBGroup-toolbarConditionAdd"
|
class="cxd-Select-arrow"
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="cxd-ButtonGroup"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
class="cxd-Button cxd-Button--default cxd-Button--xs"
|
|
||||||
type="button"
|
|
||||||
>
|
>
|
||||||
<icon-mock
|
<icon-mock
|
||||||
classname="icon icon-plus"
|
classname="icon icon-caret"
|
||||||
icon="plus"
|
icon="caret"
|
||||||
/>
|
/>
|
||||||
添加条件
|
</span>
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="cxd-Button cxd-Button--default cxd-Button--xs"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<icon-mock
|
|
||||||
classname="icon icon-plus-cicle"
|
|
||||||
icon="plus-cicle"
|
|
||||||
/>
|
|
||||||
添加条件组
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@ -133,6 +111,30 @@ exports[`Renderer:condition-builder 1`] = `
|
|||||||
空
|
空
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="cxd-CBGroup-toolbar"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="cxd-CBGroup-toolbarConditionAdd"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="cxd-ButtonGroup"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="cxd-Button cxd-Button--link cxd-Button--xs"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
添加条件
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="cxd-Button cxd-Button--link cxd-Button--xs"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
添加条件组
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span
|
<span
|
||||||
|
@ -384,6 +384,97 @@ type Value = ValueGroup;
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 自定义
|
||||||
|
|
||||||
|
- `type` 字段配置中配置成 `"custom"`
|
||||||
|
- `label` 字段名称
|
||||||
|
- `placeholder` 占位符
|
||||||
|
- `operators` 默认为空,需配置自定义判断条件,支持字符串或 key-value 格式
|
||||||
|
- `value` 字段配置右边值需要渲染的组件,支持 amis 输入类组件或自定义输入类组件
|
||||||
|
|
||||||
|
```schema: scope="body"
|
||||||
|
{
|
||||||
|
"type": "form",
|
||||||
|
"debug": true,
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "condition-builder",
|
||||||
|
"label": "条件组件",
|
||||||
|
"name": "conditions",
|
||||||
|
"description": "适合让用户自己拼查询条件,然后后端根据数据生成 query where",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"label": "自定义",
|
||||||
|
"type": "custom",
|
||||||
|
"name": "a",
|
||||||
|
"value": {
|
||||||
|
"type": "input-color"
|
||||||
|
},
|
||||||
|
"operators": [
|
||||||
|
"equal",
|
||||||
|
{
|
||||||
|
"label": "等于(自定义)",
|
||||||
|
"value": "custom_equal"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
其中`operators`通过配置 values 还支持右边多个组件的渲染,`right`值格式为对象,`key`为组件的`name`
|
||||||
|
|
||||||
|
```schema: scope="body"
|
||||||
|
{
|
||||||
|
"type": "form",
|
||||||
|
"debug": true,
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "condition-builder",
|
||||||
|
"label": "条件组件",
|
||||||
|
"name": "conditions",
|
||||||
|
"description": "适合让用户自己拼查询条件,然后后端根据数据生成 query where",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"label": "自定义",
|
||||||
|
"type": "custom",
|
||||||
|
"name": "a",
|
||||||
|
"value": {
|
||||||
|
"type": "input-color"
|
||||||
|
},
|
||||||
|
"operators": [
|
||||||
|
{
|
||||||
|
"label": "等于(自定义)",
|
||||||
|
"value": "custom_equal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "属于",
|
||||||
|
"value": "belong",
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"type": "input-text",
|
||||||
|
"name": "color1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tpl",
|
||||||
|
"tpl": "~"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "input-text",
|
||||||
|
"name": "color2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## 字段选项远程拉取
|
## 字段选项远程拉取
|
||||||
|
|
||||||
- 方式 1 配置 `source` 接口返回的数据对象 `data` 中存在 fields 变量即可。
|
- 方式 1 配置 `source` 接口返回的数据对象 `data` 中存在 fields 变量即可。
|
||||||
|
@ -28,6 +28,46 @@ export default {
|
|||||||
description:
|
description:
|
||||||
'适合让用户自己拼查询条件,然后后端根据数据生成 query where',
|
'适合让用户自己拼查询条件,然后后端根据数据生成 query where',
|
||||||
fields: [
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'switch',
|
||||||
|
type: 'custom',
|
||||||
|
label: '开关',
|
||||||
|
value: {
|
||||||
|
name: 'checkbox',
|
||||||
|
type: 'checkbox',
|
||||||
|
label: '勾选框',
|
||||||
|
option: '选项说明'
|
||||||
|
},
|
||||||
|
operators: [
|
||||||
|
'equal',
|
||||||
|
{
|
||||||
|
label: '属于',
|
||||||
|
value: 'belong'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '不属于',
|
||||||
|
value: 'not_belong',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
type: 'input-date',
|
||||||
|
name: 'date1',
|
||||||
|
label: '日期',
|
||||||
|
inputFormat: 'YYYY年MM月DD日'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'tpl',
|
||||||
|
tpl: '~'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'input-date',
|
||||||
|
name: 'date2',
|
||||||
|
label: '日期',
|
||||||
|
inputFormat: 'YYYY年MM月DD日'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '文本',
|
label: '文本',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
|
@ -1,11 +1,36 @@
|
|||||||
.#{$ns}CBGroup {
|
.#{$ns}CBGroup {
|
||||||
font-size: var(--fontSizeSm);
|
font-size: var(--fontSizeSm);
|
||||||
|
position: relative;
|
||||||
|
border: 1px solid #e8e9eb;
|
||||||
|
border-radius: 4px;
|
||||||
|
border-left: px2rem(3px) solid #e6f0ff;
|
||||||
|
padding: px2rem(30px) px2rem(27px) px2rem(17px);
|
||||||
|
margin-top: px2rem(30px);
|
||||||
|
&-toolbarCondition {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: px2rem(-44px);
|
||||||
|
margin-bottom: px2rem(16px);
|
||||||
|
.#{$ns}Select {
|
||||||
|
font-size: 12px;
|
||||||
|
height: px2rem(28px);
|
||||||
|
width: px2rem(62px);
|
||||||
|
background: #d4e5ff;
|
||||||
|
border: none;
|
||||||
|
color: #0832a6;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 0;
|
||||||
|
padding-left: px2rem(8px);
|
||||||
|
min-height: px2rem(28px);
|
||||||
|
&:hover {
|
||||||
|
background: #d4e5ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&-toolbar {
|
&-toolbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
padding-bottom: 10px;
|
margin-top: px2rem(8px);
|
||||||
|
|
||||||
.#{$ns}Button {
|
.#{$ns}Button {
|
||||||
transition: padding var(--animation-duration);
|
transition: padding var(--animation-duration);
|
||||||
min-width: unset;
|
min-width: unset;
|
||||||
@ -18,29 +43,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(:hover)
|
|
||||||
.#{$ns}CBGroup-toolbarCondition
|
|
||||||
.#{$ns}Button:not(.is-active) {
|
|
||||||
width: 0;
|
|
||||||
padding: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
opacity: 0;
|
|
||||||
margin: 0;
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.#{$ns}CBGroup-toolbarConditionAdd {
|
.#{$ns}CBGroup-toolbarConditionAdd {
|
||||||
transition: opacity var(--animation-duration);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
.#{$ns}ButtonGroup {
|
||||||
|
& > .cxd-Button:first-child {
|
||||||
|
margin-right: px2rem(24px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.#{$ns}CBDelete {
|
.#{$ns}CBDelete {
|
||||||
margin-left: var(--gap-xs);
|
margin-left: var(--gap-xs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&:not(:hover) .#{$ns}CBGroup-toolbarConditionAdd {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
}
|
||||||
|
.#{$ns}ResultBox {
|
||||||
|
padding-right: px2rem(3px);
|
||||||
}
|
}
|
||||||
|
|
||||||
&-field,
|
&-field,
|
||||||
@ -76,92 +94,79 @@
|
|||||||
&-placeholder {
|
&-placeholder {
|
||||||
color: var(--text--muted-color);
|
color: var(--text--muted-color);
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-left: 30px;
|
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
background: rgba(0, 0, 0, 0.03);
|
background: rgba(0, 0, 0, 0.03);
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
|
||||||
&:before {
|
|
||||||
position: absolute;
|
|
||||||
content: '';
|
|
||||||
top: -10px;
|
|
||||||
left: -30px;
|
|
||||||
width: var(--gap-md);
|
|
||||||
border-left: solid 1px var(--borderColor);
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
position: absolute;
|
|
||||||
content: '';
|
|
||||||
top: 50%;
|
|
||||||
width: var(--gap-md);
|
|
||||||
left: -30px;
|
|
||||||
border-top: solid 1px var(--borderColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
&:before {
|
|
||||||
border-bottom-left-radius: 5px;
|
|
||||||
border-bottom: solid 1px var(--borderColor);
|
|
||||||
bottom: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.simple {
|
&.simple {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
&:before {
|
|
||||||
display: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:after {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-toolbarCondition {
|
|
||||||
margin-right: var(--gap-base);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.#{$ns}CBDelete {
|
.#{$ns}CBDelete {
|
||||||
@include icon-color();
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
transition: opacity var(--animation-duration);
|
}
|
||||||
|
|
||||||
|
.#{$ns}CBGroupOrItem-body-group--hover {
|
||||||
|
& > .#{$ns}CBGroupOrItem-dragbar {
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
|
& > .#{$ns}CBGroup {
|
||||||
|
border-left-color: #2468f1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.#{$ns}CBGroupOrItem {
|
.#{$ns}CBGroupOrItem {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-left: px2rem(30px);
|
|
||||||
|
|
||||||
& + & {
|
& + & {
|
||||||
margin-top: px2rem(10px);
|
margin-top: px2rem(10px);
|
||||||
}
|
}
|
||||||
|
&-dragbar {
|
||||||
|
cursor: move;
|
||||||
|
width: 20px;
|
||||||
|
margin-left: -5px;
|
||||||
|
opacity: 0.6;
|
||||||
|
text-align: center;
|
||||||
|
transition: opacity var(--animation-duration) ease-out;
|
||||||
|
@include icon-color();
|
||||||
|
}
|
||||||
|
|
||||||
&-body {
|
&-body {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 2px 7px;
|
|
||||||
border-radius: 5px;
|
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
background: rgba(0, 0, 0, 0.03);
|
|
||||||
transition: all var(--animation-duration) ease-out;
|
transition: all var(--animation-duration) ease-out;
|
||||||
|
&-group {
|
||||||
|
width: 100%;
|
||||||
|
flex-direction: row;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: px2rem(16px);
|
||||||
|
> .#{$ns}CBGroupOrItem-dragbar {
|
||||||
|
left: px2rem(-16px);
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
> .#{$ns}CBGroup {
|
> .#{$ns}CBGroup {
|
||||||
margin: 3px;
|
margin: 0px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-body:not(:hover) .#{$ns}CBDelete {
|
&-item {
|
||||||
opacity: 0;
|
background-color: #f7f7f9;
|
||||||
|
width: 100%;
|
||||||
|
padding: px2rem(12px);
|
||||||
|
padding-left: px2rem(28px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
> .#{$ns}CBGroupOrItem-dragbar {
|
||||||
|
left: px2rem(10px);
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-dragging {
|
&.is-dragging {
|
||||||
@ -179,59 +184,19 @@
|
|||||||
background: rgba($info, 0.2);
|
background: rgba($info, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
&-dragbar {
|
|
||||||
cursor: move;
|
|
||||||
width: 20px;
|
|
||||||
margin-left: -5px;
|
|
||||||
opacity: 0.6;
|
|
||||||
text-align: center;
|
|
||||||
transition: opacity var(--animation-duration) ease-out;
|
|
||||||
@include icon-color();
|
|
||||||
}
|
|
||||||
|
|
||||||
.#{$ns}CBGroup {
|
.#{$ns}CBGroup {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover > &-body {
|
& > &-body > &-body-group > &-dragbar,
|
||||||
background: rgba(0, 0, 0, 0.05);
|
& > &-body > &-body-item > &-dragbar {
|
||||||
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover > &-body > &-dragbar {
|
&:hover > &-body > &-body-item > &-dragbar {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:before {
|
|
||||||
position: absolute;
|
|
||||||
content: '';
|
|
||||||
top: -10px;
|
|
||||||
left: -30px;
|
|
||||||
width: 20px;
|
|
||||||
border-left: solid 1px var(--borderColor);
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
position: absolute;
|
|
||||||
content: '';
|
|
||||||
top: 50%;
|
|
||||||
width: 20px;
|
|
||||||
left: -30px;
|
|
||||||
border-top: solid 1px var(--borderColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
&:before {
|
|
||||||
border-bottom-left-radius: 5px;
|
|
||||||
border-bottom: solid 1px var(--borderColor);
|
|
||||||
bottom: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-simple {
|
&-simple {
|
||||||
margin-bottom: var(--gap-sm);
|
margin-bottom: var(--gap-sm);
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import {Config} from './config';
|
|||||||
import InputBox from '../InputBox';
|
import InputBox from '../InputBox';
|
||||||
import Formula from './Formula';
|
import Formula from './Formula';
|
||||||
import {FormulaPickerProps} from '../formula/Picker';
|
import {FormulaPickerProps} from '../formula/Picker';
|
||||||
|
import {localeable, LocaleProps} from '../../locale';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 支持4中表达式设置方式
|
* 支持4中表达式设置方式
|
||||||
@ -30,7 +31,7 @@ import {FormulaPickerProps} from '../formula/Picker';
|
|||||||
* 4. 粗暴点,函数让用户自己书写。
|
* 4. 粗暴点,函数让用户自己书写。
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export interface ExpressionProps extends ThemeProps {
|
export interface ExpressionProps extends ThemeProps, LocaleProps {
|
||||||
value: ExpressionComplex;
|
value: ExpressionComplex;
|
||||||
data?: any;
|
data?: any;
|
||||||
index?: number;
|
index?: number;
|
||||||
@ -46,6 +47,7 @@ export interface ExpressionProps extends ThemeProps {
|
|||||||
fieldClassName?: string;
|
fieldClassName?: string;
|
||||||
formula?: FormulaPickerProps;
|
formula?: FormulaPickerProps;
|
||||||
popOverContainer?: any;
|
popOverContainer?: any;
|
||||||
|
renderEtrValue?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fieldMap = {
|
const fieldMap = {
|
||||||
@ -137,7 +139,8 @@ export class Expression extends React.Component<ExpressionProps> {
|
|||||||
disabled,
|
disabled,
|
||||||
searchable,
|
searchable,
|
||||||
formula,
|
formula,
|
||||||
popOverContainer
|
popOverContainer,
|
||||||
|
renderEtrValue
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const inputType =
|
const inputType =
|
||||||
((value as any)?.type === 'field'
|
((value as any)?.type === 'field'
|
||||||
@ -169,6 +172,7 @@ export class Expression extends React.Component<ExpressionProps> {
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
formula={formula}
|
formula={formula}
|
||||||
popOverContainer={popOverContainer}
|
popOverContainer={popOverContainer}
|
||||||
|
renderEtrValue={renderEtrValue}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
@ -231,4 +235,4 @@ export class Expression extends React.Component<ExpressionProps> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default themeable(Expression);
|
export default themeable(localeable(Expression));
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import {localeable, LocaleProps} from '../../locale';
|
||||||
import {ThemeProps, themeable} from '../../theme';
|
import {ThemeProps, themeable} from '../../theme';
|
||||||
import InputBox from '../InputBox';
|
import InputBox from '../InputBox';
|
||||||
|
|
||||||
export interface FormulaProps extends ThemeProps {
|
export interface FormulaProps extends ThemeProps, LocaleProps {
|
||||||
value: any;
|
value: any;
|
||||||
onChange: (value: any) => void;
|
onChange: (value: any) => void;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
@ -10,7 +11,13 @@ export interface FormulaProps extends ThemeProps {
|
|||||||
|
|
||||||
export class Formula extends React.Component<FormulaProps> {
|
export class Formula extends React.Component<FormulaProps> {
|
||||||
render() {
|
render() {
|
||||||
const {classnames: cx, value, onChange, disabled} = this.props;
|
const {
|
||||||
|
classnames: cx,
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
disabled,
|
||||||
|
translate: __
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cx('CBFormula')}>
|
<div className={cx('CBFormula')}>
|
||||||
@ -18,12 +25,16 @@ export class Formula extends React.Component<FormulaProps> {
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
placeholder="请输入公式"
|
placeholder={__('Condition.formula_placeholder')}
|
||||||
prefix={<span className={cx('CBFormula-label')}>表达式</span>}
|
prefix={
|
||||||
|
<span className={cx('CBFormula-label')}>
|
||||||
|
{__('Condition.expression')}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default themeable(Formula);
|
export default themeable(localeable(Formula));
|
||||||
|
@ -8,8 +8,9 @@ import ResultBox from '../ResultBox';
|
|||||||
import {Icon} from '../icons';
|
import {Icon} from '../icons';
|
||||||
import Expression from './Expression';
|
import Expression from './Expression';
|
||||||
import {Config} from './config';
|
import {Config} from './config';
|
||||||
|
import {localeable, LocaleProps} from '../../locale';
|
||||||
|
|
||||||
export interface ConditionFuncProps extends ThemeProps {
|
export interface ConditionFuncProps extends ThemeProps, LocaleProps {
|
||||||
value: ExpressionFunc;
|
value: ExpressionFunc;
|
||||||
onChange: (value: ExpressionFunc) => void;
|
onChange: (value: ExpressionFunc) => void;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
@ -68,7 +69,14 @@ export class ConditionFunc extends React.Component<ConditionFuncProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {value, classnames: cx, fieldClassName, funcs, disabled} = this.props;
|
const {
|
||||||
|
value,
|
||||||
|
classnames: cx,
|
||||||
|
fieldClassName,
|
||||||
|
funcs,
|
||||||
|
disabled,
|
||||||
|
translate: __
|
||||||
|
} = this.props;
|
||||||
const func = value
|
const func = value
|
||||||
? findTree(funcs!, item => (item as Func).type === value.func)
|
? findTree(funcs!, item => (item as Func).type === value.func)
|
||||||
: null;
|
: null;
|
||||||
@ -100,7 +108,7 @@ export class ConditionFunc extends React.Component<ConditionFuncProps> {
|
|||||||
result={func}
|
result={func}
|
||||||
onResultChange={noop}
|
onResultChange={noop}
|
||||||
onResultClick={onClick}
|
onResultClick={onClick}
|
||||||
placeholder="请选择字段"
|
placeholder={__('Condition.field_placeholder')}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
<span className={cx('CBGroup-fieldCaret')}>
|
<span className={cx('CBGroup-fieldCaret')}>
|
||||||
@ -114,11 +122,13 @@ export class ConditionFunc extends React.Component<ConditionFuncProps> {
|
|||||||
{func ? (
|
{func ? (
|
||||||
this.renderFunc(func as Func)
|
this.renderFunc(func as Func)
|
||||||
) : (
|
) : (
|
||||||
<span className={cx('CBFunc-error')}>方法未定义</span>
|
<span className={cx('CBFunc-error')}>
|
||||||
|
{__('Condition.fun_error')}
|
||||||
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default themeable(ConditionFunc);
|
export default themeable(localeable(ConditionFunc));
|
||||||
|
@ -8,6 +8,7 @@ import {Config} from './config';
|
|||||||
import {Icon} from '../icons';
|
import {Icon} from '../icons';
|
||||||
import {localeable, LocaleProps} from '../../locale';
|
import {localeable, LocaleProps} from '../../locale';
|
||||||
import {FormulaPickerProps} from '../formula/Picker';
|
import {FormulaPickerProps} from '../formula/Picker';
|
||||||
|
import Select from '../Select';
|
||||||
|
|
||||||
export interface ConditionGroupProps extends ThemeProps, LocaleProps {
|
export interface ConditionGroupProps extends ThemeProps, LocaleProps {
|
||||||
builderMode?: 'simple' | 'full';
|
builderMode?: 'simple' | 'full';
|
||||||
@ -27,13 +28,14 @@ export interface ConditionGroupProps extends ThemeProps, LocaleProps {
|
|||||||
fieldClassName?: string;
|
fieldClassName?: string;
|
||||||
formula?: FormulaPickerProps;
|
formula?: FormulaPickerProps;
|
||||||
popOverContainer?: any;
|
popOverContainer?: any;
|
||||||
|
renderEtrValue?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ConditionGroup extends React.Component<ConditionGroupProps> {
|
export class ConditionGroup extends React.Component<ConditionGroupProps> {
|
||||||
getValue() {
|
getValue() {
|
||||||
return {
|
return {
|
||||||
id: guid(),
|
id: guid(),
|
||||||
conjunction: 'and' as 'and',
|
conjunction: 'and',
|
||||||
...this.props.value
|
...this.props.value
|
||||||
} as ConditionGroupValue;
|
} as ConditionGroupValue;
|
||||||
}
|
}
|
||||||
@ -48,10 +50,10 @@ export class ConditionGroup extends React.Component<ConditionGroupProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
handleConjunctionClick() {
|
handleConjunctionChange(val: {value: 'or' | 'and'}) {
|
||||||
const onChange = this.props.onChange;
|
const onChange = this.props.onChange;
|
||||||
let value = this.getValue();
|
let value = this.getValue();
|
||||||
value.conjunction = value.conjunction === 'and' ? 'or' : 'and';
|
value.conjunction = val.value;
|
||||||
onChange(value);
|
onChange(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,11 +138,11 @@ export class ConditionGroup extends React.Component<ConditionGroupProps> {
|
|||||||
searchable,
|
searchable,
|
||||||
translate: __,
|
translate: __,
|
||||||
formula,
|
formula,
|
||||||
popOverContainer
|
popOverContainer,
|
||||||
|
renderEtrValue
|
||||||
} = this.props;
|
} = this.props;
|
||||||
return (
|
return (
|
||||||
<div className={cx('CBGroup')} data-group-id={value?.id}>
|
<div className={cx('CBGroup')} data-group-id={value?.id}>
|
||||||
<div className={cx('CBGroup-toolbar')}>
|
|
||||||
{builderMode === 'simple' && showANDOR === false ? null : (
|
{builderMode === 'simple' && showANDOR === false ? null : (
|
||||||
<div className={cx('CBGroup-toolbarCondition')}>
|
<div className={cx('CBGroup-toolbarCondition')}>
|
||||||
{showNot ? (
|
{showNot ? (
|
||||||
@ -154,56 +156,24 @@ export class ConditionGroup extends React.Component<ConditionGroupProps> {
|
|||||||
{__('Condition.not')}
|
{__('Condition.not')}
|
||||||
</Button>
|
</Button>
|
||||||
) : null}
|
) : null}
|
||||||
<div className={cx('ButtonGroup')}>
|
<Select
|
||||||
<Button
|
options={[
|
||||||
size="xs"
|
{
|
||||||
onClick={this.handleConjunctionClick}
|
label: __('Condition.and'),
|
||||||
active={value?.conjunction !== 'or'}
|
value: 'and'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __('Condition.or'),
|
||||||
|
value: 'or'
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
value={value?.conjunction || 'and'}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
>
|
onChange={this.handleConjunctionChange}
|
||||||
{__('Condition.and')}
|
clearable={false}
|
||||||
</Button>
|
/>
|
||||||
<Button
|
|
||||||
size="xs"
|
|
||||||
onClick={this.handleConjunctionClick}
|
|
||||||
active={value?.conjunction === 'or'}
|
|
||||||
disabled={disabled}
|
|
||||||
>
|
|
||||||
{__('Condition.or')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div
|
|
||||||
className={cx(
|
|
||||||
`CBGroup-toolbarConditionAdd${
|
|
||||||
builderMode === 'simple' ? '-simple' : ''
|
|
||||||
}`
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div className={cx('ButtonGroup')}>
|
|
||||||
<Button onClick={this.handleAdd} size="xs" disabled={disabled}>
|
|
||||||
<Icon icon="plus" className="icon" />
|
|
||||||
{__('Condition.add_cond')}
|
|
||||||
</Button>
|
|
||||||
{builderMode === 'simple' ? null : (
|
|
||||||
<Button
|
|
||||||
onClick={this.handleAddGroup}
|
|
||||||
size="xs"
|
|
||||||
disabled={disabled}
|
|
||||||
>
|
|
||||||
<Icon icon="plus-cicle" className="icon" />
|
|
||||||
{__('Condition.add_cond_group')}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{removeable ? (
|
|
||||||
<a className={cx('CBDelete')} onClick={onRemove}>
|
|
||||||
<Icon icon="close" className="icon" />
|
|
||||||
</a>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
<div className={cx('CBGroup-body')}>
|
<div className={cx('CBGroup-body')}>
|
||||||
{Array.isArray(value?.children) && value!.children.length ? (
|
{Array.isArray(value?.children) && value!.children.length ? (
|
||||||
value!.children.map((item, index) => (
|
value!.children.map((item, index) => (
|
||||||
@ -225,6 +195,7 @@ export class ConditionGroup extends React.Component<ConditionGroupProps> {
|
|||||||
builderMode={builderMode}
|
builderMode={builderMode}
|
||||||
formula={formula}
|
formula={formula}
|
||||||
popOverContainer={popOverContainer}
|
popOverContainer={popOverContainer}
|
||||||
|
renderEtrValue={renderEtrValue}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
@ -239,6 +210,41 @@ export class ConditionGroup extends React.Component<ConditionGroupProps> {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<div className={cx('CBGroup-toolbar')}>
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
`CBGroup-toolbarConditionAdd${
|
||||||
|
builderMode === 'simple' ? '-simple' : ''
|
||||||
|
}`
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className={cx('ButtonGroup')}>
|
||||||
|
<Button
|
||||||
|
level="link"
|
||||||
|
onClick={this.handleAdd}
|
||||||
|
size="xs"
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
|
{__('Condition.add_cond')}
|
||||||
|
</Button>
|
||||||
|
{builderMode === 'simple' ? null : (
|
||||||
|
<Button
|
||||||
|
onClick={this.handleAddGroup}
|
||||||
|
size="xs"
|
||||||
|
disabled={disabled}
|
||||||
|
level="link"
|
||||||
|
>
|
||||||
|
{__('Condition.add_cond_group')}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{removeable ? (
|
||||||
|
<a className={cx('CBDelete')} onClick={onRemove}>
|
||||||
|
{__('Condition.delete_cond_group')}
|
||||||
|
</a>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import {autobind} from '../../utils/helper';
|
|||||||
import ConditionGroup from './Group';
|
import ConditionGroup from './Group';
|
||||||
import ConditionItem from './Item';
|
import ConditionItem from './Item';
|
||||||
import {FormulaPickerProps} from '../formula/Picker';
|
import {FormulaPickerProps} from '../formula/Picker';
|
||||||
|
import Button from '../Button';
|
||||||
|
|
||||||
export interface CBGroupOrItemProps extends ThemeProps {
|
export interface CBGroupOrItemProps extends ThemeProps {
|
||||||
builderMode?: 'simple' | 'full';
|
builderMode?: 'simple' | 'full';
|
||||||
@ -26,9 +27,13 @@ export interface CBGroupOrItemProps extends ThemeProps {
|
|||||||
fieldClassName?: string;
|
fieldClassName?: string;
|
||||||
formula?: FormulaPickerProps;
|
formula?: FormulaPickerProps;
|
||||||
popOverContainer?: any;
|
popOverContainer?: any;
|
||||||
|
renderEtrValue?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CBGroupOrItem extends React.Component<CBGroupOrItemProps> {
|
export class CBGroupOrItem extends React.Component<CBGroupOrItemProps> {
|
||||||
|
state = {
|
||||||
|
hover: false
|
||||||
|
};
|
||||||
@autobind
|
@autobind
|
||||||
handleItemChange(value: any) {
|
handleItemChange(value: any) {
|
||||||
this.props.onChange(value, this.props.index);
|
this.props.onChange(value, this.props.index);
|
||||||
@ -39,6 +44,21 @@ export class CBGroupOrItem extends React.Component<CBGroupOrItemProps> {
|
|||||||
this.props.onRemove?.(this.props.index);
|
this.props.onRemove?.(this.props.index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handlerHoverIn(e: any) {
|
||||||
|
e.stopPropagation();
|
||||||
|
this.setState({
|
||||||
|
hover: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handlerHoverOut(e: any) {
|
||||||
|
this.setState({
|
||||||
|
hover: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
builderMode,
|
builderMode,
|
||||||
@ -54,7 +74,8 @@ export class CBGroupOrItem extends React.Component<CBGroupOrItemProps> {
|
|||||||
searchable,
|
searchable,
|
||||||
onDragStart,
|
onDragStart,
|
||||||
formula,
|
formula,
|
||||||
popOverContainer
|
popOverContainer,
|
||||||
|
renderEtrValue
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -65,6 +86,15 @@ export class CBGroupOrItem extends React.Component<CBGroupOrItemProps> {
|
|||||||
data-id={value?.id}
|
data-id={value?.id}
|
||||||
>
|
>
|
||||||
<div className={cx('CBGroupOrItem-body')}>
|
<div className={cx('CBGroupOrItem-body')}>
|
||||||
|
{value?.conjunction ? (
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
'CBGroupOrItem-body-group',
|
||||||
|
this.state.hover && 'CBGroupOrItem-body-group--hover'
|
||||||
|
)}
|
||||||
|
onMouseOver={this.handlerHoverIn}
|
||||||
|
onMouseOut={this.handlerHoverOut}
|
||||||
|
>
|
||||||
{draggable ? (
|
{draggable ? (
|
||||||
<a
|
<a
|
||||||
draggable
|
draggable
|
||||||
@ -74,8 +104,6 @@ export class CBGroupOrItem extends React.Component<CBGroupOrItemProps> {
|
|||||||
<Icon icon="drag-bar" className="icon" />
|
<Icon icon="drag-bar" className="icon" />
|
||||||
</a>
|
</a>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{value?.conjunction ? (
|
|
||||||
<ConditionGroup
|
<ConditionGroup
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
searchable={searchable}
|
searchable={searchable}
|
||||||
@ -89,9 +117,20 @@ export class CBGroupOrItem extends React.Component<CBGroupOrItemProps> {
|
|||||||
removeable
|
removeable
|
||||||
onRemove={this.handleItemRemove}
|
onRemove={this.handleItemRemove}
|
||||||
data={data}
|
data={data}
|
||||||
|
renderEtrValue={renderEtrValue}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<div className={cx('CBGroupOrItem-body-item')}>
|
||||||
|
{draggable ? (
|
||||||
|
<a
|
||||||
|
draggable
|
||||||
|
onDragStart={onDragStart}
|
||||||
|
className={cx('CBGroupOrItem-dragbar')}
|
||||||
|
>
|
||||||
|
<Icon icon="drag-bar" className="icon" />
|
||||||
|
</a>
|
||||||
|
) : null}
|
||||||
<ConditionItem
|
<ConditionItem
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
searchable={searchable}
|
searchable={searchable}
|
||||||
@ -104,11 +143,15 @@ export class CBGroupOrItem extends React.Component<CBGroupOrItemProps> {
|
|||||||
data={data}
|
data={data}
|
||||||
formula={formula}
|
formula={formula}
|
||||||
popOverContainer={popOverContainer}
|
popOverContainer={popOverContainer}
|
||||||
|
renderEtrValue={renderEtrValue}
|
||||||
/>
|
/>
|
||||||
<a className={cx('CBDelete')} onClick={this.handleItemRemove}>
|
<Button
|
||||||
<Icon icon="close" className="icon" />
|
className={cx('CBDelete')}
|
||||||
</a>
|
onClick={this.handleItemRemove}
|
||||||
</>
|
>
|
||||||
|
<Icon icon="remove" className="icon" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -23,6 +23,7 @@ import GroupedSelection from '../GroupedSelection';
|
|||||||
import ResultBox from '../ResultBox';
|
import ResultBox from '../ResultBox';
|
||||||
import {localeable, LocaleProps} from '../../locale';
|
import {localeable, LocaleProps} from '../../locale';
|
||||||
import {FormulaPickerProps} from '../formula/Picker';
|
import {FormulaPickerProps} from '../formula/Picker';
|
||||||
|
import {PlainObject} from '../../types';
|
||||||
|
|
||||||
const option2value = (item: any) => item.value;
|
const option2value = (item: any) => item.value;
|
||||||
|
|
||||||
@ -39,6 +40,7 @@ export interface ConditionItemProps extends ThemeProps, LocaleProps {
|
|||||||
fieldClassName?: string;
|
fieldClassName?: string;
|
||||||
formula?: FormulaPickerProps;
|
formula?: FormulaPickerProps;
|
||||||
popOverContainer?: any;
|
popOverContainer?: any;
|
||||||
|
renderEtrValue?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ConditionItem extends React.Component<ConditionItemProps> {
|
export class ConditionItem extends React.Component<ConditionItemProps> {
|
||||||
@ -91,12 +93,22 @@ export class ConditionItem extends React.Component<ConditionItemProps> {
|
|||||||
onChange(value, this.props.index);
|
onChange(value, this.props.index);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRightSubChange(index: number, rightValue: any) {
|
handleRightSubChange(
|
||||||
const origin = Array.isArray(this.props.value?.right)
|
isCustom: boolean,
|
||||||
|
index: number | string,
|
||||||
|
rightValue: any
|
||||||
|
) {
|
||||||
|
let origin;
|
||||||
|
if (isCustom) {
|
||||||
|
origin = Object.assign({}, this.props.value?.right) as PlainObject;
|
||||||
|
origin[index] = rightValue;
|
||||||
|
} else {
|
||||||
|
origin = Array.isArray(this.props.value?.right)
|
||||||
? this.props.value.right.concat()
|
? this.props.value.right.concat()
|
||||||
: [];
|
: [];
|
||||||
|
origin[index as number] = rightValue;
|
||||||
|
}
|
||||||
|
|
||||||
origin[index] = rightValue;
|
|
||||||
const value = {...this.props.value, right: origin};
|
const value = {...this.props.value, right: origin};
|
||||||
const onChange = this.props.onChange;
|
const onChange = this.props.onChange;
|
||||||
|
|
||||||
@ -145,7 +157,7 @@ export class ConditionItem extends React.Component<ConditionItemProps> {
|
|||||||
popOverContainer
|
popOverContainer
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const left = value?.left;
|
const left = value?.left;
|
||||||
let operators: Array<string> = [];
|
let operators: any[] = [];
|
||||||
|
|
||||||
if ((left as ExpressionFunc)?.type === 'func') {
|
if ((left as ExpressionFunc)?.type === 'func') {
|
||||||
const func: Func = findTree(
|
const func: Func = findTree(
|
||||||
@ -169,6 +181,16 @@ export class ConditionItem extends React.Component<ConditionItemProps> {
|
|||||||
|
|
||||||
if (Array.isArray(operators) && operators.length) {
|
if (Array.isArray(operators) && operators.length) {
|
||||||
const __ = this.props.translate;
|
const __ = this.props.translate;
|
||||||
|
const options = operators.map(operator => {
|
||||||
|
if (typeof operator === 'string') {
|
||||||
|
return {
|
||||||
|
label: __(OperationMap[operator as keyof typeof OperationMap]),
|
||||||
|
value: operator
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return operator;
|
||||||
|
}
|
||||||
|
});
|
||||||
return (
|
return (
|
||||||
<PopOverContainer
|
<PopOverContainer
|
||||||
popOverContainer={popOverContainer || (() => findDOMNode(this))}
|
popOverContainer={popOverContainer || (() => findDOMNode(this))}
|
||||||
@ -177,10 +199,7 @@ export class ConditionItem extends React.Component<ConditionItemProps> {
|
|||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
option2value={option2value}
|
option2value={option2value}
|
||||||
onChange={this.handleOperatorChange}
|
onChange={this.handleOperatorChange}
|
||||||
options={operators.map(operator => ({
|
options={options}
|
||||||
label: __(OperationMap[operator as keyof typeof OperationMap]),
|
|
||||||
value: operator
|
|
||||||
}))}
|
|
||||||
value={value.op}
|
value={value.op}
|
||||||
multiple={false}
|
multiple={false}
|
||||||
/>
|
/>
|
||||||
@ -195,9 +214,10 @@ export class ConditionItem extends React.Component<ConditionItemProps> {
|
|||||||
)}
|
)}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
allowInput={false}
|
allowInput={false}
|
||||||
result={__(
|
result={
|
||||||
OperationMap[value?.op as keyof typeof OperationMap]
|
__(OperationMap[value?.op as keyof typeof OperationMap]) ||
|
||||||
)}
|
options.find(option => option.value === value.op)?.label
|
||||||
|
}
|
||||||
onResultChange={noop}
|
onResultChange={noop}
|
||||||
onResultClick={onClick}
|
onResultClick={onClick}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
@ -263,13 +283,16 @@ export class ConditionItem extends React.Component<ConditionItemProps> {
|
|||||||
classnames: cx,
|
classnames: cx,
|
||||||
disabled,
|
disabled,
|
||||||
formula,
|
formula,
|
||||||
popOverContainer
|
popOverContainer,
|
||||||
|
renderEtrValue
|
||||||
} = this.props;
|
} = this.props;
|
||||||
let field = {
|
let field = {
|
||||||
...config.types[type],
|
...config.types[type],
|
||||||
type
|
type
|
||||||
} as FieldSimple;
|
} as FieldSimple;
|
||||||
|
|
||||||
|
let option;
|
||||||
|
|
||||||
if ((value?.left as ExpressionField)?.type === 'field') {
|
if ((value?.left as ExpressionField)?.type === 'field') {
|
||||||
const leftField: FieldSimple = findTree(
|
const leftField: FieldSimple = findTree(
|
||||||
fields,
|
fields,
|
||||||
@ -281,6 +304,9 @@ export class ConditionItem extends React.Component<ConditionItemProps> {
|
|||||||
...field,
|
...field,
|
||||||
...leftField
|
...leftField
|
||||||
};
|
};
|
||||||
|
option = field.operators?.find(
|
||||||
|
option => typeof option !== 'string' && option?.value === op
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,7 +321,7 @@ export class ConditionItem extends React.Component<ConditionItemProps> {
|
|||||||
valueField={field}
|
valueField={field}
|
||||||
value={(value.right as Array<ExpressionComplex>)?.[0]}
|
value={(value.right as Array<ExpressionComplex>)?.[0]}
|
||||||
data={data}
|
data={data}
|
||||||
onChange={this.handleRightSubChange.bind(this, 0)}
|
onChange={this.handleRightSubChange.bind(this, false, 0)}
|
||||||
fields={fields}
|
fields={fields}
|
||||||
allowedTypes={
|
allowedTypes={
|
||||||
field?.valueTypes ||
|
field?.valueTypes ||
|
||||||
@ -304,6 +330,7 @@ export class ConditionItem extends React.Component<ConditionItemProps> {
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
formula={formula}
|
formula={formula}
|
||||||
popOverContainer={popOverContainer}
|
popOverContainer={popOverContainer}
|
||||||
|
renderEtrValue={renderEtrValue}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<span className={cx('CBSeprator')}>~</span>
|
<span className={cx('CBSeprator')}>~</span>
|
||||||
@ -314,7 +341,7 @@ export class ConditionItem extends React.Component<ConditionItemProps> {
|
|||||||
valueField={field}
|
valueField={field}
|
||||||
value={(value.right as Array<ExpressionComplex>)?.[1]}
|
value={(value.right as Array<ExpressionComplex>)?.[1]}
|
||||||
data={data}
|
data={data}
|
||||||
onChange={this.handleRightSubChange.bind(this, 1)}
|
onChange={this.handleRightSubChange.bind(this, false, 1)}
|
||||||
fields={fields}
|
fields={fields}
|
||||||
allowedTypes={
|
allowedTypes={
|
||||||
field?.valueTypes ||
|
field?.valueTypes ||
|
||||||
@ -323,11 +350,36 @@ export class ConditionItem extends React.Component<ConditionItemProps> {
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
formula={formula}
|
formula={formula}
|
||||||
popOverContainer={popOverContainer}
|
popOverContainer={popOverContainer}
|
||||||
|
renderEtrValue={renderEtrValue}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
} else if (option && typeof option !== 'string' && option.values) {
|
||||||
|
return option.values.map((schema, i) => {
|
||||||
|
return (
|
||||||
|
<span key={i}>
|
||||||
|
<Expression
|
||||||
|
config={config}
|
||||||
|
op={op}
|
||||||
|
funcs={funcs}
|
||||||
|
valueField={schema}
|
||||||
|
value={value.right}
|
||||||
|
data={data}
|
||||||
|
onChange={this.handleRightSubChange.bind(this, true, schema.name)}
|
||||||
|
fields={fields}
|
||||||
|
allowedTypes={
|
||||||
|
field?.valueTypes ||
|
||||||
|
config.valueTypes || ['value', 'field', 'func', 'formula']
|
||||||
|
}
|
||||||
|
disabled={disabled}
|
||||||
|
formula={formula}
|
||||||
|
popOverContainer={popOverContainer}
|
||||||
|
renderEtrValue={renderEtrValue}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Expression
|
<Expression
|
||||||
config={config}
|
config={config}
|
||||||
@ -345,6 +397,7 @@ export class ConditionItem extends React.Component<ConditionItemProps> {
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
formula={formula}
|
formula={formula}
|
||||||
popOverContainer={popOverContainer}
|
popOverContainer={popOverContainer}
|
||||||
|
renderEtrValue={renderEtrValue}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ export interface ValueProps extends ThemeProps, LocaleProps {
|
|||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
formula?: FormulaPickerProps;
|
formula?: FormulaPickerProps;
|
||||||
popOverContainer?: any;
|
popOverContainer?: any;
|
||||||
|
renderEtrValue?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Value extends React.Component<ValueProps> {
|
export class Value extends React.Component<ValueProps> {
|
||||||
@ -32,7 +33,8 @@ export class Value extends React.Component<ValueProps> {
|
|||||||
data,
|
data,
|
||||||
disabled,
|
disabled,
|
||||||
formula,
|
formula,
|
||||||
popOverContainer
|
popOverContainer,
|
||||||
|
renderEtrValue
|
||||||
} = this.props;
|
} = this.props;
|
||||||
let input: JSX.Element | undefined = undefined;
|
let input: JSX.Element | undefined = undefined;
|
||||||
if (formula) {
|
if (formula) {
|
||||||
@ -85,7 +87,7 @@ export class Value extends React.Component<ValueProps> {
|
|||||||
input = (
|
input = (
|
||||||
<DatePicker
|
<DatePicker
|
||||||
viewMode="time"
|
viewMode="time"
|
||||||
placeholder={__(field.placeholder) || 'Time.placeholder'}
|
placeholder={__(field.placeholder) || __('Time.placeholder')}
|
||||||
format={field.format || 'HH:mm'}
|
format={field.format || 'HH:mm'}
|
||||||
inputFormat={field.inputFormat || 'HH:mm'}
|
inputFormat={field.inputFormat || 'HH:mm'}
|
||||||
value={value ?? field.defaultValue}
|
value={value ?? field.defaultValue}
|
||||||
@ -135,6 +137,23 @@ export class Value extends React.Component<ValueProps> {
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
} else if (field.type === 'custom') {
|
||||||
|
input = renderEtrValue
|
||||||
|
? renderEtrValue(field.value, {
|
||||||
|
data,
|
||||||
|
onChange,
|
||||||
|
value: value ?? field.defaultValue
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
} else {
|
||||||
|
const res = value ?? (field as any).defaultValue;
|
||||||
|
input = renderEtrValue
|
||||||
|
? renderEtrValue(field, {
|
||||||
|
data,
|
||||||
|
onChange,
|
||||||
|
value: res ? res[(field as any).name] : res
|
||||||
|
})
|
||||||
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className={cx('CBValue')}>{input}</div>;
|
return <div className={cx('CBValue')}>{input}</div>;
|
||||||
|
@ -31,6 +31,7 @@ export interface ConditionBuilderProps extends ThemeProps, LocaleProps {
|
|||||||
fieldClassName?: string;
|
fieldClassName?: string;
|
||||||
formula?: FormulaPickerProps;
|
formula?: FormulaPickerProps;
|
||||||
popOverContainer?: any;
|
popOverContainer?: any;
|
||||||
|
renderEtrValue?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class QueryBuilder extends React.Component<ConditionBuilderProps> {
|
export class QueryBuilder extends React.Component<ConditionBuilderProps> {
|
||||||
@ -211,7 +212,8 @@ export class QueryBuilder extends React.Component<ConditionBuilderProps> {
|
|||||||
searchable,
|
searchable,
|
||||||
builderMode,
|
builderMode,
|
||||||
formula,
|
formula,
|
||||||
popOverContainer
|
popOverContainer,
|
||||||
|
renderEtrValue
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const normalizedValue = Array.isArray(value?.children)
|
const normalizedValue = Array.isArray(value?.children)
|
||||||
@ -249,6 +251,7 @@ export class QueryBuilder extends React.Component<ConditionBuilderProps> {
|
|||||||
searchable={searchable}
|
searchable={searchable}
|
||||||
formula={formula}
|
formula={formula}
|
||||||
popOverContainer={popOverContainer}
|
popOverContainer={popOverContainer}
|
||||||
|
renderEtrValue={renderEtrValue}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {SchemaApi} from '../../Schema';
|
import {BaseSchema, SchemaApi} from '../../Schema';
|
||||||
import {Api} from '../../types';
|
import {Api} from '../../types';
|
||||||
|
|
||||||
export type FieldTypes =
|
export type FieldTypes =
|
||||||
@ -8,7 +8,8 @@ export type FieldTypes =
|
|||||||
| 'date'
|
| 'date'
|
||||||
| 'time'
|
| 'time'
|
||||||
| 'datetime'
|
| 'datetime'
|
||||||
| 'select';
|
| 'select'
|
||||||
|
| 'custom';
|
||||||
|
|
||||||
export type OperatorType =
|
export type OperatorType =
|
||||||
| 'equal'
|
| 'equal'
|
||||||
@ -28,7 +29,11 @@ export type OperatorType =
|
|||||||
| 'select_equals'
|
| 'select_equals'
|
||||||
| 'select_not_equals'
|
| 'select_not_equals'
|
||||||
| 'select_any_in'
|
| 'select_any_in'
|
||||||
| 'select_not_any_in';
|
| 'select_not_any_in'
|
||||||
|
| {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type FieldItem = {
|
export type FieldItem = {
|
||||||
type: 'text';
|
type: 'text';
|
||||||
@ -78,11 +83,17 @@ export interface ConditionGroupValue {
|
|||||||
|
|
||||||
export interface ConditionValue extends ConditionGroupValue {}
|
export interface ConditionValue extends ConditionGroupValue {}
|
||||||
|
|
||||||
|
interface customOperator {
|
||||||
|
lable: string;
|
||||||
|
value: string;
|
||||||
|
values?: any[];
|
||||||
|
}
|
||||||
|
|
||||||
interface BaseField {
|
interface BaseField {
|
||||||
type: FieldTypes;
|
type: FieldTypes;
|
||||||
label: string;
|
label: string;
|
||||||
valueTypes?: Array<'value' | 'field' | 'func' | 'formula'>;
|
valueTypes?: Array<'value' | 'field' | 'func' | 'formula'>;
|
||||||
operators?: Array<string>;
|
operators?: Array<string | customOperator>;
|
||||||
|
|
||||||
// valueTypes 里面配置 func 才有效。
|
// valueTypes 里面配置 func 才有效。
|
||||||
funcs?: Array<string>;
|
funcs?: Array<string>;
|
||||||
@ -158,6 +169,12 @@ interface BooleanField extends BaseField {
|
|||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface CustomField extends BaseField {
|
||||||
|
type: 'custom';
|
||||||
|
name: string;
|
||||||
|
value: BaseSchema;
|
||||||
|
}
|
||||||
|
|
||||||
interface GroupField {
|
interface GroupField {
|
||||||
type: 'group';
|
type: 'group';
|
||||||
label: string;
|
label: string;
|
||||||
@ -172,7 +189,8 @@ export type FieldSimple =
|
|||||||
| TimeField
|
| TimeField
|
||||||
| DatetimeField
|
| DatetimeField
|
||||||
| SelectField
|
| SelectField
|
||||||
| BooleanField;
|
| BooleanField
|
||||||
|
| CustomField;
|
||||||
|
|
||||||
export type Field = FieldSimple | FieldGroup | GroupField;
|
export type Field = FieldSimple | FieldGroup | GroupField;
|
||||||
|
|
||||||
|
@ -263,6 +263,7 @@ register('de-DE', {
|
|||||||
'Condition.or': 'oder',
|
'Condition.or': 'oder',
|
||||||
'Condition.add_cond': 'und Bedingung',
|
'Condition.add_cond': 'und Bedingung',
|
||||||
'Condition.add_cond_group': 'Bedingungsgruppe hinzufügen',
|
'Condition.add_cond_group': 'Bedingungsgruppe hinzufügen',
|
||||||
|
'Condition.delete_cond_group': 'Konditionsgruppe löschen',
|
||||||
'Condition.equal': 'gleich',
|
'Condition.equal': 'gleich',
|
||||||
'Condition.not_equal': 'ungleich',
|
'Condition.not_equal': 'ungleich',
|
||||||
'Condition.less': 'weniger',
|
'Condition.less': 'weniger',
|
||||||
@ -285,6 +286,9 @@ register('de-DE', {
|
|||||||
'Condition.cond_placeholder': 'Bedingung auswählen',
|
'Condition.cond_placeholder': 'Bedingung auswählen',
|
||||||
'Condition.field_placeholder': 'Feld auswählen',
|
'Condition.field_placeholder': 'Feld auswählen',
|
||||||
'Condition.blank': 'leer',
|
'Condition.blank': 'leer',
|
||||||
|
'Condition.expression': 'Ausdruck',
|
||||||
|
'Condition.formula_placeholder': 'Bitte geben Sie eine Formel ein',
|
||||||
|
'Condition.fun_error': 'Funktion ist undefiniert',
|
||||||
'InputTable.uniqueError': 'Column `{{label}}` unique validate failed',
|
'InputTable.uniqueError': 'Column `{{label}}` unique validate failed',
|
||||||
'Timeline.collapseText': 'Entfalten',
|
'Timeline.collapseText': 'Entfalten',
|
||||||
'Timeline.expandText': 'Falten',
|
'Timeline.expandText': 'Falten',
|
||||||
|
@ -265,6 +265,7 @@ register('en-US', {
|
|||||||
'Condition.or': 'or',
|
'Condition.or': 'or',
|
||||||
'Condition.add_cond': 'add condition',
|
'Condition.add_cond': 'add condition',
|
||||||
'Condition.add_cond_group': 'add condition group',
|
'Condition.add_cond_group': 'add condition group',
|
||||||
|
'Condition.delete_cond_group': 'delete condition group',
|
||||||
'Condition.equal': 'equal',
|
'Condition.equal': 'equal',
|
||||||
'Condition.not_equal': 'not equal',
|
'Condition.not_equal': 'not equal',
|
||||||
'Condition.less': 'less',
|
'Condition.less': 'less',
|
||||||
@ -287,6 +288,9 @@ register('en-US', {
|
|||||||
'Condition.cond_placeholder': 'select condition',
|
'Condition.cond_placeholder': 'select condition',
|
||||||
'Condition.field_placeholder': 'select field',
|
'Condition.field_placeholder': 'select field',
|
||||||
'Condition.blank': 'blank',
|
'Condition.blank': 'blank',
|
||||||
|
'Condition.expression': 'expression',
|
||||||
|
'Condition.formula_placeholder': 'Please enter a formula',
|
||||||
|
'Condition.fun_error': 'Function is undefined',
|
||||||
'InputTable.uniqueError': 'Column `{{label}}` unique validate failed',
|
'InputTable.uniqueError': 'Column `{{label}}` unique validate failed',
|
||||||
'Timeline.collapseText': 'Unfold',
|
'Timeline.collapseText': 'Unfold',
|
||||||
'Timeline.expandText': 'Fold',
|
'Timeline.expandText': 'Fold',
|
||||||
|
@ -272,6 +272,7 @@ register('zh-CN', {
|
|||||||
'Condition.or': '或者',
|
'Condition.or': '或者',
|
||||||
'Condition.add_cond': '添加条件',
|
'Condition.add_cond': '添加条件',
|
||||||
'Condition.add_cond_group': '添加条件组',
|
'Condition.add_cond_group': '添加条件组',
|
||||||
|
'Condition.delete_cond_group': '删除组',
|
||||||
'Condition.equal': '等于',
|
'Condition.equal': '等于',
|
||||||
'Condition.not_equal': '不等于',
|
'Condition.not_equal': '不等于',
|
||||||
'Condition.less': '小于',
|
'Condition.less': '小于',
|
||||||
@ -294,6 +295,9 @@ register('zh-CN', {
|
|||||||
'Condition.cond_placeholder': '请选择操作',
|
'Condition.cond_placeholder': '请选择操作',
|
||||||
'Condition.field_placeholder': '请选择字段',
|
'Condition.field_placeholder': '请选择字段',
|
||||||
'Condition.blank': '空',
|
'Condition.blank': '空',
|
||||||
|
'Condition.expression': '表达式',
|
||||||
|
'Condition.formula_placeholder': '请输入公式',
|
||||||
|
'Condition.fun_error': '方法未定义',
|
||||||
'InputTable.uniqueError': '列`{{label}}`没有通过唯一验证',
|
'InputTable.uniqueError': '列`{{label}}`没有通过唯一验证',
|
||||||
'Timeline.collapseText': '展开',
|
'Timeline.collapseText': '展开',
|
||||||
'Timeline.expandText': '折叠',
|
'Timeline.expandText': '折叠',
|
||||||
|
@ -9,6 +9,8 @@ import {
|
|||||||
RemoteOptionsProps,
|
RemoteOptionsProps,
|
||||||
withRemoteConfig
|
withRemoteConfig
|
||||||
} from '../../components/WithRemoteConfig';
|
} from '../../components/WithRemoteConfig';
|
||||||
|
import {Schema} from '../../types';
|
||||||
|
import {autobind} from '../../utils/helper';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 条件组合控件
|
* 条件组合控件
|
||||||
@ -59,12 +61,23 @@ export interface ConditionBuilderProps
|
|||||||
> {}
|
> {}
|
||||||
|
|
||||||
export default class ConditionBuilderControl extends React.PureComponent<ConditionBuilderProps> {
|
export default class ConditionBuilderControl extends React.PureComponent<ConditionBuilderProps> {
|
||||||
|
@autobind
|
||||||
|
renderEtrValue(schema: Schema, data: any) {
|
||||||
|
return this.props.render(
|
||||||
|
'inline',
|
||||||
|
Object.assign(schema, {label: false}),
|
||||||
|
data
|
||||||
|
);
|
||||||
|
}
|
||||||
render() {
|
render() {
|
||||||
const {className, classnames: cx, ...rest} = this.props;
|
const {className, classnames: cx, ...rest} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cx(`ConditionBuilderControl`, className)}>
|
<div className={cx(`ConditionBuilderControl`, className)}>
|
||||||
<ConditionBuilderWithRemoteOptions {...rest} />
|
<ConditionBuilderWithRemoteOptions
|
||||||
|
renderEtrValue={this.renderEtrValue}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -77,12 +90,14 @@ const ConditionBuilderWithRemoteOptions = withRemoteConfig({
|
|||||||
RemoteOptionsProps & React.ComponentProps<typeof ConditionBuilder>
|
RemoteOptionsProps & React.ComponentProps<typeof ConditionBuilder>
|
||||||
> {
|
> {
|
||||||
render() {
|
render() {
|
||||||
const {loading, config, deferLoad, disabled, ...rest} = this.props;
|
const {loading, config, deferLoad, disabled, renderEtrValue, ...rest} =
|
||||||
|
this.props;
|
||||||
return (
|
return (
|
||||||
<ConditionBuilder
|
<ConditionBuilder
|
||||||
{...rest}
|
{...rest}
|
||||||
fields={config || rest.fields || []}
|
fields={config || rest.fields || []}
|
||||||
disabled={disabled || loading}
|
disabled={disabled || loading}
|
||||||
|
renderEtrValue={renderEtrValue}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user