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:
parent
9dc412dbf3
commit
27cda38a90
@ -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 [
|
||||||
|
@ -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') {
|
||||||
|
@ -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>;
|
||||||
|
@ -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;
|
||||||
|
};
|
||||||
|
@ -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,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
@ -0,0 +1 @@
|
|||||||
|
export * from './fieldComponent';
|
@ -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';
|
||||||
|
@ -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"
|
||||||
}
|
}
|
||||||
|
@ -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',
|
||||||
|
@ -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',
|
||||||
|
@ -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',
|
||||||
|
@ -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,
|
||||||
|
@ -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({
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -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>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@ -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',
|
||||||
|
@ -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,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -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}>
|
||||||
|
@ -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}
|
||||||
|
@ -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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user