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: {},
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);

View File

@ -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'];
}

View File

@ -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]
: {};

View File

@ -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,

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) {
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({});

View File

@ -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]
: {};

View File

@ -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}

View File

@ -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,16 +283,15 @@ 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({
const schema = {
...defaultSchema,
type: 'string',
title: title,
name: 'custom.' + name,
name: '__custom.' + name,
required: false,
'x-component': component,
'x-toolbar': 'FormItemSchemaToolbar',
@ -311,10 +307,16 @@ export const FilterCustomItemInitializer: React.FC<{
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();

View File

@ -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 (
<SchemaSettingsSelectItem

View File

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

View File

@ -28,10 +28,10 @@ export const useFilterVariable = (props: any = {}) => {
});
}
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,

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