Merge pull request 'fix: 修改级联组件适配' (#355) from fix_cascade into @hera/dev

Reviewed-on: daoyoucloud/tachycode#355
Reviewed-by: sealday <zhanglin@daoyoucloud.com>
This commit is contained in:
sealday 2024-03-13 12:12:22 +08:00
commit 9ee80846fb
3 changed files with 389 additions and 20 deletions

View File

@ -0,0 +1,371 @@
import { Field } from '@formily/core';
import { ISchema, useField, useFieldSchema } from '@formily/react';
import _ from 'lodash';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings';
import { useFormBlockContext } from '../../../../block-provider';
import {
useCollectionManager_deprecated,
useCollection_deprecated,
useSortFields,
} from '../../../../collection-manager';
import { useFieldComponentName } from '../../../../common/useFieldComponentName';
import { useRecord } from '../../../../record-provider';
import { removeNullCondition, useDesignable, useFieldModeOptions, useIsAddNewForm } from '../../../../schema-component';
import { isSubMode } from '../../../../schema-component/antd/association-field/util';
import { DynamicComponentProps } from '../../../../schema-component/antd/filter/DynamicComponent';
import {
useIsAssociationField,
useIsFieldReadPretty,
useIsSelectFieldMode,
useTitleFieldOptions,
} from '../../../../schema-component/antd/form-item/FormItem.Settings';
import { useColumnSchema } from '../../../../schema-component/antd/table-v2/Table.Column.Decorator';
import { SchemaSettingsModalItem, VariableInput, getShouldChange } from '../../../../schema-settings';
import { SchemaSettingsDataScope } from '../../../../schema-settings/SchemaSettingsDataScope';
import { useLocalVariables, useVariables } from '../../../../variables';
import { useCollectionField } from '../utils';
import { ArrayItems } from '@formily/antd-v5';
export const SchemaSettingsSortingRule = function SortRuleConfigure(props) {
const field = useField();
const { dn } = useDesignable();
const { t } = useTranslation();
const currentSchema = useFieldSchema();
const { getField } = useCollection_deprecated();
const { getCollectionJoinField } = useCollectionManager_deprecated();
const fieldSchema = props?.fieldSchema ?? currentSchema;
const collectionField = getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']);
const sortFields = useSortFields(collectionField?.target);
const defaultSort = fieldSchema['x-component-props']?.service?.params?.sort || [];
const sort = defaultSort?.map((item: string) => {
return item?.startsWith('-')
? {
field: item.substring(1),
direction: 'desc',
}
: {
field: item,
direction: 'asc',
};
});
return (
<SchemaSettingsModalItem
title={t('Set default sorting rules')}
components={{ ArrayItems }}
schema={
{
type: 'object',
title: t('Set default sorting rules'),
properties: {
sort: {
type: 'array',
default: sort,
'x-component': 'ArrayItems',
'x-decorator': 'FormItem',
items: {
type: 'object',
properties: {
space: {
type: 'void',
'x-component': 'Space',
properties: {
sort: {
type: 'void',
'x-decorator': 'FormItem',
'x-component': 'ArrayItems.SortHandle',
},
field: {
type: 'string',
enum: sortFields,
required: true,
'x-decorator': 'FormItem',
'x-component': 'Select',
'x-component-props': {
style: {
width: 260,
},
},
},
direction: {
type: 'string',
'x-decorator': 'FormItem',
'x-component': 'Radio.Group',
'x-component-props': {
optionType: 'button',
},
enum: [
{
label: t('ASC'),
value: 'asc',
},
{
label: t('DESC'),
value: 'desc',
},
],
},
remove: {
type: 'void',
'x-decorator': 'FormItem',
'x-component': 'ArrayItems.Remove',
},
},
},
},
},
properties: {
add: {
type: 'void',
title: t('Add sort field'),
'x-component': 'ArrayItems.Addition',
},
},
},
},
} as ISchema
}
onSubmit={({ sort }) => {
const sortArr = sort.map((item) => {
return item.direction === 'desc' ? `-${item.field}` : item.field;
});
_.set(field.componentProps, 'service.params.sort', sortArr);
props?.onSubmitCallBack?.(sortArr);
const service = fieldSchema['x-component-props']?.service;
if (service) {
service.params['sort'] = field.componentProps?.service.params?.sort;
} else {
fieldSchema['x-component-props']['service'] = {
params: {
sort: field.componentProps?.service.params?.sort,
},
};
}
const componentProps = fieldSchema['x-component-props'];
const path = field.path?.splice(field.path?.length - 1, 1);
field.form.query(`${path.concat(`*.` + fieldSchema.name)}`).forEach((f) => {
f.componentProps = componentProps;
});
dn.emit('patch', {
schema: {
['x-uid']: fieldSchema['x-uid'],
'x-component-props': componentProps,
},
});
}}
/>
);
};
const titleField: any = {
name: 'titleField',
type: 'select',
useComponentProps() {
const { t } = useTranslation();
const field = useField<Field>();
const { dn } = useDesignable();
const options = useTitleFieldOptions();
const { uiSchema, fieldSchema: tableColumnSchema, collectionField: tableColumnField } = useColumnSchema();
const schema = useFieldSchema();
const fieldSchema = tableColumnSchema || schema;
const targetCollectionField = useCollectionField();
const collectionField = tableColumnField || targetCollectionField;
// 处理多对一关系标题显示
const { getCollectionFields } = useCollectionManager_deprecated();
const fieldNames = {
...collectionField?.uiSchema?.['x-component-props']?.['fieldNames'],
...field?.componentProps?.fieldNames,
...fieldSchema?.['x-component-props']?.['fieldNames'],
};
return {
title: t('Title field'),
options,
value: fieldNames?.label,
onChange(label) {
const schema = {
['x-uid']: fieldSchema['x-uid'],
};
const newFieldNames = {
...collectionField?.uiSchema?.['x-component-props']?.['fieldNames'],
...fieldSchema['x-component-props']?.['fieldNames'],
label,
};
fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {};
fieldSchema['x-component-props']['fieldNames'] = newFieldNames;
// 处理多对一关系标题显示
const target = getCollectionFields(collectionField.target).find((field) => field.name === label);
if (target.interface === 'm2o') {
fieldSchema['x-component-props']['x-next-title'] = {
label,
collection: target.collectionName,
};
} else {
fieldSchema['x-component-props']['x-next-title'] = null;
}
schema['x-component-props'] = fieldSchema['x-component-props'];
field.componentProps.fieldNames = fieldSchema['x-component-props'].fieldNames;
const path = field.path?.splice(field.path?.length - 1, 1);
field.form.query(`${path.concat(`*.` + fieldSchema.name)}`).forEach((f) => {
f.componentProps.fieldNames = newFieldNames;
});
dn.emit('patch', {
schema,
});
dn.refresh();
},
};
},
};
const setDefaultSortingRules = {
name: 'setDefaultSortingRules',
Component: SchemaSettingsSortingRule,
};
const setTheDataScope: any = {
name: 'setTheDataScope',
Component: SchemaSettingsDataScope,
useComponentProps() {
const { getCollectionJoinField, getAllCollectionsInheritChain } = useCollectionManager_deprecated();
const { getField } = useCollection_deprecated();
const { form } = useFormBlockContext();
const record = useRecord();
const field = useField();
const { fieldSchema: tableColumnSchema, collectionField: tableColumnField } = useColumnSchema();
const schema = useFieldSchema();
const fieldSchema = tableColumnSchema || schema;
const collectionField =
tableColumnField || getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']);
const variables = useVariables();
const localVariables = useLocalVariables();
const { dn } = useDesignable();
return {
collectionName: collectionField?.target,
defaultFilter: fieldSchema?.['x-component-props']?.service?.params?.filter || {},
form,
dynamicComponent: (props: DynamicComponentProps) => {
return (
<VariableInput
{...props}
form={form}
collectionField={props.collectionField}
record={record}
shouldChange={getShouldChange({
collectionField: props.collectionField,
variables,
localVariables,
getAllCollectionsInheritChain,
})}
/>
);
},
onSubmit: ({ filter }) => {
filter = removeNullCondition(filter);
_.set(field.componentProps, 'service.params.filter', filter);
const service = fieldSchema['x-component-props']?.service;
if (service) {
service.params['filter'] = filter;
} else {
fieldSchema['x-component-props']['service'] = {
params: {
filter: { ...filter },
},
};
}
const componentProps = fieldSchema['x-component-props'];
const path = field.path?.splice(field.path?.length - 1, 1);
field.form.query(`${path.concat(`*.` + fieldSchema.name)}`).forEach((f) => {
f.componentProps = componentProps;
});
dn.emit('patch', {
schema: {
['x-uid']: fieldSchema['x-uid'],
'x-component-props': componentProps,
},
});
dn.refresh();
},
};
},
};
const fieldComponent: any = {
name: 'fieldComponent',
type: 'select',
useComponentProps() {
const { t } = useTranslation();
const field = useField<Field>();
const { fieldSchema: tableColumnSchema, collectionField } = useColumnSchema();
const schema = useFieldSchema();
const fieldSchema = tableColumnSchema || schema;
const fieldModeOptions = useFieldModeOptions({ fieldSchema: tableColumnSchema, collectionField });
const isAddNewForm = useIsAddNewForm();
const fieldMode = useFieldComponentName();
const { dn } = useDesignable();
return {
title: t('Field component'),
options: fieldModeOptions,
value: fieldMode,
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;
// 子表单状态不允许设置默认值
if (isSubMode(fieldSchema) && isAddNewForm) {
// @ts-ignore
schema.default = null;
fieldSchema.default = null;
field?.setInitialValue?.(null);
field?.setValue?.(null);
}
void dn.emit('patch', {
schema,
});
dn.refresh();
},
};
},
};
export const cascaderComponentFieldSettings = new SchemaSettings({
name: 'fieldSettings:component:Cascader',
items: [
{
...fieldComponent,
useVisible: useIsAssociationField,
},
{
...setTheDataScope,
useVisible() {
const isFieldReadPretty = useIsFieldReadPretty();
return !isFieldReadPretty;
},
},
{
...setDefaultSortingRules,
useComponentProps() {
const { fieldSchema } = useColumnSchema();
return {
fieldSchema,
};
},
useVisible() {
const isFieldReadPretty = useIsFieldReadPretty();
return !isFieldReadPretty;
},
},
{
...titleField,
useVisible: useIsAssociationField,
},
],
});

