feat: improve filter action initializer

This commit is contained in:
chenos 2022-03-01 22:17:58 +08:00
parent bc27359637
commit 1c6289dd88
8 changed files with 163 additions and 25 deletions

View File

@ -1,5 +1,5 @@
import { useForm } from '@formily/react'; import { useForm } from '@formily/react';
import { useCollectionManager } from '.'; import { useCollection, useCollectionManager } from '.';
import { useRequest } from '../api-client'; import { useRequest } from '../api-client';
import { useRecord } from '../record-provider'; import { useRecord } from '../record-provider';
import { useActionContext } from '../schema-component'; import { useActionContext } from '../schema-component';
@ -16,6 +16,58 @@ export const useCancelAction = () => {
}; };
}; };
export const useCancelFilterAction = () => {
const form = useForm();
const ctx = useActionContext();
return {
async run() {
ctx.setVisible(false);
},
};
};
export const useCollectionFilterOptions = (collectionName: string) => {
const { getCollection, getInterface } = useCollectionManager();
const options = [];
const collection = getCollection(collectionName);
const fields = collection?.fields || [];
const field2option = (field) => {
const fieldInterface = getInterface(field.interface);
const option = {
name: field.name,
title: field?.uiSchema?.title || field.name,
operators: fieldInterface.operators || [],
};
return option;
}
fields.forEach(field => {
options.push(field2option(field));
});
return options;
}
export const useFilterDataSource = (options) => {
const { name } = useCollection();
const data = useCollectionFilterOptions(name);
return useRequest(
() =>
Promise.resolve({
data,
}),
options,
);
}
export const useFilterAction = () => {
const form = useForm();
const ctx = useActionContext();
return {
async run() {
ctx.setVisible(false);
},
};
}
export const useCreateAction = () => { export const useCreateAction = () => {
const form = useForm(); const form = useForm();
const ctx = useActionContext(); const ctx = useActionContext();

View File

@ -41,7 +41,6 @@ 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];

View File

@ -1,5 +1,5 @@
import { ObjectField as ObjectFieldModel } from '@formily/core'; import { ObjectField as ObjectFieldModel } from '@formily/core';
import { observer, useField } from '@formily/react'; import { observer, useField, useFieldSchema } from '@formily/react';
import React from 'react'; import React from 'react';
import { useRequest } from '../../../api-client'; import { useRequest } from '../../../api-client';
import { FilterContext } from './context'; import { FilterContext } from './context';
@ -14,6 +14,7 @@ const useDef = (options) => {
export const Filter: any = observer((props: any) => { export const Filter: any = observer((props: any) => {
const { useDataSource = useDef, dynamicComponent } = props; const { useDataSource = useDef, dynamicComponent } = props;
const field = useField<ObjectFieldModel>(); const field = useField<ObjectFieldModel>();
const fieldSchema = useFieldSchema();
useDataSource({ useDataSource({
onSuccess(data) { onSuccess(data) {
console.log('onSuccess', data?.data); console.log('onSuccess', data?.data);
@ -22,7 +23,7 @@ export const Filter: any = observer((props: any) => {
}); });
return ( return (
<div> <div>
<FilterContext.Provider value={{ dynamicComponent, options: field.dataSource || [] }}> <FilterContext.Provider value={{ field, fieldSchema, dynamicComponent, options: field.dataSource || [] }}>
<FilterGroup {...props} /> <FilterGroup {...props} />
</FilterContext.Provider> </FilterContext.Provider>
<pre>{JSON.stringify(field.value, null, 2)}</pre> <pre>{JSON.stringify(field.value, null, 2)}</pre>

View File

@ -2,6 +2,7 @@ import { CloseCircleOutlined } from '@ant-design/icons';
import { observer, useField } from '@formily/react'; import { observer, useField } from '@formily/react';
import { Cascader, Select, Space } from 'antd'; import { Cascader, Select, Space } from 'antd';
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { useCompile } from '../..';
import { RemoveConditionContext } from './context'; import { RemoveConditionContext } from './context';
import { DynamicComponent } from './DynamicComponent'; import { DynamicComponent } from './DynamicComponent';
import { useValues } from './useValues'; import { useValues } from './useValues';
@ -10,6 +11,7 @@ export const FilterItem = observer((props: any) => {
const field = useField<any>(); const field = useField<any>();
const remove = useContext(RemoveConditionContext); const remove = useContext(RemoveConditionContext);
const { option, options, dataIndex, operator, setDataIndex, setOperator, value, setValue } = useValues(); const { option, options, dataIndex, operator, setDataIndex, setOperator, value, setValue } = useValues();
const compile = useCompile();
return ( return (
<div style={{ marginBottom: 8 }}> <div style={{ marginBottom: 8 }}>
<Space> <Space>
@ -25,14 +27,14 @@ export const FilterItem = observer((props: any) => {
changeOnSelect={false} changeOnSelect={false}
key={field.address.toString()} key={field.address.toString()}
value={dataIndex} value={dataIndex}
options={options} options={compile(options)}
onChange={(value) => { onChange={(value) => {
setDataIndex(value); setDataIndex(value);
}} }}
/> />
<Select <Select
value={operator} value={operator}
options={option?.operators} options={compile(option?.operators)}
onChange={(value) => { onChange={(value) => {
setOperator(value); setOperator(value);
}} }}

View File

@ -1,10 +1,15 @@
import { css } from '@emotion/css'; import { css } from '@emotion/css';
import { useFieldSchema, useForm } from '@formily/react';
import { Button } from 'antd'; import { Button } from 'antd';
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useDesignable } from '../../hooks';
export const SaveDefaultValue = (props) => { export const SaveDefaultValue = (props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { dn, refresh } = useDesignable();
const fieldSchema = useFieldSchema();
const form = useForm();
return ( return (
<Button <Button
className={css` className={css`
@ -12,6 +17,22 @@ export const SaveDefaultValue = (props) => {
color: rgb(241, 139, 98); color: rgb(241, 139, 98);
`} `}
type={'dashed'} type={'dashed'}
onClick={() => {
const filterSchema = fieldSchema?.parent?.parent?.parent?.properties?.filter;
if (!filterSchema) {
return;
}
const defaultValue = form.values.filter;
dn.emit('patch', {
schema: {
'x-uid': filterSchema['x-uid'],
default: defaultValue,
},
});
dn.refresh();
filterSchema.default = defaultValue;
console.log('filterSchema', defaultValue);
}}
> >
{t('Save conditions')} {t('Save conditions')}
</Button> </Button>

View File

@ -1,4 +1,13 @@
import { ObjectField } from '@formily/core';
import { Schema } from '@formily/react';
import { createContext } from 'react'; import { createContext } from 'react';
export interface FilterContextProps {
field?: ObjectField;
fieldSchema?: Schema;
dynamicComponent?: any;
options?: any[];
}
export const RemoveConditionContext = createContext(null); export const RemoveConditionContext = createContext(null);
export const FilterContext = createContext(null); export const FilterContext = createContext<FilterContextProps>(null);

View File

@ -19,10 +19,10 @@ export const useValues = () => {
} }
const values = flat(field.value || {}, { maxDepth }); const values = flat(field.value || {}, { maxDepth });
const value = Object.values<any>(values).shift(); const value = Object.values<any>(values).shift();
const findOption = (dataIndex) => { const findOption = (dataIndex = []) => {
let items = options; let items = options;
let option; let option;
dataIndex.forEach((name, index) => { dataIndex?.forEach?.((name, index) => {
const item = items.find((item) => item.name === name); const item = items.find((item) => item.name === name);
if (item) { if (item) {
option = item; option = item;
@ -44,6 +44,10 @@ export const useValues = () => {
component: [Input, {}], component: [Input, {}],
// 当 dataIndex 变化value 清空 // 当 dataIndex 变化value 清空
setDataIndex(di: string[]) { setDataIndex(di: string[]) {
if (!di) {
field.value = {};
return;
}
const option = findOption(di); const option = findOption(di);
const op = option?.operators?.[0]?.value || '$eq'; const op = option?.operators?.[0]?.value || '$eq';
field.value = flat.unflatten({ field.value = flat.unflatten({

View File

@ -1,9 +1,73 @@
import { Switch } from 'antd'; import { Switch } from 'antd';
import flat from 'flat';
import React from 'react'; import React from 'react';
import { SchemaInitializer } from '../../SchemaInitializer'; import { SchemaInitializer } from '../../SchemaInitializer';
import { useCurrentSchema } from '../utils'; import { useCurrentSchema } from '../utils';
const schema = {
'x-component': 'Action',
'x-component-props': {
popover: true,
},
type: 'void',
title: '{{t("Filter")}}',
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: {},
'x-component': 'Filter',
'x-component-props': {
useDataSource: '{{cm.useFilterDataSource}}',
},
},
footer: {
type: 'void',
'x-component': 'Action.Popover.Footer',
properties: {
actions: {
type: 'void',
'x-component': 'ActionBar',
properties: {
saveDefault: {
type: 'void',
'x-component': 'Filter.SaveDefaultValue',
'x-component-props': {},
},
cancel: {
type: 'void',
title: '{{t("Cancel")}}',
'x-component': 'Action',
'x-component-props': {
useAction: '{{cm.useCancelFilterAction}}',
},
},
submit: {
type: 'void',
title: '{{t("Submit")}}',
'x-component': 'Action',
'x-component-props': {
type: 'primary',
useAction: '{{cm.useFilterAction}}',
},
},
},
},
},
},
},
},
},
};
export const FilterActionInitializer = (props) => { export const FilterActionInitializer = (props) => {
const { item, insert } = props; const { item, insert } = props;
const { exists, remove } = useCurrentSchema(item.schema['x-action'], 'x-action', item.find); const { exists, remove } = useCurrentSchema(item.schema['x-action'], 'x-action', item.find);
@ -15,22 +79,8 @@ export const FilterActionInitializer = (props) => {
return remove(); return remove();
} }
insert({ insert({
...schema,
...item.schema, ...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': {},
}); });
}} }}
> >