forked from p96170835/amis
半成品
This commit is contained in:
parent
93297e8c5c
commit
24ce32cafa
|
@ -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} />
|
||||||
|
)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
.#{$ns}CBCGroup {
|
.#{$ns}CBGroup {
|
||||||
&-toolbar {
|
&-toolbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
|
@ -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;
|
|
@ -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}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>;
|
||||||
|
|
Loading…
Reference in New Issue