diff --git a/packages/core/client/src/block-provider/FilterFormBlockProvider.tsx b/packages/core/client/src/block-provider/FilterFormBlockProvider.tsx
index 99d983810c..f6eacc4ede 100644
--- a/packages/core/client/src/block-provider/FilterFormBlockProvider.tsx
+++ b/packages/core/client/src/block-provider/FilterFormBlockProvider.tsx
@@ -10,10 +10,11 @@
import { useFieldSchema } from '@formily/react';
import React from 'react';
import { withDynamicSchemaProps } from '../hoc/withDynamicSchemaProps';
-import { DatePickerProvider, ActionBarProvider } from '../schema-component';
+import { DatePickerProvider, ActionBarProvider, SchemaComponentOptions } from '../schema-component';
import { DefaultValueProvider } from '../schema-settings';
import { CollectOperators } from './CollectOperators';
import { FormBlockProvider } from './FormBlockProvider';
+import { FilterCollectionField } from '../modules/blocks/filter-blocks/FilterCollectionField';
export const FilterFormBlockProvider = withDynamicSchemaProps((props) => {
const filedSchema = useFieldSchema();
@@ -21,22 +22,24 @@ export const FilterFormBlockProvider = withDynamicSchemaProps((props) => {
const deprecatedOperators = filedSchema['x-filter-operators'] || {};
return (
-
-
-
- false}>
-
-
-
-
-
+
+
+
+
+ false}>
+
+
+
+
+
+
);
});
diff --git a/packages/core/client/src/collection-manager/interfaces/properties/operators.ts b/packages/core/client/src/collection-manager/interfaces/properties/operators.ts
index a79128316b..ce0b6d411f 100644
--- a/packages/core/client/src/collection-manager/interfaces/properties/operators.ts
+++ b/packages/core/client/src/collection-manager/interfaces/properties/operators.ts
@@ -164,8 +164,49 @@ export const time = [
];
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 = [
diff --git a/packages/core/client/src/modules/blocks/filter-blocks/FilterCollectionField.tsx b/packages/core/client/src/modules/blocks/filter-blocks/FilterCollectionField.tsx
new file mode 100644
index 0000000000..28f47ad979
--- /dev/null
+++ b/packages/core/client/src/modules/blocks/filter-blocks/FilterCollectionField.tsx
@@ -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();
+ 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 ;
+};
+
+export const FilterCollectionField = connect((props) => {
+ const fieldSchema = useFieldSchema();
+ const field = useField();
+ return (
+ console.log(err)}>
+
+
+
+
+ );
+});
+
+FilterCollectionField.displayName = 'FilterCollectionField';
diff --git a/packages/core/client/src/modules/fields/component/Select/selectComponentFieldSettings.tsx b/packages/core/client/src/modules/fields/component/Select/selectComponentFieldSettings.tsx
index 41357ea3c4..8565ef3e6b 100644
--- a/packages/core/client/src/modules/fields/component/Select/selectComponentFieldSettings.tsx
+++ b/packages/core/client/src/modules/fields/component/Select/selectComponentFieldSettings.tsx
@@ -421,7 +421,13 @@ export const filterSelectComponentFieldSettings = new SchemaSettings({
return isSelectFieldMode && !isFieldReadPretty;
},
},
- getAllowMultiple({ title: 'Allow multiple selection' }),
+ {
+ ...getAllowMultiple({ title: 'Allow multiple selection' }),
+ useVisible() {
+ const field = useField();
+ return field.componentProps.multiple !== false;
+ },
+ },
{
...titleField,
useVisible: useIsAssociationField,
diff --git a/packages/core/client/src/schema-component/antd/select/Select.tsx b/packages/core/client/src/schema-component/antd/select/Select.tsx
index 79292ce358..269beecddd 100644
--- a/packages/core/client/src/schema-component/antd/select/Select.tsx
+++ b/packages/core/client/src/schema-component/antd/select/Select.tsx
@@ -17,6 +17,7 @@ import React from 'react';
import { ReadPretty } from './ReadPretty';
import { FieldNames, defaultFieldNames, getCurrentOptions } from './utils';
import { BaseOptionType, DefaultOptionType } from 'antd/es/select';
+import { useCompile } from '../../';
export type SelectProps<
ValueType = any,
@@ -120,6 +121,7 @@ const filterOption = (input, option) => (option?.label ?? '').toLowerCase().incl
const InternalSelect = connect(
(props: SelectProps) => {
const { objectValue, loading, value, rawOptions, defaultValue, ...others } = props;
+ const compile = useCompile();
let mode: any = props.multiple ? 'multiple' : props.mode;
if (mode && !['multiple', 'tags'].includes(mode)) {
mode = undefined;
@@ -172,7 +174,7 @@ const InternalSelect = connect(
);
}}
- {...others}
+ {...compile(others)}
onChange={(changed) => {
props.onChange?.(changed === undefined ? null : changed);
}}
diff --git a/packages/core/database/src/operators/boolean.ts b/packages/core/database/src/operators/boolean.ts
index 805ecf918d..b0130e5120 100644
--- a/packages/core/database/src/operators/boolean.ts
+++ b/packages/core/database/src/operators/boolean.ts
@@ -10,7 +10,26 @@
import { Op } from 'sequelize';
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 {
[Op.or]: {
[Op.is]: null,
@@ -18,10 +37,4 @@ export default {
},
};
},
-
- $isTruly() {
- return {
- [Op.eq]: true,
- };
- },
} as Record;
diff --git a/packages/core/test/src/e2e/templatesOfPage.ts b/packages/core/test/src/e2e/templatesOfPage.ts
index 1831801496..9bcd9c1026 100644
--- a/packages/core/test/src/e2e/templatesOfPage.ts
+++ b/packages/core/test/src/e2e/templatesOfPage.ts
@@ -12515,7 +12515,7 @@ export const oneFilterFormBlockWithAllAssociationFieldsV1333Beta: PageConfig = {
'x-use-decorator-props': 'useFormItemProps',
'x-collection-field': 'general.oneToOneBelongsTo',
'x-component-props': {
- multiple: false,
+ multiple: true,
fieldNames: {
label: 'id',
value: 'id',
@@ -12562,7 +12562,7 @@ export const oneFilterFormBlockWithAllAssociationFieldsV1333Beta: PageConfig = {
'x-use-decorator-props': 'useFormItemProps',
'x-collection-field': 'general.oneToOneHasOne',
'x-component-props': {
- multiple: false,
+ multiple: true,
fieldNames: {
label: 'id',
value: 'id',
@@ -12656,7 +12656,7 @@ export const oneFilterFormBlockWithAllAssociationFieldsV1333Beta: PageConfig = {
'x-use-decorator-props': 'useFormItemProps',
'x-collection-field': 'general.manyToOne',
'x-component-props': {
- multiple: false,
+ multiple: true,
fieldNames: {
label: 'id',
value: 'id',