diff --git a/packages/core/client/src/collection-manager/index.tsx b/packages/core/client/src/collection-manager/index.tsx index fbb3756f76..4585bca1cf 100644 --- a/packages/core/client/src/collection-manager/index.tsx +++ b/packages/core/client/src/collection-manager/index.tsx @@ -8,31 +8,32 @@ */ export { - useCancelAction, - useCollectionFilterOptions, - useSortFields, - useLinkageCollectionFilterOptions, - useCollectionFieldsOptions, isDeleteButtonDisabled, + useCancelAction, + useCollectionFieldsOptions, + useCollectionFilterOptions, + useCollectionFilterOptionsV2, + useLinkageCollectionFilterOptions, + useSortFields, } from './action-hooks'; +export * from './CollectionHistoryProvider'; export * from './CollectionManagerProvider'; export * from './CollectionManagerSchemaComponentProvider'; +export * from './collectionPlugin'; export * from './CollectionProvider_deprecated'; export * from './Configuration'; export { useFieldInterfaceOptions } from './Configuration/interfaces'; export * from './context'; export * from './hooks'; +export * from './interfaces'; +export * from './interfaces/properties'; export * as interfacesProperties from './interfaces/properties'; export * from './interfaces/types'; +export * from './mixins/InheritanceCollectionMixin'; export * from './ResourceActionProvider'; +export * from './sub-table'; +export { UnSupportFields } from './templates/components/UnSupportFields'; export { getConfigurableProperties } from './templates/properties'; export * from './templates/types'; export * from './types'; -export * from './interfaces'; -export * from './interfaces/properties'; -export * from './collectionPlugin'; -export * from './mixins/InheritanceCollectionMixin'; -export * from './sub-table'; -export * from './CollectionHistoryProvider'; export * from './utils'; -export { UnSupportFields } from './templates/components/UnSupportFields'; diff --git a/packages/core/client/src/schema-component/antd/variable/demos/data-scope-demo.tsx b/packages/core/client/src/schema-component/antd/variable/demos/data-scope-demo.tsx index 0ba293dae7..fd5227e484 100644 --- a/packages/core/client/src/schema-component/antd/variable/demos/data-scope-demo.tsx +++ b/packages/core/client/src/schema-component/antd/variable/demos/data-scope-demo.tsx @@ -8,28 +8,23 @@ import { SchemaSettingsModalItem, TableBlockProvider, useTableBlockProps, + Variable, } from '@nocobase/client'; import { mockApp } from '@nocobase/client/demo-utils'; import { property } from 'lodash'; import React from 'react'; -const DataScopeEditor = (props) => { - // const { getFields } = useCollectionFilterOptionsV2('roles'); - const getSchema = () => ({ - type: 'object', - title: 'Set the data scope', - properties: { - // enum: getFields(), - filter: { - 'x-component': 'Filter', - 'x-component-props': { - collectionName: 'users', - }, - }, - }, - }); - return null} schema={getSchema} />; -}; +const scopes = [ + { + label: 'Date', + value: '$date', + children: [ + { label: 'Now', value: 'now' }, + { label: 'Today', value: 'today_with_tz' }, + { label: 'Today', value: 'today_without_tz' }, + ], + }, +]; const dataScopeSettings = new SchemaSettings({ name: 'dataScopeSettings', @@ -38,7 +33,96 @@ const dataScopeSettings = new SchemaSettings({ name: 'data scope', Component: SchemaSettingsDataScope, componentProps: { - collectionName: 'users', + collectionName: 'date_collection', + }, + useComponentProps() { + return { + collectionName: 'date_collection', + dynamicComponent: (props) => { + const { collectionField } = props; + let scopes = []; + + // For date/datetime fields + if (['date', 'datetime'].includes(collectionField?.interface)) { + scopes = [ + { + label: 'Date', + value: '$date', + children: [ + { + label: 'Now', + value: 'now', + }, + { + label: 'Today', + value: 'today', + }, + { + label: 'Yesterday', + value: 'yesterday', + }, + { + label: 'Tomorrow', + value: 'tomorrow', + }, + ], + }, + ]; + } + + // For number fields + else if (['integer', 'number', 'percent'].includes(collectionField?.interface)) { + scopes = [ + { + label: 'Number', + value: '$number', + children: [ + { + label: 'Random', + value: 'random', + }, + { + label: 'Maximum', + value: 'max', + }, + { + label: 'Minimum', + value: 'min', + }, + ], + }, + ]; + } + + // For string fields + else if (['input', 'textarea', 'markdown'].includes(collectionField?.interface)) { + scopes = [ + { + label: 'String', + value: '$string', + children: [ + { + label: 'Current User', + value: 'currentUser', + children: [ + { + label: 'Name', + value: 'name', + }, + { + label: 'Email', + value: 'email', + }, + ], + }, + ], + }, + ]; + } + + return ; + }, + }; }, }, ], diff --git a/packages/core/client/src/schema-component/antd/variable/demos/date-collection-demo.tsx b/packages/core/client/src/schema-component/antd/variable/demos/date-collection-demo.tsx new file mode 100644 index 0000000000..b0abb412ec --- /dev/null +++ b/packages/core/client/src/schema-component/antd/variable/demos/date-collection-demo.tsx @@ -0,0 +1,192 @@ +import { ISchema, Schema, useField, useFieldSchema } from '@formily/react'; +import { + Plugin, + SchemaComponent, + SchemaSettings, + SchemaSettingsModalItem, + Variable, + VariableEvaluateProvider, + useVariableEvaluateContext, + CollectionField, +} from '@nocobase/client'; +import { mockApp } from '@nocobase/client/demo-utils'; +import { dayjs } from '@nocobase/utils/client'; +import React from 'react'; + +const DefaultValueEditor = () => { + const fieldSchema = useFieldSchema(); + const collectionField = fieldSchema['x-component-props']?.['field']; + const fieldType = collectionField?.interface; + + // Define date variable scopes based on field type + const dateScopes = { + date: [ + { label: 'Today', value: 'today_dateOnly' }, + { label: 'Yesterday', value: 'yesterday_dateOnly' }, + { label: 'Tomorrow', value: 'tomorrow_dateOnly' }, + ], + datetime: [ + { label: 'Now', value: 'now_withTZ' }, + { label: 'Today', value: 'today_withTZ' }, + { label: 'Yesterday', value: 'yesterday_withTZ' }, + { label: 'Tomorrow', value: 'tomorrow_withTZ' }, + ], + datetimeNoTz: [ + { label: 'Now', value: 'now_withoutTZ' }, + { label: 'Today', value: 'today_withoutTZ' }, + { label: 'Yesterday', value: 'yesterday_withoutTZ' }, + { label: 'Tomorrow', value: 'tomorrow_withoutTZ' }, + ], + }; + + const scope = dateScopes[fieldType] || []; + + // Define data with various date formats + const data = { + // Date only formats + today_dateOnly: dayjs().format('YYYY-MM-DD'), + yesterday_dateOnly: dayjs().subtract(1, 'day').format('YYYY-MM-DD'), + tomorrow_dateOnly: dayjs().add(1, 'day').format('YYYY-MM-DD'), + + // Datetime with timezone formats + now_withTZ: new Date().toISOString(), + today_withTZ: dayjs().startOf('day').toISOString(), + yesterday_withTZ: dayjs().subtract(1, 'day').startOf('day').toISOString(), + tomorrow_withTZ: dayjs().add(1, 'day').startOf('day').toISOString(), + + // Datetime without timezone formats + now_withoutTZ: dayjs().format('YYYY-MM-DD HH:mm:ss'), + today_withoutTZ: dayjs().startOf('day').format('YYYY-MM-DD HH:mm:ss'), + yesterday_withoutTZ: dayjs().subtract(1, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss'), + tomorrow_withoutTZ: dayjs().add(1, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss'), + }; + + const defaultValueSchema = { + type: 'object', + 'x-component-props': { + data, + context: {}, + }, + properties: { + variable: { + 'x-decorator': 'FormItem', + 'x-component': 'VariableInput', + }, + value: { + 'x-decorator': 'FormItem', + 'x-component': 'VariableValue', + }, + }, + }; + + const VariableInput = (props) => { + return ( + + + + ); + }; + + return ( + + null} + schema={defaultValueSchema} + components={{ VariableInput }} + /> + + ); +}; + +const dateSettings = new SchemaSettings({ + name: 'dateSettings', + items: [ + { + name: 'defaultValue', + Component: DefaultValueEditor, + }, + ], +}); + +const schema: ISchema = { + type: 'void', + name: 'root', + 'x-decorator': 'FormBlockProvider', + 'x-decorator-props': { + collection: 'date_collection', // users 数据表 + dataSource: 'main', // 多数据源标识,可以不写,默认为 main + }, + 'x-component': 'FormV2', + properties: { + dateonly: { + type: 'string', + title: 'DateOnly', + 'x-decorator': 'FormItem', + 'x-component': 'CollectionField', + 'x-settings': 'dateSettings', + required: true, + }, + datetime: { + type: 'string', + title: 'Datetime with Timezone', + 'x-decorator': 'FormItem', + 'x-component': 'CollectionField', + 'x-settings': 'dateSettings', + 'x-component-props': { + field: { + interface: 'datetime', + type: 'date', + uiSchema: { + 'x-component': 'DatePicker', + 'x-component-props': { + showTime: true, + utc: true, + }, + }, + }, + }, + required: true, + }, + datetime_withoutTZ: { + type: 'string', + title: 'Datetime without Timezone', + 'x-decorator': 'FormItem', + 'x-component': 'CollectionField', + 'x-settings': 'dateSettings', + 'x-component-props': { + field: { + interface: 'datetimeNoTz', + type: 'datetimeNoTz', + uiSchema: { + 'x-component': 'DatePicker', + 'x-component-props': { + showTime: true, + utc: false, + }, + }, + }, + }, + required: true, + }, + }, +}; + +const Demo = () => { + return ; +}; + +class DemoPlugin extends Plugin { + async load() { + this.app.router.add('root', { path: '/', Component: Demo }); + } +} + +const app = mockApp({ + designable: true, + plugins: [DemoPlugin], + schemaSettings: [dateSettings], +}); + +export default app.getRootComponent(); diff --git a/packages/core/client/src/schema-component/antd/variable/demos/filter-demo.tsx b/packages/core/client/src/schema-component/antd/variable/demos/filter-demo.tsx new file mode 100644 index 0000000000..e5ef296676 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/variable/demos/filter-demo.tsx @@ -0,0 +1,99 @@ +import { + AntdSchemaComponentProvider, + Filter, + Plugin, + SchemaComponent, + useCollectionFilterOptionsV2, + Variable, + VariableEvaluateProvider, +} from '@nocobase/client'; +import { mockApp } from '@nocobase/client/demo-utils'; +import PluginVariableFiltersClient from '@nocobase/plugin-variable-helpers/client'; +import { dayjs } from '@nocobase/utils/client'; +import { now } from 'lodash'; +import React, { useCallback } from 'react'; +const scope = [ + { label: 'v1', value: 'v1' }, + { label: 'Date', value: '$nDate', children: [{ label: 'Now', value: 'now' }] }, +]; + +const Demo = () => { + const useFilterProps = () => { + const dynamicComponent = useCallback((props) => { + const data = { + // Date only formats + today_dateOnly: dayjs().format('YYYY-MM-DD'), + yesterday_dateOnly: dayjs().subtract(1, 'day').format('YYYY-MM-DD'), + tomorrow_dateOnly: dayjs().add(1, 'day').format('YYYY-MM-DD'), + now_ts_s: new Date().getTime(), + }; + const dateScopesByType = { + dateOnly: [ + { label: 'Today', value: 'today_dateOnly' }, + { label: 'Yesterday', value: 'yesterday_dateOnly' }, + { label: 'Tomorrow', value: 'tomorrow_dateOnly' }, + ], + datetime: [ + { label: 'Now', value: 'now_withTZ' }, + { label: 'Today', value: 'today_withTZ' }, + { label: 'Yesterday', value: 'yesterday_withTZ' }, + { label: 'Tomorrow', value: 'tomorrow_withTZ' }, + ], + unixTimestamp: [{ label: 'Now', value: 'now_ts_s' }], + datetimeNoTz: [ + { label: 'Now', value: 'now_withoutTZ' }, + { label: 'Today', value: 'today_withoutTZ' }, + { label: 'Yesterday', value: 'yesterday_withoutTZ' }, + { label: 'Tomorrow', value: 'tomorrow_withoutTZ' }, + ], + }; + const { collectionField } = props; + const scope = dateScopesByType[collectionField?.type] || []; + return ( + + + + ); + }, []); + return { dynamicComponent, collectionName: 'date_collection' }; + }; + const { getFields } = useCollectionFilterOptionsV2('date_collection'); + const schema = { + type: 'void', + name: 'root', + 'x-decorator': 'FormBlockProvider', + 'x-decorator-props': { + collection: 'date_collection', // users 数据表 + dataSource: 'main', // 多数据源标识,可以不写,默认为 main + }, + properties: { + filter: { + enum: getFields(), + name: 'filter', + type: 'object', + title: 'Filter', + 'x-component': 'Filter', + 'x-component-props': { + collectionName: 'date_collection', + }, + + 'x-use-component-props': 'useFilterProps', + }, + }, + }; + return ( + + + + ); +}; + +class DemoPlugin extends Plugin { + async load() { + this.app.router.add('root', { path: '/', Component: Demo }); + } +} + +const app = mockApp({ plugins: [DemoPlugin, PluginVariableFiltersClient] }); + +export default app.getRootComponent(); diff --git a/packages/core/client/src/schema-component/antd/variable/index.md b/packages/core/client/src/schema-component/antd/variable/index.md index 27dce3f5fb..6d9a52333e 100644 --- a/packages/core/client/src/schema-component/antd/variable/index.md +++ b/packages/core/client/src/schema-component/antd/variable/index.md @@ -77,6 +77,9 @@ const scope = [ +#### 2. Filter 组件 + +