添加条件组合示例

This commit is contained in:
2betop 2020-08-19 14:25:35 +08:00
parent e3223fa7ab
commit 6efa44c11b
10 changed files with 196 additions and 59 deletions

View File

@ -15,6 +15,7 @@ import StaticFormSchema from './Form/Static';
import HintFormSchema from './Form/Hint';
import FieldSetInTabsFormSchema from './Form/FieldSetInTabs';
import ComboFormSchema from './Form/Combo';
import ConditionBuilderSchema from './Form/ConditionBuilder';
import SubFormSchema from './Form/SubForm';
import RichTextSchema from './Form/RichText';
import EditorSchema from './Form/Editor';
@ -226,6 +227,11 @@ export const examples = [
path: '/examples/form/formula',
component: makeSchemaRenderer(FormulaFormSchema)
},
{
label: '条件组合',
path: '/examples/form/condition-builder',
component: makeSchemaRenderer(ConditionBuilderSchema)
},
{
label: '引用',

View File

@ -0,0 +1,99 @@
export default {
$schema: 'https://houtai.baidu.com/v2/schemas/page.json#',
title: '条件生成器',
body: [
{
type: 'form',
api: '/api/mock2/saveForm?waitSeconds=2',
title: '',
mode: 'horizontal',
horizontal: {
leftFixed: true
},
actions: [
{
label: '查看数据',
type: 'button',
actionType: 'dialog',
dialog: {
title: '数据',
body: '<pre>${conditions|json:2}</pre>'
}
}
],
controls: [
{
type: 'condition-builder',
label: '条件组件',
name: 'conditions',
description:
'适合让用户自己拼查询条件,然后后端根据数据生成 query where',
fields: [
{
label: '文本',
type: 'text',
name: 'text'
},
{
label: '数字',
type: 'number',
name: 'number'
},
{
label: '布尔',
type: 'boolean',
name: 'boolean'
},
{
label: '选项',
type: 'select',
name: 'select',
options: [
{
label: 'A',
value: 'a'
},
{
label: 'B',
value: 'b'
},
{
label: 'C',
value: 'c'
},
{
label: 'D',
value: 'd'
},
{
label: 'E',
value: 'e'
}
]
},
{
label: '日期',
children: [
{
label: '日期',
type: 'date',
name: 'date'
},
{
label: '时间',
type: 'time',
name: 'time'
},
{
label: '日期时间',
type: 'datetime',
name: 'datetime'
}
]
}
]
}
]
}
]
};

View File

@ -137,6 +137,10 @@
background: rgba(0, 0, 0, 0.03);
transition: all 0.3s ease-out;
> .#{$ns}CBGroup {
margin: 3px;
}
}
&-body:not(:hover) .#{$ns}CBDelete {

View File

