refactor(sub-table): sub-table support selection of existing records (#3311)
* refactor: sub-table support selection of existing records * refactor: local improve * refactor: sub-table support select existing records * refactor: create action support updateAssociationValues * refactor: sub-table * fix: omit foreignKey * refactor: record picker omit foreignKey * test: manyToMany * test: subform: basic fields * test: table column & sub-table in edit form
This commit is contained in:
		
							parent
							
								
									dc71a77d8c
								
							
						
					
					
						commit
						6fb5af993e
					
				@ -268,10 +268,17 @@ export const useTableSelectorContext = () => {
 | 
				
			|||||||
export const useTableSelectorProps = () => {
 | 
					export const useTableSelectorProps = () => {
 | 
				
			||||||
  const field = useField<ArrayField>();
 | 
					  const field = useField<ArrayField>();
 | 
				
			||||||
  const ctx = useTableSelectorContext();
 | 
					  const ctx = useTableSelectorContext();
 | 
				
			||||||
 | 
					  const fieldSchema = useFieldSchema();
 | 
				
			||||||
 | 
					  const { getCollectionJoinField } = useCollectionManager();
 | 
				
			||||||
 | 
					  const collectionFieldSchema = recursiveParent(fieldSchema, 'CollectionField');
 | 
				
			||||||
 | 
					  const collectionField = getCollectionJoinField(collectionFieldSchema?.['x-collection-field']);
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    if (!ctx?.service?.loading) {
 | 
					    if (!ctx?.service?.loading) {
 | 
				
			||||||
      field.value = ctx?.service?.data?.data;
 | 
					      const data = ctx?.service?.data?.data.map((v) => {
 | 
				
			||||||
      field?.setInitialValue?.(ctx?.service?.data?.data);
 | 
					        return _.omit(v, collectionField?.foreignKey);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      field.value = data;
 | 
				
			||||||
 | 
					      field?.setInitialValue?.(data);
 | 
				
			||||||
      field.data = field.data || {};
 | 
					      field.data = field.data || {};
 | 
				
			||||||
      field.data.selectedRowKeys = ctx?.field?.data?.selectedRowKeys;
 | 
					      field.data.selectedRowKeys = ctx?.field?.data?.selectedRowKeys;
 | 
				
			||||||
      field.componentProps.pagination = field.componentProps.pagination || {};
 | 
					      field.componentProps.pagination = field.componentProps.pagination || {};
 | 
				
			||||||
 | 
				
			|||||||
@ -184,6 +184,7 @@ export const useCreateActionProps = () => {
 | 
				
			|||||||
  const compile = useCompile();
 | 
					  const compile = useCompile();
 | 
				
			||||||
  const { modal } = App.useApp();
 | 
					  const { modal } = App.useApp();
 | 
				
			||||||
  const { t } = useTranslation();
 | 
					  const { t } = useTranslation();
 | 
				
			||||||
 | 
					  const { updateAssociationValues } = useFormBlockContext();
 | 
				
			||||||
  const collectValues = useCollectValuesToSubmit();
 | 
					  const collectValues = useCollectValuesToSubmit();
 | 
				
			||||||
  const action = actionField.componentProps.saveMode || 'create';
 | 
					  const action = actionField.componentProps.saveMode || 'create';
 | 
				
			||||||
  const filterKeys = actionField.componentProps.filterKeys?.checked || [];
 | 
					  const filterKeys = actionField.componentProps.filterKeys?.checked || [];
 | 
				
			||||||
@ -205,6 +206,7 @@ export const useCreateActionProps = () => {
 | 
				
			|||||||
          triggerWorkflows: triggerWorkflows?.length
 | 
					          triggerWorkflows: triggerWorkflows?.length
 | 
				
			||||||
            ? triggerWorkflows.map((row) => [row.workflowKey, row.context].filter(Boolean).join('!')).join(',')
 | 
					            ? triggerWorkflows.map((row) => [row.workflowKey, row.context].filter(Boolean).join('!')).join(',')
 | 
				
			||||||
            : undefined,
 | 
					            : undefined,
 | 
				
			||||||
 | 
					          updateAssociationValues,
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        setVisible?.(false);
 | 
					        setVisible?.(false);
 | 
				
			||||||
        actionField.data.loading = false;
 | 
					        actionField.data.loading = false;
 | 
				
			||||||
 | 
				
			|||||||
@ -749,5 +749,7 @@
 | 
				
			|||||||
  "Current form": "Formulaire actuel",
 | 
					  "Current form": "Formulaire actuel",
 | 
				
			||||||
  "Current object": "Objet actuel",
 | 
					  "Current object": "Objet actuel",
 | 
				
			||||||
  "Linkage with form fields": "Lien avec les champs de formulaire",
 | 
					  "Linkage with form fields": "Lien avec les champs de formulaire",
 | 
				
			||||||
  "Allow add new, update and delete actions": "Autoriser les actions d'ajout, de mise à jour et de suppression"
 | 
					  "Allow add new, update and delete actions": "Autoriser les actions d'ajout, de mise à jour et de suppression",
 | 
				
			||||||
 | 
					  "Allow add new":"Autoriser les ajouts",
 | 
				
			||||||
 | 
					  "Allow selection of existing records":"Permet de sélectionner des données existantes"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -666,5 +666,7 @@
 | 
				
			|||||||
  "Search plugin": "プラグインを検索",
 | 
					  "Search plugin": "プラグインを検索",
 | 
				
			||||||
  "Author": "著者",
 | 
					  "Author": "著者",
 | 
				
			||||||
  "Plugin loading failed. Please check the server logs.": "プラグインのロードに失敗しました。サーバーログを確認してください。",
 | 
					  "Plugin loading failed. Please check the server logs.": "プラグインのロードに失敗しました。サーバーログを確認してください。",
 | 
				
			||||||
  "Automatically drop objects that depend on the collection (such as views), and in turn all objects that depend on those objects": "テーブルに依存するオブジェクト、およびそれらに依存するオブジェクトを自動的に削除する"
 | 
					  "Automatically drop objects that depend on the collection (such as views), and in turn all objects that depend on those objects": "テーブルに依存するオブジェクト、およびそれらに依存するオブジェクトを自動的に削除する",
 | 
				
			||||||
 | 
					  "Allow add new":"新規作成を許可",
 | 
				
			||||||
 | 
					  "Allow selection of existing records":"既存のデータの選択を許可"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -708,5 +708,7 @@
 | 
				
			|||||||
  "Data template": "Modelo de dados",
 | 
					  "Data template": "Modelo de dados",
 | 
				
			||||||
  "Not found": "Não encontrado",
 | 
					  "Not found": "Não encontrado",
 | 
				
			||||||
  "Add": "Adicionar",
 | 
					  "Add": "Adicionar",
 | 
				
			||||||
  "Automatically drop objects that depend on the collection (such as views), and in turn all objects that depend on those objects": "Excluir automaticamente objetos que dependem desta tabela, bem como objetos que dependem desses objetos"
 | 
					  "Automatically drop objects that depend on the collection (such as views), and in turn all objects that depend on those objects": "Excluir automaticamente objetos que dependem desta tabela, bem como objetos que dependem desses objetos",
 | 
				
			||||||
 | 
					  "Allow add new":"Permitir novas adições",
 | 
				
			||||||
 | 
					  "Allow selection of existing records":"Permitir a selecção dos registos existentes"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -839,5 +839,7 @@
 | 
				
			|||||||
  "Are you sure you want to perform the {{title}} action?": "你确定执行{{title}}操作吗?",
 | 
					  "Are you sure you want to perform the {{title}} action?": "你确定执行{{title}}操作吗?",
 | 
				
			||||||
  "Sign in with another account": "登录其他账号",
 | 
					  "Sign in with another account": "登录其他账号",
 | 
				
			||||||
  "Return to the main application": "返回主应用",
 | 
					  "Return to the main application": "返回主应用",
 | 
				
			||||||
  "Permission denied": "没有权限"
 | 
					  "Permission denied": "没有权限",
 | 
				
			||||||
 | 
					  "Allow add new":"允许新增",
 | 
				
			||||||
 | 
					  "Allow selection of existing records":"允许选择已有数据"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -597,7 +597,10 @@ test.describe('creation form block schema settings', () => {
 | 
				
			|||||||
      await page.mouse.move(300, 0);
 | 
					      await page.mouse.move(300, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // 当新增一行时,应该显示默认值
 | 
					      // 当新增一行时,应该显示默认值
 | 
				
			||||||
      await page.getByRole('button', { name: 'plus' }).click();
 | 
					      await page
 | 
				
			||||||
 | 
					        .getByTestId('drawer-Action.Container-general-Add record')
 | 
				
			||||||
 | 
					        .getByRole('button', { name: 'Add new' })
 | 
				
			||||||
 | 
					        .click();
 | 
				
			||||||
      await expect(
 | 
					      await expect(
 | 
				
			||||||
        page
 | 
					        page
 | 
				
			||||||
          .getByRole('cell', { name: 'block-item-CollectionField-users-form-users.nickname-Nickname' })
 | 
					          .getByRole('cell', { name: 'block-item-CollectionField-users-form-users.nickname-Nickname' })
 | 
				
			||||||
 | 
				
			|||||||
@ -21,7 +21,7 @@ import { useAssociationFieldContext, useFieldNames, useInsertSchema } from './ho
 | 
				
			|||||||
import schema from './schema';
 | 
					import schema from './schema';
 | 
				
			||||||
import { flatData, getLabelFormatValue, useLabelUiSchema } from './util';
 | 
					import { flatData, getLabelFormatValue, useLabelUiSchema } from './util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const useTableSelectorProps = () => {
 | 
					export const useTableSelectorProps = () => {
 | 
				
			||||||
  const field: any = useField();
 | 
					  const field: any = useField();
 | 
				
			||||||
  const {
 | 
					  const {
 | 
				
			||||||
    multiple,
 | 
					    multiple,
 | 
				
			||||||
 | 
				
			|||||||
@ -14,13 +14,18 @@ export const InternalSubTable = observer(
 | 
				
			|||||||
    const field: any = useField();
 | 
					    const field: any = useField();
 | 
				
			||||||
    const fieldSchema = useFieldSchema();
 | 
					    const fieldSchema = useFieldSchema();
 | 
				
			||||||
    const insert = useInsertSchema('SubTable');
 | 
					    const insert = useInsertSchema('SubTable');
 | 
				
			||||||
 | 
					    const insertSelector = useInsertSchema('Selector');
 | 
				
			||||||
    const { options } = useAssociationFieldContext();
 | 
					    const { options } = useAssociationFieldContext();
 | 
				
			||||||
    const { actionName } = useACLActionParamsContext();
 | 
					    const { actionName } = useACLActionParamsContext();
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
      insert(schema.SubTable);
 | 
					      insert(schema.SubTable);
 | 
				
			||||||
      field.required = fieldSchema['required'];
 | 
					      field.required = fieldSchema['required'];
 | 
				
			||||||
    }, []);
 | 
					    }, []);
 | 
				
			||||||
 | 
					    useEffect(() => {
 | 
				
			||||||
 | 
					      if (field.componentProps?.allowSelectExistingRecord) {
 | 
				
			||||||
 | 
					        insertSelector(schema.Selector);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }, [field.componentProps?.allowSelectExistingRecord]);
 | 
				
			||||||
    const option = useSchemaOptionsContext();
 | 
					    const option = useSchemaOptionsContext();
 | 
				
			||||||
    const components = {
 | 
					    const components = {
 | 
				
			||||||
      ...option.components,
 | 
					      ...option.components,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,21 +1,44 @@
 | 
				
			|||||||
import { PlusOutlined } from '@ant-design/icons';
 | 
					 | 
				
			||||||
import { css } from '@emotion/css';
 | 
					import { css } from '@emotion/css';
 | 
				
			||||||
import { ArrayField } from '@formily/core';
 | 
					import { ArrayField } from '@formily/core';
 | 
				
			||||||
import { exchangeArrayState } from '@formily/core/esm/shared/internals';
 | 
					import { exchangeArrayState } from '@formily/core/esm/shared/internals';
 | 
				
			||||||
import { observer } from '@formily/react';
 | 
					import { observer, RecursionField, useFieldSchema } from '@formily/react';
 | 
				
			||||||
import { action } from '@formily/reactive';
 | 
					import { action } from '@formily/reactive';
 | 
				
			||||||
import { isArr } from '@formily/shared';
 | 
					import { isArr } from '@formily/shared';
 | 
				
			||||||
import { Button } from 'antd';
 | 
					import { Button } from 'antd';
 | 
				
			||||||
import React from 'react';
 | 
					import { omit, unionBy, uniqBy } from 'lodash';
 | 
				
			||||||
 | 
					import React, { useState, useMemo, useContext } from 'react';
 | 
				
			||||||
 | 
					import { useTranslation } from 'react-i18next';
 | 
				
			||||||
import { FormActiveFieldsProvider } from '../../../block-provider';
 | 
					import { FormActiveFieldsProvider } from '../../../block-provider';
 | 
				
			||||||
import { FlagProvider } from '../../../flag-provider';
 | 
					import { FlagProvider } from '../../../flag-provider';
 | 
				
			||||||
import { Table } from '../table-v2/Table';
 | 
					import { Table } from '../table-v2/Table';
 | 
				
			||||||
import { useAssociationFieldContext } from './hooks';
 | 
					import { useAssociationFieldContext, useFieldNames } from './hooks';
 | 
				
			||||||
 | 
					import { ActionContextProvider } from '../action';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  FormProvider,
 | 
				
			||||||
 | 
					  RecordPickerProvider,
 | 
				
			||||||
 | 
					  SchemaComponentOptions,
 | 
				
			||||||
 | 
					  useActionContext,
 | 
				
			||||||
 | 
					  RecordPickerContext,
 | 
				
			||||||
 | 
					} from '../..';
 | 
				
			||||||
 | 
					import { getLabelFormatValue, useLabelUiSchema } from './util';
 | 
				
			||||||
 | 
					import { CollectionProvider } from '../../../collection-manager';
 | 
				
			||||||
 | 
					import { TableSelectorParamsProvider } from '../../../block-provider/TableSelectorProvider';
 | 
				
			||||||
 | 
					import { useCompile } from '../../hooks';
 | 
				
			||||||
 | 
					import { useTableSelectorProps } from './InternalPicker';
 | 
				
			||||||
 | 
					import { useCreateActionProps } from '../../../block-provider/hooks';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const SubTable: any = observer(
 | 
					export const SubTable: any = observer(
 | 
				
			||||||
  (props: any) => {
 | 
					  (props: any) => {
 | 
				
			||||||
    const { field } = useAssociationFieldContext<ArrayField>();
 | 
					    const { openSize } = props;
 | 
				
			||||||
    // useSubTableSpecialCase({ field });
 | 
					    const { field, options: collectionField } = useAssociationFieldContext<ArrayField>();
 | 
				
			||||||
 | 
					    const { t } = useTranslation();
 | 
				
			||||||
 | 
					    const [visibleSelector, setVisibleSelector] = useState(false);
 | 
				
			||||||
 | 
					    const [selectedRows, setSelectedRows] = useState([]);
 | 
				
			||||||
 | 
					    const fieldNames = useFieldNames(props);
 | 
				
			||||||
 | 
					    const fieldSchema = useFieldSchema();
 | 
				
			||||||
 | 
					    const compile = useCompile();
 | 
				
			||||||
 | 
					    const labelUiSchema = useLabelUiSchema(collectionField, fieldNames?.label || 'label');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const move = (fromIndex: number, toIndex: number) => {
 | 
					    const move = (fromIndex: number, toIndex: number) => {
 | 
				
			||||||
      if (toIndex === undefined) return;
 | 
					      if (toIndex === undefined) return;
 | 
				
			||||||
      if (!isArr(field.value)) return;
 | 
					      if (!isArr(field.value)) return;
 | 
				
			||||||
@ -32,6 +55,55 @@ export const SubTable: any = observer(
 | 
				
			|||||||
      });
 | 
					      });
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    field.move = move;
 | 
					    field.move = move;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const options = useMemo(() => {
 | 
				
			||||||
 | 
					      if (field.value && Object.keys(field.value).length > 0) {
 | 
				
			||||||
 | 
					        const opts = (Array.isArray(field.value) ? field.value : field.value ? [field.value] : [])
 | 
				
			||||||
 | 
					          .filter(Boolean)
 | 
				
			||||||
 | 
					          .map((option) => {
 | 
				
			||||||
 | 
					            const label = option?.[fieldNames.label];
 | 
				
			||||||
 | 
					            return {
 | 
				
			||||||
 | 
					              ...option,
 | 
				
			||||||
 | 
					              [fieldNames.label]: getLabelFormatValue(compile(labelUiSchema), compile(label)),
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					        return opts;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return [];
 | 
				
			||||||
 | 
					    }, [field.value, fieldNames?.label]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const pickerProps = {
 | 
				
			||||||
 | 
					      size: 'small',
 | 
				
			||||||
 | 
					      fieldNames: field.componentProps.fieldNames,
 | 
				
			||||||
 | 
					      multiple: true,
 | 
				
			||||||
 | 
					      association: {
 | 
				
			||||||
 | 
					        target: collectionField?.target,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      options,
 | 
				
			||||||
 | 
					      onChange: props?.onChange,
 | 
				
			||||||
 | 
					      selectedRows,
 | 
				
			||||||
 | 
					      setSelectedRows,
 | 
				
			||||||
 | 
					      collectionField,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    const usePickActionProps = () => {
 | 
				
			||||||
 | 
					      const { setVisible } = useActionContext();
 | 
				
			||||||
 | 
					      const { selectedRows, options, collectionField } = useContext(RecordPickerContext);
 | 
				
			||||||
 | 
					      return {
 | 
				
			||||||
 | 
					        onClick() {
 | 
				
			||||||
 | 
					          const selectData = unionBy(selectedRows, options, collectionField?.targetKey || 'id');
 | 
				
			||||||
 | 
					          const data = field.value || [];
 | 
				
			||||||
 | 
					          field.value = uniqBy(data.concat(selectData), collectionField?.targetKey || 'id');
 | 
				
			||||||
 | 
					          field.onInput(field.value);
 | 
				
			||||||
 | 
					          setVisible(false);
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    const getFilter = () => {
 | 
				
			||||||
 | 
					      const targetKey = collectionField?.targetKey || 'id';
 | 
				
			||||||
 | 
					      const list = options.map((option) => option[targetKey]).filter(Boolean);
 | 
				
			||||||
 | 
					      const filter = list.length ? { $and: [{ [`${targetKey}.$ne`]: list }] } : {};
 | 
				
			||||||
 | 
					      return filter;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <div
 | 
					      <div
 | 
				
			||||||
        className={css`
 | 
					        className={css`
 | 
				
			||||||
@ -72,6 +144,9 @@ export const SubTable: any = observer(
 | 
				
			|||||||
                .ant-formily-editable {
 | 
					                .ant-formily-editable {
 | 
				
			||||||
                  vertical-align: sub;
 | 
					                  vertical-align: sub;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                .ant-table-footer {
 | 
				
			||||||
 | 
					                  display: flex;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
              `}
 | 
					              `}
 | 
				
			||||||
              bordered
 | 
					              bordered
 | 
				
			||||||
              size={'small'}
 | 
					              size={'small'}
 | 
				
			||||||
@ -83,27 +158,80 @@ export const SubTable: any = observer(
 | 
				
			|||||||
              rowSelection={{ type: 'none', hideSelectAll: true }}
 | 
					              rowSelection={{ type: 'none', hideSelectAll: true }}
 | 
				
			||||||
              footer={() =>
 | 
					              footer={() =>
 | 
				
			||||||
                field.editable && (
 | 
					                field.editable && (
 | 
				
			||||||
 | 
					                  <>
 | 
				
			||||||
 | 
					                    {field.componentProps?.allowAddnew !== false && (
 | 
				
			||||||
                      <Button
 | 
					                      <Button
 | 
				
			||||||
                        type={'text'}
 | 
					                        type={'text'}
 | 
				
			||||||
                        block
 | 
					                        block
 | 
				
			||||||
                        className={css`
 | 
					                        className={css`
 | 
				
			||||||
                          display: block;
 | 
					                          display: block;
 | 
				
			||||||
 | 
					                          border-radius: 0px;
 | 
				
			||||||
 | 
					                          border-right: 1px solid rgba(0, 0, 0, 0.06);
 | 
				
			||||||
                        `}
 | 
					                        `}
 | 
				
			||||||
                        onClick={() => {
 | 
					                        onClick={() => {
 | 
				
			||||||
                          field.value = field.value || [];
 | 
					                          field.value = field.value || [];
 | 
				
			||||||
                          field.value.push({});
 | 
					                          field.value.push({});
 | 
				
			||||||
                          field.onInput(field.value);
 | 
					                          field.onInput(field.value);
 | 
				
			||||||
                        }}
 | 
					                        }}
 | 
				
			||||||
                    icon={<PlusOutlined />}
 | 
					 | 
				
			||||||
                      >
 | 
					                      >
 | 
				
			||||||
                    {/* {t('Add new')} */}
 | 
					                        {t('Add new')}
 | 
				
			||||||
                      </Button>
 | 
					                      </Button>
 | 
				
			||||||
 | 
					                    )}
 | 
				
			||||||
 | 
					                    {field.componentProps?.allowSelectExistingRecord && (
 | 
				
			||||||
 | 
					                      <Button
 | 
				
			||||||
 | 
					                        type={'text'}
 | 
				
			||||||
 | 
					                        block
 | 
				
			||||||
 | 
					                        className={css`
 | 
				
			||||||
 | 
					                          display: block;
 | 
				
			||||||
 | 
					                          border-radius: 0px;
 | 
				
			||||||
 | 
					                        `}
 | 
				
			||||||
 | 
					                        onClick={() => {
 | 
				
			||||||
 | 
					                          setVisibleSelector(true);
 | 
				
			||||||
 | 
					                        }}
 | 
				
			||||||
 | 
					                      >
 | 
				
			||||||
 | 
					                        {t('Select')}
 | 
				
			||||||
 | 
					                      </Button>
 | 
				
			||||||
 | 
					                    )}
 | 
				
			||||||
 | 
					                  </>
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
              isSubTable={true}
 | 
					              isSubTable={true}
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
          </FormActiveFieldsProvider>
 | 
					          </FormActiveFieldsProvider>
 | 
				
			||||||
        </FlagProvider>
 | 
					        </FlagProvider>
 | 
				
			||||||
 | 
					        <ActionContextProvider
 | 
				
			||||||
 | 
					          value={{
 | 
				
			||||||
 | 
					            openSize,
 | 
				
			||||||
 | 
					            openMode: 'drawer',
 | 
				
			||||||
 | 
					            visible: visibleSelector,
 | 
				
			||||||
 | 
					            setVisible: setVisibleSelector,
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          <RecordPickerProvider {...pickerProps}>
 | 
				
			||||||
 | 
					            <CollectionProvider name={collectionField?.target}>
 | 
				
			||||||
 | 
					              <FormProvider>
 | 
				
			||||||
 | 
					                <TableSelectorParamsProvider params={{ filter: getFilter() }}>
 | 
				
			||||||
 | 
					                  <SchemaComponentOptions
 | 
				
			||||||
 | 
					                    scope={{
 | 
				
			||||||
 | 
					                      usePickActionProps,
 | 
				
			||||||
 | 
					                      useTableSelectorProps,
 | 
				
			||||||
 | 
					                      useCreateActionProps,
 | 
				
			||||||
 | 
					                    }}
 | 
				
			||||||
 | 
					                  >
 | 
				
			||||||
 | 
					                    <RecursionField
 | 
				
			||||||
 | 
					                      onlyRenderProperties
 | 
				
			||||||
 | 
					                      basePath={field.address}
 | 
				
			||||||
 | 
					                      schema={fieldSchema.parent}
 | 
				
			||||||
 | 
					                      filterProperties={(s) => {
 | 
				
			||||||
 | 
					                        return s['x-component'] === 'AssociationField.Selector';
 | 
				
			||||||
 | 
					                      }}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                  </SchemaComponentOptions>
 | 
				
			||||||
 | 
					                </TableSelectorParamsProvider>
 | 
				
			||||||
 | 
					              </FormProvider>
 | 
				
			||||||
 | 
					            </CollectionProvider>
 | 
				
			||||||
 | 
					          </RecordPickerProvider>
 | 
				
			||||||
 | 
					        </ActionContextProvider>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
				
			|||||||
@ -32,6 +32,72 @@ export const formItemSettings = new SchemaSettings({
 | 
				
			|||||||
  name: 'FormItemSettings',
 | 
					  name: 'FormItemSettings',
 | 
				
			||||||
  items: [
 | 
					  items: [
 | 
				
			||||||
    ...(generalSettingsItems as any),
 | 
					    ...(generalSettingsItems as any),
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      name: 'allowAddNewData',
 | 
				
			||||||
 | 
					      type: 'switch',
 | 
				
			||||||
 | 
					      useVisible() {
 | 
				
			||||||
 | 
					        const readPretty = useIsFieldReadPretty();
 | 
				
			||||||
 | 
					        const isAssociationField = useIsAssociationField();
 | 
				
			||||||
 | 
					        const fieldMode = useFieldMode();
 | 
				
			||||||
 | 
					        return !readPretty && isAssociationField && ['SubTable'].includes(fieldMode);
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      useComponentProps() {
 | 
				
			||||||
 | 
					        const { t } = useTranslation();
 | 
				
			||||||
 | 
					        const field = useField<Field>();
 | 
				
			||||||
 | 
					        const fieldSchema = useFieldSchema();
 | 
				
			||||||
 | 
					        const { dn, refresh } = useDesignable();
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					          title: t('Allow add new'),
 | 
				
			||||||
 | 
					          checked: fieldSchema['x-component-props']?.allowAddnew !== (false as boolean),
 | 
				
			||||||
 | 
					          onChange(value) {
 | 
				
			||||||
 | 
					            const schema = {
 | 
				
			||||||
 | 
					              ['x-uid']: fieldSchema['x-uid'],
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            field.componentProps.allowAddnew = value;
 | 
				
			||||||
 | 
					            fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {};
 | 
				
			||||||
 | 
					            fieldSchema['x-component-props'].allowAddnew = value;
 | 
				
			||||||
 | 
					            schema['x-component-props'] = fieldSchema['x-component-props'];
 | 
				
			||||||
 | 
					            dn.emit('patch', {
 | 
				
			||||||
 | 
					              schema,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            refresh();
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      name: 'allowSelectExistingRecord',
 | 
				
			||||||
 | 
					      type: 'switch',
 | 
				
			||||||
 | 
					      useVisible() {
 | 
				
			||||||
 | 
					        const readPretty = useIsFieldReadPretty();
 | 
				
			||||||
 | 
					        const isAssociationField = useIsAssociationField();
 | 
				
			||||||
 | 
					        const fieldMode = useFieldMode();
 | 
				
			||||||
 | 
					        return !readPretty && isAssociationField && ['SubTable'].includes(fieldMode);
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      useComponentProps() {
 | 
				
			||||||
 | 
					        const { t } = useTranslation();
 | 
				
			||||||
 | 
					        const field = useField<Field>();
 | 
				
			||||||
 | 
					        const fieldSchema = useFieldSchema();
 | 
				
			||||||
 | 
					        const { dn, refresh } = useDesignable();
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					          title: t('Allow selection of existing records'),
 | 
				
			||||||
 | 
					          checked: fieldSchema['x-component-props']?.allowSelectExistingRecord,
 | 
				
			||||||
 | 
					          onChange(value) {
 | 
				
			||||||
 | 
					            const schema = {
 | 
				
			||||||
 | 
					              ['x-uid']: fieldSchema['x-uid'],
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            field.componentProps.allowSelectExistingRecord = value;
 | 
				
			||||||
 | 
					            fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {};
 | 
				
			||||||
 | 
					            fieldSchema['x-component-props'].allowSelectExistingRecord = value;
 | 
				
			||||||
 | 
					            schema['x-component-props'] = fieldSchema['x-component-props'];
 | 
				
			||||||
 | 
					            dn.emit('patch', {
 | 
				
			||||||
 | 
					              schema,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            refresh();
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      name: 'quickUpload',
 | 
					      name: 'quickUpload',
 | 
				
			||||||
      type: 'switch',
 | 
					      type: 'switch',
 | 
				
			||||||
 | 
				
			|||||||
@ -266,9 +266,9 @@ test.describe('form item & create form', () => {
 | 
				
			|||||||
    // 选择 Sub-form
 | 
					    // 选择 Sub-form
 | 
				
			||||||
    await (async (page: Page, fieldName: string) => {
 | 
					    await (async (page: Page, fieldName: string) => {
 | 
				
			||||||
      await page.getByLabel(`block-item-CollectionField-general-form-general.${fieldName}-${fieldName}`).hover();
 | 
					      await page.getByLabel(`block-item-CollectionField-general-form-general.${fieldName}-${fieldName}`).hover();
 | 
				
			||||||
      await page
 | 
					      // await page
 | 
				
			||||||
        .getByLabel(`designer-schema-settings-CollectionField-FormItem.Designer-general-general.${fieldName}`)
 | 
					      //   .getByLabel(`designer-schema-settings-CollectionField-FormItem.Designer-general-general.${fieldName}`)
 | 
				
			||||||
        .hover();
 | 
					      //   .hover();
 | 
				
			||||||
    })(page, 'manyToMany');
 | 
					    })(page, 'manyToMany');
 | 
				
			||||||
    await page.getByRole('menuitem', { name: 'Field component' }).click();
 | 
					    await page.getByRole('menuitem', { name: 'Field component' }).click();
 | 
				
			||||||
    await page.getByRole('option', { name: 'Sub-form', exact: true }).click();
 | 
					    await page.getByRole('option', { name: 'Sub-form', exact: true }).click();
 | 
				
			||||||
 | 
				
			|||||||
@ -419,7 +419,7 @@ test.describe('table column & sub-table in edit form', () => {
 | 
				
			|||||||
      ],
 | 
					      ],
 | 
				
			||||||
      variableValue: ['Current user', 'Nickname'],
 | 
					      variableValue: ['Current user', 'Nickname'],
 | 
				
			||||||
      expectVariableValue: async () => {
 | 
					      expectVariableValue: async () => {
 | 
				
			||||||
        await page.getByRole('button', { name: 'plus' }).click();
 | 
					        await page.getByRole('button', { name: 'Add new' }).click();
 | 
				
			||||||
        await expect(
 | 
					        await expect(
 | 
				
			||||||
          page
 | 
					          page
 | 
				
			||||||
            .getByLabel('block-item-CollectionField-general-form-general.singleLineText-singleLineText')
 | 
					            .getByLabel('block-item-CollectionField-general-form-general.singleLineText-singleLineText')
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user