feat: 新增自定义筛选组件,调整筛选字段内容 (#1468)

Co-authored-by: sealday <sealday@gmail.com>
Reviewed-on: daoyoucloud/tachybase#1468
Co-authored-by: wjh <wwwjh0710@163.com>
Co-committed-by: wjh <wwwjh0710@163.com>
This commit is contained in:
wjh 2024-08-21 05:57:09 +08:00 committed by sealday
parent 7850cb3324
commit f6ffc487c2
13 changed files with 180 additions and 81 deletions

View File

@ -423,14 +423,13 @@ export const useFilterBlockActionProps = () => {
customValues: {}, customValues: {},
customFilter: {}, customFilter: {},
}; };
if (Object.keys(form.values)?.includes('__custom')) {
if (Object.keys(form.values)?.includes('custom')) {
const values = { ...form.values }; const values = { ...form.values };
delete values['custom']; delete values['__custom'];
filter.formValues = { ...values }; filter.formValues = { ...values };
for (const key in form.values['custom']) { for (const key in form.values['__custom']) {
if (form.values['custom'][key]) { if (form.values['__custom'][key]) {
filter.customValues[key] = form.values['custom'][key]; filter.customValues[key] = form.values['__custom'][key];
} }
} }
filter.customFilter = getCustomCondition(filter.customValues, fieldSchema); filter.customFilter = getCustomCondition(filter.customValues, fieldSchema);

View File

@ -25,5 +25,5 @@ export function useFieldComponentName(): string {
field?.componentProps?.['mode'] || field?.componentProps?.['mode'] ||
(isFileField ? 'FileManager' : '') || (isFileField ? 'FileManager' : '') ||
collectionField?.uiSchema?.['x-component']; collectionField?.uiSchema?.['x-component'];
return map[fieldComponentName] || fieldComponentName; return map[fieldComponentName] || fieldComponentName || fieldSchema['x-component-props']?.['component'];
} }

View File

@ -12,7 +12,6 @@ import {
FilterCustomVariableInput, FilterCustomVariableInput,
SchemaSettingCollection, SchemaSettingCollection,
SchemaSettingComponent, SchemaSettingComponent,
SchemaSettingsCustomRemove,
SchemaSettingsDataScope, SchemaSettingsDataScope,
} from '../../../../schema-settings'; } from '../../../../schema-settings';
@ -111,7 +110,7 @@ export const FilterItemCustomSettings = new SchemaSettings({
const field = useField(); const field = useField();
const { dn } = useDesignable(); const { dn } = useDesignable();
const fieldName = fieldSchema['name'] as string; const fieldName = fieldSchema['name'] as string;
const name = fieldName.includes('custom') ? fieldSchema['collectionName'] : fieldName; const name = fieldName.includes('__custom') ? fieldSchema['collectionName'] : fieldName;
return { return {
collectionName: name, collectionName: name,
defaultFilter: fieldSchema?.['x-component-props']?.params?.filter || {}, defaultFilter: fieldSchema?.['x-component-props']?.params?.filter || {},
@ -133,8 +132,7 @@ export const FilterItemCustomSettings = new SchemaSettings({
}, },
useVisible() { useVisible() {
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const component = fieldSchema['x-component']; return fieldSchema?.['collectionName'];
return component !== 'Input';
}, },
}, },
{ {
@ -152,7 +150,8 @@ export const FilterItemCustomSettings = new SchemaSettings({
useVisible() { useVisible() {
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const component = fieldSchema['x-component']; 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 { dn } = useDesignable();
const compile = useCompile(); const compile = useCompile();
const collectionManage = useCollectionManager_deprecated(); const collectionManage = useCollectionManager_deprecated();
const isCustomFilterItem = ((fieldSchema?.name as string) ?? '').startsWith('custom.'); const isCustomFilterItem = ((fieldSchema?.name as string) ?? '').startsWith('__custom.');
const collectionManageField = isCustomFilterItem const collectionManageField = isCustomFilterItem
? collectionManage.collections.filter((value) => value.name === fieldSchema['x-decorator-props'])[0] ? collectionManage.collections.filter((value) => value.name === fieldSchema['x-decorator-props'])[0]
: {}; : {};

View File

@ -15,7 +15,7 @@ export const filterFormItemInitializers_deprecated = new CompatibleSchemaInitial
title: '{{t("Configure fields")}}', title: '{{t("Configure fields")}}',
items: [ items: [
{ {
type: 'itemGroup', type: 'subMenu',
name: 'displayFields', name: 'displayFields',
title: '{{t("Display fields")}}', title: '{{t("Display fields")}}',
useChildren: useFilterFormItemInitializerFields, useChildren: useFilterFormItemInitializerFields,
@ -54,7 +54,7 @@ export const filterFormItemInitializers = new CompatibleSchemaInitializer(
title: '{{t("Configure fields")}}', title: '{{t("Configure fields")}}',
items: [ items: [
{ {
type: 'itemGroup', type: 'subMenu',
name: 'displayFields', name: 'displayFields',
title: '{{t("Display fields")}}', title: '{{t("Display fields")}}',
useChildren: useFilterFormItemInitializerFields, useChildren: useFilterFormItemInitializerFields,

View File

@ -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<Field>();
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();
},
};
},
},
],
});

View File

@ -162,19 +162,22 @@ export const getCustomCondition = (filter, fieldSchema, customFlat = flat) => {
for (const filterKey in filterSchemaItem) { for (const filterKey in filterSchemaItem) {
const match = filterSchemaItem[filterKey]?.slice(11, -2); const match = filterSchemaItem[filterKey]?.slice(11, -2);
const collection = match?.split('.')[0]; const collection = match?.split('.')[0];
const filterItems = Object.keys(items).filter((item) => item.includes(collection))[0]; const filterItems = Object.keys(items).filter((item) => item.includes(collection));
if (filterItems) { if (filterItems.length > 1) {
filterSchemaItem[filterKey] = items[filterItems]; filterSchemaItem[filterKey] = filterItems.map((key) => items[key]);
} else {
filterSchemaItem[filterKey] = items[filterItems[0]];
} }
} }
for (const item in filterSchemaItem) { for (const item in filterSchemaItem) {
if (filterSchemaItem[item].includes('$nFilter')) { if (!filterSchemaItem[item] || filterSchemaItem[item].includes('$nFilter')) {
delete filterSchemaItem[item]; delete filterSchemaItem[item];
} }
} }
const flatFieldSchema = customFlat.unflatten(filterSchemaItem); const flatFieldSchema = customFlat.unflatten(filterSchemaItem);
flatFieldSchema['$and'] = flatFieldSchema?.['$and']?.filter(Boolean); flatFieldSchema['$and'] = flatFieldSchema?.['$and']?.filter(Boolean);
flatFieldSchema['$or'] = flatFieldSchema?.['$or']?.filter(Boolean); flatFieldSchema['$or'] = flatFieldSchema?.['$or']?.filter(Boolean);
return flatFieldSchema; return flatFieldSchema;
} else { } else {
return customFlat.unflatten({}); return customFlat.unflatten({});

View File

@ -508,36 +508,54 @@ export const EditOperator = () => {
['x-filter-operators']: storedOperators, ['x-filter-operators']: storedOperators,
}; };
let componentProps = {}; let componentProps = {};
const isCustom = (fieldSchema.name as string).includes('__custom');
// 根据操作符的配置,设置组件的属性 // 根据操作符的配置,设置组件的属性
if (operator?.schema?.['x-component']) { if (operator?.schema?.['x-component']) {
_.set(fieldSchema, 'x-component-props.component', operator.schema['x-component']); _.set(fieldSchema, isCustom ? 'x-component' : 'x-component-props.component', operator.schema['x-component']);
_.set(field, 'componentProps.component', operator.schema['x-component']); _.set(field, isCustom ? 'component' : 'componentProps.component', operator.schema['x-component']);
field.reset(); field.reset();
componentProps = { componentProps = {
component: operator.schema['x-component'], component: operator.schema['x-component'],
...operator.schema['x-component-props'], ...operator.schema?.['x-component-props'],
}; };
dn.emit('patch', { const dnSchema = {
schema: { schema: {
['x-uid']: fieldSchema['x-uid'], ['x-uid']: fieldSchema['x-uid'],
['x-component-props']: componentProps, ['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) { } else if (fieldSchema['x-component-props']?.component) {
_.set(fieldSchema, 'x-component-props.component', null); _.set(
_.set(field, 'componentProps.component', null); 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(); field.reset();
componentProps = { componentProps = {
component: null, component: isCustom ? fieldSchema['x-component-props']?.component : null,
...operator.schema['x-component-props'], ...operator.schema?.['x-component-props'],
}; };
dn.emit('patch', { const dnSchema = {
schema: { schema: {
['x-uid']: fieldSchema['x-uid'], ['x-uid']: fieldSchema['x-uid'],
['x-component-props']: componentProps, ['x-component-props']: componentProps,
}, },
}); };
if (isCustom) {
dnSchema.schema['x-component'] = fieldSchema['x-component-props']?.component;
}
dn.emit('patch', dnSchema);
} }
field.componentProps = componentProps; field.componentProps = componentProps;
@ -559,7 +577,7 @@ export const EditTitleField = () => {
const { dn } = useDesignable(); const { dn } = useDesignable();
const compile = useCompile(); const compile = useCompile();
const collectionManage = useCollectionManager_deprecated(); const collectionManage = useCollectionManager_deprecated();
const isCustomFilterItem = ((fieldSchema?.name as string) ?? '').startsWith('custom.'); const isCustomFilterItem = ((fieldSchema?.name as string) ?? '').startsWith('__custom.');
const collectionManageField = isCustomFilterItem const collectionManageField = isCustomFilterItem
? collectionManage.collections.filter((value) => value.name === fieldSchema['x-decorator-props'])[0] ? collectionManage.collections.filter((value) => value.name === fieldSchema['x-decorator-props'])[0]
: {}; : {};

View File

@ -27,9 +27,9 @@ const ObjectSelect = (props: Props) => {
const form = useForm(); const form = useForm();
useEffect(() => { useEffect(() => {
if (collectionName && fieldSchema['name'].toString().includes('custom') && defaultValue) { if (collectionName && fieldSchema['name'].toString().includes('__custom') && defaultValue) {
const name = fieldSchema['name'].toString().split('.')[1]; 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) => { const toValue = (v: any) => {
@ -94,9 +94,9 @@ const ObjectSelect = (props: Props) => {
} else { } else {
onChange?.(current.shift() || null); 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]; const name = fieldSchema['name'].toString().split('.')[1];
form.values['custom'][name] = changed?.['value']; form.values['__custom'][name] = changed?.['value'];
} }
}} }}
mode={mode} mode={mode}

View File

@ -43,7 +43,6 @@ import {
GeneralSchemaDesigner, GeneralSchemaDesigner,
SchemaSettingCollection, SchemaSettingCollection,
SchemaSettingComponent, SchemaSettingComponent,
SchemaSettingsCustomRemove,
SchemaSettingsDataScope, SchemaSettingsDataScope,
SchemaSettingsDivider, SchemaSettingsDivider,
SchemaSettingsRemove, SchemaSettingsRemove,
@ -58,20 +57,6 @@ const FieldComponentProps: React.FC = observer(
const schema = { const schema = {
type: 'object', type: 'object',
properties: { properties: {
mode: {
type: 'string',
enum: [
{
label: '{{ t("Single select") }}',
value: '',
},
],
'x-decorator': 'FormItem',
'x-component': 'Radio.Group',
'x-component-props': {
defaultValue: '',
},
},
options: { options: {
title: '{{t("Options")}}', title: '{{t("Options")}}',
type: 'array', type: 'array',
@ -143,7 +128,9 @@ export const useFieldComponents = () => {
{ label: t('AutoComplete'), value: 'AutoComplete' }, { label: t('AutoComplete'), value: 'AutoComplete' },
{ label: t('Select'), value: 'Select' }, { label: t('Select'), value: 'Select' },
{ label: t('AssociationCascader'), value: 'AssociationCascader' }, { 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 { return {
options, options,
@ -249,6 +236,16 @@ export const FilterCustomItemInitializer: React.FC<{
'x-decorator': 'FormItem', 'x-decorator': 'FormItem',
'x-component': 'FieldComponentProps', 'x-component': 'FieldComponentProps',
'x-visible': false, '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 defaultSchema = getInterface(component)?.default?.uiSchema || {};
const titleField = cm.getCollection(collection)?.titleField; const titleField = cm.getCollection(collection)?.titleField;
const name = uid(); const name = uid();
insert( const schema = {
gridRowColWrap({ ...defaultSchema,
...defaultSchema, type: 'string',
type: 'string', title: title,
title: title, name: '__custom.' + name,
name: 'custom.' + name, required: false,
required: false, 'x-component': component,
'x-component': component, 'x-toolbar': 'FormItemSchemaToolbar',
'x-toolbar': 'FormItemSchemaToolbar', 'x-settings': 'fieldSettings:FilterFormCustomSettings',
'x-settings': 'fieldSettings:FilterFormCustomSettings', 'x-decorator': 'FilterFormItem',
'x-decorator': 'FilterFormItem', 'x-decorator-props': collection,
'x-decorator-props': collection, 'x-component-props': {
'x-component-props': { ...(defaultSchema['x-component-props'] || {}),
...(defaultSchema['x-component-props'] || {}), fieldNames: {
fieldNames: { label: titleField,
label: titleField, value: titleField,
value: titleField,
},
associationField,
collection,
objectValue: true,
}, },
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 // eslint-disable-next-line react-hooks/exhaustive-deps
}, [theme]); }, [theme]);
@ -330,7 +332,7 @@ export const FilterItemCustomDesigner: React.FC = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const fieldName = fieldSchema['name'] as string; 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 { form } = useFormBlockContext();
const field = useField(); const field = useField();
const { dn } = useDesignable(); const { dn } = useDesignable();

View File

@ -8,7 +8,19 @@ import { SchemaSettingsSelectItem } from './SchemaSettings';
export const SchemaSettingComponent = () => { export const SchemaSettingComponent = () => {
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const field = useField(); 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(); const { dn } = useDesignable();
return ( return (
<SchemaSettingsSelectItem <SchemaSettingsSelectItem

View File

@ -51,6 +51,7 @@ import { inputNumberComponentFieldSettings } from '../modules/fields/component/I
import { subformComponentFieldSettings } from '../modules/fields/component/Nester/subformComponentFieldSettings'; import { subformComponentFieldSettings } from '../modules/fields/component/Nester/subformComponentFieldSettings';
import { recordPickerComponentFieldSettings } from '../modules/fields/component/Picker/recordPickerComponentFieldSettings'; import { recordPickerComponentFieldSettings } from '../modules/fields/component/Picker/recordPickerComponentFieldSettings';
import { subformPopoverComponentFieldSettings } from '../modules/fields/component/PopoverNester/subformPopoverComponentFieldSettings'; import { subformPopoverComponentFieldSettings } from '../modules/fields/component/PopoverNester/subformPopoverComponentFieldSettings';
import { radioComponentFieldSettings } from '../modules/fields/component/Radio/radioComponentFieldSettings';
import { selectComponentFieldSettings } from '../modules/fields/component/Select/selectComponentFieldSettings'; import { selectComponentFieldSettings } from '../modules/fields/component/Select/selectComponentFieldSettings';
import { subTablePopoverComponentFieldSettings } from '../modules/fields/component/SubTable/subTablePopoverComponentFieldSettings'; import { subTablePopoverComponentFieldSettings } from '../modules/fields/component/SubTable/subTablePopoverComponentFieldSettings';
import { tagComponentFieldSettings } from '../modules/fields/component/Tag/tagComponentFieldSettings'; import { tagComponentFieldSettings } from '../modules/fields/component/Tag/tagComponentFieldSettings';
@ -111,6 +112,7 @@ export class SchemaSettingsPlugin extends Plugin {
this.schemaSettingsManager.add(unixTimestampComponentFieldSettings); this.schemaSettingsManager.add(unixTimestampComponentFieldSettings);
this.schemaSettingsManager.add(inputNumberComponentFieldSettings); this.schemaSettingsManager.add(inputNumberComponentFieldSettings);
this.schemaSettingsManager.add(checkboxComponentFieldSettings); this.schemaSettingsManager.add(checkboxComponentFieldSettings);
this.schemaSettingsManager.add(radioComponentFieldSettings);
this.schemaSettingsManager.add(fileManagerComponentFieldSettings); this.schemaSettingsManager.add(fileManagerComponentFieldSettings);
this.schemaSettingsManager.add(tagComponentFieldSettings); this.schemaSettingsManager.add(tagComponentFieldSettings);

View File

@ -28,10 +28,10 @@ export const useFilterVariable = (props: any = {}) => {
}); });
} }
const options = customFields const options = customFields
.filter((value) => value?.props?.name.includes('custom.')) .filter((value) => value?.props?.name.includes('__custom.'))
.map((custom) => { .map((custom) => {
if (activeFields.includes(custom?.props?.name)) { if (activeFields.includes(custom?.props?.name)) {
const value = custom?.props?.name.replace(/^custom\./, ''); const value = custom?.props?.name.replace(/^__custom\./, '');
return { return {
key: custom?.props?.name, key: custom?.props?.name,
value, value,

View File

@ -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 + '条');
}
}