feat: 多对多关系可以添加关联 (#1333)

Co-authored-by: sealday <zhanglin@daoyoucloud.com>
Reviewed-on: daoyoucloud/tachybase#1333
Reviewed-by: sealday <zhanglin@daoyoucloud.com>
Co-authored-by: wjh <wwwjh0710@163.com>
Co-committed-by: wjh <wwwjh0710@163.com>
This commit is contained in:
wjh 2024-08-12 11:48:16 +08:00 committed by sealday
parent 9dc412dbf3
commit 27cda38a90
20 changed files with 291 additions and 86 deletions

View File

@ -29,7 +29,6 @@ export const SchemaInitializerItem = memo(
const childrenItems = useSchemaInitializerMenuItems(items, name, onClick); const childrenItems = useSchemaInitializerMenuItems(items, name, onClick);
const { componentCls, hashId } = useSchemaInitializerStyles(); const { componentCls, hashId } = useSchemaInitializerStyles();
const { attribute } = useAriaAttributeOfMenuItem(); const { attribute } = useAriaAttributeOfMenuItem();
const menuItems = useMemo(() => { const menuItems = useMemo(() => {
if (!(items && items.length > 0)) return undefined; if (!(items && items.length > 0)) return undefined;
return [ return [

View File

@ -1,14 +1,17 @@
import React, { createContext, useContext, useEffect, useMemo, useRef } from 'react'; import React, { createContext, useContext, useEffect, useMemo, useRef } from 'react';
import { createForm, RecursionField, Schema, useField, useFieldSchema } from '@tachybase/schema'; import { createForm, Form, Schema, useField } from '@tachybase/schema';
import { Spin } from 'antd'; import { Spin } from 'antd';
import { withDynamicSchemaProps } from '../application/hoc/withDynamicSchemaProps'; import { withDynamicSchemaProps } from '../application/hoc/withDynamicSchemaProps';
import { useCollection_deprecated } from '../collection-manager'; import {
import { CollectionRecord, useCollectionParentRecordData, useCollectionRecord } from '../data-source'; CollectionRecord,
useCollectionManager,
useCollectionParentRecordData,
useCollectionRecord,
} from '../data-source';
import { RecordProvider, useRecord } from '../record-provider'; import { RecordProvider, useRecord } from '../record-provider';
import { useActionContext, useDesignable } from '../schema-component'; import { useActionContext } from '../schema-component';
import { Templates as DataTemplateSelect } from '../schema-component/antd/form-v2/Templates';
import { BlockProvider, useBlockRequestContext } from './BlockProvider'; import { BlockProvider, useBlockRequestContext } from './BlockProvider';
import { FormActiveFieldsProvider } from './hooks/useFormActiveFields'; import { FormActiveFieldsProvider } from './hooks/useFormActiveFields';
@ -29,8 +32,9 @@ export const FormBlockContext = createContext<{
FormBlockContext.displayName = 'FormBlockContext'; FormBlockContext.displayName = 'FormBlockContext';
const InternalFormBlockProvider = (props) => { const InternalFormBlockProvider = (props) => {
const cm = useCollectionManager();
const ctx = useFormBlockContext(); const ctx = useFormBlockContext();
const { action, readPretty, params, collection } = props; const { action, readPretty, params, collection, association } = props;
const field = useField(); const field = useField();
const form = useMemo( const form = useMemo(
() => () =>
@ -55,10 +59,23 @@ const InternalFormBlockProvider = (props) => {
resource, resource,
updateAssociationValues, updateAssociationValues,
formBlockRef, formBlockRef,
collectionName: collection, collectionName: collection || cm.getCollectionField(association)?.target,
formRecord: record, formRecord: record,
}; };
}, [action, collection, ctx, field, form, params, record, resource, service, updateAssociationValues]); }, [
action,
association,
cm,
collection,
ctx,
field,
form,
params,
record,
resource,
service,
updateAssociationValues,
]);
if (service.loading && Object.keys(form?.initialValues)?.length === 0 && action) { if (service.loading && Object.keys(form?.initialValues)?.length === 0 && action) {
return <Spin />; return <Spin />;
@ -67,21 +84,24 @@ const InternalFormBlockProvider = (props) => {
return ( return (
<FormBlockContext.Provider value={formBlockValue}> <FormBlockContext.Provider value={formBlockValue}>
<RecordProvider isNew={record?.isNew} parent={record?.parentRecord?.data} record={record?.data}> <RecordProvider isNew={record?.isNew} parent={record?.parentRecord?.data} record={record?.data}>
<div ref={formBlockRef}> <div ref={formBlockRef}>{props.children}</div>
<RenderChildrenWithDataTemplates form={form} />
</div>
</RecordProvider> </RecordProvider>
</FormBlockContext.Provider> </FormBlockContext.Provider>
); );
}; };
/** /**
* @internal
* update create * update create
* @returns * @returns
*/ */
export const useFormBlockType = () => { export const useFormBlockType = () => {
const ctx = useFormBlockContext() || {}; const ctx = useFormBlockContext() || {};
const res = useMemo(() => {
return { type: ctx.type } as { type: 'update' | 'create' }; return { type: ctx.type } as { type: 'update' | 'create' };
}, [ctx.type]);
return res;
}; };
export const useIsDetailBlock = () => { export const useIsDetailBlock = () => {
@ -91,26 +111,8 @@ export const useIsDetailBlock = () => {
}; };
export const FormBlockProvider = withDynamicSchemaProps((props) => { export const FormBlockProvider = withDynamicSchemaProps((props) => {
const record = useRecord();
const parentRecordData = useCollectionParentRecordData(); const parentRecordData = useCollectionParentRecordData();
const { collection, isCusomeizeCreate, parentRecord } = props; const { parentRecord } = props;
const { __collection } = record;
const currentCollection = useCollection_deprecated();
const { designable } = useDesignable();
const isDetailBlock = useIsDetailBlock();
let detailFlag = false;
if (isDetailBlock) {
detailFlag = true;
if (!designable && __collection) {
detailFlag = __collection === collection;
}
}
const createFlag =
(currentCollection.name === (collection?.name || collection) && !isDetailBlock) || !currentCollection.name;
if (!detailFlag && !createFlag && !isCusomeizeCreate) {
return null;
}
return ( return (
<BlockProvider <BlockProvider
@ -126,11 +128,18 @@ export const FormBlockProvider = withDynamicSchemaProps((props) => {
); );
}); });
/**
* @internal
* @returns
*/
export const useFormBlockContext = () => { export const useFormBlockContext = () => {
return useContext(FormBlockContext); return useContext(FormBlockContext);
}; };
export const useFormBlockProps = () => { /**
* @internal
*/
export const useFormBlockProps = (shouldClearInitialValues = false) => {
const ctx = useFormBlockContext(); const ctx = useFormBlockContext();
const record = useRecord(); const record = useRecord();
const { fieldSchema } = useActionContext(); const { fieldSchema } = useActionContext();
@ -154,19 +163,9 @@ export const useFormBlockProps = () => {
}; };
}; };
const RenderChildrenWithDataTemplates = ({ form }) => { /**
const FieldSchema = useFieldSchema(); * @internal
const { findComponent } = useDesignable(); */
const field = useField();
const Component = findComponent(field.component?.[0]) || React.Fragment;
return (
<Component {...field.componentProps}>
<DataTemplateSelect style={{ marginBottom: 18 }} form={form} />
<RecursionField schema={FieldSchema} onlyRenderProperties />
</Component>
);
};
export const findFormBlock = (schema: Schema) => { export const findFormBlock = (schema: Schema) => {
while (schema) { while (schema) {
if (schema['x-decorator'] === 'FormBlockProvider') { if (schema['x-decorator'] === 'FormBlockProvider') {

View File

@ -236,6 +236,9 @@ export const ACLActionProvider = (props) => {
if (!actionPath) { if (!actionPath) {
return <>{props.children}</>; return <>{props.children}</>;
} }
if (!resource) {
return <>{props.children}</>;
}
const params = parseAction(actionPath, { schema, recordPkValue }); const params = parseAction(actionPath, { schema, recordPkValue });
if (!params) { if (!params) {
return <ACLActionParamsContext.Provider value={params}>{props.children}</ACLActionParamsContext.Provider>; return <ACLActionParamsContext.Provider value={params}>{props.children}</ACLActionParamsContext.Provider>;

View File

@ -1,6 +1,6 @@
import React, { createContext, FC, ReactNode, useContext } from 'react'; import React, { createContext, FC, ReactNode, useContext } from 'react';
import { CollectionFieldProvider } from '../collection-field'; import { CollectionFieldProvider, useCollectionField } from '../collection-field';
import { CollectionDeletedPlaceholder } from '../components/CollectionDeletedPlaceholder'; import { CollectionDeletedPlaceholder } from '../components/CollectionDeletedPlaceholder';
import { Collection } from './Collection'; import { Collection } from './Collection';
import { useCollectionManager } from './CollectionManagerProvider'; import { useCollectionManager } from './CollectionManagerProvider';
@ -42,3 +42,12 @@ export const AssociationProvider: FC<AssociationProviderProps> = (props) => {
</CollectionProvider> </CollectionProvider>
); );
}; };
/**
* users.roles
* @returns
*/
export const useAssociationName = () => {
const field = useCollectionField();
return field ? `${field.collectionName}.${field.name}` : null;
};

View File

@ -0,0 +1,68 @@
import { Field, useField, useFieldSchema } from '@tachybase/schema';
import _ from 'lodash';
import { useTranslation } from 'react-i18next';
import type { SchemaSettingsItemType } from '../../application';
import { useColumnSchema, useCompile, useDesignable } from '../../schema-component';
import { useCollectionField } from '../collection-field/CollectionFieldProvider';
import { useDataSourceManager } from '../data-source/DataSourceManagerProvider';
export const fieldComponentSettingsItem: SchemaSettingsItemType = {
name: 'fieldComponent',
type: 'select',
useVisible() {
const collectionField = useCollectionField();
const dm = useDataSourceManager();
if (!collectionField) return false;
const collectionInterface = dm.collectionFieldInterfaceManager.getFieldInterface(collectionField?.interface) as any;
return (
Array.isArray(collectionInterface?.componentOptions) &&
collectionInterface.componentOptions.length > 1 &&
collectionInterface.componentOptions.filter((item) => !item.useVisible || item.useVisible()).length > 1
);
},
useComponentProps() {
const { t } = useTranslation();
const field = useField<Field>();
const schema = useFieldSchema();
const collectionField = useCollectionField();
const dm = useDataSourceManager();
const collectionInterface = dm.collectionFieldInterfaceManager.getFieldInterface(collectionField?.interface);
const { fieldSchema: tableColumnSchema } = useColumnSchema();
const fieldSchema = tableColumnSchema || schema;
const { dn } = useDesignable();
const compile = useCompile();
const options =
collectionInterface?.componentOptions
?.filter((item) => !item.useVisible || item.useVisible())
?.map((item) => {
return {
label: compile(item.label),
value: item.value,
useProps: item.useProps,
};
}) || [];
return {
title: t('Field component'),
options,
value: fieldSchema['x-component-props']?.['component'] || options[0]?.value,
onChange(component) {
const componentOptions = options.find((item) => item.value === component);
const componentProps = {
component,
...(componentOptions?.useProps?.() || {}),
};
_.set(fieldSchema, 'x-component-props', componentProps);
field.componentProps = componentProps;
dn.emit('patch', {
schema: {
['x-uid']: fieldSchema['x-uid'],
'x-component-props': componentProps,
},
});
},
};
},
};

View File

@ -0,0 +1 @@
export * from './fieldComponent';

View File

@ -7,3 +7,4 @@ export * from './data-block';
export * from './data-source'; export * from './data-source';
export * from './collection-record'; export * from './collection-record';
export * from './utils'; export * from './utils';
export * from './commonsSettingsItem';

View File

@ -836,5 +836,6 @@
"Show Data":"Show Data", "Show Data":"Show Data",
"Setting Down Title":"Setting Down Title", "Setting Down Title":"Setting Down Title",
"The current return type is not a document type":"The current return type is not a document type", "The current return type is not a document type":"The current return type is not a document type",
"Test Data":"Test Data" "Test Data":"Test Data",
"Add associations": "Add associations"
} }

View File

@ -32,7 +32,10 @@ export const CreateActionInitializer = () => {
type: 'void', type: 'void',
'x-component': 'Tabs', 'x-component': 'Tabs',
'x-component-props': {}, 'x-component-props': {},
'x-initializer': 'TabPaneInitializersForCreateFormBlock', 'x-initializer': 'popup:addTab',
'x-initializer-props': {
gridInitializer: 'popup:addNew:addBlock',
},
properties: { properties: {
tab1: { tab1: {
type: 'void', type: 'void',

View File

@ -1,4 +1,7 @@
import { useTranslation } from 'react-i18next';
import { CompatibleSchemaInitializer } from '../../../application/schema-initializer/CompatibleSchemaInitializer'; import { CompatibleSchemaInitializer } from '../../../application/schema-initializer/CompatibleSchemaInitializer';
import { useCollection } from '../../../data-source/collection/CollectionProvider';
import { gridRowColWrap } from '../../../schema-initializer/utils'; import { gridRowColWrap } from '../../../schema-initializer/utils';
/** /**
@ -48,13 +51,36 @@ export const createFormBlockInitializers = new CompatibleSchemaInitializer(
type: 'itemGroup', type: 'itemGroup',
title: '{{t("Data blocks")}}', title: '{{t("Data blocks")}}',
name: 'dataBlocks', name: 'dataBlocks',
children: [ useChildren() {
const currentCollection = useCollection();
const { t } = useTranslation();
return [
{ {
name: 'form', name: 'form',
title: '{{t("Form")}}', title: '{{t("Form")}}',
Component: 'CreateFormBlockInitializer', Component: 'FormBlockInitializer',
collectionName: currentCollection.name,
dataSource: currentCollection.dataSource,
componentProps: {
filterCollections({ collection, associationField }) {
if (associationField) {
return false;
}
if (collection.name === currentCollection.name) {
return true;
}
},
showAssociationFields: true,
onlyCurrentDataSource: true,
hideSearch: true,
componentType: 'FormItem',
currentText: t('Current collection'),
otherText: t('Other collections'),
},
},
];
}, },
],
}, },
{ {
type: 'itemGroup', type: 'itemGroup',

View File

@ -26,7 +26,7 @@ export const ViewActionInitializer = (props) => {
type: 'void', type: 'void',
'x-component': 'Tabs', 'x-component': 'Tabs',
'x-component-props': {}, 'x-component-props': {},
'x-initializer': 'TabPaneInitializers', 'x-initializer': 'popup:addTab',
properties: { properties: {
tab1: { tab1: {
type: 'void', type: 'void',

View File

@ -5,6 +5,7 @@ import { FormOutlined } from '@ant-design/icons';
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../../../application'; import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
import { useBlockAssociationContext } from '../../../../block-provider'; import { useBlockAssociationContext } from '../../../../block-provider';
import { useCollection_deprecated } from '../../../../collection-manager'; import { useCollection_deprecated } from '../../../../collection-manager';
import { useAssociationName, useCollectionManager } from '../../../../data-source';
import { useRecordCollectionDataSourceItems } from '../../../../schema-initializer/utils'; import { useRecordCollectionDataSourceItems } from '../../../../schema-initializer/utils';
import { useSchemaTemplateManager } from '../../../../schema-templates'; import { useSchemaTemplateManager } from '../../../../schema-templates';
import { createDetailsUISchema } from './createDetailsUISchema'; import { createDetailsUISchema } from './createDetailsUISchema';
@ -29,8 +30,7 @@ export const RecordReadPrettyFormBlockInitializer = () => {
export function useCreateSingleDetailsSchema() { export function useCreateSingleDetailsSchema() {
const { insert } = useSchemaInitializer(); const { insert } = useSchemaInitializer();
const { getTemplateSchemaByMode } = useSchemaTemplateManager(); const { getTemplateSchemaByMode } = useSchemaTemplateManager();
const association = useBlockAssociationContext(); const association = useAssociationName();
const templateWrap = useCallback( const templateWrap = useCallback(
(templateSchema, options) => { (templateSchema, options) => {
const { item } = options; const { item } = options;
@ -38,7 +38,7 @@ export function useCreateSingleDetailsSchema() {
const blockSchema = createDetailsUISchema( const blockSchema = createDetailsUISchema(
association association
? { ? {
association, association: association,
dataSource: item.dataSource, dataSource: item.dataSource,
templateSchema: templateSchema, templateSchema: templateSchema,
isCurrent: true, isCurrent: true,

View File

@ -3,6 +3,7 @@ import React, { useCallback } from 'react';
import { FormOutlined } from '@ant-design/icons'; import { FormOutlined } from '@ant-design/icons';
import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application'; import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
import { useAssociationName } from '../../../../data-source/collection/AssociationProvider';
import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection'; import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection';
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer'; import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
import { createCreateFormBlockUISchema } from './createCreateFormBlockUISchema'; import { createCreateFormBlockUISchema } from './createCreateFormBlockUISchema';
@ -17,6 +18,8 @@ export const FormBlockInitializer = ({
showAssociationFields, showAssociationFields,
hideChildrenIfSingleCollection, hideChildrenIfSingleCollection,
hideOtherRecordsInPopup, hideOtherRecordsInPopup,
currentText,
otherText,
}: { }: {
filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean; filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean;
onlyCurrentDataSource: boolean; onlyCurrentDataSource: boolean;
@ -40,6 +43,10 @@ export const FormBlockInitializer = ({
* Other records * Other records
*/ */
hideOtherRecordsInPopup?: boolean; hideOtherRecordsInPopup?: boolean;
/** 用于更改 Current record 的文案 */
currentText?: string;
/** 用于更改 Other records 的文案 */
otherText?: string;
}) => { }) => {
const itemConfig = useSchemaInitializerItem(); const itemConfig = useSchemaInitializerItem();
const { createFormBlock, templateWrap } = useCreateFormBlock(); const { createFormBlock, templateWrap } = useCreateFormBlock();
@ -73,24 +80,47 @@ export const FormBlockInitializer = ({
showAssociationFields={showAssociationFields} showAssociationFields={showAssociationFields}
hideChildrenIfSingleCollection={hideChildrenIfSingleCollection} hideChildrenIfSingleCollection={hideChildrenIfSingleCollection}
hideOtherRecordsInPopup={hideOtherRecordsInPopup} hideOtherRecordsInPopup={hideOtherRecordsInPopup}
currentText={currentText}
otherText={otherText}
/> />
); );
}; };
export const useCreateFormBlock = () => { export const useCreateFormBlock = () => {
const { insert } = useSchemaInitializer(); const { insert } = useSchemaInitializer();
const itemConfig = useSchemaInitializerItem(); const association = useAssociationName();
const { isCusomeizeCreate: isCustomizeCreate } = itemConfig; const { isCusomeizeCreate: isCustomizeCreate } = useSchemaInitializerItem();
const createFormBlock = ({ item }) => { const createFormBlock = useCallback(
({ item, fromOthersInPopup }) => {
if (fromOthersInPopup) {
insert( insert(
createCreateFormBlockUISchema({ createCreateFormBlockUISchema({
collectionName: item.collectionName || item.name, collectionName: item.collectionName || item.name,
dataSource: item.dataSource, dataSource: item.dataSource,
isCusomeizeCreate: isCustomizeCreate, isCusomeizeCreate: true,
}), }),
); );
}; } else {
insert(
createCreateFormBlockUISchema(
association
? {
association,
dataSource: item.dataSource,
isCusomeizeCreate: isCustomizeCreate,
}
: {
collectionName: item.collectionName || item.name,
dataSource: item.dataSource,
isCusomeizeCreate: isCustomizeCreate,
},
),
);
}
},
[association, insert, isCustomizeCreate],
);
const templateWrap = (templateSchema, { item }) => { const templateWrap = (templateSchema, { item }) => {
const schema = createCreateFormBlockUISchema({ const schema = createCreateFormBlockUISchema({

View File

@ -1,10 +1,12 @@
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import { useFieldSchema } from '@tachybase/schema';
import { FormOutlined } from '@ant-design/icons'; import { FormOutlined } from '@ant-design/icons';
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../../../application'; import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
import { useBlockAssociationContext } from '../../../../block-provider'; import { useBlockAssociationContext } from '../../../../block-provider';
import { useCollection_deprecated } from '../../../../collection-manager'; import { useCollection_deprecated } from '../../../../collection-manager';
import { useAssociationName } from '../../../../data-source';
import { useRecordCollectionDataSourceItems } from '../../../../schema-initializer/utils'; import { useRecordCollectionDataSourceItems } from '../../../../schema-initializer/utils';
import { useSchemaTemplateManager } from '../../../../schema-templates'; import { useSchemaTemplateManager } from '../../../../schema-templates';
import { createEditFormBlockUISchema } from './createEditFormBlockUISchema'; import { createEditFormBlockUISchema } from './createEditFormBlockUISchema';
@ -41,10 +43,10 @@ export const RecordFormBlockInitializer = () => {
export function useCreateEditFormBlock() { export function useCreateEditFormBlock() {
const { insert } = useSchemaInitializer(); const { insert } = useSchemaInitializer();
const association = useBlockAssociationContext(); const association = useAssociationName();
const createEditFormBlock = useCallback( const createEditFormBlock = useCallback(
({ item }) => { ({ item }) => {
const collectionName = item.collectionName || item.name;
if (!association && item.associationField) { if (!association && item.associationField) {
const field = item.associationField; const field = item.associationField;
insert( insert(
@ -63,7 +65,7 @@ export function useCreateEditFormBlock() {
isCurrent: true, isCurrent: true,
} }
: { : {
collectionName: item.collectionName || item.name, collectionName,
dataSource: item.dataSource, dataSource: item.dataSource,
}, },
), ),

View File

@ -1,7 +1,19 @@
import React from 'react'; import React from 'react';
import _ from 'lodash';
import { GridRowContext } from '../../../../schema-component';
import { SchemaToolbar } from '../../../../schema-settings'; import { SchemaToolbar } from '../../../../schema-settings';
export const TableColumnSchemaToolbar = (props) => { export const TableColumnSchemaToolbar = (props) => {
return <SchemaToolbar initializer={false} showBorder={false} showBackground {...props} />; return (
<GridRowContext.Provider value={null}>
<SchemaToolbar
initializer={props.initializer || false}
showBorder={false}
showBackground
{..._.omit(props, 'initializer')}
/>
</GridRowContext.Provider>
);
}; };

View File

@ -15,7 +15,7 @@ export const createTableBlockUISchema = (options: {
return { return {
type: 'void', type: 'void',
'x-decorator': 'TableBlockProvider', 'x-decorator': 'TableBlockProvider',
'x-acl-action': `${collectionName}:list`, 'x-acl-action': `${association || collectionName}:list`,
'x-use-decorator-props': 'useTableBlockDecoratorProps', 'x-use-decorator-props': 'useTableBlockDecoratorProps',
'x-decorator-props': { 'x-decorator-props': {
collection: collectionName, collection: collectionName,
@ -63,8 +63,16 @@ export const createTableBlockUISchema = (options: {
'x-action-column': 'actions', 'x-action-column': 'actions',
'x-decorator': 'TableV2.Column.ActionBar', 'x-decorator': 'TableV2.Column.ActionBar',
'x-component': 'TableV2.Column', 'x-component': 'TableV2.Column',
'x-designer': 'TableV2.ActionColumnDesigner', 'x-component-props': {
width: 150,
fixed: 'right',
},
'x-toolbar': 'TableColumnSchemaToolbar',
'x-initializer': 'table:configureItemActions', 'x-initializer': 'table:configureItemActions',
'x-designer': 'TableV2.ActionColumnDesigner',
'x-toolbar-props': {
initializer: 'table:configureItemActions',
},
properties: { properties: {
[uid()]: { [uid()]: {
type: 'void', type: 'void',

View File

@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next';
import { useApp } from '../../../../application'; import { useApp } from '../../../../application';
import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings'; import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings';
import { useCollectionManager_deprecated } from '../../../../collection-manager'; import { useCollectionManager_deprecated } from '../../../../collection-manager';
import { fieldComponentSettingsItem, useCollection } from '../../../../data-source';
import { useDesignable } from '../../../../schema-component'; import { useDesignable } from '../../../../schema-component';
import { useAssociationFieldContext } from '../../../../schema-component/antd/association-field/hooks'; import { useAssociationFieldContext } from '../../../../schema-component/antd/association-field/hooks';
import { useColumnSchema } from '../../../../schema-component/antd/table-v2/Table.Column.Decorator'; import { useColumnSchema } from '../../../../schema-component/antd/table-v2/Table.Column.Decorator';
@ -113,12 +114,15 @@ export const tableColumnSettings = new SchemaSettings({
name: 'sortable', name: 'sortable',
type: 'switch', type: 'switch',
useVisible() { useVisible() {
const collection = useCollection();
const { collectionField } = useColumnSchema(); const { collectionField } = useColumnSchema();
const { getInterface } = useCollectionManager_deprecated(); const { getInterface } = useCollectionManager_deprecated();
const interfaceCfg = getInterface(collectionField?.interface); const interfaceCfg = getInterface(collectionField?.interface);
const { currentMode } = useAssociationFieldContext(); const { currentMode } = useAssociationFieldContext();
return interfaceCfg?.sortable === true && !currentMode; return (
interfaceCfg?.sortable === true && !currentMode && collection?.name === collectionField?.collectionName
);
}, },
useComponentProps() { useComponentProps() {
const field: any = useField(); const field: any = useField();
@ -151,7 +155,12 @@ export const tableColumnSettings = new SchemaSettings({
name: 'setDefaultValue', name: 'setDefaultValue',
useVisible() { useVisible() {
const { fieldSchema } = useColumnSchema(); const { fieldSchema } = useColumnSchema();
return !fieldSchema['x-read-pretty'];
if (!fieldSchema) {
return false;
}
return !fieldSchema?.['x-read-pretty'];
}, },
Component: SchemaSettingsDefaultValue as any, Component: SchemaSettingsDefaultValue as any,
useComponentProps() { useComponentProps() {
@ -167,6 +176,11 @@ export const tableColumnSettings = new SchemaSettings({
useVisible() { useVisible() {
const { uiSchema, fieldSchema } = useColumnSchema(); const { uiSchema, fieldSchema } = useColumnSchema();
const field: any = useField(); const field: any = useField();
if (!fieldSchema) {
return false;
}
const isSubTableColumn = ['QuickEdit', 'FormItem'].includes(fieldSchema['x-decorator']); const isSubTableColumn = ['QuickEdit', 'FormItem'].includes(fieldSchema['x-decorator']);
return isSubTableColumn && !field.readPretty && !uiSchema?.['x-read-pretty']; return isSubTableColumn && !field.readPretty && !uiSchema?.['x-read-pretty'];
}, },
@ -181,6 +195,10 @@ export const tableColumnSettings = new SchemaSettings({
title: t('Required'), title: t('Required'),
checked: fieldSchema.required as boolean, checked: fieldSchema.required as boolean,
onChange: (required) => { onChange: (required) => {
if (!fieldSchema) {
return console.error('fieldSchema is required');
}
const schema = { const schema = {
['x-uid']: fieldSchema['x-uid'], ['x-uid']: fieldSchema['x-uid'],
}; };
@ -204,6 +222,9 @@ export const tableColumnSettings = new SchemaSettings({
useVisible() { useVisible() {
const { fieldSchema, collectionField } = useColumnSchema(); const { fieldSchema, collectionField } = useColumnSchema();
const field: any = useField(); const field: any = useField();
if (!fieldSchema) {
return false;
}
const isSubTableColumn = ['QuickEdit', 'FormItem'].includes(fieldSchema['x-decorator']); const isSubTableColumn = ['QuickEdit', 'FormItem'].includes(fieldSchema['x-decorator']);
return ( return (
isSubTableColumn && isSubTableColumn &&
@ -218,6 +239,9 @@ export const tableColumnSettings = new SchemaSettings({
const { t } = useTranslation(); const { t } = useTranslation();
const { dn } = useDesignable(); const { dn } = useDesignable();
let readOnlyMode = 'editable'; let readOnlyMode = 'editable';
if (!fieldSchema) {
return console.error('fieldSchema is required') as any;
}
if (fieldSchema['x-disabled'] === true) { if (fieldSchema['x-disabled'] === true) {
readOnlyMode = 'readonly'; readOnlyMode = 'readonly';
} }
@ -283,6 +307,7 @@ export const tableColumnSettings = new SchemaSettings({
}; };
}, },
}, },
fieldComponentSettingsItem,
], ],
}, },
{ {

View File

@ -67,7 +67,6 @@ export const Action: ComposedAction = withDynamicSchemaProps(
const designerProps = fieldSchema['x-designer-props']; const designerProps = fieldSchema['x-designer-props'];
const openMode = fieldSchema?.['x-component-props']?.['openMode']; const openMode = fieldSchema?.['x-component-props']?.['openMode'];
const openSize = fieldSchema?.['x-component-props']?.['openSize']; const openSize = fieldSchema?.['x-component-props']?.['openSize'];
const disabled = form.disabled || field.disabled || field.data?.disabled || propsDisabled; const disabled = form.disabled || field.disabled || field.data?.disabled || propsDisabled;
const linkageRules = useMemo(() => fieldSchema?.['x-linkage-rules'] || [], [fieldSchema]); const linkageRules = useMemo(() => fieldSchema?.['x-linkage-rules'] || [], [fieldSchema]);
const { designable } = useDesignable(); const { designable } = useDesignable();
@ -175,7 +174,6 @@ export const Action: ComposedAction = withDynamicSchemaProps(
</ActionContextProvider> </ActionContextProvider>
); );
// fix /description
if (addChild) { if (addChild) {
return wrapSSR( return wrapSSR(
<RecordProvider record={null} parent={record}> <RecordProvider record={null} parent={record}>

View File

@ -7,12 +7,12 @@ import cls from 'classnames';
import _ from 'lodash'; import _ from 'lodash';
import { useToken } from '../__builtins__'; import { useToken } from '../__builtins__';
import { useDesignable, useFormBlockContext, useSchemaInitializerRender } from '../../../'; import { SchemaComponent, useDesignable, useFormBlockContext, useSchemaInitializerRender } from '../../../';
import { useFormBlockType } from '../../../block-provider'; import { useFormBlockType } from '../../../block-provider';
import { DndContext } from '../../common/dnd-context'; import { DndContext } from '../../common/dnd-context';
import useStyles from './Grid.style'; import useStyles from './Grid.style';
const GridRowContext = createContext<any>({}); export const GridRowContext = createContext<any>({});
GridRowContext.displayName = 'GridRowContext'; GridRowContext.displayName = 'GridRowContext';
const GridColContext = createContext<any>({}); const GridColContext = createContext<any>({});
GridColContext.displayName = 'GridColContext'; GridColContext.displayName = 'GridColContext';
@ -22,6 +22,9 @@ GridContext.displayName = 'GridContext';
const breakRemoveOnGrid = (s: Schema) => s['x-component'] === 'Grid'; const breakRemoveOnGrid = (s: Schema) => s['x-component'] === 'Grid';
const breakRemoveOnRow = (s: Schema) => s['x-component'] === 'Grid.Row'; const breakRemoveOnRow = (s: Schema) => s['x-component'] === 'Grid.Row';
const MemorizedRecursionField = React.memo(RecursionField);
MemorizedRecursionField.displayName = 'MemorizedRecursionField';
const ColDivider = (props) => { const ColDivider = (props) => {
const { token } = useToken(); const { token } = useToken();
const dragIdRef = useRef<string | null>(null); const dragIdRef = useRef<string | null>(null);
@ -306,7 +309,7 @@ export const useGridRowContext = () => {
export const Grid: any = observer( export const Grid: any = observer(
(props: any) => { (props: any) => {
const { showDivider = true } = props; const { distributed, showDivider = true } = props;
const gridRef = useRef(null); const gridRef = useRef(null);
const field = useField(); const field = useField();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
@ -316,6 +319,10 @@ export const Grid: any = observer(
const rows = useRowProperties(); const rows = useRowProperties();
const { setPrintContent } = useFormBlockContext(); const { setPrintContent } = useFormBlockContext();
const { wrapSSR, componentCls, hashId } = useStyles(); const { wrapSSR, componentCls, hashId } = useStyles();
const distributedValue =
distributed === undefined
? fieldSchema?.parent['x-component'] === 'Page' || fieldSchema?.parent['x-component'] === 'Tabs.TabPane'
: distributed;
useEffect(() => { useEffect(() => {
gridRef.current && setPrintContent?.(gridRef.current); gridRef.current && setPrintContent?.(gridRef.current);
@ -343,7 +350,11 @@ export const Grid: any = observer(
{rows.map((schema, index) => { {rows.map((schema, index) => {
return ( return (
<React.Fragment key={index}> <React.Fragment key={index}>
<RecursionField name={schema.name} schema={schema} /> {distributedValue ? (
<SchemaComponent name={schema.name} schema={schema} distributed />
) : (
<MemorizedRecursionField name={schema.name} schema={schema} />
)}
{showDivider ? ( {showDivider ? (
<RowDivider <RowDivider
rows={rows} rows={rows}

View File

@ -8,6 +8,14 @@ type SchemaComponentOnChange = {
onChange?: (s: Schema) => void; onChange?: (s: Schema) => void;
}; };
interface DistributedProps {
/**
*
* @default false
*/
distributed?: boolean;
}
function toSchema(schema?: any) { function toSchema(schema?: any) {
if (Schema.isSchemaInstance(schema)) { if (Schema.isSchemaInstance(schema)) {
return schema; return schema;
@ -55,7 +63,8 @@ const MemoizedSchemaComponent = (props: ISchemaFieldProps & SchemaComponentOnCha
}; };
export const SchemaComponent = ( export const SchemaComponent = (
props: (ISchemaFieldProps | IRecursionFieldProps) & { memoized?: boolean } & SchemaComponentOnChange, props: (ISchemaFieldProps | IRecursionFieldProps) & { memoized?: boolean } & SchemaComponentOnChange &
DistributedProps,
) => { ) => {
const { memoized, ...others } = props; const { memoized, ...others } = props;
if (memoized) { if (memoized) {