feat(collection-manager): inverse fields can be configured (#883)
* feat: inverse field * feat: improve code * feat: translations * fix: required (cherry picked from commit 306dca8e5ee1b74562d835a816b72f933465bfb6) # Conflicts: # packages/core/client/src/locale/zh_CN.ts
This commit is contained in:
		
							parent
							
								
									933c3f4463
								
							
						
					
					
						commit
						f6add23da5
					
				@ -8,10 +8,11 @@ import React, { useState } from 'react';
 | 
				
			|||||||
import { useTranslation } from 'react-i18next';
 | 
					import { useTranslation } from 'react-i18next';
 | 
				
			||||||
import { useRequest } from '../../api-client';
 | 
					import { useRequest } from '../../api-client';
 | 
				
			||||||
import { useRecord } from '../../record-provider';
 | 
					import { useRecord } from '../../record-provider';
 | 
				
			||||||
import { ActionContext, SchemaComponent, useCompile } from '../../schema-component';
 | 
					import { ActionContext, SchemaComponent, useActionContext, useCompile } from '../../schema-component';
 | 
				
			||||||
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 { useResourceActionContext, useResourceContext } from '../ResourceActionProvider';
 | 
				
			||||||
import * as components from './components';
 | 
					import * as components from './components';
 | 
				
			||||||
import { options } from './interfaces';
 | 
					import { options } from './interfaces';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -28,11 +29,14 @@ const getSchema = (schema: IField, record: any, compile): ISchema => {
 | 
				
			|||||||
    properties['defaultValue']['x-decorator'] = 'FormItem';
 | 
					    properties['defaultValue']['x-decorator'] = 'FormItem';
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const initialValue = {
 | 
					  const initialValue: any = {
 | 
				
			||||||
    name: `f_${uid()}`,
 | 
					    name: `f_${uid()}`,
 | 
				
			||||||
    ...cloneDeep(schema.default),
 | 
					    ...cloneDeep(schema.default),
 | 
				
			||||||
    interface: schema.name,
 | 
					    interface: schema.name,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					  if (initialValue.reverseField) {
 | 
				
			||||||
 | 
					    initialValue.reverseField.name = `f_${uid()}`;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  // initialValue.uiSchema.title = schema.title;
 | 
					  // initialValue.uiSchema.title = schema.title;
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    type: 'object',
 | 
					    type: 'object',
 | 
				
			||||||
@ -95,44 +99,22 @@ const useCreateCollectionField = () => {
 | 
				
			|||||||
  const { run } = useCreateAction();
 | 
					  const { run } = useCreateAction();
 | 
				
			||||||
  const { refreshCM } = useCollectionManager();
 | 
					  const { refreshCM } = useCollectionManager();
 | 
				
			||||||
  const { title } = useRecord();
 | 
					  const { title } = useRecord();
 | 
				
			||||||
 | 
					  const ctx = useActionContext();
 | 
				
			||||||
 | 
					  const { refresh } = useResourceActionContext();
 | 
				
			||||||
 | 
					  const { resource } = useResourceContext();
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    async run() {
 | 
					    async run() {
 | 
				
			||||||
      await form.submit();
 | 
					      await form.submit();
 | 
				
			||||||
      // const options = form?.values?.uiSchema?.enum?.slice() || [];
 | 
					      const values = cloneDeep(form.values);
 | 
				
			||||||
      // if (options?.length) {
 | 
					      if (values.autoCreateReverseField) {
 | 
				
			||||||
      //   form.setValuesIn(
 | 
					      } else {
 | 
				
			||||||
      //     'uiSchema.enum',
 | 
					        delete values.reverseField;
 | 
				
			||||||
      //     options.map((option) => {
 | 
					 | 
				
			||||||
      //       return {
 | 
					 | 
				
			||||||
      //         value: uid(),
 | 
					 | 
				
			||||||
      //         ...option,
 | 
					 | 
				
			||||||
      //       };
 | 
					 | 
				
			||||||
      //     }),
 | 
					 | 
				
			||||||
      //   );
 | 
					 | 
				
			||||||
      // }
 | 
					 | 
				
			||||||
      // function recursiveChildren(children = [], prefix = 'children') {
 | 
					 | 
				
			||||||
      //   children.forEach((item, index) => {
 | 
					 | 
				
			||||||
      //     const itemOptions = item.uiSchema?.enum?.slice() || [];
 | 
					 | 
				
			||||||
      //     form.setValuesIn(
 | 
					 | 
				
			||||||
      //       `${prefix}[${index}].uiSchema.enum`,
 | 
					 | 
				
			||||||
      //       itemOptions.map((option) => {
 | 
					 | 
				
			||||||
      //         return {
 | 
					 | 
				
			||||||
      //           value: uid(),
 | 
					 | 
				
			||||||
      //           ...option,
 | 
					 | 
				
			||||||
      //         };
 | 
					 | 
				
			||||||
      //       }),
 | 
					 | 
				
			||||||
      //     );
 | 
					 | 
				
			||||||
      //     recursiveChildren(item.children, `${prefix}[${index}].children`);
 | 
					 | 
				
			||||||
      //   });
 | 
					 | 
				
			||||||
      // }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      // recursiveChildren(form?.values?.children);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (['obo', 'oho', 'o2o', 'o2m', 'm2o', 'm2m', 'linkTo'].includes(form?.values?.interface) && title) {
 | 
					 | 
				
			||||||
        form.setValuesIn('reverseField.uiSchema.title', title);
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      delete values.autoCreateReverseField;
 | 
				
			||||||
      await run();
 | 
					      await resource.create({ values });
 | 
				
			||||||
 | 
					      ctx.setVisible(false);
 | 
				
			||||||
 | 
					      await form.reset();
 | 
				
			||||||
 | 
					      refresh();
 | 
				
			||||||
      await refreshCM();
 | 
					      await refreshCM();
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
@ -183,7 +165,7 @@ export const AddFieldAction = () => {
 | 
				
			|||||||
      <SchemaComponent
 | 
					      <SchemaComponent
 | 
				
			||||||
        schema={schema}
 | 
					        schema={schema}
 | 
				
			||||||
        components={{ ...components, ArrayTable }}
 | 
					        components={{ ...components, ArrayTable }}
 | 
				
			||||||
        scope={{ createOnly: true, useCreateCollectionField }}
 | 
					        scope={{ createOnly: true, useCreateCollectionField, record, showReverseFieldConfig: true }}
 | 
				
			||||||
      />
 | 
					      />
 | 
				
			||||||
    </ActionContext.Provider>
 | 
					    </ActionContext.Provider>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
				
			|||||||
@ -2,16 +2,17 @@ import { ArrayTable } from '@formily/antd';
 | 
				
			|||||||
import { ISchema, useForm } from '@formily/react';
 | 
					import { ISchema, useForm } from '@formily/react';
 | 
				
			||||||
import { uid } from '@formily/shared';
 | 
					import { uid } from '@formily/shared';
 | 
				
			||||||
import cloneDeep from 'lodash/cloneDeep';
 | 
					import cloneDeep from 'lodash/cloneDeep';
 | 
				
			||||||
 | 
					import set from 'lodash/set';
 | 
				
			||||||
import React, { useState } from 'react';
 | 
					import React, { useState } from 'react';
 | 
				
			||||||
import { useTranslation } from 'react-i18next';
 | 
					import { useTranslation } from 'react-i18next';
 | 
				
			||||||
import { useAPIClient, useRequest } from '../../api-client';
 | 
					import { useAPIClient, useRequest } from '../../api-client';
 | 
				
			||||||
import { useRecord } from '../../record-provider';
 | 
					import { useRecord } from '../../record-provider';
 | 
				
			||||||
import { ActionContext, SchemaComponent, useCompile } from '../../schema-component';
 | 
					import { ActionContext, SchemaComponent, useActionContext, useCompile } 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 { useResourceActionContext, useResourceContext } from '../ResourceActionProvider';
 | 
				
			||||||
import * as components from './components';
 | 
					import * as components from './components';
 | 
				
			||||||
import { Typography } from 'antd';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const getSchema = (schema: IField, record: any, compile): ISchema => {
 | 
					const getSchema = (schema: IField, record: any, compile): ISchema => {
 | 
				
			||||||
  if (!schema) {
 | 
					  if (!schema) {
 | 
				
			||||||
@ -50,8 +51,8 @@ const getSchema = (schema: IField, record: any, compile): ISchema => {
 | 
				
			|||||||
            type: 'void',
 | 
					            type: 'void',
 | 
				
			||||||
            'x-component': 'FieldSummary',
 | 
					            'x-component': 'FieldSummary',
 | 
				
			||||||
            'x-component-props': {
 | 
					            'x-component-props': {
 | 
				
			||||||
              schemaKey: schema.name
 | 
					              schemaKey: schema.name,
 | 
				
			||||||
            }
 | 
					            },
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          // @ts-ignore
 | 
					          // @ts-ignore
 | 
				
			||||||
          ...properties,
 | 
					          ...properties,
 | 
				
			||||||
@ -86,39 +87,23 @@ const useUpdateCollectionField = () => {
 | 
				
			|||||||
  const form = useForm();
 | 
					  const form = useForm();
 | 
				
			||||||
  const { run } = useUpdateAction();
 | 
					  const { run } = useUpdateAction();
 | 
				
			||||||
  const { refreshCM } = useCollectionManager();
 | 
					  const { refreshCM } = useCollectionManager();
 | 
				
			||||||
 | 
					  const ctx = useActionContext();
 | 
				
			||||||
 | 
					  const { refresh } = useResourceActionContext();
 | 
				
			||||||
 | 
					  const { resource, targetKey } = useResourceContext();
 | 
				
			||||||
 | 
					  const { [targetKey]: filterByTk } = useRecord();
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    async run() {
 | 
					    async run() {
 | 
				
			||||||
      await form.submit();
 | 
					      await form.submit();
 | 
				
			||||||
      const options = form?.values?.uiSchema?.enum?.slice() || [];
 | 
					      const values = cloneDeep(form.values);
 | 
				
			||||||
      form.setValuesIn(
 | 
					      if (values.autoCreateReverseField) {
 | 
				
			||||||
        'uiSchema.enum',
 | 
					      } else {
 | 
				
			||||||
        options.map((option) => {
 | 
					        delete values.reverseField;
 | 
				
			||||||
          return {
 | 
					 | 
				
			||||||
            value: uid(),
 | 
					 | 
				
			||||||
            ...option,
 | 
					 | 
				
			||||||
          };
 | 
					 | 
				
			||||||
        }),
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
      
 | 
					 | 
				
			||||||
      function recursiveChildren(children = [], prefix = 'children') {
 | 
					 | 
				
			||||||
        children.forEach((item, index) => {
 | 
					 | 
				
			||||||
          const itemOptions = item.uiSchema?.enum?.slice() || [];
 | 
					 | 
				
			||||||
          form.setValuesIn(
 | 
					 | 
				
			||||||
            `${prefix}[${index}].uiSchema.enum`,
 | 
					 | 
				
			||||||
            itemOptions.map((option) => {
 | 
					 | 
				
			||||||
              return {
 | 
					 | 
				
			||||||
                value: uid(),
 | 
					 | 
				
			||||||
                ...option,
 | 
					 | 
				
			||||||
              };
 | 
					 | 
				
			||||||
            }),
 | 
					 | 
				
			||||||
          );
 | 
					 | 
				
			||||||
          recursiveChildren(item.children, `${prefix}[${index}].children`);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      delete values.autoCreateReverseField;
 | 
				
			||||||
      recursiveChildren(form?.values?.children);
 | 
					      await resource.update({ filterByTk, values });
 | 
				
			||||||
      
 | 
					      ctx.setVisible(false);
 | 
				
			||||||
      await run();
 | 
					      await form.reset();
 | 
				
			||||||
 | 
					      refresh();
 | 
				
			||||||
      await refreshCM();
 | 
					      await refreshCM();
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
@ -132,18 +117,32 @@ export const EditFieldAction = (props) => {
 | 
				
			|||||||
  const api = useAPIClient();
 | 
					  const api = useAPIClient();
 | 
				
			||||||
  const { t } = useTranslation();
 | 
					  const { t } = useTranslation();
 | 
				
			||||||
  const compile = useCompile();
 | 
					  const compile = useCompile();
 | 
				
			||||||
 | 
					  const [data, setData] = useState<any>({});
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <ActionContext.Provider value={{ visible, setVisible }}>
 | 
					    <ActionContext.Provider value={{ visible, setVisible }}>
 | 
				
			||||||
      <a
 | 
					      <a
 | 
				
			||||||
        onClick={async () => {
 | 
					        onClick={async () => {
 | 
				
			||||||
          const { data } = await api.resource('collections.fields', record.collectionName).get({
 | 
					          const { data } = await api.resource('collections.fields', record.collectionName).get({
 | 
				
			||||||
            filterByTk: record.name,
 | 
					            filterByTk: record.name,
 | 
				
			||||||
            appends: record.interface === 'subTable' ? ['uiSchema', 'children'] : ['uiSchema'],
 | 
					            appends: ['uiSchema', 'reverseField'],
 | 
				
			||||||
          });
 | 
					          });
 | 
				
			||||||
          const schema = getSchema({
 | 
					          setData(data?.data);
 | 
				
			||||||
            ...getInterface(record.interface),
 | 
					          const interfaceConf = getInterface(record.interface);
 | 
				
			||||||
            default: data?.data,
 | 
					          const defaultValues: any = cloneDeep(data?.data) || {};
 | 
				
			||||||
          }, record, compile);
 | 
					          if (!defaultValues?.reverseField) {
 | 
				
			||||||
 | 
					            defaultValues.autoCreateReverseField = false;
 | 
				
			||||||
 | 
					            defaultValues.reverseField = interfaceConf.default?.reverseField;
 | 
				
			||||||
 | 
					            set(defaultValues.reverseField, 'name', `f_${uid()}`);
 | 
				
			||||||
 | 
					            set(defaultValues.reverseField, 'uiSchema.title', record.__parent.title);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          const schema = getSchema(
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              ...interfaceConf,
 | 
				
			||||||
 | 
					              default: defaultValues,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            record,
 | 
				
			||||||
 | 
					            compile,
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
          setSchema(schema);
 | 
					          setSchema(schema);
 | 
				
			||||||
          setVisible(true);
 | 
					          setVisible(true);
 | 
				
			||||||
        }}
 | 
					        }}
 | 
				
			||||||
@ -153,7 +152,7 @@ export const EditFieldAction = (props) => {
 | 
				
			|||||||
      <SchemaComponent
 | 
					      <SchemaComponent
 | 
				
			||||||
        schema={schema}
 | 
					        schema={schema}
 | 
				
			||||||
        components={{ ...components, ArrayTable }}
 | 
					        components={{ ...components, ArrayTable }}
 | 
				
			||||||
        scope={{ useUpdateCollectionField }}
 | 
					        scope={{ useUpdateCollectionField, showReverseFieldConfig: !data?.reverseField }}
 | 
				
			||||||
      />
 | 
					      />
 | 
				
			||||||
    </ActionContext.Provider>
 | 
					    </ActionContext.Provider>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
import { ISchema } from '@formily/react';
 | 
					import { ISchema } from '@formily/react';
 | 
				
			||||||
import { uid } from '@formily/shared';
 | 
					import { uid } from '@formily/shared';
 | 
				
			||||||
import { cloneDeep } from 'lodash';
 | 
					import { cloneDeep } from 'lodash';
 | 
				
			||||||
import { defaultProps, recordPickerSelector, recordPickerViewer, relationshipType } from './properties';
 | 
					import { defaultProps, recordPickerSelector, recordPickerViewer, relationshipType, reverseFieldProperties } from './properties';
 | 
				
			||||||
import { IField } from './types';
 | 
					import { IField } from './types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const m2m: IField = {
 | 
					export const m2m: IField = {
 | 
				
			||||||
@ -231,6 +231,7 @@ export const m2m: IField = {
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    ...reverseFieldProperties,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  filterable: {
 | 
					  filterable: {
 | 
				
			||||||
    nested: true,
 | 
					    nested: true,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
import { ISchema } from '@formily/react';
 | 
					import { ISchema } from '@formily/react';
 | 
				
			||||||
import { cloneDeep } from 'lodash';
 | 
					import { cloneDeep } from 'lodash';
 | 
				
			||||||
import { recordPickerSelector, recordPickerViewer, relationshipType } from './properties';
 | 
					import { recordPickerSelector, recordPickerViewer, relationshipType, reverseFieldProperties } from './properties';
 | 
				
			||||||
import { IField } from './types';
 | 
					import { IField } from './types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const m2o: IField = {
 | 
					export const m2o: IField = {
 | 
				
			||||||
@ -203,6 +203,7 @@ export const m2o: IField = {
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    ...reverseFieldProperties,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  filterable: {
 | 
					  filterable: {
 | 
				
			||||||
    nested: true,
 | 
					    nested: true,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
import { ISchema } from '@formily/react';
 | 
					import { ISchema } from '@formily/react';
 | 
				
			||||||
import { cloneDeep } from 'lodash';
 | 
					import { cloneDeep } from 'lodash';
 | 
				
			||||||
import { recordPickerSelector, recordPickerViewer, relationshipType } from './properties';
 | 
					import { recordPickerSelector, recordPickerViewer, relationshipType, reverseFieldProperties } from './properties';
 | 
				
			||||||
import { IField } from './types';
 | 
					import { IField } from './types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const o2m: IField = {
 | 
					export const o2m: IField = {
 | 
				
			||||||
@ -240,6 +240,7 @@ export const o2m: IField = {
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    ...reverseFieldProperties,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  filterable: {
 | 
					  filterable: {
 | 
				
			||||||
    nested: true,
 | 
					    nested: true,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
import { ISchema } from '@formily/react';
 | 
					import { ISchema } from '@formily/react';
 | 
				
			||||||
import { cloneDeep } from 'lodash';
 | 
					import { cloneDeep } from 'lodash';
 | 
				
			||||||
import { recordPickerSelector, recordPickerViewer, relationshipType } from './properties';
 | 
					import { recordPickerSelector, recordPickerViewer, relationshipType, reverseFieldProperties } from './properties';
 | 
				
			||||||
import { IField } from './types';
 | 
					import { IField } from './types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const internalSchameInitialize = (schema: ISchema, { field, block, readPretty, action }) => {
 | 
					const internalSchameInitialize = (schema: ISchema, { field, block, readPretty, action }) => {
 | 
				
			||||||
@ -37,11 +37,11 @@ const internalSchameInitialize = (schema: ISchema, { field, block, readPretty, a
 | 
				
			|||||||
                'x-initializer': 'FormItemInitializers',
 | 
					                'x-initializer': 'FormItemInitializers',
 | 
				
			||||||
                properties: {},
 | 
					                properties: {},
 | 
				
			||||||
              },
 | 
					              },
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
    }
 | 
					      },
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    schema.type = 'string';
 | 
					    schema.type = 'string';
 | 
				
			||||||
    if (block === 'Form') {
 | 
					    if (block === 'Form') {
 | 
				
			||||||
@ -57,11 +57,11 @@ const internalSchameInitialize = (schema: ISchema, { field, block, readPretty, a
 | 
				
			|||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        schema['properties'] = {
 | 
					        schema['properties'] = {
 | 
				
			||||||
          selector: cloneDeep(recordPickerSelector),
 | 
					          selector: cloneDeep(recordPickerSelector),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  }
 | 
					};
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const o2o: IField = {
 | 
					export const o2o: IField = {
 | 
				
			||||||
  name: 'o2o',
 | 
					  name: 'o2o',
 | 
				
			||||||
@ -207,6 +207,16 @@ export const o2o: IField = {
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    'reverseField.name': {
 | 
				
			||||||
 | 
					      type: 'string',
 | 
				
			||||||
 | 
					      title: '{{t("Inverse field name")}}',
 | 
				
			||||||
 | 
					      // required: true,
 | 
				
			||||||
 | 
					      'x-hidden': '{{ !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.')}}",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  filterable: {
 | 
					  filterable: {
 | 
				
			||||||
    nested: true,
 | 
					    nested: true,
 | 
				
			||||||
@ -372,6 +382,7 @@ export const oho: IField = {
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    ...reverseFieldProperties,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  filterable: {
 | 
					  filterable: {
 | 
				
			||||||
    nested: true,
 | 
					    nested: true,
 | 
				
			||||||
@ -537,6 +548,7 @@ export const obo: IField = {
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    ...reverseFieldProperties,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  filterable: {
 | 
					  filterable: {
 | 
				
			||||||
    nested: true,
 | 
					    nested: true,
 | 
				
			||||||
 | 
				
			|||||||
@ -53,6 +53,89 @@ export const relationshipType: ISchema = {
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const reverseFieldProperties: Record<string, ISchema> = {
 | 
				
			||||||
 | 
					  reverse: {
 | 
				
			||||||
 | 
					    type: 'void',
 | 
				
			||||||
 | 
					    'x-component': 'div',
 | 
				
			||||||
 | 
					    'x-hidden': '{{ !showReverseFieldConfig }}',
 | 
				
			||||||
 | 
					    properties: {
 | 
				
			||||||
 | 
					      autoCreateReverseField: {
 | 
				
			||||||
 | 
					        type: 'boolean',
 | 
				
			||||||
 | 
					        default: true,
 | 
				
			||||||
 | 
					        'x-decorator': 'FormItem',
 | 
				
			||||||
 | 
					        'x-component': 'Checkbox',
 | 
				
			||||||
 | 
					        'x-content': '{{t("Create inverse field in the target collection")}}',
 | 
				
			||||||
 | 
					        'x-reactions': [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            target: 'reverseField.type',
 | 
				
			||||||
 | 
					            when: '{{!!$self.value}}',
 | 
				
			||||||
 | 
					            fulfill: {
 | 
				
			||||||
 | 
					              state: {
 | 
				
			||||||
 | 
					                hidden: false,
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            otherwise: {
 | 
				
			||||||
 | 
					              state: {
 | 
				
			||||||
 | 
					                hidden: true,
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            target: 'reverseField.uiSchema.title',
 | 
				
			||||||
 | 
					            when: '{{!!$self.value}}',
 | 
				
			||||||
 | 
					            fulfill: {
 | 
				
			||||||
 | 
					              state: {
 | 
				
			||||||
 | 
					                hidden: false,
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            otherwise: {
 | 
				
			||||||
 | 
					              state: {
 | 
				
			||||||
 | 
					                hidden: true,
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            target: 'reverseField.name',
 | 
				
			||||||
 | 
					            when: '{{!!$self.value}}',
 | 
				
			||||||
 | 
					            fulfill: {
 | 
				
			||||||
 | 
					              state: {
 | 
				
			||||||
 | 
					                hidden: false,
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            otherwise: {
 | 
				
			||||||
 | 
					              state: {
 | 
				
			||||||
 | 
					                hidden: true,
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      'reverseField.type': {
 | 
				
			||||||
 | 
					        ...relationshipType,
 | 
				
			||||||
 | 
					        title: '{{t("Inverse relationship type")}}',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      'reverseField.uiSchema.title': {
 | 
				
			||||||
 | 
					        type: 'string',
 | 
				
			||||||
 | 
					        title: '{{t("Inverse field display name")}}',
 | 
				
			||||||
 | 
					        default: '{{record.title}}',
 | 
				
			||||||
 | 
					        required: true,
 | 
				
			||||||
 | 
					        'x-decorator': 'FormItem',
 | 
				
			||||||
 | 
					        'x-component': 'Input',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      'reverseField.name': {
 | 
				
			||||||
 | 
					        type: 'string',
 | 
				
			||||||
 | 
					        title: '{{t("Inverse field name")}}',
 | 
				
			||||||
 | 
					        required: true,
 | 
				
			||||||
 | 
					        'x-decorator': 'FormItem',
 | 
				
			||||||
 | 
					        'x-component': 'Input',
 | 
				
			||||||
 | 
					        'x-validator': 'uid',
 | 
				
			||||||
 | 
					        description:
 | 
				
			||||||
 | 
					          "{{t('Randomly generated and can be modified. Support letters, numbers and underscores, must start with an letter.')}}",
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const dateTimeProps: { [key: string]: ISchema } = {
 | 
					export const dateTimeProps: { [key: string]: ISchema } = {
 | 
				
			||||||
  'uiSchema.x-component-props.dateFormat': {
 | 
					  'uiSchema.x-component-props.dateFormat': {
 | 
				
			||||||
    type: 'string',
 | 
					    type: 'string',
 | 
				
			||||||
 | 
				
			|||||||
@ -175,7 +175,8 @@ export default {
 | 
				
			|||||||
  "Time format": "时间格式",
 | 
					  "Time format": "时间格式",
 | 
				
			||||||
  "12 hour": "12 小时制",
 | 
					  "12 hour": "12 小时制",
 | 
				
			||||||
  "24 hour": "24 小时制",
 | 
					  "24 hour": "24 小时制",
 | 
				
			||||||
  "Relationship type": "关联类型",
 | 
					  "Relationship type": "关系类型",
 | 
				
			||||||
 | 
					  "Inverse relationship type": "反向关系类型",
 | 
				
			||||||
  "Source collection": "源数据表",
 | 
					  "Source collection": "源数据表",
 | 
				
			||||||
  "Source key": "源数据表字段标识",
 | 
					  "Source key": "源数据表字段标识",
 | 
				
			||||||
  "Target collection": "目标数据表",
 | 
					  "Target collection": "目标数据表",
 | 
				
			||||||
@ -683,7 +684,9 @@ export default {
 | 
				
			|||||||
  'Marketplace': '插件市场',
 | 
					  'Marketplace': '插件市场',
 | 
				
			||||||
  'Coming soon...': '敬请期待...',
 | 
					  'Coming soon...': '敬请期待...',
 | 
				
			||||||
  'Settings center': '配置中心',
 | 
					  'Settings center': '配置中心',
 | 
				
			||||||
 | 
					 | 
				
			||||||
  'Bookmark': '书签',
 | 
					  'Bookmark': '书签',
 | 
				
			||||||
  'Manage all settings': '管理所有配置',
 | 
					  'Manage all settings': '管理所有配置',
 | 
				
			||||||
 | 
					  'Create inverse field in the target collection': '在目标数据表里创建反向关系字段',
 | 
				
			||||||
 | 
					  'Inverse field name': '反向关系字段标识',
 | 
				
			||||||
 | 
					  'Inverse field display name': '反向关系字段名称',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -41,8 +41,13 @@ export class CollectionManagerPlugin extends Plugin {
 | 
				
			|||||||
        lodash.get(newValue, 'reverseField') &&
 | 
					        lodash.get(newValue, 'reverseField') &&
 | 
				
			||||||
        !lodash.get(newValue, 'reverseField.key')
 | 
					        !lodash.get(newValue, 'reverseField.key')
 | 
				
			||||||
      ) {
 | 
					      ) {
 | 
				
			||||||
 | 
					        const field = await this.app.db
 | 
				
			||||||
 | 
					          .getModel('fields')
 | 
				
			||||||
 | 
					          .findByPk(model.get('reverseKey'), { transaction: options.transaction });
 | 
				
			||||||
 | 
					        if (field) {
 | 
				
			||||||
          throw new Error('cant update field without a reverseField key');
 | 
					          throw new Error('cant update field without a reverseField key');
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 要在 beforeInitOptions 之前处理
 | 
					    // 要在 beforeInitOptions 之前处理
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user