feat: relationships (#473)

* feat: relationship fields

* feat: improve schema
This commit is contained in:
chenos 2022-06-05 22:46:14 +08:00 committed by GitHub
parent 4e19571c08
commit 000e4e50b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 1036 additions and 4 deletions

View File

@ -12,6 +12,7 @@ import { ActionContext, SchemaComponent, useCompile } from '../../schema-compone
import { useCreateAction } from '../action-hooks';
import { useCollectionManager } from '../hooks';
import { IField } from '../interfaces/types';
import * as components from './components';
import { options } from './interfaces';
const getSchema = (schema: IField): ISchema => {
@ -160,7 +161,7 @@ export const AddFieldAction = () => {
{t('Add field')}
</Button>
</Dropdown>
<SchemaComponent schema={schema} components={{ ArrayTable }} scope={{ createOnly: true, useCreateCollectionField }} />
<SchemaComponent schema={schema} components={{ ...components, ArrayTable }} scope={{ createOnly: true, useCreateCollectionField }} />
</ActionContext.Provider>
);
};

View File

@ -11,6 +11,7 @@ import { RecordProvider } from '../../record-provider';
import { ActionContext, SchemaComponent, useActionContext, useCompile } from '../../schema-component';
import { useCollectionManager } from '../hooks';
import { IField } from '../interfaces/types';
import * as components from './components';
import { options } from './interfaces';
const getSchema = (schema: IField): ISchema => {
@ -133,7 +134,7 @@ export const AddSubFieldAction = () => {
</Button>
</Dropdown>
<RecordProvider record={{}}>
<SchemaComponent schema={schema} components={{ ArrayTable }} scope={{ createOnly: true, useCreateSubField }} />
<SchemaComponent schema={schema} components={{ ...components, ArrayTable }} scope={{ createOnly: true, useCreateSubField }} />
</RecordProvider>
</ActionContext.Provider>
);

View File

@ -10,6 +10,7 @@ import { ActionContext, SchemaComponent } from '../../schema-component';
import { useUpdateAction } from '../action-hooks';
import { useCollectionManager } from '../hooks';
import { IField } from '../interfaces/types';
import * as components from './components';
const getSchema = (schema: IField): ISchema => {
if (!schema) {
@ -133,7 +134,11 @@ export const EditFieldAction = (props) => {
>
{t('Edit')}
</a>
<SchemaComponent schema={schema} components={{ ArrayTable }} scope={{ useUpdateCollectionField }} />
<SchemaComponent
schema={schema}
components={{ ...components, ArrayTable }}
scope={{ useUpdateCollectionField }}
/>
</ActionContext.Provider>
);
};

View File

@ -10,6 +10,7 @@ import { ActionContext, SchemaComponent } from '../../schema-component';
import { useUpdateAction } from '../action-hooks';
import { useCollectionManager } from '../hooks';
import { IField } from '../interfaces/types';
import * as components from './components';
const getSchema = (schema: IField): ISchema => {
if (!schema) {
@ -114,7 +115,7 @@ export const EditSubFieldAction = (props) => {
>
{t('Edit')}
</a>
<SchemaComponent schema={schema} components={{ ArrayTable }} scope={{ useUpdateCollectionField }} />
<SchemaComponent schema={schema} components={{ ...components, ArrayTable }} scope={{ useUpdateCollectionField }} />
</ActionContext.Provider>
);
};

View File

@ -0,0 +1,103 @@
import { Field } from '@formily/core';
import { observer, useField, useForm } from '@formily/react';
import { Select } from 'antd';
import React from 'react';
import { useRecord } from '../../../record-provider';
import { useCollectionManager } from '../../hooks';
export const SourceForeignKey = observer(() => {
const record = useRecord();
const { getCollection } = useCollectionManager();
const collection = record?.collectionName ? getCollection(record.collectionName) : record;
const field = useField<Field>();
const form = useForm();
const { getCollectionFields } = useCollectionManager();
return (
<div>
<Select
allowClear
placeholder={'留空时,自动生成 FK 字段'}
disabled={field.disabled}
value={field.value}
options={getCollectionFields(collection.name).filter(field => field.type).map((field) => {
return {
label: field?.uiSchema?.title || field.name,
value: field.name,
};
})}
/>
</div>
);
});
export const ThroughForeignKey = observer(() => {
const field = useField<Field>();
const form = useForm();
const { getCollectionFields } = useCollectionManager();
return (
<div>
<Select
allowClear
placeholder={'留空时,自动生成 FK 字段'}
disabled={field.disabled}
value={field.value}
options={getCollectionFields(form.values.through).filter(field => field.type).map((field) => {
return {
label: field?.uiSchema?.title || field.name,
value: field.name,
};
})}
/>
</div>
);
});
export const TargetForeignKey = observer(() => {
const field = useField<Field>();
const form = useForm();
const { getCollectionFields } = useCollectionManager();
return (
<div>
<Select
allowClear
placeholder={'留空时,自动生成 FK 字段'}
disabled={field.disabled}
value={field.value}
options={getCollectionFields(form.values.target).filter(field => field.type).map((field) => {
return {
label: field?.uiSchema?.title || field.name,
value: field.name,
};
})}
/>
</div>
);
});
export const SourceCollection = observer(() => {
const record = useRecord();
const { getCollection } = useCollectionManager();
const collection = record?.collectionName ? getCollection(record.collectionName) : record;
return (
<div>
<Select disabled value={collection.name} options={[{ value: collection.name, label: collection.title }]} />
</div>
);
});
export const SourceKey = observer(() => {
return (
<div>
<Select disabled value={'id'} options={[{ value: 'id', label: 'ID' }]} />
</div>
);
});
export const TargetKey = observer(() => {
return (
<div>
<Select disabled value={'id'} options={[{ value: 'id', label: 'ID' }]} />
</div>
);
});

View File

@ -0,0 +1,50 @@
import { Switch } from 'antd';
import React from 'react';
export const TargetKey = () => {
return (
<div>
Target key
</div>
);
};
export const ThroughCollection = () => {
return (
<div>
Through collection <Switch size={'small'} defaultChecked checkedChildren="Auto fill" unCheckedChildren="Customize"/>
</div>
);
};
export const SourceKey = () => {
return (
<div>
Source key
</div>
);
};
export const ForeignKey = () => {
return (
<div>
Foreign key <Switch size={'small'} defaultChecked checkedChildren="Auto fill" unCheckedChildren="Customize"/>
</div>
);
};
export const ForeignKey1 = () => {
return (
<div>
Foreign key 1 <a></a>
</div>
);
};
export const ForeignKey2 = () => {
return (
<div>
Foreign key 2 <a></a>
</div>
);
};

View File

@ -10,9 +10,13 @@ export * from './icon';
export * from './id';
export * from './input';
export * from './linkTo';
export * from './m2m';
export * from './m2o';
export * from './markdown';
export * from './multipleSelect';
export * from './number';
export * from './o2m';
export * from './o2o';
export * from './password';
export * from './percent';
export * from './phone';

View File

@ -0,0 +1,244 @@
import { ISchema } from '@formily/react';
import { uid } from '@formily/shared';
import { cloneDeep } from 'lodash';
import { defaultProps, recordPickerSelector, recordPickerViewer } from './properties';
import { IField } from './types';
export const m2m: IField = {
name: 'm2m',
type: 'object',
group: 'relation',
order: 6,
title: '{{t("Many to many")}}',
isAssociation: true,
default: {
type: 'belongsToMany',
// name,
uiSchema: {
// title,
'x-component': 'RecordPicker',
'x-component-props': {
// mode: 'tags',
multiple: true,
fieldNames: {
label: 'id',
value: 'id',
},
},
},
reverseField: {
interface: 'm2m',
type: 'belongsToMany',
// name,
uiSchema: {
// title,
'x-component': 'RecordPicker',
'x-component-props': {
// mode: 'tags',
multiple: true,
fieldNames: {
label: 'id',
value: 'id',
},
},
},
},
},
schemaInitialize(schema: ISchema, { readPretty }) {
if (readPretty) {
schema['properties'] = {
viewer: cloneDeep(recordPickerViewer),
};
} else {
schema['properties'] = {
selector: cloneDeep(recordPickerSelector),
};
}
},
initialize: (values: any) => {
if (values.type === 'belongsToMany') {
if (!values.through) {
values.through = `t_${uid()}`;
}
if (!values.foreignKey) {
values.foreignKey = `f_${uid()}`;
}
if (!values.otherKey) {
values.otherKey = `f_${uid()}`;
}
if (!values.sourceKey) {
values.sourceKey = 'id';
}
if (!values.targetKey) {
values.targetKey = 'id';
}
}
},
properties: {
...defaultProps,
type: {
type: 'string',
title: '{{t("Relationship type")}}',
required: true,
'x-disabled': true,
'x-decorator': 'FormItem',
'x-component': 'Select',
enum: [
{ label: 'One to one', value: 'hasOne' },
{ label: 'One to many', value: 'hasMany' },
{ label: 'Many to one', value: 'belongsTo' },
{ label: 'Many to many', value: 'belongsToMany' },
],
},
grid: {
type: 'void',
'x-component': 'Grid',
properties: {
row1: {
type: 'void',
'x-component': 'Grid.Row',
properties: {
col11: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
source: {
type: 'string',
title: '{{t("Source collection")}}',
'x-decorator': 'FormItem',
'x-component': 'SourceCollection',
'x-disabled': true,
},
},
},
col12: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
through: {
type: 'string',
title: '{{t("Through collection")}}',
'x-disabled': '{{ !createOnly }}',
'x-reactions': ['{{useAsyncDataSource(loadCollections)}}'],
'x-decorator': 'FormItem',
'x-component': 'Select',
'x-component-props': {
allowClear: true,
placeholder: '留空时,自动生成中间表'
},
},
},
},
col13: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
target: {
type: 'string',
title: '{{t("Target collection")}}',
required: true,
'x-reactions': ['{{useAsyncDataSource(loadCollections)}}'],
'x-decorator': 'FormItem',
'x-component': 'Select',
'x-disabled': '{{ !createOnly }}',
},
},
},
},
},
row2: {
type: 'void',
'x-component': 'Grid.Row',
properties: {
col21: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
sourceKey: {
type: 'void',
title: '{{t("Source key")}}',
'x-decorator': 'FormItem',
'x-component': 'SourceKey',
'x-disabled': '{{ !createOnly }}',
},
},
},
col22: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
foreignKey: {
type: 'string',
title: '{{t("Foreign key 1")}}',
'x-decorator': 'FormItem',
'x-component': 'ThroughForeignKey',
'x-disabled': '{{ !createOnly }}',
},
},
},
col23: {
type: 'void',
'x-component': 'Grid.Col',
properties: {},
},
},
},
row3: {
type: 'void',
'x-component': 'Grid.Row',
properties: {
col21: {
type: 'void',
'x-component': 'Grid.Col',
properties: {},
},
col22: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
otherKey: {
type: 'string',
title: '{{t("Foreign key 2")}}',
'x-decorator': 'FormItem',
'x-component': 'ThroughForeignKey',
'x-disabled': '{{ !createOnly }}',
},
},
},
col23: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
targetKey: {
type: 'void',
title: '{{t("Target key")}}',
'x-decorator': 'FormItem',
'x-component': 'TargetKey',
'x-disabled': '{{ !createOnly }}',
},
},
},
},
},
},
},
},
filterable: {
nested: true,
children: [
// {
// name: 'id',
// title: '{{t("Exists")}}',
// operators: [
// { label: '{{t("exists")}}', value: '$exists', noValue: true },
// { label: '{{t("not exists")}}', value: '$notExists', noValue: true },
// ],
// schema: {
// title: '{{t("Exists")}}',
// type: 'string',
// 'x-component': 'Input',
// },
// },
],
},
};