View File

@ -4,11 +4,11 @@ import { FormProvider, connect, createSchemaField, observer, useField, useFieldS
import { uid } from '@formily/shared'; import { uid } from '@formily/shared';
import { Input, Space, Spin, Tag } from 'antd'; import { Input, Space, Spin, Tag } from 'antd';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import React, { useCallback, useEffect, useMemo, useState } from 'react'; import React, { useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { css, useAPIClient, useCollectionManager_deprecated } from '../../..'; import { css, useAPIClient, useCollectionManager_deprecated } from '../../..';
import { mergeFilter } from '../../../filter-provider/utils'; import { mergeFilter } from '../../../filter-provider/utils';
import { CustomCascader, SchemaComponent, useCompile } from '../..'; import { CustomCascader, SchemaComponent, useCompile, useDesignable } from '../..';
import useServiceOptions, { useAssociationFieldContext } from './hooks'; import useServiceOptions, { useAssociationFieldContext } from './hooks';
const EMPTY = 'N/A'; const EMPTY = 'N/A';
@ -25,30 +25,22 @@ const Cascade = connect((props) => {
const { data, mapOptions, onChange } = props; const { data, mapOptions, onChange } = props;
const [selectedOptions, setSelectedOptions] = useState([]); const [selectedOptions, setSelectedOptions] = useState([]);
const [options, setOptions] = useState(data); const [options, setOptions] = useState(data);
const fieldSchema = useFieldSchema();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const compile = useCompile();
const api = useAPIClient(); const api = useAPIClient();
const service = useServiceOptions(props);
const { options: collectionField, field: associationField } = useAssociationFieldContext<any>(); const { options: collectionField, field: associationField } = useAssociationFieldContext<any>();
const resource = api.resource(collectionField.target); const resource = api.resource(collectionField.target);
const { getCollectionJoinField, getInterface } = useCollectionManager_deprecated();
const fieldNames = associationField?.componentProps?.fieldNames; const fieldNames = associationField?.componentProps?.fieldNames;
const FieldSchema = useFieldSchema(); const fieldFilter = fieldSchema['x-component-props']?.service?.params?.filter;
const targetField = const sort = fieldSchema['x-component-props']?.service?.params?.sort;
collectionField?.target &&
fieldNames?.label &&
getCollectionJoinField(`${collectionField.target}.${fieldNames.label}`);
const operator = useMemo(() => {
if (targetField?.interface) {
return getInterface(targetField.interface)?.filterable?.operators[0].value || '$includes';
}
return '$includes';
}, [targetField]);
const field: any = useField(); const field: any = useField();
useEffect(() => { useEffect(() => {
if (props.value) { if (props.value) {
const values = Array.isArray(props.value) const values = Array.isArray(props.value)
? extractLastNonNullValueObjects(props.value?.filter((v) => v.value), true) ? extractLastNonNullValueObjects(
props.value?.filter((v) => v.value),
true,
)
: transformNestedData(props.value); : transformNestedData(props.value);
const defaultData = values?.map?.((v) => { const defaultData = values?.map?.((v) => {
return v.id; return v.id;
@ -60,11 +52,10 @@ const Cascade = connect((props) => {
const handleGetOptions = async () => { const handleGetOptions = async () => {
const response = await resource.list({ const response = await resource.list({
pageSize: 9999, pageSize: 9999,
params: service?.params, filter: mergeFilter([fieldFilter, filter]),
filter: mergeFilter([service?.params?.filter, filter]), sort,
tree: true, tree: true,
}); });
return response?.data?.data; return response?.data?.data;
}; };
@ -139,6 +130,9 @@ const Cascade = connect((props) => {
defaultValue={selectedOptions} defaultValue={selectedOptions}
options={options} options={options}
onChange={(value, option) => handleSelect(option)} onChange={(value, option) => handleSelect(option)}
onDropdownVisibleChange={(visible) => {
onDropdownVisibleChange(visible);
}}
changeOnSelect changeOnSelect
placeholder="Please select" placeholder="Please select"
/> />
@ -218,6 +212,7 @@ export const InternalCascader = observer(
...props, ...props,
style: { width: '100%' }, style: { width: '100%' },
}, },
'x-read-pretty': false,
}, },
remove: { remove: {
type: 'void', type: 'void',
@ -250,6 +245,7 @@ export const InternalCascader = observer(
...props, ...props,
style: { width: '100%' }, style: { width: '100%' },
}, },
'x-read-pretty': false,
}} }}
/> />
) : ( ) : (

View File

@ -33,6 +33,7 @@ import { filterFormBlockSettings } from '../modules/blocks/filter-blocks/form/fi
import { filterFormItemFieldSettings } from '../modules/blocks/filter-blocks/form/filterFormItemFieldSettings'; import { filterFormItemFieldSettings } from '../modules/blocks/filter-blocks/form/filterFormItemFieldSettings';
import { markdownBlockSettings } from '../modules/blocks/other-blocks/markdown/markdownBlockSettings'; import { markdownBlockSettings } from '../modules/blocks/other-blocks/markdown/markdownBlockSettings';
import { cascadeSelectComponentFieldSettings } from '../modules/fields/component/CascadeSelect/cascadeSelectComponentFieldSettings'; import { cascadeSelectComponentFieldSettings } from '../modules/fields/component/CascadeSelect/cascadeSelectComponentFieldSettings';
import { cascaderComponentFieldSettings } from '../modules/fields/component/Cascader/cascaderComponentFieldSettings';
import { datePickerComponentFieldSettings } from '../modules/fields/component/DatePicker/datePickerComponentFieldSettings'; import { datePickerComponentFieldSettings } from '../modules/fields/component/DatePicker/datePickerComponentFieldSettings';
import { fileManagerComponentFieldSettings } from '../modules/fields/component/FileManager/fileManagerComponentFieldSettings'; import { fileManagerComponentFieldSettings } from '../modules/fields/component/FileManager/fileManagerComponentFieldSettings';
import { uploadAttachmentComponentFieldSettings } from '../modules/fields/component/FileManager/uploadAttachmentComponentFieldSettings'; import { uploadAttachmentComponentFieldSettings } from '../modules/fields/component/FileManager/uploadAttachmentComponentFieldSettings';
@ -94,5 +95,6 @@ export class SchemaSettingsPlugin extends Plugin {
this.schemaSettingsManager.add(tagComponentFieldSettings); this.schemaSettingsManager.add(tagComponentFieldSettings);
this.schemaSettingsManager.add(cascadeSelectComponentFieldSettings); this.schemaSettingsManager.add(cascadeSelectComponentFieldSettings);
this.schemaSettingsManager.add(uploadAttachmentComponentFieldSettings); this.schemaSettingsManager.add(uploadAttachmentComponentFieldSettings);
this.schemaSettingsManager.add(cascaderComponentFieldSettings);
} }
} }