diff --git a/packages/core/client/src/locale/en_US.json b/packages/core/client/src/locale/en_US.json
index cc614f0c9..8f75681ec 100644
--- a/packages/core/client/src/locale/en_US.json
+++ b/packages/core/client/src/locale/en_US.json
@@ -267,6 +267,7 @@
"Sub-table": "Sub-table",
"Sub-details": "Sub-details",
"Sub-form(Popover)": "Sub-form(Popover)",
+ "Sub-form(Drawer)": "Sub-form(Drawer)",
"System info": "System info",
"Created at": "Created at",
"Last updated at": "Last updated at",
diff --git a/packages/core/client/src/locale/zh-CN.json b/packages/core/client/src/locale/zh-CN.json
index 2a5c4c32c..06e9d54d1 100644
--- a/packages/core/client/src/locale/zh-CN.json
+++ b/packages/core/client/src/locale/zh-CN.json
@@ -331,6 +331,7 @@
"Subtable": "子表格",
"Sub-form": "子表单",
"Sub-form(Popover)": "子表单(弹窗)",
+ "Sub-form(Drawer)": "子表单(抽屉)",
"Sub-details": "子详情",
"Record picker": "数据选择器",
"Toggles the subfield mode": "切换子字段模式",
diff --git a/packages/core/client/src/schema-component/antd/association-field/AssociationSelect.tsx b/packages/core/client/src/schema-component/antd/association-field/AssociationSelect.tsx
index a7ac1d6ca..250dc7432 100644
--- a/packages/core/client/src/schema-component/antd/association-field/AssociationSelect.tsx
+++ b/packages/core/client/src/schema-component/antd/association-field/AssociationSelect.tsx
@@ -4,12 +4,12 @@ import { RecursionField, connect, mapProps, observer, useField, useFieldSchema,
import { uid } from '@formily/shared';
import { Space, message } from 'antd';
import { isFunction } from 'mathjs';
-import React, { useEffect, useState } from 'react';
+import React, { useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
-import { RecordProvider, useAPIClient, useCollectionRecordData } from '../../../';
+import { RecordProvider, useAPIClient, useApp, useCollectionRecordData } from '../../../';
import { isVariable } from '../../../variables/utils/isVariable';
import { getInnermostKeyAndValue } from '../../common/utils/uitls';
-import { RemoteSelect, RemoteSelectProps } from '../remote-select';
+import { RemoteSelectProps } from '../remote-select';
import useServiceOptions, { useAssociationFieldContext } from './hooks';
export type AssociationSelectProps
= RemoteSelectProps
& {
@@ -60,6 +60,9 @@ const InternalAssociationSelect = observer(
const resource = api.resource(collectionField.target);
const linkageFields = filterAnalyses(field.componentProps?.service?.params?.filter);
const recordData = useCollectionRecordData();
+ const app = useApp();
+ const RemoteSelect = useMemo(() => app.getComponent('RemoteSelect'), [app]);
+
useEffect(() => {
const initValue = isVariable(field.value) ? undefined : field.value;
const value = Array.isArray(initValue) ? initValue.filter(Boolean) : initValue;
@@ -165,6 +168,8 @@ export const AssociationSelect = InternalAssociationSelect as unknown as Associa
export const AssociationSelectReadPretty = connect(
(props: any) => {
const service = useServiceOptions(props);
+ const app = useApp();
+ const RemoteSelect = useMemo(() => app.getComponent('RemoteSelect'), [app]) as any;
if (props.fieldNames) {
return ;
}
diff --git a/packages/core/client/src/schema-component/antd/association-field/Editable.tsx b/packages/core/client/src/schema-component/antd/association-field/Editable.tsx
index 0cb2c22e4..8d838ad08 100644
--- a/packages/core/client/src/schema-component/antd/association-field/Editable.tsx
+++ b/packages/core/client/src/schema-component/antd/association-field/Editable.tsx
@@ -14,6 +14,8 @@ import { InternaPopoverNester } from './InternalPopoverNester';
import { InternalSubTable } from './InternalSubTable';
import { CreateRecordAction } from './components/CreateRecordAction';
import { useAssociationFieldContext } from './hooks';
+import { InternalCascader } from './InternalCascader';
+import { InternaDrawerSubTable } from './InternalDrawerSubTable';
const EditableAssociationField = observer(
(props: any) => {
@@ -57,6 +59,8 @@ const EditableAssociationField = observer(
{currentMode === 'SubTable' && }
{currentMode === 'FileManager' && }
{currentMode === 'CascadeSelect' && }
+ {currentMode === 'DrawerSubTable' && }
+ {currentMode === 'Cascader' && }
);
},
diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/association-field/InternalDrawerSubTable.tsx b/packages/core/client/src/schema-component/antd/association-field/InternalDrawerSubTable.tsx
similarity index 89%
rename from packages/plugins/@hera/plugin-core/src/client/schema-components/association-field/InternalDrawerSubTable.tsx
rename to packages/core/client/src/schema-component/antd/association-field/InternalDrawerSubTable.tsx
index 0ddb3bc95..19bc5e161 100644
--- a/packages/plugins/@hera/plugin-core/src/client/schema-components/association-field/InternalDrawerSubTable.tsx
+++ b/packages/core/client/src/schema-component/antd/association-field/InternalDrawerSubTable.tsx
@@ -4,9 +4,12 @@ import { observer } from '@formily/react';
import React, { useContext, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { InternalSubTable } from './InternalSubTable';
-import { ActionContext, ActionContextProvider, __UNSAFE__, useGetAriaLabelOfPopover } from '@nocobase/client';
import { Button, Drawer } from 'antd';
-const { useAssociationFieldContext, useSetAriaLabelForPopover, ReadPrettyInternalViewer } = __UNSAFE__;
+import { useAssociationFieldContext } from './hooks';
+import { ActionContext, ActionContextProvider } from '../action/context';
+import { useGetAriaLabelOfPopover } from '../action';
+import { ReadPrettyInternalViewer } from './InternalViewer';
+import { useSetAriaLabelForPopover } from '../action/hooks/useSetAriaLabelForPopover';
export const InternaDrawerSubTable = observer(
(props) => {
diff --git a/packages/core/client/src/schema-component/hooks/useFieldModeOptions.tsx b/packages/core/client/src/schema-component/hooks/useFieldModeOptions.tsx
index 12c2fe495..52824c30b 100644
--- a/packages/core/client/src/schema-component/hooks/useFieldModeOptions.tsx
+++ b/packages/core/client/src/schema-component/hooks/useFieldModeOptions.tsx
@@ -55,6 +55,7 @@ export const useFieldModeOptions = (props?) => {
{ label: t('Cascade Select'), value: 'CascadeSelect' },
!isTableField && { label: t('Sub-form'), value: 'Nester' },
{ label: t('Sub-form(Popover)'), value: 'PopoverNester' },
+ { label: t('Sub-form(Drawer)'), value: 'DrawerSubTable' },
{ label: t('Cascader'), value: 'Cascader' },
];
}
@@ -72,6 +73,7 @@ export const useFieldModeOptions = (props?) => {
{ label: t('Record picker'), value: 'Picker' },
!isTableField && { label: t('Sub-form'), value: 'Nester' },
{ label: t('Sub-form(Popover)'), value: 'PopoverNester' },
+ { label: t('Sub-form(Drawer)'), value: 'DrawerSubTable' },
!isTableField && { label: t('Sub-table'), value: 'SubTable' },
];
case 'm2m':
@@ -88,6 +90,7 @@ export const useFieldModeOptions = (props?) => {
!isTableField && { label: t('Sub-table'), value: 'SubTable' },
!isTableField && { label: t('Sub-form'), value: 'Nester' },
{ label: t('Sub-form(Popover)'), value: 'PopoverNester' },
+ { label: t('Sub-form(Drawer)'), value: 'DrawerSubTable' },
];
case 'm2o':
case 'linkTo':
@@ -102,6 +105,7 @@ export const useFieldModeOptions = (props?) => {
{ label: t('Record picker'), value: 'Picker' },
!isTableField && { label: t('Sub-form'), value: 'Nester' },
{ label: t('Sub-form(Popover)'), value: 'PopoverNester' },
+ { label: t('Sub-form(Drawer)'), value: 'DrawerSubTable' },
{ label: t('Cascader'), value: 'Cascader' },
];
@@ -117,6 +121,7 @@ export const useFieldModeOptions = (props?) => {
{ label: t('Record picker'), value: 'Picker' },
!isTableField && { label: t('Sub-form'), value: 'Nester' },
{ label: t('Sub-form(Popover)'), value: 'PopoverNester' },
+ { label: t('Sub-form(Drawer)'), value: 'DrawerSubTable' },
];
}
}, [t, collectionField?.interface, label]);
diff --git a/packages/plugins/@hera/plugin-core/src/client/index.tsx b/packages/plugins/@hera/plugin-core/src/client/index.tsx
index 858d950d6..94216ab1e 100644
--- a/packages/plugins/@hera/plugin-core/src/client/index.tsx
+++ b/packages/plugins/@hera/plugin-core/src/client/index.tsx
@@ -36,7 +36,6 @@ import { isValid } from '@formily/shared';
import { useCreateActionProps } from './hooks/useCreateActionProps';
import { PageLayout } from './pages/PageLayout';
import { useOutboundActionProps } from './hooks/useOutboundActionProps';
-import { ExtendedAssociationField } from './schema-components/association-field/Editable';
import { AssociatedField } from './components/AssociatedField';
import { DatePicker } from './schema-components/date-picker';
import { Page } from './schema-components/page';
@@ -162,7 +161,6 @@ export class PluginCoreClient extends Plugin {
DatePicker,
RemoteSelect,
SignatureInput,
- AssociationField: ExtendedAssociationField,
OutboundButton,
OutboundLinkActionInitializer,
PDFViewerBlockInitializer,
diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/association-field/AssociationSelect.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-components/association-field/AssociationSelect.tsx
deleted file mode 100644
index f10f4d7ca..000000000
--- a/packages/plugins/@hera/plugin-core/src/client/schema-components/association-field/AssociationSelect.tsx
+++ /dev/null
@@ -1,182 +0,0 @@
-import { LoadingOutlined, PlusOutlined } from '@ant-design/icons';
-import { onFieldChange, FormPath } from '@formily/core';
-import { RecursionField, connect, mapProps, observer, useField, useFieldSchema, useForm } from '@formily/react';
-import { uid } from '@formily/shared';
-import { Space, message } from 'antd';
-import { isFunction } from 'mathjs2';
-import React, { useEffect, useState } from 'react';
-import { useTranslation } from 'react-i18next';
-import { RecordProvider, RemoteSelectProps, useAPIClient } from '@nocobase/client';
-import { __UNSAFE__ } from '@nocobase/client';
-import { RemoteSelect } from '../remote-select';
-
-const { getInnermostKeyAndValue, useServiceOptions, useAssociationFieldContext, isVariable } = __UNSAFE__;
-
-export type AssociationSelectProps
= RemoteSelectProps
& {
- action?: string;
- multiple?: boolean;
-};
-
-export const filterAnalyses = (filters): any[] => {
- if (!filters) {
- return;
- }
- const type = Object.keys(filters)[0] || '$and';
- const conditions = filters[type];
- const results = [];
- conditions?.map((c) => {
- const jsonlogic = getInnermostKeyAndValue(c);
- const operator = jsonlogic?.key;
-
- if (!operator) {
- return true;
- }
- const regex = /\{\{\$(?:[a-zA-Z_]\w*)\.([a-zA-Z_]\w*)(?:\.id)?\}\}/;
- const fieldName = jsonlogic?.value?.match?.(regex)?.[1];
- if (fieldName) {
- results.push(fieldName);
- }
- });
- return results;
-};
-
-const InternalAssociationSelect = observer((props: AssociationSelectProps) => {
- const { objectValue = true } = props;
- const field: any = useField();
- const fieldSchema = useFieldSchema();
- const service = useServiceOptions(props);
- const { options: collectionField } = useAssociationFieldContext();
- const initValue = isVariable(props.value) ? undefined : props.value;
- const value = Array.isArray(initValue) ? initValue.filter(Boolean) : initValue;
- // 因为通过 Schema 的形式书写的组件,在值变更的时候 `value` 的值没有改变,所以需要维护一个 `innerValue` 来变更值
- const [innerValue, setInnerValue] = useState(value);
- const addMode = fieldSchema['x-component-props']?.addMode;
- const isAllowAddNew = fieldSchema['x-add-new'];
- const { t } = useTranslation();
- const { multiple } = props;
- const form = useForm();
- const api = useAPIClient();
- const resource = api.resource(collectionField.target);
- const linkageFields = filterAnalyses(field.componentProps?.service?.params?.filter);
- useEffect(() => {
- const initValue = isVariable(field.value) ? undefined : field.value;
- const value = Array.isArray(initValue) ? initValue.filter(Boolean) : initValue;
- setInnerValue(value);
- }, [field.value]);
- useEffect(() => {
- const id = uid();
- form.addEffects(id, () => {
- if (linkageFields?.length > 0) {
- //支持深层次子表单
- onFieldChange('*', (fieldPath: any) => {
- if (linkageFields.includes(fieldPath.props.name) && field.value) {
- props.onChange(field.initialValue);
- setInnerValue(field.initialValue);
- }
- });
- }
- });
-
- return () => {
- form.removeEffects(id);
- };
- }, []);
-
- const handleCreateAction = async (props) => {
- const { search: value, callBack } = props;
- const {
- data: { data },
- } = await resource.create({
- values: {
- [field?.componentProps?.fieldNames?.label || 'id']: value,
- },
- });
- if (data) {
- if (['m2m', 'o2m'].includes(collectionField?.interface) && multiple !== false) {
- const values = form.getValuesIn(field.path) || [];
- values.push(data);
- form.setValuesIn(field.path, values);
- field.onInput(values);
- } else {
- form.setValuesIn(field.path, data);
- field.onInput(data);
- }
- isFunction(callBack) && callBack?.();
- message.success(t('Saved successfully'));
- }
- };
- const QuickAddContent = (props) => {
- return (
-
handleCreateAction(props)}
- style={{ cursor: 'pointer', padding: '5px 12px', color: '#0d0c0c' }}
- >
-
-
{t('Add') + ` “${props.search}” `}
-
- );
- };
- return (
-
-
- {
- const val = value?.length !== 0 ? value : null;
- props.onChange?.(val);
- }}
- CustomDropdownRender={addMode === 'quickAdd' && QuickAddContent}
- >
-
- {(addMode === 'modalAdd' || isAllowAddNew) && (
-
- {
- return s['x-component'] === 'Action';
- }}
- />
-
- )}
-
-
- );
-});
-
-interface AssociationSelectInterface {
- (props: any): React.ReactElement;
- Designer: React.FC;
- FilterDesigner: React.FC;
-}
-
-export const AssociationSelect = InternalAssociationSelect as unknown as AssociationSelectInterface;
-
-export const AssociationSelectReadPretty = connect(
- (props: any) => {
- const service = useServiceOptions(props);
- if (props.fieldNames) {
- return ;
- }
- return null;
- },
- mapProps(
- {
- dataSource: 'options',
- loading: true,
- },
- (props, field) => {
- return {
- ...props,
- fieldNames: props.fieldNames && { ...props.fieldNames, ...field.componentProps.fieldNames },
- suffixIcon: field?.['loading'] || field?.['validating'] ? : props.suffixIcon,
- };
- },
- ),
-);
diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/association-field/Editable.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-components/association-field/Editable.tsx
deleted file mode 100644
index 38e04f9b9..000000000
--- a/packages/plugins/@hera/plugin-core/src/client/schema-components/association-field/Editable.tsx
+++ /dev/null
@@ -1,95 +0,0 @@
-import { Field } from '@formily/core';
-import { connect, mapReadPretty, observer, useField, useForm } from '@formily/react';
-import React from 'react';
-import {
- SchemaComponentOptions,
- useCollection_deprecated,
- useAssociationCreateActionProps as useCAP,
- Action,
- __UNSAFE__,
-} from '@nocobase/client';
-import { InternaDrawerSubTable } from './InternalDrawerSubTable';
-import { AssociationSelect } from './AssociationSelect';
-const {
- CreateRecordAction,
- useAssociationFieldContext,
- InternalPicker,
- InternalNester,
- AssociationFieldProvider,
- InternaPopoverNester,
- InternalSubTable,
- InternalCascader,
- InternalFileManager,
- InternalCascadeSelect,
- SubTable,
- ReadPretty,
- Nester,
-} = __UNSAFE__;
-
-const EditableAssociationField = observer(
- (props: any) => {
- const { multiple } = props;
- const field: Field = useField();
- const form = useForm();
- const { options: collectionField, currentMode } = useAssociationFieldContext();
-
- const useCreateActionProps = () => {
- const { onClick } = useCAP();
- const actionField: any = useField();
- const { getPrimaryKey } = useCollection_deprecated();
- const primaryKey = getPrimaryKey();
- return {
- async onClick() {
- await onClick();
- const { data } = actionField.data?.data?.data || {};
- if (data) {
- if (['m2m', 'o2m'].includes(collectionField?.interface) && multiple !== false) {
- const values = form.getValuesIn(field.path) || [];
- if (!values.find((v) => v[primaryKey] === data[primaryKey])) {
- values.push(data);
- form.setValuesIn(field.path, values);
- field.onInput(values);
- }
- } else {
- form.setValuesIn(field.path, data);
- field.onInput(data);
- }
- }
- },
- };
- };
- return (
-
- {currentMode === 'Picker' && }
- {currentMode === 'Nester' && }
- {currentMode === 'PopoverNester' && }
- {currentMode === 'Select' && }
- {currentMode === 'SubTable' && }
- {currentMode === 'FileManager' && }
- {currentMode === 'CascadeSelect' && }
- {currentMode === 'DrawerSubTable' && }
- {currentMode === 'Cascader' && }
-
- );
- },
- { displayName: 'EditableAssociationField' },
-);
-
-const ExtendedEditable = observer(
- (props) => {
- return (
-
-
-
- );
- },
- { displayName: 'ExtendedEditable' },
-);
-
-export const ExtendedAssociationField: any = connect(ExtendedEditable, mapReadPretty(ReadPretty));
-ExtendedAssociationField.SubTable = SubTable;
-ExtendedAssociationField.Nester = Nester;
-ExtendedAssociationField.AddNewer = Action.Container;
-ExtendedAssociationField.Selector = Action.Container;
-ExtendedAssociationField.Viewer = Action.Container;
-ExtendedAssociationField.InternalSelect = InternalPicker;
diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/association-field/InternalSubTable.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-components/association-field/InternalSubTable.tsx
deleted file mode 100644
index 9fdab1a00..000000000
--- a/packages/plugins/@hera/plugin-core/src/client/schema-components/association-field/InternalSubTable.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-import { css, cx } from '@emotion/css';
-import { FormLayout } from '@formily/antd-v5';
-import { RecursionField, useField, useFieldSchema, observer } from '@formily/react';
-import React, { useEffect } from 'react';
-import { useInsertSchema } from '../../components/AssociatedField';
-import {
- ACLCollectionProvider,
- CollectionProvider_deprecated,
- __UNSAFE__,
- useACLActionParamsContext,
-} from '@nocobase/client';
-const { useAssociationFieldContext, schema } = __UNSAFE__;
-
-export const InternalSubTable = observer(
- () => {
- const field = useField();
- const fieldSchema = useFieldSchema();
- const insertNester = useInsertSchema('SubTable');
- const { options: collectionField } = useAssociationFieldContext();
- const showTitle = fieldSchema['x-decorator-props']?.showTitle ?? true;
- const { actionName } = useACLActionParamsContext();
- useEffect(() => {
- insertNester(schema.SubTable);
- }, []);
- return (
-
-
-
- .ant-card-bordered {
- border: none;
- }
- `]: showTitle === false,
- },
- )}
- >
- {
- return s['x-component'] === 'AssociationField.SubTable';
- }}
- />
-
-
-
-
- );
- },
- { displayName: 'InternalSubTable' },
-);