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:
parent
920aab949a
commit
0853915f23
@ -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,
|
||||
|
@ -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",
|
||||
|
@ -63,6 +63,7 @@ export default {
|
||||
'Collapse all':'全部收起',
|
||||
'Expand all':'全部展开',
|
||||
'Expand/Collapse':'展开/折叠',
|
||||
'Default collapse':'默认展开',
|
||||
'Tree collection': '树结构表',
|
||||
"Tree table":"树表格",
|
||||
'Parent ID': '父记录ID',
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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>;
|
||||
};
|
@ -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')}
|
||||
|
@ -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' }}
|
||||
|
@ -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}
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user