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 { useCreateAction } from '../action-hooks';
|
||||||
import { useCollectionManager } from '../hooks';
|
import { useCollectionManager } from '../hooks';
|
||||||
import { IField } from '../interfaces/types';
|
import { IField } from '../interfaces/types';
|
||||||
|
import * as components from './components';
|
||||||
import { options } from './interfaces';
|
import { options } from './interfaces';
|
||||||
|
|
||||||
const getSchema = (schema: IField): ISchema => {
|
const getSchema = (schema: IField): ISchema => {
|
||||||
@ -160,7 +161,7 @@ export const AddFieldAction = () => {
|
|||||||
{t('Add field')}
|
{t('Add field')}
|
||||||
</Button>
|
</Button>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
<SchemaComponent schema={schema} components={{ ArrayTable }} scope={{ createOnly: true, useCreateCollectionField }} />
|
<SchemaComponent schema={schema} components={{ ...components, ArrayTable }} scope={{ createOnly: true, useCreateCollectionField }} />
|
||||||
</ActionContext.Provider>
|
</ActionContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -11,6 +11,7 @@ import { RecordProvider } from '../../record-provider';
|
|||||||
import { ActionContext, SchemaComponent, useActionContext, useCompile } from '../../schema-component';
|
import { ActionContext, SchemaComponent, useActionContext, useCompile } from '../../schema-component';
|
||||||
import { useCollectionManager } from '../hooks';
|
import { useCollectionManager } from '../hooks';
|
||||||
import { IField } from '../interfaces/types';
|
import { IField } from '../interfaces/types';
|
||||||
|
import * as components from './components';
|
||||||
import { options } from './interfaces';
|
import { options } from './interfaces';
|
||||||
|
|
||||||
const getSchema = (schema: IField): ISchema => {
|
const getSchema = (schema: IField): ISchema => {
|
||||||
@ -133,7 +134,7 @@ export const AddSubFieldAction = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
<RecordProvider record={{}}>
|
<RecordProvider record={{}}>
|
||||||
<SchemaComponent schema={schema} components={{ ArrayTable }} scope={{ createOnly: true, useCreateSubField }} />
|
<SchemaComponent schema={schema} components={{ ...components, ArrayTable }} scope={{ createOnly: true, useCreateSubField }} />
|
||||||
</RecordProvider>
|
</RecordProvider>
|
||||||
</ActionContext.Provider>
|
</ActionContext.Provider>
|
||||||
);
|
);
|
||||||
|
@ -10,6 +10,7 @@ import { ActionContext, SchemaComponent } from '../../schema-component';
|
|||||||
import { useUpdateAction } from '../action-hooks';
|
import { useUpdateAction } from '../action-hooks';
|
||||||
import { useCollectionManager } from '../hooks';
|
import { useCollectionManager } from '../hooks';
|
||||||
import { IField } from '../interfaces/types';
|
import { IField } from '../interfaces/types';
|
||||||
|
import * as components from './components';
|
||||||
|
|
||||||
const getSchema = (schema: IField): ISchema => {
|
const getSchema = (schema: IField): ISchema => {
|
||||||
if (!schema) {
|
if (!schema) {
|
||||||
@ -133,7 +134,11 @@ export const EditFieldAction = (props) => {
|
|||||||
>
|
>
|
||||||
{t('Edit')}
|
{t('Edit')}
|
||||||
</a>
|
</a>
|
||||||
<SchemaComponent schema={schema} components={{ ArrayTable }} scope={{ useUpdateCollectionField }} />
|
<SchemaComponent
|
||||||
|
schema={schema}
|
||||||
|
components={{ ...components, ArrayTable }}
|
||||||
|
scope={{ useUpdateCollectionField }}
|
||||||
|
/>
|
||||||
</ActionContext.Provider>
|
</ActionContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -10,6 +10,7 @@ import { ActionContext, SchemaComponent } from '../../schema-component';
|
|||||||
import { useUpdateAction } from '../action-hooks';
|
import { useUpdateAction } from '../action-hooks';
|
||||||
import { useCollectionManager } from '../hooks';
|
import { useCollectionManager } from '../hooks';
|
||||||
import { IField } from '../interfaces/types';
|
import { IField } from '../interfaces/types';
|
||||||
|
import * as components from './components';
|
||||||
|
|
||||||
const getSchema = (schema: IField): ISchema => {
|
const getSchema = (schema: IField): ISchema => {
|
||||||
if (!schema) {
|
if (!schema) {
|
||||||
@ -114,7 +115,7 @@ export const EditSubFieldAction = (props) => {
|
|||||||
>
|
>
|
||||||
{t('Edit')}
|
{t('Edit')}
|
||||||
</a>
|
</a>
|
||||||
<SchemaComponent schema={schema} components={{ ArrayTable }} scope={{ useUpdateCollectionField }} />
|
<SchemaComponent schema={schema} components={{ ...components, ArrayTable }} scope={{ useUpdateCollectionField }} />
|
||||||
</ActionContext.Provider>
|
</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 './id';
|
||||||
export * from './input';
|
export * from './input';
|
||||||
export * from './linkTo';
|
export * from './linkTo';
|
||||||
|
export * from './m2m';
|
||||||
|
export * from './m2o';
|
||||||
export * from './markdown';
|
export * from './markdown';
|
||||||
export * from './multipleSelect';
|
export * from './multipleSelect';
|
||||||
export * from './number';
|
export * from './number';
|
||||||
|
export * from './o2m';
|
||||||
|
export * from './o2o';
|
||||||
export * from './password';
|
export * from './password';
|
||||||
export * from './percent';
|
export * from './percent';
|
||||||
export * from './phone';
|
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