feat: association-filter-improve (#1606)

* feat: snapshot-i18n fix

* feat: association-filter-improve

add data scope for association filter
add default sort for association filter

* feat(association-filter-improve): close association-filter filter fix

* feat(association-filter-improve): refresh on table refresh

* feat(association-filter-improve): refresh on table refresh

* feat(association-filter-improve): remove field filter fixed

* feat(association-filter-improve): filter fix

* fix: missing props

* refactor: re design

* fix: data scope not work

* feat: support sorting rule

* feat: support configure default collapse

* fix: multiple association item will not work

* fix: ts error

* fix: ts error

---------

Co-authored-by: anuoua <anuoua@gmail.com>
This commit is contained in:
Dunqing 2023-03-30 09:00:56 +08:00 committed by GitHub
parent 920aab949a
commit 0853915f23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 212 additions and 67 deletions

View File

@ -802,6 +802,7 @@ export const useAssociationFilterProps = () => {
const fieldSchema = useFieldSchema();
const valueKey = collectionField?.targetKey || 'id';
const labelKey = fieldSchema['x-component-props']?.fieldNames?.label || valueKey;
const field = useField()
const collectionFieldName = collectionField.name;
const { data, params, run } = useRequest(
{
@ -811,18 +812,19 @@ export const useAssociationFilterProps = () => {
fields: [labelKey, valueKey],
pageSize: 200,
page: 1,
...field.componentProps?.params
},
},
{
refreshDeps: [labelKey, valueKey],
refreshDeps: [labelKey, valueKey, JSON.stringify(field.componentProps?.params || {})],
debounceWait: 300,
},
);
const list = data?.data || [];
const onSelected = (value) => {
const filters = service.params?.[1]?.filters || {};
if (value.length) {
filters[`af.${collectionFieldName}`] = {
[`${collectionFieldName}.${valueKey}.$in`]: value,
@ -830,7 +832,6 @@ export const useAssociationFilterProps = () => {
} else {
delete filters[`af.${collectionFieldName}`];
}
service.run(
{
...service.params?.[0],
@ -969,6 +970,7 @@ export const useAssociationFilterBlockProps = () => {
});
};
return {
/** 渲染 Collapse 的列表数据 */
list,

View File

@ -115,6 +115,7 @@ export default {
'Collapse all':'Collapse all',
'Expand all':'Expand all',
'Expand/Collapse':'Expand/Collapse',
'Default collapse':'Default collapse',
"Tree table":"Tree table",
"Custom field display name": "Custom field display name",
"Display fields": "Display collection fields",

View File

@ -63,6 +63,7 @@ export default {
'Collapse all':'全部收起',
'Expand all':'全部展开',
'Expand/Collapse':'展开/折叠',
'Default collapse':'默认展开',
'Tree collection': '树结构表',
"Tree table":"树表格",
'Parent ID': '父记录ID',

View File

@ -4,6 +4,8 @@ import { useTranslation } from 'react-i18next';
import { useAPIClient } from '../../../api-client';
import { createDesignable, SchemaComponentContext, useDesignable } from '../..';
import { ActionInitializer } from '../../../schema-initializer/items/ActionInitializer';
import { useBlockRequestContext } from '../../../block-provider';
import { mergeFilter } from '../../../block-provider/SharedFilterProvider';
export const ActionBarAssociationFilterAction = (props) => {
const { refresh } = useContext(SchemaComponentContext);
@ -11,12 +13,22 @@ export const ActionBarAssociationFilterAction = (props) => {
const api = useAPIClient();
const { t } = useTranslation();
const dn = createDesignable({ t, api, refresh, current: fieldSchema });
const { service, props: blockProps } = useBlockRequestContext();
dn.loadAPIClientEvents();
const handleInsert = (s: Schema) => {
dn.insertBeforeBegin(s);
};
const handleRemove = (schema, remove) => {
remove(schema);
service.run({
...service.params?.[0],
filter: mergeFilter([blockProps?.params?.filter]),
});
};
const schema = {
type: 'void',
'x-action': 'associateFilter',
@ -28,6 +40,7 @@ export const ActionBarAssociationFilterAction = (props) => {
const newProps = {
...props,
insert: handleInsert,
remove: handleRemove,
wrap: (s) => s,
};

View File

@ -1,44 +0,0 @@
import { useFieldSchema } from '@formily/react';
import React, { createContext } from 'react';
import { useRequest } from '../../../api-client';
import { AssociationFilter } from './AssociationFilter';
export const useAssociationFieldService = (props) => {
const collectionField = AssociationFilter.useAssociationField();
const fieldSchema = useFieldSchema();
const valueKey = collectionField?.targetKey || 'id';
const labelKey = fieldSchema['x-component-props']?.fieldNames?.label || valueKey;
const service = useRequest(
{
resource: collectionField.target,
action: 'list',
params: {
fields: [labelKey, valueKey],
pageSize: 200,
page: 1,
...props.params,
},
},
{
refreshDeps: [labelKey, valueKey],
debounceWait: 300,
},
);
return service;
};
export type AssociationItemContextValue = {
service: ReturnType<typeof useRequest>;
};
export const AssociationItemContext = createContext<AssociationItemContextValue>({ service: undefined! });
export const AssociationItemDecorator: React.FC = (props) => {
const service = useAssociationFieldService(props);
return <AssociationItemContext.Provider value={{ service }}>{props.children}</AssociationItemContext.Provider>;
};

View File

@ -1,21 +1,33 @@
import { ISchema, useFieldSchema } from '@formily/react';
import { ISchema, useField, useFieldSchema } from '@formily/react';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useCollectionManager } from '../../../collection-manager';
import {
useCollection,
useCollectionFilterOptions,
useCollectionManager,
useSortFields,
} from '../../../collection-manager';
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
import { useCompile, useDesignable } from '../../hooks';
import { AssociationFilter } from './AssociationFilter';
import _ from 'lodash';
export const AssociationFilterItemDesigner = (props) => {
const fieldSchema = useFieldSchema();
const field = useField();
const { t } = useTranslation();
const collectionField = AssociationFilter.useAssociationField();
const { getCollectionJoinField } = useCollectionManager();
const { getField } = useCollection();
const collectionField = getField(fieldSchema['name']!) || getCollectionJoinField(fieldSchema['x-collection-field']);
const filterEnum = collectionField?.target ? useCollectionFilterOptions(collectionField?.target) : [];
const defaultFilter = fieldSchema?.['x-component-props']?.params?.filter || {};
const { getCollectionFields } = useCollectionManager();
const compile = useCompile();
const { dn } = useDesignable();
const targetFields = getCollectionFields(collectionField.target) ?? [];
const targetFields = collectionField?.target ? getCollectionFields(collectionField?.target) : [];
const options = targetFields
.filter(
@ -42,6 +54,20 @@ export const AssociationFilterItemDesigner = (props) => {
dn.refresh();
};
const sortFields = useSortFields(collectionField?.target);
const defaultSort = fieldSchema?.['x-component-props']?.params?.sort || [];
const sort = defaultSort?.map((item: string) => {
return item.startsWith('-')
? {
field: item.substring(1),
direction: 'desc',
}
: {
field: item,
direction: 'asc',
};
});
return (
<GeneralSchemaDesigner {...props} disableInitializer={true}>
<SchemaSettings.ModalItem
@ -75,6 +101,67 @@ export const AssociationFilterItemDesigner = (props) => {
dn.refresh();
}}
/>
<SchemaSettings.SwitchItem
title={t('Default collapse')}
checked={field.componentProps.defaultCollapse}
onChange={(v) => {
field.componentProps.defaultCollapse = v;
fieldSchema['x-component-props']['defaultCollapse'] = v;
dn.emit('patch', {
schema: {
['x-uid']: fieldSchema['x-uid'],
'x-component-props': fieldSchema['x-component-props'],
},
});
dn.refresh();
}}
/>
<SchemaSettings.ModalItem
title={t('Set the data scope')}
schema={
{
type: 'object',
title: t('Set the data scope'),
properties: {
filter: {
default: defaultFilter,
enum: filterEnum,
'x-component': 'Filter',
'x-component-props': {},
},
},
} as ISchema
}
onSubmit={({ filter }) => {
_.set(field.componentProps, 'params', {
...field.componentProps?.params,
filter,
});
fieldSchema['x-component-props']['params'] = field.componentProps.params;
dn.emit('patch', {
schema: {
['x-uid']: fieldSchema['x-uid'],
'x-component-props': fieldSchema['x-component-props'],
},
});
}}
/>
<SchemaSettings.DefaultSortingRules
sort={sort}
sortFields={sortFields}
onSubmit={({ sort }) => {
_.set(field.componentProps, 'params', {
...field.componentProps?.params,
sort: sort.map((item) => {
return item.direction === 'desc' ? `-${item.field}` : item.field;
}),
});
fieldSchema['x-component-props']['params'] = field.componentProps.params;
dn.emit('patch', {
schema: fieldSchema,
});
}}
/>
<SchemaSettings.SelectItem
key="title-field"
title={t('Title field')}

View File

@ -3,10 +3,9 @@ import { css } from '@emotion/css';
import { useFieldSchema } from '@formily/react';
import { Col, Collapse, Input, Row, Tree } from 'antd';
import cls from 'classnames';
import React, { ChangeEvent, MouseEvent, useState } from 'react';
import { useAssociationFilterProps } from '../../../block-provider/hooks';
import React, { ChangeEvent, MouseEvent, useMemo, useState } from 'react';
import { SortableItem } from '../../common';
import { useCompile, useDesigner } from '../../hooks';
import { useCompile, useDesigner, useProps } from '../../hooks';
import { AssociationFilter } from './AssociationFilter';
const { Panel } = Collapse;
@ -15,11 +14,9 @@ export const AssociationFilterItem = (props) => {
const collectionField = AssociationFilter.useAssociationField();
// 把一些可定制的状态通过 hook 提取出去了,为了兼容之前添加的 Table 区块,这里加了个默认值
const { useProps = useAssociationFilterProps } = props;
const fieldSchema = useFieldSchema();
const Designer = useDesigner();
const compile = useCompile();
const {
list,
onSelected,
@ -28,10 +25,12 @@ export const AssociationFilterItem = (props) => {
run,
valueKey: _valueKey,
labelKey: _labelKey,
} = useProps();
defaultCollapse,
} = useProps(props);
const [searchVisible, setSearchVisible] = useState(false);
const defaultActiveKeyCollapse = useMemo<React.Key[]>(() => (defaultCollapse ? [collectionField.name] : []), []);
const valueKey = _valueKey || collectionField?.targetKey || 'id';
const labelKey = _labelKey || fieldSchema['x-component-props']?.fieldNames?.label || valueKey;
@ -131,11 +130,7 @@ export const AssociationFilterItem = (props) => {
)}
>
<Designer />
<Collapse
defaultActiveKey={[collectionField.uiSchemaUid]}
ghost
expandIcon={searchVisible ? () => null : undefined}
>
<Collapse defaultActiveKey={defaultActiveKeyCollapse} ghost expandIcon={searchVisible ? () => null : undefined}>
<Panel
className={css`
& .ant-collapse-content-box {
@ -216,7 +211,7 @@ export const AssociationFilterItem = (props) => {
</Col>
</Row>
}
key={collectionField.uiSchemaUid}
key={defaultActiveKeyCollapse[0]}
>
<Tree
style={{ padding: '16px 0' }}

View File

@ -5,8 +5,13 @@ import { SchemaInitializer } from '..';
import { useCurrentSchema } from '../utils';
export const InitializerWithSwitch = (props) => {
const { type, schema, item, insert } = props;
const { exists, remove } = useCurrentSchema(schema?.[type] || item?.schema?.[type], type, item.find, item.remove);
const { type, schema, item, insert, remove: passInRemove } = props;
const { exists, remove } = useCurrentSchema(
schema?.[type] || item?.schema?.[type],
type,
item.find,
passInRemove ?? item.remove,
);
return (
<SchemaInitializer.SwitchItem
checked={exists}

View File

@ -1,5 +1,6 @@
import { css } from '@emotion/css';
import { FormDialog, FormItem, FormLayout, Input, ArrayCollapse, ArrayItems } from '@formily/antd';
import { ArrayItems } from '@formily/antd';
import { FormDialog, FormItem, FormLayout, Input, ArrayCollapse } from '@formily/antd';
import { createForm, Field, GeneralField } from '@formily/core';
import { ISchema, Schema, SchemaOptionsContext, useField, useFieldSchema, useForm } from '@formily/react';
import _ from 'lodash';
@ -853,6 +854,90 @@ SchemaSettings.BlockTitleItem = () => {
);
};
SchemaSettings.DefaultSortingRules = (props) => {
const { sort, sortFields, onSubmit } = props;
const { t } = useTranslation();
return (
<SchemaSettings.ModalItem
title={t('Set default sorting rules')}
components={{ ArrayItems }}
schema={
{
type: 'object',
title: t('Set default sorting rules'),
properties: {
sort: {
type: 'array',
default: sort,
'x-component': 'ArrayItems',
'x-decorator': 'FormItem',
items: {
type: 'object',
properties: {
space: {
type: 'void',
'x-component': 'Space',
properties: {
sort: {
type: 'void',
'x-decorator': 'FormItem',
'x-component': 'ArrayItems.SortHandle',
},
field: {
type: 'string',
enum: sortFields,
'x-decorator': 'FormItem',
'x-component': 'Select',
'x-component-props': {
style: {
width: 260,
},
},
},
direction: {
type: 'string',
'x-decorator': 'FormItem',
'x-component': 'Radio.Group',
'x-component-props': {
optionType: 'button',
},
enum: [
{
label: t('ASC'),
value: 'asc',
},
{
label: t('DESC'),
value: 'desc',
},
],
},
remove: {
type: 'void',
'x-decorator': 'FormItem',
'x-component': 'ArrayItems.Remove',
},
},
},
},
},
properties: {
add: {
type: 'void',
title: t('Add sort field'),
'x-component': 'ArrayItems.Addition',
},
},
},
},
} as ISchema
}
onSubmit={onSubmit}
/>
);
};
SchemaSettings.LinkageRules = (props) => {
const { collectionName } = props;
const fieldSchema = useFieldSchema();