半成品

This commit is contained in:
liaoxuezhi 2020-08-14 00:13:40 +08:00
parent 5bd417ede1
commit 0f0f50358f
7 changed files with 291 additions and 23 deletions

View File

@ -18,6 +18,23 @@ const fields = [
label: '入职时间',
name: 'ruzhi',
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 {
display: flex;
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 {LocaleProps, localeable} from '../../locale';
import {uncontrollable} from 'uncontrollable';
import {FieldTypes, FieldItem, Fields} from './types';
import {Fields, ConditionGroupValue} from './types';
import {ConditionGroup} from './ConditionGroup';
export interface QueryBuilderProps extends ThemeProps, LocaleProps {
fields: Fields;
value?: ConditionGroupValue;
onChange: (value: ConditionGroupValue) => void;
}
export class QueryBuilder extends React.Component<QueryBuilderProps> {
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 Button from '../Button';
import {ConditionItem} from './ConditionItem';
import {autobind} from '../../utils/helper';
import {autobind, guid} from '../../utils/helper';
export interface ConditionGroupProps {
value?: ConditionGroupValue;
fields: Fields;
onChange: (value: ConditionGroupValue) => void;
classnames: ClassNamesFn;
removeable?: boolean;
}
export class ConditionGroup extends React.Component<ConditionGroupProps> {
getValue() {
return {
conjunction: 'and' as 'and',
...this.props.value
} as ConditionGroupValue;
}
@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() {
const {classnames: cx, value, fields} = this.props;
const {classnames: cx, value, fields, onChange} = this.props;
return (
<div className={cx('CBCGroup')}>
<div className={cx('CBCGroup-toolbar')}>
<div className={cx('CBCGroup-toolbarLeft')}>
<div className={cx('CBGroup')}>
<div className={cx('CBGroup-toolbar')}>
<div className={cx('CBGroup-toolbarLeft')}>
<Button onClick={this.handleNotClick} size="sm" active={value?.not}>
</Button>
<Button size="sm" active={value?.conjunction !== 'or'}>
<Button
size="sm"
onClick={this.handleConjunctionClick}
active={value?.conjunction !== 'or'}
>
</Button>
<Button size="sm" active={value?.conjunction === 'or'}>
<Button
size="sm"
onClick={this.handleConjunctionClick}
active={value?.conjunction === 'or'}
>
</Button>
</div>
<div className={cx('CBCGroup-toolbarRight')}>
<Button size="sm"></Button>
<Button size="sm"></Button>
<div className={cx('CBGroup-toolbarRight')}>
<Button onClick={this.handleAdd} size="sm">
</Button>
<Button onClick={this.handleAddGroup} size="sm">
</Button>
</div>
</div>
{Array.isArray(value)
? value.map(item => <ConditionItem fields={fields} value={item} />)
{Array.isArray(value?.children)
? 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}
</div>
);

View File

@ -1,13 +1,64 @@
import React from 'react';
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 {
fields: Fields;
value: ConditionRule | ConditionGroupValue;
value: ConditionRule;
classnames: ClassNamesFn;
onChange: (value: ConditionRule) => void;
}
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() {
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 {
left: string;
op: OperatorType;
right: ConditionRightValue | Array<ConditionRightValue>;
id: any;
left?: string;
op?: OperatorType;
right?: ConditionRightValue | Array<ConditionRightValue>;
}
export interface ConditionGroupValue {
id: string;
conjunction: 'and' | 'or';
not?: boolean;
children?: Array<ConditionRule | ConditionGroupValue>;