diff --git a/packages/core/client/src/block-provider/hooks/index.ts b/packages/core/client/src/block-provider/hooks/index.ts index 78b2c764e..f98951c68 100644 --- a/packages/core/client/src/block-provider/hooks/index.ts +++ b/packages/core/client/src/block-provider/hooks/index.ts @@ -423,14 +423,13 @@ export const useFilterBlockActionProps = () => { customValues: {}, customFilter: {}, }; - - if (Object.keys(form.values)?.includes('custom')) { + if (Object.keys(form.values)?.includes('__custom')) { const values = { ...form.values }; - delete values['custom']; + 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]; + 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); diff --git a/packages/core/client/src/common/useFieldComponentName.tsx b/packages/core/client/src/common/useFieldComponentName.tsx index 9e255528e..9bb39f254 100644 --- a/packages/core/client/src/common/useFieldComponentName.tsx +++ b/packages/core/client/src/common/useFieldComponentName.tsx @@ -25,5 +25,5 @@ export function useFieldComponentName(): string { field?.componentProps?.['mode'] || (isFileField ? 'FileManager' : '') || collectionField?.uiSchema?.['x-component']; - return map[fieldComponentName] || fieldComponentName; + return map[fieldComponentName] || fieldComponentName || fieldSchema['x-component-props']?.['component']; } 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 index 7148f2870..e8bcbeff1 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/form/FilterItemCustomSettings.tsx +++ b/packages/core/client/src/modules/blocks/data-blocks/form/FilterItemCustomSettings.tsx @@ -12,7 +12,6 @@ import { FilterCustomVariableInput, SchemaSettingCollection, SchemaSettingComponent, - SchemaSettingsCustomRemove, SchemaSettingsDataScope, } from '../../../../schema-settings'; @@ -111,7 +110,7 @@ export const FilterItemCustomSettings = new SchemaSettings({ const field = useField(); const { dn } = useDesignable(); const fieldName = fieldSchema['name'] as string; - const name = fieldName.includes('custom') ? fieldSchema['collectionName'] : fieldName; + const name = fieldName.includes('__custom') ? fieldSchema['collectionName'] : fieldName; return { collectionName: name, defaultFilter: fieldSchema?.['x-component-props']?.params?.filter || {}, @@ -133,8 +132,7 @@ export const FilterItemCustomSettings = new SchemaSettings({ }, useVisible() { const fieldSchema = useFieldSchema(); - const component = fieldSchema['x-component']; - return component !== 'Input'; + return fieldSchema?.['collectionName']; }, }, { @@ -152,7 +150,8 @@ export const FilterItemCustomSettings = new SchemaSettings({ useVisible() { const fieldSchema = useFieldSchema(); const component = fieldSchema['x-component']; - return component === 'Select' || component === 'AutoComplete'; + const items = ['Select', 'AutoComplete', 'Radio.Group', 'Checkbox.Group']; + return items.includes(component); }, }, { @@ -167,7 +166,7 @@ export const FilterItemCustomSettings = new SchemaSettings({ const { dn } = useDesignable(); const compile = useCompile(); const collectionManage = useCollectionManager_deprecated(); - const isCustomFilterItem = ((fieldSchema?.name as string) ?? '').startsWith('custom.'); + const isCustomFilterItem = ((fieldSchema?.name as string) ?? '').startsWith('__custom.'); const collectionManageField = isCustomFilterItem ? collectionManage.collections.filter((value) => value.name === fieldSchema['x-decorator-props'])[0] : {}; 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 0395b7605..5fcd46356 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 @@ -15,7 +15,7 @@ export const filterFormItemInitializers_deprecated = new CompatibleSchemaInitial title: '{{t("Configure fields")}}', items: [ { - type: 'itemGroup', + type: 'subMenu', name: 'displayFields', title: '{{t("Display fields")}}', useChildren: useFilterFormItemInitializerFields, @@ -54,7 +54,7 @@ export const filterFormItemInitializers = new CompatibleSchemaInitializer( title: '{{t("Configure fields")}}', items: [ { - type: 'itemGroup', + type: 'subMenu', name: 'displayFields', title: '{{t("Display fields")}}', useChildren: useFilterFormItemInitializerFields, diff --git a/packages/core/client/src/modules/fields/component/Radio/radioComponentFieldSettings.tsx b/packages/core/client/src/modules/fields/component/Radio/radioComponentFieldSettings.tsx new file mode 100644 index 000000000..03ddf7746 --- /dev/null +++ b/packages/core/client/src/modules/fields/component/Radio/radioComponentFieldSettings.tsx @@ -0,0 +1,49 @@ +import { Field, useField, useFieldSchema } from '@tachybase/schema'; + +import { useTranslation } from 'react-i18next'; + +import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings'; +import { useDesignable } from '../../../../schema-component'; +import { useColumnSchema } from '../../../../schema-component/antd/table-v2/Table.Column.Decorator'; + +export const radioComponentFieldSettings = new SchemaSettings({ + name: 'fieldSettings:component:Radio group', + items: [ + { + name: 'fieldComponent', + type: 'select', + useComponentProps() { + const { t } = useTranslation(); + const field = useField(); + const { fieldSchema: tableColumnSchema } = useColumnSchema(); + const schema = useFieldSchema(); + const fieldSchema = tableColumnSchema || schema; + const fieldModeOptions = [ + { label: t('Checkbox'), value: 'Checkbox' }, + { label: t('Radio group'), value: 'Radio group' }, + ]; + const { dn } = useDesignable(); + return { + title: t('Field component'), + options: fieldModeOptions, + value: fieldSchema['x-component-props']?.mode || 'Radio group', + onChange(mode) { + const schema = { + ['x-uid']: fieldSchema['x-uid'], + }; + fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {}; + fieldSchema['x-component-props']['mode'] = mode; + schema['x-component-props'] = fieldSchema['x-component-props']; + field.componentProps = field.componentProps || {}; + field.componentProps.mode = mode; + + void dn.emit('patch', { + schema, + }); + dn.refresh(); + }, + }; + }, + }, + ], +}); 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 0b7d68662..e45653bd9 100644 --- a/packages/core/client/src/schema-component/antd/filter/useFilterActionProps.ts +++ b/packages/core/client/src/schema-component/antd/filter/useFilterActionProps.ts @@ -162,19 +162,22 @@ export const getCustomCondition = (filter, fieldSchema, customFlat = flat) => { 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]; + const filterItems = Object.keys(items).filter((item) => item.includes(collection)); + if (filterItems.length > 1) { + filterSchemaItem[filterKey] = filterItems.map((key) => items[key]); + } else { + filterSchemaItem[filterKey] = items[filterItems[0]]; } } for (const item in filterSchemaItem) { - if (filterSchemaItem[item].includes('$nFilter')) { + if (!filterSchemaItem[item] || 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({}); 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 831920577..459d4a070 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 @@ -508,36 +508,54 @@ export const EditOperator = () => { ['x-filter-operators']: storedOperators, }; let componentProps = {}; - + const isCustom = (fieldSchema.name as string).includes('__custom'); // 根据操作符的配置,设置组件的属性 if (operator?.schema?.['x-component']) { - _.set(fieldSchema, 'x-component-props.component', operator.schema['x-component']); - _.set(field, 'componentProps.component', operator.schema['x-component']); + _.set(fieldSchema, isCustom ? 'x-component' : 'x-component-props.component', operator.schema['x-component']); + _.set(field, isCustom ? 'component' : 'componentProps.component', operator.schema['x-component']); field.reset(); componentProps = { component: operator.schema['x-component'], - ...operator.schema['x-component-props'], + ...operator.schema?.['x-component-props'], }; - dn.emit('patch', { + const dnSchema = { schema: { ['x-uid']: fieldSchema['x-uid'], ['x-component-props']: componentProps, }, - }); + }; + if (isCustom) { + dnSchema.schema['x-component'] = operator.schema?.['x-component']; + dnSchema.schema['x-component-props']['component'] = fieldSchema['x-component-props']['component']; + } + dn.emit('patch', dnSchema); } else if (fieldSchema['x-component-props']?.component) { - _.set(fieldSchema, 'x-component-props.component', null); - _.set(field, 'componentProps.component', null); + _.set( + fieldSchema, + isCustom ? 'x-component' : 'x-component-props.component', + isCustom ? fieldSchema['x-component-props']?.component : null, + ); + _.set( + field, + isCustom ? 'component' : 'componentProps.component', + isCustom ? fieldSchema['x-component-props']?.component : null, + ); + field.reset(); componentProps = { - component: null, - ...operator.schema['x-component-props'], + component: isCustom ? fieldSchema['x-component-props']?.component : null, + ...operator.schema?.['x-component-props'], }; - dn.emit('patch', { + const dnSchema = { schema: { ['x-uid']: fieldSchema['x-uid'], ['x-component-props']: componentProps, }, - }); + }; + if (isCustom) { + dnSchema.schema['x-component'] = fieldSchema['x-component-props']?.component; + } + dn.emit('patch', dnSchema); } field.componentProps = componentProps; @@ -559,7 +577,7 @@ export const EditTitleField = () => { const { dn } = useDesignable(); const compile = useCompile(); const collectionManage = useCollectionManager_deprecated(); - const isCustomFilterItem = ((fieldSchema?.name as string) ?? '').startsWith('custom.'); + const isCustomFilterItem = ((fieldSchema?.name as string) ?? '').startsWith('__custom.'); const collectionManageField = isCustomFilterItem ? collectionManage.collections.filter((value) => value.name === fieldSchema['x-decorator-props'])[0] : {}; diff --git a/packages/core/client/src/schema-component/antd/select/FormulaSelect.tsx b/packages/core/client/src/schema-component/antd/select/FormulaSelect.tsx index 790759884..f262fe56d 100644 --- a/packages/core/client/src/schema-component/antd/select/FormulaSelect.tsx +++ b/packages/core/client/src/schema-component/antd/select/FormulaSelect.tsx @@ -27,9 +27,9 @@ const ObjectSelect = (props: Props) => { const form = useForm(); useEffect(() => { - if (collectionName && fieldSchema['name'].toString().includes('custom') && defaultValue) { + if (collectionName && fieldSchema['name'].toString().includes('__custom') && defaultValue) { const name = fieldSchema['name'].toString().split('.')[1]; - form.values['custom'][name] = defaultValue?.[fieldNames.value]; + form.values['__custom'][name] = defaultValue?.[fieldNames.value]; } }, []); const toValue = (v: any) => { @@ -94,9 +94,9 @@ const ObjectSelect = (props: Props) => { } else { onChange?.(current.shift() || null); } - if (collectionName && fieldSchema['name'].toString().includes('custom')) { + if (collectionName && fieldSchema['name'].toString().includes('__custom')) { const name = fieldSchema['name'].toString().split('.')[1]; - form.values['custom'][name] = changed?.['value']; + form.values['__custom'][name] = changed?.['value']; } }} mode={mode} diff --git a/packages/core/client/src/schema-initializer/items/CustomFilterFormItemInitializer.tsx b/packages/core/client/src/schema-initializer/items/CustomFilterFormItemInitializer.tsx index 38e0e0b8e..53b1c42f4 100644 --- a/packages/core/client/src/schema-initializer/items/CustomFilterFormItemInitializer.tsx +++ b/packages/core/client/src/schema-initializer/items/CustomFilterFormItemInitializer.tsx @@ -43,7 +43,6 @@ import { GeneralSchemaDesigner, SchemaSettingCollection, SchemaSettingComponent, - SchemaSettingsCustomRemove, SchemaSettingsDataScope, SchemaSettingsDivider, SchemaSettingsRemove, @@ -58,20 +57,6 @@ const FieldComponentProps: React.FC = observer( const schema = { type: 'object', properties: { - mode: { - type: 'string', - enum: [ - { - label: '{{ t("Single select") }}', - value: '', - }, - ], - 'x-decorator': 'FormItem', - 'x-component': 'Radio.Group', - 'x-component-props': { - defaultValue: '', - }, - }, options: { title: '{{t("Options")}}', type: 'array', @@ -143,7 +128,9 @@ export const useFieldComponents = () => { { label: t('AutoComplete'), value: 'AutoComplete' }, { label: t('Select'), value: 'Select' }, { label: t('AssociationCascader'), value: 'AssociationCascader' }, - // { label: t('CustomSelect***'), value: 'CustomSelect' }, + { label: t('DatePicker'), value: 'DatePicker' }, + { label: t('Radio group'), value: 'Radio.Group' }, + { label: t('Checkbox group'), value: 'Checkbox.Group' }, ]; return { options, @@ -249,6 +236,16 @@ export const FilterCustomItemInitializer: React.FC<{ 'x-decorator': 'FormItem', 'x-component': 'FieldComponentProps', 'x-visible': false, + 'x-reactions': [ + { + dependencies: ['component'], + fulfill: { + schema: { + 'x-visible': "{{$deps[0] === 'Radio.Group' || $deps[0]==='Checkbox.Group'}}", + }, + }, + }, + ], }, }, }} @@ -286,35 +283,40 @@ export const FilterCustomItemInitializer: React.FC<{ }); }, }); - const { title, component, collection, associationField } = values; + const { title, component, collection, associationField, props } = values; const defaultSchema = getInterface(component)?.default?.uiSchema || {}; const titleField = cm.getCollection(collection)?.titleField; const name = uid(); - insert( - gridRowColWrap({ - ...defaultSchema, - type: 'string', - title: title, - name: 'custom.' + name, - required: false, - 'x-component': component, - 'x-toolbar': 'FormItemSchemaToolbar', - 'x-settings': 'fieldSettings:FilterFormCustomSettings', - 'x-decorator': 'FilterFormItem', - 'x-decorator-props': collection, - 'x-component-props': { - ...(defaultSchema['x-component-props'] || {}), - fieldNames: { - label: titleField, - value: titleField, - }, - associationField, - collection, - objectValue: true, + const schema = { + ...defaultSchema, + type: 'string', + title: title, + name: '__custom.' + name, + required: false, + 'x-component': component, + 'x-toolbar': 'FormItemSchemaToolbar', + 'x-settings': 'fieldSettings:FilterFormCustomSettings', + 'x-decorator': 'FilterFormItem', + 'x-decorator-props': collection, + 'x-component-props': { + ...(defaultSchema['x-component-props'] || {}), + fieldNames: { + label: titleField, + value: titleField, }, - collectionName: collection, - }), - ); + associationField, + collection, + objectValue: true, + component: component, + ...props, + }, + collectionName: collection, + }; + if (component === 'DatePicker') { + schema['x-settings'] = 'fieldSettings:FilterFormItem'; + schema['x-designer-props'] = { interface: 'datetime' }; + } + insert(gridRowColWrap(schema)); // eslint-disable-next-line react-hooks/exhaustive-deps }, [theme]); @@ -330,7 +332,7 @@ export const FilterItemCustomDesigner: React.FC = () => { const { t } = useTranslation(); const fieldSchema = useFieldSchema(); const fieldName = fieldSchema['name'] as string; - const name = fieldName.includes('custom') ? fieldSchema['collectionName'] : fieldName; + const name = fieldName.includes('__custom') ? fieldSchema['collectionName'] : fieldName; const { form } = useFormBlockContext(); const field = useField(); const { dn } = useDesignable(); diff --git a/packages/core/client/src/schema-settings/SchemaSettingComponent.tsx b/packages/core/client/src/schema-settings/SchemaSettingComponent.tsx index e078d5955..eecd6d4fb 100644 --- a/packages/core/client/src/schema-settings/SchemaSettingComponent.tsx +++ b/packages/core/client/src/schema-settings/SchemaSettingComponent.tsx @@ -8,7 +8,19 @@ import { SchemaSettingsSelectItem } from './SchemaSettings'; export const SchemaSettingComponent = () => { const fieldSchema = useFieldSchema(); const field = useField(); - const { options } = useFieldComponents(); + const checkedItems = ['Radio.Group', 'Checkbox.Group']; + const component = fieldSchema['x-component']; + const options = useFieldComponents() + .options.map((item) => { + if (checkedItems.includes(component)) { + return checkedItems.includes(item.value) ? item : null; + } else { + return checkedItems.includes(item.value) || item.value === 'DatePicker' || item.value === 'AssociationCascader' + ? null + : item; + } + }) + .filter(Boolean); const { dn } = useDesignable(); return ( { }); } const options = customFields - .filter((value) => value?.props?.name.includes('custom.')) + .filter((value) => value?.props?.name.includes('__custom.')) .map((custom) => { if (activeFields.includes(custom?.props?.name)) { - const value = custom?.props?.name.replace(/^custom\./, ''); + const value = custom?.props?.name.replace(/^__custom\./, ''); return { key: custom?.props?.name, value, diff --git a/packages/plugins/@tachybase/plugin-ui-schema-storage/src/server/migrations/2024082000001-update-uiSchemas_customDesigner.ts b/packages/plugins/@tachybase/plugin-ui-schema-storage/src/server/migrations/2024082000001-update-uiSchemas_customDesigner.ts new file mode 100644 index 000000000..a879d6a19 --- /dev/null +++ b/packages/plugins/@tachybase/plugin-ui-schema-storage/src/server/migrations/2024082000001-update-uiSchemas_customDesigner.ts @@ -0,0 +1,15 @@ +import { Migration } from '../../../../../../core/server/src/migration'; + +export default class extends Migration { + on = 'beforeLoad'; // 'beforeLoad' or 'afterLoad' + appVersion = '<0.21.84'; + async up() { + const result: any = await this.app.db.sequelize.query(` + UPDATE public."uiSchemas" + SET name = REPLACE(name, 'custom.', '__custom.') + WHERE name LIKE 'custom.%'; + `); + const count = result[1].rowCount || 0; + console.log('共更新数据:' + count + '条'); + } +}