View File

@ -0,0 +1,200 @@
import { ISchema } from '@formily/react';
import { uid } from '@formily/shared';
import { cloneDeep } from 'lodash';
import { recordPickerSelector, recordPickerViewer } from './properties';
import { IField } from './types';
export const m2o: IField = {
name: 'm2o',
type: 'object',
group: 'relation',
order: 5,
title: '{{t("Many to one")}}',
isAssociation: true,
default: {
type: 'belongsTo',
// name,
uiSchema: {
// title,
'x-component': 'RecordPicker',
'x-component-props': {
// mode: 'tags',
multiple: false,
fieldNames: {
label: 'id',
value: 'id',
},
},
},
reverseField: {
interface: 'o2m',
type: 'hasMany',
// name,
uiSchema: {
// title,
'x-component': 'RecordPicker',
'x-component-props': {
// mode: 'tags',
multiple: true,
fieldNames: {
label: 'id',
value: 'id',
},
},
},
},
},
schemaInitialize(schema: ISchema, { readPretty }) {
if (readPretty) {
schema['properties'] = {
viewer: cloneDeep(recordPickerViewer),
};
} else {
schema['properties'] = {
selector: cloneDeep(recordPickerSelector),
};
}
},
initialize: (values: any) => {
if (values.type === 'belongsToMany') {
if (!values.through) {
values.through = `t_${uid()}`;
}
if (!values.foreignKey) {
values.foreignKey = `f_${uid()}`;
}
if (!values.otherKey) {
values.otherKey = `f_${uid()}`;
}
if (!values.sourceKey) {
values.sourceKey = 'id';
}
if (!values.targetKey) {
values.targetKey = 'id';
}
}
},
properties: {
'uiSchema.title': {
type: 'string',
title: '{{t("Field display name")}}',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
name: {
type: 'string',
title: '{{t("Field name")}}',
required: true,
'x-disabled': '{{ !createOnly }}',
'x-decorator': 'FormItem',
'x-component': 'Input',
description:
"{{t('Randomly generated and can be modified. Support letters, numbers and underscores, must start with an letter.')}}",
},
type: {
type: 'string',
title: '{{t("Relationship type")}}',
required: true,
'x-disabled': true,
'x-decorator': 'FormItem',
'x-component': 'Select',
enum: [
{ label: 'One to one', value: 'hasOne' },
{ label: 'One to many', value: 'hasMany' },
{ label: 'Many to one', value: 'belongsTo' },
{ label: 'Many to many', value: 'belongsToMany' },
],
},
grid: {
type: 'void',
'x-component': 'Grid',
properties: {
row1: {
type: 'void',
'x-component': 'Grid.Row',
properties: {
col11: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
source: {
type: 'void',
title: '{{t("Source collection")}}',
'x-decorator': 'FormItem',
'x-component': 'SourceCollection',
'x-disabled': true,
},
},
},
col12: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
target: {
type: 'string',
title: '{{t("Target collection")}}',
required: true,
'x-reactions': ['{{useAsyncDataSource(loadCollections)}}'],
'x-decorator': 'FormItem',
'x-component': 'Select',
'x-disabled': '{{ !createOnly }}',
},
},
},
},
},
row2: {
type: 'void',
'x-component': 'Grid.Row',
properties: {
col21: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
foreignKey: {
type: 'string',
title: '{{t("Foreign key")}}',
'x-decorator': 'FormItem',
'x-component': 'SourceForeignKey',
'x-disabled': '{{ !createOnly }}',
},
},
},
col22: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
targetKey: {
type: 'void',
title: '{{t("Target key")}}',
'x-decorator': 'FormItem',
'x-component': 'TargetKey',
'x-disabled': '{{ !createOnly }}',
},
},
},
},
},
},
},
},
filterable: {
nested: true,
children: [
// {
// name: 'id',
// title: '{{t("Exists")}}',
// operators: [
// { label: '{{t("exists")}}', value: '$exists', noValue: true },
// { label: '{{t("not exists")}}', value: '$notExists', noValue: true },
// ],
// schema: {
// title: '{{t("Exists")}}',
// type: 'string',
// 'x-component': 'Input',
// },
// },
],
},
};

