diff --git a/packages/core/client/src/block-provider/hooks/index.ts b/packages/core/client/src/block-provider/hooks/index.ts index 7ab28d4bd..78b2c764e 100644 --- a/packages/core/client/src/block-provider/hooks/index.ts +++ b/packages/core/client/src/block-provider/hooks/index.ts @@ -22,7 +22,7 @@ import { useCollection_deprecated, useCollectionManager_deprecated } from '../.. import { useFilterBlock } from '../../filter-provider/FilterProvider'; import { mergeFilter, transformToFilter } from '../../filter-provider/utils'; import { useRecord } from '../../record-provider'; -import { removeNullCondition, useActionContext, useCompile } from '../../schema-component'; +import { getCustomCondition, removeNullCondition, useActionContext, useCompile } from '../../schema-component'; import { isSubMode } from '../../schema-component/antd/association-field/util'; import { useCurrentUserContext } from '../../user'; import { useLocalVariables, useVariables } from '../../variables'; @@ -393,7 +393,6 @@ export const useFilterBlockActionProps = () => { const { getCollectionJoinField } = useCollectionManager_deprecated(); actionField.data = actionField.data || {}; - return { async onClick() { const { targets = [], uid } = findFilterTargets(fieldSchema); @@ -407,16 +406,44 @@ export const useFilterBlockActionProps = () => { if (!target) return; const param = block.service.params?.[0] || {}; + for (const key in form.values) { + if ( + (typeof form.values[key] === 'object' && + (JSON.stringify(form.values[key]) === '{}' || JSON.stringify(form.values[key]) === '[]')) || + !form.values[key] + ) { + delete form.values[key]; + } + } // 保留原有的 filter const storedFilter = block.service.params?.[1]?.filters || {}; + const filter = { + formValues: {}, + customValues: {}, + customFilter: {}, + }; + + if (Object.keys(form.values)?.includes('custom')) { + const values = { ...form.values }; + delete values['custom']; + filter.formValues = { ...values }; + for (const key in form.values['custom']) { + if (form.values['custom'][key]) { + filter.customValues[key] = form.values['custom'][key]; + } + } + filter.customFilter = getCustomCondition(filter.customValues, fieldSchema); + } + storedFilter[uid] = removeNullCondition( - transformToFilter(form.values, fieldSchema, getCollectionJoinField, name), + transformToFilter(filter.formValues, fieldSchema, getCollectionJoinField, name), ); const mergedFilter = mergeFilter([ ...Object.values(storedFilter).map((filter) => removeNullCondition(filter)), block.defaultFilter, + filter.customFilter, ]); if (block.dataLoadingMode === 'manual' && _.isEmpty(mergedFilter)) { diff --git a/packages/core/client/src/index.ts b/packages/core/client/src/index.ts index 6b2408183..c409ce23b 100644 --- a/packages/core/client/src/index.ts +++ b/packages/core/client/src/index.ts @@ -51,11 +51,3 @@ export * from 'react-i18next'; export { useHotkeys } from 'react-hotkeys-hook'; export * from './modules/blocks/useParentRecordCommon'; - -export * as __UNSAFE__ from './unsafe'; -export type { - DynamicComponentProps as __UNSAFE__DynamicComponentProps, - VariablesContextType as __UNSAFE__VariablesContextType, - VariableOption as __UNSAFE__VariableOption, - Option as __UNSAFE__VariableInputOption, -} from './unsafe'; diff --git a/packages/core/client/src/locale/zh-CN.json b/packages/core/client/src/locale/zh-CN.json index 237a68cb0..e6f32bbc2 100644 --- a/packages/core/client/src/locale/zh-CN.json +++ b/packages/core/client/src/locale/zh-CN.json @@ -939,5 +939,15 @@ "Show Data":"显示数据", "Setting Down Title":"设置文件名称", "The current return type is not a document type":"当前返回类型不是文档类型", - "Test Data":"测试数据" + "Test Data":"测试数据", + "Custom filter field": "自定义筛选字段", + "Custom option label": "自定义选项标签", + "Add custom field":"添加自定义字段", + "Input":"文本", + "AutoComplete":"文本选择", + "Field collection":"选择数据", + "AssociationCascader":"关系型级联选择", + "Edit Collection":"修改数据", + "Field Component":"字段组件", + "Custom filter":"自定义筛选" } diff --git a/packages/core/client/src/modules/blocks/data-blocks/form/FilterItemCustomSettings.tsx b/packages/core/client/src/modules/blocks/data-blocks/form/FilterItemCustomSettings.tsx new file mode 100644 index 000000000..7148f2870 --- /dev/null +++ b/packages/core/client/src/modules/blocks/data-blocks/form/FilterItemCustomSettings.tsx @@ -0,0 +1,294 @@ +import { Field, ISchema, useField, useFieldSchema } from '@tachybase/schema'; + +import _ from 'lodash'; +import { useTranslation } from 'react-i18next'; + +import { useCollection_deprecated, useCollectionManager_deprecated } from '../../../..//collection-manager'; +import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings'; +import { useFormBlockContext } from '../../../../block-provider'; +import { useCompile, useDesignable } from '../../../../schema-component'; +import { + EditFormulaTitleField, + FilterCustomVariableInput, + SchemaSettingCollection, + SchemaSettingComponent, + SchemaSettingsCustomRemove, + SchemaSettingsDataScope, +} from '../../../../schema-settings'; + +export const FilterItemCustomSettings = new SchemaSettings({ + name: 'fieldSettings:FilterFormCustomSettings', + items: [ + { + name: 'editFieldTitle', + type: 'modal', + useComponentProps() { + const { t } = useTranslation(); + const { dn } = useDesignable(); + const fieldSchema = useFieldSchema(); + const field = useField(); + const { getCollectionJoinField } = useCollectionManager_deprecated(); + const { getField } = useCollection_deprecated(); + const collectionField = + getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']); + + return { + title: t('Edit field title'), + schema: { + type: 'object', + title: t('Edit field title'), + properties: { + title: { + title: t('Field title'), + default: field?.title, + description: `${t('Original field title: ')}${collectionField?.uiSchema?.title}`, + 'x-decorator': 'FormItem', + 'x-component': 'Input', + 'x-component-props': {}, + }, + }, + } as ISchema, + onSubmit({ title }) { + if (title) { + field.title = title; + fieldSchema.title = title; + dn.emit('patch', { + schema: { + 'x-uid': fieldSchema['x-uid'], + title: fieldSchema.title, + }, + }); + } + dn.refresh(); + }, + }; + }, + }, + { + name: 'editDescription', + type: 'modal', + useComponentProps() { + const { t } = useTranslation(); + const { dn } = useDesignable(); + const field = useField(); + const fieldSchema = useFieldSchema(); + + return { + title: t('Edit description'), + schema: { + type: 'object', + title: t('Edit description'), + properties: { + description: { + // title: t('Description'), + default: field?.description, + 'x-decorator': 'FormItem', + 'x-component': 'Input.TextArea', + 'x-component-props': {}, + }, + }, + }, + onSubmit({ description }) { + field.description = description; + fieldSchema.description = description; + dn.emit('patch', { + schema: { + 'x-uid': fieldSchema['x-uid'], + description: fieldSchema.description, + }, + }); + dn.refresh(); + }, + }; + }, + }, + { + name: 'setTheDataScope', + Component: SchemaSettingsDataScope, + useComponentProps() { + const fieldSchema = useFieldSchema(); + const { form } = useFormBlockContext(); + const field = useField(); + const { dn } = useDesignable(); + const fieldName = fieldSchema['name'] as string; + const name = fieldName.includes('custom') ? fieldSchema['collectionName'] : fieldName; + return { + collectionName: name, + defaultFilter: fieldSchema?.['x-component-props']?.params?.filter || {}, + form: form, + 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'], + }, + }); + }, + }; + }, + useVisible() { + const fieldSchema = useFieldSchema(); + const component = fieldSchema['x-component']; + return component !== 'Input'; + }, + }, + { + name: 'fieldCollection', + Component: SchemaSettingCollection, + useVisible() { + const fieldSchema = useFieldSchema(); + const component = fieldSchema['x-component']; + return component === 'Select' || component === 'AutoComplete'; + }, + }, + { + name: 'fieldComponent', + Component: SchemaSettingComponent, + useVisible() { + const fieldSchema = useFieldSchema(); + const component = fieldSchema['x-component']; + return component === 'Select' || component === 'AutoComplete'; + }, + }, + { + name: 'titleField', + type: 'select', + useComponentProps() { + const { getCollectionFields, getCollectionJoinField } = useCollectionManager_deprecated(); + const { getField } = useCollection_deprecated(); + const field = useField(); + const fieldSchema = useFieldSchema(); + const { t } = useTranslation(); + const { dn } = useDesignable(); + const compile = useCompile(); + const collectionManage = useCollectionManager_deprecated(); + const isCustomFilterItem = ((fieldSchema?.name as string) ?? '').startsWith('custom.'); + const collectionManageField = isCustomFilterItem + ? collectionManage.collections.filter((value) => value.name === fieldSchema['x-decorator-props'])[0] + : {}; + const collectionField = + getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']); + let targetFields = []; + if (collectionField) { + targetFields = collectionField?.target + ? getCollectionFields(collectionField?.target) + : getCollectionFields(collectionField?.targetCollection) ?? []; + } else if (collectionManageField) { + targetFields = collectionManageField['fields']; + } + const options = targetFields + .filter((field) => !field?.target && field.type !== 'boolean') + .map((field) => ({ + value: field?.name, + label: compile(field?.uiSchema?.title) || field?.name, + })); + return { + title: t('Title field'), + options, + value: fieldSchema['x-component-props']?.['fieldNames']?.label || 'id', + onChange(label) { + const schema = { + ['x-uid']: fieldSchema['x-uid'], + }; + const fieldNames = { + ...collectionField?.uiSchema?.['x-component-props']?.['fieldNames'], + ...field.componentProps.fieldNames, + label, + }; + if (isCustomFilterItem) fieldNames['value'] = label; + fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {}; + fieldSchema['x-component-props']['fieldNames'] = fieldNames; + schema['x-component-props'] = fieldSchema['x-component-props']; + field.componentProps = fieldSchema['x-component-props']; + dn.emit('patch', { + schema, + }); + dn.refresh(); + }, + }; + }, + useVisible() { + const fieldSchema = useFieldSchema(); + const component = fieldSchema['x-component']; + return component === 'Select' || component === 'AutoComplete'; + }, + }, + { + name: 'EditFormulaTitleField', + Component: EditFormulaTitleField, + useVisible() { + const fieldSchema = useFieldSchema(); + const component = fieldSchema['x-component']; + return component === 'Select' || component === 'AutoComplete'; + }, + }, + { + name: 'EditCustomDefaultValue', + type: 'modal', + useComponentProps() { + const { t } = useTranslation(); + const { dn } = useDesignable(); + const field = useField(); + const fieldSchema = useFieldSchema(); + const title = fieldSchema.title; + return { + title: t('Set default value'), + schema: { + type: 'void', + title: t('Set default value'), + properties: { + default: { + title, + 'x-decorator': 'FormItem', + 'x-component': FilterCustomVariableInput, + 'x-component-props': { + fieldSchema, + }, + }, + }, + } as ISchema, + onSubmit({ default: { value } }) { + field.setValue(value); + fieldSchema['default'] = value; + fieldSchema['x-component-props']['defaultValue'] = value; + dn.emit('patch', { + schema: { + 'x-uid': fieldSchema['x-uid'], + ['x-component-props']: fieldSchema['x-component-props'], + default: fieldSchema.default, + }, + }); + dn.refresh(); + }, + }; + }, + }, + { + name: 'divider', + type: 'divider', + }, + { + name: 'delete', + // Component: SchemaSettingsCustomRemove, + // useComponentProps() { + // const { t } = useTranslation(); + // return { + // key: 'remove', + // confirm: { + // title: t('Delete field'), + // }, + // removeParentsIfNoChildren: true, + // breakRemoveOn: { + // 'x-component': 'Grid', + // }, + // }; + // }, + type: 'remove', + }, + ], +}); diff --git a/packages/core/client/src/modules/blocks/data-blocks/form/index.ts b/packages/core/client/src/modules/blocks/data-blocks/form/index.ts index 7a99f2926..663007206 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/form/index.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/form/index.ts @@ -11,3 +11,4 @@ export * from './formActionInitializers'; export * from './formItemInitializers'; export * from './updateFormActionInitializers'; export * from './createEditFormBlockUISchema'; +export * from './FilterItemCustomSettings'; diff --git a/packages/core/client/src/modules/blocks/filter-blocks/form/filterFormItemInitializers.tsx b/packages/core/client/src/modules/blocks/filter-blocks/form/filterFormItemInitializers.tsx index 912c4fa8c..0395b7605 100644 --- a/packages/core/client/src/modules/blocks/filter-blocks/form/filterFormItemInitializers.tsx +++ b/packages/core/client/src/modules/blocks/filter-blocks/form/filterFormItemInitializers.tsx @@ -37,6 +37,12 @@ export const filterFormItemInitializers_deprecated = new CompatibleSchemaInitial Component: 'MarkdownFormItemInitializer', name: 'addText', }, + { + title: '{{t("Custom filter field")}}', + name: 'custom', + type: 'item', + Component: 'FilterFormItemCustom', + }, ], }); @@ -70,6 +76,12 @@ export const filterFormItemInitializers = new CompatibleSchemaInitializer( Component: 'MarkdownFormItemInitializer', name: 'addText', }, + { + title: '{{t("Custom filter field")}}', + name: 'custom', + type: 'item', + Component: 'FilterFormItemCustom', + }, ], }, filterFormItemInitializers_deprecated, 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 63e94d8b6..d56d02257 100644 --- a/packages/core/client/src/modules/fields/component/Select/selectComponentFieldSettings.tsx +++ b/packages/core/client/src/modules/fields/component/Select/selectComponentFieldSettings.tsx @@ -20,7 +20,13 @@ import { useTitleFieldOptions, } from '../../../../schema-component/antd/form-item/FormItem.Settings'; import { useColumnSchema } from '../../../../schema-component/antd/table-v2/Table.Column.Decorator'; -import { getShouldChange, VariableInput } from '../../../../schema-settings'; +import { + EditCustomDefaultValue, + EditFormulaTitleField, + getShouldChange, + useFormulaTitleVisible, + VariableInput, +} from '../../../../schema-settings'; import { useIsShowMultipleSwitch } from '../../../../schema-settings/hooks/useIsShowMultipleSwitch'; import { SchemaSettingsDataScope } from '../../../../schema-settings/SchemaSettingsDataScope'; import { SchemaSettingsSortingRule } from '../../../../schema-settings/SchemaSettingsSortingRule'; @@ -381,5 +387,14 @@ export const selectComponentFieldSettings = new SchemaSettings({ return useIsAssociationField() && readPretty; }, }, + { + name: 'formulatitleField', + Component: EditFormulaTitleField, + useVisible: useFormulaTitleVisible, + }, + { + name: 'editDefaultValue', + Component: EditCustomDefaultValue, + }, ], }); diff --git a/packages/core/client/src/schema-component/antd/action/Action.Designer.tsx b/packages/core/client/src/schema-component/antd/action/Action.Designer.tsx index 6b859552b..adec78fda 100644 --- a/packages/core/client/src/schema-component/antd/action/Action.Designer.tsx +++ b/packages/core/client/src/schema-component/antd/action/Action.Designer.tsx @@ -20,16 +20,19 @@ import { isInitializersSame, useApp } from '../../../application'; import { usePlugin } from '../../../application/hooks'; import { SchemaSettingOptions, SchemaSettings } from '../../../application/schema-settings'; import { useSchemaToolbar } from '../../../application/schema-toolbar'; -import { useFormBlockContext } from '../../../block-provider'; +import { useFormBlockContext, useFormBlockType } from '../../../block-provider'; import { joinCollectionName, useCollection_deprecated, + useCollectionFilterOptions, useCollectionManager_deprecated, } from '../../../collection-manager'; -import { DataSourceProvider, useDataSourceKey } from '../../../data-source'; +import { DataSourceProvider, useCollection, useDataSourceKey } from '../../../data-source'; import { FlagProvider } from '../../../flag-provider'; import { SaveMode } from '../../../modules/actions/submit/createSubmitActionSettings'; +import { useRecord } from '../../../record-provider'; import { SchemaSettingOpenModeSchemaItems } from '../../../schema-items'; +import { FormFilterScope } from '../../../schema-settings/filter-form/FormFilterScope'; import { GeneralSchemaDesigner } from '../../../schema-settings/GeneralSchemaDesigner'; import { DefaultValueProvider } from '../../../schema-settings/hooks/useIsAllowToSetDefaultValue'; import { @@ -41,6 +44,8 @@ import { SchemaSettingsRemove, SchemaSettingsSwitchItem, } from '../../../schema-settings/SchemaSettings'; +import { useSchemaTemplateManager } from '../../../schema-templates'; +import { useLocalVariables, useVariables } from '../../../variables'; import { useLinkageAction } from './hooks'; import { requestSettingsSchema } from './utils'; @@ -598,6 +603,93 @@ export function WorkflowConfig() { ); } +const findGridSchema = (fieldSchema) => { + return fieldSchema.reduceProperties((buf, s) => { + if (s['x-component'] === 'FormV2') { + const f = s.reduceProperties((buf, s) => { + if (s['x-component'] === 'Grid' || s['x-component'] === 'BlockTemplate') { + return s; + } + return buf; + }, null); + if (f) { + return f; + } + } + return buf; + }, null); +}; + +export const useSetFilterScopeVisible = () => { + const fieldSchema = useFieldSchema(); + return ( + fieldSchema['x-component-props']?.useProps === '{{ useFilterBlockActionProps }}' || + fieldSchema['x-use-component-props'] === 'useFilterBlockActionProps' + ); +}; + +export const SetFilterScope = (props) => { + const { t } = useTranslation(); + const fieldSchema = useFieldSchema(); + const { collectionName } = props; + const gridSchema = findGridSchema(fieldSchema) || fieldSchema; + const { form } = useFormBlockContext(); + const type = props?.type || ['Action', 'Action.Link'].includes(fieldSchema['x-component']) ? 'button' : 'field'; + const variables = useVariables(); + const localVariables = useLocalVariables(); + const record = useRecord(); + const { type: formBlockType } = useFormBlockType(); + const schema = useMemo( + () => ({ + type: 'object', + title: t('Custom filter'), + properties: { + fieldReaction: { + 'x-component': FormFilterScope, + 'x-component-props': { + useProps: () => { + const options = useCollectionFilterOptions(collectionName); + return { + options, + defaultValues: gridSchema?.['x-filter-rules'] || fieldSchema?.['x-filter-rules'], + type, + collectionName, + form, + variables, + localVariables, + record, + formBlockType, + }; + }, + }, + }, + }, + }), + [], + ); + const { getTemplateById } = useSchemaTemplateManager(); + const { dn } = useDesignable(); + const onSubmit = useCallback( + (v) => { + const rules = v.fieldReaction.condition; + const templateId = gridSchema['x-component'] === 'BlockTemplate' && gridSchema['x-component-props'].templateId; + const uid = (templateId && getTemplateById(templateId).uid) || gridSchema['x-uid']; + const schema = { + ['x-uid']: uid, + }; + + gridSchema['x-filter-rules'] = rules; + schema['x-filter-rules'] = rules; + dn.emit('patch', { + schema, + }); + dn.refresh(); + }, + [dn, getTemplateById, gridSchema], + ); + return ; +}; + export const actionSettingsItems: SchemaSettingOptions['items'] = [ { name: 'Customize', @@ -735,6 +827,17 @@ export const actionSettingsItems: SchemaSettingOptions['items'] = [ }; }, }, + { + name: 'Customize.setFilterScope', + Component: SetFilterScope, + useVisible: useSetFilterScopeVisible, + useComponentProps() { + const collection = useCollection(); + return { + collectionName: collection.name, + }; + }, + }, { name: 'remove', sort: 100, diff --git a/packages/core/client/src/schema-component/antd/filter/useFilterActionProps.ts b/packages/core/client/src/schema-component/antd/filter/useFilterActionProps.ts index 23a6ddc36..0b7d68662 100644 --- a/packages/core/client/src/schema-component/antd/filter/useFilterActionProps.ts +++ b/packages/core/client/src/schema-component/antd/filter/useFilterActionProps.ts @@ -8,6 +8,7 @@ import { useBlockRequestContext } from '../../../block-provider'; import { useCollection_deprecated, useCollectionManager_deprecated } from '../../../collection-manager'; import { mergeFilter } from '../../../filter-provider/utils'; import { useDataLoadingMode } from '../../../modules/blocks/data-blocks/details-multi/setDataLoadingModeSettingsItem'; +import { hasDuplicateKeys } from './utils'; export const useGetFilterOptions = () => { const { getCollectionFields } = useCollectionManager_deprecated(); @@ -149,8 +150,42 @@ const isEmpty = (obj) => { ); }; -export const removeNullCondition = (filter) => { - const items = flat(filter || {}); +export const getCustomCondition = (filter, fieldSchema, customFlat = flat) => { + const filterSchema = fieldSchema ? fieldSchema['x-filter-rules'] : ''; + const filterSchemaItem = customFlat(filterSchema || '') as any; + const items = customFlat(filter || {}) as any; + const values = {}; + const isCustomFilter = filterSchema?.$and?.length || filterSchema?.$or?.length; + const isFilterCustom = isCustomFilter ? hasDuplicateKeys(items, filterSchemaItem) : false; + if (!isFilterCustom) { + if (isCustomFilter) { + for (const filterKey in filterSchemaItem) { + const match = filterSchemaItem[filterKey]?.slice(11, -2); + const collection = match?.split('.')[0]; + const filterItems = Object.keys(items).filter((item) => item.includes(collection))[0]; + if (filterItems) { + filterSchemaItem[filterKey] = items[filterItems]; + } + } + for (const item in filterSchemaItem) { + if (filterSchemaItem[item].includes('$nFilter')) { + delete filterSchemaItem[item]; + } + } + const flatFieldSchema = customFlat.unflatten(filterSchemaItem); + flatFieldSchema['$and'] = flatFieldSchema?.['$and']?.filter(Boolean); + flatFieldSchema['$or'] = flatFieldSchema?.['$or']?.filter(Boolean); + return flatFieldSchema; + } else { + return customFlat.unflatten({}); + } + } else { + return customFlat.unflatten(items); + } +}; + +export const removeNullCondition = (filter, customFlat = flat) => { + const items = customFlat(filter || {}) as any; const values = {}; for (const key in items) { const value = items[key]; @@ -158,7 +193,7 @@ export const removeNullCondition = (filter) => { values[key] = value; } } - return flat.unflatten(values); + return customFlat.unflatten(values); }; export const useFilterActionProps = () => { @@ -179,7 +214,7 @@ export const useFilterFieldProps = ({ options, service, params }) => { // filter parameter for the block const defaultFilter = params.filter; // filter parameter for the filter action - const filter = removeNullCondition(values?.filter); + const filter = removeNullCondition(values?.filter) as any; if (dataLoadingMode === 'manual' && _.isEmpty(filter)) { return service.mutate(undefined); diff --git a/packages/core/client/src/schema-component/antd/filter/utils.ts b/packages/core/client/src/schema-component/antd/filter/utils.ts new file mode 100644 index 000000000..d9da4acca --- /dev/null +++ b/packages/core/client/src/schema-component/antd/filter/utils.ts @@ -0,0 +1,12 @@ +// 数组去重 +const getArrayOfNoDuplicateValue = (arr: Array): Array => { + return [...new Set(arr)]; +}; + +// 判断两个对象是否包含有相同的key +export function hasDuplicateKeys(A: Object, B: Object): boolean { + const keysA = getArrayOfNoDuplicateValue(Object.keys(A)); + const keysB = getArrayOfNoDuplicateValue(Object.keys(B)); + const keysA_B = getArrayOfNoDuplicateValue([...keysA, ...keysB]); + return keysA_B.length < keysA.length + keysB.length; +} diff --git a/packages/core/client/src/schema-component/antd/form-item/SchemaSettingOptions.tsx b/packages/core/client/src/schema-component/antd/form-item/SchemaSettingOptions.tsx index 9da57e19d..831920577 100644 --- a/packages/core/client/src/schema-component/antd/form-item/SchemaSettingOptions.tsx +++ b/packages/core/client/src/schema-component/antd/form-item/SchemaSettingOptions.tsx @@ -558,23 +558,32 @@ export const EditTitleField = () => { const { t } = useTranslation(); const { dn } = useDesignable(); const compile = useCompile(); + const collectionManage = useCollectionManager_deprecated(); + const isCustomFilterItem = ((fieldSchema?.name as string) ?? '').startsWith('custom.'); + const collectionManageField = isCustomFilterItem + ? collectionManage.collections.filter((value) => value.name === fieldSchema['x-decorator-props'])[0] + : {}; const collectionField = getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']); - const targetFields = collectionField?.target - ? getCollectionFields(collectionField?.target) - : getCollectionFields(collectionField?.targetCollection) ?? []; + let targetFields = []; + if (collectionField) { + targetFields = collectionField?.target + ? getCollectionFields(collectionField?.target) + : getCollectionFields(collectionField?.targetCollection) ?? []; + } else if (collectionManageField) { + targetFields = collectionManageField['fields']; + } const options = targetFields .filter((field) => !field?.target && field.type !== 'boolean') .map((field) => ({ value: field?.name, label: compile(field?.uiSchema?.title) || field?.name, })); - - return options.length > 0 && fieldSchema['x-component'] === 'CollectionField' ? ( + return options.length > 0 && (fieldSchema['x-component'] === 'CollectionField' || isCustomFilterItem) ? ( { const schema = { ['x-uid']: fieldSchema['x-uid'], @@ -584,10 +593,11 @@ export const EditTitleField = () => { ...field.componentProps.fieldNames, label, }; + if (isCustomFilterItem) fieldNames['value'] = label; fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {}; fieldSchema['x-component-props']['fieldNames'] = fieldNames; schema['x-component-props'] = fieldSchema['x-component-props']; - field.componentProps.fieldNames = fieldSchema['x-component-props'].fieldNames; + field.componentProps = fieldSchema['x-component-props']; dn.emit('patch', { schema, }); diff --git a/packages/core/client/src/schema-initializer/index.ts b/packages/core/client/src/schema-initializer/index.ts index 5c108ddd3..1d8e63199 100644 --- a/packages/core/client/src/schema-initializer/index.ts +++ b/packages/core/client/src/schema-initializer/index.ts @@ -119,6 +119,7 @@ import { } from './buttons'; import * as initializerComponents from './components'; import * as items from './items'; +import { FilterFormItemCustom } from './items'; export * from './buttons'; export * from './items'; @@ -176,6 +177,7 @@ export class SchemaInitializerPlugin extends Plugin { DisassociateActionInitializer, FilterActionInitializer, RefreshActionInitializer, + FilterFormItemCustom, } as any); this.app.schemaInitializerManager.add(blockInitializers_deprecated); diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-initializer/items/CustomFilterFormItemInitializer.tsx b/packages/core/client/src/schema-initializer/items/CustomFilterFormItemInitializer.tsx similarity index 91% rename from packages/plugins/@hera/plugin-core/src/client/schema-initializer/items/CustomFilterFormItemInitializer.tsx rename to packages/core/client/src/schema-initializer/items/CustomFilterFormItemInitializer.tsx index 775d9e4f9..7bbfc7db4 100644 --- a/packages/plugins/@hera/plugin-core/src/client/schema-initializer/items/CustomFilterFormItemInitializer.tsx +++ b/packages/core/client/src/schema-initializer/items/CustomFilterFormItemInitializer.tsx @@ -1,32 +1,4 @@ -import React, { memo, Profiler, useCallback, useContext, useMemo } from 'react'; -import { - ACLCollectionFieldProvider, - BlockItem, - css, - cx, - EditComponent, - EditDescription, - FormDialog, - FormItem, - GeneralSchemaDesigner, - gridRowColWrap, - HTMLEncode, - i18n, - SchemaComponent, - SchemaComponentOptions, - SchemaInitializerItem, - SchemaSettingsDataScope, - SchemaSettingsDivider, - Select, - useAPIClient, - useCollectionManager, - useCollectionManager_deprecated, - useCompile, - useDesignable, - useFormBlockContext, - useGlobalTheme, - useSchemaInitializerItem, -} from '@tachybase/client'; +import React, { memo, useCallback, useContext, useMemo } from 'react'; import { ArrayItems, FormLayout } from '@tachybase/components'; import { Field, @@ -37,22 +9,48 @@ import { uid, useField, useFieldSchema, - useForm, } from '@tachybase/schema'; -import { ConfigProvider, Radio, Space } from 'antd'; +import { ConfigProvider, Space } from 'antd'; import _ from 'lodash'; +import { useTranslation } from 'react-i18next'; -import { tval, useTranslation } from '../../locale'; +import { useAPIClient } from '../../api-client'; +import { SchemaInitializerItem, useSchemaInitializerItem } from '../../application'; +import { SchemaSettings } from '../../application/schema-settings/SchemaSettings'; +import { useFormBlockContext } from '../../block-provider'; +import { ACLCollectionFieldProvider } from '../../built-in/acl'; +import { useCollectionManager_deprecated } from '../../collection-manager'; +import { useCollectionManager } from '../../data-source'; +import { i18n } from '../../i18n'; import { - EditDefaultValue, - EditFormulaTitleField, + BlockItem, + EditDescription, EditTitle, EditTitleField, + FormDialog, + FormItem, + HTMLEncode, + SchemaComponent, + SchemaComponentOptions, + Select, + useCompile, + useDesignable, +} from '../../schema-component'; +import { + EditCustomDefaultValue, + EditFormulaTitleField, + GeneralSchemaDesigner, SchemaSettingCollection, SchemaSettingComponent, + SchemaSettingsCustomRemove, + SchemaSettingsDataScope, + SchemaSettingsDivider, + SchemaSettingsRemove, } from '../../schema-settings'; -import { SchemaSettingsRemove } from '../../schema-settings/SchemaSettingsRemove'; +import { css, cx } from '../../style'; +import { useGlobalTheme } from '../../style/theme'; +import { gridRowColWrap } from '../utils'; const FieldComponentProps: React.FC = observer( (props) => { @@ -228,7 +226,7 @@ export const FilterCustomItemInitializer: React.FC<{ }, associationField: { type: 'string', - title: tval('Association field'), + title: t('Association field'), 'x-decorator': 'FormItem', 'x-component': 'Select', 'x-visible': false, @@ -290,7 +288,7 @@ export const FilterCustomItemInitializer: React.FC<{ }); const { title, component, collection, associationField } = values; const defaultSchema = getInterface(component)?.default?.uiSchema || {}; - const titleField = cm.getCollection(collection).titleField; + const titleField = cm.getCollection(collection)?.titleField; const name = uid(); insert( gridRowColWrap({ @@ -300,7 +298,8 @@ export const FilterCustomItemInitializer: React.FC<{ name: 'custom.' + name, required: false, 'x-component': component, - 'x-designer': 'FilterItemCustomDesigner', + 'x-toolbar': 'FormItemSchemaToolbar', + 'x-settings': 'fieldSettings:FilterFormCustomSettings', 'x-decorator': 'FilterFormItem', 'x-decorator-props': collection, 'x-component-props': { @@ -364,18 +363,9 @@ export const FilterItemCustomDesigner: React.FC = () => { {component === 'Select' || component === 'AutoComplete' ? : null} {component === 'Select' || component === 'AutoComplete' ? : null} {component === 'Select' || component === 'AutoComplete' ? : null} - + - + ); }; diff --git a/packages/core/client/src/schema-initializer/items/index.tsx b/packages/core/client/src/schema-initializer/items/index.tsx index e52a191cc..ac7f1736c 100644 --- a/packages/core/client/src/schema-initializer/items/index.tsx +++ b/packages/core/client/src/schema-initializer/items/index.tsx @@ -21,3 +21,4 @@ export * from './RecordReadPrettyAssociationFormBlockInitializer'; export * from './SelectActionInitializer'; export * from './SubmitActionInitializer'; export * from './TableActionColumnInitializer'; +export * from './CustomFilterFormItemInitializer'; diff --git a/packages/core/client/src/schema-settings/EditCustomDefaultValue.tsx b/packages/core/client/src/schema-settings/EditCustomDefaultValue.tsx new file mode 100644 index 000000000..09c0e05fb --- /dev/null +++ b/packages/core/client/src/schema-settings/EditCustomDefaultValue.tsx @@ -0,0 +1,97 @@ +import React, { useEffect, useMemo } from 'react'; +import { Field, useField, useFieldSchema } from '@tachybase/schema'; + +import { useMemoizedFn } from 'ahooks'; +import { useTranslation } from 'react-i18next'; + +import { SchemaComponent, useDesignable, VariableScopeProvider } from '../schema-component'; +import { SchemaSettingsModalItem } from './SchemaSettings'; +import { getShouldChange, useCurrentUserVariable, useDatetimeVariable, VariableInput } from './VariableInput'; + +export const EditCustomDefaultValue = () => { + const { t } = useTranslation(); + const { dn } = useDesignable(); + const field = useField(); + const fieldSchema = useFieldSchema(); + const collectionName = fieldSchema['collectionName']; + const title = fieldSchema.title; + return ( + { + field.setValue(value); + fieldSchema['default'] = value; + fieldSchema['x-component-props']['defaultValue'] = value; + dn.emit('patch', { + schema: { + 'x-uid': fieldSchema['x-uid'], + ['x-component-props']: fieldSchema['x-component-props'], + default: fieldSchema.default, + }, + }); + dn.refresh(); + }} + /> + ); +}; + +export const FilterCustomVariableInput = (props: any) => { + const { value, onChange, fieldSchema } = props; + const { currentUserSettings } = useCurrentUserVariable({ + collectionField: { uiSchema: fieldSchema }, + uiSchema: fieldSchema, + }); + const { datetimeSettings } = useDatetimeVariable({ + operator: fieldSchema['x-component-props']?.['filter-operator'], + schema: fieldSchema, + noDisabled: true, + }); + const options = useMemo( + () => [currentUserSettings, datetimeSettings].filter(Boolean), + [datetimeSettings, currentUserSettings], + ); + const schema = { + ...fieldSchema, + 'x-component': fieldSchema['x-component'] || 'Input', + 'x-decorator': '', + title: '', + name: 'value', + }; + const componentProps = fieldSchema['x-component-props'] || {}; + const handleChange = useMemoizedFn(onChange); + useEffect(() => { + if (fieldSchema.default) { + handleChange({ value: fieldSchema.default }); + } + }, [fieldSchema.default, handleChange]); + return ( + + } + fieldNames={{}} + value={value?.value} + scope={options} + onChange={(v: any) => { + onChange({ value: v }); + }} + shouldChange={getShouldChange({} as any)} + /> + + ); +}; diff --git a/packages/core/client/src/schema-settings/EditFormulaTitleField.tsx b/packages/core/client/src/schema-settings/EditFormulaTitleField.tsx new file mode 100644 index 000000000..d07dddaa9 --- /dev/null +++ b/packages/core/client/src/schema-settings/EditFormulaTitleField.tsx @@ -0,0 +1,124 @@ +import React from 'react'; +import { Field, ISchema, useField, useFieldSchema } from '@tachybase/schema'; + +import { useTranslation } from 'react-i18next'; + +import { useCollection_deprecated, useCollectionManager_deprecated } from '../collection-manager'; +import { useCompile, useDesignable } from '../schema-component'; +import { SchemaSettingsModalItem } from './SchemaSettings'; + +export const useFormulaTitleOptions = () => { + const compile = useCompile(); + const { getCollectionJoinField, getCollectionFields } = useCollectionManager_deprecated(); + const { getField } = useCollection_deprecated(); + const fieldSchema = useFieldSchema(); + const collectionManage = useCollectionManager_deprecated(); + const collectionManageField = collectionManage.collections.filter( + (value) => value.name === fieldSchema['x-decorator-props'], + )[0]; + const collectionField = getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']); + let fields = []; + if (collectionField) { + fields = getCollectionFields( + collectionField + ? collectionField.target + ? collectionField.target + : collectionField.collectionName + : fieldSchema['name'], + ); + } else if (collectionManageField) { + fields = collectionManageField['fields']; + } + const options = []; + fields?.forEach((field) => { + if (field.interface !== 'm2m') { + if (field.uiSchema) { + options.push({ + label: compile(field.uiSchema.title), + value: field.name, + children: + getCollectionFields(field.target) + ?.filter((subField) => subField.uiSchema) + .map((subField) => ({ + label: subField.uiSchema ? compile(subField.uiSchema.title) : '', + value: subField.name, + })) ?? [], + }); + } + } + }); + return options; +}; + +export const EditFormulaTitleField = () => { + const { getCollectionJoinField, collections } = useCollectionManager_deprecated(); + const { getField } = useCollection_deprecated(); + const field = useField(); + const fieldSchema = useFieldSchema(); + const { t } = useTranslation(); + const { dn } = useDesignable(); + + let collectionField = getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']); + collectionField = collectionField + ? collectionField + : collections.find((value) => value.name === fieldSchema['x-decorator-props'])?.fields; + const options = useFormulaTitleOptions(); + const editTitle = async (formula) => { + const schema = { + ['x-uid']: fieldSchema['x-uid'], + }; + const fieldNames = { + ...collectionField?.uiSchema?.['x-component-props']?.['fieldNames'], + ...field.componentProps.fieldNames, + formula, + }; + fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {}; + fieldSchema['x-component-props']['fieldNames'] = fieldNames; + schema['x-component-props'] = fieldSchema['x-component-props']; + field.componentProps.fieldNames = fieldSchema['x-component-props'].fieldNames; + dn.emit('patch', { + schema, + }); + dn.refresh(); + }; + + return ( + { + if (formula) { + editTitle(formula); + } + }} + /> + ); +}; + +export const useFormulaTitleVisible = () => { + const fieldSchema = useFieldSchema(); + const options = useFormulaTitleOptions(); + // FIXME 这里现在只有当设置为 select,默认为 select 的时候看不到 + return ( + options.length > 0 && + (fieldSchema['x-component-props']?.mode === 'Select' || fieldSchema['x-component'] === 'CollectionField') && + fieldSchema['x-component-props']?.fieldNames?.value !== undefined + ); +}; diff --git a/packages/core/client/src/schema-settings/SchemaSettingCollection.tsx b/packages/core/client/src/schema-settings/SchemaSettingCollection.tsx new file mode 100644 index 000000000..c217c2ee2 --- /dev/null +++ b/packages/core/client/src/schema-settings/SchemaSettingCollection.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import { Field, useField, useFieldSchema } from '@tachybase/schema'; + +import { useCollectionManager } from '../data-source'; +import { useDesignable } from '../schema-component'; +import { SchemaSettingsSelectItem } from './SchemaSettings'; + +export const SchemaSettingCollection = () => { + const fieldSchema = useFieldSchema(); + const collections = useCollectionManager(); + const field = useField(); + const options = collections?.dataSource['options']?.collections.map((value) => { + return { + label: value.title, + value: value.name, + }; + }); + const { dn } = useDesignable(); + return ( + { + field.setValue(''); + const titleField = collections.getCollection(name).titleField; + fieldSchema['collectionName'] = name; + fieldSchema.default = ''; + fieldSchema['x-component-props'] = { + ...fieldSchema['x-component-props'], + collection: name, + fieldNames: { + label: titleField, + value: titleField, + }, + defaultValue: '', + }; + fieldSchema['x-decorator-props'] = name; + field.componentProps = fieldSchema['x-component-props']; + void dn.emit('patch', { + schema: { + ['x-uid']: fieldSchema['x-uid'], + collectionName: fieldSchema['collectionName'], + ['x-component-props']: fieldSchema['x-component-props'], + ['x-decorator-props']: fieldSchema['x-decorator-props'], + default: '', + }, + }); + dn.refresh(); + }} + /> + ); +}; diff --git a/packages/core/client/src/schema-settings/SchemaSettingComponent.tsx b/packages/core/client/src/schema-settings/SchemaSettingComponent.tsx new file mode 100644 index 000000000..e078d5955 --- /dev/null +++ b/packages/core/client/src/schema-settings/SchemaSettingComponent.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { useField, useFieldSchema } from '@tachybase/schema'; + +import { useDesignable } from '../schema-component'; +import { useFieldComponents } from '../schema-initializer'; +import { SchemaSettingsSelectItem } from './SchemaSettings'; + +export const SchemaSettingComponent = () => { + const fieldSchema = useFieldSchema(); + const field = useField(); + const { options } = useFieldComponents(); + const { dn } = useDesignable(); + return ( + { + field.component = mode; + fieldSchema['x-component'] = mode; + field.componentProps = fieldSchema['x-component-props']; + void dn.emit('patch', { + schema: { + ['x-uid']: fieldSchema['x-uid'], + ['x-component']: fieldSchema['x-component'], + }, + }); + dn.refresh(); + }} + /> + ); +}; diff --git a/packages/core/client/src/schema-settings/SchemaSettingsPlugin.ts b/packages/core/client/src/schema-settings/SchemaSettingsPlugin.ts index 694cbaef8..f5405f3cb 100644 --- a/packages/core/client/src/schema-settings/SchemaSettingsPlugin.ts +++ b/packages/core/client/src/schema-settings/SchemaSettingsPlugin.ts @@ -23,6 +23,7 @@ import { detailsBlockSettings, singleDataDetailsBlockSettings, } from '../modules/blocks/data-blocks/details-single/detailsBlockSettings'; +import { FilterItemCustomSettings } from '../modules/blocks/data-blocks/form'; import { createFormBlockSettings, creationFormBlockSettings, @@ -95,6 +96,7 @@ export class SchemaSettingsPlugin extends Plugin { // field settings this.schemaSettingsManager.add(fieldSettingsFormItem); + this.schemaSettingsManager.add(FilterItemCustomSettings); this.schemaSettingsManager.add(tableColumnSettings); this.schemaSettingsManager.add(filterCollapseItemFieldSettings); diff --git a/packages/core/client/src/schema-settings/VariableInput/VariableInput.tsx b/packages/core/client/src/schema-settings/VariableInput/VariableInput.tsx index 3cfb6c5db..22aef9d15 100644 --- a/packages/core/client/src/schema-settings/VariableInput/VariableInput.tsx +++ b/packages/core/client/src/schema-settings/VariableInput/VariableInput.tsx @@ -1,19 +1,20 @@ -import React, { useCallback } from 'react'; +import React, { useCallback, useMemo } from 'react'; // @ts-ignore -import { Form, Schema } from '@tachybase/schema'; +import { Form, Schema, useField } from '@tachybase/schema'; import _ from 'lodash'; import { useTranslation } from 'react-i18next'; -import { CollectionFieldOptions_deprecated } from '../../collection-manager'; -import { useVariableScope, Variable } from '../../schema-component'; +import { CollectionFieldOptions_deprecated, useCollectionManager_deprecated } from '../../collection-manager'; +import { useCompile, useVariableScope, Variable } from '../../schema-component'; import { useValues } from '../../schema-component/antd/filter/useValues'; import { VariableOption, VariablesContextType } from '../../variables/types'; import { isVariable } from '../../variables/utils/isVariable'; +import { useDateVariable } from './hooks'; import { useBlockCollection } from './hooks/useBlockCollection'; import { useContextAssociationFields } from './hooks/useContextAssociationFields'; import { useCurrentRecordVariable } from './hooks/useRecordVariable'; -import { useCurrentUserVariable } from './hooks/useUserVariable'; +import { useCurrentUserVariable, useUserVariable } from './hooks/useUserVariable'; import { useVariableOptions } from './hooks/useVariableOptions'; import { Option } from './type'; diff --git a/packages/core/client/src/schema-settings/VariableInput/hooks/useFilterVariable.tsx b/packages/core/client/src/schema-settings/VariableInput/hooks/useFilterVariable.tsx new file mode 100644 index 000000000..bf4cda714 --- /dev/null +++ b/packages/core/client/src/schema-settings/VariableInput/hooks/useFilterVariable.tsx @@ -0,0 +1,61 @@ +import { useMemo } from 'react'; +import { Field, useField } from '@tachybase/schema'; + +import { useTranslation } from 'react-i18next'; + +import { useFormActiveFields } from '../../../block-provider'; +import { useFilterContext } from '../../filter-form/context'; + +/** + * 变量:`自定义筛选` + * @param props + * @returns + */ +export const useFilterVariable = (props: any = {}) => { + const customFilter = useFilterContext(); + const field = useField(); + const { getActiveFieldsName } = useFormActiveFields(); + const activeFields = getActiveFieldsName('form'); + const fields = props?.form?.fields; + const { t } = useTranslation(); + const settings = useMemo(() => { + const customFields = []; + + for (const field in fields) { + customFields.push({ + key: field, + ...fields[field], + }); + } + const options = customFields + .filter((value) => value?.props?.name.includes('custom.')) + .map((custom) => { + if (activeFields.includes(custom?.props?.name)) { + const value = custom?.props?.name.replace(/^custom\./, ''); + return { + key: custom?.props?.name, + value, + label: custom.title, + }; + } else { + return null; + } + }) + .filter(Boolean); + return { + label: t('Current filter'), + value: '$nFilter', + key: '$nFilter', + children: options, + }; + }, [fields]); + + return { + /** 变量配置 */ + currentCustomFilterSetting: settings, + /** 变量值 */ + currentCustonFilterCtx: field?.value, + /** 用于判断是否需要显示配置项 */ + shouldDisplayCustomFilter: !!customFilter, + }; +}; diff --git a/packages/core/client/src/schema-settings/VariableInput/hooks/useVariableOptions.ts b/packages/core/client/src/schema-settings/VariableInput/hooks/useVariableOptions.ts index 05e5945b2..d19193e97 100644 --- a/packages/core/client/src/schema-settings/VariableInput/hooks/useVariableOptions.ts +++ b/packages/core/client/src/schema-settings/VariableInput/hooks/useVariableOptions.ts @@ -4,6 +4,7 @@ import { Form, ISchema, Schema } from '@tachybase/schema'; import { CollectionFieldOptions_deprecated } from '../../../collection-manager'; import { useBlockCollection } from './useBlockCollection'; import { useDatetimeVariable } from './useDateVariable'; +import { useFilterVariable } from './useFilterVariable'; import { useCurrentFormVariable } from './useFormVariable'; import { useCurrentObjectVariable } from './useIterationVariable'; import { useCurrentParentRecordVariable } from './useParentRecordVariable'; @@ -96,6 +97,14 @@ export const useVariableOptions = ({ targetFieldSchema, }); + const { currentCustomFilterSetting, shouldDisplayCustomFilter } = useFilterVariable({ + schema: uiSchema, + form, + collectionName: blockParentCollectionName, + collectionField, + targetFieldSchema, + }); + return useMemo(() => { return [ currentUserSettings, @@ -106,6 +115,7 @@ export const useVariableOptions = ({ shouldDisplayCurrentRecord && currentRecordSettings, shouldDisplayCurrentParentRecord && currentParentRecordSettings, shouldDisplayPopupRecord && popupRecordSettings, + shouldDisplayCustomFilter && currentCustomFilterSetting, ].filter(Boolean); }, [ currentUserSettings, @@ -121,5 +131,7 @@ export const useVariableOptions = ({ currentParentRecordSettings, shouldDisplayPopupRecord, popupRecordSettings, + currentCustomFilterSetting, + shouldDisplayCustomFilter, ]); }; diff --git a/packages/plugins/@hera/plugin-core/src/client/components/filter-form/FormFilterScope.tsx b/packages/core/client/src/schema-settings/filter-form/FormFilterScope.tsx similarity index 73% rename from packages/plugins/@hera/plugin-core/src/client/components/filter-form/FormFilterScope.tsx rename to packages/core/client/src/schema-settings/filter-form/FormFilterScope.tsx index ace63977a..654d3dc4e 100644 --- a/packages/plugins/@hera/plugin-core/src/client/components/filter-form/FormFilterScope.tsx +++ b/packages/core/client/src/schema-settings/filter-form/FormFilterScope.tsx @@ -1,34 +1,30 @@ import React, { useMemo } from 'react'; -import { - __UNSAFE__DynamicComponentProps, - __UNSAFE__VariableOption, - __UNSAFE__VariablesContextType, - css, - FormBlockContext, - getShouldChange, - RecordProvider, - SchemaComponent, - useCollectionManager_deprecated, -} from '@tachybase/client'; import { ArrayCollapse } from '@tachybase/components'; import { Form, observer, useField, useFieldSchema } from '@tachybase/schema'; +import { css } from 'antd-style'; + +import { FormBlockContext } from '../../block-provider'; +import { useCollectionManager_deprecated } from '../../collection-manager'; +import { RecordProvider } from '../../record-provider'; +import { SchemaComponent } from '../../schema-component'; +import { DynamicComponentProps } from '../../schema-component/antd/filter/DynamicComponent'; +import { VariableOption, VariablesContextType } from '../../variables/types'; +import { getShouldChange, VariableInput } from '../VariableInput'; import { FilterContext } from './context'; -import { VariableInput } from './VariableInput'; interface usePropsReturn { options: any; defaultValues: any[]; collectionName: string; form: Form; - variables: __UNSAFE__VariablesContextType; - localVariables: __UNSAFE__VariableOption | __UNSAFE__VariableOption[]; + variables: VariablesContextType; + localVariables: VariableOption | VariableOption[]; record: Record; /** * create 表示创建表单,update 表示更新表单 */ formBlockType: 'create' | 'update'; - fields: any; } interface Props { @@ -40,7 +36,7 @@ export const FormFilterScope = observer( (props: Props) => { const fieldSchema = useFieldSchema(); const { useProps, dynamicComponent } = props; - const { options, defaultValues, collectionName, form, formBlockType, variables, localVariables, record, fields } = + const { options, defaultValues, collectionName, form, formBlockType, variables, localVariables, record } = useProps(); const { getAllCollectionsInheritChain } = useCollectionManager_deprecated(); const components = useMemo(() => ({ ArrayCollapse }), []); @@ -63,7 +59,7 @@ export const FormFilterScope = observer( `, }; }, - dynamicComponent: (props: __UNSAFE__DynamicComponentProps) => { + dynamicComponent: (props: DynamicComponentProps) => { const { collectionField } = props; return ( ); }, @@ -85,15 +80,20 @@ export const FormFilterScope = observer( }, }, }), - [collectionName, defaultValues, form, localVariables, options, props, record, variables, fields], + [collectionName, defaultValues, form, localVariables, options, props, record, variables], ); const value = useMemo( - () => ({ field: options, fieldSchema, dynamicComponent, options: options || [] }), + () => ({ + field: options, + fieldSchema, + dynamicComponent, + options: options || [], + }), [dynamicComponent, fieldSchema, options], ); return ( - + diff --git a/packages/plugins/@hera/plugin-core/src/client/components/filter-form/context.ts b/packages/core/client/src/schema-settings/filter-form/context.ts similarity index 74% rename from packages/plugins/@hera/plugin-core/src/client/components/filter-form/context.ts rename to packages/core/client/src/schema-settings/filter-form/context.ts index f92d8eef3..025dd938d 100644 --- a/packages/plugins/@hera/plugin-core/src/client/components/filter-form/context.ts +++ b/packages/core/client/src/schema-settings/filter-form/context.ts @@ -1,4 +1,4 @@ -import { createContext } from 'react'; +import { createContext, useContext } from 'react'; import { ObjectField, Schema } from '@tachybase/schema'; export interface FilterContextProps { @@ -8,8 +8,13 @@ export interface FilterContextProps { options?: any[]; disabled?: boolean; collectionName?: string; + customFilter?: boolean; } export const RemoveConditionContext = createContext(null); export const FilterContext = createContext(null); export const FilterLogicContext = createContext(null); + +export const useFilterContext = () => { + return useContext(FilterContext); +}; diff --git a/packages/plugins/@hera/plugin-core/src/client/hooks/useFilterFormCustomProps.tsx b/packages/core/client/src/schema-settings/hooks/useFilterFormCustomProps.tsx similarity index 100% rename from packages/plugins/@hera/plugin-core/src/client/hooks/useFilterFormCustomProps.tsx rename to packages/core/client/src/schema-settings/hooks/useFilterFormCustomProps.tsx diff --git a/packages/core/client/src/schema-settings/index.ts b/packages/core/client/src/schema-settings/index.ts index 5030aed16..79b5d0a40 100644 --- a/packages/core/client/src/schema-settings/index.ts +++ b/packages/core/client/src/schema-settings/index.ts @@ -9,6 +9,10 @@ export * from './SchemaSettingsDataScope'; export * from './SchemaSettingsDefaultValue'; export * from './SchemaSettingsDateFormat'; export * from './SchemaSettingsSortingRule'; +export * from './SchemaSettingCollection'; +export * from './SchemaSettingComponent'; +export * from './EditFormulaTitleField'; +export * from './EditCustomDefaultValue'; export { default as useParseDataScopeFilter } from './hooks/useParseDataScopeFilter'; diff --git a/packages/core/client/src/unsafe.tsx b/packages/core/client/src/unsafe.tsx deleted file mode 100644 index 0611e81a4..000000000 --- a/packages/core/client/src/unsafe.tsx +++ /dev/null @@ -1,58 +0,0 @@ -export { - removeGridFormItem, - useInheritsFormItemInitializerFields, - useFormItemInitializerFields, - useAssociatedFormItemInitializerFields, - useFilterInheritsFormItemInitializerFields, - useFilterFormItemInitializerFields, -} from './schema-initializer/utils'; -export { parseVariables } from './schema-component/common/utils/uitls'; -export { linkageAction } from './schema-component/antd/action/utils'; -export { useCollectionState } from './schema-settings/DataTemplates/hooks/useCollectionState'; -export { useSyncFromForm } from './schema-settings/DataTemplates/utils'; -export { ColumnFieldProvider } from './schema-component/antd/table-v2/components/ColumnFieldProvider'; -export { default as schema } from './schema-component/antd/association-field/schema'; -export { ReadPrettyInternalViewer } from './schema-component/antd/association-field/InternalViewer'; -export { useSetAriaLabelForPopover } from './schema-component/antd/action/hooks/useSetAriaLabelForPopover'; -export { InternalFileManager } from './schema-component/antd/association-field/FileManager'; -export { CreateRecordAction } from './schema-component/antd/association-field/components/CreateRecordAction'; -export { AssociationSelect } from './schema-component/antd/association-field/AssociationSelect'; -export { AssociationFieldProvider } from './schema-component/antd/association-field/AssociationFieldProvider'; -export { InternalCascadeSelect } from './schema-component/antd/association-field/InternalCascadeSelect'; -export { InternalNester } from './schema-component/antd/association-field/InternalNester'; -export { InternalPicker } from './schema-component/antd/association-field/InternalPicker'; -export { InternalSubTable } from './schema-component/antd/association-field/InternalSubTable'; -export { InternaPopoverNester } from './schema-component/antd/association-field/InternalPopoverNester'; -export { InternalCascader } from './schema-component/antd/association-field/InternalCascader'; -export { Nester } from './schema-component/antd/association-field/Nester'; -export { SubTable } from './schema-component/antd/association-field/SubTable'; -export { ReadPretty } from './schema-component/antd/association-field/ReadPretty'; -export { SubFormProvider } from './schema-component/antd/association-field/hooks'; -export { useAssociationFieldContext } from './schema-component/antd/association-field/hooks'; -export { FlagProvider } from './flag-provider'; -export { isVariable } from './variables/utils/isVariable'; -export { transformVariableValue } from './variables/utils/transformVariableValue'; -export { useVariables } from './variables'; -export { VariableInput, getShouldChange } from './schema-settings/VariableInput/VariableInput'; -export { useLocalVariables } from './variables'; -export { VariablesContext } from './variables/VariablesProvider'; -export { - EditComponent, - EditDescription, - EditOperator, - EditTitle, - EditTitleField, - EditTooltip, - EditValidationRules, -} from './schema-component/antd/form-item/SchemaSettingOptions'; -export type { DynamicComponentProps } from './schema-component/antd/filter/DynamicComponent'; -export { DetailsBlockProvider, useDetailsBlockContext } from './block-provider/DetailsBlockProvider'; -export { getInnermostKeyAndValue } from './schema-component/common/utils/uitls'; -export { default as useServiceOptions } from './schema-component/antd/association-field/hooks'; -export type { VariablesContextType, VariableOption } from './variables/types'; -export type { Option } from './schema-settings/VariableInput/type'; -export { useBlockCollection } from './schema-settings/VariableInput/hooks/useBlockCollection'; -export { useValues } from './schema-component/antd/filter/useValues'; -export { useVariableOptions } from './schema-settings/VariableInput/hooks/useVariableOptions'; -export { useContextAssociationFields } from './schema-settings/VariableInput/hooks/useContextAssociationFields'; -export { useRecordVariable } from './schema-settings/VariableInput/hooks/useRecordVariable'; diff --git a/packages/core/utils/src/common.ts b/packages/core/utils/src/common.ts index fb6141dcb..650c6077a 100644 --- a/packages/core/utils/src/common.ts +++ b/packages/core/utils/src/common.ts @@ -47,11 +47,13 @@ export const nextTick = (fn: () => void) => { }; export function fuzzysearch(needle: string, haystack: string): boolean { - const hlen = haystack.length; - const nlen = needle.length; - if (nlen > hlen) { + if (!needle || !haystack) { return false; } + + const hlen = haystack.length; + const nlen = needle.length; + if (nlen === hlen) { return needle === haystack; } diff --git a/packages/plugins/@hera/plugin-core/src/client/components/filter-form/VariableInput.tsx b/packages/plugins/@hera/plugin-core/src/client/components/filter-form/VariableInput.tsx deleted file mode 100644 index 05663d234..000000000 --- a/packages/plugins/@hera/plugin-core/src/client/components/filter-form/VariableInput.tsx +++ /dev/null @@ -1,399 +0,0 @@ -import React, { useCallback, useContext, useMemo } from 'react'; -import { - __UNSAFE__, - __UNSAFE__VariableInputOption, - __UNSAFE__VariableOption, - __UNSAFE__VariablesContextType, - CollectionFieldOptions, - isVariable, - useCollectionManager_deprecated, - useCompile, - useDateVariable, - useUserVariable, - useVariableScope, - Variable, -} from '@tachybase/client'; -// @ts-ignore -import { Form, Schema, SchemaOptionsContext, useField, useFieldSchema } from '@tachybase/schema'; - -import _ from 'lodash'; -import { useTranslation } from 'react-i18next'; - -import { FilterContext } from './context'; - -const { useBlockCollection, useValues, useVariableOptions, useContextAssociationFields, useRecordVariable } = - __UNSAFE__; - -interface GetShouldChangeProps { - collectionField: CollectionFieldOptions; - variables: __UNSAFE__VariablesContextType; - localVariables: __UNSAFE__VariableOption | __UNSAFE__VariableOption[]; - /** `useCollectionManager` 返回的 */ - getAllCollectionsInheritChain: (collectionName: string) => string[]; -} - -interface RenderSchemaComponentProps { - value: any; - onChange: (value: any) => void; -} - -type Props = { - value: any; - onChange: (value: any, optionPath?: any[]) => void; - renderSchemaComponent: (props: RenderSchemaComponentProps) => any; - schema?: any; - /** 消费变量值的字段 */ - targetFieldSchema?: Schema; - children?: any; - className?: string; - style?: React.CSSProperties; - collectionField: CollectionFieldOptions; - contextCollectionName?: string; - /**指定当前表单数据表 */ - currentFormCollectionName?: string; - /**指定当前对象数据表 */ - currentIterationCollectionName?: string; - /** - * 根据 `onChange` 的第一个参数,判断是否需要触发 `onChange` - * @param value `onChange` 的第一个参数 - * @returns 返回为 `true` 时,才会触发 `onChange` - */ - shouldChange?: (value: any, optionPath?: any[]) => Promise; - form?: Form; - /** - * 当前表单的记录,数据来自数据库 - */ - record?: Record; - /** - * 可以用该方法对内部的 scope 进行筛选和修改 - * @param scope - * @returns - */ - returnScope?: (scope: __UNSAFE__VariableInputOption[]) => any[]; - fields: any; -}; - -/** - * 注意:该组件存在以下问题: - * - 在选中选项的时候该组件不能触发重渲染 - * - 如果触发重渲染可能会导致无法展开子选项列表 - * @param props - * @returns - */ -export const VariableInput = (props: Props) => { - const { - value, - onChange, - renderSchemaComponent: RenderSchemaComponent, - style, - schema, - className, - contextCollectionName, - collectionField, - shouldChange, - form, - record, - returnScope = _.identity, - targetFieldSchema, - currentFormCollectionName, - currentIterationCollectionName, - fields, - } = props; - const { name: blockCollectionName } = useBlockCollection(); - const scope = useVariableScope(); - const { operator, schema: uiSchema = collectionField?.uiSchema } = useValues(); - - const variableOptions = useVariableOptions({ - collectionField, - form, - record, - operator, - uiSchema, - targetFieldSchema, - }); - const contextVariable = useContextAssociationFields({ schema, maxDepth: 2, contextCollectionName, collectionField }); - const { compatOldVariables } = useCompatOldVariables({ - collectionField, - uiSchema, - targetFieldSchema, - blockCollectionName, - }); - - if (contextCollectionName && variableOptions.every((item) => item.value !== contextVariable.value)) { - variableOptions.push(contextVariable); - } - - const handleChange = useCallback( - (value: any, optionPath: any[]) => { - if (!shouldChange) { - return onChange(value); - } - - // `shouldChange` 这个函数的运算量比较大,会导致展开变量列表时有明显的卡顿感,在这里加个延迟能有效解决这个问题 - setTimeout(async () => { - if (await shouldChange(value, optionPath)) { - onChange(value); - } - }); - }, - [onChange, shouldChange], - ); - const options = useVariableCustomOptions(fields); - return ( - - - - ); -}; - -/** - * 通过限制用户的选择,来防止用户选择错误的变量 - */ -export const getShouldChange = ({ - collectionField, - variables, - localVariables, - getAllCollectionsInheritChain, -}: GetShouldChangeProps) => { - const collectionsInheritChain = collectionField ? getAllCollectionsInheritChain(collectionField.target) : []; - - return async (value: any, optionPath: any[]) => { - if (_.isString(value) && value.includes('$nRole')) { - return true; - } - - if (!isVariable(value) || !variables || !collectionField) { - return true; - } - - // `json` 可以选择任意类型的变量,详见:https://tachybase.feishu.cn/docx/EmNEdEBOnoQohUx2UmBcqIQ5nyh#FPLfdSRDEoXR65xW0mBcdfL5n0c - if (collectionField.interface === 'json') { - return true; - } - - const lastOption = optionPath[optionPath.length - 1]; - - // 点击叶子节点时,必须更新 value - if (lastOption && _.isEmpty(lastOption.children) && !lastOption.loadChildren) { - return true; - } - - const collectionFieldOfVariable = await variables.getCollectionField(value, localVariables); - - if (!collectionField) { - return false; - } - - // `一对一` 和 `一对多` 的不能用于设置默认值,因为其具有唯一性 - if (['o2o', 'o2m', 'oho'].includes(collectionFieldOfVariable?.interface)) { - return false; - } - if (!collectionField.target && collectionFieldOfVariable?.target) { - return false; - } - if (collectionField.target && !collectionFieldOfVariable?.target) { - return false; - } - if ( - collectionField.target && - collectionFieldOfVariable?.target && - !collectionsInheritChain.includes(collectionFieldOfVariable?.target) - ) { - return false; - } - - return true; - }; -}; - -export interface FormatVariableScopeParam { - children: any[]; - disabled: boolean; - name: string; - title: string; -} - -export interface FormatVariableScopeReturn { - value: string; - key: string; - label: string; - disabled: boolean; - children?: any[]; -} - -/** - * 兼容老版本的变量 - * @param variables - */ -export function useCompatOldVariables(props: { - uiSchema: any; - collectionField: CollectionFieldOptions; - blockCollectionName: string; - noDisabled?: boolean; - targetFieldSchema?: Schema; -}) { - const { uiSchema, collectionField, noDisabled, targetFieldSchema, blockCollectionName } = props; - const { t } = useTranslation(); - const lowLevelUserVariable = useUserVariable({ - maxDepth: 1, - uiSchema: uiSchema, - collectionField, - noDisabled, - targetFieldSchema, - }); - const currentRecordVariable = useRecordVariable({ - schema: uiSchema, - collectionName: blockCollectionName, - collectionField, - noDisabled, - targetFieldSchema, - }); - - const compatOldVariables = useCallback( - (variables: __UNSAFE__VariableInputOption[], { value }) => { - if (!isVariable(value)) { - return variables; - } - - variables = _.cloneDeep(variables); - - const systemVariable: __UNSAFE__VariableInputOption = { - value: '$system', - key: '$system', - label: t('System variables'), - isLeaf: false, - children: [ - { - value: 'now', - key: 'now', - label: t('Current time'), - isLeaf: true, - depth: 1, - }, - ], - depth: 0, - }; - const currentTime = { - value: 'currentTime', - label: t('Current time'), - children: null, - }; - - if (value.includes('$system')) { - variables.push(systemVariable); - } - - if (value.includes(`${blockCollectionName}.`)) { - const variable = variables.find((item) => item.value === '$nForm' || item.value === '$nRecord'); - if (variable) { - variable.value = blockCollectionName; - } - } - - if (value.includes('$form')) { - const variable = variables.find((item) => item.value === '$nForm'); - if (variable) { - variable.value = '$form'; - } - } - - if (value.includes('currentUser')) { - const userVariable = variables.find((item) => item.value === '$user'); - if (userVariable) { - userVariable.value = 'currentUser'; - } else { - variables.unshift({ ...lowLevelUserVariable, value: 'currentUser' }); - } - } - - if (value.includes('currentRecord')) { - const formVariable = variables.find((item) => item.value === '$nRecord'); - if (formVariable) { - formVariable.value = 'currentRecord'; - } else { - variables.unshift({ ...currentRecordVariable, value: 'currentRecord' }); - } - } - - if (value.includes('currentTime')) { - variables.push(currentTime); - } - - if (value.includes('$date')) { - const formVariable = variables.find((item) => item.value === '$nDate'); - if (formVariable) { - formVariable.value = '$date'; - } - } - - return variables; - }, - [blockCollectionName], - ); - - return { compatOldVariables }; -} - -export const useVariableCustomOptions = (fields) => { - const field = useField(); - const { operator, schema } = field.data || {}; - const userVariable = useUserVariable({ - collectionField: { uiSchema: schema }, - uiSchema: schema, - }); - const dateVariable = useDateVariable({ operator, schema }); - const filterVariable = useFilterVariable(fields); - - const result = useMemo( - () => [userVariable, dateVariable, filterVariable].filter(Boolean), - [dateVariable, userVariable, filterVariable], - ); - - if (!operator || !schema) return []; - - return result; -}; - -export const useFilterVariable = (fields) => { - const { t } = useTranslation(); - const compile = useCompile(); - const { collections } = useCollectionManager_deprecated(); - const customFields = []; - for (const field in fields) { - customFields.push({ - key: field, - ...fields[field], - }); - } - - const options = customFields - .filter((value) => value?.props?.name.includes('custom.')) - .map((custom) => { - const value = custom?.props?.name.replace(/^custom\./, ''); - return { - key: custom?.props?.name, - value, - label: custom.title, - }; - }); - const result = useMemo( - () => ({ - label: t('Current filter'), - value: '$nFilter', - key: '$nFilter', - children: options, - }), - [options, t], - ); - - if (!options.length) return null; - - return result; -}; diff --git a/packages/plugins/@hera/plugin-core/src/client/features/extended-filter-form/index.ts b/packages/plugins/@hera/plugin-core/src/client/features/extended-filter-form/index.ts deleted file mode 100644 index 2093ca471..000000000 --- a/packages/plugins/@hera/plugin-core/src/client/features/extended-filter-form/index.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { Plugin, useCollection } from '@tachybase/client'; - -import { useFilterBlockActionProps } from '../../hooks/useFilterBlockActionProps'; -import { useFilterFormCustomProps } from '../../hooks/useFilterFormCustomProps'; -import { tval } from '../../locale'; -import { FilterFormItem, FilterFormItemCustom, FilterItemCustomDesigner } from '../../schema-initializer'; -import { - EditDefaultValue, - EditFormulaTitleField, - FilterVariableInput, - SetFilterScope, - useFormulaTitleVisible, - useSetFilterScopeVisible, -} from '../../schema-settings'; - -export class PluginExtendedFilterForm extends Plugin { - async load() { - this.app.addScopes({ - useFilterBlockActionProps, - useFilterFormCustomProps, - }); - this.app.addComponents({ - FilterFormItem, - FilterFormItemCustom, - FilterItemCustomDesigner, - FilterVariableInput, - }); - this.schemaSettingsManager.addItem('fieldSettings:component:Select', 'formulatitleField', { - Component: EditFormulaTitleField, - useVisible: useFormulaTitleVisible, - }); - this.schemaSettingsManager.addItem('fieldSettings:component:Select', 'editDefaultValue', { - Component: EditDefaultValue, - }); - const customItem = { - title: tval('Custom filter field'), - name: 'custom', - type: 'item', - Component: 'FilterFormItemCustom', - }; - this.app.schemaInitializerManager.addItem('filterForm:configureFields', 'custom-item-divider', { - type: 'divider', - }); - this.app.schemaInitializerManager.addItem('filterForm:configureFields', customItem.name, customItem); - - this.schemaSettingsManager.addItem('ActionSettings', 'Customize.setFilterScope', { - Component: SetFilterScope, - useVisible: useSetFilterScopeVisible, - useComponentProps() { - const collection = useCollection(); - return { - collectionName: collection.name, - }; - }, - }); - } -} diff --git a/packages/plugins/@hera/plugin-core/src/client/hooks/useFilterBlockActionProps.tsx b/packages/plugins/@hera/plugin-core/src/client/hooks/useFilterBlockActionProps.tsx deleted file mode 100644 index 73ae9d32e..000000000 --- a/packages/plugins/@hera/plugin-core/src/client/hooks/useFilterBlockActionProps.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import { - findFilterTargets, - mergeFilter, - transformToFilter, - useCollection_deprecated, - useCollectionManager_deprecated, - useFilterBlock, -} from '@tachybase/client'; -import { isEmpty, useField, useFieldSchema, useForm } from '@tachybase/schema'; - -import flat from 'flat'; - -import { hasDuplicateKeys } from '../utils'; - -export const removeNullCondition = (filter, fieldSchema?) => { - const filterSchema = fieldSchema ? fieldSchema['x-filter-rules'] : ''; - const filterSchemaItem = flat(filterSchema || ''); - const items = flat(filter || {}); - const values = {}; - const isCustomFilter = filterSchema?.$and?.length || filterSchema?.$or?.length; - const isFilterCustom = isCustomFilter ? hasDuplicateKeys(items, filterSchemaItem) : false; - if (!isFilterCustom) { - if (isCustomFilter) { - for (const filterKey in filterSchemaItem) { - const match = filterSchemaItem[filterKey]?.slice(11, -2); - const collection = match?.split('.')[0]; - const filterItems = Object.keys(items).filter((item) => item.includes(collection))[0]; - if (filterItems) { - filterSchemaItem[filterKey] = items[filterItems]; - } - } - for (const item in items) { - if (!item.includes('custom')) { - values[item] = items[item]; - } - } - for (const item in filterSchemaItem) { - if (filterSchemaItem[item].includes('$nFilter')) { - delete filterSchemaItem[item]; - } - } - const flatValue = flat.unflatten(values); - const flatFieldSchema = flat.unflatten(filterSchemaItem); - flatValue['$and'] = flatValue['$and']?.filter(Boolean); - flatFieldSchema['$and'] = flatFieldSchema['$and']?.filter(Boolean); - return { - $and: [flatValue, flatFieldSchema], - }; - } else { - for (const key in items) { - const value = items[key]; - if (!key.includes('custom') && value != null && !isEmpty(value)) { - values[key] = value; - } - } - return flat.unflatten(values); - } - } else { - return flat.unflatten(items); - } -}; - -export const useFilterBlockActionProps = () => { - const form = useForm(); - const actionField = useField(); - const fieldSchema = useFieldSchema(); - const { getDataBlocks } = useFilterBlock(); - const { name } = useCollection_deprecated(); - const { getCollectionJoinField } = useCollectionManager_deprecated(); - - actionField.data = actionField.data || {}; - - return { - async onClick() { - const { targets = [], uid } = findFilterTargets(fieldSchema); - - actionField.data.loading = true; - try { - // 收集 filter 的值 - await Promise.all( - getDataBlocks().map(async (block) => { - const target = targets.find((target) => target.uid === block.uid); - if (!target) return; - const param = block.service.params?.[0] || {}; - for (const key in form.values) { - if ( - (typeof form.values[key] === 'object' && - (JSON.stringify(form.values[key]) === '{}' || JSON.stringify(form.values[key]) === '[]')) || - !form.values[key] - ) { - delete form.values[key]; - } - } - // 保留原有的 filter - const storedFilter = block.service.params?.[1]?.filters || {}; - storedFilter[uid] = removeNullCondition( - transformToFilter(form.values, fieldSchema, getCollectionJoinField, name), - fieldSchema, - ); - if (block.defaultFilter) { - getDataBlocks().forEach((getblock) => { - if (getblock.uid !== block.uid && getblock.collection.name === block.collection.name) { - getblock['defaultFilter'] = block.defaultFilter; - } - }); - } - const mergedFilter = mergeFilter([ - ...Object.values(storedFilter).map((filter) => removeNullCondition(filter, fieldSchema)), - block.defaultFilter, - ]); - return block.doFilter( - { - ...param, - page: 1, - filter: mergedFilter, - }, - { filters: storedFilter }, - ); - }), - ); - actionField.data.loading = false; - } catch (error) { - console.error(error); - actionField.data.loading = false; - } - }, - }; -}; diff --git a/packages/plugins/@hera/plugin-core/src/client/index.tsx b/packages/plugins/@hera/plugin-core/src/client/index.tsx index 359c21106..47fd0a317 100644 --- a/packages/plugins/@hera/plugin-core/src/client/index.tsx +++ b/packages/plugins/@hera/plugin-core/src/client/index.tsx @@ -1,4 +1,4 @@ -import { EditTitleField, Plugin } from '@tachybase/client'; +import { Plugin, useFormulaTitleVisible } from '@tachybase/client'; import { CodeMirror } from '@tachybase/components'; import { autorun, isValid, useFieldSchema } from '@tachybase/schema'; @@ -18,10 +18,8 @@ import { PluginAssistant } from './features/assistant'; import { PluginGroupBlock } from './features/block-group'; import { PluginContextMenu } from './features/context-menu'; import { PluginCustomComponents } from './features/custom-components'; -import { PluginDemo } from './features/demo'; import { DepartmentsPlugin } from './features/departments'; import { EmbedPlugin } from './features/embed'; -import { PluginExtendedFilterForm } from './features/extended-filter-form'; import { PluginFieldAppends } from './features/field-appends'; import { PluginHeraVersion } from './features/hera-version'; import { PluginOutbound } from './features/outbound'; @@ -45,14 +43,7 @@ import { AutoComplete } from './schema-components'; import AssociationCascader from './schema-components/association-cascader/AssociationCascader'; import { CreateSubmitActionInitializer, SettingBlockInitializer } from './schema-initializer'; import { useCreateActionProps } from './schema-initializer/actions/hooks/useCreateActionProps'; -import { - EditFormulaTitleField, - EditTitle, - IsTablePageSize, - PageModeSetting, - useFormulaTitleVisible, - usePaginationVisible, -} from './schema-settings'; +import { EditTitle, IsTablePageSize, PageModeSetting, usePaginationVisible } from './schema-settings'; import { SchemaSettingsDatePickerType } from './schema-settings/SchemaSettingsDatePickerType'; import { SchemaSettingsDatePresets, @@ -77,7 +68,6 @@ export class PluginCoreClient extends Plugin { await this.app.pm.add(PluginContextMenu); await this.app.pm.add(PluginAssistant); await this.app.pm.add(PluginPDF); - await this.app.pm.add(PluginExtendedFilterForm); await this.app.pm.add(PluginOutbound); // await this.app.pm.add(PluginModeHighlight); await this.app.pm.add(PluginFieldAppends); @@ -108,10 +98,6 @@ export class PluginCoreClient extends Plugin { return v1 || v2; }, }); - this.schemaSettingsManager.addItem('FormItemSettings', 'formulatitleField', { - Component: EditFormulaTitleField, - useVisible: useFormulaTitleVisible, - }); this.schemaSettingsManager.addItem('FormItemSettings', 'isTablePageSize', { Component: IsTablePageSize, useVisible: usePaginationVisible, @@ -147,7 +133,6 @@ export class PluginCoreClient extends Plugin { CustomComponentStub, CustomField, EditTitle, - EditTitleField, Expression, PageLayout, SettingBlock: SettingBlockInitializer, diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/auto-complete/AutoComplete.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-components/auto-complete/AutoComplete.tsx index 368352493..bf4409972 100644 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/auto-complete/AutoComplete.tsx +++ b/packages/plugins/@hera/plugin-core/src/client/schema-components/auto-complete/AutoComplete.tsx @@ -10,7 +10,7 @@ import { fuzzysearch } from '../../utils'; export const AutoComplete = connect((props) => { const { fieldNames, params: fieldFilter } = props; const fieldSchema = useFieldSchema(); - const targetKey = fieldNames.value; + const targetKey = fieldNames?.value; const api = useAPIClient(); const [defultOptions, setDefultOptions] = useState([]); const [options, setOptions] = useState([]); diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-initializer/index.ts b/packages/plugins/@hera/plugin-core/src/client/schema-initializer/index.ts index 8b8ea1acd..c3d3cdfe7 100644 --- a/packages/plugins/@hera/plugin-core/src/client/schema-initializer/index.ts +++ b/packages/plugins/@hera/plugin-core/src/client/schema-initializer/index.ts @@ -1,3 +1,2 @@ -export * from './items/CustomFilterFormItemInitializer'; export * from './actions/CreateSubmitActionInitializer'; export * from './blocks/SettingBlockInitializer'; diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-settings/SchemaSettingsRemove.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-settings/SchemaSettingsRemove.tsx deleted file mode 100644 index 5cd75845e..000000000 --- a/packages/plugins/@hera/plugin-core/src/client/schema-settings/SchemaSettingsRemove.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import React, { FC } from 'react'; -import { SchemaSettingsItem, useFormActiveFields, useSchemaSettings } from '@tachybase/client'; -import { Field, ISchema, useField, useFieldSchema, useForm } from '@tachybase/schema'; - -import { App, ModalFuncProps } from 'antd'; - -import { useTranslation } from '../locale'; - -export interface SchemaSettingsRemoveProps { - confirm?: ModalFuncProps; - removeParentsIfNoChildren?: boolean; - breakRemoveOn?: ISchema | ((s: ISchema) => boolean); -} -export const SchemaSettingsRemove: FC = (props) => { - const { confirm, removeParentsIfNoChildren, breakRemoveOn } = props; - const { dn, template } = useSchemaSettings(); - const { t } = useTranslation(); - const field = useField(); - const fieldSchema = useFieldSchema(); - const form = useForm(); - const { modal } = App.useApp(); - const { removeActiveFieldName } = useFormActiveFields() || {}; - return ( - { - modal.confirm({ - title: t('Delete block'), - content: t('Are you sure you want to delete it?'), - ...confirm, - async onOk() { - const options = { - removeParentsIfNoChildren, - breakRemoveOn, - }; - const name = fieldSchema.name as string; - const fieldName = name.split('.')[1]; - const title = fieldSchema.title; - if (field?.required) { - field.required = false; - fieldSchema['required'] = false; - } - await dn.remove(null, options); - await confirm?.onOk?.(); - if (form.values['custom']) { - delete form.values['custom'][fieldName]; - } - for (const key in form.fields) { - if (key.includes(name) && form.fields[key].title === title) { - delete form.fields[key]; - } - } - removeActiveFieldName?.(fieldSchema.name as string); - if (field?.setInitialValue && field?.reset) { - field.setInitialValue(null); - field.reset(); - } - }, - }); - }} - > - {t('Delete')} - - ); -}; diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-settings/index.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-settings/index.tsx index a63eebd7c..0f1aecdcb 100644 --- a/packages/plugins/@hera/plugin-core/src/client/schema-settings/index.tsx +++ b/packages/plugins/@hera/plugin-core/src/client/schema-settings/index.tsx @@ -1,23 +1,14 @@ -import React, { useCallback, useEffect, useMemo } from 'react'; +import React, { useEffect, useMemo } from 'react'; import { getShouldChange, SchemaComponent, SchemaSettingsModalItem, - SchemaSettingsSelectItem, SchemaSettingsSwitchItem, useCollection_deprecated, - useCollectionManager, useCollectionManager_deprecated, - useCompile, useCurrentUserVariable, useDatetimeVariable, useDesignable, - useFormBlockContext, - useFormBlockType, - useLocalVariables, - useRecord, - useSchemaTemplateManager, - useVariables, VariableInput, VariableScopeProvider, } from '@tachybase/client'; @@ -26,227 +17,13 @@ import { Field, ISchema, useField, useFieldSchema } from '@tachybase/schema'; import { useMemoizedFn } from 'ahooks'; import _ from 'lodash'; -import { FormFilterScope } from '../components/filter-form/FormFilterScope'; import { useTranslation } from '../locale'; -import { useFieldComponents } from '../schema-initializer'; - -export const useFormulaTitleOptions = () => { - const compile = useCompile(); - const { getCollectionJoinField, getCollectionFields } = useCollectionManager_deprecated(); - const { getField } = useCollection_deprecated(); - const fieldSchema = useFieldSchema(); - const collectionManage = useCollectionManager_deprecated(); - const collectionManageField = collectionManage.collections.filter( - (value) => value.name === fieldSchema['x-decorator-props'], - )[0]; - const collectionField = getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']); - let fields = []; - if (collectionField) { - fields = getCollectionFields( - collectionField - ? collectionField.target - ? collectionField.target - : collectionField.collectionName - : fieldSchema['name'], - ); - } else if (collectionManageField) { - fields = collectionManageField['fields']; - } - const options = []; - fields?.forEach((field) => { - if (field.interface !== 'm2m') { - if (field.uiSchema) { - options.push({ - label: compile(field.uiSchema.title), - value: field.name, - children: - getCollectionFields(field.target) - ?.filter((subField) => subField.uiSchema) - .map((subField) => ({ - label: subField.uiSchema ? compile(subField.uiSchema.title) : '', - value: subField.name, - })) ?? [], - }); - } - } - }); - return options; -}; - -export const useFormulaTitleVisible = () => { - const fieldSchema = useFieldSchema(); - const options = useFormulaTitleOptions(); - // FIXME 这里现在只有当设置为 select,默认为 select 的时候看不到 - return ( - options.length > 0 && - (fieldSchema['x-component-props']?.mode === 'Select' || fieldSchema['x-component'] === 'CollectionField') && - fieldSchema['x-component-props']?.fieldNames?.value !== undefined - ); -}; - -export const EditFormulaTitleField = () => { - const { getCollectionJoinField, collections } = useCollectionManager_deprecated(); - const { getField } = useCollection_deprecated(); - const field = useField(); - const fieldSchema = useFieldSchema(); - const { t } = useTranslation(); - const { dn } = useDesignable(); - - let collectionField = getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']); - collectionField = collectionField - ? collectionField - : collections.find((value) => value.name === fieldSchema['x-decorator-props'])?.fields; - const options = useFormulaTitleOptions(); - const editTitle = async (formula) => { - const schema = { - ['x-uid']: fieldSchema['x-uid'], - }; - const fieldNames = { - ...collectionField?.uiSchema?.['x-component-props']?.['fieldNames'], - ...field.componentProps.fieldNames, - formula, - }; - fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {}; - fieldSchema['x-component-props']['fieldNames'] = fieldNames; - schema['x-component-props'] = fieldSchema['x-component-props']; - field.componentProps.fieldNames = fieldSchema['x-component-props'].fieldNames; - dn.emit('patch', { - schema, - }); - dn.refresh(); - }; - - return ( - { - if (formula) { - editTitle(formula); - } - }} - /> - ); -}; - -export const EditDefaultValue = () => { - const { t } = useTranslation(); - const { dn } = useDesignable(); - const field = useField(); - const fieldSchema = useFieldSchema(); - const collectionName = fieldSchema['collectionName']; - const title = fieldSchema.title; - return ( - { - field.setValue(value); - fieldSchema['default'] = value; - fieldSchema['x-component-props']['defaultValue'] = value; - dn.emit('patch', { - schema: { - 'x-uid': fieldSchema['x-uid'], - ['x-component-props']: fieldSchema['x-component-props'], - default: fieldSchema.default, - }, - }); - dn.refresh(); - }} - /> - ); -}; - -export const FilterVariableInput: React.FC = (props) => { - const { value, onChange, fieldSchema } = props; - const { currentUserSettings } = useCurrentUserVariable({ - collectionField: { uiSchema: fieldSchema }, - uiSchema: fieldSchema, - }); - const { datetimeSettings } = useDatetimeVariable({ - operator: fieldSchema['x-component-props']?.['filter-operator'], - schema: fieldSchema, - noDisabled: true, - }); - const options = useMemo( - () => [currentUserSettings, datetimeSettings].filter(Boolean), - [datetimeSettings, currentUserSettings], - ); - const schema = { - ...fieldSchema, - 'x-component': fieldSchema['x-component'] || 'Input', - 'x-decorator': '', - title: '', - name: 'value', - }; - const componentProps = fieldSchema['x-component-props'] || {}; - const handleChange = useMemoizedFn(onChange); - useEffect(() => { - if (fieldSchema.default) { - handleChange({ value: fieldSchema.default }); - } - }, [fieldSchema.default, handleChange]); - return ( - - } - fieldNames={{}} - value={value?.value} - scope={options} - onChange={(v: any) => { - onChange({ value: v }); - }} - shouldChange={getShouldChange({} as any)} - /> - - ); -}; export const usePaginationVisible = () => { const fieldSchema = useFieldSchema(); return fieldSchema['x-component-props']?.mode === 'SubTable'; }; -export const useSetFilterScopeVisible = () => { - const fieldSchema = useFieldSchema(); - return ( - fieldSchema['x-component-props']?.useProps === '{{ useFilterBlockActionProps }}' || - fieldSchema['x-use-component-props'] === 'useFilterBlockActionProps' - ); -}; - export const EditTitle = () => { const { getCollectionJoinField } = useCollectionManager_deprecated(); const { getField } = useCollection_deprecated(); @@ -294,64 +71,6 @@ export const EditTitle = () => { ); }; -export const EditTitleField = () => { - const { getCollectionFields, getCollectionJoinField } = useCollectionManager_deprecated(); - const { getField } = useCollection_deprecated(); - const field = useField(); - const fieldSchema = useFieldSchema(); - const { t } = useTranslation(); - const { dn } = useDesignable(); - const compile = useCompile(); - const collectionManage = useCollectionManager_deprecated(); - const isCustomFilterItem = ((fieldSchema?.name as string) ?? '').startsWith('custom.'); - const collectionManageField = isCustomFilterItem - ? collectionManage.collections.filter((value) => value.name === fieldSchema['x-decorator-props'])[0] - : {}; - const collectionField = getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']); - let targetFields = []; - if (collectionField) { - targetFields = collectionField?.target - ? getCollectionFields(collectionField?.target) - : getCollectionFields(collectionField?.targetCollection) ?? []; - } else if (collectionManageField) { - targetFields = collectionManageField['fields']; - } - const options = targetFields - .filter((field) => !field?.target && field.type !== 'boolean') - .map((field) => ({ - value: field?.name, - label: compile(field?.uiSchema?.title) || field?.name, - })); - - return options.length > 0 && (fieldSchema['x-component'] === 'CollectionField' || isCustomFilterItem) ? ( - { - const schema = { - ['x-uid']: fieldSchema['x-uid'], - }; - const fieldNames = { - ...collectionField?.uiSchema?.['x-component-props']?.['fieldNames'], - ...field.componentProps.fieldNames, - label, - value: label, - }; - fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {}; - fieldSchema['x-component-props']['fieldNames'] = fieldNames; - schema['x-component-props'] = fieldSchema['x-component-props']; - field.componentProps.fieldNames = fieldSchema['x-component-props'].fieldNames; - dn.emit('patch', { - schema, - }); - dn.refresh(); - }} - /> - ) : null; -}; - export const IsTablePageSize = () => { const { dn } = useDesignable(); const fieldSchema = useFieldSchema(); @@ -382,141 +101,6 @@ export const IsTablePageSize = () => { ); }; -const findGridSchema = (fieldSchema) => { - return fieldSchema.reduceProperties((buf, s) => { - if (s['x-component'] === 'FormV2') { - const f = s.reduceProperties((buf, s) => { - if (s['x-component'] === 'Grid' || s['x-component'] === 'BlockTemplate') { - return s; - } - return buf; - }, null); - if (f) { - return f; - } - } - return buf; - }, null); -}; - -export const useCollectionFilterOptions = (collectionName: string) => { - const { getCollectionFields, getInterface } = useCollectionManager_deprecated(); - const fields = getCollectionFields(collectionName); - const field2option = (field, depth) => { - if (!field.interface) { - return; - } - const fieldInterface = getInterface(field.interface); - if (!fieldInterface?.filterable) { - return; - } - const { nested, children, operators } = fieldInterface.filterable; - const option = { - name: field.name, - title: field?.uiSchema?.title || field.name, - schema: field?.uiSchema, - operators: - operators?.filter?.((operator) => { - return !operator?.visible || operator.visible(field); - }) || [], - interface: field.interface, - }; - if (field.target && depth > 2) { - return; - } - if (depth > 2) { - return option; - } - if (children?.length) { - option['children'] = children; - } - if (nested) { - const targetFields = getCollectionFields(field.target); - const options = getOptions(targetFields, depth + 1).filter(Boolean); - option['children'] = option['children'] || []; - option['children'].push(...options); - } - return option; - }; - const getOptions = (fields, depth) => { - const options = []; - fields.forEach((field) => { - const option = field2option(field, depth); - if (option) { - options.push(option); - } - }); - return options; - }; - const options = getOptions(fields, 1); - return options; -}; - -export const SetFilterScope = (props) => { - const { t } = useTranslation(); - const fieldSchema = useFieldSchema(); - const field = useField(); - const fields = field.form.fields; - const { collectionName } = props; - const gridSchema = findGridSchema(fieldSchema) || fieldSchema; - const { form } = useFormBlockContext(); - const type = props?.type || ['Action', 'Action.Link'].includes(fieldSchema['x-component']) ? 'button' : 'field'; - const variables = useVariables(); - const localVariables = useLocalVariables(); - const record = useRecord(); - const { type: formBlockType } = useFormBlockType(); - const schema = useMemo( - () => ({ - type: 'object', - title: t('Custom filter'), - properties: { - fieldReaction: { - 'x-component': FormFilterScope, - 'x-component-props': { - useProps: () => { - const options = useCollectionFilterOptions(collectionName); - return { - options, - defaultValues: gridSchema?.['x-filter-rules'] || fieldSchema?.['x-filter-rules'], - type, - collectionName, - form, - variables, - localVariables, - record, - formBlockType, - fields, - }; - }, - }, - }, - }, - }), - [], - ); - const { getTemplateById } = useSchemaTemplateManager(); - const { dn } = useDesignable(); - const onSubmit = useCallback( - (v) => { - const rules = v.fieldReaction.condition; - const templateId = gridSchema['x-component'] === 'BlockTemplate' && gridSchema['x-component-props'].templateId; - const uid = (templateId && getTemplateById(templateId).uid) || gridSchema['x-uid']; - const schema = { - ['x-uid']: uid, - }; - - gridSchema['x-filter-rules'] = rules; - schema['x-filter-rules'] = rules; - dn.emit('patch', { - schema, - }); - dn.refresh(); - }, - [dn, getTemplateById, gridSchema], - ); - return ; -}; - // 添加跳转页面选项 export const PageModeSetting = () => { const { dn } = useDesignable(); @@ -540,74 +124,3 @@ export const PageModeSetting = () => { /> ); }; - -export const SchemaSettingComponent = () => { - const fieldSchema = useFieldSchema(); - const field = useField(); - const { options } = useFieldComponents(); - const { dn } = useDesignable(); - return ( - { - field.component = mode; - fieldSchema['x-component'] = mode; - void dn.emit('patch', { - schema: { - ['x-uid']: fieldSchema['x-uid'], - ['x-component']: fieldSchema['x-component'], - }, - }); - dn.refresh(); - }} - /> - ); -}; - -export const SchemaSettingCollection = () => { - const fieldSchema = useFieldSchema(); - const collections = useCollectionManager(); - const field = useField(); - const options = collections?.dataSource['options']?.collections.map((value) => { - return { - label: value.title, - value: value.name, - }; - }); - const cm = useCollectionManager(); - const { dn } = useDesignable(); - return ( - { - field.setValue(''); - const titleField = cm.getCollection(name).titleField; - fieldSchema['collectionName'] = name; - fieldSchema.default = ''; - fieldSchema['x-component-props'] = { - ...fieldSchema['x-component-props'], - fieldNames: { - label: titleField, - value: titleField, - }, - defaultValue: '', - }; - void dn.emit('patch', { - schema: { - ['x-uid']: fieldSchema['x-uid'], - collectionName: fieldSchema['collectionName'], - ['x-component-props']: fieldSchema['x-component-props'], - default: '', - }, - }); - dn.refresh(); - }} - /> - ); -};