@ -14,6 +14,7 @@ export interface ConditionGroupProps extends ThemeProps {
value?: ConditionGroupValue;
fields: Fields;
funcs?: Funcs;
showNot?: boolean;
onChange: (value: ConditionGroupValue) => void;
removeable?: boolean;
onRemove?: (e: React.MouseEvent) => void;
@ -117,13 +118,15 @@ export class ConditionGroup extends React.Component<ConditionGroupProps> {
config,
removeable,
onRemove,
onDragStart
onDragStart,
showNot
} = this.props;
return (
<div className={cx('CBGroup')} data-group-id={value?.id}>
<div className={cx('CBGroup-toolbar')}>
<div className={cx('CBGroup-toolbarLeft')}>
{showNot ? (
<Button
onClick={this.handleNotClick}
className="m-r-xs"
@ -133,6 +136,7 @@ export class ConditionGroup extends React.Component<ConditionGroupProps> {
>
</Button>
) : null}
<div className={cx('ButtonGroup')}>
<Button
size="xs"

View File

@ -251,7 +251,8 @@ export class ConditionItem extends React.Component<ConditionItemProps> {
fields={fields}
defaultType="value"
allowedTypes={
field?.valueTypes || ['value', 'field', 'func', 'raw']
field?.valueTypes ||
config.valueTypes || ['value', 'field', 'func', 'raw']
}
/>
@ -265,7 +266,8 @@ export class ConditionItem extends React.Component<ConditionItemProps> {
fields={fields}
defaultType="value"
allowedTypes={
field?.valueTypes || ['value', 'field', 'func', 'raw']
field?.valueTypes ||
config.valueTypes || ['value', 'field', 'func', 'raw']
}
/>
</>

View File

@ -6,9 +6,9 @@ export interface BaseFieldConfig {
export interface Config {
valueTypes?: Array<'value' | 'field' | 'func' | 'raw'>;
fields: Fields;
fields?: Fields;
funcs?: Funcs;
maxLevel?: number;
maxLevel?: number; // 还没实现
types: {
[propName: string]: Type;
};
@ -124,40 +124,6 @@ const defaultConfig: Config = {
boolean: {
operators: ['equal', 'not_equal']
}
},
fields: [
// {
// type: 'text',
// name: 'test',
// label: 'Text'
// },
// {
// label: 'Group',
// children: [
// ]
// }
],
// 函数配置示例
funcs: [
// {
// label: '文本',
// children: [
// {
// type: 'LOWERCASE',
// label: '转小写',
// returnType: 'text',
// args: [
// {
// type: 'text',
// label: '文本',
// valueTypes: ['raw', 'field']
// }
// ]
// }
// ]
// }
]
}
};
export default defaultConfig;

View File

@ -9,17 +9,19 @@ import {autobind, findTreeIndex, spliceTree, getTree} from '../../utils/helper';
import {findDOMNode} from 'react-dom';
import animtion from '../../utils/Animation';
export interface QueryBuilderProps extends ThemeProps, LocaleProps {
export interface ConditionBuilderProps extends ThemeProps, LocaleProps {
fields: Fields;
funcs?: Funcs;
showNot?: boolean;
value?: ConditionGroupValue;
onChange: (value: ConditionGroupValue) => void;
}
export class QueryBuilder extends React.Component<QueryBuilderProps> {
export class QueryBuilder extends React.Component<ConditionBuilderProps> {
config = defaultConfig;
dragTarget: HTMLElement;
// dragNextSibling: Element | null;
ghost: HTMLElement;
host: HTMLElement;
lastX: number;
@ -31,6 +33,7 @@ export class QueryBuilder extends React.Component<QueryBuilderProps> {
const target = e.currentTarget;
const item = target.closest('[data-id]') as HTMLElement;
this.dragTarget = item;
// this.dragNextSibling = item.nextElementSibling;
this.host = item.closest('[data-group-id]') as HTMLElement;
const ghost = item.cloneNode(true) as HTMLElement;
@ -48,10 +51,10 @@ export class QueryBuilder extends React.Component<QueryBuilderProps> {
// 应该是 chrome 的一个bug如果你马上修改会马上执行 dragend
setTimeout(() => {
item.classList.add('is-dragging');
item.parentElement!.insertBefore(
item,
item.parentElement!.firstElementChild
); // 挪到第一个,主要是因为样式问题。
// item.parentElement!.insertBefore(
// item,
// item.parentElement!.firstElementChild
// ); // 挪到第一个,主要是因为样式问题。
}, 5);
}
@ -163,24 +166,41 @@ export class QueryBuilder extends React.Component<QueryBuilderProps> {
document.body.removeEventListener('drop', this.handleDragDrop);
this.dragTarget.classList.remove('is-dragging');
// if (this.dragNextSibling) {
// this.dragTarget.parentElement!.insertBefore(
// this.dragTarget,
// this.dragNextSibling
// );
// } else {
// this.dragTarget.parentElement!.appendChild(this.dragTarget);
// }
delete this.dragTarget;
// delete this.dragNextSibling;
this.ghost.parentElement?.removeChild(this.ghost);
delete this.ghost;
}
render() {
const {classnames: cx, fields, funcs, onChange, value} = this.props;
const {
classnames: cx,
fields,
funcs,
onChange,
value,
showNot
} = this.props;
return (
<ConditionGroup
config={this.config}
funcs={funcs}
fields={fields}
funcs={funcs || this.config.funcs}
fields={fields || this.config.fields}
value={value}
onChange={onChange}
classnames={cx}
removeable={false}
onDragStart={this.handleDragStart}
showNot={showNot}
/>
);
}

View File

@ -83,6 +83,7 @@ import './renderers/Form/Matrix';
import './renderers/Form/Range';
import './renderers/Form/Array';
import './renderers/Form/Combo';
import './renderers/Form/ConditionBuilder';
import './renderers/Form/Container';
import './renderers/Form/SubForm';
import './renderers/Form/RichText';

View File

@ -0,0 +1,31 @@
import React from 'react';
import {FormItem, FormControlProps} from './Item';
import ColorPicker from '../../components/ColorPicker';
import {Funcs, Fields} from '../../components/condition-builder/types';
import {Config} from '../../components/condition-builder/config';
import ConditionBuilder from '../../components/condition-builder/index';
export interface ConditionBuilderProps extends FormControlProps {
funcs?: Funcs;
fields: Fields;
config?: Config;
}
export default class ConditionBuilderControl extends React.PureComponent<
ConditionBuilderProps
> {
render() {
const {className, classnames: cx, ...rest} = this.props;
return (
<div className={cx(`ConditionBuilderControl`, className)}>
<ConditionBuilder {...rest} />
</div>
);
}
}
@FormItem({
type: 'condition-builder'
})
export class ConditionBuilderRenderer extends ConditionBuilderControl {}

View File

@ -1,4 +1,8 @@
// 基本上都是从 sortable 那抄的,让拖拽看起来更流畅。
// 基本上都是从 sortable 那抄的,让拖拽切换有个动画,看起来更流畅。
// 用法是移动前先 animat.capture(container) 把移动前的位置信息记住
// 然后移动节点
// 然后 animate.animateAll(); 计算移动后的位置,然后马上通过 css transform 到原来的位置
// 然后开始动画到移动后的位置。
interface Rect {
top: number;