View File

@ -0,0 +1,224 @@
import { ISchema } from '@formily/react';
import { uid } from '@formily/shared';
import { cloneDeep } from 'lodash';
import { recordPickerSelector, recordPickerViewer } from './properties';
import { IField } from './types';
export const o2m: IField = {
name: 'o2m',
type: 'object',
group: 'relation',
order: 4,
title: '{{t("One to many")}}',
isAssociation: true,
default: {
type: 'hasMany',
// name,
uiSchema: {
// title,
'x-component': 'RecordPicker',
'x-component-props': {
// mode: 'tags',
multiple: true,
fieldNames: {
label: 'id',
value: 'id',
},
},
},
reverseField: {
interface: 'm2o',
type: 'belongsTo',
// name,
uiSchema: {
// title,
'x-component': 'RecordPicker',
'x-component-props': {
// mode: 'tags',
multiple: true,
fieldNames: {
label: 'id',
value: 'id',
},
},
},
},
},
schemaInitialize(schema: ISchema, { readPretty }) {
if (readPretty) {
schema['properties'] = {
viewer: cloneDeep(recordPickerViewer),
};
} else {
schema['properties'] = {
selector: cloneDeep(recordPickerSelector),
};
}
},
initialize: (values: any) => {
if (values.type === 'belongsToMany') {
if (!values.through) {
values.through = `t_${uid()}`;
}
if (!values.foreignKey) {
values.foreignKey = `f_${uid()}`;
}
if (!values.otherKey) {
values.otherKey = `f_${uid()}`;
}
if (!values.sourceKey) {
values.sourceKey = 'id';
}
if (!values.targetKey) {
values.targetKey = 'id';
}
}
},
properties: {
'uiSchema.title': {
type: 'string',
title: '{{t("Field display name")}}',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
name: {
type: 'string',
title: '{{t("Field name")}}',
required: true,
'x-disabled': '{{ !createOnly }}',
'x-decorator': 'FormItem',
'x-component': 'Input',
description:
"{{t('Randomly generated and can be modified. Support letters, numbers and underscores, must start with an letter.')}}",
},
type: {
type: 'string',
title: '{{t("Relationship type")}}',
required: true,
'x-disabled': true,
'x-decorator': 'FormItem',
'x-component': 'Select',
enum: [
{ label: 'One to one', value: 'hasOne' },
{ label: 'One to many', value: 'hasMany' },
{ label: 'Many to one', value: 'belongsTo' },
{ label: 'Many to many', value: 'belongsToMany' },
],
},
grid: {
type: 'void',
'x-component': 'Grid',
properties: {
row1: {
type: 'void',
'x-component': 'Grid.Row',
properties: {
col11: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
source: {
type: 'void',
title: '{{t("Source collection")}}',
'x-decorator': 'FormItem',
'x-component': 'SourceCollection',
},
},
},
col12: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
target: {
type: 'string',
title: '{{t("Target collection")}}',
required: true,
'x-reactions': ['{{useAsyncDataSource(loadCollections)}}'],
'x-decorator': 'FormItem',
'x-component': 'Select',
'x-disabled': '{{ !createOnly }}',
},
},
},
},
},
row2: {
type: 'void',
'x-component': 'Grid.Row',
properties: {
col21: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
sourceKey: {
type: 'void',
title: '{{t("Source key")}}',
default: 'id',
enum: [{ label: 'ID', value: 'id' }],
'x-decorator': 'FormItem',
'x-component': 'SourceKey',
},
},
},
col22: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
foreignKey: {
type: 'string',
title: '{{t("Foreign key")}}',
'x-decorator': 'FormItem',
'x-component': 'TargetForeignKey',
},
},
},
},
},
row3: {
type: 'void',
'x-component': 'Grid.Row',
properties: {
col21: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
},
},
col22: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
targetKey: {
type: 'void',
title: '{{t("Target key")}}',
'x-decorator': 'FormItem',
'x-component': 'TargetKey',
'x-disabled': '{{ !createOnly }}',
},
},
},
},
},
},
},
},
filterable: {
nested: true,
children: [
// {
// name: 'id',
// title: '{{t("Exists")}}',
// operators: [
// { label: '{{t("exists")}}', value: '$exists', noValue: true },
// { label: '{{t("not exists")}}', value: '$notExists', noValue: true },
// ],
// schema: {
// title: '{{t("Exists")}}',
// type: 'string',
// 'x-component': 'Input',
// },
// },
],
},
};

