refactor: filter schema component (#213)
* refactor: filter schema component * feat: improve filter schema component * fix: cannot find module
This commit is contained in:
parent
5d974d7e32
commit
bc27359637
@ -40,7 +40,7 @@
|
|||||||
"@types/react-dom": "^17.0.0",
|
"@types/react-dom": "^17.0.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.9.1",
|
"@typescript-eslint/eslint-plugin": "^4.9.1",
|
||||||
"@typescript-eslint/parser": "^4.8.2",
|
"@typescript-eslint/parser": "^4.8.2",
|
||||||
"antd": "^4.18.5",
|
"antd": "^4.18.9",
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
"concurrently": "^7.0.0",
|
"concurrently": "^7.0.0",
|
||||||
"cross-env": "^5.2.0",
|
"cross-env": "^5.2.0",
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
"@formily/core": "^2.0.7",
|
"@formily/core": "^2.0.7",
|
||||||
"@formily/react": "^2.0.7",
|
"@formily/react": "^2.0.7",
|
||||||
"ahooks": "^3.0.5",
|
"ahooks": "^3.0.5",
|
||||||
"antd": "^4.18.5",
|
"antd": "^4.18.9",
|
||||||
"axios": "^0.24.0",
|
"axios": "^0.24.0",
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { observer, useField } from '@formily/react';
|
import { observer, RecursionField, useField, useFieldSchema } from '@formily/react';
|
||||||
import { Button, Modal } from 'antd';
|
import { Button, Modal, Popover } from 'antd';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
|
import { useActionContext } from '../..';
|
||||||
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
|
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
|
||||||
import { SortableItem } from '../../common';
|
import { SortableItem } from '../../common';
|
||||||
import { useDesigner } from '../../hooks';
|
import { useDesigner } from '../../hooks';
|
||||||
@ -62,43 +63,89 @@ export const actionDesignerCss = css`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const Action: ComposedAction = observer((props: any) => {
|
export const Action: ComposedAction = observer((props: any) => {
|
||||||
const { confirm, openMode, containerRefKey, component, useAction = useA, onClick, className, ...others } = props;
|
const {
|
||||||
|
popover,
|
||||||
|
confirm,
|
||||||
|
openMode,
|
||||||
|
containerRefKey,
|
||||||
|
component,
|
||||||
|
useAction = useA,
|
||||||
|
onClick,
|
||||||
|
className,
|
||||||
|
...others
|
||||||
|
} = props;
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const Designer = useDesigner();
|
const Designer = useDesigner();
|
||||||
const field = useField();
|
const field = useField();
|
||||||
const { run } = useAction();
|
const { run } = useAction();
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const renderButton = () => (
|
||||||
|
<SortableItem
|
||||||
|
{...others}
|
||||||
|
onClick={(e: React.MouseEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
const onOk = () => {
|
||||||
|
onClick?.(e);
|
||||||
|
setVisible(true);
|
||||||
|
run();
|
||||||
|
};
|
||||||
|
if (confirm) {
|
||||||
|
Modal.confirm({
|
||||||
|
...confirm,
|
||||||
|
onOk,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
onOk();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
component={component || Button}
|
||||||
|
className={classnames(className, actionDesignerCss)}
|
||||||
|
>
|
||||||
|
<Designer />
|
||||||
|
{field.title}
|
||||||
|
</SortableItem>
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<ActionContext.Provider value={{ visible, setVisible, openMode, containerRefKey }}>
|
<ActionContext.Provider value={{ button: renderButton(), visible, setVisible, openMode, containerRefKey }}>
|
||||||
<SortableItem
|
{popover && <RecursionField basePath={field.address} onlyRenderProperties schema={fieldSchema} />}
|
||||||
{...others}
|
{!popover && renderButton()}
|
||||||
onClick={(e: React.MouseEvent) => {
|
{!popover && props.children}
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
const onOk = () => {
|
|
||||||
onClick?.(e);
|
|
||||||
setVisible(true);
|
|
||||||
run();
|
|
||||||
};
|
|
||||||
if (confirm) {
|
|
||||||
Modal.confirm({
|
|
||||||
...confirm,
|
|
||||||
onOk,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
onOk();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
component={component || Button}
|
|
||||||
className={classnames(className, actionDesignerCss)}
|
|
||||||
>
|
|
||||||
<Designer />
|
|
||||||
{field.title}
|
|
||||||
</SortableItem>
|
|
||||||
{props.children}
|
|
||||||
</ActionContext.Provider>
|
</ActionContext.Provider>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Action.Popover = observer((props) => {
|
||||||
|
const { button, visible, setVisible } = useActionContext();
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
{...props}
|
||||||
|
destroyTooltipOnHide
|
||||||
|
visible={visible}
|
||||||
|
onVisibleChange={(visible) => {
|
||||||
|
setVisible(visible);
|
||||||
|
}}
|
||||||
|
content={props.children}
|
||||||
|
>
|
||||||
|
{button}
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Action.Popover.Footer = observer((props) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={css`
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
width: 100%;
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
Action.Designer = () => {
|
Action.Designer = () => {
|
||||||
return (
|
return (
|
||||||
<GeneralSchemaDesigner>
|
<GeneralSchemaDesigner>
|
||||||
|
@ -3,6 +3,7 @@ import { createContext } from 'react';
|
|||||||
export const ActionContext = createContext<ActionContextProps>({});
|
export const ActionContext = createContext<ActionContextProps>({});
|
||||||
|
|
||||||
export interface ActionContextProps {
|
export interface ActionContextProps {
|
||||||
|
button?: any;
|
||||||
visible?: boolean;
|
visible?: boolean;
|
||||||
setVisible?: (v: boolean) => void;
|
setVisible?: (v: boolean) => void;
|
||||||
openMode?: 'drawer' | 'modal' | 'page';
|
openMode?: 'drawer' | 'modal' | 'page';
|
||||||
|
@ -0,0 +1,53 @@
|
|||||||
|
import { FormItem, Input } from '@formily/antd';
|
||||||
|
import { ISchema, observer, useForm } from '@formily/react';
|
||||||
|
import { Action, Form, SchemaComponent, SchemaComponentProvider, useActionContext } from '@nocobase/client';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const useCloseAction = () => {
|
||||||
|
const { setVisible } = useActionContext();
|
||||||
|
const form = useForm();
|
||||||
|
return {
|
||||||
|
async run() {
|
||||||
|
setVisible(false);
|
||||||
|
form.submit((values) => {
|
||||||
|
console.log(values);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const schema: ISchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
action1: {
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
type: 'primary',
|
||||||
|
popover: true,
|
||||||
|
openMode: 'popover',
|
||||||
|
},
|
||||||
|
type: 'void',
|
||||||
|
title: 'Open',
|
||||||
|
properties: {
|
||||||
|
popover: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Action.Popover',
|
||||||
|
properties: {
|
||||||
|
hello: {
|
||||||
|
type: 'void',
|
||||||
|
'x-content': 'Hello',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default observer(() => {
|
||||||
|
return (
|
||||||
|
<SchemaComponentProvider scope={{ useCloseAction }} components={{ Form, Action, Input, FormItem }}>
|
||||||
|
<SchemaComponent schema={schema} />
|
||||||
|
</SchemaComponentProvider>
|
||||||
|
);
|
||||||
|
});
|
@ -36,3 +36,7 @@ group:
|
|||||||
### 不同的打开方式
|
### 不同的打开方式
|
||||||
|
|
||||||
<code src="./demos/demo3.tsx"/>
|
<code src="./demos/demo3.tsx"/>
|
||||||
|
|
||||||
|
### Action + Action.Popover
|
||||||
|
|
||||||
|
<code src="./demos/demo4.tsx"/>
|
||||||
|
@ -41,6 +41,7 @@ export const Cascader = connect(
|
|||||||
});
|
});
|
||||||
// 兼容值为 object[] 的情况
|
// 兼容值为 object[] 的情况
|
||||||
const toValue = () => {
|
const toValue = () => {
|
||||||
|
return ['11'];
|
||||||
return toArr(value).map((item) => {
|
return toArr(value).map((item) => {
|
||||||
if (typeof item === 'object') {
|
if (typeof item === 'object') {
|
||||||
return item[fieldNames.value];
|
return item[fieldNames.value];
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
import { createForm, onFieldValueChange } from '@formily/core';
|
||||||
|
import React, { useContext, useMemo } from 'react';
|
||||||
|
import { FormProvider, SchemaComponent } from '../../../schema-component';
|
||||||
|
import { useComponent } from '../../hooks';
|
||||||
|
import { FilterContext } from './context';
|
||||||
|
|
||||||
|
export const DynamicComponent = (props) => {
|
||||||
|
const { dynamicComponent } = useContext(FilterContext);
|
||||||
|
const component = useComponent(dynamicComponent);
|
||||||
|
const form = useMemo(
|
||||||
|
() =>
|
||||||
|
createForm({
|
||||||
|
values: {
|
||||||
|
value: props.value,
|
||||||
|
},
|
||||||
|
effects() {
|
||||||
|
onFieldValueChange('value', (field) => {
|
||||||
|
props?.onChange?.(field.value);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
[JSON.stringify(props.schema), JSON.stringify(props.value)],
|
||||||
|
);
|
||||||
|
const renderSchemaComponent = () => {
|
||||||
|
return (
|
||||||
|
<SchemaComponent
|
||||||
|
schema={{
|
||||||
|
name: 'value',
|
||||||
|
'x-component': 'Input',
|
||||||
|
...props.schema,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<FormProvider form={form}>
|
||||||
|
{component
|
||||||
|
? React.createElement(component, {
|
||||||
|
value: props.value,
|
||||||
|
onChange: props?.onChange,
|
||||||
|
renderSchemaComponent,
|
||||||
|
})
|
||||||
|
: renderSchemaComponent()}
|
||||||
|
</FormProvider>
|
||||||
|
);
|
||||||
|
};
|
@ -1,85 +0,0 @@
|
|||||||
import { FilterOutlined } from '@ant-design/icons';
|
|
||||||
import { FormButtonGroup, Submit } from '@formily/antd';
|
|
||||||
import { createForm } from '@formily/core';
|
|
||||||
import { FieldContext, FormContext, observer, useFieldSchema } from '@formily/react';
|
|
||||||
import { uid } from '@formily/shared';
|
|
||||||
import { Button, Popover } from 'antd';
|
|
||||||
import flatten from 'flat';
|
|
||||||
import React, { useMemo } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { useCollection, useCollectionManager } from '../../../collection-manager';
|
|
||||||
import { SchemaComponent } from '../../core';
|
|
||||||
import { useDesignable } from '../../hooks';
|
|
||||||
import { useCompile } from '../../hooks/useCompile';
|
|
||||||
import { useFilterContext } from './hooks';
|
|
||||||
|
|
||||||
export const FilterAction = observer((props: any) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const compile = useCompile();
|
|
||||||
const { DesignableBar } = useDesignable();
|
|
||||||
const filedSchema = useFieldSchema();
|
|
||||||
const form = useMemo(() => createForm(), []);
|
|
||||||
const { visible, setVisible } = useFilterContext();
|
|
||||||
const obj = flatten(form.values.filter || {});
|
|
||||||
const count = Object.values(obj).filter((i) => (Array.isArray(i) ? i.length : i)).length;
|
|
||||||
const { fields } = useCollection();
|
|
||||||
const { getInterface } = useCollectionManager();
|
|
||||||
const properties = {};
|
|
||||||
fields?.forEach((field) => {
|
|
||||||
const { operators } = getInterface(field.interface);
|
|
||||||
properties[uid()] = {
|
|
||||||
type: 'void',
|
|
||||||
title: field?.uiSchema?.title ?? field.title,
|
|
||||||
'x-component': 'Filter.Column',
|
|
||||||
'x-component-props': {
|
|
||||||
operations: [...operators],
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
[field.name ?? uid()]: { ...field?.uiSchema },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
const schema = {
|
|
||||||
type: 'void',
|
|
||||||
properties: {
|
|
||||||
filter: {
|
|
||||||
type: 'array',
|
|
||||||
'x-component': 'Filter',
|
|
||||||
properties,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<Popover
|
|
||||||
trigger={['click']}
|
|
||||||
placement={'bottomLeft'}
|
|
||||||
visible={visible}
|
|
||||||
onVisibleChange={setVisible}
|
|
||||||
content={
|
|
||||||
<div>
|
|
||||||
<FieldContext.Provider value={null}>
|
|
||||||
<FormContext.Provider value={form}>
|
|
||||||
<SchemaComponent schema={schema} name="filter" />
|
|
||||||
<FormButtonGroup align={'right'}>
|
|
||||||
<Submit
|
|
||||||
onSubmit={() => {
|
|
||||||
const { filter } = form.values;
|
|
||||||
console.log('Table.Filter====', form.values);
|
|
||||||
setVisible(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t('Submit')}
|
|
||||||
</Submit>
|
|
||||||
</FormButtonGroup>
|
|
||||||
</FormContext.Provider>
|
|
||||||
</FieldContext.Provider>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Button icon={<FilterOutlined />}>
|
|
||||||
{count > 0 ? t('{{count}} filter items', { count }) : compile(filedSchema.title)}
|
|
||||||
<DesignableBar />
|
|
||||||
</Button>
|
|
||||||
</Popover>
|
|
||||||
);
|
|
||||||
});
|
|
@ -1,61 +0,0 @@
|
|||||||
import { createForm, onFieldValueChange } from '@formily/core';
|
|
||||||
import type { ISchema, Schema } from '@formily/react';
|
|
||||||
import { connect } from '@formily/react';
|
|
||||||
import deepmerge from 'deepmerge';
|
|
||||||
import React, { useMemo } from 'react';
|
|
||||||
import { SchemaComponent } from '../../core';
|
|
||||||
|
|
||||||
interface DynamicValueProps {
|
|
||||||
value?: any;
|
|
||||||
onChange?: any;
|
|
||||||
schema?: Schema;
|
|
||||||
operation?: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const FilterDynamicValue = connect((props: DynamicValueProps) => {
|
|
||||||
const { onChange, value, operation } = props;
|
|
||||||
const fieldName = Object.keys(props?.schema?.properties || {}).shift() ?? 'value';
|
|
||||||
const fieldSchema = Object.values(props?.schema?.properties || {}).shift();
|
|
||||||
const form = useMemo(
|
|
||||||
() =>
|
|
||||||
createForm({
|
|
||||||
initialValues: {
|
|
||||||
[fieldName]: value,
|
|
||||||
},
|
|
||||||
effects(form) {
|
|
||||||
onFieldValueChange(fieldName, (field) => {
|
|
||||||
onChange(field.value);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
const extra: ISchema = deepmerge(
|
|
||||||
{
|
|
||||||
required: false,
|
|
||||||
'x-read-pretty': false,
|
|
||||||
name: fieldName,
|
|
||||||
default: value,
|
|
||||||
'x-decorator': 'FormilyFormItem',
|
|
||||||
'x-decorator-props': {
|
|
||||||
asterisk: true,
|
|
||||||
feedbackLayout: 'none',
|
|
||||||
},
|
|
||||||
'x-component-props': {
|
|
||||||
style: {
|
|
||||||
minWidth: '150px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
operation?.schema || {},
|
|
||||||
);
|
|
||||||
const schema = {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
[fieldName]: deepmerge(fieldSchema, extra, {
|
|
||||||
arrayMerge: (target, source) => source,
|
|
||||||
}) as any,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return <SchemaComponent schema={schema} />;
|
|
||||||
});
|
|
@ -1,27 +1,33 @@
|
|||||||
import { LoadingOutlined } from '@ant-design/icons';
|
import { ObjectField as ObjectFieldModel } from '@formily/core';
|
||||||
import { connect, mapProps, mapReadPretty } from '@formily/react';
|
import { observer, useField } from '@formily/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FilterAction } from './Filter.Action';
|
import { useRequest } from '../../../api-client';
|
||||||
import { FilterDynamicValue } from './Filter.DynamicValue';
|
import { FilterContext } from './context';
|
||||||
import { FilterGroup } from './FilterGroup';
|
import { FilterGroup } from './FilterGroup';
|
||||||
import './style.less';
|
import { SaveDefaultValue } from './SaveDefaultValue';
|
||||||
|
|
||||||
export const Filter: any = connect(
|
const useDef = (options) => {
|
||||||
(props) => {
|
const field = useField<ObjectFieldModel>();
|
||||||
return <FilterGroup bordered={false} {...props} />;
|
return useRequest(() => Promise.resolve({ data: field.dataSource }), options);
|
||||||
},
|
};
|
||||||
mapProps((props, field) => {
|
|
||||||
return {
|
|
||||||
...props,
|
|
||||||
suffix: <span>{field?.['loading'] || field?.['validating'] ? <LoadingOutlined /> : props.suffix}</span>,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
mapReadPretty((props) => {
|
|
||||||
return null;
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
Filter.DynamicValue = FilterDynamicValue;
|
export const Filter: any = observer((props: any) => {
|
||||||
Filter.Action = FilterAction;
|
const { useDataSource = useDef, dynamicComponent } = props;
|
||||||
|
const field = useField<ObjectFieldModel>();
|
||||||
|
useDataSource({
|
||||||
|
onSuccess(data) {
|
||||||
|
console.log('onSuccess', data?.data);
|
||||||
|
field.dataSource = data?.data || [];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<FilterContext.Provider value={{ dynamicComponent, options: field.dataSource || [] }}>
|
||||||
|
<FilterGroup {...props} />
|
||||||
|
</FilterContext.Provider>
|
||||||
|
<pre>{JSON.stringify(field.value, null, 2)}</pre>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export default Filter;
|
Filter.SaveDefaultValue = SaveDefaultValue;
|
||||||
|
@ -1,75 +1,88 @@
|
|||||||
import { CloseCircleOutlined } from '@ant-design/icons';
|
import { CloseCircleOutlined } from '@ant-design/icons';
|
||||||
import { useForm } from '@formily/react';
|
import { ObjectField as ObjectFieldModel } from '@formily/core';
|
||||||
import { isValid } from '@formily/shared';
|
import { ArrayField, connect, useField } from '@formily/react';
|
||||||
import { Select } from 'antd';
|
import { Select, Space } from 'antd';
|
||||||
import cls from 'classnames';
|
import React, { useContext } from 'react';
|
||||||
import React from 'react';
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
import { Trans } from 'react-i18next';
|
import { RemoveConditionContext } from './context';
|
||||||
import { FilterList } from './FilterList';
|
import { FilterItems } from './FilterItems';
|
||||||
|
|
||||||
const toValue = (value) => {
|
export const FilterGroup = connect((props) => {
|
||||||
if (!value) {
|
const field = useField<ObjectFieldModel>();
|
||||||
return {
|
const remove = useContext(RemoveConditionContext);
|
||||||
logical: 'and',
|
const { t } = useTranslation();
|
||||||
list: [{}],
|
const keys = Object.keys(field.value || {});
|
||||||
|
const logic = keys.includes('$or') ? '$or' : '$and';
|
||||||
|
const setLogic = (value) => {
|
||||||
|
const obj = field.value || {};
|
||||||
|
field.value = {
|
||||||
|
[value]: obj[logic] || [],
|
||||||
};
|
};
|
||||||
}
|
|
||||||
if (value.and) {
|
|
||||||
return {
|
|
||||||
logical: 'and',
|
|
||||||
list: value.and,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (value.or) {
|
|
||||||
return {
|
|
||||||
logical: 'and',
|
|
||||||
list: value.or,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
logical: 'and',
|
|
||||||
list: [{}],
|
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
export function FilterGroup(props) {
|
|
||||||
const { bordered = true, onRemove, onChange } = props;
|
|
||||||
const value = toValue(props.value);
|
|
||||||
const form = useForm();
|
|
||||||
console.log('list', form.values, value);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cls('nb-filter-group', { bordered })}>
|
<div
|
||||||
{onRemove && (
|
style={{
|
||||||
<a className={'nb-filter-group-close'} onClick={() => onRemove()}>
|
position: 'relative',
|
||||||
<CloseCircleOutlined />
|
border: '1px dashed #dedede',
|
||||||
</a>
|
padding: 14,
|
||||||
|
marginBottom: 8,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{remove && (
|
||||||
|
<CloseCircleOutlined
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
right: 10,
|
||||||
|
}}
|
||||||
|
onClick={() => remove()}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
<div style={{ marginBottom: 14 }}>
|
<div style={{ marginBottom: 8 }}>
|
||||||
<Trans>
|
<Trans>
|
||||||
|
{'Meet '}
|
||||||
<Select
|
<Select
|
||||||
style={{ width: 80 }}
|
value={logic}
|
||||||
onChange={(logical) => {
|
onChange={(value) => {
|
||||||
onChange?.({
|
setLogic(value);
|
||||||
[logical]: value.list,
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
defaultValue={value.logical}
|
|
||||||
>
|
>
|
||||||
<Select.Option value={'and'}>All</Select.Option>
|
<Select.Option value={'$and'}>All</Select.Option>
|
||||||
<Select.Option value={'or'}>Any</Select.Option>
|
<Select.Option value={'$or'}>Any</Select.Option>
|
||||||
</Select>
|
</Select>
|
||||||
|
{' conditions in the group'}
|
||||||
</Trans>
|
</Trans>
|
||||||
</div>
|
</div>
|
||||||
<FilterList
|
<div>
|
||||||
initialValue={value.list}
|
<ArrayField name={logic} component={[FilterItems]} />
|
||||||
onChange={(list: any[]) => {
|
</div>
|
||||||
const values = {
|
<Space size={16} style={{ marginTop: 8, marginBottom: 8 }}>
|
||||||
[value.logical]: list.filter((item) => isValid(item) && Object.keys(item).length),
|
<a
|
||||||
};
|
onClick={() => {
|
||||||
onChange?.(values);
|
const value = field.value || {};
|
||||||
}}
|
const items = value[logic] || [];
|
||||||
/>
|
items.push({});
|
||||||
|
field.value = {
|
||||||
|
[logic]: items,
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('Add condition')}
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
onClick={() => {
|
||||||
|
const value = field.value || {};
|
||||||
|
const items = value[logic] || [];
|
||||||
|
items.push({
|
||||||
|
$and: [{}],
|
||||||
|
});
|
||||||
|
field.value = {
|
||||||
|
[logic]: items,
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('Add condition group')}
|
||||||
|
</a>
|
||||||
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
@ -1,247 +1,54 @@
|
|||||||
import { CloseCircleOutlined } from '@ant-design/icons';
|
import { CloseCircleOutlined } from '@ant-design/icons';
|
||||||
import { FormItem as FormilyFormItem, FormLayout, Space as AntdSpace } from '@formily/antd';
|
import { observer, useField } from '@formily/react';
|
||||||
import { createForm, onFieldReact, onFieldValueChange, onFormValuesChange } from '@formily/core';
|
import { Cascader, Select, Space } from 'antd';
|
||||||
import { Field } from '@formily/core/esm/models/Field';
|
import React, { useContext } from 'react';
|
||||||
import { Form } from '@formily/core/esm/models/Form';
|
import { RemoveConditionContext } from './context';
|
||||||
import {
|
import { DynamicComponent } from './DynamicComponent';
|
||||||
FieldContext,
|
import { useValues } from './useValues';
|
||||||
FormContext,
|
|
||||||
ISchema,
|
|
||||||
Schema,
|
|
||||||
SchemaKey,
|
|
||||||
SchemaOptionsContext,
|
|
||||||
useFieldSchema,
|
|
||||||
} from '@formily/react';
|
|
||||||
import { isValid, uid } from '@formily/shared';
|
|
||||||
import { get } from 'lodash';
|
|
||||||
import React, { useContext, useMemo } from 'react';
|
|
||||||
import { SchemaComponent } from '../../core';
|
|
||||||
|
|
||||||
function useFilterColumns(): Map<SchemaKey, Schema> {
|
|
||||||
const schema = useFieldSchema();
|
|
||||||
const columns = schema.reduceProperties((columns, current) => {
|
|
||||||
if (current['x-component'] === 'Filter.Column') {
|
|
||||||
const fieldName = Object.keys(current.properties).shift();
|
|
||||||
columns.set(fieldName, current);
|
|
||||||
return columns;
|
|
||||||
}
|
|
||||||
return columns;
|
|
||||||
}, new Map<SchemaKey, Schema>());
|
|
||||||
return columns;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const FilterItem = (props) => {
|
|
||||||
const { value, initialValues = {}, onRemove, onChange } = props;
|
|
||||||
const options = useContext(SchemaOptionsContext);
|
|
||||||
const columns = useFilterColumns();
|
|
||||||
|
|
||||||
const toValues = (value) => {
|
|
||||||
if (!value) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
if (Object.keys(value).length === 0) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
const fieldName = Object.keys(value).shift();
|
|
||||||
const nested = value[fieldName];
|
|
||||||
const column = columns.get(fieldName).toJSON();
|
|
||||||
const operations = column?.['x-component-props']?.['operations'] || [];
|
|
||||||
if (!nested) {
|
|
||||||
return {
|
|
||||||
column,
|
|
||||||
operations,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (Object.keys(nested).length === 0) {
|
|
||||||
return {
|
|
||||||
column,
|
|
||||||
operations,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const operationValue = Object.keys(nested).shift();
|
|
||||||
console.log('toValues', { operationValue });
|
|
||||||
const operation = operations.find((operation) => operation.value === operationValue);
|
|
||||||
console.log('toValues', { operation });
|
|
||||||
if (!operation) {
|
|
||||||
return {
|
|
||||||
operations,
|
|
||||||
column,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (operation.noValue) {
|
|
||||||
return {
|
|
||||||
column,
|
|
||||||
operations,
|
|
||||||
operation,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
column,
|
|
||||||
operation,
|
|
||||||
operations,
|
|
||||||
value: nested[operationValue],
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const values = toValues(value);
|
|
||||||
|
|
||||||
console.log('toValues', values, value);
|
|
||||||
|
|
||||||
const Remove = (props) => {
|
|
||||||
return (
|
|
||||||
onRemove && (
|
|
||||||
<a onClick={() => onRemove()}>
|
|
||||||
<CloseCircleOutlined />
|
|
||||||
</a>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const form = useMemo(
|
|
||||||
() =>
|
|
||||||
createForm({
|
|
||||||
initialValues: values,
|
|
||||||
effects: (form) => {
|
|
||||||
onFieldValueChange('column', (field: Field, form: Form) => {
|
|
||||||
const column = (field.value || {}) as ISchema;
|
|
||||||
const operations = column?.['x-component-props']?.['operations'] || [];
|
|
||||||
field.query('operation').take((f: Field) => {
|
|
||||||
f.setDataSource(operations);
|
|
||||||
f.value = get(operations, [0]);
|
|
||||||
});
|
|
||||||
field.query('value').take((f: Field) => {
|
|
||||||
f.value = undefined;
|
|
||||||
f.componentProps.schema = column;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
onFieldReact('operation', (field: Field) => {
|
|
||||||
console.log('operation', field.value);
|
|
||||||
const operation = field.value || {};
|
|
||||||
field.query('value').take((f: Field) => {
|
|
||||||
f.visible = !operation.noValue;
|
|
||||||
if (operation.noValue) {
|
|
||||||
f.value = undefined;
|
|
||||||
}
|
|
||||||
f.componentProps.operation = operation;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
onFormValuesChange((form) => {
|
|
||||||
const { column, operation, value } = form?.values || {};
|
|
||||||
|
|
||||||
if (!operation?.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const fieldName = Object.keys(column.properties).shift();
|
|
||||||
if (operation?.noValue) {
|
|
||||||
onChange({
|
|
||||||
[fieldName]: {
|
|
||||||
[operation.value]: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
onChange(
|
|
||||||
isValid(value)
|
|
||||||
? {
|
|
||||||
[fieldName]: {
|
|
||||||
[operation.value]: value,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
: {},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
console.log('form.values', form.values);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const columnEnum: any = [...columns.values()].map((column) => column.toJSON());
|
|
||||||
const schema: ISchema = {
|
|
||||||
type: 'void',
|
|
||||||
properties: {
|
|
||||||
space: {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'AntdSpace',
|
|
||||||
properties: {
|
|
||||||
column: {
|
|
||||||
type: 'object',
|
|
||||||
name: 'column',
|
|
||||||
'x-decorator': 'FormilyFormItem',
|
|
||||||
'x-decorator-props': {
|
|
||||||
asterisk: true,
|
|
||||||
feedbackLayout: 'none',
|
|
||||||
},
|
|
||||||
'x-component': 'Select',
|
|
||||||
'x-component-props': {
|
|
||||||
objectValue: true,
|
|
||||||
style: {
|
|
||||||
width: 100,
|
|
||||||
},
|
|
||||||
fieldNames: {
|
|
||||||
label: 'title',
|
|
||||||
value: 'name',
|
|
||||||
options: 'options',
|
|
||||||
},
|
|
||||||
options: columnEnum,
|
|
||||||
},
|
|
||||||
enum: columnEnum,
|
|
||||||
},
|
|
||||||
operation: {
|
|
||||||
type: 'object',
|
|
||||||
name: 'operation',
|
|
||||||
'x-decorator': 'FormilyFormItem',
|
|
||||||
'x-decorator-props': {
|
|
||||||
asterisk: true,
|
|
||||||
feedbackLayout: 'none',
|
|
||||||
},
|
|
||||||
'x-component': 'Select',
|
|
||||||
'x-component-props': {
|
|
||||||
objectValue: true,
|
|
||||||
style: {
|
|
||||||
width: 100,
|
|
||||||
},
|
|
||||||
fieldNames: {
|
|
||||||
label: 'label',
|
|
||||||
value: 'value',
|
|
||||||
options: 'options',
|
|
||||||
},
|
|
||||||
options: values.operations,
|
|
||||||
},
|
|
||||||
enum: values.operations,
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
type: 'object',
|
|
||||||
name: 'value',
|
|
||||||
'x-decorator': 'FormilyFormItem',
|
|
||||||
'x-decorator-props': {
|
|
||||||
asterisk: true,
|
|
||||||
feedbackLayout: 'none',
|
|
||||||
},
|
|
||||||
'x-component': 'Filter.DynamicValue',
|
|
||||||
'x-component-props': {
|
|
||||||
schema: values.column,
|
|
||||||
operation: values.operation,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[uid()]: {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'Remove',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
export const FilterItem = observer((props: any) => {
|
||||||
|
const field = useField<any>();
|
||||||
|
const remove = useContext(RemoveConditionContext);
|
||||||
|
const { option, options, dataIndex, operator, setDataIndex, setOperator, value, setValue } = useValues();
|
||||||
return (
|
return (
|
||||||
<FieldContext.Provider value={null}>
|
<div style={{ marginBottom: 8 }}>
|
||||||
<FormContext.Provider value={form}>
|
<Space>
|
||||||
<FormLayout layout={'inline'}>
|
<Cascader
|
||||||
<SchemaComponent schema={schema} components={{ AntdSpace, FormilyFormItem, Remove }}></SchemaComponent>
|
fieldNames={{
|
||||||
</FormLayout>
|
label: 'title',
|
||||||
</FormContext.Provider>
|
value: 'name',
|
||||||
</FieldContext.Provider>
|
children: 'children',
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
width: 150,
|
||||||
|
}}
|
||||||
|
changeOnSelect={false}
|
||||||
|
key={field.address.toString()}
|
||||||
|
value={dataIndex}
|
||||||
|
options={options}
|
||||||
|
onChange={(value) => {
|
||||||
|
setDataIndex(value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Select
|
||||||
|
value={operator}
|
||||||
|
options={option?.operators}
|
||||||
|
onChange={(value) => {
|
||||||
|
setOperator(value);
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
minWidth: 100,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{React.createElement(DynamicComponent, {
|
||||||
|
value,
|
||||||
|
schema: option?.schema,
|
||||||
|
onChange(value) {
|
||||||
|
setValue(value);
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
<CloseCircleOutlined onClick={() => remove()} />
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export default FilterItem;
|
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
import { ArrayField as ArrayFieldModel } from '@formily/core';
|
||||||
|
import { ObjectField, observer, useField } from '@formily/react';
|
||||||
|
import React from 'react';
|
||||||
|
import { RemoveConditionContext } from './context';
|
||||||
|
import { FilterGroup } from './FilterGroup';
|
||||||
|
import { FilterItem } from './FilterItem';
|
||||||
|
|
||||||
|
export const FilterItems = observer((props) => {
|
||||||
|
const field = useField<ArrayFieldModel>();
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{field?.value?.map((item, index) => {
|
||||||
|
return (
|
||||||
|
<RemoveConditionContext.Provider value={() => field.remove(index)}>
|
||||||
|
<ObjectField name={index} component={[item.$and || item.$or ? FilterGroup : FilterItem]} />
|
||||||
|
</RemoveConditionContext.Provider>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
@ -1,83 +0,0 @@
|
|||||||
import { onFormReset } from '@formily/core';
|
|
||||||
import { useForm } from '@formily/react';
|
|
||||||
import { uid } from '@formily/shared';
|
|
||||||
import { useMap } from 'ahooks';
|
|
||||||
import React, { useEffect } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { FilterGroup } from './FilterGroup';
|
|
||||||
import { FilterItem } from './FilterItem';
|
|
||||||
|
|
||||||
export function FilterList(props) {
|
|
||||||
const { initialValue = [] } = props;
|
|
||||||
const form = useForm();
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const [map, { set, setAll, remove, reset }] = useMap<string, any>(
|
|
||||||
initialValue.map((item, index) => {
|
|
||||||
return [`index-${index}`, item];
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
useEffect(() => {
|
|
||||||
const id = uid();
|
|
||||||
form.addEffects(id, () => {
|
|
||||||
onFormReset((form) => {
|
|
||||||
setAll([]);
|
|
||||||
setTimeout(() => {
|
|
||||||
reset();
|
|
||||||
}, 0);
|
|
||||||
});
|
|
||||||
return () => {
|
|
||||||
form.removeEffects(id);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
useEffect(() => {
|
|
||||||
props.onChange?.([...map.values()]);
|
|
||||||
}, [map]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={'nb-filter-list'}>
|
|
||||||
<div>
|
|
||||||
{[...map.entries()].map(([index, item]) => {
|
|
||||||
if (item.and || item.or) {
|
|
||||||
return (
|
|
||||||
<FilterGroup
|
|
||||||
key={index}
|
|
||||||
value={item}
|
|
||||||
onChange={(value: any) => set(index, value)}
|
|
||||||
onRemove={() => remove(index)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<FilterItem
|
|
||||||
key={index}
|
|
||||||
value={item}
|
|
||||||
onChange={(value: any) => set(index, value)}
|
|
||||||
onRemove={() => remove(index)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
<div style={{ marginTop: 16 }}>
|
|
||||||
<a
|
|
||||||
onClick={() => {
|
|
||||||
set(uid(), {});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t('Add filter')}
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
style={{ marginLeft: 16 }}
|
|
||||||
onClick={() => {
|
|
||||||
set(uid(), {
|
|
||||||
and: [{}],
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t('Add filter group')}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
@ -0,0 +1,19 @@
|
|||||||
|
import { css } from '@emotion/css';
|
||||||
|
import { Button } from 'antd';
|
||||||
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
export const SaveDefaultValue = (props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
className={css`
|
||||||
|
border-color: rgb(241, 139, 98);
|
||||||
|
color: rgb(241, 139, 98);
|
||||||
|
`}
|
||||||
|
type={'dashed'}
|
||||||
|
>
|
||||||
|
{t('Save conditions')}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
@ -1,8 +1,4 @@
|
|||||||
import { createContext } from 'react';
|
import { createContext } from 'react';
|
||||||
|
|
||||||
export const FilterContext = createContext<FilterContextProps>({});
|
export const RemoveConditionContext = createContext(null);
|
||||||
|
export const FilterContext = createContext(null);
|
||||||
export interface FilterContextProps {
|
|
||||||
visible?: boolean;
|
|
||||||
setVisible?: (v: boolean) => void;
|
|
||||||
}
|
|
||||||
|
@ -1,92 +0,0 @@
|
|||||||
/**
|
|
||||||
* title: Filter
|
|
||||||
*/
|
|
||||||
import { observer, useForm } from '@formily/react';
|
|
||||||
import { AntdSchemaComponentProvider, SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
const schema = {
|
|
||||||
type: 'void',
|
|
||||||
properties: {
|
|
||||||
demo: {
|
|
||||||
name: 'filter',
|
|
||||||
type: 'array',
|
|
||||||
'x-component': 'Filter',
|
|
||||||
'x-component-props': {
|
|
||||||
onChange: (value) => {
|
|
||||||
console.log('=====', JSON.stringify(value, null, 2));
|
|
||||||
},
|
|
||||||
},
|
|
||||||
default: {
|
|
||||||
and: [
|
|
||||||
{
|
|
||||||
field1: {
|
|
||||||
eq: 'aa',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field1: {
|
|
||||||
eq: 500,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
column1: {
|
|
||||||
type: 'void',
|
|
||||||
title: '字段1',
|
|
||||||
'x-component': 'Filter.Column',
|
|
||||||
'x-component-props': {
|
|
||||||
operations: [
|
|
||||||
{ label: '等于', value: 'eq' },
|
|
||||||
{ label: '不等于', value: 'ne' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
field1: {
|
|
||||||
type: 'string',
|
|
||||||
'x-component': 'Input',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
column2: {
|
|
||||||
type: 'void',
|
|
||||||
title: '字段2',
|
|
||||||
'x-component': 'Filter.Column',
|
|
||||||
'x-component-props': {
|
|
||||||
operations: [
|
|
||||||
{ label: '大于', value: 'gt' },
|
|
||||||
{ label: '小于', value: 'lt' },
|
|
||||||
{ label: '非空', value: 'notNull', noValue: true },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
field2: {
|
|
||||||
type: 'number',
|
|
||||||
'x-component': 'InputNumber',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
type: 'string',
|
|
||||||
'x-component': 'Output',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const Output = observer(() => {
|
|
||||||
const form = useForm();
|
|
||||||
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default () => {
|
|
||||||
return (
|
|
||||||
<SchemaComponentProvider>
|
|
||||||
<AntdSchemaComponentProvider>
|
|
||||||
<SchemaComponent components={{ Output }} schema={schema} />
|
|
||||||
</AntdSchemaComponentProvider>
|
|
||||||
</SchemaComponentProvider>
|
|
||||||
);
|
|
||||||
};
|
|
101
packages/client/src/schema-component/antd/filter/demos/demo2.tsx
Normal file
101
packages/client/src/schema-component/antd/filter/demos/demo2.tsx
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import { AntdSchemaComponentProvider, Filter, Input, SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const schema: any = {
|
||||||
|
type: 'void',
|
||||||
|
properties: {
|
||||||
|
demo: {
|
||||||
|
name: 'filter',
|
||||||
|
type: 'object',
|
||||||
|
enum: [
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
title: 'Name',
|
||||||
|
operators: [
|
||||||
|
{ label: 'eq', value: '$eq' },
|
||||||
|
{ label: 'ne', value: '$ne' },
|
||||||
|
],
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
title: 'Name',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'age',
|
||||||
|
title: 'Age',
|
||||||
|
operators: [
|
||||||
|
{ label: 'in', value: '$in' },
|
||||||
|
{ label: 'not', value: '$not' },
|
||||||
|
],
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
title: 'Age',
|
||||||
|
'x-component': 'InputNumber',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'tags',
|
||||||
|
title: 'Tags',
|
||||||
|
schema: {
|
||||||
|
title: 'Tags',
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'slug',
|
||||||
|
title: 'Slug',
|
||||||
|
operators: [
|
||||||
|
{ label: 'in', value: '$in' },
|
||||||
|
{ label: 'not', value: '$not' },
|
||||||
|
],
|
||||||
|
schema: {
|
||||||
|
title: 'Slug',
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
title: 'Title',
|
||||||
|
operators: [
|
||||||
|
{ label: 'eq', value: '$eq' },
|
||||||
|
{ label: 'ne', value: '$ne' },
|
||||||
|
],
|
||||||
|
schema: {
|
||||||
|
title: 'Title',
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: {
|
||||||
|
$or: [
|
||||||
|
{
|
||||||
|
name: {
|
||||||
|
$ne: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'tags.title': {
|
||||||
|
$eq: 'aaa',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'x-component': 'Filter',
|
||||||
|
'x-component-props': {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
return (
|
||||||
|
<SchemaComponentProvider>
|
||||||
|
<AntdSchemaComponentProvider>
|
||||||
|
<SchemaComponent components={{ Input, Filter }} schema={schema} />
|
||||||
|
</AntdSchemaComponentProvider>
|
||||||
|
</SchemaComponentProvider>
|
||||||
|
);
|
||||||
|
};
|
180
packages/client/src/schema-component/antd/filter/demos/demo3.tsx
Normal file
180
packages/client/src/schema-component/antd/filter/demos/demo3.tsx
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
import { ISchema, useForm } from '@formily/react';
|
||||||
|
import {
|
||||||
|
AntdSchemaComponentProvider,
|
||||||
|
Filter,
|
||||||
|
Input,
|
||||||
|
SchemaComponent,
|
||||||
|
SchemaComponentProvider,
|
||||||
|
useActionContext,
|
||||||
|
useRequest
|
||||||
|
} from '@nocobase/client';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const dataSource = [
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
title: 'Name',
|
||||||
|
operators: [
|
||||||
|
{ label: 'eq', value: '$eq' },
|
||||||
|
{ label: 'ne', value: '$ne' },
|
||||||
|
],
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
title: 'Name',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'age',
|
||||||
|
title: 'Age',
|
||||||
|
operators: [
|
||||||
|
{ label: 'in', value: '$in' },
|
||||||
|
{ label: 'not', value: '$not' },
|
||||||
|
],
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
title: 'Age',
|
||||||
|
'x-component': 'InputNumber',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'tags',
|
||||||
|
title: 'Tags',
|
||||||
|
schema: {
|
||||||
|
title: 'Tags',
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'slug',
|
||||||
|
title: 'Slug',
|
||||||
|
operators: [
|
||||||
|
{ label: 'in', value: '$in' },
|
||||||
|
{ label: 'not', value: '$not' },
|
||||||
|
],
|
||||||
|
schema: {
|
||||||
|
title: 'Slug',
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
title: 'Title',
|
||||||
|
operators: [
|
||||||
|
{ label: 'eq', value: '$eq' },
|
||||||
|
{ label: 'ne', value: '$ne' },
|
||||||
|
],
|
||||||
|
schema: {
|
||||||
|
title: 'Title',
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const schema: ISchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
action1: {
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
type: 'primary',
|
||||||
|
popover: true,
|
||||||
|
openMode: 'popover',
|
||||||
|
},
|
||||||
|
type: 'void',
|
||||||
|
title: 'Open',
|
||||||
|
properties: {
|
||||||
|
popover: {
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'Form',
|
||||||
|
'x-decorator-props': {},
|
||||||
|
'x-component': 'Action.Popover',
|
||||||
|
'x-component-props': {
|
||||||
|
trigger: 'click',
|
||||||
|
placement: 'bottomLeft',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
filter: {
|
||||||
|
type: 'object',
|
||||||
|
default: {
|
||||||
|
$or: [
|
||||||
|
{
|
||||||
|
name: {
|
||||||
|
$ne: 'aa',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'tags.title': {
|
||||||
|
$eq: 'aaa',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'x-component': 'Filter',
|
||||||
|
'x-component-props': {
|
||||||
|
useDataSource(options) {
|
||||||
|
return useRequest(
|
||||||
|
() =>
|
||||||
|
Promise.resolve({
|
||||||
|
data: dataSource,
|
||||||
|
}),
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Action.Popover.Footer',
|
||||||
|
properties: {
|
||||||
|
actions: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ActionBar',
|
||||||
|
properties: {
|
||||||
|
saveDefault: {
|
||||||
|
type: 'void',
|
||||||
|
title: 'Submit',
|
||||||
|
'x-component': 'Filter.SaveDefaultValue',
|
||||||
|
'x-component-props': {},
|
||||||
|
},
|
||||||
|
submit: {
|
||||||
|
type: 'void',
|
||||||
|
title: 'Submit',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
type: 'primary',
|
||||||
|
useAction() {
|
||||||
|
const form = useForm();
|
||||||
|
const ctx = useActionContext();
|
||||||
|
return {
|
||||||
|
async run() {
|
||||||
|
ctx.setVisible(false);
|
||||||
|
console.log('form.values', JSON.stringify(form.values, null, 2));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
return (
|
||||||
|
<SchemaComponentProvider>
|
||||||
|
<AntdSchemaComponentProvider>
|
||||||
|
<SchemaComponent components={{ Input, Filter }} schema={schema} />
|
||||||
|
</AntdSchemaComponentProvider>
|
||||||
|
</SchemaComponentProvider>
|
||||||
|
);
|
||||||
|
};
|
170
packages/client/src/schema-component/antd/filter/demos/demo4.tsx
Normal file
170
packages/client/src/schema-component/antd/filter/demos/demo4.tsx
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
import { ISchema } from '@formily/react';
|
||||||
|
import {
|
||||||
|
AntdSchemaComponentProvider,
|
||||||
|
Filter,
|
||||||
|
Input,
|
||||||
|
SchemaComponent,
|
||||||
|
SchemaComponentProvider,
|
||||||
|
Select
|
||||||
|
} from '@nocobase/client';
|
||||||
|
import { Space } from 'antd';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
|
const options: any = [
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
title: 'Name',
|
||||||
|
operators: [
|
||||||
|
{ label: 'eq', value: '$eq' },
|
||||||
|
{ label: 'ne', value: '$ne' },
|
||||||
|
],
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
title: 'Name',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'age',
|
||||||
|
title: 'Age',
|
||||||
|
operators: [
|
||||||
|
{ label: 'in', value: '$in' },
|
||||||
|
{ label: 'not', value: '$not' },
|
||||||
|
],
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
title: 'Age',
|
||||||
|
'x-component': 'InputNumber',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'tags',
|
||||||
|
title: 'Tags',
|
||||||
|
schema: {
|
||||||
|
title: 'Tags',
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'slug',
|
||||||
|
title: 'Slug',
|
||||||
|
operators: [
|
||||||
|
{ label: 'in', value: '$in' },
|
||||||
|
{ label: 'not', value: '$not' },
|
||||||
|
],
|
||||||
|
schema: {
|
||||||
|
title: 'Slug',
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
title: 'Title',
|
||||||
|
operators: [
|
||||||
|
{ label: 'eq', value: '$eq' },
|
||||||
|
{ label: 'ne', value: '$ne' },
|
||||||
|
],
|
||||||
|
schema: {
|
||||||
|
title: 'Title',
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const defaultValue = {
|
||||||
|
$or: [
|
||||||
|
{
|
||||||
|
name: {
|
||||||
|
$ne: '{{node1.field1}}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'tags.title': {
|
||||||
|
$eq: 'aaa',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const schema: ISchema = {
|
||||||
|
type: 'void',
|
||||||
|
properties: {
|
||||||
|
demo: {
|
||||||
|
name: 'filter',
|
||||||
|
type: 'object',
|
||||||
|
enum: options,
|
||||||
|
default: defaultValue,
|
||||||
|
'x-component': 'Filter',
|
||||||
|
'x-component-props': {
|
||||||
|
dynamicComponent: 'CustomDynamicComponent',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const ExpRE = /^\s*\{\{([\s\S]*)\}\}\s*$/;
|
||||||
|
|
||||||
|
const CustomDynamicComponent = (props) => {
|
||||||
|
const { value, onChange, renderSchemaComponent } = props;
|
||||||
|
let matched = null;
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
matched = ExpRE.exec(value);
|
||||||
|
}
|
||||||
|
const [source, setSource] = useState(matched ? 'node1' : 'default');
|
||||||
|
const options = [
|
||||||
|
{
|
||||||
|
label: '字段1',
|
||||||
|
value: `{{${source}.field1}}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '字段2',
|
||||||
|
value: `{{${source}.field2}}`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
<Space>
|
||||||
|
<Select
|
||||||
|
style={{ minWidth: 120 }}
|
||||||
|
value={source}
|
||||||
|
onChange={(value) => {
|
||||||
|
setSource(value);
|
||||||
|
onChange(null);
|
||||||
|
}}
|
||||||
|
options={[
|
||||||
|
{
|
||||||
|
label: '默认',
|
||||||
|
value: 'default',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '节点1',
|
||||||
|
value: 'node1',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
{source === 'default' ? (
|
||||||
|
renderSchemaComponent()
|
||||||
|
) : (
|
||||||
|
<Select
|
||||||
|
style={{ minWidth: 120 }}
|
||||||
|
onChange={(value) => {
|
||||||
|
onChange(value);
|
||||||
|
}}
|
||||||
|
value={value}
|
||||||
|
options={options}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
return (
|
||||||
|
<SchemaComponentProvider>
|
||||||
|
<AntdSchemaComponentProvider>
|
||||||
|
<SchemaComponent components={{ Input, Filter, CustomDynamicComponent }} schema={schema} />
|
||||||
|
</AntdSchemaComponentProvider>
|
||||||
|
</SchemaComponentProvider>
|
||||||
|
);
|
||||||
|
};
|
@ -1,9 +0,0 @@
|
|||||||
import { useContext } from 'react';
|
|
||||||
import { FilterContext } from './context';
|
|
||||||
|
|
||||||
export const useFilterContext = () => {
|
|
||||||
const ctx = useContext(FilterContext);
|
|
||||||
return {
|
|
||||||
...ctx,
|
|
||||||
};
|
|
||||||
};
|
|
@ -9,6 +9,14 @@ group:
|
|||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
### Filter usage
|
### Filter Action
|
||||||
|
|
||||||
<code src="./demos/demo1.tsx" />
|
<code src="./demos/demo3.tsx" />
|
||||||
|
|
||||||
|
### Filter Default Value
|
||||||
|
|
||||||
|
<code src="./demos/demo2.tsx" />
|
||||||
|
|
||||||
|
### Custom Dynamic Component
|
||||||
|
|
||||||
|
<code src="./demos/demo4.tsx" />
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
import { useField } from '@formily/react';
|
||||||
|
import { Input } from 'antd';
|
||||||
|
import flat from 'flat';
|
||||||
|
import { useContext } from 'react';
|
||||||
|
import { FilterContext } from './context';
|
||||||
|
|
||||||
|
export const useValues = () => {
|
||||||
|
const { options } = useContext(FilterContext);
|
||||||
|
const field = useField<any>();
|
||||||
|
const obj = flat(field.value || {});
|
||||||
|
const key = Object.keys(obj).shift() || '';
|
||||||
|
const [path, others = ''] = key.split('.$');
|
||||||
|
let [operator] = others.split('.');
|
||||||
|
const dataIndex = path.split('.');
|
||||||
|
let maxDepth = dataIndex.length;
|
||||||
|
if (operator) {
|
||||||
|
operator = '$' + operator;
|
||||||
|
++maxDepth;
|
||||||
|
}
|
||||||
|
const values = flat(field.value || {}, { maxDepth });
|
||||||
|
const value = Object.values<any>(values).shift();
|
||||||
|
const findOption = (dataIndex) => {
|
||||||
|
let items = options;
|
||||||
|
let option;
|
||||||
|
dataIndex.forEach((name, index) => {
|
||||||
|
const item = items.find((item) => item.name === name);
|
||||||
|
if (item) {
|
||||||
|
option = item;
|
||||||
|
}
|
||||||
|
items = item?.children || [];
|
||||||
|
});
|
||||||
|
return option;
|
||||||
|
};
|
||||||
|
const option = findOption(dataIndex);
|
||||||
|
console.log('option', option);
|
||||||
|
const operators = option?.operators;
|
||||||
|
return {
|
||||||
|
option,
|
||||||
|
dataIndex,
|
||||||
|
options,
|
||||||
|
operators,
|
||||||
|
operator,
|
||||||
|
value,
|
||||||
|
component: [Input, {}],
|
||||||
|
// 当 dataIndex 变化,value 清空
|
||||||
|
setDataIndex(di: string[]) {
|
||||||
|
const option = findOption(di);
|
||||||
|
const op = option?.operators?.[0]?.value || '$eq';
|
||||||
|
field.value = flat.unflatten({
|
||||||
|
[`${di.join('.')}.${op}`]: null,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// 如果只是 Operator 变化,value 要保留
|
||||||
|
setOperator(op: string) {
|
||||||
|
field.value = flat.unflatten({
|
||||||
|
[`${dataIndex.join('.')}.${op}`]: value,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setValue(v: any) {
|
||||||
|
field.value = flat.unflatten({
|
||||||
|
[`${dataIndex.join('.')}.${operator || '$eq'}`]: v,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
@ -1,5 +1,5 @@
|
|||||||
import { createForm } from '@formily/core';
|
import { createForm } from '@formily/core';
|
||||||
import { FormProvider } from '@formily/react';
|
import { FormProvider, Schema } from '@formily/react';
|
||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
import { useCookieState } from 'ahooks';
|
import { useCookieState } from 'ahooks';
|
||||||
import React, { useMemo, useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
@ -12,6 +12,26 @@ const randomString = (prefix: string = '') => {
|
|||||||
return `${prefix}${uid()}`;
|
return `${prefix}${uid()}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Schema.silent(true);
|
||||||
|
|
||||||
|
const Registry = {
|
||||||
|
silent: true,
|
||||||
|
compile(expression: string, scope = {}) {
|
||||||
|
console.log('expression', expression);
|
||||||
|
if (Registry.silent) {
|
||||||
|
try {
|
||||||
|
return new Function('$root', `with($root) { return (${expression}); }`)(scope);
|
||||||
|
} catch {
|
||||||
|
return `{{${expression}}}`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return new Function('$root', `with($root) { return (${expression}); }`)(scope);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Schema.registerCompiler(Registry.compile);
|
||||||
|
|
||||||
export const SchemaComponentProvider: React.FC<ISchemaComponentProvider> = (props) => {
|
export const SchemaComponentProvider: React.FC<ISchemaComponentProvider> = (props) => {
|
||||||
const { designable, components, children } = props;
|
const { designable, components, children } = props;
|
||||||
const [, setUid] = useState(uid());
|
const [, setUid] = useState(uid());
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import flat from 'flat';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { SchemaInitializer } from '../SchemaInitializer';
|
import { SchemaInitializer } from '../SchemaInitializer';
|
||||||
@ -33,6 +34,28 @@ export const GridFormItemInitializers = (props: any) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'item',
|
||||||
|
title: t('Add Filter'),
|
||||||
|
component: 'GeneralInitializer',
|
||||||
|
schema: {
|
||||||
|
name: 'filter',
|
||||||
|
type: 'object',
|
||||||
|
default: flat.unflatten({
|
||||||
|
$or: [
|
||||||
|
{ 'aa.$eq': 'b' },
|
||||||
|
{ 'bb.field.$eq': ['aabb', 'aaa'] },
|
||||||
|
{
|
||||||
|
'bb.field': {
|
||||||
|
$eq: ['aabb', 'aaa'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
'x-component': 'Filter',
|
||||||
|
'x-component-props': {},
|
||||||
|
},
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{t('Configure fields')}
|
{t('Configure fields')}
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
import { Switch } from 'antd';
|
||||||
|
import flat from 'flat';
|
||||||
|
import React from 'react';
|
||||||
|
import { SchemaInitializer } from '../../SchemaInitializer';
|
||||||
|
import { useCurrentSchema } from '../utils';
|
||||||
|
|
||||||
|
export const FilterActionInitializer = (props) => {
|
||||||
|
const { item, insert } = props;
|
||||||
|
const { exists, remove } = useCurrentSchema(item.schema['x-action'], 'x-action', item.find);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SchemaInitializer.Item
|
||||||
|
onClick={() => {
|
||||||
|
if (exists) {
|
||||||
|
return remove();
|
||||||
|
}
|
||||||
|
insert({
|
||||||
|
...item.schema,
|
||||||
|
name: 'filter',
|
||||||
|
type: 'object',
|
||||||
|
default: flat.unflatten({
|
||||||
|
$or: [
|
||||||
|
{ 'aa.$eq': 'b' },
|
||||||
|
{ 'bb.field.$eq': ['aabb', 'aaa'] },
|
||||||
|
{
|
||||||
|
'bb.field': {
|
||||||
|
$eq: ['aabb', 'aaa'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
'x-component': 'Filter',
|
||||||
|
'x-component-props': {},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||||
|
{item.title} <Switch style={{ marginLeft: 20 }} size={'small'} checked={exists} />
|
||||||
|
</div>
|
||||||
|
</SchemaInitializer.Item>
|
||||||
|
);
|
||||||
|
};
|
@ -2,6 +2,7 @@ export * from './ActionInitializer';
|
|||||||
export * from './AddNewActionInitializer';
|
export * from './AddNewActionInitializer';
|
||||||
export * from './CalendarBlockInitializer';
|
export * from './CalendarBlockInitializer';
|
||||||
export * from './CollectionFieldInitializer';
|
export * from './CollectionFieldInitializer';
|
||||||
|
export * from './FilterActionInitializer';
|
||||||
export * from './FormBlockInitializer';
|
export * from './FormBlockInitializer';
|
||||||
export * from './GeneralInitializer';
|
export * from './GeneralInitializer';
|
||||||
export * from './MarkdownBlockInitializer';
|
export * from './MarkdownBlockInitializer';
|
||||||
|
@ -12,7 +12,7 @@ export const TableActionInitializers = {
|
|||||||
{
|
{
|
||||||
type: 'item',
|
type: 'item',
|
||||||
title: "{{t('Filter')}}",
|
title: "{{t('Filter')}}",
|
||||||
component: 'ActionInitializer',
|
component: 'FilterActionInitializer',
|
||||||
schema: {
|
schema: {
|
||||||
title: '{{ t("Filter") }}',
|
title: '{{ t("Filter") }}',
|
||||||
'x-action': 'filter',
|
'x-action': 'filter',
|
||||||
|
41
yarn.lock
41
yarn.lock
@ -4125,10 +4125,10 @@ ansi-styles@^5.0.0:
|
|||||||
resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
|
resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
|
||||||
integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
|
integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
|
||||||
|
|
||||||
antd@^4.18.5:
|
antd@^4.18.9:
|
||||||
version "4.18.5"
|
version "4.18.9"
|
||||||
resolved "https://registry.npmjs.org/antd/-/antd-4.18.5.tgz#e5ffbe238fd6fdfcd1ed39ba96e4b1bd5f589757"
|
resolved "https://registry.npmjs.org/antd/-/antd-4.18.9.tgz#a0d246786d5076ea7a53b67191cd39295fc2322a"
|
||||||
integrity sha512-5fN3C2lWAzonhOYYlNpzIw2OHl7vxFZ+4cJ7DK/XZrV+75OY61Y+OkanqMJwrFtDDamIez35OM7cAezGko9tew==
|
integrity sha512-MbtFY2J8LvXUnxYH2QehdhP9qMEpHvOp7PmiTIHc7v6aSb+LILCibskRIMGNEKvvhBvsTdq0cnjanR9/IbqEAw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ant-design/colors" "^6.0.0"
|
"@ant-design/colors" "^6.0.0"
|
||||||
"@ant-design/icons" "^4.7.0"
|
"@ant-design/icons" "^4.7.0"
|
||||||
@ -4145,8 +4145,8 @@ antd@^4.18.5:
|
|||||||
rc-collapse "~3.1.0"
|
rc-collapse "~3.1.0"
|
||||||
rc-dialog "~8.6.0"
|
rc-dialog "~8.6.0"
|
||||||
rc-drawer "~4.4.2"
|
rc-drawer "~4.4.2"
|
||||||
rc-dropdown "~3.2.0"
|
rc-dropdown "~3.2.5"
|
||||||
rc-field-form "~1.22.0-2"
|
rc-field-form "~1.23.0"
|
||||||
rc-image "~5.2.5"
|
rc-image "~5.2.5"
|
||||||
rc-input-number "~7.3.0"
|
rc-input-number "~7.3.0"
|
||||||
rc-mentions "~1.6.1"
|
rc-mentions "~1.6.1"
|
||||||
@ -4162,7 +4162,7 @@ antd@^4.18.5:
|
|||||||
rc-slider "~9.7.4"
|
rc-slider "~9.7.4"
|
||||||
rc-steps "~4.1.0"
|
rc-steps "~4.1.0"
|
||||||
rc-switch "~3.2.0"
|
rc-switch "~3.2.0"
|
||||||
rc-table "~7.22.2"
|
rc-table "~7.23.0"
|
||||||
rc-tabs "~11.10.0"
|
rc-tabs "~11.10.0"
|
||||||
rc-textarea "~0.3.0"
|
rc-textarea "~0.3.0"
|
||||||
rc-tooltip "~5.1.1"
|
rc-tooltip "~5.1.1"
|
||||||
@ -13360,7 +13360,7 @@ rc-drawer@~4.4.2:
|
|||||||
classnames "^2.2.6"
|
classnames "^2.2.6"
|
||||||
rc-util "^5.7.0"
|
rc-util "^5.7.0"
|
||||||
|
|
||||||
rc-dropdown@^3.2.0, rc-dropdown@~3.2.0:
|
rc-dropdown@^3.2.0:
|
||||||
version "3.2.0"
|
version "3.2.0"
|
||||||
resolved "https://registry.npmjs.org/rc-dropdown/-/rc-dropdown-3.2.0.tgz#da6c2ada403842baee3a9e909a0b1a91ba3e1090"
|
resolved "https://registry.npmjs.org/rc-dropdown/-/rc-dropdown-3.2.0.tgz#da6c2ada403842baee3a9e909a0b1a91ba3e1090"
|
||||||
integrity sha512-j1HSw+/QqlhxyTEF6BArVZnTmezw2LnSmRk6I9W7BCqNCKaRwleRmMMs1PHbuaG8dKHVqP6e21RQ7vPBLVnnNw==
|
integrity sha512-j1HSw+/QqlhxyTEF6BArVZnTmezw2LnSmRk6I9W7BCqNCKaRwleRmMMs1PHbuaG8dKHVqP6e21RQ7vPBLVnnNw==
|
||||||
@ -13369,10 +13369,19 @@ rc-dropdown@^3.2.0, rc-dropdown@~3.2.0:
|
|||||||
classnames "^2.2.6"
|
classnames "^2.2.6"
|
||||||
rc-trigger "^5.0.4"
|
rc-trigger "^5.0.4"
|
||||||
|
|
||||||
rc-field-form@~1.22.0-2:
|
rc-dropdown@~3.2.5:
|
||||||
version "1.22.1"
|
version "3.2.5"
|
||||||
resolved "https://registry.npmjs.org/rc-field-form/-/rc-field-form-1.22.1.tgz#0bd2f4e730ff2f071529d00bef28e062362890f5"
|
resolved "https://registry.npmjs.org/rc-dropdown/-/rc-dropdown-3.2.5.tgz#c211e571d29d15e7f725b5a75fc8c7f371fc3348"
|
||||||
integrity sha512-LweU7nBeqmC5r3HDUjRprcOXXobHXp/TGIxD7ppBq5FX6Iptt3ibdpRVg4RSyNulBNGHOuknHlRcguuIpvVMVg==
|
integrity sha512-dVO2eulOSbEf+F4OyhCY5iGiMVhUYY/qeXxL7Ex2jDBt/xc89jU07mNoowV6aWxwVOc70pxEINff0oM2ogjluA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.10.1"
|
||||||
|
classnames "^2.2.6"
|
||||||
|
rc-trigger "^5.0.4"
|
||||||
|
|
||||||
|
rc-field-form@~1.23.0:
|
||||||
|
version "1.23.0"
|
||||||
|
resolved "https://registry.npmjs.org/rc-field-form/-/rc-field-form-1.23.0.tgz#71e1430be5934118f1d00733e89896ffd81aa970"
|
||||||
|
integrity sha512-Zv5rrkbc/KJ5EDoUxX28Wy6Y8FOas03hffcYeSE821z7b7V6YtLrtQyJlv+0725xdCphqUoD/S+7b2KLl67Hew==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.8.4"
|
"@babel/runtime" "^7.8.4"
|
||||||
async-validator "^4.0.2"
|
async-validator "^4.0.2"
|
||||||
@ -13576,10 +13585,10 @@ rc-switch@~3.2.0:
|
|||||||
classnames "^2.2.1"
|
classnames "^2.2.1"
|
||||||
rc-util "^5.0.1"
|
rc-util "^5.0.1"
|
||||||
|
|
||||||
rc-table@~7.22.2:
|
rc-table@~7.23.0:
|
||||||
version "7.22.2"
|
version "7.23.0"
|
||||||
resolved "https://registry.npmjs.org/rc-table/-/rc-table-7.22.2.tgz#218f3f53bc91660560a344c8290a91a841a60b0a"
|
resolved "https://registry.npmjs.org/rc-table/-/rc-table-7.23.0.tgz#e5f76998ecf3246147d45ed311417c08886e6507"
|
||||||
integrity sha512-Ng2gNkGi6ybl6dzneRn2H4Gp8XhIbRa5rXQ7ZhZcgWVmfVMok70UHGPXcf68tXW6O0/qckTf/eOVsoviSvK4sw==
|
integrity sha512-Q1gneB2+lUa8EzCCfbrq+jO1qNSwQv1RUUXKB84W/Stdp4EvGOt2+QqGyfotMNM4JUw0fgGLwY+WjnhUhnLuQQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.10.1"
|
"@babel/runtime" "^7.10.1"
|
||||||
classnames "^2.2.5"
|
classnames "^2.2.5"
|
||||||
|
Loading…
Reference in New Issue
Block a user