feat: filter component finish

This commit is contained in:
Semmy 2022-02-27 01:49:56 +08:00
parent 6f2069e918
commit 968a7ec80f
10 changed files with 240 additions and 149 deletions

View File

@ -16,6 +16,7 @@ export const useCollectionField = () => {
}
const resourceName = `${ctx?.collectionName || collection?.name}.${ctx.name}`;
const resource = api?.resource(resourceName, record[ctx.sourceKey]);
debugger;
return {
...ctx,
uiSchema: compile(ctx.uiSchema),

View File

@ -1,14 +1,12 @@
import { FormButtonGroup, Submit } from '@formily/antd';
import { createForm } from '@formily/core';
import { FormProvider, observer, useFieldSchema } from '@formily/react';
import { Button, Popover } from 'antd';
import { observer, useFieldSchema } from '@formily/react';
import flatten from 'flat';
import React, { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { SchemaComponent } from '../../core';
import { useDesignable } from '../../hooks';
import { useCompile } from '../../hooks/useCompile';
import { IconPicker } from '../icon-picker';
import { FilterContext } from '../filter/context';
export const Filter = observer((props: any) => {
const { t } = useTranslation();
@ -25,45 +23,19 @@ export const Filter = observer((props: any) => {
properties[p.name] = p;
});
return (
<Popover
trigger={['click']}
placement={'bottomLeft'}
visible={visible}
onVisibleChange={setVisible}
content={
<div>
<FormProvider form={form}>
<FilterContext.Provider value={{ visible, setVisible }}>
<SchemaComponent
schema={{
type: 'object',
type: 'void',
properties: {
filter: {
type: 'object',
'x-component': 'Filter',
properties,
title: t('Filter'),
'x-component': 'Filter.Action',
},
},
}}
/>
<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>
</FilterContext.Provider>
);
});

View File

@ -7,6 +7,7 @@ import { uid } from '@formily/shared';
import {
AntdSchemaComponentProvider,
CollectionField,
CollectionManagerProvider,
CollectionProvider,
SchemaComponent,
SchemaComponentProvider,
@ -56,6 +57,13 @@ const schema = {
'x-action': 'calendar:viewSelect',
'x-align': 'right',
},
filter: {
type: 'void',
title: '过滤',
'x-align': 'right',
'x-component': 'Calendar.Filter',
'x-action': 'calendar:filter',
},
},
},
event: {
@ -98,9 +106,9 @@ const collection = {
type: 'string',
name: 'id',
interface: 'input',
title: 'id',
uiSchema: {
type: 'string',
title: 'id',
'x-component': 'Input',
},
},
@ -108,9 +116,9 @@ const collection = {
type: 'string',
name: 'title',
interface: 'input',
title: '标题',
uiSchema: {
type: 'string',
title: '标题',
'x-component': 'Input',
},
},
@ -118,9 +126,9 @@ const collection = {
type: 'string',
name: 'start',
interface: 'datetime',
title: '开始时间',
uiSchema: {
type: 'string',
title: '开始时间',
'x-component': 'DatePicker',
'x-component-props': {
dateFormat: 'YYYY-MM-DD',
@ -131,9 +139,9 @@ const collection = {
type: 'string',
name: 'end',
interface: 'datetime',
title: '结束时间',
uiSchema: {
type: 'string',
title: '结束时间',
'x-component': 'DatePicker',
'x-component-props': {
dateFormat: 'YYYY-MM-DD',
@ -144,6 +152,7 @@ const collection = {
};
export default () => {
return (
<CollectionManagerProvider>
<CollectionProvider collection={collection}>
<SchemaComponentProvider designable={true} components={{ CollectionField }}>
<SchemaInitializerProvider>
@ -153,5 +162,6 @@ export default () => {
</SchemaInitializerProvider>
</SchemaComponentProvider>
</CollectionProvider>
</CollectionManagerProvider>
);
};

View File

@ -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>
);
});

View File

@ -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} />;
});

View File

@ -1,14 +1,15 @@
import { LoadingOutlined } from '@ant-design/icons';
import { createForm, onFieldValueChange } from '@formily/core';
import { connect, FieldContext, FormContext, ISchema, mapProps, mapReadPretty, Schema } from '@formily/react';
import deepmerge from 'deepmerge';
import React, { useMemo } from 'react';
import { SchemaComponent } from '../../core';
import { connect, mapProps, mapReadPretty } from '@formily/react';
import React from 'react';
import { FilterAction } from './Filter.Action';
import { FilterDynamicValue } from './Filter.DynamicValue';
import { FilterGroup } from './FilterGroup';
import './style.less';
export const Filter: any = connect(
(props) => <FilterGroup bordered={false} {...props} />,
(props) => {
return <FilterGroup bordered={false} {...props} />;
},
mapProps((props, field) => {
return {
...props,
@ -20,65 +21,7 @@ export const Filter: any = connect(
}),
);
interface DynamicValueProps {
value?: any;
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>
);
});
Filter.DynamicValue = FilterDynamicValue;
Filter.Action = FilterAction;
export default Filter;

View File

@ -0,0 +1,8 @@
import { createContext } from 'react';
export const FilterContext = createContext<FilterContextProps>({});
export interface FilterContextProps {
visible?: boolean;
setVisible?: (v: boolean) => void;
}

View File

@ -0,0 +1,9 @@
import { useContext } from 'react';
import { FilterContext } from './context';
export const useFilterContext = () => {
const ctx = useContext(FilterContext);
return {
...ctx,
};
};

View File

@ -65,6 +65,7 @@ export const IconPicker = connect(
mapProps((props, field) => {
return {
...props,
value: props.type,
suffix: <span>{field?.['loading'] || field?.['validating'] ? <LoadingOutlined /> : props.suffix}</span>,
};
}),