View File

@ -0,0 +1,199 @@
import { ISchema } from '@formily/react';
import { uid } from '@formily/shared';
import { cloneDeep } from 'lodash';
import { recordPickerSelector, recordPickerViewer } from './properties';
import { IField } from './types';
export const o2o: IField = {
name: 'o2o',
type: 'object',
group: 'relation',
order: 3,
title: '{{t("One to one")}}',
isAssociation: true,
default: {
type: 'hasOne',
// name,
uiSchema: {
// title,
'x-component': 'RecordPicker',
'x-component-props': {
// mode: 'tags',
multiple: false,
fieldNames: {
label: 'id',
value: 'id',
},
},
},
reverseField: {
interface: 'm2o',
type: 'belongsTo',
// name,
uiSchema: {
// title,
'x-component': 'RecordPicker',
'x-component-props': {
// mode: 'tags',
multiple: true,
fieldNames: {
label: 'id',
value: 'id',
},
},
},
},
},
schemaInitialize(schema: ISchema, { readPretty }) {
if (readPretty) {
schema['properties'] = {
viewer: cloneDeep(recordPickerViewer),
};
} else {
schema['properties'] = {
selector: cloneDeep(recordPickerSelector),
};
}
},
initialize: (values: any) => {
if (values.type === 'belongsToMany') {
if (!values.through) {
values.through = `t_${uid()}`;
}
if (!values.foreignKey) {
values.foreignKey = `f_${uid()}`;
}
if (!values.otherKey) {
values.otherKey = `f_${uid()}`;
}
if (!values.sourceKey) {
values.sourceKey = 'id';
}
if (!values.targetKey) {
values.targetKey = 'id';
}
}
},
properties: {
'uiSchema.title': {
type: 'string',
title: '{{t("Field display name")}}',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
name: {
type: 'string',
title: '{{t("Field name")}}',
required: true,
'x-disabled': '{{ !createOnly }}',
'x-decorator': 'FormItem',
'x-component': 'Input',
description:
"{{t('Randomly generated and can be modified. Support letters, numbers and underscores, must start with an letter.')}}",
},
type: {
type: 'string',
title: '{{t("Relationship type")}}',
required: true,
'x-disabled': true,
'x-decorator': 'FormItem',
'x-component': 'Select',
enum: [
{ label: 'One to one', value: 'hasOne' },
{ label: 'One to many', value: 'hasMany' },
{ label: 'Many to one', value: 'belongsTo' },
{ label: 'Many to many', value: 'belongsToMany' },
],
},
grid: {
type: 'void',
'x-component': 'Grid',
properties: {
row1: {
type: 'void',
'x-component': 'Grid.Row',
properties: {
col11: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
source: {
type: 'void',
title: '{{t("Source collection")}}',
'x-decorator': 'FormItem',
'x-component': 'SourceCollection',
'x-disabled': true,
},
},
},
col12: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
target: {
type: 'string',
title: '{{t("Target collection")}}',
required: true,
'x-reactions': ['{{useAsyncDataSource(loadCollections)}}'],
'x-decorator': 'FormItem',
'x-component': 'Select',
'x-disabled': '{{ !createOnly }}',
},
},
},
},
},
row2: {
type: 'void',
'x-component': 'Grid.Row',
properties: {
col21: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
sourceKey: {
type: 'void',
title: '{{t("Source key")}}',
'x-decorator': 'FormItem',
'x-component': 'SourceKey',
},
},
},
col22: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
foreignKey: {
type: 'string',
title: '{{t("Foreign key")}}',
'x-decorator': 'FormItem',
'x-component': 'TargetForeignKey',
'x-disabled': '{{ !createOnly }}',
},
},
},
},
},
},
},
},
filterable: {
nested: true,
children: [
// {
// name: 'id',
// title: '{{t("Exists")}}',
// operators: [
// { label: '{{t("exists")}}', value: '$exists', noValue: true },
// { label: '{{t("not exists")}}', value: '$notExists', noValue: true },
// ],
// schema: {
// title: '{{t("Exists")}}',
// type: 'string',
// 'x-component': 'Input',
// },
// },
],
},
};