Merge pull request 'fix: 数据关联表引用自己的情况不显示内容-同步官方, 发布后需要重新配置区块' (#596) from fix_target_id2 into dev

Reviewed-on: daoyoucloud/tachycode#596
This commit is contained in:
sealday 2024-04-03 19:21:39 +08:00
commit 73d37ec19f
18 changed files with 118 additions and 37 deletions

View File

@ -5,12 +5,19 @@ import {
useCollectionParentRecordData, useCollectionParentRecordData,
useCollectionRecordData, useCollectionRecordData,
} from '../..'; } from '../..';
import { useSourceKey } from '../../modules/blocks/useSourceKey';
/** /**
* @internal * @internal
* @deprecated
* Schema x-use-decorator-props sourceId
*
* schema sourceId * schema sourceId
* recordData parentRecordData ; schema * recordData parentRecordData ; schema
* `通过点击关系字段按钮打开的弹窗中创建的非关系字段区块``关系字段区块`使 hook * `通过点击关系字段按钮打开的弹窗中创建的非关系字段区块``关系字段区块`使 hook
*
* schema https://nocobase.height.app/T-3848/description
*
* @param param0 * @param param0
* @returns * @returns
*/ */
@ -18,26 +25,19 @@ export const useDataBlockSourceId = ({ association }: { association: string }) =
const recordData = useCollectionRecordData(); const recordData = useCollectionRecordData();
const parentRecordData = useCollectionParentRecordData(); const parentRecordData = useCollectionParentRecordData();
const cm = useCollectionManager(); const cm = useCollectionManager();
const collectionOutsideBlock = useCollection<InheritanceCollectionMixin>(); const currentRecordCollection = useCollection<InheritanceCollectionMixin>();
const sourceKey = useSourceKey(association);
if (!association) return; if (!association) return;
const associationField = cm.getCollectionField(association); const sourceCollection = cm.getCollection<InheritanceCollectionMixin>(association.split('.')[0]);
const associationCollection = cm.getCollection<InheritanceCollectionMixin>(associationField.collectionName);
if ( if (
collectionOutsideBlock.name === associationCollection.name || currentRecordCollection.name === sourceCollection.name ||
collectionOutsideBlock.getParentCollectionsName?.().includes(associationCollection.name) currentRecordCollection.getParentCollectionsName?.().includes(sourceCollection.name)
) { ) {
return recordData?.[ return recordData?.[sourceKey];
associationField.sourceKey ||
associationCollection.filterTargetKey ||
associationCollection.getPrimaryKey() ||
'id'
];
} }
return parentRecordData?.[ return parentRecordData?.[sourceKey];
associationField.sourceKey || associationCollection.filterTargetKey || associationCollection.getPrimaryKey() || 'id'
];
}; };

View File

