feat: filter component finish
This commit is contained in:
parent
6f2069e918
commit
968a7ec80f
@ -16,6 +16,7 @@ export const useCollectionField = () => {
|
|||||||
}
|
}
|
||||||
const resourceName = `${ctx?.collectionName || collection?.name}.${ctx.name}`;
|
const resourceName = `${ctx?.collectionName || collection?.name}.${ctx.name}`;
|
||||||
const resource = api?.resource(resourceName, record[ctx.sourceKey]);
|
const resource = api?.resource(resourceName, record[ctx.sourceKey]);
|
||||||
|
debugger;
|
||||||
return {
|
return {
|
||||||
...ctx,
|
...ctx,
|
||||||
uiSchema: compile(ctx.uiSchema),
|
uiSchema: compile(ctx.uiSchema),
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
import { FormButtonGroup, Submit } from '@formily/antd';
|
|
||||||
import { createForm } from '@formily/core';
|
import { createForm } from '@formily/core';
|
||||||
import { FormProvider, observer, useFieldSchema } from '@formily/react';
|
import { observer, useFieldSchema } from '@formily/react';
|
||||||
import { Button, Popover } from 'antd';
|
|
||||||
import flatten from 'flat';
|
import flatten from 'flat';
|
||||||
import React, { useMemo, useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { SchemaComponent } from '../../core';
|
import { SchemaComponent } from '../../core';
|
||||||
import { useDesignable } from '../../hooks';
|
import { useDesignable } from '../../hooks';
|
||||||
import { useCompile } from '../../hooks/useCompile';
|
import { useCompile } from '../../hooks/useCompile';
|
||||||
import { IconPicker } from '../icon-picker';
|
import { FilterContext } from '../filter/context';
|
||||||
|
|
||||||
export const Filter = observer((props: any) => {
|
export const Filter = observer((props: any) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -25,45 +23,19 @@ export const Filter = observer((props: any) => {
|
|||||||
properties[p.name] = p;
|
properties[p.name] = p;
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<Popover
|
<FilterContext.Provider value={{ visible, setVisible }}>
|
||||||
trigger={['click']}
|
<SchemaComponent
|
||||||
placement={'bottomLeft'}
|
schema={{
|
||||||
visible={visible}
|
type: 'void',
|
||||||
onVisibleChange={setVisible}
|
properties: {
|
||||||
content={
|
filter: {
|
||||||
<div>
|
type: 'object',
|
||||||
<FormProvider form={form}>
|
title: t('Filter'),
|
||||||
<SchemaComponent
|
'x-component': 'Filter.Action',
|
||||||
schema={{
|
},
|
||||||
type: 'object',
|
},
|
||||||
properties: {
|
}}
|
||||||
filter: {
|
/>
|
||||||
type: 'object',
|
</FilterContext.Provider>
|
||||||
'x-component': 'Filter',
|
|
||||||
properties,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<FormButtonGroup align={'right'}>
|
|
||||||
<Submit
|
|
||||||
onSubmit={() => {
|
|
||||||
const { filter } = form.values;
|
|
||||||
console.log('Table.Filter', form.values);
|
|
||||||
setVisible(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t('Submit')}
|
|
||||||
</Submit>
|
|
||||||
</FormButtonGroup>
|
|
||||||
</FormProvider>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Button icon={<IconPicker type={icon} />}>
|
|
||||||
{count > 0 ? t('{{count}} filter items', { count }) : compile(filedSchema.title)}
|
|
||||||
<DesignableBar />
|
|
||||||
</Button>
|
|
||||||
</Popover>
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -7,6 +7,7 @@ import { uid } from '@formily/shared';
|
|||||||
import {
|
import {
|
||||||
AntdSchemaComponentProvider,
|
AntdSchemaComponentProvider,
|
||||||
CollectionField,
|
CollectionField,
|
||||||
|
CollectionManagerProvider,
|
||||||
CollectionProvider,
|
CollectionProvider,
|
||||||
SchemaComponent,
|
SchemaComponent,
|
||||||
SchemaComponentProvider,
|
SchemaComponentProvider,
|
||||||
@ -56,6 +57,13 @@ const schema = {
|
|||||||
'x-action': 'calendar:viewSelect',
|
'x-action': 'calendar:viewSelect',
|
||||||
'x-align': 'right',
|
'x-align': 'right',
|
||||||
},
|
},
|
||||||
|
filter: {
|
||||||
|
type: 'void',
|
||||||
|
title: '过滤',
|
||||||
|
'x-align': 'right',
|
||||||
|
'x-component': 'Calendar.Filter',
|
||||||
|
'x-action': 'calendar:filter',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
event: {
|
event: {
|
||||||
@ -98,9 +106,9 @@ const collection = {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
name: 'id',
|
name: 'id',
|
||||||
interface: 'input',
|
interface: 'input',
|
||||||
|
title: 'id',
|
||||||
uiSchema: {
|
uiSchema: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
title: 'id',
|
|
||||||
'x-component': 'Input',
|
'x-component': 'Input',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -108,9 +116,9 @@ const collection = {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
name: 'title',
|
name: 'title',
|
||||||
interface: 'input',
|
interface: 'input',
|
||||||
|
title: '标题',
|
||||||
uiSchema: {
|
uiSchema: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
title: '标题',
|
|
||||||
'x-component': 'Input',
|
'x-component': 'Input',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -118,9 +126,9 @@ const collection = {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
name: 'start',
|
name: 'start',
|
||||||
interface: 'datetime',
|
interface: 'datetime',
|
||||||
|
title: '开始时间',
|
||||||
uiSchema: {
|
uiSchema: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
title: '开始时间',
|
|
||||||
'x-component': 'DatePicker',
|
'x-component': 'DatePicker',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
dateFormat: 'YYYY-MM-DD',
|
dateFormat: 'YYYY-MM-DD',
|
||||||
@ -131,9 +139,9 @@ const collection = {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
name: 'end',
|
name: 'end',
|
||||||
interface: 'datetime',
|
interface: 'datetime',
|
||||||
|
title: '结束时间',
|
||||||
uiSchema: {
|
uiSchema: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
title: '结束时间',
|
|
||||||
'x-component': 'DatePicker',
|
'x-component': 'DatePicker',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
dateFormat: 'YYYY-MM-DD',
|
dateFormat: 'YYYY-MM-DD',
|
||||||
@ -144,14 +152,16 @@ const collection = {
|
|||||||
};
|
};
|
||||||
export default () => {
|
export default () => {
|
||||||
return (
|
return (
|
||||||
<CollectionProvider collection={collection}>
|
<CollectionManagerProvider>
|
||||||
<SchemaComponentProvider designable={true} components={{ CollectionField }}>
|
<CollectionProvider collection={collection}>
|
||||||
<SchemaInitializerProvider>
|
<SchemaComponentProvider designable={true} components={{ CollectionField }}>
|
||||||
<AntdSchemaComponentProvider>
|
<SchemaInitializerProvider>
|
||||||
<SchemaComponent schema={schema} />
|
<AntdSchemaComponentProvider>
|
||||||
</AntdSchemaComponentProvider>
|
<SchemaComponent schema={schema} />
|
||||||
</SchemaInitializerProvider>
|
</AntdSchemaComponentProvider>
|
||||||
</SchemaComponentProvider>
|
</SchemaInitializerProvider>
|
||||||
</CollectionProvider>
|
</SchemaComponentProvider>
|
||||||
|
</CollectionProvider>
|
||||||
|
</CollectionManagerProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -2,33 +2,33 @@ import { get } from 'lodash';
|
|||||||
import { i18n } from '../../../i18n';
|
import { i18n } from '../../../i18n';
|
||||||
|
|
||||||
export const fieldsToFilterColumns = (fields: any[], options: any = {}) => {
|
export const fieldsToFilterColumns = (fields: any[], options: any = {}) => {
|
||||||
// const { fieldNames = [] } = options;
|
// const { fieldNames = [] } = options;
|
||||||
// const properties = {};
|
// const properties = {};
|
||||||
// fields.forEach((field, index) => {
|
// fields.forEach((field, index) => {
|
||||||
// if (fieldNames?.length && !fieldNames.includes(field.name)) {
|
// if (fieldNames?.length && !fieldNames.includes(field.name)) {
|
||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
// const fieldOption = interfaces.get(field.interface);
|
// const fieldOption = interfaces.get(field.interface);
|
||||||
// if (!fieldOption?.operations) {
|
// if (!fieldOption?.operations) {
|
||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
// properties[`column${index}`] = {
|
// properties[`column${index}`] = {
|
||||||
// type: 'void',
|
// type: 'void',
|
||||||
// title: field?.uiSchema?.title,
|
// title: field?.uiSchema?.title,
|
||||||
// 'x-component': 'Filter.Column',
|
// 'x-component': 'Filter.Column',
|
||||||
// 'x-component-props': {
|
// 'x-component-props': {
|
||||||
// operations: fieldOption.operations,
|
// operations: fieldOption.operations,
|
||||||
|
// },
|
||||||
|
// properties: {
|
||||||
|
// [field.name]: {
|
||||||
|
// ...field.uiSchema,
|
||||||
|
// 'x-decorator': 'FormilyFormItem',
|
||||||
|
// title: null,
|
||||||
// },
|
// },
|
||||||
// properties: {
|
// },
|
||||||
// [field.name]: {
|
// };
|
||||||
// ...field.uiSchema,
|
// });
|
||||||
// 'x-decorator': 'FormilyFormItem',
|
// return properties;
|
||||||
// title: null,
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// };
|
|
||||||
// });
|
|
||||||
// return properties;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const toEvents = (data: any[], fieldNames: any) => {
|
export const toEvents = (data: any[], fieldNames: any) => {
|
||||||
|
@ -0,0 +1,86 @@
|
|||||||
|
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);
|
||||||
|
debugger;
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
});
|
@ -0,0 +1,61 @@
|
|||||||
|
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,14 +1,15 @@
|
|||||||
import { LoadingOutlined } from '@ant-design/icons';
|
import { LoadingOutlined } from '@ant-design/icons';
|
||||||
import { createForm, onFieldValueChange } from '@formily/core';
|
import { connect, mapProps, mapReadPretty } from '@formily/react';
|
||||||
import { connect, FieldContext, FormContext, ISchema, mapProps, mapReadPretty, Schema } from '@formily/react';
|
import React from 'react';
|
||||||
import deepmerge from 'deepmerge';
|
import { FilterAction } from './Filter.Action';
|
||||||
import React, { useMemo } from 'react';
|
import { FilterDynamicValue } from './Filter.DynamicValue';
|
||||||
import { SchemaComponent } from '../../core';
|
|
||||||
import { FilterGroup } from './FilterGroup';
|
import { FilterGroup } from './FilterGroup';
|
||||||
import './style.less';
|
import './style.less';
|
||||||
|
|
||||||
export const Filter: any = connect(
|
export const Filter: any = connect(
|
||||||
(props) => <FilterGroup bordered={false} {...props} />,
|
(props) => {
|
||||||
|
return <FilterGroup bordered={false} {...props} />;
|
||||||
|
},
|
||||||
mapProps((props, field) => {
|
mapProps((props, field) => {
|
||||||
return {
|
return {
|
||||||
...props,
|
...props,
|
||||||
@ -20,65 +21,7 @@ export const Filter: any = connect(
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
interface DynamicValueProps {
|
Filter.DynamicValue = FilterDynamicValue;
|
||||||
value?: any;
|
Filter.Action = FilterAction;
|
||||||
onChange?: any;
|
|
||||||
schema?: Schema;
|
|
||||||
operation?: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
Filter.DynamicValue = 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 (
|
|
||||||
<FieldContext.Provider value={null}>
|
|
||||||
<FormContext.Provider value={form}>
|
|
||||||
<SchemaComponent schema={schema} />
|
|
||||||
</FormContext.Provider>
|
|
||||||
</FieldContext.Provider>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default Filter;
|
export default Filter;
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
import { createContext } from 'react';
|
||||||
|
|
||||||
|
export const FilterContext = createContext<FilterContextProps>({});
|
||||||
|
|
||||||
|
export interface FilterContextProps {
|
||||||
|
visible?: boolean;
|
||||||
|
setVisible?: (v: boolean) => void;
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
import { useContext } from 'react';
|
||||||
|
import { FilterContext } from './context';
|
||||||
|
|
||||||
|
export const useFilterContext = () => {
|
||||||
|
const ctx = useContext(FilterContext);
|
||||||
|
return {
|
||||||
|
...ctx,
|
||||||
|
};
|
||||||
|
};
|
@ -65,6 +65,7 @@ export const IconPicker = connect(
|
|||||||
mapProps((props, field) => {
|
mapProps((props, field) => {
|
||||||
return {
|
return {
|
||||||
...props,
|
...props,
|
||||||
|
value: props.type,
|
||||||
suffix: <span>{field?.['loading'] || field?.['validating'] ? <LoadingOutlined /> : props.suffix}</span>,
|
suffix: <span>{field?.['loading'] || field?.['validating'] ? <LoadingOutlined /> : props.suffix}</span>,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
Loading…
Reference in New Issue
Block a user