mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 21:49:25 +08:00
refactor: optimize filter component in filter form to match filterable settings (#6110)
* fix: display checkbox field as select in filter form * fix: bug * chore: filterCollectionffield * refactor: code improve * fix: bug * fix: bug * fix: isTruly and isFalsy * fix: bug * fix: bug * fix: bug * fix: bug --------- Co-authored-by: chenos <chenlinxh@gmail.com>
This commit is contained in:
parent
7c0d424173
commit
a00dcb78cb
@ -10,10 +10,11 @@
|
|||||||
import { useFieldSchema } from '@formily/react';
|
import { useFieldSchema } from '@formily/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { withDynamicSchemaProps } from '../hoc/withDynamicSchemaProps';
|
import { withDynamicSchemaProps } from '../hoc/withDynamicSchemaProps';
|
||||||
import { DatePickerProvider, ActionBarProvider } from '../schema-component';
|
import { DatePickerProvider, ActionBarProvider, SchemaComponentOptions } from '../schema-component';
|
||||||
import { DefaultValueProvider } from '../schema-settings';
|
import { DefaultValueProvider } from '../schema-settings';
|
||||||
import { CollectOperators } from './CollectOperators';
|
import { CollectOperators } from './CollectOperators';
|
||||||
import { FormBlockProvider } from './FormBlockProvider';
|
import { FormBlockProvider } from './FormBlockProvider';
|
||||||
|
import { FilterCollectionField } from '../modules/blocks/filter-blocks/FilterCollectionField';
|
||||||
|
|
||||||
export const FilterFormBlockProvider = withDynamicSchemaProps((props) => {
|
export const FilterFormBlockProvider = withDynamicSchemaProps((props) => {
|
||||||
const filedSchema = useFieldSchema();
|
const filedSchema = useFieldSchema();
|
||||||
@ -21,22 +22,24 @@ export const FilterFormBlockProvider = withDynamicSchemaProps((props) => {
|
|||||||
const deprecatedOperators = filedSchema['x-filter-operators'] || {};
|
const deprecatedOperators = filedSchema['x-filter-operators'] || {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CollectOperators defaultOperators={deprecatedOperators}>
|
<SchemaComponentOptions components={{ CollectionField: FilterCollectionField }}>
|
||||||
<DatePickerProvider value={{ utc: false }}>
|
<CollectOperators defaultOperators={deprecatedOperators}>
|
||||||
<ActionBarProvider
|
<DatePickerProvider value={{ utc: false }}>
|
||||||
forceProps={{
|
<ActionBarProvider
|
||||||
style: {
|
forceProps={{
|
||||||
overflowX: 'auto',
|
style: {
|
||||||
maxWidth: '100%',
|
overflowX: 'auto',
|
||||||
float: 'right',
|
maxWidth: '100%',
|
||||||
},
|
float: 'right',
|
||||||
}}
|
},
|
||||||
>
|
}}
|
||||||
<DefaultValueProvider isAllowToSetDefaultValue={() => false}>
|
>
|
||||||
<FormBlockProvider name="filter-form" {...props}></FormBlockProvider>
|
<DefaultValueProvider isAllowToSetDefaultValue={() => false}>
|
||||||
</DefaultValueProvider>
|
<FormBlockProvider name="filter-form" {...props}></FormBlockProvider>
|
||||||
</ActionBarProvider>
|
</DefaultValueProvider>
|
||||||
</DatePickerProvider>
|
</ActionBarProvider>
|
||||||
</CollectOperators>
|
</DatePickerProvider>
|
||||||
|
</CollectOperators>
|
||||||
|
</SchemaComponentOptions>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -164,8 +164,49 @@ export const time = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const boolean = [
|
export const boolean = [
|
||||||
{ label: '{{t("Yes")}}', value: '$isTruly', selected: true, noValue: true },
|
{
|
||||||
{ label: '{{t("No")}}', value: '$isFalsy', noValue: true },
|
label: '{{t("Yes")}}',
|
||||||
|
value: '$isTruly',
|
||||||
|
selected: true,
|
||||||
|
noValue: true,
|
||||||
|
schema: {
|
||||||
|
'x-component': 'Select',
|
||||||
|
'x-component-props': {
|
||||||
|
multiple: false,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: '{{t("Yes")}}',
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '{{t("No")}}',
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '{{t("No")}}',
|
||||||
|
value: '$isFalsy',
|
||||||
|
noValue: true,
|
||||||
|
schema: {
|
||||||
|
'x-component': 'Select',
|
||||||
|
'x-component-props': {
|
||||||
|
multiple: false,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: '{{t("Yes")}}',
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '{{t("No")}}',
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const tableoid = [
|
export const tableoid = [
|
||||||
|
@ -0,0 +1,119 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the NocoBase (R) project.
|
||||||
|
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||||
|
* Authors: NocoBase Team.
|
||||||
|
*
|
||||||
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||||
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Field } from '@formily/core';
|
||||||
|
import { connect, Schema, useField, useFieldSchema } from '@formily/react';
|
||||||
|
import { untracked } from '@formily/reactive';
|
||||||
|
import { merge } from '@formily/shared';
|
||||||
|
import { concat } from 'lodash';
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { ErrorBoundary } from 'react-error-boundary';
|
||||||
|
import { useFormBlockContext } from '../../../block-provider/FormBlockProvider';
|
||||||
|
import { useDynamicComponentProps } from '../../../hoc/withDynamicSchemaProps';
|
||||||
|
import { ErrorFallback, useCompile, useComponent } from '../../../schema-component';
|
||||||
|
import { useIsAllowToSetDefaultValue } from '../../../schema-settings/hooks/useIsAllowToSetDefaultValue';
|
||||||
|
import { useCollectionManager_deprecated } from '../../../';
|
||||||
|
import {
|
||||||
|
CollectionFieldProvider,
|
||||||
|
useCollectionField,
|
||||||
|
} from '../../../data-source/collection-field/CollectionFieldProvider';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
component: any;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
const setFieldProps = (field: Field, key: string, value: any) => {
|
||||||
|
untracked(() => {
|
||||||
|
if (field[key] === undefined) {
|
||||||
|
field[key] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const setRequired = (field: Field, fieldSchema: Schema, uiSchema: Schema) => {
|
||||||
|
if (typeof fieldSchema['required'] === 'undefined') {
|
||||||
|
field.required = !!uiSchema['required'];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: 初步适配
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export const FilterCollectionFieldInternalField: React.FC = (props: Props) => {
|
||||||
|
const compile = useCompile();
|
||||||
|
const field = useField<Field>();
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const { getInterface } = useCollectionManager_deprecated();
|
||||||
|
const { uiSchema: uiSchemaOrigin, defaultValue, interface: collectionInterface } = useCollectionField();
|
||||||
|
const { isAllowToSetDefaultValue } = useIsAllowToSetDefaultValue();
|
||||||
|
const targetInterface = getInterface(collectionInterface);
|
||||||
|
const operator = targetInterface?.filterable?.operators?.find(
|
||||||
|
(v, index) => v.value === fieldSchema['x-filter-operator'] || index === 0,
|
||||||
|
);
|
||||||
|
const Component = useComponent(
|
||||||
|
operator?.schema?.['x-component'] ||
|
||||||
|
fieldSchema['x-component-props']?.['component'] ||
|
||||||
|
uiSchemaOrigin?.['x-component'] ||
|
||||||
|
'Input',
|
||||||
|
);
|
||||||
|
const ctx = useFormBlockContext();
|
||||||
|
const dynamicProps = useDynamicComponentProps(uiSchemaOrigin?.['x-use-component-props'], props);
|
||||||
|
// TODO: 初步适配
|
||||||
|
useEffect(() => {
|
||||||
|
if (!uiSchemaOrigin) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const uiSchema = compile(uiSchemaOrigin);
|
||||||
|
setFieldProps(field, 'content', uiSchema['x-content']);
|
||||||
|
setFieldProps(field, 'title', uiSchema.title);
|
||||||
|
setFieldProps(field, 'description', uiSchema.description);
|
||||||
|
if (ctx?.form) {
|
||||||
|
const defaultVal = isAllowToSetDefaultValue() ? fieldSchema.default || defaultValue : undefined;
|
||||||
|
defaultVal !== null && defaultVal !== undefined && setFieldProps(field, 'initialValue', defaultVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!field.validator && (uiSchema['x-validator'] || fieldSchema['x-validator'])) {
|
||||||
|
const concatSchema = concat([], uiSchema['x-validator'] || [], fieldSchema['x-validator'] || []);
|
||||||
|
field.validator = concatSchema;
|
||||||
|
}
|
||||||
|
if (fieldSchema['x-disabled'] === true) {
|
||||||
|
field.disabled = true;
|
||||||
|
}
|
||||||
|
if (fieldSchema['x-read-pretty'] === true) {
|
||||||
|
field.readPretty = true;
|
||||||
|
}
|
||||||
|
setRequired(field, fieldSchema, uiSchema);
|
||||||
|
// @ts-ignore
|
||||||
|
field.dataSource = uiSchema.enum;
|
||||||
|
const originalProps =
|
||||||
|
compile({ ...(operator?.schema?.['x-component-props'] || {}), ...(uiSchema['x-component-props'] || {}) }) || {};
|
||||||
|
|
||||||
|
field.componentProps = merge(originalProps, field.componentProps || {}, dynamicProps || {});
|
||||||
|
}, [uiSchemaOrigin]);
|
||||||
|
|
||||||
|
if (!uiSchemaOrigin) return null;
|
||||||
|
|
||||||
|
return <Component {...props} {...dynamicProps} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FilterCollectionField = connect((props) => {
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const field = useField<Field>();
|
||||||
|
return (
|
||||||
|
<ErrorBoundary FallbackComponent={ErrorFallback.Modal} onError={(err) => console.log(err)}>
|
||||||
|
<CollectionFieldProvider name={fieldSchema.name}>
|
||||||
|
<FilterCollectionFieldInternalField {...props} />
|
||||||
|
</CollectionFieldProvider>
|
||||||
|
</ErrorBoundary>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
FilterCollectionField.displayName = 'FilterCollectionField';
|
@ -421,7 +421,13 @@ export const filterSelectComponentFieldSettings = new SchemaSettings({
|
|||||||
return isSelectFieldMode && !isFieldReadPretty;
|
return isSelectFieldMode && !isFieldReadPretty;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
getAllowMultiple({ title: 'Allow multiple selection' }),
|
{
|
||||||
|
...getAllowMultiple({ title: 'Allow multiple selection' }),
|
||||||
|
useVisible() {
|
||||||
|
const field = useField();
|
||||||
|
return field.componentProps.multiple !== false;
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
...titleField,
|
...titleField,
|
||||||
useVisible: useIsAssociationField,
|
useVisible: useIsAssociationField,
|
||||||
|
@ -17,6 +17,7 @@ import React from 'react';
|
|||||||
import { ReadPretty } from './ReadPretty';
|
import { ReadPretty } from './ReadPretty';
|
||||||
import { FieldNames, defaultFieldNames, getCurrentOptions } from './utils';
|
import { FieldNames, defaultFieldNames, getCurrentOptions } from './utils';
|
||||||
import { BaseOptionType, DefaultOptionType } from 'antd/es/select';
|
import { BaseOptionType, DefaultOptionType } from 'antd/es/select';
|
||||||
|
import { useCompile } from '../../';
|
||||||
|
|
||||||
export type SelectProps<
|
export type SelectProps<
|
||||||
ValueType = any,
|
ValueType = any,
|
||||||
@ -120,6 +121,7 @@ const filterOption = (input, option) => (option?.label ?? '').toLowerCase().incl
|
|||||||
const InternalSelect = connect(
|
const InternalSelect = connect(
|
||||||
(props: SelectProps) => {
|
(props: SelectProps) => {
|
||||||
const { objectValue, loading, value, rawOptions, defaultValue, ...others } = props;
|
const { objectValue, loading, value, rawOptions, defaultValue, ...others } = props;
|
||||||
|
const compile = useCompile();
|
||||||
let mode: any = props.multiple ? 'multiple' : props.mode;
|
let mode: any = props.multiple ? 'multiple' : props.mode;
|
||||||
if (mode && !['multiple', 'tags'].includes(mode)) {
|
if (mode && !['multiple', 'tags'].includes(mode)) {
|
||||||
mode = undefined;
|
mode = undefined;
|
||||||
@ -172,7 +174,7 @@ const InternalSelect = connect(
|
|||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
{...others}
|
{...compile(others)}
|
||||||
onChange={(changed) => {
|
onChange={(changed) => {
|
||||||
props.onChange?.(changed === undefined ? null : changed);
|
props.onChange?.(changed === undefined ? null : changed);
|
||||||
}}
|
}}
|
||||||
|
@ -10,7 +10,26 @@
|
|||||||
import { Op } from 'sequelize';
|
import { Op } from 'sequelize';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
$isFalsy() {
|
$isFalsy(value) {
|
||||||
|
if (value === true || value === 'true') {
|
||||||
|
return {
|
||||||
|
[Op.or]: {
|
||||||
|
[Op.is]: null,
|
||||||
|
[Op.eq]: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
[Op.eq]: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
$isTruly(value) {
|
||||||
|
if (value === true || value === 'true') {
|
||||||
|
return {
|
||||||
|
[Op.eq]: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
[Op.or]: {
|
[Op.or]: {
|
||||||
[Op.is]: null,
|
[Op.is]: null,
|
||||||
@ -18,10 +37,4 @@ export default {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
$isTruly() {
|
|
||||||
return {
|
|
||||||
[Op.eq]: true,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
} as Record<string, any>;
|
} as Record<string, any>;
|
||||||
|
@ -12515,7 +12515,7 @@ export const oneFilterFormBlockWithAllAssociationFieldsV1333Beta: PageConfig = {
|
|||||||
'x-use-decorator-props': 'useFormItemProps',
|
'x-use-decorator-props': 'useFormItemProps',
|
||||||
'x-collection-field': 'general.oneToOneBelongsTo',
|
'x-collection-field': 'general.oneToOneBelongsTo',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
multiple: false,
|
multiple: true,
|
||||||
fieldNames: {
|
fieldNames: {
|
||||||
label: 'id',
|
label: 'id',
|
||||||
value: 'id',
|
value: 'id',
|
||||||
@ -12562,7 +12562,7 @@ export const oneFilterFormBlockWithAllAssociationFieldsV1333Beta: PageConfig = {
|
|||||||
'x-use-decorator-props': 'useFormItemProps',
|
'x-use-decorator-props': 'useFormItemProps',
|
||||||
'x-collection-field': 'general.oneToOneHasOne',
|
'x-collection-field': 'general.oneToOneHasOne',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
multiple: false,
|
multiple: true,
|
||||||
fieldNames: {
|
fieldNames: {
|
||||||
label: 'id',
|
label: 'id',
|
||||||
value: 'id',
|
value: 'id',
|
||||||
@ -12656,7 +12656,7 @@ export const oneFilterFormBlockWithAllAssociationFieldsV1333Beta: PageConfig = {
|
|||||||
'x-use-decorator-props': 'useFormItemProps',
|
'x-use-decorator-props': 'useFormItemProps',
|
||||||
'x-collection-field': 'general.manyToOne',
|
'x-collection-field': 'general.manyToOne',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
multiple: false,
|
multiple: true,
|
||||||
fieldNames: {
|
fieldNames: {
|
||||||
label: 'id',
|
label: 'id',
|
||||||
value: 'id',
|
value: 'id',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user