fix: 把remoteSelect和Select组件合并到框架 (#499)

Reviewed-on: daoyoucloud/tachycode#499
Co-authored-by: wjh <wwwjh0710@163.com>
Co-committed-by: wjh <wwwjh0710@163.com>
This commit is contained in:
wjh 2024-03-27 16:49:56 +08:00 committed by sealday
parent 0eeb44c306
commit 9b405b4b9d
18 changed files with 45 additions and 600 deletions

View File

@ -9,7 +9,7 @@ import { useTranslation } from 'react-i18next';
import { RecordProvider, useAPIClient, useApp, useCollectionRecordData } from '../../../'; import { RecordProvider, useAPIClient, useApp, useCollectionRecordData } from '../../../';
import { isVariable } from '../../../variables/utils/isVariable'; import { isVariable } from '../../../variables/utils/isVariable';
import { getInnermostKeyAndValue } from '../../common/utils/uitls'; import { getInnermostKeyAndValue } from '../../common/utils/uitls';
import { RemoteSelectProps } from '../remote-select'; import { RemoteSelectProps, RemoteSelect } from '../remote-select';
import useServiceOptions, { useAssociationFieldContext } from './hooks'; import useServiceOptions, { useAssociationFieldContext } from './hooks';
export type AssociationSelectProps<P = any> = RemoteSelectProps<P> & { export type AssociationSelectProps<P = any> = RemoteSelectProps<P> & {
@ -60,8 +60,6 @@ const InternalAssociationSelect = observer(
const resource = api.resource(collectionField.target); const resource = api.resource(collectionField.target);
const linkageFields = filterAnalyses(field.componentProps?.service?.params?.filter); const linkageFields = filterAnalyses(field.componentProps?.service?.params?.filter);
const recordData = useCollectionRecordData(); const recordData = useCollectionRecordData();
const app = useApp();
const RemoteSelect = useMemo(() => app.getComponent('RemoteSelect'), [app]);
useEffect(() => { useEffect(() => {
const initValue = isVariable(field.value) ? undefined : field.value; const initValue = isVariable(field.value) ? undefined : field.value;
@ -169,7 +167,6 @@ export const AssociationSelectReadPretty = connect(
(props: any) => { (props: any) => {
const service = useServiceOptions(props); const service = useServiceOptions(props);
const app = useApp(); const app = useApp();
const RemoteSelect = useMemo(() => app.getComponent('RemoteSelect'), [app]) as any;
if (props.fieldNames) { if (props.fieldNames) {
return <RemoteSelect.ReadPretty {...props} service={service}></RemoteSelect.ReadPretty>; return <RemoteSelect.ReadPretty {...props} service={service}></RemoteSelect.ReadPretty>;
} }

View File

@ -136,7 +136,7 @@ const InternalRemoteSelect = connect(
...service, ...service,
headers, headers,
params: { params: {
pageSize: 200, pageSize: fieldNames['formula'] ? 9999 : 200,
...service?.params, ...service?.params,
filter: service?.params?.filter, filter: service?.params?.filter,
}, },

View File

@ -1,12 +1,11 @@
import { CloseCircleFilled, CloseOutlined, LoadingOutlined } from '@ant-design/icons'; import { CloseCircleFilled, CloseOutlined } from '@ant-design/icons';
import { connect, mapProps, mapReadPretty, useFieldSchema, useForm } from '@formily/react'; import { useFieldSchema, useForm } from '@formily/react';
import { isValid, toArr } from '@formily/shared'; import { isValid, toArr } from '@formily/shared';
import { isPlainObject } from '@nocobase/utils/client'; import { isPlainObject } from '@nocobase/utils/client';
import type { SelectProps } from 'antd'; import type { SelectProps } from 'antd';
import { Select as AntdSelect, Empty, Spin, Tag } from 'antd'; import { Select as AntdSelect, Empty, Spin, Tag } from 'antd';
import React, { useEffect, useMemo, useState } from 'react'; import React, { useEffect, useMemo, useState } from 'react';
import { ReadPretty } from './ReadPretty'; import { FieldNames, getCurrentOptions } from './utils';
import { FieldNames, defaultFieldNames, getCurrentOptions } from './utils';
import { useAPIClient, useCollection_deprecated, useRequest } from '@nocobase/client'; import { useAPIClient, useCollection_deprecated, useRequest } from '@nocobase/client';
import { useAsyncEffect } from 'ahooks'; import { useAsyncEffect } from 'ahooks';
@ -21,7 +20,7 @@ type Props = SelectProps<any, any> & {
const isEmptyObject = (val: any) => !isValid(val) || (typeof val === 'object' && Object.keys(val).length === 0); const isEmptyObject = (val: any) => !isValid(val) || (typeof val === 'object' && Object.keys(val).length === 0);
const ObjectSelect = (props: Props) => { const ObjectSelect = (props: Props) => {
const { value, options, onChange, fieldNames, mode, loading, rawOptions, defaultValue, ...others } = props; const { value, options, onChange, mode, fieldNames, loading, rawOptions, defaultValue, ...others } = props;
const [defoptions, setDefOptions] = useState(); const [defoptions, setDefOptions] = useState();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const collectionName = fieldSchema['collectionName']; const collectionName = fieldSchema['collectionName'];
@ -45,6 +44,12 @@ const ObjectSelect = (props: Props) => {
setDefOptions(changOptions); setDefOptions(changOptions);
} }
}, [filterField?.filter, collectionName]); }, [filterField?.filter, collectionName]);
useEffect(() => {
if (collectionName && fieldSchema['name'].toString().includes('custom') && defaultValue) {
const name = fieldSchema['name'].toString().split('.')[1];
form.values['custom'][name] = defaultValue?.[fieldNames.value];
}
}, []);
const toValue = (v: any) => { const toValue = (v: any) => {
if (isEmptyObject(v)) { if (isEmptyObject(v)) {
return; return;
@ -107,7 +112,7 @@ const ObjectSelect = (props: Props) => {
} else { } else {
onChange?.(current.shift() || null); onChange?.(current.shift() || null);
} }
if (collectionName) { 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'];
} }
@ -131,8 +136,6 @@ const ObjectSelect = (props: Props) => {
); );
}; };
const filterOption = (input, option) => (option?.label ?? '').toLowerCase().includes((input || '').toLowerCase());
const replacePlaceholders = (inputStr, values) => { const replacePlaceholders = (inputStr, values) => {
return inputStr.replace(/{{(.*?)}}/g, function (match, placeholder) { return inputStr.replace(/{{(.*?)}}/g, function (match, placeholder) {
return Object.prototype.hasOwnProperty.call(values, placeholder) ? values[placeholder] : match; return Object.prototype.hasOwnProperty.call(values, placeholder) ? values[placeholder] : match;
@ -200,15 +203,15 @@ const useLabelOptions = (others) => {
} }
return others; return others;
}; };
const InternalSelect = connect(
(props: Props) => { const FormulaSelect = (props) => {
const { objectValue, loading, value, rawOptions, defaultValue, ...others } = props; const { objectValue, loading, value, rawOptions, defaultValue, ...others } = props;
let mode: any = props.multiple ? 'multiple' : props.mode; let mode: any = props.multiple ? 'multiple' : props.mode;
if (mode && !['multiple', 'tags'].includes(mode)) { if (mode && !['multiple', 'tags'].includes(mode)) {
mode = undefined; mode = undefined;
} }
const modifiedProps = useLabelOptions(others); const modifiedProps = useLabelOptions(others);
if (objectValue) {
return ( return (
<ObjectSelect <ObjectSelect
rawOptions={rawOptions} rawOptions={rawOptions}
@ -219,70 +222,6 @@ const InternalSelect = connect(
loading={loading} loading={loading}
/> />
); );
}
const toValue = (v) => {
if (['tags', 'multiple'].includes(props.mode) || props.multiple) {
if (v) {
return toArr(v);
}
return undefined;
}
return v;
};
return (
<AntdSelect
// @ts-ignore
role="button"
data-testid={`select-${mode || 'single'}`}
showSearch
filterOption={filterOption}
allowClear={{
clearIcon: <CloseCircleFilled role="button" aria-label="icon-close-select" />,
}}
popupMatchSelectWidth={false}
notFoundContent={loading ? <Spin /> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}
value={toValue(value)}
defaultValue={toValue(defaultValue)}
tagRender={(props) => {
return (
// @ts-ignore
<Tag
role="button"
aria-label={props.label}
closeIcon={<CloseOutlined role="button" aria-label="icon-close-tag" />}
{...props}
>
{props.label}
</Tag>
);
}}
{...others}
onChange={(changed) => {
props.onChange?.(changed === undefined ? null : changed);
}}
mode={mode}
/>
);
},
mapProps(
{
dataSource: 'options',
},
(props, field) => {
return {
...props,
fieldNames: { ...defaultFieldNames, ...props.fieldNames },
suffixIcon: field?.['loading'] || field?.['validating'] ? <LoadingOutlined /> : props.suffixIcon,
};
},
),
mapReadPretty(ReadPretty),
);
export const Select = InternalSelect as unknown as typeof InternalSelect & {
ReadPretty: typeof ReadPretty;
}; };
Select.ReadPretty = ReadPretty; export default FormulaSelect;
export default Select;

View File

@ -7,6 +7,7 @@ import { Select as AntdSelect, Empty, Spin, Tag } from 'antd';
import React from 'react'; import React from 'react';
import { ReadPretty } from './ReadPretty'; import { ReadPretty } from './ReadPretty';
import { FieldNames, defaultFieldNames, getCurrentOptions } from './utils'; import { FieldNames, defaultFieldNames, getCurrentOptions } from './utils';
import FormulaSelect from './FormulaSelect';
type Props = SelectProps<any, any> & { type Props = SelectProps<any, any> & {
objectValue?: boolean; objectValue?: boolean;
@ -103,6 +104,9 @@ const InternalSelect = connect(
if (mode && !['multiple', 'tags'].includes(mode)) { if (mode && !['multiple', 'tags'].includes(mode)) {
mode = undefined; mode = undefined;
} }
if ('formula' in others.fieldNames || 'collection' in props) {
return <FormulaSelect {...props} />;
}
if (objectValue) { if (objectValue) {
return ( return (
<ObjectSelect <ObjectSelect

View File

@ -41,6 +41,7 @@ export const removeNullCondition = (filter, fieldSchema?) => {
const flatValue = flat.unflatten(values); const flatValue = flat.unflatten(values);
const flatFieldSchema = flat.unflatten(filterSchemaItem); const flatFieldSchema = flat.unflatten(filterSchemaItem);
flatValue['$and'] = flatValue['$and']?.filter(Boolean); flatValue['$and'] = flatValue['$and']?.filter(Boolean);
flatFieldSchema['$and'] = flatFieldSchema['$and']?.filter(Boolean);
return { return {
$and: [flatValue, flatFieldSchema], $and: [flatValue, flatFieldSchema],
}; };

View File

@ -43,15 +43,7 @@ import {
GroupBlockConfigure, GroupBlockConfigure,
SignatureInput, SignatureInput,
} from './components'; } from './components';
import { import { AutoComplete, DatePicker, GroupBlock, InternalPDFViewer, MenuDesigner } from './schema-components';
AutoComplete,
DatePicker,
GroupBlock,
InternalPDFViewer,
MenuDesigner,
RemoteSelect,
Select,
} from './schema-components';
import { import {
CreateSubmitActionInitializer, CreateSubmitActionInitializer,
FilterFormItem, FilterFormItem,
@ -186,8 +178,6 @@ export class PluginCoreClient extends Plugin {
PDFViewerProvider, PDFViewerProvider,
PDFViwer: InternalPDFViewer, PDFViwer: InternalPDFViewer,
PageLayout, PageLayout,
RemoteSelect,
Select,
SettingBlock: SettingBlockInitializer, SettingBlock: SettingBlockInitializer,
SheetBlock, SheetBlock,
SheetBlockInitializer, SheetBlockInitializer,

View File

@ -3,5 +3,3 @@ export * from './blocks/GroupBlock';
export * from './blocks/PDFViewer'; export * from './blocks/PDFViewer';
export * from './date-picker'; export * from './date-picker';
export * from './deprecated/ExtendedMenuDesigner'; export * from './deprecated/ExtendedMenuDesigner';
export * from './remote-select';
export * from './select/Select';

View File

@ -1,42 +0,0 @@
import { observer, useField, useFieldSchema } from '@formily/react';
import React from 'react';
import { getValues } from './shared';
import { defaultFieldNames, useActionContext, useRecord, useRequest } from '@nocobase/client';
import { Select } from '../select';
export const ReadPretty = observer(
(props: any) => {
const fieldNames = { ...defaultFieldNames, ...props.fieldNames };
const field = useField<any>();
const fieldSchema = useFieldSchema();
const record = useRecord();
const { snapshot } = useActionContext();
const { data } = useRequest<{
data: any[];
}>(
snapshot
? async () => ({
data: record[fieldSchema.name],
})
: {
action: 'list',
...props.service,
params: {
paginate: false,
filter: {
[fieldNames.value]: {
$in: getValues(field.value, fieldNames),
},
},
},
},
{
refreshDeps: [props.service, field.value],
},
);
return <Select.ReadPretty {...props} options={data?.data}></Select.ReadPretty>;
},
{ displayName: 'ReadPretty' },
);

View File

@ -1,279 +0,0 @@
import { LoadingOutlined } from '@ant-design/icons';
import { connect, mapProps, mapReadPretty, useFieldSchema } from '@formily/react';
import { Divider, SelectProps, Tag } from 'antd';
import dayjs from 'dayjs';
import { uniqBy } from 'lodash';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { ReadPretty } from './ReadPretty';
import {
defaultFieldNames,
mergeFilter,
useCollection_deprecated,
useCollectionManager_deprecated,
useCompile,
useRequest,
} from '@nocobase/client';
import { Select } from '../select';
const EMPTY = 'N/A';
export type ResourceActionOptions<P = any> = {
resource?: string;
resourceOf?: any;
action?: string;
params?: P;
};
export type RemoteSelectProps<P = any> = SelectProps<P, any> & {
objectValue?: boolean;
onChange?: (v: any) => void;
target: string;
wait?: number;
manual?: boolean;
mapOptions?: (data: any) => RemoteSelectProps['fieldNames'];
targetField?: any;
service: ResourceActionOptions<P>;
CustomDropdownRender?: (v: any) => any;
};
const InternalRemoteSelect = connect(
(props: RemoteSelectProps) => {
const {
fieldNames = {},
service = {},
wait = 300,
value,
defaultValue,
objectValue,
manual = true,
mapOptions,
targetField: _targetField,
CustomDropdownRender,
...others
} = props;
const [open, setOpen] = useState(false);
const firstRun = useRef(false);
const fieldSchema = useFieldSchema();
const isQuickAdd = fieldSchema['x-component-props']?.addMode === 'quickAdd';
const { getField } = useCollection_deprecated();
const searchData = useRef(null);
const { getCollectionJoinField, getInterface } = useCollectionManager_deprecated();
const collectionField = getField(fieldSchema.name) || getCollectionJoinField(fieldSchema.name as string);
const targetField =
_targetField ||
(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 compile = useCompile();
const mapOptionsToTags = useCallback(
(options) => {
try {
return options
.filter((v) => ['number', 'string'].includes(typeof v[fieldNames.value]))
.map((option) => {
let label = compile(option[fieldNames.label]);
if (targetField?.uiSchema?.enum) {
if (Array.isArray(label)) {
label = label
.map((item, index) => {
const option = targetField.uiSchema.enum.find((i) => i.value === item);
if (option) {
return (
<Tag role="button" key={index} color={option.color} style={{ marginRight: 3 }}>
{option?.label || item}
</Tag>
);
} else {
return (
<Tag role="button" key={item}>
{item}
</Tag>
);
}
})
.reverse();
} else {
const item = targetField.uiSchema.enum.find((i) => i.value === label);
if (item) {
label = (
<Tag role="button" color={item.color}>
{item.label}
</Tag>
);
}
}
}
if (targetField?.type === 'date') {
label = dayjs(label).format('YYYY-MM-DD');
}
if (mapOptions) {
return mapOptions({
[fieldNames.label]: label || EMPTY,
[fieldNames.value]: option[fieldNames.value],
});
}
return {
...option,
[fieldNames.label]: label || EMPTY,
[fieldNames.value]: option[fieldNames.value],
};
})
.filter(Boolean);
} catch (err) {
console.error(err);
return options;
}
},
[targetField?.uiSchema, fieldNames],
);
const { data, run, loading } = useRequest(
{
action: 'list',
...service,
params: {
pageSize: fieldNames['formula'] ? 9999 : 200,
...service?.params,
filter: service?.params?.filter,
},
},
{
manual,
debounceWait: wait,
},
);
const runDep = useMemo(
() =>
JSON.stringify({
service,
fieldNames,
}),
[service, fieldNames],
);
const CustomRenderCom = useCallback(() => {
if (searchData.current && CustomDropdownRender) {
return (
<CustomDropdownRender
search={searchData.current}
callBack={() => {
searchData.current = null;
setOpen(false);
}}
/>
);
}
return null;
}, [searchData.current]);
useEffect(() => {
// Lazy load
if (firstRun.current) {
run();
}
}, [runDep]);
const onSearch = async (search) => {
run({
filter: mergeFilter([
search
? {
[fieldNames.label]: {
[operator]: search,
},
}
: {},
service?.params?.filter,
]),
});
searchData.current = search;
};
const options = useMemo(() => {
const v = value || defaultValue;
if (!data?.data?.length) {
return v != null ? (Array.isArray(v) ? v : [v]) : [];
}
const valueOptions =
(v != null && (Array.isArray(v) ? v : [{ ...v, [fieldNames.value]: v[fieldNames.value] || v }])) || [];
return uniqBy(data?.data?.concat(valueOptions ?? []), fieldNames.value);
}, [value, defaultValue, data?.data, fieldNames.value]);
const onDropdownVisibleChange = (visible) => {
setOpen(visible);
searchData.current = null;
if (visible) {
run();
}
firstRun.current = true;
};
return (
<Select
open={open}
popupMatchSelectWidth={false}
autoClearSearchValue
filterOption={false}
filterSort={null}
fieldNames={fieldNames as any}
onSearch={onSearch}
onDropdownVisibleChange={onDropdownVisibleChange}
objectValue={objectValue}
value={value}
defaultValue={defaultValue}
{...others}
loading={data! ? loading : true}
options={mapOptionsToTags(options)}
rawOptions={options}
dropdownRender={(menu) => {
const isFullMatch = options.some((v) => v[fieldNames.label] === searchData.current);
return (
<>
{isQuickAdd ? (
<>
{!(data?.data.length === 0 && searchData?.current) && menu}
{data?.data.length > 0 && searchData?.current && !isFullMatch && <Divider style={{ margin: 0 }} />}
{!isFullMatch && <CustomRenderCom />}
</>
) : (
menu
)}
</>
);
}}
/>
);
},
mapProps(
{
dataSource: 'options',
},
(props, field) => {
const fieldSchema = useFieldSchema();
return {
...props,
fieldNames: {
...defaultFieldNames,
...props.fieldNames,
...field.componentProps.fieldNames,
...fieldSchema['x-component-props']?.fieldNames,
},
suffixIcon: field?.['loading'] || field?.['validating'] ? <LoadingOutlined /> : props.suffixIcon,
};
},
),
mapReadPretty(ReadPretty),
);
export const RemoteSelect = InternalRemoteSelect as unknown as typeof InternalRemoteSelect & {
ReadPretty: typeof ReadPretty;
};
RemoteSelect.ReadPretty = ReadPretty;
export default RemoteSelect;

View File

@ -1,28 +0,0 @@
---
group:
title: Schema Components
order: 3
---
# RemoteSelect
## Examples
<code src="./demos/demo1.tsx"></code>
## API
基于 Ant Design 的 [Select](https://ant.design/components/select/#API),相关扩展属性有:
- `objectValue` 值为 object 类型
- `fieldNames` 默认值有区别
```ts
export const defaultFieldNames = {
label: 'label',
value: 'value',
color: 'color',
options: 'children',
};
```

View File

@ -1 +0,0 @@
export * from './RemoteSelect';

View File

@ -1,7 +0,0 @@
import { castArray } from 'lodash';
export const getValues = (values, fieldNames) => {
return castArray(values)
.filter((item) => item != null)
.map((val) => (typeof val === 'object' ? val[fieldNames.value] : val));
};

View File

@ -1,36 +0,0 @@
import { isArrayField } from '@formily/core';
import { observer, useField } from '@formily/react';
import { isValid } from '@formily/shared';
import { Tag } from 'antd';
import React from 'react';
import { defaultFieldNames, getCurrentOptions } from './utils';
import { EllipsisWithTooltip } from '@nocobase/client';
export const ReadPretty = observer(
(props: any) => {
const fieldNames = { ...defaultFieldNames, ...props.fieldNames };
const field = useField<any>();
if (!isValid(props.value)) {
return <div />;
}
if (isArrayField(field) && field?.value?.length === 0) {
return <div />;
}
const dataSource = field.dataSource || props.options || [];
const currentOptions = getCurrentOptions(field.value, dataSource, fieldNames);
return (
<div>
<EllipsisWithTooltip ellipsis={props.ellipsis}>
{currentOptions.map((option, key) => (
<Tag key={key} color={option[fieldNames.color]} icon={option.icon}>
{option[fieldNames.label]}
</Tag>
))}
</EllipsisWithTooltip>
</div>
);
},
{ displayName: 'ReadPretty' },
);

View File

@ -1,37 +0,0 @@
---
group:
title: Schema Components
order: 3
---
# Select
## Examples
### 单选
<code src="./demos/demo1.tsx"></code>
### 多选
<code src="./demos/demo2.tsx"></code>
### 值为 Object 类型的 Select
<code src="./demos/demo3.tsx"></code>
## API
基于 Ant Design 的 [Select](https://ant.design/components/select/#API),相关扩展属性有:
- `objectValue` 值为 object 类型
- `fieldNames` 默认值有区别
```ts
export const defaultFieldNames = {
label: 'label',
value: 'value',
color: 'color',
options: 'children',
};
```

View File

@ -1,2 +0,0 @@
export * from './Select';
export * from './utils';

View File

@ -1,54 +0,0 @@
import { isPlainObject } from '@nocobase/utils/client';
import { castArray } from 'lodash';
export interface FieldNames {
label: string;
value: string;
color: string;
options: string;
}
export const defaultFieldNames: FieldNames = {
label: 'label',
value: 'value',
color: 'color',
options: 'children',
};
interface Option {
label: string;
value: string;
icon?: any;
}
function flatData(data: any[], fieldNames: FieldNames): any[] {
const newArr: any[] = [];
if (!Array.isArray(data)) return newArr;
for (let i = 0; i < data.length; i++) {
const children = data[i][fieldNames.options];
if (Array.isArray(children)) {
newArr.push(...flatData(children, fieldNames));
}
newArr.push({ ...data[i] });
}
return newArr;
}
export function getCurrentOptions(values: string | string[], dataSource: any[], fieldNames: FieldNames): Option[] {
const result = flatData(dataSource, fieldNames);
const arrValues = castArray(values)
.filter((item) => item != null)
.map((val) => (isPlainObject(val) ? val[fieldNames.value] : val)) as string[];
function findOptions(options: any[]): Option[] {
if (!options) return [];
const current: Option[] = [];
for (const value of arrValues) {
const option = options.find((v) => v[fieldNames.value] === value) || { value, label: value };
current.push(option);
}
return current;
}
return findOptions(result);
}

View File

@ -23,6 +23,7 @@ import {
useFormBlockContext, useFormBlockContext,
useAPIClient, useAPIClient,
useCompile, useCompile,
Select,
} from '@nocobase/client'; } from '@nocobase/client';
import { ConfigProvider, Radio, Space } from 'antd'; import { ConfigProvider, Radio, Space } from 'antd';
import React, { memo, useCallback, useContext, useMemo, Profiler } from 'react'; import React, { memo, useCallback, useContext, useMemo, Profiler } from 'react';
@ -116,8 +117,6 @@ const FieldComponentProps: React.FC = observer(
}, },
}, },
}; };
console.log(scope, 'scope');
console.log(components, 'components');
return ( return (
<SchemaComponentOptions scope={{ ...scope }} components={{ ArrayItems, FormItem, Space }}> <SchemaComponentOptions scope={{ ...scope }} components={{ ArrayItems, FormItem, Space }}>
@ -180,7 +179,7 @@ export const FilterCustomItemInitializer: React.FC<{
); );
}, },
}} }}
components={{ ...components, FieldComponentProps }} components={{ ...components, FieldComponentProps, Select }}
> >
<FormLayout layout={'vertical'}> <FormLayout layout={'vertical'}>
<ConfigProvider locale={locale}> <ConfigProvider locale={locale}>
@ -282,12 +281,12 @@ export const FilterCustomItemInitializer: React.FC<{
const name = uid(); const name = uid();
insert( insert(
gridRowColWrap({ gridRowColWrap({
'x-component': component,
...defaultSchema, ...defaultSchema,
type: 'string', type: 'string',
title: title, title: title,
name: 'custom.' + name, name: 'custom.' + name,
required: false, required: false,
'x-component': component,
'x-designer': 'FilterItemCustomDesigner', 'x-designer': 'FilterItemCustomDesigner',
'x-decorator': 'FilterFormItem', 'x-decorator': 'FilterFormItem',
'x-decorator-props': collection, 'x-decorator-props': collection,
@ -299,6 +298,7 @@ export const FilterCustomItemInitializer: React.FC<{
}, },
associationField, associationField,
collection, collection,
objectValue: true,
}, },
collectionName: collection, collectionName: collection,
}), }),

View File

@ -43,7 +43,9 @@ export const SchemaSettingsRemove: FC<SchemaSettingsRemoveProps> = (props) => {
} }
await dn.remove(null, options); await dn.remove(null, options);
await confirm?.onOk?.(); await confirm?.onOk?.();
if (form.values['custom']) {
delete form.values['custom'][fieldName]; delete form.values['custom'][fieldName];
}
for (const key in form.fields) { for (const key in form.fields) {
if (key.includes(name) && form.fields[key].title === title) { if (key.includes(name) && form.fields[key].title === title) {
delete form.fields[key]; delete form.fields[key];