半成品

This commit is contained in:
liaoxuezhi 2020-08-14 00:13:40 +08:00
parent 93297e8c5c
commit 24ce32cafa
7 changed files with 291 additions and 23 deletions

View File

@ -18,6 +18,23 @@ const fields = [
label: '入职时间', label: '入职时间',
name: 'ruzhi', name: 'ruzhi',
type: 'datetime' type: 'datetime'
},
{
label: '关系字段',
children: [
{
label: '姓名',
name: 'name',
type: 'text'
},
{
label: '年龄',
name: 'age',
type: 'number'
}
]
} }
]; ];
@ -43,7 +60,16 @@ export default {
}, },
{ {
children: () => <ConditionBuilder fields={fields} /> name: 'a',
type: 'static',
tpl: '${a|json:2}'
},
{
name: 'a',
component: ({value, onChange}) => (
<ConditionBuilder value={value} onChange={onChange} fields={fields} />
)
} }
] ]
} }

View File

@ -1,4 +1,4 @@
.#{$ns}CBCGroup { .#{$ns}CBGroup {
&-toolbar { &-toolbar {
display: flex; display: flex;
flex-direction: row; flex-direction: row;

View File

@ -0,0 +1,96 @@
import React from 'react';
import {autobind} from '../utils/helper';
import Overlay from './Overlay';
import PopOver from './PopOver';
import {findDOMNode} from 'react-dom';
export interface PopOverContainerProps {
children: (props: {
onClick: (e: React.MouseEvent) => void;
isOpened: boolean;
ref: any;
}) => JSX.Element;
popOverRender: (props: {onClose: () => void}) => JSX.Element;
popOverContainer?: any;
popOverClassName?: string;
}
export interface PopOverContainerState {
isOpened: boolean;
}
export class PopOverContainer extends React.Component<
PopOverContainerProps,
PopOverContainerState
> {
state: PopOverContainerState = {
isOpened: false
};
target: any;
@autobind
targetRef(target: any) {
this.target = target ? findDOMNode(target) : null;
}
@autobind
handleClick() {
this.setState({
isOpened: true
});
}
@autobind
close() {
this.setState({
isOpened: false
});
}
@autobind
getTarget() {
return findDOMNode(this.target || this) as HTMLElement;
}
@autobind
getParent() {
return this.getTarget()?.parentElement;
}
render() {
const {
children,
popOverContainer,
popOverClassName,
popOverRender: dropdownRender
} = this.props;
return (
<>
{children({
isOpened: this.state.isOpened,
onClick: this.handleClick,
ref: this.targetRef
})}
<Overlay
container={popOverContainer || this.getParent}
target={this.getTarget}
placement={'auto'}
show={this.state.isOpened}
>
<PopOver
overlay
className={popOverClassName}
style={{minWidth: this.target ? this.target.offsetWidth : 'auto'}}
onHide={this.close}
>
{dropdownRender({onClose: this.close})}
</PopOver>
</Overlay>
</>
);
}
}
export default PopOverContainer;

View File

@ -2,18 +2,28 @@ import React from 'react';
import {ThemeProps, themeable} from '../../theme'; import {ThemeProps, themeable} from '../../theme';
import {LocaleProps, localeable} from '../../locale'; import {LocaleProps, localeable} from '../../locale';
import {uncontrollable} from 'uncontrollable'; import {uncontrollable} from 'uncontrollable';
import {FieldTypes, FieldItem, Fields} from './types'; import {Fields, ConditionGroupValue} from './types';
import {ConditionGroup} from './ConditionGroup'; import {ConditionGroup} from './ConditionGroup';
export interface QueryBuilderProps extends ThemeProps, LocaleProps { export interface QueryBuilderProps extends ThemeProps, LocaleProps {
fields: Fields; fields: Fields;
value?: ConditionGroupValue;
onChange: (value: ConditionGroupValue) => void;
} }
export class QueryBuilder extends React.Component<QueryBuilderProps> { export class QueryBuilder extends React.Component<QueryBuilderProps> {
render() { render() {
const {classnames: cx, fields} = this.props; const {classnames: cx, fields, onChange, value} = this.props;
return <ConditionGroup fields={fields} value={undefined} classnames={cx} />; return (
<ConditionGroup
fields={fields}
value={value}
onChange={onChange}
classnames={cx}
removeable={false}
/>
);
} }
} }

View File

@ -3,44 +3,127 @@ import {Fields, ConditionGroupValue} from './types';
import {ClassNamesFn} from '../../theme'; import {ClassNamesFn} from '../../theme';
import Button from '../Button'; import Button from '../Button';
import {ConditionItem} from './ConditionItem'; import {ConditionItem} from './ConditionItem';
import {autobind} from '../../utils/helper'; import {autobind, guid} from '../../utils/helper';
export interface ConditionGroupProps { export interface ConditionGroupProps {
value?: ConditionGroupValue; value?: ConditionGroupValue;
fields: Fields; fields: Fields;
onChange: (value: ConditionGroupValue) => void; onChange: (value: ConditionGroupValue) => void;
classnames: ClassNamesFn; classnames: ClassNamesFn;
removeable?: boolean;
} }
export class ConditionGroup extends React.Component<ConditionGroupProps> { export class ConditionGroup extends React.Component<ConditionGroupProps> {
getValue() {
return {
conjunction: 'and' as 'and',
...this.props.value
} as ConditionGroupValue;
}
@autobind @autobind
handleNotClick() {} handleNotClick() {
const onChange = this.props.onChange;
let value = this.getValue();
value.not = !value.not;
onChange(value);
}
@autobind
handleConjunctionClick() {
const onChange = this.props.onChange;
let value = this.getValue();
value.conjunction = value.conjunction === 'and' ? 'or' : 'and';
onChange(value);
}
@autobind
handleAdd() {
const onChange = this.props.onChange;
let value = this.getValue();
value.children = Array.isArray(value.children)
? value.children.concat()
: [];
value.children.push({
id: guid()
});
onChange(value);
}
@autobind
handleAddGroup() {
const onChange = this.props.onChange;
let value = this.getValue();
value.children = Array.isArray(value.children)
? value.children.concat()
: [];
value.children.push({
id: guid(),
conjunction: 'and'
});
onChange(value);
}
render() { render() {
const {classnames: cx, value, fields} = this.props; const {classnames: cx, value, fields, onChange} = this.props;
return ( return (
<div className={cx('CBCGroup')}> <div className={cx('CBGroup')}>
<div className={cx('CBCGroup-toolbar')}> <div className={cx('CBGroup-toolbar')}>
<div className={cx('CBCGroup-toolbarLeft')}> <div className={cx('CBGroup-toolbarLeft')}>
<Button onClick={this.handleNotClick} size="sm" active={value?.not}> <Button onClick={this.handleNotClick} size="sm" active={value?.not}>
</Button> </Button>
<Button size="sm" active={value?.conjunction !== 'or'}> <Button
size="sm"
onClick={this.handleConjunctionClick}
active={value?.conjunction !== 'or'}
>
</Button> </Button>
<Button size="sm" active={value?.conjunction === 'or'}> <Button
size="sm"
onClick={this.handleConjunctionClick}
active={value?.conjunction === 'or'}
>
</Button> </Button>
</div> </div>
<div className={cx('CBCGroup-toolbarRight')}> <div className={cx('CBGroup-toolbarRight')}>
<Button size="sm"></Button> <Button onClick={this.handleAdd} size="sm">
<Button size="sm"></Button>
</Button>
<Button onClick={this.handleAddGroup} size="sm">
</Button>
</div> </div>
</div> </div>
{Array.isArray(value) {Array.isArray(value?.children)
? value.map(item => <ConditionItem fields={fields} value={item} />) ? value!.children.map(item =>
(item as ConditionGroupValue).conjunction ? (
<ConditionGroup
key={item.id}
fields={fields}
value={item as ConditionGroupValue}
classnames={cx}
onChange={onChange}
/>
) : (
<ConditionItem
key={item.id}
fields={fields}
value={item}
classnames={cx}
onChange={onChange}
/>
)
)
: null} : null}
</div> </div>
); );

View File

@ -1,13 +1,64 @@
import React from 'react'; import React from 'react';
import {Fields, ConditionRule, ConditionGroupValue} from './types'; import {Fields, ConditionRule, ConditionGroupValue} from './types';
import {ClassNamesFn} from '../../theme';
import {Icon} from '../icons';
import Select from '../Select';
import {autobind} from '../../utils/helper';
import PopOverContainer from '../PopOverContainer';
import InputBox from '../InputBox';
import ListRadios from '../ListRadios';
import ResultBox from '../ResultBox';
export interface ConditionItemProps { export interface ConditionItemProps {
fields: Fields; fields: Fields;
value: ConditionRule | ConditionGroupValue; value: ConditionRule;
classnames: ClassNamesFn;
onChange: (value: ConditionRule) => void;
} }
export class ConditionItem extends React.Component<ConditionItemProps> { export class ConditionItem extends React.Component<ConditionItemProps> {
@autobind
handleLeftSelect() {}
renderLeft() {
const {value, fields} = this.props;
return (
<PopOverContainer
popOverRender={({onClose}) => (
<ListRadios showRadio={false} options={fields} onChange={onClose} />
)}
>
{({onClick, ref}) => (
<ResultBox
ref={ref}
allowInput={false}
onResultClick={onClick}
placeholder="请选择"
/>
)}
</PopOverContainer>
);
}
renderItem() {
return null;
}
render() { render() {
return <p>233</p>; const {classnames: cx} = this.props;
return (
<div className={cx('CBGroup-item')}>
<a>
<Icon icon="drag-bar" className="icon" />
</a>
<div className={cx('CBGroup-itemBody')}>
{this.renderLeft()}
{this.renderItem()}
</div>
</div>
);
} }
} }

View File

@ -40,12 +40,14 @@ export type ConditionRightValue =
}; };
export interface ConditionRule { export interface ConditionRule {
left: string; id: any;
op: OperatorType; left?: string;
right: ConditionRightValue | Array<ConditionRightValue>; op?: OperatorType;
right?: ConditionRightValue | Array<ConditionRightValue>;
} }
export interface ConditionGroupValue { export interface ConditionGroupValue {
id: string;
conjunction: 'and' | 'or'; conjunction: 'and' | 'or';
not?: boolean; not?: boolean;
children?: Array<ConditionRule | ConditionGroupValue>; children?: Array<ConditionRule | ConditionGroupValue>;