@ -7,6 +7,7 @@ import { AllDataBlockProps, useDataBlockProps } from './DataBlockProvider';
import { useDataBlockResource } from './DataBlockResourceProvider'; import { useDataBlockResource } from './DataBlockResourceProvider';
import { useDataSourceHeaders } from '../utils'; import { useDataSourceHeaders } from '../utils';
import { useDataLoadingMode } from '../../modules/blocks/data-blocks/details-multi/setDataLoadingModeSettingsItem'; import { useDataLoadingMode } from '../../modules/blocks/data-blocks/details-multi/setDataLoadingModeSettingsItem';
import { useSourceKey } from '../../modules/blocks/useSourceKey';
export const BlockRequestContext = createContext<UseRequestResult<any>>(null); export const BlockRequestContext = createContext<UseRequestResult<any>>(null);
BlockRequestContext.displayName = 'BlockRequestContext'; BlockRequestContext.displayName = 'BlockRequestContext';
@ -57,14 +58,15 @@ function useParentRequest<T>(options: Omit<AllDataBlockProps, 'type'>) {
const api = useAPIClient(); const api = useAPIClient();
const dataBlockProps = useDataBlockProps(); const dataBlockProps = useDataBlockProps();
const headers = useDataSourceHeaders(dataBlockProps.dataSource); const headers = useDataSourceHeaders(dataBlockProps.dataSource);
const sourceKey = useSourceKey(association);
return useRequest<T>( return useRequest<T>(
async () => { async () => {
if (parentRecord) return Promise.resolve({ data: parentRecord }); if (parentRecord) return Promise.resolve({ data: parentRecord });
if (!association) return Promise.resolve({ data: undefined }); if (!association || !sourceKey) return Promise.resolve({ data: undefined });
// "association": "Collection.Field" // "association": "Collection.Field"
const arr = association.split('.'); const arr = association.split('.');
// <collection>:get/<filterByTk> // <collection>:get/<filterByTk>
const url = `${arr[0]}:get/${sourceId}`; const url = `${arr[0]}:get?filter[${sourceKey}]=${sourceId}`;
const res = await api.request({ url, headers }); const res = await api.request({ url, headers });
return res.data; return res.data;
}, },

View File

@ -61,6 +61,7 @@ export * from './modules/blocks/data-blocks/table';
export * from './modules/blocks/data-blocks/form'; export * from './modules/blocks/data-blocks/form';
export * from './modules/blocks/data-blocks/table-selector'; export * from './modules/blocks/data-blocks/table-selector';
export * from './modules/blocks/data-blocks/details-multi/setDataLoadingModeSettingsItem'; export * from './modules/blocks/data-blocks/details-multi/setDataLoadingModeSettingsItem';
export * from './modules/blocks/useSourceIdCommon';
export * as __UNSAFE__ from './unsafe'; export * as __UNSAFE__ from './unsafe';
export type { export type {

View File

@ -1,4 +1,4 @@
import { useDataBlockSourceId } from '../../../../../block-provider/hooks/useDataBlockSourceId'; import { useSourceIdCommon } from '../../../useSourceIdCommon';
export function useDetailsWithPaginationDecoratorProps(props) { export function useDetailsWithPaginationDecoratorProps(props) {
let sourceId; let sourceId;
@ -6,7 +6,7 @@ export function useDetailsWithPaginationDecoratorProps(props) {
// association 的值是固定不变的,所以可以在条件中使用 hooks // association 的值是固定不变的,所以可以在条件中使用 hooks
if (props.association) { if (props.association) {
// eslint-disable-next-line react-hooks/rules-of-hooks // eslint-disable-next-line react-hooks/rules-of-hooks
sourceId = useDataBlockSourceId({ association: props.association }); sourceId = useSourceIdCommon(props.association);
} }
return { return {

View File

@ -69,6 +69,7 @@ export function useCreateSingleDetailsSchema() {
? { ? {
association, association,
dataSource: item.dataSource, dataSource: item.dataSource,
isCurrent: true,
} }
: { : {
collectionName: item.collectionName || item.name, collectionName: item.collectionName || item.name,

View File

@ -6,9 +6,14 @@ export function createDetailsUISchema(options: {
collectionName?: string; collectionName?: string;
association?: string; association?: string;
templateSchema?: ISchema; templateSchema?: ISchema;
/**
* true record useRecord record
*/
isCurrent?: boolean;
}): ISchema { }): ISchema {
const { collectionName, dataSource, association, templateSchema } = options; const { collectionName, dataSource, association, templateSchema } = options;
const resourceName = association || collectionName; const resourceName = association || collectionName;
const isCurrentObj = options.isCurrent ? { 'x-is-current': true } : {};
if (!dataSource) { if (!dataSource) {
throw new Error('dataSource are required'); throw new Error('dataSource are required');
@ -29,6 +34,7 @@ export function createDetailsUISchema(options: {
'x-toolbar': 'BlockSchemaToolbar', 'x-toolbar': 'BlockSchemaToolbar',
'x-settings': 'blockSettings:details', 'x-settings': 'blockSettings:details',
'x-component': 'CardItem', 'x-component': 'CardItem',
...isCurrentObj,
properties: { properties: {
[uid()]: { [uid()]: {
type: 'void', type: 'void',

View File

@ -1,6 +1,16 @@
import { useFieldSchema } from '@formily/react';
import { useParamsFromRecord } from '../../../../../block-provider/BlockProvider'; import { useParamsFromRecord } from '../../../../../block-provider/BlockProvider';
import { useDataBlockSourceId } from '../../../../../block-provider/hooks/useDataBlockSourceId'; import {
useCollectionParentRecordData,
useCollectionRecordData,
} from '../../../../../data-source/collection-record/CollectionRecordProvider';
import { useSourceKey } from '../../../useSourceKey';
/**
* Current record Add block
* @param props
* @returns
*/
export function useDetailsDecoratorProps(props) { export function useDetailsDecoratorProps(props) {
const params = useParamsFromRecord(); const params = useParamsFromRecord();
let sourceId; let sourceId;
@ -8,7 +18,7 @@ export function useDetailsDecoratorProps(props) {
// association 的值是固定不变的,所以可以在条件中使用 hooks // association 的值是固定不变的,所以可以在条件中使用 hooks
if (props.association) { if (props.association) {
// eslint-disable-next-line react-hooks/rules-of-hooks // eslint-disable-next-line react-hooks/rules-of-hooks
sourceId = useDataBlockSourceId({ association: props.association }); sourceId = useDetailsSourceId(props.association);
} }
return { return {
@ -16,3 +26,18 @@ export function useDetailsDecoratorProps(props) {
sourceId, sourceId,
}; };
} }
export function useDetailsSourceId(association: string) {
const fieldSchema = useFieldSchema();
const recordData = useCollectionRecordData();
const parentRecordData = useCollectionParentRecordData();
const sourceKey = useSourceKey(association);
if (!association) return;
if (fieldSchema['x-is-current']) {
return parentRecordData?.[sourceKey];
}
return recordData?.[sourceKey];
}

View File

@ -49,6 +49,7 @@ export function useCreateEditFormBlock() {
? { ? {
association, association,
dataSource: item.dataSource, dataSource: item.dataSource,
isCurrent: true,
} }
: { : {
collectionName: item.collectionName || item.name, collectionName: item.collectionName || item.name,

View File

@ -7,11 +7,16 @@ interface EditFormBlockOptions {
collectionName?: string; collectionName?: string;
association?: string; association?: string;
templateSchema?: ISchema; templateSchema?: ISchema;
/**
* true record useRecord record
*/
isCurrent?: boolean;
} }
export function createEditFormBlockUISchema(options: EditFormBlockOptions): ISchema { export function createEditFormBlockUISchema(options: EditFormBlockOptions): ISchema {
const { collectionName, dataSource, association, templateSchema } = options; const { collectionName, dataSource, association, templateSchema } = options;
const resourceName = association || collectionName; const resourceName = association || collectionName;
const isCurrentObj = options.isCurrent ? { 'x-is-current': true } : {};
if (!dataSource) { if (!dataSource) {
throw new Error('dataSource are required'); throw new Error('dataSource are required');
@ -34,6 +39,7 @@ export function createEditFormBlockUISchema(options: EditFormBlockOptions): ISch
'x-toolbar': 'BlockSchemaToolbar', 'x-toolbar': 'BlockSchemaToolbar',
'x-settings': 'blockSettings:editForm', 'x-settings': 'blockSettings:editForm',
'x-component': 'CardItem', 'x-component': 'CardItem',
...isCurrentObj,
properties: { properties: {
[uid()]: { [uid()]: {
type: 'void', type: 'void',

View File

@ -1,4 +1,4 @@
import { useFormBlockSourceId } from './useFormBlockSourceId'; import { useSourceIdCommon } from '../../../useSourceIdCommon';
export function useCreateFormBlockDecoratorProps(props) { export function useCreateFormBlockDecoratorProps(props) {
let sourceId; let sourceId;
@ -6,7 +6,7 @@ export function useCreateFormBlockDecoratorProps(props) {
// association 的值是固定不变的,所以这里可以使用 hooks // association 的值是固定不变的,所以这里可以使用 hooks
if (props.association) { if (props.association) {
// eslint-disable-next-line react-hooks/rules-of-hooks // eslint-disable-next-line react-hooks/rules-of-hooks
sourceId = useFormBlockSourceId(props); sourceId = useSourceIdCommon(props.association);
} }
return { return {

View File

@ -1,5 +1,5 @@
import { useParamsFromRecord } from '../../../../../block-provider/BlockProvider'; import { useParamsFromRecord } from '../../../../../block-provider/BlockProvider';
import { useFormBlockSourceId } from './useFormBlockSourceId'; import { useDetailsSourceId } from '../../details-single/hooks/useDetailsDecoratorProps';
export function useEditFormBlockDecoratorProps(props) { export function useEditFormBlockDecoratorProps(props) {
const params = useFormBlockParams(); const params = useFormBlockParams();
@ -7,8 +7,9 @@ export function useEditFormBlockDecoratorProps(props) {
// association 的值是固定不变的,所以这里可以使用 hooks // association 的值是固定不变的,所以这里可以使用 hooks
if (props.association) { if (props.association) {
// 复用详情区块的 sourceId 获取逻辑
// eslint-disable-next-line react-hooks/rules-of-hooks // eslint-disable-next-line react-hooks/rules-of-hooks
sourceId = useFormBlockSourceId(props); sourceId = useDetailsSourceId(props.association);
} }
return { return {

View File

@ -1,5 +0,0 @@
import { useDataBlockSourceId } from '../../../../../block-provider/hooks/useDataBlockSourceId';
export function useFormBlockSourceId(props) {
return useDataBlockSourceId(props);
}

View File

@ -1,5 +1,5 @@
import { useDataBlockSourceId } from '../../../../../block-provider/hooks/useDataBlockSourceId';
import { useGridCardBlockParams } from './useGridCardBlockParams'; import { useGridCardBlockParams } from './useGridCardBlockParams';
import { useSourceIdCommon } from '../../../useSourceIdCommon';
export function useGridCardBlockDecoratorProps(props) { export function useGridCardBlockDecoratorProps(props) {
const params = useGridCardBlockParams(props); const params = useGridCardBlockParams(props);
@ -8,7 +8,7 @@ export function useGridCardBlockDecoratorProps(props) {
// 因为 association 是固定的,所以可以在条件中使用 hooks // 因为 association 是固定的,所以可以在条件中使用 hooks
if (props.association) { if (props.association) {
// eslint-disable-next-line react-hooks/rules-of-hooks // eslint-disable-next-line react-hooks/rules-of-hooks
sourceId = useDataBlockSourceId({ association: props.association }); sourceId = useSourceIdCommon(props.association);
} }
return { return {

View File

@ -1,4 +1,4 @@
import { useDataBlockSourceId } from '../../../../../block-provider/hooks/useDataBlockSourceId'; import { useSourceIdCommon } from '../../../useSourceIdCommon';
export function useListBlockDecoratorProps(props) { export function useListBlockDecoratorProps(props) {
let sourceId; let sourceId;
@ -6,7 +6,7 @@ export function useListBlockDecoratorProps(props) {
// 因为 association 的值是固定的,所以这里可以使用 hooks // 因为 association 的值是固定的,所以这里可以使用 hooks
if (props.association) { if (props.association) {
// eslint-disable-next-line react-hooks/rules-of-hooks // eslint-disable-next-line react-hooks/rules-of-hooks
sourceId = useDataBlockSourceId({ association: props.association }); sourceId = useSourceIdCommon(props.association);
} }
return { return {

View File

@ -1,7 +1,7 @@
import { useFieldSchema } from '@nocobase/schema'; import { useFieldSchema } from '@nocobase/schema';
import { useParsedFilter } from '../../../../../block-provider/hooks/useParsedFilter'; import { useParsedFilter } from '../../../../../block-provider/hooks/useParsedFilter';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useDataBlockSourceId } from '../../../../../block-provider/hooks/useDataBlockSourceId'; import { useSourceIdCommon } from '../../../useSourceIdCommon';
export const useTableBlockDecoratorProps = (props) => { export const useTableBlockDecoratorProps = (props) => {
const params = useTableBlockParams(props); const params = useTableBlockParams(props);
@ -44,7 +44,7 @@ function useTableBlockSourceId(props) {
// 因为 association 是固定不变的,所以在条件中使用 hooks 是安全的 // 因为 association 是固定不变的,所以在条件中使用 hooks 是安全的
if (props.association) { if (props.association) {
// eslint-disable-next-line react-hooks/rules-of-hooks // eslint-disable-next-line react-hooks/rules-of-hooks
sourceId = useDataBlockSourceId({ association: props.association }); sourceId = useSourceIdCommon(props.association);
} }
return sourceId; return sourceId;

View File

@ -0,0 +1,17 @@
import { useCollectionRecordData } from '../../data-source/collection-record/CollectionRecordProvider';
import { useSourceKey } from './useSourceKey';
/**
* @internal
* sourceId hook
* @param association
* @returns
*/
export const useSourceIdCommon = (association: string) => {
const recordData = useCollectionRecordData();
const sourceKey = useSourceKey(association);
if (!association) return;
return recordData?.[sourceKey];
};

View File

@ -0,0 +1,26 @@
import { InheritanceCollectionMixin } from '../../collection-manager/mixins/InheritanceCollectionMixin';
import { useCollectionManager } from '../../data-source/collection/CollectionManagerProvider';
/**
* source collection key
* @param association string
* @returns
*/
export const useSourceKey = (association: string) => {
const cm = useCollectionManager();
if (!association) return;
const associationField = cm.getCollectionField(association);
if (!associationField) {
return;
}
const sourceCollection = cm.getCollection<InheritanceCollectionMixin>(association.split('.')[0]);
// 1. hasOne 和 hasMany 和 belongsToMany 的字段存在 sourceKey所以会直接返回 sourceKey
// 2. belongsTo 不存在 sourceKey所以会使用 filterTargetKey
// 3. 后面的主键和 id 是为了保险起见加上的;
return associationField.sourceKey || sourceCollection.filterTargetKey || sourceCollection.getPrimaryKey() || 'id';
};

View File

@ -1,4 +1,4 @@
import { useDataBlockSourceId } from '@nocobase/client'; import { useSourceIdCommon } from '@nocobase/client';
import { useCalendarBlockParams } from './useCalendarBlockParams'; import { useCalendarBlockParams } from './useCalendarBlockParams';
export function useCalendarBlockDecoratorProps(props) { export function useCalendarBlockDecoratorProps(props) {
@ -8,7 +8,7 @@ export function useCalendarBlockDecoratorProps(props) {
// 因为 association 是一个固定的值,所以可以在 hooks 中直接使用 // 因为 association 是一个固定的值,所以可以在 hooks 中直接使用
if (props.association) { if (props.association) {
// eslint-disable-next-line react-hooks/rules-of-hooks // eslint-disable-next-line react-hooks/rules-of-hooks
sourceId = useDataBlockSourceId({ association: props.association }); sourceId = useSourceIdCommon(props.association);
} }
return { return {