refactor: form data template support data scope config (#2229)
* refactor: data template support data scope config * refactor: data template support data scope config * refactor: locale improve * refactor: code improve * refactor: data template config data scope and title field should linkage with collection field * refactor: code improve * refactor: code improve * refactor: code improve * refactor: code improve * refactor: code improve * refactor: locale improve * refactor: locale improve * refactor: code improve
This commit is contained in:
		
							parent
							
								
									2c8e7b163e
								
							
						
					
					
						commit
						b42e3b4042
					
				@ -708,5 +708,6 @@ export default {
 | 
			
		||||
  "Current object":"Current object",
 | 
			
		||||
  "Linkage with form fields":"Linkage with form fields",
 | 
			
		||||
  "Allow add new, update and delete actions":"Allow add new, update and delete actions",
 | 
			
		||||
  "Date display format":"Date display format"
 | 
			
		||||
  "Date display format":"Date display format",
 | 
			
		||||
  "Assign  data scope for the template":"Assign  data scope for the template",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -619,5 +619,6 @@ export default {
 | 
			
		||||
  "Current object":"現在のオブジェクト",
 | 
			
		||||
  "Linkage with form fields":"フォームデータから連動",
 | 
			
		||||
  "Allow add new, update and delete actions":"削除変更操作の許可",
 | 
			
		||||
  "Date display format":"日付表示形式"
 | 
			
		||||
  "Date display format":"日付表示形式",
 | 
			
		||||
  "Assign  data scope for the template":"テンプレートのデータ範囲の指定",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -793,5 +793,6 @@ export default {
 | 
			
		||||
  "Linkage with form fields":"从表单字段联动",
 | 
			
		||||
  "Failed to load plugin": "插件加载失败",
 | 
			
		||||
  "Allow add new, update and delete actions":"允许增删改操作",
 | 
			
		||||
  "Date display format":"日期显示格式"
 | 
			
		||||
  "Date display format":"日期显示格式",
 | 
			
		||||
  "Assign  data scope for the template":"为模板指定数据范围",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,14 +1,16 @@
 | 
			
		||||
import { useFieldSchema } from '@formily/react';
 | 
			
		||||
import { error, forEach } from '@nocobase/utils/client';
 | 
			
		||||
import { Select } from 'antd';
 | 
			
		||||
import { Select, Space } from 'antd';
 | 
			
		||||
import _ from 'lodash';
 | 
			
		||||
import React, { useCallback, useEffect, useMemo } from 'react';
 | 
			
		||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
import { useAPIClient } from '../../../api-client';
 | 
			
		||||
import { findFormBlock } from '../../../block-provider';
 | 
			
		||||
import { useCollectionManager } from '../../../collection-manager';
 | 
			
		||||
import { useDuplicatefieldsContext } from '../../../schema-initializer/components';
 | 
			
		||||
import { compatibleDataId } from '../../../schema-settings/DataTemplates/FormDataTemplates';
 | 
			
		||||
import { useToken } from '../__builtins__';
 | 
			
		||||
import { RemoteSelect } from '../remote-select';
 | 
			
		||||
 | 
			
		||||
export interface ITemplate {
 | 
			
		||||
  config?: {
 | 
			
		||||
@ -23,9 +25,11 @@ export interface ITemplate {
 | 
			
		||||
    key: string;
 | 
			
		||||
    title: string;
 | 
			
		||||
    collection: string;
 | 
			
		||||
    dataId: number;
 | 
			
		||||
    dataId?: number;
 | 
			
		||||
    fields: string[];
 | 
			
		||||
    default?: boolean;
 | 
			
		||||
    dataScope?: object;
 | 
			
		||||
    titleField?: string;
 | 
			
		||||
  }[];
 | 
			
		||||
  /** 是否在 Form 区块显示模板选择器 */
 | 
			
		||||
  display: boolean;
 | 
			
		||||
@ -63,49 +67,38 @@ const useDataTemplates = () => {
 | 
			
		||||
      key: 'none',
 | 
			
		||||
      title: t('None'),
 | 
			
		||||
    },
 | 
			
		||||
  ].concat(items.map<any>((item, i) => ({ key: i, ...item })));
 | 
			
		||||
 | 
			
		||||
  ].concat(
 | 
			
		||||
    items.map<any>((t, i) => ({
 | 
			
		||||
      key: i,
 | 
			
		||||
      ...t,
 | 
			
		||||
      isLeaf: t.dataId !== null && t.dataId !== undefined,
 | 
			
		||||
      titleCollectionField: t?.titleField && getCollectionJoinField(`${t.collection}.${t.titleField}`),
 | 
			
		||||
    })),
 | 
			
		||||
  );
 | 
			
		||||
  const defaultTemplate = items.find((item) => item.default);
 | 
			
		||||
  return {
 | 
			
		||||
    templates,
 | 
			
		||||
    display,
 | 
			
		||||
    defaultTemplate,
 | 
			
		||||
    enabled: items.length > 0 && items.every((item) => item.dataId !== undefined),
 | 
			
		||||
    enabled: items.length > 0 && items.every((item) => item.dataId || item.dataScope),
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
function filterReferences(obj) {
 | 
			
		||||
  const filteredObj = {};
 | 
			
		||||
  for (const key in obj) {
 | 
			
		||||
    if (typeof obj[key] !== 'object') {
 | 
			
		||||
      filteredObj[key] = obj[key];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return filteredObj;
 | 
			
		||||
}
 | 
			
		||||
export const Templates = ({ style = {}, form }) => {
 | 
			
		||||
  const { token } = useToken();
 | 
			
		||||
  const { templates, display, enabled, defaultTemplate } = useDataTemplates();
 | 
			
		||||
  const [value, setValue] = React.useState(defaultTemplate?.key || 'none');
 | 
			
		||||
  const { getCollectionJoinField } = useCollectionManager();
 | 
			
		||||
  const templateOptions = compatibleDataId(templates);
 | 
			
		||||
  const [targetTemplate, setTargetTemplate] = useState(defaultTemplate?.key || 'none');
 | 
			
		||||
  const [targetTemplateData, setTemplateData] = useState(null);
 | 
			
		||||
  const api = useAPIClient();
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (enabled && defaultTemplate) {
 | 
			
		||||
      form.__template = true;
 | 
			
		||||
      fetchTemplateData(api, defaultTemplate, t)
 | 
			
		||||
        .then((data) => {
 | 
			
		||||
          if (form && data) {
 | 
			
		||||
            forEach(data, (value, key) => {
 | 
			
		||||
              if (value) {
 | 
			
		||||
                form.values[key] = value;
 | 
			
		||||
              }
 | 
			
		||||
            });
 | 
			
		||||
          }
 | 
			
		||||
          return data;
 | 
			
		||||
        })
 | 
			
		||||
        .catch((err) => {
 | 
			
		||||
          console.error(err);
 | 
			
		||||
        });
 | 
			
		||||
      if (defaultTemplate.key === 'duplicate') {
 | 
			
		||||
        handleTemplateDataChange(defaultTemplate.dataId, defaultTemplate);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
@ -122,46 +115,69 @@ export const Templates = ({ style = {}, form }) => {
 | 
			
		||||
    return { fontSize: token.fontSize, fontWeight: 'bold', whiteSpace: 'nowrap', marginRight: token.marginXS };
 | 
			
		||||
  }, [token.fontSize, token.marginXS]);
 | 
			
		||||
 | 
			
		||||
  const handleChange = useCallback(async (value, option) => {
 | 
			
		||||
    setValue(value);
 | 
			
		||||
    if (option.key !== 'none') {
 | 
			
		||||
      fetchTemplateData(api, option, t)
 | 
			
		||||
        .then((data) => {
 | 
			
		||||
          if (form && data) {
 | 
			
		||||
            // 切换之前先把之前的数据清空
 | 
			
		||||
            form.reset();
 | 
			
		||||
            form.__template = true;
 | 
			
		||||
  const handleTemplateChange = useCallback(async (value, option) => {
 | 
			
		||||
    setTargetTemplate(value);
 | 
			
		||||
    setTemplateData(null);
 | 
			
		||||
    form?.reset();
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
            forEach(data, (value, key) => {
 | 
			
		||||
              if (value) {
 | 
			
		||||
                form.values[key] = value;
 | 
			
		||||
              }
 | 
			
		||||
            });
 | 
			
		||||
          }
 | 
			
		||||
          return data;
 | 
			
		||||
        })
 | 
			
		||||
        .catch((err) => {
 | 
			
		||||
          console.error(err);
 | 
			
		||||
        });
 | 
			
		||||
    } else {
 | 
			
		||||
      form?.reset();
 | 
			
		||||
    }
 | 
			
		||||
  const handleTemplateDataChange: any = useCallback(async (value, option) => {
 | 
			
		||||
    const template = { ...option, dataId: value };
 | 
			
		||||
    setTemplateData(option);
 | 
			
		||||
    fetchTemplateData(api, template, t)
 | 
			
		||||
      .then((data) => {
 | 
			
		||||
        if (form && data) {
 | 
			
		||||
          // 切换之前先把之前的数据清空
 | 
			
		||||
          form.reset();
 | 
			
		||||
          form.__template = true;
 | 
			
		||||
 | 
			
		||||
          forEach(data, (value, key) => {
 | 
			
		||||
            if (value) {
 | 
			
		||||
              form.values[key] = value;
 | 
			
		||||
            }
 | 
			
		||||
          });
 | 
			
		||||
        }
 | 
			
		||||
        return data;
 | 
			
		||||
      })
 | 
			
		||||
      .catch((err) => {
 | 
			
		||||
        console.error(err);
 | 
			
		||||
      });
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
  if (!enabled || !display) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
  const template = templateOptions?.find((v) => v.key === targetTemplate);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div style={wrapperStyle}>
 | 
			
		||||
      <label style={labelStyle}>{t('Data template')}: </label>
 | 
			
		||||
      <Select
 | 
			
		||||
        popupMatchSelectWidth={false}
 | 
			
		||||
        options={templates}
 | 
			
		||||
        fieldNames={{ label: 'title', value: 'key' }}
 | 
			
		||||
        value={value}
 | 
			
		||||
        onChange={handleChange}
 | 
			
		||||
      />
 | 
			
		||||
      <Space wrap>
 | 
			
		||||
        <label style={labelStyle}>{t('Data template')}: </label>
 | 
			
		||||
        <Select
 | 
			
		||||
          popupMatchSelectWidth={false}
 | 
			
		||||
          options={templateOptions}
 | 
			
		||||
          fieldNames={{ label: 'title', value: 'key' }}
 | 
			
		||||
          value={targetTemplate}
 | 
			
		||||
          onChange={handleTemplateChange}
 | 
			
		||||
        />
 | 
			
		||||
        {targetTemplate !== 'none' && (
 | 
			
		||||
          <RemoteSelect
 | 
			
		||||
            style={{ width: 220 }}
 | 
			
		||||
            fieldNames={{ label: template.titleField, value: 'id' }}
 | 
			
		||||
            target={template?.collection}
 | 
			
		||||
            value={targetTemplateData}
 | 
			
		||||
            objectValue
 | 
			
		||||
            service={{
 | 
			
		||||
              resource: template?.collection,
 | 
			
		||||
              params: {
 | 
			
		||||
                filter: template?.dataScope,
 | 
			
		||||
              },
 | 
			
		||||
            }}
 | 
			
		||||
            onChange={(value) => handleTemplateDataChange(value.id, { ...value, ...template })}
 | 
			
		||||
            targetField={getCollectionJoinField(`${template?.collection}.${template.titleField}`)}
 | 
			
		||||
          />
 | 
			
		||||
        )}
 | 
			
		||||
      </Space>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
@ -175,7 +191,7 @@ function findDataTemplates(fieldSchema): ITemplate {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function fetchTemplateData(api, template: { collection: string; dataId: number; fields: string[] }, t) {
 | 
			
		||||
  if (template.fields.length === 0) {
 | 
			
		||||
  if (template.fields.length === 0 || !template.dataId) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  return api
 | 
			
		||||
 | 
			
		||||
@ -7,16 +7,11 @@ import React, { useMemo } from 'react';
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
import { mergeFilter } from '../../block-provider';
 | 
			
		||||
import { useCollectionManager } from '../../collection-manager';
 | 
			
		||||
import {
 | 
			
		||||
  AssociationSelect,
 | 
			
		||||
  SchemaComponent,
 | 
			
		||||
  SchemaComponentContext,
 | 
			
		||||
  removeNullCondition,
 | 
			
		||||
} from '../../schema-component';
 | 
			
		||||
import { SchemaComponent, SchemaComponentContext, removeNullCondition } from '../../schema-component';
 | 
			
		||||
import { ITemplate } from '../../schema-component/antd/form-v2/Templates';
 | 
			
		||||
import { AsDefaultTemplate } from './components/AsDefaultTemplate';
 | 
			
		||||
import { ArrayCollapse } from './components/DataTemplateTitle';
 | 
			
		||||
import { Designer, getSelectedIdFilter } from './components/Designer';
 | 
			
		||||
import { getSelectedIdFilter } from './components/Designer';
 | 
			
		||||
import { useCollectionState } from './hooks/useCollectionState';
 | 
			
		||||
 | 
			
		||||
const Tree = connect(
 | 
			
		||||
@ -27,11 +22,30 @@ const Tree = connect(
 | 
			
		||||
  }),
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export const compatibleDataId = (data, config?) => {
 | 
			
		||||
  return data?.map((v) => {
 | 
			
		||||
    const { dataId, ...others } = v;
 | 
			
		||||
    const obj = { ...others };
 | 
			
		||||
    if (dataId) {
 | 
			
		||||
      obj.dataScope = { $and: [{ id: { $eq: dataId } }] };
 | 
			
		||||
      obj.titleField = obj?.titleField || config?.[v.collection]?.['titleField'] || 'id';
 | 
			
		||||
    }
 | 
			
		||||
    return obj;
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const FormDataTemplates = observer(
 | 
			
		||||
  (props: any) => {
 | 
			
		||||
    const { useProps, formSchema, designerCtx } = props;
 | 
			
		||||
    const { defaultValues, collectionName } = useProps();
 | 
			
		||||
    const { collectionList, getEnableFieldTree, getOnLoadData, getOnCheck } = useCollectionState(collectionName);
 | 
			
		||||
    const {
 | 
			
		||||
      collectionList,
 | 
			
		||||
      getEnableFieldTree,
 | 
			
		||||
      getOnLoadData,
 | 
			
		||||
      getOnCheck,
 | 
			
		||||
      getScopeDataSource,
 | 
			
		||||
      useTitleFieldDataSource,
 | 
			
		||||
    } = useCollectionState(collectionName);
 | 
			
		||||
    const { getCollection, getCollectionField } = useCollectionManager();
 | 
			
		||||
    const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
@ -39,11 +53,15 @@ export const FormDataTemplates = observer(
 | 
			
		||||
    const activeData = useMemo<ITemplate>(
 | 
			
		||||
      () =>
 | 
			
		||||
        observable(
 | 
			
		||||
          defaultValues || { items: [], display: true, config: { [collectionName]: { titleField: '', filter: {} } } },
 | 
			
		||||
          { ...defaultValues, items: compatibleDataId(defaultValues?.items || [], defaultValues?.config) } || {
 | 
			
		||||
            items: [],
 | 
			
		||||
            display: true,
 | 
			
		||||
            config: { [collectionName]: { titleField: '', filter: {} } },
 | 
			
		||||
          },
 | 
			
		||||
        ),
 | 
			
		||||
      [],
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    console.log(activeData);
 | 
			
		||||
    const getTargetField = (collectionName: string) => {
 | 
			
		||||
      const collection = getCollection(collectionName);
 | 
			
		||||
      return getCollectionField(
 | 
			
		||||
@ -63,8 +81,8 @@ export const FormDataTemplates = observer(
 | 
			
		||||
      const filter = activeData.config?.[collectionName]?.filter;
 | 
			
		||||
      return _.isEmpty(filter) ? {} : removeNullCondition(mergeFilter([filter, getSelectedIdFilter(value)], '$or'));
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const components = useMemo(() => ({ ArrayCollapse }), []);
 | 
			
		||||
 | 
			
		||||
    const scope = useMemo(
 | 
			
		||||
      () => ({
 | 
			
		||||
        getEnableFieldTree,
 | 
			
		||||
@ -75,6 +93,8 @@ export const FormDataTemplates = observer(
 | 
			
		||||
        getOnLoadData,
 | 
			
		||||
        getOnCheck,
 | 
			
		||||
        collectionName,
 | 
			
		||||
        getScopeDataSource,
 | 
			
		||||
        useTitleFieldDataSource,
 | 
			
		||||
      }),
 | 
			
		||||
      [],
 | 
			
		||||
    );
 | 
			
		||||
@ -117,49 +137,39 @@ export const FormDataTemplates = observer(
 | 
			
		||||
                        options: collectionList,
 | 
			
		||||
                      },
 | 
			
		||||
                    },
 | 
			
		||||
                    dataId: {
 | 
			
		||||
                      type: 'number',
 | 
			
		||||
                      title: '{{ t("Template Data") }}',
 | 
			
		||||
                      required: true,
 | 
			
		||||
                      description: t('Select an existing piece of data as the initialization data for the form'),
 | 
			
		||||
                      'x-designer': Designer,
 | 
			
		||||
                      'x-designer-props': {
 | 
			
		||||
                        formSchema,
 | 
			
		||||
                        data: activeData,
 | 
			
		||||
                      },
 | 
			
		||||
                    dataScope: {
 | 
			
		||||
                      type: 'object',
 | 
			
		||||
                      title: '{{ t("Assign  data scope for the template") }}',
 | 
			
		||||
                      'x-decorator': 'FormItem',
 | 
			
		||||
                      'x-component': AssociationSelect,
 | 
			
		||||
                      'x-component-props': {
 | 
			
		||||
                        service: {
 | 
			
		||||
                          resource: '{{ $record.collection || collectionName }}',
 | 
			
		||||
                          params: {
 | 
			
		||||
                            filter: '{{ getFilter($self.componentProps.service.resource, $self.value) }}',
 | 
			
		||||
                          },
 | 
			
		||||
                      'x-component': 'Filter',
 | 
			
		||||
                      'x-decorator-props': {
 | 
			
		||||
                        style: {
 | 
			
		||||
                          marginBottom: '0px',
 | 
			
		||||
                        },
 | 
			
		||||
                        action: 'list',
 | 
			
		||||
                        multiple: false,
 | 
			
		||||
                        objectValue: false,
 | 
			
		||||
                        manual: false,
 | 
			
		||||
                        targetField: '{{ getTargetField($self.componentProps.service.resource) }}',
 | 
			
		||||
                        mapOptions: getMapOptions(),
 | 
			
		||||
                        fieldNames: '{{ getFieldNames($self.componentProps.service.resource) }}',
 | 
			
		||||
                      },
 | 
			
		||||
                      required: true,
 | 
			
		||||
                      'x-reactions': [
 | 
			
		||||
                        {
 | 
			
		||||
                          dependencies: ['.collection'],
 | 
			
		||||
                          fulfill: {
 | 
			
		||||
                            state: {
 | 
			
		||||
                              disabled: '{{ !$deps[0] }}',
 | 
			
		||||
                              componentProps: {
 | 
			
		||||
                                service: {
 | 
			
		||||
                                  resource: '{{ getResource($deps[0], $self) }}',
 | 
			
		||||
                                },
 | 
			
		||||
                              },
 | 
			
		||||
                            },
 | 
			
		||||
                            schema: {
 | 
			
		||||
                              enum: '{{ getScopeDataSource($deps[0]) }}',
 | 
			
		||||
                            },
 | 
			
		||||
                          },
 | 
			
		||||
                        },
 | 
			
		||||
                      ],
 | 
			
		||||
                    },
 | 
			
		||||
                    titleField: {
 | 
			
		||||
                      type: 'string',
 | 
			
		||||
                      'x-decorator': 'FormItem',
 | 
			
		||||
                      title: '{{ t("Title field") }}',
 | 
			
		||||
                      'x-component': 'Select',
 | 
			
		||||
                      required: true,
 | 
			
		||||
                      'x-reactions': '{{useTitleFieldDataSource}}',
 | 
			
		||||
                    },
 | 
			
		||||
                    fields: {
 | 
			
		||||
                      type: 'array',
 | 
			
		||||
                      title: '{{ t("Data fields") }}',
 | 
			
		||||
@ -246,15 +256,6 @@ export function getLabel(titleField) {
 | 
			
		||||
  return titleField || 'label';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getMapOptions() {
 | 
			
		||||
  return (option) => {
 | 
			
		||||
    if (option?.id === undefined) {
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
    return option;
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getResource(resource: string, field: Field) {
 | 
			
		||||
  if (resource !== field.componentProps.service.resource) {
 | 
			
		||||
    // 切换 collection 后,之前选中的其它 collection 的数据就没有意义了,需要清空
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,12 @@
 | 
			
		||||
import { ArrayField } from '@formily/core';
 | 
			
		||||
import React, { useCallback, useState } from 'react';
 | 
			
		||||
import { useCollectionManager } from '../../../collection-manager';
 | 
			
		||||
import { isTitleField } from '../../../collection-manager/Configuration/CollectionFields';
 | 
			
		||||
import { useCompile } from '../../../schema-component';
 | 
			
		||||
import { TreeNode } from '../TreeLabel';
 | 
			
		||||
 | 
			
		||||
export const useCollectionState = (currentCollectionName: string) => {
 | 
			
		||||
  const { getCollectionFields, getAllCollectionsInheritChain, getCollection } = useCollectionManager();
 | 
			
		||||
  const { getCollectionFields, getAllCollectionsInheritChain, getCollection, getInterface } = useCollectionManager();
 | 
			
		||||
  const [collectionList] = useState(getCollectionList);
 | 
			
		||||
  const compile = useCompile();
 | 
			
		||||
 | 
			
		||||
@ -150,11 +151,78 @@ export const useCollectionState = (currentCollectionName: string) => {
 | 
			
		||||
    };
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
  const getScopeDataSource = (resource: string) => {
 | 
			
		||||
    const fields = getCollectionFields(resource);
 | 
			
		||||
    const field2option = (field, depth) => {
 | 
			
		||||
      if (!field.interface) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      const fieldInterface = getInterface(field.interface);
 | 
			
		||||
      if (!fieldInterface?.filterable) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      const { nested, children, operators } = fieldInterface.filterable;
 | 
			
		||||
      const option = {
 | 
			
		||||
        name: field.name,
 | 
			
		||||
        title: field?.uiSchema?.title || field.name,
 | 
			
		||||
        schema: field?.uiSchema,
 | 
			
		||||
        operators:
 | 
			
		||||
          operators?.filter?.((operator) => {
 | 
			
		||||
            return !operator?.visible || operator.visible(field);
 | 
			
		||||
          }) || [],
 | 
			
		||||
        interface: field.interface,
 | 
			
		||||
      };
 | 
			
		||||
      if (field.target && depth > 2) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      if (depth > 2) {
 | 
			
		||||
        return option;
 | 
			
		||||
      }
 | 
			
		||||
      if (children?.length) {
 | 
			
		||||
        option['children'] = children;
 | 
			
		||||
      }
 | 
			
		||||
      if (nested) {
 | 
			
		||||
        const targetFields = getCollectionFields(field.target);
 | 
			
		||||
        const options = getOptions(targetFields, depth + 1).filter(Boolean);
 | 
			
		||||
        option['children'] = option['children'] || [];
 | 
			
		||||
        option['children'].push(...options);
 | 
			
		||||
      }
 | 
			
		||||
      return option;
 | 
			
		||||
    };
 | 
			
		||||
    const getOptions = (fields, depth) => {
 | 
			
		||||
      const options = [];
 | 
			
		||||
      fields.forEach((field) => {
 | 
			
		||||
        const option = field2option(field, depth);
 | 
			
		||||
        if (option) {
 | 
			
		||||
          options.push(option);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
      return options;
 | 
			
		||||
    };
 | 
			
		||||
    const options = getOptions(fields, 1);
 | 
			
		||||
    return options;
 | 
			
		||||
  };
 | 
			
		||||
  const useTitleFieldDataSource = (field) => {
 | 
			
		||||
    const fieldPath = field.path.entire.replace('titleField', 'collection');
 | 
			
		||||
    const collectionName = field.query(fieldPath).get('value');
 | 
			
		||||
    const targetFields = getCollectionFields(collectionName);
 | 
			
		||||
    const options = targetFields
 | 
			
		||||
      .filter((field) => {
 | 
			
		||||
        return !field.isForeignKey && getInterface(field.interface)?.titleUsable;
 | 
			
		||||
      })
 | 
			
		||||
      .map((field) => ({
 | 
			
		||||
        value: field?.name,
 | 
			
		||||
        label: compile(field?.uiSchema?.title) || field?.name,
 | 
			
		||||
      }));
 | 
			
		||||
    field.dataSource = options;
 | 
			
		||||
  };
 | 
			
		||||
  return {
 | 
			
		||||
    collectionList,
 | 
			
		||||
    getEnableFieldTree,
 | 
			
		||||
    getOnLoadData,
 | 
			
		||||
    getOnCheck,
 | 
			
		||||
    getScopeDataSource,
 | 
			
		||||
    useTitleFieldDataSource,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user