feat: relationships (#473)
* feat: relationship fields * feat: improve schema
This commit is contained in:
parent
4e19571c08
commit
000e4e50b8
@ -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>
|
||||
);
|
||||
};
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
@ -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>
|
||||
);
|
||||
});
|
@ -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>
|
||||
);
|
||||
};
|
@ -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';
|
||||
|
244
packages/core/client/src/collection-manager/interfaces/m2m.tsx
Normal file
244
packages/core/client/src/collection-manager/interfaces/m2m.tsx
Normal 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',
|
||||
// },
|
||||
// },
|
||||
],
|
||||
},
|
||||
};
|
200
packages/core/client/src/collection-manager/interfaces/m2o.tsx
Normal file
200
packages/core/client/src/collection-manager/interfaces/m2o.tsx
Normal 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',
|
||||
// },
|
||||
// },
|
||||
],
|
||||
},
|
||||
};
|
224
packages/core/client/src/collection-manager/interfaces/o2m.tsx
Normal file
224
packages/core/client/src/collection-manager/interfaces/o2m.tsx
Normal 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',
|
||||
// },
|
||||
// },
|
||||
],
|
||||
},
|
||||
};
|
199
packages/core/client/src/collection-manager/interfaces/o2o.tsx
Normal file
199
packages/core/client/src/collection-manager/interfaces/o2o.tsx
Normal 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',
|
||||
// },
|
||||
// },
|
||||
],
|
||||
},
|
||||
};
|
Loading…
Reference in New Issue
Block a user