半成品
This commit is contained in:
parent
031f748bf2
commit
8901b57389
|
@ -1,17 +1,22 @@
|
|||
.#{$ns}CBGroup {
|
||||
font-size: $fontSizeSm;
|
||||
|
||||
&-toolbar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&-field {
|
||||
&-field,
|
||||
&-operator {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
min-width: px2rem(120px);
|
||||
margin-left: $gap-xs;
|
||||
}
|
||||
|
||||
&-fieldCaret {
|
||||
&-fieldCaret,
|
||||
&-operatorCaret {
|
||||
transition: transform 0.3s ease-out;
|
||||
margin: 0 $gap-xs;
|
||||
display: flex;
|
||||
|
@ -27,7 +32,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
&-fieldInput.is-active &-fieldCaret {
|
||||
&-fieldInput.is-active &-fieldCaret,
|
||||
&-operatorInput.is-active &-operatorCaret {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
@ -97,7 +103,7 @@
|
|||
.#{$ns}CBInputSwitch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin: 0 5px;
|
||||
margin-left: 5px;
|
||||
cursor: pointer;
|
||||
> a {
|
||||
@include icon-color();
|
||||
|
@ -108,3 +114,29 @@
|
|||
height: px2rem(10px);
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}CBFunc {
|
||||
display: inline-block;
|
||||
|
||||
&-select {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&-error {
|
||||
color: $danger;
|
||||
}
|
||||
|
||||
&-args {
|
||||
display: inline-block;
|
||||
> span {
|
||||
display: inline-block;
|
||||
padding: 0 5px;
|
||||
color: $info;
|
||||
}
|
||||
|
||||
> div {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,156 @@
|
|||
import {ExpressionComplex, Field, Funcs} from './types';
|
||||
import {ExpressionComplex, Field, Funcs, Func, ExpressionFunc} from './types';
|
||||
import React from 'react';
|
||||
import ConditionField from './Field';
|
||||
import {autobind, findTree} from '../../utils/helper';
|
||||
import Value from './Value';
|
||||
import InputSwitch from './InputSwitch';
|
||||
import ConditionFunc from './Func';
|
||||
import {ThemeProps, themeable} from '../../theme';
|
||||
|
||||
/**
|
||||
* 支持4中表达式设置方式
|
||||
*
|
||||
* 1. 直接就是值,由用户直接填写。
|
||||
* 2. 选择字段,让用户选一个字段。
|
||||
* 3. 选择一个函数,然后会参数里面的输入情况是个递归。
|
||||
* 4. 粗暴点,函数让用户自己书写。
|
||||
*/
|
||||
|
||||
export interface ExpressionProps {
|
||||
export interface ExpressionProps extends ThemeProps {
|
||||
value: ExpressionComplex;
|
||||
onChange: (value: ExpressionComplex) => void;
|
||||
index?: number;
|
||||
onChange: (value: ExpressionComplex, index?: number) => void;
|
||||
valueField?: Field;
|
||||
fields?: Field[];
|
||||
funcs?: Funcs;
|
||||
defaultType?: 'value' | 'field' | 'func' | 'raw';
|
||||
allowedTypes?: Array<'value' | 'field' | 'func' | 'raw'>;
|
||||
}
|
||||
|
||||
export class Expression extends React.Component<ExpressionProps> {}
|
||||
const fieldMap = {
|
||||
value: '值',
|
||||
field: '字段',
|
||||
func: '函数',
|
||||
raw: '公式'
|
||||
};
|
||||
|
||||
export class Expression extends React.Component<ExpressionProps> {
|
||||
@autobind
|
||||
handleInputTypeChange(type: 'value' | 'field' | 'func' | 'raw') {
|
||||
let value = this.props.value;
|
||||
const onChange = this.props.onChange;
|
||||
|
||||
if (type === 'value') {
|
||||
value = '';
|
||||
} else if (type === 'func') {
|
||||
value = {
|
||||
type: 'func',
|
||||
func: (findTree(this.props.funcs!, item => (item as Func).type) as Func)
|
||||
?.type,
|
||||
args: []
|
||||
};
|
||||
} else if (type === 'field') {
|
||||
value = {
|
||||
type: 'field',
|
||||
field: ''
|
||||
};
|
||||
} else if (type === 'raw') {
|
||||
value = {
|
||||
type: 'raw',
|
||||
value: ''
|
||||
};
|
||||
}
|
||||
onChange(value, this.props.index);
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleValueChange(data: any) {
|
||||
this.props.onChange(data, this.props.index);
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleFieldChange(field: string) {
|
||||
let value = this.props.value;
|
||||
const onChange = this.props.onChange;
|
||||
value = {
|
||||
type: 'field',
|
||||
field
|
||||
};
|
||||
onChange(value, this.props.index);
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleFuncChange(func: any) {
|
||||
let value = this.props.value;
|
||||
const onChange = this.props.onChange;
|
||||
value = {
|
||||
...func,
|
||||
type: 'func'
|
||||
};
|
||||
onChange(value, this.props.index);
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleRawChange() {}
|
||||
|
||||
render() {
|
||||
const {value, defaultType, allowedTypes, funcs, fields} = this.props;
|
||||
const inputType =
|
||||
((value as any)?.type === 'field'
|
||||
? 'field'
|
||||
: (value as any)?.type === 'func'
|
||||
? 'func'
|
||||
: (value as any)?.type === 'raw'
|
||||
? 'raw'
|
||||
: value !== undefined
|
||||
? 'value'
|
||||
: undefined) ||
|
||||
defaultType ||
|
||||
allowedTypes?.[0] ||
|
||||
'value';
|
||||
|
||||
const types = allowedTypes || ['value', 'field', 'func'];
|
||||
|
||||
if ((!Array.isArray(funcs) || !funcs.length) && ~types.indexOf('func')) {
|
||||
types.splice(types.indexOf('func'), 1);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{types.length > 1 ? (
|
||||
<InputSwitch
|
||||
value={inputType}
|
||||
onChange={this.handleInputTypeChange}
|
||||
options={types.map(item => ({
|
||||
label: fieldMap[item],
|
||||
value: item
|
||||
}))}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{inputType === 'value' ? <Value /> : null}
|
||||
|
||||
{inputType === 'field' ? (
|
||||
<ConditionField
|
||||
value={(value as any)?.field}
|
||||
onChange={this.handleFieldChange}
|
||||
options={fields!}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{inputType === 'func' ? (
|
||||
<ConditionFunc
|
||||
value={value as ExpressionFunc}
|
||||
onChange={this.handleFuncChange}
|
||||
funcs={funcs}
|
||||
fields={fields}
|
||||
defaultType={defaultType}
|
||||
allowedTypes={allowedTypes}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default themeable(Expression);
|
||||
|
|
|
@ -2,21 +2,20 @@ import React from 'react';
|
|||
import PopOverContainer from '../PopOverContainer';
|
||||
import ListRadios from '../ListRadios';
|
||||
import ResultBox from '../ResultBox';
|
||||
import {ClassNamesFn} from '../../theme';
|
||||
import {ClassNamesFn, ThemeProps, themeable} from '../../theme';
|
||||
import {Icon} from '../icons';
|
||||
import {find} from 'lodash';
|
||||
import {findTree, noop} from '../../utils/helper';
|
||||
|
||||
export interface ConditionFieldProps {
|
||||
export interface ConditionFieldProps extends ThemeProps {
|
||||
options: Array<any>;
|
||||
value: any;
|
||||
onChange: (value: any) => void;
|
||||
classnames: ClassNamesFn;
|
||||
}
|
||||
|
||||
const option2value = (item: any) => item.name;
|
||||
|
||||
export default function ConditionField({
|
||||
export function ConditionField({
|
||||
options,
|
||||
onChange,
|
||||
value,
|
||||
|
@ -57,3 +56,5 @@ export default function ConditionField({
|
|||
</PopOverContainer>
|
||||
);
|
||||
}
|
||||
|
||||
export default themeable(ConditionField);
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
import React from 'react';
|
||||
import {Func, ExpressionFunc, Field, Funcs} from './types';
|
||||
import {ThemeProps, themeable} from '../../theme';
|
||||
import PopOverContainer from '../PopOverContainer';
|
||||
import ListRadios from '../ListRadios';
|
||||
import {autobind, findTree, noop} from '../../utils/helper';
|
||||
import ResultBox from '../ResultBox';
|
||||
import {Icon} from '../icons';
|
||||
import Expression from './Expression';
|
||||
|
||||
export interface ConditionFuncProps extends ThemeProps {
|
||||
value: ExpressionFunc;
|
||||
onChange: (value: ExpressionFunc) => void;
|
||||
fields?: Field[];
|
||||
funcs?: Funcs;
|
||||
defaultType?: 'value' | 'field' | 'func' | 'raw';
|
||||
allowedTypes?: Array<'value' | 'field' | 'func' | 'raw'>;
|
||||
}
|
||||
|
||||
const option2value = (item: Func) => item.type;
|
||||
|
||||
export class ConditionFunc extends React.Component<ConditionFuncProps> {
|
||||
@autobind
|
||||
handleFuncChange(type: string) {
|
||||
const value = {...this.props.value};
|
||||
value.func = type;
|
||||
this.props.onChange(value);
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleArgChange(arg: any, index: number) {
|
||||
const value = {...this.props.value};
|
||||
value.args = Array.isArray(value.args) ? value.args.concat() : [];
|
||||
value.args.splice(index, 1, arg);
|
||||
this.props.onChange(value);
|
||||
}
|
||||
|
||||
renderFunc(func: Func) {
|
||||
const {
|
||||
classnames: cx,
|
||||
fields,
|
||||
value,
|
||||
funcs,
|
||||
defaultType,
|
||||
allowedTypes
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<div className={cx('CBFunc-args')}>
|
||||
<span>(</span>
|
||||
{Array.isArray(func.args) && func.args.length ? (
|
||||
<div>
|
||||
{func.args.map((item, index) => (
|
||||
<Expression
|
||||
key={index}
|
||||
index={index}
|
||||
fields={fields}
|
||||
value={value?.args[index]}
|
||||
valueField={{type: item.type} as any}
|
||||
onChange={this.handleArgChange}
|
||||
funcs={funcs}
|
||||
defaultType={defaultType}
|
||||
// allowedTypes={allowedTypes}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
<span>)</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {value, classnames: cx, funcs} = this.props;
|
||||
const func = value
|
||||
? findTree(funcs!, item => (item as Func).type === value.func)
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div className={cx('CBFunc')}>
|
||||
<PopOverContainer
|
||||
popOverRender={({onClose}) => (
|
||||
<ListRadios
|
||||
onClick={onClose}
|
||||
showRadio={false}
|
||||
options={funcs!}
|
||||
value={(func as Func)?.type}
|
||||
option2value={option2value}
|
||||
onChange={this.handleFuncChange}
|
||||
/>
|
||||
)}
|
||||
>
|
||||
{({onClick, ref, isOpened}) => (
|
||||
<div className={cx('CBFunc-select')}>
|
||||
<ResultBox
|
||||
className={cx(
|
||||
'CBGroup-fieldInput',
|
||||
isOpened ? 'is-active' : ''
|
||||
)}
|
||||
ref={ref}
|
||||
allowInput={false}
|
||||
result={func?.label}
|
||||
onResultChange={noop}
|
||||
onResultClick={onClick}
|
||||
placeholder="请选择字段"
|
||||
>
|
||||
<span className={cx('CBGroup-fieldCaret')}>
|
||||
<Icon icon="caret" className="icon" />
|
||||
</span>
|
||||
</ResultBox>
|
||||
</div>
|
||||
)}
|
||||
</PopOverContainer>
|
||||
|
||||
{func ? (
|
||||
this.renderFunc(func as Func)
|
||||
) : (
|
||||
<span className={cx('CBFunc-error')}>方法未定义</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default themeable(ConditionFunc);
|
|
@ -4,8 +4,10 @@ import {ClassNamesFn} from '../../theme';
|
|||
import Button from '../Button';
|
||||
import {ConditionItem} from './Item';
|
||||
import {autobind, guid} from '../../utils/helper';
|
||||
import {Config} from './config';
|
||||
|
||||
export interface ConditionGroupProps {
|
||||
config: Config;
|
||||
value?: ConditionGroupValue;
|
||||
fields: Fields;
|
||||
funcs?: Funcs;
|
||||
|
@ -85,7 +87,7 @@ export class ConditionGroup extends React.Component<ConditionGroupProps> {
|
|||
}
|
||||
|
||||
render() {
|
||||
const {classnames: cx, value, fields, funcs} = this.props;
|
||||
const {classnames: cx, value, fields, funcs, config} = this.props;
|
||||
|
||||
return (
|
||||
<div className={cx('CBGroup')}>
|
||||
|
@ -125,6 +127,7 @@ export class ConditionGroup extends React.Component<ConditionGroupProps> {
|
|||
? value!.children.map((item, index) =>
|
||||
(item as ConditionGroupValue).conjunction ? (
|
||||
<ConditionGroup
|
||||
config={config}
|
||||
key={item.id}
|
||||
fields={fields}
|
||||
value={item as ConditionGroupValue}
|
||||
|
@ -135,6 +138,7 @@ export class ConditionGroup extends React.Component<ConditionGroupProps> {
|
|||
/>
|
||||
) : (
|
||||
<ConditionItem
|
||||
config={config}
|
||||
key={item.id}
|
||||
fields={fields}
|
||||
value={item}
|
||||
|
|
|
@ -2,18 +2,17 @@ import React from 'react';
|
|||
import PopOverContainer from '../PopOverContainer';
|
||||
import {Icon} from '../icons';
|
||||
import ListRadios from '../ListRadios';
|
||||
import {ClassNamesFn} from '../../theme';
|
||||
import {ClassNamesFn, themeable, ThemeProps} from '../../theme';
|
||||
|
||||
export interface InputSwitchProps {
|
||||
export interface InputSwitchProps extends ThemeProps {
|
||||
options: Array<any>;
|
||||
value: any;
|
||||
onChange: (value: any) => void;
|
||||
classnames: ClassNamesFn;
|
||||
}
|
||||
|
||||
const option2value = (item: any) => item.value;
|
||||
|
||||
export default function InputSwitch({
|
||||
export function InputSwitch({
|
||||
options,
|
||||
value,
|
||||
onChange,
|
||||
|
@ -42,3 +41,5 @@ export default function InputSwitch({
|
|||
</PopOverContainer>
|
||||
);
|
||||
}
|
||||
|
||||
export default themeable(InputSwitch);
|
||||
|
|
|
@ -1,17 +1,28 @@
|
|||
import React from 'react';
|
||||
import {Fields, ConditionRule, ConditionGroupValue, Funcs} from './types';
|
||||
import {
|
||||
Fields,
|
||||
ConditionRule,
|
||||
ConditionGroupValue,
|
||||
Funcs,
|
||||
ExpressionFunc,
|
||||
Func,
|
||||
Field,
|
||||
FieldSimple,
|
||||
ExpressionField
|
||||
} from './types';
|
||||
import {ClassNamesFn} from '../../theme';
|
||||
import {Icon} from '../icons';
|
||||
import Select from '../Select';
|
||||
import {autobind} from '../../utils/helper';
|
||||
import {autobind, findTree, noop} from '../../utils/helper';
|
||||
import Expression from './Expression';
|
||||
import {Config, OperationMap} from './config';
|
||||
import PopOverContainer from '../PopOverContainer';
|
||||
import InputBox from '../InputBox';
|
||||
import ListRadios from '../ListRadios';
|
||||
import ResultBox from '../ResultBox';
|
||||
import ConditionField from './Field';
|
||||
import InputSwitch from './InputSwitch';
|
||||
|
||||
const option2value = (item: any) => item.value;
|
||||
|
||||
export interface ConditionItemProps {
|
||||
config: Config;
|
||||
fields: Fields;
|
||||
funcs?: Funcs;
|
||||
index?: number;
|
||||
|
@ -20,17 +31,6 @@ export interface ConditionItemProps {
|
|||
onChange: (value: ConditionRule, index?: number) => void;
|
||||
}
|
||||
|
||||
const leftInputOptions = [
|
||||
{
|
||||
label: '字段',
|
||||
value: 'field'
|
||||
},
|
||||
{
|
||||
label: '函数',
|
||||
value: 'func'
|
||||
}
|
||||
];
|
||||
|
||||
export class ConditionItem extends React.Component<ConditionItemProps> {
|
||||
@autobind
|
||||
handleLeftFieldSelect(field: any) {
|
||||
|
@ -54,34 +54,101 @@ export class ConditionItem extends React.Component<ConditionItemProps> {
|
|||
onChange(value, this.props.index);
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleLeftChange(leftValue: any) {
|
||||
const value = {...this.props.value, left: leftValue};
|
||||
const onChange = this.props.onChange;
|
||||
|
||||
onChange(value, this.props.index);
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleOperatorChange() {}
|
||||
|
||||
renderLeft() {
|
||||
const {value, fields, classnames: cx, funcs} = this.props;
|
||||
const inputType =
|
||||
value.left && (value.left as any).type === 'func' ? 'func' : 'field';
|
||||
const {value, fields, funcs} = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
{Array.isArray(funcs) ? (
|
||||
<InputSwitch
|
||||
classnames={cx}
|
||||
onChange={this.handleLeftInputTypeChange}
|
||||
options={leftInputOptions}
|
||||
value={inputType}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{inputType === 'field' ? (
|
||||
<ConditionField
|
||||
classnames={cx}
|
||||
options={fields}
|
||||
value={value.left}
|
||||
onChange={this.handleLeftFieldSelect}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
<Expression
|
||||
funcs={funcs}
|
||||
value={value.left}
|
||||
onChange={this.handleLeftChange}
|
||||
fields={fields}
|
||||
defaultType="field"
|
||||
allowedTypes={['field', 'func']}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderOperator() {
|
||||
const {funcs, config, fields, value, classnames: cx} = this.props;
|
||||
const left = value?.left;
|
||||
let operators: Array<string> = [];
|
||||
|
||||
if ((left as ExpressionFunc)?.type === 'func') {
|
||||
const func: Func = findTree(
|
||||
funcs!,
|
||||
(i: Func) => i.type === (left as ExpressionFunc).type
|
||||
) as Func;
|
||||
|
||||
if (func) {
|
||||
operators = config.types[func.returnType]?.operators;
|
||||
}
|
||||
} else if ((left as ExpressionField)?.type === 'field') {
|
||||
const field: FieldSimple = findTree(
|
||||
fields,
|
||||
(i: FieldSimple) => i.name === (left as ExpressionField).field
|
||||
) as FieldSimple;
|
||||
|
||||
if (field) {
|
||||
operators = field.operators || config.types[field.type].operators;
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(operators) && operators.length) {
|
||||
return (
|
||||
<PopOverContainer
|
||||
popOverRender={({onClose}) => (
|
||||
<ListRadios
|
||||
onClick={onClose}
|
||||
option2value={option2value}
|
||||
onChange={this.handleOperatorChange}
|
||||
options={operators.map(operator => ({
|
||||
label: OperationMap[operator as keyof typeof OperationMap],
|
||||
value: operator
|
||||
}))}
|
||||
value={value.op}
|
||||
showRadio={false}
|
||||
/>
|
||||
)}
|
||||
>
|
||||
{({onClick, isOpened, ref}) => (
|
||||
<div className={cx('CBGroup-operator')}>
|
||||
<ResultBox
|
||||
className={cx(
|
||||
'CBGroup-operatorInput',
|
||||
isOpened ? 'is-active' : ''
|
||||
)}
|
||||
ref={ref}
|
||||
allowInput={false}
|
||||
result={OperationMap[value?.op as keyof typeof OperationMap]}
|
||||
onResultChange={noop}
|
||||
onResultClick={onClick}
|
||||
placeholder="请选择操作"
|
||||
>
|
||||
<span className={cx('CBGroup-operatorCaret')}>
|
||||
<Icon icon="caret" className="icon" />
|
||||
</span>
|
||||
</ResultBox>
|
||||
</div>
|
||||
)}
|
||||
</PopOverContainer>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
renderItem() {
|
||||
return null;
|
||||
}
|
||||
|
@ -97,6 +164,7 @@ export class ConditionItem extends React.Component<ConditionItemProps> {
|
|||
|
||||
<div className={cx('CBItem-itemBody')}>
|
||||
{this.renderLeft()}
|
||||
{this.renderOperator()}
|
||||
{this.renderItem()}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
import React from 'react';
|
||||
|
||||
export default class Value extends React.Component<any> {
|
||||
render() {
|
||||
return <p>Value</p>;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import {FieldTypes, OperatorType, Funcs, Fields} from './types';
|
||||
import {FieldTypes, OperatorType, Funcs, Fields, Type} from './types';
|
||||
|
||||
export interface BaseFieldConfig {
|
||||
operations: Array<OperatorType>;
|
||||
|
@ -8,9 +8,38 @@ export interface Config {
|
|||
fields: Fields;
|
||||
funcs?: Funcs;
|
||||
maxLevel?: number;
|
||||
types: {
|
||||
[propName: string]: Type;
|
||||
};
|
||||
}
|
||||
|
||||
export const OperationMap = {
|
||||
equal: '等于',
|
||||
not_equal: '不等于',
|
||||
is_empty: '为空',
|
||||
is_not_empty: '不为空',
|
||||
like: 'LIKE',
|
||||
not_like: 'NOT LIKE',
|
||||
starts_with: 'Start With',
|
||||
ends_with: 'Ends With'
|
||||
};
|
||||
|
||||
const defaultConfig: Config = {
|
||||
types: {
|
||||
text: {
|
||||
operators: [
|
||||
'equal',
|
||||
'not_equal',
|
||||
'is_empty',
|
||||
'is_not_empty',
|
||||
'like',
|
||||
'not_like',
|
||||
'starts_with',
|
||||
'ends_with'
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
fields: [
|
||||
// {
|
||||
// type: 'text',
|
||||
|
|
|
@ -4,6 +4,7 @@ import {LocaleProps, localeable} from '../../locale';
|
|||
import {uncontrollable} from 'uncontrollable';
|
||||
import {Fields, ConditionGroupValue, Funcs} from './types';
|
||||
import {ConditionGroup} from './Group';
|
||||
import defaultConfig from './config';
|
||||
|
||||
export interface QueryBuilderProps extends ThemeProps, LocaleProps {
|
||||
fields: Fields;
|
||||
|
@ -13,11 +14,14 @@ export interface QueryBuilderProps extends ThemeProps, LocaleProps {
|
|||
}
|
||||
|
||||
export class QueryBuilder extends React.Component<QueryBuilderProps> {
|
||||
config = defaultConfig;
|
||||
|
||||
render() {
|
||||
const {classnames: cx, fields, funcs, onChange, value} = this.props;
|
||||
|
||||
return (
|
||||
<ConditionGroup
|
||||
config={this.config}
|
||||
funcs={funcs}
|
||||
fields={fields}
|
||||
value={value}
|
||||
|
|
|
@ -19,25 +19,31 @@ export type FieldItem = {
|
|||
};
|
||||
|
||||
export type ExpressionSimple = string | number | object | undefined;
|
||||
export type ExpressionComplex =
|
||||
export type ExpressionValue =
|
||||
| ExpressionSimple
|
||||
| {
|
||||
type: 'value';
|
||||
value: ExpressionSimple;
|
||||
}
|
||||
| {
|
||||
type: 'func';
|
||||
func: string;
|
||||
args: Array<ExpressionComplex>;
|
||||
}
|
||||
| {
|
||||
type: 'field';
|
||||
field: string;
|
||||
}
|
||||
| {
|
||||
type: 'raw';
|
||||
field: string;
|
||||
};
|
||||
export type ExpressionFunc = {
|
||||
type: 'func';
|
||||
func: string;
|
||||
args: Array<ExpressionComplex>;
|
||||
};
|
||||
export type ExpressionField = {
|
||||
type: 'field';
|
||||
field: string;
|
||||
};
|
||||
export type ExpressionRaw = {
|
||||
type: 'raw';
|
||||
value: string;
|
||||
};
|
||||
|
||||
export type ExpressionComplex =
|
||||
| ExpressionValue
|
||||
| ExpressionFunc
|
||||
| ExpressionField
|
||||
| ExpressionRaw;
|
||||
|
||||
export interface ConditionRule {
|
||||
id: any;
|
||||
|
@ -59,6 +65,7 @@ interface BaseField {
|
|||
type: FieldTypes;
|
||||
label: string;
|
||||
valueTypes?: Array<'value' | 'field' | 'func' | 'expression'>;
|
||||
operators?: Array<string>;
|
||||
|
||||
// valueTypes 里面配置 func 才有效。
|
||||
funcs?: Array<string>;
|
||||
|
@ -123,7 +130,7 @@ interface GroupField {
|
|||
children: Array<FieldSimple>;
|
||||
}
|
||||
|
||||
type FieldSimple =
|
||||
export type FieldSimple =
|
||||
| TextField
|
||||
| NumberField
|
||||
| DateField
|
||||
|
@ -150,3 +157,7 @@ export interface FuncArg extends BaseField {
|
|||
}
|
||||
export type Funcs = Array<Func | FuncGroup>;
|
||||
export type Fields = Array<Field>;
|
||||
|
||||
export type Type = {
|
||||
operators: Array<string>;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue